Allow clicking a declared param to load it into the form for editing
Mirrors Metrics.tsx's click-to-edit pattern: clicking a param chip populates the declaration form, shows an "Editing" indicator, and swaps the submit button for Save changes / Cancel. The name field is locked while editing since declare_param overwrites in place by name — renaming would silently orphan the original and anything referencing it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5e30e02a78
commit
ed9aa8eb45
@@ -43,6 +43,11 @@ export function Params({ params, onDeclared }: { params: ParamDeclaration[]; onD
|
|||||||
const [submitError, setSubmitError] = useState<string | null>(null)
|
const [submitError, setSubmitError] = useState<string | null>(null)
|
||||||
const [submitting, setSubmitting] = useState(false)
|
const [submitting, setSubmitting] = useState(false)
|
||||||
const [collapsed, setCollapsed] = useSectionCollapsed('params')
|
const [collapsed, setCollapsed] = useSectionCollapsed('params')
|
||||||
|
// Name of the declaration currently loaded into the form for editing, or `null` when the form is
|
||||||
|
// declaring a brand-new param. The name field is locked while editing — `declareParam` overwrites
|
||||||
|
// in place by name, so changing it here would silently create a second param and orphan the
|
||||||
|
// original (and anything referencing it by name) rather than renaming it.
|
||||||
|
const [editingName, setEditingName] = useState<string | null>(null)
|
||||||
|
|
||||||
const isBoolean = kind === 'boolean'
|
const isBoolean = kind === 'boolean'
|
||||||
const isElapsedSinceLast = source === 'elapsed_since_last'
|
const isElapsedSinceLast = source === 'elapsed_since_last'
|
||||||
@@ -63,6 +68,30 @@ export function Params({ params, onDeclared }: { params: ParamDeclaration[]; onD
|
|||||||
if (next === 'elapsed_since_last') setKind('duration')
|
if (next === 'elapsed_since_last') setKind('duration')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
setEditingName(null)
|
||||||
|
setName('')
|
||||||
|
setRegex('')
|
||||||
|
setCaptureGroup('')
|
||||||
|
setJsonFieldPath('')
|
||||||
|
setSource('regex')
|
||||||
|
setKind(EMPTY_KIND)
|
||||||
|
setSubmitError(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads `param`'s current values into the declaration form for editing — a click handler for a
|
||||||
|
// declared-param badge.
|
||||||
|
function editParam(param: ParamDeclaration) {
|
||||||
|
setEditingName(param.name)
|
||||||
|
setName(param.name)
|
||||||
|
setRegex(param.message_match_regex)
|
||||||
|
setCaptureGroup(param.capture_group)
|
||||||
|
setJsonFieldPath(param.json_field_path ?? '')
|
||||||
|
setSource(param.elapsed_since_last ? 'elapsed_since_last' : param.json_field_path ? 'json_field' : 'regex')
|
||||||
|
setKind(param.kind)
|
||||||
|
setSubmitError(null)
|
||||||
|
}
|
||||||
|
|
||||||
async function handleSubmit(e: React.FormEvent) {
|
async function handleSubmit(e: React.FormEvent) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
setSubmitError(null)
|
setSubmitError(null)
|
||||||
@@ -79,15 +108,10 @@ export function Params({ params, onDeclared }: { params: ParamDeclaration[]; onD
|
|||||||
kind: effectiveSource === 'elapsed_since_last' ? 'duration' : kind,
|
kind: effectiveSource === 'elapsed_since_last' ? 'duration' : kind,
|
||||||
elapsed_since_last: effectiveSource === 'elapsed_since_last',
|
elapsed_since_last: effectiveSource === 'elapsed_since_last',
|
||||||
})
|
})
|
||||||
setName('')
|
resetForm()
|
||||||
setRegex('')
|
|
||||||
setCaptureGroup('')
|
|
||||||
setJsonFieldPath('')
|
|
||||||
setSource('regex')
|
|
||||||
setKind(EMPTY_KIND)
|
|
||||||
onDeclared()
|
onDeclared()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setSubmitError(err instanceof ApiError ? err.message : 'Failed to declare param')
|
setSubmitError(err instanceof ApiError ? err.message : `Failed to ${editingName ? 'update' : 'declare'} param`)
|
||||||
} finally {
|
} finally {
|
||||||
setSubmitting(false)
|
setSubmitting(false)
|
||||||
}
|
}
|
||||||
@@ -115,13 +139,16 @@ export function Params({ params, onDeclared }: { params: ParamDeclaration[]; onD
|
|||||||
<div className="flex gap-4 p-3" hidden={collapsed}>
|
<div className="flex gap-4 p-3" hidden={collapsed}>
|
||||||
<form onSubmit={handleSubmit} className="w-80 shrink-0 space-y-2">
|
<form onSubmit={handleSubmit} className="w-80 shrink-0 space-y-2">
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-1 block text-xs text-text-subtle">Name</label>
|
<label className="mb-1 block text-xs text-text-subtle">
|
||||||
|
Name{editingName && ' (locked while editing)'}
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
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"
|
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 disabled:opacity-50"
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
placeholder="path"
|
placeholder="path"
|
||||||
required
|
required
|
||||||
|
disabled={editingName !== null}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{!isElapsedSinceLast && (
|
{!isElapsedSinceLast && (
|
||||||
@@ -207,14 +234,31 @@ export function Params({ params, onDeclared }: { params: ParamDeclaration[]; onD
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{editingName && (
|
||||||
|
<div className="rounded border border-accent bg-accent/5 px-2 py-1 text-xs text-accent-text">
|
||||||
|
Editing "{editingName}"
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{submitError && <div className="rounded border border-danger-border bg-danger-bg/50 px-2 py-1 text-xs text-danger-text">{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
|
<div className="flex items-center gap-2">
|
||||||
type="submit"
|
<button
|
||||||
disabled={submitting}
|
type="submit"
|
||||||
className="rounded border border-border-soft px-3 py-1 text-sm text-text-secondary hover:bg-surface-2 disabled:opacity-50"
|
disabled={submitting}
|
||||||
>
|
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 param'}
|
>
|
||||||
</button>
|
{submitting ? (editingName ? 'Saving…' : 'Declaring…') : editingName ? 'Save changes' : 'Declare param'}
|
||||||
|
</button>
|
||||||
|
{editingName && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={resetForm}
|
||||||
|
disabled={submitting}
|
||||||
|
className="rounded border border-border-soft px-3 py-1 text-sm text-text-secondary hover:bg-surface-2 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
@@ -222,9 +266,15 @@ export function Params({ params, onDeclared }: { params: ParamDeclaration[]; onD
|
|||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
{params.length === 0 && <span className="text-xs text-text-faint">No params declared yet.</span>}
|
{params.length === 0 && <span className="text-xs text-text-faint">No params declared yet.</span>}
|
||||||
{params.map((p) => (
|
{params.map((p) => (
|
||||||
<span
|
<button
|
||||||
key={p.name}
|
key={p.name}
|
||||||
className="rounded border border-border-soft px-2 py-1 text-xs text-text-secondary"
|
type="button"
|
||||||
|
onClick={() => editParam(p)}
|
||||||
|
className={`rounded border px-2 py-1 text-xs ${
|
||||||
|
editingName === p.name
|
||||||
|
? 'border-accent bg-accent/10 text-accent-text'
|
||||||
|
: 'border-border-soft text-text-secondary hover:bg-surface-2'
|
||||||
|
}`}
|
||||||
title={p.elapsed_since_last ? undefined : p.message_match_regex}
|
title={p.elapsed_since_last ? undefined : p.message_match_regex}
|
||||||
>
|
>
|
||||||
{p.name} ({p.kind}
|
{p.name} ({p.kind}
|
||||||
@@ -234,7 +284,7 @@ export function Params({ params, onDeclared }: { params: ParamDeclaration[]; onD
|
|||||||
? ''
|
? ''
|
||||||
: `, ${p.json_field_path ? `json field ${p.json_field_path}` : `group ${p.capture_group}`}`}
|
: `, ${p.json_field_path ? `json field ${p.json_field_path}` : `group ${p.capture_group}`}`}
|
||||||
)
|
)
|
||||||
</span>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user