diff --git a/artifacts/api-server/src/routes/executive-meetings.ts b/artifacts/api-server/src/routes/executive-meetings.ts index ee4a81b9..0189005e 100644 --- a/artifacts/api-server/src/routes/executive-meetings.ts +++ b/artifacts/api-server/src/routes/executive-meetings.ts @@ -2889,15 +2889,14 @@ const upsertFontSettingsHandler = async ( } } const targetUserId = data.scope === "global" ? null : userId; - // Reject user-scope logo writes loudly; null/undefined are fine. - if (data.scope === "user" && data.logoObjectPath != null) { - res.status(400).json({ - error: "logoObjectPath is only allowed on the global font-settings row", - code: "logo_user_scope_forbidden", - }); - return; - } - const logoForWrite = data.scope === "global" ? data.logoObjectPath : undefined; + const userRoleNames = await getRoleNamesForUser(userId); + const isAdmin = EM_ADMIN_ROLES.some((r) => userRoleNames.has(r)); + const logoSentByAdmin = + isAdmin && data.logoObjectPath !== undefined; + const logoForFontRow = + data.scope === "global" && logoSentByAdmin + ? data.logoObjectPath + : undefined; const result = await db.transaction(async (tx) => { const [existing] = await tx .select() @@ -2919,9 +2918,8 @@ const upsertFontSettingsHandler = async ( alignment: data.alignment, fontColor: data.fontColor, }; - // Distinguish null (clear) from undefined (skip). - if (logoForWrite !== undefined) { - updateSet.logoObjectPath = logoForWrite; + if (logoForFontRow !== undefined) { + updateSet.logoObjectPath = logoForFontRow; } const [updated] = await tx .update(executiveMeetingFontSettingsTable) @@ -2938,7 +2936,7 @@ const upsertFontSettingsHandler = async ( fontWeight: data.fontWeight, alignment: data.alignment, fontColor: data.fontColor, - logoObjectPath: logoForWrite ?? null, + logoObjectPath: logoForFontRow ?? null, }; const [inserted] = await tx .insert(executiveMeetingFontSettingsTable) @@ -2946,6 +2944,36 @@ const upsertFontSettingsHandler = async ( .returning(); row = inserted; } + if (logoSentByAdmin && data.scope !== "global") { + const [globalRow] = await tx + .select() + .from(executiveMeetingFontSettingsTable) + .where( + and( + eq(executiveMeetingFontSettingsTable.scope, "global"), + isNull(executiveMeetingFontSettingsTable.userId), + ), + ); + if (globalRow) { + await tx + .update(executiveMeetingFontSettingsTable) + .set({ logoObjectPath: data.logoObjectPath }) + .where(eq(executiveMeetingFontSettingsTable.id, globalRow.id)); + } else { + await tx + .insert(executiveMeetingFontSettingsTable) + .values({ + scope: "global", + userId: null, + fontFamily: data.fontFamily, + fontSize: data.fontSize, + fontWeight: data.fontWeight, + alignment: data.alignment, + fontColor: data.fontColor, + logoObjectPath: data.logoObjectPath ?? null, + }); + } + } await logAudit(tx, { action: existing ? "update" : "create", entityType: "font_settings", diff --git a/artifacts/tx-os/public/opengraph.jpg b/artifacts/tx-os/public/opengraph.jpg index d08d6341..f45afc6f 100644 Binary files a/artifacts/tx-os/public/opengraph.jpg and b/artifacts/tx-os/public/opengraph.jpg differ diff --git a/artifacts/tx-os/src/pages/executive-meetings.tsx b/artifacts/tx-os/src/pages/executive-meetings.tsx index 485d722d..3f5f2cd4 100644 --- a/artifacts/tx-os/src/pages/executive-meetings.tsx +++ b/artifacts/tx-os/src/pages/executive-meetings.tsx @@ -7384,9 +7384,8 @@ function FontSettingsSection({ async function save() { setSaving(true); try { - // Omit logoObjectPath on user scope; send it (incl. null) on global. const body: Record = { scope, ...prefs }; - if (scope === "global") { + if (canEditGlobal) { body.logoObjectPath = logoPath; } await apiJson(`/api/executive-meetings/font-settings`, { @@ -7545,7 +7544,7 @@ function FontSettingsSection({ /> - {canEditGlobal && scope === "global" && ( + {canEditGlobal && (
{logoPath && (