Files
rehearshalhub/web/src/pages/HomePage.tsx
Mistral Vibe 6dc191585c style: align all pages with CLAUDE.md design system
- Inputs: uniform padding (8px 12px), borderRadius 7, bg-inset background
- List rows/cards: bg-subtle background, border-subtle border (bg-inset was input-only)
- Invite/admin badge borders: use accent-border var instead of raw accent
- Section headers: 11px, weight 500, uppercase, 0.7px letter-spacing
- Notification/status banners: borderRadius 8
- Remove debug console.log statements from SettingsPage avatar upload flow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:52:06 +02:00

127 lines
4.1 KiB
TypeScript

import { useState } from "react";
import { useNavigate, Navigate } from "react-router-dom";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { listBands, createBand } from "../api/bands";
export function HomePage() {
const navigate = useNavigate();
const qc = useQueryClient();
const [name, setName] = useState("");
const [slug, setSlug] = useState("");
const [ncBasePath, setNcBasePath] = useState("");
const [error, setError] = useState<string | null>(null);
const { data: bands, isLoading } = useQuery({
queryKey: ["bands"],
queryFn: listBands,
});
const createMutation = useMutation({
mutationFn: () => createBand({ name, slug, ...(ncBasePath ? { nc_base_path: ncBasePath } : {}) }),
onSuccess: (band) => {
qc.invalidateQueries({ queryKey: ["bands"] });
navigate(`/bands/${band.id}`);
},
onError: (err) => setError(err instanceof Error ? err.message : "Failed to create band"),
});
// Redirect to the first band once bands are loaded
if (!isLoading && bands && bands.length > 0) {
return <Navigate to={`/bands/${bands[0].id}`} replace />;
}
const inputStyle: React.CSSProperties = {
width: "100%",
padding: "8px 12px",
background: "var(--bg-inset)",
border: "1px solid var(--border)",
borderRadius: 7,
color: "var(--text)",
marginBottom: 12,
fontSize: 14,
};
const labelStyle: React.CSSProperties = {
display: "block",
color: "var(--text-muted)",
fontSize: 11,
marginBottom: 6,
};
return (
<div style={{ padding: 32, maxWidth: 480, margin: "0 auto" }}>
{isLoading ? (
<p style={{ color: "var(--text-muted)" }}>Loading</p>
) : (
<>
<h2 style={{ color: "var(--text)", margin: "0 0 8px", fontSize: 17, fontWeight: 500 }}>
Create your first band
</h2>
<p style={{ color: "var(--text-muted)", fontSize: 12, margin: "0 0 24px", lineHeight: 1.6 }}>
Give your band a name to get started. You can add members and connect storage after.
</p>
{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={inputStyle}
autoFocus
/>
<label style={labelStyle}>SLUG</label>
<input
value={slug}
onChange={(e) => setSlug(e.target.value)}
style={{ ...inputStyle, fontFamily: "monospace", marginBottom: 16 }}
/>
<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={{ ...inputStyle, fontSize: 13, fontFamily: "monospace", marginBottom: 4 }}
/>
<p style={{ color: "var(--text-subtle)", fontSize: 11, margin: "0 0 20px" }}>
Path relative to your Nextcloud root. Leave blank to use{" "}
<code style={{ color: "var(--text-muted)" }}>bands/{slug || "slug"}/</code>
</p>
<button
onClick={() => createMutation.mutate()}
disabled={!name || !slug || createMutation.isPending}
style={{
background: "var(--accent)",
border: "none",
borderRadius: 6,
color: "var(--accent-fg)",
cursor: "pointer",
padding: "9px 22px",
fontWeight: 600,
fontSize: 13,
}}
>
{createMutation.isPending ? "Creating…" : "Create Band"}
</button>
</>
)}
</div>
);
}