diff --git a/client/src/lib/api.ts b/client/src/lib/api.ts index 6c80da7..c07fccc 100644 --- a/client/src/lib/api.ts +++ b/client/src/lib/api.ts @@ -93,6 +93,19 @@ export const api = { } return json.data; }, + async getFileContentBlob(token: string, fileId: string) { + const response = await fetch(`/api/v1/files/${fileId}/content`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + if (!response.ok) { + throw new ApiError("Unable to load file content.", "FILE_CONTENT_FAILED"); + } + + return response.blob(); + }, getCustomers(token: string, filters?: { q?: string; status?: CrmRecordStatus; state?: string }) { return request( `/api/v1/crm/customers${buildQueryString({ diff --git a/client/src/modules/settings/CompanySettingsPage.tsx b/client/src/modules/settings/CompanySettingsPage.tsx index fd393f0..a92a6d5 100644 --- a/client/src/modules/settings/CompanySettingsPage.tsx +++ b/client/src/modules/settings/CompanySettingsPage.tsx @@ -13,14 +13,36 @@ export function CompanySettingsPage() { const [logoUrl, setLogoUrl] = useState(null); const [status, setStatus] = useState("Loading company profile..."); + async function loadLogoPreview(nextToken: string, logoFileId: string | null) { + if (!logoFileId) { + setLogoUrl((current) => { + if (current?.startsWith("blob:")) { + window.URL.revokeObjectURL(current); + } + return null; + }); + return; + } + + const blob = await api.getFileContentBlob(nextToken, logoFileId); + const objectUrl = window.URL.createObjectURL(blob); + setLogoUrl((current) => { + if (current?.startsWith("blob:")) { + window.URL.revokeObjectURL(current); + } + return objectUrl; + }); + } + useEffect(() => { if (!token) { return; } + let active = true; + api.getCompanyProfile(token).then((profile) => { setCompanyId(profile.id); - setLogoUrl(profile.logoUrl); setForm({ companyName: profile.companyName, legalName: profile.legalName, @@ -38,9 +60,37 @@ export function CompanySettingsPage() { }); applyBrandProfile(profile); setStatus("Company profile loaded."); + + if (profile.theme.logoFileId) { + loadLogoPreview(token, profile.theme.logoFileId) + .then(() => { + if (!active) { + return; + } + }) + .catch(() => { + if (active) { + setLogoUrl(null); + } + }); + } else { + setLogoUrl(null); + } }); + + return () => { + active = false; + }; }, [applyBrandProfile, token]); + useEffect(() => { + return () => { + if (logoUrl?.startsWith("blob:")) { + window.URL.revokeObjectURL(logoUrl); + } + }; + }, [logoUrl]); + if (!form || !token) { return
{status}
; } @@ -52,7 +102,7 @@ export function CompanySettingsPage() { } const profile = await api.updateCompanyProfile(token, form); applyBrandProfile(profile); - setLogoUrl(profile.logoUrl); + await loadLogoPreview(token, profile.theme.logoFileId); setStatus("Company settings saved."); } @@ -74,7 +124,7 @@ export function CompanySettingsPage() { } : current ); - setLogoUrl(`/api/v1/files/${attachment.id}/content`); + await loadLogoPreview(token, attachment.id); setStatus("Logo uploaded. Save to persist it on the profile."); }