mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 07:41:27 +00:00
fix(ui): deep audit pass — palette navigation, crash fix, honest badges
- CommandPalette: full route navigation, project deep-links fetched on open, Create group only where onAddService is wired, dead Connect items removed, decorative kbd hints dropped; workspace passes its own view targets - NodeDetailPage crash: container status arrives as a Docker state object, not a string — flattened in listAgentContainers and guarded at render for persisted-cache payloads - env badge reflects import.meta.env.MODE instead of hardcoded 'production' - Relative times capped: >1y or future timestamps render 'n/a'/'—' (zero-time heartbeats no longer show 739898d ago) - Post-auth and setup redirects land on / (dashboard is the index) Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
24b9b1267e
commit
fc82c30765
9 files changed
+154
-153
No files matched your search
@@ -17,12 +17,12 @@ import { GhostBtn, QuietBtn } from '@/shared/components/sentry';
|
||||
|
||||
function sanitizeRedirect(raw: string | null): string {
|
||||
if (!raw) {
|
||||
return '/projects';
|
||||
return '/';
|
||||
}
|
||||
|
||||
const value = raw.trim();
|
||||
if (!value.startsWith('/') || value.startsWith('//')) {
|
||||
return '/projects';
|
||||
return '/';
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -271,7 +271,7 @@ export function AcceptInvitePage() {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
if (sessionQuery.data) {
|
||||
return <Navigate to="/projects" replace />;
|
||||
return <Navigate to="/" replace />;
|
||||
}
|
||||
|
||||
const boundEmail = inviteQuery.data?.email ?? undefined;
|
||||
@@ -288,7 +288,7 @@ export function AcceptInvitePage() {
|
||||
// account, sign-in creates the browser session.
|
||||
await signInWithEmail(effectiveEmail.trim(), password);
|
||||
await queryClient.invalidateQueries({ queryKey: ['auth-session'] });
|
||||
navigate('/projects', { replace: true });
|
||||
navigate('/', { replace: true });
|
||||
} catch (exception) {
|
||||
setError(exception instanceof Error ? exception.message : 'Failed to accept invite');
|
||||
} finally {
|
||||
|
||||
@@ -148,6 +148,7 @@ type RangeKey = '1h' | '24h' | '7d';
|
||||
function formatRelative(date?: string): string {
|
||||
if (!date) return '—';
|
||||
const diff = Date.now() - new Date(date).getTime();
|
||||
if (diff < 0 || diff > 365 * 86_400_000) return '—';
|
||||
if (diff < 60_000) return 'just now';
|
||||
if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`;
|
||||
if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`;
|
||||
|
||||
@@ -376,7 +376,12 @@ export function NodeDetailPage() {
|
||||
<p className="truncate text-sm text-[var(--text-primary)]">{c.name ?? c.id}</p>
|
||||
<p className="truncate v-mono text-[11px] text-[var(--text-tertiary)]">{c.image}</p>
|
||||
</div>
|
||||
<span className="v-mono text-[11px] text-[var(--text-secondary)]">{c.status ?? '—'}</span>
|
||||
{/* Persisted query cache can replay the raw status object — flatten defensively. */}
|
||||
<span className="v-mono text-[11px] text-[var(--text-secondary)]">
|
||||
{typeof c.status === 'string'
|
||||
? c.status
|
||||
: (c.status as { state?: string } | undefined)?.state ?? '—'}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@ export function SetupWizardPage() {
|
||||
mutationFn: completeSetup,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['setup-status'] });
|
||||
navigate('/projects', { replace: true });
|
||||
navigate('/', { replace: true });
|
||||
},
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@ export function SetupWizardPage() {
|
||||
|
||||
// Wizard is optional once the instance is already configured.
|
||||
if (status && !status.needs_setup && !override) {
|
||||
return <Navigate to="/projects" replace />;
|
||||
return <Navigate to="/" replace />;
|
||||
}
|
||||
|
||||
const steps: { key: Step; label: string }[] = [
|
||||
|
||||
@@ -1124,6 +1124,9 @@ export function ProjectWorkspacePage() {
|
||||
return next;
|
||||
});
|
||||
}}
|
||||
navTargets={viewItems
|
||||
.filter((item) => signedIn || !item.authOnly)
|
||||
.map((item) => ({ label: `Go to ${item.label}`, target: item.key }))}
|
||||
/>
|
||||
|
||||
{/* Error Banner */}
|
||||
|
||||
Reference in new issue
Block a user