diff --git a/src/lib/utils/authUtil.ts b/src/lib/utils/authUtil.ts new file mode 100644 index 0000000..99db787 --- /dev/null +++ b/src/lib/utils/authUtil.ts @@ -0,0 +1,10 @@ +import { supabase } from "$lib/supabaseClient"; + +export async function getSessionAuthId() { + const { data: { session }, error } = await supabase.auth.getSession(); + if (error) { + console.error("Error fetching session:", error); + return null; + } + return session?.user?.id || null; +} diff --git a/src/routes/backoffice/issue/+page.svelte b/src/routes/backoffice/issue/+page.svelte index c203e0f..cc63b52 100644 --- a/src/routes/backoffice/issue/+page.svelte +++ b/src/routes/backoffice/issue/+page.svelte @@ -211,7 +211,6 @@ { key: "issue_number", title: "Issue Number" }, { key: "move_issue", title: "Move Issue" }, { key: "description_of_the_issue", title: "Description of The Issue" }, - { key: "reported_date", title: "Reported Date" }, { key: "issue_related_image", title: "Issue Related Image" }, { key: "issue_source", title: "Issue Source" }, { key: "reported_name", title: "Reported By" }, @@ -732,7 +731,7 @@ row[col.key as keyof Issue] as | string | number, - ).toLocaleDateString("en-US") + ).toLocaleString("en-US") : ""} {:else if col.key === "need_approval"} @@ -751,6 +750,23 @@ ❌ {/if} + {:else if col.key === "created_at"} + + + {new Date( + row[col.key as keyof Issue] as string, + ).toLocaleString("en-US")} + + {:else if col.key === "updated_at"} + + {row[col.key as keyof Issue] + ? new Date( + row[ + col.key as keyof Issue + ] as string, + ).toLocaleString("en-US") + : ""} + {:else} {row[col.key as keyof Issue]} !excludedKeys.includes(col.key), @@ -207,7 +219,7 @@ } //validation project make - function validateProjectCheckBox(project: insetProject): boolean { + function validateProjectCheckBox(project: insertProject): boolean { if (!project.project_due_date) { console.error("Project due date is required"); alert("Project due date is required"); @@ -259,6 +271,17 @@ } async function addToPo(project: Project) { + // session user + const session = await supabase.auth.getSession(); + const user = session?.data?.session?.user; + if (!user) { + console.error("User not authenticated"); + alert("User not authenticated"); + return; + } + + const inputBy = user.id; // Use user id as input_by + if (!project.add_to_po) { console.error("Project must be added to PO"); alert("Project must be added to PO"); @@ -274,6 +297,8 @@ input_by: project?.input_by, project_due_date: project?.project_due_date, picture_link: project?.picture_link, + updated_at: new Date().toISOString(), + updated_by: inputBy, }; const { data, error } = await supabase @@ -310,6 +335,16 @@ } async function saveProject(event: Event) { + //get session user + const session = await supabase.auth.getSession(); + const user = session?.data?.session?.user; + if (!user) { + console.error("User not authenticated"); + return; + } + + const inputBy = user.id; // Use email or id as input_by + const formData = new FormData(event.target as HTMLFormElement); // Upload image if selected @@ -326,12 +361,14 @@ imagePreviewUrl = data?.path; } - const projectUpdate: insetProject = { + const projectUpdate: insertProject = { issue_id: formData.get("issue_id") as string, - input_by: formData.get("input_by") as string, + input_by: inputBy, project_due_date: formData.get("project_due_date") as string, picture_link: imagePreviewUrl || (formData.get("picture_link") as string), + updated_at: new Date().toISOString(), + updated_by: user.id, }; // Validate project before saving @@ -553,6 +590,42 @@ No Picture {/if} + {:else if col.key === "project_due_date"} + + {#if row.project_due_date} + {new Date( + row.project_due_date, + ).toLocaleString("en-US")} + {:else} + No Due Date + {/if} + + {:else if col.key === "updated_at"} + + {#if row.updated_at} + {new Date( + row.updated_at, + ).toLocaleString("en-US")} + {:else} + No Update + {/if} + + {:else if col.key === "input_by"} + + {#if row.input_by} + {row.input_by} + {:else} + Not Yet Input + {/if} + + {:else if col.key === "updated_name"} + + {#if row.updated_name} + {row.updated_name} + {:else} + No Updater + {/if} + {:else} {row[col.key as keyof Projects]} !excludedKeys.includes(col.key), @@ -285,6 +304,18 @@ } async function saveProject() { + // session check + const session = await supabase.auth.getSession(); + + if (!session.data.session) { + alert("You must be logged in to perform this action."); + return; + } + + const user = session.data.session.user; + + const inputBy = user.id; + if (!validateInput()) { return; } @@ -297,6 +328,9 @@ po_price: newPurchaseOrders.po_price || 0, po_total_price: newPurchaseOrders.po_quantity * newPurchaseOrders.po_price || 0, + input_by: inputBy, + updated_by: inputBy, + updated_at: new Date().toISOString(), }; if (isEditing && currentEditingId) { @@ -400,13 +434,24 @@ } async function updateProsesToApproval(id: string, status: boolean) { + const sessionId = await getSessionAuthId(); + if (!sessionId) { + alert("You must be logged in to perform this action."); + return; + } + if (!validateInputProsesToAprroval()) { return; } const { data, error } = await supabase .from("vb_purchase_orders") - .update({ proses_to_approval: status, po_status: "REQUESTED" }) + .update({ + proses_to_approval: status, + po_status: "REQUESTED", + updated_by: sessionId, + updated_at: new Date().toISOString(), + }) .eq("id", id); if (error) { @@ -425,6 +470,11 @@ id: string, row: PurchaseOrderDisplay, ) { + const sessionId = await getSessionAuthId(); + if (!sessionId) { + alert("You must be logged in to perform this action."); + return; + } const selectedOption = (e.target as HTMLSelectElement)?.value ?? ""; const option = getStatusOption(selectedOption); @@ -454,9 +504,19 @@ } async function acknowledgedOk(id: string, status: boolean) { + const sessionId = await getSessionAuthId(); + if (!sessionId) { + alert("You must be logged in to perform this action."); + return; + } + const { data, error } = await supabase .from("vb_purchase_orders") - .update({ acknowledged: status }) + .update({ + acknowledged: status, + updated_by: sessionId, + updated_at: new Date().toISOString(), + }) .eq("id", id); if (error) { @@ -468,9 +528,19 @@ } async function receivedOk(id: string, status: boolean) { + const sessionId = await getSessionAuthId(); + if (!sessionId) { + alert("You must be logged in to perform this action."); + return; + } + const { data, error } = await supabase .from("vb_purchase_orders") - .update({ receivedOk: status }) + .update({ + receivedOk: status, + updated_by: sessionId, + updated_at: new Date().toISOString(), + }) .eq("id", id); if (error) { @@ -486,6 +556,11 @@ id: string, row: PurchaseOrderDisplay, ) { + const sessionId = await getSessionAuthId(); + if (!sessionId) { + alert("You must be logged in to perform this action."); + return; + } const selectedOption = (e.target as HTMLSelectElement)?.value ?? ""; const option = getStatusOption(selectedOption); @@ -503,7 +578,11 @@ const { data, error } = await supabase .from("vb_purchase_orders") - .update({ approval: newPurchaseOrders.approval }) + .update({ + approval: newPurchaseOrders.approval, + updated_by: sessionId, + updated_at: new Date().toISOString(), + }) .eq("id", id); if (error) { @@ -515,9 +594,18 @@ } async function completedStatusOk(id: string, status: string) { + const sessionId = await getSessionAuthId(); + if (!sessionId) { + alert("You must be logged in to perform this action."); + return; + } const { data, error } = await supabase .from("vb_purchase_orders") - .update({ completed_status: status }) + .update({ + completed_status: status, + updated_by: sessionId, + updated_at: new Date().toISOString(), + }) .eq("id", id); if (error) { @@ -753,6 +841,14 @@ > + {:else if col.key === "updated_at"} + {new Date( + row[ + col.key as keyof PurchaseOrderDisplay + ] as string, + ).toLocaleString()} {:else if col.key === "actions"} + {:else if col.key === "updated_at"} + + {new Date(row.updated_at).toLocaleString()} + + {:else if col.key === "created_at"} + + {new Date(row.created_at).toLocaleString()} + {:else} {row[ diff --git a/src/routes/backoffice/purchaseorder/approval/+page.svelte b/src/routes/backoffice/purchaseorder/approval/+page.svelte index 0ee744f..07d7f3a 100644 --- a/src/routes/backoffice/purchaseorder/approval/+page.svelte +++ b/src/routes/backoffice/purchaseorder/approval/+page.svelte @@ -2,6 +2,8 @@ import { onMount } from "svelte"; import Select from "svelte-select"; import { supabase } from "$lib/supabaseClient"; + import { getSessionAuthId } from "$lib/utils/authUtil"; + import { timestampToDateTime } from "$lib/utils/conversion"; type PurchaseOrderInsert = { issue_id: string; @@ -10,6 +12,9 @@ approved_price: number; approved_quantity: number; total_approved_order_amount?: number; + input_by?: string; + updated_by?: string; + updated_at?: string; }; let purchaseOrderInsert: PurchaseOrderInsert = { @@ -19,6 +24,9 @@ approved_price: 0, approved_quantity: 0, total_approved_order_amount: 0, + input_by: "", + updated_by: "", + updated_at: new Date().toISOString(), }; type PurchaseOrders = { @@ -42,6 +50,10 @@ input_by: string; issue_id: string; approved_by: string; + approved_name: string; + updated_by: string; + updated_at: string; + updated_name: string; created_at: string; }; @@ -91,7 +103,10 @@ key: "total_approved_order_amount", title: "Total Approved Order Amount", }, - { key: "approved_by", title: "Approved By" }, + { key: "approved_name", title: "Approved By" }, + { key: "inputed_name", title: "Input By" }, + { key: "updated_name", title: "Updated By" }, + { key: "updated_at", title: "Updated At" }, { key: "created_at", title: "Created At" }, { key: "actions", title: "Actions" }, // For edit/delete buttons ]; @@ -243,6 +258,10 @@ "approved_by", "name", "po_status", + "input_name", + "updated_name", + "updated_at", + "approved_name", ]; const formColumns = columns.filter( (col) => !excludedKeys.includes(col.key), @@ -263,6 +282,11 @@ } async function saveProject() { + const sessionId = await getSessionAuthId(); + if (!sessionId) { + alert("You must be logged in to perform this action."); + return; + } purchaseOrderInsert = { issue_id: newPurchaseOrders.issue_id || "", approved_vendor: newPurchaseOrders.approved_vendor || "", @@ -272,6 +296,9 @@ total_approved_order_amount: newPurchaseOrders.approved_quantity * newPurchaseOrders.approved_price, + input_by: sessionId, + updated_by: sessionId, + updated_at: new Date().toISOString(), }; if (!validateInput()) { @@ -439,6 +466,11 @@ ) { const selectedOption = (e.target as HTMLSelectElement)?.value ?? ""; const option = getStatusOption(selectedOption); + const sessionId = await getSessionAuthId(); + if (!sessionId) { + alert("You must be logged in to perform this action."); + return; + } newPurchaseOrders = { ...row, @@ -457,6 +489,9 @@ .update({ approval: newPurchaseOrders.approval, po_status: "APPROVED", + approved_by: sessionId, + updated_by: sessionId, + updated_at: new Date().toISOString(), }) .eq("id", id); @@ -606,61 +641,6 @@ }} /> - {:else if col.key === "received"} - - { - const isChecked = ( - e.target as HTMLInputElement - ).checked; - row.received = isChecked; - - if (isChecked) { - // map to project - await receivedOk( - row.id, - isChecked, - ); - } - }} - /> - - {:else if col.key === "completed_status"} - - - {:else if col.key === "actions"} - {:else if col.key === "date_in" || col.key === "date_out"} - - {row[col.key] - ? new Date(row[col.key]).toLocaleString("en-GB", { - day: "2-digit", - month: "2-digit", - year: "numeric", - hour: "2-digit", - minute: "2-digit", - hour12: false, - }) + {:else if col.key === "date_in" || col.key === "date_out"} + + {row[col.key] + ? new Date(row[col.key]).toLocaleString( + "en-GB", + { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour: "2-digit", + minute: "2-digit", + hour12: false, + }, + ) : "N/A"} - - + {:else} {row[col.key as keyof TimesheetDisplay]} @@ -811,138 +816,145 @@ {#if showModal} -
- - -
-

Timesheet Entry

- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- -
{form.total_work_hour}
-
- -
- - -
- - -
+
+

Timesheet Entry

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
{form.total_work_hour}
+
+ +
+ + +
+ + +
{/if} diff --git a/src/routes/backoffice/vendor/+page.svelte b/src/routes/backoffice/vendor/+page.svelte index b162c41..ea41d6c 100644 --- a/src/routes/backoffice/vendor/+page.svelte +++ b/src/routes/backoffice/vendor/+page.svelte @@ -28,18 +28,6 @@ website: string; }; - let allRowsVendor: Vendor[] = []; - let currentPage = 1; - let itemsPerPage = 10; - $: offset = (currentPage - 1) * itemsPerPage; - $: totalPages = Math.ceil(totalItems / itemsPerPage); - let totalItems = 0; - let newVendor: Record = {}; - let showModal = false; - let isEditing = false; - let currentEditingId: string | null = null; - let searchTerm = ""; - const columns = [ { key: "name", title: "Name" }, { key: "vendor_type", title: "Vendor Type" }, @@ -63,6 +51,19 @@ const excludedKeys = ["id", "created_by", "created_at", "updated_at"]; $: formColumns = columns.filter((col) => !excludedKeys.includes(col.key)); + let allRowsVendor: Vendor[] = []; + let currentPage = 1; + let offset = 0; + let itemsPerPage = 10; + let totalItems = 0; + let newVendor: Record = {}; + let showModal = false; + let isEditing = false; + let currentEditingId: string | null = null; + let searchTerm = ""; + $: offset = (currentPage - 1) * itemsPerPage; + $: totalPages = Math.ceil(totalItems / itemsPerPage); + async function fetchVendor(search = "", resetPage = false) { if (resetPage) currentPage = 1; @@ -84,7 +85,13 @@ function resetPagination() { currentPage = 1; + offset = 0; + totalItems = 0; fetchVendor(searchTerm); + showModal = false; + isEditing = false; + currentEditingId = null; + newVendor = {}; } function nextPage() { @@ -102,6 +109,7 @@ } function changePage(page: number) { + if (page < 1 || page > totalPages || page === currentPage) return; currentPage = page; fetchVendor(searchTerm); } @@ -159,7 +167,10 @@ } async function deleteVendor(id: string) { - const { error } = await supabase.from("vb_vendor").delete().eq("id", id); + const { error } = await supabase + .from("vb_vendor") + .delete() + .eq("id", id); if (error) { console.error("Delete error:", error); } else { @@ -167,7 +178,10 @@ } } - function pageRange(totalPages: number, currentPage: number): (number | string)[] { + function pageRange( + totalPages: number, + currentPage: number, + ): (number | string)[] { const range: (number | string)[] = []; const maxDisplay = 5; @@ -207,79 +221,125 @@ fetchVendor(searchTerm, true); }} /> - - - - - {#each columns as col} - - {/each} - - - - - {#each allRowsVendor as row} - +
+
{col.title}Actions
+ + {#each columns as col} - + {/each} - + - {/each} - -
{row[col.key]}{col.title} - - - Actions
+ + + {#each allRowsVendor as row} + + {#each columns as col} + {row[col.key as keyof typeof row]} + {/each} + + + + + + {/each} + + +
- Showing {(currentPage - 1) * itemsPerPage + 1}– - {Math.min(currentPage * itemsPerPage, totalItems)} of {totalItems} + Page {currentPage} of {totalPages} | Showing + {currentPage === totalPages && totalItems > 0 + ? totalItems - offset + : itemsPerPage} items | Showing + {offset + 1} to {Math.min(currentPage * itemsPerPage, totalItems)} of {totalItems}
- + {#each pageRange(totalPages, currentPage) as page} - {#if page === '...'} + {#if page === "..."} ... {:else} {/if} {/each} - +
{#if showModal} -
-
-

{isEditing ? "Edit Vendor" : "Add Vendor"}

- {#each formColumns as col} -
- - {#if col.key === 'vendor_unique'} - - {:else} - - {/if} +
+
+

+ {isEditing ? "Edit Vendor" : "Add Vendor"} +

+ {#each formColumns as col} +
+ + {#if col.key === "vendor_unique"} + + {:else} + + {/if} +
+ {/each} +
+ +
- {/each} -
- -
-
{/if}