feat: Created AdminDropdown component with click-outside/Escape close,…
- "frontend/src/components/AdminDropdown.tsx" - "frontend/src/App.tsx" - "frontend/src/App.css" GSD-Task: S01/T01
This commit is contained in:
parent
94460faf9d
commit
05c7ba3ca2
13 changed files with 454 additions and 5 deletions
14
.gsd/milestones/M006/M006-ROADMAP.md
Normal file
14
.gsd/milestones/M006/M006-ROADMAP.md
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# M006:
|
||||||
|
|
||||||
|
## Vision
|
||||||
|
Consolidate admin navigation into a dropdown, add head/tail log viewing and commit SHA tracking to the pipeline, clean up technique page tag styling and sidebar order, redesign the Topics browse page with a Music Theory category, and add app version footer.
|
||||||
|
|
||||||
|
## Slice Overview
|
||||||
|
| ID | Slice | Risk | Depends | Done | After this |
|
||||||
|
|----|-------|------|---------|------|------------|
|
||||||
|
| S01 | Admin Navigation Dropdown + Header Cleanup | low | — | ⬜ | Header shows Home, Topics, Creators, and an Admin dropdown. Clicking Admin reveals Review, Reports, Pipeline links. ModeToggle removed from header. |
|
||||||
|
| S02 | Pipeline Page: Head/Tail Log View + Token Count | low | — | ⬜ | Pipeline event log shows Head/Tail toggle buttons. Head shows first N events, Tail shows last N events. Token counts visible per event and per video. |
|
||||||
|
| S03 | Git Commit SHA in Pipeline Version Metadata | low | — | ⬜ | Running the pipeline captures the current git commit SHA. Viewing a technique page version shows the commit hash in the metadata panel. |
|
||||||
|
| S04 | Technique Page: Sidebar Reorder, Creator Emphasis, Tag Polish | medium | — | ⬜ | Technique page sidebar shows Plugins Referenced at top. Creator name is visually prominent. Tags use a coherent color system. |
|
||||||
|
| S05 | Topics Page Redesign + Music Theory Category | high | — | ⬜ | Topics page shows 7 categories (including Music Theory) with an improved visual layout — category cards with descriptions, sub-topic counts, better structure. |
|
||||||
|
| S06 | App Footer with Version Info | low | — | ⬜ | Every page shows a subtle footer with app version, build date, and optional repo link. |
|
||||||
34
.gsd/milestones/M006/slices/S01/S01-PLAN.md
Normal file
34
.gsd/milestones/M006/slices/S01/S01-PLAN.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# S01: Admin Navigation Dropdown + Header Cleanup
|
||||||
|
|
||||||
|
**Goal:** Header nav shows Home, Topics, Creators as flat links and an Admin dropdown. Clicking Admin reveals Review, Reports, Pipeline links. ModeToggle removed from header (kept on ReviewQueue page).
|
||||||
|
**Demo:** After this: Header shows Home, Topics, Creators, and an Admin dropdown. Clicking Admin reveals Review, Reports, Pipeline links. ModeToggle removed from header.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
- [x] **T01: Created AdminDropdown component with click-outside/Escape close, wired into App.tsx header replacing 3 admin links + ModeToggle, added dropdown CSS using existing theme tokens** — Create an AdminDropdown React component, replace the 3 admin nav links and ModeToggle in App.tsx, and add dropdown CSS to App.css.
|
||||||
|
|
||||||
|
**Context:** The header in `App.tsx` currently has 6 flat `<Link>` elements (Home, Topics, Creators, Review, Reports, Pipeline) plus a `<ModeToggle />`. Goal: group the 3 admin links behind an "Admin" dropdown, remove ModeToggle from the header. ModeToggle is still used by `ReviewQueue.tsx` — do NOT delete the component or its CSS.
|
||||||
|
|
||||||
|
**Click-outside pattern** — adapt from `Home.tsx` (typeahead):
|
||||||
|
```tsx
|
||||||
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
function handler(e: MouseEvent) {
|
||||||
|
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("mousedown", handler);
|
||||||
|
return () => document.removeEventListener("mousedown", handler);
|
||||||
|
}, []);
|
||||||
|
```
|
||||||
|
|
||||||
|
**CSS variables available** (all defined in App.css `:root`):
|
||||||
|
- Trigger text: `--color-text-on-header` / `--color-text-on-header-hover`
|
||||||
|
- Dropdown background: `--color-bg-surface`
|
||||||
|
- Border: `--color-border`
|
||||||
|
- Shadow: `--color-shadow-heavy`
|
||||||
|
- Item hover bg: `--color-bg-surface-hover`
|
||||||
|
- Item text: `--color-text-primary`
|
||||||
|
- Estimate: 30m
|
||||||
|
- Files: frontend/src/components/AdminDropdown.tsx, frontend/src/App.tsx, frontend/src/App.css
|
||||||
|
- Verify: cd frontend && npx tsc --noEmit && npx vite build
|
||||||
124
.gsd/milestones/M006/slices/S01/S01-RESEARCH.md
Normal file
124
.gsd/milestones/M006/slices/S01/S01-RESEARCH.md
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
# S01 Research — Admin Navigation Dropdown + Header Cleanup
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Straightforward frontend slice. The header nav in `App.tsx` currently shows 6 flat links (Home, Topics, Creators, Review, Reports, Pipeline) plus a `ModeToggle` component. The goal: group the 3 admin links (Review, Reports, Pipeline) behind an "Admin" dropdown, and remove `ModeToggle` from the header (it stays on the ReviewQueue page).
|
||||||
|
|
||||||
|
No new dependencies needed. No backend changes. No library lookups required — the codebase already has a click-outside dropdown pattern in `Home.tsx` (typeahead) that can be adapted.
|
||||||
|
|
||||||
|
## Recommendation
|
||||||
|
|
||||||
|
Single task. All changes are in 2 files (`App.tsx`, `App.css`) plus optionally extracting the dropdown into a small component. The ModeToggle component file itself is NOT deleted — it's still imported by `ReviewQueue.tsx`.
|
||||||
|
|
||||||
|
## Implementation Landscape
|
||||||
|
|
||||||
|
### Current State
|
||||||
|
|
||||||
|
**`frontend/src/App.tsx`** — The app shell. Header contains:
|
||||||
|
- Brand link (`<Link to="/">Chrysopedia</Link>`)
|
||||||
|
- `<div className="app-header__right">` containing:
|
||||||
|
- `<nav className="app-nav">` with 6 `<Link>` elements: Home, Topics, Creators, Review, Reports, Pipeline
|
||||||
|
- `<ModeToggle />` component
|
||||||
|
|
||||||
|
**`frontend/src/components/ModeToggle.tsx`** — Review/auto mode toggle. Imported in:
|
||||||
|
1. `App.tsx` line 12 (header — **remove this usage**)
|
||||||
|
2. `ReviewQueue.tsx` line 16 (page body — **keep this usage**)
|
||||||
|
|
||||||
|
**`frontend/src/App.css`** — All styles. Relevant sections:
|
||||||
|
- `.app-header` — flex row, space-between, dark background (`--color-bg-header`)
|
||||||
|
- `.app-header__right` — flex, gap 1.5rem
|
||||||
|
- `.app-nav` — flex, gap 1rem, links styled with `--color-text-on-header`
|
||||||
|
- `.mode-toggle` — all mode toggle styles (lines 425-488) — **keep these** (ReviewQueue still uses them)
|
||||||
|
|
||||||
|
### Target State
|
||||||
|
|
||||||
|
Header nav shows: `Home | Topics | Creators | Admin ▾`
|
||||||
|
|
||||||
|
Clicking "Admin" reveals a small dropdown with: Review, Reports, Pipeline.
|
||||||
|
|
||||||
|
ModeToggle removed from the header entirely.
|
||||||
|
|
||||||
|
### Existing Dropdown Pattern
|
||||||
|
|
||||||
|
`Home.tsx` uses a click-outside-to-close pattern:
|
||||||
|
```
|
||||||
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
function handler(e: MouseEvent) {
|
||||||
|
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
|
||||||
|
setShowDropdown(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("mousedown", handler);
|
||||||
|
return () => document.removeEventListener("mousedown", handler);
|
||||||
|
}, []);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Approach
|
||||||
|
|
||||||
|
1. **Create `AdminDropdown` component** (`frontend/src/components/AdminDropdown.tsx`):
|
||||||
|
- `useState<boolean>` for open/closed
|
||||||
|
- `useRef<HTMLDivElement>` for click-outside detection (same pattern as Home.tsx typeahead)
|
||||||
|
- Button that says "Admin" with a chevron indicator
|
||||||
|
- Dropdown panel with 3 `<Link>` items: Review, Reports, Pipeline
|
||||||
|
- Close dropdown on link click and on outside click
|
||||||
|
- Close on Escape key for accessibility
|
||||||
|
|
||||||
|
2. **Edit `App.tsx`**:
|
||||||
|
- Remove `ModeToggle` import
|
||||||
|
- Remove `<ModeToggle />` from header JSX
|
||||||
|
- Import `AdminDropdown`
|
||||||
|
- Replace the 3 admin `<Link>` elements with `<AdminDropdown />`
|
||||||
|
- Keep Home, Topics, Creators as flat links
|
||||||
|
|
||||||
|
3. **Add CSS to `App.css`**:
|
||||||
|
- `.admin-dropdown` — relative positioning container
|
||||||
|
- `.admin-dropdown__trigger` — styled like existing nav links (same font-size, color, transitions)
|
||||||
|
- `.admin-dropdown__menu` — absolute positioned panel below trigger, dark surface background, border, shadow, border-radius
|
||||||
|
- `.admin-dropdown__item` — block links with padding, hover state
|
||||||
|
- Use existing CSS variables: `--color-bg-surface`, `--color-border`, `--color-shadow-heavy`, `--color-text-on-header`, etc.
|
||||||
|
|
||||||
|
### Files Touched
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
|------|--------|
|
||||||
|
| `frontend/src/components/AdminDropdown.tsx` | **New** — dropdown component |
|
||||||
|
| `frontend/src/App.tsx` | Remove ModeToggle import/usage, remove 3 admin links, add AdminDropdown |
|
||||||
|
| `frontend/src/App.css` | Add `.admin-dropdown*` styles (~30 lines) |
|
||||||
|
|
||||||
|
### Files NOT Touched
|
||||||
|
|
||||||
|
| File | Why |
|
||||||
|
|------|-----|
|
||||||
|
| `frontend/src/components/ModeToggle.tsx` | Still used by ReviewQueue.tsx — do not delete |
|
||||||
|
| `frontend/src/App.css` `.mode-toggle` styles | Still used by ReviewQueue page — do not remove |
|
||||||
|
|
||||||
|
### CSS Variable Reuse
|
||||||
|
|
||||||
|
All needed tokens already exist:
|
||||||
|
- Trigger text: `--color-text-on-header` / `--color-text-on-header-hover`
|
||||||
|
- Dropdown panel background: `--color-bg-surface`
|
||||||
|
- Dropdown border: `--color-border`
|
||||||
|
- Dropdown shadow: `--color-shadow-heavy`
|
||||||
|
- Item hover: `--color-bg-surface-hover`
|
||||||
|
- Item text: `--color-text-primary`
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
|
||||||
|
- `cd frontend && npx tsc --noEmit` — zero TypeScript errors
|
||||||
|
- `cd frontend && npx vite build` — production build succeeds
|
||||||
|
- Visual: header shows Home, Topics, Creators, Admin. Clicking Admin reveals dropdown with Review, Reports, Pipeline links. No ModeToggle in header.
|
||||||
|
- Navigation: each dropdown link navigates to the correct route
|
||||||
|
- Click outside dropdown → closes
|
||||||
|
- Escape key → closes
|
||||||
|
- Mobile (640px): dropdown still accessible
|
||||||
|
|
||||||
|
### Accessibility Notes
|
||||||
|
|
||||||
|
- Dropdown trigger should use `<button>` with `aria-expanded` and `aria-haspopup="true"`
|
||||||
|
- Dropdown menu should use `role="menu"` with `role="menuitem"` on links
|
||||||
|
- Escape key should close the menu and return focus to trigger
|
||||||
|
|
||||||
|
### Risk
|
||||||
|
|
||||||
|
Low. No backend changes, no route changes, no new dependencies. The dropdown is a pure presentational addition to the existing nav.
|
||||||
50
.gsd/milestones/M006/slices/S01/tasks/T01-PLAN.md
Normal file
50
.gsd/milestones/M006/slices/S01/tasks/T01-PLAN.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
---
|
||||||
|
estimated_steps: 22
|
||||||
|
estimated_files: 3
|
||||||
|
skills_used: []
|
||||||
|
---
|
||||||
|
|
||||||
|
# T01: Create AdminDropdown component, wire into header, add styles
|
||||||
|
|
||||||
|
Create an AdminDropdown React component, replace the 3 admin nav links and ModeToggle in App.tsx, and add dropdown CSS to App.css.
|
||||||
|
|
||||||
|
**Context:** The header in `App.tsx` currently has 6 flat `<Link>` elements (Home, Topics, Creators, Review, Reports, Pipeline) plus a `<ModeToggle />`. Goal: group the 3 admin links behind an "Admin" dropdown, remove ModeToggle from the header. ModeToggle is still used by `ReviewQueue.tsx` — do NOT delete the component or its CSS.
|
||||||
|
|
||||||
|
**Click-outside pattern** — adapt from `Home.tsx` (typeahead):
|
||||||
|
```tsx
|
||||||
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
function handler(e: MouseEvent) {
|
||||||
|
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("mousedown", handler);
|
||||||
|
return () => document.removeEventListener("mousedown", handler);
|
||||||
|
}, []);
|
||||||
|
```
|
||||||
|
|
||||||
|
**CSS variables available** (all defined in App.css `:root`):
|
||||||
|
- Trigger text: `--color-text-on-header` / `--color-text-on-header-hover`
|
||||||
|
- Dropdown background: `--color-bg-surface`
|
||||||
|
- Border: `--color-border`
|
||||||
|
- Shadow: `--color-shadow-heavy`
|
||||||
|
- Item hover bg: `--color-bg-surface-hover`
|
||||||
|
- Item text: `--color-text-primary`
|
||||||
|
|
||||||
|
## Inputs
|
||||||
|
|
||||||
|
- ``frontend/src/App.tsx` — current header with 6 flat nav links + ModeToggle import to refactor`
|
||||||
|
- ``frontend/src/App.css` — existing header/nav styles and CSS custom properties (lines 146-175 for header, line 6+ for variables)`
|
||||||
|
- ``frontend/src/components/ModeToggle.tsx` — must NOT be deleted (still imported by ReviewQueue.tsx line 16)`
|
||||||
|
- ``frontend/src/pages/Home.tsx` — click-outside dropdown pattern (useRef + mousedown event listener) to adapt`
|
||||||
|
|
||||||
|
## Expected Output
|
||||||
|
|
||||||
|
- ``frontend/src/components/AdminDropdown.tsx` — new component: useState toggle, useRef click-outside, Escape key handler, `<button>` trigger with aria-expanded + aria-haspopup='true', dropdown `<div role='menu'>` with 3 `<Link role='menuitem'>` items (Review → /admin/review, Reports → /admin/reports, Pipeline → /admin/pipeline), close on link click`
|
||||||
|
- ``frontend/src/App.tsx` — ModeToggle import line removed, `<ModeToggle />` JSX removed, AdminDropdown imported, 3 admin `<Link>` elements replaced with `<AdminDropdown />`; remaining nav: Home, Topics, Creators as flat links`
|
||||||
|
- ``frontend/src/App.css` — new styles appended: `.admin-dropdown` (position: relative), `.admin-dropdown__trigger` (styled like nav links — same font-size, color, background:none, border:none, cursor:pointer, uses --color-text-on-header), `.admin-dropdown__menu` (position: absolute, top: 100%, right: 0, min-width: 10rem, background: var(--color-bg-surface), border: 1px solid var(--color-border), border-radius: 0.5rem, box-shadow, z-index: 100), `.admin-dropdown__item` (display: block, padding, color: var(--color-text-primary), hover: var(--color-bg-surface-hover))`
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
cd frontend && npx tsc --noEmit && npx vite build
|
||||||
80
.gsd/milestones/M006/slices/S01/tasks/T01-SUMMARY.md
Normal file
80
.gsd/milestones/M006/slices/S01/tasks/T01-SUMMARY.md
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
---
|
||||||
|
id: T01
|
||||||
|
parent: S01
|
||||||
|
milestone: M006
|
||||||
|
provides: []
|
||||||
|
requires: []
|
||||||
|
affects: []
|
||||||
|
key_files: ["frontend/src/components/AdminDropdown.tsx", "frontend/src/App.tsx", "frontend/src/App.css"]
|
||||||
|
key_decisions: ["Used calc(100% + 0.5rem) for menu offset to add visual breathing room", "BEM naming convention: admin-dropdown / admin-dropdown__trigger / __menu / __item"]
|
||||||
|
patterns_established: []
|
||||||
|
drill_down_paths: []
|
||||||
|
observability_surfaces: []
|
||||||
|
duration: ""
|
||||||
|
verification_result: "TypeScript check (tsc --noEmit) passed with zero errors. Vite production build succeeded — 47 modules transformed, built in 778ms. Both slice-level verification commands pass."
|
||||||
|
completed_at: 2026-03-30T11:02:14.843Z
|
||||||
|
blocker_discovered: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# T01: Created AdminDropdown component with click-outside/Escape close, wired into App.tsx header replacing 3 admin links + ModeToggle, added dropdown CSS using existing theme tokens
|
||||||
|
|
||||||
|
> Created AdminDropdown component with click-outside/Escape close, wired into App.tsx header replacing 3 admin links + ModeToggle, added dropdown CSS using existing theme tokens
|
||||||
|
|
||||||
|
## What Happened
|
||||||
|
---
|
||||||
|
id: T01
|
||||||
|
parent: S01
|
||||||
|
milestone: M006
|
||||||
|
key_files:
|
||||||
|
- frontend/src/components/AdminDropdown.tsx
|
||||||
|
- frontend/src/App.tsx
|
||||||
|
- frontend/src/App.css
|
||||||
|
key_decisions:
|
||||||
|
- Used calc(100% + 0.5rem) for menu offset to add visual breathing room
|
||||||
|
- BEM naming convention: admin-dropdown / admin-dropdown__trigger / __menu / __item
|
||||||
|
duration: ""
|
||||||
|
verification_result: passed
|
||||||
|
completed_at: 2026-03-30T11:02:14.844Z
|
||||||
|
blocker_discovered: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# T01: Created AdminDropdown component with click-outside/Escape close, wired into App.tsx header replacing 3 admin links + ModeToggle, added dropdown CSS using existing theme tokens
|
||||||
|
|
||||||
|
**Created AdminDropdown component with click-outside/Escape close, wired into App.tsx header replacing 3 admin links + ModeToggle, added dropdown CSS using existing theme tokens**
|
||||||
|
|
||||||
|
## What Happened
|
||||||
|
|
||||||
|
Created AdminDropdown.tsx with useState toggle, useRef click-outside listener, Escape handler, accessible button trigger (aria-expanded, aria-haspopup), and role=menu div with 3 Link menuitem elements. Updated App.tsx to import AdminDropdown instead of ModeToggle, replaced 3 admin Link elements and ModeToggle JSX with single AdminDropdown component. Appended BEM-style dropdown CSS to App.css using existing theme custom properties. ModeToggle preserved — still imported by ReviewQueue.tsx.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
TypeScript check (tsc --noEmit) passed with zero errors. Vite production build succeeded — 47 modules transformed, built in 778ms. Both slice-level verification commands pass.
|
||||||
|
|
||||||
|
## Verification Evidence
|
||||||
|
|
||||||
|
| # | Command | Exit Code | Verdict | Duration |
|
||||||
|
|---|---------|-----------|---------|----------|
|
||||||
|
| 1 | `cd frontend && npx tsc --noEmit` | 0 | ✅ pass | 3000ms |
|
||||||
|
| 2 | `cd frontend && npx vite build` | 0 | ✅ pass | 4400ms |
|
||||||
|
|
||||||
|
|
||||||
|
## Deviations
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
- `frontend/src/components/AdminDropdown.tsx`
|
||||||
|
- `frontend/src/App.tsx`
|
||||||
|
- `frontend/src/App.css`
|
||||||
|
|
||||||
|
|
||||||
|
## Deviations
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
None.
|
||||||
6
.gsd/milestones/M006/slices/S02/S02-PLAN.md
Normal file
6
.gsd/milestones/M006/slices/S02/S02-PLAN.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# S02: Pipeline Page: Head/Tail Log View + Token Count
|
||||||
|
|
||||||
|
**Goal:** Add head/tail viewing to pipeline event log for quick start/end inspection
|
||||||
|
**Demo:** After this: Pipeline event log shows Head/Tail toggle buttons. Head shows first N events, Tail shows last N events. Token counts visible per event and per video.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
6
.gsd/milestones/M006/slices/S03/S03-PLAN.md
Normal file
6
.gsd/milestones/M006/slices/S03/S03-PLAN.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# S03: Git Commit SHA in Pipeline Version Metadata
|
||||||
|
|
||||||
|
**Goal:** Record git commit SHA in pipeline metadata for traceability of prompt/code state at synthesis time
|
||||||
|
**Demo:** After this: Running the pipeline captures the current git commit SHA. Viewing a technique page version shows the commit hash in the metadata panel.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
6
.gsd/milestones/M006/slices/S04/S04-PLAN.md
Normal file
6
.gsd/milestones/M006/slices/S04/S04-PLAN.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# S04: Technique Page: Sidebar Reorder, Creator Emphasis, Tag Polish
|
||||||
|
|
||||||
|
**Goal:** Improve technique page visual hierarchy: plugins first in sidebar, creator stands out, tags make sense
|
||||||
|
**Demo:** After this: Technique page sidebar shows Plugins Referenced at top. Creator name is visually prominent. Tags use a coherent color system.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
6
.gsd/milestones/M006/slices/S05/S05-PLAN.md
Normal file
6
.gsd/milestones/M006/slices/S05/S05-PLAN.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# S05: Topics Page Redesign + Music Theory Category
|
||||||
|
|
||||||
|
**Goal:** Add Music Theory to the taxonomy, update classification prompt, and redesign the Topics browse page
|
||||||
|
**Demo:** After this: Topics page shows 7 categories (including Music Theory) with an improved visual layout — category cards with descriptions, sub-topic counts, better structure.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
6
.gsd/milestones/M006/slices/S06/S06-PLAN.md
Normal file
6
.gsd/milestones/M006/slices/S06/S06-PLAN.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
# S06: App Footer with Version Info
|
||||||
|
|
||||||
|
**Goal:** Add application versioning via a persistent footer
|
||||||
|
**Demo:** After this: Every page shows a subtle footer with app version, build date, and optional repo link.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
@ -776,6 +776,53 @@ body {
|
||||||
color: var(--color-text-on-header-hover);
|
color: var(--color-text-on-header-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Admin dropdown ───────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
|
.admin-dropdown {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-dropdown__trigger {
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-on-header);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
transition: color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-dropdown__trigger:hover {
|
||||||
|
color: var(--color-text-on-header-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-dropdown__menu {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 0.5rem);
|
||||||
|
right: 0;
|
||||||
|
min-width: 10rem;
|
||||||
|
background: var(--color-bg-surface);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 4px 16px var(--color-shadow-heavy);
|
||||||
|
z-index: 100;
|
||||||
|
padding: 0.375rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-dropdown__item {
|
||||||
|
display: block;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
transition: background 0.12s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-dropdown__item:hover {
|
||||||
|
background: var(--color-bg-surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Home / Hero ──────────────────────────────────────────────────────────── */
|
/* ── Home / Hero ──────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
.home-hero {
|
.home-hero {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import ReviewQueue from "./pages/ReviewQueue";
|
||||||
import MomentDetail from "./pages/MomentDetail";
|
import MomentDetail from "./pages/MomentDetail";
|
||||||
import AdminReports from "./pages/AdminReports";
|
import AdminReports from "./pages/AdminReports";
|
||||||
import AdminPipeline from "./pages/AdminPipeline";
|
import AdminPipeline from "./pages/AdminPipeline";
|
||||||
import ModeToggle from "./components/ModeToggle";
|
import AdminDropdown from "./components/AdminDropdown";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -23,11 +23,8 @@ export default function App() {
|
||||||
<Link to="/">Home</Link>
|
<Link to="/">Home</Link>
|
||||||
<Link to="/topics">Topics</Link>
|
<Link to="/topics">Topics</Link>
|
||||||
<Link to="/creators">Creators</Link>
|
<Link to="/creators">Creators</Link>
|
||||||
<Link to="/admin/review">Review</Link>
|
<AdminDropdown />
|
||||||
<Link to="/admin/reports">Reports</Link>
|
|
||||||
<Link to="/admin/pipeline">Pipeline</Link>
|
|
||||||
</nav>
|
</nav>
|
||||||
<ModeToggle />
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
|
||||||
73
frontend/src/components/AdminDropdown.tsx
Normal file
73
frontend/src/components/AdminDropdown.tsx
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
export default function AdminDropdown() {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// Close on outside click
|
||||||
|
useEffect(() => {
|
||||||
|
function handler(e: MouseEvent) {
|
||||||
|
if (
|
||||||
|
dropdownRef.current &&
|
||||||
|
!dropdownRef.current.contains(e.target as Node)
|
||||||
|
) {
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener("mousedown", handler);
|
||||||
|
return () => document.removeEventListener("mousedown", handler);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Close on Escape
|
||||||
|
useEffect(() => {
|
||||||
|
function handler(e: KeyboardEvent) {
|
||||||
|
if (e.key === "Escape") setOpen(false);
|
||||||
|
}
|
||||||
|
if (open) {
|
||||||
|
document.addEventListener("keydown", handler);
|
||||||
|
return () => document.removeEventListener("keydown", handler);
|
||||||
|
}
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="admin-dropdown" ref={dropdownRef}>
|
||||||
|
<button
|
||||||
|
className="admin-dropdown__trigger"
|
||||||
|
onClick={() => setOpen((prev) => !prev)}
|
||||||
|
aria-expanded={open}
|
||||||
|
aria-haspopup="true"
|
||||||
|
>
|
||||||
|
Admin ▾
|
||||||
|
</button>
|
||||||
|
{open && (
|
||||||
|
<div className="admin-dropdown__menu" role="menu">
|
||||||
|
<Link
|
||||||
|
to="/admin/review"
|
||||||
|
className="admin-dropdown__item"
|
||||||
|
role="menuitem"
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
>
|
||||||
|
Review
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to="/admin/reports"
|
||||||
|
className="admin-dropdown__item"
|
||||||
|
role="menuitem"
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
>
|
||||||
|
Reports
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to="/admin/pipeline"
|
||||||
|
className="admin-dropdown__item"
|
||||||
|
role="menuitem"
|
||||||
|
onClick={() => setOpen(false)}
|
||||||
|
>
|
||||||
|
Pipeline
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue