refactor(WEB): 품목관리 경로 통합 - /items 삭제 및 /production/screen-production으로 일원화

- /items 폴더 삭제 (중복 경로 제거)
- /production/screen-production에 신버전 DynamicItemForm 기반 페이지 적용
- 구버전 ItemForm 연결 제거로 등록/수정 오류 해결
- 컴포넌트 내부 경로 참조 /items → /production/screen-production 변경
  - ItemListClient, ItemForm, ItemDetailClient, ItemDetailEdit, DynamicItemForm

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-01-20 11:34:59 +09:00
parent 36322a0927
commit 6f457b28f3
14 changed files with 333 additions and 1734 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +0,0 @@
'use client';
import { use, useEffect } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
interface PageProps {
params: Promise<{ id: string }>;
}
/**
* V2 하위 호환성: /[id]/edit → /[id]?mode=edit 리다이렉트
* 기존 쿼리 파라미터(type, id)는 유지
*/
export default function ItemEditPage({ params }: PageProps) {
const { id } = use(params);
const router = useRouter();
const searchParams = useSearchParams();
useEffect(() => {
// 기존 쿼리 파라미터 유지하면서 mode=edit 추가
const type = searchParams.get('type') || 'FG';
const itemId = searchParams.get('id') || '';
router.replace(`/items/${id}?type=${type}&id=${itemId}&mode=edit`);
}, [id, router, searchParams]);
return (
<div className="flex items-center justify-center min-h-[400px]">
<div className="text-muted-foreground"> ...</div>
</div>
);
}

View File

@@ -1,34 +0,0 @@
'use client';
/**
* 품목 상세/수정 페이지 (Client Component)
* V2 패턴: ?mode=edit로 수정 모드 전환
*/
import { use } from 'react';
import { useSearchParams } from 'next/navigation';
import { ItemDetailView } from '@/components/items/ItemDetailView';
import { ItemDetailEdit } from '@/components/items/ItemDetailEdit';
interface PageProps {
params: Promise<{ id: string }>;
}
export default function ItemDetailPage({ params }: PageProps) {
const { id } = use(params);
const searchParams = useSearchParams();
// URL에서 type, id, mode 쿼리 파라미터 읽기
const itemType = searchParams.get('type') || 'FG';
const itemId = searchParams.get('id') || '';
const mode = searchParams.get('mode') === 'edit' ? 'edit' : 'view';
// 품목 코드 디코딩
const itemCode = decodeURIComponent(id);
if (mode === 'edit') {
return <ItemDetailEdit itemCode={itemCode} itemType={itemType} itemId={itemId} />;
}
return <ItemDetailView itemCode={itemCode} itemType={itemType} itemId={itemId} />;
}

View File

@@ -1,101 +0,0 @@
/**
* 품목 등록 페이지
*
* DynamicItemForm을 사용하여 품목기준관리 데이터 기반 동적 폼 렌더링
*/
'use client';
import { useState } from 'react';
import DynamicItemForm from '@/components/items/DynamicItemForm';
import type { DynamicFormData } from '@/components/items/DynamicItemForm/types';
// 2025-12-16: options 관련 변환 로직 제거
// 백엔드가 품목기준관리 field_key 매핑을 처리하므로 프론트에서 변환 불필요
import { DuplicateCodeError } from '@/lib/api/error-handler';
// 기존 ItemForm (주석처리 - 롤백 시 사용)
// import ItemForm from '@/components/items/ItemForm';
// import type { CreateItemFormData } from '@/lib/utils/validation';
export default function CreateItemPage() {
const [submitError, setSubmitError] = useState<string | null>(null);
const handleSubmit = async (data: DynamicFormData) => {
setSubmitError(null);
// 필드명 변환: spec → specification (백엔드 API 규격)
const submitData = { ...data };
if (submitData.spec !== undefined) {
submitData.specification = submitData.spec;
delete submitData.spec;
}
// 2025-12-15: item_type은 Request Body에서 필수 (ItemService.store validation)
// product_type과 item_type을 동일하게 설정
const itemType = submitData.product_type as string;
submitData.item_type = itemType;
// API 호출 전 이미지 데이터 제거 (파일 업로드는 별도 API 사용)
// bending_diagram이 base64 데이터인 경우 제거 (JSON에 포함시키면 안됨)
if (submitData.bending_diagram && typeof submitData.bending_diagram === 'string' && (submitData.bending_diagram as string).startsWith('data:')) {
delete submitData.bending_diagram;
}
// 시방서/인정서 파일 필드도 base64면 제거
if (submitData.specification_file && typeof submitData.specification_file === 'string' && (submitData.specification_file as string).startsWith('data:')) {
delete submitData.specification_file;
}
if (submitData.certification_file && typeof submitData.certification_file === 'string' && (submitData.certification_file as string).startsWith('data:')) {
delete submitData.certification_file;
}
// API 호출: POST /api/proxy/items
// 백엔드에서 product_type에 따라 Product/Material 분기 처리
const response = await fetch('/api/proxy/items', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(submitData),
});
const result = await response.json();
if (!response.ok || !result.success) {
// 2025-12-11: 백엔드 중복 에러 처리 (DuplicateCodeException)
// duplicate_id가 있으면 DuplicateCodeError throw → DynamicItemForm에서 다이얼로그 표시
if (response.status === 400 && result.duplicate_id) {
console.warn('[CreateItemPage] 품목코드 중복 에러:', result);
throw new DuplicateCodeError(
result.message || '해당 품목코드가 이미 존재합니다.',
result.duplicate_id,
result.duplicate_code
);
}
const errorMessage = result.message || '품목 등록에 실패했습니다.';
console.error('[CreateItemPage] 품목 등록 실패:', errorMessage);
setSubmitError(errorMessage);
throw new Error(errorMessage);
}
// 성공 시 DynamicItemForm 내부에서 /items로 리다이렉트 처리됨
// console.log('[CreateItemPage] 품목 등록 성공:', result.data);
// 생성된 품목 ID를 포함한 데이터 반환 (파일 업로드용)
return { id: result.data.id, ...result.data };
};
return (
<div className="p-6">
{submitError && (
<div className="mb-4 p-4 bg-red-50 border border-red-200 rounded-lg text-red-900">
{submitError}
</div>
)}
<DynamicItemForm
mode="create"
onSubmit={handleSubmit}
/>
</div>
);
}

View File

@@ -1,24 +0,0 @@
/**
* 품목 관리 페이지
*
* 품목기준관리 API 연동
* - 품목 목록: API에서 조회
* - 테이블 컬럼: custom-tabs API에서 동적 구성
*/
import ItemListClient from '@/components/items/ItemListClient';
/**
* 품목 목록 페이지
*/
export default function ItemsPage() {
return <ItemListClient />;
}
/**
* 메타데이터 설정
*/
export const metadata = {
title: '품목 관리',
description: '품목 목록 조회 및 관리',
};

View File

@@ -1,213 +1,32 @@
/**
* 품목 수정 페이지
*/
'use client';
import { useEffect, useState } from 'react';
import { useParams, useRouter } from 'next/navigation';
import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
import ItemForm from '@/components/items/ItemForm';
import { ServerErrorPage } from '@/components/common/ServerErrorPage';
import type { ItemMaster } from '@/types/item';
import type { CreateItemFormData } from '@/lib/utils/validation';
import { use, useEffect } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
// Mock 데이터 (API 연동 전 임시)
const mockItems: ItemMaster[] = [
{
id: '1',
itemCode: 'KD-FG-001',
itemName: '스크린 제품 A',
itemType: 'FG',
unit: 'EA',
specification: '2000x2000',
isActive: true,
category1: '본체부품',
category2: '가이드시스템',
salesPrice: 150000,
purchasePrice: 100000,
marginRate: 33.3,
processingCost: 20000,
laborCost: 15000,
installCost: 10000,
productCategory: 'SCREEN',
lotAbbreviation: 'KD',
note: '스크린 제품 샘플입니다.',
safetyStock: 10,
leadTime: 7,
currentRevision: 0,
isFinal: false,
createdAt: '2025-01-10T00:00:00Z',
updatedAt: '2025-01-12T00:00:00Z',
bom: [
{
id: 'bom-1',
childItemCode: 'KD-PT-001',
childItemName: '가이드레일(벽면형)',
quantity: 2,
unit: 'EA',
unitPrice: 35000,
quantityFormula: 'H / 1000',
},
{
id: 'bom-2',
childItemCode: 'KD-PT-002',
childItemName: '절곡품 샘플',
quantity: 4,
unit: 'EA',
unitPrice: 30000,
isBending: true,
},
{
id: 'bom-3',
childItemCode: 'KD-SM-001',
childItemName: '볼트 M6x20',
quantity: 20,
unit: 'EA',
unitPrice: 50,
},
],
},
{
id: '2',
itemCode: 'KD-PT-001',
itemName: '가이드레일(벽면형)',
itemType: 'PT',
unit: 'EA',
specification: '2438mm',
isActive: true,
category1: '본체부품',
category2: '가이드시스템',
category3: '가이드레일',
salesPrice: 50000,
purchasePrice: 35000,
marginRate: 30,
partType: 'ASSEMBLY',
partUsage: 'GUIDE_RAIL',
installationType: '벽면형',
assemblyType: 'M',
assemblyLength: '2438',
currentRevision: 0,
isFinal: false,
createdAt: '2025-01-10T00:00:00Z',
},
{
id: '3',
itemCode: 'KD-PT-002',
itemName: '절곡품 샘플',
itemType: 'PT',
unit: 'EA',
specification: 'EGI 1.55T',
isActive: true,
partType: 'BENDING',
material: 'EGI 1.55T',
length: '2000',
salesPrice: 30000,
currentRevision: 0,
isFinal: false,
createdAt: '2025-01-10T00:00:00Z',
},
{
id: '4',
itemCode: 'KD-RM-001',
itemName: 'SPHC-SD',
itemType: 'RM',
unit: 'KG',
specification: '1.6T x 1219 x 2438',
isActive: true,
category1: '철강재',
purchasePrice: 1500,
material: 'SPHC-SD',
currentRevision: 0,
isFinal: false,
createdAt: '2025-01-10T00:00:00Z',
},
{
id: '5',
itemCode: 'KD-SM-001',
itemName: '볼트 M6x20',
itemType: 'SM',
unit: 'EA',
specification: 'M6x20',
isActive: true,
category1: '구조재/부속품',
category2: '볼트/너트',
purchasePrice: 50,
currentRevision: 0,
isFinal: false,
createdAt: '2025-01-10T00:00:00Z',
},
];
interface PageProps {
params: Promise<{ id: string }>;
}
export default function EditItemPage() {
const params = useParams();
/**
* V2 하위 호환성: /[id]/edit → /[id]?mode=edit 리다이렉트
* 기존 쿼리 파라미터(type, id)는 유지
*/
export default function ItemEditPage({ params }: PageProps) {
const { id } = use(params);
const router = useRouter();
const [item, setItem] = useState<ItemMaster | null>(null);
const [isLoading, setIsLoading] = useState(true);
const searchParams = useSearchParams();
useEffect(() => {
// TODO: API 연동 시 fetchItemByCode() 호출
const fetchItem = async () => {
setIsLoading(true);
try {
// params.id 타입 체크
if (!params.id || typeof params.id !== 'string') {
alert('잘못된 품목 ID입니다.');
router.push('/items');
return;
}
// 기존 쿼리 파라미터 유지하면서 mode=edit 추가
const type = searchParams.get('type') || 'FG';
const itemId = searchParams.get('id') || '';
// Mock: 데이터 조회
const itemCode = decodeURIComponent(params.id);
const foundItem = mockItems.find((item) => item.itemCode === itemCode);
if (foundItem) {
setItem(foundItem);
} else {
alert('품목을 찾을 수 없습니다.');
router.push('/items');
}
} catch {
alert('품목 조회에 실패했습니다.');
router.push('/items');
} finally {
setIsLoading(false);
}
};
fetchItem();
}, [params.id, router]);
const handleSubmit = async (data: CreateItemFormData) => {
// TODO: API 연동 시 updateItem() 호출
console.log('품목 수정 데이터:', data);
// Mock: 성공 메시지
alert(`품목 "${data.itemName}" (${data.itemCode})이(가) 수정되었습니다.`);
// API 연동 예시:
// const updatedItem = await updateItem(item.itemCode, data);
// router.push(`/items/${updatedItem.itemCode}`);
};
if (isLoading) {
return <ContentLoadingSpinner text="품목 정보를 불러오는 중..." />;
}
if (!item) {
return (
<ServerErrorPage
title="품목 정보를 불러올 수 없습니다"
message="품목을 찾을 수 없습니다."
showBackButton={true}
showHomeButton={true}
/>
);
}
router.replace(`/production/screen-production/${id}?type=${type}&id=${itemId}&mode=edit`);
}, [id, router, searchParams]);
return (
<div className="p-6">
<ItemForm mode="edit" initialData={item} onSubmit={handleSubmit} />
<div className="flex items-center justify-center min-h-[400px]">
<div className="text-muted-foreground"> ...</div>
</div>
);
}
}

View File

@@ -1,183 +1,34 @@
'use client';
/**
* 품목 상세 조회 페이지 (Client Component)
* 품목 상세/수정 페이지 (Client Component)
* V2 패턴: ?mode=edit로 수정 모드 전환
*/
import { use, useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { ContentLoadingSpinner } from '@/components/ui/loading-spinner';
import ItemDetailClient from '@/components/items/ItemDetailClient';
import { ServerErrorPage } from '@/components/common/ServerErrorPage';
import type { ItemMaster } from '@/types/item';
import { use } from 'react';
import { useSearchParams } from 'next/navigation';
import { ItemDetailView } from '@/components/items/ItemDetailView';
import { ItemDetailEdit } from '@/components/items/ItemDetailEdit';
// Mock 데이터 (API 연동 전 임시)
const mockItems: ItemMaster[] = [
{
id: '1',
itemCode: 'KD-FG-001',
itemName: '스크린 제품 A',
itemType: 'FG',
unit: 'EA',
specification: '2000x2000',
isActive: true,
category1: '본체부품',
category2: '가이드시스템',
salesPrice: 150000,
purchasePrice: 100000,
marginRate: 33.3,
processingCost: 20000,
laborCost: 15000,
installCost: 10000,
productCategory: 'SCREEN',
lotAbbreviation: 'KD',
note: '스크린 제품 샘플입니다.',
safetyStock: 10,
leadTime: 7,
currentRevision: 0,
isFinal: false,
createdAt: '2025-01-10T00:00:00Z',
updatedAt: '2025-01-12T00:00:00Z',
bom: [
{
id: 'bom-1',
childItemCode: 'KD-PT-001',
childItemName: '가이드레일(벽면형)',
quantity: 2,
unit: 'EA',
unitPrice: 35000,
quantityFormula: 'H / 1000',
},
{
id: 'bom-2',
childItemCode: 'KD-PT-002',
childItemName: '절곡품 샘플',
quantity: 4,
unit: 'EA',
unitPrice: 30000,
isBending: true,
},
{
id: 'bom-3',
childItemCode: 'KD-SM-001',
childItemName: '볼트 M6x20',
quantity: 20,
unit: 'EA',
unitPrice: 50,
},
],
},
{
id: '2',
itemCode: 'KD-PT-001',
itemName: '가이드레일(벽면형)',
itemType: 'PT',
unit: 'EA',
specification: '2438mm',
isActive: true,
category1: '본체부품',
category2: '가이드시스템',
category3: '가이드레일',
salesPrice: 50000,
purchasePrice: 35000,
marginRate: 30,
partType: 'ASSEMBLY',
partUsage: 'GUIDE_RAIL',
installationType: '벽면형',
assemblyType: 'M',
assemblyLength: '2438',
currentRevision: 0,
isFinal: false,
createdAt: '2025-01-10T00:00:00Z',
},
{
id: '3',
itemCode: 'KD-PT-002',
itemName: '절곡품 샘플',
itemType: 'PT',
unit: 'EA',
specification: 'EGI 1.55T',
isActive: true,
partType: 'BENDING',
material: 'EGI 1.55T',
length: '2000',
salesPrice: 30000,
currentRevision: 0,
isFinal: false,
createdAt: '2025-01-10T00:00:00Z',
},
{
id: '4',
itemCode: 'KD-RM-001',
itemName: 'SPHC-SD',
itemType: 'RM',
unit: 'KG',
specification: '1.6T x 1219 x 2438',
isActive: true,
category1: '철강재',
purchasePrice: 1500,
material: 'SPHC-SD',
currentRevision: 0,
isFinal: false,
createdAt: '2025-01-10T00:00:00Z',
},
{
id: '5',
itemCode: 'KD-SM-001',
itemName: '볼트 M6x20',
itemType: 'SM',
unit: 'EA',
specification: 'M6x20',
isActive: true,
category1: '구조재/부속품',
category2: '볼트/너트',
purchasePrice: 50,
currentRevision: 0,
isFinal: false,
createdAt: '2025-01-10T00:00:00Z',
},
];
/**
* 품목 상세 페이지
*/
export default function ItemDetailPage({
params,
}: {
interface PageProps {
params: Promise<{ id: string }>;
}) {
}
export default function ItemDetailPage({ params }: PageProps) {
const { id } = use(params);
const router = useRouter();
const [item, setItem] = useState<ItemMaster | null>(null);
const [isLoading, setIsLoading] = useState(true);
const searchParams = useSearchParams();
useEffect(() => {
// API 연동 전 mock 데이터 사용
const foundItem = mockItems.find(
(item) => item.itemCode === decodeURIComponent(id)
);
setItem(foundItem || null);
setIsLoading(false);
}, [id]);
// URL에서 type, id, mode 쿼리 파라미터 읽기
const itemType = searchParams.get('type') || 'FG';
const itemId = searchParams.get('id') || '';
const mode = searchParams.get('mode') === 'edit' ? 'edit' : 'view';
if (isLoading) {
return <ContentLoadingSpinner text="품목 정보를 불러오는 중..." />;
// 품목 코드 디코딩
const itemCode = decodeURIComponent(id);
if (mode === 'edit') {
return <ItemDetailEdit itemCode={itemCode} itemType={itemType} itemId={itemId} />;
}
if (!item) {
return (
<ServerErrorPage
title="품목 정보를 불러올 수 없습니다"
message="품목을 찾을 수 없습니다."
showBackButton={true}
showHomeButton={true}
/>
);
}
return (
<div className="p-6">
<ItemDetailClient item={item} />
</div>
);
}
return <ItemDetailView itemCode={itemCode} itemType={itemType} itemId={itemId} />;
}

View File

@@ -1,28 +1,101 @@
/**
* 품목 등록 페이지
*
* DynamicItemForm을 사용하여 품목기준관리 데이터 기반 동적 폼 렌더링
*/
'use client';
import ItemForm from '@/components/items/ItemForm';
import type { CreateItemFormData } from '@/lib/utils/validation';
import { useState } from 'react';
import DynamicItemForm from '@/components/items/DynamicItemForm';
import type { DynamicFormData } from '@/components/items/DynamicItemForm/types';
// 2025-12-16: options 관련 변환 로직 제거
// 백엔드가 품목기준관리 field_key 매핑을 처리하므로 프론트에서 변환 불필요
import { DuplicateCodeError } from '@/lib/api/error-handler';
// 기존 ItemForm (주석처리 - 롤백 시 사용)
// import ItemForm from '@/components/items/ItemForm';
// import type { CreateItemFormData } from '@/lib/utils/validation';
export default function CreateItemPage() {
const handleSubmit = async (data: CreateItemFormData) => {
// TODO: API 연동 시 createItem() 호출
console.log('품목 등록 데이터:', data);
const [submitError, setSubmitError] = useState<string | null>(null);
// Mock: 성공 메시지
alert(`품목 "${data.itemName}" (${data.itemCode})이(가) 등록되었습니다.`);
const handleSubmit = async (data: DynamicFormData) => {
setSubmitError(null);
// API 연동 예시:
// const newItem = await createItem(data);
// router.push(`/items/${newItem.itemCode}`);
// 필드명 변환: spec → specification (백엔드 API 규격)
const submitData = { ...data };
if (submitData.spec !== undefined) {
submitData.specification = submitData.spec;
delete submitData.spec;
}
// 2025-12-15: item_type은 Request Body에서 필수 (ItemService.store validation)
// product_type과 item_type을 동일하게 설정
const itemType = submitData.product_type as string;
submitData.item_type = itemType;
// API 호출 전 이미지 데이터 제거 (파일 업로드는 별도 API 사용)
// bending_diagram이 base64 데이터인 경우 제거 (JSON에 포함시키면 안됨)
if (submitData.bending_diagram && typeof submitData.bending_diagram === 'string' && (submitData.bending_diagram as string).startsWith('data:')) {
delete submitData.bending_diagram;
}
// 시방서/인정서 파일 필드도 base64면 제거
if (submitData.specification_file && typeof submitData.specification_file === 'string' && (submitData.specification_file as string).startsWith('data:')) {
delete submitData.specification_file;
}
if (submitData.certification_file && typeof submitData.certification_file === 'string' && (submitData.certification_file as string).startsWith('data:')) {
delete submitData.certification_file;
}
// API 호출: POST /api/proxy/items
// 백엔드에서 product_type에 따라 Product/Material 분기 처리
const response = await fetch('/api/proxy/items', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(submitData),
});
const result = await response.json();
if (!response.ok || !result.success) {
// 2025-12-11: 백엔드 중복 에러 처리 (DuplicateCodeException)
// duplicate_id가 있으면 DuplicateCodeError throw → DynamicItemForm에서 다이얼로그 표시
if (response.status === 400 && result.duplicate_id) {
console.warn('[CreateItemPage] 품목코드 중복 에러:', result);
throw new DuplicateCodeError(
result.message || '해당 품목코드가 이미 존재합니다.',
result.duplicate_id,
result.duplicate_code
);
}
const errorMessage = result.message || '품목 등록에 실패했습니다.';
console.error('[CreateItemPage] 품목 등록 실패:', errorMessage);
setSubmitError(errorMessage);
throw new Error(errorMessage);
}
// 성공 시 DynamicItemForm 내부에서 /items로 리다이렉트 처리됨
// console.log('[CreateItemPage] 품목 등록 성공:', result.data);
// 생성된 품목 ID를 포함한 데이터 반환 (파일 업로드용)
return { id: result.data.id, ...result.data };
};
return (
<div className="p-6">
<ItemForm mode="create" onSubmit={handleSubmit} />
{submitError && (
<div className="mb-4 p-4 bg-red-50 border border-red-200 rounded-lg text-red-900">
{submitError}
</div>
)}
<DynamicItemForm
mode="create"
onSubmit={handleSubmit}
/>
</div>
);
}

View File

@@ -1,9 +1,9 @@
'use client';
/**
* 품목 목록 페이지 (Client Component)
* 품목 관리 페이지
*
* Next.js 15 App Router
* 품목기준관리 API 연동
* - 품목 목록: API에서 조회
* - 테이블 컬럼: custom-tabs API에서 동적 구성
*/
import ItemListClient from '@/components/items/ItemListClient';
@@ -13,4 +13,12 @@ import ItemListClient from '@/components/items/ItemListClient';
*/
export default function ItemsPage() {
return <ItemListClient />;
}
}
/**
* 메타데이터 설정
*/
export const metadata = {
title: '품목 관리',
description: '품목 목록 조회 및 관리',
};

View File

@@ -419,7 +419,7 @@ export default function DynamicItemForm({
}
}
router.push('/items');
router.push('/production/screen-production');
router.refresh();
});
} catch (error) {
@@ -603,7 +603,7 @@ export default function DynamicItemForm({
const itemType = duplicateCheckResult.duplicateItemType || selectedItemType || 'PT';
const itemId = duplicateCheckResult.duplicateId;
// code는 없으므로 id를 path에 사용 (edit 페이지에서 id 쿼리 파라미터로 조회)
router.push(`/items/${itemId}/edit?type=${itemType}&id=${itemId}`);
router.push(`/production/screen-production/${itemId}/edit?type=${itemType}&id=${itemId}`);
}
};

View File

@@ -122,7 +122,7 @@ export default function ItemDetailClient({ item }: ItemDetailClientProps) {
</Button>
<Button
type="button"
onClick={() => router.push(`/items/${encodeURIComponent(item.itemCode)}/edit?type=${item.itemType}&id=${item.id}`)}
onClick={() => router.push(`/production/screen-production/${encodeURIComponent(item.itemCode)}/edit?type=${item.itemType}&id=${item.id}`)}
>
<Edit className="w-4 h-4 mr-2" />

View File

@@ -377,7 +377,7 @@ export function ItemDetailEdit({ itemCode, itemType: urlItemType, itemId: urlIte
<div className="flex flex-col items-center justify-center py-12 gap-4">
<p className="text-destructive">{error}</p>
<button
onClick={() => router.push('/items')}
onClick={() => router.push('/production/screen-production')}
className="text-primary hover:underline"
>
@@ -392,7 +392,7 @@ export function ItemDetailEdit({ itemCode, itemType: urlItemType, itemId: urlIte
<div className="flex flex-col items-center justify-center py-12 gap-4">
<p className="text-muted-foreground"> .</p>
<button
onClick={() => router.push('/items')}
onClick={() => router.push('/production/screen-production')}
className="text-primary hover:underline"
>

View File

@@ -174,7 +174,7 @@ export default function ItemForm({ mode, initialData, onSubmit }: ItemFormProps)
certificationFileName: certificationFile?.name,
};
await onSubmit(finalData);
router.push('/items');
router.push('/production/screen-production');
router.refresh();
} catch {
alert('품목 저장에 실패했습니다.');

View File

@@ -152,12 +152,12 @@ export default function ItemListClient() {
const handleView = (itemCode: string, itemType: string, itemId: string) => {
// itemType을 query param으로 전달 (Materials 조회를 위해)
router.push(`/items/${encodeURIComponent(itemCode)}?type=${itemType}&id=${itemId}`);
router.push(`/production/screen-production/${encodeURIComponent(itemCode)}?type=${itemType}&id=${itemId}`);
};
const handleEdit = (itemCode: string, itemType: string, itemId: string) => {
// itemType을 query param으로 전달 (Materials 조회를 위해)
router.push(`/items/${encodeURIComponent(itemCode)}/edit?type=${itemType}&id=${itemId}`);
router.push(`/production/screen-production/${encodeURIComponent(itemCode)}/edit?type=${itemType}&id=${itemId}`);
};
// 삭제 확인 다이얼로그 열기
@@ -312,7 +312,7 @@ export default function ItemListClient() {
// 등록 버튼 (createButton 사용 - headerActions 대신)
createButton: {
label: '품목 등록',
onClick: () => router.push('/items/create'),
onClick: () => router.push('/production/screen-production/create'),
icon: Plus,
},