- 일반전표입력, 상품권관리, 세금계산서 발행/조회 신규 페이지 추가 - 바로빌 연동 설정 페이지 추가 - 카드관리/계좌관리 리스트 UniversalListPage 공통 구조로 전환 - 카드거래조회/은행거래조회 리팩토링 (모달 분리, 액션 확장) - 계좌 상세 폼(AccountDetailForm) 신규 구현 - 카드 상세(CardDetail) 신규 구현 + CardNumberInput 적용 - DateRangeSelector, StatCards, IntegratedListTemplateV2 공통 컴포넌트 개선 - 레거시 파일 정리 (CardManagementUnified, cardConfig, _legacy 등) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
211 lines
6.4 KiB
TypeScript
211 lines
6.4 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useCallback } from 'react';
|
|
import { toast } from 'sonner';
|
|
import { Loader2 } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { FormField } from '@/components/molecules/FormField';
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogFooter,
|
|
} from '@/components/ui/dialog';
|
|
import type { BarobillSignupFormData } from './types';
|
|
import { registerBarobillSignup } from './actions';
|
|
|
|
interface SignupModalProps {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
onSuccess: () => void;
|
|
}
|
|
|
|
const initialFormData: BarobillSignupFormData = {
|
|
businessNumber: '',
|
|
companyName: '',
|
|
ceoName: '',
|
|
businessType: '',
|
|
businessCategory: '',
|
|
address: '',
|
|
barobillId: '',
|
|
password: '',
|
|
managerName: '',
|
|
managerPhone: '',
|
|
managerEmail: '',
|
|
};
|
|
|
|
export function SignupModal({ open, onOpenChange, onSuccess }: SignupModalProps) {
|
|
const [formData, setFormData] = useState<BarobillSignupFormData>(initialFormData);
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
const handleOpenChange = useCallback((isOpen: boolean) => {
|
|
if (isOpen) setFormData(initialFormData);
|
|
onOpenChange(isOpen);
|
|
}, [onOpenChange]);
|
|
|
|
const handleChange = useCallback((key: keyof BarobillSignupFormData, value: string) => {
|
|
setFormData(prev => ({ ...prev, [key]: value }));
|
|
}, []);
|
|
|
|
const handleSubmit = useCallback(async () => {
|
|
if (!formData.businessNumber) {
|
|
toast.error('사업자등록번호를 입력해주세요.');
|
|
return;
|
|
}
|
|
if (!formData.companyName) {
|
|
toast.error('상호명을 입력해주세요.');
|
|
return;
|
|
}
|
|
if (!formData.ceoName) {
|
|
toast.error('대표자명을 입력해주세요.');
|
|
return;
|
|
}
|
|
if (!formData.barobillId) {
|
|
toast.error('바로빌 아이디를 입력해주세요.');
|
|
return;
|
|
}
|
|
if (!formData.password) {
|
|
toast.error('비밀번호를 입력해주세요.');
|
|
return;
|
|
}
|
|
|
|
setIsSubmitting(true);
|
|
try {
|
|
const result = await registerBarobillSignup(formData);
|
|
if (result.success) {
|
|
toast.success('바로빌 회원가입 정보가 등록되었습니다.');
|
|
onOpenChange(false);
|
|
onSuccess();
|
|
} else {
|
|
toast.error(result.error || '등록에 실패했습니다.');
|
|
}
|
|
} catch {
|
|
toast.error('등록 중 오류가 발생했습니다.');
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
}, [formData, onOpenChange, onSuccess]);
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={handleOpenChange}>
|
|
<DialogContent className="sm:max-w-[560px] max-h-[90vh] overflow-y-auto" aria-describedby={undefined}>
|
|
<DialogHeader>
|
|
<DialogTitle>바로빌 회원가입 정보 등록</DialogTitle>
|
|
</DialogHeader>
|
|
|
|
<div className="space-y-4 py-2">
|
|
{/* 사업자등록번호 + 상호명 */}
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<FormField
|
|
type="businessNumber"
|
|
label="사업자등록번호"
|
|
required
|
|
value={formData.businessNumber}
|
|
onChange={(v) => handleChange('businessNumber', v)}
|
|
placeholder="123-12-12345"
|
|
/>
|
|
<FormField
|
|
label="상호명"
|
|
required
|
|
value={formData.companyName}
|
|
onChange={(v) => handleChange('companyName', v)}
|
|
placeholder="(주)회사명"
|
|
/>
|
|
</div>
|
|
|
|
{/* 대표자명 + 업태 + 업종 */}
|
|
<div className="grid grid-cols-3 gap-4">
|
|
<FormField
|
|
label="대표자명"
|
|
required
|
|
value={formData.ceoName}
|
|
onChange={(v) => handleChange('ceoName', v)}
|
|
placeholder="홍길동"
|
|
/>
|
|
<FormField
|
|
label="업태"
|
|
value={formData.businessType}
|
|
onChange={(v) => handleChange('businessType', v)}
|
|
placeholder="업태"
|
|
/>
|
|
<FormField
|
|
label="업종"
|
|
value={formData.businessCategory}
|
|
onChange={(v) => handleChange('businessCategory', v)}
|
|
placeholder="업종"
|
|
/>
|
|
</div>
|
|
|
|
{/* 주소 */}
|
|
<FormField
|
|
label="주소"
|
|
value={formData.address}
|
|
onChange={(v) => handleChange('address', v)}
|
|
placeholder="서울특별시 강남구"
|
|
/>
|
|
|
|
{/* 바로빌 아이디 + 비밀번호 */}
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<FormField
|
|
label="바로빌 아이디"
|
|
required
|
|
value={formData.barobillId}
|
|
onChange={(v) => handleChange('barobillId', v)}
|
|
placeholder="Barobill_id"
|
|
/>
|
|
<FormField
|
|
type="password"
|
|
label="비밀번호"
|
|
required
|
|
value={formData.password}
|
|
onChange={(v) => handleChange('password', v)}
|
|
/>
|
|
</div>
|
|
|
|
{/* 담당자명 + 담당자 연락처 */}
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<FormField
|
|
label="담당자명"
|
|
value={formData.managerName}
|
|
onChange={(v) => handleChange('managerName', v)}
|
|
placeholder="홍길동"
|
|
/>
|
|
<FormField
|
|
type="phone"
|
|
label="담당자 연락처"
|
|
value={formData.managerPhone}
|
|
onChange={(v) => handleChange('managerPhone', v)}
|
|
placeholder="010-1234-1234"
|
|
/>
|
|
</div>
|
|
|
|
{/* 담당자 이메일 */}
|
|
<FormField
|
|
label="담당자 이메일"
|
|
value={formData.managerEmail}
|
|
onChange={(v) => handleChange('managerEmail', v)}
|
|
placeholder="manager@email.com"
|
|
/>
|
|
</div>
|
|
|
|
<DialogFooter className="gap-3">
|
|
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={isSubmitting}>
|
|
취소
|
|
</Button>
|
|
<Button onClick={handleSubmit} disabled={isSubmitting}>
|
|
{isSubmitting ? (
|
|
<>
|
|
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
|
등록 중...
|
|
</>
|
|
) : (
|
|
'등록하기'
|
|
)}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|