feat: 統一庫存管理分頁 UI 與寬度規範,並更新 SKILL 規範文件
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
|
||||
import { Head, Link, useForm, router } from '@inertiajs/react';
|
||||
import { Head, Link, useForm, router, usePage } from '@inertiajs/react';
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { usePermission } from '@/hooks/usePermission';
|
||||
import { debounce } from "lodash";
|
||||
import { SearchableSelect } from "@/Components/ui/searchable-select";
|
||||
import {
|
||||
@@ -54,6 +55,8 @@ export default function Index({ auth, docs, warehouses, filters }: any) {
|
||||
remarks: '',
|
||||
});
|
||||
|
||||
const { can } = usePermission();
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState(filters.search || "");
|
||||
const [warehouseFilter, setWarehouseFilter] = useState(filters.warehouse_id || "all");
|
||||
const [perPage, setPerPage] = useState(filters.per_page || "10");
|
||||
@@ -207,7 +210,7 @@ export default function Index({ auth, docs, warehouses, filters }: any) {
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="flex gap-2 w-full md:w-auto">
|
||||
<Can permission="inventory.view">
|
||||
<Can permission="inventory_count.create">
|
||||
<Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button className="flex-1 md:flex-none button-filled-primary">
|
||||
@@ -302,22 +305,46 @@ export default function Index({ auth, docs, warehouses, filters }: any) {
|
||||
<TableCell className="text-sm">{doc.created_by}</TableCell>
|
||||
<TableCell className="text-center">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Can permission="inventory.view">
|
||||
<Link href={route('inventory.count.show', [doc.id])}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="button-outlined-primary"
|
||||
title={['completed', 'adjusted'].includes(doc.status) ? '查閱' : '盤點'}
|
||||
>
|
||||
{['completed', 'adjusted'].includes(doc.status) ? (
|
||||
<Eye className="w-4 h-4 ml-0.5" />
|
||||
) : (
|
||||
<Pencil className="w-4 h-4 ml-0.5" />
|
||||
)}
|
||||
</Button>
|
||||
</Link>
|
||||
{!['completed', 'adjusted'].includes(doc.status) && (
|
||||
{/* Action Button Logic: Prefer Edit if allowed and status is active, otherwise fallback to View if allowed */}
|
||||
{(() => {
|
||||
const isEditable = !['completed', 'adjusted'].includes(doc.status);
|
||||
const canEdit = can('inventory_count.edit');
|
||||
const canView = can('inventory_count.view');
|
||||
|
||||
if (isEditable && canEdit) {
|
||||
return (
|
||||
<Link href={route('inventory.count.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.count.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;
|
||||
})()}
|
||||
{!['completed', 'adjusted'].includes(doc.status) && (
|
||||
<Can permission="inventory_count.delete">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -327,8 +354,8 @@ export default function Index({ auth, docs, warehouses, filters }: any) {
|
||||
>
|
||||
<Trash2 className="w-4 h-4 ml-0.5" />
|
||||
</Button>
|
||||
)}
|
||||
</Can>
|
||||
</Can>
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -351,7 +378,7 @@ export default function Index({ auth, docs, warehouses, filters }: any) {
|
||||
{ label: "50", value: "50" },
|
||||
{ label: "100", value: "100" }
|
||||
]}
|
||||
className="w-[100px] h-8"
|
||||
className="w-[90px] h-8"
|
||||
showSearch={false}
|
||||
/>
|
||||
<span>筆</span>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
|
||||
import { Head, useForm, Link, router } from '@inertiajs/react'; // Added Link
|
||||
import { Head, useForm, Link, router } from '@inertiajs/react';
|
||||
import { usePermission } from '@/hooks/usePermission'; // Added Link
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -61,7 +62,10 @@ export default function Show({ doc }: any) {
|
||||
});
|
||||
}
|
||||
|
||||
const { can } = usePermission();
|
||||
const isCompleted = ['completed', 'adjusted'].includes(doc.status);
|
||||
const canEdit = can('inventory_count.edit');
|
||||
const isReadOnly = isCompleted || !canEdit;
|
||||
|
||||
// Calculate progress
|
||||
const totalItems = doc.items.length;
|
||||
@@ -155,7 +159,7 @@ export default function Show({ doc }: any) {
|
||||
|
||||
{!isCompleted && (
|
||||
<div className="flex items-center gap-2">
|
||||
<Can permission="inventory.view">
|
||||
<Can permission="inventory_count.delete">
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="outline" size="sm" disabled={processing} className="button-outlined-error">
|
||||
@@ -176,7 +180,9 @@ export default function Show({ doc }: any) {
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</Can>
|
||||
|
||||
<Can permission="inventory_count.edit">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -261,7 +267,7 @@ export default function Show({ doc }: any) {
|
||||
<TableCell className="text-sm font-mono">{item.batch_number || '-'}</TableCell>
|
||||
<TableCell className="text-right font-medium">{item.system_qty.toFixed(0)}</TableCell>
|
||||
<TableCell className="text-right px-1 py-3">
|
||||
{isCompleted ? (
|
||||
{isReadOnly ? (
|
||||
<span className="font-semibold mr-2">{item.counted_qty}</span>
|
||||
) : (
|
||||
<Input
|
||||
@@ -290,7 +296,7 @@ export default function Show({ doc }: any) {
|
||||
</TableCell>
|
||||
<TableCell className="text-sm text-gray-500">{item.unit || item.unit_name}</TableCell>
|
||||
<TableCell className="px-1">
|
||||
{isCompleted ? (
|
||||
{isReadOnly ? (
|
||||
<span className="text-sm text-gray-600">{item.notes}</span>
|
||||
) : (
|
||||
<Input
|
||||
|
||||
Reference in New Issue
Block a user