fix search bar

This commit is contained in:
2025-06-24 22:35:34 +08:00
parent c3763c7292
commit d9284becc1

View File

@@ -88,6 +88,8 @@
]; ];
let currentUserId: string | null = null; let currentUserId: string | null = null;
let currentVillaFilter: string | null = null;
let currentSearchTerm: string | null = null;
onMount(async () => { onMount(async () => {
const { const {
@@ -153,7 +155,7 @@
} }
async function fetchTimeSheets( async function fetchTimeSheets(
filter: string | null = null, villaNameFilter: string | null = null,
searchTerm: string | null = null, searchTerm: string | null = null,
sortColumn: string | null = "created_at", sortColumn: string | null = "created_at",
sortOrder: "asc" | "desc" = "desc", sortOrder: "asc" | "desc" = "desc",
@@ -165,9 +167,17 @@
.select("*", { count: "exact" }) .select("*", { count: "exact" })
.order(sortColumn || "created_at", { .order(sortColumn || "created_at", {
ascending: sortOrder === "asc", ascending: sortOrder === "asc",
}); });
if (filter) { if (villaNameFilter) {
query = query.eq("category_of_work", filter); const { data: villaMatch } = await supabase
.from("vb_villas")
.select("id")
.eq("villa_name", villaNameFilter);
const matchedId = villaMatch?.[0]?.id;
if (matchedId) {
query = query.eq("villa_id", matchedId);
}
} }
if (searchTerm) { if (searchTerm) {
query = query.ilike("work_description", `%${searchTerm}%`); query = query.ilike("work_description", `%${searchTerm}%`);
@@ -184,7 +194,6 @@
return; return;
} }
// Ambil semua villa_id unik dari issues
const villaIds = [ const villaIds = [
...new Set(timesheet.map((i: Timesheets) => i.villa_id)), ...new Set(timesheet.map((i: Timesheets) => i.villa_id)),
]; ];
@@ -211,7 +220,8 @@
const { data: staffData, error: staffError } = await supabase const { data: staffData, error: staffError } = await supabase
.from("vb_employee") .from("vb_employee")
.select("id, employee_name") .select("id, employee_name")
.eq("employee_status", "Active"); .eq("employee_status", "Active")
.order("employee_name", { ascending: true });
if (staffError) { if (staffError) {
console.error("Error fetching staff:", staffError); console.error("Error fetching staff:", staffError);
} else if (staffData) { } else if (staffData) {
@@ -221,39 +231,38 @@
})); }));
} }
// Gabungkan data villa ke dalam setiap issue allRows = timesheet.map((tsdata: Timesheets) => {
allRows = timesheet.map((issue: Timesheets) => { const villa = villas.find((v) => v.id === tsdata.villa_id);
const villa = villas.find((v) => v.id === issue.villa_id);
// Map entered_by to staff_id // Map entered_by to staff_id
const staff = reportedBy.find((s) => s.value === issue.entered_by); const staff = reportedBy.find((s) => s.value === tsdata.entered_by);
const approver = approvers?.find((u) => u.id === issue.approved_by); const approver = approvers?.find((u) => u.id === tsdata.approved_by);
return { return {
id: issue.id, id: tsdata.id,
name: issue.work_description, // Map work_description to name name: tsdata.work_description, // Map work_description to name
staff_id: staff?.label, // Map entered_by to staff_id staff_id: staff?.label, // Map entered_by to staff_id
date_in: new Date(issue.datetime_in), date_in: new Date(tsdata.datetime_in),
date_out: new Date(issue.datetime_out), date_out: new Date(tsdata.datetime_out),
type_of_work: issue.type_of_work, type_of_work: tsdata.type_of_work,
category_of_work: issue.category_of_work, category_of_work: tsdata.category_of_work,
villa_name: villa ? villa.villa_name : "Unknown Villa", villa_name: villa ? villa.villa_name : "Unknown Villa",
approval: approval:
issue.approval == null tsdata.approval == null
? "PENDING" ? "PENDING"
: issue.approval : tsdata.approval
? "APPROVED" ? "APPROVED"
: "REJECTED", // or map as needed : "REJECTED", // or map as needed
total_hours_work: total_hours_work:
Math.abs( Math.abs(
new Date(issue.datetime_out).getTime() - new Date(tsdata.datetime_out).getTime() -
new Date(issue.datetime_in).getTime(), new Date(tsdata.datetime_in).getTime(),
) / ) /
(1000 * 60 * 60), // Convert milliseconds to hours (1000 * 60 * 60), // Convert milliseconds to hours
approved_by: approver?.full_name ?? "Not Approved", approved_by: approver?.full_name ?? "Not Approved",
approved_date: issue.approved_date, approved_date: tsdata.approved_date,
remarks: issue.remarks, remarks: tsdata.remarks,
created_at: issue.created_at created_at: tsdata.created_at
? new Date(issue.created_at) ? new Date(tsdata.created_at)
: undefined, : undefined,
} as TimesheetDisplay; } as TimesheetDisplay;
}); });
@@ -282,7 +291,8 @@
const { data: empData, error: empErr } = await supabase const { data: empData, error: empErr } = await supabase
.from("vb_employee") .from("vb_employee")
.select("id, employee_name") .select("id, employee_name")
.eq("employee_status", "Active"); .eq("employee_status", "Active")
.order("employee_name", { ascending: true });
if (!empErr && empData) { if (!empErr && empData) {
employees = empData.map((e) => ({ employees = empData.map((e) => ({
@@ -297,7 +307,8 @@
const { data: villaData, error: villaErr } = await supabase const { data: villaData, error: villaErr } = await supabase
.from("vb_villas") .from("vb_villas")
.select("id, villa_name") .select("id, villa_name")
.eq("villa_status", "Active"); .eq("villa_status", "Active")
.order("villa_name", { ascending: true });
if (!villaErr && villaData) { if (!villaErr && villaData) {
villas = villaData; villas = villaData;
@@ -318,31 +329,31 @@
let showModal = false; let showModal = false;
let isEditing = false; let isEditing = false;
let currentEditingId: string | null = null; let currentEditingId: string | null = null;
let newIssue: Record<string, any> = {}; let newTsdata: Record<string, any> = {};
const excludedKeys = ["id"]; const excludedKeys = ["id"];
const formColumns = columns.filter( const formColumns = columns.filter(
(col) => !excludedKeys.includes(col.key), (col) => !excludedKeys.includes(col.key),
); );
function openModal(issue?: Record<string, any>) { function openModal(tsdata?: Record<string, any>) {
if (issue) { if (tsdata) {
// Edit mode // Edit mode
isEditing = true; isEditing = true;
currentEditingId = issue.id; currentEditingId = tsdata.id;
form = { form = {
entered_by: entered_by:
employees.find((e) => e.name === issue.staff_id)?.id || "", employees.find((e) => e.name === tsdata.staff_id)?.id || "",
work_description: issue.name, work_description: tsdata.name,
type_of_work: issue.type_of_work, type_of_work: tsdata.type_of_work,
category_of_work: issue.category_of_work, category_of_work: tsdata.category_of_work,
villa_id: villa_id:
villas.find((v) => v.villa_name === issue.villa_name)?.id || villas.find((v) => v.villa_name === tsdata.villa_name)?.id ||
"", "",
datetime_in: issue.date_in?.toISOString().slice(0, 16), datetime_in: tsdata.date_in?.toISOString().slice(0, 16),
datetime_out: issue.date_out?.toISOString().slice(0, 16), datetime_out: tsdata.date_out?.toISOString().slice(0, 16),
total_work_hour: 0, total_work_hour: 0,
remarks: issue.remarks, remarks: tsdata.remarks,
approval: null, // leave null or bring in if editing allowed approval: null, // leave null or bring in if editing allowed
}; };
calculateTotalHours(); calculateTotalHours();
@@ -397,72 +408,14 @@
approval: null, // Default null approval: null, // Default null
}; };
async function saveIssue(event: Event) {
event.preventDefault();
const formData = new FormData(event.target as HTMLFormElement);
// Validate form data
if (!validateForm(formData)) {
console.error("Form validation failed");
return;
}
if (isEditing && currentEditingId) {
const { error } = await supabase
.from("vb_issues")
.update(newIssue)
.eq("id", currentEditingId);
if (error) {
alert("Error updating issue: " + error.message);
console.error("Error updating issue:", error);
return;
}
} else {
const TimesheetsInsert: TimesheetsInsert = {
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,
datetime_in: formData.get("date_in") as string,
datetime_out: formData.get("date_out") as string,
total_work_hour: newIssue.total_work_hour ?? 0,
remarks: formData.get("remarks") as string,
approval: null,
};
const { error } = await supabase
.from("vb_timesheet")
.insert([TimesheetsInsert]);
if (error) {
console.error("Error adding issue:", error);
return;
}
}
await fetchTimeSheets();
showModal = false;
}
async function deleteTimesheet(id: string) { async function deleteTimesheet(id: string) {
if (confirm("Are you sure you want to delete this issue?")) { if (confirm("Are you sure you want to delete this Timesheet?")) {
const { error } = await supabase const { error } = await supabase
.from("vb_timesheet") .from("vb_timesheet")
.delete() .delete()
.eq("id", id); .eq("id", id);
if (error) { if (error) {
console.error("Error deleting issue:", error); console.error("Error deleting Timesheet:", error);
return; return;
} }
await fetchTimeSheets(); await fetchTimeSheets();
@@ -493,10 +446,6 @@
return Object.keys(errors).length === 0; return Object.keys(errors).length === 0;
} }
function errorClass(field: string): string {
return $formErrors[field] ? "border-red-500" : "border";
}
async function updateApprovalStatus( async function updateApprovalStatus(
id: string, id: string,
status: string, status: string,
@@ -592,30 +541,42 @@
<div class="flex flex-col sm:flex-row sm:items-center gap-2"> <div class="flex flex-col sm:flex-row sm:items-center gap-2">
<input <input
type="text" type="text"
id="search-input"
placeholder="🔍 Search by name..." placeholder="🔍 Search by name..."
class="border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:outline-none px-4 py-2 rounded-xl text-sm w-64 transition" class="border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:outline-none px-4 py-2 rounded-xl text-sm w-64 transition"
on:input={(e) => { on:input={(e) => {
const searchTerm = ( currentSearchTerm = (e.target as HTMLInputElement).value.toLowerCase();
e.target as HTMLInputElement fetchTimeSheets(currentVillaFilter, currentSearchTerm);
).value.toLowerCase();
fetchTimeSheets(null, searchTerm, "created_at", "desc");
}} }}
/> />
<select <select
id="villa-select"
class="border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:outline-none px-4 py-2 rounded-xl text-sm w-48 transition" class="border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:outline-none px-4 py-2 rounded-xl text-sm w-48 transition"
on:change={(e) => { on:change={(e) => {
const filter = (e.target as HTMLSelectElement).value; currentVillaFilter = (e.target as HTMLSelectElement).value || null;
fetchTimeSheets(filter, null, null, "desc"); fetchTimeSheets(currentVillaFilter, currentSearchTerm);
}} }}
> >
<option value="">All Issues</option> <option value="">All Villa</option>
<option value="PROJECT">Project Issues</option> {#each dataVilla as villa}
<option value="PURCHASE_ORDER">Purchase Order Issues</option> <option value={villa.villa_name}>{villa.villa_name}</option>
{/each}
</select> </select>
<button <button
class="bg-gray-200 text-gray-700 px-4 py-2 rounded-xl hover:bg-gray-300 text-sm transition" class="bg-gray-200 text-gray-700 px-4 py-2 rounded-xl hover:bg-gray-300 text-sm transition"
on:click={() => on:click={() =>{
fetchTimeSheets(null, null, "created_at", "desc", 0, 10)} currentVillaFilter = null;
currentSearchTerm = null;
// Optional: reset UI elements if you use bind:value
const searchInput = document.querySelector('#search-input') as HTMLInputElement;
if (searchInput) searchInput.value = "";
const villaSelect = document.querySelector('#villa-select') as HTMLSelectElement;
if (villaSelect) villaSelect.value = "";
fetchTimeSheets(null, null);
}}
> >
🔄 Reset 🔄 Reset
</button> </button>