perbaikan timesheet
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
type Timesheets = {
|
type Timesheets = {
|
||||||
id: number;
|
id: number;
|
||||||
entered_by: string;
|
entered_by: string;
|
||||||
|
entered_name?: string;
|
||||||
work_description: string;
|
work_description: string;
|
||||||
type_of_work: "Running" | "Periodic" | "Irregular";
|
type_of_work: "Running" | "Periodic" | "Irregular";
|
||||||
category_of_work:
|
category_of_work:
|
||||||
@@ -24,11 +25,13 @@
|
|||||||
remarks: string;
|
remarks: string;
|
||||||
approval: boolean;
|
approval: boolean;
|
||||||
approved_by?: string;
|
approved_by?: string;
|
||||||
|
approved_name?: string;
|
||||||
approved_date?: Date;
|
approved_date?: Date;
|
||||||
created_at?: Date;
|
created_at?: Date;
|
||||||
};
|
};
|
||||||
type TimesheetsJoined = Timesheets & {
|
type TimesheetsJoined = Timesheets & {
|
||||||
vb_employee: { id: string; employee_name: string } | null;
|
vb_employee: { id: string; employee_name: string } | null;
|
||||||
|
villa_name?: string;
|
||||||
};
|
};
|
||||||
type TimesheetDisplay = {
|
type TimesheetDisplay = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -138,13 +141,8 @@
|
|||||||
let currentSearchTerm: string | null = null;
|
let currentSearchTerm: string | null = null;
|
||||||
let dataVilla: Villa[] = [];
|
let dataVilla: Villa[] = [];
|
||||||
let allRows: TimesheetDisplay[] = [];
|
let allRows: TimesheetDisplay[] = [];
|
||||||
|
let totalItems = 0;
|
||||||
let rowsPerPage = 20;
|
let rowsPerPage = 20;
|
||||||
$: totalPages = Math.ceil(allRows.length / rowsPerPage);
|
|
||||||
$: paginatedRows = allRows.slice(
|
|
||||||
(currentPage - 1) * rowsPerPage,
|
|
||||||
currentPage * rowsPerPage,
|
|
||||||
);
|
|
||||||
$: currentPage = 1;
|
|
||||||
let showModal = false;
|
let showModal = false;
|
||||||
let isEditing = false;
|
let isEditing = false;
|
||||||
let currentEditingId: string | null = null;
|
let currentEditingId: string | null = null;
|
||||||
@@ -200,7 +198,14 @@
|
|||||||
console.error("Failed to load villas", villaErr);
|
console.error("Failed to load villas", villaErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchTimeSheets();
|
fetchTimeSheets(
|
||||||
|
currentVillaFilter,
|
||||||
|
currentSearchTerm,
|
||||||
|
sortColumn,
|
||||||
|
sortOrder,
|
||||||
|
0,
|
||||||
|
rowsPerPage,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
// Reactive variables for sorting
|
// Reactive variables for sorting
|
||||||
function toggleSort(column: string) {
|
function toggleSort(column: string) {
|
||||||
@@ -210,7 +215,14 @@
|
|||||||
sortColumn = column;
|
sortColumn = column;
|
||||||
sortOrder = "asc";
|
sortOrder = "asc";
|
||||||
}
|
}
|
||||||
fetchTimeSheets(); // re-fetch with new sort
|
fetchTimeSheets(
|
||||||
|
currentVillaFilter,
|
||||||
|
currentSearchTerm,
|
||||||
|
sortColumn,
|
||||||
|
sortOrder,
|
||||||
|
(currentPage - 1) * rowsPerPage,
|
||||||
|
rowsPerPage,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to calculate total work hours
|
// Function to calculate total work hours
|
||||||
@@ -227,7 +239,22 @@
|
|||||||
}
|
}
|
||||||
// Function to go to a specific page
|
// Function to go to a specific page
|
||||||
function goToPage(page: number) {
|
function goToPage(page: number) {
|
||||||
|
|
||||||
|
console.log("Going to page:", page);
|
||||||
|
|
||||||
if (page >= 1 && page <= totalPages) currentPage = page;
|
if (page >= 1 && page <= totalPages) currentPage = page;
|
||||||
|
|
||||||
|
// Re-fetch timesheets with the current filters and pagination
|
||||||
|
console.log("Fetching timesheets for page:", currentPage);
|
||||||
|
|
||||||
|
fetchTimeSheets(
|
||||||
|
currentVillaFilter,
|
||||||
|
currentSearchTerm,
|
||||||
|
sortColumn,
|
||||||
|
sortOrder,
|
||||||
|
(currentPage - 1) * rowsPerPage,
|
||||||
|
rowsPerPage,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// Function to open the modal for adding or editing a timesheet
|
// Function to open the modal for adding or editing a timesheet
|
||||||
function openModal(tsdata?: Record<string, any>) {
|
function openModal(tsdata?: Record<string, any>) {
|
||||||
@@ -272,108 +299,72 @@
|
|||||||
|
|
||||||
showModal = true;
|
showModal = true;
|
||||||
}
|
}
|
||||||
// Function to fetch timesheets with optional filters and sorting
|
|
||||||
|
// Function to fetch timesheets
|
||||||
async function fetchTimeSheets(
|
async function fetchTimeSheets(
|
||||||
villaIdFilter: string | null = null,
|
villaIdFilter: string | null = null,
|
||||||
searchTerm: string | null = null,
|
searchTerm: string | null = null,
|
||||||
|
sortColumn: string | null = "created_at",
|
||||||
|
sortOrder: "asc" | "desc" = "desc",
|
||||||
offset: number = 0,
|
offset: number = 0,
|
||||||
limit: number = 1000,
|
limit: number = 20,
|
||||||
) {
|
) {
|
||||||
let reportedBy: { label: string; value: string }[] = [];
|
|
||||||
const { data: staffData, error: staffError } = await supabase
|
|
||||||
.from("vb_employee")
|
|
||||||
.select("id, employee_name")
|
|
||||||
.eq("employee_status", "Active")
|
|
||||||
.order("employee_name", { ascending: true });
|
|
||||||
|
|
||||||
if (staffError) {
|
console.log("Fetching timesheets with filters:", {
|
||||||
console.error("Error fetching staff:", staffError);
|
villaIdFilter,
|
||||||
} else if (staffData) {
|
searchTerm,
|
||||||
reportedBy = staffData.map((s) => ({
|
sortColumn,
|
||||||
label: s.employee_name,
|
sortOrder,
|
||||||
value: s.id,
|
offset,
|
||||||
}));
|
limit,
|
||||||
}
|
|
||||||
|
|
||||||
let query = supabase
|
|
||||||
.from("vb_timesheet")
|
|
||||||
.select(`*`)
|
|
||||||
.order(sortColumn || "created_at", {
|
|
||||||
ascending: sortOrder === "asc",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const fromIndex = offset;
|
||||||
|
const toIndex = offset + limit - 1;
|
||||||
|
|
||||||
|
let query = supabase
|
||||||
|
.from("vb_timesheet_data")
|
||||||
|
.select(
|
||||||
|
'*',
|
||||||
|
{ count: "exact" },
|
||||||
|
)
|
||||||
|
.order(sortColumn || "created_at", {
|
||||||
|
ascending: sortOrder === "asc",
|
||||||
|
}).range(fromIndex, toIndex);
|
||||||
|
|
||||||
|
if (searchTerm) {
|
||||||
|
query = query.ilike("work_description", `%${searchTerm}%`)
|
||||||
|
}
|
||||||
|
|
||||||
if (villaIdFilter) {
|
if (villaIdFilter) {
|
||||||
const { data: villaMatch } = await supabase
|
query = query.eq("villa_name", villaIdFilter);
|
||||||
.from("vb_villas")
|
|
||||||
.select("id")
|
|
||||||
.eq("villa_name", villaIdFilter);
|
|
||||||
|
|
||||||
const matchedId = villaMatch?.[0]?.id;
|
|
||||||
if (matchedId) {
|
|
||||||
query = query.eq("villa_id", matchedId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset) {
|
// Jalankan query
|
||||||
query = query.range(offset, offset + limit - 1);
|
const { data, count, error } = await query;
|
||||||
}
|
|
||||||
if (limit) {
|
console.log("Fetched timesheets:", count);
|
||||||
query = query.limit(limit);
|
|
||||||
}
|
console.log("Fetched timesheets data:", data);
|
||||||
const { data: timesheet, error } = await query;
|
|
||||||
|
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error("Error fetching timesheets:", error);
|
console.error("Error fetching timesheets:", error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const loweredSearch = searchTerm?.toLowerCase();
|
allRows = data.map((tsdata: TimesheetsJoined) => {
|
||||||
let filteredTimesheet = timesheet;
|
|
||||||
if (loweredSearch) {
|
|
||||||
filteredTimesheet = timesheet.filter((ts) => {
|
|
||||||
const workDesc = ts.work_description?.toLowerCase() || "";
|
|
||||||
const staffName = reportedBy.find((s) => s.value === ts.entered_by)?.label?.toLowerCase() || "";
|
|
||||||
return (
|
|
||||||
workDesc.includes(loweredSearch) ||
|
|
||||||
staffName.includes(loweredSearch)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const villaIds = [...new Set(filteredTimesheet.map((i: Timesheets) => i.villa_id))];
|
|
||||||
let villas = [];
|
|
||||||
if (villaIds.length > 0) {
|
|
||||||
const { data: villasData, error: villaError } = await supabase
|
|
||||||
.from("vb_villas")
|
|
||||||
.select("*")
|
|
||||||
.in("id", villaIds);
|
|
||||||
|
|
||||||
if (villaError) {
|
|
||||||
console.error("Error fetching villas:", villaError);
|
|
||||||
} else {
|
|
||||||
villas = villasData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const { data: approvers, error: approverError } = await supabase
|
|
||||||
.from("vb_users")
|
|
||||||
.select("id, full_name");
|
|
||||||
|
|
||||||
if (approverError) {
|
|
||||||
console.error("Error fetching approvers:", approverError);
|
|
||||||
}
|
|
||||||
allRows = filteredTimesheet.map((tsdata: TimesheetsJoined) => {
|
|
||||||
const villa = villas.find((v) => v.id === tsdata.villa_id);
|
|
||||||
const approver = approvers?.find((u) => u.id === tsdata.approved_by);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: tsdata.id,
|
id: tsdata.id,
|
||||||
name: tsdata.work_description,
|
name: tsdata.work_description,
|
||||||
staff_id:
|
staff_id: tsdata.entered_name || "Unknown Staff",
|
||||||
reportedBy.find((s) => s.value === tsdata.entered_by)?.label || "Unknown",
|
|
||||||
date_in: new Date(tsdata.datetime_in),
|
date_in: new Date(tsdata.datetime_in),
|
||||||
date_out: new Date(tsdata.datetime_out),
|
date_out: new Date(tsdata.datetime_out),
|
||||||
type_of_work: tsdata.type_of_work,
|
type_of_work: tsdata.type_of_work,
|
||||||
category_of_work: tsdata.category_of_work,
|
category_of_work: tsdata.category_of_work,
|
||||||
villa_name: villa ? villa.villa_name : "Unknown Villa",
|
villa_name: tsdata.villa_name || "Unknown Villa",
|
||||||
approval:
|
approval:
|
||||||
tsdata.approval == null
|
tsdata.approval == null
|
||||||
? "PENDING"
|
? "PENDING"
|
||||||
@@ -382,19 +373,186 @@
|
|||||||
: "REJECTED",
|
: "REJECTED",
|
||||||
total_hours_work:
|
total_hours_work:
|
||||||
Math.abs(
|
Math.abs(
|
||||||
new Date(tsdata.datetime_out).getTime() - new Date(tsdata.datetime_in).getTime()
|
new Date(tsdata.datetime_out).getTime() -
|
||||||
|
new Date(tsdata.datetime_in).getTime(),
|
||||||
) / (1000 * 60 * 60),
|
) / (1000 * 60 * 60),
|
||||||
approved_by: approver?.full_name ?? "Not Approved",
|
approved_by: tsdata.approved_name || "Not Approved",
|
||||||
approved_date: tsdata.approved_date,
|
approved_date: tsdata.approved_date
|
||||||
remarks: tsdata.remarks,
|
? new Date(tsdata.approved_date)
|
||||||
// created_at: tsdata.created_at ? new Date(tsdata.created_at) : undefined,
|
: undefined,
|
||||||
|
remarks: tsdata.remarks || "No remarks",
|
||||||
|
created_at: tsdata.created_at
|
||||||
|
? new Date(tsdata.created_at)
|
||||||
|
: undefined,
|
||||||
} as TimesheetDisplay;
|
} as TimesheetDisplay;
|
||||||
});
|
});
|
||||||
currentPage = 1;
|
|
||||||
|
|
||||||
console.log("Fetched rows:", allRows);
|
totalItems = count || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: totalPages = Math.ceil(totalItems / rowsPerPage);
|
||||||
|
$: currentPage = 1;
|
||||||
|
|
||||||
|
function pageRange(totalPages: number, currentPage: number,): (number | string)[] {
|
||||||
|
const range: (number | string)[] = [];
|
||||||
|
const maxDisplay = 5;
|
||||||
|
|
||||||
|
if (totalPages <= maxDisplay + 2) {
|
||||||
|
for (let i = 1; i <= totalPages; i++) range.push(i);
|
||||||
|
} else {
|
||||||
|
const start = Math.max(2, currentPage - 2);
|
||||||
|
const end = Math.min(totalPages - 1, currentPage + 2);
|
||||||
|
|
||||||
|
range.push(1);
|
||||||
|
if (start > 2) range.push("...");
|
||||||
|
|
||||||
|
for (let i = start; i <= end; i++) {
|
||||||
|
range.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end < totalPages - 1) range.push("...");
|
||||||
|
range.push(totalPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
function changePage(page: number) {
|
||||||
|
if (page < 1 || page > totalPages || page === currentPage) return;
|
||||||
|
currentPage = page;
|
||||||
|
fetchTimeSheets(
|
||||||
|
currentVillaFilter,
|
||||||
|
currentSearchTerm,
|
||||||
|
sortColumn,
|
||||||
|
sortOrder,
|
||||||
|
(currentPage - 1) * rowsPerPage,
|
||||||
|
rowsPerPage,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Function to fetch timesheets with optional filters and sorting
|
||||||
|
// async function fetchTimeSheets(
|
||||||
|
// villaIdFilter: string | null = null,
|
||||||
|
// searchTerm: string | null = null,
|
||||||
|
// offset: number = 0,
|
||||||
|
// limit: number = 1000,
|
||||||
|
// ) {
|
||||||
|
// let reportedBy: { label: string; value: string }[] = [];
|
||||||
|
// const { data: staffData, error: staffError } = await supabase
|
||||||
|
// .from("vb_employee")
|
||||||
|
// .select("id, employee_name")
|
||||||
|
// .eq("employee_status", "Active")
|
||||||
|
// .order("employee_name", { ascending: true });
|
||||||
|
|
||||||
|
// if (staffError) {
|
||||||
|
// console.error("Error fetching staff:", staffError);
|
||||||
|
// } else if (staffData) {
|
||||||
|
// reportedBy = staffData.map((s) => ({
|
||||||
|
// label: s.employee_name,
|
||||||
|
// value: s.id,
|
||||||
|
// }));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let query = supabase
|
||||||
|
// .from("vb_timesheet")
|
||||||
|
// .select(`*`)
|
||||||
|
// .order(sortColumn || "created_at", {
|
||||||
|
// ascending: sortOrder === "asc",
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (villaIdFilter) {
|
||||||
|
// const { data: villaMatch } = await supabase
|
||||||
|
// .from("vb_villas")
|
||||||
|
// .select("id")
|
||||||
|
// .eq("villa_name", villaIdFilter);
|
||||||
|
|
||||||
|
// const matchedId = villaMatch?.[0]?.id;
|
||||||
|
// if (matchedId) {
|
||||||
|
// query = query.eq("villa_id", matchedId);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (offset) {
|
||||||
|
// query = query.range(offset, offset + limit - 1);
|
||||||
|
// }
|
||||||
|
// if (limit) {
|
||||||
|
// query = query.limit(limit);
|
||||||
|
// }
|
||||||
|
// const { data: timesheet, error } = await query;
|
||||||
|
// if (error) {
|
||||||
|
// console.error("Error fetching timesheets:", error);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const loweredSearch = searchTerm?.toLowerCase();
|
||||||
|
// let filteredTimesheet = timesheet;
|
||||||
|
// if (loweredSearch) {
|
||||||
|
// filteredTimesheet = timesheet.filter((ts) => {
|
||||||
|
// const workDesc = ts.work_description?.toLowerCase() || "";
|
||||||
|
// const staffName = reportedBy.find((s) => s.value === ts.entered_by)?.label?.toLowerCase() || "";
|
||||||
|
// return (
|
||||||
|
// workDesc.includes(loweredSearch) ||
|
||||||
|
// staffName.includes(loweredSearch)
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const villaIds = [...new Set(filteredTimesheet.map((i: Timesheets) => i.villa_id))];
|
||||||
|
// let villas = [];
|
||||||
|
// if (villaIds.length > 0) {
|
||||||
|
// const { data: villasData, error: villaError } = await supabase
|
||||||
|
// .from("vb_villas")
|
||||||
|
// .select("*")
|
||||||
|
// .in("id", villaIds);
|
||||||
|
|
||||||
|
// if (villaError) {
|
||||||
|
// console.error("Error fetching villas:", villaError);
|
||||||
|
// } else {
|
||||||
|
// villas = villasData;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// const { data: approvers, error: approverError } = await supabase
|
||||||
|
// .from("vb_users")
|
||||||
|
// .select("id, full_name");
|
||||||
|
|
||||||
|
// if (approverError) {
|
||||||
|
// console.error("Error fetching approvers:", approverError);
|
||||||
|
// }
|
||||||
|
// allRows = filteredTimesheet.map((tsdata: TimesheetsJoined) => {
|
||||||
|
// const villa = villas.find((v) => v.id === tsdata.villa_id);
|
||||||
|
// const approver = approvers?.find((u) => u.id === tsdata.approved_by);
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// id: tsdata.id,
|
||||||
|
// name: tsdata.work_description,
|
||||||
|
// staff_id:
|
||||||
|
// reportedBy.find((s) => s.value === tsdata.entered_by)?.label || "Unknown",
|
||||||
|
// 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:
|
||||||
|
// tsdata.approval == null
|
||||||
|
// ? "PENDING"
|
||||||
|
// : tsdata.approval
|
||||||
|
// ? "APPROVED"
|
||||||
|
// : "REJECTED",
|
||||||
|
// total_hours_work:
|
||||||
|
// Math.abs(
|
||||||
|
// new Date(tsdata.datetime_out).getTime() - new Date(tsdata.datetime_in).getTime()
|
||||||
|
// ) / (1000 * 60 * 60),
|
||||||
|
// approved_by: approver?.full_name ?? "Not Approved",
|
||||||
|
// approved_date: tsdata.approved_date,
|
||||||
|
// remarks: tsdata.remarks,
|
||||||
|
// // created_at: tsdata.created_at ? new Date(tsdata.created_at) : undefined,
|
||||||
|
// } as TimesheetDisplay;
|
||||||
|
// });
|
||||||
|
// currentPage = 1;
|
||||||
|
|
||||||
|
// console.log("Fetched rows:", allRows);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Function to delete a timesheet
|
// Function to delete a timesheet
|
||||||
@@ -446,6 +604,8 @@
|
|||||||
let error = null;
|
let error = null;
|
||||||
|
|
||||||
if (isEditing && currentEditingId) {
|
if (isEditing && currentEditingId) {
|
||||||
|
|
||||||
|
|
||||||
const { error: updateError } = await supabase
|
const { error: updateError } = await supabase
|
||||||
.from("vb_timesheet")
|
.from("vb_timesheet")
|
||||||
.update({
|
.update({
|
||||||
@@ -486,6 +646,7 @@
|
|||||||
total_work_hour: 0,
|
total_work_hour: 0,
|
||||||
remarks: "",
|
remarks: "",
|
||||||
approval: null,
|
approval: null,
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
await fetchTimeSheets();
|
await fetchTimeSheets();
|
||||||
showModal = false;
|
showModal = false;
|
||||||
@@ -584,7 +745,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-gray-200 bg-white text-align-top">
|
<tbody class="divide-y divide-gray-200 bg-white text-align-top">
|
||||||
{#each paginatedRows as row}
|
{#each allRows as row}
|
||||||
<tr class="hover:bg-gray-50 transition">
|
<tr class="hover:bg-gray-50 transition">
|
||||||
{#each columns as col}
|
{#each columns as col}
|
||||||
{#if col.key === "name"}
|
{#if col.key === "name"}
|
||||||
@@ -633,20 +794,8 @@
|
|||||||
{:else if col.key === "approved_date"}
|
{:else if col.key === "approved_date"}
|
||||||
<td class="px-4 py-2">
|
<td class="px-4 py-2">
|
||||||
{row[col.key] &&
|
{row[col.key] &&
|
||||||
!isNaN(Date.parse(String(row[col.key])))
|
!isNaN(new Date(row[col.key] as string | number | Date).getTime())
|
||||||
? new Date(
|
? new Date(row[col.key] as string | number | Date).toLocaleString()
|
||||||
row[
|
|
||||||
col.key as keyof TimesheetDisplay
|
|
||||||
] as string | number | Date,
|
|
||||||
).toLocaleDateString("en-GB", {
|
|
||||||
timeZone: "Asia/Singapore",
|
|
||||||
day: "2-digit",
|
|
||||||
month: "2-digit",
|
|
||||||
year: "numeric",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
hour12: false,
|
|
||||||
})
|
|
||||||
: "N/A"}
|
: "N/A"}
|
||||||
</td>
|
</td>
|
||||||
{:else if col.key === "total_hours_work"}
|
{:else if col.key === "total_hours_work"}
|
||||||
@@ -656,20 +805,7 @@
|
|||||||
{:else if col.key === "created_at"}
|
{:else if col.key === "created_at"}
|
||||||
<td class="px-4 py-2">
|
<td class="px-4 py-2">
|
||||||
{row[col.key] !== undefined
|
{row[col.key] !== undefined
|
||||||
? new Date(
|
? new Date(row[col.key] as string | number | Date).toLocaleString()
|
||||||
row[col.key]!,
|
|
||||||
).toLocaleDateString(
|
|
||||||
"en-GB",
|
|
||||||
{
|
|
||||||
timeZone: "Asia/Singapore",
|
|
||||||
day: "2-digit",
|
|
||||||
month: "2-digit",
|
|
||||||
year: "numeric",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
hour12: false,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: "N/A"}
|
: "N/A"}
|
||||||
</td>
|
</td>
|
||||||
{:else if col.key === "villa_name"}
|
{:else if col.key === "villa_name"}
|
||||||
@@ -704,18 +840,7 @@
|
|||||||
{:else if col.key === "date_in" || col.key === "date_out"}
|
{:else if col.key === "date_in" || col.key === "date_out"}
|
||||||
<td class="px-4 py-2">
|
<td class="px-4 py-2">
|
||||||
{row[col.key]
|
{row[col.key]
|
||||||
? new Date(row[col.key]).toLocaleString(
|
? new Date(row[col.key]).toLocaleString()
|
||||||
"en-GB",
|
|
||||||
{
|
|
||||||
timeZone: "Asia/Singapore",
|
|
||||||
day: "2-digit",
|
|
||||||
month: "2-digit",
|
|
||||||
year: "numeric",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
hour12: false,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: "N/A"}
|
: "N/A"}
|
||||||
</td>
|
</td>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -733,10 +858,39 @@
|
|||||||
<!-- Pagination controls -->
|
<!-- Pagination controls -->
|
||||||
<div class="flex justify-between items-center text-sm">
|
<div class="flex justify-between items-center text-sm">
|
||||||
<div>
|
<div>
|
||||||
Showing {(currentPage - 1) * rowsPerPage + 1}–{Math.min(currentPage * rowsPerPage, allRows.length)}
|
Showing {(currentPage - 1) * rowsPerPage + 1}–
|
||||||
of {allRows.length}
|
{Math.min(currentPage * rowsPerPage, totalItems)} of {totalItems} items
|
||||||
|
</div>
|
||||||
|
<div class="space-x-2">
|
||||||
|
<button
|
||||||
|
class="px-3 py-1 rounded border border-gray-300 bg-white hover:bg-gray-100 disabled:opacity-50"
|
||||||
|
on:click={() => goToPage(currentPage - 1)}
|
||||||
|
disabled={currentPage === 1}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
{#each pageRange(totalPages, currentPage) as page}
|
||||||
|
{#if page === "..."}
|
||||||
|
<span class="px-2">...</span>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
on:click={() => changePage(page as number)}
|
||||||
|
class="px-2 py-1 border rounded {page === currentPage
|
||||||
|
? 'bg-blue-600 text-white'
|
||||||
|
: ''}"
|
||||||
|
>
|
||||||
|
{page}
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
<button
|
||||||
|
class="px-3 py-1 rounded border border-gray-300 bg-white hover:bg-gray-100 disabled:opacity-50"
|
||||||
|
on:click={() => goToPage(currentPage + 1)}
|
||||||
|
disabled={currentPage === totalPages}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<Pagination {totalPages} {currentPage} {goToPage} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user