Files
star-erp/resources/js/Pages/PurchaseOrder/Index.tsx

144 lines
5.1 KiB
TypeScript
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.
/**
* 採購單管理主頁面
*/
import { useState, useCallback } from "react";
import { Plus, Search, X, ShoppingCart } from 'lucide-react';
import { Button } from "@/Components/ui/button";
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout";
import { Head, router } from "@inertiajs/react";
import PurchaseOrderTable from "@/Components/PurchaseOrder/PurchaseOrderTable";
import { PurchaseOrderFilters } from "@/Components/PurchaseOrder/PurchaseOrderFilters";
import { type DateRange } from "@/Components/PurchaseOrder/DateFilter";
import type { PurchaseOrder } from "@/types/purchase-order";
import { debounce } from "lodash";
import Pagination from "@/Components/shared/Pagination";
import { getBreadcrumbs } from "@/utils/breadcrumb";
import { Can } from "@/Components/Permission/Can";
interface Props {
orders: {
data: PurchaseOrder[];
links: any[];
total: number;
from: number;
to: number;
};
filters: {
search?: string;
status?: string;
warehouse_id?: string;
sort_field?: string;
sort_direction?: string;
};
warehouses: { id: number; name: string }[];
}
export default function PurchaseOrderIndex({ orders, filters, warehouses }: Props) {
const [searchQuery, setSearchQuery] = useState(filters.search || "");
const [statusFilter, setStatusFilter] = useState<string>(filters.status || "all");
const [requesterFilter, setRequesterFilter] = useState<string>(filters.warehouse_id || "all");
const [dateRange, setDateRange] = useState<DateRange | null>(null);
const handleFilterChange = (newFilters: any) => {
router.get("/purchase-orders", {
...filters,
...newFilters,
page: 1,
}, {
preserveState: true,
replace: true,
});
};
const handleSearch = useCallback(
debounce((value: string) => {
handleFilterChange({ search: value });
}, 500),
[filters]
);
const onSearchChange = (value: string) => {
setSearchQuery(value);
handleSearch(value);
};
const onStatusChange = (value: string) => {
setStatusFilter(value);
handleFilterChange({ status: value });
};
const onWarehouseChange = (value: string) => {
setRequesterFilter(value);
handleFilterChange({ warehouse_id: value });
};
const handleClearFilters = () => {
setSearchQuery("");
setStatusFilter("all");
setRequesterFilter("all");
setDateRange(null);
router.get("/purchase-orders");
};
const hasActiveFilters = searchQuery !== "" || statusFilter !== "all" || requesterFilter !== "all" || dateRange !== null;
const handleNavigateToCreateOrder = () => {
router.get("/purchase-orders/create");
};
return (
<AuthenticatedLayout breadcrumbs={getBreadcrumbs("purchaseOrders")}>
<Head title="採購管理 - 管理採購單" />
<div className="container mx-auto p-6 max-w-7xl">
<div className="flex items-center justify-between mb-6">
<div>
<h1 className="text-2xl font-bold text-grey-0 flex items-center gap-2">
<ShoppingCart className="h-6 w-6 text-[#01ab83]" />
</h1>
<p className="text-gray-500 mt-1">
</p>
</div>
<div className="flex gap-2">
<Can permission="purchase_orders.create">
<Button
onClick={handleNavigateToCreateOrder}
className="gap-2 button-filled-primary"
>
<Plus className="h-4 w-4" />
</Button>
</Can>
</div>
</div>
<div className="mb-6">
<PurchaseOrderFilters
searchQuery={searchQuery}
statusFilter={statusFilter}
requesterFilter={requesterFilter}
warehouses={warehouses}
onSearchChange={onSearchChange}
onStatusChange={onStatusChange}
onRequesterChange={onWarehouseChange}
onClearFilters={handleClearFilters}
hasActiveFilters={hasActiveFilters}
dateRange={dateRange}
onDateRangeChange={setDateRange}
/>
</div>
<PurchaseOrderTable
orders={orders.data}
/>
<div className="mt-6 flex justify-center">
<Pagination links={orders.links} />
</div>
</div>
</AuthenticatedLayout>
);
}