refactor(ui): 統一 ActivityDetailDialog 滾動行為
- 移除 ScrollArea,改用原生的 overflow-y-auto 於 DialogContent - 參考 VendorDialog 與 ProductDialog 的標準實作方式
This commit is contained in:
@@ -5,7 +5,6 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/Components/ui/dialog";
|
} from "@/Components/ui/dialog";
|
||||||
import { Badge } from "@/Components/ui/badge";
|
import { Badge } from "@/Components/ui/badge";
|
||||||
import { ScrollArea } from "@/Components/ui/scroll-area";
|
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@@ -237,7 +236,7 @@ export default function ActivityDetailDialog({ open, onOpenChange, activity }: P
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="max-w-3xl max-h-[90vh] flex flex-col p-0 gap-0 overflow-hidden">
|
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto p-0 gap-0">
|
||||||
<DialogHeader className="p-6 pb-4 border-b pr-12">
|
<DialogHeader className="p-6 pb-4 border-b pr-12">
|
||||||
<div className="flex items-center gap-3 mb-2">
|
<div className="flex items-center gap-3 mb-2">
|
||||||
<DialogTitle className="text-xl font-bold text-gray-900">
|
<DialogTitle className="text-xl font-bold text-gray-900">
|
||||||
@@ -276,163 +275,161 @@ export default function ActivityDetailDialog({ open, onOpenChange, activity }: P
|
|||||||
</div>
|
</div>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="flex-1 overflow-hidden bg-gray-50/50">
|
<div className="bg-gray-50/50 p-6 min-h-[300px]">
|
||||||
<ScrollArea className="h-full w-full p-6">
|
{activity.event === 'created' ? (
|
||||||
{activity.event === 'created' ? (
|
<div className="border rounded-md overflow-hidden bg-white shadow-sm">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow className="bg-gray-50/50 hover:bg-gray-50/50">
|
||||||
|
<TableHead className="w-[150px]">欄位</TableHead>
|
||||||
|
<TableHead>異動前</TableHead>
|
||||||
|
<TableHead>異動後</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{filteredKeys
|
||||||
|
.filter(key => attributes[key] !== null && attributes[key] !== '' && !isSnapshotField(key))
|
||||||
|
.map((key) => (
|
||||||
|
<TableRow key={key}>
|
||||||
|
<TableCell className="font-medium text-gray-700 w-[150px]">{getFieldLabel(key)}</TableCell>
|
||||||
|
<TableCell className="text-gray-500 break-words max-w-[200px]">-</TableCell>
|
||||||
|
<TableCell className="text-gray-900 font-medium break-words max-w-[200px]">
|
||||||
|
{getFormattedValue(key, attributes[key])}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
{filteredKeys.filter(key => attributes[key] !== null && attributes[key] !== '' && !isSnapshotField(key)).length === 0 && (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={3} className="h-24 text-center text-gray-500">
|
||||||
|
無初始資料
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="border rounded-md overflow-hidden bg-white shadow-sm">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow className="bg-gray-50 hover:bg-gray-50">
|
||||||
|
<TableHead className="w-[150px]">欄位</TableHead>
|
||||||
|
<TableHead>異動前</TableHead>
|
||||||
|
<TableHead>異動後</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{filteredKeys.some(key => !isSnapshotField(key)) ? (
|
||||||
|
filteredKeys
|
||||||
|
.filter(key => !isSnapshotField(key))
|
||||||
|
.map((key) => {
|
||||||
|
const oldValue = old[key];
|
||||||
|
const newValue = attributes[key];
|
||||||
|
const isChanged = JSON.stringify(oldValue) !== JSON.stringify(newValue);
|
||||||
|
|
||||||
|
// For deleted events, we want to show the current attributes in the "Before" column
|
||||||
|
const displayBefore = activity.event === 'deleted'
|
||||||
|
? getFormattedValue(key, newValue || oldValue)
|
||||||
|
: getFormattedValue(key, oldValue);
|
||||||
|
|
||||||
|
const displayAfter = activity.event === 'deleted'
|
||||||
|
? '-'
|
||||||
|
: getFormattedValue(key, newValue);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow key={key} className={isChanged ? 'bg-amber-50/30 hover:bg-amber-50/50' : 'hover:bg-gray-50/50'}>
|
||||||
|
<TableCell className="font-medium text-gray-700 w-[150px]">{getFieldLabel(key)}</TableCell>
|
||||||
|
<TableCell className="text-gray-500 break-words max-w-[200px]">
|
||||||
|
{displayBefore}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-gray-900 font-medium break-words max-w-[200px]">
|
||||||
|
{displayAfter}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={3} className="h-24 text-center text-gray-500">
|
||||||
|
無詳細異動內容
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* Items Diff Section (Special for Purchase Orders) */}
|
||||||
|
{activity.properties?.items_diff && (
|
||||||
|
<div className="mt-6 space-y-4">
|
||||||
|
<h3 className="text-sm font-bold text-gray-900 flex items-center gap-2 px-1">
|
||||||
|
<Package className="w-4 h-4 text-primary-main" />
|
||||||
|
品項異動明細
|
||||||
|
</h3>
|
||||||
|
|
||||||
<div className="border rounded-md overflow-hidden bg-white shadow-sm">
|
<div className="border rounded-md overflow-hidden bg-white shadow-sm">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader className="bg-gray-50/50">
|
||||||
<TableRow className="bg-gray-50/50 hover:bg-gray-50/50">
|
<TableRow>
|
||||||
<TableHead className="w-[150px]">欄位</TableHead>
|
<TableHead>商品名稱</TableHead>
|
||||||
<TableHead>異動前</TableHead>
|
<TableHead className="text-center">異動類型</TableHead>
|
||||||
<TableHead>異動後</TableHead>
|
<TableHead>異動詳情 (舊 → 新)</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{filteredKeys
|
{/* Updated Items */}
|
||||||
.filter(key => attributes[key] !== null && attributes[key] !== '' && !isSnapshotField(key))
|
{activity.properties.items_diff.updated.map((item: any, idx: number) => (
|
||||||
.map((key) => (
|
<TableRow key={`upd-${idx}`} className="bg-blue-50/10 hover:bg-blue-50/20">
|
||||||
<TableRow key={key}>
|
<TableCell className="font-medium">{item.product_name}</TableCell>
|
||||||
<TableCell className="font-medium text-gray-700 w-[150px]">{getFieldLabel(key)}</TableCell>
|
<TableCell className="text-center">
|
||||||
<TableCell className="text-gray-500 break-words max-w-[200px]">-</TableCell>
|
<Badge variant="outline" className="bg-blue-50 text-blue-700 border-blue-200">更新</Badge>
|
||||||
<TableCell className="text-gray-900 font-medium break-words max-w-[200px]">
|
</TableCell>
|
||||||
{getFormattedValue(key, attributes[key])}
|
<TableCell className="text-sm">
|
||||||
</TableCell>
|
<div className="space-y-1">
|
||||||
</TableRow>
|
{item.old.quantity !== item.new.quantity && (
|
||||||
))}
|
<div>數量: <span className="text-gray-500 line-through">{item.old.quantity}</span> → <span className="text-blue-700 font-bold">{item.new.quantity}</span></div>
|
||||||
{filteredKeys.filter(key => attributes[key] !== null && attributes[key] !== '' && !isSnapshotField(key)).length === 0 && (
|
)}
|
||||||
<TableRow>
|
{item.old.unit_name !== item.new.unit_name && (
|
||||||
<TableCell colSpan={3} className="h-24 text-center text-gray-500">
|
<div>單位: <span className="text-gray-500 line-through">{item.old.unit_name || '-'}</span> → <span className="text-blue-700 font-bold">{item.new.unit_name || '-'}</span></div>
|
||||||
無初始資料
|
)}
|
||||||
|
{item.old.subtotal !== item.new.subtotal && (
|
||||||
|
<div>小計: <span className="text-gray-500 line-through">${item.old.subtotal}</span> → <span className="text-blue-700 font-bold">${item.new.subtotal}</span></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)}
|
))}
|
||||||
|
|
||||||
|
{/* Added Items */}
|
||||||
|
{activity.properties.items_diff.added.map((item: any, idx: number) => (
|
||||||
|
<TableRow key={`add-${idx}`} className="bg-green-50/10 hover:bg-green-50/20">
|
||||||
|
<TableCell className="font-medium">{item.product_name}</TableCell>
|
||||||
|
<TableCell className="text-center">
|
||||||
|
<Badge variant="outline" className="bg-green-50 text-green-700 border-green-200">新增</Badge>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-sm">
|
||||||
|
數量: {item.quantity} {item.unit_name} / 小計: ${item.subtotal}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Removed Items */}
|
||||||
|
{activity.properties.items_diff.removed.map((item: any, idx: number) => (
|
||||||
|
<TableRow key={`rem-${idx}`} className="bg-red-50/10 hover:bg-red-50/20">
|
||||||
|
<TableCell className="font-medium text-gray-400 line-through">{item.product_name}</TableCell>
|
||||||
|
<TableCell className="text-center">
|
||||||
|
<Badge variant="outline" className="bg-red-50 text-red-700 border-red-200">移除</Badge>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="text-sm text-gray-400">
|
||||||
|
原紀錄: {item.quantity} {item.unit_name}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
</div>
|
||||||
<div className="border rounded-md overflow-hidden bg-white shadow-sm">
|
)}
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow className="bg-gray-50 hover:bg-gray-50">
|
|
||||||
<TableHead className="w-[150px]">欄位</TableHead>
|
|
||||||
<TableHead>異動前</TableHead>
|
|
||||||
<TableHead>異動後</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{filteredKeys.some(key => !isSnapshotField(key)) ? (
|
|
||||||
filteredKeys
|
|
||||||
.filter(key => !isSnapshotField(key))
|
|
||||||
.map((key) => {
|
|
||||||
const oldValue = old[key];
|
|
||||||
const newValue = attributes[key];
|
|
||||||
const isChanged = JSON.stringify(oldValue) !== JSON.stringify(newValue);
|
|
||||||
|
|
||||||
// For deleted events, we want to show the current attributes in the "Before" column
|
|
||||||
const displayBefore = activity.event === 'deleted'
|
|
||||||
? getFormattedValue(key, newValue || oldValue)
|
|
||||||
: getFormattedValue(key, oldValue);
|
|
||||||
|
|
||||||
const displayAfter = activity.event === 'deleted'
|
|
||||||
? '-'
|
|
||||||
: getFormattedValue(key, newValue);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow key={key} className={isChanged ? 'bg-amber-50/30 hover:bg-amber-50/50' : 'hover:bg-gray-50/50'}>
|
|
||||||
<TableCell className="font-medium text-gray-700 w-[150px]">{getFieldLabel(key)}</TableCell>
|
|
||||||
<TableCell className="text-gray-500 break-words max-w-[200px]">
|
|
||||||
{displayBefore}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="text-gray-900 font-medium break-words max-w-[200px]">
|
|
||||||
{displayAfter}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={3} className="h-24 text-center text-gray-500">
|
|
||||||
無詳細異動內容
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* Items Diff Section (Special for Purchase Orders) */}
|
|
||||||
{activity.properties?.items_diff && (
|
|
||||||
<div className="mt-6 space-y-4">
|
|
||||||
<h3 className="text-sm font-bold text-gray-900 flex items-center gap-2 px-1">
|
|
||||||
<Package className="w-4 h-4 text-primary-main" />
|
|
||||||
品項異動明細
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div className="border rounded-md overflow-hidden bg-white shadow-sm">
|
|
||||||
<Table>
|
|
||||||
<TableHeader className="bg-gray-50/50">
|
|
||||||
<TableRow>
|
|
||||||
<TableHead>商品名稱</TableHead>
|
|
||||||
<TableHead className="text-center">異動類型</TableHead>
|
|
||||||
<TableHead>異動詳情 (舊 → 新)</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{/* Updated Items */}
|
|
||||||
{activity.properties.items_diff.updated.map((item: any, idx: number) => (
|
|
||||||
<TableRow key={`upd-${idx}`} className="bg-blue-50/10 hover:bg-blue-50/20">
|
|
||||||
<TableCell className="font-medium">{item.product_name}</TableCell>
|
|
||||||
<TableCell className="text-center">
|
|
||||||
<Badge variant="outline" className="bg-blue-50 text-blue-700 border-blue-200">更新</Badge>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="text-sm">
|
|
||||||
<div className="space-y-1">
|
|
||||||
{item.old.quantity !== item.new.quantity && (
|
|
||||||
<div>數量: <span className="text-gray-500 line-through">{item.old.quantity}</span> → <span className="text-blue-700 font-bold">{item.new.quantity}</span></div>
|
|
||||||
)}
|
|
||||||
{item.old.unit_name !== item.new.unit_name && (
|
|
||||||
<div>單位: <span className="text-gray-500 line-through">{item.old.unit_name || '-'}</span> → <span className="text-blue-700 font-bold">{item.new.unit_name || '-'}</span></div>
|
|
||||||
)}
|
|
||||||
{item.old.subtotal !== item.new.subtotal && (
|
|
||||||
<div>小計: <span className="text-gray-500 line-through">${item.old.subtotal}</span> → <span className="text-blue-700 font-bold">${item.new.subtotal}</span></div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{/* Added Items */}
|
|
||||||
{activity.properties.items_diff.added.map((item: any, idx: number) => (
|
|
||||||
<TableRow key={`add-${idx}`} className="bg-green-50/10 hover:bg-green-50/20">
|
|
||||||
<TableCell className="font-medium">{item.product_name}</TableCell>
|
|
||||||
<TableCell className="text-center">
|
|
||||||
<Badge variant="outline" className="bg-green-50 text-green-700 border-green-200">新增</Badge>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="text-sm">
|
|
||||||
數量: {item.quantity} {item.unit_name} / 小計: ${item.subtotal}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{/* Removed Items */}
|
|
||||||
{activity.properties.items_diff.removed.map((item: any, idx: number) => (
|
|
||||||
<TableRow key={`rem-${idx}`} className="bg-red-50/10 hover:bg-red-50/20">
|
|
||||||
<TableCell className="font-medium text-gray-400 line-through">{item.product_name}</TableCell>
|
|
||||||
<TableCell className="text-center">
|
|
||||||
<Badge variant="outline" className="bg-red-50 text-red-700 border-red-200">移除</Badge>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="text-sm text-gray-400">
|
|
||||||
原紀錄: {item.quantity} {item.unit_name}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</ScrollArea>
|
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
Reference in New Issue
Block a user