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_page.dart'; import 'dashboard_page.dart'; import 'license_page.dart'; import 'ticket_page.dart'; import 'transaction_page.dart'; class NewLicensePage extends StatefulWidget { final int courseId; final String courseTitle; const NewLicensePage({ super.key, required this.courseId, required this.courseTitle, }); @override State createState() => _NewLicensePageState(); } class _NewLicensePageState extends State { final TextEditingController _studentPhoneController = TextEditingController(); final TextEditingController _studentNameController = TextEditingController(); final TextEditingController _studentEmailController = TextEditingController(); final List<_DeviceLimitForm> _deviceLimits = <_DeviceLimitForm>[ _DeviceLimitForm(), ]; bool _isSubmitting = false; String? _errorMessage; @override void dispose() { _studentPhoneController.dispose(); _studentNameController.dispose(); _studentEmailController.dispose(); for (final item in _deviceLimits) { item.dispose(); } super.dispose(); } void _addDeviceLimit() { setState(() { _deviceLimits.add(_DeviceLimitForm()); }); } void _removeDeviceLimit(int index) { if (_deviceLimits.length <= 1) return; final removed = _deviceLimits.removeAt(index); removed.dispose(); setState(() {}); } Future _submit() async { final studentPhone = _studentPhoneController.text.trim(); final studentName = _studentNameController.text.trim(); final studentEmail = _studentEmailController.text.trim(); if (!RegExp(r'^09\d{9}$').hasMatch(studentPhone)) { setState(() { _errorMessage = 'شماره موبایل دانشجو باید با 09 شروع شود و 11 رقم باشد.'; }); return; } if (studentName.length > 255) { setState(() { _errorMessage = 'حداکثر طول نام دانشجو 255 کاراکتر است.'; }); return; } if (studentEmail.isNotEmpty) { final emailRegex = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$'); if (!emailRegex.hasMatch(studentEmail)) { setState(() { _errorMessage = 'ایمیل دانشجو معتبر نیست.'; }); return; } if (studentEmail.length > 255) { setState(() { _errorMessage = 'حداکثر طول ایمیل دانشجو 255 کاراکتر است.'; }); return; } } final payloadDeviceLimits = >[]; for (final item in _deviceLimits) { final limit = int.tryParse(item.limitController.text.trim()); if (limit == null || limit < 1 || limit > 10) { setState(() { _errorMessage = 'مقدار limit هر آیتم باید عدد بین 1 تا 10 باشد.'; }); return; } payloadDeviceLimits.add({ 'os': item.os, 'limit': limit, }); } if (payloadDeviceLimits.isEmpty) { setState(() { _errorMessage = 'حداقل یک device limit باید وارد شود.'; }); return; } setState(() { _isSubmitting = true; _errorMessage = null; }); try { final apiService = Provider.of(context, listen: false); final response = await apiService.createCourseLicense( courseId: widget.courseId, studentPhone: studentPhone, studentName: studentName.isEmpty ? null : studentName, studentEmail: studentEmail.isEmpty ? null : studentEmail, deviceLimits: payloadDeviceLimits, ); if (!mounted) return; final message = (response['message'] ?? 'لایسنس با موفقیت ساخته شد.').toString(); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(message)), ); Navigator.of(context).pop(true); } catch (e) { setState(() { _errorMessage = e.toString(); }); } finally { if (mounted) { setState(() { _isSubmitting = false; }); } } } @override Widget build(BuildContext context) { final isDesktop = MediaQuery.of(context).size.width > 900; return TeacherPanelScaffold( selectedMenu: PanelMenu.license, onDashboardTap: () { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => const DashboardPage()), ); }, onCourseTap: () { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (context) => const CoursePage()), ); }, onLicenseTap: () { Navigator.of(context).pushReplacement( 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: SingleChildScrollView( padding: EdgeInsets.all(isDesktop ? 24 : 12), child: Container( decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.35), borderRadius: BorderRadius.circular(10), ), padding: EdgeInsets.all(isDesktop ? 18 : 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ InkWell( onTap: () => Navigator.pop(context), child: const Icon(Icons.arrow_back, color: AppColors.sidebarBg), ), const SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ const Icon(Icons.add_card, size: 18, color: AppColors.sidebarBg), const SizedBox(width: 6), Text( 'ساخت لایسنس جدید', style: AppTextStyles.headlineMedium.copyWith( color: AppColors.sidebarBg, fontSize: 20, ), ), ], ), const SizedBox(height: 8), Container( width: double.infinity, padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: const Color(0xFFEFF5FF), borderRadius: BorderRadius.circular(8), border: Border.all(color: AppColors.sidebarBg.withValues(alpha: 0.3)), ), child: Text( 'این لایسنس برای دوره: ${widget.courseTitle} (ID: ${widget.courseId})', style: AppTextStyles.bodyLarge.copyWith(color: AppColors.sidebarBg), ), ), const SizedBox(height: 12), Container(height: 1, color: Colors.black12), const SizedBox(height: 16), _buildLabel('شماره موبایل دانشجو *'), const SizedBox(height: 6), _buildInput( _studentPhoneController, keyboardType: TextInputType.phone, hint: '09xxxxxxxxx', ), const SizedBox(height: 12), _buildLabel('نام دانشجو (اختیاری)'), const SizedBox(height: 6), _buildInput(_studentNameController), const SizedBox(height: 12), _buildLabel('ایمیل دانشجو (اختیاری)'), const SizedBox(height: 6), _buildInput( _studentEmailController, keyboardType: TextInputType.emailAddress, ), const SizedBox(height: 22), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Device Limits *', style: AppTextStyles.headlineMedium), OutlinedButton.icon( onPressed: _addDeviceLimit, icon: const Icon(Icons.add, size: 16), label: const Text('افزودن آیتم'), ), ], ), const SizedBox(height: 10), ..._deviceLimits.asMap().entries.map( (entry) { final index = entry.key; final item = entry.value; return Padding( padding: const EdgeInsets.only(bottom: 10), child: _buildDeviceLimitRow(index, item, isDesktop), ); }, ), if (_errorMessage != null) ...[ const SizedBox(height: 10), Text( _errorMessage!, style: AppTextStyles.bodyMedium.copyWith(color: Colors.red), ), ], const SizedBox(height: 22), Wrap( spacing: 10, runSpacing: 10, children: [ SizedBox( width: isDesktop ? 140 : double.infinity, height: 44, child: ElevatedButton( onPressed: _isSubmitting ? null : _submit, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF43C61C), foregroundColor: Colors.white, ), child: _isSubmitting ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Text('تایید'), ), ), SizedBox( width: isDesktop ? 140 : double.infinity, height: 44, child: ElevatedButton( onPressed: _isSubmitting ? null : () => Navigator.pop(context), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFC95757), foregroundColor: Colors.white, ), child: const Text('انصراف'), ), ), ], ), ], ), ), ), ); } Widget _buildDeviceLimitRow(int index, _DeviceLimitForm item, bool isDesktop) { final osItems = const [ DropdownMenuItem(value: 'android', child: Text('android')), DropdownMenuItem(value: 'windows', child: Text('windows')), DropdownMenuItem(value: 'linux', child: Text('linux')), DropdownMenuItem(value: 'ios', child: Text('ios')), ]; if (!isDesktop) { return Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.black12), ), child: Column( children: [ DropdownButtonFormField( initialValue: item.os, decoration: _inputDecoration('os'), items: osItems, onChanged: (v) { if (v == null) return; setState(() { item.os = v; }); }, ), const SizedBox(height: 8), TextField( controller: item.limitController, keyboardType: TextInputType.number, decoration: _inputDecoration('limit (1..10)'), ), const SizedBox(height: 8), Align( alignment: Alignment.centerLeft, child: IconButton( onPressed: _deviceLimits.length > 1 ? () => _removeDeviceLimit(index) : null, icon: const Icon(Icons.delete_outline), ), ), ], ), ); } return Row( children: [ Expanded( flex: 3, child: DropdownButtonFormField( initialValue: item.os, decoration: _inputDecoration('os'), items: osItems, onChanged: (v) { if (v == null) return; setState(() { item.os = v; }); }, ), ), const SizedBox(width: 8), Expanded( flex: 2, child: TextField( controller: item.limitController, keyboardType: TextInputType.number, decoration: _inputDecoration('limit (1..10)'), ), ), const SizedBox(width: 8), IconButton( onPressed: _deviceLimits.length > 1 ? () => _removeDeviceLimit(index) : null, icon: const Icon(Icons.delete_outline), ), ], ); } Widget _buildLabel(String text) { return Text(text, style: AppTextStyles.bodyLarge); } Widget _buildInput( TextEditingController controller, { TextInputType? keyboardType, String? hint, }) { return TextField( controller: controller, keyboardType: keyboardType, decoration: _inputDecoration(hint), ); } InputDecoration _inputDecoration(String? hint) { return InputDecoration( hintText: hint, isDense: true, filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: AppColors.sidebarBg.withValues(alpha: 0.7)), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: AppColors.sidebarBg.withValues(alpha: 0.7)), ), contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), ); } } class _DeviceLimitForm { String os = 'android'; final TextEditingController limitController; _DeviceLimitForm({ String limit = '1', }) : limitController = TextEditingController(text: limit); void dispose() { limitController.dispose(); } }