190 lines
5.6 KiB
Dart
190 lines
5.6 KiB
Dart
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,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|