first commit
This commit is contained in:
189
lib/features/admin/presentation/pages/admin_page.dart
Normal file
189
lib/features/admin/presentation/pages/admin_page.dart
Normal file
@@ -0,0 +1,189 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../shared/widgets/riotz_shell.dart';
|
||||
import '../../domain/models/admin_post_model.dart';
|
||||
import '../../domain/models/admin_user_model.dart';
|
||||
import '../providers/admin_providers.dart';
|
||||
|
||||
class AdminPage extends ConsumerWidget {
|
||||
const AdminPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isAdmin = ref.watch(isAdminProvider);
|
||||
final dataAsync = ref.watch(adminPanelDataProvider);
|
||||
|
||||
ref.listen(adminControllerProvider, (_, next) {
|
||||
next.whenOrNull(
|
||||
data: (_) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Admin action applied')),
|
||||
);
|
||||
},
|
||||
error: (error, _) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(error.toString())),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
if (!isAdmin) {
|
||||
return const RiotzShell(
|
||||
title: 'RIOTZ // Admin',
|
||||
child: Center(
|
||||
child: Text('You are not authorized for admin panel access.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return RiotzShell(
|
||||
title: 'RIOTZ // Admin',
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () async => ref.invalidate(adminPanelDataProvider),
|
||||
child: dataAsync.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (error, _) => Center(child: Text('Error: $error')),
|
||||
data: (data) => ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
Text(
|
||||
'Whitelist-based Admin Panel',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Users',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
...data.users.map(
|
||||
(user) => _UserModerationCard(
|
||||
user: user,
|
||||
onToggleBan: (banned) => ref
|
||||
.read(adminControllerProvider.notifier)
|
||||
.setUserBanned(userId: user.userId, banned: banned),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
Text(
|
||||
'Posts',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
...data.posts.map(
|
||||
(post) => _PostModerationCard(
|
||||
post: post,
|
||||
onDelete: () =>
|
||||
ref.read(adminControllerProvider.notifier).deletePost(post.id),
|
||||
onToggleFeatured: (featured) => ref
|
||||
.read(adminControllerProvider.notifier)
|
||||
.setPostFeatured(postId: post.id, featured: featured),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _UserModerationCard extends StatelessWidget {
|
||||
const _UserModerationCard({
|
||||
required this.user,
|
||||
required this.onToggleBan,
|
||||
});
|
||||
|
||||
final AdminUserModel user;
|
||||
final ValueChanged<bool> onToggleBan;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: SwitchListTile(
|
||||
value: user.banned,
|
||||
onChanged: onToggleBan,
|
||||
title: Text(user.username),
|
||||
subtitle: Text(user.userId),
|
||||
secondary: CircleAvatar(
|
||||
backgroundImage:
|
||||
user.avatarUrl.isNotEmpty ? NetworkImage(user.avatarUrl) : null,
|
||||
child: user.avatarUrl.isEmpty ? const Icon(Icons.person) : null,
|
||||
),
|
||||
activeThumbColor: Colors.redAccent,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PostModerationCard extends StatelessWidget {
|
||||
const _PostModerationCard({
|
||||
required this.post,
|
||||
required this.onDelete,
|
||||
required this.onToggleFeatured,
|
||||
});
|
||||
|
||||
final AdminPostModel post;
|
||||
final VoidCallback onDelete;
|
||||
final ValueChanged<bool> onToggleFeatured;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
post.username,
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: onDelete,
|
||||
icon: const Icon(Icons.delete_outline),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (post.caption.isNotEmpty) ...[
|
||||
Text(post.caption),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
if (post.imageUrl.isNotEmpty)
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Image.network(
|
||||
post.imageUrl,
|
||||
width: double.infinity,
|
||||
height: 160,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Text('${post.likesCount} likes'),
|
||||
const Spacer(),
|
||||
Row(
|
||||
children: [
|
||||
const Text('Feature'),
|
||||
Switch(
|
||||
value: post.featured,
|
||||
onChanged: onToggleFeatured,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user