You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Import today only creates a new project from the Home page (importNewProject). Operators working on an active codeplug cannot add or refresh data from additional CSV files — e.g. import Channels.csv first, then realise they also need Zones.csv, Contacts.csv, or TG_Lists.csv.
The store already exposes applyImportToActive and applyImportToCodeplug (src/state/codeplugStore.tsx), but no UI calls them. Import docs mention merging into the active project, yet the dropzone is Home-only.
Current merge semantics are also too blunt for incremental import:
Importing a file type replaces the entire entity array for that type (channels = result.channels ?? codeplug.channels).
Re-imported channels always get new internal ids (parseChannels calls newId()), so zone memberships break unless channel names still resolve.
Zone re-resolution by name works when channels are re-imported with the same names (see codeplugStore.test.tsx), but ids churn and duplicates are not handled.
Intended outcome
Allow importing OpenGD77 CSV file(s) into the active codeplug with sensible merge behaviour and operator feedback.
UI
Add an Import into active project entry point when a project is open (candidates: Summary, Export, or a sidebar/footer action near Settings).
Reuse ImportDropzone + importFiles; wire onResult to applyImportToActive.
Show import summary (recognised / skipped / errors) inline after merge.
Optional but recommended: preview/confirm step listing what will be added, updated, or replaced before applying.
Merge semantics (design + implement)
Entity matching rules live at the import boundary — adapters know vendor keying; the store applies the merge.
Entity
Vendor match key
Proposed merge behaviour
Channel
Channel Name (case-sensitive)
Upsert by name: update existing channel in place (preserve internal id); append channels with new names; do not wipe unrelated channels
Zone
Zone Name
Upsert by name; re-resolve memberChannelIds from sourceMemberNames
Contact
Contact Name
Upsert by name
Talk group
Contact Name where ID Type=Group
Upsert by name
RX group list
TG List Name
Upsert by name
When only one file type is imported (e.g. Zones.csv alone), other entity arrays are left unchanged (already true today).
Zone member resolution: after channel upsert, rebuild nameToId and re-resolve all zones' memberChannelIds. Surface unresolved member names in the import summary (existing validation/report patterns).
Replace-all escape hatch: optional advanced mode to replace an entire entity type from import (current behaviour) — not required for v1 if upsert covers the main use case.
Example workflow
Operator imports Channels.csv on Home → new project (unchanged).
Later, on Summary/Export, imports Zones.csv into the active project → zones added; members linked to existing channels by name.
Re-imports an updated Channels.csv → channels with matching names updated in place; new names appended; zones stay linked.
Affected
UI route with ImportDropzone wired to applyImportToActive
applyImportToCodeplug in src/state/codeplugStore.tsx — upsert merge logic per entity type
src/lib/import/opengd77/parse.ts — may need merge helpers or pass-through of existing id map
Problem
Import today only creates a new project from the Home page (
importNewProject). Operators working on an active codeplug cannot add or refresh data from additional CSV files — e.g. importChannels.csvfirst, then realise they also needZones.csv,Contacts.csv, orTG_Lists.csv.The store already exposes
applyImportToActiveandapplyImportToCodeplug(src/state/codeplugStore.tsx), but no UI calls them. Import docs mention merging into the active project, yet the dropzone is Home-only.Current merge semantics are also too blunt for incremental import:
channels = result.channels ?? codeplug.channels).parseChannelscallsnewId()), so zone memberships break unless channel names still resolve.codeplugStore.test.tsx), but ids churn and duplicates are not handled.Intended outcome
Allow importing OpenGD77 CSV file(s) into the active codeplug with sensible merge behaviour and operator feedback.
UI
ImportDropzone+importFiles; wireonResulttoapplyImportToActive.Merge semantics (design + implement)
Entity matching rules live at the import boundary — adapters know vendor keying; the store applies the merge.
Channel Name(case-sensitive)id); append channels with new names; do not wipe unrelated channelsZone NamememberChannelIdsfromsourceMemberNamesContact NameContact NamewhereID Type=GroupTG List NameWhen only one file type is imported (e.g.
Zones.csvalone), other entity arrays are left unchanged (already true today).Zone member resolution: after channel upsert, rebuild
nameToIdand re-resolve all zones'memberChannelIds. Surface unresolved member names in the import summary (existing validation/report patterns).Replace-all escape hatch: optional advanced mode to replace an entire entity type from import (current behaviour) — not required for v1 if upsert covers the main use case.
Example workflow
Channels.csvon Home → new project (unchanged).Zones.csvinto the active project → zones added; members linked to existing channels by name.Channels.csv→ channels with matching names updated in place; new names appended; zones stay linked.Affected
ImportDropzonewired toapplyImportToActiveapplyImportToCodepluginsrc/state/codeplugStore.tsx— upsert merge logic per entity typesrc/lib/import/opengd77/parse.ts— may need merge helpers or pass-through of existing id mapsrc/lib/codeplug.ts—buildNameToChannelId,resolveZoneMemberssrc/state/codeplugStore.test.tsx— upsert, partial import, unresolved membersdocs/features/import/README.md— document active-project import + merge rulesNotes / dependencies
channel.numberfrom model) — import merge should not depend on channel number as a key.Out of scope
Workflow note
Branch from
origin/main, atomic conventional commits (store merge logic, then UI, then docs/tests), PR linkingCloses #58.