Update Saqel Platform: 2026-09-08 17:30:46
This commit is contained in:
@@ -75,6 +75,28 @@ class SuperAdminRepository {
|
||||
return _decode(response)['data'];
|
||||
}
|
||||
|
||||
Future<dynamic> _post(String path, Map<String, dynamic> body) async {
|
||||
final response = await http.post(Uri.parse('${AppConfig.apiBaseUrl}$path'), headers: {
|
||||
...await _headers(), 'Content-Type': 'application/json',
|
||||
}, body: json.encode(body)).timeout(const Duration(seconds: 20));
|
||||
return _decode(response)['data'];
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> getSchools() async =>
|
||||
(await _get('/api/super-admin/schools') as List).map((e) => Map<String, dynamic>.from(e as Map)).toList();
|
||||
|
||||
Future<List<Map<String, dynamic>>> getStaff() async =>
|
||||
(await _get('/api/super-admin/staff') as List).map((e) => Map<String, dynamic>.from(e as Map)).toList();
|
||||
|
||||
Future<void> saveSchool(Map<String, dynamic> body) async { await _post('/api/super-admin/schools/save', body); }
|
||||
Future<void> toggleSchool(int id) async { await _post('/api/super-admin/schools/toggle', {'id': id}); }
|
||||
Future<void> saveStaff(Map<String, dynamic> body) async { await _post('/api/super-admin/staff/save', body); }
|
||||
Future<void> toggleStaff(int id) async { await _post('/api/super-admin/staff/toggle', {'id': id}); }
|
||||
Future<List<Map<String, dynamic>>> getDirectorates() async =>
|
||||
(await _get('/api/super-admin/directorates') as List).map((e) => Map<String, dynamic>.from(e as Map)).toList();
|
||||
Future<void> saveDirectorate(Map<String, dynamic> body) async { await _post('/api/super-admin/directorates/save', body); }
|
||||
Future<void> toggleDirectorate(int id) async { await _post('/api/super-admin/directorates/toggle', {'id': id}); }
|
||||
|
||||
Future<MacroTelemetryModel> getMacroTelemetry() async =>
|
||||
MacroTelemetryModel.fromJson(Map<String, dynamic>.from(await _get('/api/super-admin/overview') as Map));
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'tabs/macro_radar_tab.dart';
|
||||
import 'tabs/ai_cluster_tab.dart';
|
||||
import 'tabs/treasury_cliq_tab.dart';
|
||||
import 'tabs/security_integrity_tab.dart';
|
||||
import 'tabs/organization_tab.dart';
|
||||
|
||||
/**
|
||||
* ==============================================================================
|
||||
@@ -158,6 +159,7 @@ class _SuperAdminShellState extends State<SuperAdminShell> {
|
||||
alerts: state.alerts,
|
||||
isKillSwitchActive: state.isEmergencyKillSwitchActive,
|
||||
),
|
||||
const OrganizationTab(),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -201,6 +203,11 @@ class _SuperAdminShellState extends State<SuperAdminShell> {
|
||||
activeIcon: Icon(CupertinoIcons.shield_lefthalf_fill, color: SuperAdminTheme.royalGold),
|
||||
label: 'النزاهة والأمان',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(CupertinoIcons.building_2_fill),
|
||||
activeIcon: Icon(CupertinoIcons.building_2_fill, color: SuperAdminTheme.royalGold),
|
||||
label: 'الهيكل الإداري',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import '../../../data/repositories/super_admin_repository.dart';
|
||||
import '../../../core/theme/super_admin_theme.dart';
|
||||
|
||||
class OrganizationTab extends StatefulWidget { const OrganizationTab({super.key}); @override State<OrganizationTab> createState()=>_OrganizationTabState(); }
|
||||
class _OrganizationTabState extends State<OrganizationTab> {
|
||||
final repo=SuperAdminRepository(); final search=TextEditingController(); String role='all';
|
||||
List<Map<String,dynamic>> schools=[], staff=[]; bool loading=true;
|
||||
@override void initState(){super.initState(); _load();}
|
||||
Future<void> _load() async { setState(()=>loading=true); try { final r=await Future.wait([repo.getSchools(),repo.getStaff()]); if(mounted)setState((){schools=r[0];staff=r[1];loading=false;}); } catch(_){if(mounted)setState(()=>loading=false);}}
|
||||
void _showMap(Map<String,dynamic> s) { final lat=(s['latitude'] as num?)?.toDouble()??31.9539, lng=(s['longitude'] as num?)?.toDouble()??35.9106; showDialog(context:context,builder:(_)=>AlertDialog(backgroundColor:SuperAdminTheme.surfaceDark,title:Text('${s['name']}',style:const TextStyle(color:Colors.white)),content:SizedBox(width:500,height:380,child:FlutterMap(options:MapOptions(initialCenter:LatLng(lat,lng),initialZoom:13),children:[TileLayer(urlTemplate:'https://tile.openstreetmap.org/{z}/{x}/{y}.png',userAgentPackageName:'com.saqel.super_admin'),MarkerLayer(markers:[Marker(point:LatLng(lat,lng),width:42,height:42,child:const Icon(Icons.location_pin,color:Colors.red,size:42))])])),actions:[TextButton(onPressed:()=>Navigator.pop(context),child:const Text('إغلاق'))])); }
|
||||
@override Widget build(BuildContext c){ final q=search.text.trim().toLowerCase(); final filtered=staff.where((x)=> (role=='all'||x['role']==role) && (q.isEmpty||'${x['full_name']} ${x['role']}'.toLowerCase().contains(q))).toList(); return RefreshIndicator(onRefresh:_load,child:ListView(padding:const EdgeInsets.all(16),children:[Row(children:[const Expanded(child:Text('إدارة الهيكل الإداري',style:TextStyle(color:Colors.white,fontSize:20,fontWeight:FontWeight.bold))),IconButton(onPressed:_load,icon:const Icon(Icons.refresh,color:SuperAdminTheme.cyberCyan))]),const SizedBox(height:12),TextField(controller:search,onChanged:(_)=>setState((){}),style:const TextStyle(color:Colors.white),decoration:const InputDecoration(labelText:'بحث بالاسم أو الدور',labelStyle:TextStyle(color:Colors.white60),prefixIcon:Icon(Icons.search,color:SuperAdminTheme.cyberCyan))),const SizedBox(height:8),DropdownButton<String>(value:role,isExpanded:true,dropdownColor:SuperAdminTheme.surfaceDark,style:const TextStyle(color:Colors.white),items:const [DropdownMenuItem(value:'all',child:Text('كل الأدوار')),DropdownMenuItem(value:'super_admin',child:Text('Super Admin')),DropdownMenuItem(value:'school_admin',child:Text('مدير مدرسة')),DropdownMenuItem(value:'directorate_admin',child:Text('مدير مديرية')),DropdownMenuItem(value:'supervisor',child:Text('مشرف'))],onChanged:(v)=>setState(()=>role=v??'all')),Text('المدارس (${schools.length})',style:const TextStyle(color:SuperAdminTheme.royalGold,fontWeight:FontWeight.bold)),...schools.map((s)=>ListTile(onTap:()=>_showMap(s),leading:const Icon(Icons.location_on,color:SuperAdminTheme.cyberCyan),title:Text('${s['name']}',style:const TextStyle(color:Colors.white)),subtitle:Text('${s['code']} • ${s['is_active']==1?'نشطة':'موقوفة'} • اضغط لعرض الخريطة',style:const TextStyle(color:Colors.white60)))),const SizedBox(height:12),Text('الحسابات (${filtered.length})',style:const TextStyle(color:SuperAdminTheme.royalGold,fontWeight:FontWeight.bold)),if(loading)const Center(child:CircularProgressIndicator()) else ...filtered.map((s)=>ListTile(leading:const Icon(Icons.badge,color:SuperAdminTheme.cyberCyan),title:Text('${s['full_name']}',style:const TextStyle(color:Colors.white)),subtitle:Text('${s['role']} • ${s['status']}',style:const TextStyle(color:Colors.white60))))])); }
|
||||
}
|
||||
Reference in New Issue
Block a user