fix(ui): admin settings loading-loop, real user name, expanded admin nav
All checks were successful
Build & Push Docker Images / lint (pull_request) Successful in 1m0s
Build & Push Docker Images / build-and-push (pull_request) Has been skipped

SettingsFormCard
- Parent components pass `FIELDS.slice(...)` inline, so the prop reference
  changes on every render. The fetch callback's useCallback re-created
  itself, useEffect re-fired, and loading flicker meant the form never
  rendered. Capture fields in a ref so the callback is stable.

Sidebar
- Show real user name + avatar initial from session/profile, replacing
  the hardcoded "User Name" / "U" placeholder.
- Default the admin-section to expanded so its items are reachable on
  first page load (was collapsed behind a chevron).

Dashboard layout
- Pass {name, email} from the session/profile through to <Sidebar />.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Ciaccio
2026-04-27 23:44:04 +02:00
parent 4877b97f27
commit 0ccc66833d
3 changed files with 26 additions and 8 deletions

View File

@@ -1,6 +1,6 @@
'use client';
import { useCallback, useEffect, useState, type ReactNode } from 'react';
import { useCallback, useEffect, useRef, useState, type ReactNode } from 'react';
import { Loader2 } from 'lucide-react';
import { toast } from 'sonner';
@@ -67,12 +67,19 @@ export function SettingsFormCard({ title, description, fields, extra }: Settings
const [values, setValues] = useState<Record<string, unknown>>({});
const [originals, setOriginals] = useState<Record<string, unknown>>({});
// Parent components often pass `FIELDS.slice(0, 5)` directly, so the prop
// reference changes on every render. Capture it in a ref so the fetch
// callback can read the current list without being re-created and looping
// through useEffect forever.
const fieldsRef = useRef(fields);
fieldsRef.current = fields;
const fetchValues = useCallback(async () => {
setLoading(true);
try {
const res = await apiFetch<ListResponse>('/api/v1/admin/settings');
const next: Record<string, unknown> = {};
for (const field of fields) {
for (const field of fieldsRef.current) {
const port = res.data.portSettings.find((s) => s.key === field.key);
const global = res.data.globalSettings.find((s) => s.key === field.key);
next[field.key] = port?.value ?? global?.value ?? field.defaultValue;
@@ -82,7 +89,7 @@ export function SettingsFormCard({ title, description, fields, extra }: Settings
} finally {
setLoading(false);
}
}, [fields]);
}, []);
useEffect(() => {
void fetchValues();