similar to how it works for qemu, but add a confirmation dialog so one does not accidentally shutdown or reboot a node.
Signed-off-by: Dominik Csapak <d.csa...@proxmox.com> --- changes from v2: * reordered and renamed yes/no on the confirmation dialog * slightly adapted the confirmation text lib/bloc/pve_node_overview_bloc.dart | 11 +++ lib/widgets/pve_node_overview.dart | 24 ++++++ .../pve_node_power_settings_widget.dart | 84 +++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 lib/widgets/pve_node_power_settings_widget.dart diff --git a/lib/bloc/pve_node_overview_bloc.dart b/lib/bloc/pve_node_overview_bloc.dart index d14ff79..19d6563 100644 --- a/lib/bloc/pve_node_overview_bloc.dart +++ b/lib/bloc/pve_node_overview_bloc.dart @@ -57,9 +57,20 @@ class PveNodeOverviewBloc final disks = await apiClient.getNodeDisksList(nodeID); yield latestState.rebuild((b) => b..disks.replace(disks)); } + if (event is PerformNodeAction) { + await apiClient.doResourceAction(nodeID, '', 'node', event.action, + parameters: <String, String>{}); + yield latestState; + } } } abstract class PveNodeOverviewEvent {} class UpdateNodeStatus extends PveNodeOverviewEvent {} + +class PerformNodeAction extends PveNodeOverviewEvent { + final PveClusterResourceAction action; + + PerformNodeAction(this.action); +} diff --git a/lib/widgets/pve_node_overview.dart b/lib/widgets/pve_node_overview.dart index 7b65c0e..ad9a3b2 100644 --- a/lib/widgets/pve_node_overview.dart +++ b/lib/widgets/pve_node_overview.dart @@ -8,6 +8,7 @@ import 'package:pve_flutter_frontend/states/pve_node_overview_state.dart'; import 'package:pve_flutter_frontend/states/pve_task_log_state.dart'; import 'package:pve_flutter_frontend/utils/renderers.dart'; import 'package:pve_flutter_frontend/utils/utils.dart'; +import 'package:pve_flutter_frontend/widgets/pve_node_power_settings_widget.dart'; import 'package:pve_flutter_frontend/widgets/proxmox_capacity_indicator.dart'; import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart'; import 'package:pve_flutter_frontend/widgets/pve_action_card_widget.dart'; @@ -189,6 +190,16 @@ class PveNodeOverview extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ + ActionCard( + icon: const Icon( + Icons.power_settings_new, + size: 55, + color: Colors.white24, + ), + title: 'Power Settings', + onTap: () => + showPowerMenuBottomSheet(context, nBloc), + ), ActionCard( icon: const Icon( Icons.queue_play_next, @@ -443,4 +454,17 @@ class PveNodeOverview extends StatelessWidget { }, ); } + + Future<T?> showPowerMenuBottomSheet<T>( + BuildContext context, PveNodeOverviewBloc nodeBloc) async { + return showModalBottomSheet( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical(top: Radius.circular(10))), + context: context, + builder: (context) => Provider.value( + value: nodeBloc, + child: const PveNodePowerSettings(), + ), + ); + } } diff --git a/lib/widgets/pve_node_power_settings_widget.dart b/lib/widgets/pve_node_power_settings_widget.dart new file mode 100644 index 0000000..5de9c85 --- /dev/null +++ b/lib/widgets/pve_node_power_settings_widget.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:proxmox_dart_api_client/proxmox_dart_api_client.dart'; +import 'package:pve_flutter_frontend/bloc/pve_node_overview_bloc.dart'; +import 'package:pve_flutter_frontend/states/pve_node_overview_state.dart'; +import 'package:pve_flutter_frontend/widgets/proxmox_stream_builder_widget.dart'; + +class PveNodePowerSettings extends StatelessWidget { + const PveNodePowerSettings({ + super.key, + }); + @override + Widget build(BuildContext context) { + final bloc = Provider.of<PveNodeOverviewBloc>(context); + return ProxmoxStreamBuilder<PveNodeOverviewBloc, PveNodeOverviewState>( + bloc: bloc, + builder: (context, state) { + return SafeArea( + child: SingleChildScrollView( + child: Container( + constraints: BoxConstraints( + minHeight: MediaQuery.of(context).size.height / 3), + child: Column( + mainAxisSize: MainAxisSize.min, + children: <Widget>[ + ListTile( + leading: const Icon(Icons.autorenew), + title: const Text( + "Reboot", + style: TextStyle(fontWeight: FontWeight.bold), + ), + subtitle: const Text("Reboot Node"), + onTap: () => action(context, + PveClusterResourceAction.reboot, bloc, "Reboot"), + ), + ListTile( + leading: const Icon(Icons.power_settings_new), + title: const Text( + "Shutdown", + style: TextStyle(fontWeight: FontWeight.bold), + ), + subtitle: const Text("Shutdown Node"), + onTap: () => action(context, + PveClusterResourceAction.shutdown, bloc, "Shutdown"), + ), + ], + ), + ), + ), + ); + }); + } + + void action(BuildContext context, PveClusterResourceAction action, + PveNodeOverviewBloc bloc, String actionText) async { + if (await showDialog( + context: context, + builder: (context) { + return AlertDialog( + contentPadding: const EdgeInsets.fromLTRB(24.0, 12.0, 24.0, 16.0), + title: const Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: <Widget>[ + Text('Confirm'), + Icon(Icons.warning), + ], + ), + content: Text( + "Are you sure you want to do action '$actionText' on node '${bloc.nodeID}'?"), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text("Cancel")), + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: Text(actionText)) + ], + ); + })) { + bloc.events.add(PerformNodeAction(action)); + if (context.mounted) Navigator.of(context).pop(); + } + } +} -- 2.39.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel