From b4f5706522e81a827ac4d59cb975ff62b8ee1041 Mon Sep 17 00:00:00 2001 From: arteons Date: Tue, 1 Jul 2025 16:43:23 +0800 Subject: [PATCH] clean code --- src/routes/backoffice/timesheets/+page.svelte | 338 ++++++++---------- 1 file changed, 155 insertions(+), 183 deletions(-) diff --git a/src/routes/backoffice/timesheets/+page.svelte b/src/routes/backoffice/timesheets/+page.svelte index b832b37..70035f1 100644 --- a/src/routes/backoffice/timesheets/+page.svelte +++ b/src/routes/backoffice/timesheets/+page.svelte @@ -3,6 +3,7 @@ import { onMount } from "svelte"; import { writable } from "svelte/store"; + type Timesheets = { id: number; entered_by: string; @@ -76,6 +77,10 @@ key: string; title: string; }; + type Employee = { + id: string; + name: string; + }; const categoryOfWork = [ @@ -93,6 +98,7 @@ { label: "Irregular", value: "Irregular" }, ]; const columns: columns[] = [ + { key: "villa_name", title: "Villa Name" }, { key: "name", title: "Work Description" }, { key: "staff_id", title: "Staff Name" }, { key: "date_in", title: "Date In" }, @@ -100,7 +106,6 @@ { key: "type_of_work", title: "Type of Work" }, { key: "category_of_work", title: "Category of Work" }, { key: "approval", title: "Approval" }, - { key: "villa_name", title: "Villa Name" }, { key: "approved_by", title: "Approved By" }, { key: "approved_date", title: "Approved/Rejected Date" }, { key: "total_hours_work", title: "Total Hours Work" }, @@ -108,34 +113,92 @@ { key: "created_at", title: "Created At" }, { key: "actions", title: "Actions" }, ]; + const excludedKeys = ["id"]; + const formColumns = columns.filter( + (col) => !excludedKeys.includes(col.key), + ); + const typeOfWorkOptions = ["Running", "Periodic", "Irregular"]; + const categoryOptions = [ + "Cleaning", + "Gardening/Pool", + "Maintenance", + "Supervision", + "Guest Service", + "Administration", + "Non Billable", + ]; - // Store for current user ID and filters + // reactive variables let currentUserId: string | null = null; let currentVillaFilter: string | null = null; let currentSearchTerm: string | null = null; let dataVilla: Villa[] = []; let allRows: TimesheetDisplay[] = []; - + let currentPage = 1; + let rowsPerPage = 20; + $: totalPages = Math.ceil(allRows.length / rowsPerPage); + $: paginatedRows = allRows.slice( + (currentPage - 1) * rowsPerPage, + currentPage * rowsPerPage, + ); + $: currentPage = 1; + let showModal = false; + let isEditing = false; + let currentEditingId: string | null = null; + let newTsdata: Record = {}; + let employees: Employee[] = []; + let villas: Villa[] = []; + let form = { + entered_by: "", + work_description: "", + type_of_work: "Running", + category_of_work: "Cleaning", + villa_id: "", + datetime_in: "", + datetime_out: "", + total_work_hour: 0, + remarks: "", + approval: null, // Default null + }; + // Fetch initial data on mount onMount(async () => { + // get current user const { data: { user }, } = await supabase.auth.getUser(); - currentUserId = user?.id ?? null; - }); - onMount(async () => { - const { data, error } = await supabase + // fetch employees + const { data: empData, error: empErr } = await supabase + .from("vb_employee") + .select("id, employee_name") + .eq("employee_status", "Active") + .order("employee_name", { ascending: true }); + + if (!empErr && empData) { + employees = empData.map((e) => ({ + id: e.id, + name: e.employee_name, + })); + } else { + console.error("Failed to load employees", empErr); + } + + // fetch villas + const { data: villaData, error: villaErr } = await supabase .from("vb_villas") .select("id, villa_name") - .eq("villa_status", "Active"); + .eq("villa_status", "Active") + .order("villa_name", { ascending: true }); - if (error) { - console.error("Error fetching villas:", error); - } else if (data) { - dataVilla = data; + if (!villaErr && villaData) { + villas = villaData; + } else { + console.error("Failed to load villas", villaErr); } + + fetchTimeSheets(); }); - + // Function to calculate total work hours function calculateTotalHours() { if (form.datetime_in && form.datetime_out) { @@ -148,7 +211,75 @@ form.total_work_hour = 0; } } + // Function to go to a specific page + function goToPage(page: number) { + if (page >= 1 && page <= totalPages) currentPage = page; + } + // Function to open the modal for adding or editing a timesheet + function openModal(tsdata?: Record) { + if (tsdata) { + // Edit mode + isEditing = true; + currentEditingId = tsdata.id; + form = { + entered_by: + employees.find((e) => e.name === tsdata.staff_id)?.id || "", + work_description: tsdata.name, + type_of_work: tsdata.type_of_work, + category_of_work: tsdata.category_of_work, + villa_id: + villas.find((v) => v.villa_name === tsdata.villa_name)?.id || + "", + datetime_in: tsdata.date_in?.toISOString().slice(0, 16), + datetime_out: tsdata.date_out?.toISOString().slice(0, 16), + total_work_hour: 0, + remarks: tsdata.remarks, + approval: null, // leave null or bring in if editing allowed + }; + calculateTotalHours(); + } else { + // Add mode + isEditing = false; + currentEditingId = null; + form = { + entered_by: "", + work_description: "", + type_of_work: "Running", + category_of_work: "Cleaning", + villa_id: "", + datetime_in: "", + datetime_out: "", + total_work_hour: 0, + remarks: "", + approval: null, + }; + } + + showModal = true; + } + // Function to validate the form data + function validateForm(formData: FormData): boolean { + const errors: { [key: string]: string } = {}; + const requiredFields = [ + "work_description", + "type_of_work", + "category_of_work", + "villa_id", + "date_in", + "date_out", + "remarks", + ]; + + requiredFields.forEach((field) => { + if (!formData.get(field) || formData.get(field) === "") { + errors[field] = `${field.replace(/_/g, " ")} is required.`; + } + }); + + formErrors.set(errors); + return Object.keys(errors).length === 0; + } // Function to fetch timesheets with optional filters and sorting async function fetchTimeSheets( villaNameFilter: string | null = null, @@ -264,146 +395,7 @@ }); // Sort the rows based on the sortColumn and sortOrder } - let currentPage = 1; - let rowsPerPage = 20; - $: totalPages = Math.ceil(allRows.length / rowsPerPage); - $: paginatedRows = allRows.slice( - (currentPage - 1) * rowsPerPage, - currentPage * rowsPerPage, - ); - - function goToPage(page: number) { - if (page >= 1 && page <= totalPages) currentPage = page; - } - - onMount(async () => { - // get current user - const { - data: { user }, - } = await supabase.auth.getUser(); - currentUserId = user?.id ?? null; - - // fetch employees - const { data: empData, error: empErr } = await supabase - .from("vb_employee") - .select("id, employee_name") - .eq("employee_status", "Active") - .order("employee_name", { ascending: true }); - - if (!empErr && empData) { - employees = empData.map((e) => ({ - id: e.id, - name: e.employee_name, - })); - } else { - console.error("Failed to load employees", empErr); - } - - // fetch villas - const { data: villaData, error: villaErr } = await supabase - .from("vb_villas") - .select("id, villa_name") - .eq("villa_status", "Active") - .order("villa_name", { ascending: true }); - - if (!villaErr && villaData) { - villas = villaData; - } else { - console.error("Failed to load villas", villaErr); - } - - fetchTimeSheets(); // this still calls your table loader - }); - - onMount(() => { - fetchTimeSheets(); - }); - - // Initialize the first page - $: currentPage = 1; - - let showModal = false; - let isEditing = false; - let currentEditingId: string | null = null; - let newTsdata: Record = {}; - const excludedKeys = ["id"]; - const formColumns = columns.filter( - (col) => !excludedKeys.includes(col.key), - ); - - function openModal(tsdata?: Record) { - if (tsdata) { - // Edit mode - isEditing = true; - currentEditingId = tsdata.id; - - form = { - entered_by: - employees.find((e) => e.name === tsdata.staff_id)?.id || "", - work_description: tsdata.name, - type_of_work: tsdata.type_of_work, - category_of_work: tsdata.category_of_work, - villa_id: - villas.find((v) => v.villa_name === tsdata.villa_name)?.id || - "", - datetime_in: tsdata.date_in?.toISOString().slice(0, 16), - datetime_out: tsdata.date_out?.toISOString().slice(0, 16), - total_work_hour: 0, - remarks: tsdata.remarks, - approval: null, // leave null or bring in if editing allowed - }; - calculateTotalHours(); - } else { - // Add mode - isEditing = false; - currentEditingId = null; - form = { - entered_by: "", - work_description: "", - type_of_work: "Running", - category_of_work: "Cleaning", - villa_id: "", - datetime_in: "", - datetime_out: "", - total_work_hour: 0, - remarks: "", - approval: null, - }; - } - - showModal = true; - } - - type Employee = { - id: string; - name: string; - }; - - let employees: Employee[] = []; - let villas: Villa[] = []; - const typeOfWorkOptions = ["Running", "Periodic", "Irregular"]; - const categoryOptions = [ - "Cleaning", - "Gardening/Pool", - "Maintenance", - "Supervision", - "Guest Service", - "Administration", - "Non Billable", - ]; - let form = { - entered_by: "", - work_description: "", - type_of_work: "Running", - category_of_work: "Cleaning", - villa_id: "", - datetime_in: "", - datetime_out: "", - total_work_hour: 0, - remarks: "", - approval: null, // Default null - }; - + // Function to delete a timesheet async function deleteTimesheet(id: string) { if (confirm("Are you sure you want to delete this Timesheet?")) { const { error } = await supabase @@ -417,31 +409,7 @@ await fetchTimeSheets(); } } - - export let formErrors = writable<{ [key: string]: string }>({}); - - function validateForm(formData: FormData): boolean { - const errors: { [key: string]: string } = {}; - const requiredFields = [ - "work_description", - "type_of_work", - "category_of_work", - "villa_id", - "date_in", - "date_out", - "remarks", - ]; - - requiredFields.forEach((field) => { - if (!formData.get(field) || formData.get(field) === "") { - errors[field] = `${field.replace(/_/g, " ")} is required.`; - } - }); - - formErrors.set(errors); - return Object.keys(errors).length === 0; - } - + // Function to update the approval status of a timesheet async function updateApprovalStatus( id: string, status: string, @@ -464,6 +432,7 @@ await fetchTimeSheets(); } } + // Function to submit the form data async function submitForm() { calculateTotalHours(); @@ -520,6 +489,9 @@ showModal = false; } } + + export let formErrors = writable<{ [key: string]: string }>({}); +
@@ -606,19 +578,18 @@ {/each} - + {#each paginatedRows as row} {#each columns as col} {#if col.key === "name"} {row[col.key]} {:else if col.key === "approval"} - + {:else if col.key === "villa_name"} - + {row[col.key] || "Unknown Villa"} {:else if col.key === "staff_id"}