Docs, ticks, working app
This commit is contained in:
@@ -155,14 +155,14 @@ export function Metrics({ range }: { range: TimeRange | null }) {
|
||||
const groups = groupByLabels(bucketValues)
|
||||
|
||||
return (
|
||||
<div className="border-b border-neutral-800 p-3 text-sm">
|
||||
<div className="mb-2 text-xs text-neutral-500">Metrics</div>
|
||||
<div className="border-b border-border p-3 text-sm">
|
||||
<div className="mb-2 text-xs text-text-subtle">Metrics</div>
|
||||
<div className="flex gap-4">
|
||||
<form onSubmit={handleSubmit} className="w-80 shrink-0 space-y-2">
|
||||
<div>
|
||||
<label className="mb-1 block text-xs text-neutral-500">Name</label>
|
||||
<label className="mb-1 block text-xs text-text-subtle">Name</label>
|
||||
<input
|
||||
className="w-full rounded border border-neutral-700 bg-neutral-900 px-2 py-1 text-sm text-neutral-100 placeholder:text-neutral-500 focus:border-neutral-500 focus:outline-none"
|
||||
className="w-full rounded border border-border-soft bg-surface-1 px-2 py-1 text-sm text-text placeholder:text-text-subtle focus:border-border-strong focus:outline-none"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="requests"
|
||||
@@ -170,9 +170,9 @@ export function Metrics({ range }: { range: TimeRange | null }) {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="mb-1 block text-xs text-neutral-500">Message match regex</label>
|
||||
<label className="mb-1 block text-xs text-text-subtle">Message match regex</label>
|
||||
<input
|
||||
className="w-full rounded border border-neutral-700 bg-neutral-900 px-2 py-1 font-mono text-sm text-neutral-100 placeholder:text-neutral-500 focus:border-neutral-500 focus:outline-none"
|
||||
className="w-full rounded border border-border-soft bg-surface-1 px-2 py-1 font-mono text-sm text-text placeholder:text-text-subtle focus:border-border-strong focus:outline-none"
|
||||
value={regex}
|
||||
onChange={(e) => setRegex(e.target.value)}
|
||||
placeholder={String.raw`request path=(?P<path>\S+) duration=(?P<duration>\d+)`}
|
||||
@@ -180,12 +180,12 @@ export function Metrics({ range }: { range: TimeRange | null }) {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className="mb-1 flex items-center justify-between text-xs text-neutral-500">
|
||||
<div className="mb-1 flex items-center justify-between text-xs text-text-subtle">
|
||||
<span>Params</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={addMatcher}
|
||||
className="rounded border border-neutral-700 px-2 py-0.5 text-neutral-300 hover:bg-neutral-800"
|
||||
className="rounded border border-border-soft px-2 py-0.5 text-text-secondary hover:bg-surface-2"
|
||||
>
|
||||
+ Add
|
||||
</button>
|
||||
@@ -194,19 +194,19 @@ export function Metrics({ range }: { range: TimeRange | null }) {
|
||||
{matchers.map((matcher, i) => (
|
||||
<div key={i} className="flex items-center gap-1">
|
||||
<input
|
||||
className="w-24 min-w-0 flex-1 rounded border border-neutral-700 bg-neutral-900 px-1.5 py-1 text-xs text-neutral-100 placeholder:text-neutral-500 focus:border-neutral-500 focus:outline-none"
|
||||
className="w-24 min-w-0 flex-1 rounded border border-border-soft bg-surface-1 px-1.5 py-1 text-xs text-text placeholder:text-text-subtle focus:border-border-strong focus:outline-none"
|
||||
value={matcher.param_name}
|
||||
onChange={(e) => updateMatcher(i, { param_name: e.target.value })}
|
||||
placeholder="param_name"
|
||||
/>
|
||||
<input
|
||||
className="w-24 min-w-0 flex-1 rounded border border-neutral-700 bg-neutral-900 px-1.5 py-1 text-xs text-neutral-100 placeholder:text-neutral-500 focus:border-neutral-500 focus:outline-none"
|
||||
className="w-24 min-w-0 flex-1 rounded border border-border-soft bg-surface-1 px-1.5 py-1 text-xs text-text placeholder:text-text-subtle focus:border-border-strong focus:outline-none"
|
||||
value={matcher.capture_group}
|
||||
onChange={(e) => updateMatcher(i, { capture_group: e.target.value })}
|
||||
placeholder="capture_group"
|
||||
/>
|
||||
<select
|
||||
className="rounded border border-neutral-700 bg-neutral-900 px-1 py-1 text-xs text-neutral-100 focus:border-neutral-500 focus:outline-none"
|
||||
className="rounded border border-border-soft bg-surface-1 px-1 py-1 text-xs text-text focus:border-border-strong focus:outline-none"
|
||||
value={matcher.kind}
|
||||
onChange={(e) => updateMatcher(i, { kind: e.target.value as MetricParamMatcher['kind'] })}
|
||||
>
|
||||
@@ -218,7 +218,7 @@ export function Metrics({ range }: { range: TimeRange | null }) {
|
||||
type="button"
|
||||
onClick={() => removeMatcher(i)}
|
||||
disabled={matchers.length === 1}
|
||||
className="rounded border border-neutral-700 px-1.5 py-1 text-xs text-neutral-400 hover:bg-neutral-800 disabled:opacity-30"
|
||||
className="rounded border border-border-soft px-1.5 py-1 text-xs text-text-muted hover:bg-surface-2 disabled:opacity-30"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
@@ -226,28 +226,28 @@ export function Metrics({ range }: { range: TimeRange | null }) {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{submitError && <div className="rounded border border-red-900 bg-red-950/50 px-2 py-1 text-xs text-red-400">{submitError}</div>}
|
||||
{submitError && <div className="rounded border border-danger-border bg-danger-bg/50 px-2 py-1 text-xs text-danger-text">{submitError}</div>}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={submitting}
|
||||
className="rounded border border-neutral-700 px-3 py-1 text-sm text-neutral-300 hover:bg-neutral-800 disabled:opacity-50"
|
||||
className="rounded border border-border-soft px-3 py-1 text-sm text-text-secondary hover:bg-surface-2 disabled:opacity-50"
|
||||
>
|
||||
{submitting ? 'Declaring…' : 'Declare metric'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="mb-1 text-xs text-neutral-500">Declared metrics</div>
|
||||
<div className="mb-1 text-xs text-text-subtle">Declared metrics</div>
|
||||
<div className="mb-3 flex flex-wrap gap-1">
|
||||
{metrics.length === 0 && <span className="text-xs text-neutral-600">No metrics declared yet.</span>}
|
||||
{metrics.length === 0 && <span className="text-xs text-text-faint">No metrics declared yet.</span>}
|
||||
{metrics.map((m) => (
|
||||
<button
|
||||
key={m.name}
|
||||
onClick={() => setSelected(m.name)}
|
||||
className={`rounded border px-2 py-1 text-xs ${
|
||||
selected === m.name
|
||||
? 'border-sky-500 bg-sky-500/10 text-sky-300'
|
||||
: 'border-neutral-700 text-neutral-300 hover:bg-neutral-800'
|
||||
? 'border-accent bg-accent/10 text-accent-text'
|
||||
: 'border-border-soft text-text-secondary hover:bg-surface-2'
|
||||
}`}
|
||||
title={m.message_match_regex}
|
||||
>
|
||||
@@ -258,15 +258,15 @@ export function Metrics({ range }: { range: TimeRange | null }) {
|
||||
|
||||
{selected && (
|
||||
<div>
|
||||
<div className="mb-1 text-xs text-neutral-500">
|
||||
<div className="mb-1 text-xs text-text-subtle">
|
||||
{selected} — {bucketValues.length} occurrence{bucketValues.length === 1 ? '' : 's'} in range
|
||||
</div>
|
||||
{groups.length === 0 ? (
|
||||
<div className="text-xs text-neutral-600">No matching values in the active range.</div>
|
||||
<div className="text-xs text-text-faint">No matching values in the active range.</div>
|
||||
) : (
|
||||
<div className="max-h-56 overflow-auto rounded border border-neutral-800">
|
||||
<div className="max-h-56 overflow-auto rounded border border-border">
|
||||
<table className="w-full border-collapse text-xs">
|
||||
<thead className="sticky top-0 bg-neutral-900 text-neutral-500">
|
||||
<thead className="sticky top-0 bg-surface-1 text-text-subtle">
|
||||
<tr>
|
||||
<th className="px-2 py-1 text-left font-normal">Labels</th>
|
||||
<th className="px-2 py-1 text-right font-normal">Count</th>
|
||||
@@ -275,14 +275,14 @@ export function Metrics({ range }: { range: TimeRange | null }) {
|
||||
</thead>
|
||||
<tbody>
|
||||
{groups.map((group) => (
|
||||
<tr key={group.key} className="border-t border-neutral-800 text-neutral-300">
|
||||
<tr key={group.key} className="border-t border-border text-text-secondary">
|
||||
<td className="px-2 py-1 font-mono">
|
||||
{Object.entries(group.labels)
|
||||
.map(([k, v]) => `${k}=${v}`)
|
||||
.join(', ') || '(no labels)'}
|
||||
</td>
|
||||
<td className="px-2 py-1 text-right">{group.count}</td>
|
||||
<td className="px-2 py-1 font-mono text-sky-400">{sparkline(group.values, range)}</td>
|
||||
<td className="px-2 py-1 font-mono text-accent-text">{sparkline(group.values, range)}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user