Files
vberp/src/routes/backoffice/purchaseorder/+page.svelte
aji@catalis.app 2ad0f5093d perbaikan data
2025-06-22 12:26:43 +07:00

1165 lines
45 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 Select from "svelte-select";
import { supabase } from "$lib/supabaseClient";
import { timestampToDateTime } from "$lib/utils/conversion";
import { getSessionAuthId } from "$lib/utils/authUtil";
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;
};
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(),
};
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;
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;
};
let allRows: PurchaseOrderDisplay[] = [];
type columns = {
key: string;
title: string;
};
const columns: columns[] = [
{ key: "name", title: "Name" },
{ key: "purchase_order_number", title: "Purchase Order Number" },
{ key: "villa_name", title: "Villa Name" },
{ key: "priority", title: "Priority" },
{ key: "prepared_date", title: "Prepared Date" },
{ key: "po_type", title: "PO Type" },
{ key: "po_quantity", title: "PO Quantity" },
{ key: "po_price", title: "PO Price" },
{ key: "po_total_price", title: "PO Total Price" },
{ key: "po_status", title: "PO Status" },
{ key: "proses_to_approval", title: "PROSES TO APPROVAL" },
{ key: "approved_vendor", title: "Approved Vendor" },
{ key: "acknowledged", title: "Acknowledged" },
{ key: "acknowledged_name", title: "Acknowledged By" },
{ key: "approved_name", title: "Approved By" },
{ key: "approved_price", title: "Approved Price" },
{ key: "approved_quantity", title: "Approved Quantity" },
{
key: "total_approved_order_amount",
title: "Total Approved Order Amount",
},
{ key: "approval", title: "Approval" },
{ key: "completed_status", title: "Completed Status" },
{ key: "received", title: "Received" },
{ key: "received_name", title: "Received By" },
{ key: "inputed_name", title: "Input By" },
{ key: "updated_name", title: "Updated By" },
{ key: "updated_at", title: "Updated At" },
{ key: "created_at", title: "Created At" },
{ key: "actions", title: "Actions" }, // For edit/delete buttons
];
let currentPage = 1;
let rowsPerPage = 10;
$: totalPages = Math.ceil(allRows.length / rowsPerPage);
$: paginatedRows = allRows.slice(
(currentPage - 1) * rowsPerPage,
currentPage * rowsPerPage,
);
function goToPage(page: number) {
if (page >= 1 && page <= totalPages) currentPage = page;
}
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,
) {
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;
}
// 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;
});
}
//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,
}));
}
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,
}));
}
onMount(() => {
fetchPurchaseOrder();
fetchVendors();
});
$: currentPage = 1; // Reset to first page when allRows changes
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 }[] = [];
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",
];
const formColumns = columns.filter(
(col) => !excludedKeys.includes(col.key),
);
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;
}
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 { error } = await supabase
.from("vb_purchase_orders")
.delete()
.eq("id", id);
if (error) {
console.error("Error deleting project:", error);
return;
}
await fetchPurchaseOrder();
}
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" },
];
function getStatusOption(value: string) {
return statusOptions.find((option) => option.value === value) ?? null;
}
//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;
}
function validateInputApproval() {
const requiredFields = ["approved_price"];
for (const field of requiredFields) {
if (!newPurchaseOrders[field]) {
alert(`Please fill in the ${field} field.`);
return false;
}
}
return true;
}
function validateInputProsesToAprroval() {
const requiredFields = ["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;
}
async function updateProsesToApproval(id: string, status: boolean) {
const sessionId = await getSessionAuthId();
if (!sessionId) {
alert("You must be logged in to perform this action.");
return;
}
if (!validateInputProsesToAprroval()) {
return;
}
const { data, error } = await supabase
.from("vb_purchase_orders")
.update({
proses_to_approval: status,
po_status: "REQUESTED",
updated_by: sessionId,
updated_at: new Date().toISOString(),
})
.eq("id", id);
if (error) {
console.error(
"Error updating purchase order proses to approval:",
error,
);
return;
}
await fetchPurchaseOrder();
}
async function updatePurchaseOrderStatus(
e: Event,
id: string,
row: PurchaseOrderDisplay,
) {
const sessionId = await getSessionAuthId();
if (!sessionId) {
alert("You must be logged in to perform this action.");
return;
}
const selectedOption = (e.target as HTMLSelectElement)?.value ?? "";
const option = getStatusOption(selectedOption);
newPurchaseOrders = {
...row,
po_status: option?.value || row.po_status,
};
if (option?.value === "APPROVED") {
if (!validateInput()) {
e.preventDefault();
return;
}
}
const { data, error } = await supabase
.from("vb_purchase_orders")
.update({ po_status: newPurchaseOrders.po_status })
.eq("id", id);
if (error) {
console.error("Error updating purchase order status:", error);
return;
}
await fetchPurchaseOrder();
}
async function acknowledgedOk(id: string, status: boolean) {
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({
acknowledged: status,
updated_by: sessionId,
updated_at: new Date().toISOString(),
})
.eq("id", id);
if (error) {
console.error("Error acknowledging purchase order:", error);
return;
}
await fetchPurchaseOrder();
}
async function receivedOk(id: string, status: boolean) {
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({
receivedOk: status,
updated_by: sessionId,
updated_at: new Date().toISOString(),
})
.eq("id", id);
if (error) {
console.error("Error acknowledging purchase order:", error);
return;
}
await fetchPurchaseOrder();
}
async function updatePurchaseOrderApprovalStatus(
e: Event,
id: string,
row: PurchaseOrderDisplay,
) {
const sessionId = await getSessionAuthId();
if (!sessionId) {
alert("You must be logged in to perform this action.");
return;
}
const selectedOption = (e.target as HTMLSelectElement)?.value ?? "";
const option = getStatusOption(selectedOption);
newPurchaseOrders = {
...row,
approval: option?.value || row.approval,
};
if (option?.value === "APPROVED") {
if (!validateInputApproval()) {
e.preventDefault();
return;
}
}
const { data, error } = await supabase
.from("vb_purchase_orders")
.update({
approval: newPurchaseOrders.approval,
updated_by: sessionId,
updated_at: new Date().toISOString(),
})
.eq("id", id);
if (error) {
console.error("Error updating purchase order status:", error);
return;
}
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();
}
</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"
on:input={(e) => {
const searchTerm = (
e.target as HTMLInputElement
).value.toLowerCase();
fetchPurchaseOrder(null, searchTerm, "created_at", "desc");
}}
/>
<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"
on:change={(e) => {
const filter = (e.target as HTMLSelectElement).value;
fetchPurchaseOrder(filter, null, null, "desc");
}}
>
<option value="">All Issues</option>
<option value="PROJECT">Project Issues</option>
<option value="PURCHASE_ORDER">Purchase Order Issues</option>
</select>
<button
class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 text-sm"
on:click={() => openModal()}
>
Add Purchase Order
</button>
</div>
</div>
<div class="overflow-x-auto rounded-lg shadow mb-4">
<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 === "name"}
<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;"
>
{col.title}
</th>
{:else}
<th
class="px-4 py-3 text-left font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap"
>
{col.title}
</th>
{/if}
{/each}
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
{#each paginatedRows as row}
<tr class="hover:bg-gray-50 transition">
{#each columns as col}
{#if col.key === "name"}
<td
class="sticky left-0 px-4 py-2 font-medium text-blue-600"
style="background-color: #f0f8ff; cursor: pointer;"
>
{row[col.key as keyof PurchaseOrderDisplay]}
</td>
{:else if col.key === "approval"}
<td class="px-4 py-2">
<select
bind:value={
row[
col.key as keyof PurchaseOrderDisplay
]
}
class="w-full p-3 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500 text-gray-900"
on:change={(e: Event) => {
updatePurchaseOrderApprovalStatus(
e,
row.id,
row,
);
}}
disabled
>
<option value="" disabled selected
>ON PROSES</option
>
<option value="APPROVED"
>APPROVED</option
>
<option value="REJECTED"
>REJECTED</option
>
</select>
</td>
{:else if col.key === "proses_to_approval"}
<td class="px-4 py-2">
<!-- checkbox -->
<input
type="checkbox"
checked={row.proses_to_approval}
on:change={(e) => {
const isChecked = (
e.target as HTMLInputElement
).checked;
if (isChecked) {
newPurchaseOrders = {
...row,
proses_to_approval: true,
};
// map to project
updateProsesToApproval(
row.id,
isChecked,
);
} else {
newPurchaseOrders = {
...row,
proses_to_approval: false,
};
// uncheck
updateProsesToApproval(
row.id,
false,
);
}
}}
disabled={row.proses_to_approval}
/>
</td>
{:else if col.key === "acknowledged"}
<td class="px-4 py-2 text-center">
<input
type="checkbox"
checked={row.acknowledged}
on:change={async (e) => {
const isChecked = (
e.target as HTMLInputElement
).checked;
row.acknowledged = isChecked;
if (isChecked) {
// map to project
await acknowledgedOk(
row.id,
isChecked,
);
}
}}
disabled
/>
</td>
{:else if col.key === "received"}
<td class="px-4 py-2 text-center">
<input
type="checkbox"
checked={row.received}
on:change={async (e) => {
const isChecked = (
e.target as HTMLInputElement
).checked;
row.received = isChecked;
if (isChecked) {
// map to project
await receivedOk(
row.id,
isChecked,
);
}
}}
disabled
/>
</td>
{:else if col.key === "completed_status"}
<td class="px-4 py-2">
<select
bind:value={
row[
col.key as keyof PurchaseOrderDisplay
]
}
class="w-full p-3 border border-gray-300 rounded focus:outline-none focus:ring focus:ring-blue-500 text-gray-900"
on:change={async (e) => {
const isValue = (
e.target as HTMLInputElement
).value;
if (isValue) {
// map to project
await completedStatusOk(
row.id,
isValue,
);
}
}}
disabled
>
<option value="" disabled selected
>ON PROSES</option
>
<option value="RECEIVED COMPLETE"
>RECEIVED COMPLETE</option
>
<option value="RECEIVED INCOMPLETE"
>COMPLETE INCOMPLETE</option
>
</select>
</td>
{:else if col.key === "updated_at"}
<td class="px-4 py-2 text-gray-500"
>{new Date(
row[
col.key as keyof PurchaseOrderDisplay
] as string,
).toLocaleString()}</td
>
{:else if col.key === "actions"}
<td class="px-4 py-2">
<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={() => openModal(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 if col.key === "created_at"}
<td class="px-4 py-2 text-gray-500"
>{new Date(
row[
col.key as keyof PurchaseOrderDisplay
] as string,
).toLocaleString()}</td
>
{:else}
<td class="px-4 py-2 text-gray-700"
>{row[
col.key as keyof PurchaseOrderDisplay
]}</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, allRows.length)} of {allRows.length}
</div>
<div class="space-x-2">
<button
class="px-3 py-1 rounded border border-gray-300 bg-white hover:bg-gray-100 disabled:opacity-50"
on:click={() => goToPage(currentPage - 1)}
disabled={currentPage === 1}
>
Previous
</button>
{#each Array(totalPages)
.fill(0)
.map((_, i) => i + 1) as page}
<button
class="px-3 py-1 rounded border text-sm
{currentPage === page
? 'bg-blue-600 text-white border-blue-600'
: 'bg-white border-gray-300 hover:bg-gray-100'}"
on:click={() => goToPage(page)}
>
{page}
</button>
{/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>
{#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}