"use client";

import { useRouter } from "next/navigation";
import { usePopCompanyPath } from "@/hooks/usePopCompanyPath";
import React, { useEffect, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import { useAuth } from "@/hooks/useAuth";
import { apiClient } from "@/lib/api/client";
import { dataApi } from "@/lib/api/data";
import { ConfirmModal } from "../common/ConfirmModal";
import { COLOR_MAP } from "../common/theme";
import { AcceptProcessModal } from "./AcceptProcessModal";
import { ProcessDetailModal, type ProcessStep } from "./ProcessDetailModal";
import {
	isReworkProcess,
	type WorkOrderProcessView,
} from "./types";

const POP_NEW_PROD_STATE_KEY = "pop-new-production-process-state";

/** 같은 batch_id를 가진 마스터 공정(virtual split 제외)만 반환 */
function getSameBatchMasters(
	processes: WorkOrderProcess[],
	batchId: string | null | undefined,
): WorkOrderProcess[] {
	return processes.filter(
		(p) =>
			!p.parent_process_id &&
			((!batchId && !p.batch_id) || (batchId && p.batch_id === batchId)),
	);
}

/* 텍스트가 넘칠 때 자동 슬라이드 (마키) */
function AutoScrollText({
	children,
	className,
}: {
	children: React.ReactNode;
	className?: string;
}) {
	const outerRef = useRef<HTMLDivElement>(null);
	const innerRef = useRef<HTMLSpanElement>(null);
	const [needsScroll, setNeedsScroll] = useState(false);

	useEffect(() => {
		const check = () => {
			if (outerRef.current && innerRef.current) {
				setNeedsScroll(
					innerRef.current.scrollWidth > outerRef.current.clientWidth + 2,
				);
			}
		};
		check();
		window.addEventListener("resize", check);
		return () => window.removeEventListener("resize", check);
	}, [children]);

	return (
		<div
			ref={outerRef}
			className={`overflow-hidden whitespace-nowrap ${className || ""}`}
		>
			<span
				ref={innerRef}
				className="inline-block"
				style={
					needsScroll
						? { animation: "cardMarquee 8s linear infinite" }
						: undefined
				}
			>
				{children}
				{needsScroll && <span className="inline-block w-12" />}
				{needsScroll && children}
			</span>
			{needsScroll && (
				<style>{`@keyframes cardMarquee { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }`}</style>
			)}
		</div>
	);
}

/* ------------------------------------------------------------------ */
/* Types                                                               */
/* ------------------------------------------------------------------ */

interface WorkInstruction {
	id: string;
	work_instruction_no: string;
	item_id: string;
	item_name: string;
	item_code: string;
	item_number: string;
	qty: number;
	completed_qty: number;
	status: string;
	progress_status: string;
	routing: string | null;
	start_date: string;
	end_date: string;
	equipment_id: string;
	work_team: string;
	worker: string;
}

/** Phase D: 정규화 View 재export (기존 타입명 유지) */
type WorkOrderProcess = WorkOrderProcessView;

interface ProcessMng {
	id: string;
	process_code: string;
	process_name: string;
}

interface EquipmentMng {
	id: string;
	equipment_code: string;
	equipment_name: string;
}

type TabFilter = "all" | "acceptable" | "in_progress" | "waiting" | "completed";

/* ------------------------------------------------------------------ */
/* Constants                                                           */
/* ------------------------------------------------------------------ */

const TABS: {
	key: TabFilter;
	label: string;
	color: string;
	bgActive: string;
}[] = [
	{
		key: "acceptable",
		label: "접수가능",
		color: "text-amber-700",
		bgActive: "bg-amber-50",
	},
	{
		key: "in_progress",
		label: "진행중",
		color: "text-blue-700",
		bgActive: "bg-blue-50",
	},
	{
		key: "waiting",
		label: "대기",
		color: "text-gray-500",
		bgActive: "bg-gray-50",
	},
	{
		key: "completed",
		label: "완료",
		color: "text-green-700",
		bgActive: "bg-green-50",
	},
];

const STATUS_BADGE: Record<
	string,
	{ bg: string; text: string; label: string; dot: string; prefix: string }
> = {
	acceptable: {
		bg: "bg-amber-100",
		text: "text-amber-700",
		label: "접수가능",
		dot: "bg-amber-500",
		prefix: "",
	},
	waiting: {
		bg: "bg-gray-100",
		text: "text-gray-500",
		label: "대기",
		dot: "bg-gray-400",
		prefix: "",
	},
	in_progress: {
		bg: "bg-blue-100",
		text: "text-blue-700",
		label: "진행중",
		dot: "bg-blue-500",
		prefix: "\u25CF ",
	},
	completed: {
		bg: "bg-green-100",
		text: "text-green-700",
		label: "완료",
		dot: "bg-green-500",
		prefix: "\u2713 ",
	},
};

const BORDER_LEFT_COLOR: Record<string, string> = {
	acceptable: "border-l-amber-400",
	in_progress: "border-l-blue-500",
	waiting: "border-l-gray-300",
	completed: "border-l-green-500",
};

const COLS_GRID_CLASS: Record<number, string> = {
	1: "grid-cols-1",
	2: "grid-cols-1 sm:grid-cols-2",
	3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
};

/* ------------------------------------------------------------------ */
/* Compressed Process Steps (center-aligned)                           */
/* ------------------------------------------------------------------ */

function CompressedProcessSteps({
	processes,
	currentSeqNo,
	status,
	onClick,
	batchId,
	allProcesses,
}: {
	processes: WorkOrderProcess[];
	currentSeqNo: number;
	status: string;
	onClick?: () => void;
	batchId?: string;
	allProcesses?: WorkOrderProcess[];
}) {
	const sorted = getSameBatchMasters(processes, batchId).sort(
		(a, b) => a.seq_no - b.seq_no,
	);

	if (sorted.length === 0) return null;

	const currentIdx = sorted.findIndex((p) => p.seq_no === currentSeqNo);
	if (currentIdx < 0) return null;

	// For completed status: batch_id 기반 진행률 표시
	if (status === "completed") {
		// 각 마스터 seq에 최소 1건 confirmed accepted_result가 있으면 완료로 판정
		let maxCompletedSeq = currentSeqNo; // 최소한 현재 seq까지는 완료

		if (allProcesses) {
			for (const m of sorted) {
				const hasConfirmed = allProcesses.some(
					(p) =>
						p.parent_process_id === m.id &&
						p.result_status === "confirmed",
				);
				if (hasConfirmed && m.seq_no > maxCompletedSeq) {
					maxCompletedSeq = m.seq_no;
				}
			}
		}

		const completedCount = sorted.filter(
			(p) => p.seq_no <= maxCompletedSeq,
		).length;
		const allDone = completedCount === sorted.length;

		return (
			<div
				className="flex items-center justify-center gap-0.5 mb-3 py-2 px-3 bg-green-50 rounded-xl cursor-pointer hover:bg-green-100 transition"
				onClick={(e) => {
					e.stopPropagation();
					onClick?.();
				}}
			>
				{sorted.map((proc, idx) => {
					const seqNum = proc.seq_no;
					const isDone = seqNum <= maxCompletedSeq;
					return (
						<React.Fragment key={proc.id}>
							{idx > 0 && (
								<span
									className={`w-3 h-0.5 ${isDone ? "bg-green-400" : "bg-gray-300"} shrink-0`}
								/>
							)}
							<span
								className={`rounded-full flex items-center justify-center shrink-0 text-[9px] font-bold ${
									isDone
										? "bg-green-500 text-white"
										: "bg-gray-200 text-gray-400"
								}`}
								style={{
									width: idx === currentIdx ? 28 : 24,
									height: idx === currentIdx ? 28 : 24,
									fontSize: idx === currentIdx ? 10 : 9,
								}}
							>
								{isDone ? "\u2713" : idx + 1}
							</span>
						</React.Fragment>
					);
				})}
				<span className="text-[10px] text-green-600 font-bold ml-2">
					{allDone ? "전체 완료" : `${completedCount}/${sorted.length} 완료`}
				</span>
			</div>
		);
	}

	// Compressed view: show prev, current, next + collapsed counts
	const prevIdx = currentIdx - 1;
	const nextIdx = currentIdx + 1;
	const beforeCollapsed = prevIdx > 0 ? prevIdx : 0;
	const afterCollapsed =
		sorted.length - 1 - (nextIdx < sorted.length ? nextIdx : currentIdx);

	// 공정명 축약 (4글자 초과 시 잘라내기)
	const shortName = (name: string) => {
		if (!name) return "?";
		const clean = name.replace(/^제조반_/, "");
		return clean.length > 4 ? clean.slice(0, 4) : clean;
	};

	const stepLabel = (proc: WorkOrderProcess, isCurrent: boolean) => {
		const isCompleted = proc.status === "completed";
		const isInProgress =
			proc.status === "in_progress" || proc.status === "acceptable";

		if (isCurrent) {
			const bgColor = isInProgress
				? "bg-blue-500"
				: isCompleted
					? "bg-green-500"
					: "bg-amber-400";
			const ringColor = isInProgress
				? "ring-blue-200"
				: isCompleted
					? "ring-green-200"
					: "ring-amber-200";
			return (
				<span
					className={`rounded-lg ${bgColor} text-white ring-2 ${ringColor} flex items-center justify-center shrink-0 font-bold px-3 ${isInProgress ? "animate-pulse" : ""}`}
					style={{ height: 36, fontSize: 14, whiteSpace: "nowrap" }}
				>
					{shortName(proc.process_name)}
				</span>
			);
		}

		if (isCompleted) {
			return (
				<span
					className="rounded-lg bg-green-100 text-green-700 border border-green-400 flex items-center justify-center shrink-0 font-bold px-2.5"
					style={{ height: 32, fontSize: 13, whiteSpace: "nowrap" }}
				>
					{shortName(proc.process_name)}
				</span>
			);
		}

		return (
			<span
				className="rounded-lg bg-gray-100 text-gray-500 border border-gray-300 flex items-center justify-center shrink-0 font-bold px-2.5"
				style={{ height: 32, fontSize: 13, whiteSpace: "nowrap" }}
			>
				{shortName(proc.process_name)}
			</span>
		);
	};

	const lineBetween = (
		fromProc: WorkOrderProcess,
		toProc: WorkOrderProcess,
	) => {
		const fromDone = fromProc.status === "completed";
		const toDone = toProc.status === "completed";
		const color = fromDone || toDone ? "bg-green-400" : "bg-gray-200";
		return <span className={`w-3 h-0.5 ${color} shrink-0`} />;
	};

	// 모든 상태에서 클릭 가능 (공정 상세 모달)
	const isClickable = !!onClick;

	return (
		<div
			className={`flex items-center justify-center gap-1 mb-3 py-3 px-4 bg-gray-50 rounded-xl transition ${
				isClickable ? "cursor-pointer hover:bg-gray-100" : ""
			}`}
			onClick={
				isClickable
					? (e) => {
							e.stopPropagation();
							onClick?.();
						}
					: undefined
			}
		>
			{/* Collapsed before */}
			{beforeCollapsed > 0 && (
				<>
					<span
						className="rounded-lg bg-green-100 text-green-700 border border-green-300 flex items-center justify-center shrink-0 text-xs font-bold px-2"
						style={{ height: 32 }}
					>
						+{beforeCollapsed}
					</span>
					<span className="w-3 h-0.5 bg-green-400 shrink-0" />
				</>
			)}

			{/* Previous step */}
			{prevIdx >= 0 && (
				<>
					{stepLabel(sorted[prevIdx], false)}
					<span className="w-3 h-0.5 bg-gray-300 shrink-0" />
				</>
			)}

			{/* Current step */}
			{stepLabel(sorted[currentIdx], true)}

			{/* Next step */}
			{nextIdx < sorted.length && (
				<>
					<span className="w-3 h-0.5 bg-gray-300 shrink-0" />
					{stepLabel(sorted[nextIdx], false)}
				</>
			)}

			{/* Collapsed after */}
			{afterCollapsed > 0 && (
				<>
					<span className="w-3 h-0.5 bg-gray-200 shrink-0" />
					<span
						className="rounded-lg bg-gray-200 text-gray-500 flex items-center justify-center shrink-0 text-xs font-bold px-2"
						style={{ height: 32 }}
					>
						+{afterCollapsed}
					</span>
				</>
			)}

			{/* Chevron for clickable */}
			{isClickable && (
				<svg
					className="w-4 h-4 text-gray-400 ml-2 shrink-0"
					fill="none"
					stroke="currentColor"
					strokeWidth={2}
					viewBox="0 0 24 24"
				>
					<path
						strokeLinecap="round"
						strokeLinejoin="round"
						d="M8.25 4.5l7.5 7.5-7.5 7.5"
					/>
				</svg>
			)}
		</div>
	);
}

/* ------------------------------------------------------------------ */
/* Card sub-components by status                                       */
/* ------------------------------------------------------------------ */

/** Acceptable card body */
function AcceptableCardBody({
	planQty,
	prevGoodQty,
	availableQty,
	reworkAvailableQty,
}: {
	planQty: number;
	prevGoodQty: number | null;
	availableQty: number;
	reworkAvailableQty?: number;
}) {
	return (
		<>
			<div className="grid grid-cols-3 gap-2 mb-3">
				<div className="text-center bg-gray-50 rounded-xl py-4">
					<div className="text-sm text-gray-400">지시수량</div>
					<div className="text-3xl font-extrabold text-gray-900">
						{planQty.toLocaleString()}
					</div>
				</div>
				<div className="text-center bg-emerald-50 rounded-xl py-4">
					<div className="text-sm text-emerald-500">전공정양품</div>
					<div className="text-3xl font-extrabold text-emerald-600">
						{prevGoodQty !== null ? prevGoodQty.toLocaleString() : "-"}
					</div>
				</div>
				<div className="text-center bg-blue-50 rounded-xl py-4">
					<div className="text-sm text-blue-500">접수가능</div>
					<div
						className="font-extrabold text-blue-600"
						style={{ fontSize: 28 }}
					>
						{availableQty.toLocaleString()}
					</div>
				</div>
			</div>
			{reworkAvailableQty && reworkAvailableQty > 0 ? (
				reworkAvailableQty >= availableQty ? null : (
					<div className="flex items-center gap-2 mb-3 px-3 py-2 bg-amber-50 border border-amber-200 rounded-xl">
						<span className="text-xs font-bold text-white bg-amber-500 px-2 py-0.5 rounded-full">
							리워크
						</span>
						<span className="text-xs text-amber-700 font-medium">
							재작업 물량 {reworkAvailableQty}개 포함
						</span>
					</div>
				)
			) : null}
		</>
	);
}

/** In-progress card body */
function InProgressCardBody({
	inputQty,
	goodQty,
	defectQty,
	remainQty,
	progressPct,
	additionalAvailable,
}: {
	inputQty: number;
	goodQty: number;
	defectQty: number;
	remainQty: number;
	progressPct: number;
	additionalAvailable: number;
}) {
	return (
		<>
			{/* Progress bar */}
			<div className="flex items-center gap-2 mb-3">
				<div className="h-2.5 flex-1 bg-gray-100 rounded-full overflow-hidden">
					<div
						className="h-full bg-blue-500 rounded-full transition-all"
						style={{ width: `${progressPct}%` }}
					/>
				</div>
				<span className="text-xs font-bold text-blue-600">{progressPct}%</span>
			</div>

			{/* 4-col qty grid */}
			<div className="grid grid-cols-4 gap-1.5 mb-2">
				<div className="text-center bg-blue-50 rounded-xl py-3">
					<div className="text-sm text-blue-500">접수</div>
					<div className="text-3xl font-extrabold text-blue-700">
						{inputQty.toLocaleString()}
					</div>
				</div>
				<div className="text-center bg-emerald-50 rounded-xl py-3">
					<div className="text-sm text-emerald-500">양품</div>
					<div className="text-3xl font-extrabold text-emerald-700">
						{goodQty.toLocaleString()}
					</div>
				</div>
				<div className="text-center bg-red-50 rounded-xl py-3">
					<div className="text-sm text-red-500">불량</div>
					<div className="text-3xl font-extrabold text-red-700">
						{defectQty.toLocaleString()}
					</div>
				</div>
				<div className="text-center bg-amber-50 rounded-xl py-3">
					<div className="text-sm text-amber-500">잔여</div>
					<div className="text-3xl font-extrabold text-amber-700">
						{remainQty.toLocaleString()}
					</div>
				</div>
			</div>
			{additionalAvailable > 0 && (
				<div className="text-right text-sm text-violet-600 font-semibold">
					추가접수가능 {additionalAvailable.toLocaleString()}
				</div>
			)}
		</>
	);
}

/** Waiting card body */
function WaitingCardBody({
	planQty,
	prevProcessName,
	prevProgressPct,
	currentProcessName,
	currentSeqNo,
}: {
	planQty: number;
	prevProcessName: string | null;
	prevProgressPct: number | null;
	currentProcessName: string;
	currentSeqNo: number;
}) {
	return (
		<>
			<div className="grid grid-cols-3 gap-2 mb-3">
				<div className="text-center bg-gray-50 rounded-xl py-3">
					<div className="text-sm text-gray-400">지시수량</div>
					<div className="text-3xl font-extrabold text-gray-900">
						{planQty.toLocaleString()}
					</div>
				</div>
				<div className="text-center bg-blue-50 rounded-xl py-3">
					<div className="text-sm text-blue-500">이전공정</div>
					<div className="text-base font-bold text-blue-600 truncate">
						{prevProcessName || "-"}
					</div>
					{prevProgressPct !== null && (
						<div className="text-xs text-blue-400 mt-1">
							진행중 ({prevProgressPct}%)
						</div>
					)}
				</div>
				<div className="text-center bg-gray-50 rounded-xl py-3">
					<div className="text-sm text-gray-400">현재공정</div>
					<div className="text-base font-bold text-gray-600 truncate">
						{currentProcessName}
					</div>
					<div className="text-xs text-gray-400 mt-1">
						{currentSeqNo}번째 공정
					</div>
				</div>
			</div>
			<div className="text-center text-sm text-gray-400">
				이전공정 완료 시 접수 가능합니다
			</div>
		</>
	);
}

/** Completed card body */
function CompletedCardBody({
	goodQty,
	defectQty,
	planQty,
	inputQty,
}: {
	goodQty: number;
	defectQty: number;
	planQty: number;
	inputQty: number;
}) {
	return (
		<div className="grid grid-cols-4 gap-1.5">
			<div className="text-center bg-emerald-50 rounded-xl py-3">
				<div className="text-sm text-emerald-500">양품</div>
				<div className="text-3xl font-extrabold text-emerald-700">
					{goodQty.toLocaleString()}
				</div>
			</div>
			<div className="text-center bg-red-50 rounded-xl py-3">
				<div className="text-sm text-red-500">불량</div>
				<div className="text-3xl font-extrabold text-red-700">
					{defectQty.toLocaleString()}
				</div>
			</div>
			<div className="text-center bg-gray-50 rounded-xl py-3">
				<div className="text-sm text-gray-400">지시</div>
				<div className="text-3xl font-extrabold text-gray-600">
					{planQty.toLocaleString()}
				</div>
			</div>
			<div className="text-center bg-blue-50 rounded-xl py-3">
				<div className="text-sm text-blue-500">접수</div>
				<div className="text-3xl font-extrabold text-blue-700">
					{inputQty.toLocaleString()}
				</div>
			</div>
		</div>
	);
}

/** Rework card body */
function ReworkCardBody({
	reworkQty,
	availableQty,
	originProcessName,
	originProcessCode,
	originDefectQty,
	reworkRound,
}: {
	reworkQty: number;
	availableQty: number;
	originProcessName: string;
	originProcessCode: string;
	originDefectQty: number;
	reworkRound: number;
}) {
	return (
		<>
			{/* Rework info summary */}
			<div className="mb-3 p-2.5 bg-orange-50 border border-orange-200 rounded-xl">
				<div className="flex items-center gap-1.5 text-sm font-semibold text-orange-700">
					<span>{originProcessName || originProcessCode}</span>
					<span className="text-orange-400">&rarr;</span>
					<span>불량 {originDefectQty}개</span>
					<span className="text-orange-400">&rarr;</span>
					<span className="bg-orange-500 text-white text-xs px-1.5 py-0.5 rounded font-bold">
						재작업 {reworkRound}회차
					</span>
				</div>
			</div>

			<div className="grid grid-cols-2 gap-3 mb-3">
				<div className="text-center bg-orange-50 rounded-xl py-4">
					<div className="text-sm text-orange-500">재작업수량</div>
					<div
						className="font-extrabold text-orange-600"
						style={{ fontSize: 32 }}
					>
						{reworkQty.toLocaleString()}
					</div>
				</div>
				<div className="text-center bg-blue-50 rounded-xl py-4">
					<div className="text-sm text-blue-500">접수가능</div>
					<div
						className="font-extrabold text-blue-600"
						style={{ fontSize: 32 }}
					>
						{availableQty.toLocaleString()}
					</div>
				</div>
			</div>
		</>
	);
}

/* ------------------------------------------------------------------ */
/* Main Component                                                      */
/* ------------------------------------------------------------------ */

/**
 * Phase A-2b: 데이터/필터/열 상태는 page.tsx의 useProcessData에서 주입.
 * 내부 fetchAll/syncAndFetch는 제거되고 refetch()로 대체 (sync 없는 재조회).
 */
export interface WorkOrderListProps {
	instructions: WorkInstruction[];
	allProcesses: WorkOrderProcess[];
	processList: ProcessMng[];
	equipmentList: EquipmentMng[];
	itemNameMap: Record<string, string>;
	itemTypeMap: Record<string, string>;
	loading: boolean;
	selectedProcess: string;
	selectedEquipment: string;
	cardCols: 1 | 2 | 3;
	refetch: () => void;
}

export function WorkOrderList(props: WorkOrderListProps) {
	const {
		instructions,
		allProcesses,
		processList,
		equipmentList,
		itemNameMap,
		itemTypeMap,
		loading,
		selectedProcess,
		selectedEquipment,
		cardCols,
		refetch,
	} = props;
	const router = useRouter();
  const companyPath = usePopCompanyPath();
	const { user } = useAuth();
	const currentUserId = user?.userId || "";

	const [activeTab, setActiveTab] = useState<TabFilter>("acceptable");

	/* Accept Modal */
	const [acceptModal, setAcceptModal] = useState<{
		open: boolean;
		processId: string;
		processName: string;
		seqNo: number;
		maxQty: number;
		reworkSourceId?: string;
	}>({ open: false, processId: "", processName: "", seqNo: 0, maxQty: 0 });
	const [acceptLoading, setAcceptLoading] = useState(false);

	const [cancelConfirm, setCancelConfirm] = useState<{
		open: boolean;
		processId: string;
	}>({ open: false, processId: "" });

	/* Process Detail Modal */
	const [detailModal, setDetailModal] = useState<{
		open: boolean;
		wiNo: string;
		totalQty: number;
		steps: ProcessStep[];
		woId?: string;
		showReworkHistory?: boolean;
	}>({ open: false, wiNo: "", totalQty: 0, steps: [] });

	// Phase A-2b: 데이터 패칭은 useProcessData가 담당 — 내부 fetchAll/syncAndFetch 제거

	/* ---- Lookup maps ---- */
	const instructionMap = useMemo(() => {
		const map: Record<string, WorkInstruction> = {};
		for (const wi of instructions) {
			map[wi.id] = wi;
		}
		return map;
	}, [instructions]);

	const processesByWo = useMemo(() => {
		const map: Record<string, WorkOrderProcess[]> = {};
		for (const proc of allProcesses) {
			if (!proc.wo_id) continue;
			if (!map[proc.wo_id]) map[proc.wo_id] = [];
			map[proc.wo_id].push(proc);
		}
		return map;
	}, [allProcesses]);

	/** 서버 응답 batch_count/batch_index를 Drawer/카드 기존 구조로 어댑팅 */
	const multiBatchInfo = useMemo(() => {
		const map: Record<string, { isMulti: boolean; index: number; total: number; itemType: string }> = {};
		for (const proc of allProcesses) {
			const total = Math.max(Number(proc.batch_count ?? 1) || 1, 1);
			const index = Number(proc.batch_index ?? 1) || 1;
			const isMulti = total > 1;
			const bid = proc.batch_id || "";
			let itemType = "";
			if (bid) itemType = itemTypeMap[bid] || "";
			if (!itemType) {
				const wi = instructionMap[proc.wo_id];
				if (wi?.item_number) itemType = itemTypeMap[wi.item_number] || "";
			}
			map[proc.id] = { isMulti, index, total, itemType };
		}
		return map;
	}, [allProcesses, itemTypeMap, instructionMap]);

	const masterProcesses = useMemo(() => {
		// 마스터 행 + 분할 행(진행중/완료/리워크) — 중복 제거
		const seen = new Set<string>();
		return allProcesses.filter((p) => {
			if (seen.has(p.id)) return false;
			const include =
				!p.parent_process_id || // 마스터 행
				p.status === "in_progress" ||
				p.status === "completed" || // 분할 행
				isReworkProcess(p); // 재작업
			if (include) seen.add(p.id);
			return include;
		});
	}, [allProcesses]);

	const equipmentMap = useMemo(() => {
		const map: Record<string, EquipmentMng> = {};
		for (const eq of equipmentList) {
			map[eq.id] = eq;
			if (eq.equipment_code) map[eq.equipment_code] = eq;
		}
		return map;
	}, [equipmentList]);

	// Phase A-5: 필터 UI는 page.tsx의 SupplierModal/EquipmentModal에서 담당.
	// WorkOrderList는 selectedProcess/selectedEquipment props만 사용해 filteredProcesses를 계산.

	/* ---- Filtered processes ---- */
	const filteredProcesses = useMemo(() => {
		if (selectedProcess === "__all__") return []; // 공정 미선택 시 빈 목록
		return masterProcesses.filter((proc) => {
			const isRework = isReworkProcess(proc);
			const isMaster = !proc.parent_process_id;
			// 완료/진행중 탭에서는 SPLIT만 표시 (마스터 제외)
			if (
				isMaster &&
				!isRework &&
				(activeTab === "completed" || activeTab === "in_progress")
			)
				return false;
			// 리워크 마스터가 in_progress/completed면 SPLIT이 생성된 것 → 리워크 마스터 숨김 (SPLIT은 표시)
			if (
				isRework &&
				!proc.parent_process_id &&
				(proc.status === "in_progress" || proc.status === "completed")
			)
				return false;
			// 재작업 카드는 공정 필터 무시 (모든 공정에서 표시)
			if (!isRework && proc.process_code !== selectedProcess) return false;
			if (selectedEquipment !== "__all__") {
				const wi = instructionMap[proc.wo_id];
				if (!wi) return false;
				const eqId = wi.equipment_id;
				const eq = equipmentMap[eqId];
				if (!eq || eq.equipment_code !== selectedEquipment) return false;
			}
			if (activeTab !== "all" && proc.status !== activeTab) return false;
			return true;
		});
	}, [
		masterProcesses,
		selectedProcess,
		selectedEquipment,
		activeTab,
		instructionMap,
		equipmentMap,
	]);

	/* ---- Tab counts ---- */
	const tabCounts = useMemo(() => {
		const preFiltered = masterProcesses.filter((proc) => {
			const isRework = isReworkProcess(proc);
			// 재작업 카드는 공정 필터 무시 (모든 공정에서 표시)
			if (
				selectedProcess !== "__all__" &&
				!isRework &&
				proc.process_code !== selectedProcess
			)
				return false;
			if (selectedEquipment !== "__all__") {
				const wi = instructionMap[proc.wo_id];
				if (!wi) return false;
				const eq = equipmentMap[wi.equipment_id];
				if (!eq || eq.equipment_code !== selectedEquipment) return false;
			}
			return true;
		});

		const counts: Record<TabFilter, number> = {
			all: preFiltered.length,
			acceptable: 0,
			in_progress: 0,
			waiting: 0,
			completed: 0,
		};
		for (const proc of preFiltered) {
			const isMaster = !proc.parent_process_id;
			const isRw = isReworkProcess(proc);
			// 리워크 마스터가 in_progress/completed면 SPLIT이 있으므로 카운트 제외
			if (
				isRw &&
				!proc.parent_process_id &&
				(proc.status === "in_progress" || proc.status === "completed")
			)
				continue;
			if (proc.status === "acceptable") counts.acceptable++;
			else if (proc.status === "in_progress" && (!isMaster || isRw))
				counts.in_progress++;
			else if (proc.status === "completed" && (!isMaster || isRw))
				counts.completed++;
			else counts.waiting++;
		}
		return counts;
	}, [
		masterProcesses,
		selectedProcess,
		selectedEquipment,
		instructionMap,
		equipmentMap,
	]);

	/* ---- Accept handler ---- */
	const openAcceptModal = async (
		processId: string,
		processName: string,
		seqNo: number,
		reworkSourceId?: string,
	) => {
		try {
			const res = await apiClient.get("/pop/production/available-qty", {
				params: { work_order_process_id: processId },
			});
			const data = res.data?.data;
			const maxQty = data?.availableQty ?? 0;
			setAcceptModal({
				open: true,
				processId,
				processName,
				seqNo,
				maxQty,
				reworkSourceId,
			});
		} catch {
			toast.error("접수가능량 조회 실패");
		}
	};

	const handleAccept = async (qty: number) => {
		setAcceptLoading(true);
		try {
			const body: Record<string, unknown> = {
				work_order_process_id: acceptModal.processId,
				accept_qty: qty,
			};
			// 리워크 추적: rework_source_id가 있으면 전달 (원점 복귀 추적)
			if (acceptModal.reworkSourceId) {
				body.rework_source_id = acceptModal.reworkSourceId;
			}
			const res = await apiClient.post("/pop/production/accept-process", body);
			if (res.data?.success) {
				setAcceptModal((m) => ({ ...m, open: false }));
				refetch();
			} else {
				toast.error(res.data?.message || "접수 실패");
			}
		} catch (error: any) {
			toast.error(error.response?.data?.message || "접수 중 오류 발생");
		} finally {
			setAcceptLoading(false);
		}
	};

	/* ---- Navigate to work route ---- */
	const goToWork = (processId: string) => {
		try {
			const existing = JSON.parse(
				sessionStorage.getItem(POP_NEW_PROD_STATE_KEY) || "{}",
			);
			sessionStorage.setItem(
				POP_NEW_PROD_STATE_KEY,
				JSON.stringify({
					...existing,
					activeTab,
					scrollY: window.scrollY,
				}),
			);
		} catch {
			/* ignore */
		}
		router.push(companyPath(`/pop/production/work/${processId}`));
	};

	/* ---- Restore activeTab + scrollY on mount ---- */
	useEffect(() => {
		try {
			const saved = sessionStorage.getItem(POP_NEW_PROD_STATE_KEY);
			if (!saved) return;
			const parsed = JSON.parse(saved);
			if (
				parsed.activeTab === "all" ||
				parsed.activeTab === "acceptable" ||
				parsed.activeTab === "in_progress" ||
				parsed.activeTab === "waiting" ||
				parsed.activeTab === "completed"
			) {
				setActiveTab(parsed.activeTab);
			}
			if (typeof parsed.scrollY === "number") {
				requestAnimationFrame(() => window.scrollTo(0, parsed.scrollY));
			}
		} catch {
			/* ignore */
		}
		// eslint-disable-next-line react-hooks/exhaustive-deps
	}, []);

	/* ---- Open process detail modal ---- */
	const openDetailModal = (proc: WorkOrderProcess) => {
		const wi = instructionMap[proc.wo_id];
		const siblings = getSameBatchMasters(
			processesByWo[proc.wo_id] || [],
			proc.batch_id,
		).sort((a, b) => a.seq_no - b.seq_no);

		const totalQty = wi ? wi.qty : proc.plan_qty;

		const steps: ProcessStep[] = siblings.map((s) => {
			const sInput = s.input_qty;
			const sGood = s.good_qty;
			const sDefect = s.defect_qty;
			const sPlan = s.plan_qty;
			// Available = plan - input (simplified)
			const avail = Math.max(0, sPlan - sInput);
			return {
				no: s.seq_no,
				name: s.process_name || s.process_code,
				code: s.process_code,
				status: s.status,
				inputQty: sInput,
				goodQty: sGood,
				defectQty: sDefect,
				planQty: sPlan,
				availableQty: avail,
			};
		});

		// Check if this wo has any rework processes
		const hasReworks = allProcesses.some(
			(p) =>
				p.wo_id === proc.wo_id &&
				isReworkProcess(p),
		);

		setDetailModal({
			open: true,
			wiNo: wi?.work_instruction_no || "작업지시",
			totalQty,
			steps,
			woId: proc.wo_id,
			showReworkHistory: hasReworks,
		});
	};

	/* ---- Helper: get previous process display info (name + progress) ---- */
	const getPrevProcessInfo = (proc: WorkOrderProcess) => {
		const siblings = getSameBatchMasters(
			processesByWo[proc.wo_id] || [],
			proc.batch_id,
		).sort((a, b) => a.seq_no - b.seq_no);

		const currentIdx = siblings.findIndex((p) => p.id === proc.id);
		if (currentIdx <= 0)
			return {
				prevProcessName: null as string | null,
				prevProgressPct: null as number | null,
			};

		const prev = siblings[currentIdx - 1];
		const prevGood = prev.good_qty;
		const prevPlan = prev.plan_qty;
		const prevPct = prevPlan > 0 ? Math.round((prevGood / prevPlan) * 100) : 0;
		return {
			prevProcessName: prev.process_name || prev.process_code,
			prevProgressPct:
				prev.status === "in_progress"
					? prevPct
					: prev.status === "completed"
						? 100
						: null,
		};
	};

	/* ---- Helper: get equipment name ---- */
	const getEquipmentName = (proc: WorkOrderProcess): string => {
		const wi = instructionMap[proc.wo_id];
		if (!wi?.equipment_id) return "미배정";
		const eq = equipmentMap[wi.equipment_id];
		return eq?.equipment_name || "미배정";
	};

	/* ---- Render ---- */
	return (
		<div className="flex flex-col gap-3">
			{/* Phase A-3/A-5: 카드 열 토글·새로고침·필터 UI는 page.tsx가 담당 */}

			{/* ===== Tab bar ===== */}
			<div className="flex gap-1.5 overflow-x-auto no-scrollbar bg-gray-100 p-1 rounded-xl">
				{TABS.map((tab) => {
					const count = tabCounts[tab.key];
					const isActive = activeTab === tab.key;
					return (
						<button
							key={tab.key}
							onClick={() => setActiveTab(tab.key)}
							className={`flex-1 min-w-[60px] py-2.5 rounded-lg text-xs font-semibold transition-all active:scale-95 flex items-center justify-center gap-1.5 ${
								isActive
									? `${tab.bgActive} ${tab.color} shadow-sm`
									: "text-gray-500 hover:text-gray-700"
							}`}
						>
							{tab.label}
							{count > 0 && (
								<span
									className={`text-[10px] min-w-[18px] h-[18px] flex items-center justify-center rounded-full px-1 ${
										isActive ? "bg-black/10" : "bg-gray-200/60"
									}`}
								>
									{count}
								</span>
							)}
						</button>
					);
				})}
			</div>

			{/* Loading */}
			{loading && (
				<div className="flex items-center justify-center py-16">
					<div className="flex flex-col items-center gap-3">
						<div className="w-8 h-8 border-3 border-blue-500 border-t-transparent rounded-full animate-spin" />
						<span className="text-sm text-gray-400">공정 조회중...</span>
					</div>
				</div>
			)}

			{/* Empty state */}
			{!loading && filteredProcesses.length === 0 && (
				<div className="flex flex-col items-center justify-center py-16 gap-3">
					<div className="w-16 h-16 rounded-full bg-gray-100 flex items-center justify-center">
						<svg
							className="w-8 h-8 text-gray-300"
							fill="none"
							stroke="currentColor"
							strokeWidth={1.5}
							viewBox="0 0 24 24"
						>
							<path
								strokeLinecap="round"
								strokeLinejoin="round"
								d="M9 12h3.75M9 15h3.75M9 18h3.75m3 .75H18a2.25 2.25 0 002.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 00-1.123-.08m-5.801 0c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75 2.25 2.25 0 00-.1-.664m-5.8 0A2.251 2.251 0 0113.5 2.25H15a2.25 2.25 0 012.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V19.5a2.25 2.25 0 002.25 2.25h.75"
							/>
						</svg>
					</div>
					<p className="text-sm text-gray-400">
						{selectedProcess === "__all__"
							? "공정을 선택해주세요"
							: `${TABS.find((t) => t.key === activeTab)?.label || ""} 상태의 공정이 없습니다`}
					</p>
				</div>
			)}

			{/* ===== Process Cards Grid ===== */}
			{!loading && filteredProcesses.length > 0 && (
				<div
					className={`grid ${COLS_GRID_CLASS[cardCols] || "grid-cols-1"} gap-4`}
				>
					{filteredProcesses
						.sort((a, b) => {
							const order: Record<string, number> = {
								acceptable: 0,
								in_progress: 1,
								waiting: 2,
								completed: 3,
							};
							const diff = (order[a.status] ?? 2) - (order[b.status] ?? 2);
							if (diff !== 0) return diff;
							return a.seq_no - b.seq_no;
						})
						.map((proc) => {
							const wi = instructionMap[proc.wo_id];
							const badge = STATUS_BADGE[proc.status] || STATUS_BADGE.waiting;
							const siblingProcesses = getSameBatchMasters(
								processesByWo[proc.wo_id] || [],
								proc.batch_id,
							);
							const planQty = proc.plan_qty;
							const goodQty = proc.good_qty;
							const defectQty = proc.defect_qty;
							const inputQty = proc.input_qty;
							const isRework = isReworkProcess(proc);
							const borderLeft = isRework
								? "border-l-orange-500"
								: BORDER_LEFT_COLOR[proc.status] || "border-l-gray-300";
							const eqName = getEquipmentName(proc);

							// Split processes (children of this master)
							const mySplits = allProcesses
								.filter((s) => s.parent_process_id === proc.id)
								.sort((a, b) => {
									const ta = a.started_at
										? new Date(a.started_at).getTime()
										: 0;
									const tb = b.started_at
										? new Date(b.started_at).getTime()
										: 0;
									return tb - ta;
								});

							const progressPct =
								planQty > 0
									? Math.min(100, Math.round((goodQty / planQty) * 100))
									: 0;
							const remainQty = Math.max(0, inputQty - goodQty - defectQty);
							const prevInfo = getPrevProcessInfo(proc);
							const prevGoodQtyNum =
								proc.prev_good_qty != null
									? Number(proc.prev_good_qty)
									: null;

							// Server-computed: available qty
							const availableQty = Number(proc.available_qty ?? 0);

							// Additional available for in_progress
							const additionalAvailable = Math.max(0, planQty - inputQty);

							// Split order label (서버 계산 — split_no / split_total)
							const splitInfo =
								proc.split_no != null &&
								proc.split_total != null &&
								Number(proc.split_total) > 1
									? {
											order: Number(proc.split_no),
											total: Number(proc.split_total),
										}
									: null;

							// 합류 불가 리워크 감지: 접수가능 물량이 전부 리워크일 때
							const reworkQtyAvail = Number(proc.rework_available_qty ?? 0);
							const normalAvail = availableQty - reworkQtyAvail;
							const isReworkOnly =
								!isRework &&
								proc.status === "acceptable" &&
								reworkQtyAvail > 0 &&
								normalAvail <= 0 &&
								availableQty > 0;

							// 리워크 표시 여부 (실제 리워크 카드 OR 합류불가 리워크)
							const showReworkBadge = isRework || isReworkOnly;

							// Rework info: origin process + rework round
							let reworkRound = 1;
							let originProcessName = proc.process_name || proc.process_code;
							let originProcessCode = proc.process_code;
							let originDefectQty = defectQty;
							if (isRework) {
								// 신 구조: 리워크 = wop_result.is_rework='Y' (virtual 카드)
								//   같은 rework_source_id 그룹 내에서 accepted_at 순 차수 계산
								const sameSource = allProcesses.filter(
									(p) =>
										p.wo_id === proc.wo_id &&
										isReworkProcess(p) &&
										p.rework_source_id === proc.rework_source_id,
								);
								const sortedReworks = [...sameSource].sort((a, b) => {
									const da = a.accepted_at
										? new Date(a.accepted_at).getTime()
										: 0;
									const db = b.accepted_at
										? new Date(b.accepted_at).getTime()
										: 0;
									return da - db || a.id.localeCompare(b.id);
								});
								const myIdx = sortedReworks.findIndex((r) => r.id === proc.id);
								reworkRound = myIdx >= 0 ? myIdx + 1 : 1;

								// Find origin (source) process — rework_source_id 는 wop_result.id
								if (proc.rework_source_id) {
									const origin = allProcesses.find(
										(p) => p.id === proc.rework_source_id,
									);
									if (origin) {
										originProcessName =
											origin.process_name || origin.process_code;
										originProcessCode = origin.process_code;
										originDefectQty = origin.defect_qty;
									}
								}
							}

							return (
								<div
									key={`${proc.id}-${proc.status}-${proc.parent_process_id || "m"}`}
									className="card-item"
								>
									<div
										className={`bg-white rounded-2xl border-l-4 ${borderLeft} border border-gray-100 shadow-sm overflow-hidden flex flex-col ${
											proc.status === "waiting" ? "opacity-75" : ""
										} ${showReworkBadge ? "border-2 border-orange-200" : ""} ${
											proc.status === "in_progress"
												? "cursor-pointer active:scale-[0.99] transition-transform"
												: ""
										}`}
										style={{ height: "100%" }}
										onClick={
											proc.status === "in_progress"
												? () =>
														goToWork(
															proc.parent_process_id
																? proc.id
																: mySplits[0]?.id || proc.id,
														)
												: undefined
										}
									>
										<div className="p-4 flex-1 flex flex-col">
											{/* Header: Work instruction number + status badge */}
											<div className="flex items-center justify-between mb-1">
												<div className="flex items-center gap-2 min-w-0">
													{showReworkBadge && (
														<span className="bg-orange-500 text-white text-[10px] font-bold px-2 py-0.5 rounded shrink-0">
															🔄 리워크
														</span>
													)}
													<h3 className="text-xl font-extrabold text-gray-900 truncate">
														{wi?.work_instruction_no || "작업지시"}
														{splitInfo && (
															<span className="text-sm font-bold text-blue-500 ml-1">
																(접수 #{splitInfo.order})
															</span>
														)}
													</h3>
												</div>
												<span
													className={`shrink-0 ml-2 text-sm font-bold px-3 py-1.5 rounded-full ${badge.bg} ${badge.text}`}
												>
													{badge.prefix}
													{badge.label}
												</span>
											</div>

											{/* 단일/다중품목 뱃지 */}
											{(() => {
												const bInfo = multiBatchInfo[proc.id];
												if (!bInfo) return null;
												const typeLabel = bInfo.itemType || "";
												return (
													<div className="flex items-center gap-1.5 mb-1">
														{bInfo.isMulti ? (
															<span className="bg-blue-100 text-blue-700 text-xs font-bold px-2 py-0.5 rounded-full">
																다중 {bInfo.index}/{bInfo.total}{typeLabel ? ` · ${typeLabel}` : ""}
															</span>
														) : (
															<span className="bg-gray-100 text-gray-600 text-xs font-bold px-2 py-0.5 rounded-full">
																단일{typeLabel ? ` · ${typeLabel}` : ""}
															</span>
														)}
													</div>
												);
											})()}

											{/* Sub-info: item name + equipment */}
											<AutoScrollText className="text-sm text-gray-500 mb-3">
												📦 {`${wi?.item_name || "품목"}${wi?.item_code || wi?.item_number ? `(${wi?.item_code || wi?.item_number})` : ""}`}
												{" · "}
												{!isRework
													? `⚙️ ${eqName}`
													: `⚙️ ${proc.process_name || proc.process_code}`}
											</AutoScrollText>

											{/* Process steps (compressed) — both normal and rework cards */}
											{siblingProcesses.length > 1 && (
												<CompressedProcessSteps
													processes={processesByWo[proc.wo_id] || []}
													currentSeqNo={proc.seq_no}
													status={proc.status}
													onClick={() => openDetailModal(proc)}
													batchId={
														proc.batch_id ?? undefined
													}
													allProcesses={allProcesses}
												/>
											)}

											{/* Status-specific body (pushed to bottom with mt-auto) */}
											<div className="mt-auto">
												{isRework ? (
													<ReworkCardBody
														reworkQty={planQty}
														availableQty={availableQty}
														originProcessName={originProcessName}
														originProcessCode={originProcessCode}
														originDefectQty={originDefectQty}
														reworkRound={reworkRound}
													/>
												) : proc.status === "acceptable" ? (
													<AcceptableCardBody
														planQty={planQty}
														prevGoodQty={prevGoodQtyNum}
														availableQty={availableQty}
														reworkAvailableQty={reworkQtyAvail}
													/>
												) : proc.status === "in_progress" ? (
													<InProgressCardBody
														inputQty={inputQty}
														goodQty={goodQty}
														defectQty={defectQty}
														remainQty={remainQty}
														progressPct={progressPct}
														additionalAvailable={additionalAvailable}
													/>
												) : proc.status === "waiting" ? (
													<WaitingCardBody
														planQty={planQty}
														prevProcessName={prevInfo.prevProcessName}
														prevProgressPct={prevInfo.prevProgressPct}
														currentProcessName={
															proc.process_name || proc.process_code
														}
														currentSeqNo={proc.seq_no}
													/>
												) : proc.status === "completed" ? (
													<CompletedCardBody
														goodQty={goodQty}
														defectQty={defectQty}
														planQty={planQty}
														inputQty={inputQty}
													/>
												) : null}
											</div>
										</div>

										{/* Action button (full width, bottom) */}
										{isRework && proc.status === "acceptable" && (
											<button
												onClick={() =>
													openAcceptModal(
														proc.id,
														proc.process_name,
														proc.seq_no,
														(proc as unknown as Record<string, unknown>)
															.rework_source_id as string | undefined,
													)
												}
												className={`w-full py-3.5 text-sm font-bold text-white active:scale-[0.98] transition-all ${COLOR_MAP.orange.buttonBg} ${COLOR_MAP.orange.buttonBgHover}`}
											>
												리워크 접수
											</button>
										)}
										{!isRework &&
											proc.status === "acceptable" &&
											availableQty > 0 && (
												<button
													onClick={() =>
														openAcceptModal(
															proc.id,
															proc.process_name,
															proc.seq_no,
														)
													}
													className={`w-full py-3.5 text-sm font-bold text-white active:scale-[0.98] transition-all ${COLOR_MAP.amber.buttonBg} ${COLOR_MAP.amber.buttonBgHover}`}
												>
													접수
												</button>
											)}
										{!isRework &&
											proc.status === "acceptable" &&
											availableQty <= 0 &&
											inputQty > 0 && (
												<div className="w-full py-3 text-sm font-bold text-center text-gray-400 bg-gray-100">
													전량 접수 완료
												</div>
											)}
										{proc.status === "in_progress" &&
											proc.total_production_qty === 0 &&
											proc.parent_process_id && (
												<button
													onClick={(e) => {
														e.stopPropagation();
														setCancelConfirm({
															open: true,
															processId: proc.id,
														});
													}}
													className="w-full py-4 text-base font-bold text-red-500 border-t-2 border-red-100 bg-red-50/50 active:scale-[0.98] transition-all"
												>
													접수 취소
												</button>
											)}
									</div>
								</div>
							);
						})}
				</div>
			)}

			{/* Phase A-5: FilterSelectorModal 인스턴스는 page.tsx의 SupplierModal/EquipmentModal로 대체 */}

			{/* Accept Process Modal */}
			<AcceptProcessModal
				open={acceptModal.open}
				onClose={() => setAcceptModal((m) => ({ ...m, open: false }))}
				onConfirm={handleAccept}
				maxQty={acceptModal.maxQty}
				processName={acceptModal.processName}
				seqNo={acceptModal.seqNo}
				loading={acceptLoading}
			/>

			{/* Process Detail Modal */}
			<ProcessDetailModal
				open={detailModal.open}
				onClose={() => setDetailModal((m) => ({ ...m, open: false }))}
				workInstructionNo={detailModal.wiNo}
				totalQty={detailModal.totalQty}
				steps={detailModal.steps}
				woId={detailModal.woId}
				showReworkHistory={detailModal.showReworkHistory}
			/>

			{/* Cancel accept confirm modal */}
			<ConfirmModal
				open={cancelConfirm.open}
				title="접수 취소"
				message="접수를 취소하시겠습니까?"
				confirmText="취소"
				cancelText="닫기"
				variant="danger"
				onConfirm={async () => {
					const pid = cancelConfirm.processId;
					setCancelConfirm({ open: false, processId: "" });
					try {
						const res = await apiClient.post("/pop/production/cancel-accept", {
							work_order_process_id: pid,
						});
						if (res.data?.success) {
							refetch();
						} else {
							toast.error(res.data?.message || "취소 실패");
						}
					} catch (err: unknown) {
						const e2 = err as { response?: { data?: { message?: string } } };
						toast.error(e2.response?.data?.message || "취소 중 오류");
					}
				}}
				onCancel={() => setCancelConfirm({ open: false, processId: "" })}
			/>
		</div>
	);
}
