// hrm-profile.jsx — trang CHI TIẾT HỒ SƠ nhân sự (module HRM) + các form con.
// Nạp SAU hrm.jsx (dùng useHrm/HRM_* qua global), TRƯỚC main.jsx.
// 6 section: Thông tin chung · Hợp đồng (+phụ lục) · Quá trình công tác · Đào tạo · Khen thưởng-Kỷ luật · Nhân thân.

function HrmInfoRow({ label, value, mono }){
  return (
    <div className="flex g8" style={{ padding:'4px 0', borderBottom:'1px dashed var(--border-light)' }}>
      <span className="subtle" style={{ flex:'0 0 150px', fontSize:'var(--fs-base)' }}>{label}</span>
      <span className={'grow' + (mono ? ' mono' : '')} style={{ fontWeight:500, fontSize:'var(--fs-base)', minWidth:0, overflowWrap:'anywhere' }}>{value || <span className="subtle">—</span>}</span>
    </div>
  );
}

/* Ảnh chân dung: resize client về 256×256 (cover-crop) JPEG ~10–20KB rồi lưu data-URL vào cột photo
   (0056) — không qua Storage (bucket hrm-docs private, mỗi lần render sẽ phải xin signed-URL). */
async function hrmResizePhoto(file){
  const url = URL.createObjectURL(file);
  try {
    const img = await new Promise((res, rej) => { const i = new Image(); i.onload = () => res(i); i.onerror = rej; i.src = url; });
    const S = 256, c = document.createElement('canvas'); c.width = S; c.height = S;
    const ctx = c.getContext('2d');
    const r = Math.max(S / img.width, S / img.height), w = img.width * r, h = img.height * r;
    ctx.drawImage(img, (S - w) / 2, (S - h) / 2, w, h);
    return c.toDataURL('image/jpeg', 0.85);
  } finally { URL.revokeObjectURL(url); }
}

/* Cặp ô Vị trí + Phòng ban dùng chung cho 3 form (hồ sơ · hợp đồng · công tác) — đổ từ cơ cấu
   tổ chức (Thiết lập → Tổ chức). Chưa khai báo → gợi ý từ hồ sơ hiện có; danh mục rỗng hẳn → ô gõ tay.
   Giá trị cũ không còn trong danh mục vẫn giữ làm 1 option (không mất dữ liệu). */
function HrmOrgFields({ position, dept, onPosition, onDept, posLabel, flexPos, flexDept }){
  const { p, data } = useHrm();
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const st = hrmSettings(p.appSettings);
  const depts = hrmDeptNames(st, data.employees, dept);
  const poss = hrmPositions(st, data.employees, dept, position);
  const sel = (value, onChange, options) => (
    <select className="select" value={value || ''} onChange={ev => onChange(ev.target.value)}>
      <option value="">—</option>
      {options.map(x => <option key={x} value={x}>{x}</option>)}
    </select>
  );
  return (<>
    <div style={{ flex:flexPos || 1.3 }}><Field label={posLabel || L('Vị trí', 'Position')}>
      {poss.length ? sel(position, onPosition, poss)
        : <input className="input" value={position || ''} onChange={ev => onPosition(ev.target.value)} />}
    </Field></div>
    <div style={{ flex:flexDept || 1 }}><Field label={L('Phòng ban', 'Dept')}>
      {depts.length ? sel(dept, onDept, depts)
        : <input className="input" value={dept || ''} onChange={ev => onDept(ev.target.value)} />}
    </Field></div>
  </>);
}

function HrmEmployeeProfile({ empId, onBack }){
  const { p, st, canManage, data } = useHrm();
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const [tab, setTab] = React.useState('general');
  const [editOpen, setEditOpen] = React.useState(false);
  const e = data.employees.find(x => x.id === empId);
  if (!e) return (
    <div className="page pagefade">
      <button className="btn btn-sm" onClick={onBack} style={{ marginBottom:12 }}><i className="bi bi-arrow-left"></i>{L('Quay lại', 'Back')}</button>
      <EmptyState icon="bi-person-x" text={L('Không tìm thấy hồ sơ (hoặc bạn không có quyền xem).', 'Record not found (or no permission).')} />
    </div>
  );
  const stt = HRM_STATUS[e.status] || HRM_STATUS.active;
  const warns = hrmContractWarnings(data.contracts, e.id);
  const year = +todayVN().slice(0, 4);
  const counts = {
    contracts:data.contracts.filter(c => c.employeeId === e.id).length,
    career:data.careers.filter(c => c.employeeId === e.id).length,
    training:data.trainings.filter(c => c.employeeId === e.id).length,
    reviews:(data.reviews || []).filter(c => c.employeeId === e.id).length,
    decisions:data.decisions.filter(c => c.employeeId === e.id).length,
    family:data.dependents.filter(c => c.employeeId === e.id).length,
    banks:(data.bankAccounts || []).filter(c => c.employeeId === e.id).length,
    docs:(data.documents || []).filter(c => c.employeeId === e.id).length,
  };
  // NGÂN SÁCH CHIỀU RỘNG — nhãn tab CỐ Ý ngắn. Chỗ hẹp nhất KHÔNG phải .page (max 1232px) mà là màn
  // 1440px: 1440 − sidebar 248 − padding 48 = 1144px cho cả 8 tab. Đo thật ở 1440: vừa khít 1144px.
  // Đã phải rút 3 nhãn để vừa: "Tài khoản ngân hàng" → "Ngân hàng" · "Khen thưởng · Kỷ luật" →
  // "Thưởng · Kỷ luật" · "Quá trình công tác" → "Công tác" (tiêu đề BÊN TRONG mục vẫn ghi đủ).
  // → Thêm tab mới hoặc nới nhãn thì PHẢI đo lại ở 1440px, không thì dải tab trượt ngang.
  const tabs = [
    { id:'general', icon:'bi-person-vcard', label:L('Thông tin chung', 'General') },
    { id:'contracts', icon:'bi-file-earmark-text', label:L('Hợp đồng', 'Contracts'), count:counts.contracts },
    { id:'career', icon:'bi-diagram-3', label:L('Công tác', 'Career'), count:counts.career },
    { id:'training', icon:'bi-mortarboard', label:L('Đào tạo · KPI', 'Training · KPI'), count:counts.training + counts.reviews },
    { id:'decisions', icon:'bi-trophy', label:L('Thưởng · Kỷ luật', 'Rewards · Discipline'), count:counts.decisions },
    { id:'family', icon:'bi-people', label:L('Nhân thân', 'Family'), count:counts.family },
    { id:'banks', icon:'bi-bank', label:L('Ngân hàng', 'Bank'), count:counts.banks },
    { id:'docs', icon:'bi-folder2-open', label:L('Hồ sơ', 'Documents'), count:counts.docs },
  ];
  return (
    <div className="page pagefade">
      <div className="page-head">
        <div className="flex items-center g12" style={{ minWidth:0 }}>
          <button className="btn btn-sm" onClick={onBack}><i className="bi bi-arrow-left"></i></button>
          <Avatar name={e.fullName} size={44} src={e.photo} />
          <div style={{ minWidth:0 }}>
            <h1 className="truncate" style={{ marginBottom:2 }}>{e.fullName} <span className="mono" style={{ fontSize:'var(--fs-base)', color:'var(--primary)', fontWeight:600 }}>{e.id}</span></h1>
            <div className="sub flex items-center g8 wrap">{e.position}{e.dept && <span>· {e.dept}</span>}<span className={'badge ' + stt.cls}><i className="dot"></i>{stt[lang]}</span>
              {hrmManagerOf(st, data.employees, e.id).length > 0 && <span className="badge badge-info"><i className="bi bi-person-badge" style={{ marginRight:3 }}></i>{L('Quản lý phòng ban', 'Dept manager')}</span>}</div>
          </div>
        </div>
        {canManage && <button className="btn btn-primary" onClick={() => setEditOpen(true)}><i className="bi bi-pencil"></i>{L('Sửa hồ sơ', 'Edit')}</button>}
      </div>
      {warns.length > 0 && (
        <div className="col g6" style={{ marginBottom:12 }}>
          {warns.map((w, i) => (
            <div key={i} className="flex items-center g8" style={{ padding:'8px 12px', borderRadius:6, fontSize:'var(--fs-base)',
              background:'color-mix(in srgb, ' + (w.cls === 'error' ? 'var(--error)' : 'var(--warning)') + ' 10%, transparent)',
              color:w.cls === 'error' ? 'var(--error)' : 'var(--warning)' }}>
              <i className="bi bi-exclamation-triangle-fill" style={{ flex:'none' }}></i><span style={{ color:'var(--text)' }}>{w[lang]}</span>
            </div>
          ))}
        </div>
      )}
      <Tabs tabs={tabs} active={tab} onChange={setTab} />
      <div style={{ marginTop:14 }}>
        {tab === 'general' && <HrmProfGeneral e={e} year={year} />}
        {tab === 'contracts' && <HrmProfContracts e={e} />}
        {tab === 'career' && <HrmProfCareer e={e} />}
        {tab === 'training' && <div className="col g14"><HrmProfTraining e={e} /><HrmProfReviews e={e} /></div>}
        {tab === 'decisions' && <HrmProfDecisions e={e} />}
        {tab === 'family' && <HrmProfFamily e={e} />}
        {tab === 'banks' && <HrmProfBanks e={e} />}
        {tab === 'docs' && <HrmProfDocs e={e} />}
      </div>
      {editOpen && <HrmEmployeeFormModal editing={e} onClose={() => setEditOpen(false)} />}
    </div>
  );
}

/* ---------- section: Thông tin chung ---------- */
function HrmProfGeneral({ e, year }){
  const { p, st, canManage, data } = useHrm();
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const genderLabel = e.gender === 'male' ? L('Nam', 'Male') : e.gender === 'female' ? L('Nữ', 'Female') : '';
  const quota = hrmLeaveQuota(e, year, st), used = hrmLeaveUsed(data.requests, e.id, year);
  const linkedUser = e.userId ? (p.users || []).find(u => u.id === e.userId) : null;
  const linkAccount = (uid) => p.hrmPatch('employees', e.id, { ...e, userId:uid || null });
  return (
    <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fit, minmax(300px, 1fr))', gap:14, alignItems:'start' }}>
      <div className="card">
        <div className="card-head"><h3>{L('Nhân thân (NĐ 145/2020)', 'Personal details')}</h3></div>
        <div className="card-body">
          <HrmInfoRow label={L('Họ tên', 'Full name')} value={e.fullName} />
          <HrmInfoRow label={L('Giới tính', 'Gender')} value={genderLabel} />
          <HrmInfoRow label={L('Ngày sinh', 'Birth date')} value={e.birthDate && fmtDate(e.birthDate)} mono />
          <HrmInfoRow label={L('Quốc tịch', 'Nationality')} value={e.nationality} />
          <HrmInfoRow label={L('Nơi cư trú', 'Address')} value={e.address} />
          <HrmInfoRow label={L('CCCD/Hộ chiếu', 'ID number')} value={e.idNumber} mono />
          <HrmInfoRow label={L('Ngày cấp CCCD', 'ID issued')} value={e.idIssueDate && fmtDate(e.idIssueDate)} mono />
          <HrmInfoRow label={L('Điện thoại', 'Phone')} value={e.phone} mono />
          <HrmInfoRow label="Email" value={e.email} mono />
        </div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Công việc', 'Employment')}</h3></div>
        <div className="card-body">
          <HrmInfoRow label={L('Vị trí việc làm', 'Position')} value={e.position} />
          <HrmInfoRow label={L('Phòng ban', 'Department')} value={e.dept} />
          <HrmInfoRow label={L('Ngày bắt đầu', 'Start date')} value={fmtDate(e.startDate)} mono />
          <HrmInfoRow label={L('Thâm niên', 'Seniority')} value={hrmYearsOfService(e, todayVN()) + ' ' + L('năm', 'years')} />
          <HrmInfoRow label={L('Trình độ chuyên môn', 'Education')} value={e.education} />
          <HrmInfoRow label={L('Bậc kỹ năng nghề', 'Skill level')} value={e.skillLevel} />
          {e.status === 'terminated' && (<>
            <HrmInfoRow label={L('Ngày chấm dứt', 'End date')} value={e.endDate && fmtDate(e.endDate)} mono />
            <HrmInfoRow label={L('Phân loại chấm dứt', 'End type')} value={(HRM_END_TYPES[e.endType] || {})[lang]} />
            <HrmInfoRow label={L('Lý do chấm dứt', 'End reason')} value={e.endReason} />
          </>)}
          <HrmInfoRow label={L('Phép năm ' + year, 'Leave ' + year)} value={used + '/' + quota + ' ' + L('ngày (đã dùng/quỹ)', 'days (used/quota)')} mono />
        </div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('BHXH & Lương', 'Insurance & salary')}</h3></div>
        <div className="card-body">
          <HrmInfoRow label={L('Mã số BHXH', 'SI code')} value={e.siCode} mono />
          <HrmInfoRow label={L('Ngày tham gia BHXH', 'SI join date')} value={e.siJoinDate && fmtDate(e.siJoinDate)} mono />
          <HrmInfoRow label={L('Lương cơ bản', 'Base salary')} value={e.salary && e.salary.base ? vnd(e.salary.base) : ''} mono />
          {(e.salary && e.salary.allowances || []).map((a, i) => <HrmInfoRow key={i}
            label={L('Phụ cấp: ', 'Allowance: ') + a.name + (a.taxFree ? (a.taxFreeCap ? L(' (miễn thuế ≤ ', ' (tax-free ≤ ') + vnd(a.taxFreeCap) + ')' : L(' (miễn thuế)', ' (tax-free)')) : '')}
            value={vnd(a.amount)} mono />)}
          <HrmInfoRow label={L('Trần OT năm', 'Yearly OT cap')} value={e.otCapYear + 'h'} mono />
          <HrmInfoRow label={L('Điều chỉnh phép', 'Leave adjust')} value={(e.leaveAdjust > 0 ? '+' : '') + e.leaveAdjust + ' ' + L('ngày', 'days')} mono />
        </div>
      </div>
      <div className="card">
        <div className="card-head"><h3>{L('Tài khoản portal', 'Portal account')}</h3></div>
        <div className="card-body col g10">
          {linkedUser
            ? <div className="flex items-center g8"><Avatar name={linkedUser.name} size={24} /><span style={{ fontWeight:500 }}>{linkedUser.name}</span><span className="subtle mono" style={{ fontSize:'var(--fs-sm)' }}>{linkedUser.email}</span></div>
            : <div className="subtle">{L('Chưa liên kết — nhân viên chưa tự chấm công/tạo đơn được.', 'Not linked — employee cannot self check-in yet.')}</div>}
          {canManage && (
            <select className="select" value={e.userId || ''} onChange={ev => linkAccount(ev.target.value)}>
              <option value="">{L('— Không liên kết —', '— Not linked —')}</option>
              {(p.users || []).map(u => <option key={u.id} value={u.id}>{u.name} · {u.email}</option>)}
            </select>
          )}
          {e.note && <div className="subtle" style={{ fontSize:'var(--fs-base)', borderTop:'1px dashed var(--border-light)', paddingTop:8 }}><i className="bi bi-sticky" style={{ marginRight:5 }}></i>{e.note}</div>}
        </div>
      </div>
    </div>
  );
}

/* ---------- khung section bảng con (dùng chung) ---------- */
function HrmProfSection({ title, icon, addLabel, onAdd, children }){
  const { canManage } = useHrm();
  return (
    <div className="card">
      <div className="card-head"><h3><i className={'bi ' + icon} style={{ marginRight:7, color:'var(--primary)' }}></i>{title}</h3>
        {canManage && onAdd && <button className="btn btn-sm btn-primary" onClick={onAdd}><i className="bi bi-plus-lg"></i>{addLabel}</button>}</div>
      {children}
    </div>
  );
}
function HrmRowActions({ onEdit, onDelete }){
  const { canManage } = useHrm();
  const { lang } = useLang();
  if (!canManage) return <td></td>;
  return (
    <td style={{ whiteSpace:'nowrap', textAlign:'right' }} onClick={ev => ev.stopPropagation()}>
      <button className="iconbtn" title={lang === 'vi' ? 'Sửa' : 'Edit'} onClick={onEdit}><i className="bi bi-pencil" style={{ fontSize:12 }}></i></button>
      <button className="iconbtn" title={lang === 'vi' ? 'Xóa' : 'Delete'} onClick={onDelete}><i className="bi bi-trash" style={{ fontSize:12 }}></i></button>
    </td>
  );
}
function useHrmDelete(){
  const { p } = useHrm();
  const { lang } = useLang();
  const confirm = useConfirm();
  const toast = useToast();
  return async (key, id, label) => {
    const L = (vi, en) => (lang === 'vi' ? vi : en);
    if (!(await confirm({ message:L('Xóa ', 'Delete ') + label + '?', confirmLabel:L('Xóa', 'Delete') }))) return;
    p.hrmRemove(key, id); toast(L('Đã xóa', 'Deleted'), 'success');
  };
}

/* ---------- section: Hợp đồng (+ phụ lục lồng dưới HĐ gốc) ---------- */
function HrmProfContracts({ e }){
  const { data, canManage } = useHrm();
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const del = useHrmDelete();
  const [form, setForm] = React.useState(null);   // {editing?, parent?}
  const all = data.contracts.filter(c => c.employeeId === e.id);
  const parents = all.filter(c => c.kind !== 'appendix').sort((a, b) => (a.startDate || '') < (b.startDate || '') ? -1 : 1);
  const kidsOf = (id) => all.filter(c => c.parentId === id).sort((a, b) => (a.startDate || '') < (b.startDate || '') ? -1 : 1);
  const Row = ({ c, isChild }) => {
    const k = HRM_CONTRACT_KINDS[c.kind] || {}; const s = HRM_CONTRACT_STATUS[c.status] || {};
    return (
      <tr style={{ cursor:'default', background:isChild ? 'var(--bg-subtle)' : undefined }}>
        {/* Mã HĐ (thứ user quan tâm + đi ra giấy tờ) làm CHÍNH; id nội bộ chỉ để tra cứu → nhỏ, mờ. */}
        <td className="mono" style={{ fontWeight:600, color:'var(--primary)', paddingLeft:isChild ? 28 : undefined }}>
          {isChild && <i className="bi bi-arrow-return-right subtle" style={{ marginRight:6 }}></i>}{c.contractNo || <span className="subtle">{L('(chưa có số)', '(no number)')}</span>}
          <div className="subtle mono" style={{ fontSize:'var(--fs-sm)', fontWeight:400 }}>{c.id}</div></td>
        <td><span className={'badge ' + k.cls}>{k[lang]}</span></td>
        <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{c.signDate ? fmtDate(c.signDate) : '—'}</td>
        <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{c.startDate ? fmtDate(c.startDate) : '—'}{' → '}{c.endDate ? fmtDate(c.endDate) : L('Không thời hạn', 'Open')}</td>
        <td className="muted">{c.position}{c.dept ? ' · ' + c.dept : ''}</td>
        <td className="num mono">{c.salary && c.salary.base ? vnd(c.salary.base) : '—'}</td>
        <td><span className={'badge ' + s.cls}><i className="dot"></i>{s[lang]}</span></td>
        <HrmRowActions onEdit={() => setForm({ editing:c })} onDelete={() => del('contracts', c.id, L('hợp đồng ', 'contract ') + c.id)} />
      </tr>
    );
  };
  return (
    <HrmProfSection title={L('Hợp đồng lao động & phụ lục', 'Contracts & appendices')} icon="bi-file-earmark-text"
      addLabel={L('Thêm HĐ', 'New contract')} onAdd={() => setForm({})}>
      <div style={{ overflow:'auto' }}>
        <table className="tbl">
          <thead><tr><th>{L('Số HĐ', 'Contract no.')}</th><th>{L('Loại', 'Kind')}</th><th>{L('Ngày ký', 'Signed')}</th><th>{L('Hiệu lực', 'Term')}</th><th>{L('Chức vụ · Phòng ban', 'Position · Dept')}</th><th className="num">{L('Lương', 'Salary')}</th><th>{L('Trạng thái', 'Status')}</th><th style={{ width:110 }}></th></tr></thead>
          <tbody>
            {parents.length === 0 && <tr><td colSpan="8"><EmptyState icon="bi-file-earmark" text={L('Chưa có hợp đồng', 'No contracts')} /></td></tr>}
            {parents.map(c => (<React.Fragment key={c.id}>
              <Row c={c} />
              {kidsOf(c.id).map(k => <Row key={k.id} c={k} isChild />)}
              {canManage && (
                <tr style={{ cursor:'default' }}><td colSpan="8" style={{ padding:'2px 12px 8px 28px', borderTop:0 }}>
                  <button className="btn btn-ghost btn-sm" onClick={() => setForm({ parent:c })}><i className="bi bi-plus"></i>{L('Thêm phụ lục cho ' + c.id, 'Add appendix to ' + c.id)}</button>
                </td></tr>
              )}
            </React.Fragment>))}
          </tbody>
        </table>
      </div>
      {form && <HrmContractFormModal emp={e} editing={form.editing} parent={form.parent} onClose={() => setForm(null)} />}
    </HrmProfSection>
  );
}
function HrmContractFormModal({ emp, editing, parent, onClose }){
  const { p, data } = useHrm();
  const { lang } = useLang();
  const toast = useToast();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const isAppendix = !!parent || (editing && editing.kind === 'appendix');
  const [f, setF] = React.useState(editing
    ? { ...editing, base:(editing.salary && editing.salary.base) || '' }
    : { contractNo:'', kind:isAppendix ? 'appendix' : 'fixed', signDate:todayVN(), startDate:todayVN(), endDate:'', position:emp.position, dept:emp.dept, base:(emp.salary && emp.salary.base) || '', status:'active', note:'' });
  const set = (k, v) => setF(s => ({ ...s, [k]:v }));
  // cảnh báo pháp lý live: fixed > 36 tháng
  const months = f.kind === 'fixed' && f.startDate && f.endDate
    ? (+f.endDate.slice(0, 4) - +f.startDate.slice(0, 4)) * 12 + (+f.endDate.slice(5, 7) - +f.startDate.slice(5, 7)) : 0;
  const fixedCount = data.contracts.filter(c => c.employeeId === emp.id && c.kind === 'fixed' && (!editing || c.id !== editing.id)).length;
  // Số HĐ/phụ lục TỰ CẤP theo mẫu ở Thiết lập, nhưng CHO PHÉP gõ đè (nhập HĐ cũ) — giống HĐ bán:
  // f.contractNo có giá trị → dùng giá trị đó. Sửa HĐ cũ thì không tự cấp lại (giữ số đã ký).
  const st = hrmSettings(p.appSettings);
  const autoNo = editing ? '' : hrmNextContractNo(data.contracts, st, { date:f.signDate, employeeId:emp.id, parent });
  const contractNoVal = (f.contractNo || '').trim() || autoNo;
  // Mã HĐ là thứ đi ra giấy tờ pháp lý → KHÔNG cho trùng. So trên toàn bộ HĐ của mọi nhân viên.
  const dup = hrmContractNoTaken(data.contracts, contractNoVal, editing ? editing.id : null);
  const save = () => {
    if (!contractNoVal.trim()) { toast(L('Nhập số hợp đồng', 'Contract no. required'), 'error'); return; }
    if (dup) { toast(L('Số HĐ đã dùng cho ' + hrmEmpName(data.employees, dup.employeeId), 'Contract no. already used'), 'error'); return; }
    if (f.kind === 'fixed' && !f.endDate) { toast(L('HĐ xác định thời hạn cần ngày kết thúc', 'Fixed-term needs an end date'), 'error'); return; }
    if (f.endDate && f.startDate && f.endDate < f.startDate) { toast(L('Ngày kết thúc phải sau ngày bắt đầu', 'End must be after start'), 'error'); return; }
    const row = { id:editing ? editing.id : genHrmContractCode(data.contracts), employeeId:emp.id,
      parentId:editing ? editing.parentId : (parent ? parent.id : null),
      contractNo:contractNoVal, kind:isAppendix ? 'appendix' : f.kind, signDate:f.signDate || null, startDate:f.startDate || null,
      endDate:f.endDate || null, position:f.position, dept:f.dept, salary:{ ...(editing && editing.salary || {}), base:+f.base || 0 },
      status:f.status, note:f.note };
    if (editing) p.hrmPatch('contracts', editing.id, row); else p.hrmAdd('contracts', row);
    toast(L('Đã lưu hợp đồng', 'Contract saved'), 'success'); onClose();
  };
  return (
    <Modal title={(editing ? L('Sửa ', 'Edit ') : L('Thêm ', 'New ')) + (isAppendix ? L('phụ lục', 'appendix') + (parent ? ' · ' + parent.id : '') : L('hợp đồng', 'contract')) + ' · ' + emp.fullName}
      width={560} onClose={onClose} staticBackdrop
      footer={<><button className="btn" onClick={onClose}>{L('Hủy', 'Cancel')}</button><button className="btn btn-primary" onClick={save}>{editing ? L('Lưu', 'Save') : L('Thêm', 'Add')}</button></>}>
      <div className="col g16">
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Số HĐ/phụ lục', 'Contract no.')}
            hint={dup ? undefined : (autoNo ? L('Tự cấp — gõ đè nếu nhập HĐ cũ', 'Auto — type to override') : undefined)}>
            <input className="input mono" value={contractNoVal} onChange={ev => set('contractNo', ev.target.value)}
              placeholder={autoNo || '01/2026/HĐLĐ-SWO'} style={dup ? { borderColor:'var(--error)' } : undefined} />
            {dup && <div style={{ color:'var(--error)', fontSize:'var(--fs-sm)', marginTop:4 }}>
              <i className="bi bi-exclamation-circle" style={{ marginRight:4 }}></i>
              {L('Trùng số HĐ của ', 'Already used by ')}{hrmEmpName(data.employees, dup.employeeId)}</div>}
          </Field></div>
          <div style={{ flex:1 }}><Field label={L('Loại', 'Kind')}>
            {isAppendix ? <input className="input" value={L('Phụ lục', 'Appendix')} disabled />
              : <select className="select" value={f.kind} onChange={ev => set('kind', ev.target.value)}>
                  {Object.entries(HRM_CONTRACT_KINDS).filter(([id]) => id !== 'appendix').map(([id, v]) => <option key={id} value={id}>{v[lang]}</option>)}
                </select>}
          </Field></div>
          <div style={{ flex:1 }}><Field label={L('Trạng thái', 'Status')}>
            <select className="select" value={f.status} onChange={ev => set('status', ev.target.value)}>
              {Object.entries(HRM_CONTRACT_STATUS).map(([id, v]) => <option key={id} value={id}>{v[lang]}</option>)}
            </select></Field></div>
        </div>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Ngày ký', 'Sign date')}><DateInput value={f.signDate} onChange={v => set('signDate', v)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Hiệu lực từ', 'Start')}><DateInput value={f.startDate} onChange={v => set('startDate', v)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Đến (trống = vô hạn)', 'End (blank = open)')} hintError={months > 36 ? L('Quá 36 tháng — vượt trần Đ20 BLLĐ', 'Over 36 months (Art. 20)') : null}><DateInput value={f.endDate || ''} onChange={v => set('endDate', v)} /></Field></div>
        </div>
        <div className="flex g12">
          <HrmOrgFields position={f.position} dept={f.dept} onPosition={v => set('position', v)} onDept={v => set('dept', v)}
            posLabel={L('Chức vụ (ghi trong HĐ)', 'Position')} flexPos={1.4} />
          <div style={{ flex:1 }}><Field label={L('Lương cơ bản (₫)', 'Base salary')}><MoneyInput value={f.base} onChange={v => set('base', v)} /></Field></div>
        </div>
        {!isAppendix && f.kind === 'fixed' && fixedCount >= 2 && (
          <div className="badge badge-warning" style={{ alignSelf:'flex-start' }}><i className="bi bi-exclamation-triangle" style={{ marginRight:4 }}></i>
            {L('Đã có ' + fixedCount + ' HĐ xác định thời hạn — HĐ tiếp theo phải KHÔNG thời hạn (Đ20 BLLĐ).', fixedCount + ' fixed-term contracts exist — next must be indefinite (Art. 20).')}</div>
        )}
        <Field label={L('Ghi chú', 'Note')}><input className="input" value={f.note} onChange={ev => set('note', ev.target.value)} /></Field>
      </div>
    </Modal>
  );
}

/* ---------- section: Quá trình công tác ---------- */
function HrmProfCareer({ e }){
  const { p, data, canManage } = useHrm();
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const del = useHrmDelete();
  const [form, setForm] = React.useState(null);
  const rows = data.careers.filter(c => c.employeeId === e.id).sort((a, b) => a.fromDate < b.fromDate ? 1 : -1);
  return (
    <HrmProfSection title={L('Quá trình công tác tại công ty', 'Career history')} icon="bi-diagram-3"
      addLabel={L('Thêm giai đoạn', 'New period')} onAdd={() => setForm({})}>
      <div style={{ overflow:'auto' }}>
        <table className="tbl">
          <thead><tr><th>{L('Từ ngày', 'From')}</th><th>{L('Đến ngày', 'To')}</th><th>{L('Vị trí', 'Position')}</th><th>{L('Phòng ban', 'Dept')}</th><th>{L('Theo HĐ/phụ lục', 'Contract')}</th><th>{L('Ghi chú', 'Note')}</th><th style={{ width:110 }}></th></tr></thead>
          <tbody>
            {rows.length === 0 && <tr><td colSpan="7"><EmptyState icon="bi-diagram-3" text={L('Chưa có dữ liệu', 'No data')} /></td></tr>}
            {rows.map(c => (
              <tr key={c.id} style={{ cursor:'default' }}>
                <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{fmtDate(c.fromDate)}</td>
                <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{c.toDate ? fmtDate(c.toDate) : <span className="badge badge-success"><i className="dot"></i>{L('Hiện tại', 'Current')}</span>}</td>
                <td style={{ fontWeight:500 }}>{c.position}</td><td className="muted">{c.dept}</td>
                <td>{c.contractId ? <span className="mono" style={{ color:'var(--primary)', fontWeight:600, fontSize:'var(--fs-base)' }}>{c.contractId}</span> : <span className="subtle">—</span>}</td>
                <td className="muted" style={{ fontSize:'var(--fs-base)' }}>{c.note}</td>
                <HrmRowActions onEdit={() => setForm({ editing:c })} onDelete={() => del('careers', c.id, L('giai đoạn công tác', 'career period'))} />
              </tr>))}
          </tbody>
        </table>
      </div>
      {form && <HrmCareerFormModal emp={e} editing={form.editing} onClose={() => setForm(null)} />}
    </HrmProfSection>
  );
}
function HrmCareerFormModal({ emp, editing, onClose }){
  const { p, data } = useHrm();
  const { lang } = useLang();
  const toast = useToast();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const [f, setF] = React.useState(editing || { fromDate:todayVN(), toDate:'', position:emp.position, dept:emp.dept, contractId:'', note:'' });
  const set = (k, v) => setF(s => ({ ...s, [k]:v }));
  const contracts = data.contracts.filter(c => c.employeeId === emp.id);
  const save = () => {
    if (!f.fromDate) { toast(L('Nhập từ ngày', 'From date required'), 'error'); return; }
    if (f.toDate && f.toDate < f.fromDate) { toast(L('Đến ngày phải sau từ ngày', 'Invalid range'), 'error'); return; }
    const row = { id:editing ? editing.id : hrmUid('qt'), employeeId:emp.id, fromDate:f.fromDate, toDate:f.toDate || null,
      position:f.position, dept:f.dept, contractId:f.contractId || null, note:f.note };
    if (editing) { p.hrmPatch('careers', editing.id, row); }
    else {
      // giai đoạn MỚI đang mở → tự đóng giai đoạn mở cũ (đến ngày = trước 1 ngày) + sync vị trí hiện tại lên hồ sơ
      if (!row.toDate) {
        const open = data.careers.find(c => c.employeeId === emp.id && !c.toDate);
        if (open) {
          const d = new Date(f.fromDate + 'T00:00:00'); d.setDate(d.getDate() - 1);
          p.hrmPatch('careers', open.id, { ...open, toDate:d.toLocaleDateString('en-CA') });
        }
        p.hrmPatch('employees', emp.id, { ...emp, position:f.position, dept:f.dept });
      }
      p.hrmAdd('careers', row);
    }
    toast(L('Đã lưu quá trình công tác', 'Saved'), 'success'); onClose();
  };
  return (
    <Modal title={(editing ? L('Sửa giai đoạn', 'Edit period') : L('Thêm giai đoạn công tác', 'New career period')) + ' · ' + emp.fullName} width={520} onClose={onClose} staticBackdrop
      footer={<><button className="btn" onClick={onClose}>{L('Hủy', 'Cancel')}</button><button className="btn btn-primary" onClick={save}>{editing ? L('Lưu', 'Save') : L('Thêm', 'Add')}</button></>}>
      <div className="col g16">
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Từ ngày', 'From')}><DateInput value={f.fromDate} onChange={v => set('fromDate', v)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Đến ngày (trống = hiện tại)', 'To (blank = current)')} hint={!editing && !f.toDate ? L('Giai đoạn mở cũ sẽ tự đóng + cập nhật vị trí hiện tại', 'Old open period auto-closes') : null}><DateInput value={f.toDate || ''} onChange={v => set('toDate', v)} /></Field></div>
        </div>
        <div className="flex g12">
          <HrmOrgFields position={f.position} dept={f.dept} onPosition={v => set('position', v)} onDept={v => set('dept', v)} />
        </div>
        <Field label={L('Theo HĐ/phụ lục (chức vụ, phòng ban thường ghi trong HĐ)', 'Linked contract/appendix')}>
          <select className="select" value={f.contractId || ''} onChange={ev => set('contractId', ev.target.value)}>
            <option value="">{L('— Không —', '— None —')}</option>
            {contracts.map(c => <option key={c.id} value={c.id}>{c.id} · {(HRM_CONTRACT_KINDS[c.kind] || {})[lang]}{c.contractNo ? ' · ' + c.contractNo : ''}</option>)}
          </select>
        </Field>
        <Field label={L('Ghi chú', 'Note')}><input className="input" value={f.note} onChange={ev => set('note', ev.target.value)} /></Field>
      </div>
    </Modal>
  );
}

/* ---------- section: Đào tạo ---------- */
function HrmProfTraining({ e }){
  const { data } = useHrm();
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const del = useHrmDelete();
  const [form, setForm] = React.useState(null);
  const rows = data.trainings.filter(c => c.employeeId === e.id);
  return (
    <HrmProfSection title={L('Đào tạo, bồi dưỡng', 'Training')} icon="bi-mortarboard"
      addLabel={L('Thêm khóa', 'New course')} onAdd={() => setForm({})}>
      <div style={{ overflow:'auto' }}>
        <table className="tbl">
          <thead><tr><th>{L('Khóa đào tạo', 'Course')}</th><th>{L('Đơn vị', 'Provider')}</th><th>{L('Thời gian', 'Period')}</th><th className="num">{L('Số giờ', 'Hours')}</th><th className="num">{L('Chi phí', 'Cost')}</th><th>{L('Kết quả', 'Result')}</th><th style={{ width:110 }}></th></tr></thead>
          <tbody>
            {rows.length === 0 && <tr><td colSpan="7"><EmptyState icon="bi-mortarboard" text={L('Chưa có khóa đào tạo', 'No trainings')} /></td></tr>}
            {rows.map(c => (
              <tr key={c.id} style={{ cursor:'default' }}>
                <td style={{ fontWeight:500 }}>{c.name}{c.note && <div className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{c.note}</div>}</td>
                <td className="muted">{c.org}</td>
                <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{c.fromDate ? fmtDate(c.fromDate) : ''}{c.toDate ? ' → ' + fmtDate(c.toDate) : ''}</td>
                <td className="num mono">{c.hours != null ? c.hours : <span className="subtle">—</span>}</td>
                <td className="num mono">{c.cost != null ? vndShort(c.cost) : <span className="subtle">—</span>}</td>
                <td>{c.result ? <span className="badge badge-success">{c.result}</span> : <span className="subtle">—</span>}</td>
                <HrmRowActions onEdit={() => setForm({ editing:c })} onDelete={() => del('trainings', c.id, L('khóa đào tạo', 'training'))} />
              </tr>))}
          </tbody>
        </table>
      </div>
      {form && <HrmTrainingFormModal emp={e} editing={form.editing} onClose={() => setForm(null)} />}
    </HrmProfSection>
  );
}
function HrmTrainingFormModal({ emp, editing, onClose }){
  const { p } = useHrm();
  const { lang } = useLang();
  const toast = useToast();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const [f, setF] = React.useState(editing || { name:'', org:'', fromDate:'', toDate:'', hours:'', cost:'', result:'', note:'' });
  const set = (k, v) => setF(s => ({ ...s, [k]:v }));
  const save = () => {
    if (!f.name.trim()) { toast(L('Nhập tên khóa đào tạo', 'Course name required'), 'error'); return; }
    // hours/cost để TRỐNG → null ("chưa nhập"), khác hẳn 0 ("đào tạo nội bộ, không tốn phí") — báo cáo phân biệt 2 ca này.
    const num = (v) => (v === '' || v == null ? null : +v);
    const row = { id:editing ? editing.id : hrmUid('dt'), employeeId:emp.id, name:f.name, org:f.org, fromDate:f.fromDate || null, toDate:f.toDate || null, hours:num(f.hours), cost:num(f.cost), result:f.result, note:f.note };
    if (editing) p.hrmPatch('trainings', editing.id, row); else p.hrmAdd('trainings', row);
    toast(L('Đã lưu', 'Saved'), 'success'); onClose();
  };
  return (
    <Modal title={(editing ? L('Sửa khóa đào tạo', 'Edit training') : L('Thêm khóa đào tạo', 'New training')) + ' · ' + emp.fullName} width={520} onClose={onClose} staticBackdrop
      footer={<><button className="btn" onClick={onClose}>{L('Hủy', 'Cancel')}</button><button className="btn btn-primary" onClick={save}>{editing ? L('Lưu', 'Save') : L('Thêm', 'Add')}</button></>}>
      <div className="col g16">
        <Field label={L('Tên khóa/lớp', 'Course name')}><input className="input" value={f.name} onChange={ev => set('name', ev.target.value)} autoFocus /></Field>
        <div className="flex g12">
          <div style={{ flex:1.2 }}><Field label={L('Đơn vị đào tạo', 'Provider')}><input className="input" value={f.org} onChange={ev => set('org', ev.target.value)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Từ ngày', 'From')}><DateInput value={f.fromDate || ''} onChange={v => set('fromDate', v)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Đến ngày', 'To')}><DateInput value={f.toDate || ''} onChange={v => set('toDate', v)} /></Field></div>
        </div>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Số giờ học', 'Training hours')} hint={L('Để trống nếu chưa rõ', 'Leave blank if unknown')}>
            <input type="number" min="0" step="0.5" className="input mono" value={f.hours == null ? '' : f.hours} onChange={ev => set('hours', ev.target.value)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Chi phí (VNĐ)', 'Cost (VND)')} hint={L('Đào tạo nội bộ → nhập 0', 'Internal course → enter 0')}>
            <MoneyInput value={f.cost} onChange={v => set('cost', v)} /></Field></div>
        </div>
        <Field label={L('Kết quả / chứng chỉ', 'Result / certificate')}><input className="input" value={f.result} onChange={ev => set('result', ev.target.value)} placeholder={L('VD: Đạt chứng chỉ…', 'e.g. Certified…')} /></Field>
        <Field label={L('Ghi chú', 'Note')}><input className="input" value={f.note} onChange={ev => set('note', ev.target.value)} /></Field>
      </div>
    </Modal>
  );
}

/* ---------- section: Đánh giá hiệu suất (KPI/OKR — 0053) ----------
   Nằm CHUNG tab với Đào tạo: cùng nhóm "Đào tạo & Hiệu suất" của bộ báo cáo, và dải tab hồ sơ đã kín
   chỗ (xem ghi chú ở HrmEmployeeProfile) — thêm tab thứ 9 là tràn hàng. */
function HrmProfReviews({ e }){
  const { data } = useHrm();
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const del = useHrmDelete();
  const [form, setForm] = React.useState(null);
  const rows = (data.reviews || []).filter(c => c.employeeId === e.id)
    .slice().sort((a, b) => (a.reviewDate < b.reviewDate ? 1 : -1));
  return (
    <HrmProfSection title={L('Đánh giá hiệu suất (KPI · OKR)', 'Performance reviews')} icon="bi-speedometer2"
      addLabel={L('Thêm đánh giá', 'New review')} onAdd={() => setForm({})}>
      <div style={{ overflow:'auto' }}>
        <table className="tbl">
          <thead><tr><th>{L('Kỳ đánh giá', 'Period')}</th><th>{L('Loại', 'Kind')}</th><th>{L('Ngày chốt', 'Date')}</th>
            <th className="num">{L('Điểm', 'Score')}</th><th>{L('Kết quả', 'Outcome')}</th><th>{L('Chỉ tiêu', 'Goals')}</th>
            <th style={{ width:110 }}></th></tr></thead>
          <tbody>
            {rows.length === 0 && <tr><td colSpan="7"><EmptyState icon="bi-speedometer2" text={L('Chưa có kỳ đánh giá nào', 'No reviews yet')} /></td></tr>}
            {rows.map(c => {
              const rt = HRM_REVIEW_RATINGS[c.rating] || HRM_REVIEW_RATINGS.meet;
              return (
                <tr key={c.id} style={{ cursor:'default' }}>
                  <td className="mono" style={{ fontWeight:600 }}>{c.period}</td>
                  <td>{(HRM_REVIEW_KINDS[c.kind] || {})[lang] || c.kind}</td>
                  <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{fmtDate(c.reviewDate)}</td>
                  <td className="num mono" style={{ fontWeight:700 }}>{c.score != null ? c.score : '—'}</td>
                  <td><span className={'badge ' + rt.cls}><i className="dot"></i>{rt[lang] || rt.vi}</span></td>
                  <td className="subtle" style={{ fontSize:'var(--fs-sm)' }}>
                    {(c.goals || []).length ? (c.goals || []).map(g => g.name).join(' · ') : '—'}
                    {c.note && <div>{c.note}</div>}
                  </td>
                  <HrmRowActions onEdit={() => setForm({ editing:c })} onDelete={() => del('reviews', c.id, L('kỳ đánh giá', 'review'))} />
                </tr>);
            })}
          </tbody>
        </table>
      </div>
      {form && <HrmReviewFormModal emp={e} editing={form.editing} onClose={() => setForm(null)} />}
    </HrmProfSection>
  );
}
function HrmReviewFormModal({ emp, editing, onClose }){
  const { p } = useHrm();
  const { lang } = useLang();
  const toast = useToast();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const [f, setF] = React.useState(editing
    ? { ...editing, goals:(editing.goals || []).map(g => ({ ...g })) }
    : { period:'', reviewDate:todayVN(), kind:'kpi', score:'', rating:'meet', goals:[], note:'' });
  const set = (k, v) => setF(s => ({ ...s, [k]:v }));
  const setGoal = (i, k, v) => setF(s => ({ ...s, goals:s.goals.map((g, j) => j === i ? { ...g, [k]:v } : g) }));
  const addGoal = () => setF(s => ({ ...s, goals:[...s.goals, { name:'', weight:'', target:'', actual:'', score:'' }] }));
  const rmGoal = (i) => setF(s => ({ ...s, goals:s.goals.filter((g, j) => j !== i) }));
  const save = () => {
    if (!String(f.period).trim()) { toast(L('Nhập kỳ đánh giá (VD 2026-Q1)', 'Period required'), 'error'); return; }
    if (!f.reviewDate) { toast(L('Chọn ngày chốt đánh giá', 'Review date required'), 'error'); return; }
    const row = { id:editing ? editing.id : hrmUid('DG'), employeeId:emp.id, period:String(f.period).trim(),
      reviewDate:f.reviewDate, kind:f.kind, score:f.score !== '' && f.score != null ? +f.score : null, rating:f.rating,
      goals:f.goals.filter(g => String(g.name).trim()).map(g => ({ name:g.name, weight:g.weight === '' ? null : +g.weight,
        target:g.target, actual:g.actual, score:g.score === '' ? null : +g.score })),
      reviewerId:editing ? editing.reviewerId : ((p.me && p.me.id) || null), note:f.note };
    if (editing) p.hrmPatch('reviews', editing.id, row); else p.hrmAdd('reviews', row);
    toast(L('Đã lưu đánh giá', 'Review saved'), 'success'); onClose();
  };
  return (
    <Modal title={(editing ? L('Sửa đánh giá', 'Edit review') : L('Thêm đánh giá', 'New review')) + ' · ' + emp.fullName} width={720} onClose={onClose} staticBackdrop
      footer={<><button className="btn" onClick={onClose}>{L('Hủy', 'Cancel')}</button><button className="btn btn-primary" onClick={save}>{editing ? L('Lưu', 'Save') : L('Thêm', 'Add')}</button></>}>
      <div className="col g16">
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Kỳ đánh giá', 'Period')} hint={L('VD 2026-Q1', 'e.g. 2026-Q1')}>
            <input className="input mono" value={f.period} onChange={ev => set('period', ev.target.value)} placeholder="2026-Q1" autoFocus /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Ngày chốt', 'Review date')} hint={L('Mốc lọc kỳ báo cáo', 'Used by reports')}>
            <DateInput value={f.reviewDate || ''} onChange={v => set('reviewDate', v)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Loại', 'Kind')}>
            <select className="select" value={f.kind} onChange={ev => set('kind', ev.target.value)}>
              {Object.entries(HRM_REVIEW_KINDS).map(([id, v]) => <option key={id} value={id}>{v[lang]}</option>)}
            </select></Field></div>
          <div style={{ flex:1 }}><Field label={L('Điểm tổng', 'Total score')} hint={L('Thang 100', 'Out of 100')}>
            <input type="number" min="0" max="100" className="input mono" value={f.score} onChange={ev => set('score', ev.target.value)} /></Field></div>
          <div style={{ flex:1.2 }}><Field label={L('Kết quả', 'Outcome')}>
            <select className="select" value={f.rating} onChange={ev => set('rating', ev.target.value)}>
              {Object.entries(HRM_REVIEW_RATINGS).map(([id, v]) => <option key={id} value={id}>{v[lang]}</option>)}
            </select></Field></div>
        </div>
        <div>
          <div className="flex justify-between items-center" style={{ marginBottom:6 }}>
            <span style={{ fontWeight:600, fontSize:'var(--fs-base)' }}>{L('Chi tiết chỉ tiêu', 'Goals')}</span>
            <button className="btn btn-sm" onClick={addGoal}><i className="bi bi-plus-lg"></i>{L('Thêm chỉ tiêu', 'Add goal')}</button>
          </div>
          {f.goals.length === 0 && <div className="subtle" style={{ fontSize:'var(--fs-sm)' }}>
            {L('Không bắt buộc — để trống nếu chỉ cần điểm tổng.', 'Optional — leave empty if the total score is enough.')}</div>}
          <div className="col g8">
            {f.goals.map((g, i) => (
              <div key={i} className="flex g8 items-center">
                <div style={{ flex:2 }}><input className="input" value={g.name} onChange={ev => setGoal(i, 'name', ev.target.value)} placeholder={L('Tên chỉ tiêu', 'Goal name')} /></div>
                <div style={{ flex:'0 0 78px' }}><input type="number" className="input mono" value={g.weight} onChange={ev => setGoal(i, 'weight', ev.target.value)} placeholder={L('Trọng số', 'Weight')} /></div>
                <div style={{ flex:1 }}><input className="input" value={g.target} onChange={ev => setGoal(i, 'target', ev.target.value)} placeholder={L('Mục tiêu', 'Target')} /></div>
                <div style={{ flex:1 }}><input className="input" value={g.actual} onChange={ev => setGoal(i, 'actual', ev.target.value)} placeholder={L('Thực đạt', 'Actual')} /></div>
                <div style={{ flex:'0 0 72px' }}><input type="number" className="input mono" value={g.score} onChange={ev => setGoal(i, 'score', ev.target.value)} placeholder={L('Điểm', 'Score')} /></div>
                <button className="iconbtn" title={L('Xóa', 'Remove')} onClick={() => rmGoal(i)}><i className="bi bi-trash" style={{ fontSize:12 }}></i></button>
              </div>
            ))}
          </div>
        </div>
        <Field label={L('Nhận xét', 'Note')}><textarea className="input" rows={2} value={f.note} onChange={ev => set('note', ev.target.value)} /></Field>
      </div>
    </Modal>
  );
}

/* ---------- section: Khen thưởng · Kỷ luật ---------- */
function HrmProfDecisions({ e }){
  const { data } = useHrm();
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const del = useHrmDelete();
  const [form, setForm] = React.useState(null);
  const rows = data.decisions.filter(c => c.employeeId === e.id);
  return (
    <HrmProfSection title={L('Quyết định khen thưởng · kỷ luật', 'Reward & discipline decisions')} icon="bi-trophy"
      addLabel={L('Thêm quyết định', 'New decision')} onAdd={() => setForm({})}>
      <div style={{ overflow:'auto' }}>
        <table className="tbl">
          <thead><tr><th>{L('Loại', 'Kind')}</th><th>{L('Số QĐ', 'No.')}</th><th>{L('Ngày', 'Date')}</th><th>{L('Nội dung', 'Content')}</th><th className="num">{L('Số tiền', 'Amount')}</th><th style={{ width:110 }}></th></tr></thead>
          <tbody>
            {rows.length === 0 && <tr><td colSpan="6"><EmptyState icon="bi-trophy" text={L('Chưa có quyết định nào', 'No decisions')} /></td></tr>}
            {rows.map(c => { const k = HRM_DECISION_KINDS[c.kind] || {}; return (
              <tr key={c.id} style={{ cursor:'default' }}>
                <td><span className={'badge ' + k.cls}><i className={'bi ' + k.icon} style={{ marginRight:4 }}></i>{k[lang]}</span></td>
                <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{c.decisionNo}</td>
                <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{c.decisionDate ? fmtDate(c.decisionDate) : '—'}</td>
                <td style={{ fontWeight:500 }}>{c.content}{c.note && <div className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{c.note}</div>}</td>
                <td className="num mono" style={{ fontWeight:600, color:c.kind === 'reward' ? 'var(--success)' : 'var(--error)' }}>{c.amount != null ? vnd(c.amount) : '—'}</td>
                <HrmRowActions onEdit={() => setForm({ editing:c })} onDelete={() => del('decisions', c.id, L('quyết định ', 'decision ') + (c.decisionNo || ''))} />
              </tr>); })}
          </tbody>
        </table>
      </div>
      {form && <HrmDecisionFormModal emp={e} editing={form.editing} onClose={() => setForm(null)} />}
    </HrmProfSection>
  );
}
function HrmDecisionFormModal({ emp, editing, onClose }){
  const { p } = useHrm();
  const { lang } = useLang();
  const toast = useToast();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const [f, setF] = React.useState(editing || { kind:'reward', decisionNo:'', decisionDate:todayVN(), content:'', amount:'', note:'' });
  const set = (k, v) => setF(s => ({ ...s, [k]:v }));
  const save = () => {
    if (!f.content.trim()) { toast(L('Nhập nội dung quyết định', 'Content required'), 'error'); return; }
    const row = { id:editing ? editing.id : hrmUid('qd'), employeeId:emp.id, kind:f.kind, decisionNo:f.decisionNo, decisionDate:f.decisionDate || null, content:f.content, amount:f.amount !== '' && f.amount != null ? +f.amount : null, note:f.note };
    if (editing) p.hrmPatch('decisions', editing.id, row); else p.hrmAdd('decisions', row);
    toast(L('Đã lưu quyết định', 'Saved'), 'success'); onClose();
  };
  return (
    <Modal title={(editing ? L('Sửa quyết định', 'Edit decision') : L('Thêm quyết định', 'New decision')) + ' · ' + emp.fullName} width={520} onClose={onClose} staticBackdrop
      footer={<><button className="btn" onClick={onClose}>{L('Hủy', 'Cancel')}</button><button className="btn btn-primary" onClick={save}>{editing ? L('Lưu', 'Save') : L('Thêm', 'Add')}</button></>}>
      <div className="col g16">
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Loại', 'Kind')}>
            <select className="select" value={f.kind} onChange={ev => set('kind', ev.target.value)}>
              {Object.entries(HRM_DECISION_KINDS).map(([id, v]) => <option key={id} value={id}>{v[lang]}</option>)}
            </select></Field></div>
          <div style={{ flex:1 }}><Field label={L('Số quyết định', 'Decision no.')}><input className="input mono" value={f.decisionNo} onChange={ev => set('decisionNo', ev.target.value)} placeholder="QĐ-2026-…" /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Ngày QĐ', 'Date')}><DateInput value={f.decisionDate || ''} onChange={v => set('decisionDate', v)} /></Field></div>
        </div>
        <Field label={L('Nội dung', 'Content')}><textarea className="input" rows={2} value={f.content} onChange={ev => set('content', ev.target.value)} /></Field>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Số tiền thưởng/phạt (₫, nếu có)', 'Amount (optional)')}><MoneyInput value={f.amount} onChange={v => set('amount', v)} /></Field></div>
          <div style={{ flex:1.4 }}><Field label={L('Ghi chú', 'Note')}><input className="input" value={f.note} onChange={ev => set('note', ev.target.value)} /></Field></div>
        </div>
      </div>
    </Modal>
  );
}

/* ---------- section: Nhân thân ---------- */
function HrmProfFamily({ e }){
  const { data } = useHrm();
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const del = useHrmDelete();
  const [form, setForm] = React.useState(null);
  const rows = data.dependents.filter(c => c.employeeId === e.id);
  return (
    <HrmProfSection title={L('Nhân thân (người thân)', 'Family members')} icon="bi-people"
      addLabel={L('Thêm người thân', 'New member')} onAdd={() => setForm({})}>
      <div style={{ overflow:'auto' }}>
        <table className="tbl">
          <thead><tr><th>{L('Họ tên', 'Full name')}</th><th>{L('Quan hệ', 'Relation')}</th><th>{L('Ngày sinh', 'Birth date')}</th><th>{L('Điện thoại', 'Phone')}</th><th style={{ width:110 }}></th></tr></thead>
          <tbody>
            {rows.length === 0 && <tr><td colSpan="5"><EmptyState icon="bi-people" text={L('Chưa có thông tin nhân thân', 'No family records')} /></td></tr>}
            {rows.map(c => (
              <tr key={c.id} style={{ cursor:'default' }}>
                <td style={{ fontWeight:500 }}>{c.fullName}</td>
                <td><span className="badge badge-neutral">{c.relation}</span></td>
                <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{c.birthDate ? fmtDate(c.birthDate) : '—'}</td>
                <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{c.phone || '—'}</td>
                <HrmRowActions onEdit={() => setForm({ editing:c })} onDelete={() => del('dependents', c.id, L('người thân ', '') + c.fullName)} />
              </tr>))}
          </tbody>
        </table>
      </div>
      {form && <HrmDependentFormModal emp={e} editing={form.editing} onClose={() => setForm(null)} />}
    </HrmProfSection>
  );
}
function HrmDependentFormModal({ emp, editing, onClose }){
  const { p } = useHrm();
  const { lang } = useLang();
  const toast = useToast();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const rels = hrmRelations(p, editing && editing.relation);   // giữ quan hệ đang lưu dù đã xóa khỏi danh mục
  const [f, setF] = React.useState(editing || { fullName:'', relation:rels[0] || '', birthDate:'', phone:'', note:'' });
  const set = (k, v) => setF(s => ({ ...s, [k]:v }));
  const save = () => {
    if (!f.fullName.trim()) { toast(L('Nhập họ tên', 'Name required'), 'error'); return; }
    const row = { id:editing ? editing.id : hrmUid('nt'), employeeId:emp.id, fullName:f.fullName, relation:f.relation, birthDate:f.birthDate || null, phone:f.phone, note:f.note };
    if (editing) p.hrmPatch('dependents', editing.id, row); else p.hrmAdd('dependents', row);
    toast(L('Đã lưu', 'Saved'), 'success'); onClose();
  };
  return (
    <Modal title={(editing ? L('Sửa người thân', 'Edit member') : L('Thêm người thân', 'New family member')) + ' · ' + emp.fullName} width={480} onClose={onClose} staticBackdrop
      footer={<><button className="btn" onClick={onClose}>{L('Hủy', 'Cancel')}</button><button className="btn btn-primary" onClick={save}>{editing ? L('Lưu', 'Save') : L('Thêm', 'Add')}</button></>}>
      <div className="col g16">
        <div className="flex g12">
          <div style={{ flex:1.4 }}><Field label={L('Họ tên', 'Full name')}><input className="input" value={f.fullName} onChange={ev => set('fullName', ev.target.value)} autoFocus /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Quan hệ', 'Relation')} hint={L('Sửa danh mục ở Thiết lập', 'Edit list in Settings')}>
            <select className="select" value={f.relation} onChange={ev => set('relation', ev.target.value)}>
              {rels.map(r => <option key={r} value={r}>{r}</option>)}
            </select>
          </Field></div>
        </div>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Ngày sinh', 'Birth date')}><DateInput value={f.birthDate || ''} onChange={v => set('birthDate', v)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Điện thoại (nếu có)', 'Phone (optional)')}><input className="input mono" value={f.phone} onChange={ev => set('phone', ev.target.value)} /></Field></div>
        </div>
      </div>
    </Modal>
  );
}

/* ---------- section: Tài khoản ngân hàng ---------- */
function HrmProfBanks({ e }){
  const { p, data, canManage } = useHrm();
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const toast = useToast();
  const del = useHrmDelete();
  const [form, setForm] = React.useState(null);
  const [show, setShow] = React.useState(false);   // hiện đầy đủ số TK (mặc định che)
  const rows = (data.bankAccounts || []).filter(b => b.employeeId === e.id);
  const primary = rows.find(b => b.isPrimary);
  // Đổi TK nhận lương: bỏ cờ TK cũ + đặt TK mới trong CÙNG 1 lượt ghi tuần tự (xem hrmPatchMany).
  const setPrimary = (b) => {
    const patch = rows.filter(x => x.isPrimary && x.id !== b.id).map(x => ({ ...x, isPrimary:false }));
    p.hrmPatchMany('bankAccounts', [...patch, { ...b, isPrimary:true }]);
    toast(L('Đã đặt làm tài khoản nhận lương', 'Set as payroll account'), 'success');
  };
  return (
    <HrmProfSection title={L('Tài khoản ngân hàng', 'Bank accounts')} icon="bi-bank"
      addLabel={L('Thêm tài khoản', 'New account')} onAdd={() => setForm({})}>
      {rows.length > 0 && !primary && (
        <div className="flex items-center g8" style={{ margin:'0 12px 10px', padding:'8px 12px', borderRadius:6, fontSize:'var(--fs-base)',
          background:'color-mix(in srgb, var(--warning) 10%, transparent)', color:'var(--warning)' }}>
          <i className="bi bi-exclamation-triangle-fill" style={{ flex:'none' }}></i>
          <span style={{ color:'var(--text)' }}>{L('Chưa chọn tài khoản nhận lương — phiếu lương sẽ không có số tài khoản.', 'No payroll account selected.')}</span>
        </div>
      )}
      <div style={{ overflow:'auto' }}>
        <table className="tbl">
          <thead><tr><th>{L('Ngân hàng', 'Bank')}</th><th>{L('Chi nhánh', 'Branch')}</th>
            <th>{L('Số tài khoản', 'Account no.')}
              <button className="iconbtn" title={show ? L('Ẩn số TK', 'Hide') : L('Hiện số TK', 'Reveal')} style={{ marginLeft:4 }}
                onClick={() => setShow(v => !v)}><i className={'bi ' + (show ? 'bi-eye-slash' : 'bi-eye')} style={{ fontSize:12 }}></i></button>
            </th>
            <th>{L('Chủ tài khoản', 'Holder')}</th><th>{L('Nhận lương', 'Payroll')}</th><th style={{ width:110 }}></th></tr></thead>
          <tbody>
            {rows.length === 0 && <tr><td colSpan="6"><EmptyState icon="bi-bank" text={L('Chưa có tài khoản ngân hàng', 'No bank accounts')} /></td></tr>}
            {rows.map(b => { const info = vnBankBy(b.bankName); return (
              <tr key={b.id} style={{ cursor:'default' }}>
                <td style={{ fontWeight:500 }}>
                  <span className="flex items-center g8" title={info ? info.n : undefined}>
                    {info && <img src={VN_BANK_LOGO(info.c)} alt="" loading="lazy"
                      onError={ev => { ev.target.style.display = 'none'; }}
                      style={{ width:30, height:20, objectFit:'contain', flex:'none' }} />}
                    {b.bankName}
                  </span></td>
                <td className="muted">{b.branch || '—'}</td>
                <td className="mono" style={{ fontWeight:600 }}>{show ? b.accountNo : hrmMaskAccount(b.accountNo)}</td>
                <td className="muted">{b.accountHolder || '—'}</td>
                <td>{b.isPrimary
                  ? <span className="badge badge-success"><i className="dot"></i>{L('Nhận lương', 'Payroll')}</span>
                  : canManage && <button className="btn btn-ghost btn-sm" onClick={() => setPrimary(b)}>{L('Đặt nhận lương', 'Set payroll')}</button>}</td>
                <HrmRowActions onEdit={() => setForm({ editing:b })} onDelete={() => del('bankAccounts', b.id, L('tài khoản ', 'account ') + b.bankName + ' ' + hrmMaskAccount(b.accountNo))} />
              </tr>); })}
          </tbody>
        </table>
      </div>
      {form && <HrmBankFormModal emp={e} editing={form.editing} hasPrimary={!!primary} onClose={() => setForm(null)} />}
    </HrmProfSection>
  );
}
function HrmBankFormModal({ emp, editing, hasPrimary, onClose }){
  const { p, data } = useHrm();
  const { lang } = useLang();
  const toast = useToast();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  // TK đầu tiên của NV → mặc định là TK nhận lương (đỡ phải bấm thêm 1 bước).
  const [f, setF] = React.useState(editing || { bankName:'', branch:'', accountNo:'', accountHolder:emp.fullName, isPrimary:!hasPrimary, note:'' });
  const set = (k, v) => setF(s => ({ ...s, [k]:v }));
  // Danh sách ngân hàng: dùng bản nhúng NGAY (form không phải chờ mạng), rồi làm mới ngầm từ
  // VietQR — có bản mới thì thay, lỗi/offline thì thôi. Luôn giữ tên NH đang lưu của bản ghi cũ
  // (ngân hàng đổi tên/sáp nhập, hoặc NH nước ngoài không có trong danh sách) → sửa hồ sơ không
  // âm thầm đổi mất ngân hàng.
  const [banks, setBanks] = React.useState(vnBanks);
  React.useEffect(() => { let ok = true; vnBanksRefresh().then(l => { if (ok && l) setBanks(l); }); return () => { ok = false; }; }, []);
  const cur = (f.bankName || '').trim();
  const opts = cur && !banks.some(b => b.s === cur) ? [{ c:'', s:cur, n:'' }, ...banks] : banks;
  const picked = banks.find(b => b.s === cur) || null;
  const save = () => {
    if (!f.bankName.trim()) { toast(L('Nhập tên ngân hàng', 'Bank name required'), 'error'); return; }
    if (!String(f.accountNo).trim()) { toast(L('Nhập số tài khoản', 'Account no. required'), 'error'); return; }
    const row = { id:editing ? editing.id : hrmUid('tk'), employeeId:emp.id, bankName:f.bankName.trim(), branch:f.branch,
      accountNo:String(f.accountNo).replace(/\s+/g, ''), accountHolder:f.accountHolder, isPrimary:!!f.isPrimary, note:f.note };
    // Bật cờ nhận lương ở đây thì phải gỡ cờ TK cũ (unique 1-primary/NV) — ghi tuần tự.
    const others = (data.bankAccounts || []).filter(b => b.employeeId === emp.id && b.id !== row.id && b.isPrimary);
    if (row.isPrimary && others.length) p.hrmPatchMany('bankAccounts', [...others.map(b => ({ ...b, isPrimary:false })), row]);
    else if (editing) p.hrmPatch('bankAccounts', editing.id, row);
    else p.hrmAdd('bankAccounts', row);
    toast(L('Đã lưu', 'Saved'), 'success'); onClose();
  };
  return (
    <Modal title={(editing ? L('Sửa tài khoản', 'Edit account') : L('Thêm tài khoản ngân hàng', 'New bank account')) + ' · ' + emp.fullName} width={520} onClose={onClose} staticBackdrop
      footer={<><button className="btn" onClick={onClose}>{L('Hủy', 'Cancel')}</button><button className="btn btn-primary" onClick={save}>{editing ? L('Lưu', 'Save') : L('Thêm', 'Add')}</button></>}>
      <div className="col g16">
        <div className="flex g12">
          <div style={{ flex:1.3 }}><Field label={L('Ngân hàng', 'Bank')} hint={picked ? picked.n : undefined}>
            <div className="flex items-center g8" style={{ height:'var(--control-h)' }}>
              {/* logo từ cdn.vietqr.io — ảnh ngoài, lỗi/offline lần đầu thì ẩn, chữ vẫn đủ dùng */}
              {picked && picked.c && <img src={VN_BANK_LOGO(picked.c)} alt="" loading="lazy"
                onError={ev => { ev.target.style.display = 'none'; }}
                style={{ width:34, height:22, objectFit:'contain', flex:'none' }} />}
              <select className="select" value={f.bankName} onChange={ev => set('bankName', ev.target.value)} autoFocus>
                <option value="">{L('— Chọn ngân hàng —', '— Pick a bank —')}</option>
                {opts.map(b => <option key={b.s} value={b.s}>{b.s}{b.n ? ' · ' + b.n : ''}</option>)}
              </select>
            </div>
          </Field></div>
          <div style={{ flex:1 }}><Field label={L('Chi nhánh', 'Branch')}><input className="input" value={f.branch} onChange={ev => set('branch', ev.target.value)} /></Field></div>
        </div>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Số tài khoản', 'Account no.')}><input className="input mono" value={f.accountNo} onChange={ev => set('accountNo', ev.target.value)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Chủ tài khoản', 'Account holder')}><input className="input" value={f.accountHolder} onChange={ev => set('accountHolder', ev.target.value)} /></Field></div>
        </div>
        <div className="flex g12 items-start">
          <div style={{ flex:1 }}><Field label={L('Ghi chú', 'Note')}><input className="input" value={f.note} onChange={ev => set('note', ev.target.value)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Nhận lương', 'Payroll account')}>
            <label className="flex items-center g8" style={{ height:'var(--control-h)', cursor:'pointer' }}>
              <input type="checkbox" checked={!!f.isPrimary} onChange={ev => set('isPrimary', ev.target.checked)} />
              <span>{L('Dùng tài khoản này để trả lương', 'Pay salary to this account')}</span>
            </label>
          </Field></div>
        </div>
      </div>
    </Modal>
  );
}

/* ---------- section: Hồ sơ scan (file ở Storage bucket 'hrm-docs' — xem 0050) ---------- */
function HrmProfDocs({ e }){
  const { p, data, canManage } = useHrm();
  const { lang } = useLang();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const toast = useToast();
  const confirm = useConfirm();
  const [form, setForm] = React.useState(false);
  const rows = (data.documents || []).filter(d => d.employeeId === e.id);
  const missing = hrmMissingDocs(p, data.documents || [], e.id);
  const open = async (d) => {
    const url = await DB.storageSignedUrl(d.path, 120, HRM_DOC_BUCKET);
    if (!url) { toast(L('Không mở được tệp (chưa kết nối máy chủ hoặc tệp không còn).', 'Cannot open file.'), 'error'); return; }
    window.open(url, '_blank', 'noopener');
  };
  const remove = async (d) => {
    if (!(await confirm({ message:L('Xóa hồ sơ “', 'Delete “') + d.name + '”?', confirmLabel:L('Xóa', 'Delete') }))) return;
    p.hrmRemove('documents', d.id);                      // hàng biến mất ngay
    DB.storageRemove(d.path, HRM_DOC_BUCKET).catch(() => {});   // file dọn nền; sót file không chặn thao tác
    toast(L('Đã xóa', 'Deleted'), 'success');
  };
  return (
    <HrmProfSection title={L('Hồ sơ scan', 'Scanned documents')} icon="bi-folder2-open"
      addLabel={L('Tải hồ sơ lên', 'Upload')} onAdd={() => setForm(true)}>
      {missing.length > 0 && (
        <div className="flex items-center g8 wrap" style={{ margin:'0 12px 10px', padding:'8px 12px', borderRadius:6, fontSize:'var(--fs-base)',
          background:'color-mix(in srgb, var(--warning) 10%, transparent)' }}>
          <i className="bi bi-exclamation-triangle-fill" style={{ flex:'none', color:'var(--warning)' }}></i>
          <span>{L('Còn thiếu: ', 'Missing: ')}</span>
          {missing.map(t => <span key={t.id} className="badge badge-warning">{t[lang] || t.vi}</span>)}
        </div>
      )}
      <div style={{ overflow:'auto' }}>
        <table className="tbl">
          <thead><tr><th>{L('Loại hồ sơ', 'Type')}</th><th>{L('Tệp', 'File')}</th><th>{L('Ngày cấp', 'Issued')}</th>
            <th className="num">{L('Dung lượng', 'Size')}</th><th>{L('Ghi chú', 'Note')}</th><th style={{ width:110 }}></th></tr></thead>
          <tbody>
            {rows.length === 0 && <tr><td colSpan="6"><EmptyState icon="bi-folder2-open" text={L('Chưa có hồ sơ nào được tải lên', 'No documents')} /></td></tr>}
            {rows.map(d => (
              <tr key={d.id} style={{ cursor:'default' }}>
                <td><span className="badge badge-neutral">{hrmDocTypeName(p, d.docType, lang)}</span></td>
                <td>
                  <button className="btn btn-ghost btn-sm" onClick={() => open(d)} title={L('Mở / tải về', 'Open')}>
                    <i className={'bi ' + fileIcon(d.name, d.mime)}></i><span className="truncate" style={{ maxWidth:260 }}>{d.name}</span>
                  </button>
                </td>
                <td className="mono" style={{ fontSize:'var(--fs-base)' }}>{d.issuedDate ? fmtDate(d.issuedDate) : '—'}</td>
                <td className="num mono" style={{ fontSize:'var(--fs-base)' }}>{fileSizeLabel(d.size)}</td>
                <td className="muted truncate" style={{ maxWidth:220 }}>{d.note || '—'}</td>
                {canManage
                  ? <td style={{ whiteSpace:'nowrap', textAlign:'right' }}>
                      <button className="iconbtn" title={L('Tải về', 'Download')} onClick={() => open(d)}><i className="bi bi-download" style={{ fontSize:12 }}></i></button>
                      <button className="iconbtn" title={L('Xóa', 'Delete')} onClick={() => remove(d)}><i className="bi bi-trash" style={{ fontSize:12 }}></i></button>
                    </td>
                  : <td style={{ textAlign:'right' }}>
                      <button className="iconbtn" title={L('Tải về', 'Download')} onClick={() => open(d)}><i className="bi bi-download" style={{ fontSize:12 }}></i></button>
                    </td>}
              </tr>))}
          </tbody>
        </table>
      </div>
      {form && <HrmDocUploadModal emp={e} onClose={() => setForm(false)} />}
    </HrmProfSection>
  );
}
function HrmDocUploadModal({ emp, onClose }){
  const { p } = useHrm();
  const { lang } = useLang();
  const toast = useToast();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const types = hrmDocTypes(p);
  const [f, setF] = React.useState({ docType:types[0] ? types[0].id : 'khac', issuedDate:'', note:'' });
  const [files, setFiles] = React.useState([]);
  const [busy, setBusy] = React.useState(false);
  const fileRef = React.useRef();
  const set = (k, v) => setF(s => ({ ...s, [k]:v }));
  const save = async () => {
    if (!files.length) { toast(L('Chọn tệp cần tải lên', 'Pick a file'), 'error'); return; }
    if (!window.SB_READY) { toast(L('Chưa kết nối máy chủ — không tải lên được', 'Not connected — cannot upload'), 'error'); return; }
    setBusy(true); let ok = 0;
    for (const file of files){
      if (file.size > 25 * 1024 * 1024) { toast(L('Tệp quá lớn (>25MB): ', 'Too large (>25MB): ') + file.name, 'error'); continue; }
      const safe = file.name.replace(/[^\w.\-() ]+/g, '_');
      const path = emp.id + '/' + Date.now() + '_' + safe;   // thư mục cấp 1 = mã NV → policy đọc của chính chủ (0050)
      try {
        await DB.storageUpload(path, file, HRM_DOC_BUCKET);   // KHÔNG optimistic được: phải có tệp trên máy chủ rồi mới ghi hàng
        p.hrmAdd('documents', { id:hrmUid('hs'), employeeId:emp.id, docType:f.docType, name:file.name, path,
          size:file.size, mime:file.type || '', issuedDate:f.issuedDate || '', note:f.note });
        ok++;
      } catch (err) {
        toast(L('Tải lên thất bại: ', 'Upload failed: ') + file.name, 'error');
      }
    }
    setBusy(false);
    if (ok) { toast(L('Đã tải lên ', 'Uploaded ') + ok + L(' tệp', ' file(s)'), 'success'); onClose(); }
  };
  return (
    <Modal title={L('Tải hồ sơ lên', 'Upload documents') + ' · ' + emp.fullName} width={520} onClose={onClose} staticBackdrop
      footer={<><button className="btn" onClick={onClose} disabled={busy}>{L('Hủy', 'Cancel')}</button>
        <button className="btn btn-primary" onClick={save} disabled={busy}>{busy ? L('Đang tải lên…', 'Uploading…') : L('Tải lên', 'Upload')}</button></>}>
      <div className="col g16">
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Loại hồ sơ', 'Document type')}>
            <select className="select" value={f.docType} onChange={ev => set('docType', ev.target.value)}>
              {types.map(t => <option key={t.id} value={t.id}>{t[lang] || t.vi}</option>)}
            </select></Field></div>
          <div style={{ flex:1 }}><Field label={L('Ngày cấp / ký (nếu có)', 'Issued date (optional)')}><DateInput value={f.issuedDate} onChange={v => set('issuedDate', v)} /></Field></div>
        </div>
        {/* Input file ẩn + nút chuẩn — giống FileManager (đính kèm HĐ/báo giá), tránh control mặc định của trình duyệt. */}
        <Field label={L('Tệp scan (PDF hoặc ảnh · tối đa 25MB mỗi tệp)', 'Files (PDF or image · max 25MB each)')}>
          <div className="flex items-center g8" style={{ height:'var(--control-h)' }}>
            <button className="btn" disabled={busy} onClick={() => fileRef.current && fileRef.current.click()}><i className="bi bi-paperclip"></i>{L('Chọn tệp', 'Choose files')}</button>
            <span className="subtle" style={{ fontSize:'var(--fs-base)' }}>
              {files.length ? files.length + L(' tệp đã chọn', ' file(s) selected') : L('Chưa chọn tệp nào', 'No file chosen')}</span>
          </div>
          <input ref={fileRef} type="file" multiple accept=".pdf,image/*" style={{ display:'none' }} onChange={ev => setFiles([...(ev.target.files || [])])} />
        </Field>
        {files.length > 0 && (
          <div className="col g6">
            {files.map((x, i) => (
              <div key={i} className="flex items-center g8" style={{ fontSize:'var(--fs-base)' }}>
                <i className={'bi ' + fileIcon(x.name, x.type)} style={{ color:'var(--primary)' }}></i>
                <span className="truncate" style={{ flex:1 }}>{x.name}</span>
                <span className="mono subtle">{fileSizeLabel(x.size)}</span>
              </div>))}
          </div>
        )}
        <Field label={L('Ghi chú (áp cho các tệp vừa chọn)', 'Note (applies to picked files)')}><input className="input" value={f.note} onChange={ev => set('note', ev.target.value)} /></Field>
      </div>
    </Modal>
  );
}

/* ---------- form Thêm/Sửa NHÂN VIÊN (register NĐ145) ---------- */
// cccd = thông tin đọc từ thẻ (khi mở từ nút "Thêm nhanh từ CCCD" ở danh sách) → điền sẵn form.
function HrmEmployeeFormModal({ editing, onClose, onCreated, cccd }){
  const { p, data, canManage } = useHrm();
  const { lang } = useLang();
  const toast = useToast();
  const L = (vi, en) => (lang === 'vi' ? vi : en);
  const sal = editing && editing.salary || {};
  const freshInit = { fullName:(cccd && cccd.fullName) || '', gender:(cccd && cccd.gender) || 'male', birthDate:(cccd && cccd.birthDate) || '',
    nationality:'Việt Nam', address:(cccd && cccd.address) || '', idNumber:(cccd && cccd.idNumber) || '', idIssueDate:(cccd && cccd.issueDate) || '', education:'', skillLevel:'',
    position:'', dept:'', startDate:todayVN(), status:'probation', siCode:'', siJoinDate:'', base:'', payType:'fixed', siBase:'', taxDependents:0,
    allowances:[], leaveAdjust:0, otCapYear:200, email:'', phone:'', note:'', userId:'', endDate:'', endReason:'', endType:'', photo:'' };
  // Nháp CHỈ khi tạo mới không từ CCCD (quét CCCD = bắt đầu từ dữ liệu thẻ, không đè bằng nháp cũ).
  const [f, setF, clearDraft, hadDraft] = useFormDraft('hrm-employee-new', editing
    ? { ...editing, base:sal.base || '', payType:sal.payType || 'fixed', siBase:sal.siBase || '', taxDependents:sal.taxDependents || 0,
        allowances:(sal.allowances || []).map(a => ({ ...a })) }
    : freshInit, !editing && !cccd);
  const discardDraft = () => { clearDraft(); setF(freshInit); };
  const close = onClose;   // đóng form GIỮ nháp (xóa chỉ khi tạo xong / "Làm mới")
  const set = (k, v) => setF(s => ({ ...s, [k]:v }));
  const [scan, setScan] = React.useState(false);
  const photoRef = React.useRef(null);
  const pickPhoto = async (ev) => {
    const file = ev.target.files && ev.target.files[0];
    ev.target.value = '';   // chọn lại cùng file vẫn kích onChange
    if (!file) return;
    try { set('photo', await hrmResizePhoto(file)); }
    catch (e) { toast(L('Không đọc được ảnh này — thử JPG/PNG.', 'Cannot read this image — try JPG/PNG.'), 'error'); }
  };
  // Quét CCCD → chỉ ghi đè các ô QR THỰC SỰ đọc được; ô nào QR không có (giới tính lạ, địa chỉ rỗng)
  // thì GIỮ nguyên giá trị đang gõ dở, không xoá trắng công của HR.
  const applyCccd = (i) => {
    if (!i) return;
    setF(s => ({ ...s, idNumber:i.idNumber || s.idNumber, fullName:i.fullName || s.fullName,
      birthDate:i.birthDate || s.birthDate, gender:i.gender || s.gender, address:i.address || s.address,
      idIssueDate:i.issueDate || s.idIssueDate }));
    toast(L('Đã điền từ thẻ CCCD — kiểm lại trước khi lưu', 'Filled from ID card — please review'), 'success');
  };
  const setAllow = (i, k, v) => setF(s => ({ ...s, allowances:s.allowances.map((a, j) => j === i ? { ...a, [k]:v } : a) }));
  const addAllow = () => setF(s => ({ ...s, allowances:[...(s.allowances || []), { name:'', amount:'', taxFree:false, taxFreeCap:'' }] }));
  const rmAllow = (i) => setF(s => ({ ...s, allowances:s.allowances.filter((_, j) => j !== i) }));
  const save = () => {
    if (!f.fullName.trim()) { toast(L('Nhập họ tên', 'Name required'), 'error'); return; }
    if (!f.startDate) { toast(L('Nhập ngày bắt đầu làm việc', 'Start date required'), 'error'); return; }
    const row = { id:editing ? editing.id : genEmpCode(data.employees), userId:f.userId || null,
      fullName:f.fullName, gender:f.gender, birthDate:f.birthDate || null, nationality:f.nationality, address:f.address,
      idNumber:f.idNumber, idIssueDate:f.idIssueDate || null, education:f.education, skillLevel:f.skillLevel, position:f.position, dept:f.dept,
      startDate:f.startDate, endDate:f.status === 'terminated' ? (f.endDate || null) : null,
      endReason:f.status === 'terminated' ? f.endReason : '',
      endType:f.status === 'terminated' ? (f.endType || null) : null,
      siCode:f.siCode, siJoinDate:f.siJoinDate || null,
      salary:{ ...(editing && editing.salary || {}), base:+f.base || 0, payType:f.payType || 'fixed', siBase:+f.siBase || (+f.base || 0), taxDependents:+f.taxDependents || 0,
        allowances:(f.allowances || []).filter(a => (a.name || '').trim()).map(a => ({ name:a.name.trim(), amount:+a.amount || 0,
          ...(a.taxFree ? { taxFree:true, ...(+a.taxFreeCap ? { taxFreeCap:+a.taxFreeCap } : {}) } : {}) })) },
      events:(editing && editing.events) || [], leaveAdjust:+f.leaveAdjust || 0, otCapYear:+f.otCapYear || 200,
      status:f.status, email:f.email, phone:f.phone, note:f.note, photo:f.photo || '' };
    if (editing) p.hrmPatch('employees', editing.id, row); else { p.hrmAdd('employees', row); clearDraft(); }
    toast(editing ? L('Đã lưu hồ sơ', 'Saved') : L('Đã tạo hồ sơ ' + row.id, 'Employee ' + row.id + ' created'), 'success');
    onClose(); if (!editing && onCreated) onCreated(row.id);
  };
  const Sect = ({ title }) => <div style={{ fontWeight:700, fontSize:'var(--fs-sm)', color:'var(--text-muted)', textTransform:'uppercase', letterSpacing:.4, borderBottom:'1px solid var(--border-light)', paddingBottom:4 }}>{title}</div>;
  // width 880 (không phải 640): form nhiều hàng 3-4 cột + các chuỗi dài (số CCCD, địa chỉ cư trú)
  // cần chỗ thở để đọc/sửa được — 640 làm ô bóp nhỏ, chữ tràn. Mobile vẫn full-width + stack 1 cột.
  return (
    <Modal title={editing ? L('Sửa hồ sơ ', 'Edit ') + editing.id : L('Thêm nhân viên', 'New employee')} width={880} onClose={close} staticBackdrop
      footer={<><button className="btn" onClick={close}>{L('Hủy', 'Cancel')}</button><button className="btn btn-primary" onClick={save}>{editing ? L('Lưu', 'Save') : L('Tạo hồ sơ', 'Create')}</button></>}>
      <div className="col g14">
        <DraftNotice show={hadDraft} onDiscard={discardDraft} />
        <Sect title={L('Nhân thân', 'Personal')} />
        <div className="flex items-center g12">
          <Avatar name={f.fullName} size={56} src={f.photo} />
          <div className="col g6">
            <div className="flex g8">
              <button className="btn btn-sm" onClick={() => photoRef.current && photoRef.current.click()}><i className="bi bi-camera"></i>{f.photo ? L('Đổi ảnh', 'Change photo') : L('Chọn ảnh', 'Choose photo')}</button>
              {f.photo && <button className="btn btn-sm" onClick={() => set('photo', '')}><i className="bi bi-x-lg"></i>{L('Xóa ảnh', 'Remove')}</button>}
            </div>
            <span className="subtle" style={{ fontSize:'var(--fs-sm)' }}>{L('Ảnh chân dung — tự thu nhỏ 256×256 trước khi lưu.', 'Portrait — resized to 256×256 before saving.')}</span>
          </div>
          <input ref={photoRef} type="file" accept="image/*" style={{ display:'none' }} onChange={pickPhoto} />
        </div>
        {/* QUÉT CCCD = luồng nhập CHÍNH (tự điền họ tên/ngày sinh/giới tính/số thẻ/ngày cấp/địa chỉ)
            → nút nổi bật NGAY ĐẦU section thay vì chôn cạnh ô số thẻ; 1 điểm vào duy nhất. */}
        <button type="button" onClick={() => setScan(true)}
          className="flex items-center g10"
          style={{ width:'100%', padding:'10px 14px', border:'1.5px dashed var(--primary)', borderRadius:'var(--r-lg)',
            background:'var(--primary-light)', color:'var(--primary-ink)', cursor:'pointer', textAlign:'left' }}>
          <i className="bi bi-qr-code-scan" style={{ fontSize:20, flex:'none' }}></i>
          <span style={{ fontWeight:600 }}>{L('Quét mã QR thẻ CCCD', 'Scan ID card QR')}</span>
          <span className="grow" style={{ color:'var(--text-muted)', fontSize:'var(--fs-sm)' }}>
            {L('— tự điền họ tên · ngày sinh · số thẻ · ngày cấp · nơi cư trú', '— autofills the section below')}</span>
          <i className="bi bi-chevron-right" style={{ flex:'none' }}></i>
        </button>
        {/* Lưới cột 2/1/1 THỐNG NHẤT cho cả 2 hàng → các mép cột thẳng nhau theo chiều dọc (OCD).
            Ô rộng (flex:2) dành cho chuỗi dài: Họ tên · Số CCCD. */}
        <div className="flex g12">
          <div style={{ flex:2 }}><Field label={L('Họ tên', 'Full name')}><input className="input" value={f.fullName} onChange={ev => set('fullName', ev.target.value)} autoFocus /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Giới tính', 'Gender')}>
            <select className="select" value={f.gender} onChange={ev => set('gender', ev.target.value)}>
              <option value="male">{L('Nam', 'Male')}</option><option value="female">{L('Nữ', 'Female')}</option><option value="other">{L('Khác', 'Other')}</option>
            </select></Field></div>
          <div style={{ flex:1 }}><Field label={L('Ngày sinh', 'Birth date')}><DateInput value={f.birthDate || ''} onChange={v => set('birthDate', v)} /></Field></div>
        </div>
        <div className="flex g12">
          <div style={{ flex:2 }}><Field label={L('Số CCCD/Hộ chiếu', 'ID number')}><input className="input mono" value={f.idNumber} onChange={ev => set('idNumber', ev.target.value)} placeholder="068083001780" /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Ngày cấp CCCD', 'ID issued date')}><DateInput value={f.idIssueDate || ''} onChange={v => set('idIssueDate', v)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Quốc tịch', 'Nationality')}><input className="input" value={f.nationality} onChange={ev => set('nationality', ev.target.value)} /></Field></div>
        </div>
        {/* Địa chỉ cư trú = chuỗi DÀI (thôn/xã/huyện/tỉnh) → một hàng riêng full-width, không chen cột */}
        <Field label={L('Nơi cư trú', 'Address')}><input className="input" value={f.address} onChange={ev => set('address', ev.target.value)} /></Field>
        {/* 1/1 → mép giữa 50% thẳng với mép "Họ tên|Giới tính" và "Số CCCD|Ngày cấp" ở trên */}
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Điện thoại', 'Phone')}><input className="input mono" value={f.phone} onChange={ev => set('phone', ev.target.value)} /></Field></div>
          <div style={{ flex:1 }}><Field label="Email"><input className="input mono" value={f.email} onChange={ev => set('email', ev.target.value)} placeholder={'ten@' + (window.SWO_CONFIG?.LOGIN_HD || 'swo.vn')} /></Field></div>
        </div>
        <Sect title={L('Công việc', 'Employment')} />
        <div className="flex g12">
          <HrmOrgFields position={f.position} dept={f.dept} onPosition={v => set('position', v)} onDept={v => set('dept', v)}
            posLabel={L('Vị trí việc làm', 'Position')} />
          <div style={{ flex:1 }}><Field label={L('Ngày bắt đầu', 'Start date')}><DateInput value={f.startDate} onChange={v => set('startDate', v)} /></Field></div>
        </div>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Trình độ chuyên môn', 'Education')}><input className="input" value={f.education} onChange={ev => set('education', ev.target.value)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Bậc kỹ năng nghề', 'Skill level')}><input className="input" value={f.skillLevel} onChange={ev => set('skillLevel', ev.target.value)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Trạng thái', 'Status')}>
            <select className="select" value={f.status} onChange={ev => set('status', ev.target.value)}>
              {Object.entries(HRM_STATUS).map(([id, v]) => <option key={id} value={id}>{v[lang]}</option>)}
            </select></Field></div>
        </div>
        {f.status === 'terminated' && (
          <div className="flex g12">
            <div style={{ flex:1 }}><Field label={L('Ngày chấm dứt HĐLĐ', 'End date')}><DateInput value={f.endDate || ''} onChange={v => set('endDate', v)} /></Field></div>
            {/* Phân loại = ô THỐNG KÊ (báo cáo biến động tách tự nguyện/cho thôi việc); ô Lý do bên cạnh vẫn ghi chi tiết. */}
            <div style={{ flex:1 }}><Field label={L('Phân loại', 'Type')} hint={L('Dùng cho báo cáo biến động', 'Used by turnover report')}>
              <select className="select" value={f.endType || ''} onChange={ev => set('endType', ev.target.value)}>
                <option value="">{L('— Chưa phân loại —', '— Not set —')}</option>
                {Object.entries(HRM_END_TYPES).map(([id, v]) => <option key={id} value={id}>{v[lang]}</option>)}
              </select></Field></div>
            <div style={{ flex:1.6 }}><Field label={L('Lý do chấm dứt', 'End reason')}><input className="input" value={f.endReason} onChange={ev => set('endReason', ev.target.value)} /></Field></div>
          </div>
        )}
        <Sect title={L('BHXH · Lương · Phép', 'Insurance · Salary · Leave')} />
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Mã số BHXH', 'SI code')}><input className="input mono" value={f.siCode} onChange={ev => set('siCode', ev.target.value)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Ngày tham gia BHXH', 'SI join date')}><DateInput value={f.siJoinDate || ''} onChange={v => set('siJoinDate', v)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Lương cơ bản (₫)', 'Base salary')}><MoneyInput value={f.base} onChange={v => set('base', v)} /></Field></div>
        </div>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Kiểu tính lương', 'Pay type')} hint={L('Cố định: đủ công đủ lương; Theo công: × ngày công', 'Fixed vs by-workday')}>
            <select className="select" value={f.payType} onChange={ev => set('payType', ev.target.value)}>
              {Object.entries(HRM_PAY_TYPES).map(([id, v]) => <option key={id} value={id}>{v[lang]}</option>)}
            </select></Field></div>
          <div style={{ flex:1 }}><Field label={L('Lương đóng BHXH (₫)', 'SI base salary')} hint={L('Trống = theo lương cơ bản', 'Blank = base salary')}><MoneyInput value={f.siBase} onChange={v => set('siBase', v)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Số người phụ thuộc (thuế)', 'Tax dependents')} hint={L('Giảm trừ TNCN 4.4tr/người', '4.4M relief each')}><input type="number" min="0" className="input mono" value={f.taxDependents} onChange={ev => set('taxDependents', ev.target.value)} /></Field></div>
        </div>
        <Field label={L('Phụ cấp hằng tháng', 'Monthly allowances')}
          hint={L('Tick "Miễn thuế" cho khoản không chịu thuế TNCN; điền trần miễn nếu khoản đó có giới hạn (vd ăn trưa 730.000₫ — phần vượt vẫn chịu thuế).', 'Tick tax-free; set a cap when the exemption is limited (e.g. lunch 730k).')}>
          <div className="col g8">
            {(f.allowances || []).length > 0 && (
              <div className="flex g8" style={{ fontSize:'var(--fs-sm)', color:'var(--text-muted)' }}>
                <span style={{ flex:'1.4 1 0' }}>{L('Tên khoản', 'Name')}</span>
                <span style={{ flex:'1 1 0', textAlign:'right' }}>{L('Số tiền', 'Amount')}</span>
                <span style={{ flex:'0 0 76px', textAlign:'center' }}>{L('Miễn thuế', 'Tax-free')}</span>
                <span style={{ flex:'1 1 0', textAlign:'right' }}>{L('Trần miễn', 'Exempt cap')}</span>
                <span style={{ flex:'0 0 26px' }}></span>
              </div>
            )}
            {(f.allowances || []).map((a, i) => (
              <div key={i} className="flex g8 items-center">
                <input className="input" style={{ flex:'1.4 1 0' }} value={a.name} onChange={ev => setAllow(i, 'name', ev.target.value)} placeholder={L('VD: Ăn trưa', 'e.g. Lunch')} />
                <div style={{ flex:'1 1 0' }}><MoneyInput value={a.amount} onChange={v => setAllow(i, 'amount', v)} /></div>
                <label className="flex items-center justify-center" style={{ flex:'0 0 76px', height:'var(--control-h)', cursor:'pointer' }}>
                  <input type="checkbox" checked={!!a.taxFree} onChange={ev => setAllow(i, 'taxFree', ev.target.checked)} style={{ width:15, height:15 }} />
                </label>
                <div style={{ flex:'1 1 0' }}><MoneyInput value={a.taxFreeCap || ''} onChange={v => setAllow(i, 'taxFreeCap', v)} disabled={!a.taxFree}
                  placeholder={a.taxFree ? L('Miễn hết', 'All') : '—'} style={a.taxFree ? undefined : { opacity:.45, background:'var(--bg-subtle)', cursor:'not-allowed' }} /></div>
                <button className="iconbtn" style={{ flex:'0 0 26px' }} title={L('Xóa', 'Remove')} onClick={() => rmAllow(i)}><i className="bi bi-x-lg" style={{ fontSize:12 }}></i></button>
              </div>
            ))}
            <button className="btn btn-sm" style={{ alignSelf:'flex-start' }} onClick={addAllow}><i className="bi bi-plus-lg"></i>{L('Thêm phụ cấp', 'Add allowance')}</button>
          </div>
        </Field>
        <div className="flex g12">
          <div style={{ flex:1 }}><Field label={L('Điều chỉnh phép (ngày)', 'Leave adjust (days)')} hint={L('Carry-over/thưởng phép (± ngày)', 'Carry-over/bonus (± days)')}><input type="number" step="0.5" className="input mono" value={f.leaveAdjust} onChange={ev => set('leaveAdjust', ev.target.value)} /></Field></div>
          <div style={{ flex:1 }}><Field label={L('Trần OT năm (h)', 'OT cap/year (h)')} hint={L('200 · 300 ngành đặc thù (Đ107.3)', '200 · 300 special sectors')}><input type="number" className="input mono" value={f.otCapYear} onChange={ev => set('otCapYear', ev.target.value)} /></Field></div>
          <div style={{ flex:1.4 }}><Field label={L('Liên kết tài khoản portal', 'Portal account')}>
            <select className="select" value={f.userId || ''} onChange={ev => set('userId', ev.target.value)}>
              <option value="">{L('— Không —', '— None —')}</option>
              {(p.users || []).map(u => <option key={u.id} value={u.id}>{u.name} · {u.email}</option>)}
            </select></Field></div>
        </div>
        <Field label={L('Ghi chú', 'Note')}><input className="input" value={f.note} onChange={ev => set('note', ev.target.value)} /></Field>
      </div>
      {scan && <CccdScanModal onClose={() => setScan(false)} onDone={applyCccd} />}
    </Modal>
  );
}

Object.assign(window, { HrmEmployeeProfile, HrmEmployeeFormModal });
