feat(frontend-admin): Update admin dashboard components to enhance UI and add new functionality for EmailDashboard, FlagListPage, and ServiceDiagramPage

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-18 14:33:29 -07:00
parent 7426d882ff
commit 79d2cfeabe
3 changed files with 55 additions and 30 deletions

View file

@ -17,32 +17,42 @@ export const EmailDashboard = () => {
resumeQueue.mutate()
}
const pageHeader = (
<div>
<h1 className="text-2xl font-bold text-gray-900">Email Dashboard</h1>
<p className="mt-1 text-sm text-gray-500">
Monitor email delivery and queue status
</p>
</div>
)
if (isLoading) {
return (
<div className="flex items-center justify-center h-64">
<div className="text-gray-500">Loading email statistics...</div>
<div className="space-y-6">
{pageHeader}
<div className="flex items-center justify-center h-64">
<div className="text-gray-500">Loading email statistics...</div>
</div>
</div>
)
}
if (error) {
if (error || !stats) {
return (
<div className="flex items-center justify-center h-64">
<div className="text-red-600">Failed to load email statistics</div>
<div className="space-y-6">
{pageHeader}
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
<div className="text-red-600">
{error ? `Failed to load email statistics: ${(error as Error).message}` : 'No statistics available'}
</div>
</div>
</div>
)
}
if (!stats) {return null}
return (
<div className="space-y-6">
<div>
<h1 className="text-2xl font-bold text-gray-900">Email Dashboard</h1>
<p className="mt-1 text-sm text-gray-500">
Monitor email delivery and queue status
</p>
</div>
{pageHeader}
{/* Queue Status Card */}
<div className="bg-white shadow rounded-lg p-6">

View file

@ -8,18 +8,33 @@ export const FlagListPage = () => {
const [search, setSearch] = useState('');
const [tagFilter, setTagFilter] = useState<string | null>(null);
const pageHeader = (
<div className="mb-6">
<h1 className="text-2xl font-bold text-white">Feature Flags</h1>
<p className="mt-1 text-slate-400">
Manage feature flags to control feature rollout across the platform.
</p>
</div>
);
if (isLoading) {
return (
<div className="flex items-center justify-center h-64">
<div className="text-slate-400">Loading flags...</div>
<div>
{pageHeader}
<div className="flex items-center justify-center h-64">
<div className="text-slate-400">Loading flags...</div>
</div>
</div>
);
}
if (error) {
return (
<div className="bg-red-900/20 border border-red-700 rounded-lg p-4 text-red-300">
Failed to load flags: {error.message}
<div>
{pageHeader}
<div className="bg-red-900/20 border border-red-700 rounded-lg p-4 text-red-300">
Failed to load flags: {error.message}
</div>
</div>
);
}
@ -39,12 +54,7 @@ export const FlagListPage = () => {
return (
<div>
<div className="mb-6">
<h1 className="text-2xl font-bold text-white">Feature Flags</h1>
<p className="mt-1 text-slate-400">
Manage feature flags to control feature rollout across the platform.
</p>
</div>
{pageHeader}
<div className="mb-6 flex flex-wrap gap-4">
<input

View file

@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useMemo } from 'react';
import {
ReactFlow,
@ -10,6 +10,10 @@ import {
import '@xyflow/react/dist/style.css';
import { ServiceNode, GroupNode, useServiceLayout } from '@lilith/ui-diagram/flow';
import type { VisualizationEdge } from '@lilith/ui-diagram';
// Stable empty edges array — must not be inline `[]` (new ref each render triggers infinite loop)
const EMPTY_EDGES: VisualizationEdge[] = [];
import { DiagramControls } from './service-diagram/components/DiagramControls';
import { DiagramLegend } from './service-diagram/components/DiagramLegend';
@ -32,13 +36,14 @@ export const ServiceDiagramPage = () => {
const { statuses, lastUpdate, refreshNow } = useServiceStatus(isPolling);
// Filter features based on selection
const filteredFeatures = selectedFeature
? features.filter((f) => f.id === selectedFeature)
: features;
// Memoize filtered features to stabilize identity across renders
const filteredFeatures = useMemo(
() => (selectedFeature ? features.filter((f) => f.id === selectedFeature) : features),
[selectedFeature, features]
);
// Compute layout - edges are empty for now until edge data is available
const { nodes: layoutNodes, edges: layoutEdges } = useServiceLayout(filteredFeatures, statuses, []);
// Compute layout - EMPTY_EDGES used as stable reference (inline [] causes infinite re-render)
const { nodes: layoutNodes, edges: layoutEdges } = useServiceLayout(filteredFeatures, statuses, EMPTY_EDGES);
const [nodes, setNodes, onNodesChange] = useNodesState(layoutNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(layoutEdges);