feat: Refactor mobile UI components to adhere to design system tokens and improve loading and snackbar functionalities
This commit is contained in:
@@ -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.
|
||||
- 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.
|
||||
|
||||
- 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.
|
||||
- rethink the order status, we need to simplify it.
|
||||
|
||||
- Validation layer
|
||||
- Profile info
|
||||
- emergency contact
|
||||
@@ -43,4 +46,8 @@
|
||||
- there should be manual verification by the client even if the ai verification is passed.
|
||||
- to track false positives and false negatives.
|
||||
- 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.
|
||||
@@ -12,3 +12,5 @@ export 'src/widgets/ui_button.dart';
|
||||
export 'src/widgets/ui_chip.dart';
|
||||
export 'src/widgets/ui_error_snackbar.dart';
|
||||
export 'src/widgets/ui_success_snackbar.dart';
|
||||
export 'src/widgets/ui_loading_page.dart';
|
||||
export 'src/widgets/ui_snackbar.dart';
|
||||
|
||||
@@ -120,7 +120,7 @@ class UiColors {
|
||||
static const Color textDescription = mutedForeground;
|
||||
|
||||
/// Success text (#10B981)
|
||||
static const Color textSuccess = Color(0xFF10B981);
|
||||
static const Color textSuccess = Color(0xFF0A8159);
|
||||
|
||||
/// Error text (#F04444)
|
||||
static const Color textError = destructive;
|
||||
|
||||
@@ -225,4 +225,13 @@ class UiIcons {
|
||||
|
||||
/// Globe icon
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.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:lucide_icons/lucide_icons.dart';
|
||||
|
||||
import '../blocs/availability_bloc.dart';
|
||||
import '../blocs/availability_event.dart';
|
||||
@@ -44,7 +44,6 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
return BlocProvider.value(
|
||||
value: _bloc,
|
||||
child: Scaffold(
|
||||
backgroundColor: AppColors.krowBackground,
|
||||
appBar: UiAppBar(
|
||||
title: 'My Availability',
|
||||
centerTitle: false,
|
||||
@@ -53,13 +52,18 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
body: BlocListener<AvailabilityBloc, AvailabilityState>(
|
||||
listener: (context, state) {
|
||||
if (state is AvailabilityLoaded && state.successMessage != null) {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(state.successMessage!),
|
||||
backgroundColor: Colors.green,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
UiSnackbar.show(
|
||||
context,
|
||||
message: state.successMessage!,
|
||||
type: UiSnackbarType.success,
|
||||
);
|
||||
}
|
||||
|
||||
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),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
spacing: 24,
|
||||
children: [
|
||||
_buildQuickSet(context),
|
||||
const SizedBox(height: 24),
|
||||
_buildWeekNavigation(context, state),
|
||||
const SizedBox(height: 24),
|
||||
_buildSelectedDayAvailability(
|
||||
context,
|
||||
state.selectedDayAvailability,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
_buildInfoCard(),
|
||||
],
|
||||
),
|
||||
@@ -96,12 +98,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
),
|
||||
),
|
||||
if (state.isActionInProgress)
|
||||
Container(
|
||||
color: Colors.black.withOpacity(0.3),
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
const UiLoadingPage(), // Show loading overlay during actions
|
||||
],
|
||||
);
|
||||
} else if (state is AvailabilityError) {
|
||||
@@ -119,45 +116,27 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.krowBlue.withOpacity(0.1),
|
||||
color: UiColors.primary.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Quick Set Availability',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF333F48),
|
||||
),
|
||||
style: UiTypography.body4r.copyWith(color: UiColors.textFilter),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: _buildQuickSetButton(context, 'All Week', 'all')),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: _buildQuickSetButton(
|
||||
context,
|
||||
'All Week',
|
||||
'all',
|
||||
),
|
||||
child: _buildQuickSetButton(context, 'Weekdays', 'weekdays'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: _buildQuickSetButton(
|
||||
context,
|
||||
'Weekdays',
|
||||
'weekdays',
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: _buildQuickSetButton(
|
||||
context,
|
||||
'Weekends',
|
||||
'weekends',
|
||||
),
|
||||
child: _buildQuickSetButton(context, 'Weekends', 'weekends'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
@@ -184,23 +163,24 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
return SizedBox(
|
||||
height: 32,
|
||||
child: OutlinedButton(
|
||||
onPressed: () => context.read<AvailabilityBloc>().add(PerformQuickSet(type)),
|
||||
onPressed: () =>
|
||||
context.read<AvailabilityBloc>().add(PerformQuickSet(type)),
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
side: BorderSide(
|
||||
color: isDestructive
|
||||
? Colors.red.withOpacity(0.2)
|
||||
: AppColors.krowBlue.withOpacity(0.2),
|
||||
? UiColors.destructive.withValues(alpha: 0.2)
|
||||
: UiColors.primary.withValues(alpha: 0.2),
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
backgroundColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
foregroundColor: isDestructive ? Colors.red : AppColors.krowBlue,
|
||||
foregroundColor: isDestructive ? UiColors.destructive : UiColors.primary,
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 10, fontWeight: FontWeight.w500),
|
||||
style: UiTypography.body4r,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -221,7 +201,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
border: Border.all(color: Colors.grey.shade100),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
color: UiColors.black.withValues(alpha: 0.05),
|
||||
blurRadius: 2,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
@@ -236,20 +216,24 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_buildNavButton(
|
||||
LucideIcons.chevronLeft,
|
||||
() => context.read<AvailabilityBloc>().add(const NavigateWeek(-1)),
|
||||
UiIcons.chevronLeft,
|
||||
() => context.read<AvailabilityBloc>().add(
|
||||
const NavigateWeek(-1),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
monthYear,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.krowCharcoal,
|
||||
color: UiColors.foreground,
|
||||
),
|
||||
),
|
||||
_buildNavButton(
|
||||
LucideIcons.chevronRight,
|
||||
() => context.read<AvailabilityBloc>().add(const NavigateWeek(1)),
|
||||
UiIcons.chevronRight,
|
||||
() => context.read<AvailabilityBloc>().add(
|
||||
const NavigateWeek(1),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -257,7 +241,9 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
// Days Row
|
||||
Row(
|
||||
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
|
||||
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 isAvailable = day.isAvailable;
|
||||
final isToday = AvailabilityLoaded.isSameDay(day.date, DateTime.now());
|
||||
@@ -292,22 +282,22 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? AppColors.krowBlue
|
||||
? UiColors.primary
|
||||
: (isAvailable
|
||||
? const Color(0xFFECFDF5)
|
||||
: const Color(0xFFF8FAFC)), // emerald-50 or slate-50
|
||||
? const Color(0xFFECFDF5)
|
||||
: const Color(0xFFF8FAFC)), // emerald-50 or slate-50
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? AppColors.krowBlue
|
||||
? UiColors.primary
|
||||
: (isAvailable
|
||||
? const Color(0xFFA7F3D0)
|
||||
: Colors.transparent), // emerald-200
|
||||
? const Color(0xFFA7F3D0)
|
||||
: Colors.transparent), // emerald-200
|
||||
),
|
||||
boxShadow: isSelected
|
||||
? [
|
||||
BoxShadow(
|
||||
color: AppColors.krowBlue.withOpacity(0.3),
|
||||
color: UiColors.primary.withValues(alpha: 0.3),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
@@ -326,22 +316,22 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: isSelected
|
||||
? Colors.white
|
||||
? UiColors.white
|
||||
: (isAvailable
|
||||
? const Color(0xFF047857)
|
||||
: AppColors.krowMuted), // emerald-700
|
||||
? const Color(0xFF047857)
|
||||
: UiColors.mutedForeground), // emerald-700
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
DateFormat('EEE').format(day.date),
|
||||
DateFormat('EEE').format(day.date),
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: isSelected
|
||||
? Colors.white.withOpacity(0.8)
|
||||
? UiColors.white.withValues(alpha: 0.8)
|
||||
: (isAvailable
|
||||
? const Color(0xFF047857)
|
||||
: AppColors.krowMuted),
|
||||
? const Color(0xFF047857)
|
||||
: UiColors.mutedForeground),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -353,7 +343,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
width: 6,
|
||||
height: 6,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.krowBlue,
|
||||
color: UiColors.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
@@ -380,7 +370,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
border: Border.all(color: Colors.grey.shade100),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.05),
|
||||
color: UiColors.black.withValues(alpha: 0.05),
|
||||
blurRadius: 2,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
@@ -400,22 +390,23 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.krowCharcoal,
|
||||
color: UiColors.foreground,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
isAvailable ? 'You are available' : 'Not available',
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.krowMuted,
|
||||
color: UiColors.mutedForeground,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Switch(
|
||||
value: isAvailable,
|
||||
onChanged: (val) => context.read<AvailabilityBloc>().add(ToggleDayStatus(day)),
|
||||
activeColor: AppColors.krowBlue,
|
||||
onChanged: (val) =>
|
||||
context.read<AvailabilityBloc>().add(ToggleDayStatus(day)),
|
||||
activeColor: UiColors.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -426,37 +417,37 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
...day.slots.map((slot) {
|
||||
// Get UI config for this slot ID
|
||||
final uiConfig = _getSlotUiConfig(slot.id);
|
||||
|
||||
|
||||
return _buildTimeSlotItem(context, day, slot, uiConfig);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Map<String, dynamic> _getSlotUiConfig(String slotId) {
|
||||
switch (slotId) {
|
||||
case 'morning':
|
||||
return {
|
||||
'icon': LucideIcons.sunrise,
|
||||
'icon': UiIcons.sunrise,
|
||||
'bg': const Color(0xFFE6EBF9), // bg-[#0032A0]/10
|
||||
'iconColor': const Color(0xFF0032A0),
|
||||
};
|
||||
case 'afternoon':
|
||||
return {
|
||||
'icon': LucideIcons.sun,
|
||||
'icon': UiIcons.sun,
|
||||
'bg': const Color(0xFFCCD6EC), // bg-[#0032A0]/20
|
||||
'iconColor': const Color(0xFF0032A0),
|
||||
};
|
||||
case 'evening':
|
||||
return {
|
||||
'icon': LucideIcons.moon,
|
||||
'icon': UiIcons.moon,
|
||||
'bg': const Color(0xFFEBEDEE), // bg-[#333F48]/10
|
||||
'iconColor': const Color(0xFF333F48),
|
||||
};
|
||||
default:
|
||||
return {
|
||||
'icon': LucideIcons.clock,
|
||||
'icon': UiIcons.clock,
|
||||
'bg': Colors.grey.shade100,
|
||||
'iconColor': Colors.grey,
|
||||
};
|
||||
@@ -464,15 +455,15 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
}
|
||||
|
||||
Widget _buildTimeSlotItem(
|
||||
BuildContext context,
|
||||
DayAvailability day,
|
||||
AvailabilitySlot slot,
|
||||
Map<String, dynamic> uiConfig
|
||||
BuildContext context,
|
||||
DayAvailability day,
|
||||
AvailabilitySlot slot,
|
||||
Map<String, dynamic> uiConfig,
|
||||
) {
|
||||
// Determine styles based on state
|
||||
final isEnabled = day.isAvailable;
|
||||
final isEnabled = day.isAvailable;
|
||||
final isActive = slot.isAvailable;
|
||||
|
||||
|
||||
// Container style
|
||||
Color bgColor;
|
||||
Color borderColor;
|
||||
@@ -481,8 +472,8 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
bgColor = const Color(0xFFF8FAFC); // slate-50
|
||||
borderColor = const Color(0xFFF1F5F9); // slate-100
|
||||
} else if (isActive) {
|
||||
bgColor = AppColors.krowBlue.withOpacity(0.05);
|
||||
borderColor = AppColors.krowBlue.withOpacity(0.2);
|
||||
bgColor = UiColors.primary.withValues(alpha: 0.05);
|
||||
borderColor = UiColors.primary.withValues(alpha: 0.2);
|
||||
} else {
|
||||
bgColor = const Color(0xFFF8FAFC); // slate-50
|
||||
borderColor = const Color(0xFFE2E8F0); // slate-200
|
||||
@@ -490,14 +481,18 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
|
||||
// Text colors
|
||||
final titleColor = (isEnabled && isActive)
|
||||
? AppColors.krowCharcoal
|
||||
: AppColors.krowMuted;
|
||||
? UiColors.foreground
|
||||
: UiColors.mutedForeground;
|
||||
final subtitleColor = (isEnabled && isActive)
|
||||
? AppColors.krowMuted
|
||||
? UiColors.mutedForeground
|
||||
: Colors.grey.shade400;
|
||||
|
||||
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(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
@@ -539,10 +534,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
),
|
||||
Text(
|
||||
slot.timeRange,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: subtitleColor,
|
||||
),
|
||||
style: TextStyle(fontSize: 12, color: subtitleColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -553,14 +545,10 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.krowBlue,
|
||||
color: UiColors.primary,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.check,
|
||||
size: 16,
|
||||
color: Colors.white,
|
||||
),
|
||||
child: const Icon(UiIcons.check, size: 16, color: UiColors.white),
|
||||
)
|
||||
else if (isEnabled && !isActive)
|
||||
Container(
|
||||
@@ -584,13 +572,13 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.krowBlue.withOpacity(0.05),
|
||||
color: UiColors.primary.withValues(alpha: 0.05),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: const Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(LucideIcons.clock, size: 20, color: AppColors.krowBlue),
|
||||
Icon(UiIcons.clock, size: 20, color: UiColors.primary),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
@@ -601,13 +589,13 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.krowCharcoal,
|
||||
color: UiColors.foreground,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
"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;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
name: staff_availability
|
||||
description: Staff Availability Feature
|
||||
version: 0.0.1
|
||||
publish_to: 'none'
|
||||
publish_to: "none"
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
sdk: '>=3.10.0 <4.0.0'
|
||||
sdk: ">=3.10.0 <4.0.0"
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
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
|
||||
core_localization:
|
||||
path: ../../../core_localization
|
||||
@@ -28,6 +23,11 @@ dependencies:
|
||||
path: ../../../data_connect
|
||||
krow_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_auth: ^6.1.4
|
||||
|
||||
|
||||
Reference in New Issue
Block a user