'use client'; /** * 재고현황 상세/수정 페이지 * * 기획서 기준: * - 기본 정보: 자재번호, 품목코드, 품목유형, 품목명, 규격, 단위, 재고량 (읽기 전용) * - 수정 가능: 안전재고, 상태 (사용/미사용) */ import { useState, useCallback, useEffect } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate'; import { stockStatusConfig } from './stockStatusConfig'; import { ServerErrorPage } from '@/components/common/ServerErrorPage'; import { getStockById, updateStock } from './actions'; import { USE_STATUS_LABELS, ITEM_TYPE_LABELS } from './types'; import type { ItemType } from './types'; import { isNextRedirectError } from '@/lib/utils/redirect-error'; import { toast } from 'sonner'; interface StockStatusDetailProps { id: string; } // 상세 페이지용 데이터 타입 interface StockDetailData { id: string; stockNumber: string; itemCode: string; itemType: ItemType; itemName: string; specification: string; unit: string; calculatedQty: number; safetyStock: number; useStatus: 'active' | 'inactive'; } export function StockStatusDetail({ id }: StockStatusDetailProps) { const router = useRouter(); const searchParams = useSearchParams(); const initialMode = searchParams.get('mode') === 'edit' ? 'edit' : 'view'; // API 데이터 상태 const [detail, setDetail] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); // 폼 데이터 (수정 모드용) const [formData, setFormData] = useState<{ safetyStock: number; useStatus: 'active' | 'inactive'; }>({ safetyStock: 0, useStatus: 'active', }); // 저장 중 상태 const [isSaving, setIsSaving] = useState(false); // API 데이터 로드 const loadData = useCallback(async () => { setIsLoading(true); setError(null); try { const result = await getStockById(id); if (result.success && result.data) { const data = result.data; // API 응답을 상세 페이지용 데이터로 변환 const detailData: StockDetailData = { id: data.id, stockNumber: data.id, // stockNumber가 없으면 id 사용 itemCode: data.itemCode, itemType: (data.itemType || 'RM') as ItemType, itemName: data.itemName, specification: data.specification || '-', unit: data.unit, calculatedQty: data.currentStock, // 재고량 safetyStock: data.safetyStock, useStatus: data.status === null ? 'active' : 'active', // 기본값 }; setDetail(detailData); setFormData({ safetyStock: detailData.safetyStock, useStatus: detailData.useStatus, }); } else { setError(result.error || '재고 정보를 찾을 수 없습니다.'); } } catch (err) { if (isNextRedirectError(err)) throw err; console.error('[StockStatusDetail] loadData error:', err); setError('데이터를 불러오는 중 오류가 발생했습니다.'); } finally { setIsLoading(false); } }, [id]); // 데이터 로드 useEffect(() => { loadData(); }, [loadData]); // 폼 값 변경 핸들러 const handleInputChange = (field: keyof typeof formData, value: string | number) => { setFormData((prev) => ({ ...prev, [field]: field === 'useStatus' ? value : Number(value), })); }; // 저장 핸들러 const handleSave = async () => { if (!detail) return; setIsSaving(true); try { const result = await updateStock(id, formData); if (result.success) { toast.success('재고 정보가 저장되었습니다.'); // 상세 데이터 업데이트 setDetail((prev) => prev ? { ...prev, safetyStock: formData.safetyStock, useStatus: formData.useStatus, } : null ); // view 모드로 전환 router.push(`/ko/material/stock-status/${id}?mode=view`); } else { toast.error(result.error || '저장에 실패했습니다.'); } } catch (err) { if (isNextRedirectError(err)) throw err; console.error('[StockStatusDetail] handleSave error:', err); toast.error('저장 중 오류가 발생했습니다.'); } finally { setIsSaving(false); } }; // 읽기 전용 필드 렌더링 (수정 모드에서 구분용) const renderReadOnlyField = (label: string, value: string | number, isEditMode = false) => (
{isEditMode ? ( // 수정 모드: 읽기 전용임을 명확히 표시 (어두운 배경 + cursor-not-allowed)
{value}
) : ( // 보기 모드: 일반 텍스트 스타일
{value}
)}
); // 상세 보기 모드 렌더링 const renderViewContent = useCallback(() => { if (!detail) return null; return ( 기본 정보
{/* Row 1: 자재번호, 품목코드, 품목유형, 품목명 */}
{renderReadOnlyField('자재번호', detail.stockNumber)} {renderReadOnlyField('품목코드', detail.itemCode)} {renderReadOnlyField('품목유형', ITEM_TYPE_LABELS[detail.itemType] || '-')} {renderReadOnlyField('품목명', detail.itemName)}
{/* Row 2: 규격, 단위, 재고량, 안전재고 */}
{renderReadOnlyField('규격', detail.specification)} {renderReadOnlyField('단위', detail.unit)} {renderReadOnlyField('재고량', detail.calculatedQty)} {renderReadOnlyField('안전재고', detail.safetyStock)}
{/* Row 3: 상태 */}
{renderReadOnlyField('상태', USE_STATUS_LABELS[detail.useStatus])}
); }, [detail]); // 수정 모드 렌더링 const renderFormContent = useCallback(() => { if (!detail) return null; return ( 기본 정보
{/* Row 1: 자재번호, 품목코드, 품목유형, 품목명 (읽기 전용) */}
{renderReadOnlyField('자재번호', detail.stockNumber, true)} {renderReadOnlyField('품목코드', detail.itemCode, true)} {renderReadOnlyField('품목유형', ITEM_TYPE_LABELS[detail.itemType] || '-', true)} {renderReadOnlyField('품목명', detail.itemName, true)}
{/* Row 2: 규격, 단위, 재고량 (읽기 전용) + 안전재고 (수정 가능) */}
{renderReadOnlyField('규격', detail.specification, true)} {renderReadOnlyField('단위', detail.unit, true)} {renderReadOnlyField('재고량', detail.calculatedQty, true)} {/* 안전재고 (수정 가능) */}
handleInputChange('safetyStock', e.target.value)} className="mt-1.5 border-gray-300 focus:border-blue-500 focus:ring-blue-500" min={0} />
{/* Row 3: 상태 (수정 가능) */}
); }, [detail, formData]); // 에러 상태 표시 if (!isLoading && (error || !detail)) { return ( ); } return ( | undefined} itemId={id} isLoading={isLoading} renderView={() => renderViewContent()} renderForm={() => renderFormContent()} onSubmit={async () => { await handleSave(); return { success: true }; }} /> ); }