fix(import): When a imported site has detected a duplicate give the option to over…#325
fix(import): When a imported site has detected a duplicate give the option to over…#325netdata-be wants to merge 1 commit into
Conversation
…write the current ones
There was a problem hiding this comment.
Pull request overview
Adds an “overwrite existing site” path to the Universal import duplicate-resolution flow, allowing users to replace an existing dive site with imported site data when a duplicate is detected.
Changes:
- Adds a new duplicate action (
DuplicateAction.replaceSource) to the Universal adapter and review UI. - Captures site overwrite selections as
siteOverrides(import index → existing site ID) and applies them during UDDF site import by updating the existing site in place. - Updates duplicate UI styling/badges to reflect the new overwrite action.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| lib/features/import_wizard/presentation/widgets/entity_review_list.dart | Adds “Overwrite” UI option (button + badge/border styling) for duplicate entities. |
| lib/features/import_wizard/data/adapters/universal_adapter.dart | Exposes replaceSource as a supported duplicate action and builds siteOverrides for site overwrites. |
| lib/features/dive_import/data/services/uddf_entity_importer.dart | Extends site import to support overwriting existing sites based on siteOverrides. |
| Set<DuplicateAction> get supportedDuplicateActions => const { | ||
| DuplicateAction.skip, | ||
| DuplicateAction.importAsNew, | ||
| DuplicateAction.replaceSource, | ||
| }; |
| _EntityActionButton( | ||
| label: 'Overwrite', | ||
| subtitle: 'Replace existing entry', | ||
| isSelected: selectedAction == DuplicateAction.replaceSource, | ||
| color: Colors.blue.shade700, | ||
| onPressed: () => | ||
| onActionChanged(DuplicateAction.replaceSource), | ||
| ), |
| final overwrittenSite = DiveSite( | ||
| id: existingId, | ||
| diverId: diverId, | ||
| name: name, | ||
| description: siteData['description'] as String? ?? '', | ||
| location: (lat != null && lon != null) ? GeoPoint(lat, lon) : null, | ||
| minDepth: siteData['minDepth'] as double?, | ||
| maxDepth: siteData['maxDepth'] as double?, | ||
| difficulty: difficulty, | ||
| country: country, | ||
| region: region, | ||
| rating: siteData['rating'] as double?, | ||
| notes: siteData['notes'] as String? ?? '', | ||
| hazards: siteData['hazards'] as String?, | ||
| accessNotes: siteData['accessNotes'] as String?, | ||
| mooringNumber: siteData['mooringNumber'] as String?, | ||
| parkingInfo: siteData['parkingInfo'] as String?, | ||
| altitude: siteData['altitude'] as double?, | ||
| ); |
| // Handle overwrite (replaceSource): update existing sites in place. | ||
| for (final entry in overrides.entries) { | ||
| final i = entry.key; | ||
| final existingId = entry.value; | ||
| if (i >= items.length) continue; | ||
| final siteData = items[i]; | ||
| final name = siteData['name'] as String?; | ||
| if (name == null || name.isEmpty) continue; | ||
|
|
||
| final existing = existingById[existingId]; | ||
| if (existing == null) continue; | ||
|
|
||
| final uddfId = siteData['uddfId'] as String?; | ||
| final lat = siteData['latitude'] as double?; | ||
| final lon = siteData['longitude'] as double?; | ||
|
|
||
| String? country = siteData['country'] as String?; | ||
| String? region = siteData['region'] as String?; | ||
|
|
||
| if (lat != null && lon != null && (country == null || region == null)) { | ||
| try { | ||
| final geocodeResult = await LocationService.instance.reverseGeocode( | ||
| lat, | ||
| lon, | ||
| ); | ||
| country ??= geocodeResult.country; | ||
| region ??= geocodeResult.region; | ||
| } catch (_) { | ||
| // Geocoding is best-effort | ||
| } | ||
| } | ||
|
|
||
| final difficultyStr = siteData['difficulty'] as String?; | ||
| final difficulty = difficultyStr != null | ||
| ? SiteDifficulty.fromString(difficultyStr) | ||
| : null; | ||
|
|
||
| final overwrittenSite = DiveSite( | ||
| id: existingId, | ||
| diverId: diverId, | ||
| name: name, | ||
| description: siteData['description'] as String? ?? '', | ||
| location: (lat != null && lon != null) ? GeoPoint(lat, lon) : null, | ||
| minDepth: siteData['minDepth'] as double?, | ||
| maxDepth: siteData['maxDepth'] as double?, | ||
| difficulty: difficulty, | ||
| country: country, | ||
| region: region, | ||
| rating: siteData['rating'] as double?, | ||
| notes: siteData['notes'] as String? ?? '', | ||
| hazards: siteData['hazards'] as String?, | ||
| accessNotes: siteData['accessNotes'] as String?, | ||
| mooringNumber: siteData['mooringNumber'] as String?, | ||
| parkingInfo: siteData['parkingInfo'] as String?, | ||
| altitude: siteData['altitude'] as double?, | ||
| ); | ||
|
|
||
| await repository.updateSite(overwrittenSite); | ||
|
|
||
| final waterType = siteData['waterType'] as String?; | ||
| final bodyOfWater = siteData['bodyOfWater'] as String?; | ||
| if (waterType != null || bodyOfWater != null) { | ||
| await repository.applyImportedMetadata( | ||
| existingId, | ||
| DiveSitesCompanion( | ||
| waterType: waterType != null | ||
| ? Value(waterType) | ||
| : const Value.absent(), | ||
| bodyOfWater: bodyOfWater != null | ||
| ? Value(bodyOfWater) | ||
| : const Value.absent(), | ||
| ), | ||
| ); | ||
| } | ||
|
|
||
| if (uddfId != null) idMapping[uddfId] = overwrittenSite; | ||
| count++; | ||
| onProgress?.call(ImportPhase.sites, count, totalWork); | ||
| } |
|
A few things worth fixing:
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
When importing sites and an existing site has been detected give the option to overwrite the current site