refactor(WEB): CEO 대시보드 대규모 개선 및 문서/권한/스토어 리팩토링
- CEO 대시보드: 섹션별 API 연동 강화 (매출/매입/생산 실데이터 표시) - DashboardSettingsDialog 드래그 정렬 및 설정 UX 개선 - dashboard transformers 모듈 분리 (파일 분할) - DocumentTable/DocumentWrapper 공통 문서 컴포넌트 추출 - LineItemsTable organisms 컴포넌트 추가 - PurchaseOrderDocument/InspectionRequestDocument 문서 컴포넌트 리팩토링 - PermissionContext → permissionStore(Zustand) 전환 - useUIStore, stores/utils/userStorage 추가 - favoritesStore/useTableColumnStore 사용자별 저장 지원 - DepositDetail/WithdrawalDetail 삭제 (통합) - PurchaseDetail/SalesDetail 간소화 - amount.ts/formatters.ts 유틸 확장 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
93
src/components/document-system/components/DocumentTable.tsx
Normal file
93
src/components/document-system/components/DocumentTable.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface DocumentTableProps {
|
||||
children: ReactNode;
|
||||
/** 테이블 위 섹션 헤더 */
|
||||
header?: string;
|
||||
/** 헤더 배경색 (기본: dark) */
|
||||
headerVariant?: 'dark' | 'light' | 'primary';
|
||||
/** 하단 마진 (기본: mb-6) */
|
||||
spacing?: string;
|
||||
/** 추가 className (table 요소에 적용) */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 문서용 테이블 래퍼
|
||||
*
|
||||
* 일관된 border, collapse, text-size를 적용합니다.
|
||||
* 선택적으로 섹션 헤더를 포함할 수 있습니다.
|
||||
*
|
||||
* @example
|
||||
* // 기본 사용
|
||||
* <DocumentTable>
|
||||
* <thead>
|
||||
* <tr><th className={DOC_STYLES.th}>품목</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr><td className={DOC_STYLES.td}>스크린</td></tr>
|
||||
* </tbody>
|
||||
* </DocumentTable>
|
||||
*
|
||||
* @example
|
||||
* // 섹션 헤더 포함
|
||||
* <DocumentTable header="자재 내역">
|
||||
* <tbody>...</tbody>
|
||||
* </DocumentTable>
|
||||
*/
|
||||
export function DocumentTable({
|
||||
children,
|
||||
header,
|
||||
headerVariant = 'dark',
|
||||
spacing = 'mb-6',
|
||||
className,
|
||||
}: DocumentTableProps) {
|
||||
const headerClasses = {
|
||||
dark: 'bg-gray-800 text-white',
|
||||
light: 'bg-gray-100 text-gray-900',
|
||||
primary: 'bg-blue-600 text-white',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={spacing}>
|
||||
{header && (
|
||||
<div className={cn('text-center py-1 font-bold border-b border-gray-400', headerClasses[headerVariant])}>
|
||||
{header}
|
||||
</div>
|
||||
)}
|
||||
<table className={cn('w-full border-collapse border border-gray-400', className)}>
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 문서 테이블 셀 스타일 상수
|
||||
*
|
||||
* 문서 내 <th>/<td>에 일관된 스타일을 적용하기 위한 유틸리티.
|
||||
* DocumentTable 내부에서 직접 className으로 사용합니다.
|
||||
*
|
||||
* @example
|
||||
* <tr>
|
||||
* <th className={DOC_STYLES.th}>품목명</th>
|
||||
* <td className={DOC_STYLES.td}>스크린</td>
|
||||
* <td className={DOC_STYLES.tdCenter}>10</td>
|
||||
* <td className={DOC_STYLES.tdRight}>1,000,000</td>
|
||||
* </tr>
|
||||
*/
|
||||
export const DOC_STYLES = {
|
||||
/** 헤더 셀 (회색 배경, 볼드) */
|
||||
th: 'bg-gray-100 border border-gray-400 px-2 py-1 font-medium text-center',
|
||||
/** 데이터 셀 (좌측 정렬) */
|
||||
td: 'border border-gray-300 px-2 py-1',
|
||||
/** 데이터 셀 (중앙 정렬) */
|
||||
tdCenter: 'border border-gray-300 px-2 py-1 text-center',
|
||||
/** 데이터 셀 (우측 정렬, 숫자용) */
|
||||
tdRight: 'border border-gray-300 px-2 py-1 text-right',
|
||||
/** 라벨 셀 (key-value 테이블의 라벨) */
|
||||
label: 'bg-gray-100 border border-gray-300 px-2 py-1 font-medium w-24',
|
||||
/** 값 셀 (key-value 테이블의 값) */
|
||||
value: 'border border-gray-300 px-2 py-1',
|
||||
} as const;
|
||||
@@ -0,0 +1,48 @@
|
||||
import { ReactNode, forwardRef } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface DocumentWrapperProps {
|
||||
children: ReactNode;
|
||||
/** 텍스트 크기 (기본: text-xs) */
|
||||
fontSize?: 'text-[10px]' | 'text-[11px]' | 'text-xs' | 'text-sm';
|
||||
/** print-area 클래스 자동 추가 (기본: true) */
|
||||
printArea?: boolean;
|
||||
/** 추가 className */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 문서 A4 래퍼
|
||||
*
|
||||
* 모든 문서 컴포넌트의 최외곽 컨테이너.
|
||||
* 일관된 배경색, 패딩, 최소 높이, 프린트 클래스를 제공합니다.
|
||||
*
|
||||
* @example
|
||||
* <DocumentWrapper>
|
||||
* <DocumentHeader title="발주서" />
|
||||
* <DocumentTable>...</DocumentTable>
|
||||
* </DocumentWrapper>
|
||||
*
|
||||
* @example
|
||||
* // 작은 폰트 + 커스텀 클래스
|
||||
* <DocumentWrapper fontSize="text-[11px]" className="leading-tight">
|
||||
* ...
|
||||
* </DocumentWrapper>
|
||||
*/
|
||||
export const DocumentWrapper = forwardRef<HTMLDivElement, DocumentWrapperProps>(
|
||||
function DocumentWrapper({ children, fontSize = 'text-xs', printArea = true, className }, ref) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'bg-white p-8 min-h-full',
|
||||
fontSize,
|
||||
printArea && 'print-area',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -1,6 +1,8 @@
|
||||
// 문서 공통 컴포넌트
|
||||
export { ApprovalLine } from './ApprovalLine';
|
||||
export { DocumentHeader } from './DocumentHeader';
|
||||
export { DocumentWrapper } from './DocumentWrapper';
|
||||
export { DocumentTable, DOC_STYLES } from './DocumentTable';
|
||||
export { SectionHeader } from './SectionHeader';
|
||||
export { InfoTable } from './InfoTable';
|
||||
export { QualityApprovalTable } from './QualityApprovalTable';
|
||||
@@ -11,6 +13,8 @@ export { SignatureSection } from './SignatureSection';
|
||||
// Types
|
||||
export type { ApprovalPerson, ApprovalLineProps } from './ApprovalLine';
|
||||
export type { DocumentHeaderLogo, DocumentHeaderProps } from './DocumentHeader';
|
||||
export type { DocumentWrapperProps } from './DocumentWrapper';
|
||||
export type { DocumentTableProps } from './DocumentTable';
|
||||
export type { SectionHeaderProps } from './SectionHeader';
|
||||
export type { InfoTableCell, InfoTableProps } from './InfoTable';
|
||||
export type {
|
||||
|
||||
67
src/components/document-system/hooks/usePrintHandler.ts
Normal file
67
src/components/document-system/hooks/usePrintHandler.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { printElement, printArea } from '@/lib/print-utils';
|
||||
|
||||
interface UsePrintHandlerOptions {
|
||||
/** 인쇄 제목 (브라우저 다이얼로그에 표시) */
|
||||
title?: string;
|
||||
/** 추가 CSS 스타일 */
|
||||
styles?: string;
|
||||
}
|
||||
|
||||
interface UsePrintHandlerReturn {
|
||||
/** ref를 할당한 요소를 인쇄 */
|
||||
printRef: React.RefObject<HTMLDivElement | null>;
|
||||
/** ref 기반 인쇄 실행 */
|
||||
handlePrint: () => void;
|
||||
/** .print-area 클래스 기반 인쇄 실행 */
|
||||
handlePrintArea: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 문서 인쇄 훅
|
||||
*
|
||||
* ref 기반 또는 .print-area 기반으로 인쇄를 실행합니다.
|
||||
*
|
||||
* @example
|
||||
* // ref 기반 사용
|
||||
* function MyDocument() {
|
||||
* const { printRef, handlePrint } = usePrintHandler({ title: '발주서' });
|
||||
* return (
|
||||
* <>
|
||||
* <DocumentWrapper ref={printRef}>
|
||||
* <DocumentHeader title="발주서" />
|
||||
* ...
|
||||
* </DocumentWrapper>
|
||||
* <Button onClick={handlePrint}>인쇄</Button>
|
||||
* </>
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* @example
|
||||
* // .print-area 기반 사용 (DocumentWrapper의 printArea prop 활용)
|
||||
* function MyDocument() {
|
||||
* const { handlePrintArea } = usePrintHandler({ title: '검사 성적서' });
|
||||
* return (
|
||||
* <>
|
||||
* <DocumentWrapper>...</DocumentWrapper>
|
||||
* <Button onClick={handlePrintArea}>인쇄</Button>
|
||||
* </>
|
||||
* );
|
||||
* }
|
||||
*/
|
||||
export function usePrintHandler(options: UsePrintHandlerOptions = {}): UsePrintHandlerReturn {
|
||||
const { title = '문서 인쇄', styles } = options;
|
||||
const printRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const handlePrint = useCallback(() => {
|
||||
if (printRef.current) {
|
||||
printElement(printRef.current, { title, styles });
|
||||
}
|
||||
}, [title, styles]);
|
||||
|
||||
const handlePrintArea = useCallback(() => {
|
||||
printArea({ title, styles });
|
||||
}, [title, styles]);
|
||||
|
||||
return { printRef, handlePrint, handlePrintArea };
|
||||
}
|
||||
@@ -5,6 +5,9 @@ export { DocumentViewer } from './viewer';
|
||||
export {
|
||||
ApprovalLine,
|
||||
DocumentHeader,
|
||||
DocumentWrapper,
|
||||
DocumentTable,
|
||||
DOC_STYLES,
|
||||
SectionHeader,
|
||||
InfoTable,
|
||||
QualityApprovalTable,
|
||||
@@ -15,6 +18,7 @@ export {
|
||||
|
||||
// Hooks
|
||||
export { useZoom, useDrag } from './viewer/hooks';
|
||||
export { usePrintHandler } from './hooks/usePrintHandler';
|
||||
|
||||
// Presets
|
||||
export { DOCUMENT_PRESETS, getPreset, mergeWithPreset } from './presets';
|
||||
@@ -26,6 +30,8 @@ export type {
|
||||
ApprovalLineProps,
|
||||
DocumentHeaderLogo,
|
||||
DocumentHeaderProps,
|
||||
DocumentWrapperProps,
|
||||
DocumentTableProps,
|
||||
SectionHeaderProps,
|
||||
InfoTableCell,
|
||||
InfoTableProps,
|
||||
|
||||
Reference in New Issue
Block a user