Compare commits
No commits in common. "5714fd84433845fd8730d18d6b200399afc52135" and "3f0310d856adb105ef6a14f9e0b1c1d30c3899dc" have entirely different histories.
5714fd8443
...
3f0310d856
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -302,7 +302,7 @@ class _DarkModeMapComponentState extends State<DarkModeMapComponent> {
|
||||
label: 'Stop',
|
||||
icon: Icons.stop,
|
||||
onPressed: _stopNavigation,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
color: Colors.grey[600]!,
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -315,7 +315,9 @@ class _DarkModeMapComponentState extends State<DarkModeMapComponent> {
|
||||
right: 0,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? const Color(0xFF14161A)
|
||||
: Colors.white,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.2),
|
||||
@ -351,7 +353,13 @@ class _DarkModeMapComponentState extends State<DarkModeMapComponent> {
|
||||
'No address',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall,
|
||||
.bodySmall
|
||||
?.copyWith(
|
||||
color: Theme.of(context).brightness ==
|
||||
Brightness.dark
|
||||
? Colors.grey[400]
|
||||
: Colors.grey[600],
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
||||
@ -70,10 +70,9 @@ class _DeliveryListItemState extends State<DeliveryListItem>
|
||||
}
|
||||
|
||||
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;
|
||||
// Default: in-transit or pending deliveries
|
||||
return SvrntyColors.statusInTransit;
|
||||
return SvrntyColors.statusPending;
|
||||
}
|
||||
|
||||
IconData _getStatusIcon(Delivery delivery) {
|
||||
@ -113,7 +112,9 @@ class _DeliveryListItemState extends State<DeliveryListItem>
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: _isHovered || widget.isSelected
|
||||
? Theme.of(context).colorScheme.surfaceContainer
|
||||
? (isDark
|
||||
? Colors.grey[800]
|
||||
: Colors.grey[100])
|
||||
: Colors.transparent,
|
||||
boxShadow: _isHovered || widget.isSelected
|
||||
? [
|
||||
@ -169,7 +170,12 @@ class _DeliveryListItemState extends State<DeliveryListItem>
|
||||
'No address',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall,
|
||||
.bodySmall
|
||||
?.copyWith(
|
||||
color: isDark
|
||||
? Colors.grey[400]
|
||||
: Colors.grey[600],
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@ -182,6 +188,9 @@ class _DeliveryListItemState extends State<DeliveryListItem>
|
||||
.labelSmall
|
||||
?.copyWith(
|
||||
fontSize: 10,
|
||||
color: isDark
|
||||
? Colors.grey[500]
|
||||
: Colors.grey[500],
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -237,12 +246,12 @@ class _DeliveryListItemState extends State<DeliveryListItem>
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.phone,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
color: Colors.grey[700],
|
||||
size: 14,
|
||||
),
|
||||
),
|
||||
@ -264,7 +273,9 @@ class _DeliveryListItemState extends State<DeliveryListItem>
|
||||
.textTheme
|
||||
.labelSmall
|
||||
?.copyWith(
|
||||
color: SvrntyColors.warning,
|
||||
color: isDark
|
||||
? Colors.amber[300]
|
||||
: Colors.amber[700],
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
|
||||
@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -93,7 +93,7 @@ class _PremiumRouteCardState extends State<PremiumRouteCard>
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
color: isDark ? const Color(0xFF14161A) : const Color(0xFFFAFAFC),
|
||||
border: Border(
|
||||
left: BorderSide(color: accentColor, width: 4),
|
||||
),
|
||||
@ -134,7 +134,7 @@ class _PremiumRouteCardState extends State<PremiumRouteCard>
|
||||
TextSpan(
|
||||
text: ' completed',
|
||||
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(
|
||||
'$progressPercentage% progress',
|
||||
style: Theme.of(context).textTheme.labelSmall?.copyWith(
|
||||
color: isDark ? Colors.grey[400] : Colors.grey[600],
|
||||
fontSize: 11,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
@ -178,7 +179,7 @@ class _PremiumRouteCardState extends State<PremiumRouteCard>
|
||||
height: 6,
|
||||
child: LinearProgressIndicator(
|
||||
value: widget.route.progress,
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
backgroundColor: isDark ? Colors.grey[800] : Colors.grey[200],
|
||||
valueColor: AlwaysStoppedAnimation<Color>(accentColor),
|
||||
),
|
||||
),
|
||||
|
||||
@ -2,17 +2,15 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../models/delivery.dart';
|
||||
import '../models/delivery_route.dart';
|
||||
import '../providers/providers.dart';
|
||||
import '../api/client.dart';
|
||||
import '../api/openapi_config.dart';
|
||||
import '../models/delivery_commands.dart';
|
||||
import '../utils/breakpoints.dart';
|
||||
import '../utils/responsive.dart';
|
||||
import '../components/map_sidebar_layout.dart';
|
||||
import '../components/dark_mode_map.dart';
|
||||
import '../components/delivery_list_item.dart';
|
||||
import '../components/collapsible_routes_sidebar.dart'
|
||||
show CollapsibleRoutesSidebar;
|
||||
|
||||
class DeliveriesPage extends ConsumerStatefulWidget {
|
||||
final int routeFragmentId;
|
||||
@ -48,7 +46,6 @@ class _DeliveriesPageState extends ConsumerState<DeliveriesPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final deliveriesData = ref.watch(deliveriesProvider(widget.routeFragmentId));
|
||||
final routesData = ref.watch(deliveryRoutesProvider);
|
||||
final token = ref.watch(authTokenProvider).valueOrNull;
|
||||
|
||||
return Scaffold(
|
||||
@ -65,193 +62,79 @@ class _DeliveriesPageState extends ConsumerState<DeliveriesPage> {
|
||||
.where((d) => d.delivered)
|
||||
.toList();
|
||||
|
||||
return routesData.when(
|
||||
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(
|
||||
deliveries: deliveries,
|
||||
return 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),
|
||||
),
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
DeliveryListView(
|
||||
deliveries: completedDeliveries,
|
||||
selectedDelivery: _selectedDelivery,
|
||||
onDeliverySelected: (delivery) {
|
||||
setState(() {
|
||||
_selectedDelivery = delivery;
|
||||
});
|
||||
},
|
||||
onAction: (delivery, action) =>
|
||||
_handleDeliveryAction(context, delivery, action, token),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -457,7 +340,7 @@ class DeliveryCard extends StatelessWidget {
|
||||
else if (order?.isNewCustomer ?? false)
|
||||
Chip(
|
||||
label: const Text('New Customer'),
|
||||
backgroundColor: const Color(0xFFFFFBEB),
|
||||
backgroundColor: Colors.orange.shade100,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -152,14 +152,12 @@ class _LoginPageState extends ConsumerState<LoginPage> {
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
child: _isLoading
|
||||
? SizedBox(
|
||||
? const SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
),
|
||||
)
|
||||
: const Text('Login'),
|
||||
|
||||
@ -3,29 +3,17 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import '../models/delivery_route.dart';
|
||||
import '../providers/providers.dart';
|
||||
import '../utils/breakpoints.dart';
|
||||
import '../components/collapsible_routes_sidebar.dart';
|
||||
import '../components/dark_mode_map.dart';
|
||||
import '../utils/responsive.dart';
|
||||
import '../components/premium_route_card.dart';
|
||||
import 'deliveries_page.dart';
|
||||
import 'settings_page.dart';
|
||||
|
||||
class RoutesPage extends ConsumerWidget {
|
||||
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
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final routesData = ref.watch(deliveryRoutesProvider);
|
||||
final allDeliveriesData = ref.watch(allDeliveriesProvider);
|
||||
final userProfile = ref.watch(userProfileProvider);
|
||||
|
||||
return Scaffold(
|
||||
@ -85,70 +73,14 @@ class RoutesPage extends ConsumerWidget {
|
||||
child: Text('No routes available'),
|
||||
);
|
||||
}
|
||||
return allDeliveriesData.when(
|
||||
data: (allDeliveries) {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
// ignore: unused_result
|
||||
ref.refresh(deliveryRoutesProvider);
|
||||
// ignore: unused_result
|
||||
ref.refresh(allDeliveriesProvider);
|
||||
},
|
||||
child: context.isDesktop
|
||||
? Row(
|
||||
children: [
|
||||
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);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
// ignore: unused_result
|
||||
ref.refresh(deliveryRoutesProvider);
|
||||
},
|
||||
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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: context.isDesktop
|
||||
? _buildDesktopGrid(context, routes)
|
||||
: _buildMobileList(context, routes),
|
||||
);
|
||||
},
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,32 +100,6 @@ final deliveriesProvider = FutureProvider.family<List<Delivery>, int>((ref, rout
|
||||
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) {
|
||||
return 'fr';
|
||||
});
|
||||
|
||||
@ -11,174 +11,149 @@ class SvrntyColors {
|
||||
// ============================================
|
||||
|
||||
/// Crimson Red - Primary accent and brand signature
|
||||
static const Color crimsonRed = Color(0xFFDF2D45);
|
||||
static const Color crimsonRed = Color(0xDF2D45);
|
||||
|
||||
/// Almost Black - Primary dark background
|
||||
static const Color almostBlack = Color(0xFF06080C);
|
||||
static const Color almostBlack = Color(0x06080C);
|
||||
|
||||
/// Dark Slate - Secondary dark tone
|
||||
static const Color darkSlate = Color(0xFF3A4958);
|
||||
static const Color darkSlate = Color(0x3A4958);
|
||||
|
||||
/// Slate Gray - Mid-tone gray
|
||||
static const Color slateGray = Color(0xFF506576);
|
||||
static const Color slateGray = Color(0x506576);
|
||||
|
||||
/// Teal - Tertiary accent
|
||||
static const Color teal = Color(0xFF1D2C39);
|
||||
static const Color teal = Color(0x1D2C39);
|
||||
|
||||
/// Light Gray - Neutral light
|
||||
static const Color lightGray = Color(0xFFAEB8BE);
|
||||
static const Color lightGray = Color(0xAEB8BE);
|
||||
|
||||
// ============================================
|
||||
// SEMANTIC COLORS
|
||||
// ============================================
|
||||
|
||||
/// 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
|
||||
static const Color warning = Color(0xFFF59E0B);
|
||||
static const Color warning = Color(0xF59E0B);
|
||||
|
||||
/// 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
|
||||
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)
|
||||
static const Color statusPending = warning; // #F59E0B
|
||||
/// Status Pending - Awaiting action (Slate Gray)
|
||||
static const Color statusPending = slateGray;
|
||||
|
||||
/// Status In Transit - Currently being delivered (Teal Blue - active process)
|
||||
static const Color statusInTransit = slateGray; // #506576
|
||||
/// Status In Progress - Currently being delivered (Blue)
|
||||
static const Color statusInProgress = info;
|
||||
|
||||
/// Status Completed - Successfully delivered (Green - success)
|
||||
static const Color statusCompleted = success; // #22C55E
|
||||
/// Status Completed - Successfully delivered (Green)
|
||||
static const Color statusCompleted = success;
|
||||
|
||||
/// Status Failed - Failed delivery (Error Red - problem)
|
||||
static const Color statusFailed = error; // #EF4444
|
||||
/// Status Skipped - Skipped/passed delivery (Amber)
|
||||
static const Color statusSkipped = warning;
|
||||
|
||||
/// Status Cancelled - Cancelled delivery (Cool Gray - inactive)
|
||||
static const Color statusCancelled = lightGray; // #AEB8BE
|
||||
|
||||
/// 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);
|
||||
/// Status Failed - Failed delivery (Red)
|
||||
static const Color statusFailed = error;
|
||||
|
||||
// ============================================
|
||||
// SURFACE VARIANTS
|
||||
// ============================================
|
||||
|
||||
/// Surface Elevated - Light elevated surface
|
||||
static const Color surfaceElevated = Color(0xFFF5F7FA);
|
||||
static const Color surfaceElevated = Color(0xF5F7FA);
|
||||
|
||||
/// Surface Subdued - Subdued light surface
|
||||
static const Color surfaceSubdued = Color(0xFFE8EAEE);
|
||||
static const Color surfaceSubdued = Color(0xE8EAEE);
|
||||
|
||||
// ============================================
|
||||
// EXTENDED COLOR FAMILIES - SUCCESS (GREEN)
|
||||
// ============================================
|
||||
|
||||
/// Success color light theme
|
||||
static const Color successLight = Color(0xFF22C55E);
|
||||
static const Color successLight = Color(0x22C55E);
|
||||
|
||||
/// Success on color light theme
|
||||
static const Color onSuccessLight = Color(0xFFFFFFFF);
|
||||
static const Color onSuccessLight = Color(0xFFFFFF);
|
||||
|
||||
/// Success container light theme
|
||||
static const Color successContainerLight = Color(0xFFDCFCE7);
|
||||
static const Color successContainerLight = Color(0xDCFCE7);
|
||||
|
||||
/// Success on container light theme
|
||||
static const Color onSuccessContainerLight = Color(0xFF14532D);
|
||||
static const Color onSuccessContainerLight = Color(0x14532D);
|
||||
|
||||
/// Success color dark theme
|
||||
static const Color successDark = Color(0xFF4ADE80);
|
||||
static const Color successDark = Color(0x4ADE80);
|
||||
|
||||
/// Success on color dark theme
|
||||
static const Color onSuccessDark = Color(0xFF14532D);
|
||||
static const Color onSuccessDark = Color(0x14532D);
|
||||
|
||||
/// Success container dark theme
|
||||
static const Color successContainerDark = Color(0xFF15803D);
|
||||
static const Color successContainerDark = Color(0x15803D);
|
||||
|
||||
/// Success on container dark theme
|
||||
static const Color onSuccessContainerDark = Color(0xFFDCFCE7);
|
||||
static const Color onSuccessContainerDark = Color(0xDCFCE7);
|
||||
|
||||
// ============================================
|
||||
// EXTENDED COLOR FAMILIES - WARNING (AMBER)
|
||||
// ============================================
|
||||
|
||||
/// Warning color light theme
|
||||
static const Color warningLight = Color(0xFFF59E0B);
|
||||
static const Color warningLight = Color(0xF59E0B);
|
||||
|
||||
/// Warning on color light theme
|
||||
static const Color onWarningLight = Color(0xFFFFFFFF);
|
||||
static const Color onWarningLight = Color(0xFFFFFF);
|
||||
|
||||
/// Warning container light theme
|
||||
static const Color warningContainerLight = Color(0xFFFEF3C7);
|
||||
static const Color warningContainerLight = Color(0xFEF3C7);
|
||||
|
||||
/// Warning on container light theme
|
||||
static const Color onWarningContainerLight = Color(0xFF78350F);
|
||||
static const Color onWarningContainerLight = Color(0x78350F);
|
||||
|
||||
/// Warning color dark theme
|
||||
static const Color warningDark = Color(0xFFFBBF24);
|
||||
static const Color warningDark = Color(0xFBBF24);
|
||||
|
||||
/// Warning on color dark theme
|
||||
static const Color onWarningDark = Color(0xFF78350F);
|
||||
static const Color onWarningDark = Color(0x78350F);
|
||||
|
||||
/// Warning container dark theme
|
||||
static const Color warningContainerDark = Color(0xFFD97706);
|
||||
static const Color warningContainerDark = Color(0xD97706);
|
||||
|
||||
/// Warning on container dark theme
|
||||
static const Color onWarningContainerDark = Color(0xFFFEF3C7);
|
||||
static const Color onWarningContainerDark = Color(0xFEF3C7);
|
||||
|
||||
// ============================================
|
||||
// EXTENDED COLOR FAMILIES - INFO (BLUE)
|
||||
// ============================================
|
||||
|
||||
/// Info color light theme
|
||||
static const Color infoLight = Color(0xFF3B82F6);
|
||||
static const Color infoLight = Color(0x3B82F6);
|
||||
|
||||
/// Info on color light theme
|
||||
static const Color onInfoLight = Color(0xFFFFFFFF);
|
||||
static const Color onInfoLight = Color(0xFFFFFF);
|
||||
|
||||
/// Info container light theme
|
||||
static const Color infoContainerLight = Color(0xFFDEEEFF);
|
||||
static const Color infoContainerLight = Color(0xDEEEFF);
|
||||
|
||||
/// Info on container light theme
|
||||
static const Color onInfoContainerLight = Color(0xFF003DA1);
|
||||
static const Color onInfoContainerLight = Color(0x003DA1);
|
||||
|
||||
/// Info color dark theme
|
||||
static const Color infoDark = Color(0xFF90CAF9);
|
||||
static const Color infoDark = Color(0x90CAF9);
|
||||
|
||||
/// Info on color dark theme
|
||||
static const Color onInfoDark = Color(0xFF003DA1);
|
||||
static const Color onInfoDark = Color(0x003DA1);
|
||||
|
||||
/// Info container dark theme
|
||||
static const Color infoContainerDark = Color(0xFF0D47A1);
|
||||
static const Color infoContainerDark = Color(0x0D47A1);
|
||||
|
||||
/// Info on container dark theme
|
||||
static const Color onInfoContainerDark = Color(0xFFDEEEFF);
|
||||
static const Color onInfoContainerDark = Color(0xDEEEFF);
|
||||
}
|
||||
|
||||
@ -17,17 +17,17 @@ class AppGradients {
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
SvrntyColors.statusPending,
|
||||
Color(0xFF506576), // Slightly different shade for gradient effect
|
||||
Color(0x506576), // Slightly different shade for gradient effect
|
||||
],
|
||||
);
|
||||
|
||||
/// In Transit status gradient (Teal Blue)
|
||||
static const LinearGradient gradientStatusInTransit = LinearGradient(
|
||||
/// In Progress status gradient (Blue/Info)
|
||||
static const LinearGradient gradientStatusInProgress = LinearGradient(
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
SvrntyColors.statusInTransit,
|
||||
Color(0xFF647A91), // Lighter teal for gradient
|
||||
SvrntyColors.statusInProgress,
|
||||
Color(0x5B9BFF), // Lighter blue for gradient
|
||||
],
|
||||
);
|
||||
|
||||
@ -37,17 +37,17 @@ class AppGradients {
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
SvrntyColors.statusCompleted,
|
||||
Color(0xFF4ADE80), // Lighter green for gradient
|
||||
Color(0x4ADE80), // Lighter green for gradient
|
||||
],
|
||||
);
|
||||
|
||||
/// Cancelled status gradient (Light Gray)
|
||||
static const LinearGradient gradientStatusCancelled = LinearGradient(
|
||||
/// Skipped status gradient (Amber/Warning)
|
||||
static const LinearGradient gradientStatusSkipped = LinearGradient(
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
SvrntyColors.statusCancelled,
|
||||
Color(0xFFC5CBD2), // Darker gray for gradient
|
||||
SvrntyColors.statusSkipped,
|
||||
Color(0xFBBF24), // Lighter amber for gradient
|
||||
],
|
||||
);
|
||||
|
||||
@ -57,7 +57,7 @@ class AppGradients {
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
SvrntyColors.statusFailed,
|
||||
Color(0xFFFF7D7D), // Lighter red for gradient
|
||||
Color(0xFF7D7D), // Lighter red for gradient
|
||||
],
|
||||
);
|
||||
|
||||
@ -86,7 +86,7 @@ class AppGradients {
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
SvrntyColors.success,
|
||||
Color(0xFF4ADE80), // Lighter green
|
||||
Color(0x4ADE80), // Lighter green
|
||||
],
|
||||
);
|
||||
|
||||
@ -96,7 +96,7 @@ class AppGradients {
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
SvrntyColors.warning,
|
||||
Color(0xFFFBBF24), // Lighter amber
|
||||
Color(0xFBBF24), // Lighter amber
|
||||
],
|
||||
);
|
||||
|
||||
@ -106,7 +106,7 @@ class AppGradients {
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
SvrntyColors.error,
|
||||
Color(0xFFFF7D7D), // Lighter red
|
||||
Color(0xFF7D7D), // Lighter red
|
||||
],
|
||||
);
|
||||
|
||||
@ -116,7 +116,7 @@ class AppGradients {
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
SvrntyColors.info,
|
||||
Color(0xFF5B9BFF), // Lighter blue
|
||||
Color(0x5B9BFF), // Lighter blue
|
||||
],
|
||||
);
|
||||
|
||||
@ -130,7 +130,7 @@ class AppGradients {
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
SvrntyColors.crimsonRed,
|
||||
Color(0xFFC44D58), // Slightly darker shade
|
||||
Color(0xC44D58), // Slightly darker shade
|
||||
],
|
||||
);
|
||||
|
||||
@ -163,8 +163,8 @@ class AppGradients {
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
Color(0xFFFAFAFC),
|
||||
Color(0xFFF5F7FA),
|
||||
Color(0xFAFAFC),
|
||||
Color(0xF5F7FA),
|
||||
],
|
||||
);
|
||||
|
||||
@ -173,8 +173,8 @@ class AppGradients {
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
Color(0xFF1A1C1E),
|
||||
Color(0xFF2A2D34),
|
||||
Color(0x1A1C1E),
|
||||
Color(0x2A2D34),
|
||||
],
|
||||
);
|
||||
|
||||
@ -183,8 +183,8 @@ class AppGradients {
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Color(0xFFFFFFFF),
|
||||
Color(0xFFF5F7FA),
|
||||
Color(0xFFFFFF),
|
||||
Color(0xF5F7FA),
|
||||
],
|
||||
);
|
||||
|
||||
@ -193,8 +193,8 @@ class AppGradients {
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Color(0xFF2A2D34),
|
||||
Color(0xFF1F2123),
|
||||
Color(0x2A2D34),
|
||||
Color(0x1F2123),
|
||||
],
|
||||
);
|
||||
|
||||
@ -254,7 +254,7 @@ class AppGradients {
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
Color(0xFF2A2D34),
|
||||
Color(0xFF383940),
|
||||
Color(0x80383940),
|
||||
Color(0xFF2A2D34),
|
||||
],
|
||||
stops: [0.1, 0.5, 0.9],
|
||||
@ -269,16 +269,14 @@ class AppGradients {
|
||||
switch (status.toLowerCase()) {
|
||||
case 'pending':
|
||||
return gradientStatusPending;
|
||||
case 'in_transit':
|
||||
case 'in_progress':
|
||||
case 'inprogress':
|
||||
return gradientStatusInTransit;
|
||||
return gradientStatusInProgress;
|
||||
case 'completed':
|
||||
case 'done':
|
||||
return gradientStatusCompleted;
|
||||
case 'cancelled':
|
||||
case 'skipped':
|
||||
return gradientStatusCancelled;
|
||||
return gradientStatusSkipped;
|
||||
case 'failed':
|
||||
return gradientStatusFailed;
|
||||
default:
|
||||
|
||||
@ -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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user