fix phone dashboard
This commit is contained in:
@@ -8,7 +8,7 @@ class AppConstants {
|
||||
|
||||
// Production
|
||||
static const String productionApiUrl = 'https://api.rentaldrivego.ma/api/v1';
|
||||
static const String productionDashboardUrl = 'https://rentaldrivego.ma/dashboard';
|
||||
static const String productionDashboardUrl = 'https://rentaldrivego.ma';
|
||||
|
||||
// Development
|
||||
static const String devAndroidApiUrl = 'http://10.0.2.2:4000/api/v1';
|
||||
@@ -18,14 +18,12 @@ class AppConstants {
|
||||
|
||||
static String get baseUrl {
|
||||
if (apiBaseUrlOverride.isNotEmpty) return apiBaseUrlOverride;
|
||||
if (Platform.isAndroid) return devAndroidApiUrl;
|
||||
return devLocalApiUrl;
|
||||
return productionApiUrl;
|
||||
}
|
||||
|
||||
static String get dashboardBaseUrl {
|
||||
if (dashboardUrlOverride.isNotEmpty) return dashboardUrlOverride;
|
||||
if (Platform.isAndroid) return devAndroidDashboardUrl;
|
||||
return devLocalDashboardUrl;
|
||||
return productionDashboardUrl;
|
||||
}
|
||||
|
||||
static String resolveMediaUrl(String url) {
|
||||
|
||||
+171
-32
@@ -1,40 +1,179 @@
|
||||
class DashboardMetrics {
|
||||
final int totalReservations;
|
||||
final int activeReservations;
|
||||
final double totalRevenue;
|
||||
final int totalVehicles;
|
||||
final int availableVehicles;
|
||||
final int totalCustomers;
|
||||
final double occupancyRate;
|
||||
final DashboardKpis kpis;
|
||||
final List<DashboardRecentReservation> recentReservations;
|
||||
final List<DashboardSourceBreakdown> sourceBreakdown;
|
||||
final DashboardSubscription? subscription;
|
||||
|
||||
const DashboardMetrics({
|
||||
required this.totalReservations,
|
||||
required this.activeReservations,
|
||||
required this.totalRevenue,
|
||||
required this.totalVehicles,
|
||||
required this.availableVehicles,
|
||||
required this.totalCustomers,
|
||||
required this.occupancyRate,
|
||||
required this.kpis,
|
||||
required this.recentReservations,
|
||||
required this.sourceBreakdown,
|
||||
required this.subscription,
|
||||
});
|
||||
|
||||
factory DashboardMetrics.fromJson(Map<String, dynamic> json) =>
|
||||
DashboardMetrics(
|
||||
totalReservations: json['totalReservations'] as int? ?? 0,
|
||||
activeReservations: json['activeReservations'] as int? ?? 0,
|
||||
totalRevenue: (json['totalRevenue'] as num?)?.toDouble() ?? 0,
|
||||
totalVehicles: json['totalVehicles'] as int? ?? 0,
|
||||
availableVehicles: json['availableVehicles'] as int? ?? 0,
|
||||
totalCustomers: json['totalCustomers'] as int? ?? 0,
|
||||
occupancyRate: (json['occupancyRate'] as num?)?.toDouble() ?? 0,
|
||||
);
|
||||
factory DashboardMetrics.fromJson(Map<String, dynamic> json) {
|
||||
final kpiJson = json['kpis'] as Map<String, dynamic>?;
|
||||
|
||||
factory DashboardMetrics.empty() => const DashboardMetrics(
|
||||
totalReservations: 0,
|
||||
activeReservations: 0,
|
||||
totalRevenue: 0,
|
||||
totalVehicles: 0,
|
||||
availableVehicles: 0,
|
||||
totalCustomers: 0,
|
||||
occupancyRate: 0,
|
||||
return DashboardMetrics(
|
||||
kpis: DashboardKpis.fromJson(kpiJson ?? json),
|
||||
recentReservations: (json['recentReservations'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) => DashboardRecentReservation.fromJson(
|
||||
e as Map<String, dynamic>,
|
||||
),
|
||||
)
|
||||
.toList() ??
|
||||
const [],
|
||||
sourceBreakdown: (json['sourceBreakdown'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) => DashboardSourceBreakdown.fromJson(
|
||||
e as Map<String, dynamic>,
|
||||
),
|
||||
)
|
||||
.toList() ??
|
||||
const [],
|
||||
subscription: json['subscription'] is Map<String, dynamic>
|
||||
? DashboardSubscription.fromJson(
|
||||
json['subscription'] as Map<String, dynamic>,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
factory DashboardMetrics.empty() => DashboardMetrics(
|
||||
kpis: DashboardKpis.empty(),
|
||||
recentReservations: const [],
|
||||
sourceBreakdown: const [],
|
||||
subscription: null,
|
||||
);
|
||||
}
|
||||
|
||||
class DashboardKpis {
|
||||
final int totalBookings;
|
||||
final int activeVehicles;
|
||||
final double monthlyRevenue;
|
||||
final int totalCustomers;
|
||||
final double bookingsChange;
|
||||
final double vehiclesChange;
|
||||
final double revenueChange;
|
||||
final double customersChange;
|
||||
|
||||
const DashboardKpis({
|
||||
required this.totalBookings,
|
||||
required this.activeVehicles,
|
||||
required this.monthlyRevenue,
|
||||
required this.totalCustomers,
|
||||
required this.bookingsChange,
|
||||
required this.vehiclesChange,
|
||||
required this.revenueChange,
|
||||
required this.customersChange,
|
||||
});
|
||||
|
||||
factory DashboardKpis.fromJson(Map<String, dynamic> json) => DashboardKpis(
|
||||
totalBookings:
|
||||
(json['totalBookings'] as num?)?.toInt() ??
|
||||
(json['totalReservations'] as num?)?.toInt() ??
|
||||
0,
|
||||
activeVehicles:
|
||||
(json['activeVehicles'] as num?)?.toInt() ??
|
||||
(json['availableVehicles'] as num?)?.toInt() ??
|
||||
(json['totalVehicles'] as num?)?.toInt() ??
|
||||
0,
|
||||
monthlyRevenue:
|
||||
(json['monthlyRevenue'] as num?)?.toDouble() ??
|
||||
(json['totalRevenue'] as num?)?.toDouble() ??
|
||||
0,
|
||||
totalCustomers: (json['totalCustomers'] as num?)?.toInt() ?? 0,
|
||||
bookingsChange: (json['bookingsChange'] as num?)?.toDouble() ?? 0,
|
||||
vehiclesChange: (json['vehiclesChange'] as num?)?.toDouble() ?? 0,
|
||||
revenueChange: (json['revenueChange'] as num?)?.toDouble() ?? 0,
|
||||
customersChange: (json['customersChange'] as num?)?.toDouble() ?? 0,
|
||||
);
|
||||
|
||||
factory DashboardKpis.empty() => const DashboardKpis(
|
||||
totalBookings: 0,
|
||||
activeVehicles: 0,
|
||||
monthlyRevenue: 0,
|
||||
totalCustomers: 0,
|
||||
bookingsChange: 0,
|
||||
vehiclesChange: 0,
|
||||
revenueChange: 0,
|
||||
customersChange: 0,
|
||||
);
|
||||
}
|
||||
|
||||
class DashboardRecentReservation {
|
||||
final String id;
|
||||
final String bookingRef;
|
||||
final String customerName;
|
||||
final String vehicleName;
|
||||
final DateTime startDate;
|
||||
final DateTime endDate;
|
||||
final String status;
|
||||
final double totalAmount;
|
||||
|
||||
const DashboardRecentReservation({
|
||||
required this.id,
|
||||
required this.bookingRef,
|
||||
required this.customerName,
|
||||
required this.vehicleName,
|
||||
required this.startDate,
|
||||
required this.endDate,
|
||||
required this.status,
|
||||
required this.totalAmount,
|
||||
});
|
||||
|
||||
factory DashboardRecentReservation.fromJson(Map<String, dynamic> json) =>
|
||||
DashboardRecentReservation(
|
||||
id: json['id'] as String,
|
||||
bookingRef: json['bookingRef'] as String? ?? '',
|
||||
customerName: json['customerName'] as String? ?? 'Unknown customer',
|
||||
vehicleName: json['vehicleName'] as String? ?? 'Unknown vehicle',
|
||||
startDate: DateTime.tryParse(json['startDate'] as String? ?? '') ??
|
||||
DateTime.now(),
|
||||
endDate: DateTime.tryParse(json['endDate'] as String? ?? '') ??
|
||||
DateTime.now(),
|
||||
status: json['status'] as String? ?? 'UNKNOWN',
|
||||
totalAmount: (json['totalAmount'] as num?)?.toDouble() ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
class DashboardSourceBreakdown {
|
||||
final String source;
|
||||
final int count;
|
||||
final double revenue;
|
||||
|
||||
const DashboardSourceBreakdown({
|
||||
required this.source,
|
||||
required this.count,
|
||||
required this.revenue,
|
||||
});
|
||||
|
||||
factory DashboardSourceBreakdown.fromJson(Map<String, dynamic> json) =>
|
||||
DashboardSourceBreakdown(
|
||||
source: json['source'] as String? ?? 'UNKNOWN',
|
||||
count: (json['count'] as num?)?.toInt() ?? 0,
|
||||
revenue: (json['revenue'] as num?)?.toDouble() ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
class DashboardSubscription {
|
||||
final String status;
|
||||
final String? planName;
|
||||
final DateTime? trialEndsAt;
|
||||
|
||||
const DashboardSubscription({
|
||||
required this.status,
|
||||
required this.planName,
|
||||
required this.trialEndsAt,
|
||||
});
|
||||
|
||||
factory DashboardSubscription.fromJson(Map<String, dynamic> json) =>
|
||||
DashboardSubscription(
|
||||
status: json['status'] as String? ?? 'ACTIVE',
|
||||
planName: json['planName'] as String?,
|
||||
trialEndsAt: json['trialEndsAt'] == null
|
||||
? null
|
||||
: DateTime.tryParse(json['trialEndsAt'] as String),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,12 +58,27 @@ class CustomerListResponse {
|
||||
required this.pageSize,
|
||||
});
|
||||
|
||||
factory CustomerListResponse.fromApi(dynamic json) {
|
||||
if (json is List<dynamic>) {
|
||||
final customers = json
|
||||
.map((e) => Customer.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
return CustomerListResponse(
|
||||
data: customers,
|
||||
total: customers.length,
|
||||
page: 1,
|
||||
pageSize: customers.length,
|
||||
);
|
||||
}
|
||||
return CustomerListResponse.fromJson(json as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
factory CustomerListResponse.fromJson(Map<String, dynamic> json) =>
|
||||
CustomerListResponse(
|
||||
data: (json['data'] as List<dynamic>)
|
||||
data: ((json['data'] ?? const <dynamic>[]) as List<dynamic>)
|
||||
.map((e) => Customer.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
total: json['total'] as int? ?? 0,
|
||||
total: json['total'] as int? ?? ((json['data'] as List<dynamic>?)?.length ?? 0),
|
||||
page: json['page'] as int? ?? 1,
|
||||
pageSize: json['pageSize'] as int? ?? 20,
|
||||
);
|
||||
|
||||
@@ -131,12 +131,27 @@ class ReservationListResponse {
|
||||
required this.pageSize,
|
||||
});
|
||||
|
||||
factory ReservationListResponse.fromApi(dynamic json) {
|
||||
if (json is List<dynamic>) {
|
||||
final reservations = json
|
||||
.map((e) => Reservation.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
return ReservationListResponse(
|
||||
data: reservations,
|
||||
total: reservations.length,
|
||||
page: 1,
|
||||
pageSize: reservations.length,
|
||||
);
|
||||
}
|
||||
return ReservationListResponse.fromJson(json as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
factory ReservationListResponse.fromJson(Map<String, dynamic> json) =>
|
||||
ReservationListResponse(
|
||||
data: (json['data'] as List<dynamic>)
|
||||
data: ((json['data'] ?? const <dynamic>[]) as List<dynamic>)
|
||||
.map((e) => Reservation.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
total: json['total'] as int? ?? 0,
|
||||
total: json['total'] as int? ?? ((json['data'] as List<dynamic>?)?.length ?? 0),
|
||||
page: json['page'] as int? ?? 1,
|
||||
pageSize: json['pageSize'] as int? ?? 20,
|
||||
);
|
||||
|
||||
@@ -72,12 +72,27 @@ class VehicleListResponse {
|
||||
required this.pageSize,
|
||||
});
|
||||
|
||||
factory VehicleListResponse.fromApi(dynamic json) {
|
||||
if (json is List<dynamic>) {
|
||||
final vehicles = json
|
||||
.map((e) => Vehicle.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
return VehicleListResponse(
|
||||
data: vehicles,
|
||||
total: vehicles.length,
|
||||
page: 1,
|
||||
pageSize: vehicles.length,
|
||||
);
|
||||
}
|
||||
return VehicleListResponse.fromJson(json as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
factory VehicleListResponse.fromJson(Map<String, dynamic> json) =>
|
||||
VehicleListResponse(
|
||||
data: (json['data'] as List<dynamic>)
|
||||
data: ((json['data'] ?? const <dynamic>[]) as List<dynamic>)
|
||||
.map((e) => Vehicle.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
total: json['total'] as int? ?? 0,
|
||||
total: json['total'] as int? ?? ((json['data'] as List<dynamic>?)?.length ?? 0),
|
||||
page: json['page'] as int? ?? 1,
|
||||
pageSize: json['pageSize'] as int? ?? 20,
|
||||
);
|
||||
|
||||
@@ -31,7 +31,6 @@ import '../../features/client/screens/booking_screen.dart';
|
||||
import '../../features/client/screens/booking_confirmation_screen.dart';
|
||||
import '../../features/client/screens/my_bookings_screen.dart';
|
||||
import '../../features/client/models/booking_result.dart';
|
||||
import '../../features/agency_webview/screens/agency_dashboard_webview_screen.dart';
|
||||
|
||||
final routerProvider = Provider<GoRouter>((ref) {
|
||||
final router = GoRouter(
|
||||
@@ -43,6 +42,7 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
final isSplash = loc == '/splash';
|
||||
final isLanding = loc == '/landing';
|
||||
final isLogin = loc.startsWith('/login');
|
||||
final isAgency = loc == '/agency';
|
||||
|
||||
if (status == AuthStatus.unknown) {
|
||||
return isSplash ? null : '/splash';
|
||||
@@ -57,12 +57,20 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
return '/landing';
|
||||
}
|
||||
|
||||
if (isAgency) {
|
||||
if (status == AuthStatus.authenticated) {
|
||||
return auth.userType == AppConstants.userTypeEmployee
|
||||
? '/dashboard'
|
||||
: '/client';
|
||||
}
|
||||
return '/login/employee';
|
||||
}
|
||||
|
||||
if (status == AuthStatus.unauthenticated) {
|
||||
if (isLanding ||
|
||||
isLogin ||
|
||||
loc.startsWith('/client') ||
|
||||
loc == '/role' ||
|
||||
loc == '/agency') {
|
||||
loc == '/role') {
|
||||
return null;
|
||||
}
|
||||
return '/landing';
|
||||
@@ -90,6 +98,10 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
path: '/dashboard/vehicles',
|
||||
builder: (context, _) => const VehiclesScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/dashboard/fleet',
|
||||
builder: (context, _) => const VehiclesScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/dashboard/reservations',
|
||||
builder: (context, _) => const ReservationsScreen(),
|
||||
@@ -103,6 +115,11 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
builder: (_, state) =>
|
||||
VehicleDetailScreen(id: state.pathParameters['id']!),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/dashboard/fleet/:id',
|
||||
builder: (_, state) =>
|
||||
VehicleDetailScreen(id: state.pathParameters['id']!),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/dashboard/reservations/:id',
|
||||
builder: (_, state) =>
|
||||
@@ -178,7 +195,7 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
),
|
||||
GoRoute(
|
||||
path: '/agency',
|
||||
builder: (context, _) => const AgencyDashboardWebViewScreen(),
|
||||
builder: (context, _) => const EmployeeLoginScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/dashboard/reservations/new',
|
||||
@@ -188,12 +205,22 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
path: '/dashboard/vehicles/new',
|
||||
builder: (context, _) => const VehicleFormScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/dashboard/fleet/new',
|
||||
builder: (context, _) => const VehicleFormScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/dashboard/vehicles/:id/edit',
|
||||
builder: (_, state) => VehicleFormScreen(
|
||||
vehicle: state.extra as dynamic,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/dashboard/fleet/:id/edit',
|
||||
builder: (_, state) => VehicleFormScreen(
|
||||
vehicle: state.extra as dynamic,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/dashboard/customers/new',
|
||||
builder: (context, _) => const CustomerFormScreen(),
|
||||
|
||||
@@ -15,7 +15,7 @@ class CustomerService {
|
||||
'pageSize': pageSize,
|
||||
if (search != null && search.isNotEmpty) 'search': search,
|
||||
});
|
||||
return CustomerListResponse.fromJson(res.data as Map<String, dynamic>);
|
||||
return CustomerListResponse.fromApi(res.data);
|
||||
}
|
||||
|
||||
Future<Customer> getCustomer(String id) async {
|
||||
|
||||
@@ -52,7 +52,7 @@ class ReservationService {
|
||||
if (search != null && search.isNotEmpty) 'search': search,
|
||||
'customerId': ?customerId,
|
||||
});
|
||||
return ReservationListResponse.fromJson(res.data as Map<String, dynamic>);
|
||||
return ReservationListResponse.fromApi(res.data);
|
||||
}
|
||||
|
||||
Future<Reservation> getReservation(String id) async {
|
||||
|
||||
@@ -71,15 +71,14 @@ class VehicleService {
|
||||
int page = 1,
|
||||
int pageSize = 20,
|
||||
String? status,
|
||||
String? search,
|
||||
}) async {
|
||||
final res = await _client.get('/vehicles', params: {
|
||||
'page': page,
|
||||
'pageSize': pageSize,
|
||||
final params = <String, dynamic>{
|
||||
if (page != 1) 'page': page,
|
||||
if (pageSize != 20) 'pageSize': pageSize,
|
||||
'status': ?status,
|
||||
if (search != null && search.isNotEmpty) 'search': search,
|
||||
});
|
||||
return VehicleListResponse.fromJson(res.data as Map<String, dynamic>);
|
||||
};
|
||||
final res = await _client.get('/vehicles', params: params);
|
||||
return VehicleListResponse.fromApi(res.data);
|
||||
}
|
||||
|
||||
Future<Vehicle> getVehicle(String id) async {
|
||||
|
||||
Reference in New Issue
Block a user