From ce7a260760bafa81efb5e29fb6512fae084b9f99 Mon Sep 17 00:00:00 2001 From: lukas-heiligenbrunner Date: Fri, 29 Dec 2023 18:13:51 +0100 Subject: [PATCH] display api stuff in frontend --- backend/src/api/backend.rs | 1 - backend/src/api/embed.rs | 2 +- backend/src/api/list.rs | 43 ++--- backend/src/api/mod.rs | 4 +- backend/src/builder/builder.rs | 3 +- backend/src/db/builds.rs | 26 ++- backend/src/db/packages.rs | 12 +- backend/src/db/versions.rs | 8 + backend/src/main.rs | 9 +- frontend/lib/api/API.dart | 4 + frontend/lib/api/api_client.dart | 16 ++ frontend/lib/api/builds.dart | 13 ++ frontend/lib/api/packages.dart | 22 +++ frontend/lib/core/models/build.dart | 19 ++ frontend/lib/core/models/package.dart | 21 +++ ...details_mini_card.dart => chart_card.dart} | 0 .../screens/dashboard/components/charts.dart | 15 +- .../screens/dashboard/components/header.dart | 22 ++- .../components/mini_information_card.dart | 5 +- .../components/mini_information_widget.dart | 6 +- .../dashboard/components/recent_builds.dart | 97 ++++++++--- .../dashboard/components/recent_users.dart | 91 ---------- .../components/user_details_widget.dart | 2 +- .../dashboard/components/your_packages.dart | 162 ++++++++++++++++++ .../screens/dashboard/dashboard_screen.dart | 4 +- .../screens/home/components/side_menu.dart | 4 +- frontend/pubspec.lock | 8 + frontend/pubspec.yaml | 5 +- 28 files changed, 441 insertions(+), 183 deletions(-) create mode 100644 frontend/lib/api/API.dart create mode 100644 frontend/lib/api/api_client.dart create mode 100644 frontend/lib/api/builds.dart create mode 100644 frontend/lib/api/packages.dart create mode 100644 frontend/lib/core/models/build.dart create mode 100644 frontend/lib/core/models/package.dart rename frontend/lib/screens/dashboard/components/{user_details_mini_card.dart => chart_card.dart} (100%) delete mode 100644 frontend/lib/screens/dashboard/components/recent_users.dart create mode 100644 frontend/lib/screens/dashboard/components/your_packages.dart diff --git a/backend/src/api/backend.rs b/backend/src/api/backend.rs index c55da91..1259e7c 100644 --- a/backend/src/api/backend.rs +++ b/backend/src/api/backend.rs @@ -20,6 +20,5 @@ pub fn build_api() -> Vec { version_del, build_output, list_builds, - ] } diff --git a/backend/src/api/embed.rs b/backend/src/api/embed.rs index 3522175..d6005b4 100644 --- a/backend/src/api/embed.rs +++ b/backend/src/api/embed.rs @@ -3,7 +3,7 @@ use rocket::http::uri::Segments; use rocket::http::{ContentType, Method, Status}; use rocket::route::{Handler, Outcome}; use rocket::{Data, Request, Response, Route}; -use rust_embed::{RustEmbed}; +use rust_embed::RustEmbed; use std::io::Cursor; #[derive(RustEmbed)] diff --git a/backend/src/api/list.rs b/backend/src/api/list.rs index 9f708ac..07c3d97 100644 --- a/backend/src/api/list.rs +++ b/backend/src/api/list.rs @@ -115,34 +115,37 @@ pub async fn build_output( #[serde(crate = "rocket::serde")] pub struct ListBuildsModel { id: i32, - pkg_id: i32, - version_id: i32, - status: Option, + pkg_name: String, + version: String, + status: i32, } #[openapi(tag = "test")] #[get("/builds?")] pub async fn list_builds( db: &State, - pkgid: i32, + pkgid: Option, ) -> Result>, NotFound> { let db = db as &DatabaseConnection; - let build = Builds::find() - .filter(builds::Column::PkgId.eq(pkgid)) - .all(db) - .await - .map_err(|e| NotFound(e.to_string()))?; + let basequery = Builds::find() + .join_rev(JoinType::InnerJoin, packages::Relation::Builds.def()) + .join_rev(JoinType::InnerJoin, versions::Relation::Builds.def()) + .select_only() + .column_as(builds::Column::Id, "id") + .column(builds::Column::Status) + .column_as(packages::Column::Name, "pkg_name") + .column(versions::Column::Version); - Ok(Json( - build - .iter() - .map(|x| ListBuildsModel { - id: x.id, - status: x.status, - pkg_id: x.pkg_id, - version_id: x.version_id, - }) - .collect::>(), - )) + let build = match pkgid { + None => basequery.into_model::().all(db), + Some(pkgid) => basequery + .filter(builds::Column::PkgId.eq(pkgid)) + .into_model::() + .all(db), + } + .await + .map_err(|e| NotFound(e.to_string()))?; + + Ok(Json(build)) } diff --git a/backend/src/api/mod.rs b/backend/src/api/mod.rs index c6e6ac5..1de5bb2 100644 --- a/backend/src/api/mod.rs +++ b/backend/src/api/mod.rs @@ -1,6 +1,6 @@ mod add; pub mod backend; -mod list; -mod remove; #[cfg(feature = "static")] pub mod embed; +mod list; +mod remove; diff --git a/backend/src/builder/builder.rs b/backend/src/builder/builder.rs index 1fc1c06..085631a 100644 --- a/backend/src/builder/builder.rs +++ b/backend/src/builder/builder.rs @@ -71,7 +71,6 @@ pub async fn init(db: DatabaseConnection, tx: Sender) { new_build.status = Set(Some(1)); let _ = new_build.update(&db).await; - } Err(e) => { let _ = set_pkg_status( @@ -82,7 +81,7 @@ pub async fn init(db: DatabaseConnection, tx: Sender) { .await; let _ = version_model.update(&db).await; - new_build.status = Set(Some(1)); + new_build.status = Set(Some(2)); let _ = new_build.update(&db).await; println!("Error: {e}") diff --git a/backend/src/db/builds.rs b/backend/src/db/builds.rs index 4375d30..2ea7feb 100644 --- a/backend/src/db/builds.rs +++ b/backend/src/db/builds.rs @@ -17,6 +17,30 @@ pub struct Model { } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] -pub enum Relation {} +pub enum Relation { + #[sea_orm( + belongs_to = "super::versions::Entity", + from = "Column::VersionId", + to = "super::versions::Column::Id" + )] + Versions, + #[sea_orm( + belongs_to = "super::packages::Entity", + from = "Column::PkgId", + to = "super::packages::Column::Id" + )] + Packages, +} +impl Related for Entity { + fn to() -> RelationDef { + Relation::Versions.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Packages.def() + } +} impl ActiveModelBehavior for ActiveModel {} diff --git a/backend/src/db/packages.rs b/backend/src/db/packages.rs index 791cb60..7b72f74 100644 --- a/backend/src/db/packages.rs +++ b/backend/src/db/packages.rs @@ -14,16 +14,24 @@ pub struct Model { pub status: i32, } +impl ActiveModelBehavior for ActiveModel {} + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm(has_many = "super::versions::Entity")] Versions, + #[sea_orm(has_many = "super::builds::Entity")] + Builds, } -impl ActiveModelBehavior for ActiveModel {} - impl Related for Entity { fn to() -> RelationDef { Relation::Versions.def() } } + +impl Related for crate::db::versions::Entity { + fn to() -> RelationDef { + crate::db::versions::Relation::Builds.def() + } +} diff --git a/backend/src/db/versions.rs b/backend/src/db/versions.rs index af23c07..e7f049c 100644 --- a/backend/src/db/versions.rs +++ b/backend/src/db/versions.rs @@ -23,6 +23,8 @@ pub enum Relation { to = "super::packages::Column::Id" )] Packages, + #[sea_orm(has_many = "super::builds::Entity")] + Builds, } // `Related` trait has to be implemented by hand @@ -32,4 +34,10 @@ impl Related for Entity { } } +// impl Related for Entity { +// fn to() -> RelationDef { +// Relation::Builds.def() +// } +// } + impl ActiveModelBehavior for ActiveModel {} diff --git a/backend/src/main.rs b/backend/src/main.rs index 861b175..181f5ed 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -6,6 +6,8 @@ mod pkgbuild; mod repo; use crate::api::backend; +#[cfg(feature = "static")] +use crate::api::embed::CustomHandler; use crate::builder::types::Action; use crate::db::migration::Migrator; use rocket::config::Config; @@ -16,8 +18,6 @@ use sea_orm::{Database, DatabaseConnection}; use sea_orm_migration::MigratorTrait; use std::fs; use tokio::sync::broadcast; -#[cfg(feature = "static")] -use crate::api::embed::CustomHandler; fn main() { let t = tokio::runtime::Runtime::new().unwrap(); @@ -56,7 +56,6 @@ fn main() { .manage(db) .manage(tx) .mount("/", backend::build_api()) - .mount( "/docs/", make_swagger_ui(&SwaggerUIConfig { @@ -67,9 +66,7 @@ fn main() { #[cfg(feature = "static")] let rock = rock.mount("/", CustomHandler {}); - let rock = rock - .launch() - .await; + let rock = rock.launch().await; match rock { Ok(_) => println!("Rocket shut down gracefully."), Err(err) => println!("Rocket had an error: {}", err), diff --git a/frontend/lib/api/API.dart b/frontend/lib/api/API.dart new file mode 100644 index 0000000..1b771eb --- /dev/null +++ b/frontend/lib/api/API.dart @@ -0,0 +1,4 @@ +import 'api_client.dart'; + +/// use this variable to access global api +final ApiClient API = ApiClient(); diff --git a/frontend/lib/api/api_client.dart b/frontend/lib/api/api_client.dart new file mode 100644 index 0000000..6799fd2 --- /dev/null +++ b/frontend/lib/api/api_client.dart @@ -0,0 +1,16 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; + +class ApiClient { + static const String _apiBase = kDebugMode ? "http://localhost:8081" : ""; + final Dio _dio = Dio(BaseOptions(baseUrl: _apiBase)); + + String? token; + DateTime? tokenValidUntil; + + ApiClient(); + + Dio getRawClient() { + return _dio; + } +} diff --git a/frontend/lib/api/builds.dart b/frontend/lib/api/builds.dart new file mode 100644 index 0000000..0cd2c59 --- /dev/null +++ b/frontend/lib/api/builds.dart @@ -0,0 +1,13 @@ +import '../core/models/build.dart'; +import 'api_client.dart'; + +extension BuildsAPI on ApiClient { + Future> listAllBuilds() async { + final resp = await getRawClient().get("/builds"); + + final responseObject = resp.data as List; + final List packages = + responseObject.map((e) => Build.fromJson(e)).toList(growable: false); + return packages; + } +} diff --git a/frontend/lib/api/packages.dart b/frontend/lib/api/packages.dart new file mode 100644 index 0000000..8ab6287 --- /dev/null +++ b/frontend/lib/api/packages.dart @@ -0,0 +1,22 @@ +import '../core/models/package.dart'; +import 'api_client.dart'; + +extension PackagesAPI on ApiClient { + Future> listPackages() async { + final resp = await getRawClient().get("/packages/list"); + print(resp.data); + + // todo error handling + + final responseObject = resp.data as List; + final List packages = + responseObject.map((e) => Package.fromJson(e)).toList(growable: false); + return packages; + } + + Future addPackage({bool force = false, required String name}) async { + final resp = await getRawClient().post("/packages/add", data: {'force_build': force, 'name': name}); + print(resp.data); + } + +} diff --git a/frontend/lib/core/models/build.dart b/frontend/lib/core/models/build.dart new file mode 100644 index 0000000..96f90c5 --- /dev/null +++ b/frontend/lib/core/models/build.dart @@ -0,0 +1,19 @@ +class Build { + final int id; + final String pkg_name; + final String version; + final int status; + + Build( + {required this.id,required this.pkg_name, required this.version, + required this.status}); + + factory Build.fromJson(Map json) { + return Build( + id: json["id"] as int, + status: json["status"] as int, + pkg_name: json["pkg_name"] as String, + version: json["version"] as String, + ); + } +} \ No newline at end of file diff --git a/frontend/lib/core/models/package.dart b/frontend/lib/core/models/package.dart new file mode 100644 index 0000000..917b243 --- /dev/null +++ b/frontend/lib/core/models/package.dart @@ -0,0 +1,21 @@ +class Package { + final int id; + final String name; + final int count; + final int status; + + Package( + {required this.id, + required this.name, + required this.count, + required this.status}); + + factory Package.fromJson(Map json) { + return Package( + id: json["id"] as int, + count: json["count"] as int, + status: json["status"] as int, + name: json["name"] as String, + ); + } +} diff --git a/frontend/lib/screens/dashboard/components/user_details_mini_card.dart b/frontend/lib/screens/dashboard/components/chart_card.dart similarity index 100% rename from frontend/lib/screens/dashboard/components/user_details_mini_card.dart rename to frontend/lib/screens/dashboard/components/chart_card.dart diff --git a/frontend/lib/screens/dashboard/components/charts.dart b/frontend/lib/screens/dashboard/components/charts.dart index 8546f71..afdc604 100644 --- a/frontend/lib/screens/dashboard/components/charts.dart +++ b/frontend/lib/screens/dashboard/components/charts.dart @@ -28,14 +28,15 @@ class _ChartState extends State { aspectRatio: 1, child: PieChart( PieChartData( - pieTouchData: - PieTouchData(touchCallback: (pieTouchResponse, touchresponse) { + pieTouchData: PieTouchData( + touchCallback: (pieTouchResponse, touchresponse) { setState(() { - // final desiredTouch = pieTouchResponse.touchInput - // is! PointerExitEvent && - // pieTouchResponse.touchInput is! PointerUpEvent; - if ( touchresponse?.touchedSection != null) { - touchedIndex = touchresponse!.touchedSection!.touchedSectionIndex; + // final desiredTouch = pieTouchResponse.touchInput + // is! PointerExitEvent && + // pieTouchResponse.touchInput is! PointerUpEvent; + if (touchresponse?.touchedSection != null) { + touchedIndex = touchresponse! + .touchedSection!.touchedSectionIndex; } else { touchedIndex = -1; } diff --git a/frontend/lib/screens/dashboard/components/header.dart b/frontend/lib/screens/dashboard/components/header.dart index 4c64fe7..97afde5 100644 --- a/frontend/lib/screens/dashboard/components/header.dart +++ b/frontend/lib/screens/dashboard/components/header.dart @@ -1,6 +1,8 @@ +import 'package:aurcache/api/packages.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import '../../../api/API.dart'; import '../../../core/constants/color_constants.dart'; import '../../../responsive.dart'; @@ -31,7 +33,7 @@ class Header extends StatelessWidget { height: 8, ), Text( - "Welcome to AURCentral", + "Welcome to your Build server", style: Theme.of(context).textTheme.subtitle2, ), ], @@ -82,29 +84,35 @@ class ProfileCard extends StatelessWidget { } class SearchField extends StatelessWidget { - const SearchField({ + SearchField({ Key? key, }) : super(key: key); + final controller = TextEditingController(); + @override Widget build(BuildContext context) { return TextField( + controller: controller, decoration: InputDecoration( hintText: "Search", fillColor: secondaryColor, filled: true, - border: OutlineInputBorder( + border: const OutlineInputBorder( borderSide: BorderSide.none, - borderRadius: const BorderRadius.all(Radius.circular(10)), + borderRadius: BorderRadius.all(Radius.circular(10)), ), suffixIcon: InkWell( - onTap: () {}, + onTap: () { + // todo this is only temporary -> add this to a proper page + API.addPackage(name: controller.text); + }, child: Container( padding: EdgeInsets.all(defaultPadding * 0.75), margin: EdgeInsets.symmetric(horizontal: defaultPadding / 2), - decoration: BoxDecoration( + decoration: const BoxDecoration( color: greenColor, - borderRadius: const BorderRadius.all(Radius.circular(10)), + borderRadius: BorderRadius.all(Radius.circular(10)), ), child: SvgPicture.asset( "assets/icons/Search.svg", diff --git a/frontend/lib/screens/dashboard/components/mini_information_card.dart b/frontend/lib/screens/dashboard/components/mini_information_card.dart index 5e660c6..77ff36e 100644 --- a/frontend/lib/screens/dashboard/components/mini_information_card.dart +++ b/frontend/lib/screens/dashboard/components/mini_information_card.dart @@ -30,8 +30,7 @@ class MiniInformation extends StatelessWidget { defaultPadding / (Responsive.isMobile(context) ? 2 : 1), ), ), - onPressed: () { - }, + onPressed: () {}, icon: Icon(Icons.add), label: Text( "Add New", @@ -68,7 +67,7 @@ class InformationCard extends StatelessWidget { @override Widget build(BuildContext context) { return GridView.builder( - physics: NeverScrollableScrollPhysics(), + physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: dailyDatas.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( diff --git a/frontend/lib/screens/dashboard/components/mini_information_widget.dart b/frontend/lib/screens/dashboard/components/mini_information_widget.dart index 9450d85..772e247 100644 --- a/frontend/lib/screens/dashboard/components/mini_information_widget.dart +++ b/frontend/lib/screens/dashboard/components/mini_information_widget.dart @@ -21,10 +21,10 @@ class _MiniInformationWidgetState extends State { @override Widget build(BuildContext context) { return Container( - padding: EdgeInsets.all(defaultPadding), - decoration: BoxDecoration( + padding: const EdgeInsets.all(defaultPadding), + decoration: const BoxDecoration( color: secondaryColor, - borderRadius: const BorderRadius.all(Radius.circular(10)), + borderRadius: BorderRadius.all(Radius.circular(10)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/frontend/lib/screens/dashboard/components/recent_builds.dart b/frontend/lib/screens/dashboard/components/recent_builds.dart index f94b21c..39947a0 100644 --- a/frontend/lib/screens/dashboard/components/recent_builds.dart +++ b/frontend/lib/screens/dashboard/components/recent_builds.dart @@ -1,12 +1,45 @@ +import 'dart:async'; + +import 'package:aurcache/api/builds.dart'; +import 'package:aurcache/core/models/build.dart'; +import 'package:aurcache/screens/dashboard/components/your_packages.dart'; import 'package:flutter/material.dart'; +import '../../../api/API.dart'; import '../../../core/constants/color_constants.dart'; +import '../../../core/models/package.dart'; -class RecentBuilds extends StatelessWidget { +class RecentBuilds extends StatefulWidget { const RecentBuilds({ Key? key, }) : super(key: key); + @override + State createState() => _RecentBuildsState(); +} + +class _RecentBuildsState extends State { + late Future> dataFuture; + Timer? timer; + + @override + void initState() { + super.initState(); + dataFuture = API.listAllBuilds(); + + timer = Timer.periodic( + const Duration(seconds: 10), + (Timer t) => setState(() { + dataFuture = API.listAllBuilds(); + })); + } + + @override + void dispose() { + super.dispose(); + timer?.cancel(); + } + @override Widget build(BuildContext context) { return Container( @@ -24,41 +57,49 @@ class RecentBuilds extends StatelessWidget { ), SizedBox( width: double.infinity, - child: DataTable( - horizontalMargin: 0, - columnSpacing: defaultPadding, - columns: [ - DataColumn( - label: Text("Build ID"), - ), - DataColumn( - label: Text("Package Name"), - ), - DataColumn( - label: Text("Version"), - ), - DataColumn( - label: Text("Status"), - ), - ], - rows: List.generate( - 7, - (index) => recentUserDataRow(), - ), - ), + child: FutureBuilder( + future: dataFuture, + builder: (context, snapshot) { + if (snapshot.hasData) { + return DataTable( + horizontalMargin: 0, + columnSpacing: defaultPadding, + columns: const [ + DataColumn( + label: Text("Build ID"), + ), + DataColumn( + label: Text("Package Name"), + ), + DataColumn( + label: Text("Version"), + ), + DataColumn( + label: Text("Status"), + ), + ], + rows: snapshot.data!.map((e) => recentUserDataRow(e)).toList(), + ); + } else { + return Text("no data"); + } + }), ), ], ), ); } - DataRow recentUserDataRow() { + DataRow recentUserDataRow(Build build) { return DataRow( cells: [ - DataCell(Text("1")), - DataCell(Text("Resources")), - DataCell(Text("v1.2.3")), - DataCell(Icon(Icons.watch_later_outlined, color: Color(0xFF9D8D00),)), + DataCell(Text(build.id.toString())), + DataCell(Text(build.pkg_name)), + DataCell(Text(build.version)), + DataCell(Icon( + switchSuccessIcon(build.status), + color: switchSuccessColor(build.status), + )), ], ); } diff --git a/frontend/lib/screens/dashboard/components/recent_users.dart b/frontend/lib/screens/dashboard/components/recent_users.dart deleted file mode 100644 index 390f054..0000000 --- a/frontend/lib/screens/dashboard/components/recent_users.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../../core/constants/color_constants.dart'; - -class RecentUsers extends StatelessWidget { - const RecentUsers({ - Key? key, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.all(defaultPadding), - decoration: BoxDecoration( - color: secondaryColor, - borderRadius: const BorderRadius.all(Radius.circular(10)), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Your Packages", - style: Theme.of(context).textTheme.subtitle1, - ), - SingleChildScrollView( - //scrollDirection: Axis.horizontal, - child: SizedBox( - width: double.infinity, - child: DataTable( - horizontalMargin: 0, - columnSpacing: defaultPadding, - columns: [ - DataColumn( - label: Text("Package ID"), - ), - DataColumn( - label: Text("Package Name"), - ), - DataColumn( - label: Text("Number of versions"), - ), - DataColumn( - label: Text("Status"), - ), - DataColumn( - label: Text("Action"), - ), - ], - rows: List.generate( - 7, - (index) => recentUserDataRow(context), - ), - ), - ), - ), - ], - ), - ); - } -} - -DataRow recentUserDataRow(BuildContext context) { - return DataRow( - cells: [ - DataCell(Text("1")), - DataCell(Text("Resources")), - DataCell(Text("2")), - DataCell(Icon(Icons.watch_later_outlined, color: Color(0xFF9D8D00),)), - DataCell( - Row( - children: [ - TextButton( - child: Text('View', style: TextStyle(color: greenColor)), - onPressed: () {}, - ), - SizedBox( - width: 6, - ), - TextButton( - child: Text("Delete", style: TextStyle(color: Colors.redAccent)), - onPressed: () { - - }, - // Delete - ), - ], - ), - ), - ], - ); -} diff --git a/frontend/lib/screens/dashboard/components/user_details_widget.dart b/frontend/lib/screens/dashboard/components/user_details_widget.dart index 711569d..efdb876 100644 --- a/frontend/lib/screens/dashboard/components/user_details_widget.dart +++ b/frontend/lib/screens/dashboard/components/user_details_widget.dart @@ -1,4 +1,4 @@ -import 'package:aurcache/screens/dashboard/components/user_details_mini_card.dart'; +import 'package:aurcache/screens/dashboard/components/chart_card.dart'; import 'package:flutter/material.dart'; import '../../../core/constants/color_constants.dart'; diff --git a/frontend/lib/screens/dashboard/components/your_packages.dart b/frontend/lib/screens/dashboard/components/your_packages.dart new file mode 100644 index 0000000..82c7f08 --- /dev/null +++ b/frontend/lib/screens/dashboard/components/your_packages.dart @@ -0,0 +1,162 @@ +import 'dart:async'; + +import 'package:aurcache/api/packages.dart'; +import 'package:flutter/material.dart'; + +import '../../../api/API.dart'; +import '../../../core/constants/color_constants.dart'; +import '../../../core/models/package.dart'; + +class YourPackages extends StatefulWidget { + const YourPackages({ + Key? key, + }) : super(key: key); + + @override + State createState() => _YourPackagesState(); +} + +class _YourPackagesState extends State { + late Future> dataFuture; + Timer? timer; + + @override + void initState() { + super.initState(); + dataFuture = API.listPackages(); + + timer = Timer.periodic( + const Duration(seconds: 10), + (Timer t) => setState(() { + dataFuture = API.listPackages(); + })); + } + + @override + void dispose() { + super.dispose(); + timer?.cancel(); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(defaultPadding), + decoration: const BoxDecoration( + color: secondaryColor, + borderRadius: BorderRadius.all(Radius.circular(10)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Your Packages", + style: Theme.of(context).textTheme.subtitle1, + ), + SingleChildScrollView( + //scrollDirection: Axis.horizontal, + child: SizedBox( + width: double.infinity, + child: FutureBuilder( + builder: (context, snapshot) { + if (snapshot.hasData) { + return DataTable( + horizontalMargin: 0, + columnSpacing: defaultPadding, + columns: const [ + DataColumn( + label: Text("Package ID"), + ), + DataColumn( + label: Text("Package Name"), + ), + DataColumn( + label: Text("Number of versions"), + ), + DataColumn( + label: Text("Status"), + ), + DataColumn( + label: Text("Action"), + ), + ], + rows: snapshot.data! + .map((e) => buildDataRow(e)) + .toList(growable: false), + ); + } else { + return const Text("No data"); + } + }, + future: dataFuture, + ), + ), + ), + ], + ), + ); + } + + DataRow buildDataRow(Package package) { + return DataRow( + cells: [ + DataCell(Text(package.id.toString())), + DataCell(Text(package.name)), + DataCell(Text(package.count.toString())), + DataCell(IconButton( + icon: Icon( + switchSuccessIcon(package.status), + color: switchSuccessColor(package.status), + ), + onPressed: () { + // todo open build info with logs + }, + )), + DataCell( + Row( + children: [ + TextButton( + child: const Text('View', style: TextStyle(color: greenColor)), + onPressed: () {}, + ), + const SizedBox( + width: 6, + ), + TextButton( + child: const Text("Delete", + style: TextStyle(color: Colors.redAccent)), + onPressed: () {}, + ), + ], + ), + ), + ], + ); + } +} + +IconData switchSuccessIcon(int status) { + switch (status) { + case 0: + return Icons.watch_later_outlined; + case 1: + return Icons.check_circle_outline; + case 2: + return Icons.cancel_outlined; + default: + return Icons.question_mark_outlined; + } +} + +Color switchSuccessColor(int status) { + switch (status) { + case 0: + return const Color(0xFF9D8D00); + case 1: + return const Color(0xFF0A6900); + case 2: + return const Color(0xff760707); + default: + return const Color(0xFF9D8D00); + } +} diff --git a/frontend/lib/screens/dashboard/dashboard_screen.dart b/frontend/lib/screens/dashboard/dashboard_screen.dart index 7cd8f37..8b03e8b 100644 --- a/frontend/lib/screens/dashboard/dashboard_screen.dart +++ b/frontend/lib/screens/dashboard/dashboard_screen.dart @@ -5,7 +5,7 @@ import '../../responsive.dart'; import 'components/header.dart'; import 'components/mini_information_card.dart'; import 'components/recent_builds.dart'; -import 'components/recent_users.dart'; +import 'components/your_packages.dart'; import 'components/user_details_widget.dart'; class DashboardScreen extends StatelessWidget { @@ -29,7 +29,7 @@ class DashboardScreen extends StatelessWidget { flex: 5, child: Column( children: [ - RecentUsers(), + YourPackages(), SizedBox(height: defaultPadding), RecentBuilds(), if (Responsive.isMobile(context)) diff --git a/frontend/lib/screens/home/components/side_menu.dart b/frontend/lib/screens/home/components/side_menu.dart index bf09afa..ef1f3a0 100644 --- a/frontend/lib/screens/home/components/side_menu.dart +++ b/frontend/lib/screens/home/components/side_menu.dart @@ -15,7 +15,7 @@ class SideMenu extends StatelessWidget { // it enables scrolling child: Column( children: [ - DrawerHeader( + const DrawerHeader( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -29,7 +29,7 @@ class SideMenu extends StatelessWidget { SizedBox( height: defaultPadding, ), - Text("AUR Build Server") + Text("AURCache") ], )), DrawerListTile( diff --git a/frontend/pubspec.lock b/frontend/pubspec.lock index 69edddb..1afddb5 100644 --- a/frontend/pubspec.lock +++ b/frontend/pubspec.lock @@ -65,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + dio: + dependency: "direct main" + description: + name: dio + sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3" + url: "https://pub.dev" + source: hosted + version: "5.4.0" equatable: dependency: transitive description: diff --git a/frontend/pubspec.yaml b/frontend/pubspec.yaml index b06e790..9d6819a 100644 --- a/frontend/pubspec.yaml +++ b/frontend/pubspec.yaml @@ -36,12 +36,9 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 fl_chart: ^0.66.0 -# flutter_icons: -# git: -# url: https://github.com/DaveatCor/flutter_icons_fork.git -# branch: master flutter_svg: ^2.0.9 google_fonts: ^6.1.0 + dio: ^5.3.3 dev_dependencies: flutter_test: