import { useState } from "react"; import { useNavigate } from "react-router-dom"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { api } from "../api/client"; interface MemberRead { id: string; display_name: string; email: string; avatar_url: string | null; nc_username: string | null; nc_url: string | null; nc_configured: boolean; } const getMe = () => api.get("/auth/me"); const updateSettings = (data: { display_name?: string; nc_url?: string; nc_username?: string; nc_password?: string; }) => api.patch("/auth/me/settings", data); const inputStyle: React.CSSProperties = { width: "100%", padding: "8px 12px", background: "var(--bg-inset)", border: "1px solid var(--border)", borderRadius: 6, color: "var(--text)", fontSize: 14, boxSizing: "border-box", }; function SettingsForm({ me, onBack }: { me: MemberRead; onBack: () => void }) { const qc = useQueryClient(); const [displayName, setDisplayName] = useState(me.display_name ?? ""); const [ncUrl, setNcUrl] = useState(me.nc_url ?? ""); const [ncUsername, setNcUsername] = useState(me.nc_username ?? ""); const [ncPassword, setNcPassword] = useState(""); const [avatarUrl, setAvatarUrl] = useState(me.avatar_url ?? ""); const [saved, setSaved] = useState(false); const [error, setError] = useState(null); const saveMutation = useMutation({ mutationFn: () => updateSettings({ display_name: displayName || undefined, nc_url: ncUrl || undefined, nc_username: ncUsername || undefined, nc_password: ncPassword || undefined, avatar_url: avatarUrl || undefined, }), onSuccess: () => { qc.invalidateQueries({ queryKey: ["me"] }); setSaved(true); setNcPassword(""); setError(null); setTimeout(() => setSaved(false), 3000); }, onError: (err) => setError(err instanceof Error ? err.message : "Save failed"), }); const labelStyle: React.CSSProperties = { display: "block", color: "var(--text-muted)", fontSize: 11, marginBottom: 6, }; return ( <>

PROFILE

{avatarUrl && ( Profile )}
setDisplayName(e.target.value)} style={inputStyle} />

{me.email}

NEXTCLOUD CONNECTION

Configure your personal Nextcloud credentials. When set, all file operations (band folders, song uploads, scans) will use these credentials.

{me.nc_configured ? "Connected" : "Not configured"}
setNcUrl(e.target.value)} placeholder="https://cloud.example.com" style={inputStyle} /> setNcUsername(e.target.value)} style={inputStyle} /> setNcPassword(e.target.value)} placeholder={me.nc_configured ? "•••••••• (leave blank to keep existing)" : ""} style={inputStyle} />

Use an app password from Nextcloud Settings → Security for better security.

AVATAR

{ const file = e.target.files?.[0]; if (file) { // In a real app, you would upload the file to a server // and get a URL back. For now, we'll use a placeholder. const reader = new FileReader(); reader.onload = (event) => { // This is a simplified approach - in production you'd upload to server setAvatarUrl(event.target?.result as string); }; reader.readAsDataURL(file); } }} style={{ display: "none" }} id="avatar-upload" />
{avatarUrl && (
Preview
)}
{error &&

{error}

} {saved &&

Settings saved.

}
); } export function SettingsPage() { const navigate = useNavigate(); const { data: me, isLoading } = useQuery({ queryKey: ["me"], queryFn: getMe }); return (

Settings

{isLoading &&

Loading...

} {me && navigate("/")} />}
); }