feat(WEB): 자재/출고/생산/품질/단가 기능 대폭 개선 및 신규 페이지 추가

자재관리:
- 입고관리 재고조정 다이얼로그 신규 추가, 상세/목록 기능 확장
- 재고현황 컴포넌트 리팩토링

출고관리:
- 출하관리 생성/수정/목록/상세 개선
- 차량배차관리 상세/수정/목록 기능 보강

생산관리:
- 작업지시서 WIP 생산 모달 신규 추가
- 벤딩WIP/슬랫조인트바 검사 콘텐츠 신규 추가
- 작업자화면 기능 대폭 확장 (카드/목록 개선)
- 검사성적서 모달 개선

품질관리:
- 실적보고서 관리 페이지 신규 추가
- 검사관리 문서/타입/목데이터 개선

단가관리:
- 단가배포 페이지 및 컴포넌트 신규 추가
- 단가표 관리 페이지 및 컴포넌트 신규 추가

공통:
- 권한 시스템 추가 개선 (PermissionContext, usePermission, PermissionGuard)
- 메뉴 폴링 훅 개선, 레이아웃 수정
- 모바일 줌/패닝 CSS 수정
- locale 유틸 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-04 12:46:19 +09:00
parent 17c16028b1
commit c1b63b850a
70 changed files with 6832 additions and 384 deletions

View File

@@ -4,8 +4,10 @@ import { createContext, useContext, useEffect, useState, useCallback } from 'rea
import { usePathname } from 'next/navigation';
import { getRolePermissionMatrix, getPermissionMenuUrlMap } from '@/lib/permissions/actions';
import { buildMenuIdToUrlMap, convertMatrixToPermissionMap, findMatchingUrl, mergePermissionMaps } from '@/lib/permissions/utils';
import { ALL_DENIED_PERMS } from '@/lib/permissions/types';
import type { PermissionMap, PermissionAction } from '@/lib/permissions/types';
import { AccessDenied } from '@/components/common/AccessDenied';
import { stripLocalePrefix } from '@/lib/utils/locale';
interface PermissionContextType {
permissionMap: PermissionMap | null;
@@ -58,7 +60,7 @@ export function PermissionProvider({ children }: { children: React.ReactNode })
// (모든 권한 OFF → API가 해당 menuId를 생략 → "all denied"로 보완)
for (const [, url] of Object.entries(permMenuUrlMap)) {
if (url && !merged[url]) {
merged[url] = { view: false, create: false, update: false, delete: false, approve: false, export: false };
merged[url] = { ...ALL_DENIED_PERMS };
}
}
@@ -80,9 +82,10 @@ export function PermissionProvider({ children }: { children: React.ReactNode })
const can = useCallback((url: string, action: PermissionAction): boolean => {
if (!permissionMap) return true;
const perms = permissionMap[url];
if (!perms) return true;
return perms[action] ?? true;
const matchedUrl = findMatchingUrl(url, permissionMap);
if (!matchedUrl) return true;
const perms = permissionMap[matchedUrl];
return perms?.[action] ?? true;
}, [permissionMap]);
return (
@@ -98,7 +101,7 @@ export function PermissionProvider({ children }: { children: React.ReactNode })
const BYPASS_PATHS = ['/settings/permissions'];
function isGateBypassed(pathname: string): boolean {
const pathWithoutLocale = pathname.replace(/^\/(ko|en|ja)(\/|$)/, '/');
const pathWithoutLocale = stripLocalePrefix(pathname);
return BYPASS_PATHS.some(bp => pathWithoutLocale.startsWith(bp));
}