21 Commits

Author SHA1 Message Date
drew ec2a756bf5 fixed typo 2026-07-25 16:41:18 -05:00
drew b2bb60a4a3 added paths and stuff 2026-07-25 16:39:36 -05:00
drew cc8e8cdfc6 created inital detail page 2026-07-25 16:36:56 -05:00
drew fb25b8aba6 fixed a lot of stuff 2026-07-25 16:19:23 -05:00
drew 1b478cd7ba seperated out start and end date and time into seprate date and time fields 2026-07-25 15:59:46 -05:00
drew 5517924259 Fix duplicate changedOrderToJob migration blocking build
Two divergent copies of this branch each independently regenerated an
EF migration named changedOrderToJob, and merging both left two files
with the same C# class name (compile error) plus an orphaned
fixedOrderCustomerID migration referencing the already-renamed orders
table. Removed the dead/unapplied migrations, committed the two
migrations already live on the shared dev DB, and added
FixJobsCustomerIdType to bring jobs.customer_id in line with the model
(text -> integer) using an explicit USING cast.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-25 15:45:09 -05:00
drew c79bda3960 Merge remote-tracking branch 'origin/add-job-pages' into add-job-pages 2026-07-23 14:21:36 -05:00
drew 9906f015fc added new job page 2026-07-23 14:21:17 -05:00
drew 10ca133a7a changes 2026-07-23 14:20:14 -05:00
drew 85678f5742 changed order to job - updated model, 2026-07-23 14:20:14 -05:00
drew 69da029156 list hook 2026-07-23 14:20:14 -05:00
drew 087d94cbd9 Types! 2026-07-23 14:20:14 -05:00
drew 33f59fd7f8 fixed customer id type 2026-07-23 14:20:14 -05:00
Drew Rautenberg 775d64ef9e Merge pull request #43 from BarkProductions/update-dependencies
updated packages to the latest versions
2026-07-23 14:16:50 -05:00
drew 4a479283da updated packages to the latest versions 2026-07-23 14:11:49 -05:00
drew 757a51997f changes 2026-07-23 13:34:38 -05:00
Drew Rautenberg 2eaa8723d0 Merge pull request #42 from BarkProductions/40-add-job-endpoints
40 add job endpoints
2026-07-20 19:51:51 -05:00
drew 0d2e049e69 added job itms endpoints 2026-07-19 20:50:02 -05:00
drew 656ccd7d52 added endpoints and fixed some typos 2026-07-19 20:41:02 -05:00
drew 7dcc366393 added job endpoints 2026-07-19 16:10:09 -05:00
drew 8b0a90d2e5 changed order to job 2026-07-19 15:41:47 -05:00
24 changed files with 2707 additions and 1942 deletions
@@ -12,8 +12,8 @@ using barkmanapi;
namespace barkmanapi.Migrations
{
[DbContext(typeof(BarkContext))]
[Migration("20260723003407_fixedOrderCustomerID")]
partial class fixedOrderCustomerID
[Migration("20260719204137_changedOrderToJob")]
partial class changedOrderToJob
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -148,7 +148,7 @@ namespace barkmanapi.Migrations
b.ToTable("item_status", (string)null);
});
modelBuilder.Entity("barkmanapi.OrderItems", b =>
modelBuilder.Entity("barkmanapi.JobItems", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@@ -162,29 +162,29 @@ namespace barkmanapi.Migrations
.HasColumnType("text")
.HasColumnName("item_id");
b.Property<int?>("JobsId")
.HasColumnType("integer")
.HasColumnName("jobs_id");
b.Property<string>("OrderId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("order_id");
b.Property<int?>("OrdersId")
.HasColumnType("integer")
.HasColumnName("orders_id");
b.Property<int>("Quantity")
.HasColumnType("integer")
.HasColumnName("quantity");
b.HasKey("Id")
.HasName("pk_order_items");
.HasName("pk_job_items");
b.HasIndex("OrdersId")
.HasDatabaseName("ix_order_items_orders_id");
b.HasIndex("JobsId")
.HasDatabaseName("ix_job_items_jobs_id");
b.ToTable("order_items", (string)null);
b.ToTable("job_items", (string)null);
});
modelBuilder.Entity("barkmanapi.OrderStatus", b =>
modelBuilder.Entity("barkmanapi.JobStatus", b =>
{
b.Property<string>("Id")
.HasColumnType("text")
@@ -196,12 +196,12 @@ namespace barkmanapi.Migrations
.HasColumnName("name");
b.HasKey("Id")
.HasName("pk_order_status");
.HasName("pk_job_status");
b.ToTable("order_status", (string)null);
b.ToTable("job_status", (string)null);
});
modelBuilder.Entity("barkmanapi.Orders", b =>
modelBuilder.Entity("barkmanapi.Jobs", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
@@ -214,8 +214,9 @@ namespace barkmanapi.Migrations
.HasColumnType("text")
.HasColumnName("address");
b.Property<int>("CustomerId")
.HasColumnType("integer")
b.Property<string>("CustomerId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("customer_id");
b.Property<DateTimeOffset>("EndDate")
@@ -248,12 +249,12 @@ namespace barkmanapi.Migrations
.HasColumnName("venue");
b.HasKey("Id")
.HasName("pk_orders");
.HasName("pk_jobs");
b.HasIndex("StatusId")
.HasDatabaseName("ix_orders_status_id");
.HasDatabaseName("ix_jobs_status_id");
b.ToTable("orders", (string)null);
b.ToTable("jobs", (string)null);
});
modelBuilder.Entity("barkmanapi.InventoryItems", b =>
@@ -266,25 +267,25 @@ namespace barkmanapi.Migrations
b.Navigation("Status");
});
modelBuilder.Entity("barkmanapi.OrderItems", b =>
modelBuilder.Entity("barkmanapi.JobItems", b =>
{
b.HasOne("barkmanapi.Orders", null)
b.HasOne("barkmanapi.Jobs", null)
.WithMany("Items")
.HasForeignKey("OrdersId")
.HasConstraintName("fk_order_items_orders_orders_id");
.HasForeignKey("JobsId")
.HasConstraintName("fk_job_items_jobs_jobs_id");
});
modelBuilder.Entity("barkmanapi.Orders", b =>
modelBuilder.Entity("barkmanapi.Jobs", b =>
{
b.HasOne("barkmanapi.OrderStatus", "Status")
b.HasOne("barkmanapi.JobStatus", "Status")
.WithMany()
.HasForeignKey("StatusId")
.HasConstraintName("fk_orders_order_status_status_id");
.HasConstraintName("fk_jobs_job_status_status_id");
b.Navigation("Status");
});
modelBuilder.Entity("barkmanapi.Orders", b =>
modelBuilder.Entity("barkmanapi.Jobs", b =>
{
b.Navigation("Items");
});
@@ -39,7 +39,7 @@ namespace barkmanapi.Migrations
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
customer_id = table.Column<int>(type: "integer", nullable: false),
customer_id = table.Column<string>(type: "text", nullable: false),
name = table.Column<string>(type: "text", nullable: false),
status_id = table.Column<string>(type: "text", nullable: true),
start_date = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
@@ -123,7 +123,7 @@ namespace barkmanapi.Migrations
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
status_id = table.Column<string>(type: "text", nullable: true),
address = table.Column<string>(type: "text", nullable: true),
customer_id = table.Column<int>(type: "integer", nullable: false),
customer_id = table.Column<string>(type: "text", nullable: false),
end_date = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
name = table.Column<string>(type: "text", nullable: false),
notes = table.Column<string>(type: "text", nullable: true),
@@ -1,34 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace barkmanapi.Migrations
{
/// <inheritdoc />
public partial class fixedOrderCustomerID : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<int>(
name: "customer_id",
table: "orders",
type: "integer",
nullable: false,
oldClrType: typeof(string),
oldType: "text");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "customer_id",
table: "orders",
type: "text",
nullable: false,
oldClrType: typeof(int),
oldType: "integer");
}
}
}
@@ -12,8 +12,8 @@ using barkmanapi;
namespace barkmanapi.Migrations
{
[DbContext(typeof(BarkContext))]
[Migration("20260723004900_changedOrderToJob")]
partial class changedOrderToJob
[Migration("20260723200239_maybethisworks")]
partial class maybethisworks
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace barkmanapi.Migrations
{
/// <inheritdoc />
public partial class maybethisworks : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
@@ -0,0 +1,294 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using barkmanapi;
#nullable disable
namespace barkmanapi.Migrations
{
[DbContext(typeof(BarkContext))]
[Migration("20260723200547_changedordertojobpt2")]
partial class changedordertojobpt2
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("barkmanapi.Customers", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Address")
.HasColumnType("text")
.HasColumnName("address");
b.Property<string>("BillingTerms")
.HasColumnType("text")
.HasColumnName("billing_terms");
b.Property<string>("Company")
.HasColumnType("text")
.HasColumnName("company");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.HasColumnName("email");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Notes")
.HasColumnType("text")
.HasColumnName("notes");
b.Property<string>("PhoneNumber")
.HasColumnType("text")
.HasColumnName("phone_number");
b.HasKey("Id")
.HasName("pk_customers");
b.ToTable("customers", (string)null);
});
modelBuilder.Entity("barkmanapi.InventoryItems", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Barcode")
.IsRequired()
.HasColumnType("text")
.HasColumnName("barcode");
b.Property<string>("Brand")
.IsRequired()
.HasColumnType("text")
.HasColumnName("brand");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Notes")
.HasColumnType("text")
.HasColumnName("notes");
b.Property<float?>("RentalPrice")
.HasColumnType("real")
.HasColumnName("rental_price");
b.Property<float?>("ReplacementCost")
.HasColumnType("real")
.HasColumnName("replacement_cost");
b.Property<string>("SerialNumber")
.HasColumnType("text")
.HasColumnName("serial_number");
b.Property<string>("StatusId")
.HasColumnType("text")
.HasColumnName("status_id");
b.Property<float?>("Weight")
.HasColumnType("real")
.HasColumnName("weight");
b.HasKey("Id")
.HasName("pk_inventory");
b.HasIndex("Barcode")
.IsUnique()
.HasDatabaseName("ix_inventory_barcode");
b.HasIndex("StatusId")
.HasDatabaseName("ix_inventory_status_id");
b.ToTable("inventory", (string)null);
});
modelBuilder.Entity("barkmanapi.ItemStatus", b =>
{
b.Property<string>("Id")
.HasColumnType("text")
.HasColumnName("id");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.HasKey("Id")
.HasName("pk_item_status");
b.ToTable("item_status", (string)null);
});
modelBuilder.Entity("barkmanapi.JobItems", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ItemId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("item_id");
b.Property<string>("JobId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("job_id");
b.Property<int?>("JobsId")
.HasColumnType("integer")
.HasColumnName("jobs_id");
b.Property<int>("Quantity")
.HasColumnType("integer")
.HasColumnName("quantity");
b.HasKey("Id")
.HasName("pk_job_items");
b.HasIndex("JobsId")
.HasDatabaseName("ix_job_items_jobs_id");
b.ToTable("job_items", (string)null);
});
modelBuilder.Entity("barkmanapi.JobStatus", b =>
{
b.Property<string>("Id")
.HasColumnType("text")
.HasColumnName("id");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.HasKey("Id")
.HasName("pk_job_status");
b.ToTable("job_status", (string)null);
});
modelBuilder.Entity("barkmanapi.Jobs", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Address")
.HasColumnType("text")
.HasColumnName("address");
b.Property<int>("CustomerId")
.HasColumnType("integer")
.HasColumnName("customer_id");
b.Property<DateTimeOffset>("EndDate")
.HasColumnType("timestamp with time zone")
.HasColumnName("end_date");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Notes")
.HasColumnType("text")
.HasColumnName("notes");
b.Property<DateTimeOffset>("StartDate")
.HasColumnType("timestamp with time zone")
.HasColumnName("start_date");
b.Property<string>("StatusId")
.HasColumnType("text")
.HasColumnName("status_id");
b.Property<decimal>("TotalPrice")
.HasColumnType("numeric")
.HasColumnName("total_price");
b.Property<string>("Venue")
.HasColumnType("text")
.HasColumnName("venue");
b.HasKey("Id")
.HasName("pk_jobs");
b.HasIndex("StatusId")
.HasDatabaseName("ix_jobs_status_id");
b.ToTable("jobs", (string)null);
});
modelBuilder.Entity("barkmanapi.InventoryItems", b =>
{
b.HasOne("barkmanapi.ItemStatus", "Status")
.WithMany()
.HasForeignKey("StatusId")
.HasConstraintName("fk_inventory_item_status_status_id");
b.Navigation("Status");
});
modelBuilder.Entity("barkmanapi.JobItems", b =>
{
b.HasOne("barkmanapi.Jobs", null)
.WithMany("Items")
.HasForeignKey("JobsId")
.HasConstraintName("fk_job_items_jobs_jobs_id");
});
modelBuilder.Entity("barkmanapi.Jobs", b =>
{
b.HasOne("barkmanapi.JobStatus", "Status")
.WithMany()
.HasForeignKey("StatusId")
.HasConstraintName("fk_jobs_job_status_status_id");
b.Navigation("Status");
});
modelBuilder.Entity("barkmanapi.Jobs", b =>
{
b.Navigation("Items");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace barkmanapi.Migrations
{
/// <inheritdoc />
public partial class changedordertojobpt2 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "order_id",
table: "job_items",
newName: "job_id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "job_id",
table: "job_items",
newName: "order_id");
}
}
}
@@ -0,0 +1,294 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using barkmanapi;
#nullable disable
namespace barkmanapi.Migrations
{
[DbContext(typeof(BarkContext))]
[Migration("20260725204039_FixJobsCustomerIdType")]
partial class FixJobsCustomerIdType
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("barkmanapi.Customers", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Address")
.HasColumnType("text")
.HasColumnName("address");
b.Property<string>("BillingTerms")
.HasColumnType("text")
.HasColumnName("billing_terms");
b.Property<string>("Company")
.HasColumnType("text")
.HasColumnName("company");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.HasColumnName("email");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Notes")
.HasColumnType("text")
.HasColumnName("notes");
b.Property<string>("PhoneNumber")
.HasColumnType("text")
.HasColumnName("phone_number");
b.HasKey("Id")
.HasName("pk_customers");
b.ToTable("customers", (string)null);
});
modelBuilder.Entity("barkmanapi.InventoryItems", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Barcode")
.IsRequired()
.HasColumnType("text")
.HasColumnName("barcode");
b.Property<string>("Brand")
.IsRequired()
.HasColumnType("text")
.HasColumnName("brand");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Notes")
.HasColumnType("text")
.HasColumnName("notes");
b.Property<float?>("RentalPrice")
.HasColumnType("real")
.HasColumnName("rental_price");
b.Property<float?>("ReplacementCost")
.HasColumnType("real")
.HasColumnName("replacement_cost");
b.Property<string>("SerialNumber")
.HasColumnType("text")
.HasColumnName("serial_number");
b.Property<string>("StatusId")
.HasColumnType("text")
.HasColumnName("status_id");
b.Property<float?>("Weight")
.HasColumnType("real")
.HasColumnName("weight");
b.HasKey("Id")
.HasName("pk_inventory");
b.HasIndex("Barcode")
.IsUnique()
.HasDatabaseName("ix_inventory_barcode");
b.HasIndex("StatusId")
.HasDatabaseName("ix_inventory_status_id");
b.ToTable("inventory", (string)null);
});
modelBuilder.Entity("barkmanapi.ItemStatus", b =>
{
b.Property<string>("Id")
.HasColumnType("text")
.HasColumnName("id");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.HasKey("Id")
.HasName("pk_item_status");
b.ToTable("item_status", (string)null);
});
modelBuilder.Entity("barkmanapi.JobItems", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ItemId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("item_id");
b.Property<string>("JobId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("job_id");
b.Property<int?>("JobsId")
.HasColumnType("integer")
.HasColumnName("jobs_id");
b.Property<int>("Quantity")
.HasColumnType("integer")
.HasColumnName("quantity");
b.HasKey("Id")
.HasName("pk_job_items");
b.HasIndex("JobsId")
.HasDatabaseName("ix_job_items_jobs_id");
b.ToTable("job_items", (string)null);
});
modelBuilder.Entity("barkmanapi.JobStatus", b =>
{
b.Property<string>("Id")
.HasColumnType("text")
.HasColumnName("id");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.HasKey("Id")
.HasName("pk_job_status");
b.ToTable("job_status", (string)null);
});
modelBuilder.Entity("barkmanapi.Jobs", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Address")
.HasColumnType("text")
.HasColumnName("address");
b.Property<int>("CustomerId")
.HasColumnType("integer")
.HasColumnName("customer_id");
b.Property<DateTimeOffset>("EndDate")
.HasColumnType("timestamp with time zone")
.HasColumnName("end_date");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Notes")
.HasColumnType("text")
.HasColumnName("notes");
b.Property<DateTimeOffset>("StartDate")
.HasColumnType("timestamp with time zone")
.HasColumnName("start_date");
b.Property<string>("StatusId")
.HasColumnType("text")
.HasColumnName("status_id");
b.Property<decimal>("TotalPrice")
.HasColumnType("numeric")
.HasColumnName("total_price");
b.Property<string>("Venue")
.HasColumnType("text")
.HasColumnName("venue");
b.HasKey("Id")
.HasName("pk_jobs");
b.HasIndex("StatusId")
.HasDatabaseName("ix_jobs_status_id");
b.ToTable("jobs", (string)null);
});
modelBuilder.Entity("barkmanapi.InventoryItems", b =>
{
b.HasOne("barkmanapi.ItemStatus", "Status")
.WithMany()
.HasForeignKey("StatusId")
.HasConstraintName("fk_inventory_item_status_status_id");
b.Navigation("Status");
});
modelBuilder.Entity("barkmanapi.JobItems", b =>
{
b.HasOne("barkmanapi.Jobs", null)
.WithMany("Items")
.HasForeignKey("JobsId")
.HasConstraintName("fk_job_items_jobs_jobs_id");
});
modelBuilder.Entity("barkmanapi.Jobs", b =>
{
b.HasOne("barkmanapi.JobStatus", "Status")
.WithMany()
.HasForeignKey("StatusId")
.HasConstraintName("fk_jobs_job_status_status_id");
b.Navigation("Status");
});
modelBuilder.Entity("barkmanapi.Jobs", b =>
{
b.Navigation("Items");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace barkmanapi.Migrations
{
/// <inheritdoc />
public partial class FixJobsCustomerIdType : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(
"ALTER TABLE jobs ALTER COLUMN customer_id TYPE integer USING customer_id::integer;");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(
"ALTER TABLE jobs ALTER COLUMN customer_id TYPE text USING customer_id::text;");
}
}
}
@@ -0,0 +1,294 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using barkmanapi;
#nullable disable
namespace barkmanapi.Migrations
{
[DbContext(typeof(BarkContext))]
[Migration("20260725205242_change_datetimeoffset_to_datetime")]
partial class change_datetimeoffset_to_datetime
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("barkmanapi.Customers", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Address")
.HasColumnType("text")
.HasColumnName("address");
b.Property<string>("BillingTerms")
.HasColumnType("text")
.HasColumnName("billing_terms");
b.Property<string>("Company")
.HasColumnType("text")
.HasColumnName("company");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("text")
.HasColumnName("email");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Notes")
.HasColumnType("text")
.HasColumnName("notes");
b.Property<string>("PhoneNumber")
.HasColumnType("text")
.HasColumnName("phone_number");
b.HasKey("Id")
.HasName("pk_customers");
b.ToTable("customers", (string)null);
});
modelBuilder.Entity("barkmanapi.InventoryItems", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Barcode")
.IsRequired()
.HasColumnType("text")
.HasColumnName("barcode");
b.Property<string>("Brand")
.IsRequired()
.HasColumnType("text")
.HasColumnName("brand");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Notes")
.HasColumnType("text")
.HasColumnName("notes");
b.Property<float?>("RentalPrice")
.HasColumnType("real")
.HasColumnName("rental_price");
b.Property<float?>("ReplacementCost")
.HasColumnType("real")
.HasColumnName("replacement_cost");
b.Property<string>("SerialNumber")
.HasColumnType("text")
.HasColumnName("serial_number");
b.Property<string>("StatusId")
.HasColumnType("text")
.HasColumnName("status_id");
b.Property<float?>("Weight")
.HasColumnType("real")
.HasColumnName("weight");
b.HasKey("Id")
.HasName("pk_inventory");
b.HasIndex("Barcode")
.IsUnique()
.HasDatabaseName("ix_inventory_barcode");
b.HasIndex("StatusId")
.HasDatabaseName("ix_inventory_status_id");
b.ToTable("inventory", (string)null);
});
modelBuilder.Entity("barkmanapi.ItemStatus", b =>
{
b.Property<string>("Id")
.HasColumnType("text")
.HasColumnName("id");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.HasKey("Id")
.HasName("pk_item_status");
b.ToTable("item_status", (string)null);
});
modelBuilder.Entity("barkmanapi.JobItems", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ItemId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("item_id");
b.Property<string>("JobId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("job_id");
b.Property<int?>("JobsId")
.HasColumnType("integer")
.HasColumnName("jobs_id");
b.Property<int>("Quantity")
.HasColumnType("integer")
.HasColumnName("quantity");
b.HasKey("Id")
.HasName("pk_job_items");
b.HasIndex("JobsId")
.HasDatabaseName("ix_job_items_jobs_id");
b.ToTable("job_items", (string)null);
});
modelBuilder.Entity("barkmanapi.JobStatus", b =>
{
b.Property<string>("Id")
.HasColumnType("text")
.HasColumnName("id");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.HasKey("Id")
.HasName("pk_job_status");
b.ToTable("job_status", (string)null);
});
modelBuilder.Entity("barkmanapi.Jobs", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Address")
.HasColumnType("text")
.HasColumnName("address");
b.Property<int>("CustomerId")
.HasColumnType("integer")
.HasColumnName("customer_id");
b.Property<DateTime>("EndDate")
.HasColumnType("timestamp with time zone")
.HasColumnName("end_date");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Notes")
.HasColumnType("text")
.HasColumnName("notes");
b.Property<DateTime>("StartDate")
.HasColumnType("timestamp with time zone")
.HasColumnName("start_date");
b.Property<string>("StatusId")
.HasColumnType("text")
.HasColumnName("status_id");
b.Property<decimal>("TotalPrice")
.HasColumnType("numeric")
.HasColumnName("total_price");
b.Property<string>("Venue")
.HasColumnType("text")
.HasColumnName("venue");
b.HasKey("Id")
.HasName("pk_jobs");
b.HasIndex("StatusId")
.HasDatabaseName("ix_jobs_status_id");
b.ToTable("jobs", (string)null);
});
modelBuilder.Entity("barkmanapi.InventoryItems", b =>
{
b.HasOne("barkmanapi.ItemStatus", "Status")
.WithMany()
.HasForeignKey("StatusId")
.HasConstraintName("fk_inventory_item_status_status_id");
b.Navigation("Status");
});
modelBuilder.Entity("barkmanapi.JobItems", b =>
{
b.HasOne("barkmanapi.Jobs", null)
.WithMany("Items")
.HasForeignKey("JobsId")
.HasConstraintName("fk_job_items_jobs_jobs_id");
});
modelBuilder.Entity("barkmanapi.Jobs", b =>
{
b.HasOne("barkmanapi.JobStatus", "Status")
.WithMany()
.HasForeignKey("StatusId")
.HasConstraintName("fk_jobs_job_status_status_id");
b.Navigation("Status");
});
modelBuilder.Entity("barkmanapi.Jobs", b =>
{
b.Navigation("Items");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace barkmanapi.Migrations
{
/// <inheritdoc />
public partial class change_datetimeoffset_to_datetime : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
@@ -159,15 +159,15 @@ namespace barkmanapi.Migrations
.HasColumnType("text")
.HasColumnName("item_id");
b.Property<string>("JobId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("job_id");
b.Property<int?>("JobsId")
.HasColumnType("integer")
.HasColumnName("jobs_id");
b.Property<string>("OrderId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("order_id");
b.Property<int>("Quantity")
.HasColumnType("integer")
.HasColumnName("quantity");
@@ -215,7 +215,7 @@ namespace barkmanapi.Migrations
.HasColumnType("integer")
.HasColumnName("customer_id");
b.Property<DateTimeOffset>("EndDate")
b.Property<DateTime>("EndDate")
.HasColumnType("timestamp with time zone")
.HasColumnName("end_date");
@@ -228,7 +228,7 @@ namespace barkmanapi.Migrations
.HasColumnType("text")
.HasColumnName("notes");
b.Property<DateTimeOffset>("StartDate")
b.Property<DateTime>("StartDate")
.HasColumnType("timestamp with time zone")
.HasColumnName("start_date");
+162 -1
View File
@@ -51,6 +51,18 @@ var customerGroup = app.MapGroup(prefix: "/customers")
.WithTags("Customers")
.WithDescription("Endpoints for managing customers");
var jobGroup = app.MapGroup(prefix: "/jobs")
.WithTags("Jobs")
.WithDescription("Endpoints for managing jobs");
var jobStatusGroup = app.MapGroup(prefix: "/jobstatus")
.WithTags("Job Status")
.WithDescription("Endpoints for managing job status");
var jobItemGroup = app.MapGroup(prefix: "/jobitems")
.WithTags("Job Items")
.WithDescription("Endpoints for managing job items");
inventoryGroup.MapGet("", async (BarkContext db) =>
await db.Inventory.OrderBy(item => item.Barcode).ToListAsync());
@@ -144,7 +156,7 @@ itemStatusGroup.MapPut("/{id}", async (string id, ItemStatus updatedStatus, Bark
var existingStatus = await db.ItemStatus.FindAsync(id);
if (existingStatus == null)
{
return Results.NotFound(new { Message = "Inventory item not found" });
return Results.NotFound(new { Message = "Item status not found" });
}
existingStatus.Id = updatedStatus.Id;
@@ -232,6 +244,155 @@ customerGroup.MapDelete("/{id}", async (int id, BarkContext db) =>
return Results.Ok(new { Message = "Customer deleted successfully" });
});
jobGroup.MapGet("", async (BarkContext db) =>
await db.Jobs.OrderBy(jobs => jobs.Id).ToListAsync());
jobGroup.MapGet("/{id}", async (int id, BarkContext db) =>
{
var job = await db.Jobs
.FirstOrDefaultAsync(i => i.Id == id);
if (job == null)
{
return Results.NotFound(new { Message = "Job not found" });
}
return Results.Ok(job);
});
jobGroup.MapPut("/{id}", async (int id, Jobs updatedJob, BarkContext db) =>
{
var existingJob = await db.Jobs.FindAsync(id);
if (existingJob == null)
{
return Results.NotFound(new { Message = "Job not found" });
}
existingJob.Name = updatedJob.Name;
existingJob.StartDate = updatedJob.StartDate;
existingJob.EndDate = updatedJob.EndDate;
existingJob.TotalPrice = updatedJob.TotalPrice;
existingJob.Venue = updatedJob.Venue;
existingJob.Address = updatedJob.Address;
existingJob.Notes = updatedJob.Notes;
await db.SaveChangesAsync();
return Results.Ok(existingJob);
});
jobGroup.MapPost("", async (Jobs newJobInput, BarkContext db) =>
{
var newJob = new Jobs()
{
CustomerId = newJobInput.CustomerId,
Name = newJobInput.Name,
StartDate = newJobInput.StartDate,
EndDate = newJobInput.EndDate,
Venue = newJobInput.Venue,
Address = newJobInput.Address,
Notes = newJobInput.Notes,
};
db.Jobs.Add(newJob);
await db.SaveChangesAsync();
return Results.Created($"/jobs/{newJob.Id}", newJob);
});
jobStatusGroup.MapGet("/", async (BarkContext db) =>
await db.JobStatus.ToListAsync());
jobStatusGroup.MapPost("/", async (JobStatus newJobStatus, BarkContext db) =>
{
db.JobStatus.Add(newJobStatus);
await db.SaveChangesAsync();
return Results.Created($"/jobstatus/{newJobStatus.Id}", newJobStatus);
});
jobStatusGroup.MapGet("/{id}", async (string id, BarkContext db) =>
{
var jobStatus = await db.JobStatus.FindAsync(id);
if (jobStatus == null)
{
return Results.NotFound(new { Message = "Job Status not found" });
}
return Results.Ok(jobStatus);
});
jobStatusGroup.MapPut("/{id}", async (string id, JobStatus updatedStatus, BarkContext db) =>
{
var existingStatus = await db.JobStatus.FindAsync(id);
if (existingStatus == null)
{
return Results.NotFound(new { Message = "Job status not found" });
}
existingStatus.Id = updatedStatus.Id;
existingStatus.Name = updatedStatus.Name;
await db.SaveChangesAsync();
return Results.Ok(existingStatus);
});
jobStatusGroup.MapDelete("/{id}", async (string id, BarkContext db) =>
{
var jobStatus = await db.JobStatus.FindAsync(id);
if (jobStatus == null)
{
return Results.NotFound(new { Message = "Job status not found" });
}
db.JobStatus.Remove(jobStatus);
await db.SaveChangesAsync();
return Results.Ok(new { Message = "Job status deleted successfully" });
});
jobItemGroup.MapGet("", async (BarkContext db) =>
await db.JobItems.OrderBy(items => items.Id).ToListAsync());
jobItemGroup.MapGet("/{id}", async (int id, BarkContext db) =>
{
var jobItems = await db.JobItems
.FirstOrDefaultAsync(i => i.Id == id);
if (jobItems == null)
{
return Results.NotFound(new { Message = "Job Items not found" });
}
return Results.Ok(jobItems);
});
jobItemGroup.MapPut("/{id}", async (int id, JobItems updatedJobItems, BarkContext db) =>
{
var existingJobItems = await db.JobItems.FindAsync(id);
if (existingJobItems == null)
{
return Results.NotFound(new { Message = "Job Items not found" });
}
existingJobItems.JobId = updatedJobItems.JobId;
existingJobItems.ItemId = updatedJobItems.ItemId;
existingJobItems.Quantity = updatedJobItems.Quantity;
await db.SaveChangesAsync();
return Results.Ok(existingJobItems);
});
jobItemGroup.MapPost("", async (JobItems newJobItemsInput, BarkContext db) =>
{
var newJobItem = new JobItems()
{
JobId = newJobItemsInput.JobId,
ItemId = newJobItemsInput.ItemId,
Quantity = newJobItemsInput.Quantity,
};
db.JobItems.Add(newJobItem);
await db.SaveChangesAsync();
return Results.Created($"/jobItems/{newJobItem.Id}", newJobItem);
});
using (var serviceScope = app.Services.CreateScope())
{
var dbContext = serviceScope.ServiceProvider.GetRequiredService<BarkContext>();
+4 -3
View File
@@ -1,3 +1,4 @@
using System.Runtime.InteropServices.JavaScript;
using Microsoft.EntityFrameworkCore;
namespace barkmanapi;
@@ -53,8 +54,8 @@ public class Jobs
public required string Name { get; set; }
public JobStatus Status { get; set; }
public string? StatusId { get; set; }
public DateTimeOffset StartDate { get; set; }
public DateTimeOffset EndDate { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public decimal TotalPrice { get; set; }
public string? Venue { get; set; }
public string? Address { get; set; }
@@ -65,7 +66,7 @@ public class Jobs
public class JobItems
{
public int Id { get; set; }
public required string OrderId { get; set; }
public required string JobId { get; set; }
public required string ItemId { get; set; }
public int Quantity { get; set; }
}
+1
View File
@@ -0,0 +1 @@
22.22.1
+1334 -1802
View File
File diff suppressed because it is too large Load Diff
+27 -26
View File
@@ -10,34 +10,35 @@
"preview": "vite preview"
},
"dependencies": {
"@mantine/core": "^7.16.0",
"@mantine/form": "^7.16.0",
"@mantine/hooks": "^7.16.0",
"@mantine/notifications": "^7.16.2",
"@tabler/icons-react": "^3.29.0",
"@tanstack/react-query": "^5.64.1",
"@tanstack/react-query-devtools": "^5.64.1",
"axios": "^1.7.9",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router": "^7.1.1",
"react-router-dom": "^7.1.1"
"@mantine/core": "^9.4.2",
"@mantine/dates": "^9.4.2",
"@mantine/form": "^9.4.2",
"@mantine/hooks": "^9.4.2",
"@mantine/notifications": "^9.4.2",
"@tabler/icons-react": "^3.45.0",
"@tanstack/react-query": "^5.101.4",
"@tanstack/react-query-devtools": "^5.101.4",
"axios": "^1.18.1",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-router": "^8.3.0"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@tanstack/eslint-plugin-query": "^5.62.16",
"@types/react": "^18.3.18",
"@types/react-dom": "^18.3.5",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.17.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.16",
"globals": "^15.14.0",
"postcss": "^8.5.1",
"postcss-preset-mantine": "^1.17.0",
"@eslint/js": "^9.39.5",
"@tanstack/eslint-plugin-query": "^5.101.4",
"@types/node": "^26.1.1",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.4",
"eslint": "^9.39.5",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.3",
"globals": "^17.7.0",
"postcss": "^8.5.22",
"postcss-preset-mantine": "^1.18.0",
"postcss-simple-vars": "^7.0.1",
"typescript": "~5.6.2",
"typescript-eslint": "^8.18.2",
"vite": "^6.0.5"
"typescript": "^6.0.3",
"typescript-eslint": "^8.65.0",
"vite": "^8.1.5"
}
}
+6
View File
@@ -14,6 +14,9 @@ import CustomerList from "@/features/customers/CustomerList.tsx";
import AddCustomer from "@/features/customers/AddCustomer.tsx";
import CustomerDetail from "@/features/customers/CustomerDetail.tsx";
import EditCustomer from "@/features/customers/EditCustomer.tsx";
import AddJob from "@/features/jobs/AddJob.tsx";
import JobList from "@/features/jobs/JobList.tsx";
import JobDetail from "@/features/jobs/JobDetail.tsx";
// Create a client
const queryClient = new QueryClient()
@@ -54,6 +57,9 @@ function App() {
<Route path="customers/addCustomer" element={<AddCustomer/>}/>
<Route path="customers/customerDetail/:customerId" element={<CustomerDetail/>}/>
<Route path="customers/editCustomer/:customerId" element={<EditCustomer/>}/>
<Route path="jobs" element={<JobList/>}/>
<Route path="jobs/addJob" element={<AddJob/>}/>
<Route path="jobs/jobDetail/:jobId" element={<JobDetail/>}/>
</Routes>
</>
@@ -8,6 +8,7 @@ import BarkLogo from '@/assets/barklogo.png';
const links = [
{ link: '/', label: 'Home' },
{ link: '/customers', label: 'Customers' },
{ link: '/jobs', label: 'Jobs' },
{ link: '/inventory', label: 'Inventory' },
];
+36 -25
View File
@@ -1,4 +1,5 @@
import {Button, Group, TextInput, Container, Title, Flex} from '@mantine/core';
import {Button, Group, TextInput, NumberInput, Container, Title, Flex} from '@mantine/core';
import { DateTimePicker } from '@mantine/dates';
import {useForm} from '@mantine/form';
import {useMutation} from "@tanstack/react-query";
import {NewJob} from "./types.ts";
@@ -12,27 +13,33 @@ function AddJob() {
const navigate = useNavigate();
const customerQuery = useCustomerList();
const customerQuery = useJobList();
const newJobForm = useForm<NewJob>({
mode: 'uncontrolled',
initialValues: {
customerId: 0,
name: "",
company: "",
email: "",
phone: "",
startDate: "",
endDate: "",
address: "",
venue: "",
notes: "",
},
validate: {},
});
const updateCustomer = useMutation({
mutationFn: async (values: newJob) => {
const updateJob = useMutation({
mutationFn: async (values: NewJob) => {
const result = await fetch(import.meta.env.VITE_API_URL + '/customers', {
const result = await fetch(import.meta.env.VITE_API_URL + '/jobs', {
method: 'POST',
body: JSON.stringify(values),
body: JSON.stringify({
...values,
startDate: new Date(values.startDate).toISOString(),
endDate: new Date(values.endDate).toISOString(),
}),
headers: {
'Content-Type': 'application/json'
}
@@ -43,10 +50,10 @@ function AddJob() {
icon: <IconCheck size={20}/>,
color: "teal",
title: "All good!",
message: "Customer Created",
message: "Job Created",
position: 'top-center',
});
navigate("/customers");
navigate("/jobs");
}
@@ -58,7 +65,7 @@ function AddJob() {
message: "Something went wrong",
position: 'top-center',
});
throw new Error('Failed to create customer');
throw new Error('Failed to create job');
}
},
@@ -74,8 +81,8 @@ function AddJob() {
return (
<>
<form onSubmit={newCustomerForm.onSubmit(async (values) => await
updateCustomer.mutateAsync(values))}>
<form onSubmit={newJobForm.onSubmit(async (values) => await
updateJob.mutateAsync(values))}>
<Container m="lg">
<Flex mih={50}
gap="md"
@@ -85,16 +92,20 @@ function AddJob() {
wrap="wrap">
<Title order={1}>Add Item</Title>
<TextInput withAsterisk key={newCustomerForm.key('name')} size="md" label="Name"
placeholder="Name" {...newCustomerForm.getInputProps('name')}/>
<TextInput size="md" key={newCustomerForm.key('company')} label="Company"
placeholder="Company"{...newCustomerForm.getInputProps('company')}/>
<TextInput withAsterisk size="md" key={newCustomerForm.key('email')} label="Email"
placeholder="Email" {...newCustomerForm.getInputProps('email')}/>
<TextInput size="md" key={newCustomerForm.key('phone')} label="Phone"
placeholder="Phone" {...newCustomerForm.getInputProps('phone')}/>
<TextInput size="md" key={newCustomerForm.key('address')} label="Address"
placeholder="Address" {...newCustomerForm.getInputProps('address')}/>
<NumberInput withAsterisk key={newJobForm.key('customerId')} size="md" label="Customer ID"
placeholder="Customer ID" {...newJobForm.getInputProps('customerId')}/>
<TextInput withAsterisk key={newJobForm.key('name')} size="md" label="Name"
placeholder="Name" {...newJobForm.getInputProps('name')}/>
<DateTimePicker withAsterisk key={newJobForm.key('startDate')} size="md" label="Start Date"
placeholder="Start Date" {...newJobForm.getInputProps('startDate')}/>
<DateTimePicker withAsterisk key={newJobForm.key('endDate')} size="md" label="End Date"
placeholder="End Date" {...newJobForm.getInputProps('endDate')}/>
<TextInput size="md" key={newJobForm.key('address')} label="Address"
placeholder="Address" {...newJobForm.getInputProps('address')}/>
<TextInput size="md" key={newJobForm.key('venue')} label="Venue"
placeholder="Venue"{...newJobForm.getInputProps('venue')}/>
<TextInput size="md" key={newJobForm.key('notes')} label="Notes"
placeholder="Notes" {...newJobForm.getInputProps('notes')}/>
<Group justify="flex-end" mt="md">
<Button type="submit">Submit</Button>
@@ -106,4 +117,4 @@ function AddJob() {
);
}
export default AddCustomer;
export default AddJob;
+47
View File
@@ -0,0 +1,47 @@
import {Container, Text, Title} from "@mantine/core";
import {Link, useParams} from "react-router";
import {Job} from "./types.ts";
import {useQuery} from "@tanstack/react-query";
function JobDetail() {
const params = useParams();
const {isPending, error, data, isFetching} = useQuery({
queryKey: ['jobs', params.jobId],
queryFn: async (): Promise<Job> => {
const response = await fetch(
import.meta.env.VITE_API_URL + '/jobs/' + params.jobId,
)
if (!response.ok) throw new Error('Failed to fetch jobs ' + response.statusText)
return await response.json()
},
});
if (isPending) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<>
<Container m="lg">
<Title order={1}>Job Detail</Title>
<div>{isFetching ? 'Updating...' : ''}</div>
<Text>Customer ID: {data.customerId}</Text>
<Text>Name: {data.name}</Text>
{/*<Text>Status: {data.status.name}</Text>*/}
<Text>Start Date: {data.startDate}</Text>
<Text>End Date: {data.endDate}</Text>
<Text>Venue: {data.venue}</Text>
<Text>Address: {data.address}</Text>
<Text>Notes: {data.notes}</Text>
<Link to={`/inventory/editItem/${data.id}`}>Edit</Link>
</Container>
</>
)
}
export default JobDetail
+60
View File
@@ -0,0 +1,60 @@
import {Flex, Table} from '@mantine/core';
import BarkButton from "../../common/components/BarkButton.tsx";
import {Link, NavLink} from "react-router";
import useJobList from "./hooks/useJobList.tsx";
function JobList() {
const jobQuery = useJobList();
if (jobQuery.isPending) return 'Loading...'
if (jobQuery.error) return 'An error has occurred: ' + jobQuery.error.message
return (
<>
<Flex
mih={50}
gap="xl"
justify="center"
align="center"
direction="row"
wrap="wrap"
>
<p>
ARFF ARFF BARK BARK
</p>
<NavLink to={'/jobs/addJob'}><BarkButton>Add Job</BarkButton></NavLink>
</Flex>
<Table striped>
<Table.Thead>
<Table.Tr>
<Table.Th c="red">Customer ID</Table.Th>
<Table.Th c="red">Name</Table.Th>
<Table.Th c="red">Venue</Table.Th>
<Table.Th c="red">Status</Table.Th>
<Table.Th c="red">Start Date</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{jobQuery.data?.map((data) => (
<Table.Tr key={data.id}>
<Table.Td>{data.customerId}</Table.Td>
<Table.Td>{data.name}</Table.Td>
<Table.Td>{data.venue}</Table.Td>
<Table.Td>{data.status?.name ?? '—'}</Table.Td>
<Table.Td>{new Date(data.startDate).toLocaleDateString()}</Table.Td>
<Table.Td>
<Link to={`/jobs/jobDetail/${data.id}`}>Details</Link>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</>
)
}
export default JobList;
+10 -10
View File
@@ -2,22 +2,22 @@ export interface Job {
id: number,
customerId: number,
name: string,
status: {id: string; name: string},
statusId: string;
startDate: Date;
endDate: Date;
status: {id: string; name: string} | null,
statusId: string | null;
startDate: string;
endDate: string;
totalPrice: string,
venue: number,
address: number,
venue: string,
address: string,
notes: string,
}
export interface NewJob {
customerId: number,
name: string,
startDate: Date;
endDate: Date;
venue: number,
address: number,
startDate: string;
endDate: string;
venue: string,
address: string,
notes: string,
}
-1
View File
@@ -22,7 +22,6 @@
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}