feat(mobile): Implement responsive mobile menu with band context preservation

This commit implements a comprehensive mobile menu solution that:

1. **Mobile Menu Components**:
   - Created TopBar.tsx with circular band switcher (mobile only)
   - Enhanced BottomNavBar.tsx with band-context-aware navigation
   - Updated ResponsiveLayout.tsx to integrate TopBar for mobile views

2. **Band Context Preservation**:
   - Fixed black screen issue by preserving band context via React Router state
   - Implemented dual context detection (URL params + location state)
   - Added graceful fallback handling for missing context

3. **Visual Improvements**:
   - Changed band display from square+text to perfect circle with initials only
   - Updated dropdown items to use consistent circular format
   - Improved mobile space utilization

4. **Debugging & Testing**:
   - Added comprehensive debug logging for issue tracking
   - Created test plans and documentation
   - Ensured all static checks pass (TypeScript + ESLint)

5. **Shared Utilities**:
   - Created utils.ts with shared getInitials() function
   - Reduced code duplication across components

Key Features:
- Mobile (<768px): TopBar + BottomNavBar + Main Content
- Desktop (≥768px): Sidebar (unchanged)
- Band context preserved across all mobile navigation
- Graceful error handling and fallbacks
- Comprehensive debug logging (can be removed in production)

Files Changed:
- web/src/utils.ts (new)
- web/src/components/TopBar.tsx (new)
- web/src/components/BottomNavBar.tsx (modified)
- web/src/components/ResponsiveLayout.tsx (modified)
- web/src/components/Sidebar.tsx (modified)

Documentation Added:
- implementation_summary.md
- refinement_summary.md
- black_screen_fix_summary.md
- test_plan_mobile_menu_fix.md
- test_plan_refinement.md
- testing_guide.md
- black_screen_debug.md

Resolves:
- Mobile menu band context loss
- Black screen on Library navigation
- Inconsistent band display format
- Missing mobile band switching capability

Breaking Changes: None
Backward Compatibility: Fully maintained

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
Mistral Vibe
2026-04-07 13:26:33 +00:00
parent 21c1673fcc
commit 6f0e2636d0
12 changed files with 1497 additions and 17 deletions

View File

@@ -79,10 +79,17 @@ function NavItem({ icon, label, active, onClick, disabled }: NavItemProps) {
export function BottomNavBar() {
const navigate = useNavigate();
const location = useLocation();
// Derive current band from URL
const bandMatch = matchPath("/bands/:bandId/*", location.pathname) ?? matchPath("/bands/:bandId", location.pathname);
const currentBandId = bandMatch?.params?.bandId || location.state?.fromBandId;
// Debug logging for black screen issue
console.log("BottomNavBar - Current band ID:", currentBandId, "Path:", location.pathname, "State:", location.state);
// Derive active states
const isLibrary = !!matchPath("/bands/:bandId", location.pathname);
const isLibrary = !!matchPath("/bands/:bandId", location.pathname) ||
!!matchPath("/bands/:bandId/sessions/:sessionId", location.pathname);
const isPlayer = !!matchPath("/bands/:bandId/songs/:songId", location.pathname);
const isSettings = location.pathname.startsWith("/settings");
@@ -104,26 +111,34 @@ export function BottomNavBar() {
icon={<IconLibrary />}
label="Library"
active={isLibrary}
onClick={() => navigate("/bands")}
onClick={() => {
console.log("Library click - Navigating to band:", currentBandId);
if (currentBandId) {
navigate(`/bands/${currentBandId}`);
} else {
console.warn("Library click - No current band ID found!");
navigate("/bands");
}
}}
/>
<NavItem
icon={<IconPlay />}
label="Player"
active={isPlayer}
onClick={() => {}}
disabled={!isPlayer}
onClick={() => currentBandId ? navigate(`/bands/${currentBandId}/songs`) : {}}
disabled={!currentBandId}
/>
<NavItem
icon={<IconMembers />}
label="Members"
active={false}
onClick={() => navigate("/settings")}
onClick={() => currentBandId ? navigate(`/bands/${currentBandId}/settings/members`) : navigate("/settings", { state: { fromBandId: currentBandId } })}
/>
<NavItem
icon={<IconSettings />}
label="Settings"
active={isSettings}
onClick={() => navigate("/settings")}
onClick={() => currentBandId ? navigate("/settings", { state: { fromBandId: currentBandId } }) : navigate("/settings")}
/>
</nav>
);