ERP-node/frontend/components/layout/ProfileModal.tsx

254 lines
8.6 KiB
TypeScript

import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Camera, X } from "lucide-react";
import { ProfileFormData } from "@/types/profile";
// 알림 모달 컴포넌트
interface AlertModalProps {
isOpen: boolean;
onClose: () => void;
title: string;
message: string;
type?: "success" | "error" | "info";
}
function AlertModal({ isOpen, onClose, title, message, type = "info" }: AlertModalProps) {
const getTypeColor = () => {
switch (type) {
case "success":
return "text-green-600";
case "error":
return "text-destructive";
default:
return "text-primary";
}
};
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle className={getTypeColor()}>{title}</DialogTitle>
</DialogHeader>
<div className="py-4">
<p className="text-sm text-muted-foreground">{message}</p>
</div>
<div className="flex justify-end">
<Button onClick={onClose} className="w-20">
</Button>
</div>
</DialogContent>
</Dialog>
);
}
interface ProfileModalProps {
isOpen: boolean;
user: any;
formData: ProfileFormData;
selectedImage: string;
isSaving: boolean;
departments: Array<{
deptCode: string;
deptName: string;
}>;
alertModal: {
isOpen: boolean;
title: string;
message: string;
type: "success" | "error" | "info";
};
onClose: () => void;
onFormChange: (field: keyof ProfileFormData, value: string) => void;
onImageSelect: (event: React.ChangeEvent<HTMLInputElement>) => void;
onImageRemove: () => void;
onSave: () => void;
onAlertClose: () => void;
}
/**
* 프로필 수정 모달 컴포넌트
*/
export function ProfileModal({
isOpen,
user,
formData,
selectedImage,
isSaving,
departments,
alertModal,
onClose,
onFormChange,
onImageSelect,
onImageRemove,
onSave,
onAlertClose,
}: ProfileModalProps) {
return (
<>
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="sm:max-w-[500px]">
<DialogHeader>
<DialogTitle> </DialogTitle>
</DialogHeader>
<div className="grid gap-6 py-4">
{/* 프로필 사진 섹션 */}
<div className="flex flex-col items-center space-y-4">
<div className="relative">
<div className="relative flex h-24 w-24 shrink-0 overflow-hidden rounded-full">
{selectedImage && selectedImage.trim() !== "" ? (
<img
src={selectedImage}
alt="프로필 사진 미리보기"
className="aspect-square h-full w-full object-cover"
/>
) : (
<div className="flex h-full w-full items-center justify-center rounded-full bg-slate-200 text-2xl font-semibold text-slate-700">
{formData.userName?.substring(0, 1)?.toUpperCase() || "U"}
</div>
)}
</div>
{selectedImage && selectedImage.trim() !== "" ? (
<Button
type="button"
variant="destructive"
size="icon"
className="absolute -top-2 -right-2 h-6 w-6 rounded-full"
onClick={onImageRemove}
>
<X className="h-3 w-3" />
</Button>
) : null}
</div>
<div className="flex gap-2">
<Button
type="button"
variant="outline"
size="sm"
onClick={() => document.getElementById("profile-image-input")?.click()}
className="flex items-center gap-2"
>
<Camera className="h-4 w-4" />
</Button>
</div>
<input
id="profile-image-input"
type="file"
accept="image/*"
onChange={onImageSelect}
className="hidden"
/>
</div>
{/* 사용자 정보 폼 */}
<div className="grid gap-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="userId"> ID</Label>
<Input id="userId" value={user?.userId || ""} disabled className="bg-muted" />
</div>
<div className="space-y-2">
<Label htmlFor="userName"></Label>
<Input
id="userName"
value={formData.userName}
onChange={(e) => onFormChange("userName", e.target.value)}
placeholder="이름을 입력하세요"
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="email"></Label>
<Input
id="email"
type="email"
value={formData.email}
onChange={(e) => onFormChange("email", e.target.value)}
placeholder="이메일을 입력하세요"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="deptName"></Label>
<Select value={formData.deptName} onValueChange={(value) => onFormChange("deptName", value)}>
<SelectTrigger>
<SelectValue placeholder="부서 선택" />
</SelectTrigger>
<SelectContent>
{Array.isArray(departments) && departments.length > 0 ? (
departments.map((department) => (
<SelectItem key={department.deptCode} value={department.deptName}>
{department.deptName}
</SelectItem>
))
) : (
<SelectItem value="no-data" disabled>
</SelectItem>
)}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="positionName"></Label>
<Input
id="positionName"
value={formData.positionName}
onChange={(e) => onFormChange("positionName", e.target.value)}
placeholder="직급을 입력하세요"
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="locale"></Label>
<Select value={formData.locale || ""} onValueChange={(value) => onFormChange("locale", value)}>
<SelectTrigger>
<SelectValue placeholder="선택해주세요" />
</SelectTrigger>
<SelectContent>
<SelectItem value="KR"> (KR)</SelectItem>
<SelectItem value="US">English (US)</SelectItem>
<SelectItem value="JP"> (JP)</SelectItem>
<SelectItem value="CN"> (CN)</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
<DialogFooter>
<Button type="button" variant="outline" onClick={onClose} disabled={isSaving}>
</Button>
<Button type="button" onClick={onSave} disabled={isSaving}>
{isSaving ? "저장 중..." : "저장"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* 알림 모달 */}
<AlertModal
isOpen={alertModal.isOpen}
onClose={onAlertClose}
title={alertModal.title}
message={alertModal.message}
type={alertModal.type}
/>
</>
);
}