229 lines
7.2 KiB
TypeScript
229 lines
7.2 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
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 { Textarea } from "@/components/ui/textarea";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { Loader2 } from "lucide-react";
|
|
import { reportApi } from "@/lib/api/reportApi";
|
|
import { useToast } from "@/hooks/use-toast";
|
|
import { CreateReportRequest, ReportTemplate } from "@/types/report";
|
|
|
|
interface ReportCreateModalProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
onSuccess: () => void;
|
|
}
|
|
|
|
export function ReportCreateModal({ isOpen, onClose, onSuccess }: ReportCreateModalProps) {
|
|
const [formData, setFormData] = useState<CreateReportRequest>({
|
|
reportNameKor: "",
|
|
reportNameEng: "",
|
|
templateId: undefined,
|
|
reportType: "BASIC",
|
|
description: "",
|
|
});
|
|
const [templates, setTemplates] = useState<ReportTemplate[]>([]);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [isLoadingTemplates, setIsLoadingTemplates] = useState(false);
|
|
const { toast } = useToast();
|
|
|
|
// 템플릿 목록 불러오기
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
fetchTemplates();
|
|
}
|
|
}, [isOpen]);
|
|
|
|
const fetchTemplates = async () => {
|
|
setIsLoadingTemplates(true);
|
|
try {
|
|
const response = await reportApi.getTemplates();
|
|
if (response.success && response.data) {
|
|
setTemplates([...response.data.system, ...response.data.custom]);
|
|
}
|
|
} catch (error: any) {
|
|
toast({
|
|
title: "오류",
|
|
description: "템플릿 목록을 불러오는데 실패했습니다.",
|
|
variant: "destructive",
|
|
});
|
|
} finally {
|
|
setIsLoadingTemplates(false);
|
|
}
|
|
};
|
|
|
|
const handleSubmit = async () => {
|
|
// 유효성 검증
|
|
if (!formData.reportNameKor.trim()) {
|
|
toast({
|
|
title: "입력 오류",
|
|
description: "리포트명(한글)을 입력해주세요.",
|
|
variant: "destructive",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!formData.reportType) {
|
|
toast({
|
|
title: "입력 오류",
|
|
description: "리포트 타입을 선택해주세요.",
|
|
variant: "destructive",
|
|
});
|
|
return;
|
|
}
|
|
|
|
setIsLoading(true);
|
|
try {
|
|
const response = await reportApi.createReport(formData);
|
|
if (response.success) {
|
|
toast({
|
|
title: "성공",
|
|
description: "리포트가 생성되었습니다.",
|
|
});
|
|
handleClose();
|
|
onSuccess();
|
|
}
|
|
} catch (error: any) {
|
|
toast({
|
|
title: "오류",
|
|
description: error.message || "리포트 생성에 실패했습니다.",
|
|
variant: "destructive",
|
|
});
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleClose = () => {
|
|
setFormData({
|
|
reportNameKor: "",
|
|
reportNameEng: "",
|
|
templateId: undefined,
|
|
reportType: "BASIC",
|
|
description: "",
|
|
});
|
|
onClose();
|
|
};
|
|
|
|
return (
|
|
<Dialog open={isOpen} onOpenChange={handleClose}>
|
|
<DialogContent className="sm:max-w-[500px]">
|
|
<DialogHeader>
|
|
<DialogTitle>새 리포트 생성</DialogTitle>
|
|
<DialogDescription>새로운 리포트를 생성합니다. 필수 항목을 입력해주세요.</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-4 py-4">
|
|
{/* 리포트명 (한글) */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="reportNameKor">
|
|
리포트명 (한글) <span className="text-destructive">*</span>
|
|
</Label>
|
|
<Input
|
|
id="reportNameKor"
|
|
placeholder="예: 발주서"
|
|
value={formData.reportNameKor}
|
|
onChange={(e) => setFormData({ ...formData, reportNameKor: e.target.value })}
|
|
/>
|
|
</div>
|
|
|
|
{/* 리포트명 (영문) */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="reportNameEng">리포트명 (영문)</Label>
|
|
<Input
|
|
id="reportNameEng"
|
|
placeholder="예: Purchase Order"
|
|
value={formData.reportNameEng}
|
|
onChange={(e) => setFormData({ ...formData, reportNameEng: e.target.value })}
|
|
/>
|
|
</div>
|
|
|
|
{/* 템플릿 선택 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="templateId">템플릿</Label>
|
|
<Select
|
|
value={formData.templateId || "none"}
|
|
onValueChange={(value) => setFormData({ ...formData, templateId: value === "none" ? undefined : value })}
|
|
disabled={isLoadingTemplates}
|
|
>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="템플릿 선택 (선택사항)" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="none">템플릿 없음</SelectItem>
|
|
{templates.map((template) => (
|
|
<SelectItem key={template.template_id} value={template.template_id}>
|
|
{template.template_name_kor}
|
|
{template.is_system === "Y" && " (시스템)"}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{/* 리포트 타입 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="reportType">
|
|
리포트 타입 <span className="text-destructive">*</span>
|
|
</Label>
|
|
<Select
|
|
value={formData.reportType}
|
|
onValueChange={(value) => setFormData({ ...formData, reportType: value })}
|
|
>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="타입 선택" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="ORDER">발주서</SelectItem>
|
|
<SelectItem value="INVOICE">청구서</SelectItem>
|
|
<SelectItem value="STATEMENT">거래명세서</SelectItem>
|
|
<SelectItem value="RECEIPT">영수증</SelectItem>
|
|
<SelectItem value="BASIC">기본</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{/* 설명 */}
|
|
<div className="space-y-2">
|
|
<Label htmlFor="description">설명</Label>
|
|
<Textarea
|
|
id="description"
|
|
placeholder="리포트에 대한 설명을 입력하세요"
|
|
value={formData.description}
|
|
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
|
|
rows={3}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<DialogFooter>
|
|
<Button variant="outline" onClick={handleClose} disabled={isLoading}>
|
|
취소
|
|
</Button>
|
|
<Button onClick={handleSubmit} disabled={isLoading}>
|
|
{isLoading ? (
|
|
<>
|
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
생성 중...
|
|
</>
|
|
) : (
|
|
"생성"
|
|
)}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|