From 429f1ba6eedb7ecd1194c64ae95a47bc4f0e7f39 Mon Sep 17 00:00:00 2001 From: kjs Date: Fri, 13 Mar 2026 14:01:09 +0900 Subject: [PATCH] feat: add item list mode configuration and screen code handling - Introduced `itemListMode` to the process work standard configuration, allowing users to select between displaying all items or only registered items. - Added `screenCode` to automatically set the screen ID when in registered mode. - Updated the `ProcessWorkStandardComponent` to handle the new configuration and adjust item fetching logic accordingly. - Enhanced the `ProcessWorkStandardConfigPanel` to include a select input for item list mode, improving user experience and configurability. These changes aim to enhance the flexibility and usability of the process work standard component. Made-with: Cursor --- .../ProcessWorkStandardComponent.tsx | 35 ++++++++---- .../ProcessWorkStandardConfigPanel.tsx | 25 +++++++++ .../ProcessWorkStandardRenderer.tsx | 3 +- .../v2-process-work-standard/config.ts | 2 + .../hooks/useProcessWorkStandard.ts | 54 ++++++++++++++++++- .../v2-process-work-standard/types.ts | 5 ++ 6 files changed, 111 insertions(+), 13 deletions(-) diff --git a/frontend/lib/registry/components/v2-process-work-standard/ProcessWorkStandardComponent.tsx b/frontend/lib/registry/components/v2-process-work-standard/ProcessWorkStandardComponent.tsx index cf7b306f..ad169acd 100644 --- a/frontend/lib/registry/components/v2-process-work-standard/ProcessWorkStandardComponent.tsx +++ b/frontend/lib/registry/components/v2-process-work-standard/ProcessWorkStandardComponent.tsx @@ -17,25 +17,37 @@ interface ProcessWorkStandardComponentProps { formData?: Record; isPreview?: boolean; tableName?: string; + screenId?: number | string; } export function ProcessWorkStandardComponent({ config: configProp, isPreview, + screenId, }: ProcessWorkStandardComponentProps) { + const resolvedConfig = useMemo(() => { + const merged = { + ...configProp, + }; + if (merged.itemListMode === "registered" && !merged.screenCode && screenId) { + merged.screenCode = `screen_${screenId}`; + } + return merged; + }, [configProp, screenId]); + const config: ProcessWorkStandardConfig = useMemo( () => ({ ...defaultConfig, - ...configProp, - dataSource: { ...defaultConfig.dataSource, ...configProp?.dataSource }, - phases: configProp?.phases?.length - ? configProp.phases + ...resolvedConfig, + dataSource: { ...defaultConfig.dataSource, ...resolvedConfig?.dataSource }, + phases: resolvedConfig?.phases?.length + ? resolvedConfig.phases : defaultConfig.phases, - detailTypes: configProp?.detailTypes?.length - ? configProp.detailTypes + detailTypes: resolvedConfig?.detailTypes?.length + ? resolvedConfig.detailTypes : defaultConfig.detailTypes, }), - [configProp] + [resolvedConfig] ); const { @@ -46,7 +58,8 @@ export function ProcessWorkStandardComponent({ selectedDetailsByPhase, selection, loading, - fetchItems, + isRegisteredMode, + loadItems, selectItem, selectProcess, fetchWorkItemDetails, @@ -112,8 +125,8 @@ export function ProcessWorkStandardComponent({ ); const handleInit = useCallback(() => { - fetchItems(); - }, [fetchItems]); + loadItems(); + }, [loadItems]); const splitRatio = config.splitRatio || 30; @@ -144,7 +157,7 @@ export function ProcessWorkStandardComponent({ items={items} routings={routings} selection={selection} - onSearch={(keyword) => fetchItems(keyword)} + onSearch={(keyword) => loadItems(keyword)} onSelectItem={selectItem} onSelectProcess={selectProcess} onInit={handleInit} diff --git a/frontend/lib/registry/components/v2-process-work-standard/ProcessWorkStandardConfigPanel.tsx b/frontend/lib/registry/components/v2-process-work-standard/ProcessWorkStandardConfigPanel.tsx index 21a5d69f..27af18f4 100644 --- a/frontend/lib/registry/components/v2-process-work-standard/ProcessWorkStandardConfigPanel.tsx +++ b/frontend/lib/registry/components/v2-process-work-standard/ProcessWorkStandardConfigPanel.tsx @@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { ProcessWorkStandardConfig, WorkPhaseDefinition, DetailTypeDefinition } from "./types"; import { defaultConfig } from "./config"; @@ -81,6 +82,30 @@ export function ProcessWorkStandardConfigPanel({

공정 작업기준 설정

+ {/* 품목 목록 모드 */} +
+

품목 목록 모드

+
+ +

+ {config.itemListMode === "registered" + ? "품목별 라우팅 탭에서 등록한 품목만 표시됩니다. screenCode는 화면 ID 기준으로 자동 설정됩니다." + : "모든 품목을 표시합니다."} +

+
+
+ {/* 데이터 소스 설정 */}

데이터 소스 설정

diff --git a/frontend/lib/registry/components/v2-process-work-standard/ProcessWorkStandardRenderer.tsx b/frontend/lib/registry/components/v2-process-work-standard/ProcessWorkStandardRenderer.tsx index cb1e0e85..aedfae37 100644 --- a/frontend/lib/registry/components/v2-process-work-standard/ProcessWorkStandardRenderer.tsx +++ b/frontend/lib/registry/components/v2-process-work-standard/ProcessWorkStandardRenderer.tsx @@ -9,7 +9,7 @@ export class ProcessWorkStandardRenderer extends AutoRegisteringComponentRendere static componentDefinition = V2ProcessWorkStandardDefinition; render(): React.ReactElement { - const { formData, isPreview, config, tableName } = this.props as Record< + const { formData, isPreview, config, tableName, screenId } = this.props as Record< string, unknown >; @@ -20,6 +20,7 @@ export class ProcessWorkStandardRenderer extends AutoRegisteringComponentRendere formData={formData as Record} tableName={tableName as string} isPreview={isPreview as boolean} + screenId={screenId as number | string} /> ); } diff --git a/frontend/lib/registry/components/v2-process-work-standard/config.ts b/frontend/lib/registry/components/v2-process-work-standard/config.ts index 43ad60cd..22b36ea3 100644 --- a/frontend/lib/registry/components/v2-process-work-standard/config.ts +++ b/frontend/lib/registry/components/v2-process-work-standard/config.ts @@ -31,4 +31,6 @@ export const defaultConfig: ProcessWorkStandardConfig = { splitRatio: 30, leftPanelTitle: "품목 및 공정 선택", readonly: false, + itemListMode: "all", + screenCode: "", }; diff --git a/frontend/lib/registry/components/v2-process-work-standard/hooks/useProcessWorkStandard.ts b/frontend/lib/registry/components/v2-process-work-standard/hooks/useProcessWorkStandard.ts index e909d291..194a7d95 100644 --- a/frontend/lib/registry/components/v2-process-work-standard/hooks/useProcessWorkStandard.ts +++ b/frontend/lib/registry/components/v2-process-work-standard/hooks/useProcessWorkStandard.ts @@ -32,7 +32,9 @@ export function useProcessWorkStandard(config: ProcessWorkStandardConfig) { processName: null, }); - // 품목 목록 조회 + const isRegisteredMode = config.itemListMode === "registered"; + + // 품목 목록 조회 (전체 모드) const fetchItems = useCallback( async (search?: string) => { try { @@ -59,6 +61,53 @@ export function useProcessWorkStandard(config: ProcessWorkStandardConfig) { [config.dataSource] ); + // 등록 품목 조회 (등록 모드) + const fetchRegisteredItems = useCallback( + async (search?: string) => { + const screenCode = config.screenCode; + if (!screenCode) { + console.warn("screenCode가 설정되지 않았습니다"); + setItems([]); + return; + } + try { + setLoading(true); + const ds = config.dataSource; + const params = new URLSearchParams({ + tableName: ds.itemTable, + nameColumn: ds.itemNameColumn, + codeColumn: ds.itemCodeColumn, + routingTable: ds.routingVersionTable, + routingFkColumn: ds.routingFkColumn, + ...(search ? { search } : {}), + }); + const res = await apiClient.get( + `${API_BASE}/registered-items/${encodeURIComponent(screenCode)}?${params}` + ); + if (res.data?.success) { + setItems(res.data.data || []); + } + } catch (err) { + console.error("등록 품목 조회 실패", err); + } finally { + setLoading(false); + } + }, + [config.dataSource, config.screenCode] + ); + + // 모드에 따라 적절한 함수 호출 + const loadItems = useCallback( + async (search?: string) => { + if (isRegisteredMode) { + await fetchRegisteredItems(search); + } else { + await fetchItems(search); + } + }, + [isRegisteredMode, fetchItems, fetchRegisteredItems] + ); + // 라우팅 + 공정 조회 const fetchRoutings = useCallback( async (itemCode: string) => { @@ -340,7 +389,10 @@ export function useProcessWorkStandard(config: ProcessWorkStandardConfig) { selection, loading, saving, + isRegisteredMode, fetchItems, + fetchRegisteredItems, + loadItems, selectItem, selectProcess, fetchWorkItems, diff --git a/frontend/lib/registry/components/v2-process-work-standard/types.ts b/frontend/lib/registry/components/v2-process-work-standard/types.ts index 6d2b0bea..7185429b 100644 --- a/frontend/lib/registry/components/v2-process-work-standard/types.ts +++ b/frontend/lib/registry/components/v2-process-work-standard/types.ts @@ -37,6 +37,10 @@ export interface ProcessWorkStandardConfig { splitRatio?: number; leftPanelTitle?: string; readonly?: boolean; + /** 품목 목록 모드: all=전체, registered=등록된 품목만 */ + itemListMode?: "all" | "registered"; + /** 등록 모드 시 화면 코드 (자동 설정됨) */ + screenCode?: string; } // ============================================================ @@ -121,6 +125,7 @@ export interface ProcessWorkStandardComponentProps { formData?: Record; isPreview?: boolean; tableName?: string; + screenId?: number | string; } // 선택 상태