add side panel and more build log controls
This commit is contained in:
		| @@ -42,8 +42,7 @@ class _APIBuilderState<T extends BaseProvider, K, DTO> | |||||||
|  |  | ||||||
|     if (widget.interval != null) { |     if (widget.interval != null) { | ||||||
|       timer = Timer.periodic(widget.interval!, (Timer t) { |       timer = Timer.periodic(widget.interval!, (Timer t) { | ||||||
|         Provider.of<T>(context, listen: false) |         Provider.of<T>(context, listen: false).refresh(context); | ||||||
|             .refresh(context); |  | ||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -31,11 +31,13 @@ class _BuildOutputState extends State<BuildOutput> { | |||||||
|         scrollDirection: Axis.vertical, //.horizontal |         scrollDirection: Axis.vertical, //.horizontal | ||||||
|         child: Padding( |         child: Padding( | ||||||
|           padding: const EdgeInsets.only(left: 30, right: 15), |           padding: const EdgeInsets.only(left: 30, right: 15), | ||||||
|           child: Text( |           child: SelectionArea( | ||||||
|             output, |             child: Text( | ||||||
|             style: const TextStyle( |               output, | ||||||
|               fontSize: 16.0, |               style: const TextStyle( | ||||||
|               color: Colors.white, |                 fontSize: 16.0, | ||||||
|  |                 color: Colors.white, | ||||||
|  |               ), | ||||||
|             ), |             ), | ||||||
|           ), |           ), | ||||||
|         ), |         ), | ||||||
|   | |||||||
| @@ -2,17 +2,18 @@ import 'package:flutter/material.dart'; | |||||||
|  |  | ||||||
| import '../../constants/color_constants.dart'; | import '../../constants/color_constants.dart'; | ||||||
|  |  | ||||||
| class ChartCard extends StatelessWidget { | class SideCard extends StatelessWidget { | ||||||
|   const ChartCard({ |   const SideCard({ | ||||||
|     Key? key, |     super.key, | ||||||
|     required this.title, |     required this.title, | ||||||
|     required this.color, |     this.color, | ||||||
|     required this.textRight, |     required this.textRight, | ||||||
|     required this.subtitle, |     this.subtitle, | ||||||
|   }) : super(key: key); |   }); | ||||||
|  |  | ||||||
|   final Color color; |   final Color? color; | ||||||
|   final String title, textRight, subtitle; |   final String title, textRight; | ||||||
|  |   final String? subtitle; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
| @@ -27,12 +28,13 @@ class ChartCard extends StatelessWidget { | |||||||
|       ), |       ), | ||||||
|       child: Row( |       child: Row( | ||||||
|         children: [ |         children: [ | ||||||
|           SizedBox( |           if (color != null) | ||||||
|               height: 20, |             SizedBox( | ||||||
|               width: 20, |                 height: 20, | ||||||
|               child: Container( |                 width: 20, | ||||||
|                 color: color, |                 child: Container( | ||||||
|               )), |                   color: color, | ||||||
|  |                 )), | ||||||
|           Expanded( |           Expanded( | ||||||
|             child: Padding( |             child: Padding( | ||||||
|               padding: const EdgeInsets.symmetric(horizontal: defaultPadding), |               padding: const EdgeInsets.symmetric(horizontal: defaultPadding), | ||||||
| @@ -44,13 +46,14 @@ class ChartCard extends StatelessWidget { | |||||||
|                     maxLines: 1, |                     maxLines: 1, | ||||||
|                     overflow: TextOverflow.ellipsis, |                     overflow: TextOverflow.ellipsis, | ||||||
|                   ), |                   ), | ||||||
|                   Text( |                   if (subtitle != null) | ||||||
|                     subtitle, |                     Text( | ||||||
|                     style: Theme.of(context) |                       subtitle!, | ||||||
|                         .textTheme |                       style: Theme.of(context) | ||||||
|                         .bodySmall! |                           .textTheme | ||||||
|                         .copyWith(color: Colors.white70), |                           .bodySmall! | ||||||
|                   ), |                           .copyWith(color: Colors.white70), | ||||||
|  |                     ), | ||||||
|                 ], |                 ], | ||||||
|               ), |               ), | ||||||
|             ), |             ), | ||||||
|   | |||||||
| @@ -39,21 +39,21 @@ class SidePanel extends StatelessWidget { | |||||||
|               nrbuilds: nrbuilds, |               nrbuilds: nrbuilds, | ||||||
|               nrfailedbuilds: nrfailedbuilds, |               nrfailedbuilds: nrfailedbuilds, | ||||||
|               nrActiveBuilds: nrActiveBuilds), |               nrActiveBuilds: nrActiveBuilds), | ||||||
|           ChartCard( |           SideCard( | ||||||
|             color: const Color(0xff0a7005), |             color: const Color(0xff0a7005), | ||||||
|             title: "Successful Builds", |             title: "Successful Builds", | ||||||
|             textRight: |             textRight: | ||||||
|                 "${((nrbuilds - nrfailedbuilds) * 100 / nrbuilds).toStringAsFixed(2)}%", |                 "${((nrbuilds - nrfailedbuilds) * 100 / nrbuilds).toStringAsFixed(2)}%", | ||||||
|             subtitle: (nrbuilds - nrfailedbuilds).toString(), |             subtitle: (nrbuilds - nrfailedbuilds).toString(), | ||||||
|           ), |           ), | ||||||
|           ChartCard( |           SideCard( | ||||||
|             color: const Color(0xff760707), |             color: const Color(0xff760707), | ||||||
|             title: "Failed Builds", |             title: "Failed Builds", | ||||||
|             textRight: |             textRight: | ||||||
|                 "${(nrfailedbuilds * 100 / nrbuilds).toStringAsFixed(2)}%", |                 "${(nrfailedbuilds * 100 / nrbuilds).toStringAsFixed(2)}%", | ||||||
|             subtitle: nrfailedbuilds.toString(), |             subtitle: nrfailedbuilds.toString(), | ||||||
|           ), |           ), | ||||||
|           ChartCard( |           SideCard( | ||||||
|             color: const Color(0xff9d8d00), |             color: const Color(0xff9d8d00), | ||||||
|             title: "Active Builds", |             title: "Active Builds", | ||||||
|             textRight: |             textRight: | ||||||
|   | |||||||
| @@ -7,7 +7,10 @@ import 'package:flutter/material.dart'; | |||||||
| import 'package:go_router/go_router.dart'; | import 'package:go_router/go_router.dart'; | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
|  |  | ||||||
|  | import '../components/confirm_popup.dart'; | ||||||
|  | import '../components/dashboard/chart_card.dart'; | ||||||
| import '../components/dashboard/your_packages.dart'; | import '../components/dashboard/your_packages.dart'; | ||||||
|  | import '../constants/color_constants.dart'; | ||||||
|  |  | ||||||
| class BuildScreen extends StatefulWidget { | class BuildScreen extends StatefulWidget { | ||||||
|   const BuildScreen({super.key, required this.buildID}); |   const BuildScreen({super.key, required this.buildID}); | ||||||
| @@ -32,50 +35,201 @@ class _BuildScreenState extends State<BuildScreen> { | |||||||
|             interval: const Duration(seconds: 10), |             interval: const Duration(seconds: 10), | ||||||
|             onLoad: () => const Text("loading"), |             onLoad: () => const Text("loading"), | ||||||
|             onData: (buildData) { |             onData: (buildData) { | ||||||
|               final start_time = DateTime.fromMillisecondsSinceEpoch( |               return Row( | ||||||
|                   (buildData.start_time ?? 0) * 1000); |                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |  | ||||||
|               return Column( |  | ||||||
|                 crossAxisAlignment: CrossAxisAlignment.start, |  | ||||||
|                 mainAxisAlignment: MainAxisAlignment.start, |  | ||||||
|                 children: [ |                 children: [ | ||||||
|                   Row( |                   Expanded( | ||||||
|                     mainAxisAlignment: MainAxisAlignment.start, |                     child: Column( | ||||||
|                     children: [ |                       crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|                       const SizedBox( |                       mainAxisAlignment: MainAxisAlignment.start, | ||||||
|                         width: 10, |                       children: [ | ||||||
|                       ), |                         _buildTopBar(buildData), | ||||||
|                       IconButton( |                         const SizedBox( | ||||||
|                         icon: Icon( |                           height: 15, | ||||||
|                           switchSuccessIcon(buildData.status), |  | ||||||
|                           color: switchSuccessColor(buildData.status), |  | ||||||
|                         ), |                         ), | ||||||
|                         onPressed: () { |                         _buildPage(buildData) | ||||||
|                           context.replace("/build/${buildData.id}"); |                       ], | ||||||
|                         }, |                     ), | ||||||
|                       ), |  | ||||||
|                       const SizedBox( |  | ||||||
|                         width: 10, |  | ||||||
|                       ), |  | ||||||
|                       Text( |  | ||||||
|                         buildData.pkg_name, |  | ||||||
|                         style: const TextStyle(fontWeight: FontWeight.bold), |  | ||||||
|                       ), |  | ||||||
|                       const SizedBox( |  | ||||||
|                         width: 10, |  | ||||||
|                       ), |  | ||||||
|                       Text("triggered ${start_time.readableDuration()}") |  | ||||||
|                     ], |  | ||||||
|                   ), |                   ), | ||||||
|                   const SizedBox( |                   _buildSideBar(), | ||||||
|                     height: 15, |  | ||||||
|                   ), |  | ||||||
|                   _buildPage(buildData) |  | ||||||
|                 ], |                 ], | ||||||
|               ); |               ); | ||||||
|             }), |             }), | ||||||
|       ), |       ), | ||||||
|       appBar: AppBar(), |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Widget _buildTopBar(Build buildData) { | ||||||
|  |     final start_time = | ||||||
|  |         DateTime.fromMillisecondsSinceEpoch((buildData.start_time ?? 0) * 1000); | ||||||
|  |  | ||||||
|  |     return Container( | ||||||
|  |       color: secondaryColor, | ||||||
|  |       child: Padding( | ||||||
|  |         padding: const EdgeInsets.only(top: 10, bottom: 10), | ||||||
|  |         child: Row( | ||||||
|  |           mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |           children: [ | ||||||
|  |             Row( | ||||||
|  |               mainAxisAlignment: MainAxisAlignment.start, | ||||||
|  |               children: [ | ||||||
|  |                 const SizedBox( | ||||||
|  |                   width: 10, | ||||||
|  |                 ), | ||||||
|  |                 IconButton( | ||||||
|  |                   icon: const Icon( | ||||||
|  |                     Icons.arrow_back, | ||||||
|  |                     size: 28, | ||||||
|  |                   ), | ||||||
|  |                   onPressed: () { | ||||||
|  |                     context.pop(); | ||||||
|  |                   }, | ||||||
|  |                 ), | ||||||
|  |                 const SizedBox( | ||||||
|  |                   width: 10, | ||||||
|  |                 ), | ||||||
|  |                 IconButton( | ||||||
|  |                   icon: Icon( | ||||||
|  |                     switchSuccessIcon(buildData.status), | ||||||
|  |                     color: switchSuccessColor(buildData.status), | ||||||
|  |                   ), | ||||||
|  |                   onPressed: () { | ||||||
|  |                     context.replace("/build/${buildData.id}"); | ||||||
|  |                   }, | ||||||
|  |                 ), | ||||||
|  |                 const SizedBox( | ||||||
|  |                   width: 10, | ||||||
|  |                 ), | ||||||
|  |                 Text( | ||||||
|  |                   buildData.pkg_name, | ||||||
|  |                   style: const TextStyle(fontWeight: FontWeight.bold), | ||||||
|  |                 ), | ||||||
|  |                 const SizedBox( | ||||||
|  |                   width: 10, | ||||||
|  |                 ), | ||||||
|  |                 Text("triggered ${start_time.readableDuration()}") | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |             Row( | ||||||
|  |               children: [ | ||||||
|  |                 IconButton( | ||||||
|  |                   onPressed: () {}, | ||||||
|  |                   icon: const Icon(Icons.read_more), | ||||||
|  |                   tooltip: "Follow log", | ||||||
|  |                 ), | ||||||
|  |                 IconButton( | ||||||
|  |                   onPressed: () {}, | ||||||
|  |                   icon: const Icon(Icons.vertical_align_top_rounded), | ||||||
|  |                   tooltip: "Go to Top", | ||||||
|  |                 ), | ||||||
|  |                 IconButton( | ||||||
|  |                   onPressed: () {}, | ||||||
|  |                   icon: Icon(Icons.vertical_align_bottom_rounded), | ||||||
|  |                   tooltip: "Go to Bottom", | ||||||
|  |                 ), | ||||||
|  |                 const SizedBox( | ||||||
|  |                   width: 15, | ||||||
|  |                 ), | ||||||
|  |               ], | ||||||
|  |             ) | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Widget _buildSideBar() { | ||||||
|  |     return SizedBox( | ||||||
|  |       width: 300, | ||||||
|  |       child: Container( | ||||||
|  |         color: secondaryColor, | ||||||
|  |         padding: const EdgeInsets.all(defaultPadding), | ||||||
|  |         child: Column( | ||||||
|  |           crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |           children: [ | ||||||
|  |             const SizedBox( | ||||||
|  |               height: 45, | ||||||
|  |             ), | ||||||
|  |             Divider(), | ||||||
|  |             const Text( | ||||||
|  |               "Actions:", | ||||||
|  |               style: TextStyle(fontSize: 18), | ||||||
|  |               textAlign: TextAlign.start, | ||||||
|  |             ), | ||||||
|  |             const SizedBox( | ||||||
|  |               height: 15, | ||||||
|  |             ), | ||||||
|  |             Row( | ||||||
|  |               children: [ | ||||||
|  |                 ElevatedButton( | ||||||
|  |                   onPressed: () async { | ||||||
|  |                     final confirmResult = await showConfirmationDialog( | ||||||
|  |                       context, | ||||||
|  |                       "Delete Build", | ||||||
|  |                       "Are you sure to delete this Package?", | ||||||
|  |                       () async {}, | ||||||
|  |                       () {}, | ||||||
|  |                     ); | ||||||
|  |                   }, | ||||||
|  |                   child: const Text( | ||||||
|  |                     "Delete", | ||||||
|  |                     style: TextStyle(color: Colors.redAccent), | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |                 const SizedBox( | ||||||
|  |                   width: 10, | ||||||
|  |                 ), | ||||||
|  |                 ElevatedButton( | ||||||
|  |                   onPressed: () async { | ||||||
|  |                     final confirmResult = await showConfirmationDialog( | ||||||
|  |                       context, | ||||||
|  |                       "Delete Build", | ||||||
|  |                       "Are you sure to delete this Package?", | ||||||
|  |                       () async {}, | ||||||
|  |                       () {}, | ||||||
|  |                     ); | ||||||
|  |                   }, | ||||||
|  |                   child: const Text( | ||||||
|  |                     "Retry", | ||||||
|  |                     style: TextStyle(color: Colors.orangeAccent), | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |             const SizedBox( | ||||||
|  |               height: 15, | ||||||
|  |             ), | ||||||
|  |             Divider(), | ||||||
|  |             const SizedBox( | ||||||
|  |               height: 15, | ||||||
|  |             ), | ||||||
|  |             Text( | ||||||
|  |               "Build Information:", | ||||||
|  |               style: TextStyle(fontSize: 18), | ||||||
|  |               textAlign: TextAlign.start, | ||||||
|  |             ), | ||||||
|  |             SizedBox( | ||||||
|  |               height: 20, | ||||||
|  |             ), | ||||||
|  |             SideCard( | ||||||
|  |               title: "Build Number", | ||||||
|  |               textRight: "7", | ||||||
|  |             ), | ||||||
|  |             SideCard( | ||||||
|  |               title: "Finished", | ||||||
|  |               textRight: "7", | ||||||
|  |             ), | ||||||
|  |             SideCard( | ||||||
|  |               title: "Queued", | ||||||
|  |               textRight: "7", | ||||||
|  |             ), | ||||||
|  |             SideCard( | ||||||
|  |               title: "Duration", | ||||||
|  |               textRight: "7", | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user