Fix PDF font color not reflecting system settings

Root cause: resolveFontPrefsForUser() uses `userRow ?? globalRow` precedence.
When an admin saved font settings with scope="global", only the global row
was updated. If the admin also had a user-scope row (created by prior saves
with the default scope="user"), the user-scope row still overrode the global
row during PDF generation, causing the PDF to show the old color.

Backend fix (executive-meetings.ts):
- When saving with scope="global", delete the current admin's user-scope row
  within the same transaction. This ensures global settings immediately apply
  to the admin who set them. Other users' personal rows are unaffected.

Frontend fix (executive-meetings.tsx):
- Pass both `globalFont` and effective `font` to FontSettingsSection.
- When the scope dropdown changes, the form now loads the selected scope's
  actual saved values instead of always showing the effective (user-override)
  values. This prevents confusion where the admin edits "global" but sees
  their personal values in the form.

Verified end-to-end:
- PATCH font-settings with scope=user + fontColor=#ff0000 → DB updated → PDF uses red
- PATCH font-settings with scope=global + fontColor=#0000ff → global updated,
  user-scope row deleted → PDF resolves to global blue
- TypeScript compiles cleanly, e2e Playwright test passes
This commit is contained in:
riyadhafraa
2026-05-04 12:17:40 +00:00
parent 4fcb80b44a
commit 77538a93c0
3 changed files with 24 additions and 3 deletions
@@ -2970,6 +2970,16 @@ const upsertFontSettingsHandler = async (
});
}
}
if (data.scope === "global") {
await tx
.delete(executiveMeetingFontSettingsTable)
.where(
and(
eq(executiveMeetingFontSettingsTable.scope, "user"),
eq(executiveMeetingFontSettingsTable.userId, userId),
),
);
}
await logAudit(tx, {
action: existing ? "update" : "create",
entityType: "font_settings",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 25 KiB

@@ -952,6 +952,13 @@ function ExecutiveMeetingsPageInner() {
{section === "settings" && me && (
<SettingsSection
font={effectiveFont}
globalFont={fontResp?.global ? {
fontFamily: fontResp.global.fontFamily,
fontSize: fontResp.global.fontSize,
fontWeight: fontResp.global.fontWeight,
alignment: fontResp.global.alignment,
fontColor: fontResp.global.fontColor ?? "#000000",
} : null}
globalLogoObjectPath={fontResp?.global?.logoObjectPath ?? null}
canEditGlobalFont={me.canEditGlobalFontSettings}
columns={columns}
@@ -7216,6 +7223,7 @@ function PdfSection({
function SettingsSection({
font,
globalFont,
globalLogoObjectPath,
canEditGlobalFont,
columns,
@@ -7225,6 +7233,7 @@ function SettingsSection({
t,
}: {
font: FontPrefs;
globalFont: FontPrefs | null;
globalLogoObjectPath: string | null;
canEditGlobalFont: boolean;
columns: ColumnSetting[];
@@ -7254,6 +7263,7 @@ function SettingsSection({
<section className="bg-white border border-gray-200 rounded-lg p-4 sm:p-6">
<FontSettingsSection
font={font}
globalFont={globalFont}
globalLogoObjectPath={globalLogoObjectPath}
canEditGlobal={canEditGlobalFont}
t={t}
@@ -7372,11 +7382,13 @@ function AlertPrefsCard({ t }: { t: (k: string) => string }) {
function FontSettingsSection({
font,
globalFont,
globalLogoObjectPath,
canEditGlobal,
t,
}: {
font: FontPrefs;
globalFont: FontPrefs | null;
globalLogoObjectPath: string | null;
canEditGlobal: boolean;
t: (k: string) => string;
@@ -7385,13 +7397,12 @@ function FontSettingsSection({
const { toast } = useToast();
const [scope, setScope] = useState<"user" | "global">("user");
const [prefs, setPrefs] = useState<FontPrefs>(font);
// Local mirror of the global logo path for instant preview.
const [logoPath, setLogoPath] = useState<string | null>(globalLogoObjectPath);
const [saving, setSaving] = useState(false);
useEffect(() => {
setPrefs(font);
}, [font]);
setPrefs(scope === "global" && globalFont ? globalFont : font);
}, [font, globalFont, scope]);
useEffect(() => {
setLogoPath(globalLogoObjectPath);
}, [globalLogoObjectPath]);