feat(validation): add garage-side community confirmation action

This commit is contained in:
Lukas Müllner 2026-03-04 11:34:13 +01:00
parent b26bcf6226
commit d3f6fc3853

View file

@ -93,7 +93,7 @@ class GarageScreenState extends State<GarageScreen> {
final data = await supabase final data = await supabase
.from('hotwheels') .from('hotwheels')
.select( .select(
'id, created_at, hw_id, notes, user_image_url, global_cars(name, series, year, color)') 'id, created_at, hw_id, notes, user_image_url, global_cars(name, series, year, color, is_verified, confirmation_count)')
.eq('collection_id', widget.collectionId) .eq('collection_id', widget.collectionId)
.order('created_at', ascending: false) .order('created_at', ascending: false)
.range(from, to); .range(from, to);
@ -476,6 +476,8 @@ class GarageScreenState extends State<GarageScreen> {
final name = global?['name'] as String?; final name = global?['name'] as String?;
final series = global?['series'] as String?; final series = global?['series'] as String?;
final year = global?['year'] as int?; final year = global?['year'] as int?;
final verified = global?['is_verified'] == true;
final confirmations = (global?['confirmation_count'] as num?)?.toInt() ?? 0;
final notes = car['notes'] as String?; final notes = car['notes'] as String?;
final imageUrl = car['signed_image_url'] as String?; final imageUrl = car['signed_image_url'] as String?;
@ -597,11 +599,35 @@ class GarageScreenState extends State<GarageScreen> {
_DetailRow(icon: Icons.collections, label: 'Series', value: series), _DetailRow(icon: Icons.collections, label: 'Series', value: series),
if (year != null) if (year != null)
_DetailRow(icon: Icons.calendar_today, label: 'Year', value: '$year'), _DetailRow(icon: Icons.calendar_today, label: 'Year', value: '$year'),
_DetailRow(
icon: verified ? Icons.verified : Icons.hourglass_bottom,
label: 'Validation',
value: verified
? 'Verified by community'
: 'Pending verification',
),
_DetailRow(
icon: Icons.how_to_vote,
label: 'Confirmations',
value: '$confirmations',
),
if (notes != null && notes.isNotEmpty) if (notes != null && notes.isNotEmpty)
_DetailRow(icon: Icons.notes, label: 'Notes', value: notes), _DetailRow(icon: Icons.notes, label: 'Notes', value: notes),
const SizedBox(height: 24), const SizedBox(height: 24),
if (!verified) ...[
SizedBox(
width: double.infinity,
child: OutlinedButton.icon(
onPressed: () => _confirmCatalogEntry(hwId, context),
icon: const Icon(Icons.thumb_up_alt_outlined, size: 18),
label: const Text('Confirm Catalog Entry'),
),
),
const SizedBox(height: 10),
],
// Edit & Delete buttons // Edit & Delete buttons
Row( Row(
children: [ children: [
@ -730,6 +756,36 @@ class GarageScreenState extends State<GarageScreen> {
} }
} }
Future<void> _confirmCatalogEntry(String hwId, BuildContext sheetContext) async {
final user = supabase.auth.currentUser;
if (user == null) return;
try {
final existingVote = await supabase
.from('car_votes')
.select('id')
.eq('hw_id', hwId)
.eq('user_id', user.id)
.maybeSingle();
if (existingVote != null) {
showGlobalSnackBar('You already confirmed this catalog entry.');
return;
}
await supabase.from('car_votes').insert({
'hw_id': hwId,
'user_id': user.id,
});
showGlobalSnackBar('Thanks! Your validation vote was recorded.');
if (sheetContext.mounted) Navigator.pop(sheetContext);
_loadCars(reset: true);
} catch (e) {
showGlobalSnackBar('Failed to submit validation vote: $e', isError: true);
}
}
Future<String?> _pickTargetCollection() async { Future<String?> _pickTargetCollection() async {
final collections = await CollectionService.getMyCollections(); final collections = await CollectionService.getMyCollections();
if (!mounted) return null; if (!mounted) return null;