import { useRef, useState, useEffect } from "react"; import { useNavigate, useLocation, matchPath } from "react-router-dom"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { listBands, createBand } from "../api/bands"; import { getInitials } from "../utils"; import { useBandStore } from "../stores/bandStore"; // ── Shared primitives ────────────────────────────────────────────────────────── const inputStyle: React.CSSProperties = { width: "100%", padding: "8px 11px", background: "rgba(255,255,255,0.04)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 7, color: "#e8e9f0", fontSize: 13, fontFamily: "inherit", outline: "none", boxSizing: "border-box", }; const labelStyle: React.CSSProperties = { display: "block", fontSize: 10, fontWeight: 600, letterSpacing: "0.06em", color: "rgba(232,233,240,0.4)", marginBottom: 5, }; // ── Error banner ─────────────────────────────────────────────────────────────── function ErrorBanner({ msg }: { msg: string }) { return (

{msg}

); } // ── Band creation form ───────────────────────────────────────────────────────── function BandStep({ onClose }: { onClose: () => void }) { const navigate = useNavigate(); const qc = useQueryClient(); const [name, setName] = useState(""); const [slug, setSlug] = useState(""); const [error, setError] = useState(null); const nameRef = useRef(null); useEffect(() => { nameRef.current?.focus(); }, []); const mutation = useMutation({ mutationFn: () => createBand({ name, slug }), onSuccess: (band) => { qc.invalidateQueries({ queryKey: ["bands"] }); onClose(); navigate(`/bands/${band.id}`); }, onError: (err) => setError(err instanceof Error ? err.message : "Failed to create band"), }); const handleNameChange = (v: string) => { setName(v); setSlug(v.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "")); }; return ( <> {error && }
handleNameChange(e.target.value)} style={inputStyle} placeholder="e.g. The Midnight Trio" onKeyDown={(e) => { if (e.key === "Enter" && name && slug) mutation.mutate(); }} />
setSlug(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ""))} style={{ ...inputStyle, fontFamily: "monospace" }} placeholder="the-midnight-trio" />

Connect storage after creating the band via Settings → Storage.

); } // ── Create Band Modal ────────────────────────────────────────────────────────── function CreateBandModal({ onClose }: { onClose: () => void }) { // Close on Escape useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", handler); return () => document.removeEventListener("keydown", handler); }, [onClose]); return (
e.stopPropagation()} style={{ background: "#112018", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 14, padding: 28, width: 420, boxShadow: "0 24px 64px rgba(0,0,0,0.6)" }} >

New band

Create a workspace for your recordings.

); } // ── TopBandBar ───────────────────────────────────────────────────────────────── export function TopBandBar() { const navigate = useNavigate(); const location = useLocation(); const [open, setOpen] = useState(false); const [showCreate, setShowCreate] = useState(false); const ref = useRef(null); const { data: bands } = useQuery({ queryKey: ["bands"], queryFn: listBands }); const { activeBandId, setActiveBandId } = useBandStore(); // Sync store from URL when on a band page const urlMatch = matchPath("/bands/:bandId/*", location.pathname) ?? matchPath("/bands/:bandId", location.pathname); const urlBandId = urlMatch?.params?.bandId ?? null; useEffect(() => { if (urlBandId) setActiveBandId(urlBandId); }, [urlBandId, setActiveBandId]); const currentBandId = urlBandId ?? activeBandId; const activeBand = bands?.find((b) => b.id === currentBandId) ?? null; // Close dropdown on outside click useEffect(() => { if (!open) return; const handler = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); }; document.addEventListener("mousedown", handler); return () => document.removeEventListener("mousedown", handler); }, [open]); const border = "rgba(255,255,255,0.06)"; return ( <> {showCreate && setShowCreate(false)} />}
{/* Band switcher */}
{open && (
{bands?.map((band) => ( ))}
)}
); }