/* activity.jsx — ขั้นที่ 1 ของหน้ากรอกงบ: หน่วยงานต้นสังกัดกรอกงบเป็น "การ์ดกิจกรรม"
 * กิจกรรม = หน่วยงานย่อย + ชื่อ + ช่วงเดือน + รายละเอียด + รายการค่าใช้จ่าย (รหัสบัญชี · จำนวนเงิน · หมายเหตุ)
 * บันทึกแล้วเซิร์ฟเวอร์รวมรายการตามรหัสลงตารางขั้นที่ 2 (ตรวจสรุป) ให้เอง — ยอดในตารางขั้นที่ 2 แก้ตรงไม่ได้
 * ค่าใช้จ่ายที่ไม่ได้มาจากกิจกรรม (เงินเดือน ค่าโทรศัพท์) ใส่เป็นกิจกรรมประเภท "ค่าใช้จ่ายประจำ" */

const KIND_LABEL = Object.fromEntries(R.ACTIVITY_KINDS.map((k) => [k.key, k.label]));
const RECURRING_NAME = 'ค่าใช้จ่ายประจำ';

/** ช่องเลือกรหัสค่าใช้จ่าย: พิมพ์ชื่อหรือรหัสแล้วเลือกจากรายการ (รหัสที่ปีนี้ฝ่ายมีอยู่ขึ้นก่อน) */
function AcctPicker({ value, accounts, known, onPick, invalid }) {
  const [q, setQ] = useState(null);               // null = ปิดรายการ
  const [hi, setHi] = useState(0);
  const box = useRef(null);
  const listRef = useRef(null);
  const cur = accounts.find((a) => a.code === value);
  const open = q !== null;
  useEffect(() => {
    if (!open) return undefined;
    const close = (e) => { if (box.current && !box.current.contains(e.target)) setQ(null); };
    document.addEventListener('mousedown', close);
    return () => document.removeEventListener('mousedown', close);
  }, [open]);
  const needle = (q || '').trim().toLowerCase();
  const list = !open ? [] : accounts
    .filter((a) => !needle || a.code.includes(needle) || a.name.toLowerCase().includes(needle) || (a.cat || '').toLowerCase().includes(needle))
    .sort((x, y) => (known.has(y.code) - known.has(x.code)) || (x.code < y.code ? -1 : 1))
    .slice(0, 80);
  const pick = (a) => { onPick(a.code); setQ(null); };
  const moveHi = (step) => {
    const n = Math.max(0, Math.min(list.length - 1, hi + step));
    setHi(n);
    const el = listRef.current && listRef.current.children[n];
    if (el) el.scrollIntoView({ block: 'nearest' });
  };
  return (
    <div className="acctpick" ref={box}>
      <input
        type="text" autoComplete="off" spellCheck={false}
        className={invalid ? 'invalid' : ''}
        value={open ? q : cur ? cur.code + ' ' + cur.name : ''}
        placeholder="พิมพ์ค้นชื่อ เช่น ที่พัก"
        aria-label="รหัสค่าใช้จ่าย" aria-expanded={open}
        onFocus={() => { setQ(''); setHi(0); }}
        onChange={(e) => { setQ(e.target.value); setHi(0); }}
        onKeyDown={(e) => {
          if (!open) return;
          if (e.key === 'ArrowDown') { e.preventDefault(); moveHi(1); }
          else if (e.key === 'ArrowUp') { e.preventDefault(); moveHi(-1); }
          else if (e.key === 'Enter') { e.preventDefault(); if (list[hi]) pick(list[hi]); }
          else if (e.key === 'Escape') { e.stopPropagation(); setQ(null); e.target.blur(); }
        }}
      />
      {open && (
        <div className="acctlist" role="listbox" ref={listRef}>
          {list.length === 0 && <div className="empty small">ไม่พบรหัส ลองคำอื่น หรือสอบถามฝ่ายบัญชี</div>}
          {list.map((a, i) => (
            <button
              type="button" key={a.code} role="option" aria-selected={i === hi} className={i === hi ? 'on' : ''}
              onMouseDown={(e) => { e.preventDefault(); pick(a); }} onMouseEnter={() => setHi(i)}
            >
              <span className="code">{a.code}</span>
              <span className="nm">{a.name}</span>
              {!known.has(a.code) && <span className="badge">รหัสใหม่</span>}
              <span className="cat">{a.cat}</span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

/** เลือกเดือน 1–12 */
function MonthPick({ value, onChange, label }) {
  return (
    <select value={value || ''} onChange={(e) => onChange(Number(e.target.value))} aria-label={label}>
      {R.MONTHS_TH.map((m, i) => <option key={i} value={i + 1}>{m}</option>)}
    </select>
  );
}

/** แบบฟอร์มสร้าง/แก้กิจกรรม (modal) */
function ActivityEditor({ deptCode, data, initial, onClose, onSaved, onData, beforeWrite }) {
  const seq = useRef(0);
  const withKey = (it) => ({ key: ++seq.current, ...it });
  const blank = () => withKey({ acct: '', amount: null, note: '', qty: null, unitPrice: null, timing: 'spread', month: null, months: null });
  const isEdit = !!(initial && initial.id);
  const [f, setF] = useState(() => {
    const kind = (initial && initial.kind) || 'activity';
    return {
      subunitId: (initial && initial.subunitId) || (data.subunits[0] && data.subunits[0].id) || null,
      kind, name: (initial && initial.name) || '', detail: (initial && initial.detail) || '',
      startMonth: (initial && initial.startMonth) || 1, endMonth: (initial && initial.endMonth) || 12,
      items: initial && initial.items && initial.items.length ? initial.items.map(withKey) : [blank()],
    };
  });
  const [tried, setTried] = useState(false);
  const [busy, setBusy] = useState(false);
  const [dirty, setDirty] = useState(false);
  const [armed, setArmed] = useState(false);
  const [newSu, setNewSu] = useState(null);       // null = ไม่ได้กำลังเพิ่มหน่วยงานย่อย
  const set = (patch) => { setF((x) => ({ ...x, ...patch })); setDirty(true); setArmed(false); };
  const setItem = (key, patch) => set({ items: f.items.map((it) => (it.key === key ? { ...it, ...patch } : it)) });

  const accounts = useMemo(() => data.accounts.filter((a) => a.filler !== 'central'), [data]);
  const known = useMemo(() => new Set(data.lines.filter((l) => !l.added).map((l) => l.acct)), [data]);
  const nameOf = (code) => (data.accounts.find((a) => a.code === code) || {}).name || '';
  const amountOf = (it) => (it.timing === 'custom' ? R.sum(it.months) : it.amount == null ? 0 : R.round2(it.amount));
  const total = R.sum(f.items.map(amountOf));

  // ตรวจก่อนบันทึก (เซิร์ฟเวอร์ตรวจซ้ำด้วยกติกาเดียวกัน)
  const itemErr = (it) => (!it.acct ? 'เลือกรหัสค่าใช้จ่าย'
    : !(amountOf(it) > 0) ? 'ใส่จำนวนเงินมากกว่า 0'
    : it.timing === 'month' && !it.month ? 'เลือกเดือนที่จ่าย' : '');
  const errors = [];
  if (!f.name.trim()) errors.push('ใส่ชื่อกิจกรรม');
  if (!f.subunitId) errors.push('เลือกหน่วยงานย่อย');
  if (f.startMonth > f.endMonth) errors.push('เดือนเริ่มต้องไม่หลังเดือนสิ้นสุด');
  f.items.forEach((it, i) => { const e = itemErr(it); if (e) errors.push(`รายการที่ ${i + 1}: ${e}`); });
  const unknown = Array.from(new Set(f.items.filter((it) => it.acct && !known.has(it.acct)).map((it) => it.acct)));
  // รหัสเดียวกันในกิจกรรมอื่นของฝ่าย — เตือนให้ตรวจว่าไม่ได้นับค่าใช้จ่ายเดียวกันซ้ำ
  const elsewhere = (acct) => data.activities.filter((a) => a.id !== (initial && initial.id) && a.items.some((x) => x.acct === acct))
    .map((a) => ({ name: a.name, amount: R.sum(a.items.filter((x) => x.acct === acct).map(R.itemAmount)) }));

  const close = () => {
    if (dirty && !armed) { setArmed(true); toast('ยังไม่ได้บันทึกกิจกรรม — กดปิดอีกครั้งถ้าต้องการทิ้งสิ่งที่แก้', 'error'); return; }
    onClose();
  };
  const save = async () => {
    setTried(true);
    if (errors.length) { toast(errors[0], 'error'); return; }
    setBusy(true);
    try {
      await beforeWrite();
      const body = {
        subunitId: f.subunitId, kind: f.kind, name: f.name.trim(), detail: f.detail, startMonth: f.startMonth, endMonth: f.endMonth,
        items: f.items.map(({ key, ...it }) => ({
          ...it, amount: amountOf(it), month: it.timing === 'month' ? it.month : null, months: it.timing === 'custom' ? it.months : null,
        })),
      };
      const r = isEdit
        ? await API.updateActivity(deptCode, initial.id, { ...body, expectedUpdatedAt: initial.updatedAt })
        : await API.createActivity(deptCode, body);
      onSaved(r, isEdit);
    } catch (e) {
      toast(e.message, 'error');
      if (e.status === 409) onSaved(null, isEdit, true);
    } finally { setBusy(false); }
  };
  const addSubunit = async () => {
    const name = (newSu || '').trim();
    if (!name) return;
    setBusy(true);
    try {
      const r = await API.createSubunit(deptCode, name);
      onData(r);
      set({ subunitId: r.id });
      setNewSu(null);
      toast('เพิ่มหน่วยงานย่อย "' + name + '" แล้ว');
    } catch (e) { toast(e.message, 'error'); } finally { setBusy(false); }
  };
  const setTiming = (it, timing) => {
    if (timing === 'custom') {
      // เริ่มจากการกระจายแบบเดิม ผู้ใช้แก้ต่อทีละเดือน
      const months = R.itemMonths(f, { ...it, amount: amountOf(it), timing: it.timing === 'custom' ? 'spread' : it.timing });
      setItem(it.key, { timing, months });
    } else if (timing === 'month') {
      setItem(it.key, { timing, month: it.month || f.startMonth, amount: it.timing === 'custom' ? R.sum(it.months) : it.amount });
    } else {
      setItem(it.key, { timing, amount: it.timing === 'custom' ? R.sum(it.months) : it.amount });
    }
  };
  const setCalc = (it, patch) => {
    const n = { ...it, ...patch };
    const amount = n.qty != null && n.unitPrice != null ? R.round2(n.qty * n.unitPrice) : n.amount;
    setItem(it.key, { ...patch, amount });
  };

  return (
    <Modal
      title={isEdit ? 'แก้กิจกรรม' : f.kind === 'recurring' ? 'เพิ่มค่าใช้จ่ายประจำ' : 'เพิ่มกิจกรรม'} wide="xxl" onClose={close}
      footer={<>
        <span className="small" style={{ marginRight: 'auto', alignSelf: 'center' }}>
          รวมทั้งกิจกรรม <b style={{ fontSize: 16, color: 'var(--navy)' }}>{fmt(total)}</b> บาท · {f.items.length} รายการ
        </span>
        <button className="btn" onClick={close}>ยกเลิก</button>
        <button className="btn primary" disabled={busy} onClick={save}><Icon name="check" />{busy ? 'กำลังบันทึก…' : 'บันทึกกิจกรรม'}</button>
      </>}
    >
      <div className="actform">
        <div className="field">ประเภท
          <div className="seg lg" role="group" aria-label="ประเภท">
            {R.ACTIVITY_KINDS.map((k) => (
              <button
                key={k.key} type="button" className={f.kind === k.key ? 'on' : ''}
                onClick={() => set(k.key === 'recurring' && !isEdit && !f.name.trim()
                  ? { kind: k.key, name: RECURRING_NAME, startMonth: 1, endMonth: 12 } : { kind: k.key })}
              >{k.label}</button>
            ))}
          </div>
        </div>
        <label className="field">หน่วยงานย่อย
          {newSu === null ? (
            <select value={f.subunitId || ''} onChange={(e) => (e.target.value === 'new' ? setNewSu('') : set({ subunitId: Number(e.target.value) }))}>
              {data.subunits.map((s) => <option key={s.id} value={s.id}>{s.name}</option>)}
              <option value="new">+ เพิ่มหน่วยงานย่อยใหม่…</option>
            </select>
          ) : (
            <span className="inline-add">
              <input type="text" autoFocus value={newSu} maxLength={100} placeholder="ชื่อหน่วยงานย่อย เช่น HRD" onChange={(e) => setNewSu(e.target.value)}
                onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); addSubunit(); } else if (e.key === 'Escape') { e.stopPropagation(); setNewSu(null); } }} />
              <button type="button" className="btn sm primary" disabled={busy || !newSu.trim()} onClick={addSubunit}>เพิ่ม</button>
              <button type="button" className="btn sm" onClick={() => setNewSu(null)}>ยกเลิก</button>
            </span>
          )}
        </label>
        <label className="field grow2">{f.kind === 'recurring' ? 'ชื่อรายการ' : 'ชื่อกิจกรรม'}
          <input
            type="text" value={f.name} maxLength={200} className={tried && !f.name.trim() ? 'invalid' : ''}
            placeholder={f.kind === 'recurring' ? 'เช่น ค่าใช้จ่ายประจำ, เงินเดือนและสวัสดิการ' : 'เช่น เอาติ้งประจำปี, อบรมหลักสูตรผู้นำ'}
            onChange={(e) => set({ name: e.target.value })}
          />
        </label>
        <div className="field">ช่วงเวลา
          <span className="period">
            <MonthPick value={f.startMonth} label="เดือนเริ่ม" onChange={(m) => set({ startMonth: m, endMonth: Math.max(m, f.endMonth) })} />
            <span>ถึง</span>
            <MonthPick value={f.endMonth} label="เดือนสิ้นสุด" onChange={(m) => set({ endMonth: m, startMonth: Math.min(m, f.startMonth) })} />
            <button type="button" className={'btn sm' + (f.startMonth === 1 && f.endMonth === 12 ? ' on' : '')} onClick={() => set({ startMonth: 1, endMonth: 12 })}>ทั้งปี</button>
          </span>
        </div>
        <label className="field full">รายละเอียด (ไม่บังคับ)
          <textarea rows={2} value={f.detail} maxLength={4000} placeholder="เช่น จัดที่หัวหิน 2 วัน 1 คืน พนักงาน 50 คน · วัตถุประสงค์ · ผู้รับผิดชอบ" onChange={(e) => set({ detail: e.target.value })} />
        </label>
      </div>

      {data.me && data.me.role === 'dept' && (
        <div className="banner yellow" style={{ margin: 0, boxShadow: 'none' }}>
          <Icon name="alert" />
          <div>
            <b>ไม่แน่ใจรหัสค่าใช้จ่าย ให้สอบถามฝ่ายบัญชีก่อน</b> เพื่อให้เลือกหมวดและรหัสบัญชีได้ถูกต้องตามที่ฝ่ายบัญชีกำหนด
            {unknown.length > 0 && <div className="small">รหัสที่ปีนี้ฝ่ายไม่มี: {unknown.map((c) => c + ' ' + nameOf(c)).join(', ')} — ขั้นตรวจสรุปจะให้ใส่คำชี้แจงทุกรหัสใหม่</div>}
          </div>
        </div>
      )}

      <div className="itemtable">
        <div className="ithead">
          <div>#</div><div>รหัสค่าใช้จ่าย</div><div title="ไม่บังคับ: กรอกทั้งสองช่องแล้วระบบคูณให้">จำนวน × ราคาต่อหน่วย</div>
          <div className="num">จำนวนเงิน (บาท)</div><div>การจ่าย</div><div>หมายเหตุ / ที่มาของตัวเลข</div><div />
        </div>
        {f.items.map((it, i) => {
          const err = tried ? itemErr(it) : '';
          const calc = it.qty != null && it.unitPrice != null;
          return (
            <div key={it.key} className={'itrow' + (err ? ' bad' : '')}>
              <div className="mute small">{i + 1}</div>
              <AcctPicker value={it.acct} accounts={accounts} known={known} invalid={tried && !it.acct} onPick={(acct) => setItem(it.key, { acct })} />
              <div className="calc">
                <NumInput value={it.qty} onValue={(x) => setCalc(it, { qty: x })} placeholder="จำนวน" ariaLabel={'จำนวน รายการที่ ' + (i + 1)} />
                <span>×</span>
                <NumInput value={it.unitPrice} onValue={(x) => setCalc(it, { unitPrice: x })} placeholder="ราคา" ariaLabel={'ราคาต่อหน่วย รายการที่ ' + (i + 1)} />
              </div>
              {it.timing === 'custom' || calc ? (
                <div className="num amt-ro" title={calc ? 'จำนวน × ราคาต่อหน่วย (ล้างช่องใดช่องหนึ่งเพื่อพิมพ์ยอดเอง)' : 'ผลรวมรายเดือน'}>{fmt(amountOf(it))}</div>
              ) : (
                <NumInput className={'amt' + (tried && !(amountOf(it) > 0) ? ' invalid' : '')} value={it.amount} onValue={(x) => setItem(it.key, { amount: x })} placeholder="0" ariaLabel={'จำนวนเงิน รายการที่ ' + (i + 1)} />
              )}
              <div className="timing">
                <select value={it.timing} onChange={(e) => setTiming(it, e.target.value)} aria-label={'การจ่าย รายการที่ ' + (i + 1)}>
                  <option value="spread">{f.startMonth === 1 && f.endMonth === 12 ? 'เฉลี่ยทุกเดือน' : 'เฉลี่ยตามช่วงกิจกรรม'}</option>
                  <option value="month">จ่ายครั้งเดียว</option>
                  <option value="custom">กำหนดรายเดือนเอง</option>
                </select>
                {it.timing === 'month' && <MonthPick value={it.month} label={'เดือนที่จ่าย รายการที่ ' + (i + 1)} onChange={(m) => setItem(it.key, { month: m })} />}
              </div>
              <input type="text" value={it.note} maxLength={1000} placeholder="เช่น 100 ห้อง × 1,500 บาท" aria-label={'หมายเหตุ รายการที่ ' + (i + 1)} onChange={(e) => setItem(it.key, { note: e.target.value })} />
              <button type="button" className="btn ghost icon-only" title="ลบรายการนี้" aria-label={'ลบรายการที่ ' + (i + 1)}
                onClick={() => set({ items: f.items.length > 1 ? f.items.filter((x) => x.key !== it.key) : [blank()] })}><Icon name="x" /></button>
              {it.timing === 'custom' && (() => {
                const ref = data.lines.find((l) => l.acct === it.acct && !l.added);
                return (
                  <div className="itmonths">
                    <MonthsEditor
                      months={it.months} cfg={data.round} nav={'im-' + it.key} budget={ref ? ref.budget : null} actual={ref ? ref.actual : null}
                      onChange={(months) => setItem(it.key, { months })}
                    />
                    <div className="recur-help small">
                      {ref && <div className="quick"><button type="button" className="btn sm" onClick={() => setItem(it.key, { months: ref.budget.map((x) => Math.max(0, R.round2(x))) })}>ตาม Budget {data.round.baseYear} ทุกเดือน</button></div>}
                      <p className="mute">พิมพ์ยอดแต่ละเดือน (Enter = เดือนถัดไป) จำนวนเงินของรายการนี้ = ผลรวมทั้ง 12 เดือน{ref ? ' · ตัวเลขสีจาง = Budget/Actual ปี ' + data.round.baseYear + ' ไว้เทียบ' : ''}</p>
                    </div>
                  </div>
                );
              })()}
              {err && <div className="iterr err small">{err}</div>}
              {it.acct && elsewhere(it.acct).length > 0 && (
                <div className="iterr small dupwarn">
                  <Icon name="alert" />รหัสนี้มีในกิจกรรมอื่นแล้ว: {elsewhere(it.acct).map((x) => '"' + x.name + '" ' + fmt(x.amount) + ' บาท').join(', ')} — ตรวจว่าไม่ได้นับค่าใช้จ่ายเดียวกันซ้ำ
                </div>
              )}
            </div>
          );
        })}
        <div><button type="button" className="btn sm" onClick={() => set({ items: [...f.items, blank()] })}><Icon name="plus" />เพิ่มรายการค่าใช้จ่าย</button></div>
      </div>
      {tried && errors.length > 0 && <p className="err small">ยังบันทึกไม่ได้: {errors.join(' · ')}</p>}
    </Modal>
  );
}

/** กิจกรรม 1 แถว (แนวนอน) — ฝ่ายที่มีกิจกรรมเยอะไล่ดูได้ทีละบรรทัด คลิกเพื่อเปิดรายละเอียด */
function ActivityRow({ a, nameOf, share, maxTotal, onOpen, focused }) {
  const total = R.activityTotal(a);
  const accts = Array.from(new Set(a.items.map((it) => it.acct)));
  return (
    <button type="button" id={'act-' + a.id} className={'actrow' + (a.kind === 'recurring' ? ' recurring' : '') + (focused ? ' focused' : '') + (a.items.length ? '' : ' bad')} onClick={onOpen}>
      <span className="ar-name">
        <b>{a.name}</b>
        <span className="ar-sub">
          {a.kind === 'recurring' && <span className="tag">ค่าใช้จ่ายประจำ</span>}
          {a.items.length === 0
            ? <span className="err">ยังไม่มีรายการค่าใช้จ่าย</span>
            : <span className="mute">{accts.slice(0, 3).map(nameOf).join(' · ')}{accts.length > 3 ? ' และอีก ' + (accts.length - 3) + ' รหัส' : ''}</span>}
        </span>
      </span>
      <span className="ar-period">{R.periodText(a)}</span>
      <span className="ar-items">{a.items.length} รายการ</span>
      <span className="ar-total">{fmt(total)}</span>
      <span className="ar-share" title={'สัดส่วนของยอดรวมทุกกิจกรรมของฝ่าย ' + share.toFixed(1) + '%'}>
        <span className="bar"><i style={{ width: (maxTotal > 0 ? (total / maxTotal) * 100 : 0) + '%' }} /></span>
        <span>{share.toFixed(1)}%</span>
      </span>
      <Icon name="chevron" className="chev side" />
    </button>
  );
}

/** รายละเอียดของ 1 กิจกรรม (เปิดจากแถว): รายการค่าใช้จ่าย + ยอดรายเดือนแนวตั้ง + ปุ่มแก้/ทำสำเนา/ลบ */
function ActivityDetail({ a, data, subName, nameOf, canEdit, onClose, onEdit, onCopy, onDelete }) {
  const total = R.activityTotal(a);
  const months = R.MONTHS_TH.map((_, i) => R.round2(a.items.reduce((t, it) => t + (Number(R.itemMonths(a, it)[i]) || 0), 0)));
  const how = (it) => (it.timing === 'month' ? 'จ่ายครั้งเดียว ' + R.MONTHS_TH[(it.month || 1) - 1] : it.timing === 'custom' ? 'กำหนดรายเดือน' : 'เฉลี่ยตามช่วงกิจกรรม');
  return (
    <Modal title={a.name} wide="xxl" onClose={onClose} footer={<>
      <span className="small mute" style={{ marginRight: 'auto', alignSelf: 'center' }}>
        {a.updatedBy === 'system' ? 'ระบบย้ายมาจากตารางเดิม' : 'แก้ล่าสุด ' + (a.updatedBy || '') + ' ' + fmtShort(a.updatedAt)}
      </span>
      {canEdit && <>
        <button className="btn danger" onClick={onDelete}><Icon name="trash" />ลบ</button>
        <button className="btn" onClick={onCopy}><Icon name="copy" />ทำสำเนา</button>
        <button className="btn primary" onClick={onEdit}><Icon name="edit" />แก้ไข</button>
      </>}
      <button className="btn" onClick={onClose}>ปิด</button>
    </>}>
      <div className="ad-head">
        <span className={'tag' + (a.kind === 'recurring' ? '' : ' ok')}>{KIND_LABEL[a.kind] || a.kind}</span>
        <span>{subName}</span><span className="mute">·</span><span>{R.periodText(a)}</span>
        <span className="grow" />
        <span className="ad-total"><span className="mute small">รวมทั้งกิจกรรม</span> <b>{fmt(total)}</b> <span className="mute small">บาท</span></span>
      </div>
      {a.detail && <div className="ad-detail">{a.detail}</div>}
      <div className="ldgrid">
        <div>
          {a.items.length === 0 ? (
            <p className="err small">ยังไม่มีรายการค่าใช้จ่าย — ส่งงบไม่ได้จนกว่าจะเพิ่มรายการหรือลบกิจกรรมนี้</p>
          ) : (
            <table className="t aditems">
              <thead><tr><th>รหัสค่าใช้จ่าย</th><th>การจ่าย</th><th>หมายเหตุ / ที่มาของตัวเลข</th><th className="num focus">ยอด</th></tr></thead>
              <tbody>
                {a.items.map((it, i) => (
                  <tr key={i}>
                    <td><span className="mute small">{it.acct}</span> {nameOf(it.acct)}</td>
                    <td className="small nowrap">{how(it)}</td>
                    <td className="small">
                      {it.qty != null && it.unitPrice != null && <span className="mute">{fmt(it.qty)} × {fmt(it.unitPrice)}{it.note ? ' · ' : ''}</span>}
                      {it.note}
                    </td>
                    <td className="num focus">{fmt(R.itemAmount(it))}</td>
                  </tr>
                ))}
              </tbody>
              <tfoot><tr><td colSpan={3}>รวม {a.items.length} รายการ</td><td className="num focus">{fmt(total)}</td></tr></tfoot>
            </table>
          )}
        </div>
        <MonthsEditor months={months} cfg={data.round} readOnly />
      </div>
    </Modal>
  );
}

/** จัดการหน่วยงานย่อย: เพิ่ม · เปลี่ยนชื่อ · ลบ (ลบหน่วยที่มีกิจกรรม = ย้ายกิจกรรมไปหน่วยอื่น) */
function SubunitManager({ deptCode, data, onData, onClose, beforeWrite }) {
  const [names, setNames] = useState(() => Object.fromEntries(data.subunits.map((s) => [s.id, s.name])));
  const [add, setAdd] = useState('');
  const [del, setDel] = useState(null);           // { id, moveTo }
  const [busy, setBusy] = useState(false);
  const used = (id) => data.activities.filter((a) => a.subunitId === id).length;
  const run = async (fn, msg) => {
    setBusy(true);
    try { await beforeWrite(); const r = await fn(); onData(r); setNames(Object.fromEntries(r.subunits.map((s) => [s.id, s.name]))); if (msg) toast(msg); return true; }
    catch (e) { toast(e.message, 'error'); return false; } finally { setBusy(false); }
  };
  return (
    <Modal title="หน่วยงานย่อยของฝ่าย" wide onClose={onClose} footer={<button className="btn primary" onClick={onClose}>เสร็จ</button>}>
      <p className="mute small">แบ่งงบตามหน่วยงานภายในฝ่าย เช่น ฝ่ายบุคคลมี HRD และ HRM — รายงานจะแยกให้เห็นว่ายอดมาจากหน่วยไหน ฝ่ายที่ไม่แบ่งหน่วยใช้หน่วยเดียวได้</p>
      <table className="t">
        <thead><tr><th>ชื่อหน่วยงานย่อย</th><th className="num">กิจกรรม</th><th /></tr></thead>
        <tbody>
          {data.subunits.map((s) => (
            <tr key={s.id}>
              <td>
                <span className="inline-add">
                  <input type="text" value={names[s.id] || ''} maxLength={100} aria-label={'ชื่อหน่วยงานย่อย ' + s.name} onChange={(e) => setNames({ ...names, [s.id]: e.target.value })} />
                  {(names[s.id] || '').trim() !== s.name && (
                    <button className="btn sm primary" disabled={busy || !(names[s.id] || '').trim()} onClick={() => run(() => API.renameSubunit(deptCode, s.id, names[s.id].trim()), 'เปลี่ยนชื่อแล้ว')}>บันทึกชื่อ</button>
                  )}
                </span>
              </td>
              <td className="num">{used(s.id)}</td>
              <td style={{ textAlign: 'right' }}>
                {data.subunits.length > 1 && <button className="btn sm ghost danger" disabled={busy} onClick={() => setDel({ id: s.id, moveTo: data.subunits.find((x) => x.id !== s.id).id })}><Icon name="trash" />ลบ</button>}
              </td>
            </tr>
          ))}
        </tbody>
      </table>
      <span className="inline-add">
        <input type="text" value={add} maxLength={100} placeholder="ชื่อหน่วยงานย่อยใหม่ เช่น HRM" onChange={(e) => setAdd(e.target.value)}
          onKeyDown={async (e) => { if (e.key === 'Enter' && add.trim()) { if (await run(() => API.createSubunit(deptCode, add.trim()), 'เพิ่มแล้ว')) setAdd(''); } }} />
        <button className="btn sm primary" disabled={busy || !add.trim()} onClick={async () => { if (await run(() => API.createSubunit(deptCode, add.trim()), 'เพิ่มแล้ว')) setAdd(''); }}><Icon name="plus" />เพิ่มหน่วยงานย่อย</button>
      </span>
      {del && (() => {
        const s = data.subunits.find((x) => x.id === del.id);
        const n = used(del.id);
        return (
          <div className="banner yellow" style={{ margin: 0, boxShadow: 'none' }}>
            <Icon name="alert" />
            <div style={{ flex: 1 }}>
              <b>ลบหน่วยงาน "{s && s.name}"</b>
              {n > 0 ? (
                <div className="small">มีกิจกรรม {n} รายการ ย้ายไปที่{' '}
                  <select value={del.moveTo} onChange={(e) => setDel({ ...del, moveTo: Number(e.target.value) })}>
                    {data.subunits.filter((x) => x.id !== del.id).map((x) => <option key={x.id} value={x.id}>{x.name}</option>)}
                  </select>{' '}ยอดเงินไม่เปลี่ยน
                </div>
              ) : <div className="small">ไม่มีกิจกรรมในหน่วยนี้</div>}
              <div style={{ marginTop: 6, display: 'flex', gap: 6 }}>
                <button className="btn sm danger" disabled={busy} onClick={async () => { if (await run(() => API.deleteSubunit(deptCode, del.id, n ? del.moveTo : null), 'ลบหน่วยงานย่อยแล้ว')) setDel(null); }}>ยืนยันการลบ</button>
                <button className="btn sm" onClick={() => setDel(null)}>ยกเลิก</button>
              </div>
            </div>
          </div>
        );
      })()}
    </Modal>
  );
}

/** เพิ่มรหัสที่ยังไม่ระบุเป็น "ค่าใช้จ่ายประจำ" — ตั้งต้นเท่า Budget (แบ่งเดือนตาม Budget) หรือ Forecast (เฉลี่ยทุกเดือน)
 *  แต่ละรหัสเลือกได้ว่าเฉลี่ยทุกเดือน หรือกำหนดยอดรายเดือนเอง (ตารางเดือนแนวตั้งเทียบ Budget/Actual ปีอ้างอิง)
 *  ต่อท้ายกิจกรรมค่าใช้จ่ายประจำที่มีอยู่แล้วของหน่วยงานย่อยนั้น หรือสร้างใหม่ถ้ายังไม่มี */
function RecurringAdd({ deptCode, data, accts, onClose, onData, beforeWrite }) {
  const cfg = data.round;
  const lines = data.lines.filter((l) => accts.includes(l.acct));
  const evOf = (l) => R.evaluateLine(l, cfg);
  const single = lines.length === 1;
  const startPlan = (b) => Object.fromEntries(lines.map((l) => {
    const ev = evOf(l);
    if (b === 'budget') {
      const months = l.budget.map((x) => Math.max(0, R.round2(x)));
      // Budget เท่ากันทุกเดือน (ต่างกันแค่เศษหาร) = เฉลี่ยทุกเดือน · ไม่เท่ากัน = แบ่งเดือนตาม Budget
      const p = R.patternOf(months);
      return [l.acct, p.kind === 'even' || p.kind === 'zero' ? { mode: 'even', amount: Math.round(ev.B), months: null } : { mode: 'months', amount: 0, months }];
    }
    return [l.acct, { mode: 'even', amount: Math.max(0, Math.round(ev.P || 0)), months: null }];
  }));
  const [base, setBase] = useState('budget');
  const [plan, setPlan] = useState(() => startPlan('budget'));
  const [open, setOpen] = useState(() => (single ? { [accts[0]]: true } : {}));
  const [su, setSu] = useState(data.subunits[0] ? data.subunits[0].id : null);
  const [busy, setBusy] = useState(false);
  const amountOf = (acct) => { const p = plan[acct]; return p.mode === 'months' ? R.sum(p.months) : R.round2(p.amount || 0); };
  const monthsOf = (acct) => { const p = plan[acct]; return p.mode === 'months' ? p.months : R.spread(p.amount || 0, null); };
  const setRow = (acct, patch) => setPlan((x) => ({ ...x, [acct]: { ...x[acct], ...patch } }));
  const setMode = (acct, mode) => (mode === 'months'
    ? setRow(acct, { mode, months: monthsOf(acct) })
    : setRow(acct, { mode, amount: amountOf(acct) }));
  const target = data.activities.find((a) => a.kind === 'recurring' && a.subunitId === su && a.name === RECURRING_NAME);
  const ready = lines.filter((l) => amountOf(l.acct) > 0);
  const save = async () => {
    if (!ready.length) { toast('ยอดเป็น 0 ทุกรายการ — ถ้าไม่ขอ ให้เลือก "ไม่ขอ" แทน', 'error'); return; }
    setBusy(true);
    try {
      await beforeWrite();
      const items = ready.map((l) => {
        const p = plan[l.acct];
        return p.mode === 'months'
          ? { acct: l.acct, amount: R.sum(p.months), note: '', qty: null, unitPrice: null, timing: 'custom', month: null, months: p.months }
          : { acct: l.acct, amount: amountOf(l.acct), note: '', qty: null, unitPrice: null, timing: 'spread', month: null, months: null };
      });
      const r = target
        ? await API.updateActivity(deptCode, target.id, { ...target, items: [...target.items, ...items], expectedUpdatedAt: target.updatedAt })
        : await API.createActivity(deptCode, { subunitId: su, kind: 'recurring', name: RECURRING_NAME, detail: '', startMonth: 1, endMonth: 12, items });
      onData(r);
      toast(`เพิ่ม ${items.length} รหัสเป็นค่าใช้จ่ายประจำแล้ว`);
      onClose();
    } catch (e) { toast(e.message, 'error'); } finally { setBusy(false); }
  };
  return (
    <Modal title="เพิ่มเป็นค่าใช้จ่ายประจำ" wide="xl" onClose={onClose} footer={<>
      <span className="small" style={{ marginRight: 'auto', alignSelf: 'center' }}>รวม <b>{fmt(R.sum(ready.map((l) => amountOf(l.acct))))}</b> บาท · {ready.length} รหัส</span>
      <button className="btn" onClick={onClose}>ยกเลิก</button>
      <button className="btn primary" disabled={busy} onClick={save}><Icon name="check" />เพิ่ม</button>
    </>}>
      <p className="small">
        สำหรับค่าใช้จ่ายที่เกิดทุกเดือนโดยไม่ได้มาจากกิจกรรม เช่น เงินเดือน ค่าโทรศัพท์ ·
        เลือก "เฉลี่ยทุกเดือน" หรือ "กำหนดรายเดือน" เพื่อใส่ยอดแต่ละเดือนเอง (เช่น ปรับเงินเดือนตั้งแต่ เม.ย.) แก้ทีหลังได้ที่การ์ดกิจกรรม
      </p>
      <div className="formgrid">
        <label className="field">ยอดตั้งต้น
          <select value={base} onChange={(e) => { setBase(e.target.value); setPlan(startPlan(e.target.value)); }}>
            <option value="budget">เท่า Budget {cfg.baseYear} (แบ่งเดือนตาม Budget)</option>
            <option value="forecast">เท่า Forecast {cfg.baseYear} (เฉลี่ยทุกเดือน)</option>
          </select>
        </label>
        <label className="field">หน่วยงานย่อย
          <select value={su || ''} onChange={(e) => setSu(Number(e.target.value))}>
            {data.subunits.map((s) => <option key={s.id} value={s.id}>{s.name}</option>)}
          </select>
        </label>
        <div className="small mute">{target ? 'ต่อท้ายกิจกรรม "' + RECURRING_NAME + '" ที่มีอยู่แล้ว' : 'สร้างกิจกรรม "' + RECURRING_NAME + '" ใหม่'}</div>
      </div>
      <div className="bulk recur">
        <table className="t">
          <thead>
            <tr>
              <th>รหัสค่าใช้จ่าย</th><th className="num old">Budget {cfg.baseYear}</th><th className="num old">Forecast {cfg.baseYear}</th>
              <th>การจ่าย</th><th className="num focus">ยอดที่จะขอ ปี {cfg.year}</th><th />
            </tr>
          </thead>
          <tbody>
            {lines.map((l) => {
              const ev = evOf(l), p = plan[l.acct], isOpen = !!open[l.acct];
              return (
                <React.Fragment key={l.acct}>
                  <tr>
                    <td><span className="mute small">{l.acct}</span> {l.name}</td>
                    <td className="num old">{fmt(ev.B)}</td>
                    <td className="num old">{ev.P == null ? '–' : fmt(ev.P)}</td>
                    <td style={{ width: 170 }}>
                      <select value={p.mode} onChange={(e) => { setMode(l.acct, e.target.value); if (e.target.value === 'months') setOpen({ ...open, [l.acct]: true }); }} aria-label={'การจ่าย ' + l.name}>
                        <option value="even">เฉลี่ยทุกเดือน</option>
                        <option value="months">กำหนดรายเดือน</option>
                      </select>
                    </td>
                    <td className="focus" style={{ width: 180 }}>
                      {p.mode === 'even'
                        ? <NumInput className="amt" value={p.amount} onValue={(x) => setRow(l.acct, { amount: x == null ? 0 : x })} ariaLabel={'ยอดที่จะขอ ' + l.name} />
                        : <div className="num amt-ro" title="ผลรวมยอดรายเดือน">{fmt(amountOf(l.acct))}</div>}
                    </td>
                    <td style={{ width: 120, textAlign: 'right' }}>
                      <button className="btn sm ghost" onClick={() => setOpen({ ...open, [l.acct]: !isOpen })} aria-expanded={isOpen}>
                        รายเดือน <Icon name="chevron" className={'chev' + (isOpen ? ' open' : '')} />
                      </button>
                    </td>
                  </tr>
                  {isOpen && (
                    <tr className="recur-months">
                      <td colSpan={6}>
                        <div className="recur-grid">
                          <MonthsEditor
                            months={monthsOf(l.acct)} budget={l.budget} actual={l.actual} cfg={cfg} nav={'rc-' + l.acct}
                            onChange={(months) => setRow(l.acct, { mode: 'months', months })}
                          />
                          <div className="recur-help small">
                            <div className="quick">
                              <button className="btn sm" onClick={() => setRow(l.acct, { mode: 'months', months: l.budget.map((x) => Math.max(0, R.round2(x))) })}>ตาม Budget {cfg.baseYear} ทุกเดือน</button>
                              <button className="btn sm" onClick={() => setRow(l.acct, { mode: 'even', amount: amountOf(l.acct) })}>เฉลี่ยยอดรวมเท่ากันทุกเดือน</button>
                            </div>
                            <p className="mute">พิมพ์ยอดในคอลัมน์ "ยอดขอปี {cfg.year}" ทีละเดือน (Enter = เดือนถัดไป) ระบบรวมเป็นยอดทั้งปีให้ · พิมพ์แล้วการจ่ายจะเปลี่ยนเป็น "กำหนดรายเดือน" ให้เอง</p>
                            <p className="mute">ตัวเลขสีจาง = Budget และ Actual ปี {cfg.baseYear} ไว้เทียบ</p>
                          </div>
                        </div>
                      </td>
                    </tr>
                  )}
                </React.Fragment>
              );
            })}
          </tbody>
        </table>
      </div>
      {lines.some((l) => !(amountOf(l.acct) > 0)) && <p className="small mute">รหัสที่ยอดเป็น 0 จะไม่ถูกเพิ่ม — ถ้าไม่ขอปีนี้ ให้กด "ไม่ขอ" ที่บรรทัดนั้นในขั้นตรวจสรุป</p>}
    </Modal>
  );
}

/** ขั้นที่ 1: รายการกิจกรรมของฝ่าย จัดกลุ่มตามหน่วยงานย่อย */
function ActivityBoard({ deptCode, data, me, canEdit, onData, reload, beforeWrite, onReview, focusId, focusKey, issues, prefill, onPrefillUsed }) {
  const [suF, setSuF] = useState('all');
  const [q, setQ] = useState('');
  const [editing, setEditing] = useState(null);   // { initial }
  const [detail, setDetail] = useState(null);     // id ของกิจกรรมที่เปิดดูรายละเอียด
  const [order, setOrder] = useState('added');    // added | amount | name
  const [manage, setManage] = useState(false);
  const [del, setDel] = useState(null);
  const [busy, setBusy] = useState(false);
  const cfg = data.round;
  const dataMe = useMemo(() => ({ ...data, me }), [data, me]);
  const subName = (id) => (data.subunits.find((s) => s.id === id) || {}).name || '–';
  const acctName = useMemo(() => Object.fromEntries(data.accounts.map((a) => [a.code, a.name])), [data]);
  const nameOf = (c) => acctName[c] || '';

  // มาจากขั้นที่ 2 "+ กิจกรรม": เปิดกิจกรรมใหม่ที่ใส่รหัสนั้นไว้ให้แล้ว
  useEffect(() => {
    if (!prefill || !canEdit) return;
    setEditing({ initial: { kind: 'activity', items: [{ acct: prefill, amount: null, note: '', qty: null, unitPrice: null, timing: 'spread', month: null, months: null }] } });
    onPrefillUsed();
  }, [prefill]);

  useEffect(() => {
    if (!focusId) return;
    setDetail(focusId);
    const t = setTimeout(() => { const el = document.getElementById('act-' + focusId); if (el) el.scrollIntoView({ block: 'center', behavior: 'smooth' }); }, 80);
    return () => clearTimeout(t);
  }, [focusId, focusKey]);

  const needle = q.trim().toLowerCase();
  const match = (a) => !needle || a.name.toLowerCase().includes(needle) || a.detail.toLowerCase().includes(needle)
    || a.items.some((it) => it.acct.includes(needle) || nameOf(it.acct).toLowerCase().includes(needle) || (it.note || '').toLowerCase().includes(needle));
  const shown = data.activities.filter((a) => (suF === 'all' || a.subunitId === suF) && match(a));
  const totalOf = (list) => R.sum(list.map(R.activityTotal));
  const all = data.activities;
  const groups = data.subunits
    .map((s) => ({ s, list: shown.filter((a) => a.subunitId === s.id).sort((x, y) => (x.kind !== y.kind ? (x.kind === 'activity' ? -1 : 1)
      : order === 'amount' ? R.activityTotal(y) - R.activityTotal(x) : order === 'name' ? x.name.localeCompare(y.name, 'th') : x.id - y.id)) }))
    .filter((g) => g.list.length);

  const saved = (r, wasEdit, conflict) => {
    setEditing(null);
    if (conflict || !r) { reload(); return; }
    onData(r);
    toast(wasEdit ? 'บันทึกกิจกรรมแล้ว ยอดในขั้นที่ 2 อัปเดตแล้ว' : 'เพิ่มกิจกรรมแล้ว ยอดในขั้นที่ 2 อัปเดตแล้ว');
  };
  const doDelete = async () => {
    setBusy(true);
    try { await beforeWrite(); onData(await API.deleteActivity(deptCode, del.id, del.updatedAt)); toast('ลบกิจกรรมแล้ว'); setDel(null); }
    catch (e) { toast(e.message, 'error'); if (e.status === 409) { setDel(null); reload(); } } finally { setBusy(false); }
  };
  const recurringTotal = totalOf(all.filter((a) => a.kind === 'recurring'));
  const allTotal = totalOf(all);
  const maxTotal = Math.max(0, ...all.map(R.activityTotal));

  return (
    <div>
      <div className="tiles">
        <div className="tile t-request"><div className="k">ยอดรวมจากกิจกรรม</div><div className="v">{fmt(totalOf(all))}</div><div className="s">{all.length} กิจกรรม · ระบบรวมตามรหัสไปที่ขั้นที่ 2 ให้เอง</div></div>
        <div className="tile t-budget"><div className="k">กิจกรรม</div><div className="v">{fmt(R.round2(totalOf(all) - recurringTotal))}</div>
          <div className="s">{all.filter((a) => a.kind === 'activity').length} กิจกรรม{totalOf(all) > 0 ? ' · ' + Math.round(((totalOf(all) - recurringTotal) / totalOf(all)) * 100) + '% ของยอดรวม' : ''}</div></div>
        <div className="tile t-actual"><div className="k">ค่าใช้จ่ายประจำ</div><div className="v">{fmt(recurringTotal)}</div>
          <div className="s">{totalOf(all) > 0 ? Math.round((recurringTotal / totalOf(all)) * 100) + '% ของยอดรวม · ' : ''}เงินเดือน ค่าโทรศัพท์ ฯลฯ ที่เกิดทุกเดือน</div></div>
        <div className={'tile ' + (issues ? 't-flag' : 't-status')}>
          <div className="k">ขั้นที่ 2: ตรวจก่อนส่ง</div>
          <div className="v">{issues ? issues + ' รายการ' : 'พร้อม'}</div>
          <div className="s"><button className="link" onClick={onReview}>{issues ? 'ไปดูสิ่งที่ต้องจัดการ →' : 'ไปตรวจสรุปและส่งงบ →'}</button></div>
        </div>
      </div>

      <div className="card">
        <div className="toolbar">
          <div className="chips" role="group" aria-label="กรองตามหน่วยงานย่อย">
            <button className={'chip' + (suF === 'all' ? ' on' : '')} onClick={() => setSuF('all')}>ทุกหน่วยงาน {all.length}</button>
            {data.subunits.map((s) => {
              const list = all.filter((a) => a.subunitId === s.id);
              return <button key={s.id} className={'chip' + (suF === s.id ? ' on' : '')} onClick={() => setSuF(s.id)} title={'รวม ' + fmt(totalOf(list)) + ' บาท'}>{s.name} {list.length}</button>;
            })}
          </div>
          {canEdit && <button className="btn sm ghost" onClick={() => setManage(true)}><Icon name="edit" />หน่วยงานย่อย</button>}
          <div className="search">
            <Icon name="search" />
            <input type="text" value={q} onChange={(e) => setQ(e.target.value)} placeholder="ค้นกิจกรรม รหัส หรือหมายเหตุ" aria-label="ค้นกิจกรรม" />
          </div>
          <select value={order} onChange={(e) => setOrder(e.target.value)} aria-label="เรียงกิจกรรม" style={{ height: 30, fontSize: 13 }}>
            <option value="added">เรียง: ตามที่เพิ่ม</option>
            <option value="amount">เรียง: ยอดมาก → น้อย</option>
            <option value="name">เรียง: ชื่อ ก–ฮ</option>
          </select>
          <div style={{ flex: 1 }} />
          {canEdit && <>
            <button className="btn sm primary" onClick={() => setEditing({ initial: { kind: 'activity', subunitId: suF === 'all' ? undefined : suF } })}><Icon name="plus" />เพิ่มกิจกรรม</button>
            <button className="btn sm" title="เฉพาะค่าใช้จ่ายที่เกิดทุกเดือนโดยไม่ได้มาจากกิจกรรม เช่น เงินเดือน ค่าโทรศัพท์" onClick={() => setEditing({ initial: { kind: 'recurring', name: RECURRING_NAME, subunitId: suF === 'all' ? undefined : suF } })}><Icon name="plus" />เพิ่มค่าใช้จ่ายประจำ</button>
          </>}
        </div>

        <div className="actboard">
          {all.length === 0 && (
            <div className="actempty">
              <h3>เริ่มจากกิจกรรมที่ฝ่ายจะทำในปี {cfg.year}</h3>
              {canEdit ? (
                <>
                  <ol className="startsteps">
                    <li><b>เพิ่มกิจกรรม</b> งาน โครงการ อบรม หรือเอาติ้ง ที่ฝ่ายวางแผนไว้ แล้วใส่ค่าใช้จ่ายที่ต้องใช้ในแต่ละกิจกรรม เช่น ค่าที่พัก ค่าอาหาร พร้อมที่มาของตัวเลข</li>
                    <li><b>ค่าใช้จ่ายประจำ</b> เฉพาะค่าใช้จ่ายที่เกิดทุกเดือนโดยไม่ได้มาจากกิจกรรม เช่น เงินเดือน ค่าโทรศัพท์ ใส่หลังจากใส่กิจกรรมครบแล้ว</li>
                    <li><b>ขั้นที่ 2 ตรวจสรุป</b> รหัสที่ปีนี้มีงบแต่ยังไม่มีใครใส่จะขึ้นให้เลือก: ใส่ในกิจกรรม เป็นค่าใช้จ่ายประจำ หรือไม่ขอ</li>
                  </ol>
                  <button className="btn primary" onClick={() => setEditing({ initial: { kind: 'activity' } })}><Icon name="plus" />เพิ่มกิจกรรมแรก</button>
                </>
              ) : <p className="mute">ฝ่ายนี้ยังไม่ได้กรอกกิจกรรม</p>}
            </div>
          )}
          {all.length > 0 && groups.length === 0 && <div className="empty">ไม่พบกิจกรรมที่ตรงกับที่ค้น</div>}
          {groups.length > 0 && (
            <div className="actrow head" aria-hidden="true">
              <span className="ar-name">กิจกรรม · รหัสค่าใช้จ่ายในกิจกรรม</span><span className="ar-period">ช่วงเวลา</span><span className="ar-items">รายการ</span>
              <span className="ar-total">ยอดรวม (บาท)</span><span className="ar-share">สัดส่วนของฝ่าย</span><span />
            </div>
          )}
          {groups.map((g) => (
            <section key={g.s.id} className="actgroup">
              <div className="lgroup"><span>{g.s.name}</span><span className="sub">{g.list.length} กิจกรรม · {fmt(totalOf(g.list))} บาท</span></div>
              <div className="actlist">
                {g.list.map((a) => (
                  <ActivityRow
                    key={a.id} a={a} nameOf={nameOf} focused={a.id === focusId || a.id === detail}
                    share={allTotal > 0 ? (R.activityTotal(a) / allTotal) * 100 : 0} maxTotal={maxTotal}
                    onOpen={() => setDetail(a.id)}
                  />
                ))}
              </div>
            </section>
          ))}
        </div>
      </div>

      {detail && all.find((a) => a.id === detail) && (() => {
        const a = all.find((x) => x.id === detail);
        return (
          <ActivityDetail
            a={a} data={data} subName={subName(a.subunitId)} nameOf={nameOf} canEdit={canEdit}
            onClose={() => setDetail(null)}
            onEdit={() => { setDetail(null); setEditing({ initial: a }); }}
            onCopy={() => { setDetail(null); setEditing({ initial: { ...a, id: undefined, updatedAt: undefined, name: a.name + ' (สำเนา)' } }); }}
            onDelete={() => { setDetail(null); setDel(a); }}
          />
        );
      })()}
      {editing && (
        <ActivityEditor
          deptCode={deptCode} data={dataMe} initial={editing.initial} beforeWrite={beforeWrite}
          onClose={() => setEditing(null)} onSaved={saved} onData={onData}
        />
      )}
      {manage && <SubunitManager deptCode={deptCode} data={data} onData={onData} beforeWrite={beforeWrite} onClose={() => setManage(false)} />}
      {del && (
        <Modal title="ลบกิจกรรม" onClose={() => setDel(null)} footer={<>
          <button className="btn" onClick={() => setDel(null)}>ยกเลิก</button>
          <button className="btn danger" disabled={busy} onClick={doDelete}><Icon name="trash" />ลบกิจกรรม</button>
        </>}>
          <p>ลบ <b>{del.name}</b> ({subName(del.subunitId)}) ยอด <b>{fmt(R.activityTotal(del))}</b> บาท?</p>
          <p className="mute small">ยอดของทุกรหัสในกิจกรรมนี้จะหายจากตารางขั้นที่ 2 รหัสที่ไม่มีกิจกรรมอื่นใช้จะกลับเป็น "ยังไม่ระบุ"</p>
        </Modal>
      )}
    </div>
  );
}
