fix search bar
This commit is contained in:
@@ -88,6 +88,8 @@
|
||||
];
|
||||
|
||||
let currentUserId: string | null = null;
|
||||
let currentVillaFilter: string | null = null;
|
||||
let currentSearchTerm: string | null = null;
|
||||
|
||||
onMount(async () => {
|
||||
const {
|
||||
@@ -153,7 +155,7 @@
|
||||
}
|
||||
|
||||
async function fetchTimeSheets(
|
||||
filter: string | null = null,
|
||||
villaNameFilter: string | null = null,
|
||||
searchTerm: string | null = null,
|
||||
sortColumn: string | null = "created_at",
|
||||
sortOrder: "asc" | "desc" = "desc",
|
||||
@@ -165,9 +167,17 @@
|
||||
.select("*", { count: "exact" })
|
||||
.order(sortColumn || "created_at", {
|
||||
ascending: sortOrder === "asc",
|
||||
});
|
||||
if (filter) {
|
||||
query = query.eq("category_of_work", filter);
|
||||
});
|
||||
if (villaNameFilter) {
|
||||
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) {
|
||||
query = query.ilike("work_description", `%${searchTerm}%`);
|
||||
@@ -184,7 +194,6 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Ambil semua villa_id unik dari issues
|
||||
const villaIds = [
|
||||
...new Set(timesheet.map((i: Timesheets) => i.villa_id)),
|
||||
];
|
||||
@@ -211,7 +220,8 @@
|
||||
const { data: staffData, error: staffError } = await supabase
|
||||
.from("vb_employee")
|
||||
.select("id, employee_name")
|
||||
.eq("employee_status", "Active");
|
||||
.eq("employee_status", "Active")
|
||||
.order("employee_name", { ascending: true });
|
||||
if (staffError) {
|
||||
console.error("Error fetching staff:", staffError);
|
||||
} else if (staffData) {
|
||||
@@ -221,39 +231,38 @@
|
||||
}));
|
||||
}
|
||||
|
||||
// Gabungkan data villa ke dalam setiap issue
|
||||
allRows = timesheet.map((issue: Timesheets) => {
|
||||
const villa = villas.find((v) => v.id === issue.villa_id);
|
||||
allRows = timesheet.map((tsdata: Timesheets) => {
|
||||
const villa = villas.find((v) => v.id === tsdata.villa_id);
|
||||
// Map entered_by to staff_id
|
||||
const staff = reportedBy.find((s) => s.value === issue.entered_by);
|
||||
const approver = approvers?.find((u) => u.id === issue.approved_by);
|
||||
const staff = reportedBy.find((s) => s.value === tsdata.entered_by);
|
||||
const approver = approvers?.find((u) => u.id === tsdata.approved_by);
|
||||
|
||||
return {
|
||||
id: issue.id,
|
||||
name: issue.work_description, // Map work_description to name
|
||||
id: tsdata.id,
|
||||
name: tsdata.work_description, // Map work_description to name
|
||||
staff_id: staff?.label, // 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,
|
||||
date_in: new Date(tsdata.datetime_in),
|
||||
date_out: new Date(tsdata.datetime_out),
|
||||
type_of_work: tsdata.type_of_work,
|
||||
category_of_work: tsdata.category_of_work,
|
||||
villa_name: villa ? villa.villa_name : "Unknown Villa",
|
||||
approval:
|
||||
issue.approval == null
|
||||
tsdata.approval == null
|
||||
? "PENDING"
|
||||
: issue.approval
|
||||
: tsdata.approval
|
||||
? "APPROVED"
|
||||
: "REJECTED", // or map as needed
|
||||
total_hours_work:
|
||||
Math.abs(
|
||||
new Date(issue.datetime_out).getTime() -
|
||||
new Date(issue.datetime_in).getTime(),
|
||||
new Date(tsdata.datetime_out).getTime() -
|
||||
new Date(tsdata.datetime_in).getTime(),
|
||||
) /
|
||||
(1000 * 60 * 60), // Convert milliseconds to hours
|
||||
approved_by: approver?.full_name ?? "Not Approved",
|
||||
approved_date: issue.approved_date,
|
||||
remarks: issue.remarks,
|
||||
created_at: issue.created_at
|
||||
? new Date(issue.created_at)
|
||||
approved_date: tsdata.approved_date,
|
||||
remarks: tsdata.remarks,
|
||||
created_at: tsdata.created_at
|
||||
? new Date(tsdata.created_at)
|
||||
: undefined,
|
||||
} as TimesheetDisplay;
|
||||
});
|
||||
@@ -282,7 +291,8 @@
|
||||
const { data: empData, error: empErr } = await supabase
|
||||
.from("vb_employee")
|
||||
.select("id, employee_name")
|
||||
.eq("employee_status", "Active");
|
||||
.eq("employee_status", "Active")
|
||||
.order("employee_name", { ascending: true });
|
||||
|
||||
if (!empErr && empData) {
|
||||
employees = empData.map((e) => ({
|
||||
@@ -297,7 +307,8 @@
|
||||
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 (!villaErr && villaData) {
|
||||
villas = villaData;
|
||||
@@ -318,31 +329,31 @@
|
||||
let showModal = false;
|
||||
let isEditing = false;
|
||||
let currentEditingId: string | null = null;
|
||||
let newIssue: Record<string, any> = {};
|
||||
let newTsdata: Record<string, any> = {};
|
||||
const excludedKeys = ["id"];
|
||||
const formColumns = columns.filter(
|
||||
(col) => !excludedKeys.includes(col.key),
|
||||
);
|
||||
|
||||
function openModal(issue?: Record<string, any>) {
|
||||
if (issue) {
|
||||
function openModal(tsdata?: Record<string, any>) {
|
||||
if (tsdata) {
|
||||
// Edit mode
|
||||
isEditing = true;
|
||||
currentEditingId = issue.id;
|
||||
currentEditingId = tsdata.id;
|
||||
|
||||
form = {
|
||||
entered_by:
|
||||
employees.find((e) => e.name === issue.staff_id)?.id || "",
|
||||
work_description: issue.name,
|
||||
type_of_work: issue.type_of_work,
|
||||
category_of_work: issue.category_of_work,
|
||||
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 === issue.villa_name)?.id ||
|
||||
villas.find((v) => v.villa_name === tsdata.villa_name)?.id ||
|
||||
"",
|
||||
datetime_in: issue.date_in?.toISOString().slice(0, 16),
|
||||
datetime_out: issue.date_out?.toISOString().slice(0, 16),
|
||||
datetime_in: tsdata.date_in?.toISOString().slice(0, 16),
|
||||
datetime_out: tsdata.date_out?.toISOString().slice(0, 16),
|
||||
total_work_hour: 0,
|
||||
remarks: issue.remarks,
|
||||
remarks: tsdata.remarks,
|
||||
approval: null, // leave null or bring in if editing allowed
|
||||
};
|
||||
calculateTotalHours();
|
||||
@@ -397,72 +408,14 @@
|
||||
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) {
|
||||
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
|
||||
.from("vb_timesheet")
|
||||
.delete()
|
||||
.eq("id", id);
|
||||
if (error) {
|
||||
console.error("Error deleting issue:", error);
|
||||
console.error("Error deleting Timesheet:", error);
|
||||
return;
|
||||
}
|
||||
await fetchTimeSheets();
|
||||
@@ -493,10 +446,6 @@
|
||||
return Object.keys(errors).length === 0;
|
||||
}
|
||||
|
||||
function errorClass(field: string): string {
|
||||
return $formErrors[field] ? "border-red-500" : "border";
|
||||
}
|
||||
|
||||
async function updateApprovalStatus(
|
||||
id: string,
|
||||
status: string,
|
||||
@@ -592,30 +541,42 @@
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
id="search-input"
|
||||
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"
|
||||
on:input={(e) => {
|
||||
const searchTerm = (
|
||||
e.target as HTMLInputElement
|
||||
).value.toLowerCase();
|
||||
fetchTimeSheets(null, searchTerm, "created_at", "desc");
|
||||
currentSearchTerm = (e.target as HTMLInputElement).value.toLowerCase();
|
||||
fetchTimeSheets(currentVillaFilter, currentSearchTerm);
|
||||
}}
|
||||
/>
|
||||
<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"
|
||||
on:change={(e) => {
|
||||
const filter = (e.target as HTMLSelectElement).value;
|
||||
fetchTimeSheets(filter, null, null, "desc");
|
||||
currentVillaFilter = (e.target as HTMLSelectElement).value || null;
|
||||
fetchTimeSheets(currentVillaFilter, currentSearchTerm);
|
||||
}}
|
||||
>
|
||||
<option value="">All Issues</option>
|
||||
<option value="PROJECT">Project Issues</option>
|
||||
<option value="PURCHASE_ORDER">Purchase Order Issues</option>
|
||||
<option value="">All Villa</option>
|
||||
{#each dataVilla as villa}
|
||||
<option value={villa.villa_name}>{villa.villa_name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<button
|
||||
class="bg-gray-200 text-gray-700 px-4 py-2 rounded-xl hover:bg-gray-300 text-sm transition"
|
||||
on:click={() =>
|
||||
fetchTimeSheets(null, null, "created_at", "desc", 0, 10)}
|
||||
on:click={() =>{
|
||||
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
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user