ERP-node/frontend/components/multilang/LangKeyModal.tsx

276 lines
8.8 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
interface Language {
langCode: string;
langName: string;
langNative: string;
}
interface LangKey {
keyId?: number;
companyCode: string;
menuCode: string;
langKey: string;
keyType: string;
description: string;
isActive: string;
}
interface LangText {
textId?: number;
keyId?: number;
langCode: string;
langText: string;
isActive: string;
}
interface LangKeyModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
langKey?: LangKey;
languages: Language[];
companies: { code: string; name: string }[];
menus: { code: string; name: string }[];
keyTypes: { code: string; name: string }[];
onSave: (keyData: LangKey, textData: LangText[]) => void;
}
export function LangKeyModal({
open,
onOpenChange,
langKey,
languages,
companies,
menus,
keyTypes,
onSave,
}: LangKeyModalProps) {
const [keyData, setKeyData] = useState<LangKey>({
companyCode: "",
menuCode: "",
langKey: "",
keyType: "TEXT",
description: "",
isActive: "Y",
});
const [textData, setTextData] = useState<LangText[]>([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (langKey) {
setKeyData(langKey);
fetchLangTexts(langKey.keyId!);
} else {
setKeyData({
companyCode: "",
menuCode: "",
langKey: "",
keyType: "TEXT",
description: "",
isActive: "Y",
});
initializeTextData();
}
}, [langKey, open]);
const initializeTextData = () => {
const initialTexts = languages.map((lang) => ({
langCode: lang.langCode,
langText: "",
isActive: "Y",
}));
setTextData(initialTexts);
};
const fetchLangTexts = async (keyId: number) => {
try {
const response = await fetch(`/api/multilang/keys/${keyId}/texts`);
const data = await response.json();
if (data.success) {
const texts = data.data;
const allTexts = languages.map((lang) => {
const existingText = texts.find((t: LangText) => t.langCode === lang.langCode);
return (
existingText || {
langCode: lang.langCode,
langText: "",
isActive: "Y",
}
);
});
setTextData(allTexts);
}
} catch (error) {
console.error("다국어 텍스트 조회 실패:", error);
initializeTextData();
}
};
const handleSave = async () => {
if (!keyData.companyCode || !keyData.menuCode || !keyData.langKey) {
alert("필수 항목을 입력해주세요.");
return;
}
setLoading(true);
try {
await onSave(keyData, textData);
onOpenChange(false);
} catch (error) {
console.error("저장 실패:", error);
alert("저장에 실패했습니다.");
} finally {
setLoading(false);
}
};
const updateTextData = (langCode: string, value: string) => {
setTextData((prev) => prev.map((text) => (text.langCode === langCode ? { ...text, langText: value } : text)));
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-h-[90vh] max-w-4xl overflow-y-auto">
<DialogHeader>
<DialogTitle>{langKey ? "다국어 키 수정" : "새 다국어 키 추가"}</DialogTitle>
</DialogHeader>
<div className="space-y-6">
{/* 키 정보 */}
<Card>
<CardHeader>
<CardTitle> </CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<Label htmlFor="companyCode"> *</Label>
<Select
value={keyData.companyCode}
onValueChange={(value) => setKeyData((prev) => ({ ...prev, companyCode: value }))}
>
<SelectTrigger>
<SelectValue placeholder="회사 선택" />
</SelectTrigger>
<SelectContent>
{companies.map((company) => (
<SelectItem key={company.code} value={company.code}>
{company.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label htmlFor="menuCode"> *</Label>
<Select
value={keyData.menuCode}
onValueChange={(value) => setKeyData((prev) => ({ ...prev, menuCode: value }))}
>
<SelectTrigger>
<SelectValue placeholder="메뉴 선택" />
</SelectTrigger>
<SelectContent>
{menus.map((menu) => (
<SelectItem key={menu.code} value={menu.code}>
{menu.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<Label htmlFor="langKey"> *</Label>
<Input
id="langKey"
value={keyData.langKey}
onChange={(e) => setKeyData((prev) => ({ ...prev, langKey: e.target.value }))}
placeholder="예: menu.dashboard, button.save"
/>
</div>
<div>
<Label htmlFor="keyType"> </Label>
<Select
value={keyData.keyType}
onValueChange={(value) => setKeyData((prev) => ({ ...prev, keyType: value }))}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{keyTypes.map((type) => (
<SelectItem key={type.code} value={type.code}>
{type.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<div>
<Label htmlFor="description"></Label>
<Textarea
id="description"
value={keyData.description}
onChange={(e) => setKeyData((prev) => ({ ...prev, description: e.target.value }))}
placeholder="키에 대한 설명을 입력하세요"
rows={3}
/>
</div>
</CardContent>
</Card>
{/* 다국어 텍스트 */}
<Card>
<CardHeader>
<CardTitle> </CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{textData.map((text, index) => (
<div key={text.langCode} className="flex items-center space-x-4">
<div className="w-24">
<Badge variant="outline" className="w-full justify-center">
{languages.find((l) => l.langCode === text.langCode)?.langName}
</Badge>
</div>
<div className="flex-1">
<Input
value={text.langText}
onChange={(e) => updateTextData(text.langCode, e.target.value)}
placeholder={`${languages.find((l) => l.langCode === text.langCode)?.langName} 텍스트 입력`}
/>
</div>
</div>
))}
</div>
</CardContent>
</Card>
{/* 버튼 */}
<div className="flex justify-end space-x-2">
<Button variant="outline" onClick={() => onOpenChange(false)}>
</Button>
<Button onClick={handleSave} disabled={loading}>
{loading ? "저장 중..." : "저장"}
</Button>
</div>
</div>
</DialogContent>
</Dialog>
);
}