From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id 8C3011FF0AF for ; Fri, 23 Oct 2026 13:41:06 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 5B53221672; Fri, 23 Oct 2026 13:41:06 +0200 (CEST) From: Shan Shaji To: pve-devel@lists.proxmox.com Subject: [PATCH pve_flutter_frontend 3/3] cleanup: run dart format Date: Fri, 23 Oct 2026 13:40:36 +0200 Message-ID: <20261023114036.70719-4-s.shaji@proxmox.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20261023114036.70719-1-s.shaji@proxmox.com> References: <20261023114036.70719-1-s.shaji@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1792755645647 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.374 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: 4NZF2G5DVJ5OMMBX4ROYEOXDZV2ZTYQJ X-Message-ID-Hash: 4NZF2G5DVJ5OMMBX4ROYEOXDZV2ZTYQJ X-MailFrom: s.shaji@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Signed-off-by: Shan Shaji --- lib/utils/links.dart | 4 +- .../firstWelcomeScreen/pve_welcome_faq.dart | 5 +- lib/widgets/pve_lxc_overview.dart | 316 ++++---- lib/widgets/pve_node_overview.dart | 707 +++++++++--------- lib/widgets/pve_qemu_overview.dart | 301 ++++---- 5 files changed, 664 insertions(+), 669 deletions(-) diff --git a/lib/utils/links.dart b/lib/utils/links.dart index a161d36..708f732 100644 --- a/lib/utils/links.dart +++ b/lib/utils/links.dart @@ -9,8 +9,8 @@ Future tryLaunchUrl(Uri url) async { } class Links { - static final pvePricing = - Uri.parse('https://www.proxmox.com/en/products/proxmox-virtual-environment/pricing'); + static final pvePricing = Uri.parse( + 'https://www.proxmox.com/en/products/proxmox-virtual-environment/pricing'); static final proxmoxForum = Uri.parse('https://forum.proxmox.com'); static final pveUserList = Uri.parse('https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-user'); diff --git a/lib/widgets/firstWelcomeScreen/pve_welcome_faq.dart b/lib/widgets/firstWelcomeScreen/pve_welcome_faq.dart index b34c4f5..0c76bbb 100644 --- a/lib/widgets/firstWelcomeScreen/pve_welcome_faq.dart +++ b/lib/widgets/firstWelcomeScreen/pve_welcome_faq.dart @@ -39,7 +39,7 @@ class PveWelcomePageFAQ extends StatelessWidget { ), const PveAnswer( text: - "Spice is currently supported, and we also support VNC through noVNC." ), + "Spice is currently supported, and we also support VNC through noVNC."), const PveQuestion(text: "Which Spice client works?"), const PveAnswer( text: @@ -47,7 +47,8 @@ class PveWelcomePageFAQ extends StatelessWidget { Center( child: OutlinedButton( onPressed: () { - final link = Platform.isIOS ? Links.aSpiceClient : Links.opaqueApp; + final link = + Platform.isIOS ? Links.aSpiceClient : Links.opaqueApp; launchUrl(link); }, style: OutlinedButton.styleFrom( diff --git a/lib/widgets/pve_lxc_overview.dart b/lib/widgets/pve_lxc_overview.dart index b0b210a..d6cc721 100644 --- a/lib/widgets/pve_lxc_overview.dart +++ b/lib/widgets/pve_lxc_overview.dart @@ -66,190 +66,190 @@ class PveLxcOverview extends StatelessWidget { ), backgroundColor: Theme.of(context).colorScheme.surfaceContainer, body: SingleChildScrollView( - child: Column( - children: [ - PveGuestOverviewHeader( - background: !(status?.template ?? false) - ? PveGuestHeaderRRDPageView( - rrdData: state.rrdData, - ) - : Center( - child: Text( - "TEMPLATE", - style: TextStyle( - color: Theme.of(context) - .colorScheme - .onSurfaceVariant, - ), + child: Column( + children: [ + PveGuestOverviewHeader( + background: !(status?.template ?? false) + ? PveGuestHeaderRRDPageView( + rrdData: state.rrdData, + ) + : Center( + child: Text( + "TEMPLATE", + style: TextStyle( + color: Theme.of(context) + .colorScheme + .onSurfaceVariant, ), ), - width: width, - guestID: guestID, - guestStatus: status?.getLxcStatus(), - guestName: config?.hostname ?? 'CT $guestID', - guestNodeID: state.nodeID, - guestType: 'lxc', - ha: status?.ha, - template: status?.template ?? false, - ), - ProxmoxStreamBuilder( - bloc: taskBloc, - builder: (context, taskState) { - if (taskState.tasks.isNotEmpty) { - return PveTaskExpansionTile( - task: taskState.tasks.first, - showMorePage: Provider( - create: (context) => PveTaskLogBloc( - apiClient: taskBloc.apiClient, - init: PveTaskLogState.init(state.nodeID), + ), + width: width, + guestID: guestID, + guestStatus: status?.getLxcStatus(), + guestName: config?.hostname ?? 'CT $guestID', + guestNodeID: state.nodeID, + guestType: 'lxc', + ha: status?.ha, + template: status?.template ?? false, + ), + ProxmoxStreamBuilder( + bloc: taskBloc, + builder: (context, taskState) { + if (taskState.tasks.isNotEmpty) { + return PveTaskExpansionTile( + task: taskState.tasks.first, + showMorePage: Provider( + create: (context) => PveTaskLogBloc( + apiClient: taskBloc.apiClient, + init: PveTaskLogState.init(state.nodeID), + ) + ..events.add( + FilterTasksByGuestID( + guestID: guestID, + ), ) - ..events.add( - FilterTasksByGuestID( - guestID: guestID, - ), - ) - ..events.add(LoadTasks()), - dispose: (context, bloc) => bloc.dispose(), - child: const PveTaskLog(), - ), - ); - } - return Container(); - }, - ), - SizedBox( - height: 130, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - if (!(status?.template ?? false)) - ActionCard.withIcon( - 'Power Settings', - Icons.power_settings_new, - () => showPowerMenuBottomSheet( - context, lxcBloc)), - if (!(status?.template ?? false)) - ActionCard.withIcon( - 'Console', - Icons.queue_play_next, - () => showConsoleMenuBottomSheet( - context, - lxcBloc.apiClient, - guestID, - state.nodeID, - 'lxc')), + ..events.add(LoadTasks()), + dispose: (context, bloc) => bloc.dispose(), + child: const PveTaskLog(), + ), + ); + } + return Container(); + }, + ), + SizedBox( + height: 130, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + if (!(status?.template ?? false)) + ActionCard.withIcon( + 'Power Settings', + Icons.power_settings_new, + () => showPowerMenuBottomSheet( + context, lxcBloc)), + if (!(status?.template ?? false)) ActionCard.withIcon( - 'Options', - Icons.settings, - () => Navigator.of(context) - .push(MaterialPageRoute( - builder: (context) => PveLxcOptions( - lxcBloc: lxcBloc, - ), - fullscreenDialog: true))), - if (!resourceBloc.latestState.isStandalone) - ActionCard.withFaIcon( - 'Migrate', - FontAwesomeIcons.paperPlane, - () => Navigator.of(context).push( - _createMigrationRoute( - guestID, - state.nodeID, - resourceBloc.apiClient!))), + 'Console', + Icons.queue_play_next, + () => showConsoleMenuBottomSheet( + context, + lxcBloc.apiClient, + guestID, + state.nodeID, + 'lxc')), + ActionCard.withIcon( + 'Options', + Icons.settings, + () => Navigator.of(context) + .push(MaterialPageRoute( + builder: (context) => PveLxcOptions( + lxcBloc: lxcBloc, + ), + fullscreenDialog: true))), + if (!resourceBloc.latestState.isStandalone) ActionCard.withFaIcon( - 'Backup', - FontAwesomeIcons.floppyDisk, + 'Migrate', + FontAwesomeIcons.paperPlane, () => Navigator.of(context).push( - _createBackupRoute(guestID, state.nodeID, + _createMigrationRoute( + guestID, + state.nodeID, resourceBloc.apiClient!))), - ], + ActionCard.withFaIcon( + 'Backup', + FontAwesomeIcons.floppyDisk, + () => Navigator.of(context).push( + _createBackupRoute(guestID, state.nodeID, + resourceBloc.apiClient!))), + ], + ), + ), + ), + if (config != null) ...[ + PveResourceDataCardWidget( + title: const Text( + 'Resources', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, ), ), + children: [ + ListTile( + leading: const FaIcon(FontAwesomeIcons.memory), + title: Text('${config.memory}'), + subtitle: const Text('Memory'), + dense: true, + ), + ListTile( + leading: const Icon(Icons.cached), + title: Text('${config.swap}'), + subtitle: const Text('Swap'), + dense: true, + ), + ListTile( + leading: const Icon(Icons.memory), + title: Text('${config.cores ?? 'unlimited'}'), + subtitle: const Text('Cores'), + dense: true, + ), + ListTile( + leading: const FaIcon(FontAwesomeIcons.hardDrive), + dense: true, + title: Text('${config.rootfs}'), + ), + ], ), - if (config != null) ...[ - PveResourceDataCardWidget( + PveResourceDataCardWidget( title: const Text( - 'Resources', + 'Network', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 20, ), ), children: [ - ListTile( - leading: const FaIcon(FontAwesomeIcons.memory), - title: Text('${config.memory}'), - subtitle: const Text('Memory'), - dense: true, - ), - ListTile( - leading: const Icon(Icons.cached), - title: Text('${config.swap}'), - subtitle: const Text('Swap'), - dense: true, + for (var net in config.net!) + ListTile( + leading: + const FaIcon(FontAwesomeIcons.ethernet), + dense: true, + title: Text(net), + ), + ]), + PveResourceDataCardWidget( + title: const Text( + 'DNS', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, ), + ), + children: [ ListTile( - leading: const Icon(Icons.memory), - title: Text('${config.cores ?? 'unlimited'}'), - subtitle: const Text('Cores'), + leading: const FaIcon(FontAwesomeIcons.globe), dense: true, + title: Text( + config.searchdomain ?? 'Use host settings'), + subtitle: const Text('DNS Domain'), ), ListTile( - leading: const FaIcon(FontAwesomeIcons.hardDrive), + leading: const FaIcon( + FontAwesomeIcons.magnifyingGlass), dense: true, - title: Text('${config.rootfs}'), + title: Text( + config.nameserver ?? 'Use host settings'), + subtitle: const Text('DNS Server'), ), - ], - ), - PveResourceDataCardWidget( - title: const Text( - 'Network', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20, - ), - ), - children: [ - for (var net in config.net!) - ListTile( - leading: - const FaIcon(FontAwesomeIcons.ethernet), - dense: true, - title: Text(net), - ), - ]), - PveResourceDataCardWidget( - title: const Text( - 'DNS', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20, - ), - ), - children: [ - ListTile( - leading: const FaIcon(FontAwesomeIcons.globe), - dense: true, - title: Text( - config.searchdomain ?? 'Use host settings'), - subtitle: const Text('DNS Domain'), - ), - ListTile( - leading: const FaIcon( - FontAwesomeIcons.magnifyingGlass), - dense: true, - title: Text( - config.nameserver ?? 'Use host settings'), - subtitle: const Text('DNS Server'), - ), - ]), - ], - SizedBox(height: MediaQuery.of(context).viewPadding.bottom) + ]), ], - ), + SizedBox(height: MediaQuery.of(context).viewPadding.bottom) + ], ), + ), ); }), ); diff --git a/lib/widgets/pve_node_overview.dart b/lib/widgets/pve_node_overview.dart index 4ecdede..b9cc4db 100644 --- a/lib/widgets/pve_node_overview.dart +++ b/lib/widgets/pve_node_overview.dart @@ -63,391 +63,388 @@ class PveNodeOverview extends StatelessWidget { ), backgroundColor: Theme.of(context).colorScheme.surfaceContainer, body: SingleChildScrollView( - child: Column( - children: [ - if (rrd.isNotEmpty) - Container( - height: 200, - color: Theme.of(context).colorScheme.primary, - child: ScrollConfiguration( - behavior: PVEScrollBehavior(), - child: PageView.builder( - itemCount: 4, - itemBuilder: (context, item) { - final page = item + 1; - final pageIndicator = Text( - '$page of 4', - style: const TextStyle( - color: Colors.white54, - fontWeight: FontWeight.w500, - ), - ); - double? lastCpu = rrd.last.cpu; - String lastCpuText = lastCpu != null - ? "${(lastCpu * 100.0).toStringAsFixed(2)} %" - : ""; - return Column( - children: [ - if (item == 0) - Expanded( - child: PveRRDChart( - title: - 'CPU (${state.status?.cpuinfo.cpus ?? '-'})', - subtitle: lastCpuText, - data: rrd.where((e) => e.cpu != null).map( - (e) => Point( - e.time!.millisecondsSinceEpoch, - (e.cpu ?? 0) * 100.0)), - icon: Icon(Icons.memory, color: fgColor), - bottomRight: pageIndicator, - dataRenderer: (data) => - '${data.toStringAsFixed(2)} %', - ), + child: Column( + children: [ + if (rrd.isNotEmpty) + Container( + height: 200, + color: Theme.of(context).colorScheme.primary, + child: ScrollConfiguration( + behavior: PVEScrollBehavior(), + child: PageView.builder( + itemCount: 4, + itemBuilder: (context, item) { + final page = item + 1; + final pageIndicator = Text( + '$page of 4', + style: const TextStyle( + color: Colors.white54, + fontWeight: FontWeight.w500, + ), + ); + double? lastCpu = rrd.last.cpu; + String lastCpuText = lastCpu != null + ? "${(lastCpu * 100.0).toStringAsFixed(2)} %" + : ""; + return Column( + children: [ + if (item == 0) + Expanded( + child: PveRRDChart( + title: + 'CPU (${state.status?.cpuinfo.cpus ?? '-'})', + subtitle: lastCpuText, + data: rrd.where((e) => e.cpu != null).map( + (e) => Point( + e.time!.millisecondsSinceEpoch, + (e.cpu ?? 0) * 100.0)), + icon: Icon(Icons.memory, color: fgColor), + bottomRight: pageIndicator, + dataRenderer: (data) => + '${data.toStringAsFixed(2)} %', ), - if (item == 1) - Expanded( - child: PveRRDChart( - title: 'Memory', - subtitle: Renderers.formatSize( - rrd.last.memused ?? 0), - data: rrd.map((e) => Point( - e.time!.millisecondsSinceEpoch, - e.memused ?? 0)), - icon: FaIcon(FontAwesomeIcons.memory, - color: fgColor), - bottomRight: pageIndicator, - dataRenderer: (data) => - Renderers.formatSize(data), - ), + ), + if (item == 1) + Expanded( + child: PveRRDChart( + title: 'Memory', + subtitle: Renderers.formatSize( + rrd.last.memused ?? 0), + data: rrd.map((e) => Point( + e.time!.millisecondsSinceEpoch, + e.memused ?? 0)), + icon: FaIcon(FontAwesomeIcons.memory, + color: fgColor), + bottomRight: pageIndicator, + dataRenderer: (data) => + Renderers.formatSize(data), ), - if (item == 2) - Expanded( - child: PveRRDChart( - title: 'I/O wait', - subtitle: - rrd.last.iowait?.toStringAsFixed(2) ?? - '0', - data: rrd.map((e) => Point( - e.time!.millisecondsSinceEpoch, - e.iowait ?? 0)), - icon: Icon(Icons.timer, color: fgColor), - bottomRight: pageIndicator, - dataRenderer: (data) => - data.toStringAsFixed(3), - ), + ), + if (item == 2) + Expanded( + child: PveRRDChart( + title: 'I/O wait', + subtitle: + rrd.last.iowait?.toStringAsFixed(2) ?? + '0', + data: rrd.map((e) => Point( + e.time!.millisecondsSinceEpoch, + e.iowait ?? 0)), + icon: Icon(Icons.timer, color: fgColor), + bottomRight: pageIndicator, + dataRenderer: (data) => + data.toStringAsFixed(3), ), - if (item == 3) - Expanded( - child: PveRRDChart( - title: 'Load', - subtitle: rrd.last.loadavg - ?.toStringAsFixed(2) ?? - '0', - data: rrd.map((e) => Point( - e.time!.millisecondsSinceEpoch, - e.loadavg ?? 0)), - icon: Icon(Icons.show_chart, - color: fgColor), - bottomRight: pageIndicator, - dataRenderer: (data) => - data.toStringAsFixed(2), - ), + ), + if (item == 3) + Expanded( + child: PveRRDChart( + title: 'Load', + subtitle: + rrd.last.loadavg?.toStringAsFixed(2) ?? + '0', + data: rrd.map((e) => Point( + e.time!.millisecondsSinceEpoch, + e.loadavg ?? 0)), + icon: + Icon(Icons.show_chart, color: fgColor), + bottomRight: pageIndicator, + dataRenderer: (data) => + data.toStringAsFixed(2), ), - ], - ); - }, - ), + ), + ], + ); + }, ), ), - ProxmoxStreamBuilder( - bloc: tBloc, - builder: (context, taskState) { - if (taskState.tasks.isNotEmpty) { - return Padding( - padding: const EdgeInsets.all(4.0), - child: PveTaskExpansionTile( - task: taskState.tasks.first, - showMorePage: Provider( - create: (context) => PveTaskLogBloc( - apiClient: tBloc.apiClient, - init: PveTaskLogState.init(nodeID), - )..events.add(LoadTasks()), - dispose: (context, bloc) => bloc.dispose(), - child: const PveTaskLog(), - ), - ), - ); - } - return Container(); - }, ), - SizedBox( - height: 130, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ActionCard( - icon: const Icon( - Icons.power_settings_new, - size: 55, - color: Colors.white24, - ), - title: 'Power Settings', - onTap: () => - showPowerMenuBottomSheet(context, nBloc), + ProxmoxStreamBuilder( + bloc: tBloc, + builder: (context, taskState) { + if (taskState.tasks.isNotEmpty) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: PveTaskExpansionTile( + task: taskState.tasks.first, + showMorePage: Provider( + create: (context) => PveTaskLogBloc( + apiClient: tBloc.apiClient, + init: PveTaskLogState.init(nodeID), + )..events.add(LoadTasks()), + dispose: (context, bloc) => bloc.dispose(), + child: const PveTaskLog(), ), - ActionCard( - icon: const Icon( - Icons.queue_play_next, - size: 55, - color: Colors.white24, - ), - title: 'Console', - onTap: () => showConsoleMenuBottomSheet( - context, nBloc.apiClient, null, nodeID, 'node'), + ), + ); + } + return Container(); + }, + ), + SizedBox( + height: 130, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + 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, + size: 55, + color: Colors.white24, + ), + title: 'Console', + onTap: () => showConsoleMenuBottomSheet( + context, nBloc.apiClient, null, nodeID, 'node'), + ), + ], ), ), - PveResourceDataCardWidget( - expandable: false, - showTitleTrailing: true, - title: const Text( - 'Summary', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20, - ), + ), + PveResourceDataCardWidget( + expandable: false, + showTitleTrailing: true, + title: const Text( + 'Summary', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, + ), + ), + titleTrailing: Text(Renderers.renderDuration( + Duration(seconds: status?.uptime ?? 0))), + subtitle: Text(status?.pveversion ?? ''), + children: [ + ListTile( + dense: true, + title: Text(status?.kversion ?? 'unknown'), + subtitle: const Text('Kernel'), ), - titleTrailing: Text(Renderers.renderDuration( - Duration(seconds: status?.uptime ?? 0))), - subtitle: Text(status?.pveversion ?? ''), - children: [ + if (status?.cpuinfo != null) ListTile( dense: true, - title: Text(status?.kversion ?? 'unknown'), - subtitle: const Text('Kernel'), + title: Text( + '${status!.cpuinfo.cpus} x ${status.cpuinfo.model}'), + subtitle: Text( + 'CPU Information (Socket: ${status.cpuinfo.sockets})'), ), - if (status?.cpuinfo != null) - ListTile( - dense: true, - title: Text( - '${status!.cpuinfo.cpus} x ${status.cpuinfo.model}'), - subtitle: Text( - 'CPU Information (Socket: ${status.cpuinfo.sockets})'), - ), - if (status?.ksm.shared ?? false) - CheckboxListTile( - dense: true, - value: status?.ksm.shared ?? false, - title: const Text('Kernel same-page merging (KSM)', - style: TextStyle(color: Colors.black)), - onChanged: null, - ), - if (status?.rootfs != null) ...[ - const Divider( - indent: 10, - endIndent: 10, - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: ListTile( - title: const Text('HD space (root)'), - subtitle: ProxmoxCapacityIndicator( - icon: FaIcon( - FontAwesomeIcons.solidHardDrive, - color: Colors.blueGrey[300], - ), - usedValue: - Renderers.formatSize(status!.rootfs.used), - totalValue: - Renderers.formatSize(status.rootfs.total), - usedPercent: - (status.rootfs.used) / (status.rootfs.total), + if (status?.ksm.shared ?? false) + CheckboxListTile( + dense: true, + value: status?.ksm.shared ?? false, + title: const Text('Kernel same-page merging (KSM)', + style: TextStyle(color: Colors.black)), + onChanged: null, + ), + if (status?.rootfs != null) ...[ + const Divider( + indent: 10, + endIndent: 10, + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: ListTile( + title: const Text('HD space (root)'), + subtitle: ProxmoxCapacityIndicator( + icon: FaIcon( + FontAwesomeIcons.solidHardDrive, + color: Colors.blueGrey[300], ), + usedValue: + Renderers.formatSize(status!.rootfs.used), + totalValue: + Renderers.formatSize(status.rootfs.total), + usedPercent: + (status.rootfs.used) / (status.rootfs.total), ), ), - ], - ], - ), - PveResourceDataCardWidget( - expandable: true, - showTitleTrailing: !state.allServicesRunning, - titleTrailing: const Icon(Icons.warning), - title: const Text( - 'Services', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20, ), + ], + ], + ), + PveResourceDataCardWidget( + expandable: true, + showTitleTrailing: !state.allServicesRunning, + titleTrailing: const Icon(Icons.warning), + title: const Text( + 'Services', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (state.allServicesRunning) const Text('All running'), - if (!state.allServicesRunning) - const Text('One or more not running'), - const Divider(), - ], - ), - children: state.services - .map( - (s) => ListTile( - dense: true, - title: Text(s.name), - subtitle: Text(s.desc), - trailing: getServiceStateIcon(context, s), - ), - ) - .toList() - ..sort((a, b) => (a.title as Text) - .data! - .compareTo((b.title as Text).data!)), ), - PveResourceDataCardWidget( - expandable: !state.updatesQueryPermissionFailure, - showTitleTrailing: state.updates.isNotEmpty, - titleTrailing: const Icon(Icons.info_outline), - title: const Text( - 'Updates', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20, - ), - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (state.updatesQueryPermissionFailure) - const Text('Lacking permission to query updates'), - if (state.updates.isEmpty && - !state.updatesQueryPermissionFailure) - const Text('No updates available'), - if (state.updates.isNotEmpty) - Text( - '${state.updates.length} packages are ready to update'), - if (!state.updatesQueryPermissionFailure) - const Divider(), - ], + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (state.allServicesRunning) const Text('All running'), + if (!state.allServicesRunning) + const Text('One or more not running'), + const Divider(), + ], + ), + children: state.services + .map( + (s) => ListTile( + dense: true, + title: Text(s.name), + subtitle: Text(s.desc), + trailing: getServiceStateIcon(context, s), + ), + ) + .toList() + ..sort((a, b) => (a.title as Text) + .data! + .compareTo((b.title as Text).data!)), + ), + PveResourceDataCardWidget( + expandable: !state.updatesQueryPermissionFailure, + showTitleTrailing: state.updates.isNotEmpty, + titleTrailing: const Icon(Icons.info_outline), + title: const Text( + 'Updates', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, ), - children: state.updates - .map( - (s) => ListTile( - dense: true, - title: Text(s.title), - subtitle: Text( - '${s.package}: ${s.oldVersion ?? ''} -> ${s.version}'), - trailing: IconButton( - icon: const Icon( - Icons.info, - ), - onPressed: () => showDialog( - context: context, - builder: (context) => SimpleDialog( - title: const Text('Description'), - contentPadding: const EdgeInsets.all(24), - children: [ - Text( - s.description ?? '', - style: const TextStyle(fontSize: 12), - ) - ], - ), + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (state.updatesQueryPermissionFailure) + const Text('Lacking permission to query updates'), + if (state.updates.isEmpty && + !state.updatesQueryPermissionFailure) + const Text('No updates available'), + if (state.updates.isNotEmpty) + Text( + '${state.updates.length} packages are ready to update'), + if (!state.updatesQueryPermissionFailure) const Divider(), + ], + ), + children: state.updates + .map( + (s) => ListTile( + dense: true, + title: Text(s.title), + subtitle: Text( + '${s.package}: ${s.oldVersion ?? ''} -> ${s.version}'), + trailing: IconButton( + icon: const Icon( + Icons.info, + ), + onPressed: () => showDialog( + context: context, + builder: (context) => SimpleDialog( + title: const Text('Description'), + contentPadding: const EdgeInsets.all(24), + children: [ + Text( + s.description ?? '', + style: const TextStyle(fontSize: 12), + ) + ], ), ), ), - ) - .toList() - ..sort((a, b) => (a.title as Text) - .data! - .compareTo((b.title as Text).data!)), - ), - PveResourceDataCardWidget( - expandable: false, - title: const Text( - 'Disks', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20, - ), + ), + ) + .toList() + ..sort((a, b) => (a.title as Text) + .data! + .compareTo((b.title as Text).data!)), + ), + PveResourceDataCardWidget( + expandable: false, + title: const Text( + 'Disks', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, ), - showTitleTrailing: !state.allDisksHealthy, - titleTrailing: const Icon(Icons.warning), - subtitle: state.allDisksHealthy - ? const Text('No health issues') - : const Text('Check disks, health error indicated!'), - children: state.disks - .map( - (d) => ListTile( - dense: true, - leading: FaIcon(FontAwesomeIcons.solidHardDrive, - color: state.isDiskHealthy(d) - ? Colors.grey - : Colors.red), - title: - Text('${d.type!.toUpperCase()}: ${d.devPath}'), - subtitle: Text( - 'Usage: ${d.used} ${Renderers.formatSize(d.size ?? 0)}'), - trailing: IconButton( - icon: const Icon( - Icons.info, - ), - onPressed: () => showDialog( - context: context, - builder: (context) => SimpleDialog( - title: const Text('Details'), - contentPadding: const EdgeInsets.all(24), - children: [ - ListTile( - title: const Text("Device"), - subtitle: Text(d.devPath!), - ), - ListTile( - title: const Text("Type"), - subtitle: Text(d.type!), - ), - ListTile( - title: const Text("Usage"), - subtitle: Text(d.used!), - ), - ListTile( - title: const Text("GPT"), - subtitle: Text(d.gpt.toString()), - ), - ListTile( - title: const Text("Model"), - subtitle: Text(d.model!), - ), - ListTile( - title: const Text("Serial"), - subtitle: Text(d.serial!), - ), - ListTile( - title: const Text("S.M.A.R.T"), - subtitle: Text(d.health!), - ), - ListTile( - title: const Text("Wearout"), - subtitle: Text(d.wearoutPercentage), - ) - ], - ), + ), + showTitleTrailing: !state.allDisksHealthy, + titleTrailing: const Icon(Icons.warning), + subtitle: state.allDisksHealthy + ? const Text('No health issues') + : const Text('Check disks, health error indicated!'), + children: state.disks + .map( + (d) => ListTile( + dense: true, + leading: FaIcon(FontAwesomeIcons.solidHardDrive, + color: state.isDiskHealthy(d) + ? Colors.grey + : Colors.red), + title: Text('${d.type!.toUpperCase()}: ${d.devPath}'), + subtitle: Text( + 'Usage: ${d.used} ${Renderers.formatSize(d.size ?? 0)}'), + trailing: IconButton( + icon: const Icon( + Icons.info, + ), + onPressed: () => showDialog( + context: context, + builder: (context) => SimpleDialog( + title: const Text('Details'), + contentPadding: const EdgeInsets.all(24), + children: [ + ListTile( + title: const Text("Device"), + subtitle: Text(d.devPath!), + ), + ListTile( + title: const Text("Type"), + subtitle: Text(d.type!), + ), + ListTile( + title: const Text("Usage"), + subtitle: Text(d.used!), + ), + ListTile( + title: const Text("GPT"), + subtitle: Text(d.gpt.toString()), + ), + ListTile( + title: const Text("Model"), + subtitle: Text(d.model!), + ), + ListTile( + title: const Text("Serial"), + subtitle: Text(d.serial!), + ), + ListTile( + title: const Text("S.M.A.R.T"), + subtitle: Text(d.health!), + ), + ListTile( + title: const Text("Wearout"), + subtitle: Text(d.wearoutPercentage), + ) + ], ), ), ), - ) - .toList() - ..sort((a, b) => (a.title as Text) - .data! - .compareTo((b.title as Text).data!)), - ), - SizedBox(height: MediaQuery.of(context).viewPadding.bottom) - ], - ), + ), + ) + .toList() + ..sort((a, b) => (a.title as Text) + .data! + .compareTo((b.title as Text).data!)), + ), + SizedBox(height: MediaQuery.of(context).viewPadding.bottom) + ], ), + ), ); }, ); diff --git a/lib/widgets/pve_qemu_overview.dart b/lib/widgets/pve_qemu_overview.dart index 2f366a8..322fcf7 100644 --- a/lib/widgets/pve_qemu_overview.dart +++ b/lib/widgets/pve_qemu_overview.dart @@ -67,176 +67,173 @@ class PveQemuOverview extends StatelessWidget { ), backgroundColor: Theme.of(context).colorScheme.surfaceContainer, body: SingleChildScrollView( - child: Column( - children: [ - PveGuestOverviewHeader( - background: !(status?.template ?? false) - ? PveGuestHeaderRRDPageView( - rrdData: rrdData, - ) - : const Center( - child: Text( - "TEMPLATE", - style: TextStyle( - color: Colors.white, - ), + child: Column( + children: [ + PveGuestOverviewHeader( + background: !(status?.template ?? false) + ? PveGuestHeaderRRDPageView( + rrdData: rrdData, + ) + : const Center( + child: Text( + "TEMPLATE", + style: TextStyle( + color: Colors.white, ), ), - width: width, - guestID: guestID, - guestStatus: status?.getQemuStatus(), - guestName: config?.name ?? 'VM $guestID', - guestNodeID: state.nodeID, - guestType: 'qemu', - ha: status?.ha, - template: status?.template ?? false, - ), - ProxmoxStreamBuilder( - bloc: taskBloc, - builder: (context, taskState) { - if (taskState.tasks.isNotEmpty) { - return PveTaskExpansionTile( - headerColor: - Theme.of(context).colorScheme.onSurfaceVariant, - task: taskState.tasks.first, - showMorePage: Provider( - create: (context) => PveTaskLogBloc( - apiClient: taskBloc.apiClient, - init: PveTaskLogState.init(state.nodeID), + ), + width: width, + guestID: guestID, + guestStatus: status?.getQemuStatus(), + guestName: config?.name ?? 'VM $guestID', + guestNodeID: state.nodeID, + guestType: 'qemu', + ha: status?.ha, + template: status?.template ?? false, + ), + ProxmoxStreamBuilder( + bloc: taskBloc, + builder: (context, taskState) { + if (taskState.tasks.isNotEmpty) { + return PveTaskExpansionTile( + headerColor: + Theme.of(context).colorScheme.onSurfaceVariant, + task: taskState.tasks.first, + showMorePage: Provider( + create: (context) => PveTaskLogBloc( + apiClient: taskBloc.apiClient, + init: PveTaskLogState.init(state.nodeID), + ) + ..events.add( + FilterTasksByGuestID( + guestID: guestID, + ), ) - ..events.add( - FilterTasksByGuestID( - guestID: guestID, - ), - ) - ..events.add(LoadTasks()), - dispose: (context, bloc) => bloc.dispose(), - child: const PveTaskLog(), - ), - ); - } - return Container(); - }, - ), - SizedBox( - height: 130, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - if (!(status?.template ?? false)) - ActionCard.withIcon( - 'Power Settings', - Icons.power_settings_new, - () => - showPowerMenuBottomSheet(context, bloc)), - if (!(status?.template ?? false)) - ActionCard.withIcon( - 'Console', - Icons.queue_play_next, - () => showConsoleMenuBottomSheet( - context, - bloc.apiClient, - guestID, - state.nodeID, - 'qemu', - allowSpice: status?.spice ?? false, - )), + ..events.add(LoadTasks()), + dispose: (context, bloc) => bloc.dispose(), + child: const PveTaskLog(), + ), + ); + } + return Container(); + }, + ), + SizedBox( + height: 130, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + if (!(status?.template ?? false)) + ActionCard.withIcon( + 'Power Settings', + Icons.power_settings_new, + () => showPowerMenuBottomSheet(context, bloc)), + if (!(status?.template ?? false)) ActionCard.withIcon( - 'Options', - Icons.settings, - () => Navigator.of(context) - .push(_createOptionsRoute(bloc))), - if (!rBloc.latestState.isStandalone) - ActionCard.withFaIcon( - 'Migrate', - FontAwesomeIcons.paperPlane, - () => Navigator.of(context).push( - _createMigrationRoute(guestID, - state.nodeID, bloc.apiClient))), + 'Console', + Icons.queue_play_next, + () => showConsoleMenuBottomSheet( + context, + bloc.apiClient, + guestID, + state.nodeID, + 'qemu', + allowSpice: status?.spice ?? false, + )), + ActionCard.withIcon( + 'Options', + Icons.settings, + () => Navigator.of(context) + .push(_createOptionsRoute(bloc))), + if (!rBloc.latestState.isStandalone) ActionCard.withFaIcon( - 'Backup', - FontAwesomeIcons.floppyDisk, + 'Migrate', + FontAwesomeIcons.paperPlane, () => Navigator.of(context).push( - _createBackupRoute(guestID, state.nodeID, + _createMigrationRoute(guestID, state.nodeID, bloc.apiClient))), - ], - ), + ActionCard.withFaIcon( + 'Backup', + FontAwesomeIcons.floppyDisk, + () => Navigator.of(context).push( + _createBackupRoute( + guestID, state.nodeID, bloc.apiClient))), + ], ), ), - if (config != null) - PveResourceDataCardWidget( - expandable: false, - title: const Text( - 'Hardware', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20, - ), + ), + if (config != null) + PveResourceDataCardWidget( + expandable: false, + title: const Text( + 'Hardware', + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 20, ), - children: [ - ListTile( - leading: const FaIcon(FontAwesomeIcons.memory), - title: Text('${config.memory}'), - subtitle: const Text('Memory'), - dense: true, - ), - ListTile( - leading: const Icon(Icons.memory), - title: Text( - '${config.cores} Cores ${config.sockets} Socket'), - subtitle: const Text('Processor'), - dense: true, - ), + ), + children: [ + ListTile( + leading: const FaIcon(FontAwesomeIcons.memory), + title: Text('${config.memory}'), + subtitle: const Text('Memory'), + dense: true, + ), + ListTile( + leading: const Icon(Icons.memory), + title: Text( + '${config.cores} Cores ${config.sockets} Socket'), + subtitle: const Text('Processor'), + dense: true, + ), + ListTile( + leading: const FaIcon(FontAwesomeIcons.microchip), + title: + Text(config.bios?.name ?? 'Default (SeaBIOS)'), + subtitle: const Text('BIOS'), + dense: true, + ), + ListTile( + leading: const FaIcon(FontAwesomeIcons.gears), + dense: true, + title: Text(config.machine ?? 'Default (i440fx)'), + subtitle: const Text('Machine Type'), + ), + ListTile( + leading: const FaIcon(FontAwesomeIcons.database), + title: + Text(config.scsihw?.name ?? 'Default (i440fx)'), + subtitle: const Text('SCSI Controller'), + dense: true, + ), + for (var ide in config.ide!) ListTile( - leading: const FaIcon(FontAwesomeIcons.microchip), - title: Text( - config.bios?.name ?? 'Default (SeaBIOS)'), - subtitle: const Text('BIOS'), + leading: + const FaIcon(FontAwesomeIcons.compactDisc), + title: Text(ide), + subtitle: const Text('CD/DVD Drive'), dense: true, ), + for (var scsi in config.scsi!) ListTile( - leading: const FaIcon(FontAwesomeIcons.gears), + leading: const FaIcon(FontAwesomeIcons.hardDrive), + title: Text(scsi), + subtitle: const Text('Hard Disk'), dense: true, - title: Text(config.machine ?? 'Default (i440fx)'), - subtitle: const Text('Machine Type'), ), + for (var net in config.net!) ListTile( - leading: const FaIcon(FontAwesomeIcons.database), - title: Text( - config.scsihw?.name ?? 'Default (i440fx)'), - subtitle: const Text('SCSI Controller'), + leading: const FaIcon(FontAwesomeIcons.ethernet), dense: true, - ), - for (var ide in config.ide!) - ListTile( - leading: - const FaIcon(FontAwesomeIcons.compactDisc), - title: Text(ide), - subtitle: const Text('CD/DVD Drive'), - dense: true, - ), - for (var scsi in config.scsi!) - ListTile( - leading: - const FaIcon(FontAwesomeIcons.hardDrive), - title: Text(scsi), - subtitle: const Text('Hard Disk'), - dense: true, - ), - for (var net in config.net!) - ListTile( - leading: - const FaIcon(FontAwesomeIcons.ethernet), - dense: true, - subtitle: const Text('Network Device'), - title: Text(net), - ) - ]), - SizedBox(height: MediaQuery.of(context).viewPadding.bottom) - ], - )), + subtitle: const Text('Network Device'), + title: Text(net), + ) + ]), + SizedBox(height: MediaQuery.of(context).viewPadding.bottom) + ], + )), ); }), ); -- 2.50.1