"use client";

import React, { useState, useMemo, useCallback, useEffect } from "react";
import {
  BarChart3,
  Circle,
  CheckCircle2,
  AlertCircle,
  Clock,
  LayoutGrid,
  List,
  Timer,
  ChevronLeft,
  ChevronRight,
  ChevronDown,
  ChevronUp,
  Plus,
  Trash2,
  Paperclip,
  ShoppingCart,
  Handshake,
  FileText,
  Image as ImageIcon,
  Ruler,
  Box,
  FolderOpen,
  Upload,
  X,
  Pencil,
  Calendar,
  Search,
  RotateCcw,
  ClipboardList,
  FileEdit,
  Inbox,
  Loader2,
  PointerIcon,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Progress } from "@/components/ui/progress";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Slider } from "@/components/ui/slider";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter,
} from "@/components/ui/dialog";
import {
  ResizablePanelGroup,
  ResizablePanel,
  ResizableHandle,
} from "@/components/ui/resizable";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import { toast } from "sonner";
import { useAuth } from "@/hooks/useAuth";
import {
  getMyWork,
  updateTask,
  createWorkLog,
  deleteWorkLog,
  createSubItem,
  updateSubItem,
  deleteSubItem,
  createPurchaseReq,
  createCoopReq,
  addCoopResponse,
} from "@/lib/api/design";

// ========== 타입 ==========
interface SubItem {
  id: number | string;
  _id?: string;
  name: string;
  weight: number;
  progress: number;
  status: string;
}
interface Attachment {
  id: number;
  name: string;
  type: string;
  size: string;
}
interface PurchaseReq {
  id: number | string;
  _id?: string;
  item: string;
  qty: number;
  unit: string;
  reason: string;
  status: string;
}
interface CoopResponse {
  date: string;
  user: string;
  content: string;
}
interface CoopReq {
  id: string;
  _id?: string;
  toUser: string;
  toDept: string;
  title: string;
  desc: string;
  status: string;
  dueDate: string;
  responses: CoopResponse[];
}
interface WorkLog {
  _id?: string;
  startDt: string;
  endDt: string;
  hours: number;
  desc: string;
  subItemId: number | string | null;
  attachments: Attachment[];
  purchaseReqs: PurchaseReq[];
  coopReqs: CoopReq[];
}
interface Task {
  _id?: string;
  name: string;
  category: string;
  assignee: string;
  start: string;
  end: string;
  status: string;
  progress: number;
  priority: string;
  subItems: SubItem[];
  workLogs: WorkLog[];
}
interface Project {
  id: string;
  project_id?: string;
  name: string;
  customer: string;
  status: string;
  tasks: Task[];
}
interface MyTask extends Task {
  projectId: string;
  projectName: string;
}

// ========== 유틸 ==========
const USER_INFO: Record<string, { role: string; color: string }> = {
  최기구: { role: "기구설계 파트", color: "bg-blue-500" },
  이설계: { role: "기구설계 파트", color: "bg-emerald-500" },
  김전장: { role: "전장설계 파트", color: "bg-amber-500" },
  박도면: { role: "기구설계 파트", color: "bg-violet-500" },
  정SW: { role: "SW개발 파트", color: "bg-pink-500" },
  한조립: { role: "조립/시운전 파트", color: "bg-red-500" },
  박구매: { role: "구매/조달 파트", color: "bg-teal-500" },
  팀장: { role: "설계팀 팀장", color: "bg-indigo-500" },
};

const USERS = Object.keys(USER_INFO);

function calcHours(s: string, e: string): number {
  if (!s || !e) return 0;
  const d = (new Date(e).getTime() - new Date(s).getTime()) / 3600000;
  return d > 0 ? Math.round(d * 10) / 10 : 0;
}

function calcAutoProgress(task: Task): number {
  if (!task.subItems?.length) return task.progress;
  const tw = task.subItems.reduce((s, i) => s + i.weight, 0);
  if (!tw) return 0;
  return Math.round(
    task.subItems.reduce((s, i) => s + (i.progress * i.weight) / tw, 0)
  );
}

function fmtDtShort(dt: string) {
  if (!dt) return "";
  return dt.substring(5, 10) + " " + dt.substring(11, 16);
}

function getWeekStart(d: Date): Date {
  const r = new Date(d);
  const day = r.getDay();
  r.setDate(r.getDate() - day + (day === 0 ? -6 : 1));
  r.setHours(0, 0, 0, 0);
  return r;
}

function getFileIcon(type: string) {
  switch (type) {
    case "image":
      return <ImageIcon className="h-4 w-4" />;
    case "drawing":
      return <Ruler className="h-4 w-4" />;
    case "3d":
      return <Box className="h-4 w-4" />;
    case "document":
      return <FileText className="h-4 w-4" />;
    default:
      return <FolderOpen className="h-4 w-4" />;
  }
}

function getFileType(name: string): string {
  const ext = (name.split(".").pop() || "").toLowerCase();
  if (["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(ext))
    return "image";
  if (["dwg", "dxf"].includes(ext)) return "drawing";
  if (["step", "stp", "igs", "stl"].includes(ext)) return "3d";
  if (
    ["pdf", "doc", "docx", "xlsx", "xls", "pptx", "txt", "csv"].includes(ext)
  )
    return "document";
  return "other";
}

function getProgressColor(p: number) {
  if (p >= 80) return "text-emerald-500";
  if (p >= 40) return "text-blue-500";
  if (p > 0) return "text-amber-500";
  return "text-muted-foreground";
}

function getProgressBg(p: number) {
  if (p >= 80) return "bg-emerald-500";
  if (p >= 40) return "bg-blue-500";
  if (p > 0) return "bg-amber-500";
  return "bg-muted";
}

const STATUS_STYLES: Record<string, string> = {
  대기: "bg-muted text-foreground",
  진행중: "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300",
  검토중:
    "bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-300",
  완료: "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/30 dark:text-emerald-300",
  지연: "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300",
};

const PRIORITY_STYLES: Record<string, string> = {
  긴급: "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300",
  높음: "bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-300",
  보통: "bg-muted text-foreground",
  낮음: "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/30 dark:text-emerald-300",
};

const PR_STATUS_STYLES: Record<string, string> = {
  요청: "bg-red-100 text-red-800",
  견적중: "bg-amber-100 text-amber-800",
  발주완료: "bg-blue-100 text-blue-800",
  입고완료: "bg-emerald-100 text-emerald-800",
};

const CR_STATUS_STYLES: Record<string, string> = {
  요청: "bg-amber-100 text-amber-800",
  접수: "bg-amber-100 text-amber-800",
  진행중: "bg-blue-100 text-blue-800",
  완료: "bg-emerald-100 text-emerald-800",
};

const CR_NEXT: Record<string, string> = {
  요청: "접수",
  접수: "진행중",
  진행중: "완료",
};

// ========== API 응답 매핑 ==========
function mapApiTaskToTask(api: any): Task {
  const subItems: SubItem[] = (api.sub_items || []).map((s: any) => ({
    id: s.id,
    _id: String(s.id),
    name: s.name || "",
    weight: Number(s.weight) || 0,
    progress: Number(s.progress) || 0,
    status: s.status || "대기",
  }));
  const workLogs: WorkLog[] = (api.work_logs || []).map((w: any) => {
    const attachments: Attachment[] = (w.attachments || []).map((a: any) => ({
      id: a.id ?? a.file_name?.length ?? 0,
      name: a.file_name || a.name || "",
      type: a.file_type || getFileType(a.file_name || ""),
      size: a.file_size || a.size || "",
    }));
    const purchaseReqs: PurchaseReq[] = (w.purchase_reqs || []).map((pr: any) => ({
      id: pr.id,
      _id: pr.id ? String(pr.id) : undefined,
      item: pr.item || "",
      qty: Number(pr.qty) || 1,
      unit: pr.unit || "EA",
      reason: pr.reason || "",
      status: pr.status || "요청",
    }));
    const coopReqs: CoopReq[] = (w.coop_reqs || []).map((c: any) => ({
      id: c.id || `CR-${String(c.id)}`,
      _id: c.id ? String(c.id) : undefined,
      toUser: c.to_user || "",
      toDept: c.to_dept || "",
      title: c.title || "",
      desc: c.description || c.desc || "",
      status: c.status || "요청",
      dueDate: c.due_date || "",
      responses: (c.responses || []).map((r: any) => ({
        date: r.response_date || r.date || "",
        user: r.user_name || r.user || "",
        content: r.content || "",
      })),
    }));
    return {
      _id: w.id ? String(w.id) : undefined,
      startDt: w.start_dt || "",
      endDt: w.end_dt || "",
      hours: Number(w.hours) || 0,
      desc: w.description || w.desc || "",
      subItemId: w.sub_item_id ?? null,
      attachments,
      purchaseReqs,
      coopReqs,
    };
  });
  return {
    _id: api.id ? String(api.id) : undefined,
    name: api.name || "",
    category: api.category || "",
    assignee: api.assignee || "",
    start: api.start_date || api.start || "",
    end: api.end_date || api.end || "",
    status: api.status || "대기",
    progress: Number(api.progress) || 0,
    priority: api.priority || "보통",
    subItems,
    workLogs,
  };
}

function mapApiTasksToProjects(apiTasks: any[]): Project[] {
  const projectMap = new Map<string, Project>();
  for (const api of apiTasks) {
    const pId = api.project_id || api.project_no;
    const pNo = api.project_no || api.project_id || "";
    if (!projectMap.has(pId)) {
      projectMap.set(pId, {
        id: pNo,
        project_id: pId,
        name: api.project_name || "",
        customer: api.project_customer || "",
        status: api.project_status || "진행중",
        tasks: [],
      });
    }
    projectMap.get(pId)!.tasks.push(mapApiTaskToTask(api));
  }
  return Array.from(projectMap.values());
}

// ========== 메인 컴포넌트 ==========
export default function MyWorkPage() {
  const { userName } = useAuth();
  const [projects, setProjects] = useState<Project[]>([]);
  const [loading, setLoading] = useState(true);
  const [currentUser, setCurrentUser] = useState(userName || "최기구");
  const [viewMode, setViewMode] = useState<"kanban" | "list" | "timesheet">("kanban");
  const [timesheetWeekOffset, setTimesheetWeekOffset] = useState(0);

  // 필터
  const [filterProject, setFilterProject] = useState("");
  const [filterStatus, setFilterStatus] = useState("");
  const [filterPriority, setFilterPriority] = useState("");
  const [filterCategory, setFilterCategory] = useState("");
  const [filterKeyword, setFilterKeyword] = useState("");

  // 선택된 업무
  const [selectedTaskKey, setSelectedTaskKey] = useState<string | null>(null);
  const [selectedSubItemId, setSelectedSubItemId] = useState<number | string>("__unassigned__");

  // 편집 상태
  const [editingLogIdx, setEditingLogIdx] = useState<number>(-1);
  const [editForm, setEditForm] = useState({ startDt: "", endDt: "", desc: "" });

  // 수행항목 추가
  const [newSiName, setNewSiName] = useState("");
  const [newSiWeight, setNewSiWeight] = useState("");

  // 모달
  const [attModalOpen, setAttModalOpen] = useState(false);
  const [prModalOpen, setPrModalOpen] = useState(false);
  const [crModalOpen, setCrModalOpen] = useState(false);
  const [modalLogIdx, setModalLogIdx] = useState(-1);
  const [modalAttachments, setModalAttachments] = useState<Attachment[]>([]);
  const [modalPurchaseReqs, setModalPurchaseReqs] = useState<PurchaseReq[]>([]);
  const [modalCoopReqs, setModalCoopReqs] = useState<CoopReq[]>([]);

  // 구매요청 폼
  const [prForm, setPrForm] = useState({ item: "", qty: "1", unit: "EA", reason: "", status: "요청" });
  // 협조요청 폼
  const [crForm, setCrForm] = useState({ title: "", toUser: "", desc: "", dueDate: "" });

  const today = useMemo(() => new Date(), []);
  const todayStr = useMemo(() => {
    const dn = ["일", "월", "화", "수", "목", "금", "토"];
    return `${today.getFullYear()}.${String(today.getMonth() + 1).padStart(2, "0")}.${String(today.getDate()).padStart(2, "0")} (${dn[today.getDay()]})`;
  }, [today]);

  const projectsRef = React.useRef<Project[]>([]);
  projectsRef.current = projects;

  const fetchMyWork = useCallback(async () => {
    setLoading(true);
    try {
      const params: { status?: string; project_id?: string } = {};
      if (filterStatus && filterStatus !== "__all__") params.status = filterStatus;
      if (filterProject && filterProject !== "__all__") {
        const proj = projectsRef.current.find((p) => p.id === filterProject || p.project_id === filterProject);
        if (proj?.project_id) params.project_id = proj.project_id;
        else params.project_id = filterProject;
      }
      const res = await getMyWork(params);
      if (res.success && res.data) {
        setProjects(mapApiTasksToProjects(res.data));
      } else {
        setProjects([]);
      }
    } catch {
      toast.error("업무 목록을 불러오는데 실패했습니다.");
      setProjects([]);
    } finally {
      setLoading(false);
    }
  }, [filterStatus, filterProject]);

  useEffect(() => {
    if (userName) setCurrentUser(userName);
  }, [userName]);

  useEffect(() => {
    fetchMyWork();
  }, [fetchMyWork]);

  const projectList = useMemo(() => {
    const ids = new Set<string>();
    projects.forEach((p) => ids.add(`${p.id}|${p.name}`));
    return Array.from(ids).map((v) => {
      const [id, name] = v.split("|");
      return { id, name };
    });
  }, [projects]);

  // 내 업무 목록
  const myTasks = useMemo<MyTask[]>(() => {
    const tasks: MyTask[] = [];
    projects.forEach((p) =>
      p.tasks.forEach((t) => {
        if (t.assignee === currentUser)
          tasks.push({ ...t, projectId: p.id, projectName: p.name });
      })
    );
    return tasks;
  }, [projects, currentUser]);

  // 필터링된 업무
  const filteredTasks = useMemo(() => {
    return myTasks.filter((t) => {
      if (filterProject && filterProject !== "__all__" && t.projectId !== filterProject) return false;
      if (filterStatus && filterStatus !== "__all__" && t.status !== filterStatus) return false;
      if (filterPriority && filterPriority !== "__all__" && (t.priority || "보통") !== filterPriority) return false;
      if (filterCategory && filterCategory !== "__all__" && t.category !== filterCategory) return false;
      if (filterKeyword && !t.name.toLowerCase().includes(filterKeyword.toLowerCase()) && !t.projectName.toLowerCase().includes(filterKeyword.toLowerCase())) return false;
      return true;
    });
  }, [myTasks, filterProject, filterStatus, filterPriority, filterCategory, filterKeyword]);

  // 수신된 협조요청
  const receivedCoops = useMemo(() => {
    const coops: (CoopReq & { projectId: string; projectName: string; taskName: string; fromUser: string })[] = [];
    projects.forEach((p) =>
      p.tasks.forEach((t) =>
        (t.workLogs || []).forEach((wl) =>
          (wl.coopReqs || []).forEach((cr) => {
            if (cr.toUser === currentUser && cr.status !== "완료")
              coops.push({ ...cr, projectId: p.id, projectName: p.name, taskName: t.name, fromUser: t.assignee });
          })
        )
      )
    );
    return coops;
  }, [projects, currentUser]);

  // 선택된 업무 참조
  const selectedTask = useMemo(() => {
    if (!selectedTaskKey) return null;
    const [pid, tname] = selectedTaskKey.split("||");
    for (const p of projects) {
      if (p.id === pid) {
        const t = p.tasks.find((task) => task.name === tname);
        if (t) return { task: t, projectId: p.id, projectName: p.name };
      }
    }
    return null;
  }, [selectedTaskKey, projects]);

  // 통계
  const stats = useMemo(() => {
    const total = myTasks.length + receivedCoops.length;
    const progress = myTasks.filter((t) => t.status === "진행중").length;
    const done = myTasks.filter((t) => t.status === "완료").length;
    const delay = myTasks.filter((t) => t.status !== "완료" && new Date(t.end) < today).length;
    const ws = getWeekStart(today);
    const we = new Date(ws);
    we.setDate(we.getDate() + 6);
    let wh = 0;
    myTasks.forEach((t) =>
      (t.workLogs || []).forEach((l) => {
        const ld = new Date(l.startDt);
        if (ld >= ws && ld <= we) wh += l.hours;
      })
    );
    return { total, progress, done, delay, weekHours: wh };
  }, [myTasks, receivedCoops, today]);

  const handleSelectTask = useCallback((pid: string, tname: string) => {
    setSelectedTaskKey(`${pid}||${tname}`);
    setEditingLogIdx(-1);
    setSelectedSubItemId("__unassigned__");
  }, []);

  const handleResetFilter = useCallback(() => {
    setFilterProject("");
    setFilterStatus("");
    setFilterPriority("");
    setFilterCategory("");
    setFilterKeyword("");
  }, []);

  // 업무 데이터 변경 헬퍼
  const updateProjects = useCallback((updater: (draft: Project[]) => void) => {
    setProjects((prev) => {
      const copy = JSON.parse(JSON.stringify(prev));
      updater(copy);
      return copy;
    });
  }, []);

  const getTaskRef = useCallback(
    (projectId: string, taskName: string) => {
      for (const p of projects) {
        if (p.id === projectId) {
          return p.tasks.find((t) => t.name === taskName) || null;
        }
      }
      return null;
    },
    [projects]
  );

  // 수행항목 추가
  const handleAddSubItem = useCallback(async () => {
    if (!selectedTask || !newSiName.trim()) return;
    const w = parseInt(newSiWeight) || 0;
    if (w <= 0) return;
    const taskId = selectedTask.task._id;
    if (!taskId) return;
    const res = await createSubItem(taskId, { name: newSiName.trim(), weight: w, progress: 0, status: "대기" });
    if (res.success) {
      toast.success("수행항목이 추가되었습니다.");
      setNewSiName("");
      setNewSiWeight("");
      fetchMyWork();
    } else {
      toast.error(res.message || "수행항목 추가에 실패했습니다.");
    }
  }, [selectedTask, newSiName, newSiWeight, fetchMyWork]);

  // 수행항목 삭제
  const handleDeleteSubItem = useCallback(
    async (idx: number) => {
      if (!selectedTask) return;
      const si = selectedTask.task.subItems[idx];
      const siId = si?._id ?? si?.id;
      if (siId) {
        const res = await deleteSubItem(String(siId));
        if (res.success) {
          toast.success("수행항목이 삭제되었습니다.");
          fetchMyWork();
        } else {
          toast.error(res.message || "수행항목 삭제에 실패했습니다.");
        }
      } else {
        updateProjects((draft) => {
          for (const p of draft) {
            if (p.id === selectedTask.projectId) {
              const t = p.tasks.find((x) => x.name === selectedTask.task.name);
              if (t) {
                t.subItems.splice(idx, 1);
                t.progress = calcAutoProgress(t);
              }
            }
          }
        });
      }
    },
    [selectedTask, updateProjects, fetchMyWork]
  );

  // 수행항목 체크 토글
  const handleToggleSubCheck = useCallback(
    async (idx: number) => {
      if (!selectedTask) return;
      const si = selectedTask.task.subItems[idx];
      const siId = si?._id ?? si?.id;
      const newProgress = (si?.progress ?? 0) >= 100 ? 0 : 100;
      const newStatus = newProgress >= 100 ? "완료" : "대기";
      if (siId) {
        const res = await updateSubItem(String(siId), { progress: newProgress, status: newStatus });
        if (res.success) {
          toast.success("수행항목이 업데이트되었습니다.");
          fetchMyWork();
        } else {
          toast.error(res.message || "수행항목 업데이트에 실패했습니다.");
        }
      } else {
        updateProjects((draft) => {
          for (const p of draft) {
            if (p.id === selectedTask.projectId) {
              const t = p.tasks.find((x) => x.name === selectedTask.task.name);
              if (t && t.subItems[idx]) {
                t.subItems[idx].progress = newProgress;
                t.subItems[idx].status = newStatus;
                t.progress = calcAutoProgress(t);
              }
            }
          }
        });
      }
    },
    [selectedTask, updateProjects, fetchMyWork]
  );

  // 수행항목 진행률 변경
  const handleUpdateSubProgress = useCallback(
    async (siId: number | string, val: number) => {
      if (!selectedTask) return;
      const si = selectedTask.task.subItems.find((s) => s.id === siId || s._id === String(siId));
      const apiId = si?._id ?? si?.id;
      if (apiId) {
        const res = await updateSubItem(String(apiId), {
          progress: val,
          status: val >= 100 ? "완료" : val > 0 ? "진행중" : "대기",
        });
        if (res.success) {
          fetchMyWork();
        } else {
          toast.error(res.message || "진행률 업데이트에 실패했습니다.");
        }
      } else {
        updateProjects((draft) => {
          for (const p of draft) {
            if (p.id === selectedTask.projectId) {
              const t = p.tasks.find((x) => x.name === selectedTask.task.name);
              if (t) {
                const subSi = t.subItems.find((s) => s.id === siId || s._id === String(siId));
                if (subSi) {
                  subSi.progress = val;
                  subSi.status = val >= 100 ? "완료" : val > 0 ? "진행중" : "대기";
                  t.progress = calcAutoProgress(t);
                }
              }
            }
          }
        });
      }
    },
    [selectedTask, updateProjects, fetchMyWork]
  );

  // 수행기록 편집 시작
  const handleStartEdit = useCallback(
    (idx: number) => {
      if (!selectedTask) return;
      const log = selectedTask.task.workLogs[idx];
      if (log) {
        setEditForm({ startDt: log.startDt, endDt: log.endDt, desc: log.desc });
      }
      setEditingLogIdx(idx);
    },
    [selectedTask]
  );

  // 새 기록 추가 시작
  const handleStartNewLog = useCallback(
    (siId: number | string | null) => {
      const now = new Date();
      const ds = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
      setEditForm({ startDt: `${ds}T08:00`, endDt: `${ds}T17:00`, desc: "" });
      setEditingLogIdx(-2);
      if (siId === null) setSelectedSubItemId("__unassigned__");
      else setSelectedSubItemId(siId);
    },
    []
  );

  // 기록 저장
  const handleSaveLog = useCallback(
    async (logIdx: number, siId: number | string | null) => {
      if (!selectedTask) return;
      if (!editForm.startDt || !editForm.endDt || !editForm.desc.trim()) return;
      const hours = calcHours(editForm.startDt, editForm.endDt);
      if (hours <= 0) return;
      const taskId = selectedTask.task._id;
      if (!taskId) return;
      if (logIdx < 0) {
        const res = await createWorkLog(taskId, {
          start_dt: editForm.startDt,
          end_dt: editForm.endDt,
          hours,
          description: editForm.desc.trim(),
          sub_item_id: siId ?? null,
        });
        if (res.success) {
          toast.success("작업일지가 등록되었습니다.");
          setEditingLogIdx(-1);
          fetchMyWork();
        } else {
          toast.error(res.message || "작업일지 등록에 실패했습니다.");
        }
      } else {
        updateProjects((draft) => {
          for (const p of draft) {
            if (p.id === selectedTask.projectId) {
              const t = p.tasks.find((x) => x.name === selectedTask.task.name);
              if (t) {
                t.workLogs[logIdx] = { ...t.workLogs[logIdx], startDt: editForm.startDt, endDt: editForm.endDt, hours, desc: editForm.desc.trim(), subItemId: siId, attachments: t.workLogs[logIdx].attachments || [], purchaseReqs: t.workLogs[logIdx].purchaseReqs || [], coopReqs: t.workLogs[logIdx].coopReqs || [] };
              }
            }
          }
        });
        setEditingLogIdx(-1);
      }
    },
    [selectedTask, editForm, updateProjects, fetchMyWork]
  );

  // 기록 삭제
  const handleDeleteLog = useCallback(
    async (idx: number) => {
      if (!selectedTask) return;
      const log = selectedTask.task.workLogs[idx];
      const workLogId = log?._id;
      if (workLogId) {
        const res = await deleteWorkLog(String(workLogId));
        if (res.success) {
          toast.success("작업일지가 삭제되었습니다.");
          setEditingLogIdx(-1);
          fetchMyWork();
        } else {
          toast.error(res.message || "작업일지 삭제에 실패했습니다.");
        }
      } else {
        updateProjects((draft) => {
          for (const p of draft) {
            if (p.id === selectedTask.projectId) {
              const t = p.tasks.find((x) => x.name === selectedTask.task.name);
              if (t) t.workLogs.splice(idx, 1);
            }
          }
        });
        setEditingLogIdx(-1);
      }
    },
    [selectedTask, updateProjects, fetchMyWork]
  );

  // 모달: 첨부파일
  const openAttModal = useCallback(
    (logIdx: number) => {
      if (!selectedTask) return;
      setModalLogIdx(logIdx);
      const atts = logIdx >= 0 ? [...(selectedTask.task.workLogs[logIdx]?.attachments || [])] : [];
      setModalAttachments(atts);
      setAttModalOpen(true);
    },
    [selectedTask]
  );

  const saveAttModal = useCallback(() => {
    if (!selectedTask || modalLogIdx < 0) return;
    updateProjects((draft) => {
      for (const p of draft) {
        if (p.id === selectedTask.projectId) {
          const t = p.tasks.find((x) => x.name === selectedTask.task.name);
          if (t && t.workLogs[modalLogIdx]) t.workLogs[modalLogIdx].attachments = [...modalAttachments];
        }
      }
    });
    setAttModalOpen(false);
  }, [selectedTask, modalLogIdx, modalAttachments, updateProjects]);

  // 모달: 구매요청
  const openPrModal = useCallback(
    (logIdx: number) => {
      if (!selectedTask) return;
      setModalLogIdx(logIdx);
      const prs = logIdx >= 0 ? [...(selectedTask.task.workLogs[logIdx]?.purchaseReqs || []).map((p) => ({ ...p }))] : [];
      setModalPurchaseReqs(prs);
      setPrModalOpen(true);
    },
    [selectedTask]
  );

  const savePrModal = useCallback(async () => {
    if (!selectedTask || modalLogIdx < 0) return;
    const workLog = selectedTask.task.workLogs[modalLogIdx];
    const workLogId = workLog?._id;
    if (workLogId) {
      const newItems = modalPurchaseReqs.filter((pr) => !pr._id);
      for (const pr of newItems) {
        const res = await createPurchaseReq(String(workLogId), {
          item: pr.item,
          qty: pr.qty,
          unit: pr.unit,
          reason: pr.reason,
          status: pr.status,
        });
        if (!res.success) {
          toast.error(res.message || "구매요청 등록에 실패했습니다.");
          return;
        }
      }
      if (newItems.length > 0) toast.success("구매요청이 등록되었습니다.");
    }
    updateProjects((draft) => {
      for (const p of draft) {
        if (p.id === selectedTask.projectId) {
          const t = p.tasks.find((x) => x.name === selectedTask.task.name);
          if (t && t.workLogs[modalLogIdx]) t.workLogs[modalLogIdx].purchaseReqs = [...modalPurchaseReqs];
        }
      }
    });
    setPrModalOpen(false);
    if (workLogId && newItems.length > 0) fetchMyWork();
  }, [selectedTask, modalLogIdx, modalPurchaseReqs, updateProjects, fetchMyWork]);

  // 모달: 협조요청
  const openCrModal = useCallback(
    (logIdx: number) => {
      if (!selectedTask) return;
      setModalLogIdx(logIdx);
      const crs = logIdx >= 0 ? [...(selectedTask.task.workLogs[logIdx]?.coopReqs || []).map((c) => ({ ...c, responses: [...(c.responses || [])] }))] : [];
      setModalCoopReqs(crs);
      setCrModalOpen(true);
    },
    [selectedTask]
  );

  const saveCrModal = useCallback(async () => {
    if (!selectedTask || modalLogIdx < 0) return;
    const workLog = selectedTask.task.workLogs[modalLogIdx];
    const workLogId = workLog?._id;
    if (workLogId) {
      const newItems = modalCoopReqs.filter((cr) => !cr._id);
      for (const cr of newItems) {
        const res = await createCoopReq(String(workLogId), {
          to_user: cr.toUser,
          to_dept: cr.toDept,
          title: cr.title,
          description: cr.desc,
          due_date: cr.dueDate,
        });
        if (!res.success) {
          toast.error(res.message || "협조요청 등록에 실패했습니다.");
          return;
        }
      }
      if (newItems.length > 0) toast.success("협조요청이 등록되었습니다.");
    }
    updateProjects((draft) => {
      for (const p of draft) {
        if (p.id === selectedTask.projectId) {
          const t = p.tasks.find((x) => x.name === selectedTask.task.name);
          if (t && t.workLogs[modalLogIdx]) t.workLogs[modalLogIdx].coopReqs = [...modalCoopReqs];
        }
      }
    });
    setCrModalOpen(false);
    if (workLogId && modalCoopReqs.filter((cr) => !cr._id).length > 0) fetchMyWork();
  }, [selectedTask, modalLogIdx, modalCoopReqs, updateProjects, fetchMyWork]);

  // ===== 칸반 보드 =====
  const kanbanCols = useMemo(() => {
    const cols: Record<string, MyTask[]> = { 대기: [], 진행중: [], 검토중: [], 완료: [] };
    filteredTasks.forEach((t) => {
      if (cols[t.status]) cols[t.status].push(t);
    });
    return cols;
  }, [filteredTasks]);

  const kanbanConfig = [
    { key: "대기", icon: <Circle className="h-3.5 w-3.5" />, color: "border-muted-foreground/30", titleColor: "text-muted-foreground" },
    { key: "진행중", icon: <Circle className="h-3.5 w-3.5 text-blue-500" />, color: "border-blue-500", titleColor: "text-blue-500" },
    { key: "검토중", icon: <Circle className="h-3.5 w-3.5 text-amber-500" />, color: "border-amber-500", titleColor: "text-amber-500" },
    { key: "완료", icon: <CheckCircle2 className="h-3.5 w-3.5 text-emerald-500" />, color: "border-emerald-500", titleColor: "text-emerald-500" },
  ];

  // ===== 타임시트 =====
  const timesheetData = useMemo(() => {
    const ws = getWeekStart(today);
    ws.setDate(ws.getDate() + timesheetWeekOffset * 7);
    const we = new Date(ws);
    we.setDate(we.getDate() + 6);
    const dn = ["월", "화", "수", "목", "금", "토", "일"];
    const wds: Date[] = [];
    for (let i = 0; i < 7; i++) {
      const d = new Date(ws);
      d.setDate(d.getDate() + i);
      wds.push(d);
    }
    const ph: Record<string, { id: string; name: string; days: number[]; total: number; tasks: Record<string, number[]> }> = {};
    myTasks.forEach((t) => {
      const k = `${t.projectId}|${t.projectName}`;
      if (!ph[k]) ph[k] = { id: t.projectId, name: t.projectName, days: [0, 0, 0, 0, 0, 0, 0], total: 0, tasks: {} };
      (t.workLogs || []).forEach((l) => {
        const ld = new Date(l.startDt);
        const di = wds.findIndex((d) => d.toDateString() === ld.toDateString());
        if (di >= 0) {
          ph[k].days[di] += l.hours;
          ph[k].total += l.hours;
          if (!ph[k].tasks[t.name]) ph[k].tasks[t.name] = [0, 0, 0, 0, 0, 0, 0];
          ph[k].tasks[t.name][di] += l.hours;
        }
      });
    });
    const dt = [0, 0, 0, 0, 0, 0, 0];
    Object.values(ph).forEach((p) => p.days.forEach((h, i) => (dt[i] += h)));
    const gt = dt.reduce((s, h) => s + h, 0);
    return { ws, we, dn, wds, ph, dt, gt };
  }, [myTasks, today, timesheetWeekOffset]);

  // ========== 렌더링 ==========
  if (loading && projects.length === 0) {
    return (
      <div className="flex h-full flex-col items-center justify-center gap-2 p-3">
        <Loader2 className="h-8 w-8 animate-spin text-primary" />
        <span className="text-sm text-muted-foreground">업무 목록을 불러오는 중...</span>
      </div>
    );
  }

  return (
    <div className="flex h-full flex-col gap-2 p-3">
      {/* 헤더 */}
      <div className="flex shrink-0 items-center justify-between">
        <div className="flex items-center gap-3">
          <h1 className="text-xl font-bold">
            <BarChart3 className="mr-1.5 inline h-5 w-5" />
            내 업무 현황
          </h1>
          <div className="flex items-center gap-2 rounded-lg border bg-card px-2.5 py-1.5">
            <Avatar className="h-7 w-7">
              <AvatarFallback className={cn("text-xs font-bold text-white", USER_INFO[currentUser]?.color || "bg-primary")}>
                {currentUser.charAt(0)}
              </AvatarFallback>
            </Avatar>
            <div className="leading-tight">
              <div className="text-xs font-semibold">{currentUser}</div>
              <div className="text-[10px] text-muted-foreground">{USER_INFO[currentUser]?.role}</div>
            </div>
            <Select value={currentUser} onValueChange={(v) => { setCurrentUser(v); setSelectedTaskKey(null); }}>
              <SelectTrigger className="h-6 w-20 border-0 bg-transparent text-xs text-primary" size="xs">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                {USERS.map((u) => (
                  <SelectItem key={u} value={u}>{u}</SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>
        </div>
        <div className="flex items-center gap-1.5 rounded-lg border bg-card px-2.5 py-1.5 text-xs text-muted-foreground">
          <Calendar className="h-3.5 w-3.5" />
          {todayStr}
        </div>
      </div>

      {/* 검색 필터 */}
      <div className="flex shrink-0 flex-wrap items-center gap-2 rounded-lg border bg-card px-2.5 py-1.5">
        <Select value={filterProject} onValueChange={setFilterProject}>
          <SelectTrigger className="h-7 w-[130px] text-xs" size="xs"><SelectValue placeholder="전체 프로젝트" /></SelectTrigger>
          <SelectContent>
            <SelectItem value="__all__">전체 프로젝트</SelectItem>
            {projectList.map((p) => (
              <SelectItem key={p.id} value={p.id}>{p.id} {p.name}</SelectItem>
            ))}
          </SelectContent>
        </Select>
        <Select value={filterStatus} onValueChange={setFilterStatus}>
          <SelectTrigger className="h-7 w-[100px] text-xs" size="xs"><SelectValue placeholder="전체 상태" /></SelectTrigger>
          <SelectContent>
            <SelectItem value="__all__">전체 상태</SelectItem>
            {["대기", "진행중", "검토중", "완료"].map((s) => (<SelectItem key={s} value={s}>{s}</SelectItem>))}
          </SelectContent>
        </Select>
        <Select value={filterPriority} onValueChange={setFilterPriority}>
          <SelectTrigger className="h-7 w-[110px] text-xs" size="xs"><SelectValue placeholder="전체 우선순위" /></SelectTrigger>
          <SelectContent>
            <SelectItem value="__all__">전체 우선순위</SelectItem>
            {["긴급", "높음", "보통", "낮음"].map((s) => (<SelectItem key={s} value={s}>{s}</SelectItem>))}
          </SelectContent>
        </Select>
        <Select value={filterCategory} onValueChange={setFilterCategory}>
          <SelectTrigger className="h-7 w-[110px] text-xs" size="xs"><SelectValue placeholder="전체 유형" /></SelectTrigger>
          <SelectContent>
            <SelectItem value="__all__">전체 유형</SelectItem>
            {["기구설계", "전장설계", "SW개발", "구매/조달", "조립/시운전", "검토/승인"].map((s) => (<SelectItem key={s} value={s}>{s}</SelectItem>))}
          </SelectContent>
        </Select>
        <div className="relative">
          <Search className="absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" />
          <Input
            value={filterKeyword}
            onChange={(e) => setFilterKeyword(e.target.value)}
            placeholder="업무명 검색..."
            className="h-7 w-[120px] pl-7 text-xs"
          />
        </div>
        <Button variant="outline" size="sm" className="h-7 text-xs" onClick={handleResetFilter}>
          <RotateCcw className="mr-1 h-3 w-3" />초기화
        </Button>
      </div>

      {/* 통계 카드 */}
      <div className="grid shrink-0 grid-cols-5 gap-2">
        {[
          { icon: <ClipboardList className="h-4 w-4 text-blue-500" />, value: stats.total, label: "전체 업무", bg: "bg-blue-50 dark:bg-blue-900/20" },
          { icon: <Circle className="h-4 w-4 text-amber-500" />, value: stats.progress, label: "진행중", bg: "bg-amber-50 dark:bg-amber-900/20" },
          { icon: <CheckCircle2 className="h-4 w-4 text-emerald-500" />, value: stats.done, label: "완료", bg: "bg-emerald-50 dark:bg-emerald-900/20" },
          { icon: <AlertCircle className="h-4 w-4 text-red-500" />, value: stats.delay, label: "지연", bg: "bg-red-50 dark:bg-red-900/20" },
          { icon: <Clock className="h-4 w-4 text-violet-500" />, value: `${stats.weekHours}h`, label: "이번주 투입", bg: "bg-violet-50 dark:bg-violet-900/20" },
        ].map((s, i) => (
          <Card key={i} className="flex flex-row items-center gap-2 rounded-lg border px-3 py-2 shadow-none">
            <div className={cn("flex h-8 w-8 items-center justify-center rounded-lg", s.bg)}>{s.icon}</div>
            <div>
              <div className="text-lg font-bold">{s.value}</div>
              <div className="text-[11px] text-muted-foreground">{s.label}</div>
            </div>
          </Card>
        ))}
      </div>

      {/* 메인 영역 */}
      <ResizablePanelGroup direction="horizontal" className="min-h-0 flex-1 rounded-lg">
        {/* 왼쪽: 업무 현황 */}
        <ResizablePanel defaultSize={45} minSize={25}>
          <div className="flex h-full flex-col overflow-hidden rounded-lg border bg-card">
            <div className="flex shrink-0 items-center justify-between border-b bg-muted/30 px-3 py-1.5">
              <span className="text-sm font-bold">
                <ClipboardList className="mr-1 inline h-4 w-4" />
                업무 현황
              </span>
              <Tabs value={viewMode} onValueChange={(v) => setViewMode(v as any)}>
                <TabsList className="h-7">
                  <TabsTrigger value="kanban" className="h-5 gap-1 px-2 text-[11px]">
                    <LayoutGrid className="h-3 w-3" />보드
                  </TabsTrigger>
                  <TabsTrigger value="list" className="h-5 gap-1 px-2 text-[11px]">
                    <List className="h-3 w-3" />목록
                  </TabsTrigger>
                  <TabsTrigger value="timesheet" className="h-5 gap-1 px-2 text-[11px]">
                    <Timer className="h-3 w-3" />타임시트
                  </TabsTrigger>
                </TabsList>
              </Tabs>
            </div>
            <ScrollArea className="flex-1">
              <div className="p-2">
                {viewMode === "kanban" && (
                  <div className="grid grid-cols-4 gap-2">
                    {kanbanConfig.map((col) => {
                      const tasks = kanbanCols[col.key] || [];
                      const extraCount = col.key === "대기" ? receivedCoops.length : 0;
                      return (
                        <div key={col.key} className="flex flex-col rounded-lg bg-muted/50 p-2">
                          <div className={cn("mb-2 flex items-center justify-between border-b-2 pb-1.5", col.color)}>
                            <span className={cn("flex items-center gap-1 text-xs font-bold", col.titleColor)}>
                              {col.icon}{col.key}
                            </span>
                            <Badge variant="outline" className="h-5 px-1.5 text-[10px]">{tasks.length + extraCount}</Badge>
                          </div>
                          <div className="flex flex-1 flex-col gap-1.5 overflow-y-auto">
                            {col.key === "대기" && receivedCoops.map((cr, i) => (
                              <div key={`coop-${i}`} className="rounded-md border border-amber-200 bg-amber-50 p-2 dark:border-amber-900/50 dark:bg-amber-900/10">
                                <div className="text-[10px] text-amber-600">
                                  <Handshake className="mr-0.5 inline h-3 w-3" /> 협조 · {cr.fromUser}
                                </div>
                                <div className="text-xs font-semibold">{cr.title}</div>
                                <div className="mt-0.5 text-[10px] text-muted-foreground">{cr.projectName}</div>
                                <div className={cn("mt-0.5 text-[10px]", new Date(cr.dueDate) < today ? "font-semibold text-destructive" : "text-emerald-600")}>
                                  <Calendar className="mr-0.5 inline h-3 w-3" />{cr.dueDate}
                                </div>
                              </div>
                            ))}
                            {tasks.map((t) => {
                              const isDelay = t.status !== "완료" && new Date(t.end) < today;
                              const dd = Math.ceil((new Date(t.end).getTime() - today.getTime()) / 86400000);
                              const dueText = isDelay ? `${Math.abs(dd)}일 초과` : dd === 0 ? "오늘" : dd <= 3 ? `D-${dd}` : t.end;
                              const attCount = (t.workLogs || []).reduce((n, wl) => n + (wl.attachments || []).length, 0);
                              const coopCount = (t.workLogs || []).reduce((n, wl) => n + (wl.coopReqs || []).filter((c) => c.status !== "완료").length, 0);
                              const isSelected = selectedTaskKey === `${t.projectId}||${t.name}`;
                              return (
                                <div
                                  key={`${t.projectId}-${t.name}`}
                                  className={cn(
                                    "cursor-pointer rounded-md border bg-card p-2 transition-all hover:shadow-sm",
                                    isSelected && "border-primary ring-2 ring-primary/20"
                                  )}
                                  onClick={() => handleSelectTask(t.projectId, t.name)}
                                >
                                  <div className="text-[10px] text-muted-foreground">{t.projectId} · {t.projectName}</div>
                                  <div className="text-xs font-semibold">{t.name}</div>
                                  <div className="mt-1 flex items-center justify-between">
                                    <Badge className={cn("h-4 px-1.5 text-[9px]", PRIORITY_STYLES[t.priority || "보통"])}>{t.priority || "보통"}</Badge>
                                    <span className="text-[10px] text-muted-foreground">{t.category}</span>
                                  </div>
                                  <div className="mt-1 flex items-center gap-1.5">
                                    <div className="h-1 flex-1 overflow-hidden rounded-full bg-muted">
                                      <div className={cn("h-full rounded-full", getProgressBg(t.progress))} style={{ width: `${t.progress}%` }} />
                                    </div>
                                    <span className={cn("text-[10px] font-semibold", getProgressColor(t.progress))}>{t.progress}%</span>
                                  </div>
                                  <div className={cn("mt-0.5 text-[10px]", isDelay ? "font-semibold text-destructive" : dd <= 3 ? "text-amber-500" : "text-emerald-600")}>
                                    <Calendar className="mr-0.5 inline h-3 w-3" />{dueText}
                                  </div>
                                  {(coopCount > 0 || attCount > 0) && (
                                    <div className="mt-1 flex gap-1">
                                      {coopCount > 0 && <Badge variant="outline" className="h-4 gap-0.5 px-1 text-[9px]"><Handshake className="h-2.5 w-2.5" />{coopCount}</Badge>}
                                      {attCount > 0 && <Badge variant="outline" className="h-4 gap-0.5 px-1 text-[9px]"><Paperclip className="h-2.5 w-2.5" />{attCount}</Badge>}
                                    </div>
                                  )}
                                </div>
                              );
                            })}
                            {tasks.length === 0 && extraCount === 0 && (
                              <div className="py-3 text-center text-[11px] text-muted-foreground">없음</div>
                            )}
                          </div>
                        </div>
                      );
                    })}
                  </div>
                )}

                {viewMode === "list" && (
                  <Table>
                    <TableHeader>
                      <TableRow>
                        <TableHead className="w-[90px] text-[11px]">프로젝트</TableHead>
                        <TableHead className="text-[11px]">업무명</TableHead>
                        <TableHead className="w-[65px] text-[11px]">유형</TableHead>
                        <TableHead className="w-[55px] text-center text-[11px]">상태</TableHead>
                        <TableHead className="w-[80px] text-[11px]">종료일</TableHead>
                        <TableHead className="w-[70px] text-[11px]">진행률</TableHead>
                      </TableRow>
                    </TableHeader>
                    <TableBody>
                      {filteredTasks
                        .sort((a, b) => {
                          const ad = a.status !== "완료" && new Date(a.end) < today;
                          const bd = b.status !== "완료" && new Date(b.end) < today;
                          if (ad && !bd) return -1;
                          if (!ad && bd) return 1;
                          const ord: Record<string, number> = { 진행중: 0, 대기: 1, 검토중: 2, 완료: 3 };
                          return (ord[a.status] ?? 9) - (ord[b.status] ?? 9);
                        })
                        .map((t) => {
                          const isDelay = t.status !== "완료" && new Date(t.end) < today;
                          const displayStatus = isDelay ? "지연" : t.status;
                          const isSelected = selectedTaskKey === `${t.projectId}||${t.name}`;
                          return (
                            <TableRow
                              key={`${t.projectId}-${t.name}`}
                              className={cn("cursor-pointer", isSelected && "bg-accent")}
                              onClick={() => handleSelectTask(t.projectId, t.name)}
                            >
                              <TableCell className="text-[10px]">
                                <span className="font-semibold text-primary">{t.projectId}</span>
                                <br />
                                <span className="text-muted-foreground">{t.projectName}</span>
                              </TableCell>
                              <TableCell className="text-xs font-medium">{t.name}</TableCell>
                              <TableCell className="text-[11px]">{t.category}</TableCell>
                              <TableCell className="text-center">
                                <Badge className={cn("text-[10px]", STATUS_STYLES[displayStatus])}>{displayStatus}</Badge>
                              </TableCell>
                              <TableCell className={cn("text-[11px]", isDelay && "font-semibold text-destructive")}>{t.end}</TableCell>
                              <TableCell>
                                <div className="flex items-center gap-1.5">
                                  <div className="h-1 w-12 overflow-hidden rounded-full bg-muted">
                                    <div className={cn("h-full rounded-full", getProgressBg(t.progress))} style={{ width: `${t.progress}%` }} />
                                  </div>
                                  <span className="text-[10px]">{t.progress}%</span>
                                </div>
                              </TableCell>
                            </TableRow>
                          );
                        })}
                      {filteredTasks.length === 0 && (
                        <TableRow>
                          <TableCell colSpan={6} className="py-8 text-center text-xs text-muted-foreground">검색 결과 없음</TableCell>
                        </TableRow>
                      )}
                    </TableBody>
                  </Table>
                )}

                {viewMode === "timesheet" && (
                  <div>
                    <div className="mb-2 flex items-center gap-2">
                      <Button variant="outline" size="sm" className="h-7 text-xs" onClick={() => setTimesheetWeekOffset((p) => p - 1)}>
                        <ChevronLeft className="h-3 w-3" />
                      </Button>
                      <span className="text-sm font-semibold">
                        <Calendar className="mr-1 inline h-3.5 w-3.5" />
                        {timesheetData.ws.getMonth() + 1}/{timesheetData.ws.getDate()} ~ {timesheetData.we.getMonth() + 1}/{timesheetData.we.getDate()}
                      </span>
                      <Button variant="outline" size="sm" className="h-7 text-xs" onClick={() => setTimesheetWeekOffset((p) => p + 1)}>
                        <ChevronRight className="h-3 w-3" />
                      </Button>
                      {timesheetWeekOffset !== 0 && (
                        <Button variant="outline" size="sm" className="h-7 text-xs text-primary" onClick={() => setTimesheetWeekOffset(0)}>이번주</Button>
                      )}
                      <span className="ml-auto text-xs text-muted-foreground">
                        총 <strong className="text-primary">{timesheetData.gt}h</strong>
                      </span>
                    </div>
                    <Table>
                      <TableHeader>
                        <TableRow>
                          <TableHead className="min-w-[120px] text-[11px]">프로젝트/업무</TableHead>
                          {timesheetData.wds.map((d, i) => (
                            <TableHead
                              key={i}
                              className={cn(
                                "text-center text-[10px]",
                                i >= 5 && "bg-red-50 dark:bg-red-900/10",
                                d.toDateString() === today.toDateString() && "bg-blue-50 text-primary dark:bg-blue-900/10"
                              )}
                            >
                              {timesheetData.dn[i]}
                              <br />
                              <span className="text-[9px]">{d.getMonth() + 1}/{d.getDate()}</span>
                            </TableHead>
                          ))}
                          <TableHead className="text-center text-[11px] font-bold">합계</TableHead>
                        </TableRow>
                      </TableHeader>
                      <TableBody>
                        {Object.keys(timesheetData.ph).length === 0 && (
                          <TableRow>
                            <TableCell colSpan={9} className="py-8 text-center text-xs text-muted-foreground">기록 없음</TableCell>
                          </TableRow>
                        )}
                        {Object.values(timesheetData.ph).map((p) => (
                          <React.Fragment key={p.id}>
                            <TableRow className="bg-muted/30">
                              <TableCell className="text-[10px] font-bold text-primary">{p.id} {p.name}</TableCell>
                              {p.days.map((v, i) => (
                                <TableCell key={i} className={cn("text-center text-[11px] font-semibold", i >= 5 && "bg-red-50 dark:bg-red-900/10", v > 0 ? "" : "text-muted-foreground/30")}>
                                  {v > 0 ? `${v}h` : "-"}
                                </TableCell>
                              ))}
                              <TableCell className="text-center text-xs font-bold text-primary">{p.total}h</TableCell>
                            </TableRow>
                            {Object.entries(p.tasks).map(([tn, days]) => {
                              const tt = days.reduce((s, v) => s + v, 0);
                              return (
                                <TableRow key={tn}>
                                  <TableCell className="pl-4 text-[10px] text-muted-foreground">└ {tn}</TableCell>
                                  {days.map((v, i) => (
                                    <TableCell key={i} className={cn("text-center text-[10px]", i >= 5 && "bg-red-50 dark:bg-red-900/10", v > 0 ? "" : "text-muted-foreground/30")}>
                                      {v > 0 ? `${v}h` : "-"}
                                    </TableCell>
                                  ))}
                                  <TableCell className="text-center text-[10px] font-semibold">{tt}h</TableCell>
                                </TableRow>
                              );
                            })}
                          </React.Fragment>
                        ))}
                        <TableRow className="bg-blue-50 font-bold dark:bg-blue-900/10">
                          <TableCell className="text-[11px]">합계</TableCell>
                          {timesheetData.dt.map((v, i) => (
                            <TableCell key={i} className={cn("text-center text-[11px]", i >= 5 && "bg-red-50 dark:bg-red-900/10", v > 0 ? "text-primary" : "text-muted-foreground/30")}>
                              {v > 0 ? `${v}h` : "-"}
                            </TableCell>
                          ))}
                          <TableCell className="text-center text-sm font-bold text-primary">{timesheetData.gt}h</TableCell>
                        </TableRow>
                      </TableBody>
                    </Table>
                  </div>
                )}
              </div>
            </ScrollArea>
          </div>
        </ResizablePanel>

        <ResizableHandle withHandle />

        {/* 오른쪽: 상세 패널 */}
        <ResizablePanel defaultSize={55} minSize={30}>
          <div className="flex h-full flex-col overflow-hidden rounded-lg border bg-card">
            {!selectedTask ? (
              <div className="flex h-full flex-col items-center justify-center gap-2 text-muted-foreground">
                <PointerIcon className="h-8 w-8" />
                <span className="text-sm">왼쪽에서 업무를 선택하세요</span>
              </div>
            ) : (
              <>
                {/* 상세 헤더 */}
                <div className="flex shrink-0 items-center justify-between border-b bg-muted/30 px-3 py-2">
                  <span className="text-sm font-bold">{selectedTask.task.name}</span>
                  <Button variant="ghost" size="sm" className="h-6 w-6 p-0" onClick={() => setSelectedTaskKey(null)}>
                    <X className="h-3.5 w-3.5" />
                  </Button>
                </div>

                {/* 업무 정보 바 */}
                <div className="shrink-0 border-b bg-muted/10 px-3 py-2">
                  <div className="grid grid-cols-[auto_1fr_auto_1fr] gap-x-3 gap-y-0.5 text-xs">
                    <span className="text-muted-foreground">프로젝트</span>
                    <span className="font-semibold text-primary">{selectedTask.projectId} {selectedTask.projectName}</span>
                    <span className="text-muted-foreground">상태</span>
                    <span className="flex items-center gap-1.5">
                      <Badge className={cn("text-[10px]", STATUS_STYLES[selectedTask.task.status])}>{selectedTask.task.status}</Badge>
                      {selectedTask.task.subItems.length > 0 && <Badge variant="outline" className="text-[9px]">자동</Badge>}
                    </span>
                    <span className="text-muted-foreground">유형</span>
                    <span className="font-semibold">{selectedTask.task.category}</span>
                    <span className="text-muted-foreground">기간</span>
                    <span className="font-semibold">{selectedTask.task.start} ~ {selectedTask.task.end}</span>
                  </div>
                  <div className="mt-1.5 flex items-center gap-2">
                    <Progress value={selectedTask.task.subItems.length > 0 ? calcAutoProgress(selectedTask.task) : selectedTask.task.progress} className="h-1.5 flex-1" />
                    <span className={cn("text-sm font-bold", getProgressColor(selectedTask.task.subItems.length > 0 ? calcAutoProgress(selectedTask.task) : selectedTask.task.progress))}>
                      {selectedTask.task.subItems.length > 0 ? calcAutoProgress(selectedTask.task) : selectedTask.task.progress}%
                    </span>
                  </div>
                </div>

                {/* 수행항목 + 기록 분할 */}
                <ResizablePanelGroup direction="horizontal" className="min-h-0 flex-1">
                  {/* 수행항목 */}
                  <ResizablePanel defaultSize={40} minSize={20}>
                    <div className="flex h-full flex-col">
                      <div className="shrink-0 border-b bg-muted/30 px-3 py-1.5 text-xs font-bold">
                        <ClipboardList className="mr-1 inline h-3.5 w-3.5" />수행항목
                      </div>
                      <ScrollArea className="flex-1">
                        {/* 추가 입력 */}
                        <div className="flex gap-1 border-b bg-muted/10 p-2">
                          <Input value={newSiName} onChange={(e) => setNewSiName(e.target.value)} placeholder="수행항목 추가..." className="h-6 flex-1 text-xs" />
                          <Input value={newSiWeight} onChange={(e) => setNewSiWeight(e.target.value)} placeholder="%" type="number" className="h-6 w-10 text-xs" min={1} max={100} />
                          <Button size="sm" className="h-6 px-2 text-[10px]" onClick={handleAddSubItem}>
                            <Plus className="mr-0.5 h-3 w-3" />추가
                          </Button>
                        </div>

                        {/* 수행항목 리스트 */}
                        {selectedTask.task.subItems.map((si, idx) => {
                          const done = si.progress >= 100;
                          const siLogs = selectedTask.task.workLogs.filter((wl) => wl.subItemId == si.id || String(wl.subItemId) === String(si.id));
                          const isSelected = selectedSubItemId === si.id;
                          return (
                            <div
                              key={si.id}
                              className={cn(
                                "flex cursor-pointer items-center gap-1.5 border-b px-2.5 py-1.5 transition-colors hover:bg-accent/50",
                                isSelected && "border-l-2 border-l-primary bg-accent"
                              )}
                              onClick={() => { setSelectedSubItemId(si.id); setEditingLogIdx(-1); }}
                            >
                              <Checkbox
                                checked={done}
                                onCheckedChange={() => handleToggleSubCheck(idx)}
                                onClick={(e) => e.stopPropagation()}
                                className="h-4 w-4"
                              />
                              <span className={cn("min-w-0 flex-1 truncate text-xs font-semibold", done && "text-muted-foreground line-through")}>
                                {si.name}
                              </span>
                              <Badge variant="outline" className="h-4 px-1 text-[9px]">{si.weight}%</Badge>
                              <div className="h-1 w-10 overflow-hidden rounded-full bg-muted">
                                <div className={cn("h-full rounded-full", getProgressBg(si.progress))} style={{ width: `${si.progress}%` }} />
                              </div>
                              <span className={cn("min-w-[28px] text-right text-[10px] font-bold", getProgressColor(si.progress))}>{si.progress}%</span>
                              <span className="min-w-[16px] text-center text-[10px] text-muted-foreground">{siLogs.length}</span>
                              <Button variant="ghost" size="sm" className="h-5 w-5 p-0 opacity-20 hover:opacity-100" onClick={(e) => { e.stopPropagation(); handleDeleteSubItem(idx); }}>
                                <Trash2 className="h-3 w-3" />
                              </Button>
                            </div>
                          );
                        })}

                        {/* 요약 */}
                        {selectedTask.task.subItems.length > 0 && (
                          <div className="flex items-center justify-between border-b bg-blue-50 px-2.5 py-1 text-[11px] dark:bg-blue-900/10">
                            <span>
                              가중 진행률: <strong className="text-primary">{calcAutoProgress(selectedTask.task)}%</strong>
                              {selectedTask.task.subItems.reduce((s, i) => s + i.weight, 0) !== 100 && (
                                <span className="text-destructive"> (합계:{selectedTask.task.subItems.reduce((s, i) => s + i.weight, 0)}%)</span>
                              )}
                            </span>
                            <span className="text-muted-foreground">
                              {selectedTask.task.subItems.filter((i) => i.progress >= 100).length}/{selectedTask.task.subItems.length}
                            </span>
                          </div>
                        )}

                        {/* 일반 수행기록 */}
                        <div
                          className={cn(
                            "flex cursor-pointer items-center gap-1.5 border-t px-2.5 py-1.5 text-xs font-bold text-muted-foreground transition-colors hover:bg-accent/50",
                            selectedSubItemId === "__unassigned__" && "border-l-2 border-l-primary bg-accent"
                          )}
                          onClick={() => { setSelectedSubItemId("__unassigned__"); setEditingLogIdx(-1); }}
                        >
                          <FileEdit className="h-3.5 w-3.5" />
                          일반 수행기록
                          <span className="ml-1 text-[10px] font-normal text-muted-foreground">
                            ({selectedTask.task.workLogs.filter((wl) => !wl.subItemId).length}건)
                          </span>
                        </div>
                      </ScrollArea>
                    </div>
                  </ResizablePanel>

                  <ResizableHandle />

                  {/* 수행기록 */}
                  <ResizablePanel defaultSize={60} minSize={30}>
                    <div className="flex h-full flex-col">
                      <div className="shrink-0 border-b bg-muted/30 px-3 py-1.5 text-xs font-bold">
                        <FileEdit className="mr-1 inline h-3.5 w-3.5" />수행기록
                      </div>
                      <ScrollArea className="flex-1">
                        {selectedSubItemId === "__unassigned__" ? (
                          <RenderLogs
                            title="일반 수행기록"
                            logs={selectedTask.task.workLogs
                              .map((wl, i) => ({ ...wl, _idx: i }))
                              .filter((wl) => !wl.subItemId)
                              .sort((a, b) => b.startDt.localeCompare(a.startDt))}
                            editingLogIdx={editingLogIdx}
                            editForm={editForm}
                            setEditForm={setEditForm}
                            onStartEdit={handleStartEdit}
                            onStartNew={() => handleStartNewLog(null)}
                            onSave={(logIdx) => handleSaveLog(logIdx, null)}
                            onCancel={() => setEditingLogIdx(-1)}
                            onDelete={handleDeleteLog}
                            onOpenAtt={openAttModal}
                            onOpenPr={openPrModal}
                            onOpenCr={openCrModal}
                          />
                        ) : (
                          (() => {
                            const si = selectedTask.task.subItems.find((s) => s.id === selectedSubItemId);
                            if (!si) return (
                              <div className="flex h-full flex-col items-center justify-center gap-1 text-muted-foreground">
                                <PointerIcon className="h-6 w-6" />
                                <span className="text-xs">왼쪽에서 수행항목을 선택하세요</span>
                              </div>
                            );
                            const siLogs = selectedTask.task.workLogs
                              .map((wl, i) => ({ ...wl, _idx: i }))
                              .filter((wl) => wl.subItemId == si.id || String(wl.subItemId) === String(si.id))
                              .sort((a, b) => b.startDt.localeCompare(a.startDt));
                            return (
                              <>
                                <div className="border-b bg-muted/10 p-2.5">
                                  <div className="flex items-center gap-2">
                                    <span className="text-sm font-bold">{si.name}</span>
                                    <Badge variant="outline" className="text-[9px]">{si.weight}%</Badge>
                                  </div>
                                  <div className="mt-1.5 flex items-center gap-2">
                                    <Slider
                                      className="flex-1"
                                      value={[si.progress]}
                                      min={0}
                                      max={100}
                                      step={5}
                                      onValueChange={([v]) => handleUpdateSubProgress(si.id, v)}
                                    />
                                    <span className={cn("min-w-[32px] text-right text-xs font-bold", getProgressColor(si.progress))}>{si.progress}%</span>
                                  </div>
                                  <div className="mt-0.5 text-[10px] text-muted-foreground">{siLogs.length}건의 기록</div>
                                </div>
                                <RenderLogs
                                  title={si.name}
                                  logs={siLogs}
                                  editingLogIdx={editingLogIdx}
                                  editForm={editForm}
                                  setEditForm={setEditForm}
                                  onStartEdit={handleStartEdit}
                                  onStartNew={() => handleStartNewLog(si.id)}
                                  onSave={(logIdx) => handleSaveLog(logIdx, si.id)}
                                  onCancel={() => setEditingLogIdx(-1)}
                                  onDelete={handleDeleteLog}
                                  onOpenAtt={openAttModal}
                                  onOpenPr={openPrModal}
                                  onOpenCr={openCrModal}
                                />
                              </>
                            );
                          })()
                        )}
                      </ScrollArea>
                    </div>
                  </ResizablePanel>
                </ResizablePanelGroup>
              </>
            )}
          </div>
        </ResizablePanel>
      </ResizablePanelGroup>

      {/* 첨부파일 모달 */}
      <Dialog open={attModalOpen} onOpenChange={setAttModalOpen}>
        <DialogContent className="max-w-[95vw] sm:max-w-[600px]">
          <DialogHeader>
            <DialogTitle className="text-base"><Paperclip className="mr-1.5 inline h-4 w-4" />첨부파일 ({modalAttachments.length})</DialogTitle>
            <DialogDescription className="text-xs">파일을 추가하거나 삭제할 수 있습니다.</DialogDescription>
          </DialogHeader>
          <div className="space-y-2">
            {modalAttachments.map((f, i) => (
              <div key={f.id} className="flex items-center gap-2 rounded-md border bg-muted/30 p-2">
                {getFileIcon(f.type)}
                <div className="flex-1">
                  <div className="text-xs font-semibold">{f.name}</div>
                  <div className="text-[10px] text-muted-foreground">{f.type} · {f.size}</div>
                </div>
                <Button variant="outline" size="sm" className="h-6 text-[10px] text-destructive" onClick={() => setModalAttachments((p) => p.filter((_, idx) => idx !== i))}>삭제</Button>
              </div>
            ))}
            <div
              className="cursor-pointer rounded-lg border-2 border-dashed p-4 text-center transition-colors hover:border-primary hover:bg-accent/50"
              onClick={() => {
                const name = prompt("파일명 (예: photo.jpg, drawing.dwg)");
                if (!name) return;
                setModalAttachments((p) => [...p, { id: Date.now(), name, type: getFileType(name), size: `${(Math.random() * 10 + 0.5).toFixed(1)}MB` }]);
              }}
            >
              <Upload className="mx-auto h-5 w-5 text-muted-foreground" />
              <div className="mt-1 text-xs text-muted-foreground">클릭하여 파일 추가</div>
            </div>
          </div>
          <DialogFooter className="gap-2 sm:gap-0">
            <Button variant="outline" onClick={() => setAttModalOpen(false)} className="h-8 text-xs">닫기</Button>
            <Button onClick={saveAttModal} className="h-8 text-xs">저장</Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* 구매요청 모달 */}
      <Dialog open={prModalOpen} onOpenChange={setPrModalOpen}>
        <DialogContent className="max-w-[95vw] sm:max-w-[600px]">
          <DialogHeader>
            <DialogTitle className="text-base"><ShoppingCart className="mr-1.5 inline h-4 w-4" />구매요청 관리</DialogTitle>
            <DialogDescription className="text-xs">구매요청을 등록하고 관리합니다.</DialogDescription>
          </DialogHeader>
          <div className="space-y-3">
            <div className="grid grid-cols-2 gap-2">
              <div>
                <Label className="text-xs">자재명 *</Label>
                <Input value={prForm.item} onChange={(e) => setPrForm((p) => ({ ...p, item: e.target.value }))} placeholder="예: LM가이드 HSR15-R" className="h-7 text-xs" />
              </div>
              <div className="grid grid-cols-2 gap-1">
                <div>
                  <Label className="text-xs">수량</Label>
                  <Input value={prForm.qty} onChange={(e) => setPrForm((p) => ({ ...p, qty: e.target.value }))} type="number" className="h-7 text-xs" min={1} />
                </div>
                <div>
                  <Label className="text-xs">단위</Label>
                  <Select value={prForm.unit} onValueChange={(v) => setPrForm((p) => ({ ...p, unit: v }))}>
                    <SelectTrigger className="h-7 text-xs" size="xs"><SelectValue /></SelectTrigger>
                    <SelectContent>
                      {["EA", "SET", "BOX", "M", "KG"].map((u) => (<SelectItem key={u} value={u}>{u}</SelectItem>))}
                    </SelectContent>
                  </Select>
                </div>
              </div>
            </div>
            <div>
              <Label className="text-xs">사유</Label>
              <Input value={prForm.reason} onChange={(e) => setPrForm((p) => ({ ...p, reason: e.target.value }))} placeholder="구매 사유 입력" className="h-7 text-xs" />
            </div>
            <div className="flex items-end gap-2">
              <div className="flex-1">
                <Label className="text-xs">상태</Label>
                <Select value={prForm.status} onValueChange={(v) => setPrForm((p) => ({ ...p, status: v }))}>
                  <SelectTrigger className="h-7 text-xs" size="xs"><SelectValue /></SelectTrigger>
                  <SelectContent>
                    {["요청", "견적중", "발주완료", "입고완료"].map((s) => (<SelectItem key={s} value={s}>{s}</SelectItem>))}
                  </SelectContent>
                </Select>
              </div>
              <Button size="sm" className="h-7 text-xs" onClick={() => {
                if (!prForm.item.trim()) return;
                setModalPurchaseReqs((p) => [...p, { id: Date.now(), item: prForm.item.trim(), qty: parseInt(prForm.qty) || 1, unit: prForm.unit, reason: prForm.reason, status: prForm.status }]);
                setPrForm((p) => ({ ...p, item: "", reason: "" }));
              }}>
                <Plus className="mr-0.5 h-3 w-3" />추가
              </Button>
            </div>
            <div className="text-xs font-semibold">등록된 구매요청 ({modalPurchaseReqs.length})</div>
            {modalPurchaseReqs.length === 0 && <div className="py-3 text-center text-xs text-muted-foreground">등록된 요청이 없습니다.</div>}
            {modalPurchaseReqs.map((pr, i) => (
              <div key={pr.id} className="flex items-center gap-2 rounded-md border bg-muted/30 p-2">
                <ShoppingCart className="h-3.5 w-3.5" />
                <strong className="text-xs">{pr.item}</strong>
                <span className="text-[10px] text-muted-foreground">{pr.qty} {pr.unit}</span>
                {pr.reason && <span className="text-[10px] text-muted-foreground">{pr.reason}</span>}
                <Badge className={cn("text-[9px]", PR_STATUS_STYLES[pr.status])}>{pr.status}</Badge>
                <Button variant="ghost" size="sm" className="ml-auto h-5 w-5 p-0 opacity-30 hover:opacity-100" onClick={() => setModalPurchaseReqs((p) => p.filter((_, idx) => idx !== i))}>
                  <Trash2 className="h-3 w-3" />
                </Button>
              </div>
            ))}
          </div>
          <DialogFooter className="gap-2 sm:gap-0">
            <Button variant="outline" onClick={() => setPrModalOpen(false)} className="h-8 text-xs">닫기</Button>
            <Button onClick={savePrModal} className="h-8 text-xs">저장</Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* 협조요청 모달 */}
      <Dialog open={crModalOpen} onOpenChange={setCrModalOpen}>
        <DialogContent className="max-w-[95vw] sm:max-w-[600px]">
          <DialogHeader>
            <DialogTitle className="text-base"><Handshake className="mr-1.5 inline h-4 w-4" />협조요청 관리</DialogTitle>
            <DialogDescription className="text-xs">협조요청을 등록하고 관리합니다.</DialogDescription>
          </DialogHeader>
          <div className="space-y-3">
            <div>
              <Label className="text-xs">요청 제목 *</Label>
              <Input value={crForm.title} onChange={(e) => setCrForm((p) => ({ ...p, title: e.target.value }))} placeholder="협조 요청 제목" className="h-7 text-xs" />
            </div>
            <div className="grid grid-cols-2 gap-2">
              <div>
                <Label className="text-xs">대상자 *</Label>
                <Select value={crForm.toUser} onValueChange={(v) => setCrForm((p) => ({ ...p, toUser: v }))}>
                  <SelectTrigger className="h-7 text-xs" size="xs"><SelectValue placeholder="선택" /></SelectTrigger>
                  <SelectContent>
                    {USERS.filter((u) => u !== currentUser).map((u) => (<SelectItem key={u} value={u}>{u} ({USER_INFO[u].role})</SelectItem>))}
                  </SelectContent>
                </Select>
              </div>
              <div>
                <Label className="text-xs">기한</Label>
                <Input type="date" value={crForm.dueDate} onChange={(e) => setCrForm((p) => ({ ...p, dueDate: e.target.value }))} className="h-7 text-xs" />
              </div>
            </div>
            <div>
              <Label className="text-xs">요청 내용</Label>
              <Textarea value={crForm.desc} onChange={(e) => setCrForm((p) => ({ ...p, desc: e.target.value }))} placeholder="상세 요청 내용" className="min-h-[50px] text-xs" rows={2} />
            </div>
            <div className="text-right">
              <Button size="sm" className="h-7 text-xs" onClick={() => {
                if (!crForm.title.trim() || !crForm.toUser) return;
                setModalCoopReqs((p) => [...p, { id: `CR-${Date.now()}`, toUser: crForm.toUser, toDept: USER_INFO[crForm.toUser]?.role || "", title: crForm.title.trim(), desc: crForm.desc, status: "요청", dueDate: crForm.dueDate, responses: [] }]);
                setCrForm((p) => ({ ...p, title: "", desc: "" }));
              }}>
                <Plus className="mr-0.5 h-3 w-3" />추가
              </Button>
            </div>
            <div className="text-xs font-semibold">등록된 협조요청 ({modalCoopReqs.length})</div>
            {modalCoopReqs.length === 0 && <div className="py-3 text-center text-xs text-muted-foreground">등록된 요청이 없습니다.</div>}
            {modalCoopReqs.map((cr, i) => (
              <div key={cr.id} className="space-y-1 rounded-md border bg-muted/30 p-2">
                <div className="flex items-center gap-2">
                  <Handshake className="h-3.5 w-3.5" />
                  <strong className="flex-1 text-xs">{cr.title}</strong>
                  <span className="text-[10px] text-muted-foreground">{cr.toUser} ({cr.toDept})</span>
                  <Badge className={cn("text-[9px]", CR_STATUS_STYLES[cr.status])}>{cr.status}</Badge>
                  <Button variant="ghost" size="sm" className="h-5 w-5 p-0 opacity-30 hover:opacity-100" onClick={() => setModalCoopReqs((p) => p.filter((_, idx) => idx !== i))}>
                    <Trash2 className="h-3 w-3" />
                  </Button>
                </div>
                {cr.desc && <div className="pl-5 text-[11px] text-muted-foreground">{cr.desc}</div>}
                <div className="pl-5 text-[10px] text-muted-foreground">기한: {cr.dueDate}</div>
                {(cr.responses || []).map((r, ri) => (
                  <div key={ri} className="pl-5 text-[10px] text-muted-foreground">
                    <strong className="text-primary">{r.user}</strong>: {r.content} <span className="text-muted-foreground">({r.date})</span>
                  </div>
                ))}
                {cr.toUser === currentUser && cr.status !== "완료" && CR_NEXT[cr.status] && (
                  <div className="pl-5">
                    <Button size="sm" className="h-5 px-2 text-[10px]" onClick={() => {
                      setModalCoopReqs((p) => p.map((c, idx) => idx === i ? { ...c, status: CR_NEXT[c.status] || c.status } : c));
                    }}>
                      {CR_NEXT[cr.status]}
                    </Button>
                  </div>
                )}
              </div>
            ))}
          </div>
          <DialogFooter className="gap-2 sm:gap-0">
            <Button variant="outline" onClick={() => setCrModalOpen(false)} className="h-8 text-xs">닫기</Button>
            <Button onClick={saveCrModal} className="h-8 text-xs">저장</Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}

// ========== 수행기록 렌더 서브컴포넌트 ==========
interface RenderLogsProps {
  title: string;
  logs: (WorkLog & { _idx: number })[];
  editingLogIdx: number;
  editForm: { startDt: string; endDt: string; desc: string };
  setEditForm: React.Dispatch<React.SetStateAction<{ startDt: string; endDt: string; desc: string }>>;
  onStartEdit: (idx: number) => void;
  onStartNew: () => void;
  onSave: (logIdx: number) => void;
  onCancel: () => void;
  onDelete: (idx: number) => void;
  onOpenAtt: (idx: number) => void;
  onOpenPr: (idx: number) => void;
  onOpenCr: (idx: number) => void;
}

function RenderLogs({ title, logs, editingLogIdx, editForm, setEditForm, onStartEdit, onStartNew, onSave, onCancel, onDelete, onOpenAtt, onOpenPr, onOpenCr }: RenderLogsProps) {
  return (
    <div>
      {logs.length === 0 && editingLogIdx !== -2 && (
        <div className="flex flex-col items-center justify-center gap-1 py-8 text-muted-foreground">
          <FileEdit className="h-6 w-6" />
          <span className="text-xs">기록이 없습니다</span>
        </div>
      )}
      {logs.map((log) => {
        const idx = log._idx;
        if (editingLogIdx === idx) {
          return (
            <EditLogRow
              key={idx}
              logIdx={idx}
              editForm={editForm}
              setEditForm={setEditForm}
              onSave={onSave}
              onCancel={onCancel}
              onOpenAtt={onOpenAtt}
              onOpenPr={onOpenPr}
              onOpenCr={onOpenCr}
              attCount={(log.attachments || []).length}
              prCount={(log.purchaseReqs || []).length}
              crCount={(log.coopReqs || []).length}
            />
          );
        }
        const attC = (log.attachments || []).length;
        const prC = (log.purchaseReqs || []).length;
        const crC = (log.coopReqs || []).length;
        return (
          <div
            key={idx}
            className="flex cursor-pointer items-center gap-1.5 border-b px-2.5 py-1.5 text-xs transition-colors hover:bg-accent/50"
            onClick={() => onStartEdit(idx)}
            title="클릭하여 수정"
          >
            <span className="min-w-[75px] text-[11px] text-muted-foreground">{fmtDtShort(log.startDt)}~{log.endDt.substring(11, 16)}</span>
            <Badge variant="outline" className="h-4 min-w-[26px] justify-center px-1 text-[10px] font-bold text-primary">{log.hours}h</Badge>
            <span className="min-w-0 flex-1 truncate">{log.desc}</span>
            <div className="flex gap-1">
              {attC > 0 && (
                <Badge variant="outline" className="h-4 cursor-pointer gap-0.5 px-1 text-[9px] hover:border-primary" onClick={(e) => { e.stopPropagation(); onOpenAtt(idx); }}>
                  <Paperclip className="h-2.5 w-2.5" />{attC}
                </Badge>
              )}
              {prC > 0 && (
                <Badge className="h-4 cursor-pointer gap-0.5 bg-amber-50 px-1 text-[9px] text-amber-800 hover:bg-amber-100 dark:bg-amber-900/20" onClick={(e) => { e.stopPropagation(); onOpenPr(idx); }}>
                  <ShoppingCart className="h-2.5 w-2.5" />{prC}
                </Badge>
              )}
              {crC > 0 && (
                <Badge className="h-4 cursor-pointer gap-0.5 bg-pink-50 px-1 text-[9px] text-pink-800 hover:bg-pink-100 dark:bg-pink-900/20" onClick={(e) => { e.stopPropagation(); onOpenCr(idx); }}>
                  <Handshake className="h-2.5 w-2.5" />{crC}
                </Badge>
              )}
            </div>
            <Button variant="ghost" size="sm" className="h-5 w-5 p-0 opacity-20 hover:opacity-100" onClick={(e) => { e.stopPropagation(); onDelete(idx); }}>
              <Trash2 className="h-3 w-3" />
            </Button>
          </div>
        );
      })}
      {editingLogIdx === -2 && (
        <EditLogRow
          logIdx={-1}
          editForm={editForm}
          setEditForm={setEditForm}
          onSave={onSave}
          onCancel={onCancel}
          onOpenAtt={onOpenAtt}
          onOpenPr={onOpenPr}
          onOpenCr={onOpenCr}
          attCount={0}
          prCount={0}
          crCount={0}
        />
      )}
      <Button variant="outline" size="sm" className="m-2 h-6 border-dashed text-[10px] text-muted-foreground" onClick={onStartNew}>
        <Plus className="mr-0.5 h-3 w-3" />기록 추가
      </Button>
    </div>
  );
}

// ========== 편집 행 ==========
interface EditLogRowProps {
  logIdx: number;
  editForm: { startDt: string; endDt: string; desc: string };
  setEditForm: React.Dispatch<React.SetStateAction<{ startDt: string; endDt: string; desc: string }>>;
  onSave: (logIdx: number) => void;
  onCancel: () => void;
  onOpenAtt: (idx: number) => void;
  onOpenPr: (idx: number) => void;
  onOpenCr: (idx: number) => void;
  attCount: number;
  prCount: number;
  crCount: number;
}

function EditLogRow({ logIdx, editForm, setEditForm, onSave, onCancel, onOpenAtt, onOpenPr, onOpenCr, attCount, prCount, crCount }: EditLogRowProps) {
  const hours = calcHours(editForm.startDt, editForm.endDt);
  return (
    <div className="space-y-1.5 rounded-md border border-emerald-300 bg-emerald-50/50 p-2.5 dark:border-emerald-800 dark:bg-emerald-900/10">
      <div className="flex flex-wrap items-center gap-1.5">
        <Input type="datetime-local" value={editForm.startDt} onChange={(e) => setEditForm((p) => ({ ...p, startDt: e.target.value }))} className="h-6 w-[155px] text-[11px]" />
        <span className="text-xs text-muted-foreground">~</span>
        <Input type="datetime-local" value={editForm.endDt} onChange={(e) => setEditForm((p) => ({ ...p, endDt: e.target.value }))} className="h-6 w-[155px] text-[11px]" />
        <span className="min-w-[30px] text-xs font-semibold text-primary">{hours}h</span>
      </div>
      <Textarea
        value={editForm.desc}
        onChange={(e) => setEditForm((p) => ({ ...p, desc: e.target.value }))}
        placeholder="수행 내용을 입력하세요..."
        className="min-h-[32px] text-xs"
        rows={1}
      />
      <div className="flex items-center justify-between">
        <div className="flex gap-1">
          <Button variant="outline" size="sm" className="h-5 px-1.5 text-[9px]" onClick={() => onOpenAtt(logIdx)}>
            <Paperclip className="mr-0.5 h-2.5 w-2.5" />첨부{attCount > 0 && ` (${attCount})`}
          </Button>
          <Button variant="outline" size="sm" className="h-5 px-1.5 text-[9px]" onClick={() => onOpenPr(logIdx)}>
            <ShoppingCart className="mr-0.5 h-2.5 w-2.5" />구매{prCount > 0 && ` (${prCount})`}
          </Button>
          <Button variant="outline" size="sm" className="h-5 px-1.5 text-[9px]" onClick={() => onOpenCr(logIdx)}>
            <Handshake className="mr-0.5 h-2.5 w-2.5" />협조{crCount > 0 && ` (${crCount})`}
          </Button>
        </div>
        <div className="flex gap-1">
          <Button variant="outline" size="sm" className="h-5 px-2 text-[10px]" onClick={onCancel}>취소</Button>
          <Button size="sm" className="h-5 bg-emerald-500 px-2 text-[10px] hover:bg-emerald-600" onClick={() => onSave(logIdx)}>저장</Button>
        </div>
      </div>
    </div>
  );
}
