-
2일차
-
+
+
2일차: 로트추적
+
- {day2Percentage}%
+ {day2Progress.completed}/{day2Progress.total}
diff --git a/src/app/[locale]/(protected)/quality/qms/components/RouteList.tsx b/src/app/[locale]/(protected)/quality/qms/components/RouteList.tsx
index 32b373cc..e436d620 100644
--- a/src/app/[locale]/(protected)/quality/qms/components/RouteList.tsx
+++ b/src/app/[locale]/(protected)/quality/qms/components/RouteList.tsx
@@ -1,17 +1,19 @@
"use client";
import React, { useState } from 'react';
-import { ChevronDown, ChevronUp, MapPin } from 'lucide-react';
+import { ChevronDown, ChevronUp, MapPin, Check } from 'lucide-react';
import { RouteItem } from '../types';
+import { cn } from '@/lib/utils';
interface RouteListProps {
routes: RouteItem[];
selectedId: string | null;
onSelect: (route: RouteItem) => void;
+ onToggleItem: (routeId: string, itemId: string, isCompleted: boolean) => void;
reportCode: string | null;
}
-export const RouteList = ({ routes, selectedId, onSelect, reportCode }: RouteListProps) => {
+export const RouteList = ({ routes, selectedId, onSelect, onToggleItem, reportCode }: RouteListProps) => {
const [expandedId, setExpandedId] = useState
(null);
const handleClick = (route: RouteItem) => {
@@ -19,6 +21,11 @@ export const RouteList = ({ routes, selectedId, onSelect, reportCode }: RouteLis
setExpandedId(expandedId === route.id ? null : route.id);
};
+ const handleToggle = (e: React.MouseEvent, routeId: string, itemId: string, currentState: boolean) => {
+ e.stopPropagation();
+ onToggleItem(routeId, itemId, !currentState);
+ };
+
return (
@@ -37,6 +44,8 @@ export const RouteList = ({ routes, selectedId, onSelect, reportCode }: RouteLis
routes.map((route) => {
const isSelected = selectedId === route.id;
const isExpanded = expandedId === route.id;
+ const completedCount = route.subItems.filter(item => item.isCompleted).length;
+ const totalCount = route.subItems.length;
return (
@@ -52,6 +61,16 @@ export const RouteList = ({ routes, selectedId, onSelect, reportCode }: RouteLis
{route.code}
+ {totalCount > 0 && (
+
+ {completedCount}/{totalCount}
+
+ )}
수주일: {route.date}
현장: {route.site}
@@ -75,23 +94,35 @@ export const RouteList = ({ routes, selectedId, onSelect, reportCode }: RouteLis
{route.subItems.map((item) => (
-
-
{item.name}
+
+
+ {item.name}
+
{item.location}
-
handleToggle(e, route.id, item.id, item.isCompleted)}
+ className={cn(
+ "flex items-center gap-1 text-[10px] font-bold px-2 py-1 rounded border transition-all",
+ item.isCompleted
+ ? "text-green-600 border-green-300 bg-green-100 hover:bg-green-200"
+ : "text-gray-500 border-gray-300 bg-white hover:bg-blue-50 hover:text-blue-600 hover:border-blue-300"
+ )}
>
- {item.status}
-
+
+ {item.isCompleted ? '완료' : '확인'}
+
))}
diff --git a/src/app/[locale]/(protected)/quality/qms/mockData.ts b/src/app/[locale]/(protected)/quality/qms/mockData.ts
index c0a2b985..cd98a852 100644
--- a/src/app/[locale]/(protected)/quality/qms/mockData.ts
+++ b/src/app/[locale]/(protected)/quality/qms/mockData.ts
@@ -146,7 +146,7 @@ export const MOCK_REPORTS: InspectionReport[] = [
];
// 수주루트 목록 (reportId로 연결)
-export const MOCK_ROUTES: Record
= {
+export const MOCK_ROUTES_INITIAL: Record = {
'1': [
{
id: '1-1',
@@ -155,13 +155,13 @@ export const MOCK_ROUTES: Record = {
site: '강남 아파트 A동',
locationCount: 7,
subItems: [
- { id: '1-1-1', name: 'KD-SS-240924-19-01', location: '101동 501호', status: '합격' },
- { id: '1-1-2', name: 'KD-SS-240924-19-02', location: '101동 502호', status: '합격' },
- { id: '1-1-3', name: 'KD-SS-240924-19-03', location: '101동 503호', status: '합격' },
- { id: '1-1-4', name: 'KD-SS-240924-19-04', location: '101동 601호', status: '합격' },
- { id: '1-1-5', name: 'KD-SS-240924-19-05', location: '101동 602호', status: '합격' },
- { id: '1-1-6', name: 'KD-SS-240924-19-06', location: '101동 603호', status: '합격' },
- { id: '1-1-7', name: 'KD-SS-240924-19-07', location: '102동 501호', status: '합격' },
+ { id: '1-1-1', name: 'KD-SS-240924-19-01', location: '101동 501호', isCompleted: true },
+ { id: '1-1-2', name: 'KD-SS-240924-19-02', location: '101동 502호', isCompleted: true },
+ { id: '1-1-3', name: 'KD-SS-240924-19-03', location: '101동 503호', isCompleted: true },
+ { id: '1-1-4', name: 'KD-SS-240924-19-04', location: '101동 601호', isCompleted: true },
+ { id: '1-1-5', name: 'KD-SS-240924-19-05', location: '101동 602호', isCompleted: true },
+ { id: '1-1-6', name: 'KD-SS-240924-19-06', location: '101동 603호', isCompleted: false },
+ { id: '1-1-7', name: 'KD-SS-240924-19-07', location: '102동 501호', isCompleted: false },
],
},
{
@@ -171,8 +171,8 @@ export const MOCK_ROUTES: Record = {
site: '강남 아파트 B동',
locationCount: 7,
subItems: [
- { id: '1-2-1', name: 'KD-SS-241024-15-01', location: '103동 501호', status: '합격' },
- { id: '1-2-2', name: 'KD-SS-241024-15-02', location: '103동 502호', status: '대기' },
+ { id: '1-2-1', name: 'KD-SS-241024-15-01', location: '103동 501호', isCompleted: true },
+ { id: '1-2-2', name: 'KD-SS-241024-15-02', location: '103동 502호', isCompleted: false },
],
},
],
@@ -184,8 +184,8 @@ export const MOCK_ROUTES: Record = {
site: '서초 오피스텔 본관',
locationCount: 8,
subItems: [
- { id: '2-1-1', name: 'SC-AP-241101-01-01', location: '1층 로비', status: '합격' },
- { id: '2-1-2', name: 'SC-AP-241101-01-02', location: '2층 사무실', status: '합격' },
+ { id: '2-1-1', name: 'SC-AP-241101-01-01', location: '1층 로비', isCompleted: true },
+ { id: '2-1-2', name: 'SC-AP-241101-01-02', location: '2층 사무실', isCompleted: false },
],
},
],
@@ -197,7 +197,7 @@ export const MOCK_ROUTES: Record = {
site: '송파 주상복합 A타워',
locationCount: 10,
subItems: [
- { id: '3-1-1', name: 'SP-CW-240801-01-01', location: '1층 외벽', status: '합격' },
+ { id: '3-1-1', name: 'SP-CW-240801-01-01', location: '1층 외벽', isCompleted: false },
],
},
{
diff --git a/src/app/[locale]/(protected)/quality/qms/page.tsx b/src/app/[locale]/(protected)/quality/qms/page.tsx
index ad1bda51..3f6ba24a 100644
--- a/src/app/[locale]/(protected)/quality/qms/page.tsx
+++ b/src/app/[locale]/(protected)/quality/qms/page.tsx
@@ -15,7 +15,7 @@ import { AuditSettingsPanel, SettingsButton, type AuditDisplaySettings } from '.
import { InspectionReport, RouteItem, Document, DocumentItem, ChecklistCategory } from './types';
import {
MOCK_REPORTS,
- MOCK_ROUTES,
+ MOCK_ROUTES_INITIAL,
MOCK_DOCUMENTS,
DEFAULT_DOCUMENTS,
MOCK_DAY1_CATEGORIES,
@@ -55,6 +55,9 @@ export default function QualityInspectionPage() {
const [selectedReport, setSelectedReport] = useState(null);
const [selectedRoute, setSelectedRoute] = useState(null);
+ // 2일차 루트 데이터 상태 (완료 토글용)
+ const [routesData, setRoutesData] = useState>(MOCK_ROUTES_INITIAL);
+
// 모달 상태
const [modalOpen, setModalOpen] = useState(false);
const [selectedDoc, setSelectedDoc] = useState(null);
@@ -70,20 +73,20 @@ export default function QualityInspectionPage() {
return { completed, total };
}, [day1Categories]);
- // ===== 2일차 진행률 계산 (로트 추적 완료 기준) =====
+ // ===== 2일차 진행률 계산 (개소별 완료 기준) =====
const day2Progress = useMemo(() => {
let completed = 0;
let total = 0;
- Object.values(MOCK_ROUTES).forEach(routes => {
+ Object.values(routesData).forEach(routes => {
routes.forEach(route => {
- total++;
- const allPassed = route.subItems.length > 0 &&
- route.subItems.every(item => item.status === '합격');
- if (allPassed) completed++;
+ route.subItems.forEach(item => {
+ total++;
+ if (item.isCompleted) completed++;
+ });
});
});
return { completed, total };
- }, []);
+ }, [routesData]);
// ===== 1일차 필터링된 카테고리 (완료 항목 숨기기 옵션) =====
const filteredDay1Categories = useMemo(() => {
@@ -165,8 +168,8 @@ export default function QualityInspectionPage() {
const currentRoutes = useMemo(() => {
if (!selectedReport) return [];
- return MOCK_ROUTES[selectedReport.id] || [];
- }, [selectedReport]);
+ return routesData[selectedReport.id] || [];
+ }, [selectedReport, routesData]);
const currentDocuments = useMemo(() => {
if (!selectedRoute) return DEFAULT_DOCUMENTS;
@@ -204,6 +207,26 @@ export default function QualityInspectionPage() {
setSearchTerm(term);
};
+ // ===== 2일차 개소별 완료 토글 =====
+ const handleToggleItem = useCallback((routeId: string, itemId: string, isCompleted: boolean) => {
+ setRoutesData(prev => {
+ const newData = { ...prev };
+ for (const reportId of Object.keys(newData)) {
+ newData[reportId] = newData[reportId].map(route => {
+ if (route.id !== routeId) return route;
+ return {
+ ...route,
+ subItems: route.subItems.map(item => {
+ if (item.id !== itemId) return item;
+ return { ...item, isCompleted };
+ }),
+ };
+ });
+ }
+ return newData;
+ });
+ }, []);
+
return (
{/* 헤더 (설정 버튼 포함) */}
@@ -316,6 +339,7 @@ export default function QualityInspectionPage() {
routes={currentRoutes}
selectedId={selectedRoute?.id || null}
onSelect={handleRouteSelect}
+ onToggleItem={handleToggleItem}
reportCode={selectedReport?.code || null}
/>
diff --git a/src/app/[locale]/(protected)/quality/qms/types.ts b/src/app/[locale]/(protected)/quality/qms/types.ts
index a8fabc3f..d3da2363 100644
--- a/src/app/[locale]/(protected)/quality/qms/types.ts
+++ b/src/app/[locale]/(protected)/quality/qms/types.ts
@@ -23,7 +23,7 @@ export interface UnitInspection {
id: string;
name: string; // e.g., KD-SS-240924-19-01
location: string; // e.g., 101동 501호
- status: '합격' | '불합격' | '대기';
+ isCompleted: boolean; // 확인 완료 여부
}
export interface Document {