281 lines
9.1 KiB
Dart
281 lines
9.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:provider/provider.dart';
|
|
import '../providers/user_provider.dart';
|
|
import '../providers/group_provider.dart';
|
|
import '../theme/app_theme.dart';
|
|
import '../widgets/app_sidebar.dart';
|
|
import '../widgets/responsive_layout.dart';
|
|
import '../widgets/stat_card.dart';
|
|
import '../config/app_config.dart';
|
|
|
|
class DashboardScreen extends StatefulWidget {
|
|
const DashboardScreen({super.key});
|
|
|
|
@override
|
|
State<DashboardScreen> createState() => _DashboardScreenState();
|
|
}
|
|
|
|
class _DashboardScreenState extends State<DashboardScreen> {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
context.read<UserProvider>().loadUsers();
|
|
context.read<GroupProvider>().loadGroups();
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ResponsiveLayout(
|
|
title: 'داشبورد',
|
|
sidebar: const AppSidebar(),
|
|
body: _DashboardBody(),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _DashboardBody extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SingleChildScrollView(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// ── Header ─────────────────────────────────────────────────
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Text(
|
|
'داشبورد',
|
|
style: TextStyle(
|
|
fontSize: 26,
|
|
fontWeight: FontWeight.w800,
|
|
color: AppTheme.textPrimary,
|
|
),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
'خلاصه وضعیت سیستم NEDA',
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
color: AppTheme.textSecondary,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
if (AppConfig.debugMode)
|
|
Container(
|
|
padding:
|
|
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
|
decoration: BoxDecoration(
|
|
color: AppTheme.warning.withValues(alpha: 0.1),
|
|
borderRadius: BorderRadius.circular(20),
|
|
border: Border.all(
|
|
color: AppTheme.warning.withValues(alpha: 0.4)),
|
|
),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(Icons.bug_report_rounded,
|
|
size: 14, color: AppTheme.warning),
|
|
const SizedBox(width: 6),
|
|
Text(
|
|
'Debug Mode',
|
|
style: TextStyle(
|
|
color: AppTheme.warning,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 28),
|
|
|
|
// ── Stat cards ─────────────────────────────────────────────
|
|
_StatsGrid(),
|
|
const SizedBox(height: 28),
|
|
|
|
// ── Quick actions ──────────────────────────────────────────
|
|
const Text(
|
|
'دسترسی سریع',
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w700,
|
|
color: AppTheme.textPrimary,
|
|
),
|
|
),
|
|
const SizedBox(height: 16),
|
|
_QuickActions(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _StatsGrid extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Consumer2<UserProvider, GroupProvider>(
|
|
builder: (_, users, groups, __) {
|
|
return LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
final crossAxisCount = constraints.maxWidth >= 700 ? 4 : 2;
|
|
return GridView.count(
|
|
crossAxisCount: crossAxisCount,
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
mainAxisSpacing: 16,
|
|
crossAxisSpacing: 16,
|
|
childAspectRatio: constraints.maxWidth >= 700 ? 1.6 : 1.4,
|
|
children: [
|
|
StatCard(
|
|
title: 'کل کاربران',
|
|
value: users.isLoading ? '...' : '${users.totalCount}',
|
|
icon: Icons.people_rounded,
|
|
color: AppTheme.primary,
|
|
subtitle: '${users.activeCount} فعال',
|
|
),
|
|
StatCard(
|
|
title: 'کل گروهها',
|
|
value: groups.isLoading ? '...' : '${groups.totalCount}',
|
|
icon: Icons.groups_rounded,
|
|
color: const Color(0xFF7C3AED),
|
|
subtitle: '${groups.activeCount} فعال',
|
|
),
|
|
StatCard(
|
|
title: 'کاربران غیرفعال',
|
|
value: users.isLoading
|
|
? '...'
|
|
: '${users.totalCount - users.activeCount}',
|
|
icon: Icons.person_off_rounded,
|
|
color: AppTheme.warning,
|
|
),
|
|
StatCard(
|
|
title: 'گروههای غیرفعال',
|
|
value: groups.isLoading
|
|
? '...'
|
|
: '${groups.totalCount - groups.activeCount}',
|
|
icon: Icons.group_off_rounded,
|
|
color: AppTheme.danger,
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class _QuickActions extends StatelessWidget {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return LayoutBuilder(builder: (context, constraints) {
|
|
final isWide = constraints.maxWidth >= 500;
|
|
return Wrap(
|
|
spacing: 12,
|
|
runSpacing: 12,
|
|
children: [
|
|
_ActionCard(
|
|
label: 'ایجاد کاربر جدید',
|
|
icon: Icons.person_add_rounded,
|
|
color: AppTheme.primary,
|
|
onTap: () => context.go('/users'),
|
|
),
|
|
_ActionCard(
|
|
label: 'ایجاد گروه جدید',
|
|
icon: Icons.group_add_rounded,
|
|
color: const Color(0xFF7C3AED),
|
|
onTap: () => context.go('/groups'),
|
|
),
|
|
_ActionCard(
|
|
label: 'مدیریت کاربران',
|
|
icon: Icons.manage_accounts_rounded,
|
|
color: const Color(0xFF0891B2),
|
|
onTap: () => context.go('/users'),
|
|
),
|
|
_ActionCard(
|
|
label: 'مدیریت گروهها',
|
|
icon: Icons.settings_rounded,
|
|
color: AppTheme.success,
|
|
onTap: () => context.go('/groups'),
|
|
),
|
|
_ActionCard(
|
|
label: 'ارسال اعلان عمومی',
|
|
icon: Icons.campaign_rounded,
|
|
color: const Color(0xFFEA580C),
|
|
onTap: () => context.go('/notifications'),
|
|
),
|
|
]
|
|
.map((w) => SizedBox(
|
|
width: isWide
|
|
? (constraints.maxWidth - 48) / 5
|
|
: (constraints.maxWidth - 12) / 2,
|
|
child: w,
|
|
))
|
|
.toList(),
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
class _ActionCard extends StatelessWidget {
|
|
final String label;
|
|
final IconData icon;
|
|
final Color color;
|
|
final VoidCallback onTap;
|
|
|
|
const _ActionCard({
|
|
required this.label,
|
|
required this.icon,
|
|
required this.color,
|
|
required this.onTap,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Card(
|
|
child: InkWell(
|
|
onTap: onTap,
|
|
borderRadius: BorderRadius.circular(12),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
width: 44,
|
|
height: 44,
|
|
decoration: BoxDecoration(
|
|
color: color.withValues(alpha: 0.12),
|
|
borderRadius: BorderRadius.circular(10),
|
|
),
|
|
child: Icon(icon, color: color, size: 22),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
label,
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
color: AppTheme.textPrimary,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|