Files
vberp/note.md
2025-07-08 17:36:04 +14:00

22 KiB
Raw Permalink Blame History

<script lang="ts"> import { supabase } from "$lib/supabaseClient"; import { onMount } from "svelte"; import { writable } from "svelte/store"; type Transport = { id: string; guest_name: string; requested_date: Date; area: string; pickup_date: Date; request_things: string; additional_notes: string; requested_by: string; vendor_email_address: string; created_at?: Date; }; type TransportDisplay = { guest_name: string; requested_date: Date; area: string; pickup_date: Date; request_things: string; additional_notes: string; requested_by: string; vendor_email_address: string; created_at?: Date; }; const area = [ { label: "Laksamana", value: "Laksamana" }, { label: "Drupadi", value: "Drupadi" }, { label: "Abimanyu", value: "Abimanyu" }, { label: "Seminyak", value: "Seminyak" }, ]; const requestThings = [ { label: "Airport pickup", value: "Airport pickup" }, { label: "Airport transfer", value: "Airport transfer" }, { label: "Day car charter", value: "Day car charter" }, { label: "One way drop off", value: "One way drop off" }, { label: "One way pickup", value: "One way pickup" }, { label: "Other", value: "Other" }, ]; let allRows: Transport[] = []; type columns = { key: string; title: string; }; const columns: columns[] = [ { key: "guest_name", title: "Guest Name" }, { key: "requested_date", title: "Requested Date" }, { key: "area", title: "Area" }, { key: "pickup_date", title: "Pickup Date" }, { key: "request_things", title: "Request Things" }, { key: "additional_notes", title: "Additional Notes" }, { key: "requested_by", title: "Requested By" }, { key: "vendor_email_address", title: "Vendor Email Address" }, { key: "created_at", title: "Created At" }, { key: "actions", title: "Actions" }, ]; async function fetchTransport( filter: string | null = null, searchTerm: string | null = null, sortColumn: string | null = "created_at", sortOrder: "asc" | "desc" = "desc", offset: number = 0, limit: number = 10, ) { let query = supabase .from("vb_transport") .select("*", { count: "exact" }) .order(sortColumn || "created_at", { ascending: sortOrder === "asc", }); if (filter) { query = query.eq("area", filter); } if (searchTerm) { query = query.ilike("guest_name", `%${searchTerm}%`); } if (offset) { query = query.range(offset, offset + limit - 1); } if (limit) { query = query.limit(limit); } const { data: transportResponse, error } = await query; if (error) { console.error("Error fetching timesheets:", error); return; } allRows = transportResponse.map((row) => ({ ...row, requested_date: new Date(row.requested_date), pickup_date: new Date(row.pickup_date), created_at: row.created_at ? new Date(row.created_at) : undefined, })) as Transport[]; } let currentPage = 1; const rowsPerPage = 10; const totalRows = allRows.length; const totalPages = Math.ceil(totalRows / rowsPerPage); $: paginatedRows = allRows.slice( (currentPage - 1) * rowsPerPage, currentPage * rowsPerPage, ); console.log("Total Rows:", totalRows); function goToPage(page: number) { if (page >= 1 && page <= totalPages) currentPage = page; fetchTransport( null, null, "created_at", "desc", (currentPage - 1) * rowsPerPage, rowsPerPage, ); } onMount(() => { fetchTransport(); }); // Initialize the first page $: currentPage = 1; let showModal = false; let isEditing = false; let currentEditingId: string | null = null; let newTransport: Transport = { id: "", guest_name: "", requested_date: new Date(), area: "", pickup_date: new Date(), request_things: "", additional_notes: "", requested_by: "", vendor_email_address: "", }; const excludedKeys = ["id", "actions", "created_at"]; const formColumns = columns.filter( (col) => !excludedKeys.includes(col.key), ); function openModal(transport?: Transport) { if (transport) { isEditing = true; currentEditingId = transport.id; newTransport = { ...transport }; } else { isEditing = false; currentEditingId = null; newTransport = { id: "", guest_name: "", requested_date: new Date(), area: "", pickup_date: new Date(), request_things: "", additional_notes: "", requested_by: "", vendor_email_address: "", }; } showModal = true; } async function saveIssue(event: Event) { event.preventDefault(); const formData = new FormData(event.target as HTMLFormElement); // Validate form data if (!validateForm(formData)) { console.error("Form validation failed"); return; } if (isEditing && currentEditingId) { const { error } = await supabase .from("vb_tansport") .update(newTransport) .eq("id", currentEditingId); if (error) { alert("Error updating transport: " + error.message); console.error("Error updating transport:", error); return; } } await fetchTransport(); showModal = false; } async function deleteTransport(id: string) { if (confirm("Are you sure you want to delete this transport?")) { const { error } = await supabase .from("vb_transport") .delete() .eq("id", id); if (error) { console.error("Error deleting issue:", error); return; } await fetchTransport(); } } export let formErrors = writable<{ [key: string]: string }>({}); function validateForm(formData: FormData): boolean { const errors: { [key: string]: string } = {}; const requiredFields = [ "guest_name", "requested_date", "area", "pickup_date", "request_things", "additional_notes", "requested_by", "vendor_email_address", ]; requiredFields.forEach((field) => { if (!formData.get(field) || formData.get(field) === "") { errors[field] = `${field.replace(/_/g, " ")} is required.`; } }); formErrors.set(errors); return Object.keys(errors).length === 0; } function errorClass(field: string): string { return $formErrors[field] ? "border-red-500" : "border"; } </script>

🚗 Transport Request List

Manage and track all transport requests efficiently.

{ const searchTerm = ( e.target as HTMLInputElement ).value.toLowerCase(); fetchTransport(null, searchTerm, "created_at", "desc"); }} /> { const filter = (e.target as HTMLSelectElement).value; fetchTransport(filter, null, null, "desc"); }} > All Area {#each area as a} {a.label} {/each} { const filter = (e.target as HTMLSelectElement).value; fetchTransport(null, filter, "created_at", "desc"); }} > All Requests {#each requestThings as r} {r.label} {/each} fetchTransport(null, null, "created_at", "desc", 0, 10)} > 🔄 Reset
{#each columns as col} {#if col.key === "guest_name"} {:else} {/if} {/each} {#each paginatedRows as row} {#each columns as col} {#if col.key === "guest_name"} {:else if col.key === "requested_date"} {:else if col.key === "pickup_date"} {:else if col.key === "created_at"} {:else if col.key === "area"} {:else if col.key === "request_things"} {:else if col.key === "additional_notes"} {:else if col.key === "requested_by"} {:else if col.key === "vendor_email_address"} {:else if col.key === "actions"} {:else} {/if} {/each} {/each}
{col.title} {col.title}
{row[col.key as keyof Transport]} {row[col.key as keyof Transport] ? new Date( row[ col.key as keyof Transport ] as string | number | Date, ).toLocaleDateString() : "N/A"} {new Date( new Date( row[col.key as keyof Transport] as | string | number | Date, ).toLocaleDateString() || "N/A", ).toLocaleDateString()} {row[col.key as keyof Transport] ? new Date( row[ col.key as keyof Transport ] as string | number | Date, ).toLocaleDateString() : "N/A"} {row[col.key as keyof Transport]} {row[col.key as keyof Transport]} {row[col.key as keyof Transport] || "No additional notes"} {row[col.key as keyof Transport]} {row[col.key as keyof TransportDisplay] || "No email provided"} openModal(row)} > ✏️ Edit deleteTransport(row.id)} > 🗑️ Delete {row[col.key as keyof TransportDisplay]}
<!-- 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>

{#if showModal}

{isEditing ? "Edit Transport Request" : "New Transport Request"}

{#each formColumns as col}
{#if col.key === "requested_date" || col.key === "pickup_date"} {:else if col.key === "area"} Select Area {#each area as a} {a.label} {/each} {:else if col.key === "request_things"} Select Request {#each requestThings as r} {r.label} {/each} {:else} <input type="text" name={col.key} bind:value={ newTransport[col.key as keyof Transport] } placeholder={col.title} class="w-full border rounded-xl px-4 py- 2 focus:outline-none focus:ring-2 focus:ring-purple-400 {errorClass(col.key)}" /> {/if} {#if $formErrors[col.key]}

{$formErrors[col.key]}

{/if}
{/each}
Additional Notes {#if $formErrors.additional_notes}

{$formErrors.additional_notes}

{/if}
<button type="button" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-xl hover:bg-gray-300" on:click={() => (showModal = false)} > Cancel {isEditing ? "Update" : "Create"}
{/if}