330 lines
14 KiB
Dart
330 lines
14 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import '../../core/constants/app_colors.dart';
|
|
import '../../core/constants/app_text_styles.dart';
|
|
import '../../data/services/api_service.dart';
|
|
import '../widgets/dashboard_card.dart';
|
|
import '../widgets/chart_widget.dart';
|
|
import '../widgets/teacher_panel_scaffold.dart';
|
|
import 'course_page.dart';
|
|
import 'license_page.dart';
|
|
import 'plan_renewal_page.dart';
|
|
import 'ticket_page.dart';
|
|
import 'transaction_page.dart';
|
|
|
|
class DashboardPage extends StatefulWidget {
|
|
const DashboardPage({super.key});
|
|
|
|
@override
|
|
State<DashboardPage> createState() => _DashboardPageState();
|
|
}
|
|
|
|
class _DashboardPageState extends State<DashboardPage>
|
|
with TickerProviderStateMixin {
|
|
Map<String, dynamic>? _dashboardData;
|
|
bool _isLoading = true;
|
|
late AnimationController _staggerController;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_staggerController = AnimationController(
|
|
vsync: this,
|
|
duration: const Duration(milliseconds: 1200),
|
|
);
|
|
_loadData();
|
|
}
|
|
|
|
Future<void> _loadData() async {
|
|
try {
|
|
final apiService = Provider.of<ApiService>(context, listen: false);
|
|
final data = await apiService.getDashboardData();
|
|
setState(() {
|
|
_dashboardData = data;
|
|
_isLoading = false;
|
|
});
|
|
_staggerController.forward();
|
|
} catch (e) {
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_staggerController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return TeacherPanelScaffold(
|
|
selectedMenu: PanelMenu.dashboard,
|
|
onDashboardTap: () {},
|
|
onCourseTap: () {
|
|
Navigator.of(context).push(
|
|
MaterialPageRoute(builder: (context) => const CoursePage()),
|
|
);
|
|
},
|
|
onLicenseTap: () {
|
|
Navigator.of(context).push(
|
|
MaterialPageRoute(builder: (context) => const PanelLicensePage()),
|
|
);
|
|
},
|
|
onTransactionTap: () {
|
|
Navigator.of(context).push(
|
|
MaterialPageRoute(builder: (context) => const TransactionPage()),
|
|
);
|
|
},
|
|
onTicketTap: () {
|
|
Navigator.of(context).push(
|
|
MaterialPageRoute(builder: (context) => const TicketPage()),
|
|
);
|
|
},
|
|
child: _isLoading
|
|
? const Center(child: CircularProgressIndicator())
|
|
: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
final cardWidth = constraints.maxWidth > 800
|
|
? (constraints.maxWidth - 24) / 2
|
|
: constraints.maxWidth;
|
|
|
|
return Wrap(
|
|
spacing: 24,
|
|
runSpacing: 24,
|
|
alignment: WrapAlignment.start,
|
|
children: [
|
|
_buildAnimatedCard(
|
|
0,
|
|
cardWidth,
|
|
DashboardCard(
|
|
title: 'پلن فضا',
|
|
borderColor: AppColors.planCardBorder,
|
|
iconOrAction: Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: const BoxDecoration(
|
|
color: AppColors.planCardBorder,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: const Icon(
|
|
Icons.cloud,
|
|
color: Colors.white,
|
|
size: 32,
|
|
),
|
|
),
|
|
content: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'${_dashboardData!['plan']['used']} / ${_dashboardData!['plan']['total']} GB',
|
|
style: AppTextStyles.headlineMedium,
|
|
),
|
|
Text(
|
|
'${_dashboardData!['plan']['days_left']} روز',
|
|
style: AppTextStyles.bodyMedium,
|
|
),
|
|
const SizedBox(height: 8),
|
|
OutlinedButton(
|
|
onPressed: () {
|
|
Navigator.of(context).push(
|
|
MaterialPageRoute(
|
|
builder: (context) =>
|
|
const PlanRenewalPage(),
|
|
),
|
|
);
|
|
},
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: AppColors.planCardBorder,
|
|
side: const BorderSide(
|
|
color: AppColors.planCardBorder,
|
|
),
|
|
),
|
|
child: const Text('تغییر / تمدید'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
_buildAnimatedCard(
|
|
1,
|
|
cardWidth,
|
|
DashboardCard(
|
|
title: 'اعتبار',
|
|
borderColor: AppColors.creditCardBorder,
|
|
iconOrAction: Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: const BoxDecoration(
|
|
color: AppColors.creditCardBorder,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: const Icon(
|
|
Icons.attach_money,
|
|
color: Colors.white,
|
|
size: 32,
|
|
),
|
|
),
|
|
content: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'${_dashboardData!['credit']['amount']} تومان',
|
|
style: AppTextStyles.headlineMedium,
|
|
),
|
|
const SizedBox(height: 8),
|
|
OutlinedButton(
|
|
onPressed: () {},
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: AppColors.creditCardBorder,
|
|
side: const BorderSide(
|
|
color: AppColors.creditCardBorder,
|
|
),
|
|
),
|
|
child: const Text('افزایش اعتبار'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
_buildAnimatedCard(
|
|
2,
|
|
cardWidth,
|
|
DashboardCard(
|
|
title: 'کلید API',
|
|
borderColor: AppColors.apiCardBorder,
|
|
iconOrAction: Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: const BoxDecoration(
|
|
color: AppColors.apiCardBorder,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: const Icon(
|
|
Icons.vpn_key,
|
|
color: Colors.white,
|
|
size: 32,
|
|
),
|
|
),
|
|
content: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
_dashboardData!['api_key']['key'],
|
|
style: AppTextStyles.bodyMedium.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
Text(
|
|
_dashboardData!['api_key']['secret'],
|
|
style: AppTextStyles.bodyMedium,
|
|
),
|
|
const SizedBox(height: 8),
|
|
OutlinedButton(
|
|
onPressed: () {},
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: AppColors.apiCardBorder,
|
|
side: const BorderSide(
|
|
color: AppColors.apiCardBorder,
|
|
),
|
|
),
|
|
child: const Text('کپی API'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
_buildAnimatedCard(
|
|
3,
|
|
cardWidth,
|
|
DashboardCard(
|
|
title: 'ریبرندینگ',
|
|
borderColor: AppColors.rebrandingCardBorder,
|
|
iconOrAction: Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: const BoxDecoration(
|
|
color: AppColors.rebrandingCardBorder,
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: const Icon(
|
|
Icons.palette,
|
|
color: Colors.white,
|
|
size: 32,
|
|
),
|
|
),
|
|
content: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
_dashboardData!['rebranding']['status'],
|
|
style: AppTextStyles.headlineMedium,
|
|
),
|
|
const SizedBox(height: 8),
|
|
OutlinedButton(
|
|
onPressed: () {},
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor:
|
|
AppColors.rebrandingCardBorder,
|
|
side: const BorderSide(
|
|
color: AppColors.rebrandingCardBorder,
|
|
),
|
|
),
|
|
child: const Text('سفارش'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
const SizedBox(height: 32),
|
|
Text(
|
|
'عملکرد ۳۰ روز گذشته',
|
|
style: AppTextStyles.headlineMedium,
|
|
),
|
|
const SizedBox(height: 16),
|
|
FadeTransition(
|
|
opacity: CurvedAnimation(
|
|
parent: _staggerController,
|
|
curve: const Interval(0.6, 1.0, curve: Curves.easeOut),
|
|
),
|
|
child: ActivityChart(
|
|
data: List<Map<String, dynamic>>.from(
|
|
_dashboardData!['chart_data'],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildAnimatedCard(int index, double width, Widget card) {
|
|
final interval = Interval(
|
|
index * 0.15,
|
|
(index * 0.15) + 0.4,
|
|
curve: Curves.easeOut,
|
|
);
|
|
|
|
return SizedBox(
|
|
width: width,
|
|
child: SlideTransition(
|
|
position: Tween<Offset>(
|
|
begin: const Offset(0, 0.3),
|
|
end: Offset.zero,
|
|
).animate(CurvedAnimation(parent: _staggerController, curve: interval)),
|
|
child: FadeTransition(
|
|
opacity: CurvedAnimation(parent: _staggerController, curve: interval),
|
|
child: card,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|