* [pve-devel] [PATCH pve-flutter-frontend v2] node overview: add power settings menu @ 2024-04-17 6:45 Dominik Csapak 2024-04-17 8:19 ` Folke Gleumes 2024-04-17 8:48 ` [pve-devel] applied: " Thomas Lamprecht 0 siblings, 2 replies; 4+ messages in thread From: Dominik Csapak @ 2024-04-17 6:45 UTC (permalink / raw) To: pve-devel 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.csapak@proxmox.com> --- changes from v1: * add an AlertDialog as confirmation before executing the action 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..621ac68 --- /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 $actionText node '${bloc.nodeID}'?"), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(true), + child: const Text("Yes")), + TextButton( + onPressed: () => Navigator.of(context).pop(false), + child: const Text("No")) + ], + ); + })) { + bloc.events.add(PerformNodeAction(action)); + if (context.mounted) Navigator.of(context).pop(); + } + } +} -- 2.39.2 ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [pve-devel] [PATCH pve-flutter-frontend v2] node overview: add power settings menu 2024-04-17 6:45 [pve-devel] [PATCH pve-flutter-frontend v2] node overview: add power settings menu Dominik Csapak @ 2024-04-17 8:19 ` Folke Gleumes 2024-04-17 8:33 ` Dominik Csapak 2024-04-17 8:48 ` [pve-devel] applied: " Thomas Lamprecht 1 sibling, 1 reply; 4+ messages in thread From: Folke Gleumes @ 2024-04-17 8:19 UTC (permalink / raw) To: Proxmox VE development discussion On Wed, 2024-04-17 at 08:45 +0200, Dominik Csapak wrote: > 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.csapak@proxmox.com> > --- > changes from v1: > * add an AlertDialog as confirmation before executing the action > > 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.d > art'; > 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..621ac68 > --- /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.d > art'; > + > +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 $actionText node > '${bloc.nodeID}'?"), > + actions: [ > + TextButton( > + onPressed: () => Navigator.of(context).pop(true), > + child: const Text("Yes")), > + TextButton( > + onPressed: () => Navigator.of(context).pop(false), > + child: const Text("No")) > + ], The order of buttons probably should be reversed. According to material guidelines a dismissive action has to be placed to the left of confirming actions [0]. In practice, I accidentally shut down my test VM because I intuitively went for the leftmost button to abort. To make the actions even more obvious, they could be phrased as Cancel and Shutdown/Reboot. > + ); > + })) { > + bloc.events.add(PerformNodeAction(action)); > + if (context.mounted) Navigator.of(context).pop(); > + } > + } > +} Besides the button ordering, everything worked well, so if those are changed, consider this: Tested-by: Folke Gleumes <f.gleumes@proxmox.com> [0] https://m2.material.io/components/dialogs#actions ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [pve-devel] [PATCH pve-flutter-frontend v2] node overview: add power settings menu 2024-04-17 8:19 ` Folke Gleumes @ 2024-04-17 8:33 ` Dominik Csapak 0 siblings, 0 replies; 4+ messages in thread From: Dominik Csapak @ 2024-04-17 8:33 UTC (permalink / raw) To: Proxmox VE development discussion, Folke Gleumes On 4/17/24 10:19, Folke Gleumes wrote: > On Wed, 2024-04-17 at 08:45 +0200, Dominik Csapak wrote: >> 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.csapak@proxmox.com> >> --- >> changes from v1: >> * add an AlertDialog as confirmation before executing the action >> >> 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.d >> art'; >> 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..621ac68 >> --- /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.d >> art'; >> + >> +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 $actionText node >> '${bloc.nodeID}'?"), >> + actions: [ >> + TextButton( >> + onPressed: () => Navigator.of(context).pop(true), >> + child: const Text("Yes")), >> + TextButton( >> + onPressed: () => Navigator.of(context).pop(false), >> + child: const Text("No")) >> + ], > > The order of buttons probably should be reversed. According to material > guidelines a dismissive action has to be placed to the left of > confirming actions [0]. In practice, I accidentally shut down my test > VM because I intuitively went for the leftmost button to abort. To make > the actions even more obvious, they could be phrased as Cancel and > Shutdown/Reboot. fair enough, i used the same order as we do for the desktop ui (left yes, right no), but that is probably also not super ideal? i'll send a v3 > >> + ); >> + })) { >> + bloc.events.add(PerformNodeAction(action)); >> + if (context.mounted) Navigator.of(context).pop(); >> + } >> + } >> +} > > Besides the button ordering, everything worked well, so if those are > changed, consider this: > > Tested-by: Folke Gleumes <f.gleumes@proxmox.com> > > [0] https://m2.material.io/components/dialogs#actions > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ^ permalink raw reply [flat|nested] 4+ messages in thread
* [pve-devel] applied: [PATCH pve-flutter-frontend v2] node overview: add power settings menu 2024-04-17 6:45 [pve-devel] [PATCH pve-flutter-frontend v2] node overview: add power settings menu Dominik Csapak 2024-04-17 8:19 ` Folke Gleumes @ 2024-04-17 8:48 ` Thomas Lamprecht 1 sibling, 0 replies; 4+ messages in thread From: Thomas Lamprecht @ 2024-04-17 8:48 UTC (permalink / raw) To: Proxmox VE development discussion, Dominik Csapak Am 17/04/2024 um 08:45 schrieb Dominik Csapak: > 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.csapak@proxmox.com> > --- > changes from v1: > * add an AlertDialog as confirmation before executing the action > > 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 > > applied this already a bit ago [0] with some code style follow-up [1], so can you please send the changes to adapt to Folke's review as patch on-top of current master? [0]: https://git.proxmox.com/?p=flutter/pve_flutter_frontend.git;a=commitdiff;h=16ddf759f338e59501520068867624b549215553 [1]: https://git.proxmox.com/?p=flutter/pve_flutter_frontend.git;a=commitdiff;h=e883ee3b9c9c7c6cee85d5ac77dcc86dbf7c12c0 ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2024-04-17 8:48 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2024-04-17 6:45 [pve-devel] [PATCH pve-flutter-frontend v2] node overview: add power settings menu Dominik Csapak 2024-04-17 8:19 ` Folke Gleumes 2024-04-17 8:33 ` Dominik Csapak 2024-04-17 8:48 ` [pve-devel] applied: " Thomas Lamprecht
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox