feat: 입고 등록 DevMode 채우기 및 AuthContext 초기값 수정

- DevToolbar에 입고(자재) 버튼 추가, 2행 레이아웃 정리
  (회계 | 기준 | 자재를 구분선으로 한 줄 배치)
- receivingData 생성기 추가 (품목, 공급업체, 수량 랜덤)
- ReceivingDetail에 useDevFill 연동 (로트번호, 작성자 포함)
- transformFrontendToApi에서 orderQty 미입력 시 receivingQty 사용
- AuthContext 초기값 null로 변경 (SSR 시 드미트리 하드코딩 제거)
This commit is contained in:
2026-01-29 13:48:12 +09:00
parent 3dab72701e
commit 260167c6a8
7 changed files with 239 additions and 78 deletions

View File

@@ -18,7 +18,8 @@ import React, { createContext, useContext, useState, useCallback, useEffect, Rea
export type DevFillPageType =
| 'quote' | 'quoteV2' | 'order' | 'workOrder' | 'workOrderComplete' | 'shipment' // 판매/생산 플로우
| 'deposit' | 'withdrawal' | 'purchaseApproval' | 'cardTransaction' // 회계 플로우
| 'client'; // 기준정보
| 'client' // 기준정보
| 'receiving'; // 자재
// 폼 채우기 함수 타입
type FillFormFunction = (data?: unknown) => void | Promise<void>;

View File

@@ -35,6 +35,8 @@ import {
CreditCard, // 카드
// 기준정보 아이콘
Building2, // 거래처
// 자재 아이콘
PackageCheck, // 입고
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
@@ -57,6 +59,8 @@ const PAGE_PATTERNS: { pattern: RegExp; type: DevFillPageType; label: string }[]
{ pattern: /\/accounting\/card-transactions\/new/, type: 'cardTransaction', label: '카드' },
// 기준정보
{ pattern: /\/client-management-sales-admin\/new/, type: 'client', label: '거래처' },
// 자재
{ pattern: /\/material\/receiving-management\/new/, type: 'receiving', label: '입고' },
];
// ?mode=new 쿼리 파라미터를 사용하는 페이지 매핑
@@ -91,6 +95,11 @@ const MASTER_DATA_STEPS: { type: DevFillPageType; label: string; icon: typeof Fi
{ type: 'client', label: '거래처', icon: Building2, path: '/sales/client-management-sales-admin/new', fillEnabled: true },
];
// 자재 단계 정의
const MATERIAL_STEPS: { type: DevFillPageType; label: string; icon: typeof FileText; path: string; fillEnabled: boolean }[] = [
{ type: 'receiving', label: '입고', icon: PackageCheck, path: '/material/receiving-management/new', fillEnabled: true },
];
export function DevToolbar() {
const pathname = usePathname();
const router = useRouter();
@@ -320,9 +329,10 @@ export function DevToolbar() {
</div>
)}
{/* 회계 플로우 버튼 영역 */}
{/* 2행: 회계 + 기준정보 + 자재 버튼 영역 */}
{isExpanded && (
<div className="flex items-center gap-2 px-3 pb-3 border-t border-yellow-300 pt-3">
{/* 회계 */}
<span className="text-xs text-yellow-600 font-medium mr-1">:</span>
{ACCOUNTING_STEPS.map((step) => {
const Icon = step.icon;
@@ -330,7 +340,6 @@ export function DevToolbar() {
const isRegistered = hasRegisteredForm(step.type);
const isCurrentLoading = isLoading === step.type;
// 활성화된 페이지: 폼 채우기 (fillEnabled가 true인 경우만)
if (isActive && step.fillEnabled) {
return (
<Button
@@ -352,7 +361,6 @@ export function DevToolbar() {
);
}
// 비활성화된 페이지 또는 이동만 가능한 버튼: 해당 페이지로 이동
return (
<Button
key={step.type}
@@ -367,12 +375,11 @@ export function DevToolbar() {
</Button>
);
})}
</div>
)}
{/* 기준정보 버튼 영역 */}
{isExpanded && (
<div className="flex items-center gap-2 px-3 pb-3 border-t border-yellow-300 pt-3">
{/* 구분선 */}
<div className="w-px h-6 bg-yellow-300 mx-1" />
{/* 기준정보 */}
<span className="text-xs text-yellow-600 font-medium mr-1">:</span>
{MASTER_DATA_STEPS.map((step) => {
const Icon = step.icon;
@@ -380,7 +387,6 @@ export function DevToolbar() {
const isRegistered = hasRegisteredForm(step.type);
const isCurrentLoading = isLoading === step.type;
// 활성화된 페이지: 폼 채우기 (fillEnabled가 true인 경우만)
if (isActive && step.fillEnabled) {
return (
<Button
@@ -402,7 +408,6 @@ export function DevToolbar() {
);
}
// 비활성화된 페이지: 해당 페이지로 이동
return (
<Button
key={step.type}
@@ -417,6 +422,53 @@ export function DevToolbar() {
</Button>
);
})}
{/* 구분선 */}
<div className="w-px h-6 bg-yellow-300 mx-1" />
{/* 자재 */}
<span className="text-xs text-yellow-600 font-medium mr-1">:</span>
{MATERIAL_STEPS.map((step) => {
const Icon = step.icon;
const isActive = activePage === step.type;
const isRegistered = hasRegisteredForm(step.type);
const isCurrentLoading = isLoading === step.type;
if (isActive && step.fillEnabled) {
return (
<Button
key={step.type}
size="sm"
variant="default"
disabled={!isRegistered || isCurrentLoading}
className="bg-purple-500 hover:bg-purple-600 text-white border-purple-600"
onClick={() => handleFillForm(step.type)}
title="폼 자동 채우기"
>
{isCurrentLoading ? (
<Loader2 className="w-4 h-4 mr-1 animate-spin" />
) : (
<Icon className="w-4 h-4 mr-1" />
)}
{step.label}
</Button>
);
}
return (
<Button
key={step.type}
size="sm"
variant="outline"
className={`border-purple-300 text-purple-700 hover:bg-purple-100 hover:border-purple-500 ${isActive ? 'bg-purple-100 border-purple-500' : ''}`}
onClick={() => handleNavigate(step.path)}
title={`${step.label} 페이지로 이동`}
>
<Icon className="w-4 h-4 mr-1" />
{step.label}
</Button>
);
})}
</div>
)}
@@ -424,7 +476,7 @@ export function DevToolbar() {
{isExpanded && !activePage && (
<div className="px-3 pb-3">
<p className="text-xs text-yellow-600">
///////
////////
</p>
</div>
)}

View File

@@ -0,0 +1,62 @@
/**
* 입고 샘플 데이터 생성기
*/
import {
randomPick,
randomInt,
randomRemark,
today,
} from './index';
// ===== 상수 정의 =====
const SUPPLIERS = [
'(주)대한철강', '삼성전자부품', '한국플라스틱', '글로벌전자',
'동양화학', '한국볼트', '지오TNS (KG스틸)', 'SK이노베이션',
'포스코', 'LG화학', '현대중공업', '한화솔루션',
];
const ITEMS = [
{ code: 'STEEL-001', name: 'SUS304 스테인리스 판재', spec: '1000x2000x3T', unit: 'EA' },
{ code: 'STEEL-002', name: '알루미늄 프로파일', spec: '40x40x2000L', unit: 'EA' },
{ code: 'ELEC-002', name: 'MCU 컨트롤러 IC', spec: 'STM32F103C8T6', unit: 'EA' },
{ code: 'ELEC-005', name: 'DC 모터 24V', spec: '24V 100RPM', unit: 'EA' },
{ code: 'ELEC-007', name: '커패시터 100uF', spec: '100uF 50V', unit: 'EA' },
{ code: 'PLAS-003', name: 'ABS 사출 케이스', spec: '150x100x50', unit: 'SET' },
{ code: 'CHEM-001', name: '에폭시 접착제', spec: '500ml', unit: 'EA' },
{ code: 'BOLT-001', name: 'SUS 볼트 M8x30', spec: 'M8x30 SUS304', unit: 'EA' },
{ code: '80008', name: '80008 egi1.55', spec: '1.55 * 1218 × 480', unit: 'EA' },
];
// ===== 타입 정의 =====
export interface ReceivingFormData {
itemCode: string;
itemName: string;
specification: string;
unit: string;
supplier: string;
receivingQty: number;
receivingDate: string;
status: string;
remark: string;
}
// ===== 메인 생성 함수 =====
export function generateReceivingData(): ReceivingFormData {
const item = randomPick(ITEMS);
return {
itemCode: item.code,
itemName: item.name,
specification: item.spec,
unit: item.unit,
supplier: randomPick(SUPPLIERS),
receivingQty: randomInt(10, 500),
receivingDate: today(),
status: 'receiving_pending',
remark: randomRemark(),
};
}

View File

@@ -14,4 +14,5 @@ export { generateQuoteData, generateQuoteItem } from './generators/quoteData';
export { generateOrderData, generateOrderDataFull } from './generators/orderData';
export { generateWorkOrderData } from './generators/workOrderData';
export { generateShipmentData } from './generators/shipmentData';
export { generateDepositData, generateWithdrawalData, generatePurchaseApprovalData, generateCardTransactionData } from './generators/accountingData';
export { generateDepositData, generateWithdrawalData, generatePurchaseApprovalData, generateCardTransactionData } from './generators/accountingData';
export { generateReceivingData } from './generators/receivingData';