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:
@@ -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 |
Reference in New Issue
Block a user