Skip to content

feat(ui): standardise export validation and warning report format #106

Description

@pskillen

Problem

Export adapters (OpenGD77 shipped; CHIRP imminent; DM32, qDMR, and others planned) must surface errors and warnings when the internal codeplug does not map cleanly to a vendor wire format — truncation, unsupported fields, cardinality caps, unresolvable references, and profile-specific limits.

Today there is no shared contract for those messages:

  • OpenGD77 export serialises and downloads with no structured report (#95 — e.g. silent zone-member truncation beyond 80 slots).
  • ExportFromActivePanel is format-specific (hard-wired to opengd77ExportAdapter) and has no pre-export or post-export feedback UI.
  • Import already has a message shape (ImportResult.errors / skipped as { fileName, message } in src/lib/import/types.ts), but export has no equivalent.
  • CRUD validation uses ValidationIssue (field, message, severity) in src/lib/validation/channel.ts — vendor-neutral, entity-scoped — but export adapters do not participate.

As the exporter list grows, we risk one-off UI per format unless we standardise now.

Intended outcome

Introduce a vendor-neutral export validation/report format and a single UI surface that any export adapter can feed — including pre-export validation the operator can review before clicking download.

This ticket is a building block for #86 (optional target-radio validation hints in CRUD). #86 needs a stable way to surface compatibility warnings during editing; this ticket defines the shared report shape and export-page UX that #86 can reuse and extend.

Design principles

Layer Role
Internal model + CRUD validation Vendor-neutral errors only (required fields, FK integrity) — unchanged
Export validation (new) Format/profile-specific errors (block export) and warnings (export proceeds with truncation/loss)
Export UI (new) One report component — grouped, filterable, navigable — not per-adapter bespoke panels

Export validation lives at the import/export boundary (src/lib/export/), consistent with vendor boundaries.

Proposed report shape (illustrative — final API TBD)

Shared types e.g. src/lib/export/types.ts:

export type ExportReportSeverity = 'error' | 'warning';

export type ExportReportComponent =
  | 'channels'
  | 'zones'
  | 'contacts'
  | 'talkGroups'
  | 'rxGroupLists'
  | 'project';

export interface ExportReportIssue {
  severity: ExportReportSeverity;
  component: ExportReportComponent;
  /** Internal entity id when the issue is entity-scoped; omit for project-level */
  entityId?: string;
  /** Internal field or wire column when helpful for UI anchoring */
  field?: string;
  /** CPS file affected when relevant (symmetry with import `ImportMessage.fileName`) */
  fileName?: string;
  message: string;
  /** Machine-readable rule id for tests and future #86 hint correlation */
  code?: string;
}

export interface ExportValidationResult {
  issues: ExportReportIssue[];
  /** false when any error-severity issue is present */
  canExport: boolean;
}

Grouping: issues are tagged by component so the UI can show per-section summaries (e.g. "3 channel warnings, 1 zone error") and deep-link to CRUD detail routes where entityId is set.

Pre-export vs on-export: adapters expose validateExport(codeplug, options?) → ExportValidationResult for the export page preview; serialisation may append additional issues discovered during wire conversion (same shape). Options may later carry radio profile id (#72) without changing the report type.

Adapter contract

Extend export adapter registry (src/lib/export/registry.ts):

Method Purpose
validateExport(codeplug, options?) Pre-export scan — no file generation
export(codeplug, options?) Returns { files, report: ExportValidationResult } (or equivalent) instead of silent download-only side effects

Migrate OpenGD77 first; CHIRP export (imminent) should implement the contract from day one.

Known OpenGD77 issues to fold in (#95):

  • Zone member truncation beyond 80 → warning with entity ids
  • Channel count / Channel Number over 1023 → warning
  • RX group list truncation (32) → warning (test exists; wire into report)

Contributor checklist — adding-a-new-vendor.md

Required deliverable. Update docs/features/import-export/adding-a-new-vendor.md so every future format ships with standard export validation — not ad-hoc per-adapter UI or silent truncation.

Concrete updates:

§2 Code layout / adapter contract (export) — expand the one-line export contract to require:

  • Implement validateExport(codeplug, options?) returning ExportValidationResult
  • Emit ExportReportIssue from serialisation for truncation, lossy conversion, and profile cap violations
  • Tag issues with component, entityId, and fileName where applicable
  • Use stable code ids for rules that may correlate with feat: optional target-radio validation hints in CRUD UI #86 hints later
  • Do not build format-specific export report UI — feed the shared ExportReportPanel

§4 Tests — add row/scenario:

  • Export validation unit tests: fixture codeplug triggers expected issues (severity, component, code)
  • Round-trip tests assert warnings are emitted for known lossy behaviour (not only silent truncation)

§5 UI — add checklist items:

  • Export adapter registered in exportAdapters; no hard-wiring in ExportFromActivePanel
  • Pre-export report visible on export page before download; canExport === false disables buttons

§7 Feature docs — ensure adding-a-new-vendor.md itself is listed (self-referential maintenance).

Worked example (§8) — add OpenGD77 validateExport / report emission to the walk-through table once shipped.

This keeps CHIRP and subsequent formats on the standard path from day one.

UI surfaces

Surface Behaviour
Export page (/export) On format/profile selection, run validateExport and show report before download buttons; disable download when canExport === false
Report panel (shared component) Severity badges, component sections, expandable issue rows; link to entity detail when entityId present
Post-download Optional toast/summary if on-export issues differ from pre-export scan

Decouple ExportFromActivePanel from OpenGD77-specific adapter wiring — drive from exportAdapters registry + selected vendorFormat.

Style: errors red, warnings amber — distinct from CRUD save-blocking errors and from #86 compatibility hints (which are advisory during edit).

Relationship to #86

This ticket (#106) #86
Export-time and pre-export report format + export-page UI CRUD-time warning hints driven by project target radios
ExportReportIssue at format/profile boundary ValidationIssue with severity: 'warning' during edit
Shared code ids where rules overlap (e.g. zone member cap) Optional: CRUD hints reference same rule codes for consistent copy
Ships first Consumes report types / UI patterns; may call validateExport with project target profiles

#86 explicitly lists pre-export correlation ("12 hints would become export errors") as nice-to-have — this ticket makes that possible.

Affected

  • src/lib/export/types.ts (new shared types)
  • src/lib/export/registry.ts — adapter interface
  • src/lib/export/opengd77/validateExport, emit issues from serialise.ts
  • src/components/ExportFromActivePanel/ — registry-driven, report UI
  • New shared component e.g. src/components/ExportReportPanel/
  • docs/features/import-export/adding-a-new-vendor.md — export validation checklist (required; see above)
  • docs/features/import-export/README.md — implementation status row
  • Tests: unit tests per adapter rule; UI smoke on export page with fixture codeplug triggering warnings/errors

Out of scope

Manual verify

  1. OpenGD77 export with a clean codeplug → empty report, downloads enabled.
  2. Zone with >80 members → pre-export warning listing zone + dropped members; download still allowed (warning) or blocked per rule severity.
  3. Switch export format in UI → same report panel layout, different adapter messages (no OpenGD77-only markup).
  4. Issue with entityId → link navigates to correct channel/zone detail.
  5. Error-severity issue → download buttons disabled until resolved.
  6. adding-a-new-vendor.md checklist includes export validation items; a new contributor can follow it without reading this issue.

Workflow note

Multi-commit feature: branch from origin/main, atomic conventional commits (shared types → OpenGD77 validate → report UI → registry decouple → adding-a-new-vendor checklist → docs/tests), PR linking Closes #106.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions