diff --git a/src/routes/backoffice/timesheets/+page.svelte b/src/routes/backoffice/timesheets/+page.svelte index a79842f..bc67bbb 100644 --- a/src/routes/backoffice/timesheets/+page.svelte +++ b/src/routes/backoffice/timesheets/+page.svelte @@ -4,57 +4,69 @@ import { writable } from "svelte/store"; type Timesheets = { - id: string; - name: string; - staff_id: string; - report_by: string; - date_in: Date; - date_out: Date; - type_of_work: string; - category_of_work: string; - approval: string; + id: number; + entered_by: string; + work_description: string; + type_of_work: "Running" | "Periodic" | "Irregular"; + category_of_work: + | "Cleaning" + | "Gardening/Pool" + | "Maintenance" + | "Supervision" + | "Guest Service" + | "Administration" + | "Non Billable"; villa_id: string; - villa_name: string; - approved_by: string; - approved_date: Date; - total_hours_work: number; + datetime_in: string; + datetime_out: string; + total_work_hour: number; remarks: string; - vacant: boolean; + approval: boolean; created_at?: Date; }; type TimesheetDisplay = { - id: string; - name: string; - report_by: string; - date_in: Date; - date_out: Date; - type_of_work: string; - category_of_work: string; - approval: string; - villa_name: string; - approved_by: string; - approved_date: Date; - total_hours_work: number; - remarks: string; - vacant: boolean; - created_at?: Date; - }; - - type TimesheetsInsert = { + id: number; name: string; staff_id: string; date_in: Date; date_out: Date; - type_of_work: string; - category_of_work: string; + type_of_work: "Running" | "Periodic" | "Irregular"; + category_of_work: + | "Cleaning" + | "Gardening/Pool" + | "Maintenance" + | "Supervision" + | "Guest Service" + | "Administration" + | "Non Billable"; + villa_name?: string; approval: string; - villa_id: string; - approved_by: string; - approved_date: Date; + approved_by?: string; + approved_date?: Date; total_hours_work: number; remarks: string; - vacant: boolean; + created_at?: Date; + }; + + type TimesheetsInsert = { + entered_by: string; + work_description: string; + type_of_work: "Running" | "Periodic" | "Irregular"; + category_of_work: + | "Cleaning" + | "Gardening/Pool" + | "Maintenance" + | "Supervision" + | "Guest Service" + | "Administration" + | "Non Billable"; + villa_id: string; + datetime_in: string; + datetime_out: string; + total_work_hour: number; + remarks: string; + approval: boolean; }; const categoryOfWork = [ @@ -97,7 +109,7 @@ } }); - let allRows: Timesheets[] = []; + let allRows: TimesheetDisplay[] = []; type columns = { key: string; @@ -106,7 +118,7 @@ const columns: columns[] = [ { key: "name", title: "Name" }, - { key: "report_by", title: "Staff Report" }, + { key: "staff_id", title: "Staff Report" }, { key: "date_in", title: "Date In" }, { key: "date_out", title: "Date Out" }, { key: "type_of_work", title: "Type of Work" }, @@ -131,7 +143,7 @@ limit: number = 10, ) { let query = supabase - .from("vb_timesheets") + .from("vb_timesheet") .select("*", { count: "exact" }) .order(sortColumn || "created_at", { ascending: sortOrder === "asc", @@ -140,7 +152,7 @@ query = query.eq("category_of_work", filter); } if (searchTerm) { - query = query.ilike("name", `%${searchTerm}%`); + query = query.ilike("work_description", `%${searchTerm}%`); } if (offset) { query = query.range(offset, offset + limit - 1); @@ -153,6 +165,7 @@ console.error("Error fetching timesheets:", error); return; } + // Ambil semua villa_id unik dari issues const villaIds = [ ...new Set(timesheet.map((i: Timesheets) => i.villa_id)), @@ -169,13 +182,34 @@ } // Gabungkan data villa ke dalam setiap issue - allRows = timesheet.map((timesheet: Timesheets) => ({ - ...timesheet, - villa_name: - villas.find((v) => v.id === timesheet.villa_id).name || null, - approval: timesheet.approval || "", - report_by: timesheet.staff_id || "Unknown", - })); + allRows = timesheet.map((issue: Timesheets) => { + const villa = villas.find((v) => v.id === issue.villa_id); + + return { + id: issue.id, + name: issue.work_description, // Map work_description to name + staff_id: issue.entered_by, // Map entered_by to staff_id + date_in: new Date(issue.datetime_in), + date_out: new Date(issue.datetime_out), + type_of_work: issue.type_of_work, + category_of_work: issue.category_of_work, + villa_name: villa ? villa.name : "Unknown Villa", + approval: issue.approval ? "APPROVED" : "PENDING", // or map as needed + approved_by: undefined, // Set as needed + approved_date: undefined, // Set as needed + total_hours_work: + Math.abs( + new Date(issue.datetime_out).getTime() - + new Date(issue.datetime_in).getTime(), + ) / + (1000 * 60 * 60), // Convert milliseconds to hours + remarks: issue.remarks, + created_at: issue.created_at + ? new Date(issue.created_at) + : undefined, + } as TimesheetDisplay; + }); + // Sort the rows based on the sortColumn and sortOrder } let currentPage = 1; let rowsPerPage = 5; @@ -200,16 +234,7 @@ let isEditing = false; let currentEditingId: string | null = null; let newIssue: Record = {}; - const excludedKeys = [ - "id", - "created_at", - "approval", - "approved_by", - "approved_date", - "villa_name", - "report_by", - "actions", - ]; + const excludedKeys = ["id"]; const formColumns = columns.filter( (col) => !excludedKeys.includes(col.key), ); @@ -251,33 +276,33 @@ } } else { const TimesheetsInsert: TimesheetsInsert = { - name: formData.get("name") as string, - staff_id: formData.get("staff_id") as string, - date_in: new Date(formData.get("date_in") as string), - date_out: new Date(formData.get("date_out") as string), - type_of_work: formData.get("type_of_work") as string, - category_of_work: formData.get("category_of_work") as string, - approval: formData.get("approval") as string, + entered_by: formData.get("entered_by") as string, + work_description: formData.get("work_description") as string, + type_of_work: formData.get("type_of_work") as + | "Running" + | "Periodic" + | "Irregular", + category_of_work: formData.get("category_of_work") as + | "Cleaning" + | "Gardening/Pool" + | "Maintenance" + | "Supervision" + | "Guest Service" + | "Administration" + | "Non Billable", villa_id: formData.get("villa_id") as string, - approved_by: formData.get("approved_by") as string, - approved_date: new Date( - formData.get("approved_date") as string, + datetime_in: formData.get("date_in") as string, + datetime_out: formData.get("date_out") as string, + total_work_hour: Math.abs( + new Date(formData.get("date_out") as string).getTime() - + new Date(formData.get("date_in") as string).getTime(), ), - //calculate total_hours_work - total_hours_work: - Math.abs( - new Date(formData.get("date_out") as string).getTime() - - new Date( - formData.get("date_in") as string, - ).getTime(), - ) / - (1000 * 60 * 60), // Convert milliseconds to hours remarks: formData.get("remarks") as string, - vacant: formData.get("vacant") === "true", + approval: false, // Default to false for new entries }; const { error } = await supabase - .from("vb_timesheets") + .from("vb_timesheet") .insert([TimesheetsInsert]); if (error) { console.error("Error adding issue:", error); @@ -291,7 +316,7 @@ async function deleteTimesheet(id: string) { if (confirm("Are you sure you want to delete this issue?")) { const { error } = await supabase - .from("vb_timesheets") + .from("vb_timesheet") .delete() .eq("id", id); if (error) { @@ -307,13 +332,13 @@ function validateForm(formData: FormData): boolean { const errors: { [key: string]: string } = {}; const requiredFields = [ - "name", + "work_description", "type_of_work", - "villa_id", - "date_out", - "reported_by", "category_of_work", + "villa_id", "date_in", + "date_out", + "remarks", ]; requiredFields.forEach((field) => { @@ -335,7 +360,7 @@ status: string, ): Promise { const { error } = await supabase - .from("vb_timesheets") + .from("vb_timesheet") .update({ approval: status }) .eq("id", id); @@ -430,6 +455,65 @@ > {row[col.key]} + {:else if col.key === "approval"} + + + {row[col.key]} + + {#if row.approval === "PENDING"} + + {/if} + + {:else if col.key === "approved_by"} + + {row[col.key] || "Not Approved"} + + {:else if col.key === "approved_date"} + + {row[col.key] !== undefined + ? new Date( + row[col.key]!, + ).toLocaleDateString() + : "N/A"} + + {:else if col.key === "total_hours_work"} + + {row[col.key].toFixed(2)} hours + + {:else if col.key === "created_at"} + + {row[col.key] !== undefined + ? new Date( + row[col.key]!, + ).toLocaleDateString() + : "N/A"} + + {:else if col.key === "villa_name"} + + {row[col.key] || "Unknown Villa"} + + {:else if col.key === "staff_id"} + + {row[col.key] || "Unknown Staff"} + + {:else if col.key === "remarks"} + + {row[col.key] || "No remarks"} + {:else if col.key === "actions"} - {:else if col.key === "approval"} - {#if row[col.key as keyof Timesheets] === "APPROVED"} - - ✅ Approved - - {:else if row[col.key as keyof Timesheets] === "PENDING"} - - ⏳ Pending - - {:else if row[col.key as keyof Timesheets] === "REJECTED"} - - ❌ Rejected - - {:else} - - - - - {/if} - {:else if col.key === "vacant"} - - {#if row[col.key as keyof Timesheets]} - ✅ Vacant - {:else} - ❌ Not Vacant - {/if} - {:else} - {row[col.key as keyof Timesheets]} + {row[col.key as keyof TimesheetDisplay]} {/if} {/each} @@ -586,78 +594,80 @@ {#each formColumns as col} {#if col.key === "name"} -
- +
+ - {#if $formErrors.name} -

- {$formErrors.name} + {#if $formErrors[col.key]} +

+ {$formErrors[col.key]}

{/if}
- {:else if col.key === "vacant"} -
- - -
- {:else if col.key === "reported_by"} -
- - + {#if $formErrors[col.key]} +

+ {$formErrors[col.key]} +

+ {/if} +
+ {:else if col.key === "type_of_work" || col.key === "category_of_work"} +
+ + - {#if $formErrors.reported_by} -

- {$formErrors.reported_by} + {#if $formErrors[col.key]} +

+ {$formErrors[col.key]}

{/if}
- {:else if col.key === "villa_name"} -
- + {:else if col.key === "villa_id"} +
+ - {#if $formErrors.villa_name} -

- {$formErrors.villa_name} + {#if $formErrors[col.key]} +

+ {$formErrors[col.key]}

{/if}
- {:else if col.key === "remarks"} -
- - - {#if $formErrors.remarks} -

- {$formErrors.remarks} -

- {/if} -
- {:else if col.key === "date_in"} -
- + {:else if col.key === "date_in" || col.key === "date_out"} +
+ - {#if $formErrors.date_in} -

- {$formErrors.date_in} -

- {/if} -
- {:else if col.key === "date_out"} -
- - - {#if $formErrors.date_out} -

- {$formErrors.date_out} -

- {/if} -
- {:else if col.key === "type_of_work"} -
- - - {#if $formErrors.type_of_work} -

- {$formErrors.type_of_work} -

- {/if} -
- {:else if col.key === "category_of_work"} -
- - -
- {#if $formErrors.category_of_work} -

- {$formErrors.category_of_work} -

- {/if} - {:else} -
- - + {#if $formErrors[col.key]} +

+ {$formErrors[col.key]} +

+ {/if}
+ {:else if col.key === "entered_by"} +
+ + + {#if $formErrors[col.key]} +

+ {$formErrors[col.key]} +

+ {/if} +
+ {:else} + {/if} {/each} +
+ + + {#if $formErrors.total_work_hour} +

+ {$formErrors.total_work_hour} +

+ {/if} +