From 0c8ba6467d41caf49411c3ed820c2e801cb0dd21 Mon Sep 17 00:00:00 2001 From: arteons Date: Wed, 25 Jun 2025 08:57:05 +0800 Subject: [PATCH] update bug issue and order timesheet --- src/components/Sidebar.svelte | 42 - src/routes/backoffice/issue/+page.svelte | 264 ++++- .../purchaseorder/acknowledged/+page.svelte | 972 ---------------- .../purchaseorder/approval/+page.svelte | 1006 ----------------- .../purchaseorder/complete/+page.svelte | 916 --------------- .../purchaseorder/received/+page.svelte | 989 ---------------- src/routes/timesheet/+page.svelte | 6 +- 7 files changed, 240 insertions(+), 3955 deletions(-) delete mode 100644 src/routes/backoffice/purchaseorder/acknowledged/+page.svelte delete mode 100644 src/routes/backoffice/purchaseorder/approval/+page.svelte delete mode 100644 src/routes/backoffice/purchaseorder/complete/+page.svelte delete mode 100644 src/routes/backoffice/purchaseorder/received/+page.svelte diff --git a/src/components/Sidebar.svelte b/src/components/Sidebar.svelte index 7882532..fe9b27b 100644 --- a/src/components/Sidebar.svelte +++ b/src/components/Sidebar.svelte @@ -85,48 +85,6 @@ icon: "📦", url: "/backoffice/purchaseorder", roles: ["it", "guest", "accounting", "ga", "office", "hm", "vm"], - sub: [ - { - name: "Approval", - icon: "✅", - url: "/backoffice/purchaseorder/approval", - roles: ["it", "user", "ga", "office", "hm", "vm"], - }, - { - name: "Acknowledged", - icon: "📋", - url: "/backoffice/purchaseorder/acknowledged", - roles: ["it", "user", "ga", "office", "hm", "vm"], - }, - { - name: "Complete", - icon: "✔️", - url: "/backoffice/purchaseorder/complete", - roles: [ - "it", - "user", - "accounting", - "ga", - "office", - "hm", - "vm", - ], - }, - { - name: "Received", - icon: "📥", - url: "/backoffice/purchaseorder/received", - roles: [ - "it", - "user", - "accounting", - "ga", - "office", - "hm", - "vm", - ], - }, - ], }, { name: "Timesheets", diff --git a/src/routes/backoffice/issue/+page.svelte b/src/routes/backoffice/issue/+page.svelte index afe7c8f..ed1c0c4 100644 --- a/src/routes/backoffice/issue/+page.svelte +++ b/src/routes/backoffice/issue/+page.svelte @@ -84,6 +84,17 @@ { label: "Other - Transport", value: "Other - Transport" }, ]; + type PurchaseOrder = { + villa_id: string; + issue_id: string; + po_status: string; + requested_by: string; + requested_date: string; + po_due: string; + po_item: string; + po_quantity: string; + po_type: string; + }; const areaOfVilla = [ { label: "All Bathrooms", value: "All Bathrooms" }, { label: "All Guest Houses", value: "All Guest Houses" }, @@ -152,6 +163,18 @@ let currentSearchTerm: string | null = null; let showProjectModal = false; let selectedIssueId: string | null = null; + let showPurchaseOrderModal = false; + let newPO: PurchaseOrder = { + villa_id: "", + issue_id: "", + po_status: "requested", + requested_by: "", + requested_date: "", + po_due: "", + po_item: "", + po_quantity: "", + po_type: "" + }; let newProject = { project_name: "", issue_id: "", @@ -165,6 +188,7 @@ let dataUser: User[] = []; let projectIssueMap: Set = new Set(); + let purchaseOrderMap: Set = new Set(); async function fetchExistingProjectLinks() { const { data, error } = await supabase @@ -177,6 +201,10 @@ console.error("Error loading existing projects:", error); } } + async function fetchExistingPurchaseOrders() { + const { data } = await supabase.from("vb_purchase_orders").select("issue_id"); + if (data) purchaseOrderMap = new Set(data.map(p => p.issue_id)); + } onMount(async () => { await fetchExistingProjectLinks(); @@ -241,6 +269,16 @@ need_approval: boolean; }; + type POItem = { + item_name: string; + }; + + let poItems: POItem[] = []; + + async function fetchPoItems() { + const { data } = await supabase.from("vb_po_item").select("item_name"); + if (data) poItems = data; + } let allRows: Issue[] = []; let offset = 0; let limit = 10; @@ -330,7 +368,9 @@ } onMount(() => { - fetchIssues(null, null, "created_at", "desc", offset, limit); + fetchIssues(null, null, "created_at", "desc", offset, limit); + fetchPoItems(); + fetchExistingPurchaseOrders(); }); let showModal = false; @@ -367,6 +407,10 @@ function openModal(issue?: Record) { if (issue) { + if (projectIssueMap.has(issue.id)) { + alert("This issue is linked to a project and cannot be edited."); + return; + } isEditing = true; currentEditingId = issue.id; newIssue = { ...issue }; @@ -503,19 +547,26 @@ } async function deleteIssue(id: string) { - if (confirm("Are you sure you want to delete this issue?")) { - const { error } = await supabase - .from("vb_issues") - .delete() - .eq("id", id); - if (error) { - console.error("Error deleting issue:", error); - return; - } + if (projectIssueMap.has(id)) { + alert("This issue is linked to a project and cannot be deleted."); + return; + } + + const confirmDelete = confirm("Are you sure you want to delete this issue?"); + if (!confirmDelete) return; + + const { error } = await supabase.from("vb_issues").delete().eq("id", id); + + if (error) { + console.error("Delete failed:", error); + alert("Failed to delete issue."); + } else { await fetchIssues(); + alert("Issue deleted."); } } + let selectedFile: File | null = null; let imagePreviewUrl: string | null = null; @@ -615,6 +666,64 @@ showProjectModal = true; } + function openPurchaseOrderModal(issue) { + if (purchaseOrderMap.has(issue.id)) { + alert("This issue already has a purchase order."); + return; + } + + const today = new Date(); + const dueDate = new Date(today); + dueDate.setDate(dueDate.getDate() + 2); + + const formatDate = (d) => d.toISOString().split("T")[0]; + + newPO = { + issue_id: issue.id, + villa_id: issue.villa_id, + po_status: "requested", + requested_by: "", + requested_date: formatDate(today), + po_due: formatDate(dueDate), + po_item: "", + po_quantity: "", + po_type: "" + }; + + selectedIssueSummary = issue.description_of_the_issue ?? ""; + showPurchaseOrderModal = true; + } + async function submitPurchaseOrder() { + if (!newPO.requested_by || !newPO.po_item || !newPO.po_quantity || !newPO.po_type) { + alert("Please complete all required fields."); + return; + } + + const { data: existing } = await supabase + .from("vb_purchase_orders") + .select("id") + .eq("issue_id", newPO.issue_id) + .maybeSingle(); + + if (existing) { + alert("Purchase order already exists for this issue."); + return; + } + + const { error } = await supabase.from("vb_purchase_orders").insert(newPO); + + if (error) { + console.error(error); + alert("Failed to create PO."); + return; + } + + // reactively update map + purchaseOrderMap = new Set([...purchaseOrderMap, newPO.issue_id]); + + alert("Purchase Order submitted!"); + showPurchaseOrderModal = false; + } async function submitProject() { if (!newProject.project_name || !newProject.input_by || !newProject.assigned_to) { alert("Please fill all required fields"); @@ -805,14 +914,35 @@ {:else if col.key === "actions"} - {#if projectIssueMap.has(row.id)} - - - {:else} - - - {/if} - + {#if projectIssueMap.has(row.id) || purchaseOrderMap.has(row.id)} + + + {:else} + + + {/if} {:else if col.key === "move_issue"} {#if row[col.key as keyof Issue] === "PROJECT"} @@ -834,28 +964,46 @@ {:else} - + {#if projectIssueMap.has(row.id)} + {:else} {/if} - + + {#if purchaseOrderMap.has(row.id)} + + + {:else} + + {/if} {/if} @@ -1405,4 +1553,64 @@ -{/if} \ No newline at end of file +{/if} +{#if showPurchaseOrderModal} +
+
+

Create Purchase Order

+ +

Related Issue: “{selectedIssueSummary}”

+ + + + + + + + + + + + + +
+ + +
+
+
+{/if} diff --git a/src/routes/backoffice/purchaseorder/acknowledged/+page.svelte b/src/routes/backoffice/purchaseorder/acknowledged/+page.svelte deleted file mode 100644 index 29d48e9..0000000 --- a/src/routes/backoffice/purchaseorder/acknowledged/+page.svelte +++ /dev/null @@ -1,972 +0,0 @@ - - -
-
-
-

- 📦 - Purchase Order Acknowledged List -

-

- Manage your purchase orders efficiently. You can add, edit, or - delete purchase orders as needed. -

-
-
- { - const searchTerm = ( - e.target as HTMLInputElement - ).value.toLowerCase(); - fetchPurchaseOrder(null, searchTerm, "created_at", "desc"); - }} - /> - -
-
-
- - - - {#each columns as col} - {#if col.key === "name"} - - {:else} - - {/if} - {/each} - - - - {#each paginatedRows as row} - - {#each columns as col} - {#if col.key === "name"} - - {:else if col.key === "approval"} - - {:else if col.key === "acknowledged"} - - {:else if col.key === "received"} - - {:else if col.key === "completed_status"} - - {:else if col.key === "move_issue"} - - {:else if col.key === "updated_at"} - - {:else if col.key === "created_at"} - - {:else} - - {/if} - {/each} - - {/each} - -
- {col.title} - - {col.title} -
- {row[col.key as keyof PurchaseOrderDisplay]} - - - - { - const isChecked = ( - e.target as HTMLInputElement - ).checked; - row.acknowledged = isChecked; - - if (isChecked) { - // map to project - await acknowledgedOk( - row.id, - isChecked, - ); - } - }} - /> - - { - const isChecked = ( - e.target as HTMLInputElement - ).checked; - row.received = isChecked; - - if (isChecked) { - // map to project - await receivedOk( - row.id, - isChecked, - ); - } - }} - /> - - - - - - - {new Date(row.updated_at).toLocaleString()} - - {new Date(row.created_at).toLocaleString()} - {row[ - col.key as keyof PurchaseOrderDisplay - ]}
-
- - -
-
- Showing {(currentPage - 1) * rowsPerPage + 1}– - {Math.min(currentPage * rowsPerPage, allRows.length)} of {allRows.length} -
-
- - {#each Array(totalPages) - .fill(0) - .map((_, i) => i + 1) as page} - - {/each} - -
-
-
- -{#if showModal} -
-
-

- {isEditing ? "Edit Project" : "Add Project"} -

-
- -
- - -
- {#each formColumns as col} - {#if col.key === "po_status"} -
- - -
- {:else if col.key === "po_type"} -
- - -
- {:else if col.key === "prepared_date"} -
- - -
- {:else if col.key === "po_quantity"} -
- - -
- {:else if col.key === "approved_price"} -
- - -
- {:else if col.key === "approved_vendor"} -
- - -
- {:else} -
- - -
- {/if} - {/each} -
- - -
-
-
-
-{/if} diff --git a/src/routes/backoffice/purchaseorder/approval/+page.svelte b/src/routes/backoffice/purchaseorder/approval/+page.svelte deleted file mode 100644 index 07d7f3a..0000000 --- a/src/routes/backoffice/purchaseorder/approval/+page.svelte +++ /dev/null @@ -1,1006 +0,0 @@ - - -
-
-
-

- 📦 - Purchase Order List -

-

- Manage your purchase orders efficiently. You can add, edit, or - delete purchase orders as needed. -

-
-
- { - const searchTerm = ( - e.target as HTMLInputElement - ).value.toLowerCase(); - fetchPurchaseOrder(null, searchTerm, "created_at", "desc"); - }} - /> - -
-
-
- - - - {#each columns as col} - {#if col.key === "name"} - - {:else} - - {/if} - {/each} - - - - {#each paginatedRows as row} - - {#each columns as col} - {#if col.key === "name"} - - {:else if col.key === "approval"} - - {:else if col.key === "acknowledged"} - - {:else if col.key === "actions"} - - {:else if col.key === "move_issue"} - - {:else if col.key === "updated_at"} - - {:else if col.key === "created_at"} - - {:else if col.key === "po_price"} - - {:else if col.key === "po_total_price"} - - {:else} - - {/if} - {/each} - - {/each} - -
- {col.title} - - {col.title} -
- {row[col.key as keyof PurchaseOrderDisplay]} - - - - { - const isChecked = ( - e.target as HTMLInputElement - ).checked; - row.acknowledged = isChecked; - - if (isChecked) { - // map to project - await acknowledgedOk( - row.id, - isChecked, - ); - } - }} - /> - - - - - - - {timestampToDateTime( - row[ - col.key as keyof PurchaseOrderDisplay - ] as string, - )}{timestampToDateTime( - row[ - col.key as keyof PurchaseOrderDisplay - ] as string, - )}{row.approved_price.toLocaleString( - "en-US", - { - style: "currency", - currency: "USD", - }, - )}{( - row.approved_quantity * - row.approved_price - ).toLocaleString("en-US", { - style: "currency", - currency: "USD", - })}{row[ - col.key as keyof PurchaseOrderDisplay - ]}
-
- - -
-
- Showing {(currentPage - 1) * rowsPerPage + 1}– - {Math.min(currentPage * rowsPerPage, allRows.length)} of {allRows.length} -
-
- - {#each Array(totalPages) - .fill(0) - .map((_, i) => i + 1) as page} - - {/each} - -
-
-
- -{#if showModal} -
-
-

- {isEditing ? "Edit Project" : "Add Project"} -

-
- -
- - -
- {#each formColumns as col} - {#if col.key === "po_status"} -
- - -
- {:else if col.key === "po_type"} -
- - -
- {:else if col.key === "po_quantity"} -
- - -
- {:else if col.key === "approved_price"} -
- - -
- {:else if col.key === "approved_quantity"} -
- - -
- {:else if col.key === "total_approved_order_amount"} -
- - -
- {:else if col.key === "approved_vendor"} -
- - -
- {:else if col.key === "approved_by"} -
- - -
- {:else} -
- - -
- {/if} - {/each} -
- - -
-
-
-
-{/if} diff --git a/src/routes/backoffice/purchaseorder/complete/+page.svelte b/src/routes/backoffice/purchaseorder/complete/+page.svelte deleted file mode 100644 index cc416ac..0000000 --- a/src/routes/backoffice/purchaseorder/complete/+page.svelte +++ /dev/null @@ -1,916 +0,0 @@ - - -
-
-
-

- 📦 - Purchase Order Complete List -

-

- Manage your purchase orders efficiently. You can add, edit, or - delete purchase orders as needed. -

-
-
- { - const searchTerm = ( - e.target as HTMLInputElement - ).value.toLowerCase(); - fetchPurchaseOrder(null, searchTerm, "created_at", "desc"); - }} - /> - -
-
-
- - - - {#each columns as col} - {#if col.key === "name"} - - {:else} - - {/if} - {/each} - - - - {#each paginatedRows as row} - - {#each columns as col} - {#if col.key === "name"} - - {:else if col.key === "approval"} - - {:else if col.key === "received"} - - {:else if col.key === "completed_status"} - - {:else if col.key === "actions"} - - {:else if col.key === "move_issue"} - - {:else if col.key === "updated_at"} - - {:else if col.key === "created_at"} - - {:else} - - {/if} - {/each} - - {/each} - -
- {col.title} - - {col.title} -
- {row[col.key as keyof PurchaseOrderDisplay]} - - - - { - const isChecked = ( - e.target as HTMLInputElement - ).checked; - row.received = isChecked; - - if (isChecked) { - // map to project - await receivedOk( - row.id, - isChecked, - ); - } - }} - /> - - - - - - - - - - {new Date( - row[ - col.key as keyof PurchaseOrderDisplay - ] as string, - ).toLocaleString("en-US")} - - {new Date( - row[ - col.key as keyof PurchaseOrderDisplay - ] as string, - ).toLocaleString("en-US")} - {row[ - col.key as keyof PurchaseOrderDisplay - ]}
-
- - -
-
- Showing {(currentPage - 1) * rowsPerPage + 1}– - {Math.min(currentPage * rowsPerPage, allRows.length)} of {allRows.length} -
-
- - {#each Array(totalPages) - .fill(0) - .map((_, i) => i + 1) as page} - - {/each} - -
-
-
- -{#if showModal} -
-
-

- {isEditing ? "Edit Project" : "Add Project"} -

-
- -
- - -
- {#each formColumns as col} - {#if col.key === "po_status"} -
- - -
- {:else if col.key === "po_type"} -
- - -
- {:else if col.key === "prepared_date"} -
- - -
- {:else if col.key === "po_quantity"} -
- - -
- {:else if col.key === "approved_price"} -
- - -
- {:else if col.key === "approved_vendor"} -
- - -
- {:else} -
- - -
- {/if} - {/each} -
- - -
-
-
-
-{/if} diff --git a/src/routes/backoffice/purchaseorder/received/+page.svelte b/src/routes/backoffice/purchaseorder/received/+page.svelte deleted file mode 100644 index b3423b9..0000000 --- a/src/routes/backoffice/purchaseorder/received/+page.svelte +++ /dev/null @@ -1,989 +0,0 @@ - - -
-
-
-

- 📦 - Purchase Order Received List -

-

- Manage your purchase orders efficiently. You can add, edit, or - delete purchase orders as needed. -

-
-
- { - const searchTerm = ( - e.target as HTMLInputElement - ).value.toLowerCase(); - fetchPurchaseOrder(null, searchTerm, "created_at", "desc"); - }} - /> - -
-
-
- - - - {#each columns as col} - {#if col.key === "name"} - - {:else} - - {/if} - {/each} - - - - {#each paginatedRows as row} - - {#each columns as col} - {#if col.key === "name"} - - {:else if col.key === "approval"} - - {:else if col.key === "acknowledged"} - - {:else if col.key === "received"} - - {:else if col.key === "completed_status"} - - {:else if col.key === "updated_at"} - - {:else if col.key === "created_at"} - - {:else if col.key === "actions"} - - {:else if col.key === "move_issue"} - - {:else} - - {/if} - {/each} - - {/each} - -
- {col.title} - - {col.title} -
- {row[col.key as keyof PurchaseOrderDisplay]} - - - - { - const isChecked = ( - e.target as HTMLInputElement - ).checked; - row.acknowledged = isChecked; - - if (isChecked) { - // map to project - await acknowledgedOk( - row.id, - isChecked, - ); - } - }} - /> - - { - const isChecked = ( - e.target as HTMLInputElement - ).checked; - row.received = isChecked; - - if (isChecked) { - // map to project - await receivedOk( - row.id, - isChecked, - ); - } - }} - /> - - - - {new Date( - row[ - col.key as keyof PurchaseOrderDisplay - ] as string, - ).toLocaleString("en-US")} - - {new Date( - row[ - col.key as keyof PurchaseOrderDisplay - ] as string, - ).toLocaleString("en-US")} - - - - - - - {row[ - col.key as keyof PurchaseOrderDisplay - ]}
-
- - -
-
- Showing {(currentPage - 1) * rowsPerPage + 1}– - {Math.min(currentPage * rowsPerPage, allRows.length)} of {allRows.length} -
-
- - {#each Array(totalPages) - .fill(0) - .map((_, i) => i + 1) as page} - - {/each} - -
-
-
- -{#if showModal} -
-
-

- {isEditing ? "Edit Project" : "Add Project"} -

-
- -
- - -
- {#each formColumns as col} - {#if col.key === "po_status"} -
- - -
- {:else if col.key === "po_type"} -
- - -
- {:else if col.key === "prepared_date"} -
- - -
- {:else if col.key === "po_quantity"} -
- - -
- {:else if col.key === "approved_price"} -
- - -
- {:else if col.key === "approved_vendor"} -
- - -
- {:else} -
- - -
- {/if} - {/each} -
- - -
-
-
-
-{/if} diff --git a/src/routes/timesheet/+page.svelte b/src/routes/timesheet/+page.svelte index 746cb5c..f5aa5bd 100644 --- a/src/routes/timesheet/+page.svelte +++ b/src/routes/timesheet/+page.svelte @@ -69,7 +69,8 @@ const { data: villaData, error: villaError } = await supabase .from("vb_villas") .select("id, villa_name, villa_status") - .eq("villa_status", "Active"); + .eq("villa_status", "Active") + .order("villa_name", { ascending: true }); if (villaError) { console.error("Failed to fetch villas:", villaError.message); @@ -81,7 +82,8 @@ const { data: empData, error: empError } = await supabase .from("vb_employee") .select("id, employee_name") - .eq("employee_status", "Active"); + .eq("employee_status", "Active") + .order("employee_name", { ascending: true }); if (empError) { console.error("Failed to fetch employees:", empError.message);