Compare commits

..

No commits in common. "5714fd84433845fd8730d18d6b200399afc52135" and "3f0310d856adb105ef6a14f9e0b1c1d30c3899dc" have entirely different histories.

12 changed files with 234 additions and 1197 deletions

View File

@ -1,200 +0,0 @@
import 'package:flutter/material.dart';
import '../models/delivery_route.dart';
import '../theme/spacing_system.dart';
import '../theme/size_system.dart';
import '../theme/animation_system.dart';
import '../theme/color_system.dart';
import '../utils/breakpoints.dart';
import 'glassmorphic_route_card.dart';
class CollapsibleRoutesSidebar extends StatefulWidget {
final List<DeliveryRoute> routes;
final DeliveryRoute? selectedRoute;
final ValueChanged<DeliveryRoute> onRouteSelected;
const CollapsibleRoutesSidebar({
super.key,
required this.routes,
this.selectedRoute,
required this.onRouteSelected,
});
@override
State<CollapsibleRoutesSidebar> createState() =>
_CollapsibleRoutesSidebarState();
}
class _CollapsibleRoutesSidebarState extends State<CollapsibleRoutesSidebar>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
bool _isExpanded = true;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
if (_isExpanded) {
_animationController.forward();
}
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
void _toggleSidebar() {
setState(() {
_isExpanded = !_isExpanded;
});
if (_isExpanded) {
_animationController.forward();
} else {
_animationController.reverse();
}
}
@override
Widget build(BuildContext context) {
final isMobile = context.isMobile;
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
// On mobile, always show as collapsible
if (isMobile) {
return Container(
color: isDarkMode ? SvrntyColors.almostBlack : Colors.white,
child: Column(
children: [
// Header with toggle button
Container(
padding: EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: isDarkMode ? SvrntyColors.darkSlate : SvrntyColors.slateGray.withValues(alpha: 0.2),
width: 1,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (_isExpanded)
Text(
'Routes',
style: Theme.of(context).textTheme.titleMedium,
),
IconButton(
icon: AnimatedRotation(
turns: _isExpanded ? 0 : -0.25,
duration: Duration(
milliseconds: AppAnimations.durationFast.inMilliseconds,
),
child: const Icon(Icons.chevron_right),
),
onPressed: _toggleSidebar,
iconSize: AppSizes.iconMd,
),
],
),
),
// Collapsible content
if (_isExpanded)
Expanded(
child: _buildRoutesList(context),
),
],
),
);
}
// On tablet/desktop, show full sidebar with toggle
return Container(
width: _isExpanded ? 280 : 64,
color: isDarkMode ? SvrntyColors.almostBlack : Colors.white,
child: Column(
children: [
// Header with toggle button
Container(
height: kToolbarHeight,
padding: EdgeInsets.symmetric(horizontal: AppSpacing.xs),
decoration: BoxDecoration(
border: Border(
left: BorderSide(
color: isDarkMode ? SvrntyColors.darkSlate : SvrntyColors.slateGray.withValues(alpha: 0.2),
width: 1,
),
),
),
child: Row(
mainAxisAlignment: _isExpanded ? MainAxisAlignment.spaceBetween : MainAxisAlignment.center,
children: [
if (_isExpanded)
Expanded(
child: Text(
'Routes',
style: Theme.of(context).textTheme.titleMedium,
overflow: TextOverflow.ellipsis,
),
),
SizedBox(
width: AppSizes.buttonHeightMd,
height: AppSizes.buttonHeightMd,
child: IconButton(
icon: AnimatedRotation(
turns: _isExpanded ? 0 : -0.5,
duration: Duration(
milliseconds: AppAnimations.durationFast.inMilliseconds,
),
child: const Icon(Icons.chevron_right),
),
onPressed: _toggleSidebar,
iconSize: AppSizes.iconMd,
),
),
],
),
),
// Routes list
Expanded(
child: _buildRoutesList(context),
),
],
),
);
}
Widget _buildRoutesList(BuildContext context) {
return ListView.builder(
padding: EdgeInsets.all(AppSpacing.sm),
itemCount: widget.routes.length,
itemBuilder: (context, index) {
final route = widget.routes[index];
final isSelected = widget.selectedRoute?.id == route.id;
return Padding(
padding: EdgeInsets.only(bottom: AppSpacing.sm),
child: _buildRouteButton(context, route, isSelected),
);
},
);
}
Widget _buildRouteButton(
BuildContext context,
DeliveryRoute route,
bool isSelected,
) {
return GlassmorphicRouteCard(
route: route,
isSelected: isSelected,
isCollapsed: !_isExpanded,
onTap: () => widget.onRouteSelected(route),
);
}
}

View File

@ -302,7 +302,7 @@ class _DarkModeMapComponentState extends State<DarkModeMapComponent> {
label: 'Stop', label: 'Stop',
icon: Icons.stop, icon: Icons.stop,
onPressed: _stopNavigation, onPressed: _stopNavigation,
color: Theme.of(context).colorScheme.onSurface, color: Colors.grey[600]!,
), ),
], ],
), ),
@ -315,7 +315,9 @@ class _DarkModeMapComponentState extends State<DarkModeMapComponent> {
right: 0, right: 0,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface, color: Theme.of(context).brightness == Brightness.dark
? const Color(0xFF14161A)
: Colors.white,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.2), color: Colors.black.withOpacity(0.2),
@ -351,7 +353,13 @@ class _DarkModeMapComponentState extends State<DarkModeMapComponent> {
'No address', 'No address',
style: Theme.of(context) style: Theme.of(context)
.textTheme .textTheme
.bodySmall, .bodySmall
?.copyWith(
color: Theme.of(context).brightness ==
Brightness.dark
? Colors.grey[400]
: Colors.grey[600],
),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),

View File

@ -70,10 +70,9 @@ class _DeliveryListItemState extends State<DeliveryListItem>
} }
Color _getStatusColor(Delivery delivery) { Color _getStatusColor(Delivery delivery) {
if (delivery.isSkipped == true) return SvrntyColors.statusCancelled; if (delivery.isSkipped == true) return SvrntyColors.statusSkipped;
if (delivery.delivered == true) return SvrntyColors.statusCompleted; if (delivery.delivered == true) return SvrntyColors.statusCompleted;
// Default: in-transit or pending deliveries return SvrntyColors.statusPending;
return SvrntyColors.statusInTransit;
} }
IconData _getStatusIcon(Delivery delivery) { IconData _getStatusIcon(Delivery delivery) {
@ -113,7 +112,9 @@ class _DeliveryListItemState extends State<DeliveryListItem>
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
color: _isHovered || widget.isSelected color: _isHovered || widget.isSelected
? Theme.of(context).colorScheme.surfaceContainer ? (isDark
? Colors.grey[800]
: Colors.grey[100])
: Colors.transparent, : Colors.transparent,
boxShadow: _isHovered || widget.isSelected boxShadow: _isHovered || widget.isSelected
? [ ? [
@ -169,7 +170,12 @@ class _DeliveryListItemState extends State<DeliveryListItem>
'No address', 'No address',
style: Theme.of(context) style: Theme.of(context)
.textTheme .textTheme
.bodySmall, .bodySmall
?.copyWith(
color: isDark
? Colors.grey[400]
: Colors.grey[600],
),
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@ -182,6 +188,9 @@ class _DeliveryListItemState extends State<DeliveryListItem>
.labelSmall .labelSmall
?.copyWith( ?.copyWith(
fontSize: 10, fontSize: 10,
color: isDark
? Colors.grey[500]
: Colors.grey[500],
), ),
), ),
], ],
@ -237,12 +246,12 @@ class _DeliveryListItemState extends State<DeliveryListItem>
vertical: 4, vertical: 4,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHighest, color: Colors.grey[300],
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(6),
), ),
child: Icon( child: Icon(
Icons.phone, Icons.phone,
color: Theme.of(context).colorScheme.onSurface, color: Colors.grey[700],
size: 14, size: 14,
), ),
), ),
@ -264,7 +273,9 @@ class _DeliveryListItemState extends State<DeliveryListItem>
.textTheme .textTheme
.labelSmall .labelSmall
?.copyWith( ?.copyWith(
color: SvrntyColors.warning, color: isDark
? Colors.amber[300]
: Colors.amber[700],
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
), ),

View File

@ -1,330 +0,0 @@
import 'package:flutter/material.dart';
import 'dart:ui' as ui;
import '../models/delivery_route.dart';
import '../theme/spacing_system.dart';
import '../theme/size_system.dart';
import '../theme/animation_system.dart';
import '../theme/color_system.dart';
/// Modern glassmorphic route card with status-based gradient and animated progress
class GlassmorphicRouteCard extends StatefulWidget {
final DeliveryRoute route;
final bool isSelected;
final VoidCallback onTap;
final bool isCollapsed;
const GlassmorphicRouteCard({
super.key,
required this.route,
required this.isSelected,
required this.onTap,
this.isCollapsed = false,
});
@override
State<GlassmorphicRouteCard> createState() => _GlassmorphicRouteCardState();
}
class _GlassmorphicRouteCardState extends State<GlassmorphicRouteCard>
with SingleTickerProviderStateMixin {
late AnimationController _hoverController;
bool _isHovered = false;
@override
void initState() {
super.initState();
_hoverController = AnimationController(
duration: Duration(milliseconds: AppAnimations.durationFast.inMilliseconds),
vsync: this,
);
}
@override
void dispose() {
_hoverController.dispose();
super.dispose();
}
/// Calculate color based on completion percentage
Color _getProgressColor(double progress) {
if (progress < 0.3) {
// Red to orange (0-30%)
return Color.lerp(
SvrntyColors.crimsonRed,
const Color(0xFFFF9800),
(progress / 0.3),
)!;
} else if (progress < 0.7) {
// Orange to yellow (30-70%)
return Color.lerp(
const Color(0xFFFF9800),
const Color(0xFFFFC107),
((progress - 0.3) / 0.4),
)!;
} else {
// Yellow to green (70-100%)
return Color.lerp(
const Color(0xFFFFC107),
const Color(0xFF4CAF50),
((progress - 0.7) / 0.3),
)!;
}
}
void _setHovered(bool hovered) {
setState(() {
_isHovered = hovered;
});
if (hovered) {
_hoverController.forward();
} else {
_hoverController.reverse();
}
}
@override
Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
final progress = widget.route.deliveredCount / widget.route.deliveriesCount;
final progressColor = _getProgressColor(progress);
if (widget.isCollapsed) {
return _buildCollapsedCard(context, isDarkMode, progress, progressColor);
}
return _buildExpandedCard(context, isDarkMode, progress, progressColor);
}
Widget _buildCollapsedCard(BuildContext context, bool isDarkMode,
double progress, Color progressColor) {
return MouseRegion(
onEnter: (_) => _setHovered(true),
onExit: (_) => _setHovered(false),
child: GestureDetector(
onTap: widget.onTap,
child: AnimatedContainer(
duration: Duration(
milliseconds: AppAnimations.durationFast.inMilliseconds,
),
height: AppSizes.buttonHeightMd,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(AppSpacing.md),
),
child: Stack(
alignment: Alignment.center,
children: [
// Glassmorphic background
ClipRRect(
borderRadius: BorderRadius.circular(AppSpacing.md),
child: BackdropFilter(
filter: ui.ImageFilter.blur(sigmaX: 8, sigmaY: 8),
child: Container(
decoration: BoxDecoration(
color: (isDarkMode
? SvrntyColors.darkSlate
: Colors.white)
.withValues(alpha: 0.7),
border: Border.all(
color: (isDarkMode ? Colors.white : Colors.white)
.withValues(alpha: 0.2),
width: 1.5,
),
),
),
),
),
// Progress indicator at bottom
Positioned(
bottom: 0,
left: 0,
right: 0,
child: ClipRRect(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(AppSpacing.md - AppSpacing.xs),
bottomRight: Radius.circular(AppSpacing.md - AppSpacing.xs),
),
child: LinearProgressIndicator(
value: progress,
minHeight: 3,
backgroundColor: progressColor.withValues(alpha: 0.2),
valueColor: AlwaysStoppedAnimation<Color>(progressColor),
),
),
),
// Content
Center(
child: Text(
widget.route.name.substring(0, 1).toUpperCase(),
style: Theme.of(context).textTheme.labelLarge?.copyWith(
fontWeight: FontWeight.bold,
fontSize: 16,
color: widget.isSelected ? progressColor : null,
),
),
),
],
),
),
),
);
}
Widget _buildExpandedCard(BuildContext context, bool isDarkMode,
double progress, Color progressColor) {
return MouseRegion(
onEnter: (_) => _setHovered(true),
onExit: (_) => _setHovered(false),
child: GestureDetector(
onTap: widget.onTap,
child: AnimatedBuilder(
animation: _hoverController,
builder: (context, child) {
final hoverValue = _hoverController.value;
final blurSigma = 8 + (hoverValue * 3);
final bgOpacity = 0.7 + (hoverValue * 0.1);
return AnimatedContainer(
duration: Duration(
milliseconds: AppAnimations.durationFast.inMilliseconds,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(AppSpacing.lg),
boxShadow: [
BoxShadow(
color: progressColor.withValues(alpha: 0.1 + (hoverValue * 0.2)),
blurRadius: 12 + (hoverValue * 8),
offset: Offset(0, 2 + (hoverValue * 2)),
),
],
),
child: Stack(
children: [
// Glassmorphic background
ClipRRect(
borderRadius: BorderRadius.circular(AppSpacing.lg),
child: BackdropFilter(
filter: ui.ImageFilter.blur(sigmaX: blurSigma, sigmaY: blurSigma),
child: Container(
decoration: BoxDecoration(
color: (isDarkMode
? SvrntyColors.darkSlate
: Colors.white)
.withValues(alpha: bgOpacity),
border: Border.all(
color: (isDarkMode ? Colors.white : Colors.white)
.withValues(alpha: 0.2 + (hoverValue * 0.15)),
width: 1.5,
),
borderRadius: BorderRadius.circular(AppSpacing.lg),
),
),
),
),
// Content
Padding(
padding: EdgeInsets.all(AppSpacing.md),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// Route name
Text(
widget.route.name,
style: Theme.of(context)
.textTheme
.labelLarge
?.copyWith(
color: widget.isSelected ? progressColor : null,
fontWeight: FontWeight.bold,
fontSize: 14,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: AppSpacing.xs),
// Delivery count
RichText(
text: TextSpan(
children: [
TextSpan(
text: '${widget.route.deliveredCount}',
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(
color: progressColor,
fontWeight: FontWeight.w600,
fontSize: 11,
),
),
TextSpan(
text: '/${widget.route.deliveriesCount}',
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(
fontSize: 11,
color: (Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black)
.withValues(alpha: 0.6),
),
),
],
),
),
SizedBox(height: AppSpacing.sm),
// Animated gradient progress bar
ClipRRect(
borderRadius: BorderRadius.circular(3),
child: Stack(
children: [
// Background
Container(
height: 6,
decoration: BoxDecoration(
color: progressColor
.withValues(alpha: 0.15),
),
),
// Progress fill with gradient
ClipRRect(
borderRadius: BorderRadius.circular(3),
child: Container(
height: 6,
width: 100 * progress,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
SvrntyColors.crimsonRed,
progressColor,
],
),
borderRadius: BorderRadius.circular(3),
boxShadow: [
BoxShadow(
color: progressColor
.withValues(alpha: 0.4),
blurRadius: 4,
),
],
),
),
),
],
),
),
],
),
),
],
),
);
},
),
),
);
}
}

View File

@ -93,7 +93,7 @@ class _PremiumRouteCardState extends State<PremiumRouteCard>
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
color: Theme.of(context).colorScheme.surface, color: isDark ? const Color(0xFF14161A) : const Color(0xFFFAFAFC),
border: Border( border: Border(
left: BorderSide(color: accentColor, width: 4), left: BorderSide(color: accentColor, width: 4),
), ),
@ -134,7 +134,7 @@ class _PremiumRouteCardState extends State<PremiumRouteCard>
TextSpan( TextSpan(
text: ' completed', text: ' completed',
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).textTheme.bodySmall?.color, color: isDark ? Colors.grey[400] : Colors.grey[600],
), ),
), ),
], ],
@ -167,6 +167,7 @@ class _PremiumRouteCardState extends State<PremiumRouteCard>
Text( Text(
'$progressPercentage% progress', '$progressPercentage% progress',
style: Theme.of(context).textTheme.labelSmall?.copyWith( style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: isDark ? Colors.grey[400] : Colors.grey[600],
fontSize: 11, fontSize: 11,
letterSpacing: 0.3, letterSpacing: 0.3,
), ),
@ -178,7 +179,7 @@ class _PremiumRouteCardState extends State<PremiumRouteCard>
height: 6, height: 6,
child: LinearProgressIndicator( child: LinearProgressIndicator(
value: widget.route.progress, value: widget.route.progress,
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest, backgroundColor: isDark ? Colors.grey[800] : Colors.grey[200],
valueColor: AlwaysStoppedAnimation<Color>(accentColor), valueColor: AlwaysStoppedAnimation<Color>(accentColor),
), ),
), ),

View File

@ -2,17 +2,15 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../models/delivery.dart'; import '../models/delivery.dart';
import '../models/delivery_route.dart';
import '../providers/providers.dart'; import '../providers/providers.dart';
import '../api/client.dart'; import '../api/client.dart';
import '../api/openapi_config.dart'; import '../api/openapi_config.dart';
import '../models/delivery_commands.dart'; import '../models/delivery_commands.dart';
import '../utils/breakpoints.dart'; import '../utils/breakpoints.dart';
import '../utils/responsive.dart';
import '../components/map_sidebar_layout.dart'; import '../components/map_sidebar_layout.dart';
import '../components/dark_mode_map.dart'; import '../components/dark_mode_map.dart';
import '../components/delivery_list_item.dart'; import '../components/delivery_list_item.dart';
import '../components/collapsible_routes_sidebar.dart'
show CollapsibleRoutesSidebar;
class DeliveriesPage extends ConsumerStatefulWidget { class DeliveriesPage extends ConsumerStatefulWidget {
final int routeFragmentId; final int routeFragmentId;
@ -48,7 +46,6 @@ class _DeliveriesPageState extends ConsumerState<DeliveriesPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final deliveriesData = ref.watch(deliveriesProvider(widget.routeFragmentId)); final deliveriesData = ref.watch(deliveriesProvider(widget.routeFragmentId));
final routesData = ref.watch(deliveryRoutesProvider);
final token = ref.watch(authTokenProvider).valueOrNull; final token = ref.watch(authTokenProvider).valueOrNull;
return Scaffold( return Scaffold(
@ -65,38 +62,7 @@ class _DeliveriesPageState extends ConsumerState<DeliveriesPage> {
.where((d) => d.delivered) .where((d) => d.delivered)
.toList(); .toList();
return routesData.when( return MapSidebarLayout(
data: (routes) {
DeliveryRoute? currentRoute;
try {
currentRoute = routes.firstWhere(
(r) => r.id == widget.routeFragmentId,
);
} catch (_) {
currentRoute = routes.isNotEmpty ? routes.first : null;
}
return Row(
children: [
if (context.isDesktop && routes.isNotEmpty)
CollapsibleRoutesSidebar(
routes: routes,
selectedRoute: currentRoute,
onRouteSelected: (route) {
if (route.id != widget.routeFragmentId) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => DeliveriesPage(
routeFragmentId: route.id,
routeName: route.name,
),
),
);
}
},
),
Expanded(
child: MapSidebarLayout(
mapWidget: DarkModeMapComponent( mapWidget: DarkModeMapComponent(
deliveries: deliveries, deliveries: deliveries,
selectedDelivery: _selectedDelivery, selectedDelivery: _selectedDelivery,
@ -170,89 +136,6 @@ class _DeliveriesPageState extends ConsumerState<DeliveriesPage> {
), ),
], ],
), ),
),
),
],
);
},
loading: () => const Center(
child: CircularProgressIndicator(),
),
error: (error, stackTrace) => MapSidebarLayout(
mapWidget: DarkModeMapComponent(
deliveries: deliveries,
selectedDelivery: _selectedDelivery,
onDeliverySelected: (delivery) {
setState(() {
_selectedDelivery = delivery;
});
},
),
sidebarWidget: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: SegmentedButton<int>(
segments: const [
ButtonSegment(
value: 0,
label: Text('To Do'),
),
ButtonSegment(
value: 1,
label: Text('Delivered'),
),
],
selected: <int>{_currentSegment},
onSelectionChanged: (Set<int> newSelection) {
setState(() {
_currentSegment = newSelection.first;
_pageController.animateToPage(
_currentSegment,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
});
},
),
),
Expanded(
child: PageView(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_currentSegment = index;
});
},
children: [
DeliveryListView(
deliveries: todoDeliveries,
selectedDelivery: _selectedDelivery,
onDeliverySelected: (delivery) {
setState(() {
_selectedDelivery = delivery;
});
},
onAction: (delivery, action) =>
_handleDeliveryAction(context, delivery, action, token),
),
DeliveryListView(
deliveries: completedDeliveries,
selectedDelivery: _selectedDelivery,
onDeliverySelected: (delivery) {
setState(() {
_selectedDelivery = delivery;
});
},
onAction: (delivery, action) =>
_handleDeliveryAction(context, delivery, action, token),
),
],
),
),
],
),
),
); );
}, },
loading: () => const Center( loading: () => const Center(
@ -457,7 +340,7 @@ class DeliveryCard extends StatelessWidget {
else if (order?.isNewCustomer ?? false) else if (order?.isNewCustomer ?? false)
Chip( Chip(
label: const Text('New Customer'), label: const Text('New Customer'),
backgroundColor: const Color(0xFFFFFBEB), backgroundColor: Colors.orange.shade100,
), ),
], ],
), ),

View File

@ -152,14 +152,12 @@ class _LoginPageState extends ConsumerState<LoginPage> {
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
), ),
child: _isLoading child: _isLoading
? SizedBox( ? const SizedBox(
height: 20, height: 20,
width: 20, width: 20,
child: CircularProgressIndicator( child: CircularProgressIndicator(
strokeWidth: 2, strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>( valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
Theme.of(context).colorScheme.primary,
),
), ),
) )
: const Text('Login'), : const Text('Login'),

View File

@ -3,29 +3,17 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/delivery_route.dart'; import '../models/delivery_route.dart';
import '../providers/providers.dart'; import '../providers/providers.dart';
import '../utils/breakpoints.dart'; import '../utils/breakpoints.dart';
import '../components/collapsible_routes_sidebar.dart'; import '../utils/responsive.dart';
import '../components/dark_mode_map.dart'; import '../components/premium_route_card.dart';
import 'deliveries_page.dart'; import 'deliveries_page.dart';
import 'settings_page.dart'; import 'settings_page.dart';
class RoutesPage extends ConsumerWidget { class RoutesPage extends ConsumerWidget {
const RoutesPage({super.key}); const RoutesPage({super.key});
void _navigateToDeliveries(BuildContext context, DeliveryRoute route) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => DeliveriesPage(
routeFragmentId: route.id,
routeName: route.name,
),
),
);
}
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final routesData = ref.watch(deliveryRoutesProvider); final routesData = ref.watch(deliveryRoutesProvider);
final allDeliveriesData = ref.watch(allDeliveriesProvider);
final userProfile = ref.watch(userProfileProvider); final userProfile = ref.watch(userProfileProvider);
return Scaffold( return Scaffold(
@ -85,70 +73,14 @@ class RoutesPage extends ConsumerWidget {
child: Text('No routes available'), child: Text('No routes available'),
); );
} }
return allDeliveriesData.when(
data: (allDeliveries) {
return RefreshIndicator( return RefreshIndicator(
onRefresh: () async { onRefresh: () async {
// ignore: unused_result // ignore: unused_result
ref.refresh(deliveryRoutesProvider); ref.refresh(deliveryRoutesProvider);
// ignore: unused_result
ref.refresh(allDeliveriesProvider);
}, },
child: context.isDesktop child: context.isDesktop
? Row( ? _buildDesktopGrid(context, routes)
children: [ : _buildMobileList(context, routes),
Expanded(
child: DarkModeMapComponent(
deliveries: allDeliveries,
selectedDelivery: null,
onDeliverySelected: null,
),
),
CollapsibleRoutesSidebar(
routes: routes,
selectedRoute: null,
onRouteSelected: (route) {
_navigateToDeliveries(context, route);
},
),
],
)
: Column(
children: [
Expanded(
child: DarkModeMapComponent(
deliveries: allDeliveries,
selectedDelivery: null,
onDeliverySelected: null,
),
),
CollapsibleRoutesSidebar(
routes: routes,
selectedRoute: null,
onRouteSelected: (route) {
_navigateToDeliveries(context, route);
},
),
],
),
);
},
loading: () => const Center(
child: CircularProgressIndicator(),
),
error: (error, stackTrace) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Error loading deliveries: $error'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => ref.refresh(allDeliveriesProvider),
child: const Text('Retry'),
),
],
),
),
); );
}, },
loading: () => const Center( loading: () => const Center(
@ -171,4 +103,53 @@ class RoutesPage extends ConsumerWidget {
); );
} }
Widget _buildMobileList(BuildContext context, List<DeliveryRoute> routes) {
final spacing = ResponsiveSpacing.md(context);
return ListView.builder(
padding: EdgeInsets.all(ResponsiveSpacing.md(context)),
itemCount: routes.length,
itemBuilder: (context, index) {
final route = routes[index];
return Padding(
padding: EdgeInsets.only(bottom: spacing),
child: _buildRouteCard(context, route),
);
},
);
}
Widget _buildDesktopGrid(BuildContext context, List<DeliveryRoute> routes) {
final spacing = ResponsiveSpacing.lg(context);
final columns = context.isTablet ? 2 : 3;
return GridView.builder(
padding: EdgeInsets.all(spacing),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: columns,
crossAxisSpacing: spacing,
mainAxisSpacing: spacing,
childAspectRatio: 1.2,
),
itemCount: routes.length,
itemBuilder: (context, index) {
final route = routes[index];
return _buildRouteCard(context, route);
},
);
}
Widget _buildRouteCard(BuildContext context, DeliveryRoute route) {
return PremiumRouteCard(
route: route,
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => DeliveriesPage(
routeFragmentId: route.id,
routeName: route.name,
),
),
);
},
);
}
} }

View File

@ -100,32 +100,6 @@ final deliveriesProvider = FutureProvider.family<List<Delivery>, int>((ref, rout
return result.whenSuccess((deliveries) => deliveries) ?? []; return result.whenSuccess((deliveries) => deliveries) ?? [];
}); });
/// Provider to get all deliveries from all routes
final allDeliveriesProvider = FutureProvider<List<Delivery>>((ref) async {
final routes = ref.watch(deliveryRoutesProvider).valueOrNull ?? [];
if (routes.isEmpty) {
return [];
}
// Fetch deliveries for all routes
final deliveriesFutures = routes.map((route) {
return ref.watch(deliveriesProvider(route.id)).when(
data: (deliveries) => deliveries,
loading: () => <Delivery>[],
error: (_, __) => <Delivery>[],
);
});
// Combine all deliveries
final allDeliveries = <Delivery>[];
for (final deliveries in deliveriesFutures) {
allDeliveries.addAll(deliveries);
}
return allDeliveries;
});
final languageProvider = StateProvider<String>((ref) { final languageProvider = StateProvider<String>((ref) {
return 'fr'; return 'fr';
}); });

View File

@ -11,174 +11,149 @@ class SvrntyColors {
// ============================================ // ============================================
/// Crimson Red - Primary accent and brand signature /// Crimson Red - Primary accent and brand signature
static const Color crimsonRed = Color(0xFFDF2D45); static const Color crimsonRed = Color(0xDF2D45);
/// Almost Black - Primary dark background /// Almost Black - Primary dark background
static const Color almostBlack = Color(0xFF06080C); static const Color almostBlack = Color(0x06080C);
/// Dark Slate - Secondary dark tone /// Dark Slate - Secondary dark tone
static const Color darkSlate = Color(0xFF3A4958); static const Color darkSlate = Color(0x3A4958);
/// Slate Gray - Mid-tone gray /// Slate Gray - Mid-tone gray
static const Color slateGray = Color(0xFF506576); static const Color slateGray = Color(0x506576);
/// Teal - Tertiary accent /// Teal - Tertiary accent
static const Color teal = Color(0xFF1D2C39); static const Color teal = Color(0x1D2C39);
/// Light Gray - Neutral light /// Light Gray - Neutral light
static const Color lightGray = Color(0xFFAEB8BE); static const Color lightGray = Color(0xAEB8BE);
// ============================================ // ============================================
// SEMANTIC COLORS // SEMANTIC COLORS
// ============================================ // ============================================
/// Success - Green for positive actions and completed states /// Success - Green for positive actions and completed states
static const Color success = Color(0xFF22C55E); static const Color success = Color(0x22C55E);
/// Warning - Amber for warnings and attention-needed states /// Warning - Amber for warnings and attention-needed states
static const Color warning = Color(0xFFF59E0B); static const Color warning = Color(0xF59E0B);
/// Info - Blue for informational and in-progress states /// Info - Blue for informational and in-progress states
static const Color info = Color(0xFF3B82F6); static const Color info = Color(0x3B82F6);
/// Error - Red for errors, failures, and destructive actions /// Error - Red for errors, failures, and destructive actions
static const Color error = Color(0xFFEF4444); static const Color error = Color(0xEF4444);
// ============================================ // ============================================
// DELIVERY STATUS COLORS (OPTIMIZED SVRNTY MAPPING) // DELIVERY STATUS COLORS
// ============================================ // ============================================
/// Status Pending - Awaiting action (Amber - attention needed) /// Status Pending - Awaiting action (Slate Gray)
static const Color statusPending = warning; // #F59E0B static const Color statusPending = slateGray;
/// Status In Transit - Currently being delivered (Teal Blue - active process) /// Status In Progress - Currently being delivered (Blue)
static const Color statusInTransit = slateGray; // #506576 static const Color statusInProgress = info;
/// Status Completed - Successfully delivered (Green - success) /// Status Completed - Successfully delivered (Green)
static const Color statusCompleted = success; // #22C55E static const Color statusCompleted = success;
/// Status Failed - Failed delivery (Error Red - problem) /// Status Skipped - Skipped/passed delivery (Amber)
static const Color statusFailed = error; // #EF4444 static const Color statusSkipped = warning;
/// Status Cancelled - Cancelled delivery (Cool Gray - inactive) /// Status Failed - Failed delivery (Red)
static const Color statusCancelled = lightGray; // #AEB8BE static const Color statusFailed = error;
/// Status On Hold - Paused/waiting (Slate Blue - informational)
static const Color statusOnHold = darkSlate; // #3A4958
// ============================================
// STATUS COLOR LIGHT BACKGROUNDS
// ============================================
/// Pending background (light amber)
static const Color statusPendingBg = Color(0xFFFEF3C7);
/// In Transit background (light teal)
static const Color statusInTransitBg = Color(0xFFE0E7ED);
/// Completed background (light green)
static const Color statusCompletedBg = Color(0xFFD1FAE5);
/// Failed background (light red)
static const Color statusFailedBg = Color(0xFFFEE2E2);
/// Cancelled background (light gray)
static const Color statusCancelledBg = Color(0xFFF3F4F6);
/// On Hold background (light slate)
static const Color statusOnHoldBg = Color(0xFFE2E8F0);
// ============================================ // ============================================
// SURFACE VARIANTS // SURFACE VARIANTS
// ============================================ // ============================================
/// Surface Elevated - Light elevated surface /// Surface Elevated - Light elevated surface
static const Color surfaceElevated = Color(0xFFF5F7FA); static const Color surfaceElevated = Color(0xF5F7FA);
/// Surface Subdued - Subdued light surface /// Surface Subdued - Subdued light surface
static const Color surfaceSubdued = Color(0xFFE8EAEE); static const Color surfaceSubdued = Color(0xE8EAEE);
// ============================================ // ============================================
// EXTENDED COLOR FAMILIES - SUCCESS (GREEN) // EXTENDED COLOR FAMILIES - SUCCESS (GREEN)
// ============================================ // ============================================
/// Success color light theme /// Success color light theme
static const Color successLight = Color(0xFF22C55E); static const Color successLight = Color(0x22C55E);
/// Success on color light theme /// Success on color light theme
static const Color onSuccessLight = Color(0xFFFFFFFF); static const Color onSuccessLight = Color(0xFFFFFF);
/// Success container light theme /// Success container light theme
static const Color successContainerLight = Color(0xFFDCFCE7); static const Color successContainerLight = Color(0xDCFCE7);
/// Success on container light theme /// Success on container light theme
static const Color onSuccessContainerLight = Color(0xFF14532D); static const Color onSuccessContainerLight = Color(0x14532D);
/// Success color dark theme /// Success color dark theme
static const Color successDark = Color(0xFF4ADE80); static const Color successDark = Color(0x4ADE80);
/// Success on color dark theme /// Success on color dark theme
static const Color onSuccessDark = Color(0xFF14532D); static const Color onSuccessDark = Color(0x14532D);
/// Success container dark theme /// Success container dark theme
static const Color successContainerDark = Color(0xFF15803D); static const Color successContainerDark = Color(0x15803D);
/// Success on container dark theme /// Success on container dark theme
static const Color onSuccessContainerDark = Color(0xFFDCFCE7); static const Color onSuccessContainerDark = Color(0xDCFCE7);
// ============================================ // ============================================
// EXTENDED COLOR FAMILIES - WARNING (AMBER) // EXTENDED COLOR FAMILIES - WARNING (AMBER)
// ============================================ // ============================================
/// Warning color light theme /// Warning color light theme
static const Color warningLight = Color(0xFFF59E0B); static const Color warningLight = Color(0xF59E0B);
/// Warning on color light theme /// Warning on color light theme
static const Color onWarningLight = Color(0xFFFFFFFF); static const Color onWarningLight = Color(0xFFFFFF);
/// Warning container light theme /// Warning container light theme
static const Color warningContainerLight = Color(0xFFFEF3C7); static const Color warningContainerLight = Color(0xFEF3C7);
/// Warning on container light theme /// Warning on container light theme
static const Color onWarningContainerLight = Color(0xFF78350F); static const Color onWarningContainerLight = Color(0x78350F);
/// Warning color dark theme /// Warning color dark theme
static const Color warningDark = Color(0xFFFBBF24); static const Color warningDark = Color(0xFBBF24);
/// Warning on color dark theme /// Warning on color dark theme
static const Color onWarningDark = Color(0xFF78350F); static const Color onWarningDark = Color(0x78350F);
/// Warning container dark theme /// Warning container dark theme
static const Color warningContainerDark = Color(0xFFD97706); static const Color warningContainerDark = Color(0xD97706);
/// Warning on container dark theme /// Warning on container dark theme
static const Color onWarningContainerDark = Color(0xFFFEF3C7); static const Color onWarningContainerDark = Color(0xFEF3C7);
// ============================================ // ============================================
// EXTENDED COLOR FAMILIES - INFO (BLUE) // EXTENDED COLOR FAMILIES - INFO (BLUE)
// ============================================ // ============================================
/// Info color light theme /// Info color light theme
static const Color infoLight = Color(0xFF3B82F6); static const Color infoLight = Color(0x3B82F6);
/// Info on color light theme /// Info on color light theme
static const Color onInfoLight = Color(0xFFFFFFFF); static const Color onInfoLight = Color(0xFFFFFF);
/// Info container light theme /// Info container light theme
static const Color infoContainerLight = Color(0xFFDEEEFF); static const Color infoContainerLight = Color(0xDEEEFF);
/// Info on container light theme /// Info on container light theme
static const Color onInfoContainerLight = Color(0xFF003DA1); static const Color onInfoContainerLight = Color(0x003DA1);
/// Info color dark theme /// Info color dark theme
static const Color infoDark = Color(0xFF90CAF9); static const Color infoDark = Color(0x90CAF9);
/// Info on color dark theme /// Info on color dark theme
static const Color onInfoDark = Color(0xFF003DA1); static const Color onInfoDark = Color(0x003DA1);
/// Info container dark theme /// Info container dark theme
static const Color infoContainerDark = Color(0xFF0D47A1); static const Color infoContainerDark = Color(0x0D47A1);
/// Info on container dark theme /// Info on container dark theme
static const Color onInfoContainerDark = Color(0xFFDEEEFF); static const Color onInfoContainerDark = Color(0xDEEEFF);
} }

View File

@ -17,17 +17,17 @@ class AppGradients {
end: Alignment.centerRight, end: Alignment.centerRight,
colors: [ colors: [
SvrntyColors.statusPending, SvrntyColors.statusPending,
Color(0xFF506576), // Slightly different shade for gradient effect Color(0x506576), // Slightly different shade for gradient effect
], ],
); );
/// In Transit status gradient (Teal Blue) /// In Progress status gradient (Blue/Info)
static const LinearGradient gradientStatusInTransit = LinearGradient( static const LinearGradient gradientStatusInProgress = LinearGradient(
begin: Alignment.centerLeft, begin: Alignment.centerLeft,
end: Alignment.centerRight, end: Alignment.centerRight,
colors: [ colors: [
SvrntyColors.statusInTransit, SvrntyColors.statusInProgress,
Color(0xFF647A91), // Lighter teal for gradient Color(0x5B9BFF), // Lighter blue for gradient
], ],
); );
@ -37,17 +37,17 @@ class AppGradients {
end: Alignment.centerRight, end: Alignment.centerRight,
colors: [ colors: [
SvrntyColors.statusCompleted, SvrntyColors.statusCompleted,
Color(0xFF4ADE80), // Lighter green for gradient Color(0x4ADE80), // Lighter green for gradient
], ],
); );
/// Cancelled status gradient (Light Gray) /// Skipped status gradient (Amber/Warning)
static const LinearGradient gradientStatusCancelled = LinearGradient( static const LinearGradient gradientStatusSkipped = LinearGradient(
begin: Alignment.centerLeft, begin: Alignment.centerLeft,
end: Alignment.centerRight, end: Alignment.centerRight,
colors: [ colors: [
SvrntyColors.statusCancelled, SvrntyColors.statusSkipped,
Color(0xFFC5CBD2), // Darker gray for gradient Color(0xFBBF24), // Lighter amber for gradient
], ],
); );
@ -57,7 +57,7 @@ class AppGradients {
end: Alignment.centerRight, end: Alignment.centerRight,
colors: [ colors: [
SvrntyColors.statusFailed, SvrntyColors.statusFailed,
Color(0xFFFF7D7D), // Lighter red for gradient Color(0xFF7D7D), // Lighter red for gradient
], ],
); );
@ -86,7 +86,7 @@ class AppGradients {
end: Alignment.centerRight, end: Alignment.centerRight,
colors: [ colors: [
SvrntyColors.success, SvrntyColors.success,
Color(0xFF4ADE80), // Lighter green Color(0x4ADE80), // Lighter green
], ],
); );
@ -96,7 +96,7 @@ class AppGradients {
end: Alignment.centerRight, end: Alignment.centerRight,
colors: [ colors: [
SvrntyColors.warning, SvrntyColors.warning,
Color(0xFFFBBF24), // Lighter amber Color(0xFBBF24), // Lighter amber
], ],
); );
@ -106,7 +106,7 @@ class AppGradients {
end: Alignment.centerRight, end: Alignment.centerRight,
colors: [ colors: [
SvrntyColors.error, SvrntyColors.error,
Color(0xFFFF7D7D), // Lighter red Color(0xFF7D7D), // Lighter red
], ],
); );
@ -116,7 +116,7 @@ class AppGradients {
end: Alignment.centerRight, end: Alignment.centerRight,
colors: [ colors: [
SvrntyColors.info, SvrntyColors.info,
Color(0xFF5B9BFF), // Lighter blue Color(0x5B9BFF), // Lighter blue
], ],
); );
@ -130,7 +130,7 @@ class AppGradients {
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: [
SvrntyColors.crimsonRed, SvrntyColors.crimsonRed,
Color(0xFFC44D58), // Slightly darker shade Color(0xC44D58), // Slightly darker shade
], ],
); );
@ -163,8 +163,8 @@ class AppGradients {
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: [
Color(0xFFFAFAFC), Color(0xFAFAFC),
Color(0xFFF5F7FA), Color(0xF5F7FA),
], ],
); );
@ -173,8 +173,8 @@ class AppGradients {
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: [
Color(0xFF1A1C1E), Color(0x1A1C1E),
Color(0xFF2A2D34), Color(0x2A2D34),
], ],
); );
@ -183,8 +183,8 @@ class AppGradients {
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [ colors: [
Color(0xFFFFFFFF), Color(0xFFFFFF),
Color(0xFFF5F7FA), Color(0xF5F7FA),
], ],
); );
@ -193,8 +193,8 @@ class AppGradients {
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [ colors: [
Color(0xFF2A2D34), Color(0x2A2D34),
Color(0xFF1F2123), Color(0x1F2123),
], ],
); );
@ -254,7 +254,7 @@ class AppGradients {
end: Alignment.centerRight, end: Alignment.centerRight,
colors: [ colors: [
Color(0xFF2A2D34), Color(0xFF2A2D34),
Color(0xFF383940), Color(0x80383940),
Color(0xFF2A2D34), Color(0xFF2A2D34),
], ],
stops: [0.1, 0.5, 0.9], stops: [0.1, 0.5, 0.9],
@ -269,16 +269,14 @@ class AppGradients {
switch (status.toLowerCase()) { switch (status.toLowerCase()) {
case 'pending': case 'pending':
return gradientStatusPending; return gradientStatusPending;
case 'in_transit':
case 'in_progress': case 'in_progress':
case 'inprogress': case 'inprogress':
return gradientStatusInTransit; return gradientStatusInProgress;
case 'completed': case 'completed':
case 'done': case 'done':
return gradientStatusCompleted; return gradientStatusCompleted;
case 'cancelled':
case 'skipped': case 'skipped':
return gradientStatusCancelled; return gradientStatusSkipped;
case 'failed': case 'failed':
return gradientStatusFailed; return gradientStatusFailed;
default: default:

View File

@ -1,262 +0,0 @@
import 'package:flutter/material.dart';
import 'color_system.dart';
/// SVRNTY Status Color Utility
/// Provides consistent color access for delivery status indicators across the app
class StatusColorScheme {
// Pending: Amber - Attention needed
static const Color pending = SvrntyColors.statusPending; // #F59E0B
static const Color pendingBackground = SvrntyColors.statusPendingBg; // #FEF3C7
static const Color pendingText = Color(0xFF92400E);
// In Transit: Teal Blue - Active process
static const Color inTransit = SvrntyColors.statusInTransit; // #506576
static const Color inTransitBackground = SvrntyColors.statusInTransitBg; // #E0E7ED
static const Color inTransitText = Color(0xFF1D2C39);
// Completed: Green - Success
static const Color completed = SvrntyColors.statusCompleted; // #22C55E
static const Color completedBackground = SvrntyColors.statusCompletedBg; // #D1FAE5
static const Color completedText = Color(0xFF065F46);
// Failed: Red - Problem
static const Color failed = SvrntyColors.statusFailed; // #EF4444
static const Color failedBackground = SvrntyColors.statusFailedBg; // #FEE2E2
static const Color failedText = Color(0xFF991B1B);
// Cancelled: Gray - Inactive
static const Color cancelled = SvrntyColors.statusCancelled; // #AEB8BE
static const Color cancelledBackground = SvrntyColors.statusCancelledBg; // #F3F4F6
static const Color cancelledText = Color(0xFF374151);
// On Hold: Slate Blue - Paused/Informational
static const Color onHold = SvrntyColors.statusOnHold; // #3A4958
static const Color onHoldBackground = SvrntyColors.statusOnHoldBg; // #E2E8F0
static const Color onHoldText = Color(0xFF1E293B);
/// Get status color by status type
static Color getStatusColor(String status) {
switch (status.toLowerCase()) {
case 'pending':
return pending;
case 'in_transit':
case 'in_progress':
case 'processing':
return inTransit;
case 'completed':
case 'delivered':
case 'done':
return completed;
case 'failed':
case 'error':
return failed;
case 'cancelled':
case 'skipped':
case 'rejected':
return cancelled;
case 'on_hold':
case 'paused':
case 'waiting':
return onHold;
default:
return inTransit;
}
}
/// Get status background color by status type
static Color getStatusBackground(String status) {
switch (status.toLowerCase()) {
case 'pending':
return pendingBackground;
case 'in_transit':
case 'in_progress':
case 'processing':
return inTransitBackground;
case 'completed':
case 'delivered':
case 'done':
return completedBackground;
case 'failed':
case 'error':
return failedBackground;
case 'cancelled':
case 'skipped':
case 'rejected':
return cancelledBackground;
case 'on_hold':
case 'paused':
case 'waiting':
return onHoldBackground;
default:
return inTransitBackground;
}
}
/// Get status text color by status type
static Color getStatusText(String status) {
switch (status.toLowerCase()) {
case 'pending':
return pendingText;
case 'in_transit':
case 'in_progress':
case 'processing':
return inTransitText;
case 'completed':
case 'delivered':
case 'done':
return completedText;
case 'failed':
case 'error':
return failedText;
case 'cancelled':
case 'skipped':
case 'rejected':
return cancelledText;
case 'on_hold':
case 'paused':
case 'waiting':
return onHoldText;
default:
return inTransitText;
}
}
/// Get status icon by status type
static IconData getStatusIcon(String status) {
switch (status.toLowerCase()) {
case 'pending':
return Icons.schedule;
case 'in_transit':
case 'in_progress':
case 'processing':
return Icons.local_shipping;
case 'completed':
case 'delivered':
case 'done':
return Icons.check_circle;
case 'failed':
case 'error':
return Icons.error;
case 'cancelled':
case 'skipped':
case 'rejected':
return Icons.cancel;
case 'on_hold':
case 'paused':
case 'waiting':
return Icons.pause_circle;
default:
return Icons.info;
}
}
/// Get status label by status type
static String getStatusLabel(String status) {
switch (status.toLowerCase()) {
case 'pending':
return 'Pending';
case 'in_transit':
case 'in_progress':
return 'In Transit';
case 'processing':
return 'Processing';
case 'completed':
case 'delivered':
return 'Delivered';
case 'done':
return 'Completed';
case 'failed':
case 'error':
return 'Failed';
case 'cancelled':
return 'Cancelled';
case 'skipped':
return 'Skipped';
case 'rejected':
return 'Rejected';
case 'on_hold':
return 'On Hold';
case 'paused':
return 'Paused';
case 'waiting':
return 'Waiting';
default:
return status;
}
}
}
/// Status Badge Widget
class StatusBadgeWidget extends StatelessWidget {
final String status;
final bool showIcon;
final bool showLabel;
final double fontSize;
const StatusBadgeWidget({
Key? key,
required this.status,
this.showIcon = true,
this.showLabel = true,
this.fontSize = 12,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: StatusColorScheme.getStatusBackground(status),
borderRadius: BorderRadius.circular(6),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (showIcon) ...[
Icon(
StatusColorScheme.getStatusIcon(status),
color: StatusColorScheme.getStatusColor(status),
size: fontSize + 2,
),
const SizedBox(width: 4),
],
if (showLabel)
Text(
StatusColorScheme.getStatusLabel(status),
style: TextStyle(
color: StatusColorScheme.getStatusColor(status),
fontWeight: FontWeight.w600,
fontSize: fontSize,
),
),
],
),
);
}
}
/// Status Accent Bar Widget (for list items)
class StatusAccentBar extends StatelessWidget {
final String status;
final double width;
final double height;
const StatusAccentBar({
Key? key,
required this.status,
this.width = 4,
this.height = 60,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: width,
height: height,
decoration: BoxDecoration(
color: StatusColorScheme.getStatusColor(status),
borderRadius: BorderRadius.circular(width / 2),
),
);
}
}