- 공통 템플릿 타입 수정 (IntegratedDetailTemplate, UniversalListPage) - 페이지(app/[locale]) 타입 호환성 수정 (80개) - 재고/자재 모듈 타입 수정 (StockStatus, ReceivingManagement) - 생산 모듈 타입 수정 (WorkOrders, WorkerScreen, WorkResults) - 주문/출고 모듈 타입 수정 (ShipmentManagement, Orders) - 견적/단가 모듈 타입 수정 (Quotes, Pricing) - 건설 모듈 타입 수정 (49개, 17개 하위 모듈) - HR 모듈 타입 수정 (CardManagement, VacationManagement 등) - 설정 모듈 타입 수정 (PermissionManagement, AccountManagement 등) - 게시판 모듈 타입 수정 (BoardManagement, BoardList 등) - 회계 모듈 타입 수정 (VendorManagement, BadDebtCollection 등) - 기타 모듈 타입 수정 (CEODashboard, clients, vehicle 등) - 유틸/훅/API 타입 수정 (hooks, contexts, lib) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
246 lines
8.5 KiB
TypeScript
246 lines
8.5 KiB
TypeScript
'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: '',
|
|
cardNumber: '',
|
|
cardName: '',
|
|
expiryDate: '',
|
|
pinPrefix: '',
|
|
status: 'active',
|
|
userId: '',
|
|
});
|
|
|
|
// 직원 목록 상태
|
|
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}?mode=view`);
|
|
} 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: CardFormData) => ({ ...prev, cardNumber: formatted }));
|
|
};
|
|
|
|
// 유효기간 포맷팅 (MMYY)
|
|
const handleExpiryDateChange = (value: string) => {
|
|
const digits = value.replace(/\D/g, '').slice(0, 4);
|
|
setFormData((prev: CardFormData) => ({ ...prev, expiryDate: digits }));
|
|
};
|
|
|
|
// 비밀번호 앞 2자리
|
|
const handlePinPrefixChange = (value: string) => {
|
|
const digits = value.replace(/\D/g, '').slice(0, 2);
|
|
setFormData((prev: CardFormData) => ({ ...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: CardFormData) => ({ ...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: CardFormData) => ({ ...prev, cardName: e.target.value }))}
|
|
placeholder="카드명을 입력해주세요"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="status">상태</Label>
|
|
<Select
|
|
value={formData.status}
|
|
onValueChange={(value) => setFormData((prev: CardFormData) => ({ ...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: CardFormData) => ({ ...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>
|
|
);
|
|
} |