This commit is contained in:
2025-07-01 13:47:28 +08:00
parent d4fcb15f84
commit edeb0870f3

View File

@@ -50,6 +50,92 @@
received: null,
po_remark: ""
};
let showAddPOModal = false;
let addPOForm = {
po_type: "",
villa_id: "",
po_item: "",
po_quantity: 1,
po_status: "requested",
po_remark: "",
requested_by: "",
requested_date: new Date().toISOString().split("T")[0]
};
let villaOptions = [];
let poItemOptions = [];
let employeeOptions = [];
async function fetchAddPODropdowns() {
const [{ data: villas }, { data: items }, { data: employees }] = await Promise.all([
supabase.from("vb_villas").select("id, villa_name").eq("villa_status", "Active").order("villa_name", { ascending: true }),
supabase.from("vb_po_item").select("id, item_name").order("item_name", { ascending: true }),
supabase.from("vb_employee").select("id, employee_name").eq("employee_status", "Active").order("employee_name", { ascending: true }),
]);
villaOptions = villas || [];
poItemOptions = items || [];
employeeOptions = employees || [];
}
async function openAddPOModal() {
await fetchAddPODropdowns();
addPOForm = {
po_type: "",
villa_id: "",
po_item: "",
po_quantity: 1,
po_status: "requested",
po_remark: "",
requested_by: "",
requested_date: new Date().toISOString().split("T")[0]
};
showAddPOModal = true;
}
async function saveAddPO() {
// Basic required field check
if (
!addPOForm.po_type ||
!addPOForm.villa_id ||
!addPOForm.po_item ||
!addPOForm.requested_by ||
!addPOForm.po_quantity ||
addPOForm.po_quantity <= 0
) {
alert("Please fill in all required fields correctly.");
return;
}
// If you use auth:
const sessionId = await getSessionAuthId();
if (!sessionId) {
alert("You must be logged in.");
return;
}
const { error } = await supabase.from("vb_purchase_orders").insert({
po_type: addPOForm.po_type,
villa_id: addPOForm.villa_id,
po_item: addPOForm.po_item,
po_quantity: addPOForm.po_quantity,
po_status: "requested",
po_remark: addPOForm.po_remark,
requested_by: addPOForm.requested_by,
requested_date: addPOForm.requested_date,
created_at: new Date().toISOString()
});
if (error) {
console.error("Error adding PO:", error);
alert("Failed to add purchase order.");
return;
}
showAddPOModal = false;
await fetchPurchaseOrder();
}
function openEditModal(row) {
selectedPO = row;
@@ -278,7 +364,6 @@
let selectedPO = null;
let preparedByOptions: any[] = [];
let poItemOptions: any[] = [];
let vendorOptions: any[] = [];
let preparedForm = {
@@ -415,77 +500,59 @@
vendorOptions = vendors || [];
}
async function fetchPurchaseOrder(
filter: string | null = null,
search: string | null = null,
sort: string | null = null,
order: "asc" | "desc" = "desc",
offset: number = 0,
limit: number = 1000,
filter: string | null = null,
search: string | null = null,
sort: string | null = null,
order: "asc" | "desc" = "desc",
offset: number = 0,
limit: number = 1000,
) {
let query = supabase
.from("vb_purchaseorder_data")
.select("*")
.order(sort || "created_at", { ascending: order === "asc" })
.range(offset, offset + limit - 1);
if (filter) {
query = query.eq("po_type", filter);
}
if (search) {
query = query.ilike("purchase_order_number", `%${search}%`);
}
const { data, error } = await query;
if (error) {
console.error("Error fetching purchase orders:", error);
return;
}
let query = supabase
.from("vb_purchaseorder_data")
.select("*")
.order(sort || "created_at", { ascending: order === "asc" })
.range(offset, offset + limit - 1);
// fetch issue and villa names
const issueIds = data.map((row) => row.issue_id);
const { data: issues, error: issueError } = await supabase
.from("vb_issues")
.select("*")
.in("id", issueIds);
if (issueError) {
console.error("Error fetching issues:", issueError);
return;
}
const villaIds = issues.map((row) => row.villa_name).filter(Boolean);
const { data: villas, error: villaError } = await supabase
.from("villas")
.select("id, name")
.in("id", villaIds);
if (villaError) {
console.error("Error fetching villas:", villaError);
return;
}
// masukkan villa name dan issue name ke dalam data
allRows = data.map((row: any) => {
const issue = issues.find((issue) => issue.id === row.issue_id);
const villa = villas.find((villa) => villa.id === issue.villa_id);
const vendor = vendors.find(
(vendor) => vendor.id === row.approved_vendor,
);
return {
...row,
name: issue ? issue.name : "Unknown Issue",
villa_name: row.villa_data,
priority: issue ? issue.priority : "Unknown Priority",
approved_vendor: vendor
? vendor.name
: "Unknown Approved Vendor",
approval: row.approval || "",
completed_status: row.completed_status || "",
proses_to_approval: row.proses_to_approval || false,
po_price: row.po_price || 0,
po_total_price: row.po_total_price || 0,
} as PurchaseOrderDisplay;
});
if (filter) {
query = query.eq("po_type", filter);
}
if (search) {
query = query.ilike("purchase_order_number", `%${search}%`);
}
const { data, error } = await query;
if (error) {
console.error("Error fetching purchase orders:", error);
return;
}
if (!data || data.length === 0) {
console.warn("No purchase orders found.");
allRows = [];
return;
}
allRows = data.map((row: any) => {
return {
...row,
issue_name: row.issue_name || "N/A",
villa_name: row.villa_data || "N/A",
po_number: row.purchase_order_number || "",
approval: row.approval || "",
completed_status: row.completed_status || "",
proses_to_approval: row.proses_to_approval || false,
po_price: row.po_price || 0,
po_total_price: row.po_total_price || 0,
} as PurchaseOrderDisplay;
});
console.log("✅ Fetched rows:", data);
console.log("✅ Final mapped rows:", allRows);
}
//fetch all issues
async function fetchIssues() {
const { data, error } = await supabase
@@ -1006,29 +1073,6 @@
await fetchPurchaseOrder();
}
async function completedStatusOk(id: string, status: string) {
const sessionId = await getSessionAuthId();
if (!sessionId) {
alert("You must be logged in to perform this action.");
return;
}
const { data, error } = await supabase
.from("vb_purchase_orders")
.update({
completed_status: status,
updated_by: sessionId,
updated_at: new Date().toISOString(),
})
.eq("id", id);
if (error) {
console.error("Error acknowledging purchase order:", error);
return;
}
await fetchPurchaseOrder();
}
function openPaymentModal(row) {
selectedPO = row;
@@ -1169,9 +1213,9 @@
</select>
<button
class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 text-sm"
on:click={() => openModal()}
on:click={openAddPOModal}
>
Add Purchase Order
Add Purchase Order
</button>
</div>
</div>
@@ -1957,3 +2001,66 @@
</div>
</div>
{/if}
{#if showAddPOModal}
<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">
<div class="bg-white rounded-lg shadow-lg w-full max-w-2xl max-h-[90vh] overflow-y-auto p-6 space-y-4">
<h2 class="text-xl font-bold">Add Purchase Order</h2>
<!-- PO Type -->
<label>PO Type</label>
<select bind:value={addPOForm.po_type} class="w-full border p-2">
<option value="" disabled>Select PO Type</option>
<option value="purchase">Purchase</option>
<option value="project">Project</option>
<option value="repair">Repair</option>
</select>
<!-- Villa -->
<label>Villa</label>
<select bind:value={addPOForm.villa_id} class="w-full border p-2">
<option value="" disabled>Select Villa</option>
{#each villaOptions as v}
<option value={v.id}>{v.villa_name}</option>
{/each}
</select>
<!-- PO Item -->
<label>PO Item</label>
<select bind:value={addPOForm.po_item} class="w-full border p-2">
<option value="" disabled>Select Item</option>
{#each poItemOptions as item}
<option value={item.item_name}>{item.item_name}</option>
{/each}
</select>
<!-- Quantity -->
<label>PO Quantity</label>
<input type="number" bind:value={addPOForm.po_quantity} class="w-full border p-2"/>
<!-- Remark -->
<label>PO Remark</label>
<textarea bind:value={addPOForm.po_remark} class="w-full border p-2"></textarea>
<!-- Requested By -->
<label>Requested By</label>
<select bind:value={addPOForm.requested_by} class="w-full border p-2">
<option value="" disabled>Select Employee</option>
{#each employeeOptions as e}
<option value={e.id}>{e.employee_name}</option>
{/each}
</select>
<!-- Requested Date -->
<label>Requested Date</label>
<input type="date" bind:value={addPOForm.requested_date} class="w-full border p-2"/>
<!-- Actions -->
<div class="flex justify-end space-x-2 pt-4">
<button on:click={saveAddPO} class="bg-blue-600 text-white px-4 py-2 rounded">Save</button>
<button on:click={() => showAddPOModal = false} class="px-4 py-2 rounded border">Cancel</button>
</div>
</div>
</div>
</div>
{/if}