Reporting Hub Build¶
Owner: Bryan · Target time once familiar: about 10 to 15 minutes to a live gated site. Stack: MkDocs Material, private GitHub repo, Cloudflare Pages, Cloudflare Access.
This is Phase 2 of Client Onboarding: the end-to-end build of a new client's gated reporting hub. Exact Cloudflare and GitHub values live in Cloudflare (Pages + Access).
0. Gather these first¶
| Thing | Example | Used for |
|---|---|---|
| Client short name (slug, lowercase, no spaces) | acmestore |
repo + subdomain |
| Client display name | Acme Store |
site title, footer |
| Client website URL | https://acmestore.com |
footer credit link |
| Client email domain (for allow-list) | @acmestore.com |
Cloudflare Access |
| Specific client emails (if no shared domain) | [email protected] |
Cloudflare Access |
| Logo file | white/light wordmark PNG or SVG | header branding |
| Brand colors | hex codes (primary/accent/bg/ink) | theme palette |
| Brand fonts | e.g. Fraunces / Inter | typography |
Missing brand assets can usually be pulled from the client's site: logo at …/cdn/shop/files/*Logo.png on Shopify, colors and fonts from the site CSS.
Naming: repo = client-<slug>, Pages project = client-<slug> (deploys to client-<slug>.pages.dev), subdomain = <slug>.dalayon.work.
Generate the scaffold:
.\new-client.ps1 -Slug acmestore -Name "Acme Store" -ClientDomain "@acmestore.com" -Website "https://acmestore.com"
This pre-fills the name, subdomain, email domain, and footer ("Made by Bryan Tirado for <Client>").
Phase A: Create the site locally¶
The generator produces the full interactive stack. If you build by hand instead, copy an existing client folder (client-sample is the cleanest reference) and delete .git/, .venv/, site/, and the contents of personal-notes/ from the copy, then find-and-replace the client specifics in mkdocs.yml, docs/index.md, and README.md.
Preview locally:
cd client-<slug>
py -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe -m mkdocs serve
Open http://127.0.0.1:8000, confirm it looks right, then Ctrl+C.
Phase B: Push to GitHub as private¶
In GitHub Desktop: File → Add Local Repository → select client-<slug> → create a repository. Then Publish repository:
- Owner: Dalayon-Reports
- Keep this code private (critical).
Sanity check the Changes list: it must not include .venv/, site/, or personal-notes/. If it does, stop; .gitignore is not applying.
Phase C: Deploy on Cloudflare Pages¶
- Cloudflare dashboard → Compute → Workers & Pages → Create application.
- It defaults to the Worker flow. Click "Looking to deploy Pages? Get started" to switch to Pages.
- Connect to Git → GitHub → Dalayon-Reports → select
client-<slug>→ Begin setup. - Build settings:
| Field | Value |
|---|---|
| Project name | client-<slug> |
| Production branch | main |
| Framework preset | None |
| Build command | pip install -r requirements.txt && mkdocs build |
| Build output directory | site |
| Env variable | PYTHON_VERSION = 3.12 |
- Save and Deploy, wait for the green build, site live at
client-<slug>.pages.dev.
Phase D: Custom domain¶
In the Pages project → Custom domains → Set up a domain → enter <slug>.dalayon.work → Activate. DNS auto-creates (dalayon.work is on Cloudflare); SSL goes active in a few minutes.
Phase E: Gate it with Cloudflare Access¶
Full detail with screenshots-worth of values is in Cloudflare (Pages + Access). The short version:
- Zero Trust → Access → Applications → Add an application → Self-hosted.
- Destinations: add the custom hostname
<slug>.dalayon.workandclient-<slug>.pages.dev. Gating both closes the.pages.devback door. Do not skip it. - Policy: Allow, Include emails ending in
@<clientdomain>and/or specific emails. Include your own team emails so you do not lock yourselves out. - Leave "Accept all available identity providers" on (One-time PIN + Google are already account-wide).
- Session duration 24 hours. Create.
Phase F: Verify¶
- Incognito →
https://<slug>.dalayon.work→ should hit the Access login. - Log in with an allowed email → site loads.
- Incognito →
client-<slug>.pages.dev→ should also require login. - (Optional) a non-listed email → should be denied.
Phase G: Branding¶
Match the site to the client's brand so reports feel native. The template already handles the light/dark toggle, footer credit, and universal CSS fixes. Three brand-specific steps remain:
- Logo + favicon. Save the client's logo to
docs/assets/and uncommenttheme.logo/theme.faviconinmkdocs.yml. Add a small squarefavicon.svg(brand-color tile + initials). - Colors. In
docs/stylesheets/extra.css, uncomment the per-client brand section and set the--brand-*variables and the light/dark scheme blocks. - Fonts. Uncomment
theme.fontinmkdocs.ymland the heading@importblock inextra.css.
Branding heuristics:
- Header color follows the logo. Light or vivid logo → dark brand-colored header. Dark or black logo → white/light header (copy the
.md-headeroverride fromclient-sundaenco). - Palette comes from the client's own assets. Pull their logo, read its colors. If no hex codes are exposed, derive them from the logo.
- Light-only vs light+dark is a brand judgment: playful and editorial brands went light-only; the rest kept the toggle.
- Fonts match brand character: serif for fashion/editorial, condensed/industrial for rugged, geometric sans for tech.
Worked examples: client-sample (cleanest), client-theladcollective (dark header, multi-region), client-megansalmon (light header), client-hardhat (dark + hi-vis accent).
Phase H: Populate the dashboards¶
The template ships interactive, brand-aware widgets (Overview KPIs, Conversion funnel + weekly chart, Site Performance vitals chart, Activity Log, CRO rounds). They stay empty until data exists. To populate them, follow Data Refresh: drop exports into team-data/, set the currency in tools/build_overview_data.py, run the builder, then author the static page content from the same merged data.
Add CRO rounds as you run them (docs/cro/round-<n>/ + a nav block + a linked row in cro/index.md). See CRO Delivery.
When the engagement ends¶
Follow Client Offboarding.