feat: band NC folder config, fix watcher event filter, add light/dark theme

- Add PATCH /bands/{id} endpoint for admins to update nc_folder_path
- Add band NC scan folder UI panel with inline edit
- Fix watcher: use activity type field (not human-readable subject) for upload detection
- Reorder watcher filters: audio extension check first, then band path, then type
- Add dark/light theme toggle using GitHub Primer-inspired CSS custom properties
- All inline styles migrated to CSS variables for theme-awareness

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Steffen Schuhmann
2026-03-29 00:29:58 +01:00
parent 5536bf4394
commit fbac62a0ea
13 changed files with 419 additions and 211 deletions

View File

@@ -33,21 +33,32 @@ export function HomePage() {
navigate("/login");
}
const inputStyle: React.CSSProperties = {
width: "100%", padding: "8px 12px",
background: "var(--bg-inset)",
border: "1px solid var(--border)",
borderRadius: 6, color: "var(--text)",
marginBottom: 12, fontSize: 14, boxSizing: "border-box",
};
const labelStyle: React.CSSProperties = {
display: "block", color: "var(--text-muted)", fontSize: 11, marginBottom: 6,
};
return (
<div style={{ background: "#080A0E", minHeight: "100vh", color: "#E2E6F0", padding: 24 }}>
<div style={{ background: "var(--bg)", minHeight: "100vh", color: "var(--text)", padding: 24 }}>
<div style={{ maxWidth: 720, margin: "0 auto" }}>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 32 }}>
<h1 style={{ color: "#F0A840", fontFamily: "monospace", margin: 0 }}> RehearsalHub</h1>
<h1 style={{ color: "var(--accent)", fontFamily: "monospace", margin: 0 }}> RehearsalHub</h1>
<div style={{ display: "flex", gap: 8 }}>
<button
onClick={() => navigate("/settings")}
style={{ background: "none", border: "1px solid #1C2235", borderRadius: 6, color: "#5A6480", cursor: "pointer", padding: "6px 14px", fontSize: 12 }}
style={{ background: "none", border: "1px solid var(--border)", borderRadius: 6, color: "var(--text-muted)", cursor: "pointer", padding: "6px 14px", fontSize: 12 }}
>
Settings
</button>
<button
onClick={handleSignOut}
style={{ background: "none", border: "1px solid #1C2235", borderRadius: 6, color: "#5A6480", cursor: "pointer", padding: "6px 14px", fontSize: 12 }}
style={{ background: "none", border: "1px solid var(--border)", borderRadius: 6, color: "var(--text-muted)", cursor: "pointer", padding: "6px 14px", fontSize: 12 }}
>
Sign Out
</button>
@@ -55,56 +66,56 @@ export function HomePage() {
</div>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 }}>
<h2 style={{ color: "#E2E6F0", margin: 0, fontSize: 16 }}>Your Bands</h2>
<h2 style={{ color: "var(--text)", margin: 0, fontSize: 16 }}>Your Bands</h2>
<button
onClick={() => setShowCreate(!showCreate)}
style={{ background: "#F0A840", border: "none", borderRadius: 6, color: "#080A0E", cursor: "pointer", padding: "6px 14px", fontSize: 12, fontWeight: 600 }}
style={{ background: "var(--accent)", border: "none", borderRadius: 6, color: "var(--accent-fg)", cursor: "pointer", padding: "6px 14px", fontSize: 12, fontWeight: 600 }}
>
+ New Band
</button>
</div>
{showCreate && (
<div style={{ background: "#0E1118", border: "1px solid #1C2235", borderRadius: 8, padding: 20, marginBottom: 20 }}>
{error && <p style={{ color: "#E85878", fontSize: 13, marginBottom: 12 }}>{error}</p>}
<label style={{ display: "block", color: "#5A6480", fontSize: 11, marginBottom: 6 }}>BAND NAME</label>
<div style={{ background: "var(--bg-subtle)", border: "1px solid var(--border)", borderRadius: 8, padding: 20, marginBottom: 20 }}>
{error && <p style={{ color: "var(--danger)", fontSize: 13, marginBottom: 12 }}>{error}</p>}
<label style={labelStyle}>BAND NAME</label>
<input
value={name}
onChange={(e) => {
setName(e.target.value);
setSlug(e.target.value.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, ""));
}}
style={{ width: "100%", padding: "8px 12px", background: "#131720", border: "1px solid #1C2235", borderRadius: 6, color: "#E2E6F0", marginBottom: 12, fontSize: 14, boxSizing: "border-box" }}
style={inputStyle}
/>
<label style={{ display: "block", color: "#5A6480", fontSize: 11, marginBottom: 6 }}>SLUG</label>
<label style={labelStyle}>SLUG</label>
<input
value={slug}
onChange={(e) => setSlug(e.target.value)}
style={{ width: "100%", padding: "8px 12px", background: "#131720", border: "1px solid #1C2235", borderRadius: 6, color: "#E2E6F0", marginBottom: 16, fontSize: 14, fontFamily: "monospace", boxSizing: "border-box" }}
style={{ ...inputStyle, fontFamily: "monospace", marginBottom: 16 }}
/>
<label style={{ display: "block", color: "#5A6480", fontSize: 11, marginBottom: 6 }}>
NEXTCLOUD BASE FOLDER <span style={{ color: "#38496A" }}>(optional)</span>
<label style={labelStyle}>
NEXTCLOUD BASE FOLDER <span style={{ color: "var(--text-subtle)" }}>(optional)</span>
</label>
<input
value={ncBasePath}
onChange={(e) => setNcBasePath(e.target.value)}
placeholder={`bands/${slug || "my-band"}/`}
style={{ width: "100%", padding: "8px 12px", background: "#131720", border: "1px solid #1C2235", borderRadius: 6, color: "#E2E6F0", marginBottom: 4, fontSize: 13, fontFamily: "monospace", boxSizing: "border-box" }}
style={{ ...inputStyle, fontSize: 13, fontFamily: "monospace", marginBottom: 4 }}
/>
<p style={{ color: "#38496A", fontSize: 11, margin: "0 0 16px" }}>
Path relative to your Nextcloud root. Leave blank to use <code style={{ color: "#5A6480" }}>bands/{slug || "slug"}/</code>
<p style={{ color: "var(--text-subtle)", fontSize: 11, margin: "0 0 16px" }}>
Path relative to your Nextcloud root. Leave blank to use <code style={{ color: "var(--text-muted)" }}>bands/{slug || "slug"}/</code>
</p>
<div style={{ display: "flex", gap: 8 }}>
<button
onClick={() => createMutation.mutate()}
disabled={!name || !slug}
style={{ background: "#F0A840", border: "none", borderRadius: 6, color: "#080A0E", cursor: "pointer", padding: "8px 18px", fontWeight: 600, fontSize: 13 }}
style={{ background: "var(--accent)", border: "none", borderRadius: 6, color: "var(--accent-fg)", cursor: "pointer", padding: "8px 18px", fontWeight: 600, fontSize: 13 }}
>
Create
</button>
<button
onClick={() => { setShowCreate(false); setError(null); setNcBasePath(""); }}
style={{ background: "none", border: "1px solid #1C2235", borderRadius: 6, color: "#5A6480", cursor: "pointer", padding: "8px 18px", fontSize: 13 }}
style={{ background: "none", border: "1px solid var(--border)", borderRadius: 6, color: "var(--text-muted)", cursor: "pointer", padding: "8px 18px", fontSize: 13 }}
>
Cancel
</button>
@@ -112,28 +123,28 @@ export function HomePage() {
</div>
)}
{isLoading && <p style={{ color: "#5A6480" }}>Loading...</p>}
{isLoading && <p style={{ color: "var(--text-muted)" }}>Loading...</p>}
<div style={{ display: "grid", gap: 8 }}>
{bands?.map((band) => (
<button
key={band.id}
onClick={() => navigate(`/bands/${band.id}`)}
style={{ background: "#0E1118", border: "1px solid #1C2235", borderRadius: 8, padding: 16, textAlign: "left", cursor: "pointer", color: "#E2E6F0" }}
style={{ background: "var(--bg-subtle)", border: "1px solid var(--border)", borderRadius: 8, padding: 16, textAlign: "left", cursor: "pointer", color: "var(--text)" }}
>
<div style={{ fontWeight: 600, marginBottom: 4 }}>{band.name}</div>
<div style={{ fontSize: 11, color: "#5A6480", fontFamily: "monospace" }}>{band.slug}</div>
<div style={{ fontSize: 11, color: "var(--text-muted)", fontFamily: "monospace" }}>{band.slug}</div>
{band.genre_tags.length > 0 && (
<div style={{ marginTop: 8, display: "flex", gap: 4 }}>
{band.genre_tags.map((t) => (
<span key={t} style={{ background: "#0A2820", color: "#38C9A8", fontSize: 10, padding: "2px 6px", borderRadius: 3, fontFamily: "monospace" }}>{t}</span>
<span key={t} style={{ background: "var(--teal-bg)", color: "var(--teal)", fontSize: 10, padding: "2px 6px", borderRadius: 3, fontFamily: "monospace" }}>{t}</span>
))}
</div>
)}
</button>
))}
{bands?.length === 0 && (
<p style={{ color: "#5A6480", fontSize: 13 }}>No bands yet. Create one to get started.</p>
<p style={{ color: "var(--text-muted)", fontSize: 13 }}>No bands yet. Create one to get started.</p>
)}
</div>
</div>