Skip to content

Commit b5687c6

Browse files
committed
feat(content): add headlines page with data table
- Implemented headline listing - Added edit and delete actions - Integrated pagination
1 parent fe09c63 commit b5687c6

File tree

1 file changed

+174
-4
lines changed

1 file changed

+174
-4
lines changed
Lines changed: 174 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,186 @@
1+
import 'package:data_table_2/data_table_2.dart';
12
import 'package:flutter/material.dart';
3+
import 'package:flutter_bloc/flutter_bloc.dart';
4+
import 'package:go_router/go_router.dart';
5+
import 'package:ht_dashboard/content_management/bloc/content_management_bloc.dart';
6+
import 'package:ht_dashboard/l10n/app_localizations.dart'; // Corrected import
7+
import 'package:ht_dashboard/l10n/l10n.dart';
8+
import 'package:ht_dashboard/router/routes.dart';
9+
import 'package:ht_dashboard/shared/constants/app_spacing.dart';
10+
import 'package:ht_dashboard/shared/utils/date_formatter.dart';
11+
import 'package:ht_dashboard/shared/widgets/failure_state_widget.dart';
12+
import 'package:ht_dashboard/shared/widgets/loading_state_widget.dart';
13+
import 'package:ht_shared/ht_shared.dart';
214

315
/// {@template headlines_page}
4-
/// A placeholder page for Headlines.
16+
/// A page for displaying and managing Headlines in a tabular format.
517
/// {@endtemplate}
6-
class HeadlinesPage extends StatelessWidget {
18+
class HeadlinesPage extends StatefulWidget {
719
/// {@macro headlines_page}
820
const HeadlinesPage({super.key});
921

22+
@override
23+
State<HeadlinesPage> createState() => _HeadlinesPageState();
24+
}
25+
26+
class _HeadlinesPageState extends State<HeadlinesPage> {
27+
static const int _rowsPerPage = 10;
28+
29+
@override
30+
void initState() {
31+
super.initState();
32+
context.read<ContentManagementBloc>().add(
33+
const LoadHeadlinesRequested(limit: _rowsPerPage),
34+
);
35+
}
36+
1037
@override
1138
Widget build(BuildContext context) {
12-
return const Center(
13-
child: Text('Headlines Page'),
39+
final l10n = context.l10n;
40+
return Padding(
41+
padding: const EdgeInsets.all(AppSpacing.lg),
42+
child: BlocBuilder<ContentManagementBloc, ContentManagementState>(
43+
builder: (context, state) {
44+
if (state.headlinesStatus == ContentManagementStatus.loading &&
45+
state.headlines.isEmpty) {
46+
return LoadingStateWidget(
47+
icon: Icons.newspaper,
48+
headline: l10n.loadingHeadlines,
49+
subheadline: l10n.pleaseWait,
50+
);
51+
}
52+
53+
if (state.headlinesStatus == ContentManagementStatus.failure) {
54+
return FailureStateWidget(
55+
message: state.errorMessage ?? l10n.unknownError,
56+
onRetry: () => context.read<ContentManagementBloc>().add(
57+
const LoadHeadlinesRequested(limit: _rowsPerPage),
58+
),
59+
);
60+
}
61+
62+
if (state.headlines.isEmpty) {
63+
return Center(
64+
child: Text(l10n.noHeadlinesFound),
65+
);
66+
}
67+
68+
return PaginatedDataTable2(
69+
columns: [
70+
DataColumn2(
71+
label: Text(l10n.headlineTitle),
72+
size: ColumnSize.L,
73+
),
74+
DataColumn2(
75+
label: Text(l10n.source),
76+
size: ColumnSize.M,
77+
),
78+
DataColumn2(
79+
label: Text(l10n.publishedAt),
80+
size: ColumnSize.S,
81+
),
82+
DataColumn2(
83+
label: Text(l10n.actions),
84+
size: ColumnSize.S,
85+
),
86+
],
87+
source: _HeadlinesDataSource(
88+
context: context,
89+
headlines: state.headlines,
90+
l10n: l10n,
91+
),
92+
rowsPerPage: _rowsPerPage,
93+
availableRowsPerPage: const [_rowsPerPage],
94+
onPageChanged: (pageIndex) {
95+
final newOffset = pageIndex * _rowsPerPage;
96+
if (newOffset >= state.headlines.length &&
97+
state.headlinesHasMore) {
98+
context.read<ContentManagementBloc>().add(
99+
LoadHeadlinesRequested(
100+
startAfterId: state.headlinesCursor,
101+
limit: _rowsPerPage,
102+
),
103+
);
104+
}
105+
},
106+
empty: Center(child: Text(l10n.noHeadlinesFound)),
107+
showCheckboxColumn: false,
108+
showFirstLastButtons: true,
109+
fit: FlexFit.tight,
110+
headingRowHeight: 56,
111+
dataRowHeight: 56,
112+
columnSpacing: AppSpacing.md,
113+
horizontalMargin: AppSpacing.md,
114+
);
115+
},
116+
),
14117
);
15118
}
16119
}
120+
121+
class _HeadlinesDataSource extends DataTableSource {
122+
_HeadlinesDataSource({
123+
required this.context,
124+
required this.headlines,
125+
required this.l10n,
126+
});
127+
128+
final BuildContext context;
129+
final List<Headline> headlines;
130+
final AppLocalizations l10n;
131+
132+
@override
133+
DataRow? getRow(int index) {
134+
if (index >= headlines.length) {
135+
return null;
136+
}
137+
final headline = headlines[index];
138+
return DataRow2(
139+
cells: [
140+
DataCell(Text(headline.title)),
141+
DataCell(Text(headline.source?.name ?? l10n.unknown)),
142+
DataCell(
143+
Text(
144+
headline.publishedAt != null
145+
? DateFormatter.formatDate(headline.publishedAt!)
146+
: l10n.unknown,
147+
),
148+
),
149+
DataCell(
150+
Row(
151+
children: [
152+
IconButton(
153+
icon: const Icon(Icons.edit),
154+
onPressed: () {
155+
// Navigate to edit page
156+
context.goNamed(
157+
Routes.editHeadlineName,
158+
pathParameters: {'id': headline.id},
159+
);
160+
},
161+
),
162+
IconButton(
163+
icon: const Icon(Icons.delete),
164+
onPressed: () {
165+
// Dispatch delete event
166+
context.read<ContentManagementBloc>().add(
167+
DeleteHeadlineRequested(headline.id),
168+
);
169+
},
170+
),
171+
],
172+
),
173+
),
174+
],
175+
);
176+
}
177+
178+
@override
179+
bool get isRowCountApproximate => false;
180+
181+
@override
182+
int get rowCount => headlines.length;
183+
184+
@override
185+
int get selectedRowCount => 0;
186+
}

0 commit comments

Comments
 (0)