fix: [card] CardForm 초기값에 누락된 CardFormData 필수 필드 추가
This commit is contained in:
254
src/components/hr/CardManagement/CardForm.tsx
Executable file
254
src/components/hr/CardManagement/CardForm.tsx
Executable file
@@ -0,0 +1,254 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { PageLayout } from '@/components/organisms/PageLayout';
|
||||
import { PageHeader } from '@/components/organisms/PageHeader';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
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 { CreditCard, ArrowLeft, Save } from 'lucide-react';
|
||||
import type { Card as CardType, CardFormData, CardCompany, CardStatus } from './types';
|
||||
import { CARD_COMPANIES, CARD_STATUS_LABELS } from './types';
|
||||
import { getActiveEmployees } from './actions';
|
||||
|
||||
interface CardFormProps {
|
||||
mode: 'create' | 'edit';
|
||||
card?: CardType;
|
||||
onSubmit: (data: CardFormData) => void;
|
||||
}
|
||||
|
||||
export function CardForm({ mode, card, onSubmit }: CardFormProps) {
|
||||
const router = useRouter();
|
||||
const [formData, setFormData] = useState<CardFormData>({
|
||||
cardCompany: '',
|
||||
cardType: '',
|
||||
cardNumber: '',
|
||||
cardName: '',
|
||||
alias: '',
|
||||
expiryDate: '',
|
||||
csv: '',
|
||||
paymentDay: '',
|
||||
pinPrefix: '',
|
||||
totalLimit: 0,
|
||||
usedAmount: 0,
|
||||
remainingLimit: 0,
|
||||
status: 'active',
|
||||
userId: '',
|
||||
memo: '',
|
||||
});
|
||||
|
||||
// 직원 목록 상태
|
||||
const [employees, setEmployees] = useState<Array<{ id: string; label: string }>>([]);
|
||||
const [isLoadingEmployees, setIsLoadingEmployees] = useState(true);
|
||||
|
||||
// 직원 목록 로드
|
||||
useEffect(() => {
|
||||
const loadEmployees = async () => {
|
||||
setIsLoadingEmployees(true);
|
||||
const result = await getActiveEmployees();
|
||||
if (result.success && result.data) {
|
||||
setEmployees(result.data);
|
||||
}
|
||||
setIsLoadingEmployees(false);
|
||||
};
|
||||
loadEmployees();
|
||||
}, []);
|
||||
|
||||
// 수정 모드일 때 기존 데이터 로드
|
||||
useEffect(() => {
|
||||
if (mode === 'edit' && card) {
|
||||
setFormData({
|
||||
cardCompany: card.cardCompany,
|
||||
cardNumber: card.cardNumber,
|
||||
cardName: card.cardName,
|
||||
expiryDate: card.expiryDate,
|
||||
pinPrefix: card.pinPrefix,
|
||||
status: card.status,
|
||||
userId: card.user?.id || '',
|
||||
});
|
||||
}
|
||||
}, [mode, card]);
|
||||
|
||||
const handleBack = () => {
|
||||
if (mode === 'edit' && card) {
|
||||
router.push(`/ko/hr/card-management/${card.id}`);
|
||||
} else {
|
||||
router.push('/ko/hr/card-management');
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
onSubmit(formData);
|
||||
};
|
||||
|
||||
// 카드번호 포맷팅 (1234-1234-1234-1234)
|
||||
const handleCardNumberChange = (value: string) => {
|
||||
const digits = value.replace(/\D/g, '').slice(0, 16);
|
||||
const parts = digits.match(/.{1,4}/g) || [];
|
||||
const formatted = parts.join('-');
|
||||
setFormData(prev => ({ ...prev, cardNumber: formatted }));
|
||||
};
|
||||
|
||||
// 유효기간 포맷팅 (MMYY)
|
||||
const handleExpiryDateChange = (value: string) => {
|
||||
const digits = value.replace(/\D/g, '').slice(0, 4);
|
||||
setFormData(prev => ({ ...prev, expiryDate: digits }));
|
||||
};
|
||||
|
||||
// 비밀번호 앞 2자리
|
||||
const handlePinPrefixChange = (value: string) => {
|
||||
const digits = value.replace(/\D/g, '').slice(0, 2);
|
||||
setFormData(prev => ({ ...prev, pinPrefix: digits }));
|
||||
};
|
||||
|
||||
return (
|
||||
<PageLayout>
|
||||
<PageHeader
|
||||
title={mode === 'create' ? '카드 등록' : '카드 수정'}
|
||||
description={mode === 'create' ? '새로운 카드를 등록합니다' : '카드 정보를 수정합니다'}
|
||||
icon={CreditCard}
|
||||
/>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
{/* 기본 정보 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">기본 정보</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="cardCompany">카드사</Label>
|
||||
<Select
|
||||
value={formData.cardCompany}
|
||||
onValueChange={(value) => setFormData(prev => ({ ...prev, cardCompany: value as CardCompany }))}
|
||||
>
|
||||
<SelectTrigger id="cardCompany">
|
||||
<SelectValue placeholder="카드사를 선택하세요" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{CARD_COMPANIES.map((company) => (
|
||||
<SelectItem key={company.value} value={company.value}>
|
||||
{company.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="cardNumber">카드번호</Label>
|
||||
<Input
|
||||
id="cardNumber"
|
||||
value={formData.cardNumber}
|
||||
onChange={(e) => handleCardNumberChange(e.target.value)}
|
||||
placeholder="1234-1234-1234-1234"
|
||||
maxLength={19}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="expiryDate">유효기간</Label>
|
||||
<Input
|
||||
id="expiryDate"
|
||||
value={formData.expiryDate}
|
||||
onChange={(e) => handleExpiryDateChange(e.target.value)}
|
||||
placeholder="MMYY"
|
||||
maxLength={4}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="pinPrefix">카드 비밀번호 앞 2자리</Label>
|
||||
<Input
|
||||
id="pinPrefix"
|
||||
type="password"
|
||||
value={formData.pinPrefix}
|
||||
onChange={(e) => handlePinPrefixChange(e.target.value)}
|
||||
placeholder="**"
|
||||
maxLength={2}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="cardName">카드명</Label>
|
||||
<Input
|
||||
id="cardName"
|
||||
value={formData.cardName}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, cardName: e.target.value }))}
|
||||
placeholder="카드명을 입력해주세요"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="status">상태</Label>
|
||||
<Select
|
||||
value={formData.status}
|
||||
onValueChange={(value) => setFormData(prev => ({ ...prev, status: value as CardStatus }))}
|
||||
>
|
||||
<SelectTrigger id="status">
|
||||
<SelectValue placeholder="상태 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="active">{CARD_STATUS_LABELS.active}</SelectItem>
|
||||
<SelectItem value="suspended">{CARD_STATUS_LABELS.suspended}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 사용자 정보 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">사용자 정보</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="userId">부서 / 이름 / 직책</Label>
|
||||
<Select
|
||||
value={formData.userId}
|
||||
onValueChange={(value) => setFormData(prev => ({ ...prev, userId: value }))}
|
||||
disabled={isLoadingEmployees}
|
||||
>
|
||||
<SelectTrigger id="userId">
|
||||
<SelectValue placeholder={isLoadingEmployees ? '직원 목록 로딩 중...' : '선택해서 해당 카드의 사용자로 설정'} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{employees.map((employee) => (
|
||||
<SelectItem key={employee.id} value={employee.id}>
|
||||
{employee.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 버튼 영역 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Button type="button" variant="outline" onClick={handleBack}>
|
||||
<ArrowLeft className="w-4 h-4 mr-2" />
|
||||
취소
|
||||
</Button>
|
||||
<Button type="submit">
|
||||
<Save className="w-4 h-4 mr-2" />
|
||||
{mode === 'create' ? '등록' : '저장'}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</PageLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user