diff --git a/ui/src/components/EnvironmentSelector.module.css b/ui/src/components/EnvironmentSelector.module.css index 99c4fd7a..2d3518ac 100644 --- a/ui/src/components/EnvironmentSelector.module.css +++ b/ui/src/components/EnvironmentSelector.module.css @@ -1,24 +1,4 @@ +/* Layout wrapper — DS Select handles its own appearance */ .select { - appearance: none; - background: transparent; - border: none; - padding: 0 14px 0 0; - margin: 0; - font: inherit; - color: inherit; - text-transform: inherit; - letter-spacing: inherit; - cursor: pointer; - outline: none; - line-height: 1; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 8 5'%3E%3Cpath d='M1 1l3 3 3-3' stroke='currentColor' fill='none' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 0 center; - background-size: 8px 5px; -} - -.select:focus-visible { - outline: 1px solid var(--amber); - outline-offset: 2px; - border-radius: 2px; + min-width: 100px; } diff --git a/ui/src/components/EnvironmentSelector.tsx b/ui/src/components/EnvironmentSelector.tsx index 5817445a..2230e654 100644 --- a/ui/src/components/EnvironmentSelector.tsx +++ b/ui/src/components/EnvironmentSelector.tsx @@ -1,3 +1,5 @@ +import { useMemo } from 'react'; +import { Select } from '@cameleer/design-system'; import styles from './EnvironmentSelector.module.css'; interface EnvironmentSelectorProps { @@ -9,17 +11,20 @@ interface EnvironmentSelectorProps { export function EnvironmentSelector({ environments, value, onChange }: EnvironmentSelectorProps) { if (environments.length === 0) return null; + const options = useMemo( + () => [ + { value: '', label: 'All Envs' }, + ...environments.map((env) => ({ value: env, label: env })), + ], + [environments], + ); + return ( - + /> ); } diff --git a/ui/src/components/ExecutionDiagram/tabs/LogTab.module.css b/ui/src/components/ExecutionDiagram/tabs/LogTab.module.css index d9326afe..2cf20e2a 100644 --- a/ui/src/components/ExecutionDiagram/tabs/LogTab.module.css +++ b/ui/src/components/ExecutionDiagram/tabs/LogTab.module.css @@ -15,69 +15,13 @@ .filterInput { width: 100%; - padding: 4px 8px; font-size: 12px; - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - background: var(--bg-surface); - color: var(--text-primary); - outline: none; - font-family: var(--font-body); } .logList { flex: 1; overflow-y: auto; font-size: 12px; - font-family: var(--font-mono); -} - -.logTable { - width: 100%; - border-collapse: collapse; -} - -.logRow { - border-bottom: 1px solid var(--border-subtle); -} - -.timestampCell { - padding: 3px 6px; - white-space: nowrap; - color: var(--text-muted); -} - -.levelCell { - padding: 3px 4px; - white-space: nowrap; - font-weight: 600; - width: 40px; -} - -.levelError { - color: var(--error); -} - -.levelWarn { - color: var(--warning); -} - -.levelDebug { - color: var(--text-muted); -} - -.levelTrace { - color: var(--text-faint, var(--text-muted)); -} - -.levelDefault { - color: var(--text-secondary); -} - -.messageCell { - padding: 3px 6px; - color: var(--text-primary); - word-break: break-word; } .footer { @@ -86,12 +30,3 @@ font-size: 12px; text-align: center; } - -.openLogsButton { - background: none; - border: none; - color: var(--amber); - cursor: pointer; - font-size: 12px; - font-family: var(--font-body); -} diff --git a/ui/src/components/ExecutionDiagram/tabs/LogTab.tsx b/ui/src/components/ExecutionDiagram/tabs/LogTab.tsx index df3c1980..79b28189 100644 --- a/ui/src/components/ExecutionDiagram/tabs/LogTab.tsx +++ b/ui/src/components/ExecutionDiagram/tabs/LogTab.tsx @@ -1,7 +1,9 @@ import { useState, useMemo } from 'react'; import { useNavigate } from 'react-router'; +import { Input, Button, LogViewer } from '@cameleer/design-system'; +import type { LogEntry } from '@cameleer/design-system'; import { useApplicationLogs } from '../../../api/queries/logs'; -import type { LogEntryResponse } from '../../../api/queries/logs'; +import { mapLogLevel } from '../../../utils/agent-utils'; import logStyles from './LogTab.module.css'; import diagramStyles from '../ExecutionDiagram.module.css'; @@ -11,25 +13,6 @@ interface LogTabProps { processorId: string | null; } -function levelClass(level: string): string { - switch (level?.toUpperCase()) { - case 'ERROR': return logStyles.levelError; - case 'WARN': return logStyles.levelWarn; - case 'DEBUG': return logStyles.levelDebug; - case 'TRACE': return logStyles.levelTrace; - default: return logStyles.levelDefault; - } -} - -function formatTime(iso: string): string { - const d = new Date(iso); - const h = String(d.getHours()).padStart(2, '0'); - const m = String(d.getMinutes()).padStart(2, '0'); - const s = String(d.getSeconds()).padStart(2, '0'); - const ms = String(d.getMilliseconds()).padStart(3, '0'); - return `${h}:${m}:${s}.${ms}`; -} - export function LogTab({ applicationId, exchangeId, processorId }: LogTabProps) { const [filter, setFilter] = useState(''); const navigate = useNavigate(); @@ -40,9 +23,9 @@ export function LogTab({ applicationId, exchangeId, processorId }: LogTabProps) { exchangeId, limit: 500 }, ); - const entries: LogEntryResponse[] = useMemo(() => { + const entries = useMemo(() => { if (!logs) return []; - let items = logs as LogEntryResponse[]; + let items = [...logs]; // If a processor is selected, filter logs by logger name containing the processor ID if (processorId) { @@ -62,7 +45,11 @@ export function LogTab({ applicationId, exchangeId, processorId }: LogTabProps) ); } - return items; + return items.map((e) => ({ + timestamp: e.timestamp ?? '', + level: mapLogLevel(e.level), + message: e.message ?? '', + })); }, [logs, processorId, filter]); if (isLoading) { @@ -72,11 +59,11 @@ export function LogTab({ applicationId, exchangeId, processorId }: LogTabProps) return (
- setFilter(e.target.value)} + onClear={() => setFilter('')} className={logStyles.filterInput} />
@@ -87,31 +74,16 @@ export function LogTab({ applicationId, exchangeId, processorId }: LogTabProps)
) : ( <> - - - {entries.map((entry, i) => ( - - - - - - ))} - -
- {formatTime(entry.timestamp)} - - {entry.level} - - {entry.message} -
+ {exchangeId && (
- +
)} diff --git a/ui/src/pages/Admin/AppConfigDetailPage.module.css b/ui/src/pages/Admin/AppConfigDetailPage.module.css index 76a07f66..6536bedf 100644 --- a/ui/src/pages/Admin/AppConfigDetailPage.module.css +++ b/ui/src/pages/Admin/AppConfigDetailPage.module.css @@ -17,71 +17,12 @@ margin-bottom: 20px; } -.backBtn { - background: none; - border: none; - color: var(--amber); - cursor: pointer; - font-size: 13px; - font-weight: 500; - padding: 4px 0; -} - -.backBtn:hover { - color: var(--amber-deep); - text-decoration: underline; -} - .toolbarActions { display: flex; gap: 8px; align-items: center; } -.editBtn { - background: none; - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - color: var(--text-secondary); - cursor: pointer; - font-size: 12px; - padding: 5px 12px; -} - -.editBtn:hover { - border-color: var(--amber); - color: var(--amber); -} - -.cancelBtn { - background: none; - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - color: var(--text-muted); - cursor: pointer; - font-size: 12px; - padding: 5px 12px; -} - -.cancelBtn:hover { - border-color: var(--text-faint); - color: var(--text-primary); -} - -.removeBtn { - background: none; - border: none; - color: var(--text-faint); - cursor: pointer; - font-size: 16px; - padding: 0 4px; - line-height: 1; -} - -.removeBtn:hover { - color: var(--error); -} - .header { margin-bottom: 16px; background: var(--bg-surface); @@ -122,13 +63,7 @@ min-width: 140px; } -.label { - font-size: 12px; - font-weight: 600; - color: var(--text-secondary); -} - -.select { +.numberInput { padding: 6px 10px; border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); @@ -140,24 +75,10 @@ max-width: 360px; } -.select:focus { +.numberInput:focus { border-color: var(--amber); } -.toggleRow { - display: flex; - align-items: center; - gap: 8px; - font-size: 13px; - color: var(--text-secondary); - cursor: pointer; -} - -.toggleRow input { - accent-color: var(--amber); - cursor: pointer; -} - .tapBadges { display: flex; flex-wrap: wrap; diff --git a/ui/src/pages/Admin/AppConfigDetailPage.tsx b/ui/src/pages/Admin/AppConfigDetailPage.tsx index 7b0f2529..7d41152f 100644 --- a/ui/src/pages/Admin/AppConfigDetailPage.tsx +++ b/ui/src/pages/Admin/AppConfigDetailPage.tsx @@ -2,7 +2,7 @@ import { useEffect, useState, useMemo } from 'react'; import { useParams, useNavigate } from 'react-router'; import { ArrowLeft, Pencil, X } from 'lucide-react'; import { - Button, SectionHeader, MonoText, Badge, DataTable, Spinner, Toggle, useToast, + Button, SectionHeader, MonoText, Badge, DataTable, Spinner, Toggle, Select, Label, useToast, } from '@cameleer/design-system'; import type { Column } from '@cameleer/design-system'; import { useApplicationConfig, useUpdateApplicationConfig } from '../../api/queries/commands'; @@ -205,16 +205,16 @@ export default function AppConfigDetailPage() { } if (editing) { return ( - + options={[ + { value: 'NONE', label: 'None' }, + { value: 'INPUT', label: 'Input' }, + { value: 'OUTPUT', label: 'Output' }, + { value: 'BOTH', label: 'Both' }, + ]} + /> ); } return ; @@ -248,9 +248,9 @@ export default function AppConfigDetailPage() { render: (_v: unknown, row: TracedTapRow) => { if (row.captureMode === null) return null; return ( - + ); }, }] : []), @@ -304,16 +304,16 @@ export default function AppConfigDetailPage() { return (
- + {editing ? (
- +
) : ( - + )}
@@ -330,79 +330,87 @@ export default function AppConfigDetailPage() { Settings
- + {editing ? ( - + updateField('agentLogLevel', e.target.value)}> - - - - - - + updateField('engineLevel', e.target.value)}> - - - - - + updateField('payloadCaptureMode', e.target.value)}> - - - - - + updateField('metricsEnabled', e.target.checked)} /> - {form.metricsEnabled ? 'Enabled' : 'Disabled'} - + updateField('metricsEnabled', (e.target as HTMLInputElement).checked)} + label={form.metricsEnabled ? 'Enabled' : 'Disabled'} + /> ) : ( )}
- + {editing ? ( - updateField('samplingRate', parseFloat(e.target.value) || 0)} /> ) : ( @@ -410,13 +418,13 @@ export default function AppConfigDetailPage() { )}
- + {editing ? ( - + updateField('compressSuccess', (e.target as HTMLInputElement).checked)} + label={form.compressSuccess ? 'On' : 'Off'} + /> ) : ( )} diff --git a/ui/src/pages/AgentHealth/AgentHealth.module.css b/ui/src/pages/AgentHealth/AgentHealth.module.css index 10479b58..470f32a4 100644 --- a/ui/src/pages/AgentHealth/AgentHealth.module.css +++ b/ui/src/pages/AgentHealth/AgentHealth.module.css @@ -58,102 +58,12 @@ color: var(--text-muted); } -.configSelect { - padding: 5px 10px; - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - background: var(--bg-body); - color: var(--text-primary); - font-size: 12px; - font-family: var(--font-mono); - outline: none; - cursor: pointer; -} - -.configSelect:focus { - border-color: var(--amber); -} - -.configSelect:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.configToggle { - display: flex; - align-items: center; - gap: 6px; - font-size: 12px; - font-family: var(--font-mono); - color: var(--text-secondary); - cursor: pointer; -} - -.configToggle input { - accent-color: var(--amber); - cursor: pointer; -} - -.configEditBtn { - background: transparent; - border: none; - color: var(--text-faint); - opacity: 0.75; - cursor: pointer; - font-size: 14px; - padding: 4px 6px; - border-radius: var(--radius-sm); - line-height: 1; - display: inline-flex; - align-self: flex-end; -} - -.configEditBtn:hover { - color: var(--text-primary); - opacity: 1; -} - .configActions { display: flex; gap: 8px; align-self: flex-end; } -.configSaveBtn { - padding: 4px 12px; - border: none; - border-radius: var(--radius-sm); - background: var(--amber); - color: var(--bg-surface); - font-size: 12px; - font-weight: 600; - cursor: pointer; -} - -.configSaveBtn:hover { - background: var(--amber-deep); -} - -.configSaveBtn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.configCancelBtn { - padding: 4px 12px; - border: 1px solid var(--border-subtle); - border-radius: var(--radius-sm); - background: transparent; - color: var(--text-muted); - font-size: 12px; - cursor: pointer; -} - -.configCancelBtn:hover { - color: var(--text-primary); - border-color: var(--text-faint); -} - /* Section header */ .sectionTitle { font-size: 13px; diff --git a/ui/src/pages/AgentHealth/AgentHealth.tsx b/ui/src/pages/AgentHealth/AgentHealth.tsx index 3e2d2885..14a40e74 100644 --- a/ui/src/pages/AgentHealth/AgentHealth.tsx +++ b/ui/src/pages/AgentHealth/AgentHealth.tsx @@ -4,7 +4,7 @@ import { ExternalLink, RefreshCw, Pencil } from 'lucide-react'; import { StatCard, StatusDot, Badge, MonoText, GroupCard, DataTable, EventFeed, - LogViewer, ButtonGroup, SectionHeader, Toggle, useToast, + LogViewer, ButtonGroup, SectionHeader, Toggle, Select, Button, useToast, } from '@cameleer/design-system'; import type { Column, FeedEvent, LogEntry, ButtonGroupItem } from '@cameleer/design-system'; import styles from './AgentHealth.module.css'; @@ -356,45 +356,53 @@ export default function AgentHealth() { <>
App Log Level - + setConfigDraft(d => ({ ...d, agentLogLevel: e.target.value }))}> - - - - - - + setConfigDraft(d => ({ ...d, engineLevel: e.target.value }))}> - - - - - + setConfigDraft(d => ({ ...d, payloadCaptureMode: e.target.value }))}> - - - - - +