Restart numbering for individuals after each subheading

Modify attendee numbering logic to reset at each subheading, ensuring proper sequence and display across different views and PDF outputs.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 17c29c3f-e55d-4765-875c-0387b6936812
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/piUNOmy
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
riyadhafraa
2026-04-30 12:47:50 +00:00
parent 0e4fcc81c7
commit 76f0dfacb4
6 changed files with 70 additions and 27 deletions
+7 -3
View File
@@ -531,15 +531,19 @@ export async function renderSchedulePdf(input: RenderPdfInput): Promise<Buffer>
meeting.attendees.length === 0
? "—"
: (() => {
// Walking person counter — subheadings are non-numbered
// labels, so they don't advance the index. Same logic as
// the on-screen AttendeeFlow and the print page.
// Walking person counter — each subheading starts a
// fresh numbered section, so persons restart at 1- after
// every subheading. Same logic as the on-screen
// AttendeeFlow and the client print page.
let personIdx = 0;
return meeting.attendees
.map((a) => {
const isSub = (a.kind ?? "person") === "subheading";
const name = htmlToPlain(a.name);
if (isSub) {
// Reset the in-section counter so the next person
// starts at 1.
personIdx = 0;
// Brackets keep subheadings visually distinct in the
// monospace plain-text PDF cell where bold/colour
// can't carry. Adapter prefix is non-numeric so a
@@ -275,14 +275,16 @@ export default function ExecutiveMeetingsPrintPage() {
data-testid={`em-print-attendees-${m.id}`}
>
{(() => {
// Walking person counter — subheadings are
// labels, so they don't advance the print
// index. Mirrors AttendeeFlow on screen.
// Each subheading starts a fresh numbered
// section. Mirrors AttendeeFlow on screen.
let personIdx = 0;
return attendees.map((a, idx) => {
const isSub =
(a.kind ?? "person") === "subheading";
if (isSub) {
// Reset the in-section counter so the
// next person starts at 1.
personIdx = 0;
return (
<div
key={a.id ?? `sub-${idx}`}
@@ -3362,23 +3362,56 @@ function AttendeeFlow({
const editable = canMutate && !!onSaveAttendeeName;
const showAdd = canMutate && !!onStartAdd && !isPending;
// Person count drives the running "1-, 2-…" index. Subheadings are
// labels, not attendees, so they must NOT advance the counter.
const personCount = items.reduce(
(n, { a }) => n + ((a.kind ?? "person") === "person" ? 1 : 0),
0,
// Each subheading starts a fresh numbered section. Within a section,
// persons are numbered 1..N starting over after every subheading.
// Numbering rules:
// - If the cell has NO subheading: keep the legacy "don't number a
// single name" rule (only show numbers when 2+ persons exist).
// - If the cell HAS any subheading: always show numbers, even for
// solo persons inside a section, so the grouping reads clearly.
// Pre-compute per-list-position metadata so the render loop is O(n).
const hasAnySubheading = items.some(
({ a }) => (a.kind ?? "person") === "subheading",
);
const sectionMeta = items.map(() => ({
sectionPersonCount: 0,
idxInSection: 0,
}));
{
let secStart = 0;
let secCount = 0;
const finalize = (endExclusive: number) => {
for (let k = secStart; k < endExclusive; k++) {
sectionMeta[k].sectionPersonCount = secCount;
}
};
items.forEach(({ a }, idx) => {
if ((a.kind ?? "person") === "subheading") {
finalize(idx);
secStart = idx + 1;
secCount = 0;
} else {
sectionMeta[idx].idxInSection = secCount;
secCount += 1;
}
});
finalize(items.length);
}
// Person count of the trailing (last) section — drives whether the
// pending "+ name" ghost row shows a leading number.
let lastSectionPersonCount = 0;
for (let k = items.length - 1; k >= 0; k--) {
if ((items[k].a.kind ?? "person") === "subheading") break;
lastSectionPersonCount += 1;
}
if (items.length === 0 && !showAdd && !isPending) {
return <span className="text-xs text-gray-500"></span>;
}
// Walking counter — incremented only as we render person rows.
let personIdx = 0;
return (
<ul className="flex flex-wrap justify-center items-baseline gap-x-4 gap-y-0.5 text-[#0B1E3F] leading-snug">
{items.map(({ a, i }) => {
{items.map(({ a, i }, listIdx) => {
const isSub = (a.kind ?? "person") === "subheading";
if (isSub) {
// Subheading row: full-width line break inside the flex-wrap so
@@ -3411,10 +3444,9 @@ function AttendeeFlow({
</li>
);
}
// Person row — capture index BEFORE incrementing so the displayed
// number matches what the user expects (1-based).
const myDisplayIdx = personIdx;
personIdx += 1;
// Person row — section-local 0-based index pre-computed above.
const meta = sectionMeta[listIdx];
const myDisplayIdx = meta.idxInSection;
return (
<li
key={a.id ?? i}
@@ -3428,7 +3460,7 @@ function AttendeeFlow({
(editable ? " min-w-[3rem]" : "")
}
>
{personCount > 1 && (
{(meta.sectionPersonCount > 1 || hasAnySubheading) && (
<span
data-testid={`em-attendee-index-${i}`}
className="text-gray-500 me-1"
@@ -3462,8 +3494,10 @@ function AttendeeFlow({
})}
{canMutate && isPending && pendingKind === "person" && onCommitAdd && (
<li className="min-w-[6rem]" data-testid={`em-add-attendee-pending-${addType}`}>
{personCount > 0 && (
<span className="text-gray-500 me-1">{personCount + 1}-</span>
{(lastSectionPersonCount > 0 || hasAnySubheading) && (
<span className="text-gray-500 me-1">
{lastSectionPersonCount + 1}-
</span>
)}
<EditableCell
// pendingKey changes every time the user clicks "+" so React
@@ -160,8 +160,9 @@ for (const lang of ["en", "ar"]) {
endTime: "10:00:00",
});
// Internal group with a subheading wedged between the second and
// third person. After rendering we should see persons numbered
// 1-, 2-, 3- (subheading skipped) PLUS the subheading row itself.
// third person. The subheading starts a fresh numbered section, so
// persons render as 1-, 2- (before the subheading) and 1- (after),
// PLUS the subheading row itself.
await insertAttendee({
meetingId,
name: "Person Alpha",
@@ -217,13 +218,15 @@ for (const lang of ["en", "ar"]) {
const personRows = row.locator('[data-testid^="em-attendee-row-"]');
await expect(personRows).toHaveCount(3);
// Three person indices in order — the subheading must not have
// bumped the counter to 4.
// Numbering restarts after the subheading: section 1 has Alpha
// and Beta numbered "1-" and "2-"; section 2 has Gamma alone,
// numbered "1-" (the cell has a subheading so we always show the
// index, even for a solo person inside a section).
const indices = row.locator('[data-testid^="em-attendee-index-"]');
await expect(indices).toHaveCount(3);
const indexTexts = await indices.allInnerTexts();
const trimmed = indexTexts.map((s) => s.trim().replace(/\s+/g, ""));
expect(trimmed).toEqual(["1-", "2-", "3-"]);
expect(trimmed).toEqual(["1-", "2-", "1-"]);
} finally {
if (originalAdminPreferredLanguage) {
await setAdminPreferredLanguage(originalAdminPreferredLanguage).catch(
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB