From 100211761029cccd58dc6a548350574100c7790b Mon Sep 17 00:00:00 2001 From: Ryan Moon Date: Sun, 29 Mar 2026 16:32:18 -0500 Subject: [PATCH] Show store logo in sidebar with Amplified by Forte branding Sidebar header now loads the store logo from the files API and displays it scaled to fit. Below the logo: "Amplified by Forte" in subtle text. Falls back to store name as text if no logo is uploaded. Logo fetched via authenticated request with blob URL and proper cleanup. --- packages/admin/src/routes/_authenticated.tsx | 52 ++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/packages/admin/src/routes/_authenticated.tsx b/packages/admin/src/routes/_authenticated.tsx index 7b5e1b7..4b9a957 100644 --- a/packages/admin/src/routes/_authenticated.tsx +++ b/packages/admin/src/routes/_authenticated.tsx @@ -1,6 +1,8 @@ import { createFileRoute, Outlet, Link, redirect, useRouter } from '@tanstack/react-router' import { useQuery } from '@tanstack/react-query' -import { useEffect } from 'react' +import { queryOptions } from '@tanstack/react-query' +import { useEffect, useState } from 'react' +import { api } from '@/lib/api-client' import { useAuthStore } from '@/stores/auth.store' import { myPermissionsOptions } from '@/api/rbac' import { Avatar } from '@/components/shared/avatar-upload' @@ -17,6 +19,49 @@ export const Route = createFileRoute('/_authenticated')({ component: AuthenticatedLayout, }) +function StoreLogo() { + const token = useAuthStore((s) => s.token) + const [src, setSrc] = useState(null) + + const { data: storeData } = useQuery(queryOptions({ + queryKey: ['store'], + queryFn: () => api.get<{ id: string; name: string }>('/v1/store'), + enabled: !!token, + })) + + const { data: filesData } = useQuery(queryOptions({ + queryKey: ['files', 'company', storeData?.id ?? ''], + queryFn: () => api.get<{ data: { id: string; path: string }[] }>('/v1/files', { entityType: 'company', entityId: storeData?.id }), + enabled: !!storeData?.id, + })) + + const logoFile = filesData?.data?.find((f) => f.path.includes('/logo-')) + + useEffect(() => { + if (!logoFile || !token) { setSrc(null); return } + let cancelled = false + let blobUrl: string | null = null + async function load() { + try { + const res = await fetch(`/v1/files/serve/${logoFile!.path}`, { + headers: { Authorization: `Bearer ${token}` }, + }) + if (!res.ok || cancelled) return + const blob = await res.blob() + if (!cancelled) { blobUrl = URL.createObjectURL(blob); setSrc(blobUrl) } + } catch { /* ignore */ } + } + load() + return () => { cancelled = true; if (blobUrl) URL.revokeObjectURL(blobUrl) } + }, [logoFile?.path, token]) + + if (src) { + return {storeData?.name + } + + return

{storeData?.name ?? 'Forte'}

+} + function NavLink({ to, icon, label }: { to: string; icon: React.ReactNode; label: string }) { return (