From b65e03e1c4e699306584931b1f8a9261ce37d494 Mon Sep 17 00:00:00 2001 From: Thomas Joise Date: Mon, 13 Jul 2026 16:35:05 +0530 Subject: [PATCH] =?UTF-8?q?feat(hq-web):=20Document=20Template=20page=20?= =?UTF-8?q?=E2=80=94=20editable=20letterhead=20data=20+=20live=20preview?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- apps/hq-web/src/Layout.tsx | 4 +- apps/hq-web/src/api.ts | 9 ++ apps/hq-web/src/main.tsx | 4 +- apps/hq-web/src/pages/CompanyProfile.tsx | 54 --------- apps/hq-web/src/pages/DocumentTemplate.tsx | 124 +++++++++++++++++++++ 5 files changed, 137 insertions(+), 58 deletions(-) delete mode 100644 apps/hq-web/src/pages/CompanyProfile.tsx create mode 100644 apps/hq-web/src/pages/DocumentTemplate.tsx diff --git a/apps/hq-web/src/Layout.tsx b/apps/hq-web/src/Layout.tsx index 79aa3c2..3735d9f 100644 --- a/apps/hq-web/src/Layout.tsx +++ b/apps/hq-web/src/Layout.tsx @@ -25,8 +25,8 @@ export function Layout() { if (!hasSession()) return - // Company (the company.* settings editor) is owner-only — staff never see the item. - const nav_items = role() === 'owner' ? [...BASE_NAV, { to: '/settings/company', label: 'Company' }] : BASE_NAV + // Document Template (company.* + template.* letterhead data) is owner-only — staff never see the item. + const nav_items = role() === 'owner' ? [...BASE_NAV, { to: '/settings/template', label: 'Document Template' }] : BASE_NAV return (
diff --git a/apps/hq-web/src/api.ts b/apps/hq-web/src/api.ts index 3b4cd6c..f6c35e1 100644 --- a/apps/hq-web/src/api.ts +++ b/apps/hq-web/src/api.ts @@ -325,6 +325,15 @@ export const getCompanyProfile = (): Promise> => export const putCompanyProfile = (body: Record): Promise> => apiFetch<{ company: Record }>('/settings/company', { method: 'PUT', body: JSON.stringify(body) }).then((r) => r.company) +export const getTemplateSettings = (): Promise> => + apiFetch<{ settings: Record }>('/settings/template').then((r) => r.settings) +export const putTemplateSettings = ( + body: { company?: Record; template?: Record; titles?: Record }, +): Promise> => + apiFetch<{ settings: Record }>('/settings/template', { method: 'PUT', body: JSON.stringify(body) }).then((r) => r.settings) +export const uploadTemplateLogo = (dataUri: string): Promise => + apiFetch<{ logo: string }>('/settings/template/logo', { method: 'POST', body: JSON.stringify({ dataUri }) }).then((r) => r.logo) + /** The PDF route needs the bearer header, which an