ERP-node/frontend/components/barcode/designer/BarcodeTemplatePalette.tsx

133 lines
4.3 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, useEffect, useMemo } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Loader2, Search } from "lucide-react";
import { useBarcodeDesigner } from "@/contexts/BarcodeDesignerContext";
import { barcodeApi, BarcodeLabelTemplate } from "@/lib/api/barcodeApi";
type Category = "all" | "basic" | "zebra";
export function BarcodeTemplatePalette() {
const { applyTemplate } = useBarcodeDesigner();
const [templates, setTemplates] = useState<BarcodeLabelTemplate[]>([]);
const [loading, setLoading] = useState(true);
const [category, setCategory] = useState<Category>("all");
const [searchText, setSearchText] = useState("");
useEffect(() => {
(async () => {
try {
const res = await barcodeApi.getTemplates();
if (res.success && res.data) setTemplates(res.data);
} catch {
setTemplates([]);
} finally {
setLoading(false);
}
})();
}, []);
const filtered = useMemo(() => {
let list = templates;
if (category === "basic") {
list = list.filter((t) => t.template_id.startsWith("TMPL_"));
} else if (category === "zebra") {
list = list.filter((t) => t.template_id.startsWith("ZJ"));
}
const q = searchText.trim().toLowerCase();
if (q) {
list = list.filter(
(t) =>
t.template_id.toLowerCase().includes(q) ||
(t.template_name_kor && t.template_name_kor.toLowerCase().includes(q)) ||
(t.template_name_eng && t.template_name_eng.toLowerCase().includes(q))
);
}
return list;
}, [templates, category, searchText]);
if (loading) {
return (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm"> </CardTitle>
</CardHeader>
<CardContent className="flex justify-center py-4">
<Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
</CardContent>
</Card>
);
}
return (
<Card>
<CardHeader className="pb-2">
<CardTitle className="text-sm"> </CardTitle>
</CardHeader>
<CardContent className="space-y-2">
<div className="relative">
<Search className="text-muted-foreground absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2" />
<Input
placeholder="코드·이름으로 찾기"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
className="h-8 pl-8 text-xs"
/>
</div>
<div className="flex gap-1">
<Button
variant={category === "all" ? "secondary" : "ghost"}
size="sm"
className="flex-1 text-xs"
onClick={() => setCategory("all")}
>
</Button>
<Button
variant={category === "basic" ? "secondary" : "ghost"}
size="sm"
className="flex-1 text-xs"
onClick={() => setCategory("basic")}
>
</Button>
<Button
variant={category === "zebra" ? "secondary" : "ghost"}
size="sm"
className="flex-1 text-xs"
onClick={() => setCategory("zebra")}
>
</Button>
</div>
<ScrollArea className="h-[280px] pr-2">
<div className="space-y-1">
{filtered.length === 0 ? (
<p className="text-muted-foreground py-2 text-center text-xs"> </p>
) : (
filtered.map((t) => (
<Button
key={t.template_id}
variant="outline"
size="sm"
className="h-auto w-full justify-start py-1.5 text-left"
onClick={() => applyTemplate(t.template_id)}
>
<span className="truncate">{t.template_name_kor}</span>
<span className="text-muted-foreground ml-1 shrink-0 text-xs">
{t.width_mm}×{t.height_mm}
</span>
</Button>
))
)}
</div>
</ScrollArea>
</CardContent>
</Card>
);
}