rev timesheet & project
This commit is contained in:
32
src/lib/CurrencyInput.svelte
Normal file
32
src/lib/CurrencyInput.svelte
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export let value: number = 0; // The raw number
|
||||||
|
export let label: string = ""; // Field label
|
||||||
|
export let onInput: (() => void) | null = null; // Optional extra handler
|
||||||
|
|
||||||
|
let formatted = "";
|
||||||
|
|
||||||
|
// Format whenever value changes
|
||||||
|
$: formatted = `Rp ${value.toLocaleString("id-ID", {
|
||||||
|
minimumFractionDigits: 0
|
||||||
|
})}`;
|
||||||
|
|
||||||
|
function handleInput(e: Event) {
|
||||||
|
let raw = (e.target as HTMLInputElement).value;
|
||||||
|
raw = raw.replace(/^Rp\s?/, "").replace(/[^\d]/g, "");
|
||||||
|
value = parseInt(raw) || 0;
|
||||||
|
formatted = `Rp ${value.toLocaleString("id-ID")}`;
|
||||||
|
|
||||||
|
// ✅ If extra handler provided, run it
|
||||||
|
if (onInput) onInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<label class="block text-sm font-medium text-gray-700">{label}</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
bind:value={formatted}
|
||||||
|
placeholder="Rp 0"
|
||||||
|
class="w-full border p-2 rounded"
|
||||||
|
on:input={handleInput}
|
||||||
|
/>
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
import { supabase } from "$lib/supabaseClient";
|
import { supabase } from "$lib/supabaseClient";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
|
import Pagination from "$lib/Pagination.svelte";
|
||||||
|
|
||||||
const priority = [
|
const priority = [
|
||||||
{ label: "Low", value: "Low" },
|
{ label: "Low", value: "Low" },
|
||||||
@@ -137,8 +138,9 @@
|
|||||||
{ label: "Water Feature", value: "Water Feature" }
|
{ label: "Water Feature", value: "Water Feature" }
|
||||||
];
|
];
|
||||||
const columns: columns[] = [
|
const columns: columns[] = [
|
||||||
{ key: "description_of_the_issue", title: "Description of The Issue" },
|
|
||||||
{ key: "issue_number", title: "Issue Number" },
|
{ key: "issue_number", title: "Issue Number" },
|
||||||
|
{ key: "description_of_the_issue", title: "Description of The Issue" },
|
||||||
{ key: "villa_name", title: "Villa Name" },
|
{ key: "villa_name", title: "Villa Name" },
|
||||||
{ key: "villa_id", title: "Villa ID" },
|
{ key: "villa_id", title: "Villa ID" },
|
||||||
{ key: "input_by", title: "Input By" },
|
{ key: "input_by", title: "Input By" },
|
||||||
@@ -253,7 +255,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Reactive variables
|
// Reactive variables
|
||||||
let currentVillaFilter: string | null = null;
|
let currentVillaFilter: string | null = "";
|
||||||
let currentSearchTerm: string | null = null;
|
let currentSearchTerm: string | null = null;
|
||||||
let showProjectModal = false;
|
let showProjectModal = false;
|
||||||
let selectedIssueId: string | null = null;
|
let selectedIssueId: string | null = null;
|
||||||
@@ -295,8 +297,31 @@
|
|||||||
let totalItems = 0;
|
let totalItems = 0;
|
||||||
let currentPage = offset + 1;
|
let currentPage = offset + 1;
|
||||||
let rowsPerPage = limit;
|
let rowsPerPage = limit;
|
||||||
$: totalPages = Math.ceil(totalItems / rowsPerPage);
|
let filteredRows: Issue[] = [];
|
||||||
|
$: paginatedRows = filteredRows.slice(
|
||||||
|
(currentPage - 1) * rowsPerPage,
|
||||||
|
currentPage * rowsPerPage
|
||||||
|
);
|
||||||
|
|
||||||
|
$: totalItems = filteredRows.length;
|
||||||
|
$: totalPages = Math.ceil(totalItems / rowsPerPage);
|
||||||
|
$: {
|
||||||
|
let rows = allRows;
|
||||||
|
|
||||||
|
if (currentVillaFilter) {
|
||||||
|
rows = rows.filter(r => r.villa_id === currentVillaFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentSearchTerm && currentSearchTerm.trim() !== "") {
|
||||||
|
const term = currentSearchTerm.toLowerCase();
|
||||||
|
rows = rows.filter(r =>
|
||||||
|
(r.description_of_the_issue || "").toLowerCase().includes(term)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredRows = rows;
|
||||||
|
currentPage = 1; // Optional: reset page when filter/search changes
|
||||||
|
}
|
||||||
// Fetch existing project links and purchase orders
|
// Fetch existing project links and purchase orders
|
||||||
async function fetchExistingProjectLinks() {
|
async function fetchExistingProjectLinks() {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
@@ -319,49 +344,21 @@
|
|||||||
if (data) poItems = data;
|
if (data) poItems = data;
|
||||||
}
|
}
|
||||||
// Fetch issues with optional filters
|
// Fetch issues with optional filters
|
||||||
async function fetchIssues(
|
async function fetchIssues() {
|
||||||
search: string | null = null,
|
|
||||||
villaNameFilter: string | null = null,
|
|
||||||
sort: string | null = "created_at",
|
|
||||||
order: "asc" | "desc" = "desc",
|
|
||||||
offset: number = 0,
|
|
||||||
limit: number = 10,
|
|
||||||
) {
|
|
||||||
let query = supabase
|
let query = supabase
|
||||||
.from("vb_issues_data")
|
.from("vb_issues_data")
|
||||||
.select("*", { count: "exact" })
|
.select("*").order("issue_number", { ascending: true });
|
||||||
.order(sort || "created_at", { ascending: order === "asc" })
|
|
||||||
.range(offset, offset + limit - 1);
|
|
||||||
|
|
||||||
if (villaNameFilter) {
|
const { data: issues, error } = await query;
|
||||||
const villa = dataVilla.find(v => v.villa_name === villaNameFilter);
|
|
||||||
if (villa) {
|
|
||||||
query = query.eq("villa_id", villa.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (search) {
|
|
||||||
query = query.ilike("description_of_the_issue", `%${search}%`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data: issues, error, count } = await query;
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error("Error fetching issues:", error);
|
console.error(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count !== undefined) {
|
allRows = issues || [];
|
||||||
totalItems = count ?? 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!issues || issues.length === 0) {
|
|
||||||
allRows = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gabungkan data villa ke dalam setiap issue
|
|
||||||
allRows = issues;
|
|
||||||
}
|
|
||||||
// Function to handle form submission and save issue
|
// Function to handle form submission and save issue
|
||||||
async function saveIssue(event: Event) {
|
async function saveIssue(event: Event) {
|
||||||
const session = await supabase.auth.getSession();
|
const session = await supabase.auth.getSession();
|
||||||
@@ -610,8 +607,6 @@
|
|||||||
requested_by: "",
|
requested_by: "",
|
||||||
requested_date: formatDate(today),
|
requested_date: formatDate(today),
|
||||||
po_due: formatDate(dueDate),
|
po_due: formatDate(dueDate),
|
||||||
po_item: "",
|
|
||||||
po_quantity: "",
|
|
||||||
po_type: ""
|
po_type: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -748,39 +743,24 @@
|
|||||||
id="issue-search"
|
id="issue-search"
|
||||||
placeholder="🔍 Search by Issue..."
|
placeholder="🔍 Search by Issue..."
|
||||||
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"
|
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) => {
|
bind:value={currentSearchTerm}
|
||||||
currentSearchTerm = (e.target as HTMLInputElement).value.toLowerCase();
|
|
||||||
fetchIssues( currentSearchTerm, currentVillaFilter);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<select
|
<select
|
||||||
id="villa-filter"
|
id="villa-filter"
|
||||||
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"
|
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) => {
|
bind:value={currentVillaFilter}
|
||||||
currentVillaFilter = (e.target as HTMLSelectElement).value || null;
|
|
||||||
fetchIssues(currentSearchTerm, currentVillaFilter);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<option value="">All Villas</option>
|
<option value="">All Villas</option>
|
||||||
{#each dataVilla as villa}
|
{#each dataVilla as villa}
|
||||||
<option value={villa.villa_name}>{villa.villa_name}</option>
|
<option value={villa.id}>{villa.villa_name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
<button
|
<button
|
||||||
class="bg-gray-200 text-gray-700 px-4 py-2 rounded-xl hover:bg-gray-300 text-sm transition"
|
class="bg-gray-200 text-gray-700 px-4 py-2 rounded-xl hover:bg-gray-300 text-sm transition"
|
||||||
on:click={() =>{
|
on:click={() =>{
|
||||||
currentVillaFilter = null;
|
currentVillaFilter = "";
|
||||||
currentSearchTerm = null;
|
currentSearchTerm = "";
|
||||||
const searchInput = document.getElementById("issue-search") as HTMLInputElement;
|
|
||||||
if (searchInput) searchInput.value = "";
|
|
||||||
|
|
||||||
const moveSelect = document.getElementById("move-filter") as HTMLSelectElement;
|
|
||||||
if (moveSelect) moveSelect.value = "";
|
|
||||||
|
|
||||||
const villaSelect = document.getElementById("villa-filter") as HTMLSelectElement;
|
|
||||||
if (villaSelect) villaSelect.value = "";
|
|
||||||
|
|
||||||
fetchIssues(null, null, null);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
🔄 Reset
|
🔄 Reset
|
||||||
@@ -798,7 +778,7 @@
|
|||||||
<thead class="bg-gray-100">
|
<thead class="bg-gray-100">
|
||||||
<tr>
|
<tr>
|
||||||
{#each formColumnsDisplay as col}
|
{#each formColumnsDisplay as col}
|
||||||
{#if col.key === "description_of_the_issue"}
|
{#if col.key === "issue_number"}
|
||||||
<th
|
<th
|
||||||
class="sticky left-0 px-4 py-3 text-left font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap"
|
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;"
|
style="background-color: #f0f8ff; z-index: 10;"
|
||||||
@@ -816,10 +796,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-gray-200 bg-white">
|
<tbody class="divide-y divide-gray-200 bg-white">
|
||||||
{#each allRows as row}
|
{#each paginatedRows as row}
|
||||||
<tr class="hover:bg-gray-50 transition">
|
<tr class="hover:bg-gray-50 transition">
|
||||||
{#each formColumnsDisplay as col}
|
{#each formColumnsDisplay as col}
|
||||||
{#if col.key === "description_of_the_issue"}
|
{#if col.key === "issue_number"}
|
||||||
<td
|
<td
|
||||||
class="sticky left-0 px-4 py-2 font-medium text-blue-600 max-w-xs whitespace-normal align-top break-words"
|
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;"
|
style="background-color: #f0f8ff; cursor: pointer;"
|
||||||
@@ -1007,39 +987,31 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Pagination controls -->
|
<!-- Pagination controls -->
|
||||||
<div class="flex justify-between items-center text-sm">
|
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-2 text-sm">
|
||||||
|
<!-- Left: Showing X–Y of Z -->
|
||||||
<div>
|
<div>
|
||||||
Showing {(currentPage - 1) * rowsPerPage + 1}–
|
Showing {(currentPage - 1) * rowsPerPage + 1}–
|
||||||
{Math.min(currentPage * rowsPerPage, allRows.length)} of {allRows.length}
|
{Math.min(currentPage * rowsPerPage, filteredRows.length)}
|
||||||
|
of {filteredRows.length}
|
||||||
</div>
|
</div>
|
||||||
<div class="space-x-2">
|
|
||||||
<button
|
<!-- Right: Rows per page & Pagination -->
|
||||||
class="px-3 py-1 rounded border border-gray-300 bg-white hover:bg-gray-100 disabled:opacity-50"
|
<div class="flex items-center space-x-2">
|
||||||
on:click={() => goToPage(currentPage - 1)}
|
<label class="text-gray-600">Rows per page:</label>
|
||||||
disabled={currentPage === 1}
|
<select
|
||||||
|
bind:value={rowsPerPage}
|
||||||
|
class="border px-2 py-1 rounded"
|
||||||
|
on:change={() => {
|
||||||
|
currentPage = 1; // Reset to first page when changing page size
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Previous
|
<option value="10">10</option>
|
||||||
</button>
|
<option value="20">20</option>
|
||||||
{#each Array(totalPages)
|
<option value="50">50</option>
|
||||||
.fill(0)
|
<option value="100">100</option>
|
||||||
.map((_, i) => i + 1) as page}
|
</select>
|
||||||
<button
|
|
||||||
class="px-3 py-1 rounded border text-sm
|
<Pagination {totalPages} {currentPage} {goToPage} />
|
||||||
{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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1486,21 +1458,6 @@
|
|||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="block">
|
|
||||||
Product *
|
|
||||||
<select bind:value={newPO.po_item} class="input w-full">
|
|
||||||
<option value="">-- Select Product --</option>
|
|
||||||
{#each poItems as item}
|
|
||||||
<option value={item.item_name}>{item.item_name}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label class="block">
|
|
||||||
Quantity *
|
|
||||||
<input type="number" bind:value={newPO.po_quantity} class="input w-full" />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label class="block">
|
<label class="block">
|
||||||
Type *
|
Type *
|
||||||
<select bind:value={newPO.po_type} class="input w-full">
|
<select bind:value={newPO.po_type} class="input w-full">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { supabase } from "$lib/supabaseClient";
|
import { supabase } from "$lib/supabaseClient";
|
||||||
import { getSessionAuthId } from "$lib/utils/authUtil";
|
import { getSessionAuthId } from "$lib/utils/authUtil";
|
||||||
|
import CurrencyInput from "$lib/CurrencyInput.svelte";
|
||||||
|
|
||||||
|
|
||||||
type PurchaseOrderInsert = {
|
type PurchaseOrderInsert = {
|
||||||
@@ -77,9 +78,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const columns: columns[] = [
|
const columns: columns[] = [
|
||||||
|
|
||||||
|
{ key: "purchase_order_number", title: "PO Number" },
|
||||||
{ key: "issue_name", title: "Issue Name" },
|
{ key: "issue_name", title: "Issue Name" },
|
||||||
{ key: "requested_date", title: "Requested Date" },
|
{ key: "requested_date", title: "Requested Date" },
|
||||||
{ key: "purchase_order_number", title: "PO Number" },
|
|
||||||
{ key: "po_status", title: "PO Status" },
|
{ key: "po_status", title: "PO Status" },
|
||||||
{ key: "villa_data", title: "Villa Name" },
|
{ key: "villa_data", title: "Villa Name" },
|
||||||
{ key: "po_item", title: "PO Product" },
|
{ key: "po_item", title: "PO Product" },
|
||||||
@@ -224,6 +226,8 @@
|
|||||||
acknowledged_by: "",
|
acknowledged_by: "",
|
||||||
acknowledged_date: ""
|
acknowledged_date: ""
|
||||||
};
|
};
|
||||||
|
let selectedVillaId: string | null = null;
|
||||||
|
let searchTerm: string = "";
|
||||||
let showPreparedModal = false;
|
let showPreparedModal = false;
|
||||||
let selectedPO = null;
|
let selectedPO = null;
|
||||||
let preparedByOptions: any[] = [];
|
let preparedByOptions: any[] = [];
|
||||||
@@ -245,6 +249,7 @@
|
|||||||
approved_price: 0,
|
approved_price: 0,
|
||||||
total_approved_order_amount: 0
|
total_approved_order_amount: 0
|
||||||
};
|
};
|
||||||
|
let formattedPrice = "";
|
||||||
let showReceivedModal = false;
|
let showReceivedModal = false;
|
||||||
let receivedForm = {
|
let receivedForm = {
|
||||||
po_number: "",
|
po_number: "",
|
||||||
@@ -273,6 +278,11 @@
|
|||||||
currentPage * rowsPerPage,
|
currentPage * rowsPerPage,
|
||||||
);
|
);
|
||||||
$: currentPage = 1; // Reset to first page when allRows changes
|
$: currentPage = 1; // Reset to first page when allRows changes
|
||||||
|
$: formattedPrice = preparedForm.q1_vendor_price.toLocaleString("id-ID", {
|
||||||
|
style: "currency",
|
||||||
|
currency: "IDR",
|
||||||
|
minimumFractionDigits: 0,
|
||||||
|
});
|
||||||
|
|
||||||
// Function to format numbers as ordinal (1st, 2nd, 3rd, etc.)
|
// Function to format numbers as ordinal (1st, 2nd, 3rd, etc.)
|
||||||
function ordinal(num: number) {
|
function ordinal(num: number) {
|
||||||
@@ -682,7 +692,7 @@
|
|||||||
let query = supabase
|
let query = supabase
|
||||||
.from("vb_purchaseorder_data")
|
.from("vb_purchaseorder_data")
|
||||||
.select("*")
|
.select("*")
|
||||||
.order(sort || "created_at", { ascending: order === "asc" })
|
.order(sort || "purchase_order_number", { ascending: order === "desc" })
|
||||||
.range(offset, offset + limit - 1);
|
.range(offset, offset + limit - 1);
|
||||||
|
|
||||||
if (filter) {
|
if (filter) {
|
||||||
@@ -1065,7 +1075,7 @@
|
|||||||
<thead class="bg-gray-100">
|
<thead class="bg-gray-100">
|
||||||
<tr>
|
<tr>
|
||||||
{#each columns as col}
|
{#each columns as col}
|
||||||
{#if col.key === "issue_name"}
|
{#if col.key === "purchase_order_number"}
|
||||||
<th
|
<th
|
||||||
class="sticky left-0 px-4 py-3 text-left font-semibold text-gray-700 uppercase tracking-wider whitespace-nowrap"
|
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;"
|
style="background-color: #f0f8ff; z-index: 10;"
|
||||||
@@ -1098,10 +1108,10 @@
|
|||||||
<td class="px-4 py-2 text-gray-700 max-w-xs whitespace-normal align-top break-words">
|
<td class="px-4 py-2 text-gray-700 max-w-xs whitespace-normal align-top break-words">
|
||||||
{row[col.key] || "—"}
|
{row[col.key] || "—"}
|
||||||
</td>
|
</td>
|
||||||
{:else if col.key === "issue_name"}
|
{: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"
|
<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;">
|
style="background-color: #f0f8ff; cursor: pointer;">
|
||||||
{row.issue_name || "—"}
|
{row.purchase_order_number || "—"}
|
||||||
</td>
|
</td>
|
||||||
{:else if col.key === "prepared"}
|
{:else if col.key === "prepared"}
|
||||||
<td class="px-4 py-2 text-center">
|
<td class="px-4 py-2 text-center">
|
||||||
@@ -1209,15 +1219,6 @@
|
|||||||
>
|
>
|
||||||
Payment
|
Payment
|
||||||
</button>
|
</button>
|
||||||
</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={() => openEditModal(row)}
|
|
||||||
>
|
|
||||||
✏️ Edit
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<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"
|
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"
|
||||||
@@ -1232,6 +1233,16 @@
|
|||||||
🖨️ Print
|
🖨️ Print
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
|
</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={() => openEditModal(row)}
|
||||||
|
>
|
||||||
|
✏️ Edit
|
||||||
|
</button>
|
||||||
|
|
||||||
<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"
|
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)}
|
on:click={() => deleteProject(row.id)}
|
||||||
@@ -1567,8 +1578,23 @@
|
|||||||
<option value={v.name}>{v.name}</option>
|
<option value={v.name}>{v.name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
<input type="number" bind:value={preparedForm.q1_vendor_price} placeholder="Q1 Vendor Price" class="w-full border p-2" />
|
<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 -->
|
<!-- Repeat for Q2, Q3 -->
|
||||||
<!-- Approved -->
|
<!-- Approved -->
|
||||||
<label>Approved Quantity</label>
|
<label>Approved Quantity</label>
|
||||||
@@ -1581,9 +1607,11 @@
|
|||||||
<option value={v.name}>{v.name}</option>
|
<option value={v.name}>{v.name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
|
<CurrencyInput
|
||||||
<label>Approved Price</label>
|
bind:value={preparedForm.approved_price}
|
||||||
<input type="number" bind:value={preparedForm.approved_price} on:input={() => updateTotalAmount()} class="w-full border p-2" />
|
label="Approved Price"
|
||||||
|
onInput={updateTotalAmount}
|
||||||
|
/>
|
||||||
|
|
||||||
<label>Total Approved Order Amount</label>
|
<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" />
|
<input type="number" value={preparedForm.total_approved_order_amount} disabled class="w-full border p-2 bg-gray-100" />
|
||||||
|
|||||||
Reference in New Issue
Block a user