바코드 기능 커밋밋
This commit is contained in:
132
frontend/components/barcode/designer/BarcodeTemplatePalette.tsx
Normal file
132
frontend/components/barcode/designer/BarcodeTemplatePalette.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user