feat: Daum 우편번호 서비스 연동 및 악성채권 UI 개선

- useDaumPostcode 공통 훅 생성 (Daum Postcode API 연동)
- 우편번호 찾기 기능 적용: 악성채권, 거래처, 직원, 회사정보, 주문등록
- 악성채권 페이지 토글 순서 변경 (라벨 → 토글)
- 악성채권 토글 기능 수정 (매출/매입 → 등록/해제)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
byeongcheolryu
2025-12-24 17:46:23 +09:00
parent c1abf89d80
commit 41ef0bdd86
7 changed files with 221 additions and 25 deletions

View File

@@ -1,6 +1,7 @@
'use client';
import { useState, useCallback, useMemo } from 'react';
import { useDaumPostcode } from '@/hooks/useDaumPostcode';
import { useRouter } from 'next/navigation';
import { format } from 'date-fns';
import { AlertTriangle, Plus, X, FileText, Receipt, CreditCard, Upload, Download, Trash2 } from 'lucide-react';
@@ -141,6 +142,17 @@ export function BadDebtDetail({ mode, recordId }: BadDebtDetailProps) {
const initialData = recordId ? getMockRecord(recordId) : getEmptyRecord();
const [formData, setFormData] = useState(initialData);
// Daum 우편번호 서비스
const { openPostcode } = useDaumPostcode({
onComplete: (result) => {
setFormData(prev => ({
...prev,
zipCode: result.zonecode,
address1: result.address,
}));
},
});
// 다이얼로그 상태
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const [showSaveDialog, setShowSaveDialog] = useState(false);
@@ -349,26 +361,28 @@ export function BadDebtDetail({ mode, recordId }: BadDebtDetailProps) {
{renderField('거래처 코드', 'vendorCode', formData.vendorCode, { disabled: true })}
{renderField('거래처명', 'vendorName', formData.vendorName, { required: true })}
{renderField('대표자명', 'representativeName', formData.representativeName)}
{/* 거래처 유형 */}
{/* 거래처 유형 - 읽기 전용 */}
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label className="text-sm font-medium text-gray-700"> </Label>
<Switch
checked={formData.vendorType === 'both'}
onCheckedChange={(checked) => handleChange('vendorType', checked ? 'both' : 'sales')}
disabled={isViewMode}
className="data-[state=checked]:bg-orange-500"
/>
</div>
<Label className="text-sm font-medium text-gray-700"> </Label>
<Input
value={VENDOR_TYPE_LABELS[formData.vendorType] || '매출매입'}
disabled
className="bg-gray-50"
/>
</div>
{/* 악성채권 등록 */}
{/* 악성채권 등록 토글 + 업태/업종 */}
<div className="space-y-2">
<Label className="text-sm font-medium text-gray-700"> </Label>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Label className="text-sm font-medium text-gray-700"> </Label>
<Switch
checked={formData.settingToggle}
onCheckedChange={(checked) => handleChange('settingToggle', checked)}
disabled={isViewMode}
className="data-[state=checked]:bg-orange-500"
/>
</div>
</div>
<div className="grid grid-cols-2 gap-2">
<Input
value={formData.businessType}
@@ -406,7 +420,7 @@ export function BadDebtDetail({ mode, recordId }: BadDebtDetailProps) {
disabled={isViewMode}
className="w-[120px] bg-white"
/>
<Button variant="outline" disabled={isViewMode} className="shrink-0">
<Button variant="outline" disabled={isViewMode} onClick={openPostcode} className="shrink-0">
</Button>
</div>

View File

@@ -1,6 +1,7 @@
'use client';
import { useState, useCallback, useMemo } from 'react';
import { useDaumPostcode } from '@/hooks/useDaumPostcode';
import { useRouter } from 'next/navigation';
import { Building2, Trash2, Plus, X } from 'lucide-react';
import { Alert, AlertDescription } from '@/components/ui/alert';
@@ -147,6 +148,17 @@ export function VendorDetail({ mode, vendorId }: VendorDetailProps) {
const initialData = vendorId ? getMockVendor(vendorId) : getEmptyVendor();
const [formData, setFormData] = useState(initialData);
// Daum 우편번호 서비스
const { openPostcode } = useDaumPostcode({
onComplete: (result) => {
setFormData(prev => ({
...prev,
zipCode: result.zonecode,
address1: result.address,
}));
},
});
// 다이얼로그 상태
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const [showSaveDialog, setShowSaveDialog] = useState(false);
@@ -421,7 +433,7 @@ export function VendorDetail({ mode, vendorId }: VendorDetailProps) {
disabled={isViewMode}
className="w-[120px] bg-white"
/>
<Button variant="outline" disabled={isViewMode} className="shrink-0">
<Button variant="outline" disabled={isViewMode} onClick={openPostcode} className="shrink-0">
</Button>
</div>

View File

@@ -1,6 +1,7 @@
'use client';
import { useState, useEffect } from 'react';
import { useDaumPostcode } from '@/hooks/useDaumPostcode';
import { useRouter } from 'next/navigation';
import { PageLayout } from '@/components/organisms/PageLayout';
import { PageHeader } from '@/components/organisms/PageHeader';
@@ -77,6 +78,20 @@ export function EmployeeForm({
const router = useRouter();
const [formData, setFormData] = useState<EmployeeFormData>(initialFormData);
// Daum 우편번호 서비스
const { openPostcode } = useDaumPostcode({
onComplete: (result) => {
setFormData(prev => ({
...prev,
address: {
...prev.address,
zipCode: result.zonecode,
address1: result.address,
},
}));
},
});
// 항목 설정 상태
const [showFieldSettings, setShowFieldSettings] = useState(false);
const [fieldSettings, setFieldSettings] = useState<FieldSettings>(initialFieldSettings);
@@ -367,7 +382,7 @@ export function EmployeeForm({
<div className="space-y-2">
<Label></Label>
<div className="flex gap-2">
<Button type="button" variant="default" size="sm" className="bg-blue-500 hover:bg-blue-600">
<Button type="button" variant="default" size="sm" onClick={openPostcode} className="bg-blue-500 hover:bg-blue-600">
</Button>
<Input

View File

@@ -12,6 +12,7 @@
*/
import { useState, useEffect, useCallback } from "react";
import { useDaumPostcode } from "@/hooks/useDaumPostcode";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Button } from "@/components/ui/button";
@@ -183,6 +184,17 @@ export function OrderRegistration({
const [isSaving, setIsSaving] = useState(false);
const [fieldErrors, setFieldErrors] = useState<FieldErrors>({});
// Daum 우편번호 서비스
const { openPostcode } = useDaumPostcode({
onComplete: (result) => {
setForm((prev) => ({
...prev,
zipCode: result.zonecode,
address: result.address,
}));
},
});
// 금액 계산
useEffect(() => {
const subtotal = form.items.reduce((sum, item) => sum + item.amount, 0);
@@ -692,7 +704,7 @@ export function OrderRegistration({
}
className="w-32"
/>
<Button variant="outline" type="button">
<Button variant="outline" type="button" onClick={openPostcode}>
</Button>
</div>

View File

@@ -1,6 +1,7 @@
'use client';
import { useState, useRef, useCallback } from 'react';
import { useDaumPostcode } from '@/hooks/useDaumPostcode';
import { Building2, Plus, Save, Upload, X, Search } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -113,9 +114,19 @@ export function CompanyInfoManagement() {
}
};
// Daum 우편번호 서비스
const { openPostcode } = useDaumPostcode({
onComplete: (result) => {
setFormData(prev => ({
...prev,
zipCode: result.zonecode,
address: result.address,
}));
},
});
const handleAddressSearch = () => {
// TODO: 다음 주소 API 연동
console.log('주소 검색');
openPostcode();
};
const handleSave = async () => {