Update Saqel Platform: 2026-09-05 22:17:15
This commit is contained in:
@@ -0,0 +1,934 @@
|
||||
import 'dart:math' as math;
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../widgets/luxury_widgets.dart';
|
||||
|
||||
/// ==============================================================================
|
||||
/// SAQEL ENTERPRISE (EDTECH 2.0) - VOCATIONAL TRAINING CORPORATION (VTC) HUB
|
||||
/// ==============================================================================
|
||||
///
|
||||
/// بوابة منظومة مؤسسة التدريب المهني (VTC) والتعليم التقني والتدريب المهني:
|
||||
/// 1. دليل الـ 140 مهنة وحرفة موزعة على 8 قطاعات إنتاجية وصناعية وطنية.
|
||||
/// 2. تخصص صيانة وتشخيص المركبات الكهربائية والهجينة (EV & Hybrid Specialist).
|
||||
/// 3. محاكي فحص بطاريات الجهد العالي ونظام العزل (HVIL & BMS 120 FPS Lab).
|
||||
/// 4. نظام الكفايات المهنية (CBT) ومعايير السلامة المهنية للمركبات الكهربائية (NFPA 70E).
|
||||
class VocationalTrainingScreen extends StatefulWidget {
|
||||
const VocationalTrainingScreen({super.key});
|
||||
|
||||
@override
|
||||
State<VocationalTrainingScreen> createState() => _VocationalTrainingScreenState();
|
||||
}
|
||||
|
||||
class _VocationalTrainingScreenState extends State<VocationalTrainingScreen>
|
||||
with SingleTickerProviderStateMixin {
|
||||
int _selectedSectorIndex = 0;
|
||||
int _activeTabIndex = 0; // 0 = 140 Trades Directory, 1 = EV Diagnostic Lab, 2 = CBT Modules
|
||||
|
||||
// EV Lab Parameters
|
||||
double _batterySoc = 82.0; // State of Charge %
|
||||
double _packVoltage = 384.0; // Volts DC
|
||||
double _isolationResistance = 5.2; // Mega-Ohms (Threshold > 0.5 MΩ)
|
||||
bool _hvilInterlockClosed = true; // High Voltage Interlock Loop
|
||||
bool _contactorPrechargeActive = true;
|
||||
String _activeDtcCode = 'P0000: No Faults Detected (Normal Operation)';
|
||||
bool _simulationRunning = true;
|
||||
late final AnimationController _pulseController;
|
||||
|
||||
final List<Map<String, dynamic>> _sectors = [
|
||||
{
|
||||
'title': 'المركبات والطاقة المتجددة',
|
||||
'icon': CupertinoIcons.bolt_car,
|
||||
'color': Color(0xFF10B981),
|
||||
'tradesCount': 18,
|
||||
'featured': 'ميكانيك وتشخيص المركبات الكهربائية والهجينة (EV)',
|
||||
'description': 'فحص أنظمة الجهد العالي، بطاريات الليثيوم، ومحولات التيار (Inverters).',
|
||||
'trades': [
|
||||
'تشخيص وصيانة المركبات الكهربائية والهجينة',
|
||||
'كهرباء وإلكترونيات السيارات الحديثة',
|
||||
'ميكانيك محركات الديزل والبنزين المتقدم',
|
||||
'تركيب وصيانة أنظمة الطاقة الشمسية الكهروضوئية',
|
||||
'صيانة طواحين الرياح وأنظمة التوليد الهجينة',
|
||||
'فحص بطاريات الجهد العالي وإعادة تدوير الخلايا',
|
||||
],
|
||||
},
|
||||
{
|
||||
'title': 'التكنولوجيا والتحول الرقمي',
|
||||
'icon': CupertinoIcons.device_laptop,
|
||||
'color': Color(0xFF00F5D4),
|
||||
'tradesCount': 22,
|
||||
'featured': 'أمن الشبكات والدعم الفني السحابي',
|
||||
'description': 'هندسة الشبكات المحلية، الألياف الضوئية، وصيانة الخوادم والأنظمة الذكية.',
|
||||
'trades': [
|
||||
'فني صيانة شبكات الحاسوب والألياف الضوئية',
|
||||
'الدعم الفني لأنظمة الحوسبة السحابية',
|
||||
'فني صيانة الهواتف الذكية والأجهزة اللوحية',
|
||||
'تشغيل وبرمجة أنظمة التحكم الرقمي (CNC)',
|
||||
'فني تركيب كاميرات المراقبة وأنظمة الأمان الذكية',
|
||||
],
|
||||
},
|
||||
{
|
||||
'title': 'الصناعات الهندسية والميكانيكية',
|
||||
'icon': CupertinoIcons.wrench_fill,
|
||||
'color': Color(0xFFF59E0B),
|
||||
'tradesCount': 28,
|
||||
'featured': 'اللحام وتشكيل المعادن المتقدم (TIG/MIG)',
|
||||
'description': 'الخراطة، تشكيل المعادن، واللحام بالأرغون وخطوط الإنتاج المؤتمتة.',
|
||||
'trades': [
|
||||
'لحام الأنابيب الصناعية وضغط الغاز (TIG/MIG)',
|
||||
'الخراطة والتفريز وتشكيل المعادن المبرمج',
|
||||
'صيانة خطوط الإنتاج والآلات الصناعية',
|
||||
'التمديدات الكهروميكانيكية للمصانع',
|
||||
'تشكيل وهيكلة الألمنيوم والواجهات المعمارية',
|
||||
],
|
||||
},
|
||||
{
|
||||
'title': 'الضيافة والسياحة والفندقة',
|
||||
'icon': CupertinoIcons.building_2_fill,
|
||||
'color': Color(0xFFEC4899),
|
||||
'tradesCount': 16,
|
||||
'featured': 'فنون الطهي وإنتاج الأغذية الفندقية',
|
||||
'description': 'إدارة المكاتب الأمامية، فنون الطهي الدولي، وخدمة الغرف والضيافة.',
|
||||
'trades': [
|
||||
'إنتاج الأغذية والطهي الشرقي والغربي',
|
||||
'فنون الحلويات والمخبوزات الفرنسية',
|
||||
'إدارة المطاعم وخدمة الضيوف الراقية',
|
||||
'الإشراف الداخلي وإدارة الغرف الفندقية',
|
||||
],
|
||||
},
|
||||
{
|
||||
'title': 'الزراعة الحديثة والري الذكي',
|
||||
'icon': CupertinoIcons.leaf_arrow_circlepath,
|
||||
'color': Color(0xFF22C55E),
|
||||
'tradesCount': 14,
|
||||
'featured': 'الزراعة المائية والبيوت البلاستيكية الذكية',
|
||||
'description': 'أنظمة الزراعة بدون تربة (Hydroponics)، الري المحوسب، وتنسيق الحدائق.',
|
||||
'trades': [
|
||||
'تقنيات الزراعة المائية والعمودية المغلقة',
|
||||
'إدارة وصيانة شبكات الري بالتنقيط المؤتمتة',
|
||||
'إنتاج النباتات الطبية والعطرية العضوية',
|
||||
'تنسيق وتصميم الحدائق والمتنزهات الخضراء',
|
||||
],
|
||||
},
|
||||
{
|
||||
'title': 'الصناعات الدوائية والكيميائية',
|
||||
'icon': CupertinoIcons.lab_flask_solid,
|
||||
'color': Color(0xFF8B5CF6),
|
||||
'tradesCount': 12,
|
||||
'featured': 'تشغيل خطوط التعبئة والتغليف الدوائي',
|
||||
'description': 'تطبيق معايير ممارسات التصنيع الجيد (GMP) ومراقبة جودة الإنتاج الكيميائي.',
|
||||
'trades': [
|
||||
'فني تشغيل آلات التصنيع الدوائي المعقم',
|
||||
'مساعد مراقبة الجودة والتحاليل الكيميائية',
|
||||
'صيانة المضخات والمفاعلات الكيميائية',
|
||||
],
|
||||
},
|
||||
{
|
||||
'title': 'الحلاقة والتجميل والعناية',
|
||||
'icon': CupertinoIcons.scissors,
|
||||
'color': Color(0xFFF43F5E),
|
||||
'tradesCount': 15,
|
||||
'featured': 'تصفيف الشعر والتجميل الاحترافي',
|
||||
'description': 'تقنيات العناية بالبشرة، التصميم المسرحي، وإدارة الصالونات الحديثة.',
|
||||
'trades': [
|
||||
'الحلاقة وتصفيف الشعر الرجالي المتقدم',
|
||||
'التجميل وتصفيف الشعر النسائي',
|
||||
'العناية بالبشرة والمساج العلاجي',
|
||||
],
|
||||
},
|
||||
{
|
||||
'title': 'الحرف التقليدية والمشغولات اليدوية',
|
||||
'icon': CupertinoIcons.cube_box_fill,
|
||||
'color': Color(0xFFD97706),
|
||||
'tradesCount': 15,
|
||||
'featured': 'صياغة الذهب والمجوهرات والفسيفساء',
|
||||
'description': 'إحياء التراث الحرفي الأردني، فن الفسيفساء المادبي، وحفر الخشب التراثي.',
|
||||
'trades': [
|
||||
'صياغة الحلي والمجوهرات وتركيب الأحجار الكريمة',
|
||||
'فن الفسيفساء والترميم الأثري الحرفي',
|
||||
'النجارة التراثية والحفر على الخشب والزخرفة',
|
||||
'الخزف والفخار الفني والتشكيل اليدوي',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pulseController = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(seconds: 2),
|
||||
)..repeat(reverse: true);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pulseController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _triggerFault(String faultCode, String description, double rIso, bool hvilState) {
|
||||
setState(() {
|
||||
_activeDtcCode = '$faultCode: $description';
|
||||
_isolationResistance = rIso;
|
||||
_hvilInterlockClosed = hvilState;
|
||||
if (!hvilState || rIso < 0.5) {
|
||||
_contactorPrechargeActive = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _clearFaults() {
|
||||
setState(() {
|
||||
_activeDtcCode = 'P0000: No Faults Detected (Normal Operation)';
|
||||
_isolationResistance = 5.2;
|
||||
_hvilInterlockClosed = true;
|
||||
_contactorPrechargeActive = true;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final activeSector = _sectors[_selectedSectorIndex];
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.darkBackground,
|
||||
appBar: AppBar(
|
||||
backgroundColor: const Color(0xFF091620),
|
||||
elevation: 0,
|
||||
title: Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF10B981).withAlpha(30),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: const Color(0xFF10B981).withAlpha(100)),
|
||||
),
|
||||
child: const Icon(CupertinoIcons.wrench_fill, color: Color(0xFF10B981), size: 18),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'مؤسسة التدريب المهني (VTC Hub)',
|
||||
style: TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w800),
|
||||
),
|
||||
Text(
|
||||
'140 مهنة وحرفة معتمدة • الشراكة الاستراتيجية للتحول المهني',
|
||||
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 11),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Column(
|
||||
children: [
|
||||
// Top Executive Stats Strip
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF0A1E1D),
|
||||
border: Border(bottom: BorderSide(color: Color(0xFF10B981), width: 0.8)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
_buildExecutiveStat('140 مهنة', 'دليل المهن الوزاري', CupertinoIcons.square_grid_2x2_fill),
|
||||
_buildVerticalDivider(),
|
||||
_buildExecutiveStat('8 قطاعات', 'سلاسل القيمة الصناعية', CupertinoIcons.layers_fill),
|
||||
_buildVerticalDivider(),
|
||||
_buildExecutiveStat('EV 384V', 'مختبر فحص الهايبرد والكهرباء', CupertinoIcons.bolt_car_fill),
|
||||
_buildVerticalDivider(),
|
||||
_buildExecutiveStat('CBT Level 3', 'الكفايات والسلامة الدولية', CupertinoIcons.checkmark_seal_fill),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Navigation Tabs (Directory / EV Lab / CBT)
|
||||
Container(
|
||||
color: AppColors.darkSurface,
|
||||
child: Row(
|
||||
children: [
|
||||
_buildTopTabItem(0, 'دليل الـ 140 مهنة (8 قطاعات) 📋', CupertinoIcons.list_bullet),
|
||||
_buildTopTabItem(1, 'مختبر فحص المركبات الكهربائية ⚡', CupertinoIcons.bolt_fill),
|
||||
_buildTopTabItem(2, 'شهادات الكفايات المهنية (CBT) 🎓', CupertinoIcons.rosette),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Tab View Body
|
||||
Expanded(
|
||||
child: IndexedStack(
|
||||
index: _activeTabIndex,
|
||||
children: [
|
||||
_buildTradesDirectoryTab(activeSector),
|
||||
_buildEvDiagnosticLabTab(),
|
||||
_buildCbtCompetenciesTab(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTopTabItem(int index, String label, IconData icon) {
|
||||
final isSelected = _activeTabIndex == index;
|
||||
|
||||
return Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => setState(() => _activeTabIndex = index),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: isSelected ? const Color(0xFF10B981) : Colors.transparent,
|
||||
width: 3,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 16, color: isSelected ? const Color(0xFF10B981) : AppColors.textSecondaryDark),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: isSelected ? Colors.white : AppColors.textSecondaryDark,
|
||||
fontSize: 11.5,
|
||||
fontWeight: isSelected ? FontWeight.w800 : FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildExecutiveStat(String title, String subtitle, IconData icon) {
|
||||
return Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, color: const Color(0xFF10B981), size: 18),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(title, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 12.5)),
|
||||
Text(subtitle, style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 9.5), maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildVerticalDivider() {
|
||||
return Container(
|
||||
width: 1,
|
||||
height: 24,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
color: Colors.white12,
|
||||
);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// TAB 1: 140 VOCATIONAL TRADES DIRECTORY & 8 SECTORS
|
||||
// ===========================================================================
|
||||
Widget _buildTradesDirectoryTab(Map<String, dynamic> activeSector) {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
// Sector Pill Chips Row
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: _sectors.asMap().entries.map((entry) {
|
||||
final idx = entry.key;
|
||||
final sec = entry.value;
|
||||
final isSelected = _selectedSectorIndex == idx;
|
||||
final secColor = sec['color'] as Color;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => setState(() => _selectedSectorIndex = idx),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(left: 8),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? secColor.withAlpha(40) : AppColors.darkSurface,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
border: Border.all(
|
||||
color: isSelected ? secColor : AppColors.darkCardBorder,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(sec['icon'] as IconData, size: 15, color: isSelected ? secColor : Colors.white70),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
sec['title'] as String,
|
||||
style: TextStyle(
|
||||
color: isSelected ? Colors.white : Colors.white70,
|
||||
fontWeight: isSelected ? FontWeight.w800 : FontWeight.w500,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: secColor.withAlpha(60),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(
|
||||
'${sec['tradesCount']}',
|
||||
style: TextStyle(color: secColor, fontSize: 10, fontWeight: FontWeight.w900),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Featured Specialization Spotlight Card
|
||||
Container(
|
||||
padding: const EdgeInsets.all(18),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
(activeSector['color'] as Color).withAlpha(40),
|
||||
const Color(0xFF0F1A28),
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: (activeSector['color'] as Color).withAlpha(120), width: 1.5),
|
||||
boxShadow: const [
|
||||
BoxShadow(color: Colors.black45, blurRadius: 16, offset: Offset(0, 6)),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: (activeSector['color'] as Color).withAlpha(40),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(activeSector['icon'] as IconData, color: activeSector['color'] as Color, size: 22),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
activeSector['title'] as String,
|
||||
style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w800),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
'التخصص الاستراتيجي: ${activeSector['featured']}',
|
||||
style: TextStyle(color: activeSector['color'] as Color, fontSize: 12, fontWeight: FontWeight.w700),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
activeSector['description'] as String,
|
||||
style: const TextStyle(color: Colors.white70, fontSize: 12.5, height: 1.5),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Trades List
|
||||
const Text(
|
||||
'المهن والحرف المعتمدة في هذا القطاع:',
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 13.5),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
...((activeSector['trades'] as List<String>).asMap().entries.map((tEntry) {
|
||||
final idx = tEntry.key + 1;
|
||||
final tradeTitle = tEntry.value;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.darkSurface,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
border: Border.all(color: AppColors.darkCardBorder),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 28,
|
||||
height: 28,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withAlpha(10),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
'$idx',
|
||||
style: const TextStyle(color: AppColors.saqelCyan, fontSize: 12, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
tradeTitle,
|
||||
style: const TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF10B981).withAlpha(20),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: const Color(0xFF10B981).withAlpha(80)),
|
||||
),
|
||||
child: const Text('دبلوم مهني VTC', style: TextStyle(color: Color(0xFF10B981), fontSize: 10, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList()),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// TAB 2: INTERACTIVE EV & HYBRID DIAGNOSTIC VIRTUAL LAB (120 FPS)
|
||||
// ===========================================================================
|
||||
Widget _buildEvDiagnosticLabTab() {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
// Live Diagnostics Canvas (Battery Pack & Inverter)
|
||||
Container(
|
||||
height: 280,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF070E14),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: _activeDtcCode.startsWith('P0000') ? const Color(0xFF10B981).withAlpha(80) : const Color(0xFFFF2D55),
|
||||
width: 1.5,
|
||||
),
|
||||
boxShadow: const [
|
||||
BoxShadow(color: Colors.black54, blurRadius: 16, offset: Offset(0, 6)),
|
||||
],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(19),
|
||||
child: CustomPaint(
|
||||
painter: _EvDiagnosticsPainter(
|
||||
batterySoc: _batterySoc,
|
||||
packVoltage: _packVoltage,
|
||||
isolationResistance: _isolationResistance,
|
||||
hvilClosed: _hvilInterlockClosed,
|
||||
isFaulted: !_activeDtcCode.startsWith('P0000'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
|
||||
// Live Diagnostic Telemetry Grid
|
||||
Row(
|
||||
children: [
|
||||
_buildTelemetryCard(
|
||||
'حالة الشحن (SoC)',
|
||||
'${_batterySoc.toInt()}%',
|
||||
const Color(0xFF10B981),
|
||||
CupertinoIcons.battery_75_percent,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_buildTelemetryCard(
|
||||
'جهد البطارية (Pack V)',
|
||||
'${_packVoltage.toInt()} V',
|
||||
AppColors.saqelCyan,
|
||||
CupertinoIcons.bolt_fill,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_buildTelemetryCard(
|
||||
'مقاومة العزل (R_iso)',
|
||||
'${_isolationResistance.toStringAsFixed(1)} MΩ',
|
||||
_isolationResistance >= 0.5 ? const Color(0xFF10B981) : const Color(0xFFFF2D55),
|
||||
CupertinoIcons.shield_lefthalf_fill,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_buildTelemetryCard(
|
||||
'حلقة القفل (HVIL)',
|
||||
_hvilInterlockClosed ? 'مغلقة (آمن)' : 'مفتوحة (خطر)',
|
||||
_hvilInterlockClosed ? const Color(0xFF10B981) : const Color(0xFFFF2D55),
|
||||
CupertinoIcons.lock_shield_fill,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
|
||||
// Active DTC Code Box
|
||||
Container(
|
||||
padding: const EdgeInsets.all(14),
|
||||
decoration: BoxDecoration(
|
||||
color: _activeDtcCode.startsWith('P0000') ? const Color(0xFF0F261E) : const Color(0xFF330E14),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: _activeDtcCode.startsWith('P0000') ? const Color(0xFF10B981) : const Color(0xFFFF2D55),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
_activeDtcCode.startsWith('P0000') ? CupertinoIcons.check_mark_circled_solid : CupertinoIcons.exclamationmark_triangle_fill,
|
||||
color: _activeDtcCode.startsWith('P0000') ? const Color(0xFF10B981) : const Color(0xFFFF2D55),
|
||||
size: 22,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('كود التشخيص والأعطال (OBD-II DTC Readout):', style: TextStyle(color: Colors.white70, fontSize: 11)),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
_activeDtcCode,
|
||||
style: TextStyle(
|
||||
color: _activeDtcCode.startsWith('P0000') ? const Color(0xFF10B981) : const Color(0xFFFF2D55),
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
|
||||
// Simulated Fault Injection Controls
|
||||
LuxuryCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('حقن الأعطال المخبرية (Fault Injection Simulation):', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 13)),
|
||||
const SizedBox(height: 4),
|
||||
const Text('اختر عطلاً كهربائياً لاختبار استجابة نظام إدارة البطارية (BMS) وفصل قواطع الجهد العالي:', style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 11)),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
OutlinedButton.icon(
|
||||
icon: const Icon(CupertinoIcons.shield_slash, color: Color(0xFFFF2D55), size: 16),
|
||||
label: const Text('عطل تسريب العزل (P0AA6 - Loss of Isolation)', style: TextStyle(color: Colors.white, fontSize: 11.5)),
|
||||
style: OutlinedButton.styleFrom(side: const BorderSide(color: Color(0xFFFF2D55))),
|
||||
onPressed: () => _triggerFault('P0AA6', 'Hybrid Battery Voltage Isolation Fault (Leakage < 0.5 MΩ)', 0.1, true),
|
||||
),
|
||||
OutlinedButton.icon(
|
||||
icon: const Icon(CupertinoIcons.lock_slash_fill, color: Color(0xFFF59E0B), size: 16),
|
||||
label: const Text('قطع حلقة الأمان (P0A0D - HVIL Open Circuit)', style: TextStyle(color: Colors.white, fontSize: 11.5)),
|
||||
style: OutlinedButton.styleFrom(side: const BorderSide(color: Color(0xFFF59E0B))),
|
||||
onPressed: () => _triggerFault('P0A0D', 'High Voltage System Interlock Circuit Open (HVIL Broken)', 5.2, false),
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
icon: const Icon(CupertinoIcons.arrow_counterclockwise, size: 16),
|
||||
label: const Text('إعادة ضبط وتشغيل النظام السليم (Clear DTC)', style: TextStyle(fontSize: 11.5, fontWeight: FontWeight.bold)),
|
||||
style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFF10B981), foregroundColor: Colors.black),
|
||||
onPressed: _clearFaults,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTelemetryCard(String label, String value, Color color, IconData icon) {
|
||||
return Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.darkSurface,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
border: Border.all(color: AppColors.darkCardBorder),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(icon, color: color, size: 18),
|
||||
const SizedBox(height: 4),
|
||||
Text(value, style: TextStyle(color: color, fontWeight: FontWeight.w800, fontSize: 13.5)),
|
||||
const SizedBox(height: 2),
|
||||
Text(label, style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 9.5), textAlign: TextAlign.center, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// TAB 3: CBT COMPETENCIES & CERTIFICATION STANDARDS
|
||||
// ===========================================================================
|
||||
Widget _buildCbtCompetenciesTab() {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
// Level 3 Certification Banner
|
||||
Container(
|
||||
padding: const EdgeInsets.all(18),
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(
|
||||
colors: [Color(0xFF132A1C), Color(0xFF0F1A28)],
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: const Color(0xFF10B981), width: 1.5),
|
||||
),
|
||||
child: const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(CupertinoIcons.rosette, color: Color(0xFF10B981), size: 24),
|
||||
SizedBox(width: 10),
|
||||
Text(
|
||||
'نظام التدريب المبني على الكفايات (CBT Level 3)',
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 15),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Text(
|
||||
'برنامج معتمد دولياً لتأهيل فنيي المركبات الكهربائية والهجينة وفق معايير السلامة المهنية العالمية (NFPA 70E و ISO 26262 و ECE-R100). يشترط إتقان 5 كفايات إلزامية لنيل رخصة الفحص الميداني.',
|
||||
style: TextStyle(color: Colors.white70, fontSize: 12.5, height: 1.5),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
_buildCbtModuleCard(
|
||||
'الكفاية 1: إجراءات السلامة وفصل الجهد العالي (HV De-energization)',
|
||||
'التحقق من خلو الجهد الكهربائي (Zero Voltage Verification) واستخدام قفازات الفئة Class 0 (1000V).',
|
||||
'95% إتقان مطلوب',
|
||||
CupertinoIcons.shield_fill,
|
||||
const Color(0xFF10B981),
|
||||
),
|
||||
_buildCbtModuleCard(
|
||||
'الكفاية 2: تشخيص خلايا بطارية الجهد العالي (BMS Cell Balancing)',
|
||||
'قراءة قيم الجهد لكل خلية (3.2V - 4.2V) وفحص مقاومة العزل وتوازن درجات الحرارة.',
|
||||
'معتمد دولياً',
|
||||
CupertinoIcons.battery_full,
|
||||
AppColors.saqelCyan,
|
||||
),
|
||||
_buildCbtModuleCard(
|
||||
'الكفاية 3: فحص محول التيار والمحرك الكهربائي (Inverter & Motor)',
|
||||
'فحص إشارات التردد العالي (PWM Gate Signals) وقياس مقاومة أطوار المحرك الثلاثية (U-V-W).',
|
||||
'مستوى متقدم',
|
||||
CupertinoIcons.bolt_horizontal_fill,
|
||||
const Color(0xFFF59E0B),
|
||||
),
|
||||
_buildCbtModuleCard(
|
||||
'الكفاية 4: بروتوكول حلقة الأمان وقفل الفحص (HVIL Loop Diagnostics)',
|
||||
'كشف انقطاع أسلاك التوصيل السريع وإشارات تأكيد إغلاق غطاء مقبس الخدمة (Service Plug).',
|
||||
'إلزامي للفحص',
|
||||
CupertinoIcons.lock_shield,
|
||||
const Color(0xFF8B5CF6),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCbtModuleCard(String title, String desc, String badge, IconData icon, Color color) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.darkSurface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: AppColors.darkCardBorder),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withAlpha(30),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Icon(icon, color: color, size: 20),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 13),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withAlpha(20),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: color.withAlpha(80)),
|
||||
),
|
||||
child: Text(badge, style: TextStyle(color: color, fontSize: 10.5, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(desc, style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 12, height: 1.4)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// EV DIAGNOSTICS CUSTOM PAINTER (120 FPS HIGH-VOLTAGE ARCHITECTURE)
|
||||
// =============================================================================
|
||||
class _EvDiagnosticsPainter extends CustomPainter {
|
||||
final double batterySoc;
|
||||
final double packVoltage;
|
||||
final double isolationResistance;
|
||||
final bool hvilClosed;
|
||||
final bool isFaulted;
|
||||
|
||||
_EvDiagnosticsPainter({
|
||||
required this.batterySoc,
|
||||
required this.packVoltage,
|
||||
required this.isolationResistance,
|
||||
required this.hvilClosed,
|
||||
required this.isFaulted,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final w = size.width;
|
||||
final h = size.height;
|
||||
final cy = h / 2;
|
||||
|
||||
// Watermark
|
||||
const watermarkSpan = TextSpan(
|
||||
text: 'منصة صَقِل • مختبر فحص المركبات الكهربائية VTC EV Lab',
|
||||
style: TextStyle(color: Color(0x3300F5D4), fontSize: 11, fontWeight: FontWeight.bold),
|
||||
);
|
||||
final watermarkPainter = TextPainter(text: watermarkSpan, textDirection: TextDirection.rtl)..layout();
|
||||
watermarkPainter.paint(canvas, const Offset(12, 12));
|
||||
|
||||
// 1. High Voltage Battery Pack Silhouette
|
||||
final bRect = RRect.fromRectAndRadius(
|
||||
Rect.fromLTWH(24, cy - 65, w * 0.40, 130),
|
||||
const Radius.circular(16),
|
||||
);
|
||||
canvas.drawRRect(bRect, Paint()..color = const Color(0xFF0F1E29));
|
||||
canvas.drawRRect(bRect, Paint()..color = const Color(0xFF10B981)..style = PaintingStyle.stroke..strokeWidth = 2);
|
||||
|
||||
// Battery Pack label
|
||||
const bSpan = TextSpan(
|
||||
text: 'بطارية الجهد العالي (384V DC)\nLi-Ion NMC Battery Pack',
|
||||
style: TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.bold),
|
||||
);
|
||||
final btp = TextPainter(text: bSpan, textDirection: TextDirection.rtl)..layout();
|
||||
btp.paint(canvas, Offset(36, cy - 45));
|
||||
|
||||
// SoC Bar inside pack
|
||||
final socW = (w * 0.40 - 24) * (batterySoc / 100.0);
|
||||
canvas.drawRRect(
|
||||
RRect.fromRectAndRadius(Rect.fromLTWH(36, cy + 20, socW, 16), const Radius.circular(6)),
|
||||
Paint()..color = const Color(0xFF10B981),
|
||||
);
|
||||
|
||||
// 2. Inverter & Motor Unit
|
||||
final invRect = RRect.fromRectAndRadius(
|
||||
Rect.fromLTWH(w * 0.60, cy - 65, w * 0.34, 130),
|
||||
const Radius.circular(16),
|
||||
);
|
||||
canvas.drawRRect(invRect, Paint()..color = const Color(0xFF1E1A2E));
|
||||
canvas.drawRRect(
|
||||
invRect,
|
||||
Paint()
|
||||
..color = isFaulted ? const Color(0xFFFF2D55) : const Color(0xFF8B5CF6)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 2,
|
||||
);
|
||||
|
||||
final invSpan = TextSpan(
|
||||
text: 'محول القدرة والمحرك (Inverter)\nAC 3-Phase Permanent Magnet',
|
||||
style: TextStyle(color: isFaulted ? const Color(0xFFFF2D55) : Colors.white, fontSize: 11, fontWeight: FontWeight.bold),
|
||||
);
|
||||
final itp = TextPainter(text: invSpan, textDirection: TextDirection.rtl)..layout();
|
||||
itp.paint(canvas, Offset(w * 0.60 + 12, cy - 45));
|
||||
|
||||
// 3. High-Voltage Bus Cables (Orange Cables +HV and -HV)
|
||||
final hvCableColor = isFaulted ? const Color(0xFFFF2D55) : const Color(0xFFFF7A00); // Standard Orange EV Cable
|
||||
final cablePaint = Paint()..color = hvCableColor..strokeWidth = 4..strokeCap = StrokeCap.round;
|
||||
|
||||
// +HV Cable (Top)
|
||||
canvas.drawLine(Offset(24 + w * 0.40, cy - 20), Offset(w * 0.60, cy - 20), cablePaint);
|
||||
// -HV Cable (Bottom)
|
||||
canvas.drawLine(Offset(24 + w * 0.40, cy + 20), Offset(w * 0.60, cy + 20), cablePaint);
|
||||
|
||||
// Cable Voltage Badge
|
||||
final voltSpan = TextSpan(
|
||||
text: isFaulted ? '⚠️ جهد معزول تلقائياً' : '+384V DC Bus',
|
||||
style: TextStyle(color: hvCableColor, fontSize: 10, fontWeight: FontWeight.w800),
|
||||
);
|
||||
final vtp = TextPainter(text: voltSpan, textDirection: TextDirection.ltr)..layout();
|
||||
vtp.paint(canvas, Offset(w * 0.46, cy - 36));
|
||||
|
||||
// 4. HVIL Interlock Loop Indicator
|
||||
final hvilColor = hvilClosed ? const Color(0xFF10B981) : const Color(0xFFFF2D55);
|
||||
final hvilPaint = Paint()..color = hvilColor..strokeWidth = 2..style = PaintingStyle.stroke;
|
||||
canvas.drawCircle(Offset(w * 0.50, cy + 45), 8, hvilPaint);
|
||||
canvas.drawCircle(Offset(w * 0.50, cy + 45), 4, Paint()..color = hvilColor);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _EvDiagnosticsPainter oldDelegate) => true;
|
||||
}
|
||||
Reference in New Issue
Block a user