Skip to content

Commit 97f5c36

Browse files
committed
feat(content_management): add ArchivedTopicsPage with pagination and state management
1 parent 9772698 commit 97f5c36

File tree

1 file changed

+187
-0
lines changed

1 file changed

+187
-0
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
import 'package:core/core.dart';
2+
import 'package:data_repository/data_repository.dart';
3+
import 'package:data_table_2/data_table_2.dart';
4+
import 'package:flutter/material.dart';
5+
import 'package:flutter_bloc/flutter_bloc.dart';
6+
import 'package:flutter_news_app_web_dashboard_full_source_code/content_management/bloc/archived_topics/archived_topics_bloc.dart';
7+
import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/app_localizations.dart';
8+
import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart';
9+
import 'package:flutter_news_app_web_dashboard_full_source_code/shared/shared.dart';
10+
import 'package:intl/intl.dart';
11+
import 'package:ui_kit/ui_kit.dart';
12+
13+
class ArchivedTopicsPage extends StatelessWidget {
14+
const ArchivedTopicsPage({super.key});
15+
16+
@override
17+
Widget build(BuildContext context) {
18+
return BlocProvider(
19+
create: (context) => ArchivedTopicsBloc(
20+
topicsRepository: context.read<DataRepository<Topic>>(),
21+
)..add(const LoadArchivedTopicsRequested(limit: kDefaultRowsPerPage)),
22+
child: const _ArchivedTopicsView(),
23+
);
24+
}
25+
}
26+
27+
class _ArchivedTopicsView extends StatelessWidget {
28+
const _ArchivedTopicsView();
29+
30+
@override
31+
Widget build(BuildContext context) {
32+
final l10n = AppLocalizationsX(context).l10n;
33+
return Scaffold(
34+
appBar: AppBar(
35+
title: Text(l10n.archivedTopics), //TODO(you): Localize this string
36+
),
37+
body: Padding(
38+
padding: const EdgeInsets.all(AppSpacing.lg),
39+
child: BlocBuilder<ArchivedTopicsBloc, ArchivedTopicsState>(
40+
builder: (context, state) {
41+
if (state.status == ArchivedTopicsStatus.loading &&
42+
state.topics.isEmpty) {
43+
return LoadingStateWidget(
44+
icon: Icons.topic,
45+
headline: l10n.loadingArchivedTopics, //TODO(you): Localize this string
46+
subheadline: l10n.pleaseWait,
47+
);
48+
}
49+
50+
if (state.status == ArchivedTopicsStatus.failure) {
51+
return FailureStateWidget(
52+
exception: state.exception!,
53+
onRetry: () => context.read<ArchivedTopicsBloc>().add(
54+
const LoadArchivedTopicsRequested(
55+
limit: kDefaultRowsPerPage,
56+
),
57+
),
58+
);
59+
}
60+
61+
if (state.topics.isEmpty) {
62+
return Center(child: Text(l10n.noArchivedTopicsFound)); //TODO(you): Localize this string
63+
}
64+
65+
return Column(
66+
children: [
67+
if (state.status == ArchivedTopicsStatus.loading &&
68+
state.topics.isNotEmpty)
69+
const LinearProgressIndicator(),
70+
Expanded(
71+
child: PaginatedDataTable2(
72+
columns: [
73+
DataColumn2(
74+
label: Text(l10n.topicName),
75+
size: ColumnSize.L,
76+
),
77+
DataColumn2(
78+
label: Text(l10n.lastUpdated),
79+
size: ColumnSize.M,
80+
),
81+
DataColumn2(
82+
label: Text(l10n.actions),
83+
size: ColumnSize.S,
84+
fixedWidth: 120,
85+
),
86+
],
87+
source: _TopicsDataSource(
88+
context: context,
89+
topics: state.topics,
90+
hasMore: state.hasMore,
91+
l10n: l10n,
92+
),
93+
rowsPerPage: kDefaultRowsPerPage,
94+
availableRowsPerPage: const [kDefaultRowsPerPage],
95+
onPageChanged: (pageIndex) {
96+
final newOffset = pageIndex * kDefaultRowsPerPage;
97+
if (newOffset >= state.topics.length &&
98+
state.hasMore &&
99+
state.status != ArchivedTopicsStatus.loading) {
100+
context.read<ArchivedTopicsBloc>().add(
101+
LoadArchivedTopicsRequested(
102+
startAfterId: state.cursor,
103+
limit: kDefaultRowsPerPage,
104+
),
105+
);
106+
}
107+
},
108+
empty: Center(child: Text(l10n.noTopicsFound)),
109+
showCheckboxColumn: false,
110+
showFirstLastButtons: true,
111+
fit: FlexFit.tight,
112+
headingRowHeight: 56,
113+
dataRowHeight: 56,
114+
columnSpacing: AppSpacing.md,
115+
horizontalMargin: AppSpacing.md,
116+
),
117+
),
118+
],
119+
);
120+
},
121+
),
122+
),
123+
);
124+
}
125+
}
126+
127+
class _TopicsDataSource extends DataTableSource {
128+
_TopicsDataSource({
129+
required this.context,
130+
required this.topics,
131+
required this.hasMore,
132+
required this.l10n,
133+
});
134+
135+
final BuildContext context;
136+
final List<Topic> topics;
137+
final bool hasMore;
138+
final AppLocalizations l10n;
139+
140+
@override
141+
DataRow? getRow(int index) {
142+
if (index >= topics.length) {
143+
return null;
144+
}
145+
final topic = topics[index];
146+
return DataRow2(
147+
cells: [
148+
DataCell(
149+
Text(
150+
topic.name,
151+
maxLines: 2,
152+
overflow: TextOverflow.ellipsis,
153+
),
154+
),
155+
DataCell(
156+
Text(
157+
DateFormat('dd-MM-yyyy').format(topic.updatedAt.toLocal()),
158+
),
159+
),
160+
DataCell(
161+
Row(
162+
children: [
163+
IconButton(
164+
icon: const Icon(Icons.restore),
165+
tooltip: l10n.restore,
166+
onPressed: () {
167+
context.read<ArchivedTopicsBloc>().add(
168+
RestoreTopicRequested(topic.id),
169+
);
170+
},
171+
),
172+
],
173+
),
174+
),
175+
],
176+
);
177+
}
178+
179+
@override
180+
bool get isRowCountApproximate => hasMore;
181+
182+
@override
183+
int get rowCount => topics.length;
184+
185+
@override
186+
int get selectedRowCount => 0;
187+
}

0 commit comments

Comments
 (0)