Files
Groupware/Project/frontend/src/components/kuntae/KuntaeEditModal.tsx

296 lines
14 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { X, Save, Calendar, Clock, FileText, User } from 'lucide-react';
import { KuntaeModel } from '@/types';
interface KuntaeEditModalProps {
isOpen: boolean;
onClose: () => void;
onSave: (data: KuntaeFormData) => Promise<void>;
initialData?: KuntaeModel | null;
mode: 'add' | 'edit' | 'copy';
}
export interface KuntaeFormData {
idx?: number;
cate: string;
sdate: string;
edate: string;
term: number;
crtime: number;
termDr: number;
drTime: number;
contents: string;
uid: string;
}
const CATE_OPTIONS = ['연차', '대체', '공가', '경조', '병가', '오전반차', '오후반차', '조퇴', '외출', '지각', '결근', '휴직', '교육', '출장', '재택', '특근', '당직', '기타'];
export function KuntaeEditModal({ isOpen, onClose, onSave, initialData, mode }: KuntaeEditModalProps) {
const [formData, setFormData] = useState<KuntaeFormData>({
cate: '연차',
sdate: new Date().toISOString().split('T')[0],
edate: new Date().toISOString().split('T')[0],
term: 1,
crtime: 0,
termDr: 0,
drTime: 0,
contents: '',
uid: '',
});
const [saving, setSaving] = useState(false);
useEffect(() => {
if (isOpen) {
if (initialData) {
setFormData({
idx: mode === 'edit' ? initialData.idx : undefined,
cate: initialData.cate || '연차',
sdate: initialData.sdate || new Date().toISOString().split('T')[0],
edate: initialData.edate || new Date().toISOString().split('T')[0],
term: initialData.term || 0,
crtime: initialData.crtime || 0,
termDr: initialData.termDr || 0,
drTime: initialData.DrTime || 0,
contents: initialData.contents || '',
uid: initialData.uid || '',
});
} else {
// 초기화
setFormData({
cate: '연차',
sdate: new Date().toISOString().split('T')[0],
edate: new Date().toISOString().split('T')[0],
term: 1,
crtime: 0,
termDr: 0,
drTime: 0,
contents: '',
uid: '', // 상위 컴포넌트에서 현재 사용자 ID를 주입받거나 여기서 처리해야 함
});
}
}
}, [isOpen, initialData, mode]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: name === 'term' || name === 'crtime' || name === 'termDr' || name === 'drTime'
? parseFloat(value) || 0
: value
}));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setSaving(true);
try {
await onSave(formData);
onClose();
} catch (error) {
console.error('Save error:', error);
alert('저장 중 오류가 발생했습니다.');
} finally {
setSaving(false);
}
};
// 날짜 변경 시 기간 자동 계산 (단순 1일 차이)
useEffect(() => {
if (formData.sdate && formData.edate) {
const start = new Date(formData.sdate);
const end = new Date(formData.edate);
const diffTime = Math.abs(end.getTime() - start.getTime());
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1;
// 연차/휴가 등일 때만 자동 계산
if (['연차', '공가', '경조', '병가', '교육', '출장'].includes(formData.cate)) {
setFormData(prev => ({ ...prev, term: diffDays }));
}
}
}, [formData.sdate, formData.edate, formData.cate]);
if (!isOpen) return null;
const title = mode === 'add' ? '근태 등록' : mode === 'edit' ? '근태 수정' : '근태 복사 등록';
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm animate-fade-in">
<div className="bg-[#1e1e2e] rounded-2xl shadow-2xl w-full max-w-lg border border-white/10 overflow-hidden">
{/* 헤더 */}
<div className="px-6 py-4 border-b border-white/10 flex justify-between items-center bg-white/5">
<h2 className="text-xl font-bold text-white flex items-center">
<Calendar className="w-5 h-5 mr-2 text-primary-400" />
{title}
</h2>
<button onClick={onClose} className="text-white/50 hover:text-white transition-colors">
<X className="w-6 h-6" />
</button>
</div>
{/* 폼 */}
<form onSubmit={handleSubmit} className="p-6 space-y-4">
{/* 구분 및 사용자 */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-white/70 text-sm font-medium mb-1"></label>
<select
name="cate"
value={formData.cate}
onChange={handleChange}
className="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400"
>
{CATE_OPTIONS.map(opt => (
<option key={opt} value={opt} className="bg-[#1e1e2e]">{opt}</option>
))}
</select>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-1"> ID</label>
<div className="relative">
<User className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-white/50" />
<input
type="text"
name="uid"
value={formData.uid}
onChange={handleChange}
readOnly={mode === 'edit'} // 수정 시에는 사용자 변경 불가
className={`w-full bg-white/5 border border-white/10 rounded-lg pl-10 pr-3 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 ${mode === 'edit' ? 'opacity-50 cursor-not-allowed' : ''}`}
placeholder="사번 입력"
/>
</div>
</div>
</div>
{/* 기간 */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-white/70 text-sm font-medium mb-1"></label>
<input
type="date"
name="sdate"
value={formData.sdate}
onChange={handleChange}
className="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400"
/>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-1"></label>
<input
type="date"
name="edate"
value={formData.edate}
onChange={handleChange}
className="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400"
/>
</div>
</div>
{/* 사용량 (일/시간) */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-white/70 text-sm font-medium mb-1"> ()</label>
<div className="relative">
<Calendar className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-white/50" />
<input
type="number"
name="term"
value={formData.term}
onChange={handleChange}
step="0.5"
className="w-full bg-white/5 border border-white/10 rounded-lg pl-10 pr-3 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400"
/>
</div>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-1"> ()</label>
<div className="relative">
<Clock className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-white/50" />
<input
type="number"
name="crtime"
value={formData.crtime}
onChange={handleChange}
step="0.5"
className="w-full bg-white/5 border border-white/10 rounded-lg pl-10 pr-3 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400"
/>
</div>
</div>
</div>
{/* 발생량 (일/시간) - 대체근무 등 발생 시 */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-white/70 text-sm font-medium mb-1"> ()</label>
<input
type="number"
name="termDr"
value={formData.termDr}
onChange={handleChange}
step="0.5"
className="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400"
/>
</div>
<div>
<label className="block text-white/70 text-sm font-medium mb-1"> ()</label>
<input
type="number"
name="drTime"
value={formData.drTime}
onChange={handleChange}
step="0.5"
className="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400"
/>
</div>
</div>
{/* 내용 */}
<div>
<label className="block text-white/70 text-sm font-medium mb-1"></label>
<div className="relative">
<FileText className="absolute left-3 top-3 w-4 h-4 text-white/50" />
<textarea
name="contents"
value={formData.contents}
onChange={handleChange}
rows={3}
className="w-full bg-white/5 border border-white/10 rounded-lg pl-10 pr-3 py-2 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 resize-none"
placeholder="근태 사유 또는 내용 입력"
/>
</div>
</div>
{/* 버튼 */}
<div className="flex justify-end space-x-3 pt-4 border-t border-white/10">
<button
type="button"
onClick={onClose}
className="px-4 py-2 rounded-lg text-white/70 hover:text-white hover:bg-white/10 transition-colors"
>
</button>
<button
type="submit"
disabled={saving}
className="bg-primary-500 hover:bg-primary-600 text-white px-6 py-2 rounded-lg transition-colors flex items-center disabled:opacity-50"
>
{saving ? (
<>
<div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin mr-2" />
...
</>
) : (
<>
<Save className="w-4 h-4 mr-2" />
</>
)}
</button>
</div>
</form>
</div>
</div>
);
}