Carry the Hanzo mark across the Help Center

The desk SPA's titles, marks and install icons come from this repo's own
source, so they change here: the shell title and apple title, the PWA
manifest name, the sidebar and welcome-ticket copy, the fallback logo
component, the SPA favicon and the 180/192/512 install icons.

The login page, navbar, footer and favicon are rendered by the Frappe core,
which reads those marks from Website Settings / Navbar Settings / System
Settings at run time. helpdesk/brand.py writes them and the entrypoint runs
it per site on every boot, next to the SSO setup. Each field is set to a
constant and guarded on the field existing, so a boot against a site that
already exists restores the same values instead of failing.

The marks are served from this app's own public directory
(helpdesk/public -> /assets/helpdesk), so no page reaches a third-party host
for a logo.
This commit is contained in:
zooqueen
2026-08-13 23:19:08 -07:00
parent 6827018615
commit 5d7054c177
15 changed files with 122 additions and 27 deletions
+5
View File
@@ -75,6 +75,11 @@ provision_site() {
# IAM SSO + login hardening (idempotent, secrets from env / KMS-backed).
bench --site "${site}" execute helpdesk.hanzo_sso.setup \
|| echo "[entrypoint] SSO provisioning skipped/failed for ${site} (non-fatal)"
# Brand the pages Frappe renders itself — login, navbar, footer, favicon.
# Each field is set to a constant, so this restores the brand on every boot
# of a site that already exists just as it sets it on a new one.
bench --site "${site}" execute helpdesk.brand.setup \
|| echo "[entrypoint] brand provisioning skipped/failed for ${site} (non-fatal)"
}
IFS=',' read -ra _orgs <<< "$HANZO_ORGS"
+2 -2
View File
@@ -3,10 +3,10 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Helpdesk</title>
<title>Hanzo Help Center</title>
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="Frappe Helpdesk" />
<meta name="apple-mobile-web-app-title" content="Hanzo Help Center" />
<meta name="apple-mobile-web-app-status-bar-style" content="white" />
<!-- PWA -->
+10 -4
View File
@@ -1,4 +1,10 @@
<svg width="118" height="118" viewBox="0 0 118 118" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M93.9278 0H23.1013C10.3428 0 0 10.3428 0 23.1013V93.9278C0 106.686 10.3428 117.029 23.1013 117.029H93.9278C106.686 117.029 117.029 106.686 117.029 93.9278V23.1013C117.029 10.3428 106.686 0 93.9278 0Z" fill="#7D42FB"/>
<path d="M95.9759 50.8753V27.8265L21 27.8265V38.3271H85.5278V48.3027C81.3275 49.5103 78.2824 53.3955 78.2824 57.9632C78.2824 62.531 81.3275 66.3637 85.5278 67.5713V77.5468H31.5006V50.1403H21V88.0474H96.0284V64.9986L89.7805 60.5359V55.3906L96.0284 50.9278L95.9759 50.8753Z" fill="#EDF7FF"/>
</svg>
<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<rect width="64" height="64" rx="8" fill="#ffffff"/>
<g transform="translate(8, 8) scale(0.716)">
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#000000"/>
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#000000"/>
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#000000"/>
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#000000"/>
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#000000"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 624 B

After

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

+9 -9
View File
@@ -1,18 +1,18 @@
<template>
<svg
width="118"
height="118"
viewBox="0 0 118 118"
width="67"
height="67"
viewBox="0 0 67 67"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="currentColor" />
<path
d="M93.9278 0H23.1013C10.3428 0 0 10.3428 0 23.1013V93.9278C0 106.686 10.3428 117.029 23.1013 117.029H93.9278C106.686 117.029 117.029 106.686 117.029 93.9278V23.1013C117.029 10.3428 106.686 0 93.9278 0Z"
fill="#7D42FB"
/>
<path
d="M95.9759 50.8753V27.8265L21 27.8265V38.3271H85.5278V48.3027C81.3275 49.5103 78.2824 53.3955 78.2824 57.9632C78.2824 62.531 81.3275 66.3637 85.5278 67.5713V77.5468H31.5006V50.1403H21V88.0474H96.0284V64.9986L89.7805 60.5359V55.3906L96.0284 50.9278L95.9759 50.8753Z"
fill="#EDF7FF"
d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z"
fill="currentColor"
/>
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="currentColor" />
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="currentColor" />
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="currentColor" />
</svg>
</template>
+2 -2
View File
@@ -155,7 +155,7 @@
v-model="showHelpModal"
v-model:articles="articles"
appName="helpdesk"
title="Frappe Helpdesk"
title="Hanzo Help Center"
:logo="logo"
docsLink="https://docs.frappe.io/helpdesk"
:afterSkip="(step: string) => capture('onboarding_step_skipped_' + step)"
@@ -634,7 +634,7 @@ const articles = ref([
],
},
{
title: __("Frappe Helpdesk Mobile"),
title: __("Hanzo Help Center Mobile"),
opened: false,
subArticles: [
{ name: "pwa-installation", title: __("Mobile App Installation") },
+2 -2
View File
@@ -55,8 +55,8 @@ export default defineConfig(async ({ mode }) => {
},
manifest: {
display: "standalone",
name: "Frappe Helpdesk",
short_name: "Helpdesk",
name: "Hanzo Help Center",
short_name: "Help Center",
start_url: "/helpdesk",
description:
"Modern, Streamlined, Free and Open Source Customer Service Software",
+70
View File
@@ -0,0 +1,70 @@
"""Hanzo Help Center brand for the pages Frappe itself renders.
The desk SPA carries its marks in this repo's source. The login page, the
website navbar and the website footer are rendered by the Frappe core, which
reads its marks from Website Settings / Navbar Settings / System Settings at
run time. Writing those rows is how the Help Center brands those pages — the
core stays untouched.
Called by the container entrypoint on every boot via
`bench --site <site> execute helpdesk.brand.setup`. Every write sets a field
to a constant, so a re-run against an existing site restores the same values.
The marks are served from this app's own public directory
(`helpdesk/public` -> `/assets/helpdesk`), so no page reaches a third-party
host for a logo.
"""
import frappe
NAME = "Hanzo Help Center"
PUBLISHER = "Hanzo AI"
MARK = "/assets/helpdesk/hanzo-mark.svg"
FAVICON = "/assets/helpdesk/hanzo-favicon.svg"
# Single doctype -> {fieldname: value}.
#
# app_logo is set on both Singles because the core reads it from two places:
# `get_app_logo()` (login page, desk toolbar) takes Website Settings first and
# Navbar Settings second, while the desktop page reads Navbar Settings direct.
#
# brand_html is the navbar-brand slot the core reads FIRST, ahead of
# banner_image, so setting it alone makes the navbar mark deterministic;
# banner_image is left empty rather than kept as a second source for one slot.
# The navbar stylesheet already caps a brand image at 22px, so the tag carries
# no sizing of its own.
#
# footer_powered must be non-empty: the footer falls back to the framework's
# own credit line when it is blank.
BRAND = {
"Website Settings": {
"app_name": NAME,
"app_logo": MARK,
"favicon": FAVICON,
"brand_html": f'<img src="{MARK}" alt="{NAME}">',
"footer_powered": PUBLISHER,
},
"Navbar Settings": {"app_logo": MARK},
# The login page falls back to this when Website Settings carries no
# app_name, and the core ships it defaulted to the framework's name.
"System Settings": {"app_name": NAME},
}
def setup():
written = [
f"{doctype}.{fieldname}"
for doctype, fields in BRAND.items()
for fieldname, value in fields.items()
if _set_single_if_field(doctype, fieldname, value)
]
frappe.db.commit()
print(f"brand: set {', '.join(written) or 'nothing — no fields present'}")
def _set_single_if_field(doctype: str, fieldname: str, value) -> bool:
"""set_single_value only when the field exists on this Frappe version."""
if not frappe.get_meta(doctype).has_field(fieldname):
return False
frappe.db.set_single_value(doctype, fieldname, value)
return True
+1 -1
View File
@@ -8,4 +8,4 @@ Configuration for docs
def get_context(context):
context.brand_html = "Frappe Helpdesk"
context.brand_html = "Hanzo Help Center"
+10
View File
@@ -0,0 +1,10 @@
<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<rect width="64" height="64" rx="8" fill="#ffffff"/>
<g transform="translate(8, 8) scale(0.716)">
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#000000"/>
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#000000"/>
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#000000"/>
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#000000"/>
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#000000"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 535 B

+7
View File
@@ -0,0 +1,7 @@
<svg viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#000000"/>
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#000000"/>
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#000000"/>
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#000000"/>
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 417 B

+3 -6
View File
@@ -9,7 +9,7 @@ Hey {{ first_name }} 👋,
<br><br>
We thought we'd use this space to give you a quick walkthrough while you're here. 🙂
<br><br>
This is a sample ticket we created to show you how Frappe Helpdesk works.
This is a sample ticket we created to show you how the Hanzo Help Center works.
<br><br>
Getting started is easy. In the main <b>Ticket activity area</b>, when you reply to a ticket your response shows up directly below in the activity thread. This way you can keep track of all your ticket activities in one single place. ✨
<br>
@@ -38,15 +38,12 @@ What you can do next:<br>
<li>Invite your team and auto-assign tickets using assignment rules.</li>
<li>Set up SLAs to manage response times for your agents.</li>
<li>Add a Knowledge Base for self-serve support.</li>
<li>Check out our documentation at <a href="https://docs.frappe.io/helpdesk" style="color: #3b82f6;">https://docs.frappe.io/helpdesk</a> </li>
</ul>
<br>
Feel free to click around and explore our product.
<br><br>
If you need help, reach out to us here: <a href="https://support.frappe.io/helpdesk" style="color: #3b82f6;">https://support.frappe.io/helpdesk</a>
Feel free to click around and explore.
<br><br>
Cheers,<br>
Team Frappe
Team Hanzo
</div>
"""
+1 -1
View File
@@ -23,6 +23,6 @@
{% endmacro %} {% macro signature() %}
<p class="m-0">
Thanks,<br />
Team Frappe
Team Hanzo
</p>
{% endmacro %}