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 ddd90aa5a5 changed order to job - updated model, 2026-07-22 19:49:24 -05:00
drew c2c48b4cb1 list hook 2026-07-22 19:44:11 -05:00
drew 137f9ee669 Types! 2026-07-22 19:38:58 -05:00
drew fc44790c96 fixed customer id type 2026-07-22 19:34:41 -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
22 changed files with 2926 additions and 1846 deletions
@@ -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("20260723200239_maybethisworks")]
partial class maybethisworks
{
/// <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<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");
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,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") .HasColumnType("text")
.HasColumnName("item_id"); .HasColumnName("item_id");
b.Property<string>("JobId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("job_id");
b.Property<int?>("JobsId") b.Property<int?>("JobsId")
.HasColumnType("integer") .HasColumnType("integer")
.HasColumnName("jobs_id"); .HasColumnName("jobs_id");
b.Property<string>("OrderId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("order_id");
b.Property<int>("Quantity") b.Property<int>("Quantity")
.HasColumnType("integer") .HasColumnType("integer")
.HasColumnName("quantity"); .HasColumnName("quantity");
@@ -211,12 +211,11 @@ namespace barkmanapi.Migrations
.HasColumnType("text") .HasColumnType("text")
.HasColumnName("address"); .HasColumnName("address");
b.Property<string>("CustomerId") b.Property<int>("CustomerId")
.IsRequired() .HasColumnType("integer")
.HasColumnType("text")
.HasColumnName("customer_id"); .HasColumnName("customer_id");
b.Property<DateTimeOffset>("EndDate") b.Property<DateTime>("EndDate")
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("end_date"); .HasColumnName("end_date");
@@ -229,7 +228,7 @@ namespace barkmanapi.Migrations
.HasColumnType("text") .HasColumnType("text")
.HasColumnName("notes"); .HasColumnName("notes");
b.Property<DateTimeOffset>("StartDate") b.Property<DateTime>("StartDate")
.HasColumnType("timestamp with time zone") .HasColumnType("timestamp with time zone")
.HasColumnName("start_date"); .HasColumnName("start_date");
+3 -2
View File
@@ -290,6 +290,7 @@ jobGroup.MapPost("", async (Jobs newJobInput, BarkContext db) =>
EndDate = newJobInput.EndDate, EndDate = newJobInput.EndDate,
Venue = newJobInput.Venue, Venue = newJobInput.Venue,
Address = newJobInput.Address, Address = newJobInput.Address,
Notes = newJobInput.Notes,
}; };
db.Jobs.Add(newJob); db.Jobs.Add(newJob);
@@ -370,7 +371,7 @@ jobItemGroup.MapPut("/{id}", async (int id, JobItems updatedJobItems, BarkContex
return Results.NotFound(new { Message = "Job Items not found" }); return Results.NotFound(new { Message = "Job Items not found" });
} }
existingJobItems.OrderId = updatedJobItems.OrderId; existingJobItems.JobId = updatedJobItems.JobId;
existingJobItems.ItemId = updatedJobItems.ItemId; existingJobItems.ItemId = updatedJobItems.ItemId;
existingJobItems.Quantity = updatedJobItems.Quantity; existingJobItems.Quantity = updatedJobItems.Quantity;
@@ -382,7 +383,7 @@ jobItemGroup.MapPost("", async (JobItems newJobItemsInput, BarkContext db) =>
{ {
var newJobItem = new JobItems() var newJobItem = new JobItems()
{ {
OrderId = newJobItemsInput.OrderId, JobId = newJobItemsInput.JobId,
ItemId = newJobItemsInput.ItemId, ItemId = newJobItemsInput.ItemId,
Quantity = newJobItemsInput.Quantity, Quantity = newJobItemsInput.Quantity,
}; };
+5 -4
View File
@@ -1,3 +1,4 @@
using System.Runtime.InteropServices.JavaScript;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace barkmanapi; namespace barkmanapi;
@@ -49,12 +50,12 @@ public class Customers
public class Jobs public class Jobs
{ {
public int Id { get; set; } public int Id { get; set; }
public required string CustomerId { get; set; } public required int CustomerId { get; set; }
public required string Name { get; set; } public required string Name { get; set; }
public JobStatus Status { get; set; } public JobStatus Status { get; set; }
public string? StatusId { get; set; } public string? StatusId { get; set; }
public DateTimeOffset StartDate { get; set; } public DateTime StartDate { get; set; }
public DateTimeOffset EndDate { get; set; } public DateTime EndDate { get; set; }
public decimal TotalPrice { get; set; } public decimal TotalPrice { get; set; }
public string? Venue { get; set; } public string? Venue { get; set; }
public string? Address { get; set; } public string? Address { get; set; }
@@ -65,7 +66,7 @@ public class Jobs
public class JobItems public class JobItems
{ {
public int Id { get; set; } public int Id { get; set; }
public required string OrderId { get; set; } public required string JobId { get; set; }
public required string ItemId { get; set; } public required string ItemId { get; set; }
public int Quantity { get; set; } public int Quantity { get; set; }
} }
+1
View File
@@ -0,0 +1 @@
22.22.1
+1333 -1801
View File
File diff suppressed because it is too large Load Diff
+27 -26
View File
@@ -10,34 +10,35 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@mantine/core": "^7.16.0", "@mantine/core": "^9.4.2",
"@mantine/form": "^7.16.0", "@mantine/dates": "^9.4.2",
"@mantine/hooks": "^7.16.0", "@mantine/form": "^9.4.2",
"@mantine/notifications": "^7.16.2", "@mantine/hooks": "^9.4.2",
"@tabler/icons-react": "^3.29.0", "@mantine/notifications": "^9.4.2",
"@tanstack/react-query": "^5.64.1", "@tabler/icons-react": "^3.45.0",
"@tanstack/react-query-devtools": "^5.64.1", "@tanstack/react-query": "^5.101.4",
"axios": "^1.7.9", "@tanstack/react-query-devtools": "^5.101.4",
"react": "^18.3.1", "axios": "^1.18.1",
"react-dom": "^18.3.1", "react": "^19.2.8",
"react-router": "^7.1.1", "react-dom": "^19.2.8",
"react-router-dom": "^7.1.1" "react-router": "^8.3.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.17.0", "@eslint/js": "^9.39.5",
"@tanstack/eslint-plugin-query": "^5.62.16", "@tanstack/eslint-plugin-query": "^5.101.4",
"@types/react": "^18.3.18", "@types/node": "^26.1.1",
"@types/react-dom": "^18.3.5", "@types/react": "^19.2.17",
"@vitejs/plugin-react": "^4.3.4", "@types/react-dom": "^19.2.3",
"eslint": "^9.17.0", "@vitejs/plugin-react": "^6.0.4",
"eslint-plugin-react-hooks": "^5.0.0", "eslint": "^9.39.5",
"eslint-plugin-react-refresh": "^0.4.16", "eslint-plugin-react-hooks": "^7.1.1",
"globals": "^15.14.0", "eslint-plugin-react-refresh": "^0.5.3",
"postcss": "^8.5.1", "globals": "^17.7.0",
"postcss-preset-mantine": "^1.17.0", "postcss": "^8.5.22",
"postcss-preset-mantine": "^1.18.0",
"postcss-simple-vars": "^7.0.1", "postcss-simple-vars": "^7.0.1",
"typescript": "~5.6.2", "typescript": "^6.0.3",
"typescript-eslint": "^8.18.2", "typescript-eslint": "^8.65.0",
"vite": "^6.0.5" "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 AddCustomer from "@/features/customers/AddCustomer.tsx";
import CustomerDetail from "@/features/customers/CustomerDetail.tsx"; import CustomerDetail from "@/features/customers/CustomerDetail.tsx";
import EditCustomer from "@/features/customers/EditCustomer.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 // Create a client
const queryClient = new QueryClient() const queryClient = new QueryClient()
@@ -54,6 +57,9 @@ function App() {
<Route path="customers/addCustomer" element={<AddCustomer/>}/> <Route path="customers/addCustomer" element={<AddCustomer/>}/>
<Route path="customers/customerDetail/:customerId" element={<CustomerDetail/>}/> <Route path="customers/customerDetail/:customerId" element={<CustomerDetail/>}/>
<Route path="customers/editCustomer/:customerId" element={<EditCustomer/>}/> <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> </Routes>
</> </>
@@ -8,6 +8,7 @@ import BarkLogo from '@/assets/barklogo.png';
const links = [ const links = [
{ link: '/', label: 'Home' }, { link: '/', label: 'Home' },
{ link: '/customers', label: 'Customers' }, { link: '/customers', label: 'Customers' },
{ link: '/jobs', label: 'Jobs' },
{ link: '/inventory', label: 'Inventory' }, { link: '/inventory', label: 'Inventory' },
]; ];
+120
View File
@@ -0,0 +1,120 @@
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";
import {useNavigate} from "react-router";
import {IconX, IconCheck} from '@tabler/icons-react';
import {notifications} from '@mantine/notifications';
import useJobList from "./hooks/useJobList.tsx";
function AddJob() {
const navigate = useNavigate();
const customerQuery = useJobList();
const newJobForm = useForm<NewJob>({
mode: 'uncontrolled',
initialValues: {
customerId: 0,
name: "",
startDate: "",
endDate: "",
address: "",
venue: "",
notes: "",
},
validate: {},
});
const updateJob = useMutation({
mutationFn: async (values: NewJob) => {
const result = await fetch(import.meta.env.VITE_API_URL + '/jobs', {
method: 'POST',
body: JSON.stringify({
...values,
startDate: new Date(values.startDate).toISOString(),
endDate: new Date(values.endDate).toISOString(),
}),
headers: {
'Content-Type': 'application/json'
}
});
if (result.ok) {
notifications.show({
icon: <IconCheck size={20}/>,
color: "teal",
title: "All good!",
message: "Job Created",
position: 'top-center',
});
navigate("/jobs");
}
if (!result.ok) {
notifications.show({
icon: <IconX size={20}/>,
color: "red",
title: "Bummer!",
message: "Something went wrong",
position: 'top-center',
});
throw new Error('Failed to create job');
}
},
})
if (customerQuery.isPending) return 'Loading...'
if (customerQuery.error) return 'An error has occurred: ' + customerQuery.error.message
return (
<>
<form onSubmit={newJobForm.onSubmit(async (values) => await
updateJob.mutateAsync(values))}>
<Container m="lg">
<Flex mih={50}
gap="md"
justify="flex-start"
align="flex-start"
direction="column"
wrap="wrap">
<Title order={1}>Add Item</Title>
<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>
</Group>
</Flex>
</Container>
</form>
</>
);
}
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;
@@ -0,0 +1,17 @@
import {useQuery} from "@tanstack/react-query";
import {Job} from "../types.ts";
const useJobList = () => useQuery({
queryKey: ['job'],
queryFn: async (): Promise<Job[]> => {
const response = await fetch(
import.meta.env.VITE_API_URL + '/jobs',
)
if (!response.ok) throw new Error('Failed to fetch jobs ' + response.statusText)
return await response.json()
},
});
export default useJobList;
+23
View File
@@ -0,0 +1,23 @@
export interface Job {
id: number,
customerId: number,
name: string,
status: {id: string; name: string} | null,
statusId: string | null;
startDate: string;
endDate: string;
totalPrice: string,
venue: string,
address: string,
notes: string,
}
export interface NewJob {
customerId: number,
name: string,
startDate: string;
endDate: string;
venue: string,
address: string,
notes: string,
}
-1
View File
@@ -22,7 +22,6 @@
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true, "noUncheckedSideEffectImports": true,
"baseUrl": ".",
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"]
} }