penambahan fitur upload
This commit is contained in:
132
src/routes/backoffice/vendor/+page.svelte
vendored
132
src/routes/backoffice/vendor/+page.svelte
vendored
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { supabase } from "$lib/supabaseClient";
|
||||
import { onMount } from "svelte";
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
type Vendor = {
|
||||
id: string;
|
||||
@@ -18,6 +19,10 @@
|
||||
|
||||
let allRowsVendor: Vendor[] = [];
|
||||
let allRowsContactVendor: ContactVendor[] = [];
|
||||
let offset = 0;
|
||||
let limit = 10;
|
||||
let totalItems = 0;
|
||||
export let formErrors = writable<{ [key: string]: string }>({});
|
||||
|
||||
type columns = {
|
||||
key: string;
|
||||
@@ -35,26 +40,48 @@
|
||||
{ key: "created_at", title: "Created At" },
|
||||
];
|
||||
|
||||
onMount(async () => {
|
||||
const { data: vendorData, error: vendorError } = await supabase
|
||||
async function fetchVendor(
|
||||
searchTerm: string = "",
|
||||
orderBy: string = "created_at",
|
||||
orderDirection: "asc" | "desc" = "desc",
|
||||
limit: number = 10,
|
||||
offset: number = 0,
|
||||
) {
|
||||
let query = supabase
|
||||
.from("vendor")
|
||||
.select("*")
|
||||
.order("created_at", { ascending: false });
|
||||
.select("*", { count: "exact" })
|
||||
.order(orderBy, { ascending: orderDirection === "asc" })
|
||||
.range(offset, offset + limit - 1);
|
||||
|
||||
if (vendorError) {
|
||||
console.error("Error fetching vendors:", vendorError);
|
||||
} else {
|
||||
allRowsVendor = vendorData as Vendor[];
|
||||
if (searchTerm) {
|
||||
query = query.ilike("name", `%${searchTerm}%`);
|
||||
}
|
||||
|
||||
const { data, error, count } = await query;
|
||||
|
||||
if (error) {
|
||||
console.error("Error fetching vendors:", error);
|
||||
return [];
|
||||
}
|
||||
|
||||
allRowsVendor = data as Vendor[];
|
||||
|
||||
const { count: total } = await supabase
|
||||
.from("vendor")
|
||||
.select("*", { count: "exact" })
|
||||
.ilike("name", `%${searchTerm}%`);
|
||||
|
||||
totalItems = total || 0;
|
||||
offset = Math.floor(totalItems / limit) * limit;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
fetchVendor();
|
||||
});
|
||||
|
||||
let currentPage = 1;
|
||||
let itemsPerPage = 10;
|
||||
$: totalPages = Math.ceil(allRowsVendor.length / itemsPerPage);
|
||||
$: paginatedRows = allRowsVendor.slice(
|
||||
(currentPage - 1) * itemsPerPage,
|
||||
currentPage * itemsPerPage,
|
||||
);
|
||||
let currentPage = offset + 1;
|
||||
let itemsPerPage = limit;
|
||||
let totalPages = Math.ceil(totalItems / itemsPerPage);
|
||||
|
||||
function nextPage() {
|
||||
if (currentPage < totalPages) {
|
||||
@@ -276,22 +303,65 @@
|
||||
</script>
|
||||
|
||||
<!-- Table untuk daftar Vendor -->
|
||||
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-gray-800">Vendor List</h2>
|
||||
<p class="text-sm text-gray-600">Manage your vendor and contact data</p>
|
||||
</div>
|
||||
<button
|
||||
class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 text-sm"
|
||||
on:click={() => {
|
||||
showModal = true;
|
||||
isEditing = false;
|
||||
newVendor = {};
|
||||
currentEditingId = null;
|
||||
}}
|
||||
<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"
|
||||
>
|
||||
➕ Add Vendor
|
||||
</button>
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-gray-800">🏡 Vendor List</h2>
|
||||
<p class="text-sm text-gray-500">
|
||||
Manage your vendors and their contact information here.
|
||||
</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();
|
||||
fetchVendor(searchTerm, "created_at", "desc", limit, 0);
|
||||
}}
|
||||
/>
|
||||
<!-- 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"
|
||||
on:change={(e) => {
|
||||
const value = (e.target as HTMLSelectElement).value;
|
||||
fetchVendor("", value, "desc", limit, 0);
|
||||
}}
|
||||
>
|
||||
<option value="created_at">Sort by Created At</option>
|
||||
<option value="name">Sort by Name</option>
|
||||
<option value="vendor_type">Sort by Vendor Type</option>
|
||||
<option value="vendor_status">Sort by Vendor Status</option>
|
||||
<option value="vendor_subtype">Sort by Vendor Subtype</option>
|
||||
</select>
|
||||
<!-- button reset -->
|
||||
<button
|
||||
class="bg-gray-200 text-gray-700 px-4 py-2 rounded hover:bg-gray-300 text-sm"
|
||||
on:click={() => {
|
||||
fetchVendor();
|
||||
resetPagination();
|
||||
}}
|
||||
>
|
||||
🔄 Reset
|
||||
</button>
|
||||
<button
|
||||
class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 text-sm"
|
||||
on:click={() => {
|
||||
showModal = true;
|
||||
isEditing = false;
|
||||
newVendor = {};
|
||||
currentEditingId = null;
|
||||
}}
|
||||
>
|
||||
➕ Add Vendor
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Vendor Table -->
|
||||
@@ -310,7 +380,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 bg-white">
|
||||
{#each paginatedRows as vendor}
|
||||
{#each allRowsVendor as vendor}
|
||||
<tr
|
||||
class="hover:bg-gray-50 transition"
|
||||
on:click={() => handleVendorClick(vendor.id)}
|
||||
|
||||
Reference in New Issue
Block a user