ERP-node/frontend/components/barcode/BarcodeListTable.tsx

245 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useState } from "react";
import { BarcodeLabelMaster } from "@/types/barcode";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { Copy, Trash2, Loader2 } from "lucide-react";
import { barcodeApi } from "@/lib/api/barcodeApi";
import { useToast } from "@/hooks/use-toast";
import { useRouter } from "next/navigation";
import { format } from "date-fns";
interface BarcodeListTableProps {
labels: BarcodeLabelMaster[];
total: number;
page: number;
limit: number;
isLoading: boolean;
onPageChange: (page: number) => void;
onRefresh: () => void;
}
export function BarcodeListTable({
labels,
total,
page,
limit,
isLoading,
onPageChange,
onRefresh,
}: BarcodeListTableProps) {
const [deleteTarget, setDeleteTarget] = useState<string | null>(null);
const [isDeleting, setIsDeleting] = useState(false);
const [isCopying, setIsCopying] = useState(false);
const { toast } = useToast();
const router = useRouter();
const totalPages = Math.ceil(total / limit);
const handleEdit = (labelId: string) => {
router.push(`/admin/screenMng/barcodeList/designer/${labelId}`);
};
const handleCopy = async (labelId: string) => {
setIsCopying(true);
try {
const response = await barcodeApi.copyLabel(labelId);
if (response.success) {
toast({
title: "성공",
description: "바코드 라벨이 복사되었습니다.",
});
onRefresh();
}
} catch (error: any) {
toast({
title: "오류",
description: error.message || "바코드 라벨 복사에 실패했습니다.",
variant: "destructive",
});
} finally {
setIsCopying(false);
}
};
const handleDeleteClick = (labelId: string) => {
setDeleteTarget(labelId);
};
const handleDeleteConfirm = async () => {
if (!deleteTarget) return;
setIsDeleting(true);
try {
const response = await barcodeApi.deleteLabel(deleteTarget);
if (response.success) {
toast({
title: "성공",
description: "바코드 라벨이 삭제되었습니다.",
});
setDeleteTarget(null);
onRefresh();
}
} catch (error: any) {
toast({
title: "오류",
description: error.message || "바코드 라벨 삭제에 실패했습니다.",
variant: "destructive",
});
} finally {
setIsDeleting(false);
}
};
const formatDate = (dateString: string | null) => {
if (!dateString) return "-";
try {
return format(new Date(dateString), "yyyy-MM-dd");
} catch {
return dateString;
}
};
if (isLoading) {
return (
<div className="flex h-64 items-center justify-center">
<Loader2 className="text-muted-foreground h-8 w-8 animate-spin" />
</div>
);
}
if (labels.length === 0) {
return (
<div className="text-muted-foreground flex h-64 flex-col items-center justify-center">
<p> .</p>
</div>
);
}
return (
<>
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[80px]">No</TableHead>
<TableHead></TableHead>
<TableHead className="w-[120px]">릿 </TableHead>
<TableHead className="w-[120px]"></TableHead>
<TableHead className="w-[120px]"></TableHead>
<TableHead className="w-[200px]"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{labels.map((label, index) => {
const rowNumber = (page - 1) * limit + index + 1;
return (
<TableRow
key={label.label_id}
onClick={() => handleEdit(label.label_id)}
className="cursor-pointer hover:bg-muted/50"
>
<TableCell className="font-medium">{rowNumber}</TableCell>
<TableCell>
<div>
<div className="font-medium">{label.label_name_kor}</div>
{label.label_name_eng && (
<div className="text-muted-foreground text-sm">{label.label_name_eng}</div>
)}
</div>
</TableCell>
<TableCell>
{label.width_mm != null && label.height_mm != null
? `${label.width_mm}×${label.height_mm}mm`
: label.template_type || "-"}
</TableCell>
<TableCell>{label.created_by || "-"}</TableCell>
<TableCell>{formatDate(label.updated_at || label.created_at)}</TableCell>
<TableCell>
<div className="flex gap-2" onClick={(e) => e.stopPropagation()}>
<Button
size="icon"
variant="outline"
onClick={() => handleCopy(label.label_id)}
disabled={isCopying}
className="h-8 w-8"
title="복사"
>
<Copy className="h-4 w-4" />
</Button>
<Button
size="icon"
variant="destructive"
onClick={() => handleDeleteClick(label.label_id)}
className="h-8 w-8"
title="삭제"
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
{totalPages > 1 && (
<div className="flex items-center justify-center gap-2 p-4">
<Button variant="outline" size="sm" onClick={() => onPageChange(page - 1)} disabled={page === 1}>
</Button>
<span className="text-muted-foreground text-sm">
{page} / {totalPages}
</span>
<Button variant="outline" size="sm" onClick={() => onPageChange(page + 1)} disabled={page === totalPages}>
</Button>
</div>
)}
<AlertDialog open={deleteTarget !== null} onOpenChange={(open) => !open && setDeleteTarget(null)}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle> </AlertDialogTitle>
<AlertDialogDescription>
?
<br />
.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={isDeleting}></AlertDialogCancel>
<AlertDialogAction
onClick={handleDeleteConfirm}
disabled={isDeleting}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
{isDeleting ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
...
</>
) : (
"삭제"
)}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
}