add Sort Function
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
export let value: number = 0; // The raw number
|
||||
export let label: string = ""; // Field label
|
||||
export let onInput: (() => void) | null = null; // Optional extra handler
|
||||
|
||||
export let disabled: boolean = false;
|
||||
export let className: string = "";
|
||||
let formatted = "";
|
||||
|
||||
// Format whenever value changes
|
||||
@@ -26,7 +27,8 @@
|
||||
<input
|
||||
type="text"
|
||||
bind:value={formatted}
|
||||
placeholder="Rp 0"
|
||||
placeholder="Rp 0"
|
||||
class="w-full border p-2 rounded ${className}"
|
||||
on:input={handleInput}
|
||||
disabled={disabled}
|
||||
/>
|
||||
@@ -270,6 +270,8 @@
|
||||
let dataUser: User[] = [];
|
||||
let projectIssueMap: Set<string> = new Set();
|
||||
let purchaseOrderMap: Set<string> = new Set();
|
||||
let sortColumn: string | null = "created_at"; // or any default column
|
||||
let sortOrder: "asc" | "desc" = "desc";
|
||||
let poItems: POItem[] = [];
|
||||
let newPO: PurchaseOrder = {
|
||||
villa_id: "",
|
||||
@@ -349,11 +351,20 @@
|
||||
const { data } = await supabase.from("vb_po_item").select("item_name");
|
||||
if (data) poItems = data;
|
||||
}
|
||||
function getDBColumn(key: string) {
|
||||
switch (key) {
|
||||
case "villa_name": return "villa_name";
|
||||
case "reported_by": return "reported_by";
|
||||
// add mappings if needed
|
||||
default: return key;
|
||||
}
|
||||
}
|
||||
// Fetch issues with optional filters
|
||||
async function fetchIssues() {
|
||||
let query = supabase
|
||||
.from("vb_issues_data")
|
||||
.select("*").order("issue_number", { ascending: true });
|
||||
.select("*")
|
||||
.order(sortColumn || "created_at", { ascending: sortOrder === "asc" });
|
||||
|
||||
const { data: issues, error } = await query;
|
||||
|
||||
@@ -599,7 +610,15 @@
|
||||
alert("Purchase Order submitted!");
|
||||
showPurchaseOrderModal = false;
|
||||
}
|
||||
|
||||
function toggleSort(column: string) {
|
||||
if (sortColumn === column) {
|
||||
sortOrder = sortOrder === "asc" ? "desc" : "asc";
|
||||
} else {
|
||||
sortColumn = column;
|
||||
sortOrder = "asc";
|
||||
}
|
||||
fetchIssues(); // re-fetch or re-sort your rows
|
||||
}
|
||||
// Function to open purchase order modal
|
||||
function openPurchaseOrderModal(issue) {
|
||||
if (purchaseOrderMap.has(issue.id)) {
|
||||
@@ -787,7 +806,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-x-auto rounded-lg shadow mb-4">
|
||||
<div class="overflow-x-auto rounded-lg shadow mb-4 max-h-[70vh]">
|
||||
<table class="min-w-[1000px] divide-y divide-gray-200 text-sm w-max">
|
||||
<thead class="bg-gray-100">
|
||||
<tr>
|
||||
@@ -796,14 +815,22 @@
|
||||
<th
|
||||
class="sticky left-0 px-4 py-3 text-left font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap"
|
||||
style="background-color: #f0f8ff; z-index: 10;"
|
||||
on:click={() => toggleSort(col.key)}
|
||||
>
|
||||
{col.title}
|
||||
{#if sortColumn === col.key}
|
||||
{sortOrder === 'asc' ? ' 🔼' : ' 🔽'}
|
||||
{/if}
|
||||
</th>
|
||||
{:else}
|
||||
<th
|
||||
class="px-4 py-3 text-left font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap"
|
||||
on:click={() => toggleSort(col.key)}
|
||||
>
|
||||
{col.title}
|
||||
{#if sortColumn === col.key}
|
||||
{sortOrder === 'asc' ? ' 🔼' : ' 🔽'}
|
||||
{/if}
|
||||
</th>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -161,6 +161,8 @@
|
||||
updated_by: "",
|
||||
updated_at: new Date().toISOString(),
|
||||
};
|
||||
let sortColumn: string | null = "created_at"; // or any default
|
||||
let sortOrder: "asc" | "desc" = "desc";
|
||||
let showEditModal = false;
|
||||
let editForm = {
|
||||
po_number: "",
|
||||
@@ -191,7 +193,13 @@
|
||||
approval: "",
|
||||
approved_by: "",
|
||||
approved_date: "",
|
||||
reject_comment: ""
|
||||
reject_comment: "",
|
||||
po_remark: "",
|
||||
approved_quantity: 0,
|
||||
approved_vendor: "",
|
||||
approved_price: 0,
|
||||
po_item: "",
|
||||
total_approved_order_amount: 0
|
||||
};
|
||||
let villaOptions = [];
|
||||
let poItemOptions = [];
|
||||
@@ -283,6 +291,21 @@
|
||||
currency: "IDR",
|
||||
minimumFractionDigits: 0,
|
||||
});
|
||||
// function Sort
|
||||
function toggleSort(column: string) {
|
||||
if (sortColumn === column) {
|
||||
sortOrder = sortOrder === "asc" ? "desc" : "asc";
|
||||
} else {
|
||||
sortColumn = column;
|
||||
sortOrder = "asc";
|
||||
}
|
||||
fetchPurchaseOrder(
|
||||
selectedVillaId,
|
||||
searchTerm,
|
||||
sortColumn,
|
||||
sortOrder,
|
||||
);
|
||||
}
|
||||
|
||||
// Function to format numbers as ordinal (1st, 2nd, 3rd, etc.)
|
||||
function ordinal(num: number) {
|
||||
@@ -323,7 +346,13 @@
|
||||
approval: row.approval ? "approve" : "reject",
|
||||
approved_by: currentUserId,
|
||||
approved_date: new Date().toISOString().split("T")[0],
|
||||
reject_comment: row.reject_comment || ""
|
||||
reject_comment: row.reject_comment || "",
|
||||
po_remark: row.po_remark || "",
|
||||
approved_quantity: row.approved_quantity || 0,
|
||||
approved_vendor: row.approved_vendor || "",
|
||||
approved_price: row.approved_price || 0,
|
||||
po_item: row.po_item || "",
|
||||
total_approved_order_amount: row.total_approved_order_amount || 0
|
||||
};
|
||||
|
||||
showApprovalModal = true;
|
||||
@@ -478,7 +507,8 @@
|
||||
approved_by: approvalForm.approval === "approve" ? currentUserId : null,
|
||||
approved_date: approvalForm.approved_date,
|
||||
reject_comment: approvalForm.reject_comment || null,
|
||||
po_status: "approved"
|
||||
po_status: "approved",
|
||||
po_remark: approvalForm.po_remark || null
|
||||
})
|
||||
.eq("id", selectedPO.id);
|
||||
|
||||
@@ -692,7 +722,7 @@
|
||||
let query = supabase
|
||||
.from("vb_purchaseorder_data")
|
||||
.select("*")
|
||||
.order(sort || "purchase_order_number", { ascending: order === "desc" })
|
||||
.order(sortColumn || "created_at", { ascending: sortOrder === "asc" })
|
||||
.range(offset, offset + limit - 1);
|
||||
|
||||
if (filter) {
|
||||
@@ -1070,7 +1100,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-x-auto rounded-lg shadow mb-4">
|
||||
<div class="overflow-x-auto rounded-lg shadow mb-4 max-h-[70vh]">
|
||||
<table class="min-w-[1000px] divide-y divide-gray-200 text-sm w-max">
|
||||
<thead class="bg-gray-100">
|
||||
<tr>
|
||||
@@ -1079,14 +1109,22 @@
|
||||
<th
|
||||
class="sticky left-0 px-4 py-3 text-left font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap"
|
||||
style="background-color: #f0f8ff; z-index: 10;"
|
||||
on:click={() => toggleSort(col.key)}
|
||||
>
|
||||
{col.title}
|
||||
{#if sortColumn === col.key}
|
||||
{sortOrder === 'asc' ? ' 🔼' : ' 🔽'}
|
||||
{/if}
|
||||
</th>
|
||||
{:else}
|
||||
<th
|
||||
class="px-4 py-3 text-left font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap"
|
||||
on:click={() => toggleSort(col.key)}
|
||||
>
|
||||
{col.title}
|
||||
{#if sortColumn === col.key}
|
||||
{sortOrder === 'asc' ? ' 🔼' : ' 🔽'}
|
||||
{/if}
|
||||
</th>
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -1650,11 +1688,10 @@
|
||||
<!-- Payment Amounts -->
|
||||
{#each [1,2,3,4,5,6] as num}
|
||||
<label>{ordinal(num)} Pay Amount</label>
|
||||
<input
|
||||
type="number"
|
||||
<CurrencyInput
|
||||
bind:value={paymentForm[`${ordinal(num)}_pay_amt`]}
|
||||
on:input={updateDueRemaining}
|
||||
class="w-full border p-2"
|
||||
onInput={updateDueRemaining}
|
||||
className="w-full"
|
||||
/>
|
||||
|
||||
<label>{ordinal(num)} Pay Date</label>
|
||||
@@ -1667,7 +1704,7 @@
|
||||
|
||||
<!-- Due Remaining -->
|
||||
<label>Due Remaining</label>
|
||||
<input type="number" value={paymentForm.due_remaining} disabled class="w-full border p-2 bg-gray-100"/>
|
||||
<CurrencyInput value={paymentForm.due_remaining} disabled className="w-full border p-2 bg-gray-100"/>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="flex justify-end space-x-2 pt-4">
|
||||
@@ -1693,27 +1730,89 @@
|
||||
class="w-full border p-2 bg-gray-100"
|
||||
/>
|
||||
|
||||
<!-- Approval -->
|
||||
<!-- PO Item -->
|
||||
{#if approvalForm.po_item}
|
||||
<label>PO Item</label>
|
||||
<input
|
||||
type="text"
|
||||
value={approvalForm.po_item}
|
||||
disabled
|
||||
class="w-full border p-2 bg-gray-100"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- Approved Quantity -->
|
||||
{#if approvalForm.approved_quantity != null}
|
||||
<label>Approved Quantity</label>
|
||||
<input
|
||||
type="number"
|
||||
value={approvalForm.approved_quantity}
|
||||
disabled
|
||||
class="w-full border p-2 bg-gray-100"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- Approved Price -->
|
||||
{#if approvalForm.approved_price != null}
|
||||
<label>Approved Price</label>
|
||||
<CurrencyInput
|
||||
value={approvalForm.approved_price}
|
||||
disabled
|
||||
className="w-full border p-2 bg-gray-100"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- Total Approved Order Amount -->
|
||||
{#if approvalForm.total_approved_order_amount != null}
|
||||
<label>Total Approved Order Amount</label>
|
||||
<CurrencyInput
|
||||
value={approvalForm.total_approved_order_amount}
|
||||
disabled
|
||||
className="w-full border p-2 bg-gray-100"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- Approved Vendor -->
|
||||
{#if approvalForm.approved_vendor}
|
||||
<label>Approved Vendor</label>
|
||||
<input
|
||||
type="text"
|
||||
value={approvalForm.approved_vendor}
|
||||
disabled
|
||||
class="w-full border p-2 bg-gray-100"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- Approval Decision -->
|
||||
<label>Approval</label>
|
||||
<select bind:value={approvalForm.approval} class="w-full border p-2">
|
||||
<option value="" disabled>Select Approval</option>
|
||||
<option value="approve">Approve</option>
|
||||
<option value="reject">Reject</option>
|
||||
</select>
|
||||
|
||||
<!-- Reject Comment -->
|
||||
{#if approvalForm.approval === 'reject'}
|
||||
<label>Reject Comment</label>
|
||||
<textarea
|
||||
<label>Reject Comment</label>
|
||||
<textarea
|
||||
bind:value={approvalForm.reject_comment}
|
||||
class="w-full border p-2"
|
||||
rows="3"
|
||||
placeholder="Enter reason for rejection..."
|
||||
></textarea>
|
||||
></textarea>
|
||||
{/if}
|
||||
|
||||
<!-- Hidden: approved_by -->
|
||||
<input type="hidden" value={approvalForm.approved_by} />
|
||||
<!-- PO Remark (editable!) -->
|
||||
<label>PO Remark</label>
|
||||
<textarea
|
||||
bind:value={approvalForm.po_remark}
|
||||
class="w-full border p-2"
|
||||
rows="3"
|
||||
placeholder="Add or edit PO remark..."
|
||||
></textarea>
|
||||
|
||||
<!-- Hidden: approved_date -->
|
||||
<!-- Hidden fields -->
|
||||
<input type="hidden" value={approvalForm.approved_by} />
|
||||
<input type="hidden" value={approvalForm.approved_date} />
|
||||
|
||||
<!-- Actions -->
|
||||
@@ -1726,6 +1825,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
{#if showAcknowledgedModal}
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50 overflow-y-auto">
|
||||
<div class="min-h-screen flex items-center justify-center p-4">
|
||||
|
||||
@@ -332,9 +332,10 @@
|
||||
'*',
|
||||
{ count: "exact" },
|
||||
)
|
||||
.order(sortColumn || "created_at", {
|
||||
ascending: sortOrder === "asc",
|
||||
}).range(fromIndex, toIndex);
|
||||
.order(getDBColumn(sortColumn) || "created_at", {
|
||||
ascending: sortOrder === "asc"
|
||||
})
|
||||
.range(fromIndex, toIndex);
|
||||
|
||||
if (typeof searchTerm === "string" && searchTerm.length > 4) {
|
||||
// Supabase ilike only supports one column at a time, so use or for multiple columns
|
||||
@@ -422,7 +423,13 @@
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
function getDBColumn(key: string) {
|
||||
switch (key) {
|
||||
case "name": return "work_description";
|
||||
case "staff_id": return "entered_by";
|
||||
default: return key;
|
||||
}
|
||||
}
|
||||
function changePage(page: number) {
|
||||
if (page < 1 || page > totalPages || page === currentPage) return;
|
||||
currentPage = page;
|
||||
@@ -673,7 +680,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-x-auto rounded-lg shadow mb-4">
|
||||
<div class="overflow-x-auto rounded-lg shadow mb-4 max-h-[70vh]">
|
||||
<table class="min-w-[1000px] divide-y divide-gray-200 text-sm w-max">
|
||||
<thead class="bg-gray-100">
|
||||
<tr>
|
||||
@@ -682,8 +689,13 @@
|
||||
<th
|
||||
class="sticky left-0 px-4 py-3 text-left font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap"
|
||||
style="background-color: #f0f8ff; z-index: 10;"
|
||||
on:click={() => toggleSort(col.key)}
|
||||
|
||||
>
|
||||
{col.title}
|
||||
{#if sortColumn === col.key}
|
||||
{sortOrder === 'asc' ? ' 🔼' : ' 🔽'}
|
||||
{/if}
|
||||
</th>
|
||||
{:else}
|
||||
<th
|
||||
@@ -693,7 +705,7 @@
|
||||
{col.title}
|
||||
{#if sortColumn === col.key}
|
||||
{sortOrder === 'asc' ? ' 🔼' : ' 🔽'}
|
||||
{/if}
|
||||
{/if}
|
||||
</th>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
Reference in New Issue
Block a user