import 'package:flutter/material.dart'; import '../control_plane_status_client.dart'; class RuntimePanelOperationsHistorySection extends StatelessWidget { final bool isLoading; final String? error; final EdgeOperationsResponseView? response; const RuntimePanelOperationsHistorySection({ required this.isLoading, required this.error, required this.response, super.key, }); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Operations Execution History', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white, ), ), const SizedBox(height: 12), _OperationsHistoryList( isLoading: isLoading, error: error, response: response, ), ], ); } } class _OperationsHistoryList extends StatelessWidget { final bool isLoading; final String? error; final EdgeOperationsResponseView? response; const _OperationsHistoryList({ required this.isLoading, required this.error, required this.response, }); @override Widget build(BuildContext context) { // not-loaded: fetch has not completed yet if (isLoading && response == null) { return const _OperationsHistoryLoading(); } // error from repository fetch if (error != null) { return _OperationsHistoryError(message: error!); } // loaded-empty: fetch completed with empty operations final operations = response?.operations ?? const []; if (operations.isEmpty) { return const _OperationsHistoryEmpty(); } return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: operations.length, itemBuilder: (context, index) { return _OperationRecordCard(record: operations[index]); }, ); } } class _OperationsHistoryLoading extends StatelessWidget { const _OperationsHistoryLoading(); @override Widget build(BuildContext context) { return const Center( child: Padding( padding: EdgeInsets.all(24.0), child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Color(0xFF6366F1)), ), ), ); } } class _OperationsHistoryError extends StatelessWidget { final String message; const _OperationsHistoryError({required this.message}); @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(24.0), child: Text( 'Error: $message', style: const TextStyle(color: Color(0xFFEF4444)), ), ), ); } } class _OperationsHistoryEmpty extends StatelessWidget { const _OperationsHistoryEmpty(); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(24), width: double.infinity, decoration: BoxDecoration( color: const Color(0xFF1E293B), borderRadius: BorderRadius.circular(12), ), child: const Center( child: Text( 'No operation executions recorded.', style: TextStyle(color: Color(0xFF94A3B8), fontSize: 13), ), ), ); } } class _OperationRecordCard extends StatelessWidget { final EdgeCommandRecordView record; const _OperationRecordCard({required this.record}); @override Widget build(BuildContext context) { final opColor = _getOpStatusColor(record.status); return Card( color: const Color(0xFF1E293B), margin: const EdgeInsets.only(bottom: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: const Color(0xFF334155)), ), child: ListTile( leading: Icon( record.status.toLowerCase() == 'success' ? Icons.check_circle : Icons.pending, color: opColor, ), title: Text( record.operation, style: const TextStyle( fontWeight: FontWeight.bold, color: Colors.white, ), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), Text( 'ID: ${record.commandId} | Selector: ${record.targetSelector.isNotEmpty ? record.targetSelector : "all"}', style: const TextStyle(color: Color(0xFF64748B), fontSize: 11), ), if (record.summary.isNotEmpty) ...[ const SizedBox(height: 4), Text( record.summary, style: const TextStyle(color: Color(0xFF94A3B8), fontSize: 12), ), ], if (record.error.isNotEmpty) ...[ const SizedBox(height: 4), Text( 'Error: ${record.error}', style: const TextStyle(color: Color(0xFFEF4444), fontSize: 12), ), ], ], ), trailing: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: opColor.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(4), ), child: Text( record.status.toUpperCase(), style: TextStyle( color: opColor, fontSize: 10, fontWeight: FontWeight.bold, ), ), ), ), ); } Color _getOpStatusColor(String status) { switch (status.toLowerCase()) { case 'success': return const Color(0xFF10B981); case 'accepted': case 'pending': return const Color(0xFFF59E0B); case 'error': case 'failed': return const Color(0xFFEF4444); default: return const Color(0xFF94A3B8); } } }