DrumFlow is a single-page React application built with a component-based architecture, using React Hooks for state management and localStorage for data persistence.
- Centralized State: All global state lives in
App.js - Props Down, Callbacks Up: Data flows down via props, events bubble up via callbacks
- Custom Hooks: Business logic encapsulated in reusable hooks
- Local Storage: Automatic persistence via
useEffect
App.js (State Container)
├── Header (Navigation + Stats)
├── HomePage (Dashboard)
├── LibraryPage (Lessons Browser)
├── SongsPage (Song Progression)
└── SessionFlow (Practice Session)
┌─────────────────────────────────────────┐
│ App.js (State) │
│ ┌──────────────────────────────────┐ │
│ │ songs, pinnedItems, excludedItems│ │
│ │ stats, sessionType, currentScreen│ │
│ └──────────────────────────────────┘ │
└────────┬────────────────────────────────┘
│
├──> Header (read stats)
│
├──> HomePage (read routine, trigger actions)
│
├──> LibraryPage (read/write pins & excludes)
│
├──> SongsPage (read/write songs)
│
└──> SessionFlow (read routine, trigger completion)
| Key | Description | Structure |
|---|---|---|
drumflow_songs |
Song progression & status | Array of song objects |
drumflow_pinned |
Pinned lesson IDs | Array of strings |
drumflow_excluded |
Excluded lesson IDs | Array of strings |
drumflow_stats |
User statistics | { streak, sessions, lastPracticeDate } |
drumflow_todays_routine |
Today's generated routine | { date, routine: { warmup, groove, song } } |
Responsibilities:
- Global state management
- localStorage persistence
- Screen navigation routing
- Event handling orchestration
State:
{
currentScreen: 'home' | 'library' | 'songs' | 'session',
songs: Song[],
pinnedItems: string[],
excludedItems: string[],
stats: { streak, sessions, lastPracticeDate },
sessionType: '20min' | '10min',
sessionStep: number,
isInSession: boolean
}Props:
{
currentScreen: string,
onNavigate: (screen) => void,
stats: StatsObject,
songs: Song[]
}Responsibilities:
- Navigation tabs
- Display user stats (streak, sessions, songs)
- Highlight active screen
Props:
{
routine: RoutineObject,
onGenerateRoutine: () => void,
onStartSession: (type) => void,
songs: Song[],
onMarkComplete: (songId) => void
}Features:
- Session start buttons (20min, 10min)
- Today's routine card
- Regenerate routine
- Current & goal song display
- Mark song complete
Props:
{
onBack: () => void,
pinnedItems: string[],
excludedItems: string[],
onTogglePin: (id) => void,
onToggleExclude: (id) => void
}Features:
- 3 tabs: Warmups, Grooves & Skills, Practice Lessons
- Pin/Exclude functionality
- Video links to YouTube
- Filter by lesson type
Props:
{
onBack: () => void,
songs: Song[],
onMarkComplete: (songId) => void,
onToggleHidden: (songId) => void
}Features:
- Search songs by title/difficulty
- Toggle show/hide hidden songs
- Sort by level or difficulty
- Mark current song complete
- Hide/show songs
- Links to tutorials, play-alongs, sheet music
Props:
{
sessionType: '20min' | '10min',
currentStep: number,
routine: RoutineObject,
onNext: () => void,
onExit: () => void
}Features:
- Step-by-step practice flow
- Progress circle indicator
- Video/song links for each step
- Continue/Complete buttons
- Exit to home
Location: src/hooks/useRoutineGenerator.js
Purpose: Generate daily practice routines with smart rotation
Algorithm:
IF no pinned items:
Rotate through all non-excluded items (day-of-year % available.length)
ELSE IF 1 pinned item:
Always use that item
ELSE IF 2+ pinned items:
Rotate only through pinned items (day-of-year % pinned.length)
Returns:
{
routine: { warmup, groove, song },
generateRoutine: () => void
}Dependencies:
- Regenerates when
pinnedItemsorexcludedItemschange - Saves to localStorage with today's date
{
id: string,
level: number,
title: string,
difficulty: string,
tutorialUrl: string,
playalongUrl: string,
sheetMusic: string,
tempo: string,
status: 'planned' | 'current' | 'completed' | 'goal',
hidden?: boolean
}{
id: string,
title: string,
url: string,
duration: string,
level?: string,
description: string,
lessonType: 'warmup' | 'groove' | 'practice'
}{
warmup: Lesson,
groove: Lesson,
song: Song
}{
streak: number,
sessions: number,
lastPracticeDate: string | null
}1. User clicks "Start Your Session (20min)" on HomePage
2. App.js sets sessionType='20min', isInSession=true, currentScreen='session'
3. SessionFlow renders with routine from useRoutineGenerator
4. User clicks through steps (warmup → groove → song)
5. On "Complete Session", App.js updates stats and returns to home
1. User clicks "Mark Complete" on HomePage
2. App.js calls handleMarkSongComplete(songId)
3. Song status changes to 'completed'
4. Next non-hidden, non-goal song becomes 'current'
5. State persists to localStorage
6. UI updates to show new current song
1. User clicks Pin icon in LibraryPage
2. LibraryPage calls onTogglePin(lessonId)
3. App.js updates pinnedItems array
4. useRoutineGenerator detects change
5. New routine generated respecting pins
6. HomePage shows updated routine
Pros:
- Zero backend complexity
- Works offline
- Fast and responsive
- Private by default
- No hosting costs
Cons:
- Data lost if browser cache cleared
- No cross-device sync
- Limited to ~10MB storage
Decision: localStorage is perfect for MVP and solo users. Future versions can add optional cloud sync.
Reasoning:
- Simple state-based navigation is sufficient
- Fewer dependencies
- No URL management needed (single-page feel)
- Easy to reason about
Trade-off: URLs don't reflect current screen. Acceptable for this use case.
Benefits:
- Separates business logic from UI
- Reusable if needed elsewhere
- Easy to test
- Encapsulates localStorage logic
Benefits:
- Single source of truth
- Easy to debug
- Clear data flow
- Simple persistence strategy
Alternative Considered: Context API - overkill for this scale
- Routine generates correctly
- Pins/excludes affect routine
- Sessions complete successfully
- Streak increments properly
- Song progression works
- localStorage persists data
- All external links work
- Unit tests for useRoutineGenerator
- Integration tests for state management
- E2E tests for critical user flows
- Minimal re-renders (proper React.memo usage if needed)
- localStorage reads/writes batched in useEffect
- No expensive computations in render
- Tailwind CSS via CDN (no build needed)
- Lucide React icons (tree-shakeable)
- No heavy dependencies
- No authentication: Not needed for MVP
- Local-only data: Nothing sent to servers
- External links: All open in new tabs with
rel="noopener noreferrer" - No tracking: No analytics or user tracking
- Add React Context for deeply nested props
- Implement React Router if multi-page needed
- Consider Redux if state becomes complex
- User accounts: Need backend + authentication
- Cloud sync: Need database + API
- Real-time features: Need WebSockets
- Mobile app: Need React Native refactor
// 1. Imports
import React from 'react';
import { Icon } from 'lucide-react';
// 2. Component function with JSDoc
/**
* ComponentName description
* @param {Object} props - Props description
*/
const ComponentName = ({ prop1, prop2 }) => {
// 3. State and hooks
const [state, setState] = useState(initial);
// 4. Event handlers
const handleEvent = () => {
// logic
};
// 5. Render
return (
<div>
{/* JSX */}
</div>
);
};
// 6. Export
export default ComponentName;- Components: PascalCase (HomePage.jsx)
- Hooks: camelCase with 'use' prefix (useRoutineGenerator.js)
- Functions: camelCase (handleStartSession)
- Constants: UPPER_SNAKE_CASE (MAX_SESSIONS)
- Files: Match component name
This architecture document should be updated as the system evolves.