feat: enhance AttachmentPreviewDialog integration; add icon variant and improve file display in material cards

This commit is contained in:
Yoga Pangestu 2026-09-04 11:51:27 +07:00
parent 19397d3b5b
commit facf7ab177
3 changed files with 68 additions and 59 deletions

View File

@ -1,11 +1,17 @@
import { Paperclip } from 'lucide-react'; import { Eye, Paperclip } from 'lucide-react';
import { useState } from 'react'; import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/tooltip';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp']; const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
@ -19,12 +25,14 @@ type AttachmentPreviewDialogProps = {
fileUrl: string; fileUrl: string;
fileName: string; fileName: string;
className?: string; className?: string;
variant?: 'link' | 'icon';
}; };
export function AttachmentPreviewDialog({ export function AttachmentPreviewDialog({
fileUrl, fileUrl,
fileName, fileName,
className, className,
variant = 'link',
}: AttachmentPreviewDialogProps) { }: AttachmentPreviewDialogProps) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const extension = fileExtension(fileName); const extension = fileExtension(fileName);
@ -34,17 +42,34 @@ export function AttachmentPreviewDialog({
return ( return (
<> <>
<button {variant === 'icon' ? (
type="button" <Tooltip>
onClick={() => setOpen(true)} <TooltipTrigger asChild>
className={cn( <Button
'inline-flex items-center gap-1 text-primary underline underline-offset-4 hover:text-primary/80', type="button"
className, variant="ghost"
)} size="icon"
> className={className}
<Paperclip className="h-3.5 w-3.5 shrink-0" /> onClick={() => setOpen(true)}
<span className="truncate">{fileName}</span> >
</button> <Eye className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="top">Lihat</TooltipContent>
</Tooltip>
) : (
<button
type="button"
onClick={() => setOpen(true)}
className={cn(
'inline-flex items-center gap-1 text-primary underline underline-offset-4 hover:text-primary/80',
className,
)}
>
<Paperclip className="h-3.5 w-3.5 shrink-0" />
<span className="truncate">{fileName}</span>
</button>
)}
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogContent <DialogContent

View File

@ -27,7 +27,14 @@ export function createMaterialCard(params: CreateCardParams) {
</Badge> </Badge>
)} )}
</div> </div>
{(canUpdate || canDelete) && ( <div className="flex items-center">
{material.file_url && material.file_name && (
<AttachmentPreviewDialog
fileUrl={material.file_url}
fileName={material.file_name}
variant="icon"
/>
)}
<RowActions <RowActions
actions={[ actions={[
{ {
@ -46,7 +53,7 @@ export function createMaterialCard(params: CreateCardParams) {
}, },
]} ]}
/> />
)} </div>
</div> </div>
<div> <div>
@ -63,18 +70,6 @@ export function createMaterialCard(params: CreateCardParams) {
? `${courseClass.course?.code ?? ''} ${courseClass.course?.name ?? ''}` ? `${courseClass.course?.code ?? ''} ${courseClass.course?.name ?? ''}`
: '-'} : '-'}
</p> </p>
{material.files.length > 0 && (
<div className="flex flex-col gap-1">
{material.files.map((file) => (
<AttachmentPreviewDialog
key={file.id}
fileUrl={file.url}
fileName={file.name}
/>
))}
</div>
)}
</div> </div>
); );
}; };

View File

@ -64,38 +64,27 @@ export function createMaterialColumns(
return `${courseClass.course?.code ?? ''} ${courseClass.course?.name ?? ''}`; return `${courseClass.course?.code ?? ''} ${courseClass.course?.name ?? ''}`;
}, },
}, },
{
accessorKey: 'file_name',
header: () => <span>File</span>,
cell: ({ row }) => {
const material = row.original;
if (!material.file_url || !material.file_name) {
return <span className="text-muted-foreground">-</span>;
}
return (
<AttachmentPreviewDialog
fileUrl={material.file_url}
fileName={material.file_name}
/>
);
},
},
]; ];
if (canUpdate || canDelete) { columns.push({
columns.push({ id: 'actions',
id: 'actions', header: () => <span className="block text-center">Aksi</span>,
header: () => <span className="block text-center">Aksi</span>, meta: {
meta: { className: 'w-[130px] text-center',
className: 'w-[100px] text-center', headerClassName: 'w-[130px] text-center',
headerClassName: 'w-[100px] text-center', },
}, cell: ({ row }) => {
cell: ({ row }) => { const material = row.original;
const material = row.original;
return ( return (
<div className="flex items-center justify-center">
{material.file_url && material.file_name && (
<AttachmentPreviewDialog
fileUrl={material.file_url}
fileName={material.file_name}
variant="icon"
/>
)}
<RowActions <RowActions
actions={[ actions={[
{ {
@ -114,10 +103,10 @@ export function createMaterialColumns(
}, },
]} ]}
/> />
); </div>
}, );
}); },
} });
return columns; return columns;
} }