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:
유병철
2026-02-23 20:59:25 +09:00
parent 718be1cfdb
commit 8f4a7ee842
43 changed files with 3489 additions and 3463 deletions

View File

@@ -2,12 +2,13 @@
/**
* 발주서 문서 컴포넌트
* - 스크린샷 형식 + 지출결의서 디자인 스타일
* - DocumentWrapper + DocumentTable building block 활용
*/
import { getTodayString } from "@/lib/utils/date";
import { OrderItem } from "../actions";
import { formatNumber } from '@/lib/utils/amount';
import { DocumentWrapper, DocumentTable, DOC_STYLES } from '@/components/document-system';
/**
* 수량 포맷 함수
@@ -61,7 +62,7 @@ export function PurchaseOrderDocument({
remarks,
}: PurchaseOrderDocumentProps) {
return (
<div className="bg-white p-8 min-h-full">
<DocumentWrapper fontSize="text-sm">
{/* 헤더: 제목 + 로트번호/결재란 */}
<div className="flex justify-between items-start mb-6">
<h1 className="text-2xl font-bold tracking-widest"> </h1>
@@ -98,101 +99,95 @@ export function PurchaseOrderDocument({
</div>
{/* 신청업체 */}
<div className="border border-gray-300 mb-4">
<table className="w-full text-sm">
<tbody>
<tr>
<td rowSpan={2} className="bg-gray-100 border-r border-b border-gray-300 p-2 text-center font-medium w-20">
</td>
<td className="bg-gray-100 border-r border-b border-gray-300 p-2 w-20"></td>
<td className="border-r border-b border-gray-300 p-2">{client}</td>
<td className="bg-gray-100 border-r border-b border-gray-300 p-2 w-20"></td>
<td className="border-b border-gray-300 p-2">{orderDate}</td>
</tr>
<tr>
<td className="bg-gray-100 border-r border-b border-gray-300 p-2"></td>
<td className="border-r border-b border-gray-300 p-2">{manager}</td>
<td className="bg-gray-100 border-r border-b border-gray-300 p-2"></td>
<td className="border-b border-gray-300 p-2">{managerContact}</td>
</tr>
<tr>
<td className="bg-gray-100 border-r border-gray-300 p-2 text-center font-medium"></td>
<td className="bg-gray-100 border-r border-gray-300 p-2">FAX</td>
<td className="border-r border-gray-300 p-2">-</td>
<td className="bg-gray-100 border-r border-gray-300 p-2"><br/>()</td>
<td className="border-gray-300 p-2">{installationCount}</td>
</tr>
</tbody>
</table>
</div>
<DocumentTable spacing="mb-4" className="text-sm">
<tbody>
<tr>
<td rowSpan={2} className={`${DOC_STYLES.label} text-center w-20`}>
</td>
<td className={`${DOC_STYLES.label} w-20`}></td>
<td className={DOC_STYLES.value}>{client}</td>
<td className={`${DOC_STYLES.label} w-20`}></td>
<td className={DOC_STYLES.value}>{orderDate}</td>
</tr>
<tr>
<td className={`${DOC_STYLES.label} w-20`}></td>
<td className={DOC_STYLES.value}>{manager}</td>
<td className={`${DOC_STYLES.label} w-20`}></td>
<td className={DOC_STYLES.value}>{managerContact}</td>
</tr>
<tr>
<td className={`${DOC_STYLES.label} text-center w-20`}></td>
<td className={`${DOC_STYLES.label} w-20`}>FAX</td>
<td className={DOC_STYLES.value}>-</td>
<td className={`${DOC_STYLES.label} w-20`}><br/>()</td>
<td className={DOC_STYLES.value}>{installationCount}</td>
</tr>
</tbody>
</DocumentTable>
{/* 신청내용 */}
<div className="border border-gray-300 mb-4">
<table className="w-full text-sm">
<tbody>
<tr>
<td rowSpan={3} className="bg-gray-100 border-r border-gray-300 p-2 text-center font-medium w-20">
</td>
<td className="bg-gray-100 border-r border-b border-gray-300 p-2 w-20"></td>
<td colSpan={3} className="border-b border-gray-300 p-2">{siteName}</td>
</tr>
<tr>
<td className="bg-gray-100 border-r border-b border-gray-300 p-2"><br/></td>
<td className="border-r border-b border-gray-300 p-2">{deliveryRequestDate}</td>
<td className="bg-gray-100 border-r border-b border-gray-300 p-2 w-20"></td>
<td className="border-b border-gray-300 p-2">{deliveryMethod}</td>
</tr>
<tr>
<td className="bg-gray-100 border-r border-gray-300 p-2"></td>
<td className="border-r border-gray-300 p-2">{expectedShipDate}</td>
<td className="bg-gray-100 border-r border-gray-300 p-2"></td>
<td className="border-gray-300 p-2">{address}</td>
</tr>
</tbody>
</table>
</div>
<DocumentTable spacing="mb-4" className="text-sm">
<tbody>
<tr>
<td rowSpan={3} className={`${DOC_STYLES.label} text-center w-20`}>
</td>
<td className={`${DOC_STYLES.label} w-20`}></td>
<td colSpan={3} className={DOC_STYLES.value}>{siteName}</td>
</tr>
<tr>
<td className={`${DOC_STYLES.label} w-20`}><br/></td>
<td className={DOC_STYLES.value}>{deliveryRequestDate}</td>
<td className={`${DOC_STYLES.label} w-20`}></td>
<td className={DOC_STYLES.value}>{deliveryMethod}</td>
</tr>
<tr>
<td className={`${DOC_STYLES.label} w-20`}></td>
<td className={DOC_STYLES.value}>{expectedShipDate}</td>
<td className={`${DOC_STYLES.label} w-20`}></td>
<td className={DOC_STYLES.value}>{address}</td>
</tr>
</tbody>
</DocumentTable>
{/* 부자재 */}
<div className="mb-4">
<p className="text-sm font-medium mb-2"> </p>
<div className="border border-gray-300">
<table className="w-full text-sm">
<thead>
<tr className="bg-gray-100 border-b border-gray-300">
<th className="p-2 text-center font-medium border-r border-gray-300 w-16"></th>
<th className="p-2 text-left font-medium border-r border-gray-300"></th>
<th className="p-2 text-center font-medium border-r border-gray-300 w-20"></th>
<th className="p-2 text-center font-medium border-r border-gray-300 w-24">(mm)</th>
<th className="p-2 text-center font-medium border-r border-gray-300 w-16"></th>
<th className="p-2 text-center font-medium w-24"></th>
</tr>
</thead>
<tbody>
{items.length > 0 ? (
items.map((item, index) => (
<tr key={item.id} className="border-b border-gray-300">
<td className="p-2 text-center border-r border-gray-300">{index + 1}</td>
<td className="p-2 border-r border-gray-300">{item.itemName}</td>
<td className="p-2 text-center border-r border-gray-300">{item.spec}</td>
<td className="p-2 text-center border-r border-gray-300">
{item.width ? `${item.width}` : "-"}
</td>
<td className="p-2 text-center border-r border-gray-300">{formatQuantity(item.quantity, item.unit)}</td>
<td className="p-2 text-center">{item.symbol || "-"}</td>
</tr>
))
) : (
<tr className="border-b border-gray-300">
<td colSpan={6} className="p-4 text-center text-gray-400">
<DocumentTable spacing="">
<thead>
<tr>
<th className={`${DOC_STYLES.th} w-16`}></th>
<th className={`${DOC_STYLES.th} text-left`}></th>
<th className={`${DOC_STYLES.th} w-20`}></th>
<th className={`${DOC_STYLES.th} w-24`}>(mm)</th>
<th className={`${DOC_STYLES.th} w-16`}></th>
<th className={`${DOC_STYLES.th} w-24`}></th>
</tr>
</thead>
<tbody>
{items.length > 0 ? (
items.map((item, index) => (
<tr key={item.id}>
<td className={DOC_STYLES.tdCenter}>{index + 1}</td>
<td className={DOC_STYLES.td}>{item.itemName}</td>
<td className={DOC_STYLES.tdCenter}>{item.spec}</td>
<td className={DOC_STYLES.tdCenter}>
{item.width ? `${item.width}` : "-"}
</td>
<td className={DOC_STYLES.tdCenter}>{formatQuantity(item.quantity, item.unit)}</td>
<td className={DOC_STYLES.tdCenter}>{item.symbol || "-"}</td>
</tr>
)}
</tbody>
</table>
</div>
))
) : (
<tr>
<td colSpan={6} className="p-4 text-center text-gray-400 border border-gray-300">
</td>
</tr>
)}
</tbody>
</DocumentTable>
</div>
{/* 특이사항 */}
@@ -219,6 +214,6 @@ export function PurchaseOrderDocument({
<div className="text-center text-sm text-gray-600">
문의: 홍길동 | 010-1234-5678
</div>
</div>
</DocumentWrapper>
);
}
}