Task #207: custom subheadings inside executive-meeting attendee cells

Adds a new `kind` column (`varchar(16) NOT NULL DEFAULT 'person'`) on
`executive_meeting_attendees` so users can interleave free-text section
labels with person rows in a meeting's attendee list. Subheadings are
excluded from the running attendee number and from the per-meeting
attendee count surface, but reorder and delete identically to person
rows.

DB
- New `kind` column in `lib/db/src/schema/executive-meetings.ts`,
  defaulting to `"person"`. Applied via direct SQL because
  `drizzle-kit push` trips on a pre-existing duplicate-row issue in
  `app_permissions` (already documented in replit.md).

API (artifacts/api-server/src/routes/executive-meetings.ts)
- `attendeeSchema` accepts `kind: z.enum(["person","subheading"])` with
  default `"person"`.
- All 4 insert paths round-trip `kind`: POST create, PATCH meeting
  update (attendees replacement), PUT `/attendees`, and duplicate.
- `pdf-renderer` mapper forwards `kind`. `PdfMeetingAttendee.kind`
  typed as `string | null` to match the DB column shape.

Frontend (artifacts/tx-os/src/pages/executive-meetings.tsx)
- `AttendeeFlow` renders subheadings on a separate full-width row
  (`basis-full`, semibold, centered) and increments the running
  person index only for `kind === "person"`. Pending ghost row branches
  on `pendingKind`.
- Inline "+ subheading" chip via `onStartAdd(type, "subheading")`.
- Manage dialog: addSubheading button, subheading rows hide the title
  field, show a kind badge, and reorder/delete identically. Manage
  list summary count filters to person rows only (architect fix).
- Print page renders subheadings as `.em-print-subheading` and skips
  them in the running counter.
- New locale keys under `executiveMeetings.schedule` and
  `executiveMeetings.manage.attendees` in both `ar.json` and `en.json`.

PDF
- Subheadings print as `— label —` and never advance `personIdx`.

Tests
- New spec `executive-meetings-attendee-subheadings.spec.mjs` seeds
  mixed person+subheading rows and asserts (a) the subheading row
  renders, (b) numbering stays `1-`, `2-`, `3-` with a subheading
  wedged between persons, (c) zero-subheading meetings keep legacy
  numbering. Runs in both AR and EN. All 4 cases pass.

Code review
- Architect found one regression (Manage list summary count included
  subheadings) — fixed.
This commit is contained in:
riyadhafraa
2026-04-30 11:45:59 +00:00
parent 986bb8e7b2
commit 89df2385a8
9 changed files with 808 additions and 168 deletions
+6
View File
@@ -883,11 +883,14 @@
"timeEnd": "وقت النهاية",
"timeOrderError": "وقت النهاية قبل وقت البداية",
"addAttendee": "أضف حاضر",
"addSubheading": "+ عنوان فرعي",
"addVirtualAttendee": "+ حاضر عبر الاتصال المرئي",
"addInternalAttendee": "+ حاضر داخلي",
"addExternalAttendee": "+ حاضر خارجي",
"newAttendeeAria": "اسم الحاضر الجديد",
"editAttendeeAria": "تعديل اسم الحاضر",
"newSubheadingAria": "نص العنوان الفرعي الجديد",
"editSubheadingAria": "تعديل نص العنوان الفرعي",
"deleteRow": "حذف الاجتماع",
"deleteRowConfirm": "حذف هذا الاجتماع؟\n\n\"{{title}}\"\n\nلا يمكن التراجع عن هذا الإجراء.",
"deleted": "تم حذف الاجتماع",
@@ -983,6 +986,9 @@
"id": "رقم الحاضر",
"type": "نوع الحضور",
"add": "إضافة حاضر",
"addSubheading": "إضافة عنوان فرعي",
"subheadingName": "نص العنوان الفرعي",
"subheadingBadge": "عنوان فرعي",
"remove": "إزالة",
"moveUp": "تحريك للأعلى",
"moveDown": "تحريك للأسفل",