refactor(ui): 統一 ActivityDetailDialog 滾動行為

- 移除 ScrollArea,改用原生的 overflow-y-auto 於 DialogContent

- 參考 VendorDialog 與 ProductDialog 的標準實作方式
This commit is contained in:
2026-01-19 15:38:44 +08:00
parent f83baffddb
commit 6bd52fe3db

View File

@@ -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>