feat: enhance staff home page with new widgets and improved UI consistency
This commit is contained in:
@@ -35,4 +35,9 @@ extension HomeNavigator on IModularNavigator {
|
||||
pushNamed('/shifts?tab=$tab');
|
||||
}
|
||||
}
|
||||
|
||||
/// Navigates to the settings page.
|
||||
void pushSettings() {
|
||||
pushNamed('/settings');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,12 @@ import 'package:staff_home/src/presentation/widgets/worker/benefits_widget.dart'
|
||||
import 'package:staff_home/src/presentation/widgets/worker/improve_yourself_widget.dart';
|
||||
import 'package:staff_home/src/presentation/widgets/worker/more_ways_widget.dart';
|
||||
|
||||
/// The home page for the staff worker application.
|
||||
///
|
||||
/// This page displays the worker's dashboard including today's shifts,
|
||||
/// tomorrow's shifts, recommended shifts, benefits, and other relevant
|
||||
/// information. It follows Clean Architecture principles with state
|
||||
/// managed by [HomeCubit].
|
||||
/// The home page for the staff worker application.
|
||||
///
|
||||
/// This page displays the worker's dashboard including today's shifts,
|
||||
@@ -27,6 +33,7 @@ import 'package:staff_home/src/presentation/widgets/worker/more_ways_widget.dart
|
||||
/// information. It follows Clean Architecture principles with state
|
||||
/// managed by [HomeCubit].
|
||||
class WorkerHomePage extends StatelessWidget {
|
||||
/// Creates a [WorkerHomePage].
|
||||
const WorkerHomePage({super.key});
|
||||
|
||||
@override
|
||||
@@ -37,19 +44,18 @@ class WorkerHomePage extends StatelessWidget {
|
||||
final sectionsI18n = i18n.sections;
|
||||
final emptyI18n = i18n.empty_states;
|
||||
|
||||
return BlocProvider(
|
||||
create: (_) => Modular.get<HomeCubit>()..loadShifts(),
|
||||
return BlocProvider<HomeCubit>(
|
||||
create: (context) => Modular.get<HomeCubit>()..loadShifts(),
|
||||
child: Scaffold(
|
||||
backgroundColor: UiColors.background,
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.only(bottom: 100),
|
||||
padding: const EdgeInsets.only(bottom: UiConstants.space6),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const HomeHeader(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space4),
|
||||
child: Column(
|
||||
children: [
|
||||
BlocBuilder<HomeCubit, HomeState>(
|
||||
@@ -61,23 +67,23 @@ class WorkerHomePage extends StatelessWidget {
|
||||
return PlaceholderBanner(
|
||||
title: bannersI18n.complete_profile_title,
|
||||
subtitle: bannersI18n.complete_profile_subtitle,
|
||||
bg: Colors.blue[50]!,
|
||||
accent: Colors.blue,
|
||||
bg: UiColors.bgHighlight,
|
||||
accent: UiColors.primary,
|
||||
onTap: () {
|
||||
Modular.to.pushWorkerProfile();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
PlaceholderBanner(
|
||||
title: bannersI18n.availability_title,
|
||||
subtitle: bannersI18n.availability_subtitle,
|
||||
bg: Colors.orange[50]!,
|
||||
accent: Colors.orange,
|
||||
bg: UiColors.accent.withOpacity(0.1),
|
||||
accent: UiColors.accent,
|
||||
onTap: () => Modular.to.pushAvailability(),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
|
||||
// Auto Match Toggle
|
||||
BlocBuilder<HomeCubit, HomeState>(
|
||||
@@ -94,7 +100,7 @@ class WorkerHomePage extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
|
||||
// Quick Actions
|
||||
Row(
|
||||
|
||||
@@ -2,49 +2,43 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:design_system/design_system.dart';
|
||||
|
||||
|
||||
/// Widget for displaying an empty state message, using design system tokens.
|
||||
class EmptyStateWidget extends StatelessWidget {
|
||||
/// The message to display.
|
||||
final String message;
|
||||
/// Optional action link label.
|
||||
final String? actionLink;
|
||||
/// Optional action callback.
|
||||
final VoidCallback? onAction;
|
||||
|
||||
const EmptyStateWidget({
|
||||
super.key,
|
||||
required this.message,
|
||||
this.actionLink,
|
||||
this.onAction,
|
||||
});
|
||||
/// Creates an [EmptyStateWidget].
|
||||
const EmptyStateWidget({super.key, required this.message, this.actionLink, this.onAction});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.all(UiConstants.space4),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF1F3F5),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: UiColors.bgSecondary,
|
||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
message,
|
||||
style: const TextStyle(
|
||||
color: UiColors.mutedForeground,
|
||||
fontSize: 14,
|
||||
),
|
||||
style: UiTypography.body2r.copyWith(color: UiColors.mutedForeground),
|
||||
),
|
||||
if (actionLink != null)
|
||||
GestureDetector(
|
||||
onTap: onAction,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
padding: const EdgeInsets.only(top: UiConstants.space2),
|
||||
child: Text(
|
||||
actionLink!,
|
||||
style: const TextStyle(
|
||||
color: UiColors.primary,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
style: UiTypography.body2m.copyWith(color: UiColors.primary),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
import 'package:staff_home/src/presentation/navigation/home_navigator.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
/// Header widget for the staff home page, using design system tokens.
|
||||
class HomeHeader extends StatelessWidget {
|
||||
/// Creates a [HomeHeader].
|
||||
const HomeHeader({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final headerI18n = t.staff.home.header;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 24, 20, 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
UiConstants.space4,
|
||||
UiConstants.space4,
|
||||
UiConstants.space4,
|
||||
UiConstants.space3,
|
||||
),
|
||||
child:Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
@@ -25,12 +26,12 @@ class HomeHeader extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: UiColors.primary.withValues(alpha: 0.2),
|
||||
color: UiColors.primary.withOpacity(0.2),
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: CircleAvatar(
|
||||
backgroundColor: UiColors.primary.withValues(alpha: 0.1),
|
||||
backgroundColor: UiColors.primary.withOpacity(0.1),
|
||||
child: const Text(
|
||||
'K',
|
||||
style: TextStyle(
|
||||
@@ -40,83 +41,22 @@ class HomeHeader extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const SizedBox(width: UiConstants.space3),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
headerI18n.welcome_back,
|
||||
style: const TextStyle(
|
||||
color: UiColors.mutedForeground,
|
||||
fontSize: 14,
|
||||
),
|
||||
style: UiTypography.body3r.copyWith(color: UiColors.mutedForeground),
|
||||
),
|
||||
Text(
|
||||
headerI18n.user_name_placeholder,
|
||||
style: const TextStyle(
|
||||
color: UiColors.foreground,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: UiTypography.headline4m,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Modular.to.pushMessages(),
|
||||
child: Stack(
|
||||
children: [
|
||||
_buildHeaderIcon(LucideIcons.bell),
|
||||
const Positioned(
|
||||
top: -2,
|
||||
right: -2,
|
||||
child: CircleAvatar(
|
||||
radius: 8,
|
||||
backgroundColor: Color(0xFFF04444),
|
||||
child: Text(
|
||||
'2',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () => Modular.to.pushWorkerProfile(),
|
||||
child: _buildHeaderIcon(LucideIcons.settings),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeaderIcon(IconData icon) {
|
||||
return Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
blurRadius: 2,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Icon(icon, color: UiColors.mutedForeground, size: 20),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,10 @@ import 'package:design_system/design_system.dart';
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
import 'package:staff_home/src/presentation/navigation/home_navigator.dart';
|
||||
|
||||
|
||||
/// Card widget for displaying pending payment information, using design system tokens.
|
||||
class PendingPaymentCard extends StatelessWidget {
|
||||
/// Creates a [PendingPaymentCard].
|
||||
const PendingPaymentCard({super.key});
|
||||
|
||||
@override
|
||||
@@ -15,15 +18,15 @@ class PendingPaymentCard extends StatelessWidget {
|
||||
return GestureDetector(
|
||||
onTap: () => Modular.to.pushPayments(),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.all(UiConstants.space4),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [Colors.blue[50]!.withValues(alpha: 0.5), Colors.blue[50]!],
|
||||
colors: [UiColors.primary.withOpacity(0.08), UiColors.primary.withOpacity(0.04)],
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.blue[100]!.withValues(alpha: 0.5)),
|
||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||
border: Border.all(color: UiColors.primary.withOpacity(0.12)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -35,35 +38,28 @@ class PendingPaymentCard extends StatelessWidget {
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFFE8F0FF),
|
||||
color: UiColors.bgHighlight,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.dollarSign,
|
||||
color: Color(0xFF0047FF),
|
||||
color: UiColors.primary,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const SizedBox(width: UiConstants.space3),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
pendingI18n.title,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14,
|
||||
color: UiColors.foreground,
|
||||
),
|
||||
style: UiTypography.body1b,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Text(
|
||||
pendingI18n.subtitle,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: UiColors.mutedForeground,
|
||||
),
|
||||
style: UiTypography.body3r.copyWith(color: UiColors.mutedForeground),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
@@ -72,20 +68,16 @@ class PendingPaymentCard extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
const Row(
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'\$285.00',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
color: Color(0xFF0047FF),
|
||||
),
|
||||
style: UiTypography.headline4m,
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
SizedBox(width: UiConstants.space2),
|
||||
Icon(
|
||||
LucideIcons.chevronRight,
|
||||
color: Color(0xFF94A3B8),
|
||||
color: UiColors.mutedForeground,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -3,61 +3,58 @@ import 'package:lucide_icons/lucide_icons.dart';
|
||||
|
||||
import 'package:design_system/design_system.dart';
|
||||
|
||||
|
||||
/// Banner widget for placeholder actions, using design system tokens.
|
||||
class PlaceholderBanner extends StatelessWidget {
|
||||
/// Banner title
|
||||
final String title;
|
||||
/// Banner subtitle
|
||||
final String subtitle;
|
||||
/// Banner background color
|
||||
final Color bg;
|
||||
/// Banner accent color
|
||||
final Color accent;
|
||||
/// Optional tap callback
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const PlaceholderBanner({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.bg,
|
||||
required this.accent,
|
||||
this.onTap,
|
||||
});
|
||||
/// Creates a [PlaceholderBanner].
|
||||
const PlaceholderBanner({super.key, required this.title, required this.subtitle, required this.bg, required this.accent, this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.all(UiConstants.space4),
|
||||
decoration: BoxDecoration(
|
||||
color: bg,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: accent.withValues(alpha: 0.3)),
|
||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||
border: Border.all(color: accent.withOpacity(0.3)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
width: 40,
|
||||
height: 40,
|
||||
padding: const EdgeInsets.all(UiConstants.space2),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: UiColors.bgBanner,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(LucideIcons.star, color: accent, size: 20),
|
||||
child: Icon(LucideIcons.sparkles, color: accent, size: 20),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const SizedBox(width: UiConstants.space3),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: UiColors.foreground,
|
||||
),
|
||||
style: UiTypography.body1b,
|
||||
),
|
||||
Text(
|
||||
subtitle,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: UiColors.mutedForeground,
|
||||
),
|
||||
style: UiTypography.body3r.copyWith(color: UiColors.mutedForeground),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -2,17 +2,18 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:design_system/design_system.dart';
|
||||
|
||||
|
||||
/// Widget for a quick action button on the home page, using design system tokens.
|
||||
class QuickActionItem extends StatelessWidget {
|
||||
/// The icon to display.
|
||||
final IconData icon;
|
||||
/// The label for the action.
|
||||
final String label;
|
||||
/// The callback when tapped.
|
||||
final VoidCallback onTap;
|
||||
|
||||
const QuickActionItem({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.onTap,
|
||||
});
|
||||
/// Creates a [QuickActionItem].
|
||||
const QuickActionItem({super.key, required this.icon, required this.label, required this.onTap});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -21,16 +22,16 @@ class QuickActionItem extends StatelessWidget {
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
padding: const EdgeInsets.all(12),
|
||||
width: 64,
|
||||
height: 64,
|
||||
padding: const EdgeInsets.all(UiConstants.space4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: const Color(0xFFF1F5F9)),
|
||||
color: UiColors.bgBanner,
|
||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||
border: Border.all(color: UiColors.bgSecondary),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: UiColors.foreground.withOpacity(0.05),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -38,14 +39,10 @@ class QuickActionItem extends StatelessWidget {
|
||||
),
|
||||
child: Icon(icon, color: UiColors.primary, size: 24),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: UiColors.foreground,
|
||||
),
|
||||
style: UiTypography.body3r.copyWith(color: UiColors.foreground),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -3,32 +3,29 @@ import 'package:lucide_icons/lucide_icons.dart';
|
||||
|
||||
import 'package:design_system/design_system.dart';
|
||||
|
||||
|
||||
/// Section header widget for home page sections, using design system tokens.
|
||||
class SectionHeader extends StatelessWidget {
|
||||
/// Section title
|
||||
final String title;
|
||||
/// Optional action label
|
||||
final String? action;
|
||||
/// Optional action callback
|
||||
final VoidCallback? onAction;
|
||||
|
||||
const SectionHeader({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.action,
|
||||
this.onAction,
|
||||
});
|
||||
/// Creates a [SectionHeader].
|
||||
const SectionHeader({super.key, required this.title, this.action, this.onAction});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: UiColors.foreground,
|
||||
),
|
||||
style: UiTypography.headline4m,
|
||||
),
|
||||
if (action != null)
|
||||
if (onAction != null)
|
||||
@@ -38,11 +35,7 @@ class SectionHeader extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
action!,
|
||||
style: const TextStyle(
|
||||
color: UiColors.primary,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
style: UiTypography.body2m.copyWith(color: UiColors.primary),
|
||||
),
|
||||
const Icon(
|
||||
LucideIcons.chevronRight,
|
||||
@@ -56,19 +49,15 @@ class SectionHeader extends StatelessWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.primary.withValues(alpha: 0.08),
|
||||
color: UiColors.primary.withOpacity(0.08),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: UiColors.primary.withValues(alpha: 0.2),
|
||||
color: UiColors.primary.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
action!,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: UiColors.primary,
|
||||
),
|
||||
style: UiTypography.body3r.copyWith(color: UiColors.primary),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -3,33 +3,33 @@ import 'package:lucide_icons/lucide_icons.dart';
|
||||
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
|
||||
/// Toggle widget for auto-match feature, using design system tokens.
|
||||
class AutoMatchToggle extends StatefulWidget {
|
||||
/// Whether auto-match is enabled.
|
||||
final bool enabled;
|
||||
/// Callback when toggled.
|
||||
final ValueChanged<bool> onToggle;
|
||||
|
||||
const AutoMatchToggle({
|
||||
super.key,
|
||||
required this.enabled,
|
||||
required this.onToggle,
|
||||
});
|
||||
/// Creates an [AutoMatchToggle].
|
||||
const AutoMatchToggle({super.key, required this.enabled, required this.onToggle});
|
||||
|
||||
@override
|
||||
State<AutoMatchToggle> createState() => _AutoMatchToggleState();
|
||||
}
|
||||
|
||||
class _AutoMatchToggleState extends State<AutoMatchToggle>
|
||||
with SingleTickerProviderStateMixin {
|
||||
class _AutoMatchToggleState extends State<AutoMatchToggle> with SingleTickerProviderStateMixin {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final i18n = t.staff.home.auto_match;
|
||||
final Color primary = Theme.of(context).colorScheme.primary;
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
gradient: widget.enabled
|
||||
? const LinearGradient(
|
||||
colors: [Color(0xFF0032A0), Color(0xFF0047CC)],
|
||||
? LinearGradient(
|
||||
colors: [primary, primary.withOpacity(0.8)],
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
)
|
||||
@@ -39,7 +39,7 @@ class _AutoMatchToggleState extends State<AutoMatchToggle>
|
||||
boxShadow: widget.enabled
|
||||
? [
|
||||
BoxShadow(
|
||||
color: const Color(0xFF0032A0).withValues(alpha: 0.3),
|
||||
color: primary.withOpacity(0.3),
|
||||
blurRadius: 10,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
@@ -58,15 +58,13 @@ class _AutoMatchToggleState extends State<AutoMatchToggle>
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.enabled
|
||||
? Colors.white.withValues(alpha: 0.2)
|
||||
: const Color(0xFF0032A0).withValues(alpha: 0.1),
|
||||
? Colors.white.withOpacity(0.2)
|
||||
: primary.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(
|
||||
LucideIcons.zap,
|
||||
color: widget.enabled
|
||||
? Colors.white
|
||||
: const Color(0xFF0032A0),
|
||||
color: widget.enabled ? Colors.white : primary,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
@@ -78,18 +76,14 @@ class _AutoMatchToggleState extends State<AutoMatchToggle>
|
||||
i18n.title,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: widget.enabled
|
||||
? Colors.white
|
||||
: const Color(0xFF0F172A),
|
||||
color: widget.enabled ? Colors.white : const Color(0xFF0F172A),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
widget.enabled ? i18n.finding_shifts : i18n.get_matched,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: widget.enabled
|
||||
? const Color(0xFFF8E08E)
|
||||
: Colors.grey.shade500,
|
||||
color: widget.enabled ? const Color(0xFFF8E08E) : Colors.grey.shade500,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -5,7 +5,10 @@ import 'dart:math' as math;
|
||||
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
|
||||
|
||||
/// Widget for displaying staff benefits, using design system tokens.
|
||||
class BenefitsWidget extends StatelessWidget {
|
||||
/// Creates a [BenefitsWidget].
|
||||
const BenefitsWidget({super.key});
|
||||
|
||||
@override
|
||||
@@ -14,12 +17,12 @@ class BenefitsWidget extends StatelessWidget {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.grey.shade100),
|
||||
border: Border.all(color: Theme.of(context).dividerColor),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: Theme.of(context).colorScheme.onBackground.withOpacity(0.05),
|
||||
blurRadius: 2,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
@@ -32,10 +35,7 @@ class BenefitsWidget extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
i18n.title,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF0F172A),
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () => Modular.to.pushNamed('/benefits'),
|
||||
@@ -43,16 +43,12 @@ class BenefitsWidget extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
i18n.view_all,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF0032A0),
|
||||
),
|
||||
style: Theme.of(context).textTheme.labelLarge?.copyWith(color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
const Icon(
|
||||
Icon(
|
||||
LucideIcons.chevronRight,
|
||||
size: 16,
|
||||
color: Color(0xFF0032A0),
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -67,19 +63,19 @@ class BenefitsWidget extends StatelessWidget {
|
||||
label: i18n.items.sick_days,
|
||||
current: 10,
|
||||
total: 40,
|
||||
color: const Color(0xFF0A39DF),
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
_BenefitItem(
|
||||
label: i18n.items.vacation,
|
||||
current: 40,
|
||||
total: 40,
|
||||
color: const Color(0xFF0A39DF),
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
_BenefitItem(
|
||||
label: i18n.items.holidays,
|
||||
current: 24,
|
||||
total: 24,
|
||||
color: const Color(0xFF0A39DF),
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -2,8 +2,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
|
||||
/// Widget for displaying self-improvement resources, using design system tokens.
|
||||
class ImproveYourselfWidget extends StatelessWidget {
|
||||
/// Creates an [ImproveYourselfWidget].
|
||||
const ImproveYourselfWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final i18n = t.staff.home.improve;
|
||||
@@ -12,16 +15,14 @@ class ImproveYourselfWidget extends StatelessWidget {
|
||||
'id': 'training',
|
||||
'title': i18n.items.training.title,
|
||||
'description': i18n.items.training.description,
|
||||
'image':
|
||||
'https://images.unsplash.com/photo-1524995997946-a1c2e315a42f?w=400&h=300&fit=crop',
|
||||
'image': 'https://images.unsplash.com/photo-1524995997946-a1c2e315a42f?w=400&h=300&fit=crop',
|
||||
'page': i18n.items.training.page,
|
||||
},
|
||||
{
|
||||
'id': 'podcast',
|
||||
'title': i18n.items.podcast.title,
|
||||
'description': i18n.items.podcast.description,
|
||||
'image':
|
||||
'https://images.unsplash.com/photo-1478737270239-2f02b77fc618?w=400&h=300&fit=crop',
|
||||
'image': 'https://images.unsplash.com/photo-1478737270239-2f02b77fc618?w=400&h=300&fit=crop',
|
||||
'page': i18n.items.podcast.page,
|
||||
},
|
||||
];
|
||||
@@ -31,11 +32,7 @@ class ImproveYourselfWidget extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
i18n.title,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF0F172A),
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
SingleChildScrollView(
|
||||
@@ -56,12 +53,12 @@ class ImproveYourselfWidget extends StatelessWidget {
|
||||
width: 160,
|
||||
margin: const EdgeInsets.only(right: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.grey.shade100),
|
||||
border: Border.all(color: Theme.of(context).dividerColor),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: Theme.of(context).colorScheme.onBackground.withOpacity(0.05),
|
||||
blurRadius: 2,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
@@ -78,7 +75,7 @@ class ImproveYourselfWidget extends StatelessWidget {
|
||||
item['image']!,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) => Container(
|
||||
color: Colors.grey.shade200,
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
child: const Icon(
|
||||
Icons.image_not_supported,
|
||||
color: Colors.grey,
|
||||
@@ -93,11 +90,7 @@ class ImproveYourselfWidget extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
item['title']!,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF0F172A),
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
|
||||
@@ -2,8 +2,12 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
|
||||
|
||||
/// Widget for displaying more ways to use Krow, using design system tokens.
|
||||
class MoreWaysToUseKrowWidget extends StatelessWidget {
|
||||
/// Creates a [MoreWaysToUseKrowWidget].
|
||||
const MoreWaysToUseKrowWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final i18n = t.staff.home.more_ways;
|
||||
@@ -11,15 +15,13 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
|
||||
{
|
||||
'id': 'benefits',
|
||||
'title': i18n.items.benefits.title,
|
||||
'image':
|
||||
'https://images.unsplash.com/photo-1481627834876-b7833e8f5570?w=400&h=300&fit=crop',
|
||||
'image': 'https://images.unsplash.com/photo-1481627834876-b7833e8f5570?w=400&h=300&fit=crop',
|
||||
'page': i18n.items.benefits.page,
|
||||
},
|
||||
{
|
||||
'id': 'refer',
|
||||
'title': i18n.items.refer.title,
|
||||
'image':
|
||||
'https://images.unsplash.com/photo-1529156069898-49953e39b3ac?w=400&h=300&fit=crop',
|
||||
'image': 'https://images.unsplash.com/photo-1529156069898-49953e39b3ac?w=400&h=300&fit=crop',
|
||||
'page': i18n.items.refer.page,
|
||||
},
|
||||
];
|
||||
@@ -29,11 +31,7 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
i18n.title,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF0F172A),
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
SingleChildScrollView(
|
||||
@@ -54,12 +52,12 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
|
||||
width: 160,
|
||||
margin: const EdgeInsets.only(right: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: Colors.grey.shade100),
|
||||
border: Border.all(color: Theme.of(context).dividerColor),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: Theme.of(context).colorScheme.onBackground.withOpacity(0.05),
|
||||
blurRadius: 2,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
@@ -76,7 +74,7 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
|
||||
item['image']!,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) => Container(
|
||||
color: Colors.grey.shade200,
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
child: const Icon(
|
||||
Icons.image_not_supported,
|
||||
color: Colors.grey,
|
||||
@@ -88,11 +86,7 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(
|
||||
item['title']!,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF0F172A),
|
||||
),
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user