Skip to content

feat(tui): custom-field labels, detail-view list rendering, bulk-edit polish (#132, #133, #134)#135

Merged
thomaschristory merged 5 commits into
mainfrom
feat/tui-custom-fields-tags-1.6.0
Jun 29, 2026
Merged

feat(tui): custom-field labels, detail-view list rendering, bulk-edit polish (#132, #133, #134)#135
thomaschristory merged 5 commits into
mainfrom
feat/tui-custom-fields-tags-1.6.0

Conversation

@thomaschristory

Copy link
Copy Markdown
Owner

Closes #132, #133, #134 — three tightly-coupled TUI improvements for the 1.6.0 milestone. They share a custom-field-definitions resolver (and #134 a tags resolver), mirroring the #130 object-type resolver pattern, so they ship together to avoid release churn.

#133 — detail view renders list-of-object fields like the list table (bug)

The detail view stringified list values (tags, other M2M/list fields) to a raw [{'id': …, 'display': …}] repr. They now route through the shared flatten/render_cell pipeline → comma-joined display, colored when object colors are on. Both the editable and read-only branches are covered. view._cell was promoted to public render_cell.

#132 — custom-field column labels (enhancement)

Custom-field columns showed the raw custom_fields.<name> key. A new CustomFieldResolver maps the key → the field's NetBox label (GET /api/extras/custom-fields/, scoped by object type), consumed by:

  • TUI list headers and the column chooser (f)
  • CLI table/csv headers — for list, get, and create/update responses

The raw key stays the selector/persistence key, so --columns and saved prefs are unchanged. Labels resolve only when a custom-field column is shown; missing defs, offline, unknown fields, and label collisions all fall back to the raw key. json/jsonl/yaml are untouched.

#134 — bulk-edit polish + per-field custom fields + tags dropdown (enhancement)

  • Per-field custom fields: custom_fields expands into one typed widget per field (text/number/switch/select/multiselect) in both the single EditForm and the BulkEditForm, via the shared forms.py layer. Values stage flat as custom_fields.<name> and are folded back into a nested custom_fields object before the PATCH/POST.
  • Tags multi-select: tags renders as a SelectionList populated from GET /api/extras/tags/, submitting the NetBox-writable [{name, slug}] list. Single edit only stages a tags change when the selection actually differs; bulk replaces ("Set tags to…").
  • Layout polish: bulk form CSS aligns the include-toggle + label + widget with the single-edit experience.
  • Graceful degradation: falls back to the prior single text inputs when tag/custom-field definitions can't be fetched.

Notes

  • New framework-free resolvers live in nsc/savedfilters/ alongside the feat(saved-searches): back saved searches with NetBox native SavedFilter #130 object-type resolver.
  • Widget ids encode the custom_fields. dot as a hyphen (Textual rejects dots in ids).
  • An adversarial multi-agent review ran against the branch; its confirmed findings (cf-multiselect pre-seeding, get/response header relabeling, FK id encoding consistency) are fixed in the final commit.
  • Version bump to 1.6.0 is intentionally left for the separate release PR, per this repo's release flow.

Testing

  • just lint (ruff + mypy --strict) clean.
  • just test1324 passed, 1 skipped (19 new tests across resolvers, detail rendering, CLI/TUI headers, forms helpers, and bulk/single edit flows).

🤖 Generated with Claude Code

Thomas Christory and others added 5 commits June 29, 2026 15:31
…t use

Mirror the #130 ObjectTypeResolver: fetch-once-per-scope, cache, return None
on failure. CustomFieldResolver maps custom_fields.<name> -> label/type/choices
scoped by object type; TagsResolver fetches the global tag list. custom_field_labels
builds a display-label map with collision/fallback handling.

Foundation for #132, #134.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…able (#133)

The detail view stringified list values (tags, other M2M/list fields) to a raw
list-of-dicts repr. Route them through the shared flatten/render_cell pipeline so
they show a comma-joined display string, colored when object colors are enabled,
matching the list table. Promotes view._cell to public render_cell.

Closes #133

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ser (#132)

Custom-field columns showed the raw custom_fields.<name> key everywhere. Add a
display-label layer (CustomFieldResolver + custom_field_labels) that maps the key
to the field's NetBox label for: TUI list headers, the column chooser, and CLI
table/CSV headers. The raw key stays the selector/persistence key, so --columns
and saved prefs are unchanged. Labels resolve only when a custom-field column is
shown; missing defs, offline, unknown fields, and label collisions all fall back
to the raw key.

Closes #132

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ct (#134)

- Expand custom_fields into one typed widget per field (text/number/switch/
  select/multiselect) in both the single EditForm and the BulkEditForm, via a
  shared forms.py layer. Values stage flat as custom_fields.<name> and are folded
  back into a nested custom_fields object before the PATCH/POST.
- Render tags as a SelectionList multi-select populated from /api/extras/tags/,
  submitting the NetBox-writable [{name, slug}] list. Single edit only stages a
  tags change when the selection actually differs; bulk replaces (Set tags to…).
- Polish the bulk form layout (CSS) to align include-toggle + label + widget with
  the single-edit experience.
- Widget ids encode the custom_fields dot as a hyphen (Textual rejects dots).
- Degrade gracefully to the prior single text inputs when tag/custom-field
  definitions can't be fetched.

Closes #134

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- EditForm: pre-seed custom-field multiselect widgets from the record's current
  values (previously only tags seeded; cf multiselects rendered empty). Stage a
  multiselect change only when the value set actually differs, so an unchanged
  pre-seeded selection — regardless of order — never forces a spurious patch.
- CLI: relabel custom-field column headers in 'get' and create/update response
  output too (handle_get + _render_response), deriving the collection path from a
  detail path so the resolver matches. Matches handle_list; gated on a cf column.
- BulkEditForm: encode FK widget/button ids consistently at creation (Textual
  rejects dots), matching how they are queried back.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom-field columns show raw custom_fields.<name> instead of the field's label

1 participant