feat: type schedule and auth models

This commit is contained in:
Artem Kokos
2026-04-23 20:57:15 +07:00
parent fa403bfcce
commit 0fdaf0bac4
11 changed files with 531 additions and 243 deletions

View File

@@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../app/error_message.dart';
import '../app/load_state.dart';
import '../models/api_key_info.dart';
import '../providers/providers.dart';
import '../widgets/load_error_view.dart';
@@ -50,7 +51,10 @@ class _ApiKeysScreenState extends ConsumerState<ApiKeysScreen> {
);
}
Widget _buildContent(LoadState<List<dynamic>> keysState, List<dynamic> keys) {
Widget _buildContent(
LoadState<List<ApiKeyInfo>> keysState,
List<ApiKeyInfo> keys,
) {
if ((keysState.isIdle || keysState.isLoading) && keys.isEmpty) {
return const Center(
child: CircularProgressIndicator(color: Colors.deepOrange),
@@ -103,13 +107,10 @@ class _ApiKeysScreenState extends ConsumerState<ApiKeysScreen> {
final keyIndex = index - statusHeaderCount;
final key = keys[keyIndex];
final map = key is Map
? Map<String, dynamic>.from(key)
: <String, dynamic>{};
return _ApiKeyCard(
data: map,
onRevoke: () => _revokeKey(map),
onActivate: () => _activateKey(map),
data: key,
onRevoke: () => _revokeKey(key),
onActivate: () => _activateKey(key),
);
},
),
@@ -185,14 +186,12 @@ class _ApiKeysScreenState extends ConsumerState<ApiKeysScreen> {
);
}
Future<void> _revokeKey(Map<String, dynamic> data) async {
final key = (data['key'] ?? data['token'] ?? '').toString();
final name = (data['name'] ?? '').toString();
Future<void> _revokeKey(ApiKeyInfo data) async {
final confirmed = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Отозвать ключ?'),
content: Text('Отозвать "$name"?'),
content: Text('Отозвать "${data.name}"?'),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(false),
@@ -210,7 +209,7 @@ class _ApiKeysScreenState extends ConsumerState<ApiKeysScreen> {
);
if (confirmed == true) {
try {
await ref.read(apiKeysProvider.notifier).revoke(key);
await ref.read(apiKeysProvider.notifier).revoke(data.key);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
@@ -222,10 +221,9 @@ class _ApiKeysScreenState extends ConsumerState<ApiKeysScreen> {
}
}
Future<void> _activateKey(Map<String, dynamic> data) async {
final key = (data['key'] ?? data['token'] ?? '').toString();
Future<void> _activateKey(ApiKeyInfo data) async {
try {
await ref.read(apiKeysProvider.notifier).activate(key);
await ref.read(apiKeysProvider.notifier).activate(data.key);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
@@ -308,7 +306,7 @@ class _LastCreatedKeyBanner extends StatelessWidget {
/// Карточка одного API-ключа
class _ApiKeyCard extends StatelessWidget {
final Map<String, dynamic> data;
final ApiKeyInfo data;
final VoidCallback onRevoke;
final VoidCallback onActivate;
@@ -320,30 +318,25 @@ class _ApiKeyCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final name = (data['name'] ?? 'Без имени').toString();
final isAdmin = data['is_admin'] == true;
final isActive = data['is_active'] ?? data['active'] ?? true;
final createdAt = data['created_at'] ?? '';
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: ListTile(
leading: Icon(
Icons.vpn_key,
color: isActive
? (isAdmin ? Colors.amber : Colors.deepOrange)
color: data.isActive
? (data.isAdmin ? Colors.amber : Colors.deepOrange)
: Colors.white24,
),
title: Row(
children: [
Text(
name,
data.name,
style: TextStyle(
fontWeight: FontWeight.bold,
color: isActive ? Colors.white : Colors.white38,
color: data.isActive ? Colors.white : Colors.white38,
),
),
if (isAdmin) ...[
if (data.isAdmin) ...[
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
@@ -357,7 +350,7 @@ class _ApiKeyCard extends StatelessWidget {
),
),
],
if (!isActive) ...[
if (!data.isActive) ...[
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
@@ -373,13 +366,13 @@ class _ApiKeyCard extends StatelessWidget {
],
],
),
subtitle: createdAt.toString().isNotEmpty
subtitle: data.formattedCreatedAt.isNotEmpty
? Text(
'Создан: ${_formatDate(createdAt.toString())}',
'Создан: ${data.formattedCreatedAt}',
style: const TextStyle(fontSize: 11, color: Colors.white30),
)
: null,
trailing: isActive
trailing: data.isActive
? IconButton(
icon: const Icon(
Icons.block,
@@ -401,14 +394,4 @@ class _ApiKeyCard extends StatelessWidget {
),
);
}
String _formatDate(String iso) {
try {
final d = DateTime.parse(iso);
String pad(int n) => n.toString().padLeft(2, '0');
return '${pad(d.day)}.${pad(d.month)}.${d.year}';
} catch (_) {
return iso;
}
}
}