Files
vberp/src/routes/backoffice/purchaseorder/+page.svelte
2025-08-11 03:59:13 +08:00

3002 lines
116 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts">
import { onMount } from "svelte";
import { supabase } from "$lib/supabaseClient";
import { getSessionAuthId } from "$lib/utils/authUtil";
import CurrencyInput from "$lib/CurrencyInput.svelte";
type PurchaseOrderInsert = {
issue_id: string;
prepared_date: string;
po_type: string;
po_quantity: number;
po_price: number;
po_total_price: number;
input_by: string;
updated_by: string;
updated_at: string;
};
type PurchaseOrders = {
id: string;
purchase_order_number: string;
prepared_date: string;
po_type: string;
po_quantity: number;
po_status: string;
approved_vendor: string;
proses_to_approval: boolean;
acknowledged: boolean;
approved_vendor_id: string;
acknowledge_by: string;
approved_price: number;
approved_quantity: number;
total_approved_order_amount: number;
approval: string;
completed_status: string;
received: boolean;
received_by: string;
input_by: string;
issue_id: string;
approved_by: string;
created_at: string;
};
type PurchaseOrderDisplay = {
id: string;
name: string;
purchase_order_number: string;
villa_name: string;
priority: string;
prepared_date: string;
po_type: string;
po_quantity: number;
po_price: number;
po_total_price: number;
po_status: string;
approved_vendor: string;
acknowledged: boolean;
acknowledge_by: string;
approved_by: string;
prepared: boolean | null;
approved_price: number;
approved_quantity: number;
total_approved_order_amount: number;
proses_to_approval: boolean;
approval: string;
completed_status: string;
received: boolean;
received_by: string;
created_at: string;
updated_at?: string;
updated_by?: string;
updated_name?: string;
input_by?: string;
input_name?: string;
};
type columns = {
key: string;
title: string;
};
const columns: columns[] = [
{ key: "purchase_order_number", title: "PO Number" },
{ key: "issue_name", title: "Issue Name" },
{ key: "requested_date", title: "Requested Date" },
{ key: "po_status", title: "PO Status" },
{ key: "villa_data", title: "Villa Name" },
{ key: "po_item", title: "PO Product" },
{ key: "po_remark", title: "PO Remark" },
{ key: "po_due", title: "PO Due" },
{ key: "prepared", title: "Prepare PO" },
{ key: "approved", title: "Approval" },
{ key: "acknowledged", title: "Acknowledge" },
{ key: "completed", title: "Complete" },
{ key: "received", title: "Receive" },
{ key: "updated_at", title: "Updated At" },
{ key: "payment", title: "Payment" },
{ key: "actions", title: "Actions" }, // For edit/delete buttons
];
const excludedKeys = [
"id",
"priority",
"villa_name",
"purchase_order_number",
"issue_id",
"number_project",
"input_by",
"created_at",
"actions",
"acknowledged",
"acknowledge_by",
"approval",
"completed_status",
"received",
"received_by",
"proses_to_approval",
"approved_by",
"name",
"po_status",
"approved_quantity",
"total_approved_order_amount",
"approved_vendor",
"approved_price",
"updated_at",
"updated_by",
"updated_name",
"inputed_name",
"approved_name",
"acknowledged_name",
"received_name",
];
const formColumns = columns.filter(
(col) => !excludedKeys.includes(col.key),
);
const statusOptions = [
{ label: "Requested", value: "REQUESTED", color: "#e5e7eb" },
{ label: "Prepared", value: "PREPARED", color: "#93c5fd" },
{ label: "Approved", value: "APPROVED", color: "#34d399" },
{ label: "Acknowledged", value: "ACKNOWLEDGE", color: "#60a5fa" },
{
label: "Received - Incomplete",
value: "RECEIVED INCOMPLETE",
color: "#fb923c",
},
{
label: "Received - Completed",
value: "RECEIVED COMPLETED",
color: "#10b981",
},
{ label: "Canceled", value: "CANCELED", color: "#f87171" },
];
//reactive variables
let purchaseOrderInsert: PurchaseOrderInsert = {
issue_id: "",
prepared_date: "",
po_type: "",
po_quantity: 0,
po_price: 0,
po_total_price: 0,
input_by: "",
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: "",
issue_name: "",
villa_name: "",
po_status: "",
prepared: null,
approved: null,
acknowledged: null,
completed: null,
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 showApprovalModal = false;
let approvalForm = {
po_number: "",
approval: "",
approved_by: "",
approved_date: "",
reject_comment: "",
po_remark: "",
approved_quantity: 0,
approved_vendor: "",
approved_price: 0,
po_item: "",
total_approved_order_amount: 0,
};
type VillaField = {
id: string;
villa_name: string;
};
let villaOptions: VillaField[] = [];
let poItemOptions = [];
let employeeOptions = [];
let allRows: PurchaseOrderDisplay[] = [];
let currentPage = 1;
let rowsPerPage = 10;
let totalItems = 0;
let totalPages = 0;
let currentUserId = "";
let currentUserFullName = "";
let showPaymentModal = false;
let paymentForm = {
payment_method: "",
total_approved_order_amount: 0,
"1st_pay_amt": 0,
"2nd_pay_amt": 0,
"3rd_pay_amt": 0,
"4th_pay_amt": 0,
"5th_pay_amt": 0,
"6th_pay_amt": 0,
"1st_pay_date": "",
"2nd_pay_date": "",
"3rd_pay_date": "",
"4th_pay_date": "",
"5th_pay_date": "",
"6th_pay_date": "",
due_remaining: 0,
};
let showAcknowledgedModal = false;
let acknowledgedForm = {
po_number: "",
reject_comment: "",
acknowledged: "",
acknowledged_by: "",
acknowledged_date: "",
po_remark: "",
approved_quantity: 0,
approved_vendor: "",
approved_price: 0,
po_item: "",
total_approved_order_amount: 0,
};
let selectedVillaId: string | null = null;
let searchTerm: string = "";
let showPreparedModal = false;
let selectedStatus: string | null = null;
let selectedPO: string | null = null;
let preparedByOptions: any[] = [];
let vendorOptions: any[] = [];
let preparedForm = {
prepared_by: "",
prepared_date: "",
po_item: "",
po_quantity: 1,
q1_vendor: "",
q2_vendor: "",
q3_vendor: "",
q1_vendor_price: 0,
q2_vendor_price: 0,
q3_vendor_price: 0,
po_remark: "",
approved_quantity: 0,
approved_vendor: "",
approved_price: 0,
total_approved_order_amount: 0,
};
let formattedPrice = "";
let showReceivedModal = false;
let receivedForm = {
po_number: "",
received: "",
reject_comment: "",
received_by: "",
received_date: "",
approved_quantity: 0,
approved_vendor: "",
approved_price: 0,
po_item: "",
total_approved_order_amount: 0,
};
let showCompletedModal = false;
let completedForm = {
po_number: "",
completed: "",
reject_comment: "",
completed_by: "",
completed_date: "",
po_remark: "",
approved_quantity: 0,
approved_vendor: "",
approved_price: 0,
po_item: "",
total_approved_order_amount: 0,
};
let showModal = false;
let isEditing = false;
let currentEditingId: string | null = null;
let newPurchaseOrders: Record<string, any> = {};
let vendors: { id: string; name: string }[] = [];
let issues: { id: string; name: string }[] = [];
let printingId: string | null = null;
$: formattedPrice = preparedForm.q1_vendor_price.toLocaleString("id-ID", {
style: "currency",
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) {
if (num === 1) return "1st";
if (num === 2) return "2nd";
if (num === 3) return "3rd";
return `${num}th`;
}
// Open edit modal for purchase order
function openEditModal(row) {
selectedPO = row;
editForm = {
po_number: row.purchase_order_number || "",
issue_name: row.issue_name || "",
villa_name: row.villa_data || "",
po_status: row.po_status || "",
prepared: row.prepared,
approved: row.approved,
acknowledged: row.acknowledged,
completed: row.completed,
received: row.received,
po_remark: row.po_remark || "",
};
showEditModal = true;
}
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;
fetchPurchaseOrder(
selectedVillaId,
searchTerm,
sortColumn,
sortOrder,
(currentPage - 1) * rowsPerPage,
rowsPerPage,
selectedStatus,
);
}
// pagination functions
function goToPage(page: number) {
if (page >= 1 && page <= totalPages) currentPage = page;
}
// Open approval modal for purchase order
function openApprovalModal(row) {
selectedPO = row;
approvalForm = {
po_number: row.purchase_order_number || "",
approval: row.approval ? "approve" : "reject",
approved_by: currentUserId,
approved_date: new Date().toISOString().split("T")[0],
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;
}
let showDetailsModal = false;
let detailsRow: any = null;
function openDetailsModal(row) {
detailsRow = row; // id is already in row.id if you need it as a matcher
showDetailsModal = true;
}
function closeDetailsModal() {
showDetailsModal = false;
detailsRow = null;
}
// acknowledge purchase order
function openAcknowledgedModal(row) {
console.log("Opening acknowledged modal with row:", row);
selectedPO = row;
acknowledgedForm = {
po_number: row.purchase_order_number || "",
reject_comment: row.reject_comment || "",
acknowledged: row.acknowledged ? "acknowledged" : "reject",
acknowledged_by: currentUserId,
acknowledged_date: new Date().toISOString().split("T")[0],
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,
};
showAcknowledgedModal = true;
}
// open receive modal
function openReceivedModal(row) {
selectedPO = row;
receivedForm = {
po_number: row.purchase_order_number || "",
received: row.received ? "received" : "reject",
received_by: currentUserId,
received_date: new Date().toISOString().split("T")[0],
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,
};
showReceivedModal = true;
}
// open completed modal
function openCompletedModal(row) {
selectedPO = row;
completedForm = {
po_number: row.purchase_order_number || "",
completed: row.completed ? "completed" : "reject",
completed_by: currentUserId,
completed_date: new Date().toISOString().split("T")[0],
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,
};
console.log("Opening completed modal with form:", completedForm);
showCompletedModal = true;
}
// Update total amount in prepared form
function updateTotalAmount() {
preparedForm.total_approved_order_amount =
preparedForm.approved_quantity * preparedForm.approved_price;
}
function getStatusOption(value: string) {
return statusOptions.find((option) => option.value === value) ?? null;
}
// open payment modal for purchase order
function openPaymentModal(row) {
selectedPO = row;
paymentForm = {
payment_method: row.payment_method || "",
total_approved_order_amount: row.total_approved_order_amount || 0,
"1st_pay_amt": row["1st_pay_amt"] || 0,
"2nd_pay_amt": row["2nd_pay_amt"] || 0,
"3rd_pay_amt": row["3rd_pay_amt"] || 0,
"4th_pay_amt": row["4th_pay_amt"] || 0,
"5th_pay_amt": row["5th_pay_amt"] || 0,
"6th_pay_amt": row["6th_pay_amt"] || 0,
"1st_pay_date": row["1st_pay_date"] || "",
"2nd_pay_date": row["2nd_pay_date"] || "",
"3rd_pay_date": row["3rd_pay_date"] || "",
"4th_pay_date": row["4th_pay_date"] || "",
"5th_pay_date": row["5th_pay_date"] || "",
"6th_pay_date": row["6th_pay_date"] || "",
due_remaining: calculateDueRemaining(row),
};
showPaymentModal = true;
}
//validate input fields purchase order
function validateInput() {
const requiredFields = [
"prepared_date",
"po_type",
"po_quantity",
"po_price",
];
for (const field of requiredFields) {
if (!newPurchaseOrders[field]) {
alert(`Please fill in the ${field} field.`);
return false;
}
}
return true;
}
// Calculate remaining due amount
function calculateDueRemaining(source) {
const total = source.total_approved_order_amount || 0;
const sum =
(source["1st_pay_amt"] || 0) +
(source["2nd_pay_amt"] || 0) +
(source["3rd_pay_amt"] || 0) +
(source["4th_pay_amt"] || 0) +
(source["5th_pay_amt"] || 0) +
(source["6th_pay_amt"] || 0);
return total - sum;
}
// Update due remaining amount in payment form
function updateDueRemaining() {
paymentForm.due_remaining =
paymentForm.total_approved_order_amount -
paymentForm["1st_pay_amt"] -
paymentForm["2nd_pay_amt"] -
paymentForm["3rd_pay_amt"] -
paymentForm["4th_pay_amt"] -
paymentForm["5th_pay_amt"] -
paymentForm["6th_pay_amt"];
}
// Save payment data for purchase order
async function savePayment() {
const { error } = await supabase
.from("vb_purchase_orders")
.update({
payment_method: paymentForm.payment_method,
"1st_pay_amt": paymentForm["1st_pay_amt"],
"2nd_pay_amt": paymentForm["2nd_pay_amt"],
"3rd_pay_amt": paymentForm["3rd_pay_amt"],
"4th_pay_amt": paymentForm["4th_pay_amt"],
"5th_pay_amt": paymentForm["5th_pay_amt"],
"6th_pay_amt": paymentForm["6th_pay_amt"],
"1st_pay_date": paymentForm["1st_pay_date"] || null,
"2nd_pay_date": paymentForm["2nd_pay_date"] || null,
"3rd_pay_date": paymentForm["3rd_pay_date"] || null,
"4th_pay_date": paymentForm["4th_pay_date"] || null,
"5th_pay_date": paymentForm["5th_pay_date"] || null,
"6th_pay_date": paymentForm["6th_pay_date"] || null,
due_remaining: paymentForm.due_remaining,
})
.eq("id", selectedPO.id);
if (error) {
console.error("Error saving payment data:", error);
alert("Failed to save.");
return;
}
showPaymentModal = false;
await fetchPurchaseOrder();
}
// Save approval for purchase order
async function saveApproval() {
const { error } = await supabase
.from("vb_purchase_orders")
.update({
approved: approvalForm.approval === "approve",
approved_by:
approvalForm.approval === "approve" ? currentUserId : null,
approved_date: approvalForm.approved_date,
reject_comment: approvalForm.reject_comment || null,
po_status: "approved",
po_remark: approvalForm.po_remark || null,
})
.eq("id", selectedPO.id);
if (error) {
console.error("Error saving approval:", error);
alert("Failed to save.");
return;
}
showApprovalModal = false;
await fetchPurchaseOrder();
}
// populate dropdowns for adding purchase orders
async function fetchDropdowns() {
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 || [];
}
// Fetch purchase orders add
async function openAddPOModal() {
await fetchDropdowns();
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;
}
// Save new purchase order
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();
}
// Fetch current user details
async function fetchCurrentUser() {
const {
data: { user },
} = await supabase.auth.getUser();
if (user) {
currentUserId = user.id;
const { data, error } = await supabase
.from("vb_users")
.select("full_name")
.eq("id", user.id)
.single();
if (!error && data) {
currentUserFullName = data.full_name;
}
}
}
// Save edited purchase order
async function saveEdit() {
const { error } = await supabase
.from("vb_purchase_orders")
.update({
prepared: editForm.prepared,
approved: editForm.approved,
acknowledged: editForm.acknowledged,
completed: editForm.completed,
received: editForm.received,
po_remark: editForm.po_remark,
updated_at: new Date().toISOString(), // ✅ safe current timestamp
updated_by: currentUserId, // ✅ user UUID
})
.eq("id", selectedPO.id);
if (error) {
console.error("Error saving edit:", error);
alert("Failed to save changes.");
return;
}
showEditModal = false;
await fetchPurchaseOrder(
selectedVillaId,
searchTerm,
sortColumn,
sortOrder,
(currentPage - 1) * rowsPerPage,
rowsPerPage,
selectedStatus,
);
}
// save completed purchase order
async function saveCompleted() {
const { error } = await supabase
.from("vb_purchase_orders")
.update({
completed: completedForm.completed === "completed",
completed_by:
completedForm.completed === "completed"
? currentUserId
: null,
completed_date: completedForm.completed_date,
po_status: "completed",
})
.eq("id", selectedPO.id);
if (error) {
console.error("Error saving completed:", error);
alert("Failed to save.");
return;
}
// ✅ Fire the webhook after saving:
try {
await fetch(
"https://flow.catalis.app/webhook-test/vb_need_complete_new",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
po_id: selectedPO.id,
completed: completedForm.completed === "completed",
completed_by:
completedForm.completed === "completed"
? currentUserId
: null,
completed_date: completedForm.completed_date,
}),
},
);
} catch (webhookError) {
console.error("Webhook failed:", webhookError);
alert("Saved, but webhook failed.");
}
showCompletedModal = false;
await fetchPurchaseOrder();
}
// open prepared modal for purchase order
async function openPreparedModal(row) {
selectedPO = row;
// Prefill existing values
preparedForm = {
prepared_by: row.prepared_by || "",
prepared_date: row.prepared_date || "",
po_item: row.po_item || "",
po_quantity: row.po_quantity || 1,
q1_vendor: row.q1_vendor || "",
q2_vendor: row.q2_vendor || "",
q3_vendor: row.q3_vendor || "",
q1_vendor_price: row.q1_vendor_price || 0,
q2_vendor_price: row.q2_vendor_price || 0,
q3_vendor_price: row.q3_vendor_price || 0,
po_remark: row.po_remark || "",
approved_quantity: row.approved_quantity || 0,
approved_vendor: row.approved_vendor || "",
approved_price: row.approved_price || 0,
total_approved_order_amount:
row.approved_quantity * row.approved_price,
};
// Load dropdowns
await fetchPreparedDropdowns();
showPreparedModal = true;
}
// Fetch dropdowns for prepared modal
async function fetchPreparedDropdowns() {
const [{ data: employees }, { data: items }, { data: vendors }] =
await Promise.all([
supabase
.from("vb_employee")
.select("id, employee_name")
.eq("employee_status", "Active"),
supabase.from("vb_po_item").select("id, item_name"),
supabase.from("vb_vendor").select("id, name"),
]);
preparedByOptions = employees || [];
poItemOptions = items || [];
vendorOptions = vendors || [];
}
// Save fetch purchase order
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,
poStatus: string | null = null,
) {
let query = supabase
.from("vb_purchaseorder_data")
.select("*", { count: "exact" })
.order(sortColumn || "created_at", {
ascending: sortOrder === "asc",
})
.range(offset, offset + limit - 1);
if (filter) {
query = query.eq("villa_id", filter);
}
if (search) {
query = query.or(
`purchase_order_number.ilike.%${search}%,` +
`issue_name.ilike.%${search}%,` +
`po_item.ilike.%${search}%`,
);
}
if (poStatus) {
query = query.eq("po_status", poStatus);
}
const { data, count, error } = await query;
console.log("total count:", count);
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);
totalItems = count || 0;
console.log("✅ Total items:", totalItems);
}
//fetch on mount
onMount(() => {
fetchPurchaseOrder(
selectedVillaId,
searchTerm,
sortColumn,
sortOrder,
(currentPage - 1) * rowsPerPage,
rowsPerPage,
selectedStatus,
);
fetchVendors();
fetchCurrentUser();
fetchDropdowns();
});
$: totalPages = Math.ceil(totalItems / rowsPerPage);
$: currentPage = 1;
//fetch all issues
async function fetchIssues() {
const { data, error } = await supabase
.from("vb_issues")
.select("id, name");
if (error) {
console.error("Error fetching issues:", error);
return [];
}
issues = data.map((issue) => ({
id: issue.id,
name: issue.name,
}));
}
// Fetch vendors for dropdown
async function fetchVendors() {
const { data, error } = await supabase
.from("vb_vendor")
.select("id, name");
if (error) {
console.error("Error fetching vendors:", error);
return [];
}
vendors = data.map((vendor) => ({
id: vendor.id,
name: vendor.name,
}));
}
// Open modal for adding or editing purchase order
async function openModal(purchase: PurchaseOrderDisplay | null = null) {
await fetchIssues();
if (purchase) {
isEditing = true;
currentEditingId = purchase.id;
newPurchaseOrders = { ...purchase };
} else {
isEditing = false;
currentEditingId = null;
newPurchaseOrders = {};
}
showModal = true;
}
// Save prepared purchase order
async function savePrepared() {
const { error } = await supabase
.from("vb_purchase_orders")
.update({
prepared_by: preparedForm.prepared_by,
prepared_date: preparedForm.prepared_date,
po_item: preparedForm.po_item,
po_quantity: preparedForm.po_quantity,
q1_vendor: preparedForm.q1_vendor,
q2_vendor: preparedForm.q2_vendor,
q3_vendor: preparedForm.q3_vendor,
q1_vendor_price: preparedForm.q1_vendor_price,
q2_vendor_price: preparedForm.q2_vendor_price,
q3_vendor_price: preparedForm.q3_vendor_price,
approved_quantity: preparedForm.approved_quantity,
approved_vendor: preparedForm.approved_vendor,
approved_price: preparedForm.approved_price,
total_approved_order_amount:
preparedForm.total_approved_order_amount,
po_remark: preparedForm.po_remark,
po_status: "prepared",
prepared: true,
})
.eq("id", selectedPO.id);
if (error) {
console.error("Error saving prepared data:", error);
alert("Failed to save.");
return;
}
try {
await fetch("https://flow.catalis.app/webhook/vb_approval_po_new", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
po_id: selectedPO.id,
po_status: "prepared",
prepared_by: preparedForm.prepared_by,
prepared_date: preparedForm.prepared_date,
po_item: preparedForm.po_item,
po_quantity: preparedForm.po_quantity,
approved_quantity: preparedForm.approved_quantity,
approved_vendor: preparedForm.approved_vendor,
approved_price: preparedForm.approved_price,
total_approved_order_amount:
preparedForm.total_approved_order_amount,
}),
});
} catch (webhookError) {
console.error("Failed to fire webhook:", webhookError);
alert("Save worked but webhook failed.");
}
showPreparedModal = false;
await fetchPurchaseOrder();
}
// Save project function
async function saveProject() {
// session check
const session = await supabase.auth.getSession();
if (!session.data.session) {
alert("You must be logged in to perform this action.");
return;
}
const user = session.data.session.user;
const inputBy = user.id;
if (!validateInput()) {
return;
}
purchaseOrderInsert = {
issue_id: newPurchaseOrders.issue_id || "",
prepared_date: newPurchaseOrders.prepared_date || "",
po_type: newPurchaseOrders.po_type || "",
po_quantity: newPurchaseOrders.po_quantity || 0,
po_price: newPurchaseOrders.po_price || 0,
po_total_price:
newPurchaseOrders.po_quantity * newPurchaseOrders.po_price || 0,
input_by: inputBy,
updated_by: inputBy,
updated_at: new Date().toISOString(),
};
if (isEditing && currentEditingId) {
const { data, error } = await supabase
.from("vb_purchase_orders")
.update(purchaseOrderInsert)
.eq("id", currentEditingId);
if (error) {
console.error("Error updating purchase order:", error);
return;
}
} else {
const { data, error } = await supabase
.from("vb_purchase_orders")
.insert(purchaseOrderInsert);
if (error) {
console.error("Error inserting purchase order:", error);
return;
}
}
await fetchPurchaseOrder();
showModal = false;
}
async function deleteProject(id: string) {
const confirmed = confirm(
"Are you sure you want to delete this purchase order? This action cannot be undone.",
);
if (!confirmed) {
return; // User chickened out — good.
}
const { error } = await supabase
.from("vb_purchase_orders")
.delete()
.eq("id", id);
if (error) {
console.error("Error deleting project:", error);
alert("Failed to delete project.");
return;
}
await fetchPurchaseOrder();
alert("Purchase order deleted successfully.");
}
// Save received purchase order
async function saveReceived() {
const { error } = await supabase
.from("vb_purchase_orders")
.update({
received: receivedForm.received === "received",
received_by:
receivedForm.received === "received" ? currentUserId : null,
received_date: receivedForm.received_date,
po_status: "received",
})
.eq("id", selectedPO.id);
if (error) {
console.error("Error saving received:", error);
alert("Failed to save.");
return;
}
// ✅ Fire the webhook after the DB update succeeds:
try {
await fetch(
"https://flow.catalis.app/webhook-test/vb_need_received_new",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
po_id: selectedPO.id,
received: receivedForm.received === "received",
received_by:
receivedForm.received === "received"
? currentUserId
: null,
received_date: receivedForm.received_date,
}),
},
);
} catch (webhookError) {
console.error("Webhook failed:", webhookError);
alert("Saved, but webhook failed.");
}
showReceivedModal = false;
await fetchPurchaseOrder();
}
// Save acknowledged purchase order
async function saveAcknowledged() {
const { error } = await supabase
.from("vb_purchase_orders")
.update({
acknowledged: acknowledgedForm.acknowledged === "acknowledged",
acknowledge_by:
acknowledgedForm.acknowledged === "acknowledged"
? currentUserId
: null,
acknowledged_date: acknowledgedForm.acknowledged_date,
po_status: "acknowledged",
})
.eq("id", selectedPO.id);
if (error) {
console.error("Error saving acknowledged:", error);
alert("Failed to save.");
return;
}
// ✅ Fire the webhook after successful update:
try {
await fetch(
"https://flow.catalis.app/webhook/vb_acknowledged_new",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
po_id: selectedPO.id,
acknowledged:
acknowledgedForm.acknowledged === "acknowledged",
acknowledged_by:
acknowledgedForm.acknowledged === "acknowledged"
? currentUserId
: null,
acknowledged_date: acknowledgedForm.acknowledged_date,
}),
},
);
} catch (webhookError) {
console.error("Failed to fire webhook:", webhookError);
alert("Saved, but webhook failed.");
}
showAcknowledgedModal = false;
await fetchPurchaseOrder();
}
// Print purchase order
async function printPO(row) {
printingId = row.id; // set loading
try {
const response = await fetch(
"https://flow.catalis.app/webhook/vb_print_po_new",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(row),
},
);
if (!response.ok) {
console.error(
"Failed to call print webhook:",
response.statusText,
);
alert("Failed to send print request.");
return;
}
alert("Print request sent successfully!");
} catch (error) {
console.error("Error sending print request:", error);
alert("An error occurred while sending the print request.");
} finally {
printingId = null; // unset loading
}
}
</script>
<div>
<div
class="p-6 bg-white shadow-md rounded-2xl mb-4 flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4"
>
<div>
<h2
class="text-lg font-semibold text-gray-800 flex items-center gap-2"
>
<span>📦</span>
Purchase Order List
</h2>
<p class="text-sm text-gray-600">
Manage your purchase orders efficiently. You can add, edit, or
delete purchase orders as needed.
</p>
</div>
<div class="flex flex-col sm:flex-row sm:items-center gap-2">
<input
type="text"
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"
bind:value={searchTerm}
on:input={(e) => {
searchTerm = (
e.target as HTMLInputElement
).value.toLowerCase();
fetchPurchaseOrder(
selectedVillaId,
searchTerm,
sortColumn,
sortOrder,
currentPage - 1,
rowsPerPage,
selectedStatus,
);
}}
/>
<!-- 🏡 Villa Filter -->
<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"
bind:value={selectedVillaId}
on:change={() => {
fetchPurchaseOrder(
selectedVillaId,
searchTerm,
sortColumn,
sortOrder,
currentPage - 1,
rowsPerPage,
selectedStatus,
);
}}
>
<option value="">All Villas</option>
{#each villaOptions as villa}
<option value={villa.id}>{villa.villa_name}</option>
{/each}
</select>
<!-- 📦 PO Status Filter -->
<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"
bind:value={selectedStatus}
on:change={(e) => {
selectedStatus = (e.target as HTMLSelectElement).value;
fetchPurchaseOrder(
selectedVillaId,
searchTerm,
sortColumn,
sortOrder,
currentPage - 1,
rowsPerPage,
selectedStatus,
);
}}
>
<option value="">All Statuses</option>
<option value="requested">Requested</option>
<option value="prepared">Prepared</option>
<option value="approved">Approved</option>
<option value="acknowledged">Acknowledged</option>
</select>
<!-- Add Button -->
<button
class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 text-sm"
on:click={openAddPOModal}
>
Add Purchase Order
</button>
</div>
</div>
<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>
{#each columns as col}
{#if col.key === "purchase_order_number"}
<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}
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
{#each allRows as row}
<tr class="hover:bg-gray-50 transition">
{#each columns as col}
{#if col.key === "requested_date" || col.key === "po_due" || col.key === "updated_at"}
<td class="px-4 py-2 text-gray-500">
{#if row[col.key]}
{new Date(
row[col.key],
).toLocaleString()}
{:else}
{/if}
</td>
{:else if col.key === "po_remark"}
<td
class="px-4 py-2 text-gray-700 max-w-xs whitespace-normal align-top break-words"
>
{row[col.key] || "—"}
</td>
{:else if col.key === "purchase_order_number"}
<td
class="sticky left-0 px-4 py-2 font-medium text-blue-600 max-w-xs whitespace-normal align-top break-words"
style="background-color: #f0f8ff; cursor: pointer;"
>
{row.purchase_order_number || "—"}
</td>
{:else if col.key === "prepared"}
<td class="px-4 py-2 text-center">
<button
class="bg-blue-600 text-white text-xs px-3 py-1.5 rounded hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
on:click={() => openPreparedModal(row)}
disabled={row.prepared === true}
>
{row.prepared === true
? "Prepared"
: "Set Prepared"}
</button>
</td>
{:else if col.key === "approved"}
<td class="px-4 py-2 text-center">
<button
class="inline-flex items-center gap-1 rounded bg-yellow-600 px-3 py-1.5 text-white text-xs font-medium
hover:bg-yellow-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
on:click={() => openApprovalModal(row)}
disabled={row.approved === true ||
row.approved === false ||
row.po_status !== "prepared"}
>
{#if row.approved === true}
Approved
{:else if row.approved === false}
Rejected
{:else if row.po_status !== "prepared"}
Not Prepared
{:else}
Approval
{/if}
</button>
</td>
{:else if col.key === "acknowledged"}
<td class="px-4 py-2 text-center">
<button
class="inline-flex items-center gap-1 rounded bg-green-600 px-3 py-1.5 text-white text-xs font-medium
hover:bg-green-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
on:click={() =>
openAcknowledgedModal(row)}
disabled={row.approved !== true ||
row.acknowledged === true ||
row.acknowledged === false}
>
{#if row.acknowledged === true}
Acknowledged
{:else if row.acknowledged === false}
Rejected
{:else if row.approved !== true}
Pending
{:else}
Acknowledge
{/if}
</button>
</td>
{:else if col.key === "completed"}
<td class="px-4 py-2 text-center">
<button
class="inline-flex items-center gap-1 rounded bg-purple-600 px-3 py-1.5 text-white text-xs font-medium
hover:bg-purple-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
on:click={() => openCompletedModal(row)}
disabled={row.acknowledged !== true ||
row.completed === true ||
row.completed === false}
>
{#if row.completed === true}
Completed
{:else if row.completed === false}
Rejected
{:else if row.acknowledged !== true}
Pending
{:else}
Complete
{/if}
</button>
</td>
{:else if col.key === "received"}
<td class="px-4 py-2 text-center">
<button
class="inline-flex items-center gap-1 rounded bg-green-600 px-3 py-1.5 text-white text-xs font-medium
hover:bg-green-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
on:click={() => openReceivedModal(row)}
disabled={row.completed !== true ||
row.received === true ||
row.received === false}
>
{#if row.received === true}
Received
{:else if row.received === false}
Rejected
{:else if row.completed !== true}
Pending
{:else}
Receive
{/if}
</button>
</td>
{:else if col.key === "payment"}
<td class="px-4 py-2 text-center">
<button
class="inline-flex items-center gap-1 rounded bg-emerald-600 px-3 py-1.5 text-white text-xs font-medium hover:bg-emerald-700"
on:click={() => openPaymentModal(row)}
>
Payment
</button>
<button
class="inline-flex items-center gap-1 rounded bg-teal-600 px-3 py-1.5 text-white text-xs font-medium hover:bg-teal-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
on:click={() => printPO(row)}
disabled={printingId === row.id ||
row.acknowledged !== true}
>
{#if printingId === row.id}
🔄 Printing...
{:else if row.acknowledged !== true}
❌ Not Acknowledged
{:else}
🖨️ Print
{/if}
</button>
</td>
{:else if col.key === "actions"}
<td class="px-4 py-2">
<button
class="inline-flex items-center gap-1 rounded bg-gray-700 px-3 py-1.5 text-white text-xs font-medium hover:bg-gray-800"
on:click={() => openDetailsModal(row)}
>
👁️ Details
</button>
<button
class="inline-flex items-center gap-1 rounded bg-blue-600 px-3 py-1.5 text-white text-xs font-medium hover:bg-blue-700"
on:click={() => openEditModal(row)}
>
✏️ Edit
</button>
<button
class="inline-flex items-center gap-1 rounded bg-red-600 px-3 py-1.5 text-white text-xs font-medium hover:bg-red-700"
on:click={() => deleteProject(row.id)}
>
🗑️ Delete
</button>
</td>
{:else}
<td class="px-4 py-2 text-gray-700">
{row[col.key] ?? "—"}
</td>
{/if}
{/each}
</tr>
{/each}
</tbody>
</table>
</div>
<!-- Pagination controls -->
<div class="flex justify-between items-center text-sm">
<div>
Showing {(currentPage - 1) * rowsPerPage + 1}
{Math.min(currentPage * rowsPerPage, totalItems)} of {totalItems} items
</div>
<div class="flex items-center space-x-4">
<div class="space-x-2">
<label for="rowsPerPage" class="text-sm">Rows per page:</label>
<select
id="rowsPerPage"
class="border border-gray-300 px-2 py-1 rounded text-sm"
bind:value={rowsPerPage}
on:change={() => {
currentPage = 1; // Reset to first page on change
fetchPurchaseOrder(
selectedVillaId,
searchTerm,
sortColumn,
sortOrder,
0,
rowsPerPage,
selectedStatus,
);
}}
>
{#each [10, 20, 50, 100] as option}
<option value={option}>{option}</option>
{/each}
</select>
<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 border-blue-600'
: 'bg-white border-gray-300 hover:bg-gray-100'}"
>
{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>
</div>
</div>
{#if showModal}
<div
class="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-50 z-50"
>
<div
class="bg-white p-6 rounded shadow-lg w-[600px] max-h-[90vh] overflow-y-auto space-y-4"
>
<h3 class="text-lg font-semibold mb-4">
{isEditing ? "Edit Project" : "Add Project"}
</h3>
<form on:submit|preventDefault={saveProject}>
<!-- choose issuess -->
<div class="mb-4">
<label
for="issue_id"
class="block text-sm font-medium text-gray-700 mb-1"
>
Choose Issue
</label>
<select
id="issue_id"
bind:value={newPurchaseOrders.issue_id}
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500"
on:change={(e: Event) => {
const selectedIssue =
(e.target as HTMLSelectElement)?.value ?? "";
newPurchaseOrders.issue_id = selectedIssue;
}}
disabled
>
<option value="">Select Issue</option>
{#each issues as issue}
<option value={issue.id}>{issue.name}</option>
{/each}
</select>
</div>
{#each formColumns as col}
{#if col.key === "po_status"}
<div class="mb-4">
<label
for={col.key}
class="block text-sm font-medium text-gray-700 mb-1"
>
{col.title}
</label>
<select
id={col.key}
bind:value={newPurchaseOrders[col.key]}
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500"
on:change={(e: Event) => {
const selectedOption =
(e.target as HTMLSelectElement)
?.value ?? "";
const option =
getStatusOption(selectedOption);
if (
option?.value === "APPROVED" &&
!validateInput()
) {
e.preventDefault();
return;
}
}}
>
{#each statusOptions as option}
<option
value={option.value}
style="background-color: {option.color};"
>
{option.label}
</option>
{/each}
</select>
</div>
{:else if col.key === "po_type"}
<div class="mb-4">
<label
for={col.key}
class="block text-sm font-medium text-gray-700 mb-1"
>
{col.title}
</label>
<select
id={col.key}
bind:value={newPurchaseOrders[col.key]}
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500"
>
<option value="">Select PO Type</option>
<option value="Regular">Regular</option>
<option value="Urgent">Urgent</option>
</select>
</div>
{:else if col.key === "prepared_date"}
<div class="mb-4">
<label
for={col.key}
class="block text-sm font-medium text-gray-700 mb-1"
>
{col.title}
</label>
<input
type="date"
id={col.key}
bind:value={newPurchaseOrders[col.key]}
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500"
/>
</div>
{:else if col.key === "po_price"}
<div class="mb-4">
<label
for={col.key}
class="block text-sm font-medium text-gray-700 mb-1"
>
{col.title}
</label>
<input
name="po_price"
placeholder="Enter PO Price"
type="number"
id={col.key}
bind:value={newPurchaseOrders[col.key]}
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500"
/>
</div>
{:else if col.key === "po_quantity"}
<div class="mb-4">
<label
for={col.key}
class="block text-sm font-medium text-gray-700 mb-1"
>
{col.title}
</label>
<input
name="po_quantity"
placeholder="Enter PO Quantity"
type="number"
id={col.key}
bind:value={newPurchaseOrders[col.key]}
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500"
/>
</div>
{:else if col.key === "po_total_price"}
<div class="mb-4">
<label
for={col.key}
class="block text-sm font-medium text-gray-700 mb-1"
>
{col.title}
</label>
<!-- calculate po quantity * price -->
<input
name="po_total_price"
type="number"
id={col.key}
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500"
readonly
value={newPurchaseOrders.po_quantity *
newPurchaseOrders.po_price}
/>
</div>
{:else if col.key === "approved_price"}
<div class="mb-4">
<label
for={col.key}
class="block text-sm font-medium text-gray-700 mb-1"
>
{col.title}
</label>
<input
type="number"
id={col.key}
bind:value={newPurchaseOrders[col.key]}
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500"
/>
</div>
{:else if col.key === "approved_vendor"}
<div class="mb-4">
<label
for={col.key}
class="block text-sm font-medium text-gray-700 mb-1"
>
{col.title}
</label>
<select
id={col.key}
bind:value={newPurchaseOrders[col.key]}
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500"
on:change={(e: Event) => {
const selectedVendor =
(e.target as HTMLSelectElement)
?.value ?? "";
newPurchaseOrders[col.key] = selectedVendor;
}}
>
<option value="">Select Vendor</option>
{#each vendors as vendor}
<option value={vendor.id}>
{vendor.name}
</option>
{/each}
</select>
</div>
{:else}
<div class="mb-4">
<label
for={col.key}
class="block text-sm font-medium text-gray-700 mb-1"
>
{col.title}
</label>
<input
type="text"
id={col.key}
bind:value={newPurchaseOrders[col.key]}
class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500"
/>
</div>
{/if}
{/each}
<div class="flex justify-end space-x-2">
<button
type="button"
class="px-4 py-2 bg-gray-200 text-gray-700 rounded hover:bg-gray-300"
on:click={() => (showModal = false)}
>
Cancel
</button>
<button
type="submit"
class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Save
</button>
</div>
</form>
</div>
</div>
{/if}
{#if showPreparedModal}
<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-lg font-bold">Set Prepared</h2>
<!-- prepared_by -->
<label>Prepared By</label>
<select
bind:value={preparedForm.prepared_by}
class="w-full border p-2"
>
<option value="" disabled>Select Employee</option>
{#each preparedByOptions as emp}
<option value={emp.id}>{emp.employee_name}</option>
{/each}
</select>
<!-- prepared_date -->
<label>Prepared Date</label>
<input
type="date"
bind:value={preparedForm.prepared_date}
class="w-full border p-2"
/>
<!-- po_item -->
<label>PO Item</label>
<select
bind:value={preparedForm.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>
<!-- po_quantity -->
<label>PO Quantity</label>
<input
type="number"
bind:value={preparedForm.po_quantity}
class="w-full border p-2"
/>
<!-- Vendors and prices -->
<label>Q1 Vendor</label>
<select
bind:value={preparedForm.q1_vendor}
class="w-full border p-2"
>
<option value="" disabled>Select Vendor</option>
{#each vendorOptions as v}
<option value={v.name}>{v.name}</option>
{/each}
</select>
<CurrencyInput
bind:value={preparedForm.q1_vendor_price}
label="Q1 Vendor Price"
/>
<label>Q2 Vendor</label>
<select
bind:value={preparedForm.q2_vendor}
class="w-full border p-2"
>
<option value="" disabled>Select Vendor</option>
{#each vendorOptions as v}
<option value={v.name}>{v.name}</option>
{/each}
</select>
<CurrencyInput
bind:value={preparedForm.q2_vendor_price}
label="Q1 Vendor Price"
/>
<label>Q3 Vendor</label>
<select
bind:value={preparedForm.q3_vendor}
class="w-full border p-2"
>
<option value="" disabled>Select Vendor</option>
{#each vendorOptions as v}
<option value={v.name}>{v.name}</option>
{/each}
</select>
<CurrencyInput
bind:value={preparedForm.q3_vendor_price}
label="Q1 Vendor Price"
/>
<!-- Repeat for Q2, Q3 -->
<!-- Approved -->
<label>Approved Quantity</label>
<input
type="number"
bind:value={preparedForm.approved_quantity}
on:input={() => updateTotalAmount()}
class="w-full border p-2"
/>
<label>Approved Vendor</label>
<select
bind:value={preparedForm.approved_vendor}
class="w-full border p-2"
>
<option value="" disabled>Select Vendor</option>
{#each vendorOptions as v}
<option value={v.name}>{v.name}</option>
{/each}
</select>
<CurrencyInput
bind:value={preparedForm.approved_price}
label="Approved Price"
onInput={updateTotalAmount}
/>
<label>Total Approved Order Amount</label>
<input
type="number"
value={preparedForm.total_approved_order_amount}
disabled
class="w-full border p-2 bg-gray-100"
/>
<label>PO Remark</label>
<textarea
bind:value={preparedForm.po_remark}
class="w-full border p-2"
></textarea>
<div class="flex justify-end space-x-2 pt-4">
<button
on:click={savePrepared}
class="bg-blue-600 text-white px-4 py-2 rounded"
>Save</button
>
<button
on:click={() => (showPreparedModal = false)}
class="px-4 py-2 rounded border">Cancel</button
>
</div>
</div>
</div>
</div>
{/if}
{#if showPaymentModal}
<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">Set Payment</h2>
<!-- Payment Method -->
<label>Payment Method</label>
<select
bind:value={paymentForm.payment_method}
class="w-full border p-2"
>
<option value="" disabled>Select Method</option>
<option value="Gradualy">Gradualy</option>
<option value="Full Before Received"
>Full Before Received</option
>
<option value="Full After Received"
>Full After Received</option
>
</select>
<!-- Total Approved Order Amount (hidden input, readonly) -->
<label>Total Approved Order Amount</label>
<input
type="number"
value={paymentForm.total_approved_order_amount}
disabled
class="w-full border p-2 bg-gray-100"
/>
<!-- Payment Amounts -->
{#each [1, 2, 3, 4, 5, 6] as num}
<label>{ordinal(num)} Pay Amount</label>
<CurrencyInput
bind:value={paymentForm[`${ordinal(num)}_pay_amt`]}
onInput={updateDueRemaining}
className="w-full"
/>
<label>{ordinal(num)} Pay Date</label>
<input
type="date"
bind:value={paymentForm[`${ordinal(num)}_pay_date`]}
class="w-full border p-2"
/>
{/each}
<!-- Due Remaining -->
<label>Due Remaining</label>
<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">
<button
on:click={savePayment}
class="bg-blue-600 text-white px-4 py-2 rounded"
>Save</button
>
<button
on:click={() => (showPaymentModal = false)}
class="px-4 py-2 rounded border">Cancel</button
>
</div>
</div>
</div>
</div>
{/if}
{#if showApprovalModal}
<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-md max-h-[90vh] overflow-y-auto p-6 space-y-4"
>
<h2 class="text-xl font-bold">Approve Purchase Order</h2>
<!-- PO Number -->
<label>PO Number</label>
<input
type="text"
value={approvalForm.po_number}
disabled
class="w-full border p-2 bg-gray-100"
/>
<!-- 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
bind:value={approvalForm.reject_comment}
class="w-full border p-2"
rows="3"
placeholder="Enter reason for rejection..."
></textarea>
{/if}
<!-- 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 fields -->
<input type="hidden" value={approvalForm.approved_by} />
<input type="hidden" value={approvalForm.approved_date} />
<!-- Actions -->
<div class="flex justify-end space-x-2 pt-4">
<button
on:click={saveApproval}
class="bg-blue-600 text-white px-4 py-2 rounded"
>Save</button
>
<button
on:click={() => (showApprovalModal = false)}
class="px-4 py-2 rounded border">Cancel</button
>
</div>
</div>
</div>
</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">
<div
class="bg-white rounded-lg shadow-lg w-full max-w-md max-h-[90vh] overflow-y-auto p-6 space-y-4"
>
<h2 class="text-xl font-bold">Acknowledge Purchase Order</h2>
<!-- PO Number -->
<label>PO Number</label>
<input
type="text"
value={acknowledgedForm.po_number}
disabled
class="w-full border p-2 bg-gray-100"
/>
<!-- Acknowledged -->
<label>Acknowledged</label>
<select
bind:value={acknowledgedForm.acknowledged}
class="w-full border p-2"
>
<option value="" disabled>Select Acknowledgement</option>
<option value="acknowledged">Acknowledged</option>
<option value="reject">Reject</option>
</select>
<!-- Reject Comment -->
{#if acknowledgedForm.acknowledged === "reject"}
<label>Reject Comment</label>
<textarea
bind:value={acknowledgedForm.reject_comment}
class="w-full border p-2"
rows="3"
placeholder="Enter reason for rejection..."
></textarea>
{/if}
<!-- PO Item -->
{#if acknowledgedForm.po_item}
<label>PO Item</label>
<input
type="text"
value={acknowledgedForm.po_item}
disabled
class="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Approved Quantity -->
{#if acknowledgedForm.approved_quantity != null}
<label>Approved Quantity</label>
<input
type="number"
value={acknowledgedForm.approved_quantity}
disabled
class="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Approved Price -->
{#if acknowledgedForm.approved_price != null}
<label>Approved Price</label>
<CurrencyInput
value={acknowledgedForm.approved_price}
disabled
className="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Total Approved Order Amount -->
{#if acknowledgedForm.total_approved_order_amount != null}
<label>Total Approved Order Amount</label>
<CurrencyInput
value={acknowledgedForm.total_approved_order_amount}
disabled
className="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Approved Vendor -->
{#if acknowledgedForm.approved_vendor}
<label>Approved Vendor</label>
<input
type="text"
value={acknowledgedForm.approved_vendor}
disabled
class="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Hidden fields -->
<input type="hidden" value={acknowledgedForm.acknowledged_by} />
<input
type="hidden"
value={acknowledgedForm.acknowledged_date}
/>
<!-- Actions -->
<div class="flex justify-end space-x-2 pt-4">
<button
on:click={saveAcknowledged}
class="bg-blue-600 text-white px-4 py-2 rounded"
>Save</button
>
<button
on:click={() => (showAcknowledgedModal = false)}
class="px-4 py-2 rounded border">Cancel</button
>
</div>
</div>
</div>
</div>
{/if}
{#if showCompletedModal}
<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-md max-h-[90vh] overflow-y-auto p-6 space-y-4"
>
<h2 class="text-xl font-bold">Complete Purchase Order</h2>
<!-- PO Number -->
<label>PO Number</label>
<input
type="text"
value={completedForm.po_number}
disabled
class="w-full border p-2 bg-gray-100"
/>
<!-- Completed -->
<label>Completion</label>
<select
bind:value={completedForm.completed}
class="w-full border p-2"
>
<option value="" disabled>Select Status</option>
<option value="completed">Completed</option>
<option value="reject">Reject</option>
</select>
<!-- Reject Comment -->
{#if completedForm.completed === "reject"}
<label>Reject Comment</label>
<textarea
bind:value={completedForm.reject_comment}
class="w-full border p-2"
rows="3"
placeholder="Enter reason for rejection..."
></textarea>
{/if}
<!-- PO Item -->
{#if completedForm.po_item}
<label>PO Item</label>
<input
type="text"
value={completedForm.po_item}
disabled
class="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Approved Quantity -->
{#if completedForm.approved_quantity != null}
<label>Approved Quantity</label>
<input
type="number"
value={completedForm.approved_quantity}
disabled
class="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Approved Price -->
{#if completedForm.approved_price != null}
<label>Approved Price</label>
<CurrencyInput
value={completedForm.approved_price}
disabled
className="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Total Approved Order Amount -->
{#if completedForm.total_approved_order_amount != null}
<label>Total Approved Order Amount</label>
<CurrencyInput
value={completedForm.total_approved_order_amount}
disabled
className="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Approved Vendor -->
{#if completedForm.approved_vendor}
<label>Approved Vendor</label>
<input
type="text"
value={completedForm.approved_vendor}
disabled
class="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Hidden -->
<input type="hidden" value={completedForm.completed_by} />
<input type="hidden" value={completedForm.completed_date} />
<!-- Actions -->
<div class="flex justify-end space-x-2 pt-4">
<button
on:click={saveCompleted}
class="bg-blue-600 text-white px-4 py-2 rounded"
>Save</button
>
<button
on:click={() => (showCompletedModal = false)}
class="px-4 py-2 rounded border">Cancel</button
>
</div>
</div>
</div>
</div>
{/if}
{#if showReceivedModal}
<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-md max-h-[90vh] overflow-y-auto p-6 space-y-4"
>
<h2 class="text-xl font-bold">Receive Purchase Order</h2>
<!-- PO Number -->
<label>PO Number</label>
<input
type="text"
value={receivedForm.po_number}
disabled
class="w-full border p-2 bg-gray-100"
/>
<!-- Received -->
<label>Received</label>
<select
bind:value={receivedForm.received}
class="w-full border p-2"
>
<option value="" disabled>Select Status</option>
<option value="received">Received</option>
<option value="reject">Reject</option>
</select>
<!-- PO Item -->
{#if receivedForm.po_item}
<label>PO Item</label>
<input
type="text"
value={receivedForm.po_item}
disabled
class="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Approved Quantity -->
{#if receivedForm.approved_quantity != null}
<label>Approved Quantity</label>
<input
type="number"
value={receivedForm.approved_quantity}
disabled
class="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Approved Price -->
{#if receivedForm.approved_price != null}
<label>Approved Price</label>
<CurrencyInput
value={receivedForm.approved_price}
disabled
className="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Total Approved Order Amount -->
{#if receivedForm.total_approved_order_amount != null}
<label>Total Approved Order Amount</label>
<CurrencyInput
value={receivedForm.total_approved_order_amount}
disabled
className="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Approved Vendor -->
{#if receivedForm.approved_vendor}
<label>Approved Vendor</label>
<input
type="text"
value={receivedForm.approved_vendor}
disabled
class="w-full border p-2 bg-gray-100"
/>
{/if}
<!-- Hidden -->
<input type="hidden" value={receivedForm.received_by} />
<input type="hidden" value={receivedForm.received_date} />
<!-- Actions -->
<div class="flex justify-end space-x-2 pt-4">
<button
on:click={saveReceived}
class="bg-blue-600 text-white px-4 py-2 rounded"
>Save</button
>
<button
on:click={() => (showReceivedModal = false)}
class="px-4 py-2 rounded border">Cancel</button
>
</div>
</div>
</div>
</div>
{/if}
{#if showEditModal}
<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">Edit Purchase Order</h2>
<!-- PO Number -->
<label>PO Number</label>
<input
type="text"
value={editForm.po_number}
disabled
class="w-full border p-2 bg-gray-100"
/>
<!-- Issue Name -->
<label>Issue Name</label>
<input
type="text"
value={editForm.issue_name}
disabled
class="w-full border p-2 bg-gray-100"
/>
<!-- Villa ID -->
<label>Villa ID</label>
<input
type="text"
value={editForm.villa_name}
disabled
class="w-full border p-2 bg-gray-100"
/>
<!-- PO Status -->
<label>PO Status</label>
<input
type="text"
value={editForm.po_status}
disabled
class="w-full border p-2 bg-gray-100"
/>
<!-- Status Toggles -->
<div class="space-y-2">
{#each ["prepared", "approved", "acknowledged", "completed", "received"] as key}
<div class="flex items-center gap-2">
<label class="w-32 capitalize">{key}</label>
<select
bind:value={editForm[key]}
class="w-40 border p-2"
>
<option value={null}>Unset</option>
<option value={true}>True</option>
<option value={false}>False</option>
</select>
<button
type="button"
class="text-xs text-blue-600 underline"
on:click={() => (editForm[key] = null)}
>Reset</button
>
</div>
{/each}
</div>
<!-- PO Remark -->
<label>PO Remark</label>
<textarea
bind:value={editForm.po_remark}
class="w-full border p-2"
rows="3"
></textarea>
<!-- Actions -->
<div class="flex justify-end space-x-2 pt-4">
<button
on:click={saveEdit}
class="bg-blue-600 text-white px-4 py-2 rounded"
>Save</button
>
<button
on:click={() => (showEditModal = false)}
class="px-4 py-2 rounded border">Cancel</button
>
</div>
</div>
</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}
{#if showDetailsModal}
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div class="bg-white w-full max-w-3xl max-h-[90vh] overflow-y-auto rounded-2xl shadow-lg p-6">
<div class="flex items-start justify-between mb-4">
<h2 class="text-xl font-bold">Purchase Order Details</h2>
<button class="text-gray-500 hover:text-gray-700" on:click={closeDetailsModal}>✕</button>
</div>
<!-- Top detail fields -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm text-gray-600">PO Number</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.purchase_order_number ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">PO Status</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.po_status ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Villa Name</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.villa_data ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">PO Type</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.po_type ?? "—"} />
</div>
<div class="md:col-span-2">
<label class="block text-sm text-gray-600">Related Issue</label>
<textarea class="w-full border rounded p-2 bg-gray-100" rows="3" readonly>
{detailsRow?.po_issue_name ?? "no issue"}</textarea>
</div>
<div>
<label class="block text-sm text-gray-600">PO Item</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.po_item ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">PO Qty</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.po_quantity ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Request Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.requested_date ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Request By</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.requested_by_name ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">PO Due Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.po_due ?? "—"} />
</div>
</div>
<!-- Prepared section -->
<div class="my-6 border-t pt-4">
<h3 class="text-lg font-semibold mb-3">Prepared Section</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm text-gray-600">Prepared Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.prepared_date ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Prepared By</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.prepared_by_name ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Q1 Vendor</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.q1_vendor ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Q1 Vendor Price</label>
<CurrencyInput
value={detailsRow?.q1_vendor_price ?? "—"}
disabled
className="w-full border p-2 bg-gray-100"
/>
</div>
<div>
<label class="block text-sm text-gray-600">Q2 Vendor</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.q2_vendor ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Q2 Vendor Price</label>
<CurrencyInput
value={detailsRow?.q2_vendor_price ?? "—"}
disabled
className="w-full border p-2 bg-gray-100"
/>
</div>
<div>
<label class="block text-sm text-gray-600">Q3 Vendor</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.q3_vendor ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Q3 Vendor Price</label>
<CurrencyInput
value={detailsRow?.q3_vendor_price ?? "—"}
disabled
className="w-full border p-2 bg-gray-100"
/>
</div>
<div class="md:col-span-2">
<label class="block text-sm text-gray-600">Remarks</label>
<textarea class="w-full border rounded p-2 bg-gray-100" rows="3" readonly>
{detailsRow?.po_remark ?? "—"}</textarea>
</div>
<!-- Newly added approved vendor/pricing fields -->
<div>
<label class="block text-sm text-gray-600">Approved Vendor</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.approved_vendor ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Approved Price</label>
<CurrencyInput
value={detailsRow?.approved_price ?? "—"}
disabled
className="w-full border p-2 bg-gray-100"
/>
</div>
<div>
<label class="block text-sm text-gray-600">Approved Quantity</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.approved_quantity ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Approved Total Price</label>
<CurrencyInput
value={detailsRow?.total_approved_order_amount ?? "—"}
disabled
className="w-full border p-2 bg-gray-100"
/>
</div>
</div>
</div>
<!-- Payment section -->
<div class="my-6 border-t pt-4">
<h3 class="text-lg font-semibold mb-3">Payment Section</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm text-gray-600">Payment Method</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.payment_method ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Full Payment Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.full_pay_date ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">1st Payment Amount</label>
<CurrencyInput
value={detailsRow?.["1st_pay_amt"] ?? "—"}
disabled
className="w-full border p-2 bg-gray-100"
/>
</div>
<div>
<label class="block text-sm text-gray-600">1st Payment Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.["1st_pay_date"] ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">2nd Payment Amount</label>
<CurrencyInput
value={detailsRow?.["2rd_pay_amt"] ?? "—"}
disabled
className="w-full border p-2 bg-gray-100"
/>
</div>
<div>
<label class="block text-sm text-gray-600">2nd Payment Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.["2rd_pay_date"] ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">3rd Payment Amount</label>
<CurrencyInput
value={detailsRow?.["3rd_pay_amt"] ?? "—"}
disabled
className="w-full border p-2 bg-gray-100"
/>
</div>
<div>
<label class="block text-sm text-gray-600">3rd Payment Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.["3rd_pay_date"] ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">4th Payment Amount</label>
<CurrencyInput
value={detailsRow?.["4th_pay_amt"] ?? "—"}
disabled
className="w-full border p-2 bg-gray-100"
/>
</div>
<div>
<label class="block text-sm text-gray-600">4th Payment Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.["4th_pay_date"] ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">5th Payment Amount</label>
<CurrencyInput
value={detailsRow?.["5th_pay_amt"] ?? "—"}
disabled
className="w-full border p-2 bg-gray-100"
/>
</div>
<div>
<label class="block text-sm text-gray-600">5th Payment Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.["5th_pay_date"] ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">6th Payment Amount</label>
<CurrencyInput
value={detailsRow?.["6th_pay_amt"] ?? "—"}
disabled
className="w-full border p-2 bg-gray-100"
/>
</div>
<div>
<label class="block text-sm text-gray-600">6th Payment Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.["6th_pay_date"] ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Due Payment</label>
<CurrencyInput
value={detailsRow?.due_remaining ?? "—"}
disabled
className="w-full border p-2 bg-gray-100"
/>
</div>
</div>
</div>
<!-- Approval section -->
<div class="my-6 border-t pt-4">
<h3 class="text-lg font-semibold mb-3">Approval Section</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm text-gray-600">Approved By</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.approved_name ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Approved Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.approved_date ?? "—"} />
</div>
</div>
</div>
<!-- Acknowledged section -->
<div class="my-6 border-t pt-4">
<h3 class="text-lg font-semibold mb-3">Acknowledged Section</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm text-gray-600">Acknowledged By</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.acknowledged_name ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Acknowledged Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.acknowledged_date ?? "—"} />
</div>
</div>
</div>
<!-- Received section -->
<div class="my-6 border-t pt-4">
<h3 class="text-lg font-semibold mb-3">Received Section</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm text-gray-600">Received By</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.received_name ?? "—"} />
</div>
<div>
<label class="block text-sm text-gray-600">Received Date</label>
<input class="w-full border rounded p-2 bg-gray-100" readonly value={detailsRow?.received_date ?? "—"} />
</div>
</div>
</div>
<div class="flex justify-end mt-4">
<button class="px-4 py-2 rounded border hover:bg-gray-100" on:click={closeDetailsModal}>Close</button>
</div>
</div>
</div>
{/if}