204 lines
7.6 KiB
Dart
204 lines
7.6 KiB
Dart
import 'package:cached_network_image/cached_network_image.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import '../providers/dashboard_providers.dart';
|
|
import '../../../core/constants/app_constants.dart';
|
|
import '../../../widgets/status_badge.dart';
|
|
import '../../../widgets/error_view.dart';
|
|
|
|
class VehicleDetailScreen extends ConsumerWidget {
|
|
final String id;
|
|
|
|
const VehicleDetailScreen({super.key, required this.id});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final vehicleAsync = ref.watch(vehicleDetailProvider(id));
|
|
|
|
return vehicleAsync.when(
|
|
loading: () => const Scaffold(
|
|
body: Center(child: CircularProgressIndicator()),
|
|
),
|
|
error: (e, _) => Scaffold(
|
|
appBar: AppBar(),
|
|
body: ErrorView(
|
|
message: 'Could not load vehicle',
|
|
onRetry: () => ref.invalidate(vehicleDetailProvider(id)),
|
|
),
|
|
),
|
|
data: (vehicle) {
|
|
final baseUrl =
|
|
AppConstants.baseUrl.replaceAll('/api/v1', '');
|
|
return Scaffold(
|
|
body: CustomScrollView(
|
|
slivers: [
|
|
SliverAppBar(
|
|
expandedHeight: 240,
|
|
pinned: true,
|
|
flexibleSpace: FlexibleSpaceBar(
|
|
background: vehicle.primaryPhoto != null
|
|
? CachedNetworkImage(
|
|
imageUrl: vehicle.primaryPhoto!.startsWith('http')
|
|
? vehicle.primaryPhoto!
|
|
: '$baseUrl${vehicle.primaryPhoto!}',
|
|
fit: BoxFit.cover,
|
|
errorWidget: (_, _, _) => Container(
|
|
color: const Color(0xFFF3F4F6),
|
|
child: const Icon(Icons.directions_car,
|
|
size: 72, color: Color(0xFFD1D5DB)),
|
|
),
|
|
)
|
|
: Container(
|
|
color: const Color(0xFFF3F4F6),
|
|
child: const Icon(Icons.directions_car,
|
|
size: 72, color: Color(0xFFD1D5DB)),
|
|
),
|
|
),
|
|
),
|
|
SliverPadding(
|
|
padding: const EdgeInsets.all(20),
|
|
sliver: SliverList(
|
|
delegate: SliverChildListDelegate([
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
vehicle.displayName,
|
|
style: const TextStyle(
|
|
fontSize: 22,
|
|
fontWeight: FontWeight.bold,
|
|
color: Color(0xFF111928),
|
|
),
|
|
),
|
|
),
|
|
StatusBadge(status: vehicle.status),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
vehicle.licensePlate,
|
|
style: const TextStyle(
|
|
fontSize: 15, color: Color(0xFF6B7280)),
|
|
),
|
|
const SizedBox(height: 20),
|
|
_InfoGrid(vehicle: vehicle),
|
|
const SizedBox(height: 20),
|
|
const Text(
|
|
'Pricing',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: Color(0xFF111928),
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Card(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Row(
|
|
children: [
|
|
const Icon(Icons.attach_money,
|
|
color: Color(0xFF1A56DB)),
|
|
const SizedBox(width: 8),
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'\$${vehicle.dailyRate.toStringAsFixed(2)}/day',
|
|
style: const TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
color: Color(0xFF1A56DB),
|
|
),
|
|
),
|
|
const Text(
|
|
'Daily rate',
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Color(0xFF6B7280)),
|
|
),
|
|
],
|
|
),
|
|
const Spacer(),
|
|
Switch(
|
|
value: vehicle.isPublished,
|
|
onChanged: (v) async {
|
|
try {
|
|
await ref
|
|
.read(vehicleServiceProvider)
|
|
.togglePublish(id, v);
|
|
ref.invalidate(vehicleDetailProvider(id));
|
|
} catch (_) {}
|
|
},
|
|
),
|
|
const Text(
|
|
'Published',
|
|
style: TextStyle(
|
|
fontSize: 12, color: Color(0xFF6B7280)),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
]),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class _InfoGrid extends StatelessWidget {
|
|
final dynamic vehicle;
|
|
|
|
const _InfoGrid({required this.vehicle});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final items = [
|
|
('Category', vehicle.category),
|
|
if (vehicle.seats != null) ('Seats', '${vehicle.seats}'),
|
|
if (vehicle.transmission != null) ('Transmission', vehicle.transmission!),
|
|
if (vehicle.fuelType != null) ('Fuel', vehicle.fuelType!),
|
|
if (vehicle.mileage != null)
|
|
('Mileage', '${vehicle.mileage} km'),
|
|
];
|
|
|
|
return Wrap(
|
|
spacing: 12,
|
|
runSpacing: 12,
|
|
children: items
|
|
.map((item) => Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 14, vertical: 10),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF3F4F6),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
item.$1,
|
|
style: const TextStyle(
|
|
fontSize: 11, color: Color(0xFF9CA3AF)),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
item.$2,
|
|
style: const TextStyle(
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF111928),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
))
|
|
.toList(),
|
|
);
|
|
}
|
|
}
|