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
|
// Edit & Delete buttons
|
||||||
Row(
|
Row(
|
||||||
children: [
|
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(
|
Expanded(
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: () => _editCar(car, context),
|
onPressed: () => _editCar(car, context),
|
||||||
|
|
@ -608,7 +616,7 @@ class GarageScreenState extends State<GarageScreen> {
|
||||||
label: const Text('Edit Details'),
|
label: const Text('Edit Details'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: OutlinedButton.icon(
|
child: OutlinedButton.icon(
|
||||||
onPressed: () => _deleteCar(car, context),
|
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(
|
Future<void> _deleteCar(
|
||||||
Map<String, dynamic> car, BuildContext sheetContext) async {
|
Map<String, dynamic> car, BuildContext sheetContext) async {
|
||||||
if (!sheetContext.mounted) return;
|
if (!sheetContext.mounted) return;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue