Replace portal title with entity name

Store a short entity name instead of the full home
heading so orgs can brand portals for sub-entities.
Restore hero i18n composition and keep the English
document title composed from the entity name.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-07-21 19:06:55 +02:00
parent 0fe814cfe4
commit 0d832508c9
46 changed files with 148 additions and 97 deletions

View File

@@ -45,6 +45,7 @@
}
},
"home": {
"heroTitle": "Vertrauen bei {{name}}.",
"heroDescription": "Willkommen auf unserem Compliance-Portal. Hier finden Sie unsere Sicherheitsdokumentation und Zertifizierungen.",
"sections": {
"compliance": "Compliance",

View File

@@ -55,6 +55,7 @@
}
},
"home": {
"heroTitle": "Compliance at {{name}}.",
"heroDescription": "Welcome to our Compliance Portal. Find our security documentation and certifications here.",
"sections": {
"compliance": "Compliance",
@@ -104,4 +105,4 @@
"previous": "Previous page",
"next": "Next page"
}
}
}

View File

@@ -45,6 +45,7 @@
}
},
"home": {
"heroTitle": "Confianza en {{name}}.",
"heroDescription": "Bienvenido a nuestro Portal de cumplimiento. Aquí encontrará nuestra documentación de seguridad y nuestras certificaciones.",
"sections": {
"compliance": "Cumplimiento",

View File

@@ -45,6 +45,7 @@
}
},
"home": {
"heroTitle": "La confiance chez {{name}}.",
"heroDescription": "Bienvenue dans notre Portail de conformité. Retrouvez ici notre documentation et nos certifications de sécurité.",
"sections": {
"compliance": "Conformité",

View File

@@ -45,6 +45,7 @@
}
},
"home": {
"heroTitle": "Kepercayaan di {{name}}.",
"heroDescription": "Selamat datang di Portal Kepatuhan kami. Temukan dokumentasi keamanan dan sertifikasi kami di sini.",
"sections": {
"compliance": "Kepatuhan",

View File

@@ -45,6 +45,7 @@
}
},
"home": {
"heroTitle": "La fiducia in {{name}}.",
"heroDescription": "Benvenuti nel nostro Portale di conformità. Qui potete trovare la nostra documentazione di sicurezza e le nostre certificazioni.",
"sections": {
"compliance": "Conformità",

View File

@@ -45,6 +45,7 @@
}
},
"home": {
"heroTitle": "{{name}}における信頼。",
"heroDescription": "コンプライアンスポータルへようこそ。セキュリティに関する文書や認証はこちらからご確認いただけます。",
"sections": {
"compliance": "コンプライアンス",

View File

@@ -45,6 +45,7 @@
}
},
"home": {
"heroTitle": "{{name}}의 신뢰.",
"heroDescription": "컴플라이언스 포털에 오신 것을 환영합니다. 이곳에서 보안 문서와 인증 정보를 확인하실 수 있습니다.",
"sections": {
"compliance": "컴플라이언스",

View File

@@ -45,6 +45,7 @@
}
},
"home": {
"heroTitle": "Zaufanie w {{name}}.",
"heroDescription": "Witamy w naszym Portalu Zgodności. Znajdziesz tu naszą dokumentację bezpieczeństwa i certyfikaty.",
"sections": {
"compliance": "Zgodność",

View File

@@ -45,6 +45,7 @@
}
},
"home": {
"heroTitle": "A confiança na {{name}}.",
"heroDescription": "Bem-vindo ao nosso Portal de Conformidade. Encontre aqui a nossa documentação de segurança e certificações.",
"sections": {
"compliance": "Conformidade",

View File

@@ -45,6 +45,7 @@
}
},
"home": {
"heroTitle": "{{name}}'de Güven.",
"heroDescription": "Uyumluluk Portalımıza hoş geldiniz. Güvenlik belgelerimizi ve sertifikalarımızı burada bulabilirsiniz.",
"sections": {
"compliance": "Uyumluluk",

View File

@@ -45,6 +45,7 @@
}
},
"home": {
"heroTitle": "Довіра в {{name}}.",
"heroDescription": "Вітаємо у нашому Порталі відповідності. Тут ви знайдете нашу документацію з безпеки та сертифікати.",
"sections": {
"compliance": "Відповідність",

View File

@@ -45,6 +45,7 @@
}
},
"home": {
"heroTitle": "信赖 {{name}}。",
"heroDescription": "欢迎访问我们的合规门户。您可以在此查看我们的安全文档和认证。",
"sections": {
"compliance": "合规",

View File

@@ -45,7 +45,7 @@ const topBarFragment = graphql`
}
currentCompliancePortal @required(action: THROW) {
themedLogoUrl
title
entityName
}
}
`;
@@ -66,7 +66,7 @@ export function TopBar({ queryKey }: TopBarProps) {
const localizedPath = useLocalizedPath();
const { currentCompliancePortal } = data;
const title = currentCompliancePortal.title;
const entityName = currentCompliancePortal.entityName;
const logoUrl = currentCompliancePortal.themedLogoUrl ?? undefined;
const slots = topBar();
@@ -82,11 +82,11 @@ export function TopBar({ queryKey }: TopBarProps) {
color="neutral"
radius="small"
src={logoUrl}
fallback={title.charAt(0) || "?"}
fallback={entityName.charAt(0) || "?"}
className={slots.logo()}
/>
<Text size={2} weight="medium" color="neutral" highContrast className={slots.brandName()}>
{title}
{entityName}
</Text>
<Text size={2} color="neutral" className={slots.tagline()}>
{t("topBar.tagline")}

View File

@@ -45,7 +45,7 @@ export const subscribeDialogProviderFragment = graphql`
}
currentCompliancePortal @required(action: THROW) {
id
title
entityName
viewerSubscription {
id
}
@@ -71,7 +71,7 @@ export function SubscribeDialogProvider({
const [unsubscribeFromMailingList, isUnsubscribing] = useUnsubscribeFromMailingList();
const viewer = data.viewer;
const { id: trustCenterId, title, viewerSubscription } = data.currentCompliancePortal;
const { id: trustCenterId, entityName, viewerSubscription } = data.currentCompliancePortal;
const isSubscribed = viewerSubscription != null;
const openSubscribe = useCallback(() => {
@@ -127,7 +127,7 @@ export function SubscribeDialogProvider({
onOpenChange={setDialogOpen}
trustCenterId={trustCenterId}
viewerEmail={viewer.email}
organizationName={title}
organizationName={entityName}
/>
)}
</SubscribeDialogContextProvider>

View File

@@ -34,7 +34,7 @@ import type { HomePageQuery } from "./__generated__/HomePageQuery.graphql";
export const homePageQuery = graphql`
query HomePageQuery @throwOnFieldError {
currentCompliancePortal @required(action: THROW) {
title
entityName
...CompliancePortalContactInfo_compliancePortal
...ComplianceFrameworksSection_compliancePortal
...SecurityCommitmentsSection_compliancePortal
@@ -52,12 +52,12 @@ export function HomePage({ queryRef }: HomePageProps) {
const { t } = useTranslation();
const data = usePreloadedQuery<HomePageQuery>(homePageQuery, queryRef);
const { currentCompliancePortal } = data;
const { title } = currentCompliancePortal;
const { entityName } = currentCompliancePortal;
return (
<>
<Hero
title={title}
title={t("home.heroTitle", { name: entityName })}
description={t("home.heroDescription")}
>
<CompliancePortalContactInfo compliancePortalKey={currentCompliancePortal} />

View File

@@ -63,7 +63,7 @@ export const ndaPageQuery = graphql`
id
}
currentCompliancePortal @required(action: THROW) {
title
entityName
nonDisclosureAgreement {
fileUrl
}
@@ -238,7 +238,7 @@ export function NDAPage({ queryRef }: NDAPageProps) {
{t("title")}
</Heading>
<Text size={2} color="neutral">
{t("subtitle", { name: compliancePortal.title })}
{t("subtitle", { name: compliancePortal.entityName })}
</Text>
{signature.consentText != null && (
<Text size={1} color="faint" className={slots.consent()}>

View File

@@ -32,7 +32,7 @@ const updateCompliancePageMutation = graphql`
id
active
searchEngineIndexing
title
entityName
description
websiteUrl
email

View File

@@ -25,7 +25,7 @@ import { useFormWithSchema } from "#/hooks/useFormWithSchema";
const compliancePageFragment = graphql`
fragment CompliancePageProfileSection_compliancePageFragment on CompliancePortal {
id
title
entityName
description
websiteUrl
email
@@ -35,7 +35,7 @@ const compliancePageFragment = graphql`
`;
const profileSchema = z.object({
title: z.string().min(1),
entityName: z.string().min(1),
description: z.string().optional(),
websiteUrl: z.string().optional(),
email: z.string().optional(),
@@ -58,7 +58,7 @@ export function CompliancePageProfileSection(props: {
const { formState, handleSubmit, register } = useFormWithSchema(profileSchema, {
defaultValues: {
title: compliancePage.title,
entityName: compliancePage.entityName,
description: compliancePage.description || "",
websiteUrl: compliancePage.websiteUrl || "",
email: compliancePage.email || "",
@@ -73,7 +73,7 @@ export function CompliancePageProfileSection(props: {
variables: {
input: {
compliancePortalId: compliancePage.id,
title: data.title,
entityName: data.entityName,
description: data.description || null,
websiteUrl: data.websiteUrl || null,
email: data.email || null,
@@ -96,11 +96,11 @@ export function CompliancePageProfileSection(props: {
</div>
<Card padded className="space-y-4">
<Field
{...register("title")}
{...register("entityName")}
readOnly={readOnly}
name="title"
label={__("Title")}
placeholder={__("Compliance at Acme.")}
name="entityName"
label={__("Entity name")}
placeholder={__("Acme")}
/>
<div>
<Label>{__("Description")}</Label>

View File

@@ -233,7 +233,7 @@ export function OrganizationSidebar({
: (
<div className="size-24 rounded-2xl border border-border-mid shadow-mid bg-level-1" />
)}
<h1 className="text-2xl mt-6">{compliancePortal.title}</h1>
<h1 className="text-2xl mt-6">{compliancePortal.entityName}</h1>
<p className="text-sm text-txt-secondary mt-1">
{compliancePortal.description}
</p>

View File

@@ -24,7 +24,7 @@ import { CompliancePortalContext } from "#/providers/CompliancePortalProvider";
export function useCompliancePortal(): {
id: string;
title: string;
entityName: string;
} {
const context = useContext(CompliancePortalContext);
if (!context) {

View File

@@ -47,7 +47,7 @@ export const ndaPageQuery = graphql`
id
}
currentCompliancePortal @required(action: THROW) {
title
entityName
nonDisclosureAgreement {
fileName
fileUrl
@@ -244,7 +244,7 @@ export function NDAPage(props: {
__(
"%s requires you to sign an NDA before accessing compliance documents.",
),
compliancePortal.title,
compliancePortal.entityName,
)}
</p>
{isMobile && nda?.fileUrl && (

View File

@@ -107,7 +107,7 @@ export function OverviewPage() {
url={getCompliancePortalUrl("documents")}
/>
<Subprocessors
title={compliancePortal.title}
title={compliancePortal.entityName}
subprocessors={fragment.subprocessors.edges}
url={getCompliancePortalUrl("subprocessors")}
/>

View File

@@ -45,7 +45,7 @@ export function SubprocessorsPage({ queryRef }: Props) {
<p className="text-sm text-txt-secondary mb-4">
{sprintf(
__("Third-party subprocessors %s work with:"),
data.currentCompliancePortal?.title ?? "",
data.currentCompliancePortal?.entityName ?? "",
)}
</p>
<Rows>

View File

@@ -31,7 +31,7 @@ import type { ConnectPageQuery } from "./__generated__/ConnectPageQuery.graphql"
export const connectPageQuery = graphql`
query ConnectPageQuery {
currentCompliancePortal @required(action: THROW) {
title
entityName
}
}
`;
@@ -45,10 +45,10 @@ export function ConnectPage(props: {
const safeContinueUrl = useSafeContinueUrl();
const {
currentCompliancePortal: { title },
currentCompliancePortal: { entityName },
} = usePreloadedQuery<ConnectPageQuery>(connectPageQuery, queryRef);
usePageTitle(__(`Connect to ${title}'s Compliance Page`));
usePageTitle(__(`Connect to ${entityName}'s Compliance Page`));
const initiateURL = new URL("/initiate", window.location.origin);
initiateURL.searchParams.set("continue", safeContinueUrl.toString());
@@ -57,7 +57,7 @@ export function ConnectPage(props: {
<div className="space-y-6 w-full max-w-md mx-auto pt-8">
<div className="space-y-2 text-center">
<h1 className="text-3xl font-bold">
{__(`Connect to ${title}'s Compliance Page`)}
{__(`Connect to ${entityName}'s Compliance Page`)}
</h1>
<p className="text-txt-tertiary">
{__(

View File

@@ -31,7 +31,7 @@ export const currentCompliancePortalGraphQuery = graphql`
currentCompliancePortal @required(action: THROW) {
id
slug
title
entityName
description
websiteUrl
email
@@ -120,6 +120,7 @@ export const currentTrustSubprocessorsQuery = graphql`
query CompliancePortalGraphCurrentSubprocessorsQuery {
currentCompliancePortal {
id
entityName
subprocessors(first: 50) {
edges {
node {