fix search bar
This commit is contained in:
@@ -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",
|
||||||
@@ -166,8 +168,16 @@
|
|||||||
.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>
|
||||||
|
|||||||
Reference in New Issue
Block a user