This commit is contained in:
Phuoc Nguyen
2025-11-10 14:21:27 +07:00
parent 2a71c65577
commit 36bdf6613b
33 changed files with 2206 additions and 252 deletions

View File

@@ -19,20 +19,36 @@ import 'package:worker/features/news/presentation/widgets/news_card.dart';
///
/// Features:
/// - Standard AppBar with title "Tin tức & chuyên môn"
/// - Horizontal scrollable category chips (Tất cả, Tin tức, Chuyên môn, Dự án, Sự kiện, Khuyến mãi)
/// - Horizontal scrollable category chips (dynamic from Frappe API)
/// - Featured article section (large card)
/// - "Mới nhất" section with news cards list
/// - RefreshIndicator for pull-to-refresh
/// - Loading and error states
class NewsListPage extends ConsumerWidget {
class NewsListPage extends ConsumerStatefulWidget {
const NewsListPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
ConsumerState<NewsListPage> createState() => _NewsListPageState();
}
class _NewsListPageState extends ConsumerState<NewsListPage> {
/// Currently selected category name (null = All)
String? selectedCategoryName;
@override
Widget build(BuildContext context) {
// Watch providers
final featuredArticleAsync = ref.watch(featuredArticleProvider);
final filteredArticlesAsync = ref.watch(filteredNewsArticlesProvider);
final selectedCategory = ref.watch(selectedNewsCategoryProvider);
final newsArticlesAsync = ref.watch(newsArticlesProvider);
// Filter articles by selected category
final filteredArticles = newsArticlesAsync.whenData((articles) {
if (selectedCategoryName == null) {
return articles;
}
// TODO: Filter by category when articles have category field
return articles;
});
return Scaffold(
backgroundColor: Colors.white,
@@ -40,9 +56,10 @@ class NewsListPage extends ConsumerWidget {
body: RefreshIndicator(
onRefresh: () async {
// Invalidate providers to trigger refresh
ref.invalidate(newsArticlesProvider);
ref.invalidate(featuredArticleProvider);
ref.invalidate(filteredNewsArticlesProvider);
ref
..invalidate(newsArticlesProvider)
..invalidate(featuredArticleProvider)
..invalidate(blogCategoriesProvider);
},
child: CustomScrollView(
slivers: [
@@ -51,11 +68,11 @@ class NewsListPage extends ConsumerWidget {
child: Padding(
padding: const EdgeInsets.only(top: 4, bottom: AppSpacing.md),
child: CategoryFilterChips(
selectedCategory: selectedCategory,
onCategorySelected: (category) {
ref
.read(selectedNewsCategoryProvider.notifier)
.setCategory(category);
selectedCategoryName: selectedCategoryName,
onCategorySelected: (categoryName) {
setState(() {
selectedCategoryName = categoryName;
});
},
),
),
@@ -148,7 +165,7 @@ class NewsListPage extends ConsumerWidget {
const SliverToBoxAdapter(child: SizedBox(height: AppSpacing.md)),
// News List
filteredArticlesAsync.when(
filteredArticles.when(
data: (articles) {
if (articles.isEmpty) {
return SliverFillRemaining(child: _buildEmptyState());