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:
@@ -2210,6 +2210,18 @@ router.post(
|
||||
isHighlighted: 0,
|
||||
notes: stripTagsToPlainTextOrNull(source.notes),
|
||||
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,
|
||||
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));
|
||||
});
|
||||
|
||||
// #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.
|
||||
test("Sanitization: POST strips HTML from location, meetingUrl, and notes", async () => {
|
||||
const create = await api(adminCookie, "POST", "/api/executive-meetings", {
|
||||
|
||||
@@ -17,7 +17,12 @@ const Switch = React.forwardRef<
|
||||
>
|
||||
<SwitchPrimitives.Thumb
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user