최초커밋
This commit is contained in:
275
frontend/components/multilang/LangKeyModal.tsx
Normal file
275
frontend/components/multilang/LangKeyModal.tsx
Normal file
@@ -0,0 +1,275 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user