From 7a9ce396f4f91a544a9e3c46a1fef70c2489130c Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 31 Aug 2026 15:50:47 +0200 Subject: [PATCH] Implemented Reports Screen and some Icon details --- ac_web/lib/Layouts/MobileView.dart | 5 +- ac_web/lib/Pages/ReportDetails.dart | 26 ++------ ac_web/lib/Pages/Reports.dart | 76 ++++++++++++++++++++++ ac_web/lib/Pages/ReportsByType.dart | 32 +++++++++ ac_web/lib/Widgets/LatestReportColumn.dart | 48 ++++++++------ ac_web/lib/Widgets/LatestReportEntry.dart | 9 ++- ac_web/lib/Widgets/ReportTypeIcons.dart | 23 +++++++ 7 files changed, 175 insertions(+), 44 deletions(-) create mode 100644 ac_web/lib/Pages/Reports.dart create mode 100644 ac_web/lib/Pages/ReportsByType.dart create mode 100644 ac_web/lib/Widgets/ReportTypeIcons.dart diff --git a/ac_web/lib/Layouts/MobileView.dart b/ac_web/lib/Layouts/MobileView.dart index 6267e3d..5d9a0b0 100644 --- a/ac_web/lib/Layouts/MobileView.dart +++ b/ac_web/lib/Layouts/MobileView.dart @@ -1,5 +1,6 @@ import 'package:ac_web/Pages/AgentAvailabilities.dart'; import 'package:ac_web/Pages/LatestReports.dart'; +import 'package:ac_web/Pages/Reports.dart'; import 'package:flutter/material.dart'; class MobileView extends StatefulWidget { @@ -38,8 +39,8 @@ class MobileViewState extends State { ), body: [ - // Homepage - Text("Homepage"), + // Reports grouped by their type + Reports(), // Eighteen LatestReports(), diff --git a/ac_web/lib/Pages/ReportDetails.dart b/ac_web/lib/Pages/ReportDetails.dart index dea4184..b611a85 100644 --- a/ac_web/lib/Pages/ReportDetails.dart +++ b/ac_web/lib/Pages/ReportDetails.dart @@ -1,6 +1,6 @@ -import 'package:ac_web/Api/Dtos/EReportType.dart'; import 'package:ac_web/Api/Dtos/ReportDto.dart'; import 'package:ac_web/Widgets/MarkdownText.dart'; +import 'package:ac_web/Widgets/ReportTypeIcons.dart'; import 'package:flutter/material.dart'; /// Page that shows every detail of a single report, including the full text. @@ -25,9 +25,13 @@ class ReportDetails extends StatelessWidget{ spacing: 12, children: [ Row( - spacing: 8, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 16, children: [ - Icon(_iconOf(report.type)), + CircleAvatar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + child: Icon(report.type.icon, color: Theme.of(context).colorScheme.onSurface), + ), Expanded( child: Text( report.title.isEmpty ? "Untitled Report" : report.title, @@ -79,22 +83,6 @@ class ReportDetails extends StatelessWidget{ return "${timestamp.day}. ${_monthNames[timestamp.month - 1]} ${timestamp.year}, $hour:$minute"; } - static IconData _iconOf(EReportType type) { - switch(type){ - case EReportType.log: - return Icons.receipt_long; - case EReportType.codeReview: - return Icons.code; - case EReportType.projectPlan: - return Icons.assignment; - case EReportType.research: - return Icons.science; - case EReportType.newspaper: - return Icons.newspaper; - case EReportType.unknown: - return Icons.question_mark; - } - } static const List _monthNames = [ "January", diff --git a/ac_web/lib/Pages/Reports.dart b/ac_web/lib/Pages/Reports.dart new file mode 100644 index 0000000..ee4a509 --- /dev/null +++ b/ac_web/lib/Pages/Reports.dart @@ -0,0 +1,76 @@ +import 'package:ac_web/Api/Dtos/EReportType.dart'; +import 'package:ac_web/Pages/ReportsByType.dart'; +import 'package:ac_web/Widgets/LatestReportHeader.dart'; +import 'package:ac_web/Widgets/ReportTypeIcons.dart'; +import 'package:flutter/material.dart'; + +/// Overview page that lists every available report type as a button. +class Reports extends StatelessWidget { + const Reports({super.key}); + + @override + Widget build(BuildContext context) { + + List widgets = []; + + widgets.add(LatestReportHeader(text: "Reports", bigHeader: true,)); + widgets.add(const SizedBox(height: 8)); + + for (final EReportType type in EReportType.values) { + widgets.add(_buildButton(context, type)); + } + + return ListView( + padding: EdgeInsetsGeometry.all(8), + children: widgets, + ); + } + + Widget _buildButton(BuildContext context, EReportType type) { + + final ColorScheme colors = Theme.of(context).colorScheme; + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: Material( + color: colors.surfaceContainerHighest, + borderRadius: BorderRadius.circular(12), + clipBehavior: Clip.antiAlias, + child: InkWell( + onTap: () => _openType(context, type), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + CircleAvatar( + backgroundColor: colors.inversePrimary, + child: Icon(type.icon, color: colors.onSurface), + ), + const SizedBox(width: 16), + Expanded( + child: Text( + type.displayName, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + ), + const Icon(Icons.chevron_right), + ], + ), + ), + ), + ), + ); + } + + /// Opens a page that only shows the reports of the given type. + void _openType(BuildContext context, EReportType type) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => ReportsByType(type: type), + ), + ); + } +} diff --git a/ac_web/lib/Pages/ReportsByType.dart b/ac_web/lib/Pages/ReportsByType.dart new file mode 100644 index 0000000..a0b4a62 --- /dev/null +++ b/ac_web/lib/Pages/ReportsByType.dart @@ -0,0 +1,32 @@ +import 'package:ac_web/Api/Dtos/EReportType.dart'; +import 'package:ac_web/Widgets/LatestReportColumn.dart'; +import 'package:ac_web/Widgets/ReportTypeIcons.dart'; +import 'package:flutter/material.dart'; + +/// Shows all reports of a single [EReportType], grouped by day just like the +/// latest reports page. +class ReportsByType extends StatelessWidget { + const ReportsByType({super.key, required this.type}); + + final EReportType type; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: Row( + children: [ + Icon(type.icon), + const SizedBox(width: 8), + Text(type.displayName), + ], + ), + ), + body: LatestReportColumn( + title: type.displayName, + filterType: type, + ), + ); + } +} diff --git a/ac_web/lib/Widgets/LatestReportColumn.dart b/ac_web/lib/Widgets/LatestReportColumn.dart index 2bbb2e7..6f78602 100644 --- a/ac_web/lib/Widgets/LatestReportColumn.dart +++ b/ac_web/lib/Widgets/LatestReportColumn.dart @@ -4,10 +4,17 @@ import 'package:ac_web/Api/Dtos/ReportDto.dart'; import 'package:ac_web/Pages/ReportDetails.dart'; import 'package:ac_web/Widgets/LatestReportEntry.dart'; import 'package:ac_web/Widgets/LatestReportHeader.dart'; +import 'package:ac_web/Widgets/ReportTypeIcons.dart'; import 'package:flutter/material.dart'; class LatestReportColumn extends StatefulWidget{ - const LatestReportColumn({super.key}); + const LatestReportColumn({super.key, this.title = "Latest Reports", this.filterType}); + + /// Headline drawn above the first day header. + final String title; + + /// When set, only reports of this type are shown. + final EReportType? filterType; @override State createState() => _LatestReportColumnState(); @@ -38,7 +45,13 @@ class _LatestReportColumnState extends State{ } // The list of reports fetched from the ReportsController. - final List reports = snapshot.data ?? []; + List reports = snapshot.data ?? []; + + if (widget.filterType != null) { + reports = reports + .where((report) => report.type == widget.filterType) + .toList(); + } return _buildList(reports); }, @@ -49,8 +62,19 @@ class _LatestReportColumnState extends State{ List widgets = []; + if (reports.isEmpty) { + return ListView( + padding: EdgeInsetsGeometry.all(8), + children: [ + LatestReportHeader(text: widget.title, bigHeader: true,), + const SizedBox(height: 16), + const Text("No reports available."), + ], + ); + } + // Title Entry - widgets.add(LatestReportHeader(text: "Latest Reports", bigHeader: true,)); + widgets.add(LatestReportHeader(text: widget.title, bigHeader: true,)); DateTime? lastDay; @@ -67,23 +91,7 @@ class _LatestReportColumnState extends State{ final ReportDto report = reports[i]; - IconData icon; - - switch(reports[i].type){ - - case EReportType.log: - icon = Icons.receipt_long; - case EReportType.codeReview: - icon = Icons.code; - case EReportType.projectPlan: - icon = Icons.assignment; - case EReportType.research: - icon = Icons.science; - case EReportType.newspaper: - icon = Icons.newspaper; - case EReportType.unknown: - icon = Icons.question_mark; - } + final IconData icon = reports[i].type.icon; widgets.add(LatestReportEntry( text: reports[i].title, diff --git a/ac_web/lib/Widgets/LatestReportEntry.dart b/ac_web/lib/Widgets/LatestReportEntry.dart index 77ac32e..d49fce0 100644 --- a/ac_web/lib/Widgets/LatestReportEntry.dart +++ b/ac_web/lib/Widgets/LatestReportEntry.dart @@ -31,7 +31,10 @@ class LatestReportEntry extends StatelessWidget{ child: Container( child: Row( children: [ - Icon(icon), + CircleAvatar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + child: Icon(icon, color: Theme.of(context).colorScheme.onSurface), + ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -53,8 +56,8 @@ class LatestReportEntry extends StatelessWidget{ ), ), ], - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 8, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 16, ), margin: EdgeInsetsGeometry.all(16), ), diff --git a/ac_web/lib/Widgets/ReportTypeIcons.dart b/ac_web/lib/Widgets/ReportTypeIcons.dart new file mode 100644 index 0000000..fa3b81c --- /dev/null +++ b/ac_web/lib/Widgets/ReportTypeIcons.dart @@ -0,0 +1,23 @@ +import 'package:ac_web/Api/Dtos/EReportType.dart'; +import 'package:flutter/material.dart'; + +/// Icons used for the different report types, so that every page draws a +/// report type the same way. +extension ReportTypeIcons on EReportType { + IconData get icon { + switch (this) { + case EReportType.log: + return Icons.receipt_long; + case EReportType.codeReview: + return Icons.code; + case EReportType.projectPlan: + return Icons.assignment; + case EReportType.research: + return Icons.science; + case EReportType.newspaper: + return Icons.newspaper; + case EReportType.unknown: + return Icons.question_mark; + } + } +}