Files
star-erp/resources/js/Pages/Sales/Import/Index.tsx

208 lines
10 KiB
TypeScript
Raw Normal View History

import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head, Link } from '@inertiajs/react';
import { Button } from '@/Components/ui/button';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/Components/ui/table';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/Components/ui/alert-dialog";
import { Badge } from "@/Components/ui/badge";
import { Plus, FileUp, Eye, Trash2 } from 'lucide-react';
import { useState, useEffect } from "react";
import { format } from 'date-fns';
import Pagination from "@/Components/shared/Pagination";
import { SearchableSelect } from "@/Components/ui/searchable-select";
import { router } from "@inertiajs/react";
import { usePermission } from "@/hooks/usePermission";
interface ImportBatch {
id: number;
import_date: string;
status: 'pending' | 'confirmed';
total_quantity: number;
total_amount: number;
importer?: {
name: string;
};
created_at: string;
}
interface Props {
batches: {
data: ImportBatch[];
links: any[]; // Pagination links
};
filters?: { // Add filters prop if not present, though we main need per_page state
per_page?: string;
}
}
export default function SalesImportIndex({ batches, filters = {} }: Props) {
const { can } = usePermission();
const [perPage, setPerPage] = useState(filters?.per_page?.toString() || "10");
useEffect(() => {
if (filters?.per_page) {
setPerPage(filters.per_page.toString());
}
}, [filters?.per_page]);
const handlePerPageChange = (value: string) => {
setPerPage(value);
router.get(
route("sales-imports.index"),
{ per_page: value },
{ preserveState: true, preserveScroll: true, replace: true }
);
};
return (
<AuthenticatedLayout
breadcrumbs={[
{ label: '銷售管理', href: '#' },
{ label: '銷售單匯入', href: route('sales-imports.index'), isPage: true },
]}
>
<Head title="銷售單匯入管理" />
<div className="container mx-auto p-6 max-w-7xl">
<div className="flex justify-between items-center mb-6">
<div>
<h1 className="text-2xl font-bold text-gray-900 flex items-center gap-2">
<FileUp className="h-6 w-6 text-primary-main" />
</h1>
<p className="text-gray-500 mt-1">
</p>
</div>
{can('sales_imports.create') && (
<Link href={route('sales-imports.create')}>
<Button className="button-filled-primary gap-2">
<Plus className="h-4 w-4" />
</Button>
</Link>
)}
</div>
<div className="bg-white rounded-lg border shadow-sm overflow-hidden">
<Table>
<TableHeader className="bg-gray-50">
<TableRow>
<TableHead className="w-[100px]">ID</TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="text-center w-[120px]"></TableHead>
<TableHead className="text-right w-[150px]"></TableHead>
<TableHead className="text-center w-[100px]"></TableHead>
<TableHead className="text-center w-[120px]"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{batches.data.length === 0 ? (
<TableRow>
<TableCell colSpan={7} className="text-center py-8 text-gray-500">
</TableCell>
</TableRow>
) : (
batches.data.map((batch) => (
<TableRow key={batch.id} className="hover:bg-gray-50/50">
<TableCell className="font-medium">#{batch.id}</TableCell>
<TableCell>
{format(new Date(batch.created_at), 'yyyy/MM/dd HH:mm')}
</TableCell>
<TableCell>{batch.importer?.name || '--'}</TableCell>
<TableCell className="text-center font-bold text-gray-900">
{Math.floor(batch.total_quantity || 0).toLocaleString()}
</TableCell>
<TableCell className="text-right font-bold text-primary-main">
NT$ {Number(batch.total_amount || 0).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 2 })}
</TableCell>
<TableCell className="text-center">
<Badge variant={batch.status === 'confirmed' ? 'default' : 'secondary'}>
{batch.status === 'confirmed' ? '已確認' : '待確認'}
</Badge>
</TableCell>
<TableCell>
<div className="flex justify-center gap-2">
<Link href={route('sales-imports.show', batch.id)}>
<Button variant="outline" size="sm" className="button-outlined-primary" title="查看詳情">
<Eye className="h-4 w-4" />
</Button>
</Link>
{batch.status === 'pending' && can('sales_imports.delete') && (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline" size="sm" className="button-outlined-error" title="刪除">
<Trash2 className="h-4 w-4" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription>
#{batch.id}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction
className="bg-red-600 hover:bg-red-700"
onClick={() => router.delete(route('sales-imports.destroy', batch.id), { preserveScroll: true })}
>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)}
</div>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</div>
{/* Pagination */}
<div className="mt-4 flex flex-col sm:flex-row items-center justify-between gap-4">
<div className="flex items-center gap-2 text-sm text-gray-500">
<span></span>
<SearchableSelect
value={perPage}
onValueChange={handlePerPageChange}
options={[
{ label: "10", value: "10" },
{ label: "20", value: "20" },
{ label: "50", value: "50" },
{ label: "100", value: "100" },
]}
className="w-[100px] h-8"
showSearch={false}
/>
<span></span>
</div>
<Pagination links={batches.links} />
</div>
</div>
</AuthenticatedLayout>
);
}