/* WAREHOUSE OS 2.0 · PERSONAL TASK
   Mobile-first Swiss workspace shared by the standalone TASK route and Records. */
(() => {
const W2 = window.W2;
const { t } = window.W2_LANG;
const { useState: S, useEffect: E, useMemo: M, useCallback: C, useRef: R } = React;
const { Icon: I, Btn: B, Label: L, Empty } = W2;

window.W2_LANG.addEN({
  "任務": "Tasks", "任務與計劃": "Tasks & plans", "個人行動中心": "Personal action centre",
  "今天": "Today", "收件匣": "Inbox", "日曆": "Calendar", "計劃": "Plans", "透視": "Insights",
  "所有與你相關的操作、安排與檔案跟進，都在同一條時間線。": "Every operation, schedule and record follow-up connected to you, on one timeline.",
  "新增": "Add", "新增任務": "New task", "新增日程": "New event", "新增計劃": "New plan",
  "建立跟進任務": "Create follow-up task", "從此檔案建立跟進任務": "Create a follow-up task from this record",
  "任務標題": "Task title", "日程名稱": "Event title", "計劃名稱": "Plan title",
  "說明與交付標準": "Notes & completion criteria", "開始": "Start", "截止": "Due",
  "全天": "All day", "時間": "Time", "優先級": "Priority", "負責人": "Assignee",
  "負責部門": "Owning team",
  "類別": "Category", "可見範圍": "Visibility", "地點": "Location", "選填地點或會議連結": "Optional place or meeting link",
  "工作": "Work", "會議": "Meeting", "出差": "Travel", "考試": "Exam", "個人": "Personal", "檔案": "Record", "其他": "Other",
  "僅自己": "Private", "團隊": "Team", "全公司": "Company",
  "未指定": "Unassigned", "所屬計劃": "Plan", "不加入計劃": "No plan",
  "本人（預設）": "Myself (default)",
  "建立任務": "Create task", "建立日程": "Create event", "建立計劃": "Create plan",
  "建立中…": "Creating…", "任務已建立": "Task created", "無法建立任務": "Could not create task",
  "任務資料暫時無法載入": "Tasks are temporarily unavailable", "任務服務尚未啟用或暫時無法連線。": "The task service is not enabled or is temporarily unreachable.",
  "重新載入": "Reload", "同步中": "Syncing", "剛剛同步": "Synced just now",
  "待開始": "Planned", "進行中": "In progress", "已暫停": "Paused", "已完成": "Completed", "已取消": "Cancelled",
  "立即開始": "Start now", "繼續": "Resume", "暫停": "Pause", "等待": "Wait", "完成": "Complete", "取消任務": "Cancel task",
  "狀態更新失敗": "Could not update status", "逾期": "Overdue", "今天截止": "Due today", "即將開始": "Starting soon",
  "高": "High", "普通": "Normal", "低": "Low", "緊急": "Urgent",
  "我的今日": "My day", "今日重點": "Today focus", "已排程": "Scheduled", "未完成": "Open", "完成率": "Completion",
  "今日沒有排定任務": "Nothing scheduled today", "把下一個行動放進今天，或從檔案建立跟進。": "Add the next action to today, or create a follow-up from a record.",
  "查看全部任務": "View all tasks", "全部": "All", "未結束": "Open", "已結束": "Closed",
  "搜尋任務、計劃或來源": "Search tasks, plans or sources", "沒有符合條件的任務": "No tasks match these filters",
  "清除篩選": "Clear filters", "本月": "This month", "上一個月": "Previous month", "下一個月": "Next month",
  "當日安排": "Day agenda", "這一天尚無安排": "No schedule for this day", "加入這一天": "Add to this day",
  "週一": "MON", "週二": "TUE", "週三": "WED", "週四": "THU", "週五": "FRI", "週六": "SAT", "週日": "SUN",
  "沒有計劃": "No plans yet", "用計劃把多個任務組成一段可追蹤的工作。": "Use a plan to turn related tasks into trackable work.",
  "未歸入計劃": "No plan", "項任務": "tasks", "來源分佈": "Source mix", "類別分佈": "Category mix", "狀態結構": "Status structure",
  "未來七日負載": "Next 7 days", "過去七日完成": "Completed in 7 days", "本週容量": "Weekly capacity",
  "無排程": "Unscheduled", "來自檔案": "From records", "系統操作": "System operation", "個人建立": "Personal",
  "來源": "Source", "打開來源": "Open source", "更多狀態": "More status actions", "關閉": "Close",
  "需要標題": "A title is required", "截止時間不得早於開始時間": "Due time cannot be earlier than the start time",
  "日": "day", "月": "month", "年": "year", "今日": "TODAY", "已完成工作": "Completed work",
  "管理任務、日程與部門計劃，並保留與原始檔案的關聯。": "Manage tasks, events and team plans while preserving links to source records.",
  "資料權限由任務發起人、負責人、參與者與部門範圍共同決定。": "Visibility follows creator, assignee, participant and department scope.",
  "選擇類型": "Choose type", "任務 API 回傳格式不完整": "The task API returned an incomplete response",
});

const arr = value => Array.isArray(value) ? value : [];
const obj = value => value && typeof value === "object" && !Array.isArray(value) ? value : {};
const first = (...values) => values.find(value => value !== undefined && value !== null && value !== "");
const key = value => String(value == null ? "" : value).trim().toLowerCase();
const pad = value => String(value).padStart(2, "0");
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
const number = value => Number.isFinite(Number(value)) ? Number(value) : 0;
const dateObject = value => {
  if (!value) return null;
  let date;
  if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)) {
    const parts = value.split("-").map(Number);
    date = new Date(parts[0], parts[1] - 1, parts[2], 12, 0, 0);
  } else date = value instanceof Date ? new Date(value.getTime()) : new Date(value);
  return Number.isNaN(date.getTime()) ? null : date;
};
const dateKey = value => {
  const date = dateObject(value);
  return date ? `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` : "";
};
const todayKey = () => dateKey(new Date());
const addDays = (value, amount) => {
  const date = dateObject(value) || new Date();
  date.setDate(date.getDate() + amount);
  return date;
};
const dayDiff = value => {
  const date = dateObject(value);
  if (!date) return null;
  const target = new Date(date.getFullYear(), date.getMonth(), date.getDate());
  const now = new Date();
  const base = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  return Math.round((target - base) / 86400000);
};
const coversDay = (task, token) => {
  const start = dateKey(task && (task.start || task.due));
  const end = dateKey(task && (task.due || task.start));
  if (!start && !end) return false;
  const lower = start || end;
  const upper = end && end >= lower ? end : lower;
  return token >= lower && token <= upper;
};
const locale = () => ({ tw: "zh-TW", cn: "zh-CN", en: "en-US" }[window.W2_LANG.lang()] || "zh-TW");
const dayLabel = value => {
  const date = dateObject(value);
  if (!date) return t("無排程");
  return new Intl.DateTimeFormat(locale(), { month: "short", day: "numeric", weekday: "short" }).format(date);
};
const timeLabel = (value, allDay) => {
  const date = dateObject(value);
  if (!date) return "—";
  if (allDay) return t("全天");
  return new Intl.DateTimeFormat(locale(), { hour: "2-digit", minute: "2-digit", hour12: false }).format(date);
};
const monthLabel = value => new Intl.DateTimeFormat(locale(), { year: "numeric", month: "long" }).format(value);
const inputDate = value => dateKey(value);
const inputTime = value => {
  const date = dateObject(value);
  return date ? `${pad(date.getHours())}:${pad(date.getMinutes())}` : "09:00";
};
const toISO = (day, time, allDay, endOfDay) => {
  if (!day) return null;
  const parts = day.split("-").map(Number);
  const clock = allDay ? (endOfDay ? [23, 59] : [0, 0]) : String(time || "09:00").split(":").map(Number);
  const date = new Date(parts[0], parts[1] - 1, parts[2], clock[0] || 0, clock[1] || 0, endOfDay && allDay ? 59 : 0);
  return date.toISOString();
};
const isTerminal = status => ["completed", "cancelled"].includes(status);
const canonicalStatus = value => ({
  todo: "planned", open: "planned", pending: "planned", assigned: "planned", scheduled: "planned", planned: "planned",
  active: "active", doing: "active", in_progress: "active", started: "active",
  paused: "paused", waiting: "paused", blocked: "paused", on_hold: "paused",
  completed: "completed", complete: "completed", done: "completed", closed: "completed",
  cancelled: "cancelled", canceled: "cancelled",
}[key(value)] || key(value) || "planned");
const actionStatus = value => {
  const normalized = key(value);
  if (["start", "resume", "activate", "in_progress"].includes(normalized)) return "active";
  if (["pause", "wait", "waiting", "hold"].includes(normalized)) return "paused";
  if (["complete", "completed", "done", "close"].includes(normalized)) return "completed";
  if (["cancel", "cancelled", "canceled"].includes(normalized)) return "cancelled";
  const mapped = canonicalStatus(normalized);
  return ["planned", "active", "paused", "completed", "cancelled"].includes(mapped) ? mapped : "";
};
const statusLabel = status => ({ planned: "待開始", active: "進行中", paused: "已暫停", completed: "已完成", cancelled: "已取消" }[status] || status);
const statusTone = status => ({ planned: "plan", active: "active", paused: "pause", completed: "done", cancelled: "cancel" }[status] || "plan");
const priorityValue = value => {
  const normalized = key(value);
  if (["urgent", "critical", "p0", "0"].includes(normalized)) return "urgent";
  if (["high", "p1", "1"].includes(normalized)) return "high";
  if (["low", "p3", "3"].includes(normalized)) return "low";
  return "normal";
};
const priorityLabel = value => ({ urgent: "緊急", high: "高", normal: "普通", low: "低" }[value] || value);
const categoryLabel = value => ({ meeting: "會議", travel: "出差", exam: "考試", work: "工作", personal: "個人", record: "檔案", other: "其他" }[key(value)] || value || "其他");
const kindValue = task => {
  const value = key(first(task.kind, task.task_kind, task.task_type, task.type, task.category));
  if (["event", "calendar", "calendar_event", "meeting", "travel", "exam"].includes(value)) return "event";
  if (["plan", "project", "milestone"].includes(value)) return "plan";
  return "task";
};
const tasksFrom = data => {
  if (Array.isArray(data)) return data;
  const source = obj(data);
  return arr(first(source.tasks, source.items, source.results, source.data));
};
const taskFrom = data => {
  const source = obj(data);
  return obj(first(source.task, source.item, source.result, source.data, source));
};
const normalizeTask = rawValue => {
  const raw = obj(rawValue);
  const sourceValue = first(raw.source, raw.linked_entity, raw.origin, {});
  const source = obj(sourceValue);
  const assignee = obj(first(raw.assignee, arr(raw.assignees)[0], raw.owner, raw.responsible_user, {}));
  const plan = obj(first(raw.plan, raw.parent_plan, {}));
  const start = first(raw.starts_at, raw.start_at, raw.scheduled_start, raw.planned_start, raw.start_date);
  const due = first(raw.due_at, raw.ends_at, raw.end_at, raw.scheduled_end, raw.planned_end, raw.due_date, raw.end_date, start);
  const sourceType = String(first(raw.source_type, typeof sourceValue === "string" ? sourceValue : null, source.type, ""));
  const implicitSourceRef = typeof sourceValue === "string" && raw.source_id != null ? `${sourceValue}:${raw.source_id}` : "";
  const explicitSourceRef = String(first(raw.source_ref, raw.source_entity_ref, raw.entity_ref, source.entity_ref, source.ref, ""));
  const sourceRef = explicitSourceRef || (["task", "native", "personal"].includes(key(sourceType)) ? "" : implicitSourceRef);
  return {
    raw,
    id: first(raw.id, raw.task_id, raw.uuid, raw.key),
    title: String(first(raw.title, raw.task_name, raw.name, raw.summary, t("未命名"))),
    description: String(first(raw.description, raw.notes, raw.detail, raw.details, "")),
    kind: kindValue(raw), category: key(first(raw.category, "other")), status: canonicalStatus(raw.status), priority: priorityValue(raw.priority),
    start, due, allDay: raw.all_day === true || raw.is_all_day === true,
    assigneeId: first(raw.assignee_user_id, raw.assignee_id, assignee.id, assignee.user_id),
    assigneeName: String(first(raw.assignee_name, raw.assignee_user_name, assignee.display_name, assignee.name, assignee.username, "")),
    creatorName: String(first(raw.creator_name, raw.created_by_name, raw.reporter_name, "")),
    planId: first(raw.plan_id, raw.parent_plan_id, plan.id, plan.plan_id),
    planTitle: String(first(raw.plan_title, raw.plan_name, plan.title, plan.name, "")),
    sourceRef,
    sourceTitle: String(first(raw.source_title, raw.source_name, source.title, source.name, "")),
    sourceType,
    completedAt: first(raw.completed_at, raw.closed_at),
    progress: clamp(number(first(raw.progress, raw.progress_percent, 0)), 0, 100),
    actions: arr(first(raw.actions, obj(raw.capabilities).actions)).map(action => actionStatus(typeof action === "object" ? first(action.status, action.target_status, action.action, action.key) : action)).filter(Boolean),
    lockVersion: first(raw.lock_version, raw.version),
    canUpdate: raw.read_only !== true && obj(raw.capabilities).can_update === true && first(raw.lock_version, raw.version) != null,
  };
};
const uniqueTasks = values => {
  const seen = new Set();
  return values.filter(task => {
    const token = String(first(task.id, `${task.title}|${task.start}|${task.due}`));
    if (seen.has(token)) return false;
    seen.add(token); return true;
  });
};
const usersFromMeta = meta => arr(first(meta.users, meta.assignees, obj(meta.options).assignees, obj(meta.data).users));
const plansFromMeta = meta => arr(first(meta.plans, obj(meta.options).plans, obj(meta.data).plans));
const orgUnitsFromMeta = meta => arr(first(meta.org_units, meta.owner_org_units, obj(meta.options).org_units, obj(meta.data).org_units));
const capabilitiesFromMeta = meta => obj(first(meta.capabilities, obj(meta.data).capabilities, meta.permissions));
const clientRequestId = () => {
  try { if (window.crypto && typeof window.crypto.randomUUID === "function") return window.crypto.randomUUID(); } catch (error) {}
  return `task-${Date.now()}-${Math.random().toString(16).slice(2)}`;
};
const idValue = value => /^\d+$/.test(String(value || "")) ? Number(value) : (value || null);
const sourceParts = value => {
  const match = String(value || "").match(/^([a-z][a-z0-9_-]*):(.+)$/i);
  if (!match) return { type: null, id: null };
  return { type: match[1].toLowerCase(), id: /^\d+$/.test(match[2]) ? Number(match[2]) : match[2] };
};

const queryFromHash = () => {
  const raw = String(location.hash || "").split("?")[1] || "";
  try { return new URLSearchParams(raw); } catch (error) { return new URLSearchParams(); }
};
const composerHref = options => {
  const params = new URLSearchParams();
  params.set("view", options && options.view || "inbox");
  params.set("create", options && options.mode || "task");
  Object.entries(obj(options)).forEach(([name, value]) => {
    if (["view", "mode"].includes(name) || value == null || value === "") return;
    params.set(name, String(value));
  });
  return "#/tasks?" + params.toString();
};
W2.openTaskComposer = options => {
  const detail = obj(options);
  if ((location.hash || "").startsWith("#/tasks")) {
    window.dispatchEvent(new CustomEvent("w2-open-task-composer", { detail }));
    return;
  }
  location.hash = composerHref(detail);
};

const SourceMark = ({ task }) => {
  if (!task.sourceRef && !task.sourceTitle) return null;
  const open = event => {
    event.stopPropagation();
    const parsed = W2.parseEntityRef && W2.parseEntityRef(task.sourceRef);
    if (parsed && parsed.type === "record") {
      location.hash = `#/cases?record=${encodeURIComponent(parsed.id)}`;
      return;
    }
    if (parsed && parsed.type === "ai_confirmation_action" && W2.openSecretary) {
      W2.openSecretary("");
      return;
    }
    if (task.sourceRef && W2.openEntity && W2.openEntity(task.sourceRef)) return;
    const fallbackRoute = parsed && ({ alert: "alerts", work_task: "erp", wf_task: "procurement" }[parsed.type]);
    if (fallbackRoute) location.hash = `#/${fallbackRoute}`;
  };
  return <button type="button" className="task-source" onClick={open} disabled={!task.sourceRef} title={t("打開來源")}>
    <I name="doc" size={11}/><span>{task.sourceTitle || task.sourceRef}</span><I name="arrow" size={10}/>
  </button>;
};

const TaskCard = ({ task, onStatus, busy, compact = false }) => {
  const dueDiff = dayDiff(task.due);
  const overdue = !isTerminal(task.status) && dueDiff != null && dueDiff < 0;
  const dueToday = !isTerminal(task.status) && dueDiff === 0;
  const allowed = status => !task.actions.length || task.actions.includes(status);
  const options = (task.status === "planned" ? [["active", "立即開始"], ["paused", "等待"], ["completed", "完成"], ["cancelled", "取消任務"]]
    : task.status === "active" ? [["completed", "完成"], ["paused", "暫停"], ["cancelled", "取消任務"]]
    : task.status === "paused" ? [["active", "繼續"], ["completed", "完成"], ["cancelled", "取消任務"]] : []).filter(([status]) => allowed(status));
  const primary = options[0] || [];
  const next = primary[0] || "";
  const nextLabel = t(primary[1] || "");
  const alternatives = options.slice(1);
  return <article className={`task-card is-${statusTone(task.status)} priority-${task.priority}${compact ? " compact" : ""}`}>
    <div className="task-card-rule"/>
    <div className="task-card-main">
      <div className="task-card-meta">
        <span className={`task-status is-${statusTone(task.status)}`}><i/>{t(statusLabel(task.status))}</span>
        <span className="task-category">{t(categoryLabel(task.category))}</span>
        {task.priority !== "normal" && <span className={`task-priority ${task.priority}`}>{t(priorityLabel(task.priority))}</span>}
        {overdue && <span className="task-deadline danger">{t("逾期")} {Math.abs(dueDiff)}D</span>}
        {dueToday && <span className="task-deadline">{t("今天截止")}</span>}
      </div>
      <h3>{task.title}</h3>
      {!compact && task.description && <p>{task.description}</p>}
      <div className="task-card-facts">
        <span><I name="clock" size={12}/>{task.start ? `${dayLabel(task.start)} · ${timeLabel(task.start, task.allDay)}` : t("無排程")}</span>
        {task.assigneeName && <span><I name="user" size={12}/>{task.assigneeName}</span>}
        {task.planTitle && <span><I name="layers" size={12}/>{task.planTitle}</span>}
      </div>
      <SourceMark task={task}/>
    </div>
    {task.canUpdate && !isTerminal(task.status) && <div className="task-card-actions">
      {next && <button type="button" className="task-action-primary" disabled={busy} onClick={() => onStatus(task, next)}>
        <I name={next === "completed" ? "check" : "arrow"} size={13}/>{busy ? "…" : nextLabel}
      </button>}
      {!!alternatives.length && <details className="task-action-more"><summary aria-label={t("更多狀態")}>•••</summary><div>{alternatives.map(([status, label]) => <button type="button" key={status} disabled={busy} onClick={() => onStatus(task, status)}>{t(label)}</button>)}</div></details>}
    </div>}
  </article>;
};

const ViewEmpty = ({ view, onCreate }) => {
  const copy = view === "today"
    ? ["clock", "今日沒有排定任務", "把下一個行動放進今天，或從檔案建立跟進。"]
    : view === "plans" ? ["layers", "沒有計劃", "用計劃把多個任務組成一段可追蹤的工作。"]
    : ["clipboard", "沒有符合條件的任務", "所有與你相關的操作、安排與檔案跟進，都在同一條時間線。"];
  return <Empty icon={copy[0]} title={t(copy[1])} sub={t(copy[2])} action={onCreate ? <B kind="primary" icon="plus" onClick={onCreate}>{t("新增")}</B> : null}/>;
};

const TodayView = ({ tasks, onStatus, busyId, onCreate, onViewAll }) => {
  const today = todayKey();
  const scheduled = tasks.filter(task => coversDay(task, today));
  const overdue = tasks.filter(task => !isTerminal(task.status) && dateKey(task.due) && dateKey(task.due) < today);
  const visible = uniqueTasks([...overdue, ...scheduled]).sort((a, b) => String(a.start || a.due || "").localeCompare(String(b.start || b.due || "")));
  const open = visible.filter(task => !isTerminal(task.status)).length;
  const completed = visible.filter(task => task.status === "completed").length;
  const rate = visible.length ? Math.round(completed / visible.length * 100) : 0;
  return <div className="task-view task-today-view">
    <section className="task-day-poster">
      <div><L red>{t("我的今日")}</L><h2>{new Intl.DateTimeFormat(locale(), { weekday: "long", month: "long", day: "numeric" }).format(new Date())}</h2></div>
      <div className="task-day-score"><strong>{pad(open)}</strong><span>{t("未完成")}</span></div>
      <div className="task-day-score"><strong>{pad(completed)}</strong><span>{t("已完成")}</span></div>
      <div className="task-day-score red"><strong>{rate}<small>%</small></strong><span>{t("完成率")}</span></div>
    </section>
    <div className="task-section-head"><div><span>01</span><h2>{t("今日重點")}</h2></div><button type="button" onClick={onViewAll}>{t("查看全部任務")} →</button></div>
    {visible.length ? <div className="task-list">{visible.map(task => <TaskCard key={task.id || task.title} task={task} busy={busyId === task.id} onStatus={onStatus}/>)}</div> : <ViewEmpty view="today" onCreate={onCreate}/>}
  </div>;
};

const InboxView = ({ tasks, onStatus, busyId, onCreate }) => {
  const [filter, setFilter] = S("open");
  const [search, setSearch] = S("");
  const shown = tasks.filter(task => {
    if (filter === "open" && isTerminal(task.status)) return false;
    if (filter === "closed" && !isTerminal(task.status)) return false;
    const needle = key(search);
    return !needle || key([task.title, task.description, task.planTitle, task.sourceTitle, task.assigneeName].join(" ")).includes(needle);
  }).sort((a, b) => {
    const terminal = Number(isTerminal(a.status)) - Number(isTerminal(b.status));
    if (terminal) return terminal;
    const priority = { urgent: 0, high: 1, normal: 2, low: 3 };
    return priority[a.priority] - priority[b.priority] || String(a.due || "9999").localeCompare(String(b.due || "9999"));
  });
  return <div className="task-view">
    <div className="task-filterbar">
      <div className="task-segments" role="tablist" aria-label={t("收件匣")}>{[["all", "全部"], ["open", "未結束"], ["closed", "已結束"]].map(([id, label]) => <button type="button" role="tab" aria-selected={filter === id} className={filter === id ? "on" : ""} key={id} onClick={() => setFilter(id)}>{t(label)}<b>{id === "all" ? tasks.length : tasks.filter(task => id === "closed" ? isTerminal(task.status) : !isTerminal(task.status)).length}</b></button>)}</div>
      <label className="task-search"><I name="search" size={14}/><input value={search} onChange={event => setSearch(event.target.value)} placeholder={t("搜尋任務、計劃或來源")}/>{search && <button type="button" onClick={() => setSearch("")} aria-label={t("清除篩選")}><I name="x" size={12}/></button>}</label>
    </div>
    {shown.length ? <div className="task-list">{shown.map(task => <TaskCard key={task.id || task.title} task={task} busy={busyId === task.id} onStatus={onStatus}/>)}</div> : <ViewEmpty view="inbox" onCreate={onCreate}/>}
  </div>;
};

const CalendarView = ({ tasks, onStatus, busyId, onCreate }) => {
  const now = new Date();
  const [month, setMonth] = S(new Date(now.getFullYear(), now.getMonth(), 1));
  const [selected, setSelected] = S(todayKey());
  const firstDay = new Date(month.getFullYear(), month.getMonth(), 1);
  const offset = (firstDay.getDay() + 6) % 7;
  const gridStart = addDays(firstDay, -offset);
  const days = Array.from({ length: 42 }, (_, index) => addDays(gridStart, index));
  const byDay = M(() => {
    const map = {};
    tasks.forEach(task => {
      const start = dateObject(task.start || task.due);
      const end = dateObject(task.due || task.start) || start;
      if (!start) return;
      const cursor = new Date(start.getFullYear(), start.getMonth(), start.getDate(), 12);
      const rawLast = end >= start ? end : start;
      const last = new Date(rawLast.getFullYear(), rawLast.getMonth(), rawLast.getDate(), 12);
      for (let days = 0; cursor <= last && days < 367; days += 1) {
        const value = dateKey(cursor);
        (map[value] = map[value] || []).push(task);
        cursor.setDate(cursor.getDate() + 1);
      }
    });
    return map;
  }, [tasks]);
  const agenda = arr(byDay[selected]).slice().sort((a, b) => String(a.start || "").localeCompare(String(b.start || "")));
  const move = delta => setMonth(current => new Date(current.getFullYear(), current.getMonth() + delta, 1));
  return <div className="task-view task-calendar-view">
    <section className="task-calendar">
      <header><div><L red>CALENDAR</L><h2>{monthLabel(month)}</h2></div><div className="task-calendar-controls"><button type="button" onClick={() => { setMonth(new Date(now.getFullYear(), now.getMonth(), 1)); setSelected(todayKey()); }}>{t("本月")}</button><button type="button" onClick={() => move(-1)} aria-label={t("上一個月")}>←</button><button type="button" onClick={() => move(1)} aria-label={t("下一個月")}>→</button></div></header>
      <div className="task-weekdays">{["週一", "週二", "週三", "週四", "週五", "週六", "週日"].map(day => <span key={day}>{t(day)}</span>)}</div>
      <div className="task-month-grid">{days.map(day => {
        const token = dateKey(day); const items = arr(byDay[token]); const outside = day.getMonth() !== month.getMonth();
        return <button type="button" key={token} className={`${selected === token ? "selected " : ""}${token === todayKey() ? "today " : ""}${outside ? "outside" : ""}`} onClick={() => setSelected(token)}>
          <span className="task-day-number">{day.getDate()}</span><span className="task-day-dots">{items.slice(0, 3).map((item, index) => <i key={item.id || index} className={`is-${statusTone(item.status)}`}/>)}</span>{items.length > 3 && <small>+{items.length - 3}</small>}
        </button>;
      })}</div>
    </section>
    <aside className="task-agenda"><div className="task-section-head"><div><span>02</span><h2>{t("當日安排")}</h2></div>{onCreate && <button type="button" onClick={() => onCreate("event", { date: selected })}>+ {t("加入這一天")}</button>}</div><div className="task-agenda-date">{dayLabel(selected + "T12:00:00")}</div>{agenda.length ? <div className="task-list compact-list">{agenda.map(task => <TaskCard compact key={task.id || task.title} task={task} busy={busyId === task.id} onStatus={onStatus}/>)}</div> : <Empty icon="clock" title={t("這一天尚無安排")}/>}</aside>
  </div>;
};

const PlansView = ({ tasks, meta, onStatus, busyId, onCreate }) => {
  const metaPlans = plansFromMeta(meta).map(item => ({ id: first(item.id, item.plan_id), title: String(first(item.title, item.name, item.plan_name, t("未命名"))), raw: item }));
  const planItems = tasks.filter(task => task.kind === "plan").map(task => ({ id: task.id, title: task.title, raw: task.raw, progress: task.progress }));
  const derived = tasks.filter(task => task.planId || task.planTitle).map(task => ({ id: task.planId || task.planTitle, title: task.planTitle || String(task.planId), raw: {} }));
  const plans = [];
  [...metaPlans, ...planItems, ...derived].forEach(plan => { if (!plans.some(item => String(item.id) === String(plan.id))) plans.push(plan); });
  const unplanned = tasks.filter(task => task.kind !== "plan" && !task.planId && !task.planTitle && !isTerminal(task.status));
  if (!plans.length && !unplanned.length) return <div className="task-view"><ViewEmpty view="plans" onCreate={onCreate ? () => onCreate("plan") : null}/></div>;
  const groups = [...plans.map(plan => ({ ...plan, tasks: tasks.filter(task => task.kind !== "plan" && (String(task.planId || task.planTitle) === String(plan.id) || (!task.planId && task.planTitle === plan.title))) })), ...(unplanned.length ? [{ id: "__none", title: t("未歸入計劃"), tasks: unplanned }] : [])];
  return <div className="task-view task-plans-view"><div className="task-section-head"><div><span>01</span><h2>{t("計劃")}</h2></div>{onCreate && <button type="button" onClick={() => onCreate("plan")}>+ {t("新增計劃")}</button>}</div><div className="task-plan-grid">{groups.map((plan, index) => {
    const complete = plan.tasks.filter(task => task.status === "completed").length;
    const progress = plan.tasks.length ? Math.round(complete / plan.tasks.length * 100) : number(first(plan.progress, plan.raw.progress, plan.raw.progress_percent));
    return <section className="task-plan" key={String(plan.id)}><header><span className="task-plan-index">P{pad(index + 1)}</span><span className="task-plan-progress">{progress}%</span></header><h2>{plan.title}</h2><div className="task-progress"><i style={{ width: `${clamp(progress, 0, 100)}%` }}/></div><div className="task-plan-meta"><span>{plan.tasks.length} {t("項任務")}</span><span>{complete} {t("已完成")}</span></div>{plan.tasks.length ? <div className="task-list compact-list">{plan.tasks.slice(0, 5).map(task => <TaskCard compact key={task.id || task.title} task={task} busy={busyId === task.id} onStatus={onStatus}/>)}</div> : <div className="task-plan-empty">—</div>}</section>;
  })}</div></div>;
};

const Bar = ({ label, value, total, red }) => <div className="task-bar"><div><span>{label}</span><b>{value}</b></div><i><em style={{ width: `${total ? value / total * 100 : 0}%`, background: red ? "var(--red)" : "var(--ink)" }}/></i></div>;
const InsightsView = ({ tasks }) => {
  const measurable = tasks.filter(task => task.kind !== "plan" && task.status !== "cancelled");
  const completed = measurable.filter(task => task.status === "completed");
  const completion = measurable.length ? Math.round(completed.length / measurable.length * 100) : 0;
  const last7 = Array.from({ length: 7 }, (_, index) => addDays(new Date(), index - 6));
  const next7 = Array.from({ length: 7 }, (_, index) => addDays(new Date(), index));
  const completeSeries = last7.map(day => completed.filter(task => dateKey(task.completedAt || task.due) === dateKey(day)).length);
  const loadSeries = next7.map(day => tasks.filter(task => !isTerminal(task.status) && coversDay(task, dateKey(day))).length);
  const maxLoad = Math.max(1, ...loadSeries, ...completeSeries);
  const sourceCounts = tasks.reduce((map, task) => {
    const source = task.sourceRef ? (task.sourceRef.startsWith("record:") ? t("來自檔案") : t("系統操作")) : t("個人建立");
    map[source] = (map[source] || 0) + 1; return map;
  }, {});
  const categoryCounts = tasks.reduce((map, task) => {
    const label = t(categoryLabel(task.category));
    map[label] = (map[label] || 0) + 1; return map;
  }, {});
  return <div className="task-view task-insights-view">
    <section className="task-insight-lead"><div><L red>{t("完成率")}</L><strong>{completion}<small>%</small></strong></div><p>{t("已完成工作")}<b>{completed.length}</b><br/>{t("未完成")}<b>{measurable.filter(task => !isTerminal(task.status)).length}</b></p></section>
    <div className="task-insight-grid">
      <section><div className="task-section-head"><div><span>01</span><h2>{t("狀態結構")}</h2></div></div>{["planned", "active", "paused", "completed", "cancelled"].map((status, index) => <Bar key={status} label={t(statusLabel(status))} value={tasks.filter(task => task.status === status).length} total={tasks.length} red={index === 1}/>)}</section>
      <section><div className="task-section-head"><div><span>02</span><h2>{t("類別分佈")}</h2></div></div>{Object.entries(categoryCounts).map(([label, value]) => <Bar key={label} label={label} value={value} total={tasks.length}/>)}</section>
      <section><div className="task-section-head"><div><span>03</span><h2>{t("來源分佈")}</h2></div></div>{Object.entries(sourceCounts).map(([label, value]) => <Bar key={label} label={label} value={value} total={tasks.length}/>)}</section>
      <section className="wide"><div className="task-section-head"><div><span>04</span><h2>{t("未來七日負載")}</h2></div><b>{loadSeries.reduce((sum, value) => sum + value, 0)} {t("項任務")}</b></div><div className="task-week-chart">{next7.map((day, index) => <div key={dateKey(day)}><span className="task-chart-value">{loadSeries[index]}</span><i><em style={{ height: `${loadSeries[index] / maxLoad * 100}%` }}/></i><small>{new Intl.DateTimeFormat(locale(), { weekday: "short" }).format(day)}</small></div>)}</div></section>
    </div>
  </div>;
};

const categoriesFor = (mode, sourceRef) => mode === "event"
  ? ["meeting", "travel", "exam", "personal", "work"]
  : mode === "plan" ? ["work", "personal"]
  : (String(sourceRef || "").startsWith("record:") ? ["record", "work", "other"] : ["work", "record", "other"]);
const initialForm = (mode, seed = {}) => {
  const day = seed.date || todayKey();
  const sourceRef = seed.source_ref || seed.sourceRef || "";
  const category = seed.category || categoriesFor(mode || "task", sourceRef)[0];
  return {
    mode: mode || "task", title: seed.title || seed.source_title || "", description: "", priority: "normal", category,
    startDate: day, startTime: "09:00", dueDate: day, dueTime: mode === "event" ? "10:00" : "17:00", allDay: mode === "plan",
    visibility: "private", location: "", assigneeId: "", ownerOrgUnitId: "", planId: "", sourceRef, sourceTitle: seed.source_title || seed.sourceTitle || "",
  };
};
const TaskComposer = ({ mode: initialMode, seed, requestKey, meta, tasks, onClose, onCreated }) => {
  const [form, setForm] = S(() => initialForm(initialMode, seed));
  const [busy, setBusy] = S(false);
  const [error, setError] = S("");
  const requestId = R(requestKey || clientRequestId());
  const capabilities = capabilitiesFromMeta(meta);
  const canManage = capabilities.can_manage === true;
  const canAssign = canManage || capabilities.can_assign === true;
  const users = usersFromMeta(meta);
  const orgUnits = orgUnitsFromMeta(meta);
  const plans = [];
  [...plansFromMeta(meta), ...tasks.filter(task => task.kind === "plan").map(task => ({ id: task.id, title: task.title }))].forEach(plan => {
    const id = first(plan.id, plan.plan_id);
    if (id != null && !plans.some(existing => String(first(existing.id, existing.plan_id)) === String(id))) plans.push(plan);
  });
  const update = (name, value) => setForm(current => ({ ...current, [name]: value }));
  const setMode = mode => setForm(current => ({ ...initialForm(mode, { ...seed, date: current.startDate, sourceRef: current.sourceRef }), title: current.title, description: current.description, sourceRef: current.sourceRef, sourceTitle: current.sourceTitle, visibility: current.visibility }));
  const submit = async event => {
    event.preventDefault(); setError("");
    if (!form.title.trim()) { setError(t("需要標題")); return; }
    const startsAt = toISO(form.startDate, form.startTime, form.allDay, false);
    const dueAt = toISO(form.dueDate, form.dueTime, form.allDay, true);
    if (startsAt && dueAt && new Date(dueAt) < new Date(startsAt)) { setError(t("截止時間不得早於開始時間")); return; }
    const source = sourceParts(form.sourceRef);
    const payload = {
      title: form.title.trim(), description: form.description.trim() || null,
      kind: form.mode, category: form.category, status: "planned", priority: form.priority, visibility: form.visibility,
      start_at: startsAt, end_at: ["event", "plan"].includes(form.mode) ? dueAt : null, due_at: form.mode === "task" ? dueAt : null,
      all_day: !!form.allDay, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC", location: form.location.trim() || null,
      owner_org_unit_id: idValue(form.ownerOrgUnitId), plan_id: idValue(form.planId), assignees: form.assigneeId ? [idValue(form.assigneeId)] : [],
      client_request_id: requestId.current, source_type: source.type, source_entity_id: source.id,
    };
    setBusy(true);
    const submitTenant = W2.tenant();
    try {
      const data = await W2.post("/api/tasks", payload);
      if (submitTenant === W2.tenant()) await onCreated(taskFrom(data));
    }
    catch (exception) { setError(exception.message || t("無法建立任務")); }
    finally { setBusy(false); }
  };
  return <div className="task-sheet-backdrop" role="presentation" onMouseDown={event => { if (event.target === event.currentTarget) onClose(); }}>
    <section className="task-sheet" role="dialog" aria-modal="true" aria-labelledby="task-composer-title">
      <header><div><L red>NEW ACTION</L><h2 id="task-composer-title">{t(form.mode === "event" ? "新增日程" : form.mode === "plan" ? "新增計劃" : "新增任務")}</h2></div><button type="button" onClick={onClose} aria-label={t("關閉")}><I name="x" size={16}/></button></header>
      <form onSubmit={submit}>
        <div className="task-kind-switch" role="tablist" aria-label={t("選擇類型")}>{[["task", "任務", "clipboard"], ["event", "日曆", "clock"], ["plan", "計劃", "layers"]].map(([id, label, icon]) => <button type="button" role="tab" aria-selected={form.mode === id} className={form.mode === id ? "on" : ""} key={id} onClick={() => setMode(id)}><I name={icon} size={14}/>{t(label)}</button>)}</div>
        {form.sourceRef && <div className="task-linked-source"><I name="doc" size={13}/><div><L dim>{t("來源")}</L><strong>{form.sourceTitle || form.sourceRef}</strong></div></div>}
        <label className="task-field full"><L dim>{t(form.mode === "event" ? "日程名稱" : form.mode === "plan" ? "計劃名稱" : "任務標題")} *</L><input autoFocus maxLength="240" value={form.title} onChange={event => update("title", event.target.value)}/></label>
        <label className="task-field full"><L dim>{t("說明與交付標準")}</L><textarea maxLength="2000" value={form.description} onChange={event => update("description", event.target.value)}/></label>
        <label className="task-field"><L dim>{t("類別")}</L><select value={form.category} onChange={event => update("category", event.target.value)}>{categoriesFor(form.mode, form.sourceRef).map(category => <option key={category} value={category}>{t(categoryLabel(category))}</option>)}</select></label>
        <label className="task-field"><L dim>{t("可見範圍")}</L><select value={form.visibility} onChange={event => update("visibility", event.target.value)}><option value="private">{t("僅自己")}</option>{canAssign && <option value="team">{t("團隊")}</option>}{canManage && <option value="company">{t("全公司")}</option>}</select></label>
        {form.mode === "event" && <label className="task-field full"><L dim>{t("地點")}</L><input maxLength="240" value={form.location} onChange={event => update("location", event.target.value)} placeholder={t("選填地點或會議連結")}/></label>}
        <label className="task-field"><L dim>{t("開始")}</L><input type="date" value={form.startDate} onChange={event => update("startDate", event.target.value)}/></label>
        {!form.allDay && <label className="task-field"><L dim>{t("時間")}</L><input type="time" value={form.startTime} onChange={event => update("startTime", event.target.value)}/></label>}
        <label className="task-field"><L dim>{t("截止")}</L><input type="date" value={form.dueDate} min={form.startDate} onChange={event => update("dueDate", event.target.value)}/></label>
        {!form.allDay && <label className="task-field"><L dim>{t("時間")}</L><input type="time" value={form.dueTime} onChange={event => update("dueTime", event.target.value)}/></label>}
        <label className="task-check full"><input type="checkbox" checked={form.allDay} onChange={event => update("allDay", event.target.checked)}/><span><I name="clock" size={13}/>{t("全天")}</span></label>
        <label className="task-field"><L dim>{t("優先級")}</L><select value={form.priority} onChange={event => update("priority", event.target.value)}><option value="urgent">{t("緊急")}</option><option value="high">{t("高")}</option><option value="normal">{t("普通")}</option><option value="low">{t("低")}</option></select></label>
        {canAssign && !!users.length && <label className="task-field"><L dim>{t("負責人")}</L><select value={form.assigneeId} onChange={event => update("assigneeId", event.target.value)}><option value="">{t("本人（預設）")}</option>{users.map((user, index) => <option key={first(user.id, user.user_id, index)} value={first(user.id, user.user_id, "")}>{first(user.display_name, user.name, user.username, user.id)}</option>)}</select></label>}
        {canManage && !!orgUnits.length && <label className="task-field"><L dim>{t("負責部門")}</L><select value={form.ownerOrgUnitId} onChange={event => update("ownerOrgUnitId", event.target.value)}><option value="">{t("未指定")}</option>{orgUnits.map((unit, index) => <option key={first(unit.id, unit.org_unit_id, unit.unit_id, index)} value={first(unit.id, unit.org_unit_id, unit.unit_id, "")}>{first(unit.name, unit.org_unit_name, unit.unit_name, unit.code, unit.id)}</option>)}</select></label>}
        {!!plans.length && form.mode !== "plan" && <label className="task-field full"><L dim>{t("所屬計劃")}</L><select value={form.planId} onChange={event => update("planId", event.target.value)}><option value="">{t("不加入計劃")}</option>{plans.map((plan, index) => <option key={first(plan.id, plan.plan_id, index)} value={first(plan.id, plan.plan_id, "")}>{first(plan.title, plan.name, plan.plan_name, plan.id)}</option>)}</select></label>}
        {error && <div className="task-form-error full" role="alert">{error}</div>}
        <footer className="full"><B type="button" onClick={onClose}>{t("取消")}</B><B type="submit" kind="primary" icon="plus" disabled={busy}>{busy ? t("建立中…") : t(form.mode === "event" ? "建立日程" : form.mode === "plan" ? "建立計劃" : "建立任務")}</B></footer>
      </form>
    </section>
  </div>;
};

const VIEWS = [["today", "今天", "check"], ["inbox", "收件匣", "clipboard"], ["calendar", "日曆", "clock"], ["plans", "計劃", "layers"], ["insights", "透視", "chart"]];
const TaskManagementPanel = ({ embedded = false, initialView, boot }) => {
  const query = queryFromHash();
  const validViews = VIEWS.map(item => item[0]);
  const [view, setView] = S(() => validViews.includes(initialView) ? initialView : validViews.includes(query.get("view")) ? query.get("view") : embedded ? "inbox" : "today");
  const [tasks, setTasks] = S([]);
  const [meta, setMeta] = S({});
  const [loading, setLoading] = S(true);
  const [error, setError] = S("");
  const [metaWarning, setMetaWarning] = S("");
  const [busyId, setBusyId] = S(null);
  const [composer, setComposer] = S(null);
  const [syncedAt, setSyncedAt] = S(null);
  const mounted = R(true);
  const loadSequence = R(0);
  const tenant = W2.tenant();
  const canCreate = capabilitiesFromMeta(meta).can_create === true || (W2.hasPermission && W2.hasPermission("tasks.create"));
  E(() => () => { mounted.current = false; }, []);
  const load = C(async ({ quiet = false } = {}) => {
    const request = ++loadSequence.current;
    if (!quiet) setLoading(true);
    let nextMeta = {};
    try {
      nextMeta = obj(await W2.json("/api/tasks/meta"));
      if (mounted.current && request === loadSequence.current) { setMeta(nextMeta); setMetaWarning(""); }
    } catch (exception) {
      if (mounted.current && request === loadSequence.current) setMetaWarning(exception.message || "");
    }
    if (!mounted.current || request !== loadSequence.current) return;
    const capabilities = capabilitiesFromMeta(nextMeta);
    const scope = embedded && (capabilities.can_assign === true || capabilities.can_manage === true) ? "managed" : "mine";
    try {
      const data = await W2.json(`/api/tasks?scope=${scope}`);
      if (!mounted.current || request !== loadSequence.current) return;
      setTasks(tasksFrom(data).map(normalizeTask));
      if (Object.keys(obj(data.capabilities)).length) setMeta(current => ({ ...current, capabilities: { ...capabilitiesFromMeta(current), ...obj(data.capabilities) } }));
      setError(""); setSyncedAt(new Date());
    } catch (exception) {
      if (mounted.current && request === loadSequence.current) setError(exception.message || t("任務服務尚未啟用或暫時無法連線。"));
    } finally { if (mounted.current && request === loadSequence.current) setLoading(false); }
  }, [tenant, embedded]);
  E(() => {
    setTasks([]); setMeta({}); setError(""); setMetaWarning(""); setBusyId(null); setComposer(null);
    load();
  }, [load]);
  E(() => {
    const onVisible = () => { if (document.visibilityState === "visible") load({ quiet: true }); };
    const onPageShow = () => load({ quiet: true });
    const onDomainChange = () => load({ quiet: true });
    document.addEventListener("visibilitychange", onVisible); window.addEventListener("pageshow", onPageShow);
    window.addEventListener("w2-agent-complete", onDomainChange); window.addEventListener("w2-record-created", onDomainChange);
    return () => {
      document.removeEventListener("visibilitychange", onVisible); window.removeEventListener("pageshow", onPageShow);
      window.removeEventListener("w2-agent-complete", onDomainChange); window.removeEventListener("w2-record-created", onDomainChange);
    };
  }, [load]);
  E(() => {
    if (embedded || !canCreate) return undefined;
    const initialQuery = queryFromHash();
    const requested = initialQuery.get("create");
    if (["task", "event", "plan"].includes(requested)) setComposer({ mode: requested, requestKey: clientRequestId(), seed: { source_ref: initialQuery.get("source_ref") || "", source_title: initialQuery.get("source_title") || "", date: initialQuery.get("date") || "" } });
    const open = event => setComposer({ mode: obj(event.detail).mode || "task", requestKey: clientRequestId(), seed: obj(event.detail) });
    window.addEventListener("w2-open-task-composer", open);
    return () => window.removeEventListener("w2-open-task-composer", open);
  }, [embedded, canCreate]);
  const chooseView = next => {
    setView(next);
    if (!embedded && (location.hash || "").startsWith("#/tasks")) history.replaceState(null, "", `#/tasks?view=${encodeURIComponent(next)}`);
  };
  const openCreate = (mode = "task", seed = {}) => { if (canCreate) setComposer({ mode, seed, requestKey: clientRequestId() }); };
  const updateStatus = async (task, status) => {
    if (busyId != null) return;
    const actionTenant = W2.tenant();
    setBusyId(task.id); setError("");
    try {
      const apiStatus = ({ active: "in_progress", paused: "waiting" }[status] || status);
      const body = { status: apiStatus, expected_version: task.lockVersion };
      const data = await W2.post(`/api/tasks/${encodeURIComponent(task.id)}/status`, body);
      if (actionTenant !== W2.tenant()) return;
      const updatedRaw = taskFrom(data);
      const updated = Object.keys(updatedRaw).length ? normalizeTask(updatedRaw) : { ...task, status: canonicalStatus(apiStatus) };
      setTasks(current => current.map(item => String(item.id) === String(task.id) ? updated : item));
      setSyncedAt(new Date());
    } catch (exception) { setError(exception.message || t("狀態更新失敗")); }
    finally { setBusyId(null); }
  };
  const created = async raw => {
    setComposer(null);
    if (raw && Object.keys(raw).length) setTasks(current => uniqueTasks([normalizeTask(raw), ...current]));
    await load({ quiet: true });
  };
  const availableViews = embedded ? VIEWS.filter(item => ["inbox", "plans", "insights"].includes(item[0])) : VIEWS;
  const content = loading && !tasks.length
    ? <div className="task-loading" aria-live="polite"><span/><span/><span/><small>{t("同步中")}</small></div>
    : error && !tasks.length ? <div className="task-api-error" role="alert"><I name="alert" size={24}/><h2>{t("任務資料暫時無法載入")}</h2><p>{error}</p><B icon="refresh" onClick={() => load()}>{t("重新載入")}</B></div>
    : view === "today" ? <TodayView tasks={tasks} busyId={busyId} onStatus={updateStatus} onCreate={canCreate ? () => openCreate("task") : null} onViewAll={() => chooseView("inbox")}/>
    : view === "inbox" ? <InboxView tasks={tasks} busyId={busyId} onStatus={updateStatus} onCreate={canCreate ? () => openCreate("task") : null}/>
    : view === "calendar" ? <CalendarView tasks={tasks} busyId={busyId} onStatus={updateStatus} onCreate={canCreate ? openCreate : null}/>
    : view === "plans" ? <PlansView tasks={tasks} meta={meta} busyId={busyId} onStatus={updateStatus} onCreate={canCreate ? openCreate : null}/>
    : <InsightsView tasks={tasks}/>;
  return <section className={`task-workspace${embedded ? " embedded" : ""}`}>
    <header className="task-hero">
      <div className="task-hero-title"><L red>{embedded ? "RECORDS × TASK" : "00 · PERSONAL OPERATIONS"}</L><h1>{embedded ? t("任務與計劃") : "TASK."}</h1><p>{t(embedded ? "管理任務、日程與部門計劃，並保留與原始檔案的關聯。" : "所有與你相關的操作、安排與檔案跟進，都在同一條時間線。")}</p></div>
      <div className="task-hero-actions"><button type="button" className="task-sync" onClick={() => load()} disabled={loading} aria-label={t("重新載入")}><I name="refresh" size={14}/><span>{loading ? t("同步中") : t("剛剛同步")}</span></button>{canCreate && <button type="button" className="task-create" onClick={() => openCreate("task")}><I name="plus" size={17}/><span>{t("新增")}</span></button>}</div>
    </header>
    <nav className="task-view-nav" role="tablist" aria-label={t("個人行動中心")}>{availableViews.map(([id, label, icon], index) => <button type="button" role="tab" aria-selected={view === id} className={view === id ? "on" : ""} key={id} onClick={() => chooseView(id)}><span>{pad(index + 1)}</span><I name={icon} size={15}/><b>{t(label)}</b></button>)}</nav>
    {(error && tasks.length > 0) && <div className="task-inline-error" role="alert"><span>{error}</span><button type="button" onClick={() => load()}>{t("重新載入")}</button></div>}
    {metaWarning && !error && <div className="task-meta-warning" title={metaWarning}>{t("資料權限由任務發起人、負責人、參與者與部門範圍共同決定。")}</div>}
    {content}
    {!embedded && <nav className="task-mobile-nav" aria-label={t("個人行動中心")}>{VIEWS.map(([id, label, icon]) => <button type="button" className={view === id ? "on" : ""} key={id} onClick={() => chooseView(id)}><I name={icon} size={18}/><span>{t(label)}</span></button>)}</nav>}
    {composer && <TaskComposer key={composer.requestKey} requestKey={composer.requestKey} mode={composer.mode} seed={composer.seed} meta={meta} tasks={tasks} onClose={() => setComposer(null)} onCreated={created}/>}
  </section>;
};

const TaskPage = props => <div className="task-page"><TaskManagementPanel key={W2.tenant()} {...props}/></div>;
W2.TaskManagementPanel = TaskManagementPanel;
W2.PAGES.tasks = TaskPage;
})();
