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