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/teacher_panel_scaffold.dart'; import 'course_details_page.dart'; import 'dashboard_page.dart'; import 'license_page.dart'; import 'new_course_page.dart'; import 'ticket_page.dart'; import 'transaction_page.dart'; class CoursePage extends StatefulWidget { const CoursePage({super.key}); @override State createState() => _CoursePageState(); } class _CoursePageState extends State { bool _isLoading = true; String? _errorMessage; List> _courses = >[]; int _currentPage = 1; int _lastPage = 1; @override void initState() { super.initState(); _loadCourses(); } Future _loadCourses({int page = 1}) async { setState(() { _isLoading = true; _errorMessage = null; }); try { final apiService = Provider.of(context, listen: false); final response = await apiService.getCourses(page: page); final data = response['data']; final meta = response['meta']; setState(() { _courses = data is List ? data .whereType() .map((item) => Map.from(item)) .toList() : >[]; if (meta is Map) { _currentPage = (meta['current_page'] as num?)?.toInt() ?? page; _lastPage = (meta['last_page'] as num?)?.toInt() ?? 1; } else { _currentPage = page; _lastPage = 1; } _isLoading = false; }); } catch (e) { setState(() { _isLoading = false; _errorMessage = e.toString(); }); } } @override Widget build(BuildContext context) { final isDesktop = MediaQuery.of(context).size.width > 900; return TeacherPanelScaffold( selectedMenu: PanelMenu.course, onDashboardTap: () { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => const DashboardPage()), ); }, onCourseTap: () {}, 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: Padding( padding: EdgeInsets.all(isDesktop ? 24 : 12), child: Container( decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.35), borderRadius: BorderRadius.circular(10), ), child: Column( children: [ Padding( padding: EdgeInsets.all(isDesktop ? 14 : 10), child: _CourseToolbar(isDesktop: isDesktop), ), Expanded( child: Container( margin: EdgeInsets.fromLTRB( isDesktop ? 14 : 8, 0, isDesktop ? 14 : 8, isDesktop ? 14 : 8, ), decoration: BoxDecoration( color: Colors.white, border: Border.all(color: Colors.black12), borderRadius: BorderRadius.circular(8), ), child: Column( children: [ Container( height: 46, decoration: const BoxDecoration( color: Color(0xFFD5D5D5), borderRadius: BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8), ), ), child: const Row( children: [ _HeaderCell(title: 'عنوان دوره', flex: 4), _HeaderCell(title: 'مدرس', flex: 3), _HeaderCell(title: 'توضیحات', flex: 5), _HeaderCell(title: 'کاور', flex: 2), ], ), ), Expanded(child: _buildContent(isDesktop)), _PaginationBar( currentPage: _currentPage, lastPage: _lastPage, onPrevious: _currentPage > 1 ? () => _loadCourses(page: _currentPage - 1) : null, onNext: _currentPage < _lastPage ? () => _loadCourses(page: _currentPage + 1) : null, ), ], ), ), ), ], ), ), ), ); } Widget _buildContent(bool isDesktop) { if (_isLoading) { return const Center(child: CircularProgressIndicator()); } if (_errorMessage != null) { return Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( _errorMessage!, style: AppTextStyles.bodyMedium.copyWith(color: Colors.red), textAlign: TextAlign.center, ), const SizedBox(height: 12), OutlinedButton( onPressed: () => _loadCourses(page: _currentPage), child: const Text('تلاش مجدد'), ), ], ), ), ); } if (_courses.isEmpty) { return const Center(child: _EmptyCourseState()); } return ListView.separated( itemCount: _courses.length, separatorBuilder: (_, _) => const Divider(height: 1), itemBuilder: (context, index) { final course = _courses[index]; final title = (course['title'] ?? '-').toString(); final teacherName = _extractTeacherName(course['teacher_name']); final description = (course['description'] ?? '-').toString(); final coverUrl = course['cover_url']?.toString(); final courseId = (course['id'] as num?)?.toInt(); return InkWell( onTap: courseId == null ? null : () { Navigator.of(context).push( MaterialPageRoute( builder: (_) => CourseDetailsPage(courseId: courseId), ), ); }, child: SizedBox( height: isDesktop ? 68 : 88, child: Row( children: [ _BodyCell(text: title, flex: 4), _BodyCell(text: teacherName, flex: 3), _BodyCell(text: description, flex: 5), Expanded( flex: 2, child: Center( child: coverUrl == null || coverUrl.isEmpty ? const Icon(Icons.image_not_supported_outlined) : ClipRRect( borderRadius: BorderRadius.circular(6), child: Image.network( coverUrl, width: 42, height: 42, fit: BoxFit.cover, errorBuilder: (_, _, _) => const Icon( Icons.broken_image_outlined, ), ), ), ), ), ], ), ), ); }, ); } String _extractTeacherName(dynamic teacherObj) { if (teacherObj is Map) { final value = teacherObj['user_name']; if (value is String && value.trim().isNotEmpty) { return value; } } return '-'; } } class _CourseToolbar extends StatelessWidget { final bool isDesktop; const _CourseToolbar({required this.isDesktop}); @override Widget build(BuildContext context) { return Wrap( spacing: 8, runSpacing: 8, crossAxisAlignment: WrapCrossAlignment.center, children: [ SizedBox( height: 36, width: isDesktop ? 120 : double.infinity, child: ElevatedButton.icon( onPressed: () { Navigator.of(context).push( MaterialPageRoute(builder: (context) => const NewCoursePage()), ); }, icon: const Icon(Icons.add, size: 18), label: const Text('دوره جدید'), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF56B33E), foregroundColor: Colors.white, ), ), ), SizedBox( width: isDesktop ? 180 : double.infinity, height: 36, child: DropdownButtonFormField( initialValue: 'فیلتر نوع محتوا', decoration: InputDecoration( filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(6), borderSide: BorderSide(color: Colors.grey.shade400), ), contentPadding: const EdgeInsets.symmetric(horizontal: 10), ), items: const [ DropdownMenuItem( value: 'فیلتر نوع محتوا', child: Text('فیلتر نوع محتوا'), ), DropdownMenuItem(value: 'ویدئو', child: Text('ویدئو')), DropdownMenuItem(value: 'صوت', child: Text('صوت')), DropdownMenuItem(value: 'فایل', child: Text('فایل')), ], onChanged: (_) {}, ), ), SizedBox( width: isDesktop ? 380 : double.infinity, height: 36, child: TextField( decoration: InputDecoration( hintText: 'جست و جو دوره', prefixIcon: const Icon(Icons.search, size: 18), border: OutlineInputBorder( borderRadius: BorderRadius.circular(6), borderSide: BorderSide(color: Colors.grey.shade400), ), contentPadding: const EdgeInsets.symmetric(vertical: 8), ), ), ), ], ); } } class _HeaderCell extends StatelessWidget { final String title; final int flex; const _HeaderCell({required this.title, required this.flex}); @override Widget build(BuildContext context) { return Expanded( flex: flex, child: Center( child: Text( title, style: AppTextStyles.bodyLarge.copyWith(fontWeight: FontWeight.w700), ), ), ); } } class _BodyCell extends StatelessWidget { final String text; final int flex; const _BodyCell({required this.text, required this.flex}); @override Widget build(BuildContext context) { return Expanded( flex: flex, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Text( text, maxLines: 2, overflow: TextOverflow.ellipsis, style: AppTextStyles.bodyMedium, textAlign: TextAlign.center, ), ), ); } } class _PaginationBar extends StatelessWidget { final int currentPage; final int lastPage; final VoidCallback? onPrevious; final VoidCallback? onNext; const _PaginationBar({ required this.currentPage, required this.lastPage, this.onPrevious, this.onNext, }); @override Widget build(BuildContext context) { return Container( height: 52, padding: const EdgeInsets.symmetric(horizontal: 12), decoration: const BoxDecoration( border: Border(top: BorderSide(color: Colors.black12)), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'صفحه $currentPage از $lastPage', style: AppTextStyles.bodyMedium, ), Row( children: [ TextButton(onPressed: onPrevious, child: const Text('قبلی')), const SizedBox(width: 8), TextButton(onPressed: onNext, child: const Text('بعدی')), ], ), ], ), ); } } class _EmptyCourseState extends StatelessWidget { const _EmptyCourseState(); @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 340, constraints: const BoxConstraints(maxWidth: 340), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20), decoration: BoxDecoration( color: Colors.grey.shade50, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade200), ), child: Column( children: [ Icon( Icons.desktop_windows_outlined, size: 120, color: AppColors.sidebarBg.withValues(alpha: 0.7), ), const SizedBox(height: 6), Text( 'NO DATA', style: AppTextStyles.headlineMedium.copyWith( color: AppColors.sidebarBg, fontSize: 18, ), ), ], ), ), const SizedBox(height: 22), Text( 'هنوز اطلاعاتی وارد نشده است.', style: AppTextStyles.bodyMedium.copyWith(color: AppColors.sidebarBg), ), ], ); } }