feat: 實作銷售單匯入權限控管並全面精簡權限顯示名稱
This commit is contained in:
@@ -165,14 +165,14 @@ export default function AuthenticatedLayout({
|
||||
id: "sales-management",
|
||||
label: "銷售管理",
|
||||
icon: <TrendingUp className="h-5 w-5" />,
|
||||
// permission: ["sales.view_imports"], // Temporarily disabled for immediate visibility
|
||||
permission: "sales_imports.view",
|
||||
children: [
|
||||
{
|
||||
id: "sales-import-list",
|
||||
label: "銷售單匯入",
|
||||
icon: <FileUp className="h-4 w-4" />,
|
||||
route: "/sales/imports",
|
||||
// permission: "sales.view_imports",
|
||||
permission: "sales_imports.view",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ChevronsDown, ChevronsUp } from "lucide-react";
|
||||
export interface Permission {
|
||||
id: number;
|
||||
name: string;
|
||||
display_name?: string;
|
||||
}
|
||||
|
||||
export interface GroupedPermission {
|
||||
@@ -104,7 +105,7 @@ export default function PermissionSelector({ groupedPermissions, selectedPermiss
|
||||
|
||||
// Filter permissions that match
|
||||
const matchingPermissions = group.permissions.filter(p => {
|
||||
const translatedName = translateAction(p.name);
|
||||
const translatedName = p.display_name || translateAction(p.name);
|
||||
return translatedName.includes(searchQuery) ||
|
||||
p.name.toLowerCase().includes(searchQuery.toLowerCase());
|
||||
});
|
||||
@@ -306,7 +307,7 @@ function PermissionItem({ permission, selectedPermissions, onToggle, translate }
|
||||
htmlFor={permission.name}
|
||||
className="text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 cursor-pointer text-gray-700 hover:text-primary-main transition-colors"
|
||||
>
|
||||
{translate(permission.name)}
|
||||
{permission.display_name || translate(permission.name)}
|
||||
</label>
|
||||
<p className="text-[10px] text-gray-400 font-mono">
|
||||
{permission.name}
|
||||
|
||||
@@ -27,6 +27,7 @@ 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;
|
||||
@@ -51,6 +52,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function SalesImportIndex({ batches, filters = {} }: Props) {
|
||||
const { can } = usePermission();
|
||||
const [perPage, setPerPage] = useState(filters?.per_page?.toString() || "10");
|
||||
|
||||
useEffect(() => {
|
||||
@@ -88,12 +90,14 @@ export default function SalesImportIndex({ batches, filters = {} }: Props) {
|
||||
匯入並管理銷售出貨紀錄
|
||||
</p>
|
||||
</div>
|
||||
<Link href={route('sales-imports.create')}>
|
||||
<Button className="button-filled-primary gap-2">
|
||||
<Plus className="h-4 w-4" />
|
||||
新增匯入
|
||||
</Button>
|
||||
</Link>
|
||||
{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">
|
||||
@@ -142,7 +146,7 @@ export default function SalesImportIndex({ batches, filters = {} }: Props) {
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
{batch.status === 'pending' && (
|
||||
{batch.status === 'pending' && can('sales_imports.delete') && (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="outline" size="sm" className="button-outlined-error" title="刪除">
|
||||
|
||||
@@ -26,6 +26,7 @@ import { Badge } from "@/Components/ui/badge";
|
||||
import { ArrowLeft, CheckCircle, Trash2, Printer } from 'lucide-react';
|
||||
import { format } from 'date-fns';
|
||||
import Pagination from "@/Components/shared/Pagination";
|
||||
import { usePermission } from "@/hooks/usePermission";
|
||||
|
||||
interface ImportItem {
|
||||
id: number;
|
||||
@@ -76,6 +77,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function SalesImportShow({ import: batch, items, filters = {} }: Props) {
|
||||
const { can } = usePermission();
|
||||
const { post, processing } = useForm({});
|
||||
const [perPage, setPerPage] = useState(filters?.per_page?.toString() || "10");
|
||||
|
||||
@@ -139,65 +141,74 @@ export default function SalesImportShow({ import: batch, items, filters = {} }:
|
||||
{batch.status === 'confirmed' ? '已確認' : '待確認'}
|
||||
</Badge>
|
||||
{batch.status === 'pending' && (
|
||||
<>
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="gap-2 button-outlined-error"
|
||||
>
|
||||
<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={handleDelete}
|
||||
<div className="flex gap-3">
|
||||
{can('sales_imports.delete') && (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="button-outlined-error gap-2 h-10 px-6"
|
||||
>
|
||||
確認刪除
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
刪除匯入
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>確認刪除匯入紀錄</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
確定要刪除此筆匯入紀錄嗎?此操作將會移除所有明細資料且無法復原。
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
className="bg-red-600 hover:bg-red-700"
|
||||
onClick={handleDelete}
|
||||
>
|
||||
確認刪除
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)}
|
||||
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button
|
||||
className="button-filled-primary gap-2"
|
||||
disabled={processing}
|
||||
>
|
||||
<CheckCircle className="h-4 w-4" />
|
||||
{processing ? '處理中...' : '確認並扣庫'}
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>確認執行庫存扣取</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
確認要執行扣庫嗎?系統將會根據此匯入內容減少對應倉庫的商品庫存。此操作無法復原。
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
className="button-filled-primary"
|
||||
onClick={handleConfirm}
|
||||
{can('sales_imports.confirm') && (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button
|
||||
className="button-filled-primary gap-2 h-10 px-8 shadow-md hover:shadow-lg transition-all"
|
||||
disabled={processing}
|
||||
>
|
||||
確認執行
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</>
|
||||
<CheckCircle className="h-4 w-4" />
|
||||
{processing ? '處理中...' : '確認扣庫並入帳'}
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent className="sm:max-w-md">
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>確認匯入並扣減庫存</AlertDialogTitle>
|
||||
<AlertDialogDescription className="text-gray-600 leading-relaxed">
|
||||
按下「確認」後,系統將依據此匯入清冊進行庫存扣點。
|
||||
<div className="bg-amber-50 border-l-4 border-amber-400 p-4 mt-3 rounded">
|
||||
<p className="text-amber-800 text-sm font-medium">
|
||||
注意:此操作無法復原,請確保匯入資料正確無誤。
|
||||
</p>
|
||||
</div>
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter className="mt-4">
|
||||
<AlertDialogCancel>我再看看</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
className="bg-primary-main hover:bg-primary-dark text-white px-8"
|
||||
onClick={handleConfirm}
|
||||
>
|
||||
確認並執行
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{batch.status === 'confirmed' && (
|
||||
<Button variant="outline" className="gap-2 button-outlined-primary">
|
||||
|
||||
Reference in New Issue
Block a user