Skip to content

Commit 3dbf219

Browse files
committed
feat(dashboard): introduces the user interface for the first phase of the dashboard.
It includes: - Localization: New strings for dashboard-specific labels, loading states, and error messages have been added to the .arb files and localization classes. - UI Implementation: The DashboardPage is now a stateful widget that fetches data on initialization. It uses a BlocBuilder to react to the DashboardState. - State Handling: The UI correctly displays a LoadingStateWidget while fetching data and a FailureStateWidget if an error occurs, with a functional retry button. - Summary Cards: On success, the page displays a row of three _SummaryCard widgets, presenting the total counts for headlines, categories, and sources. This card is a private, reusable component designed for clarity and consistency, adhering to the project's theme and spacing constants. This completes the foundational UI for the dashboard.
1 parent 14ac870 commit 3dbf219

File tree

6 files changed

+236
-7
lines changed

6 files changed

+236
-7
lines changed

lib/dashboard/view/dashboard_page.dart

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,126 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:ht_dashboard/dashboard/bloc/dashboard_bloc.dart';
4+
import 'package:ht_dashboard/l10n/l10n.dart';
5+
import 'package:ht_dashboard/shared/shared.dart';
26

37
/// {@template dashboard_page}
4-
/// A placeholder page for the dashboard.
8+
/// The main dashboard page, displaying key statistics and quick actions.
59
/// {@endtemplate}
6-
class DashboardPage extends StatelessWidget {
10+
class DashboardPage extends StatefulWidget {
711
/// {@macro dashboard_page}
812
const DashboardPage({super.key});
913

14+
@override
15+
State<DashboardPage> createState() => _DashboardPageState();
16+
}
17+
18+
class _DashboardPageState extends State<DashboardPage> {
19+
@override
20+
void initState() {
21+
super.initState();
22+
// Dispatch the event to load dashboard data when the page is initialized.
23+
context.read<DashboardBloc>().add(DashboardSummaryLoaded());
24+
}
25+
26+
@override
27+
Widget build(BuildContext context) {
28+
final l10n = context.l10n;
29+
return Scaffold(
30+
body: BlocBuilder<DashboardBloc, DashboardState>(
31+
builder: (context, state) {
32+
if (state.status == DashboardStatus.loading ||
33+
state.status == DashboardStatus.initial) {
34+
return LoadingStateWidget(
35+
icon: Icons.dashboard_outlined,
36+
headline: l10n.loadingDashboard,
37+
subheadline: l10n.loadingDashboardSubheadline,
38+
);
39+
}
40+
if (state.status == DashboardStatus.failure) {
41+
return FailureStateWidget(
42+
message: state.errorMessage ?? l10n.dashboardLoadFailure,
43+
onRetry: () {
44+
context.read<DashboardBloc>().add(DashboardSummaryLoaded());
45+
},
46+
);
47+
}
48+
if (state.status == DashboardStatus.success && state.summary != null) {
49+
final summary = state.summary!;
50+
return ListView(
51+
padding: const EdgeInsets.all(AppSpacing.lg),
52+
children: [
53+
Row(
54+
children: [
55+
Expanded(
56+
child: _SummaryCard(
57+
icon: Icons.article_outlined,
58+
title: l10n.totalHeadlines,
59+
value: summary.headlineCount.toString(),
60+
),
61+
),
62+
const SizedBox(width: AppSpacing.lg),
63+
Expanded(
64+
child: _SummaryCard(
65+
icon: Icons.category_outlined,
66+
title: l10n.totalCategories,
67+
value: summary.categoryCount.toString(),
68+
),
69+
),
70+
const SizedBox(width: AppSpacing.lg),
71+
Expanded(
72+
child: _SummaryCard(
73+
icon: Icons.source_outlined,
74+
title: l10n.totalSources,
75+
value: summary.sourceCount.toString(),
76+
),
77+
),
78+
],
79+
),
80+
// Other dashboard components will go here in future phases
81+
],
82+
);
83+
}
84+
return const SizedBox.shrink(); // Fallback for unexpected states
85+
},
86+
),
87+
);
88+
}
89+
}
90+
91+
/// A private widget to display a single summary statistic on the dashboard.
92+
class _SummaryCard extends StatelessWidget {
93+
const _SummaryCard({
94+
required this.icon,
95+
required this.title,
96+
required this.value,
97+
});
98+
99+
final IconData icon;
100+
final String title;
101+
final String value;
102+
10103
@override
11104
Widget build(BuildContext context) {
12-
return const Scaffold(
13-
body: Center(
14-
child: Text('Welcome to the Dashboard!'),
105+
final theme = Theme.of(context);
106+
return Card(
107+
child: Padding(
108+
padding: const EdgeInsets.all(AppSpacing.lg),
109+
child: Column(
110+
crossAxisAlignment: CrossAxisAlignment.start,
111+
children: [
112+
Icon(icon, size: 28, color: theme.colorScheme.primary),
113+
const SizedBox(height: AppSpacing.sm),
114+
Text(value, style: theme.textTheme.headlineMedium),
115+
const SizedBox(height: AppSpacing.xs),
116+
Text(
117+
title,
118+
style: theme.textTheme.bodyMedium?.copyWith(
119+
color: theme.colorScheme.onSurfaceVariant,
120+
),
121+
),
122+
],
123+
),
15124
),
16125
);
17126
}

lib/l10n/app_localizations.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,42 @@ abstract class AppLocalizations {
13151315
/// In en, this message translates to:
13161316
/// **'Draft'**
13171317
String get contentStatusDraft;
1318+
1319+
/// Label for the total headlines summary card on the dashboard
1320+
///
1321+
/// In en, this message translates to:
1322+
/// **'Total Headlines'**
1323+
String get totalHeadlines;
1324+
1325+
/// Label for the total categories summary card on the dashboard
1326+
///
1327+
/// In en, this message translates to:
1328+
/// **'Total Categories'**
1329+
String get totalCategories;
1330+
1331+
/// Label for the total sources summary card on the dashboard
1332+
///
1333+
/// In en, this message translates to:
1334+
/// **'Total Sources'**
1335+
String get totalSources;
1336+
1337+
/// Headline for the dashboard loading state
1338+
///
1339+
/// In en, this message translates to:
1340+
/// **'Loading Dashboard'**
1341+
String get loadingDashboard;
1342+
1343+
/// Subheadline for the dashboard loading state
1344+
///
1345+
/// In en, this message translates to:
1346+
/// **'Fetching latest statistics...'**
1347+
String get loadingDashboardSubheadline;
1348+
1349+
/// Error message when dashboard data fails to load
1350+
///
1351+
/// In en, this message translates to:
1352+
/// **'Failed to load dashboard data.'**
1353+
String get dashboardLoadFailure;
13181354
}
13191355

13201356
class _AppLocalizationsDelegate

lib/l10n/app_localizations_ar.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,4 +686,22 @@ class AppLocalizationsAr extends AppLocalizations {
686686

687687
@override
688688
String get contentStatusDraft => 'مسودة';
689+
690+
@override
691+
String get totalHeadlines => 'إجمالي العناوين';
692+
693+
@override
694+
String get totalCategories => 'إجمالي الفئات';
695+
696+
@override
697+
String get totalSources => 'إجمالي المصادر';
698+
699+
@override
700+
String get loadingDashboard => 'جاري تحميل لوحة القيادة';
701+
702+
@override
703+
String get loadingDashboardSubheadline => 'جاري جلب أحدث الإحصائيات...';
704+
705+
@override
706+
String get dashboardLoadFailure => 'فشل تحميل بيانات لوحة القيادة.';
689707
}

lib/l10n/app_localizations_en.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,4 +684,22 @@ class AppLocalizationsEn extends AppLocalizations {
684684

685685
@override
686686
String get contentStatusDraft => 'Draft';
687+
688+
@override
689+
String get totalHeadlines => 'Total Headlines';
690+
691+
@override
692+
String get totalCategories => 'Total Categories';
693+
694+
@override
695+
String get totalSources => 'Total Sources';
696+
697+
@override
698+
String get loadingDashboard => 'Loading Dashboard';
699+
700+
@override
701+
String get loadingDashboardSubheadline => 'Fetching latest statistics...';
702+
703+
@override
704+
String get dashboardLoadFailure => 'Failed to load dashboard data.';
687705
}

lib/l10n/arb/app_ar.arb

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,5 +825,29 @@
825825
"contentStatusArchived": "مؤرشف",
826826
"@contentStatusArchived": {},
827827
"contentStatusDraft": "مسودة",
828-
"@contentStatusDraft": {}
828+
"@contentStatusDraft": {},
829+
"totalHeadlines": "إجمالي العناوين",
830+
"@totalHeadlines": {
831+
"description": "تسمية بطاقة ملخص إجمالي العناوين في لوحة القيادة"
832+
},
833+
"totalCategories": "إجمالي الفئات",
834+
"@totalCategories": {
835+
"description": "تسمية بطاقة ملخص إجمالي الفئات في لوحة القيادة"
836+
},
837+
"totalSources": "إجمالي المصادر",
838+
"@totalSources": {
839+
"description": "تسمية بطاقة ملخص إجمالي المصادر في لوحة القيادة"
840+
},
841+
"loadingDashboard": "جاري تحميل لوحة القيادة",
842+
"@loadingDashboard": {
843+
"description": "عنوان حالة تحميل لوحة القيادة"
844+
},
845+
"loadingDashboardSubheadline": "جاري جلب أحدث الإحصائيات...",
846+
"@loadingDashboardSubheadline": {
847+
"description": "عنوان فرعي لحالة تحميل لوحة القيادة"
848+
},
849+
"dashboardLoadFailure": "فشل تحميل بيانات لوحة القيادة.",
850+
"@dashboardLoadFailure": {
851+
"description": "رسالة خطأ عند فشل تحميل بيانات لوحة القيادة"
852+
}
829853
}

lib/l10n/arb/app_en.arb

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,5 +825,29 @@
825825
"contentStatusArchived": "Archived",
826826
"@contentStatusArchived": {},
827827
"contentStatusDraft": "Draft",
828-
"@contentStatusDraft": {}
828+
"@contentStatusDraft": {},
829+
"totalHeadlines": "Total Headlines",
830+
"@totalHeadlines": {
831+
"description": "Label for the total headlines summary card on the dashboard"
832+
},
833+
"totalCategories": "Total Categories",
834+
"@totalCategories": {
835+
"description": "Label for the total categories summary card on the dashboard"
836+
},
837+
"totalSources": "Total Sources",
838+
"@totalSources": {
839+
"description": "Label for the total sources summary card on the dashboard"
840+
},
841+
"loadingDashboard": "Loading Dashboard",
842+
"@loadingDashboard": {
843+
"description": "Headline for the dashboard loading state"
844+
},
845+
"loadingDashboardSubheadline": "Fetching latest statistics...",
846+
"@loadingDashboardSubheadline": {
847+
"description": "Subheadline for the dashboard loading state"
848+
},
849+
"dashboardLoadFailure": "Failed to load dashboard data.",
850+
"@dashboardLoadFailure": {
851+
"description": "Error message when dashboard data fails to load"
852+
}
829853
}

0 commit comments

Comments
 (0)