import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:manager_app/l10n/app_localizations.dart'; import 'package:manager_app/Models/managerContext.dart'; import 'package:manager_app/app_context.dart'; import 'package:manager_app/Components/common_loader.dart'; import 'package:manager_app/constants.dart'; import 'package:provider/provider.dart'; class UsersScreen extends StatefulWidget { const UsersScreen({Key? key}) : super(key: key); @override _UsersScreenState createState() => _UsersScreenState(); } class _UsersScreenState extends State { List> _users = []; bool _loading = true; static const _roleNames = ['SuperAdmin', 'InstanceAdmin', 'ContentEditor', 'Viewer']; static String _roleName(dynamic v) { if (v is String) return v; final i = v as int?; if (i != null && i >= 0 && i < _roleNames.length) return _roleNames[i]; return '—'; } static int _roleToInt(dynamic v) { if (v is int) return v; if (v is String) { final i = _roleNames.indexOf(v); return i >= 0 ? i : 3; } return 3; } List _allowedRoles(int callerRoleValue) => [0, 1, 2, 3].where((r) => r >= callerRoleValue).toList(); Future _loadUsers(ManagerAppContext ctx) async { try { final response = await ctx.clientAPI!.userApi!.userGetWithHttpInfo(); if (response.statusCode == 200) { final List json = jsonDecode(utf8.decode(response.bodyBytes)); setState(() { _users = json.cast>(); _loading = false; }); } } catch (e) { setState(() => _loading = false); } } Future _createUser(ManagerAppContext ctx, String email, String firstName, String lastName, String password, int roleValue) async { final body = { 'email': email, 'firstName': firstName, 'lastName': lastName, 'password': password, 'role': roleValue, }; await ctx.clientAPI!.apiApi!.invokeAPI( '/api/User', 'POST', [], body, {}, {}, 'application/json'); await _loadUsers(ctx); } Future _updateUser(ManagerAppContext ctx, String id, String firstName, String lastName, int roleValue) async { final body = { 'id': id, 'firstName': firstName, 'lastName': lastName, 'role': roleValue, }; await ctx.clientAPI!.apiApi!.invokeAPI( '/api/User', 'PUT', [], body, {}, {}, 'application/json'); await _loadUsers(ctx); } Future _deleteUser(ManagerAppContext ctx, String id) async { await ctx.clientAPI!.userApi!.userDeleteUser(id); await _loadUsers(ctx); } void _showCreateDialog(BuildContext context, ManagerAppContext ctx) { final l = AppLocalizations.of(context)!; final callerRole = _roleToInt(ctx.role?.value); final emailCtrl = TextEditingController(); final firstCtrl = TextEditingController(); final lastCtrl = TextEditingController(); final passCtrl = TextEditingController(); int selectedRole = callerRole; showDialog( context: context, builder: (_) => StatefulBuilder(builder: (ctx2, setLocal) { return AlertDialog( title: Text(l.createUserTitle), content: SingleChildScrollView( child: Column(mainAxisSize: MainAxisSize.min, children: [ TextField(controller: emailCtrl, decoration: InputDecoration(labelText: l.email)), TextField(controller: firstCtrl, decoration: InputDecoration(labelText: l.firstName)), TextField(controller: lastCtrl, decoration: InputDecoration(labelText: l.lastName)), TextField(controller: passCtrl, obscureText: true, decoration: InputDecoration(labelText: l.password)), const SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(l.role, style: const TextStyle(fontSize: 12, color: Colors.grey)), DropdownButton( value: selectedRole, isExpanded: true, items: _allowedRoles(callerRole) .map((r) => DropdownMenuItem(value: r, child: Text(_roleName(r)))) .toList(), onChanged: (v) => setLocal(() => selectedRole = v!), ), ], ), ]), ), actions: [ TextButton(onPressed: () => Navigator.pop(ctx2), child: Text(l.cancel)), ElevatedButton( onPressed: () async { Navigator.pop(ctx2); await _createUser(ctx, emailCtrl.text, firstCtrl.text, lastCtrl.text, passCtrl.text, selectedRole); }, child: Text(l.create), ), ], ); }), ); } void _showEditDialog(BuildContext context, ManagerAppContext ctx, Map user) { final l = AppLocalizations.of(context)!; final callerRole = _roleToInt(ctx.role?.value); final firstCtrl = TextEditingController(text: user['firstName'] as String? ?? ''); final lastCtrl = TextEditingController(text: user['lastName'] as String? ?? ''); int selectedRole = _roleToInt(user['role']); showDialog( context: context, builder: (_) => StatefulBuilder(builder: (ctx2, setLocal) { return AlertDialog( title: Text(l.editUserTitle), content: SingleChildScrollView( child: Column(mainAxisSize: MainAxisSize.min, children: [ TextField(controller: firstCtrl, decoration: InputDecoration(labelText: l.firstName)), TextField(controller: lastCtrl, decoration: InputDecoration(labelText: l.lastName)), const SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(l.role, style: const TextStyle(fontSize: 12, color: Colors.grey)), DropdownButton( value: selectedRole, isExpanded: true, items: _allowedRoles(callerRole) .map((r) => DropdownMenuItem(value: r, child: Text(_roleName(r)))) .toList(), onChanged: (v) => setLocal(() => selectedRole = v!), ), ], ), ]), ), actions: [ TextButton(onPressed: () => Navigator.pop(ctx2), child: Text(l.cancel)), ElevatedButton( onPressed: () async { Navigator.pop(ctx2); await _updateUser(ctx, user['id'] as String, firstCtrl.text, lastCtrl.text, selectedRole); }, child: Text(l.save), ), ], ); }), ); } void _confirmDelete(BuildContext context, ManagerAppContext ctx, Map user) { final l = AppLocalizations.of(context)!; showDialog( context: context, builder: (_) => AlertDialog( title: Text(l.deleteUserTitle), content: Text(l.deleteUserConfirm(user['email'] as String? ?? '')), actions: [ TextButton(onPressed: () => Navigator.pop(context), child: Text(l.cancel)), ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: Colors.red), onPressed: () async { Navigator.pop(context); await _deleteUser(ctx, user['id'] as String); }, child: Text(l.delete, style: const TextStyle(color: Colors.white)), ), ], ), ); } @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { final ctx = Provider.of(context, listen: false).getContext() as ManagerAppContext; _loadUsers(ctx); }); } static Color _roleColor(dynamic v) { switch (_roleToInt(v)) { case 0: return Colors.purple; case 1: return Colors.blue; case 2: return Colors.teal; default: return Colors.grey; } } @override Widget build(BuildContext context) { final l = AppLocalizations.of(context)!; final appContext = Provider.of(context); final managerCtx = appContext.getContext() as ManagerAppContext; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(l.usersTitle, style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: kPrimaryColor)), ElevatedButton.icon( onPressed: () => _showCreateDialog(context, managerCtx), icon: const Icon(Icons.add), label: Text(l.createUserBtn), ), ], ), const SizedBox(height: 16), if (_loading) const CommonLoader() else if (_users.isEmpty) Center(child: Text(l.noUsers)) else Expanded( child: Card( elevation: 0, color: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: Colors.grey.shade200), ), clipBehavior: Clip.antiAlias, child: SingleChildScrollView( child: SizedBox( width: double.infinity, child: DataTable( horizontalMargin: 16, columnSpacing: 24, headingRowColor: WidgetStateProperty.all(Colors.grey.shade50), dividerThickness: 1, columns: [ DataColumn(label: Text(l.email, style: const TextStyle(fontWeight: FontWeight.w600))), DataColumn(label: Text(l.firstName, style: const TextStyle(fontWeight: FontWeight.w600))), DataColumn(label: Text(l.lastName, style: const TextStyle(fontWeight: FontWeight.w600))), DataColumn(label: Text(l.role, style: const TextStyle(fontWeight: FontWeight.w600))), DataColumn(label: Text(l.actions, style: const TextStyle(fontWeight: FontWeight.w600))), ], rows: _users.map((user) { final roleColor = _roleColor(user['role']); return DataRow(cells: [ DataCell(Text(user['email'] as String? ?? '')), DataCell(Text(user['firstName'] as String? ?? '')), DataCell(Text(user['lastName'] as String? ?? '')), DataCell(Chip( label: Text( _roleName(user['role']), style: TextStyle(color: roleColor, fontSize: 12), ), backgroundColor: roleColor.withValues(alpha: 0.08), side: BorderSide(color: roleColor.withValues(alpha: 0.3)), padding: const EdgeInsets.symmetric(horizontal: 4), visualDensity: VisualDensity.compact, )), DataCell(Row(children: [ IconButton( icon: Icon(Icons.edit, color: kPrimaryColor, size: 20), tooltip: l.tooltipEdit, onPressed: () => _showEditDialog(context, managerCtx, user), ), IconButton( icon: const Icon(Icons.delete_outline, color: Colors.red, size: 20), tooltip: l.tooltipDelete, onPressed: () => _confirmDelete(context, managerCtx, user), ), ])), ]); }).toList(), ), ), ), ), ), ], ); } }