Preserve meeting metadata when duplicating and fix switch alignment in RTL

Update executive meeting duplication to maintain `isExternal` and `rowColor` properties, and adjust switch component's RTL translation for correct alignment.
This commit is contained in:
Riyadh
2026-07-08 10:06:53 +00:00
parent 5a3d7464d9
commit 459999a9e2
3 changed files with 48 additions and 1 deletions
@@ -2210,6 +2210,18 @@ router.post(
isHighlighted: 0, isHighlighted: 0,
notes: stripTagsToPlainTextOrNull(source.notes), notes: stripTagsToPlainTextOrNull(source.notes),
attachments: source.attachments, attachments: source.attachments,
// #678: Carry the external-meeting flag onto the copy. Omitting it
// silently reset duplicates/repeats to "internal" (default false),
// losing the red tint + no-auto-postpone behavior of the source.
isExternal: source.isExternal,
// Same class of bug: rowColor is a shared editorial signal about
// the meeting (urgent / VIP), and the merge overlay is user
// content — both should survive a duplicate. mergeText is
// re-sanitized like the other rich-text fields above.
rowColor: source.rowColor,
mergeStartColumn: source.mergeStartColumn,
mergeEndColumn: source.mergeEndColumn,
mergeText: source.mergeText ? sanitizeRichText(source.mergeText) : source.mergeText,
createdBy: userId, createdBy: userId,
updatedBy: userId, updatedBy: userId,
}; };
@@ -409,6 +409,36 @@ test("Meetings: POST /duplicate clones a meeting onto another date", async () =>
assert.ok(dayBody.meetings.some((m) => m.id === newRow.id)); assert.ok(dayBody.meetings.some((m) => m.id === newRow.id));
}); });
// #678: duplicating/repeating must preserve the external-meeting flag
// (and the shared row colour) instead of silently resetting the copy
// to a normal internal meeting.
test("Meetings: POST /duplicate preserves isExternal and rowColor on the copy", async () => {
const create = await api(adminCookie, "POST", "/api/executive-meetings", {
titleAr: "خارجي",
titleEn: "External",
meetingDate: today,
isExternal: true,
attendees: [],
});
assert.equal(create.status, 201);
const original = await create.json();
created.meetingIds.push(original.id);
assert.equal(original.isExternal, true, "source must be external");
assert.equal(original.rowColor, "red", "external source is force-tinted red");
const dup = await api(
adminCookie,
"POST",
`/api/executive-meetings/${original.id}/duplicate`,
{ targetDate: tomorrow },
);
assert.equal(dup.status, 201);
const copy = await dup.json();
created.meetingIds.push(copy.id);
assert.equal(copy.isExternal, true, "duplicate must stay external (#678)");
assert.equal(copy.rowColor, "red", "duplicate keeps the red row colour");
});
// #189: plain-text sanitization for location / meetingUrl / notes. // #189: plain-text sanitization for location / meetingUrl / notes.
test("Sanitization: POST strips HTML from location, meetingUrl, and notes", async () => { test("Sanitization: POST strips HTML from location, meetingUrl, and notes", async () => {
const create = await api(adminCookie, "POST", "/api/executive-meetings", { const create = await api(adminCookie, "POST", "/api/executive-meetings", {
+6 -1
View File
@@ -17,7 +17,12 @@ const Switch = React.forwardRef<
> >
<SwitchPrimitives.Thumb <SwitchPrimitives.Thumb
className={cn( className={cn(
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:ltr:translate-x-4 data-[state=checked]:rtl:-translate-x-4 data-[state=unchecked]:ltr:translate-x-0 data-[state=unchecked]:rtl:translate-x-4" // #682: in RTL the flex container already places the resting
// (unchecked) thumb at the track's start edge, so unchecked must
// be translate-x-0 in BOTH directions. The old rtl:translate-x-4
// pushed the resting thumb sideways, making OFF look like ON in
// Arabic. Checked travel: +x in LTR, -x in RTL.
"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=unchecked]:translate-x-0 data-[state=checked]:ltr:translate-x-4 data-[state=checked]:rtl:-translate-x-4"
)} )}
/> />
</SwitchPrimitives.Root> </SwitchPrimitives.Root>