refactor: add optional subtitle to various widgets for enhanced context

This commit is contained in:
Achintha Isuru
2026-01-30 01:59:41 -05:00
parent 1e8d6ae65b
commit ed71d2b4a3
7 changed files with 109 additions and 18 deletions

View File

@@ -74,23 +74,32 @@ class ClientHomePage extends StatelessWidget {
/// Builds the widget list in normal mode with visibility filters.
Widget _buildNormalModeList(ClientHomeState state) {
return ListView(
padding: const EdgeInsets.fromLTRB(
UiConstants.space4,
0,
UiConstants.space4,
100,
),
children: state.widgetOrder.map((String id) {
return Padding(
padding: const EdgeInsets.only(bottom: UiConstants.space4),
child: DashboardWidgetBuilder(
id: id,
state: state,
isEditMode: false,
),
return ListView.separated(
padding: const EdgeInsets.only(bottom: 100),
separatorBuilder: (BuildContext context, int index) {
return const Divider(color: UiColors.border, height: 0.2);
},
itemCount: state.widgetOrder.length,
itemBuilder: (BuildContext context, int index) {
final String id = state.widgetOrder[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (index != 0) const SizedBox(height: UiConstants.space8),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space4,
),
child: DashboardWidgetBuilder(
id: id,
state: state,
isEditMode: false,
),
),
const SizedBox(height: UiConstants.space8),
],
);
}).toList(),
},
);
}
}

View File

@@ -10,11 +10,15 @@ class ActionsWidget extends StatelessWidget {
/// Callback when Create Order is pressed.
final VoidCallback onCreateOrderPressed;
/// Optional subtitle for the section.
final String? subtitle;
/// Creates an [ActionsWidget].
const ActionsWidget({
super.key,
required this.onRapidPressed,
required this.onCreateOrderPressed,
this.subtitle,
});
@override
@@ -22,8 +26,11 @@ class ActionsWidget extends StatelessWidget {
// Check if client_home exists in t
final TranslationsClientHomeActionsEn i18n = t.client_home.actions;
return Row(
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
/// TODO: FEATURE_NOT_YET_IMPLEMENTED
// Expanded(
// child: _ActionCard(
@@ -55,6 +62,8 @@ class ActionsWidget extends StatelessWidget {
),
),
],
),
],
);
}
}

View File

@@ -12,17 +12,22 @@ class CoverageWidget extends StatelessWidget {
/// The percentage of coverage (0-100).
final int coveragePercent;
/// Optional subtitle for the section.
final String? subtitle;
/// Creates a [CoverageWidget].
const CoverageWidget({
super.key,
this.totalNeeded = 0,
this.totalConfirmed = 0,
this.coveragePercent = 0,
this.subtitle,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -53,6 +58,12 @@ class CoverageWidget extends StatelessWidget {
),
],
),
if (subtitle != null) ...<Widget>[
Text(
subtitle!,
style: UiTypography.body2r.textSecondary,
),
],
const SizedBox(height: UiConstants.space2),
Row(
children: <Widget>[

View File

@@ -56,11 +56,15 @@ class DashboardWidgetBuilder extends StatelessWidget {
/// Builds the actual widget content based on the widget ID.
Widget _buildWidgetContent(BuildContext context) {
// Only show subtitle in normal mode
final String? subtitle = !isEditMode ? _getWidgetSubtitle(id) : null;
switch (id) {
case 'actions':
return ActionsWidget(
onRapidPressed: () => Modular.to.pushRapidOrder(),
onCreateOrderPressed: () => Modular.to.pushCreateOrder(),
subtitle: subtitle,
);
case 'reorder':
return ReorderWidget(
@@ -88,6 +92,7 @@ class DashboardWidgetBuilder extends StatelessWidget {
},
);
},
subtitle: subtitle,
);
case 'spending':
return SpendingWidget(
@@ -95,6 +100,7 @@ class DashboardWidgetBuilder extends StatelessWidget {
next7DaysSpending: state.dashboardData.next7DaysSpending,
weeklyShifts: state.dashboardData.weeklyShifts,
next7DaysScheduled: state.dashboardData.next7DaysScheduled,
subtitle: subtitle,
);
case 'coverage':
return CoverageWidget(
@@ -106,10 +112,12 @@ class DashboardWidgetBuilder extends StatelessWidget {
100)
.toInt()
: 0,
subtitle: subtitle,
);
case 'liveActivity':
return LiveActivityWidget(
onViewAllPressed: () => Modular.to.navigate('/client-main/coverage/'),
subtitle: subtitle,
);
default:
return const SizedBox.shrink();
@@ -133,4 +141,21 @@ class DashboardWidgetBuilder extends StatelessWidget {
return '';
}
}
String _getWidgetSubtitle(String id) {
switch (id) {
case 'actions':
return 'Quick access to create and manage orders';
case 'reorder':
return 'Easily reorder from your past activity';
case 'spending':
return 'Track your spending and budget in real-time';
case 'coverage':
return 'Overview of your current shift coverage';
case 'liveActivity':
return 'Real-time updates on your active shifts';
default:
return '';
}
}
}

View File

@@ -10,8 +10,15 @@ class LiveActivityWidget extends StatefulWidget {
/// Callback when "View all" is pressed.
final VoidCallback onViewAllPressed;
/// Optional subtitle for the section.
final String? subtitle;
/// Creates a [LiveActivityWidget].
const LiveActivityWidget({super.key, required this.onViewAllPressed});
const LiveActivityWidget({
super.key,
required this.onViewAllPressed,
this.subtitle
});
@override
State<LiveActivityWidget> createState() => _LiveActivityWidgetState();
@@ -100,6 +107,7 @@ class _LiveActivityWidgetState extends State<LiveActivityWidget> {
final TranslationsClientHomeEn i18n = t.client_home;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -121,6 +129,13 @@ class _LiveActivityWidgetState extends State<LiveActivityWidget> {
),
],
),
if (widget.subtitle != null) ...<Widget>[
const SizedBox(height: UiConstants.space1),
Text(
widget.subtitle!,
style: UiTypography.body2r.textSecondary,
),
],
const SizedBox(height: UiConstants.space2),
FutureBuilder<_LiveActivityData>(
future: _liveActivityFuture,

View File

@@ -11,11 +11,15 @@ class ReorderWidget extends StatelessWidget {
/// Callback when a reorder button is pressed.
final Function(Map<String, dynamic> shiftData) onReorderPressed;
/// Optional subtitle for the section.
final String? subtitle;
/// Creates a [ReorderWidget].
const ReorderWidget({
super.key,
required this.orders,
required this.onReorderPressed,
this.subtitle,
});
@override
@@ -33,6 +37,13 @@ class ReorderWidget extends StatelessWidget {
letterSpacing: 0.5,
),
),
if (subtitle != null) ...<Widget>[
const SizedBox(height: UiConstants.space1),
Text(
subtitle!,
style: UiTypography.body2r.textSecondary,
),
],
const SizedBox(height: UiConstants.space2),
SizedBox(
height: 140,

View File

@@ -16,6 +16,9 @@ class SpendingWidget extends StatelessWidget {
/// The number of scheduled shifts for next 7 days.
final int next7DaysScheduled;
/// Optional subtitle for the section.
final String? subtitle;
/// Creates a [SpendingWidget].
const SpendingWidget({
super.key,
@@ -23,6 +26,7 @@ class SpendingWidget extends StatelessWidget {
required this.next7DaysSpending,
required this.weeklyShifts,
required this.next7DaysScheduled,
this.subtitle,
});
@override
@@ -38,6 +42,13 @@ class SpendingWidget extends StatelessWidget {
letterSpacing: 0.5,
),
),
if (subtitle != null) ...<Widget>[
const SizedBox(height: UiConstants.space1),
Text(
subtitle!,
style: UiTypography.body2r.textSecondary,
),
],
const SizedBox(height: UiConstants.space2),
Container(
padding: const EdgeInsets.all(UiConstants.space3),