diff --git a/ui/src/pages/Admin/ClickHouseAdminPage.module.css b/ui/src/pages/Admin/ClickHouseAdminPage.module.css index 6123c7d3..6c636748 100644 --- a/ui/src/pages/Admin/ClickHouseAdminPage.module.css +++ b/ui/src/pages/Admin/ClickHouseAdminPage.module.css @@ -5,12 +5,8 @@ flex-wrap: wrap; } +/* pipelineCard — card styling via sectionStyles.section */ .pipelineCard { - background: var(--bg-surface); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-card); - padding: 16px 20px; margin-bottom: 16px; } diff --git a/ui/src/pages/Admin/ClickHouseAdminPage.tsx b/ui/src/pages/Admin/ClickHouseAdminPage.tsx index 34e22dd0..f1fd1ade 100644 --- a/ui/src/pages/Admin/ClickHouseAdminPage.tsx +++ b/ui/src/pages/Admin/ClickHouseAdminPage.tsx @@ -2,6 +2,7 @@ import { StatCard, DataTable, ProgressBar } from '@cameleer/design-system'; import type { Column } from '@cameleer/design-system'; import { useClickHouseStatus, useClickHouseTables, useClickHousePerformance, useClickHouseQueries, useIndexerPipeline } from '../../api/queries/admin/clickhouse'; import styles from './ClickHouseAdminPage.module.css'; +import sectionStyles from '../../styles/section-card.module.css'; import tableStyles from '../../styles/table-section.module.css'; export default function ClickHouseAdminPage() { @@ -53,7 +54,7 @@ export default function ClickHouseAdminPage() { {/* Pipeline */} {pipeline && ( -
+
Indexer Pipeline
0 ? (pipeline.queueDepth / pipeline.maxQueueSize) * 100 : 0} />
diff --git a/ui/src/pages/Admin/DatabaseAdminPage.tsx b/ui/src/pages/Admin/DatabaseAdminPage.tsx index 5a02693e..2448b9fc 100644 --- a/ui/src/pages/Admin/DatabaseAdminPage.tsx +++ b/ui/src/pages/Admin/DatabaseAdminPage.tsx @@ -2,6 +2,7 @@ import { StatCard, Card, DataTable, Badge, Button, ProgressBar, Spinner } from ' import type { Column } from '@cameleer/design-system'; import { useDatabaseStatus, useConnectionPool, useDatabaseTables, useActiveQueries, useKillQuery } from '../../api/queries/admin/database'; import styles from './DatabaseAdminPage.module.css'; +import tableStyles from '../../styles/table-section.module.css'; export default function DatabaseAdminPage() { const { data: status, isError: statusError } = useDatabaseStatus(); @@ -54,13 +55,17 @@ export default function DatabaseAdminPage() { )} -
-
Tables
+
+
+ Tables +
({ ...t, id: t.tableName }))} sortable pageSize={20} />
-
-
Active Queries
+
+
+ Active Queries +
({ ...q, id: String(q.pid) }))} />
diff --git a/ui/src/pages/Admin/EnvironmentsPage.tsx b/ui/src/pages/Admin/EnvironmentsPage.tsx index 58aa0974..f3cb475d 100644 --- a/ui/src/pages/Admin/EnvironmentsPage.tsx +++ b/ui/src/pages/Admin/EnvironmentsPage.tsx @@ -25,6 +25,7 @@ import { } from '../../api/queries/admin/environments'; import type { Environment } from '../../api/queries/admin/environments'; import styles from './UserManagement.module.css'; +import sectionStyles from '../../styles/section-card.module.css'; function slugify(name: string): string { return name @@ -263,34 +264,38 @@ export default function EnvironmentsPage() {
- Configuration -
-
- handleToggleProduction(!selected.production)} /> - Production environment - {selected.production ? ( - - ) : ( - - )} +
+ Configuration +
+
+ handleToggleProduction(!selected.production)} /> + Production environment + {selected.production ? ( + + ) : ( + + )} +
- Status -
-
- handleToggleEnabled(!selected.enabled)} /> - {selected.enabled ? 'Enabled' : 'Disabled'} +
+ Status +
+
+ handleToggleEnabled(!selected.enabled)} /> + {selected.enabled ? 'Enabled' : 'Disabled'} + {!selected.enabled && ( + + )} +
{!selected.enabled && ( - +

+ Disabled environments do not allow new deployments. Active + deployments can only be started, stopped, or deleted. +

)}
- {!selected.enabled && ( -

- Disabled environments do not allow new deployments. Active - deployments can only be started, stopped, or deleted. -

- )}
{ @@ -385,7 +390,7 @@ function DefaultResourcesSection({ environment, onSave, saving }: { } return ( - <> +
Default Resource Limits

These defaults apply to new apps in this environment unless overridden per-app. @@ -444,7 +449,7 @@ function DefaultResourcesSection({ environment, onSave, saving }: { )}

- +
); } @@ -478,7 +483,7 @@ function JarRetentionSection({ environment, onSave, saving }: { } return ( - <> +
JAR Retention

Old JAR versions are cleaned up nightly. Currently deployed versions are never deleted. @@ -512,6 +517,6 @@ function JarRetentionSection({ environment, onSave, saving }: { )}

- +
); } diff --git a/ui/src/pages/Admin/GroupsTab.tsx b/ui/src/pages/Admin/GroupsTab.tsx index b826d997..e6a8967d 100644 --- a/ui/src/pages/Admin/GroupsTab.tsx +++ b/ui/src/pages/Admin/GroupsTab.tsx @@ -32,6 +32,7 @@ import { } from '../../api/queries/admin/rbac'; import type { GroupDetail } from '../../api/queries/admin/rbac'; import styles from './UserManagement.module.css'; +import sectionStyles from '../../styles/section-card.module.css'; const BUILTIN_ADMINS_ID = '00000000-0000-0000-0000-000000000010'; @@ -361,69 +362,75 @@ export default function GroupsTab({ highlightId, onHighlightConsumed }: { highli )} - Members (direct) -
- {members.map((u) => ( - handleRemoveMember(u.userId)} +
+ Members (direct) +
+ {members.map((u) => ( + handleRemoveMember(u.userId)} + /> + ))} + {members.length === 0 && ( + (no members) + )} + - ))} - {members.length === 0 && ( - (no members) - )} - -
- {children.length > 0 && ( - - + all members of {children.map((c) => c.name).join(', ')} - - )} - - Child groups -
- {children.map((c) => ( - - ))} - {children.length === 0 && ( +
+ {children.length > 0 && ( - (no child groups) + + all members of {children.map((c) => c.name).join(', ')} )}
- Assigned roles -
- {(selectedGroup.directRoles ?? []).map((r) => ( - { - if (members.length > 0) { - setRemoveRoleTarget(r.id); - } else { - handleRemoveRole(r.id); - } - }} +
+ Child groups +
+ {children.map((c) => ( + + ))} + {children.length === 0 && ( + + (no child groups) + + )} +
+
+ +
+ Assigned roles +
+ {(selectedGroup.directRoles ?? []).map((r) => ( + { + if (members.length > 0) { + setRemoveRoleTarget(r.id); + } else { + handleRemoveRole(r.id); + } + }} + /> + ))} + {(selectedGroup.directRoles ?? []).length === 0 && ( + (no roles) + )} + - ))} - {(selectedGroup.directRoles ?? []).length === 0 && ( - (no roles) - )} - +
) : null diff --git a/ui/src/pages/Admin/UsersTab.tsx b/ui/src/pages/Admin/UsersTab.tsx index 7e7ec05f..50b66890 100644 --- a/ui/src/pages/Admin/UsersTab.tsx +++ b/ui/src/pages/Admin/UsersTab.tsx @@ -35,6 +35,7 @@ import { import type { UserDetail } from '../../api/queries/admin/rbac'; import { useAuthStore } from '../../auth/auth-store'; import styles from './UserManagement.module.css'; +import sectionStyles from '../../styles/section-card.module.css'; export default function UsersTab({ highlightId, onHighlightConsumed }: { highlightId?: string | null; onHighlightConsumed?: () => void }) { const { toast } = useToast(); @@ -366,24 +367,27 @@ export default function UsersTab({ highlightId, onHighlightConsumed }: { highlig
- Status -
- +
+ Status +
+ +
+ +
+ ID + {selected.userId} + Created + + {new Date(selected.createdAt).toLocaleDateString()} + + Provider + {selected.provider} +
-
- ID - {selected.userId} - Created - - {new Date(selected.createdAt).toLocaleDateString()} - - Provider - {selected.provider} -
- - Security -
+
+ Security +
{selected.provider === 'local' ? ( <>
@@ -445,133 +449,138 @@ export default function UsersTab({ highlightId, onHighlightConsumed }: { highlig )} +
- Group membership (direct only) -
- {selected.directGroups.map((g) => ( - { - removeFromGroup.mutate( - { userId: selected.userId, groupId: g.id }, - { - onSuccess: () => - toast({ title: 'Group removed', variant: 'success' }), - onError: () => - toast({ - title: 'Failed to remove group', - variant: 'error', - duration: 86_400_000, - }), - }, - ); - }} - /> - ))} - {selected.directGroups.length === 0 && ( - (no groups) - )} - { - for (const groupId of ids) { - addToGroup.mutate( - { userId: selected.userId, groupId }, - { - onSuccess: () => - toast({ title: 'Added to group', variant: 'success' }), - onError: () => - toast({ - title: 'Failed to add group', - variant: 'error', - duration: 86_400_000, - }), - }, - ); - } - }} - placeholder="+ Add" - /> -
- - - Effective roles (direct + inherited) - -
- {selected.directRoles.map((r) => ( - { - removeRole.mutate( - { userId: selected.userId, roleId: r.id }, - { - onSuccess: () => - toast({ - title: 'Role removed', - description: r.name, - variant: 'success', - }), - onError: () => - toast({ - title: 'Failed to remove role', - variant: 'error', - duration: 86_400_000, - }), - }, - ); - }} - /> - ))} - {inheritedRoles.map((r) => ( - - ))} - {selected.directRoles.length === 0 && - inheritedRoles.length === 0 && ( - (no roles) +
+ Group membership (direct only) +
+ {selected.directGroups.map((g) => ( + { + removeFromGroup.mutate( + { userId: selected.userId, groupId: g.id }, + { + onSuccess: () => + toast({ title: 'Group removed', variant: 'success' }), + onError: () => + toast({ + title: 'Failed to remove group', + variant: 'error', + duration: 86_400_000, + }), + }, + ); + }} + /> + ))} + {selected.directGroups.length === 0 && ( + (no groups) )} - { - for (const roleId of roleIds) { - assignRole.mutate( - { userId: selected.userId, roleId }, - { - onSuccess: () => - toast({ - title: 'Role assigned', - variant: 'success', - }), - onError: () => - toast({ - title: 'Failed to assign role', - variant: 'error', - duration: 86_400_000, - }), - }, - ); - } - }} - placeholder="+ Add" - /> + { + for (const groupId of ids) { + addToGroup.mutate( + { userId: selected.userId, groupId }, + { + onSuccess: () => + toast({ title: 'Added to group', variant: 'success' }), + onError: () => + toast({ + title: 'Failed to add group', + variant: 'error', + duration: 86_400_000, + }), + }, + ); + } + }} + placeholder="+ Add" + /> +
+
+ +
+ + Effective roles (direct + inherited) + +
+ {selected.directRoles.map((r) => ( + { + removeRole.mutate( + { userId: selected.userId, roleId: r.id }, + { + onSuccess: () => + toast({ + title: 'Role removed', + description: r.name, + variant: 'success', + }), + onError: () => + toast({ + title: 'Failed to remove role', + variant: 'error', + duration: 86_400_000, + }), + }, + ); + }} + /> + ))} + {inheritedRoles.map((r) => ( + + ))} + {selected.directRoles.length === 0 && + inheritedRoles.length === 0 && ( + (no roles) + )} + { + for (const roleId of roleIds) { + assignRole.mutate( + { userId: selected.userId, roleId }, + { + onSuccess: () => + toast({ + title: 'Role assigned', + variant: 'success', + }), + onError: () => + toast({ + title: 'Failed to assign role', + variant: 'error', + duration: 86_400_000, + }), + }, + ); + } + }} + placeholder="+ Add" + /> +
+ {inheritedRoles.length > 0 && ( + + Roles with ↑ are inherited through group membership + + )}
- {inheritedRoles.length > 0 && ( - - Roles with ↑ are inherited through group membership - - )} ) : null } diff --git a/ui/src/pages/AgentHealth/AgentHealth.tsx b/ui/src/pages/AgentHealth/AgentHealth.tsx index 2cff185f..2c43c55e 100644 --- a/ui/src/pages/AgentHealth/AgentHealth.tsx +++ b/ui/src/pages/AgentHealth/AgentHealth.tsx @@ -8,6 +8,7 @@ import { } from '@cameleer/design-system'; import type { Column, FeedEvent, LogEntry, ButtonGroupItem } from '@cameleer/design-system'; import styles from './AgentHealth.module.css'; +import sectionStyles from '../../styles/section-card.module.css'; import logStyles from '../../styles/log-panel.module.css'; import { useAgents, useAgentEvents } from '../../api/queries/agents'; import { useApplicationLogs } from '../../api/queries/logs'; @@ -353,7 +354,7 @@ export default function AgentHealth() { {/* Application config bar */} {appId && appConfig && ( -
+
{configEditing ? ( <>
@@ -569,7 +570,7 @@ export default function AgentHealth() { )}
-
+
Timeline
diff --git a/ui/src/pages/AgentInstance/AgentInstance.module.css b/ui/src/pages/AgentInstance/AgentInstance.module.css index f7198378..e437f2b1 100644 --- a/ui/src/pages/AgentInstance/AgentInstance.module.css +++ b/ui/src/pages/AgentInstance/AgentInstance.module.css @@ -44,12 +44,8 @@ font-family: var(--font-mono); } -/* Process info card */ +/* Process info card — card styling via sectionStyles.section */ .processCard { - background: var(--bg-surface); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-card); padding: 16px; margin-bottom: 20px; } @@ -116,12 +112,8 @@ gap: 14px; } -/* Timeline card */ +/* Timeline card — card styling via sectionStyles.section */ .timelineCard { - background: var(--bg-surface); - border: 1px solid var(--border-subtle); - border-radius: var(--radius-lg); - box-shadow: var(--shadow-card); overflow: hidden; display: flex; flex-direction: column; diff --git a/ui/src/pages/AgentInstance/AgentInstance.tsx b/ui/src/pages/AgentInstance/AgentInstance.tsx index f1f6d831..ba583f45 100644 --- a/ui/src/pages/AgentInstance/AgentInstance.tsx +++ b/ui/src/pages/AgentInstance/AgentInstance.tsx @@ -8,6 +8,7 @@ import { } from '@cameleer/design-system'; import type { FeedEvent, LogEntry, ButtonGroupItem } from '@cameleer/design-system'; import styles from './AgentInstance.module.css'; +import sectionStyles from '../../styles/section-card.module.css'; import logStyles from '../../styles/log-panel.module.css'; import chartCardStyles from '../../styles/chart-card.module.css'; import { useAgents, useAgentEvents } from '../../api/queries/agents'; @@ -238,7 +239,7 @@ export default function AgentInstance() {
{/* Process info card */} -
+
Process Information
{agent.capabilities?.jvmVersion && ( @@ -438,7 +439,7 @@ export default function AgentInstance() { )}
-
+
Timeline
diff --git a/ui/src/pages/Dashboard/Dashboard.tsx b/ui/src/pages/Dashboard/Dashboard.tsx index 112cae58..58bbe14f 100644 --- a/ui/src/pages/Dashboard/Dashboard.tsx +++ b/ui/src/pages/Dashboard/Dashboard.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo, useCallback, useEffect } from 'react' +import React, { useState, useMemo, useCallback, useEffect } from 'react' import { useParams, useNavigate, useSearchParams } from 'react-router' import { AlertTriangle, X, Search, Footprints, RotateCcw } from 'lucide-react' import { @@ -113,7 +113,17 @@ function buildBaseColumns(): Column[] { header: 'Exchange ID', sortable: true, render: (_: unknown, row: Row) => ( - {row.executionId} + { + e.stopPropagation(); + navigator.clipboard.writeText(row.executionId); + }} + > + ...{row.executionId.slice(-8)} + ), }, { diff --git a/ui/src/pages/DashboardTab/DashboardL2.tsx b/ui/src/pages/DashboardTab/DashboardL2.tsx index b334b6a3..aef35faa 100644 --- a/ui/src/pages/DashboardTab/DashboardL2.tsx +++ b/ui/src/pages/DashboardTab/DashboardL2.tsx @@ -420,7 +420,7 @@ export default function DashboardL2() { {/* Top 5 Errors — hidden when empty */} {errorRows.length > 0 && ( -
+
Top Errors {errorRows.length} error types diff --git a/ui/src/pages/DashboardTab/DashboardL3.tsx b/ui/src/pages/DashboardTab/DashboardL3.tsx index b3143ddd..47b7f181 100644 --- a/ui/src/pages/DashboardTab/DashboardL3.tsx +++ b/ui/src/pages/DashboardTab/DashboardL3.tsx @@ -392,7 +392,7 @@ export default function DashboardL3() { {/* Process Diagram with Latency Heatmap */} {appId && routeId && ( -
+