feat(garage): add single-car move action from detail sheet
- Add Move action to car detail controls (Move / Edit / Remove) - Introduce target collection picker dialog for single-entry move - Reuse existing collection membership logic and refresh grid after move - Keep bulk multi-select move workflow unchanged
This commit is contained in:
parent
b9d1d750b6
commit
ddc1572273
1 changed files with 83 additions and 1 deletions
|
|
@ -601,6 +601,14 @@ class GarageScreenState extends State<GarageScreen> {
|
|||
// Edit & Delete buttons
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => _moveSingleCar(car, context),
|
||||
icon: const Icon(Icons.drive_file_move_outline, size: 18),
|
||||
label: const Text('Move'),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () => _editCar(car, context),
|
||||
|
|
@ -608,7 +616,7 @@ class GarageScreenState extends State<GarageScreen> {
|
|||
label: const Text('Edit Details'),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => _deleteCar(car, context),
|
||||
|
|
@ -714,6 +722,80 @@ class GarageScreenState extends State<GarageScreen> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<String?> _pickTargetCollection() async {
|
||||
final collections = await CollectionService.getMyCollections();
|
||||
if (!mounted) return null;
|
||||
|
||||
final candidates = collections
|
||||
.where((c) => c.id != widget.collectionId)
|
||||
.toList(growable: false);
|
||||
|
||||
if (candidates.isEmpty) {
|
||||
showGlobalSnackBar('No other collection available.', isError: true);
|
||||
return null;
|
||||
}
|
||||
|
||||
String? targetId;
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (_) => StatefulBuilder(
|
||||
builder: (context, setSheetState) => AlertDialog(
|
||||
title: const Text('Move Car'),
|
||||
content: DropdownButtonFormField<String>(
|
||||
initialValue: targetId,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Target collection',
|
||||
),
|
||||
items: candidates
|
||||
.map(
|
||||
(c) => DropdownMenuItem<String>(
|
||||
value: c.id,
|
||||
child: Text(c.name),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (value) => setSheetState(() => targetId = value),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: targetId == null
|
||||
? null
|
||||
: () => Navigator.pop(context, true),
|
||||
child: const Text('Move'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (confirmed != true || targetId == null) return null;
|
||||
return targetId;
|
||||
}
|
||||
|
||||
Future<void> _moveSingleCar(
|
||||
Map<String, dynamic> car, BuildContext sheetContext) async {
|
||||
try {
|
||||
final targetId = await _pickTargetCollection();
|
||||
if (targetId == null) return;
|
||||
|
||||
await supabase
|
||||
.from('hotwheels')
|
||||
.update({'collection_id': targetId})
|
||||
.eq('id', car['id']);
|
||||
|
||||
if (!mounted) return;
|
||||
if (sheetContext.mounted) Navigator.pop(sheetContext);
|
||||
showGlobalSnackBar('${car['hw_id']} moved to another collection.');
|
||||
await _loadCars(reset: true);
|
||||
} catch (e) {
|
||||
showGlobalSnackBar('Failed to move car: $e', isError: true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _deleteCar(
|
||||
Map<String, dynamic> car, BuildContext sheetContext) async {
|
||||
if (!sheetContext.mounted) return;
|
||||
|
|
|
|||
Loading…
Reference in a new issue