feat(WEB): 입력 컴포넌트 공통화 및 UI 개선
- 숫자/통화/전화번호/사업자번호 등 특수 입력 컴포넌트 추가 - MobileCard 컴포넌트 통합 (ListMobileCard 제거) - IntegratedListTemplateV2 페이지네이션 버그 수정 (NaN 이슈) - IntegratedDetailTemplate 타이틀 중복 수정 - 문서 시스템 컴포넌트 추가 - 헤더 벨 아이콘 포커스 스타일 개선 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,8 +48,9 @@ import {
|
||||
type UniversalListConfig,
|
||||
type TabOption,
|
||||
} from '@/components/templates/UniversalListPage';
|
||||
import { ListMobileCard, InfoField } from '@/components/organisms/ListMobileCard';
|
||||
import { DocumentDetailModal } from '@/components/approval/DocumentDetail';
|
||||
import { ListMobileCard, InfoField } from '@/components/organisms/MobileCard';
|
||||
// import { DocumentDetailModal } from '@/components/approval/DocumentDetail';
|
||||
import { DocumentDetailModalV2 as DocumentDetailModal } from '@/components/approval/DocumentDetail';
|
||||
import type {
|
||||
DocumentType,
|
||||
ProposalDocumentData,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Plus, X, Upload, FileText, ExternalLink } from 'lucide-react';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { CurrencyInput } from '@/components/ui/currency-input';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -146,11 +147,10 @@ export function ExpenseReportForm({ data, onChange }: ExpenseReportFormProps) {
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Input
|
||||
type="number"
|
||||
<CurrencyInput
|
||||
placeholder="금액을 입력해주세요"
|
||||
value={item.amount || ''}
|
||||
onChange={(e) => handleItemChange(index, 'amount', Number(e.target.value) || 0)}
|
||||
value={item.amount || 0}
|
||||
onChange={(value) => handleItemChange(index, 'amount', value ?? 0)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Mic, Upload, X, FileText, ExternalLink } from 'lucide-react';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { CurrencyInput } from '@/components/ui/currency-input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import type { ProposalData, UploadedFile } from './types';
|
||||
|
||||
@@ -131,12 +132,11 @@ export function ProposalForm({ data, onChange }: ProposalFormProps) {
|
||||
{/* 예상 비용 */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="estimatedCost">예상 비용</Label>
|
||||
<Input
|
||||
<CurrencyInput
|
||||
id="estimatedCost"
|
||||
type="number"
|
||||
placeholder="금액을 입력해주세요"
|
||||
value={data.estimatedCost || ''}
|
||||
onChange={(e) => onChange({ ...data, estimatedCost: Number(e.target.value) || 0 })}
|
||||
value={data.estimatedCost || 0}
|
||||
onChange={(value) => onChange({ ...data, estimatedCost: value ?? 0 })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
'use client';
|
||||
|
||||
import { DocumentViewer } from '@/components/document-system';
|
||||
import { ProposalDocument } from './ProposalDocument';
|
||||
import { ExpenseReportDocument } from './ExpenseReportDocument';
|
||||
import { ExpenseEstimateDocument } from './ExpenseEstimateDocument';
|
||||
import type {
|
||||
DocumentType,
|
||||
DocumentDetailModalProps,
|
||||
ProposalDocumentData,
|
||||
ExpenseReportDocumentData,
|
||||
ExpenseEstimateDocumentData,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* 문서 상세 모달 V2
|
||||
*
|
||||
* DocumentViewer를 사용하여 통합 UI 제공
|
||||
* - 줌/드래그 기능 추가
|
||||
* - 모드에 따른 버튼 자동 설정
|
||||
*/
|
||||
export function DocumentDetailModalV2({
|
||||
open,
|
||||
onOpenChange,
|
||||
documentType,
|
||||
data,
|
||||
mode = 'inbox',
|
||||
documentStatus,
|
||||
onEdit,
|
||||
onCopy,
|
||||
onApprove,
|
||||
onReject,
|
||||
onSubmit,
|
||||
}: DocumentDetailModalProps) {
|
||||
// 문서 타입별 제목
|
||||
const getDocumentTitle = () => {
|
||||
switch (documentType) {
|
||||
case 'proposal':
|
||||
return '품의서';
|
||||
case 'expenseReport':
|
||||
return '지출결의서';
|
||||
case 'expenseEstimate':
|
||||
return '지출 예상 내역서';
|
||||
default:
|
||||
return '문서';
|
||||
}
|
||||
};
|
||||
|
||||
// 모드에 따른 프리셋 결정
|
||||
const getPreset = () => {
|
||||
// 기안함 모드 + 임시저장 상태: 복제, 상신, 인쇄
|
||||
if (mode === 'draft' && documentStatus === 'draft') {
|
||||
return 'approval-draft' as const;
|
||||
}
|
||||
// 결재함 모드: 수정, 반려, 승인, 인쇄
|
||||
if (mode === 'inbox') {
|
||||
return 'approval-inbox' as const;
|
||||
}
|
||||
// 그 외 (참조함 등): 인쇄만
|
||||
return 'readonly' as const;
|
||||
};
|
||||
|
||||
// 문서 콘텐츠 렌더링
|
||||
const renderDocument = () => {
|
||||
switch (documentType) {
|
||||
case 'proposal':
|
||||
return <ProposalDocument data={data as ProposalDocumentData} />;
|
||||
case 'expenseReport':
|
||||
return <ExpenseReportDocument data={data as ExpenseReportDocumentData} />;
|
||||
case 'expenseEstimate':
|
||||
return <ExpenseEstimateDocument data={data as ExpenseEstimateDocumentData} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DocumentViewer
|
||||
title={getDocumentTitle()}
|
||||
subtitle={`${getDocumentTitle()} 상세`}
|
||||
preset={getPreset()}
|
||||
open={open}
|
||||
onOpenChange={onOpenChange}
|
||||
onEdit={onEdit}
|
||||
onCopy={onCopy}
|
||||
onApprove={onApprove}
|
||||
onReject={onReject}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
{renderDocument()}
|
||||
</DocumentViewer>
|
||||
);
|
||||
}
|
||||
@@ -207,4 +207,7 @@ export function DocumentDetailModal({
|
||||
export type { DocumentType, DocumentDetailModalProps } from './types';
|
||||
export { ProposalDocument } from './ProposalDocument';
|
||||
export { ExpenseReportDocument } from './ExpenseReportDocument';
|
||||
export { ExpenseEstimateDocument } from './ExpenseEstimateDocument';
|
||||
export { ExpenseEstimateDocument } from './ExpenseEstimateDocument';
|
||||
|
||||
// V2 - DocumentViewer 기반
|
||||
export { DocumentDetailModalV2 } from './DocumentDetailModalV2';
|
||||
@@ -36,8 +36,9 @@ import {
|
||||
UniversalListPage,
|
||||
type UniversalListConfig,
|
||||
} from '@/components/templates/UniversalListPage';
|
||||
import { ListMobileCard, InfoField } from '@/components/organisms/ListMobileCard';
|
||||
import { DocumentDetailModal } from '@/components/approval/DocumentDetail';
|
||||
import { ListMobileCard, InfoField } from '@/components/organisms/MobileCard';
|
||||
// import { DocumentDetailModal } from '@/components/approval/DocumentDetail';
|
||||
import { DocumentDetailModalV2 as DocumentDetailModal } from '@/components/approval/DocumentDetail';
|
||||
import type {
|
||||
DocumentType,
|
||||
ProposalDocumentData,
|
||||
|
||||
@@ -42,8 +42,9 @@ import {
|
||||
type TabOption,
|
||||
} from '@/components/templates/UniversalListPage';
|
||||
import { DateRangeSelector } from '@/components/molecules/DateRangeSelector';
|
||||
import { ListMobileCard, InfoField } from '@/components/organisms/ListMobileCard';
|
||||
import { DocumentDetailModal } from '@/components/approval/DocumentDetail';
|
||||
import { ListMobileCard, InfoField } from '@/components/organisms/MobileCard';
|
||||
// import { DocumentDetailModal } from '@/components/approval/DocumentDetail';
|
||||
import { DocumentDetailModalV2 as DocumentDetailModal } from '@/components/approval/DocumentDetail';
|
||||
import type { DocumentType, ProposalDocumentData, ExpenseReportDocumentData, ExpenseEstimateDocumentData } from '@/components/approval/DocumentDetail/types';
|
||||
import type {
|
||||
ReferenceTabType,
|
||||
|
||||
Reference in New Issue
Block a user