feat: 統一庫存管理分頁 UI 與寬度規範,並更新 SKILL 規範文件

This commit is contained in:
2026-02-03 17:24:34 +08:00
parent 15aaa039e4
commit bd999c7bb6
17 changed files with 357 additions and 205 deletions

View File

@@ -1,5 +1,6 @@
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head, useForm, router, Link } from '@inertiajs/react';
import { usePermission } from '@/hooks/usePermission';
import {
Table,
TableBody,
@@ -71,7 +72,7 @@ export default function Index({ docs, warehouses, filters }: { docs: DocsPaginat
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [searchQuery, setSearchQuery] = useState(filters.search || '');
const [warehouseId, setWarehouseId] = useState(filters.warehouse_id || '');
const [perPage, setPerPage] = useState(filters.per_page || '15');
const [perPage, setPerPage] = useState(filters.per_page || '10');
const [deleteId, setDeleteId] = useState<string | null>(null);
// For Count Doc Selection
@@ -115,9 +116,12 @@ export default function Index({ docs, warehouses, filters }: { docs: DocsPaginat
debouncedFilter({ search: searchQuery, warehouse_id: val, per_page: perPage });
};
const handlePerPageChange = (val: string) => {
setPerPage(val);
debouncedFilter({ search: searchQuery, warehouse_id: warehouseId, per_page: val });
const handlePerPageChange = (value: string) => {
setPerPage(value);
router.get(route('inventory.adjust.index'),
{ ...filters, search: searchQuery, warehouse_id: warehouseId, per_page: value, page: 1 },
{ preserveState: false, replace: true, preserveScroll: true }
);
};
const confirmDelete = (id: string, e: React.MouseEvent) => {
@@ -134,6 +138,8 @@ export default function Index({ docs, warehouses, filters }: { docs: DocsPaginat
}
};
const { can } = usePermission();
const { data, setData, post, processing, reset } = useForm({
count_doc_id: null as string | null,
warehouse_id: '',
@@ -229,7 +235,7 @@ export default function Index({ docs, warehouses, filters }: { docs: DocsPaginat
{/* Action Buttons */}
<div className="flex gap-2 w-full md:w-auto">
<Can permission="inventory.adjust">
<Can permission="inventory_adjust.create">
<Button
className="flex-1 md:flex-none button-filled-primary h-9"
onClick={() => setIsDialogOpen(true)}
@@ -286,30 +292,55 @@ export default function Index({ docs, warehouses, filters }: { docs: DocsPaginat
<TableCell className="text-gray-500 text-sm">{doc.posted_at || '-'}</TableCell>
<TableCell className="text-center">
<div className="flex items-center justify-center gap-2" onClick={(e) => e.stopPropagation()}>
<Link href={route('inventory.adjust.show', [doc.id])}>
<Button
variant="outline"
size="sm"
className="button-outlined-primary"
title={doc.status === 'posted' ? '查閱' : '編輯'}
>
{doc.status === 'posted' ? (
<Eye className="w-4 h-4" />
) : (
<Pencil className="w-4 h-4" />
)}
</Button>
</Link>
{(() => {
const isDraft = doc.status === 'draft';
const canEdit = can('inventory_adjust.edit');
const canView = can('inventory_adjust.view');
if (isDraft && canEdit) {
return (
<Link href={route('inventory.adjust.show', [doc.id])}>
<Button
variant="outline"
size="sm"
className="button-outlined-primary"
title="編輯"
>
<Pencil className="w-4 h-4 ml-0.5" />
</Button>
</Link>
);
}
if (canView) {
return (
<Link href={route('inventory.adjust.show', [doc.id])}>
<Button
variant="outline"
size="sm"
className="button-outlined-primary"
title="查閱"
>
<Eye className="w-4 h-4 ml-0.5" />
</Button>
</Link>
);
}
return null;
})()}
{doc.status === 'draft' && (
<Button
variant="outline"
size="sm"
className="button-outlined-error"
title="刪除"
onClick={(e) => confirmDelete(doc.id, e)}
>
<Trash2 className="w-4 h-4" />
</Button>
<Can permission="inventory_adjust.delete">
<Button
variant="outline"
size="sm"
className="button-outlined-error"
title="刪除"
onClick={(e) => confirmDelete(doc.id, e)}
>
<Trash2 className="w-4 h-4 ml-0.5" />
</Button>
</Can>
)}
</div>
</TableCell>
@@ -328,12 +359,12 @@ export default function Index({ docs, warehouses, filters }: { docs: DocsPaginat
value={perPage}
onValueChange={handlePerPageChange}
options={[
{ label: "15", value: "15" },
{ label: "30", value: "30" },
{ label: "10", value: "10" },
{ label: "20", value: "20" },
{ label: "50", value: "50" },
{ label: "100", value: "100" }
]}
className="w-[100px] h-8"
className="w-[90px] h-8"
showSearch={false}
/>
<span></span>