import 'package:alert_banner/exports.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:json_string/json_string.dart'; import 'package:oc_front/core/services/specialized_services/logs_service.dart'; import 'package:oc_front/main.dart'; import 'package:oc_front/models/logs.dart'; import 'package:oc_front/models/workflow.dart'; import 'package:oc_front/widgets/dialog/alert.dart'; class LogsWidget extends StatefulWidget { String? level; String search = ""; WorkflowExecution? exec; LogsWidget ({ Key? key, this.search = "", this.level, this.exec }): super(key: key); @override LogsWidgetState createState() => LogsWidgetState(); } class LogsWidgetState extends State { @override Widget build(BuildContext context) { if (widget.exec == null) { return Container(); } else { String start = ""; String end = ""; try { if (widget.exec!.endDate != null && widget.exec!.endDate != "") { var startD = DateTime.parse(widget.exec!.executionData!); var endD = DateTime.parse(widget.exec!.endDate!); var diff = endD.difference(startD); if (diff.inDays < 30) { var rest = ((30 - diff.inDays) ~/ 2) - 1; start = (startD.subtract(Duration(days: rest)).microsecondsSinceEpoch).toString(); end = (endD.add(Duration(days: rest)).microsecondsSinceEpoch).toString(); } else { start = (startD.microsecondsSinceEpoch).toString(); end = (startD.add( const Duration(days: 29)).microsecondsSinceEpoch).toString(); } } else { start = (DateTime.parse(widget.exec!.executionData!).subtract( const Duration(days: 14)).microsecondsSinceEpoch).toString(); end = (DateTime.parse(widget.exec!.executionData!).add( const Duration(days: 14)).microsecondsSinceEpoch).toString(); } } catch(e) { /* */ } return FutureBuilder(future: LogsService().search(context, [], { "workflow_execution_id": widget.exec!.id, "start": start, "end": end }), builder: (a, b) { Future.delayed(const Duration(minutes: 1), () { setState(() {}); }); List logs = []; if (b.data != null && b.data!.data != null) { var d = b.data!.data!; for( var r in d.result) { for (var element in r.logs) { element.level = r.level; logs.add(element); } logs.sort((a, b) => a.timestamp!.compareTo(b.timestamp!)); } } List itemRows = logs.where((element) => (element.message?.toLowerCase().contains(widget.search.toLowerCase()) ?? true) && (widget.level?.contains(element.level ?? "") ?? true) ).map((e) => LogWidget(item: e)).toList(); return Stack( children: [ SingleChildScrollView( child: itemRows.isEmpty ? Container( height: getMainHeight(context) - 100, child: const Center( child: Text("no log registered", style: TextStyle(color: Colors.grey, fontSize: 25 ),))) : Column( children: [...itemRows, Container(height: 50,) ] ) ), ]); }); } } } class LogWidget extends StatefulWidget { final Log item; bool expanded = false; LogWidget ({ Key? key, required this.item }): super(key: key); @override LogWidgetState createState() => LogWidgetState(); } class LogWidgetState extends State { @override Widget build(BuildContext context) { Map map = {}; try { map = JsonString(widget.item.rawMessage?.replaceAll("\\", "") ?? "").decodedValue as Map; } catch (e) { /* */} return Container( decoration: const BoxDecoration( border: Border(bottom: BorderSide(color: Colors.white)), ), padding: const EdgeInsets.only(top: 10, left: 30, right: 30, bottom: 10), child: Wrap( children: [ Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( width: 10, height: 15, color: widget.item.level?.toLowerCase() == "info" ? Colors.green : ( widget.item.level?.toLowerCase() == "error" ? redColor : ( widget.item.level?.toLowerCase() == "warning" ? Colors.orange : Colors.blue))), InkWell( mouseCursor: map.isEmpty ? MouseCursor.defer : SystemMouseCursors.click, onTap: () { if (map.isNotEmpty ) { setState(() { widget.expanded = !widget.expanded; }); } }, child: SizedBox( height: 20, child: Padding( padding: EdgeInsets.symmetric(horizontal: widget.expanded ? 0 : 5), child: Icon( widget.expanded ? Icons.keyboard_arrow_down_outlined : Icons.arrow_forward_ios, size: widget.expanded ? 25 : 15, color: map.isEmpty ? Colors.grey : Colors.black, weight: widget.expanded ? 100 : 1000,)))), Padding( padding: const EdgeInsets.only(right: 10), child: Text("${widget.item.timestamp?.toLocal().toString().substring(3)}", style: const TextStyle(fontSize: 13, color: Colors.black, fontWeight: FontWeight.w500))), Tooltip( message : "copy to clipboard", child: InkWell( child: const Icon(Icons.copy, size: 15, color: Colors.grey), onTap: () { if (widget.item.message != null) { Clipboard.setData(ClipboardData(text: widget.item.message!)); showAlertBanner(context, () {}, const InfoAlertBannerChild(text: "Copy to clipboard"), // <-- Put any widget here you want! alertBannerLocation: AlertBannerLocation.bottom,); } })), ]), widget.expanded ? Container( margin: const EdgeInsets.symmetric(vertical: 10), decoration: BoxDecoration( color: Colors.grey, borderRadius: BorderRadius.circular(4)), padding: const EdgeInsets.all(10), child: Column( children: map.keys.map((e) => Padding( padding: const EdgeInsets.all(2), child: Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [Flexible( child:Text("$e: \"${map[e]}\"", style: const TextStyle(fontSize: 11, color: Colors.white))), ]) )).toList() )) : Container(), Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [Flexible( child:Text(widget.item.message ?? "unknown message", style: const TextStyle(fontSize: 14, color: Colors.black))), ]) ])); } }