import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:manager_app/Models/managerContext.dart'; import 'package:manager_app/app_context.dart'; import 'package:manager_app/constants.dart'; import 'package:manager_api_new/api.dart'; import 'package:provider/provider.dart'; class ApiKeysScreen extends StatefulWidget { const ApiKeysScreen({Key? key}) : super(key: key); @override _ApiKeysScreenState createState() => _ApiKeysScreenState(); } class _ApiKeysScreenState extends State { List> _keys = []; bool _loading = true; static String _appTypeName(int? v) { switch (v) { case 0: return 'VisitApp'; case 1: return 'TabletApp'; default: return 'Other'; } } Future _loadKeys(ManagerAppContext ctx) async { try { final response = await ctx.clientAPI!.apiKeyApi!.apiKeyGetApiKeysWithHttpInfo(); if (response.statusCode == 200) { final List json = jsonDecode(utf8.decode(response.bodyBytes)); setState(() { _keys = json.cast>(); _loading = false; }); } } catch (e) { setState(() => _loading = false); } } Future _createKey(ManagerAppContext ctx, String name, ApiKeyAppType appType) async { final response = await ctx.clientAPI!.apiKeyApi!.apiKeyCreateApiKeyWithHttpInfo( CreateApiKeyRequest(name: name, appType: appType), ); if (response.statusCode == 200 || response.statusCode == 201) { final Map json = jsonDecode(utf8.decode(response.bodyBytes)); await _loadKeys(ctx); return json['key'] as String?; } return null; } Future _revokeKey(ManagerAppContext ctx, String id) async { await ctx.clientAPI!.apiKeyApi!.apiKeyRevokeApiKey(id); await _loadKeys(ctx); } void _showCreateDialog(BuildContext context, ManagerAppContext ctx) { final nameCtrl = TextEditingController(); ApiKeyAppType selectedType = ApiKeyAppType.number0; showDialog( context: context, builder: (_) => StatefulBuilder(builder: (ctx2, setLocal) { return AlertDialog( title: const Text('Créer une clé API'), content: Column(mainAxisSize: MainAxisSize.min, children: [ TextField(controller: nameCtrl, decoration: const InputDecoration(labelText: 'Nom')), const SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Type d\'application', style: TextStyle(fontSize: 12, color: Colors.grey)), DropdownButton( value: selectedType, isExpanded: true, items: ApiKeyAppType.values .map((t) => DropdownMenuItem(value: t, child: Text(_appTypeName(t.value)))) .toList(), onChanged: (v) => setLocal(() => selectedType = v!), ), ], ), ]), actions: [ TextButton(onPressed: () => Navigator.pop(ctx2), child: const Text('Annuler')), ElevatedButton( onPressed: () async { Navigator.pop(ctx2); final plainKey = await _createKey(ctx, nameCtrl.text, selectedType); if (plainKey != null && context.mounted) { _showPlainKeyDialog(context, plainKey); } }, child: const Text('Créer'), ), ], ); }), ); } void _showPlainKeyDialog(BuildContext context, String plainKey) { showDialog( context: context, barrierDismissible: false, builder: (_) => AlertDialog( title: const Text('Clé API créée'), content: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Copiez cette clé maintenant — elle ne sera plus affichée.', style: TextStyle(color: Colors.orange, fontWeight: FontWeight.w600), ), const SizedBox(height: 12), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(4), border: Border.all(color: Colors.grey.shade300), ), child: Row(children: [ Expanded(child: SelectableText(plainKey, style: const TextStyle(fontFamily: 'monospace', fontSize: 13))), IconButton( icon: const Icon(Icons.copy, size: 18), tooltip: 'Copier', onPressed: () => Clipboard.setData(ClipboardData(text: plainKey)), ), ]), ), ]), actions: [ ElevatedButton( onPressed: () => Navigator.pop(context), child: const Text('J\'ai copié la clé'), ), ], ), ); } void _confirmRevoke(BuildContext context, ManagerAppContext ctx, Map key) { showDialog( context: context, builder: (_) => AlertDialog( title: const Text('Révoquer la clé API'), content: Text('Révoquer « ${key['name']} » ? Les apps utilisant cette clé perdront l\'accès.'), actions: [ TextButton(onPressed: () => Navigator.pop(context), child: const Text('Annuler')), ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: Colors.red), onPressed: () async { Navigator.pop(context); await _revokeKey(ctx, key['id'] as String); }, child: const Text('Révoquer', style: TextStyle(color: Colors.white)), ), ], ), ); } @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { final ctx = Provider.of(context, listen: false).getContext() as ManagerAppContext; _loadKeys(ctx); }); } @override Widget build(BuildContext context) { final appContext = Provider.of(context); final managerCtx = appContext.getContext() as ManagerAppContext; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Clés API', style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: kPrimaryColor)), ElevatedButton.icon( onPressed: () => _showCreateDialog(context, managerCtx), icon: const Icon(Icons.add), label: const Text('Créer une clé'), ), ], ), const SizedBox(height: 16), if (_loading) const Center(child: CircularProgressIndicator()) else if (_keys.isEmpty) const Center(child: Text('Aucune clé API')) else Expanded( child: SingleChildScrollView( child: DataTable( columns: const [ DataColumn(label: Text('Nom')), DataColumn(label: Text('Type')), DataColumn(label: Text('Créée le')), DataColumn(label: Text('Statut')), DataColumn(label: Text('Actions')), ], rows: _keys.map((key) { final isActive = key['isActive'] as bool? ?? false; final dateRaw = key['dateCreation'] as String?; final date = dateRaw != null ? DateTime.tryParse(dateRaw)?.toLocal() : null; final dateStr = date != null ? '${date.day.toString().padLeft(2, '0')}/${date.month.toString().padLeft(2, '0')}/${date.year}' : '—'; return DataRow(cells: [ DataCell(Text(key['name'] as String? ?? '')), DataCell(Text(_appTypeName(key['appType'] as int?))), DataCell(Text(dateStr)), DataCell(Chip( label: Text(isActive ? 'Active' : 'Révoquée', style: TextStyle(color: isActive ? Colors.green.shade700 : Colors.red.shade700, fontSize: 12)), backgroundColor: isActive ? Colors.green.shade50 : Colors.red.shade50, side: BorderSide(color: isActive ? Colors.green.shade200 : Colors.red.shade200), )), DataCell(isActive ? IconButton( icon: const Icon(Icons.block, color: Colors.red), tooltip: 'Révoquer', onPressed: () => _confirmRevoke(context, managerCtx, key), ) : const SizedBox()), ]); }).toList(), ), ), ), ], ); } }