refactor(WEB): V2 파일 통합, store 구조 정리 및 대시보드 개선

- V2 컴포넌트를 원본에 통합 후 V2 파일 삭제 (InspectionModal, BillDetail, ContractDocumentModal, LaborDetailClient, PricingDetailClient, QuoteRegistration)
- store → stores 디렉토리 이동 및 favoritesStore 추가
- dashboard_type3~5 추가 및 기존 대시보드 차트/훅 분리
- Sidebar 리팩토링 및 HeaderFavoritesBar 추가
- DashboardSwitcher 컴포넌트 추가
- 백업 파일(.v1-backup) 및 불필요 코드 정리
- InspectionPreviewModal 레이아웃 개선

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-11 15:09:51 +09:00
parent e14335b635
commit a38996b751
96 changed files with 4930 additions and 6550 deletions

View File

@@ -536,59 +536,6 @@ export async function deleteItemFile(
return data.data;
}
// ===== 레거시 파일 업로드 (하위 호환성) =====
/**
* @deprecated uploadItemFile 사용 권장 (ID 기반)
* 파일 업로드 (시방서, 인정서, 전개도 등) - 품목 코드 기반
*/
export async function uploadFile(
itemCode: string,
file: File,
fileType: 'specification' | 'certification' | 'bending_diagram'
): Promise<{ url: string; filename: string }> {
const formData = new FormData();
formData.append('file', file);
formData.append('type', fileType);
const token = getAuthToken();
const headers: Record<string, string> = {};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(
`${API_URL}/api/items/${encodeURIComponent(itemCode)}/files`,
{
method: 'POST',
headers,
body: formData,
credentials: 'include',
}
);
const data = await handleApiResponse<ApiResponse<{ url: string; filename: string }>>(response);
return data.data;
}
/**
* @deprecated deleteItemFile 사용 권장 (ID 기반)
* 파일 삭제 - 품목 코드 기반
*/
export async function deleteFile(
itemCode: string,
fileType: 'specification' | 'certification' | 'bending_diagram'
): Promise<void> {
const response = await fetch(
`${API_URL}/api/items/${encodeURIComponent(itemCode)}/files/${fileType}`,
createFetchOptions({
method: 'DELETE',
})
);
await handleApiResponse<ApiResponse<null>>(response);
}
// ===== 검색 및 필터 =====
/**

View File

@@ -7,8 +7,8 @@
*/
import { transformApiMenusToMenuItems, deserializeMenuItems } from './menuTransform';
import { useMenuStore } from '@/store/menuStore';
import type { SerializableMenuItem } from '@/store/menuStore';
import { useMenuStore } from '@/stores/menuStore';
import type { SerializableMenuItem } from '@/stores/menuStore';
/**
* 메뉴 해시 생성 (변경 감지용)

View File

@@ -1,4 +1,4 @@
import type { MenuItem, SerializableMenuItem } from '@/store/menuStore';
import type { MenuItem, SerializableMenuItem } from '@/stores/menuStore';
import {
LayoutDashboard,
Folder,
@@ -180,6 +180,42 @@ export const iconMap: Record<string, LucideIcon> = {
server: Server,
};
// 역방향 아이콘 맵 (LucideIcon → string name)
const reverseIconMap = new Map<LucideIcon, string>();
for (const [name, icon] of Object.entries(iconMap)) {
// 첫 번째 매핑만 저장 (중복 아이콘 방지)
if (!reverseIconMap.has(icon)) {
reverseIconMap.set(icon, name);
}
}
/**
* LucideIcon 컴포넌트에서 문자열 이름을 조회
*/
export function getIconName(icon: LucideIcon): string {
return reverseIconMap.get(icon) || 'folder';
}
// 기본 즐겨찾기 항목 (최초 사용자용)
import type { FavoriteItem } from '@/stores/favoritesStore';
export const DEFAULT_FAVORITES: FavoriteItem[] = [
{
id: 'default-comprehensive-analysis',
label: '종합분석',
iconName: 'bar-chart-3',
path: '/reports/comprehensive-analysis',
addedAt: 0,
},
{
id: 'default-qms',
label: '품질인정심사',
iconName: 'award',
path: '/quality/qms',
addedAt: 1,
},
];
// API 메뉴 데이터 타입
interface ApiMenu {
id: number;