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:
parent
7426d882ff
commit
79d2cfeabe
3 changed files with 55 additions and 30 deletions
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue