feat(WEB): 공정관리/작업지시/작업자화면 기능 강화 및 템플릿 개선
- 공정관리: ProcessDetail/ProcessForm/ProcessList 개선, StepDetail/StepForm 신규 추가 - 작업지시: WorkOrderDetail/Edit/List UI 개선, 작업지시서 문서 추가 - 작업자화면: WorkerScreen 대폭 개선, MaterialInputModal/WorkLogModal 수정, WorkItemCard 신규 - 영업주문: 주문 상세 페이지 개선 - 입고관리: 상세/actions 수정 - 템플릿: IntegratedDetailTemplate/IntegratedListTemplateV2/UniversalListPage 기능 확장 - UI: confirm-dialog 개선 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 공정 단계 상세/수정/등록 페이지
|
||||
*
|
||||
* - /[id]/steps/[stepId] → 상세 보기
|
||||
* - /[id]/steps/[stepId]?mode=edit → 수정
|
||||
* - /[id]/steps/new → 등록
|
||||
*/
|
||||
|
||||
import { use } from 'react';
|
||||
import { StepDetailClient } from '@/components/process-management/StepDetailClient';
|
||||
|
||||
export default function StepDetailPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ id: string; stepId: string }>;
|
||||
}) {
|
||||
const { id, stepId } = use(params);
|
||||
|
||||
return <StepDetailClient processId={id} stepId={stepId} />;
|
||||
}
|
||||
@@ -73,6 +73,7 @@ import {
|
||||
} from "@/components/orders";
|
||||
import { sendSalesOrderNotification } from "@/lib/actions/fcm";
|
||||
import { OrderSalesDetailEdit } from "@/components/orders/OrderSalesDetailEdit";
|
||||
import { getDepartmentTree, type DepartmentRecord } from "@/components/hr/DepartmentManagement/actions";
|
||||
|
||||
/**
|
||||
* 수량 포맷 함수
|
||||
@@ -156,6 +157,8 @@ export default function OrderDetailPage() {
|
||||
const [isProductionDialogOpen, setIsProductionDialogOpen] = useState(false);
|
||||
const [isCreatingProduction, setIsCreatingProduction] = useState(false);
|
||||
const [productionPriority, setProductionPriority] = useState<"normal" | "high" | "urgent">("normal");
|
||||
const [productionDepartmentId, setProductionDepartmentId] = useState<string>("");
|
||||
const [departments, setDepartments] = useState<DepartmentRecord[]>([]);
|
||||
const [productionMemo, setProductionMemo] = useState("");
|
||||
// 생산지시 완료 알림 모달 상태
|
||||
const [isProductionSuccessDialogOpen, setIsProductionSuccessDialogOpen] = useState(false);
|
||||
@@ -203,11 +206,45 @@ export default function OrderDetailPage() {
|
||||
router.push(`/sales/order-management-sales/${orderId}?mode=edit`);
|
||||
};
|
||||
|
||||
const handleProductionOrder = () => {
|
||||
// 부서 트리를 평탄화
|
||||
const flattenDepartments = (depts: DepartmentRecord[]): DepartmentRecord[] => {
|
||||
const result: DepartmentRecord[] = [];
|
||||
const traverse = (list: DepartmentRecord[]) => {
|
||||
for (const dept of list) {
|
||||
result.push(dept);
|
||||
if (dept.children?.length) traverse(dept.children);
|
||||
}
|
||||
};
|
||||
traverse(depts);
|
||||
return result;
|
||||
};
|
||||
|
||||
const handleProductionOrder = async () => {
|
||||
// 생산지시 생성 모달 열기
|
||||
setProductionPriority("normal");
|
||||
setProductionMemo("");
|
||||
setProductionDepartmentId("");
|
||||
setIsProductionDialogOpen(true);
|
||||
|
||||
// 부서 목록 로드
|
||||
if (departments.length === 0) {
|
||||
const result = await getDepartmentTree();
|
||||
if (result.success && result.data) {
|
||||
const flatList = flattenDepartments(result.data);
|
||||
setDepartments(flatList);
|
||||
// 디폴트: 생산부서
|
||||
const defaultDept = flatList.find(d => d.name.includes("생산"));
|
||||
if (defaultDept) {
|
||||
setProductionDepartmentId(String(defaultDept.id));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 이미 로드된 경우 디폴트 설정
|
||||
const defaultDept = departments.find(d => d.name.includes("생산"));
|
||||
if (defaultDept) {
|
||||
setProductionDepartmentId(String(defaultDept.id));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 생산지시 확정 처리
|
||||
@@ -217,6 +254,7 @@ export default function OrderDetailPage() {
|
||||
try {
|
||||
const result = await createProductionOrder(order.id, {
|
||||
priority: productionPriority,
|
||||
departmentId: productionDepartmentId ? Number(productionDepartmentId) : undefined,
|
||||
memo: productionMemo || undefined,
|
||||
});
|
||||
if (result.success && result.data) {
|
||||
@@ -470,7 +508,7 @@ export default function OrderDetailPage() {
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
<InfoItem label="로트번호" value={order.lotNumber} />
|
||||
<InfoItem label="접수일" value={order.orderDate} />
|
||||
<InfoItem label="수주일" value={order.orderDate} />
|
||||
<InfoItem label="수주처" value={order.client} />
|
||||
<InfoItem label="현장명" value={order.siteName} />
|
||||
<InfoItem label="담당자" value={order.manager} />
|
||||
@@ -1255,6 +1293,26 @@ export default function OrderDetailPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 부서 */}
|
||||
<div className="space-y-2">
|
||||
<Label className="text-base font-medium">부서</Label>
|
||||
<Select
|
||||
value={productionDepartmentId}
|
||||
onValueChange={setProductionDepartmentId}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="부서를 선택하세요" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{departments.map((dept) => (
|
||||
<SelectItem key={dept.id} value={String(dept.id)}>
|
||||
{dept.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 비고 */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="productionMemo">비고</Label>
|
||||
|
||||
@@ -478,7 +478,7 @@ function OrderListContent() {
|
||||
{ key: "lotNumber", label: "로트번호", className: "px-2" },
|
||||
{ key: "siteName", label: "현장명", className: "px-2" },
|
||||
{ key: "expectedShipDate", label: "출고예정일", className: "px-2" },
|
||||
{ key: "orderDate", label: "접수일", className: "px-2" },
|
||||
{ key: "orderDate", label: "수주일", className: "px-2" },
|
||||
{ key: "client", label: "수주처", className: "px-2" },
|
||||
{ key: "productName", label: "제품명", className: "px-2" },
|
||||
{ key: "receiver", label: "수신자", className: "px-2" },
|
||||
|
||||
Reference in New Issue
Block a user