feat: Refactor mobile UI components to adhere to design system tokens and improve loading and snackbar functionalities

This commit is contained in:
Achintha Isuru
2026-02-10 15:34:52 -05:00
parent a34cc5b462
commit 4f94bf6835
8 changed files with 259 additions and 132 deletions

View File

@@ -28,10 +28,13 @@
- track minimum shift hours in the staff profile and show a warning if they try to apply for shifts that are below their minimum hours. - track minimum shift hours in the staff profile and show a warning if they try to apply for shifts that are below their minimum hours.
- this need to be added in the BE and also a FE validation (5 hrs). - this need to be added in the BE and also a FE validation (5 hrs).
- Cannot cancel before 24 hours of the shift start time. If do we should charge for 4 hours of work for each shifts. - Cannot cancel before 24 hours of the shift start time. If do we should charge for 4 hours of work for each shifts.
- verify the order creation process in the client app. - verify the order creation process in the client app.
- Vendor don't need to verify the order, when the order is created it should be automatically published. - Vendor don't need to verify the order, when the order is created it should be automatically published.
- rethink the order status, we need to simplify it. - rethink the order status, we need to simplify it.
- Validation layer - Validation layer
- Profile info - Profile info
- emergency contact - emergency contact
@@ -43,4 +46,8 @@
- there should be manual verification by the client even if the ai verification is passed. - there should be manual verification by the client even if the ai verification is passed.
- to track false positives and false negatives. - to track false positives and false negatives.
- documents - documents
- tax forms - tax forms
- How do we handle the current bank account verifcaiton in the current legacy application.
- We need have a show a list of clothing items in the staff app -> shift page.
- Template models for the pdf reports in the client and web apps.

View File

@@ -12,3 +12,5 @@ export 'src/widgets/ui_button.dart';
export 'src/widgets/ui_chip.dart'; export 'src/widgets/ui_chip.dart';
export 'src/widgets/ui_error_snackbar.dart'; export 'src/widgets/ui_error_snackbar.dart';
export 'src/widgets/ui_success_snackbar.dart'; export 'src/widgets/ui_success_snackbar.dart';
export 'src/widgets/ui_loading_page.dart';
export 'src/widgets/ui_snackbar.dart';

View File

@@ -120,7 +120,7 @@ class UiColors {
static const Color textDescription = mutedForeground; static const Color textDescription = mutedForeground;
/// Success text (#10B981) /// Success text (#10B981)
static const Color textSuccess = Color(0xFF10B981); static const Color textSuccess = Color(0xFF0A8159);
/// Error text (#F04444) /// Error text (#F04444)
static const Color textError = destructive; static const Color textError = destructive;

View File

@@ -225,4 +225,13 @@ class UiIcons {
/// Globe icon /// Globe icon
static const IconData globe = _IconLib.globe; static const IconData globe = _IconLib.globe;
/// Sunrise icon
static const IconData sunrise = _IconLib.sunrise;
/// Sun icon
static const IconData sun = _IconLib.sun;
/// Moon icon
static const IconData moon = _IconLib.moon;
} }

View File

@@ -0,0 +1,33 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import '../ui_colors.dart';
/// A common loading page that adheres to the design system.
/// It features a blurred background using [UiColors.popupShadow] and a [CircularProgressIndicator].
class UiLoadingPage extends StatelessWidget {
/// Creates a [UiLoadingPage].
const UiLoadingPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: UiColors.transparent,
body: Stack(
fit: StackFit.expand,
children: <Widget>[
// Background blur and color
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(color: UiColors.popupShadow),
),
),
// Center loading indicator
const Center(child: CircularProgressIndicator()),
],
),
);
}
}

View File

@@ -0,0 +1,99 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import '../ui_colors.dart';
import '../ui_icons.dart';
import '../ui_typography.dart';
/// Types of snackbars available in the design system.
enum UiSnackbarType {
/// Success state - green text and light blurred green background.
success,
/// Message state - blue text and light blurred blue background.
message,
/// Warning state - dark yellow text and light blurred yellow background.
warning,
/// Error state - red text and light blurred red background.
error,
}
/// A centralized snackbar widget that adheres to the design system with glassmorphism effects.
class UiSnackbar {
UiSnackbar._();
/// Shows a snackbar with the specified [message] and [type].
static void show(
BuildContext context, {
required String message,
required UiSnackbarType type,
Duration duration = const Duration(seconds: 3),
}) {
final Color textColor;
final Color backgroundColor;
final IconData icon;
switch (type) {
case UiSnackbarType.success:
textColor = UiColors.textSuccess;
backgroundColor = UiColors.tagSuccess.withValues(alpha: 0.7);
icon = UiIcons.success;
break;
case UiSnackbarType.message:
textColor = UiColors.primary;
backgroundColor = UiColors.tagInProgress.withValues(alpha: 0.7);
icon = UiIcons.info;
break;
case UiSnackbarType.warning:
textColor = UiColors.textWarning;
backgroundColor = UiColors.tagPending.withValues(alpha: 0.7);
icon = UiIcons.warning;
break;
case UiSnackbarType.error:
textColor = UiColors.textError;
backgroundColor = UiColors.tagError.withValues(alpha: 0.7);
icon = UiIcons.error;
break;
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: duration,
backgroundColor: UiColors.transparent,
elevation: 0,
behavior: SnackBarBehavior.floating,
content: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: textColor.withValues(alpha: 0.2),
width: 1,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
spacing: 12,
children: <Widget>[
Icon(icon, color: textColor, size: 20),
Expanded(
child: Text(
message,
style: UiTypography.body2b.copyWith(color: textColor),
),
),
],
),
),
),
),
),
);
}
}

View File

@@ -1,9 +1,9 @@
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart' hide ModularWatchExtension; import 'package:flutter_modular/flutter_modular.dart'
hide ModularWatchExtension;
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../blocs/availability_bloc.dart'; import '../blocs/availability_bloc.dart';
import '../blocs/availability_event.dart'; import '../blocs/availability_event.dart';
@@ -44,7 +44,6 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
return BlocProvider.value( return BlocProvider.value(
value: _bloc, value: _bloc,
child: Scaffold( child: Scaffold(
backgroundColor: AppColors.krowBackground,
appBar: UiAppBar( appBar: UiAppBar(
title: 'My Availability', title: 'My Availability',
centerTitle: false, centerTitle: false,
@@ -53,13 +52,18 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
body: BlocListener<AvailabilityBloc, AvailabilityState>( body: BlocListener<AvailabilityBloc, AvailabilityState>(
listener: (context, state) { listener: (context, state) {
if (state is AvailabilityLoaded && state.successMessage != null) { if (state is AvailabilityLoaded && state.successMessage != null) {
ScaffoldMessenger.of(context).hideCurrentSnackBar(); UiSnackbar.show(
ScaffoldMessenger.of(context).showSnackBar( context,
SnackBar( message: state.successMessage!,
content: Text(state.successMessage!), type: UiSnackbarType.success,
backgroundColor: Colors.green, );
behavior: SnackBarBehavior.floating, }
),
if (state is AvailabilityError) {
UiSnackbar.show(
context,
message: state.message,
type: UiSnackbarType.error,
); );
} }
}, },
@@ -78,16 +82,14 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
padding: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
spacing: 24,
children: [ children: [
_buildQuickSet(context), _buildQuickSet(context),
const SizedBox(height: 24),
_buildWeekNavigation(context, state), _buildWeekNavigation(context, state),
const SizedBox(height: 24),
_buildSelectedDayAvailability( _buildSelectedDayAvailability(
context, context,
state.selectedDayAvailability, state.selectedDayAvailability,
), ),
const SizedBox(height: 24),
_buildInfoCard(), _buildInfoCard(),
], ],
), ),
@@ -96,12 +98,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
), ),
), ),
if (state.isActionInProgress) if (state.isActionInProgress)
Container( const UiLoadingPage(), // Show loading overlay during actions
color: Colors.black.withOpacity(0.3),
child: const Center(
child: CircularProgressIndicator(),
),
),
], ],
); );
} else if (state is AvailabilityError) { } else if (state is AvailabilityError) {
@@ -119,45 +116,27 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.krowBlue.withOpacity(0.1), color: UiColors.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const Text( Text(
'Quick Set Availability', 'Quick Set Availability',
style: TextStyle( style: UiTypography.body4r.copyWith(color: UiColors.textFilter),
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF333F48),
),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Row( Row(
children: [ children: [
Expanded(child: _buildQuickSetButton(context, 'All Week', 'all')),
const SizedBox(width: 8),
Expanded( Expanded(
child: _buildQuickSetButton( child: _buildQuickSetButton(context, 'Weekdays', 'weekdays'),
context,
'All Week',
'all',
),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded( Expanded(
child: _buildQuickSetButton( child: _buildQuickSetButton(context, 'Weekends', 'weekends'),
context,
'Weekdays',
'weekdays',
),
),
const SizedBox(width: 8),
Expanded(
child: _buildQuickSetButton(
context,
'Weekends',
'weekends',
),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded( Expanded(
@@ -184,23 +163,24 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
return SizedBox( return SizedBox(
height: 32, height: 32,
child: OutlinedButton( child: OutlinedButton(
onPressed: () => context.read<AvailabilityBloc>().add(PerformQuickSet(type)), onPressed: () =>
context.read<AvailabilityBloc>().add(PerformQuickSet(type)),
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
side: BorderSide( side: BorderSide(
color: isDestructive color: isDestructive
? Colors.red.withOpacity(0.2) ? UiColors.destructive.withValues(alpha: 0.2)
: AppColors.krowBlue.withOpacity(0.2), : UiColors.primary.withValues(alpha: 0.2),
), ),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
foregroundColor: isDestructive ? Colors.red : AppColors.krowBlue, foregroundColor: isDestructive ? UiColors.destructive : UiColors.primary,
), ),
child: Text( child: Text(
label, label,
style: const TextStyle(fontSize: 10, fontWeight: FontWeight.w500), style: UiTypography.body4r,
maxLines: 1, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@@ -221,7 +201,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
border: Border.all(color: Colors.grey.shade100), border: Border.all(color: Colors.grey.shade100),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2, blurRadius: 2,
offset: const Offset(0, 1), offset: const Offset(0, 1),
), ),
@@ -236,20 +216,24 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
_buildNavButton( _buildNavButton(
LucideIcons.chevronLeft, UiIcons.chevronLeft,
() => context.read<AvailabilityBloc>().add(const NavigateWeek(-1)), () => context.read<AvailabilityBloc>().add(
const NavigateWeek(-1),
),
), ),
Text( Text(
monthYear, monthYear,
style: const TextStyle( style: const TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: AppColors.krowCharcoal, color: UiColors.foreground,
), ),
), ),
_buildNavButton( _buildNavButton(
LucideIcons.chevronRight, UiIcons.chevronRight,
() => context.read<AvailabilityBloc>().add(const NavigateWeek(1)), () => context.read<AvailabilityBloc>().add(
const NavigateWeek(1),
),
), ),
], ],
), ),
@@ -257,7 +241,9 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
// Days Row // Days Row
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: state.days.map((day) => _buildDayItem(context, day, state.selectedDate)).toList(), children: state.days
.map((day) => _buildDayItem(context, day, state.selectedDate))
.toList(),
), ),
], ],
), ),
@@ -274,12 +260,16 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
color: Color(0xFFF1F5F9), // slate-100 color: Color(0xFFF1F5F9), // slate-100
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: Icon(icon, size: 20, color: AppColors.krowMuted), child: Icon(icon, size: 20, color: UiColors.mutedForeground),
), ),
); );
} }
Widget _buildDayItem(BuildContext context, DayAvailability day, DateTime selectedDate) { Widget _buildDayItem(
BuildContext context,
DayAvailability day,
DateTime selectedDate,
) {
final isSelected = AvailabilityLoaded.isSameDay(day.date, selectedDate); final isSelected = AvailabilityLoaded.isSameDay(day.date, selectedDate);
final isAvailable = day.isAvailable; final isAvailable = day.isAvailable;
final isToday = AvailabilityLoaded.isSameDay(day.date, DateTime.now()); final isToday = AvailabilityLoaded.isSameDay(day.date, DateTime.now());
@@ -292,22 +282,22 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
padding: const EdgeInsets.symmetric(vertical: 12), padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected color: isSelected
? AppColors.krowBlue ? UiColors.primary
: (isAvailable : (isAvailable
? const Color(0xFFECFDF5) ? const Color(0xFFECFDF5)
: const Color(0xFFF8FAFC)), // emerald-50 or slate-50 : const Color(0xFFF8FAFC)), // emerald-50 or slate-50
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
border: Border.all( border: Border.all(
color: isSelected color: isSelected
? AppColors.krowBlue ? UiColors.primary
: (isAvailable : (isAvailable
? const Color(0xFFA7F3D0) ? const Color(0xFFA7F3D0)
: Colors.transparent), // emerald-200 : Colors.transparent), // emerald-200
), ),
boxShadow: isSelected boxShadow: isSelected
? [ ? [
BoxShadow( BoxShadow(
color: AppColors.krowBlue.withOpacity(0.3), color: UiColors.primary.withValues(alpha: 0.3),
blurRadius: 8, blurRadius: 8,
offset: const Offset(0, 4), offset: const Offset(0, 4),
), ),
@@ -326,22 +316,22 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: isSelected color: isSelected
? Colors.white ? UiColors.white
: (isAvailable : (isAvailable
? const Color(0xFF047857) ? const Color(0xFF047857)
: AppColors.krowMuted), // emerald-700 : UiColors.mutedForeground), // emerald-700
), ),
), ),
const SizedBox(height: 2), const SizedBox(height: 2),
Text( Text(
DateFormat('EEE').format(day.date), DateFormat('EEE').format(day.date),
style: TextStyle( style: TextStyle(
fontSize: 10, fontSize: 10,
color: isSelected color: isSelected
? Colors.white.withOpacity(0.8) ? UiColors.white.withValues(alpha: 0.8)
: (isAvailable : (isAvailable
? const Color(0xFF047857) ? const Color(0xFF047857)
: AppColors.krowMuted), : UiColors.mutedForeground),
), ),
), ),
], ],
@@ -353,7 +343,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
width: 6, width: 6,
height: 6, height: 6,
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: AppColors.krowBlue, color: UiColors.primary,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
), ),
@@ -380,7 +370,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
border: Border.all(color: Colors.grey.shade100), border: Border.all(color: Colors.grey.shade100),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withOpacity(0.05), color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2, blurRadius: 2,
offset: const Offset(0, 1), offset: const Offset(0, 1),
), ),
@@ -400,22 +390,23 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
style: const TextStyle( style: const TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: AppColors.krowCharcoal, color: UiColors.foreground,
), ),
), ),
Text( Text(
isAvailable ? 'You are available' : 'Not available', isAvailable ? 'You are available' : 'Not available',
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
color: AppColors.krowMuted, color: UiColors.mutedForeground,
), ),
), ),
], ],
), ),
Switch( Switch(
value: isAvailable, value: isAvailable,
onChanged: (val) => context.read<AvailabilityBloc>().add(ToggleDayStatus(day)), onChanged: (val) =>
activeColor: AppColors.krowBlue, context.read<AvailabilityBloc>().add(ToggleDayStatus(day)),
activeColor: UiColors.primary,
), ),
], ],
), ),
@@ -426,37 +417,37 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
...day.slots.map((slot) { ...day.slots.map((slot) {
// Get UI config for this slot ID // Get UI config for this slot ID
final uiConfig = _getSlotUiConfig(slot.id); final uiConfig = _getSlotUiConfig(slot.id);
return _buildTimeSlotItem(context, day, slot, uiConfig); return _buildTimeSlotItem(context, day, slot, uiConfig);
}).toList(), }).toList(),
], ],
), ),
); );
} }
Map<String, dynamic> _getSlotUiConfig(String slotId) { Map<String, dynamic> _getSlotUiConfig(String slotId) {
switch (slotId) { switch (slotId) {
case 'morning': case 'morning':
return { return {
'icon': LucideIcons.sunrise, 'icon': UiIcons.sunrise,
'bg': const Color(0xFFE6EBF9), // bg-[#0032A0]/10 'bg': const Color(0xFFE6EBF9), // bg-[#0032A0]/10
'iconColor': const Color(0xFF0032A0), 'iconColor': const Color(0xFF0032A0),
}; };
case 'afternoon': case 'afternoon':
return { return {
'icon': LucideIcons.sun, 'icon': UiIcons.sun,
'bg': const Color(0xFFCCD6EC), // bg-[#0032A0]/20 'bg': const Color(0xFFCCD6EC), // bg-[#0032A0]/20
'iconColor': const Color(0xFF0032A0), 'iconColor': const Color(0xFF0032A0),
}; };
case 'evening': case 'evening':
return { return {
'icon': LucideIcons.moon, 'icon': UiIcons.moon,
'bg': const Color(0xFFEBEDEE), // bg-[#333F48]/10 'bg': const Color(0xFFEBEDEE), // bg-[#333F48]/10
'iconColor': const Color(0xFF333F48), 'iconColor': const Color(0xFF333F48),
}; };
default: default:
return { return {
'icon': LucideIcons.clock, 'icon': UiIcons.clock,
'bg': Colors.grey.shade100, 'bg': Colors.grey.shade100,
'iconColor': Colors.grey, 'iconColor': Colors.grey,
}; };
@@ -464,15 +455,15 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
} }
Widget _buildTimeSlotItem( Widget _buildTimeSlotItem(
BuildContext context, BuildContext context,
DayAvailability day, DayAvailability day,
AvailabilitySlot slot, AvailabilitySlot slot,
Map<String, dynamic> uiConfig Map<String, dynamic> uiConfig,
) { ) {
// Determine styles based on state // Determine styles based on state
final isEnabled = day.isAvailable; final isEnabled = day.isAvailable;
final isActive = slot.isAvailable; final isActive = slot.isAvailable;
// Container style // Container style
Color bgColor; Color bgColor;
Color borderColor; Color borderColor;
@@ -481,8 +472,8 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
bgColor = const Color(0xFFF8FAFC); // slate-50 bgColor = const Color(0xFFF8FAFC); // slate-50
borderColor = const Color(0xFFF1F5F9); // slate-100 borderColor = const Color(0xFFF1F5F9); // slate-100
} else if (isActive) { } else if (isActive) {
bgColor = AppColors.krowBlue.withOpacity(0.05); bgColor = UiColors.primary.withValues(alpha: 0.05);
borderColor = AppColors.krowBlue.withOpacity(0.2); borderColor = UiColors.primary.withValues(alpha: 0.2);
} else { } else {
bgColor = const Color(0xFFF8FAFC); // slate-50 bgColor = const Color(0xFFF8FAFC); // slate-50
borderColor = const Color(0xFFE2E8F0); // slate-200 borderColor = const Color(0xFFE2E8F0); // slate-200
@@ -490,14 +481,18 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
// Text colors // Text colors
final titleColor = (isEnabled && isActive) final titleColor = (isEnabled && isActive)
? AppColors.krowCharcoal ? UiColors.foreground
: AppColors.krowMuted; : UiColors.mutedForeground;
final subtitleColor = (isEnabled && isActive) final subtitleColor = (isEnabled && isActive)
? AppColors.krowMuted ? UiColors.mutedForeground
: Colors.grey.shade400; : Colors.grey.shade400;
return GestureDetector( return GestureDetector(
onTap: isEnabled ? () => context.read<AvailabilityBloc>().add(ToggleSlotStatus(day, slot.id)) : null, onTap: isEnabled
? () => context.read<AvailabilityBloc>().add(
ToggleSlotStatus(day, slot.id),
)
: null,
child: AnimatedContainer( child: AnimatedContainer(
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
margin: const EdgeInsets.only(bottom: 12), margin: const EdgeInsets.only(bottom: 12),
@@ -539,10 +534,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
), ),
Text( Text(
slot.timeRange, slot.timeRange,
style: TextStyle( style: TextStyle(fontSize: 12, color: subtitleColor),
fontSize: 12,
color: subtitleColor,
),
), ),
], ],
), ),
@@ -553,14 +545,10 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
width: 24, width: 24,
height: 24, height: 24,
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: AppColors.krowBlue, color: UiColors.primary,
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: const Icon( child: const Icon(UiIcons.check, size: 16, color: UiColors.white),
LucideIcons.check,
size: 16,
color: Colors.white,
),
) )
else if (isEnabled && !isActive) else if (isEnabled && !isActive)
Container( Container(
@@ -584,13 +572,13 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.krowBlue.withOpacity(0.05), color: UiColors.primary.withValues(alpha: 0.05),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: const Row( child: const Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Icon(LucideIcons.clock, size: 20, color: AppColors.krowBlue), Icon(UiIcons.clock, size: 20, color: UiColors.primary),
SizedBox(width: 12), SizedBox(width: 12),
Expanded( Expanded(
child: Column( child: Column(
@@ -601,13 +589,13 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: AppColors.krowCharcoal, color: UiColors.foreground,
), ),
), ),
SizedBox(height: 2), SizedBox(height: 2),
Text( Text(
"When enabled, you'll only be matched with shifts during your available times.", "When enabled, you'll only be matched with shifts during your available times.",
style: TextStyle(fontSize: 12, color: AppColors.krowMuted), style: TextStyle(fontSize: 12, color: UiColors.mutedForeground),
), ),
], ],
), ),
@@ -618,14 +606,3 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
} }
} }
class AppColors {
static const Color krowBlue = Color(0xFF0A39DF);
static const Color krowYellow = Color(0xFFFFED4A);
static const Color krowCharcoal = Color(0xFF121826);
static const Color krowMuted = Color(0xFF6A7382);
static const Color krowBorder = Color(0xFFE3E6E9);
static const Color krowBackground = Color(0xFFFAFBFC);
static const Color white = Colors.white;
static const Color black = Colors.black;
}

View File

@@ -1,22 +1,17 @@
name: staff_availability name: staff_availability
description: Staff Availability Feature description: Staff Availability Feature
version: 0.0.1 version: 0.0.1
publish_to: 'none' publish_to: "none"
resolution: workspace resolution: workspace
environment: environment:
sdk: '>=3.10.0 <4.0.0' sdk: ">=3.10.0 <4.0.0"
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_bloc: ^8.1.3
equatable: ^2.0.5
intl: ^0.20.0
lucide_icons: ^0.257.0
flutter_modular: ^6.3.2
# Internal packages # Internal packages
core_localization: core_localization:
path: ../../../core_localization path: ../../../core_localization
@@ -28,6 +23,11 @@ dependencies:
path: ../../../data_connect path: ../../../data_connect
krow_core: krow_core:
path: ../../../core path: ../../../core
flutter_bloc: ^8.1.3
equatable: ^2.0.5
intl: ^0.20.0
flutter_modular: ^6.3.2
firebase_data_connect: ^0.2.2+2 firebase_data_connect: ^0.2.2+2
firebase_auth: ^6.1.4 firebase_auth: ^6.1.4