You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
6.8 KiB
Markdown

# Branches in this repo
## `main` — the dev workspace
Every feature, every client's code, all in one place — this is where you
work day to day. **`main` is never pushed to a client's GitLab directly** —
it's the source a client branch gets cut from.
## `client/<slug>` — one per client, generated, not edited
Produced by:
```bash
scripts/export_client.sh <slug> <FLAG1,FLAG2,...>
```
Flags are the `VITE_ALLOW_ACCESS_<FLAG>` suffixes already used in
`.env.example`: `EFFICIENCY`, `PFI`, `RELIABILITY`, `RISK_MATRIX`, `LCCA`,
`RBD`, `OH`, `IRCFA`, `AHM`. Auth, the dashboard shell, admin, and ops-dock
have no flag and always ship.
Each export is `main`, with:
- `.env.example`'s `VITE_ALLOW_ACCESS_*` defaults set so a plain
`cp .env.example .env` on the client's side turns on only what they
licensed,
- no `Jenkinsfile` (client has their own CI), no `memory/` (dev notes),
no `ref/` (internal PRD/screenshots), no `scripts/`.
**These branches are disposable — never hand-edit them.** The script does
`git checkout -B client/<slug> main`, so re-running it regenerates the
branch from scratch every time. If a client needs a hotfix that shouldn't
wait for the next full export, make the fix on `main` first, then re-run
the export.
Pushing to the client's GitLab is a separate, explicit step (the script
prints the command, or pass `--push <remote-url>` to do it inline):
```bash
git push <client-remote> client/<slug>:main
```
### What the flags do and don't do — read this before relying on them
Unlike the backend, **this does not remove any code from the repo.**
`src/router/index.tsx` statically imports every page under `src/pages/`
regardless of flags — they're still individually code-split via `lazy()`
(so a page nobody licensed isn't in anyone's initial JS payload), but the
source is still sitting in the export, reachable by anyone with repo access.
What a disabled flag *does* block, via `src/router/FeatureGate.tsx`: every
route under that domain — both the flat `/xxx-app/*` paths and the
`/unit/:unitId/xxx/*` paths, plus its `/landing/*` teaser page where one
exists — renders `UnderConstructionPage` instead. `FeatureGate` wraps the
layout route's `element`, not something inside it, so the real layout
component (and whatever data-fetching hooks it kicks off on mount) is
never mounted at all when disabled, and there's no `<Outlet />` in the
tree for any child route to render into — direct URL navigation to a
sub-page hits the same placeholder as the domain's root. So a client
*can't see* a feature they weren't sold, even by guessing/typing the URL.
They just aren't prevented from finding it in the repo's source if they
have repo access at all — that's the part that would need the bigger job
below.
If you need the frontend repo itself to not contain another client's page
source at all (not just block them from seeing it rendered), that's a
bigger job: `router/index.tsx`'s route tree would need to be filtered per
export the way `config/urls.py` is on the backend, and every page under
an unlicensed feature would need to come out of `src/pages/` and
`src/features/` too. Not done here — flag if you want it built.
### Known gap: hardcoded branding
`public/Logo_PLN.png`, `Logo_PLN.svg`, `rcfa-app/logo-tjb.png` and similar
are referenced directly (by that literal filename) from ~16 files —
nav sidebars, the login page, PDF/Excel report generators. `export_client.sh`
prints a warning when these are still present in an export, but does not
attempt to fix it: swapping branding per client needs those references
replaced with a generic path (e.g. `/branding/logo.png`) on `main` first,
the same way `config/settings_local.py` only works on the backend because
`main` already has a hook pointed at it. Until that refactor happens, every
client export currently ships the same PLN/TJB branding and plant-specific
copy (see also `src/pages/unit-3`, and `SchematicName`-style hardcoding on
the backend) regardless of who it's actually for.
## `overlay/<slug>` — one per client, permanent, hand-maintained
For whatever's unique to one client and genuinely not shared with any
other. Created with:
```bash
scripts/new_client_overlay.sh <slug>
```
This is an **orphan branch** — no shared history with `main`, no shared
history with any other client's overlay. Nobody working on
`overlay/client-b` ever has a reason to check out `overlay/client-a`; the
two are as separate as if they were different repos.
An overlay may only ever *add* files, never edit one that also exists on
`main`. Three extension points exist, all the same shape — a path that
doesn't exist on `main`, discovered via `import.meta.glob`, which
resolves to an empty object when nothing matches, so each is always safe
to be absent:
- **Routes** — `src/router/overlay/<slug>.tsx`, picked up by
`src/router/index.tsx` via `import.meta.glob("./overlay/*.tsx")`.
Export `routes: RouteObject[]`. `scripts/new_client_overlay.sh` scaffolds
this one with an empty array — safe by construction, since an empty
array adds nothing until you fill it in.
- **Layout/shell** — `src/shared/components/layouts/overlay/AppLayout.tsx`,
picked up by `src/shared/components/layouts/AppLayout.tsx` (every
domain's `*LayoutRoute` imports `AppLayout` from there, never
`DefaultAppLayout` directly, so this one file swaps the whole shell —
sidebar, navbar, colors — everywhere at once). Default export, same
props as `DefaultAppLayout`.
- **Theme** — `src/styles/overlay/*.css`, loaded by `src/main.tsx` after
`index.css`, so redefining `:root`/`.dark`'s CSS custom properties
(`--primary`, `--background`, etc.) there wins the cascade.
The layout and theme hooks are **not** scaffolded with a placeholder file
the way routes are, and that's deliberate: an empty `routes` array has no
effect, but the layout/theme hooks activate the instant a matching file
exists at all, regardless of content. Only add one once you have a real
replacement.
Also allowed, same "new path only" rule:
- New components/pages that an overlay route or layout references.
- New static assets under a path `main` doesn't already use (see the
branding gap above for why an *existing* asset path isn't safe to
override yet).
That constraint isn't arbitrary: `client/<slug>` is regenerated from
scratch on every export, so `.env.example` and every existing file are
rewritten every single time. An overlay branch has no commit in common
with whatever `client/<slug>` looks like *this* time, so if it touched a
file the export also touches, every future export would hit the same
merge conflict forever. Sticking to new, untouched paths sidesteps that
entirely — a merge that only adds files can't conflict with content it
never shared.
`export_client.sh` merges `overlay/<slug>` into the freshly-built
`client/<slug>` automatically, if that branch exists, right before the
final guard — so overlay content gets the same leak check as everything
else.