refactor(WEB): 컴포넌트 레지스트리 개선 및 미사용 코드 정리
- component-registry를 파일 시스템 기반 동적 스캔으로 전환 (정적 JSON 삭제) - 미사용 컴포넌트 삭제 (EmptyState, StandardDialog) - 회사정보 관리 페이지 개선 - 컴포넌트 계층 정의 가이드 문서 추가 - middleware 및 useDaumPostcode 수정 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,6 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export interface StandardDialogProps {
|
||||
@@ -84,136 +83,3 @@ export function StandardDialog({
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 확인 다이얼로그
|
||||
*/
|
||||
export interface ConfirmDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
title: string;
|
||||
description: string;
|
||||
onConfirm: () => void;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
confirmVariant?:
|
||||
| "default"
|
||||
| "destructive"
|
||||
| "outline"
|
||||
| "secondary"
|
||||
| "ghost"
|
||||
| "link";
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
export function ConfirmDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
title,
|
||||
description,
|
||||
onConfirm,
|
||||
confirmText = "확인",
|
||||
cancelText = "취소",
|
||||
confirmVariant = "default",
|
||||
loading = false,
|
||||
}: ConfirmDialogProps) {
|
||||
const handleConfirm = () => {
|
||||
onConfirm();
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader className="px-6 pt-6">
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
<DialogDescription>{description}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="px-6 pb-4"></div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => onOpenChange(false)}
|
||||
disabled={loading}
|
||||
>
|
||||
{cancelText}
|
||||
</Button>
|
||||
<Button
|
||||
variant={confirmVariant}
|
||||
onClick={handleConfirm}
|
||||
disabled={loading}
|
||||
>
|
||||
{confirmText}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 폼 다이얼로그
|
||||
*/
|
||||
export interface FormDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
title: string;
|
||||
description?: string;
|
||||
children: React.ReactNode;
|
||||
onSave: () => void;
|
||||
onCancel?: () => void;
|
||||
saveText?: string;
|
||||
cancelText?: string;
|
||||
size?: "sm" | "md" | "lg" | "xl" | "full";
|
||||
loading?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export function FormDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
onSave,
|
||||
onCancel,
|
||||
saveText = "저장",
|
||||
cancelText = "취소",
|
||||
size = "md",
|
||||
loading = false,
|
||||
disabled = false,
|
||||
}: FormDialogProps) {
|
||||
const handleCancel = () => {
|
||||
if (onCancel) {
|
||||
onCancel();
|
||||
}
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
onSave();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className={cn(sizeClasses[size])}>
|
||||
<DialogHeader className="px-6 pt-6">
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
{description && <DialogDescription>{description}</DialogDescription>}
|
||||
</DialogHeader>
|
||||
|
||||
<div className="px-6 pb-4 overflow-y-auto max-h-[60vh]">{children}</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={handleCancel} disabled={loading}>
|
||||
{cancelText}
|
||||
</Button>
|
||||
<Button onClick={handleSave} disabled={loading || disabled}>
|
||||
{saveText}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -6,8 +6,8 @@ export { IconWithBadge } from "./IconWithBadge";
|
||||
export { TableActions } from "./TableActions";
|
||||
export type { TableAction } from "./TableActions";
|
||||
|
||||
export { StandardDialog, ConfirmDialog, FormDialog } from "./StandardDialog";
|
||||
export type { StandardDialogProps, ConfirmDialogProps, FormDialogProps } from "./StandardDialog";
|
||||
export { StandardDialog } from "./StandardDialog";
|
||||
export type { StandardDialogProps } from "./StandardDialog";
|
||||
|
||||
export { YearQuarterFilter } from "./YearQuarterFilter";
|
||||
export type { Quarter } from "./YearQuarterFilter";
|
||||
@@ -1,37 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { LucideIcon } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
interface EmptyStateProps {
|
||||
icon?: LucideIcon;
|
||||
title: string;
|
||||
description?: string;
|
||||
action?: {
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
};
|
||||
}
|
||||
|
||||
export function EmptyState({ icon: Icon, title, description, action }: EmptyStateProps) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center py-12 text-center">
|
||||
{Icon && (
|
||||
<div className="mb-4 p-4 bg-muted rounded-full">
|
||||
<Icon className="w-8 h-8 text-muted-foreground" />
|
||||
</div>
|
||||
)}
|
||||
<h3 className="text-lg font-semibold mb-2">{title}</h3>
|
||||
{description && (
|
||||
<p className="text-sm text-muted-foreground mb-6 max-w-md">
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
{action && (
|
||||
<Button onClick={action.onClick}>
|
||||
{action.label}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,7 @@ export { DataTable } from "./DataTable";
|
||||
export type { Column, CellType } from "./DataTable";
|
||||
export { MobileCard, ListMobileCard, InfoField } from "./MobileCard";
|
||||
export type { MobileCardProps, InfoFieldProps } from "./MobileCard";
|
||||
export { EmptyState } from "./EmptyState";
|
||||
export { EmptyState, TableEmptyState } from "@/components/ui/empty-state";
|
||||
export { ScreenVersionHistory } from "./ScreenVersionHistory";
|
||||
export { SearchableSelectionModal } from "./SearchableSelectionModal";
|
||||
export type { SearchableSelectionModalProps, SingleSelectProps, MultipleSelectProps } from "./SearchableSelectionModal";
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Building2, Plus, Save, X, Loader2 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { BusinessNumberInput } from '@/components/ui/business-number-input';
|
||||
import { FormField } from '@/components/molecules/FormField';
|
||||
import { AccountNumberInput } from '@/components/ui/account-number-input';
|
||||
import { ImageUpload } from '@/components/ui/image-upload';
|
||||
import { FileInput } from '@/components/ui/file-input';
|
||||
@@ -213,50 +213,38 @@ export function CompanyInfoManagement() {
|
||||
|
||||
{/* 회사명 / 대표자명 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="companyName">회사명</Label>
|
||||
<Input
|
||||
id="companyName"
|
||||
value={formData.companyName}
|
||||
onChange={(e) => handleChange('companyName', e.target.value)}
|
||||
placeholder="회사명"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="representativeName">대표자명</Label>
|
||||
<Input
|
||||
id="representativeName"
|
||||
value={formData.representativeName}
|
||||
onChange={(e) => handleChange('representativeName', e.target.value)}
|
||||
placeholder="대표자명"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
<FormField
|
||||
label="회사명"
|
||||
value={formData.companyName}
|
||||
onChange={(value) => handleChange('companyName', value)}
|
||||
placeholder="회사명"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
<FormField
|
||||
label="대표자명"
|
||||
value={formData.representativeName}
|
||||
onChange={(value) => handleChange('representativeName', value)}
|
||||
placeholder="대표자명"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 업태 / 업종 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="businessType">업태</Label>
|
||||
<Input
|
||||
id="businessType"
|
||||
value={formData.businessType}
|
||||
onChange={(e) => handleChange('businessType', e.target.value)}
|
||||
placeholder="업태명"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="businessCategory">업종</Label>
|
||||
<Input
|
||||
id="businessCategory"
|
||||
value={formData.businessCategory}
|
||||
onChange={(e) => handleChange('businessCategory', e.target.value)}
|
||||
placeholder="업종명"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
<FormField
|
||||
label="업태"
|
||||
value={formData.businessType}
|
||||
onChange={(value) => handleChange('businessType', value)}
|
||||
placeholder="업태명"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
<FormField
|
||||
label="업종"
|
||||
value={formData.businessCategory}
|
||||
onChange={(value) => handleChange('businessCategory', value)}
|
||||
placeholder="업종명"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 주소 */}
|
||||
@@ -290,28 +278,20 @@ export function CompanyInfoManagement() {
|
||||
|
||||
{/* 이메일 / 세금계산서 이메일 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">이메일 (아이디)</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
value={formData.email}
|
||||
onChange={(e) => handleChange('email', e.target.value)}
|
||||
placeholder="abc@email.com"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="taxInvoiceEmail">세금계산서 이메일</Label>
|
||||
<Input
|
||||
id="taxInvoiceEmail"
|
||||
type="email"
|
||||
value={formData.taxInvoiceEmail}
|
||||
onChange={(e) => handleChange('taxInvoiceEmail', e.target.value)}
|
||||
placeholder="abc@email.com"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
<FormField
|
||||
label="이메일 (아이디)"
|
||||
value={formData.email}
|
||||
onChange={(value) => handleChange('email', value)}
|
||||
placeholder="abc@email.com"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
<FormField
|
||||
label="세금계산서 이메일"
|
||||
value={formData.taxInvoiceEmail}
|
||||
onChange={(value) => handleChange('taxInvoiceEmail', value)}
|
||||
placeholder="abc@email.com"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 담당자명 / 담당자 연락처 - 임시 주석처리 (추후 사용 가능) */}
|
||||
@@ -352,16 +332,14 @@ export function CompanyInfoManagement() {
|
||||
placeholder="파일을 선택하세요"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="businessNumber">사업자등록번호</Label>
|
||||
<BusinessNumberInput
|
||||
id="businessNumber"
|
||||
value={formData.businessNumber}
|
||||
onChange={(value) => handleChange('businessNumber', value)}
|
||||
placeholder="123-12-12345"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
<FormField
|
||||
label="사업자등록번호"
|
||||
type="businessNumber"
|
||||
value={formData.businessNumber}
|
||||
onChange={(value) => handleChange('businessNumber', value)}
|
||||
placeholder="123-12-12345"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -374,16 +352,13 @@ export function CompanyInfoManagement() {
|
||||
<CardContent className="space-y-6">
|
||||
{/* 결제 은행 / 계좌 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="paymentBank">결제 은행</Label>
|
||||
<Input
|
||||
id="paymentBank"
|
||||
value={formData.paymentBank}
|
||||
onChange={(e) => handleChange('paymentBank', e.target.value)}
|
||||
placeholder="은행명"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
<FormField
|
||||
label="결제 은행"
|
||||
value={formData.paymentBank}
|
||||
onChange={(value) => handleChange('paymentBank', value)}
|
||||
placeholder="은행명"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="paymentAccount">계좌</Label>
|
||||
<AccountNumberInput
|
||||
@@ -398,16 +373,13 @@ export function CompanyInfoManagement() {
|
||||
|
||||
{/* 예금주 / 결제일 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="paymentAccountHolder">예금주</Label>
|
||||
<Input
|
||||
id="paymentAccountHolder"
|
||||
value={formData.paymentAccountHolder}
|
||||
onChange={(e) => handleChange('paymentAccountHolder', e.target.value)}
|
||||
placeholder="예금주명"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
</div>
|
||||
<FormField
|
||||
label="예금주"
|
||||
value={formData.paymentAccountHolder}
|
||||
onChange={(value) => handleChange('paymentAccountHolder', value)}
|
||||
placeholder="예금주명"
|
||||
disabled={!isEditMode}
|
||||
/>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="paymentDay">결제일</Label>
|
||||
{isEditMode ? (
|
||||
|
||||
Reference in New Issue
Block a user