diff --git a/wiki/_master-index.md b/wiki/_master-index.md index bfdfb1c..7fc75e3 100644 --- a/wiki/_master-index.md +++ b/wiki/_master-index.md @@ -23,7 +23,7 @@ This 3-hop pattern works for hundreds of articles without vector search. | [[wiki/tech-patterns/_index\|tech-patterns/]] | Recurring tech stacks: FastAPI, React/Vite, Next.js, Azure AD, AI, Box, One2Edit, Redis/Celery, cost-tracker | 15 | | [[wiki/architecture/_index\|architecture/]] | Cross-cutting architectural patterns: Docker Compose, multi-agent AI, GCP timeout, RAG, hotfolder, optical-dev deploy, cost-tracker, new-project checklist, troubleshooting playbooks, ADR log | 10 | | [[wiki/client-knowledge/_index\|client-knowledge/]] | Per-client notes for Ford, H&M, L'Oréal, Barclays, Ferrero, 3M | 6 | -| [[wiki/concepts/_index\|concepts/]] | Atomic knowledge extracted from Claude Code sessions | 61 | +| [[wiki/concepts/_index\|concepts/]] | Atomic knowledge extracted from Claude Code sessions | 62 | | [[wiki/connections/_index\|connections/]] | Cross-cutting insights linking 2+ concepts: FastAPI+Azure AD+Docker trinity, AI→cost-tracker, Apache+Vite basePath, GCP→REST polling, Box+hotfolder, Docker DNS+AdGuard | 9 | | [[wiki/qa/_index\|qa/]] | Filed answers to queries (saved with `--file-back`) | 0 | | [[wiki/homelab/_index\|homelab/]] | Self-hosted infra: Proxmox install, IOMMU/PCI passthrough, hypervisor setup, budget builds, HP Elitedesk G3, Homarr API + Apps + Boards + Certificates + Integrations + Settings + Tasks + AdGuard + Clock + Docker Stats + Docker Integration + Download Client + Firewall + Proxmox Integration + Radarr + Readarr + Sonarr + Bookmarks + Calendar + Icons + App Widget + Weather + GitHub + Nextcloud + qBittorrent + RSS Feed + Speedtest Tracker + System Health Monitoring + System Resources + Services Map + Media Stack | 39 | diff --git a/wiki/client-knowledge/barclays.md b/wiki/client-knowledge/barclays.md index 4ecab09..45d6bae 100644 --- a/wiki/client-knowledge/barclays.md +++ b/wiki/client-knowledge/barclays.md @@ -91,6 +91,23 @@ DISABLE_AUTH=true --- +## Banner Builder UI Rebrand (2026-04-28) + +The banner builder UI was rebranded from the Barclays design system to the **Oliver Modcomms design system**. Visual layer changed; core functionality unchanged. + +**What changed:** +- Tailwind tokens renamed from `barclays-*` → `oliver-*` +- Layout: horizontal nav → dark vertical sidebar (`w-[220px]`), matching the Modcomms pattern +- Theme colour picker added to both `BannerEditor` and `VariantsGrid` views; variants have a `theme` enum: `navy | sky-blue | yellow | lime | teal` +- Logo: placeholder `CopyGenBannerAgent_RFA.png` generated via `sips` (PIL not available on macOS); real CopyGen/Oliver asset needed + +**What did NOT change (by design):** +- Internal localStorage key names (`barclays-*`) intentionally kept to avoid invalidating in-flight user sessions — see [[wiki/concepts/localstorage-key-migration-rebrand|localstorage-key-migration-rebrand]] for why and how to handle this in a future release +- AI Refine/Improve box only mutates copy text fields — cannot change visual theme/colours (by architecture) +- Barclays brand hex codes (`#00AEEF`, `#00395D`, etc.) remain correct in `tailwind.config.ts` as of commit `47b3f12` + +--- + --- ## Lessons from banner-builder (2026-04-28) @@ -144,3 +161,4 @@ See [[wiki/concepts/pydantic-model-dict-interface]] for full pattern. - [[wiki/concepts/export-endpoint-filter-pattern|export-endpoint-filter-pattern]] — variant_ids in exports - [[wiki/concepts/zustand-async-hydration]] — Zustand hydration timing bug (Banner Builder) - [[wiki/concepts/pydantic-model-dict-interface]] — Pydantic vs dict silent failure (Banner Builder tasks.py) +- [[wiki/concepts/localstorage-key-migration-rebrand]] — localStorage key migration after rebrand (Banner Builder 2026-04-28) diff --git a/wiki/concepts/_index.md b/wiki/concepts/_index.md index c9c8ad3..0624eeb 100644 --- a/wiki/concepts/_index.md +++ b/wiki/concepts/_index.md @@ -69,5 +69,7 @@ | [[wiki/concepts/zustand-async-hydration]] | Zustand persist hydrates localStorage asynchronously — components must gate API calls behind hasHydrated or token will be null on first render | daily/2026-04-28.md | 2026-04-28 | | [[wiki/concepts/pydantic-model-dict-interface]] | Pydantic model passed where dict expected — .get() returns None silently instead of raising; use isinstance check or model_dump() at boundary | daily/2026-04-28.md | 2026-04-28 | +| [[wiki/concepts/localstorage-key-migration-rebrand]] | localStorage key migration after renaming storage keys in a rebrand — session loss, Zustand persist name, migration script pattern | daily/2026-04-28.md | 2026-04-28 | + diff --git a/wiki/concepts/localstorage-key-migration-rebrand.md b/wiki/concepts/localstorage-key-migration-rebrand.md new file mode 100644 index 0000000..c208b89 --- /dev/null +++ b/wiki/concepts/localstorage-key-migration-rebrand.md @@ -0,0 +1,117 @@ +--- +title: "Frontend — localStorage Key Migration When Rebranding" +aliases: [localstorage-key-migration, storage-key-rename, rebrand-session-loss, zustand-persist-rename] +tags: [frontend, localstorage, zustand, react, rebrand, auth, migration] +sources: + - "daily/2026-04-28.md" +created: 2026-04-28 +updated: 2026-04-28 +--- + +# Frontend — localStorage Key Migration When Rebranding + +When a frontend app is rebranded and its localStorage key names are changed (e.g., from `barclays-auth-storage` to `oliver-auth-storage`), all existing user sessions are silently invalidated. The old keys remain in the browser's localStorage but are no longer read — on next load, the app finds no data under the new key name and treats the user as unauthenticated. + +## Key Points + +- **Old localStorage keys persist forever** — renaming the key in code does not migrate existing browser data; the old key sits orphaned, the new key is empty +- **All users are effectively logged out** after a key rename deploy — no error, no warning; the app simply starts fresh as if the user never logged in +- **Zustand `persist` middleware uses a `name` option as the localStorage key** — changing this name causes the same session loss +- **Fix: migration script at app entry point** — on load, check for old key, copy data to new key, delete old key; runs once per user, transparent +- **Decision to keep internal keys**: in barclays-banner-builder, internal session keys (`barclays-*`) were intentionally NOT renamed to avoid invalidating in-flight sessions during the rebrand; only UI-visible branding strings were changed + +## Details + +### Why This Happens + +Browsers store localStorage data keyed by the exact string used in `localStorage.setItem(key, value)`. When a Zustand `persist` store has `name: "barclays-auth-storage"`, Zustand writes to and reads from that exact string. When the name is changed to `"oliver-auth-storage"`, Zustand looks for the new key — finds nothing — initialises with the default (unauthenticated) state. The old `barclays-auth-storage` entry remains in the browser forever. + +This is not a bug in Zustand or localStorage — it's the expected behavior of key-value storage. The same problem occurs with any keyed storage: sessionStorage, IndexedDB, cookie names, etc. + +### Symptom + +After a rebrand deploy: +- Users who were logged in are suddenly redirected to the login page +- No error in the console or network tab +- `Application → Local Storage` in DevTools shows both old and new keys: old with data, new empty +- Logging in again works fine (new key is populated); old key remains as clutter + +### The Migration Script + +Add to `main.tsx` (or the app's entry point), before the React tree mounts: + +```typescript +// main.tsx — runs once before React mounts +function migrateStorage() { + const OLD_KEY = "barclays-auth-storage"; + const NEW_KEY = "oliver-auth-storage"; + + const oldData = localStorage.getItem(OLD_KEY); + if (oldData && !localStorage.getItem(NEW_KEY)) { + // Copy old data to new key + localStorage.setItem(NEW_KEY, oldData); + // Clean up old key + localStorage.removeItem(OLD_KEY); + console.log("[migration] Migrated localStorage from", OLD_KEY, "to", NEW_KEY); + } +} + +migrateStorage(); + +ReactDOM.createRoot(document.getElementById("root")!).render(); +``` + +This migration is safe to ship before or alongside the key rename: if old data exists and new key is empty, migrate; otherwise no-op. + +### When to Keep Old Keys (No Migration) + +In some cases, deliberately not migrating is the right choice: + +- **During a deploy with in-flight user sessions**: renaming now would log everyone out immediately; decide whether a one-time logout is acceptable +- **When the stored data format changed** during the rebrand: migrating old-format data into a new-format key would corrupt state; better to let users re-login with a clean slate +- **Internal-only keys** (e.g., cache keys, feature flags): user impact is minimal, no migration needed + +barclays-banner-builder 2026-04-28 decision: `barclays-*` session keys kept unchanged because the rebrand was cosmetic (visual redesign only), and invalidating active work sessions mid-project was unacceptable. + +### Zustand-Specific Pattern + +For Zustand `persist` stores, the migration can also be done inside the store's `onRehydrateStorage` callback: + +```typescript +// Alternative: migrate inside Zustand store initialisation +export const useAuthStore = create()( + persist( + (set) => ({ /* ... */ }), + { + name: "oliver-auth-storage", // ← new key + onRehydrateStorage: () => { + // Check for old key and migrate if needed + const old = localStorage.getItem("barclays-auth-storage"); + if (old) { + localStorage.setItem("oliver-auth-storage", old); + localStorage.removeItem("barclays-auth-storage"); + } + }, + } + ) +); +``` + +This is neater for Zustand stores but couples the migration to the store initialisation rather than the app entry point. + +### Other Storage Types + +The same pattern applies to: +- `sessionStorage` — same API, same key-rename problem (though sessions are cleared on tab close anyway) +- Cookie names — renaming a session cookie name logs all users out; same migration approach applies server-side +- IndexedDB database names — renaming requires schema migration via `onupgradeneeded` + +## Related Concepts + +- [[wiki/concepts/zustand-async-hydration]] — Zustand persist hydration timing; keys are the configuration point where naming matters +- [[wiki/concepts/memory-compiler-mac-migration]] — similar "config key mismatch" pattern: hooks reference paths/identifiers that break when the environment changes +- [[wiki/client-knowledge/barclays]] — barclays-banner-builder rebrand context where this was encountered + +## Sources + +- [[daily/2026-04-28.md]] — barclays-banner-builder UI rebrand from Barclays design tokens to Oliver Modcomms design system; `barclays-*` localStorage keys intentionally kept to avoid session invalidation; decision documented as tech debt item (migrate to `oliver-*` in a future release with migration script) diff --git a/wiki/log.md b/wiki/log.md index 0db2b18..c72c42d 100644 --- a/wiki/log.md +++ b/wiki/log.md @@ -3,6 +3,11 @@ +## [2026-04-29T21:00:00+01:00] compile | 2026-04-28.md (pass 3) +- Source: daily/2026-04-28.md +- Articles created: [[wiki/concepts/localstorage-key-migration-rebrand]] +- Articles updated: [[wiki/client-knowledge/barclays]] (added Banner Builder UI rebrand section: Oliver Modcomms design system, vertical sidebar, theme picker, localStorage key intentionally kept) + ## [2026-04-28T23:15:00+01:00] compile | 2026-04-28.md (pass 2) - Source: daily/2026-04-28.md - Articles created: (none — all primary knowledge captured in pass 1)