import 'package:flutter/material.dart'; import 'package:manager_api_new/api.dart'; import 'package:provider/provider.dart'; import '../app_context.dart'; import '../Models/managerContext.dart'; import '../constants.dart'; import '../l10n/app_localizations.dart'; class QuotaBarsWidget extends StatefulWidget { const QuotaBarsWidget({Key? key}) : super(key: key); @override State createState() => _QuotaBarsWidgetState(); } class _QuotaBarsWidgetState extends State { InstanceQuotaDTO? _quota; bool _loading = true; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) => _fetchQuota()); } Future _fetchQuota() async { final managerContext = Provider.of(context, listen: false).getContext() as ManagerAppContext; final instanceId = managerContext.instanceId; final client = managerContext.clientAPI; if (instanceId == null || client == null) { setState(() => _loading = false); return; } try { final quota = await client.instanceApi!.instanceGetQuota(instanceId); if (mounted) setState(() { _quota = quota; _loading = false; }); } catch (_) { if (mounted) setState(() => _loading = false); } } Color _barColor(int used, int quota) { if (quota == 0) return kPrimaryColor; final ratio = used / quota; if (ratio >= 0.95) return Colors.red; if (ratio >= 0.80) return Colors.orange; return kPrimaryColor; } double? _barValue(int used, int quota) { if (quota == 0) return null; return (used / quota).clamp(0.0, 1.0); } String _formatBytes(int bytes) { if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(0)} KB'; if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB'; } @override Widget build(BuildContext context) { final managerContext = Provider.of(context).getContext() as ManagerAppContext; final isAssistant = managerContext.instanceDTO?.isAssistant ?? false; final l10n = AppLocalizations.of(context)!; if (_loading) { return const SizedBox( height: 8, child: LinearProgressIndicator(), ); } if (_quota == null) return const SizedBox.shrink(); final storageUsed = _quota!.storageUsedBytes ?? 0; final storageQuota = _quota!.storageQuotaBytes ?? 0; final aiUsed = _quota!.aiRequestsUsed ?? 0; final aiQuota = _quota!.aiRequestsPerMonth ?? 0; return Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _QuotaBar( icon: Icons.storage, label: l10n.storageLabel, value: _barValue(storageUsed, storageQuota), color: _barColor(storageUsed, storageQuota), subtitle: storageQuota == 0 ? '${_formatBytes(storageUsed)} · ${l10n.unlimited}' : '${_formatBytes(storageUsed)} / ${_formatBytes(storageQuota)}', ), if (isAssistant) ...[ const SizedBox(height: 8), _QuotaBar( icon: Icons.chat_bubble_outline, label: l10n.aiThisMonthLabel, value: _barValue(aiUsed, aiQuota), color: _barColor(aiUsed, aiQuota), subtitle: aiQuota == 0 ? '$aiUsed ${l10n.requestsAbbr} · ${l10n.unlimited}' : '$aiUsed / $aiQuota ${l10n.requestsAbbr}.', ), ], ], ), ); } } class _QuotaBar extends StatelessWidget { final IconData icon; final String label; final double? value; final Color color; final String subtitle; const _QuotaBar({ required this.icon, required this.label, required this.value, required this.color, required this.subtitle, }); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(icon, size: 14, color: kBodyTextColor), const SizedBox(width: 4), Text( label, style: const TextStyle( fontSize: 11, color: kBodyTextColor, fontWeight: FontWeight.w500, fontFamily: 'Helvetica', ), ), ], ), const SizedBox(height: 4), ClipRRect( borderRadius: BorderRadius.circular(2), child: LinearProgressIndicator( value: value, minHeight: 5, backgroundColor: Colors.grey.shade200, valueColor: AlwaysStoppedAnimation(color), ), ), const SizedBox(height: 2), Text( subtitle, style: TextStyle( fontSize: 10, color: kBodyTextColor.withValues(alpha: 0.6), fontFamily: 'Helvetica', ), ), ], ); } }