Refactor UI components to use design system tokens for consistency
- Removed the UiSuccessSnackbar widget as it is no longer needed. - Updated WorkerHomePage to replace hardcoded spacing with UiConstants. - Refactored HomeHeader to use UiConstants for dimensions and colors. - Modified PlaceholderBanner to utilize UiConstants for sizes and colors. - Adjusted QuickActionItem to apply UiConstants for dimensions and icon sizes. - Updated RecommendedShiftCard to use design system typography and constants. - Refined SectionHeader to implement design system styles and spacing. - Enhanced ShiftCard to adopt design system tokens for colors and spacing. - Updated AutoMatchToggle to use design system colors and dimensions. - Refactored BenefitsWidget to apply design system styles and constants. - Improved ImproveYourselfWidget to utilize design system tokens for styling. - Updated MoreWaysToUseKrowWidget to implement design system styles and constants.
This commit is contained in:
@@ -10,7 +10,5 @@ export 'src/widgets/ui_step_indicator.dart';
|
|||||||
export 'src/widgets/ui_icon_button.dart';
|
export 'src/widgets/ui_icon_button.dart';
|
||||||
export 'src/widgets/ui_button.dart';
|
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_success_snackbar.dart';
|
|
||||||
export 'src/widgets/ui_loading_page.dart';
|
export 'src/widgets/ui_loading_page.dart';
|
||||||
export 'src/widgets/ui_snackbar.dart';
|
export 'src/widgets/ui_snackbar.dart';
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ class UiTheme {
|
|||||||
}
|
}
|
||||||
return UiColors.switchInactive;
|
return UiColors.switchInactive;
|
||||||
}),
|
}),
|
||||||
thumbColor: const WidgetStatePropertyAll(UiColors.white),
|
thumbColor: const WidgetStatePropertyAll<Color>(UiColors.white),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Checkbox Theme
|
// Checkbox Theme
|
||||||
|
|||||||
@@ -1,202 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:core_localization/core_localization.dart';
|
|
||||||
import '../ui_colors.dart';
|
|
||||||
import '../ui_typography.dart';
|
|
||||||
|
|
||||||
/// Centralized error snackbar for consistent error presentation across the app.
|
|
||||||
///
|
|
||||||
/// This widget automatically resolves localization keys and displays
|
|
||||||
/// user-friendly error messages with optional error codes for support.
|
|
||||||
///
|
|
||||||
/// Usage:
|
|
||||||
/// ```dart
|
|
||||||
/// UiErrorSnackbar.show(
|
|
||||||
/// context,
|
|
||||||
/// messageKey: 'errors.auth.invalid_credentials',
|
|
||||||
/// errorCode: 'AUTH_001',
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
class UiErrorSnackbar {
|
|
||||||
/// Shows an error snackbar with a localized message.
|
|
||||||
///
|
|
||||||
/// [messageKey] should be a dot-separated path like 'errors.auth.invalid_credentials'
|
|
||||||
/// [errorCode] is optional and will be shown in smaller text for support reference
|
|
||||||
/// [duration] controls how long the snackbar is visible
|
|
||||||
static void show(
|
|
||||||
BuildContext context, {
|
|
||||||
required String messageKey,
|
|
||||||
String? errorCode,
|
|
||||||
Duration duration = const Duration(seconds: 4),
|
|
||||||
}) {
|
|
||||||
// 1. Added explicit type 'Translations' to satisfy the lint
|
|
||||||
final Translations texts = Translations.of(context);
|
|
||||||
final String message = _getMessageFromKey(texts, messageKey);
|
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.error_outline, color: UiColors.white),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
message,
|
|
||||||
style: UiTypography.body2m.copyWith(color: UiColors.white),
|
|
||||||
),
|
|
||||||
if (errorCode != null) ...[
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
'Error Code: $errorCode',
|
|
||||||
style: UiTypography.footnote2r.copyWith(
|
|
||||||
// 3. Fixed deprecated member use
|
|
||||||
color: UiColors.white.withValues(alpha: 0.7),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
backgroundColor: UiColors.error,
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
duration: duration,
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
|
||||||
margin: const EdgeInsets.all(16),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolves a localization key path to the actual translated message.
|
|
||||||
///
|
|
||||||
/// Supports keys like:
|
|
||||||
/// - errors.auth.invalid_credentials
|
|
||||||
/// - errors.hub.has_orders
|
|
||||||
/// - errors.generic.unknown
|
|
||||||
static String _getMessageFromKey(Translations texts, String key) {
|
|
||||||
// Parse key like "errors.auth.invalid_credentials"
|
|
||||||
final parts = key.split('.');
|
|
||||||
if (parts.length < 2) return texts.errors.generic.unknown;
|
|
||||||
|
|
||||||
try {
|
|
||||||
switch (parts[1]) {
|
|
||||||
case 'auth':
|
|
||||||
return _getAuthError(texts, parts.length > 2 ? parts[2] : '');
|
|
||||||
case 'hub':
|
|
||||||
return _getHubError(texts, parts.length > 2 ? parts[2] : '');
|
|
||||||
case 'order':
|
|
||||||
return _getOrderError(texts, parts.length > 2 ? parts[2] : '');
|
|
||||||
case 'profile':
|
|
||||||
return _getProfileError(texts, parts.length > 2 ? parts[2] : '');
|
|
||||||
case 'shift':
|
|
||||||
return _getShiftError(texts, parts.length > 2 ? parts[2] : '');
|
|
||||||
case 'generic':
|
|
||||||
return _getGenericError(texts, parts.length > 2 ? parts[2] : '');
|
|
||||||
default:
|
|
||||||
return texts.errors.generic.unknown;
|
|
||||||
}
|
|
||||||
} catch (_) {
|
|
||||||
return texts.errors.generic.unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _getAuthError(Translations texts, String key) {
|
|
||||||
switch (key) {
|
|
||||||
case 'invalid_credentials':
|
|
||||||
return texts.errors.auth.invalid_credentials;
|
|
||||||
case 'account_exists':
|
|
||||||
return texts.errors.auth.account_exists;
|
|
||||||
case 'session_expired':
|
|
||||||
return texts.errors.auth.session_expired;
|
|
||||||
case 'user_not_found':
|
|
||||||
return texts.errors.auth.user_not_found;
|
|
||||||
case 'unauthorized_app':
|
|
||||||
return texts.errors.auth.unauthorized_app;
|
|
||||||
case 'weak_password':
|
|
||||||
return texts.errors.auth.weak_password;
|
|
||||||
case 'sign_up_failed':
|
|
||||||
return texts.errors.auth.sign_up_failed;
|
|
||||||
case 'sign_in_failed':
|
|
||||||
return texts.errors.auth.sign_in_failed;
|
|
||||||
case 'not_authenticated':
|
|
||||||
return texts.errors.auth.not_authenticated;
|
|
||||||
case 'password_mismatch':
|
|
||||||
return texts.errors.auth.password_mismatch;
|
|
||||||
case 'google_only_account':
|
|
||||||
return texts.errors.auth.google_only_account;
|
|
||||||
default:
|
|
||||||
return texts.errors.generic.unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _getHubError(Translations texts, String key) {
|
|
||||||
switch (key) {
|
|
||||||
case 'has_orders':
|
|
||||||
return texts.errors.hub.has_orders;
|
|
||||||
case 'not_found':
|
|
||||||
return texts.errors.hub.not_found;
|
|
||||||
case 'creation_failed':
|
|
||||||
return texts.errors.hub.creation_failed;
|
|
||||||
default:
|
|
||||||
return texts.errors.generic.unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _getOrderError(Translations texts, String key) {
|
|
||||||
switch (key) {
|
|
||||||
case 'missing_hub':
|
|
||||||
return texts.errors.order.missing_hub;
|
|
||||||
case 'missing_vendor':
|
|
||||||
return texts.errors.order.missing_vendor;
|
|
||||||
case 'creation_failed':
|
|
||||||
return texts.errors.order.creation_failed;
|
|
||||||
case 'shift_creation_failed':
|
|
||||||
return texts.errors.order.shift_creation_failed;
|
|
||||||
case 'missing_business':
|
|
||||||
return texts.errors.order.missing_business;
|
|
||||||
default:
|
|
||||||
return texts.errors.generic.unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _getProfileError(Translations texts, String key) {
|
|
||||||
switch (key) {
|
|
||||||
case 'staff_not_found':
|
|
||||||
return texts.errors.profile.staff_not_found;
|
|
||||||
case 'business_not_found':
|
|
||||||
return texts.errors.profile.business_not_found;
|
|
||||||
case 'update_failed':
|
|
||||||
return texts.errors.profile.update_failed;
|
|
||||||
default:
|
|
||||||
return texts.errors.generic.unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _getShiftError(Translations texts, String key) {
|
|
||||||
switch (key) {
|
|
||||||
case 'no_open_roles':
|
|
||||||
return texts.errors.shift.no_open_roles;
|
|
||||||
case 'application_not_found':
|
|
||||||
return texts.errors.shift.application_not_found;
|
|
||||||
case 'no_active_shift':
|
|
||||||
return texts.errors.shift.no_active_shift;
|
|
||||||
default:
|
|
||||||
return texts.errors.generic.unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static String _getGenericError(Translations texts, String key) {
|
|
||||||
switch (key) {
|
|
||||||
case 'unknown':
|
|
||||||
return texts.errors.generic.unknown;
|
|
||||||
case 'no_connection':
|
|
||||||
return texts.errors.generic.no_connection;
|
|
||||||
default:
|
|
||||||
return texts.errors.generic.unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,12 +8,6 @@ import '../ui_icons.dart';
|
|||||||
/// This widget shows a series of circular step indicators connected by lines,
|
/// This widget shows a series of circular step indicators connected by lines,
|
||||||
/// with different visual states for completed, active, and inactive steps.
|
/// with different visual states for completed, active, and inactive steps.
|
||||||
class UiStepIndicator extends StatelessWidget {
|
class UiStepIndicator extends StatelessWidget {
|
||||||
/// The list of icons to display for each step.
|
|
||||||
final List<IconData> stepIcons;
|
|
||||||
|
|
||||||
/// The index of the currently active step (0-based).
|
|
||||||
final int currentStep;
|
|
||||||
|
|
||||||
/// Creates a [UiStepIndicator].
|
/// Creates a [UiStepIndicator].
|
||||||
const UiStepIndicator({
|
const UiStepIndicator({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -21,6 +15,12 @@ class UiStepIndicator extends StatelessWidget {
|
|||||||
required this.currentStep,
|
required this.currentStep,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The list of icons to display for each step.
|
||||||
|
final List<IconData> stepIcons;
|
||||||
|
|
||||||
|
/// The index of the currently active step (0-based).
|
||||||
|
final int currentStep;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
/// Builds the step indicator UI.
|
/// Builds the step indicator UI.
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -35,7 +35,7 @@ class UiStepIndicator extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.symmetric(vertical: UiConstants.space2),
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space2),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: List.generate(stepIcons.length, (int index) {
|
children: List<Widget>.generate(stepIcons.length, (int index) {
|
||||||
final bool isActive = index == currentStep;
|
final bool isActive = index == currentStep;
|
||||||
final bool isCompleted = index < currentStep;
|
final bool isCompleted = index < currentStep;
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import '../ui_colors.dart';
|
|
||||||
import '../ui_typography.dart';
|
|
||||||
|
|
||||||
/// Centralized success snackbar for consistent success message presentation.
|
|
||||||
///
|
|
||||||
/// This widget provides a unified way to show success feedback across the app
|
|
||||||
/// with consistent styling and behavior.
|
|
||||||
///
|
|
||||||
/// Usage:
|
|
||||||
/// ```dart
|
|
||||||
/// UiSuccessSnackbar.show(
|
|
||||||
/// context,
|
|
||||||
/// message: 'Profile updated successfully!',
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
class UiSuccessSnackbar {
|
|
||||||
/// Shows a success snackbar with a custom message.
|
|
||||||
///
|
|
||||||
/// [message] is the success message to display
|
|
||||||
/// [duration] controls how long the snackbar is visible
|
|
||||||
static void show(
|
|
||||||
BuildContext context, {
|
|
||||||
required String message,
|
|
||||||
Duration duration = const Duration(seconds: 3),
|
|
||||||
}) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Row(
|
|
||||||
children: [
|
|
||||||
Icon(Icons.check_circle_outline, color: UiColors.white),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
message,
|
|
||||||
style: UiTypography.body2m.copyWith(color: UiColors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
backgroundColor: UiColors.success,
|
|
||||||
behavior: SnackBarBehavior.floating,
|
|
||||||
duration: duration,
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
|
||||||
margin: const EdgeInsets.all(16),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -104,7 +104,7 @@ class WorkerHomePage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
// Today's Shifts
|
// Today's Shifts
|
||||||
BlocBuilder<HomeCubit, HomeState>(
|
BlocBuilder<HomeCubit, HomeState>(
|
||||||
@@ -123,9 +123,11 @@ class WorkerHomePage extends StatelessWidget {
|
|||||||
if (state.status == HomeStatus.loading)
|
if (state.status == HomeStatus.loading)
|
||||||
const Center(
|
const Center(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: UiConstants.space10,
|
||||||
width: 40,
|
width: UiConstants.space10,
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(
|
||||||
|
color: UiColors.primary,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else if (shifts.isEmpty)
|
else if (shifts.isEmpty)
|
||||||
@@ -149,7 +151,7 @@ class WorkerHomePage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
// Tomorrow's Shifts
|
// Tomorrow's Shifts
|
||||||
BlocBuilder<HomeCubit, HomeState>(
|
BlocBuilder<HomeCubit, HomeState>(
|
||||||
@@ -177,7 +179,7 @@ class WorkerHomePage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
// Recommended Shifts
|
// Recommended Shifts
|
||||||
SectionHeader(
|
SectionHeader(
|
||||||
@@ -197,7 +199,8 @@ class WorkerHomePage extends StatelessWidget {
|
|||||||
itemCount: state.recommendedShifts.length,
|
itemCount: state.recommendedShifts.length,
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
itemBuilder: (context, index) => Padding(
|
itemBuilder: (context, index) => Padding(
|
||||||
padding: const EdgeInsets.only(right: 12),
|
padding: const EdgeInsets.only(
|
||||||
|
right: UiConstants.space3),
|
||||||
child: RecommendedShiftCard(
|
child: RecommendedShiftCard(
|
||||||
shift: state.recommendedShifts[index],
|
shift: state.recommendedShifts[index],
|
||||||
),
|
),
|
||||||
@@ -206,7 +209,7 @@ class WorkerHomePage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -28,23 +28,20 @@ class HomeHeader extends StatelessWidget {
|
|||||||
spacing: UiConstants.space3,
|
spacing: UiConstants.space3,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 48,
|
width: UiConstants.space12,
|
||||||
height: 48,
|
height: UiConstants.space12,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: UiColors.primary.withOpacity(0.2),
|
color: UiColors.primary.withValues(alpha: 0.2),
|
||||||
width: 2,
|
width: 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: CircleAvatar(
|
child: CircleAvatar(
|
||||||
backgroundColor: UiColors.primary.withOpacity(0.1),
|
backgroundColor: UiColors.primary.withValues(alpha: 0.1),
|
||||||
child: Text(
|
child: Text(
|
||||||
initial,
|
initial,
|
||||||
style: const TextStyle(
|
style: UiTypography.body1b.textPrimary,
|
||||||
color: UiColors.primary,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -29,19 +29,19 @@ class PlaceholderBanner extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: bg,
|
color: bg,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(color: accent.withOpacity(0.3)),
|
border: Border.all(color: accent.withValues(alpha: 0.3)),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 40,
|
width: UiConstants.space10,
|
||||||
height: 40,
|
height: UiConstants.space10,
|
||||||
padding: const EdgeInsets.all(UiConstants.space2),
|
padding: const EdgeInsets.all(UiConstants.space2),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: UiColors.bgBanner,
|
color: UiColors.bgBanner,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Icon(UiIcons.sparkles, color: accent, size: 20),
|
child: Icon(UiIcons.sparkles, color: accent, size: UiConstants.space5),
|
||||||
),
|
),
|
||||||
const SizedBox(width: UiConstants.space3),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ class QuickActionItem extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 64,
|
width: UiConstants.space16,
|
||||||
height: 64,
|
height: UiConstants.space16,
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgBanner,
|
color: UiColors.bgBanner,
|
||||||
@@ -31,13 +31,13 @@ class QuickActionItem extends StatelessWidget {
|
|||||||
border: Border.all(color: UiColors.bgSecondary),
|
border: Border.all(color: UiColors.bgSecondary),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.foreground.withOpacity(0.05),
|
color: UiColors.foreground.withValues(alpha: 0.05),
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
offset: const Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Icon(icon, color: UiColors.primary, size: 24),
|
child: Icon(icon, color: UiColors.primary, size: UiConstants.space6),
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space2),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ class RecommendedShiftCard extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final recI18n = t.staff.home.recommended_card;
|
final recI18n = t.staff.home.recommended_card;
|
||||||
final duration = 8;
|
|
||||||
final totalPay = duration * shift.hourlyRate;
|
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@@ -23,14 +21,14 @@ class RecommendedShiftCard extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 300,
|
width: 300,
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withValues(alpha: 0.02),
|
color: UiColors.black.withValues(alpha: 0.02),
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
offset: const Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
@@ -45,51 +43,43 @@ class RecommendedShiftCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
recI18n.act_now,
|
recI18n.act_now,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3m.copyWith(color: UiColors.textError),
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFFDC2626),
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: UiConstants.space2),
|
||||||
const SizedBox(width: 8),
|
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 8,
|
horizontal: UiConstants.space2,
|
||||||
vertical: 2,
|
vertical: 2,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFE8F0FF),
|
color: UiColors.tagInProgress,
|
||||||
borderRadius: BorderRadius.circular(999),
|
borderRadius: UiConstants.radiusFull,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
recI18n.one_day,
|
recI18n.one_day,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3m.textPrimary,
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Color(0xFF0047FF),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 44,
|
width: UiConstants.space10,
|
||||||
height: 44,
|
height: UiConstants.space10,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFE8F0FF),
|
color: UiColors.tagInProgress,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
UiIcons.calendar,
|
UiIcons.calendar,
|
||||||
color: Color(0xFF0047FF),
|
color: UiColors.primary,
|
||||||
size: 20,
|
size: UiConstants.space5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -97,25 +87,16 @@ class RecommendedShiftCard extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
shift.title,
|
shift.title,
|
||||||
style: const TextStyle(
|
style: UiTypography.body1m.textPrimary,
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 16,
|
|
||||||
color: UiColors.foreground,
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'\$${totalPay.round()}',
|
'\$${shift.hourlyRate}/h',
|
||||||
style: const TextStyle(
|
style: UiTypography.headline4m.textPrimary,
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: UiColors.foreground,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -125,17 +106,11 @@ class RecommendedShiftCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
shift.clientName,
|
shift.clientName,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'\$${shift.hourlyRate.toStringAsFixed(0)}/hr • ${duration}h',
|
'\$${shift.hourlyRate.toStringAsFixed(0)}/hr',
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.textSecondary,
|
||||||
fontSize: 10,
|
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -144,57 +119,48 @@ class RecommendedShiftCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
const Icon(
|
||||||
UiIcons.calendar,
|
UiIcons.calendar,
|
||||||
size: 14,
|
size: UiConstants.space3,
|
||||||
color: UiColors.mutedForeground,
|
color: UiColors.mutedForeground,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
recI18n.today,
|
recI18n.today,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: UiConstants.space3),
|
||||||
const SizedBox(width: 12),
|
|
||||||
const Icon(
|
const Icon(
|
||||||
UiIcons.clock,
|
UiIcons.clock,
|
||||||
size: 14,
|
size: UiConstants.space3,
|
||||||
color: UiColors.mutedForeground,
|
color: UiColors.mutedForeground,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
recI18n.time_range(
|
recI18n.time_range(
|
||||||
start: shift.startTime,
|
start: shift.startTime,
|
||||||
end: shift.endTime,
|
end: shift.endTime,
|
||||||
),
|
),
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
const Icon(
|
||||||
UiIcons.mapPin,
|
UiIcons.mapPin,
|
||||||
size: 14,
|
size: UiConstants.space3,
|
||||||
color: UiColors.mutedForeground,
|
color: UiColors.mutedForeground,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: UiConstants.space1),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
shift.locationAddress,
|
shift.locationAddress,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
|
|
||||||
|
|
||||||
/// Section header widget for home page sections, using design system tokens.
|
/// Section header widget for home page sections, using design system tokens.
|
||||||
class SectionHeader extends StatelessWidget {
|
class SectionHeader extends StatelessWidget {
|
||||||
/// Section title
|
/// Section title
|
||||||
@@ -21,25 +20,29 @@ class SectionHeader extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: UiConstants.space3),
|
padding: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: action != null
|
||||||
|
? Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: UiTypography.headline4m,
|
style: UiTypography.body2m.textPrimary,
|
||||||
),
|
),
|
||||||
if (action != null)
|
|
||||||
if (onAction != null)
|
if (onAction != null)
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: onAction,
|
onTap: onAction,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
action!,
|
action ?? '',
|
||||||
style: UiTypography.body2m.copyWith(color: UiColors.primary),
|
style: UiTypography.body3r.textPrimary,
|
||||||
),
|
),
|
||||||
const Icon(
|
const Icon(
|
||||||
UiIcons.chevronRight,
|
UiIcons.chevronRight,
|
||||||
size: 16,
|
size: UiConstants.space4,
|
||||||
color: UiColors.primary,
|
color: UiColors.primary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -47,17 +50,28 @@ class SectionHeader extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
else
|
else
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space2,
|
||||||
|
vertical: 2,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withOpacity(0.08),
|
color: UiColors.primary.withValues(alpha: 0.08),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius:
|
||||||
|
BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: UiColors.primary.withOpacity(0.2),
|
color: UiColors.primary.withValues(alpha: 0.2),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
action!,
|
action!,
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.primary),
|
style: UiTypography.body3r.textPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
title,
|
||||||
|
style: UiTypography.body2m.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -77,15 +77,15 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
Modular.to.pushShiftDetails(widget.shift);
|
Modular.to.pushShiftDetails(widget.shift);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
margin: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withValues(alpha: 0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
blurRadius: 2,
|
blurRadius: 2,
|
||||||
offset: const Offset(0, 1),
|
offset: const Offset(0, 1),
|
||||||
),
|
),
|
||||||
@@ -94,16 +94,17 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 48,
|
width: UiConstants.space12,
|
||||||
height: 48,
|
height: UiConstants.space12,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: widget.shift.logoUrl != null
|
child: widget.shift.logoUrl != null
|
||||||
? ClipRRect(
|
? ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius:
|
||||||
|
BorderRadius.circular(UiConstants.radiusBase),
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
widget.shift.logoUrl!,
|
widget.shift.logoUrl!,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
@@ -114,7 +115,7 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
color: UiColors.mutedForeground,
|
color: UiColors.mutedForeground,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -125,28 +126,18 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.shift.title,
|
widget.shift.title,
|
||||||
style: const TextStyle(
|
style: UiTypography.body1m.textPrimary,
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: UiColors.foreground,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text.rich(
|
Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '\$${widget.shift.hourlyRate}',
|
text: '\$${widget.shift.hourlyRate}',
|
||||||
style: const TextStyle(
|
style: UiTypography.body1b.textPrimary,
|
||||||
fontWeight: FontWeight.bold,
|
children: [
|
||||||
fontSize: 16,
|
|
||||||
color: UiColors.foreground,
|
|
||||||
),
|
|
||||||
children: const [
|
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '/h',
|
text: '/h',
|
||||||
style: TextStyle(
|
style: UiTypography.body3r,
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -155,19 +146,13 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
widget.shift.clientName,
|
widget.shift.clientName,
|
||||||
style: const TextStyle(
|
style: UiTypography.body2r.textSecondary,
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
fontSize: 13,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
'${_formatTime(widget.shift.startTime)} • ${widget.shift.location}',
|
'${_formatTime(widget.shift.startTime)} • ${widget.shift.location}',
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.textSecondary,
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -179,14 +164,14 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(bottom: 16),
|
margin: const EdgeInsets.only(bottom: UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withValues(alpha: 0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
offset: const Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
@@ -195,7 +180,7 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// Header
|
// Header
|
||||||
@@ -203,16 +188,17 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 56,
|
width: UiConstants.space14,
|
||||||
height: 56,
|
height: UiConstants.space14,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: widget.shift.logoUrl != null
|
child: widget.shift.logoUrl != null
|
||||||
? ClipRRect(
|
? ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius:
|
||||||
|
BorderRadius.circular(UiConstants.radiusBase),
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
widget.shift.logoUrl!,
|
widget.shift.logoUrl!,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
@@ -226,25 +212,21 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 16,
|
horizontal: UiConstants.space4,
|
||||||
vertical: 6,
|
vertical: 6,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary,
|
color: UiColors.primary,
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: UiConstants.radiusFull,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Assigned ${_getTimeAgo(widget.shift.createdDate).replaceAll('Pending ', '')}',
|
'Assigned ${_getTimeAgo(widget.shift.createdDate).replaceAll('Pending ', '')}',
|
||||||
style: const TextStyle(
|
style: UiTypography.body3m.white,
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
// Title and Rate
|
// Title and Rate
|
||||||
Row(
|
Row(
|
||||||
@@ -257,18 +239,11 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
widget.shift.title,
|
widget.shift.title,
|
||||||
style: const TextStyle(
|
style: UiTypography.headline3m.textPrimary,
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: UiColors.foreground,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
widget.shift.clientName,
|
widget.shift.clientName,
|
||||||
style: const TextStyle(
|
style: UiTypography.body2r.textSecondary,
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -276,30 +251,23 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
Text.rich(
|
Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '\$${widget.shift.hourlyRate}',
|
text: '\$${widget.shift.hourlyRate}',
|
||||||
style: const TextStyle(
|
style: UiTypography.headline3m.textPrimary,
|
||||||
fontWeight: FontWeight.bold,
|
children: [
|
||||||
fontSize: 20,
|
|
||||||
color: UiColors.foreground,
|
|
||||||
),
|
|
||||||
children: const [
|
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '/h',
|
text: '/h',
|
||||||
style: TextStyle(
|
style: UiTypography.body1r,
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
// Location and Date
|
// Location and Date
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
Icon(
|
||||||
UiIcons.mapPin,
|
UiIcons.mapPin,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: UiColors.mutedForeground,
|
color: UiColors.mutedForeground,
|
||||||
@@ -308,15 +276,12 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.shift.location,
|
widget.shift.location,
|
||||||
style: const TextStyle(
|
style: UiTypography.body2r.textSecondary,
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: UiConstants.space4),
|
||||||
const Icon(
|
Icon(
|
||||||
UiIcons.calendar,
|
UiIcons.calendar,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: UiColors.mutedForeground,
|
color: UiColors.mutedForeground,
|
||||||
@@ -324,14 +289,11 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
Text(
|
Text(
|
||||||
'${_formatDate(widget.shift.date)}, ${_formatTime(widget.shift.startTime)}',
|
'${_formatDate(widget.shift.date)}, ${_formatTime(widget.shift.startTime)}',
|
||||||
style: const TextStyle(
|
style: UiTypography.body2r.textSecondary,
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
// Tags
|
// Tags
|
||||||
Wrap(
|
Wrap(
|
||||||
@@ -347,13 +309,13 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
_buildTag(
|
_buildTag(
|
||||||
UiIcons.timer,
|
UiIcons.timer,
|
||||||
'No experience',
|
'No experience',
|
||||||
const Color(0xFFFEE2E2),
|
UiColors.tagError,
|
||||||
const Color(0xFFDC2626),
|
UiColors.textError,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -361,46 +323,48 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
// Actions
|
// Actions
|
||||||
if (!widget.compact)
|
if (!widget.compact)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space5,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 48,
|
height: UiConstants.space12,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: widget.onApply,
|
onPressed: widget.onApply,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: UiColors.foreground,
|
backgroundColor: UiColors.primary,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: UiColors.white,
|
||||||
|
elevation: 0,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius:
|
||||||
|
BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: const Text(
|
child: const Text('Accept shift'),
|
||||||
'Accept shift',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.w600),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: UiConstants.space2),
|
||||||
const SizedBox(height: 8),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 48,
|
height: UiConstants.space12,
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
onPressed: widget.onDecline,
|
onPressed: widget.onDecline,
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
foregroundColor: const Color(0xFFEF4444),
|
foregroundColor: UiColors.destructive,
|
||||||
side: const BorderSide(color: Color(0xFFFCA5A5)),
|
side: BorderSide(
|
||||||
|
color: UiColors.destructive.withValues(alpha: 0.3),
|
||||||
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius:
|
||||||
|
BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: const Text(
|
child: const Text('Decline shift'),
|
||||||
'Decline shift',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.w600),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: UiConstants.space5),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -414,7 +378,7 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: bg,
|
color: bg,
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: UiConstants.radiusFull,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -424,11 +388,7 @@ class _ShiftCardState extends State<ShiftCard> {
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: TextStyle(
|
style: UiTypography.body3m.copyWith(color: text),
|
||||||
color: text,
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -18,29 +18,33 @@ class AutoMatchToggle extends StatefulWidget {
|
|||||||
State<AutoMatchToggle> createState() => _AutoMatchToggleState();
|
State<AutoMatchToggle> createState() => _AutoMatchToggleState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AutoMatchToggleState extends State<AutoMatchToggle> with SingleTickerProviderStateMixin {
|
class _AutoMatchToggleState extends State<AutoMatchToggle>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.staff.home.auto_match;
|
final i18n = t.staff.home.auto_match;
|
||||||
final Color primary = Theme.of(context).colorScheme.primary;
|
|
||||||
return AnimatedContainer(
|
return AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
gradient: widget.enabled
|
gradient: widget.enabled
|
||||||
? LinearGradient(
|
? LinearGradient(
|
||||||
colors: [primary, primary.withOpacity(0.8)],
|
colors: [
|
||||||
|
UiColors.primary,
|
||||||
|
UiColors.primary.withValues(alpha: 0.8),
|
||||||
|
],
|
||||||
begin: Alignment.centerLeft,
|
begin: Alignment.centerLeft,
|
||||||
end: Alignment.centerRight,
|
end: Alignment.centerRight,
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
color: widget.enabled ? null : Colors.white,
|
color: widget.enabled ? null : UiColors.white,
|
||||||
border: widget.enabled ? null : Border.all(color: Colors.grey.shade200),
|
border:
|
||||||
|
widget.enabled ? null : Border.all(color: UiColors.border),
|
||||||
boxShadow: widget.enabled
|
boxShadow: widget.enabled
|
||||||
? [
|
? [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: primary.withOpacity(0.3),
|
color: UiColors.primary.withValues(alpha: 0.3),
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
offset: const Offset(0, 4),
|
offset: const Offset(0, 4),
|
||||||
),
|
),
|
||||||
@@ -55,36 +59,39 @@ class _AutoMatchToggleState extends State<AutoMatchToggle> with SingleTickerProv
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 40,
|
width: UiConstants.space10,
|
||||||
height: 40,
|
height: UiConstants.space10,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: widget.enabled
|
color: widget.enabled
|
||||||
? Colors.white.withOpacity(0.2)
|
? UiColors.white.withValues(alpha: 0.2)
|
||||||
: primary.withOpacity(0.1),
|
: UiColors.primary.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius:
|
||||||
|
BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
UiIcons.zap,
|
UiIcons.zap,
|
||||||
color: widget.enabled ? Colors.white : primary,
|
color: widget.enabled ? UiColors.white : UiColors.primary,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
i18n.title,
|
i18n.title,
|
||||||
style: TextStyle(
|
style: UiTypography.body1b.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
color: widget.enabled
|
||||||
color: widget.enabled ? Colors.white : const Color(0xFF0F172A),
|
? UiColors.white
|
||||||
|
: UiColors.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
widget.enabled ? i18n.finding_shifts : i18n.get_matched,
|
widget.enabled ? i18n.finding_shifts : i18n.get_matched,
|
||||||
style: TextStyle(
|
style: UiTypography.body3r.copyWith(
|
||||||
fontSize: 12,
|
color: widget.enabled
|
||||||
color: widget.enabled ? const Color(0xFFF8E08E) : Colors.grey.shade500,
|
? UiColors.accent
|
||||||
|
: UiColors.textInactive,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -94,10 +101,10 @@ class _AutoMatchToggleState extends State<AutoMatchToggle> with SingleTickerProv
|
|||||||
Switch(
|
Switch(
|
||||||
value: widget.enabled,
|
value: widget.enabled,
|
||||||
onChanged: widget.onToggle,
|
onChanged: widget.onToggle,
|
||||||
activeThumbColor: Colors.white,
|
activeThumbColor: UiColors.white,
|
||||||
activeTrackColor: Colors.white.withValues(alpha: 0.3),
|
activeTrackColor: UiColors.white.withValues(alpha: 0.3),
|
||||||
inactiveThumbColor: Colors.white,
|
inactiveThumbColor: UiColors.white,
|
||||||
inactiveTrackColor: Colors.grey.shade300,
|
inactiveTrackColor: UiColors.border,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -107,20 +114,19 @@ class _AutoMatchToggleState extends State<AutoMatchToggle> with SingleTickerProv
|
|||||||
? Column(
|
? Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Container(
|
Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
color: Colors.white.withValues(alpha: 0.2),
|
color: UiColors.white.withValues(alpha: 0.2),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Text(
|
Text(
|
||||||
i18n.matching_based_on,
|
i18n.matching_based_on,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.copyWith(
|
||||||
color: Color(0xFFF8E08E),
|
color: UiColors.accent,
|
||||||
fontSize: 12,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
@@ -145,17 +151,17 @@ class _AutoMatchToggleState extends State<AutoMatchToggle> with SingleTickerProv
|
|||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withValues(alpha: 0.2),
|
color: UiColors.white.withValues(alpha: 0.2),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Icon(icon, size: 12, color: Colors.white),
|
Icon(icon, size: 12, color: UiColors.white),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 12),
|
style: UiTypography.body3r.white,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ class BenefitsWidget extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.staff.home.benefits;
|
final i18n = t.staff.home.benefits;
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.background,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(color: Theme.of(context).dividerColor),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Theme.of(context).colorScheme.onBackground.withOpacity(0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
blurRadius: 2,
|
blurRadius: 2,
|
||||||
offset: const Offset(0, 1),
|
offset: const Offset(0, 1),
|
||||||
),
|
),
|
||||||
@@ -36,7 +36,7 @@ class BenefitsWidget extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
i18n.title,
|
i18n.title,
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: UiTypography.title1m.textPrimary,
|
||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => Modular.to.pushNamed('/benefits'),
|
onTap: () => Modular.to.pushNamed('/benefits'),
|
||||||
@@ -44,19 +44,19 @@ class BenefitsWidget extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
i18n.view_all,
|
i18n.view_all,
|
||||||
style: Theme.of(context).textTheme.labelLarge?.copyWith(color: Theme.of(context).colorScheme.primary),
|
style: UiTypography.buttonL.textPrimary,
|
||||||
),
|
),
|
||||||
Icon(
|
Icon(
|
||||||
UiIcons.chevronRight,
|
UiIcons.chevronRight,
|
||||||
size: 16,
|
size: UiConstants.space4,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: UiColors.primary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@@ -64,19 +64,19 @@ class BenefitsWidget extends StatelessWidget {
|
|||||||
label: i18n.items.sick_days,
|
label: i18n.items.sick_days,
|
||||||
current: 10,
|
current: 10,
|
||||||
total: 40,
|
total: 40,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: UiColors.primary,
|
||||||
),
|
),
|
||||||
_BenefitItem(
|
_BenefitItem(
|
||||||
label: i18n.items.vacation,
|
label: i18n.items.vacation,
|
||||||
current: 40,
|
current: 40,
|
||||||
total: 40,
|
total: 40,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: UiColors.primary,
|
||||||
),
|
),
|
||||||
_BenefitItem(
|
_BenefitItem(
|
||||||
label: i18n.items.holidays,
|
label: i18n.items.holidays,
|
||||||
current: 24,
|
current: 24,
|
||||||
total: 24,
|
total: 24,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: UiColors.primary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -105,13 +105,13 @@ class _BenefitItem extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 56,
|
width: UiConstants.space14,
|
||||||
height: 56,
|
height: UiConstants.space14,
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
painter: _CircularProgressPainter(
|
painter: _CircularProgressPainter(
|
||||||
progress: current / total,
|
progress: current / total,
|
||||||
color: color,
|
color: color,
|
||||||
backgroundColor: const Color(0xFFE5E7EB),
|
backgroundColor: UiColors.border,
|
||||||
strokeWidth: 4,
|
strokeWidth: 4,
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
@@ -120,32 +120,21 @@ class _BenefitItem extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'${current.toInt()}/${total.toInt()}',
|
'${current.toInt()}/${total.toInt()}',
|
||||||
style: const TextStyle(
|
style: UiTypography.body3m.textPrimary,
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF1E293B),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
i18n.hours_label,
|
i18n.hours_label,
|
||||||
style: const TextStyle(
|
style: UiTypography.footnote1r.textTertiary,
|
||||||
fontSize: 8,
|
|
||||||
color: Color(0xFF94A3B8),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3m.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Color(0xFF475569),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
@@ -32,9 +33,9 @@ class ImproveYourselfWidget extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
i18n.title,
|
i18n.title,
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: UiTypography.title1m.textPrimary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
@@ -51,14 +52,14 @@ class ImproveYourselfWidget extends StatelessWidget {
|
|||||||
onTap: () => Modular.to.pushNamed(item['page']!),
|
onTap: () => Modular.to.pushNamed(item['page']!),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 160,
|
width: 160,
|
||||||
margin: const EdgeInsets.only(right: 12),
|
margin: const EdgeInsets.only(right: UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.background,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(color: Theme.of(context).dividerColor),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Theme.of(context).colorScheme.onBackground.withOpacity(0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
blurRadius: 2,
|
blurRadius: 2,
|
||||||
offset: const Offset(0, 1),
|
offset: const Offset(0, 1),
|
||||||
),
|
),
|
||||||
@@ -69,36 +70,33 @@ class ImproveYourselfWidget extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 96,
|
height: UiConstants.space24,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
item['image']!,
|
item['image']!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
errorBuilder: (context, error, stackTrace) => Container(
|
errorBuilder: (context, error, stackTrace) => Container(
|
||||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
color: UiColors.background,
|
||||||
child: const Icon(
|
child: Icon(
|
||||||
Icons.image_not_supported,
|
UiIcons.zap,
|
||||||
color: Colors.grey,
|
color: UiColors.mutedForeground,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(UiConstants.space3),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
item['title']!,
|
item['title']!,
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: UiTypography.body1m.textPrimary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
item['description']!,
|
item['description']!,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
|
|
||||||
|
|
||||||
/// Widget for displaying more ways to use Krow, using design system tokens.
|
/// Widget for displaying more ways to use Krow, using design system tokens.
|
||||||
class MoreWaysToUseKrowWidget extends StatelessWidget {
|
class MoreWaysToUseKrowWidget extends StatelessWidget {
|
||||||
/// Creates a [MoreWaysToUseKrowWidget].
|
/// Creates a [MoreWaysToUseKrowWidget].
|
||||||
@@ -31,9 +31,9 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
i18n.title,
|
i18n.title,
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: UiTypography.title1m.textPrimary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
@@ -50,14 +50,14 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
|
|||||||
onTap: () => Modular.to.pushNamed(item['page']!),
|
onTap: () => Modular.to.pushNamed(item['page']!),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 160,
|
width: 160,
|
||||||
margin: const EdgeInsets.only(right: 12),
|
margin: const EdgeInsets.only(right: UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.background,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(color: Theme.of(context).dividerColor),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Theme.of(context).colorScheme.onBackground.withOpacity(0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
blurRadius: 2,
|
blurRadius: 2,
|
||||||
offset: const Offset(0, 1),
|
offset: const Offset(0, 1),
|
||||||
),
|
),
|
||||||
@@ -68,25 +68,25 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 96,
|
height: UiConstants.space24,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
item['image']!,
|
item['image']!,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
errorBuilder: (context, error, stackTrace) => Container(
|
errorBuilder: (context, error, stackTrace) => Container(
|
||||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
color: UiColors.background,
|
||||||
child: const Icon(
|
child: Icon(
|
||||||
Icons.image_not_supported,
|
UiIcons.zap,
|
||||||
color: Colors.grey,
|
color: UiColors.mutedForeground,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(UiConstants.space3),
|
||||||
child: Text(
|
child: Text(
|
||||||
item['title']!,
|
item['title']!,
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: UiTypography.body1m.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user