Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 271 additions & 0 deletions docs/admin-settings.md

Large diffs are not rendered by default.

503 changes: 503 additions & 0 deletions plans/001-store-content-foundation-and-general.md

Large diffs are not rendered by default.

187 changes: 187 additions & 0 deletions plans/002-branding-settings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# Plan 002: Branding settings sub-option

> **Executor instructions**: Follow step by step; run every verification command; honor STOP
> conditions. Update this plan's row in `plans/README.md` when done.
>
> **Drift check (run first)**: `git diff --stat 9add86d..HEAD -- src/features/settings src/stores src/components/store/shared src/components/layout/data`
> Also confirm plan 001 has landed (see "Depends on"). On excerpt mismatch, STOP.

## Status

- **Priority**: P2
- **Effort**: S
- **Risk**: LOW
- **Depends on**: plans/001-store-content-foundation-and-general.md
- **Category**: direction
- **Planned at**: commit `9add86d`, 2026-07-20

## Why this matters

Admins should control store branding — logo and brand color — without editing code. This adds a
Branding sub-option that persists brand values and wires the storefront navbar/footer logo to read
from the store. It is the smallest storefront-consuming slice, so it validates the
"admin edits → storefront reflects" loop end to end.

## Prerequisite from plan 001 (must already exist)

- `src/stores/storeContentStore.ts` exporting `useStoreContentStore` (Zustand + `persist`,
localStorage key `store-content`) plus per-slice selector hooks.
- The sub-option pattern: `src/features/settings/<name>/{index.tsx,<name>-form.tsx}` + route file
`src/routes/user/_authenticated/settings/<name>.lazy.tsx` + a nav entry in
`src/features/settings/index.tsx`.
- The route-tree regeneration workflow: run `npx vite build` (regenerates
`src/routeTree.gen.ts`) BEFORE `pnpm build`.

If any prerequisite is missing, STOP — plan 001 is not done.

## Current state

Storefront logo is hard-coded. Footer — `src/components/store/shared/footer.tsx:12-16`:

```tsx
<img src='images/nexf-white-logo.svg' alt='Logo' className='w-1/2 sm:h-6 sm:w-21 p-4 sm:p-0' />
```

The navbar logo lives in `src/components/store/shared/navbar.tsx` (search for `<img` / `logo`).
Both use string `src` paths under `public/`. This plan makes those `src` values (and a brand color)
come from the store, with the current paths as defaults so nothing changes visually until edited.

## Commands you will need

| Purpose | Command | Expected |
|---------|---------|----------|
| Regenerate route tree | `npx vite build` | exit 0; updates `src/routeTree.gen.ts` |
| Build + typecheck | `pnpm build` | exit 0 |
| Lint | `pnpm lint` | exit 0 |
| Format | `pnpm format` | exit 0 |
| Tests | `pnpm test` | all pass |

## Scope

**In scope:**
- `src/stores/storeContentStore.ts` (edit — add `branding` slice + selectors)
- `src/features/settings/branding/index.tsx` (create)
- `src/features/settings/branding/branding-form.tsx` (create)
- `src/routes/user/_authenticated/settings/branding.lazy.tsx` (create)
- `src/features/settings/index.tsx` (edit — add inner-nav item)
- `src/components/layout/data/sidebar-data.ts` (edit — add child under Settings collapsible)
- `src/components/store/shared/footer.tsx` (edit — logo `src` from store)
- `src/components/store/shared/navbar.tsx` (edit — logo `src` from store)
- `src/stores/storeContentStore.test.ts` (edit — add branding assertions)

**Out of scope:**
- Applying the brand color to the Tailwind theme (CSS-first config in `src/index.css`). Store the
value; do NOT rewire theme tokens in this plan — note it as a follow-up.
- Any other storefront component.

## Git workflow

- Branch: `advisor/002-branding-settings`. Conventional commit, e.g.
`feat(settings): add branding sub-option`. No `Co-Authored-By` trailer.

## Steps

### Step 1: Add the `branding` slice to the store

In `src/stores/storeContentStore.ts`, add alongside the existing `general` slice (mirror its shape):

```tsx
export interface BrandingSettings {
logoLight: string // path under public/, e.g. 'images/nexf-logo.svg'
logoDark: string // used on dark backgrounds (footer), e.g. 'images/nexf-white-logo.svg'
favicon: string
brandColor: string // hex, e.g. '#2563eb'
}
```

Add `branding: BrandingSettings` + `setBranding` to the state interface and the `create(...)` body,
a `defaultBranding` seeded from the CURRENT hard-coded paths (read them from `footer.tsx` /
`navbar.tsx` — do not guess), and selector hooks `useBrandingSettings` / `useSetBrandingSettings`
following the `useGeneralSettings` pattern.

**Verify**: included in Step 5 build.

### Step 2: Branding form + index + route

Create `src/features/settings/branding/branding-form.tsx` modeled on
`src/features/settings/general/general-form.tsx` (from plan 001): zod schema for the four fields
(`Input`s; `brandColor` may use `<input type='color'>` wrapped, or a plain text `Input` validated
as hex), `defaultValues` from `useBrandingSettings()`, submit calls `useSetBrandingSettings()` +
`toast`.

Create `src/features/settings/branding/index.tsx` (copy `general/index.tsx`, retitle
"Branding" / "Logo, favicon and brand color.").

Create `src/routes/user/_authenticated/settings/branding.lazy.tsx` (copy `general.lazy.tsx`, swap
path to `/user/_authenticated/settings/branding` and component to `SettingsBranding`).

**Verify**: `npx vite build` → exit 0; `grep -n "settings/branding" src/routeTree.gen.ts` → ≥1 match.

### Step 3: Add the nav item to BOTH navs

Plan 001 established that a Settings sub-option must be registered in two places (see plan 001
"Current state" for the full explanation). Append this item to the END of both lists, after the
existing admin **Store** block items (keep that block contiguous; do NOT re-add the
`// --- Store settings ---` label comment — plan 001 already added it):

- **Left sidebar** — `src/components/layout/data/sidebar-data.ts`, add a child to the Settings
collapsible's `items` (full url, bare icon component):
`{ title: 'Branding', url: '/user/settings/branding', icon: IconPhoto }` (import `IconPhoto` into
the existing `@tabler/icons-react` import block).
- **Settings inner nav** — `src/features/settings/index.tsx`, add (JSX icon, prefix-less href):
`{ title: 'Branding', icon: <IconPhoto size={18} />, href: '/settings/branding' }`.

### Step 4: Wire storefront logos to the store

In `src/components/store/shared/footer.tsx`, replace the hard-coded logo `src` with the store value:

```tsx
const branding = useBrandingSettings()
// ...
<img src={branding.logoDark} alt='Logo' className='w-1/2 sm:h-6 sm:w-21 p-4 sm:p-0' />
```

Do the equivalent in `src/components/store/shared/navbar.tsx` using `branding.logoLight`. Keep all
existing className / layout untouched — change only the `src`. Import the selector from
`@/stores/storeContentStore`.

**Verify**: `pnpm build` → exit 0; storefront still renders the same logos (defaults equal the old
paths).

### Step 5: Format, lint, test

Extend `src/stores/storeContentStore.test.ts` with a branding block (defaults present; `setBranding`
updates). Then:

**Verify**: `pnpm format` → exit 0; `pnpm lint` → exit 0; `pnpm test` → all pass; `pnpm build` → exit 0.

## Test plan

- Add to `storeContentStore.test.ts`: `branding` defaults exist; `setBranding` updates `logoLight`.
- Verification: `pnpm test` → all pass.

## Done criteria

- [ ] `pnpm build`, `pnpm lint`, `pnpm test` all exit 0.
- [ ] `grep -n "settings/branding" src/routeTree.gen.ts` → ≥1 match.
- [ ] "Branding" appears under Settings in the left sidebar (`grep -n "settings/branding" src/components/layout/data/sidebar-data.ts` → 1 match) AND in the Settings inner nav.
- [ ] `/user/settings/branding` renders; changing logo path + Save changes the storefront logo after reload.
- [ ] Footer/navbar logos read from the store (no hard-coded logo `src` remains in those two files:
`grep -n "nexf-white-logo" src/components/store/shared/footer.tsx` → no match).
- [ ] `plans/README.md` row for 002 → DONE.

## STOP conditions

- Plan 001 artifacts missing (store or pattern absent).
- The navbar logo isn't a simple `<img src=...>` you can repoint — report what you found instead.
- Excerpts don't match live code (drift).

## Maintenance notes

- **Admin-only by intent** and **client-side-persisted now** — see plan 001 "Cross-cutting
decisions". Do not add role guards or `@/api/*` calls here; cover this sub-option when
roadmap-item-1 route guards land.
- **Deferred:** applying `brandColor` to the Tailwind v4 theme (`src/index.css`). Storing it now is
cheap; theming is a separate change with visual-regression risk.
- Logo paths point at `public/` assets; uploading new images is out of scope (no backend). Admins
can only select among existing public paths until an upload/asset service exists.
Loading
Loading