diff --git a/lib/core/models/shared_workspace_local.dart b/lib/core/models/shared_workspace_local.dart index 120bc95..a0186d4 100644 --- a/lib/core/models/shared_workspace_local.dart +++ b/lib/core/models/shared_workspace_local.dart @@ -14,6 +14,7 @@ class WorkSpaceItem { } class SharedWorkspaceLocal { + static String? current; static Map workspaces = {}; static final SharedService _service = SharedService(); @@ -23,7 +24,6 @@ class SharedWorkspaceLocal { var vals = value.data!.values; for (var element in vals) { var ws = SharedWorkspace().deserialize(element); - print(element); if (ws.id == null) { continue; } workspaces[ws.id!] = ws; } diff --git a/lib/core/models/workspace_local.dart b/lib/core/models/workspace_local.dart index f143a18..df9b307 100644 --- a/lib/core/models/workspace_local.dart +++ b/lib/core/models/workspace_local.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_flow_chart/flutter_flow_chart.dart'; import 'package:oc_front/core/sections/end_drawer.dart'; +import 'package:oc_front/models/response.dart'; import 'package:oc_front/models/search.dart'; import 'package:oc_front/models/workspace.dart'; import 'package:oc_front/core/services/specialized_services/workspace_service.dart'; @@ -34,14 +35,14 @@ class WorkspaceLocal { _service.put(context, ws.id!, { "active" : true }, {}); } if (ws.active == true && changeCurrent) { - print(ws.serialize()); current = ws.id; } fill(); } } else { - WorkspaceLocal.createWorkspace("main", context); + WorkspaceLocal.createWorkspace("main", null); } + print("qsdqsd $current"); }); } @@ -83,7 +84,7 @@ class WorkspaceLocal { static Future createWorkspace(String name, BuildContext? context) async { Workspace n = Workspace(name: name); - await _service.post(context, n.serialize(), {}).then((value) { + await _service.post(null, n.serialize(), {}).then((value) { if (value.data != null) { workspaces[value.data!.id!] = value.data!; current = value.data!.id; @@ -97,11 +98,12 @@ class WorkspaceLocal { } static void changeWorkspaceByName(String name) { - var id = workspaces.entries.firstWhere((element) => element.value.name == "${name}_workspace").key; + var id = workspaces.entries.firstWhere((element) => element.value.name == "${name}_workspace").key; changeWorkspace(id); } static void changeWorkspace(String id) { + print("WORKSPACES ID $id"); _service.put(null, id, { "active" : true }, {}); current = id; fill(); @@ -118,6 +120,25 @@ class WorkspaceLocal { } return res; } + + static List getWorkspacesShallow() { + List res = []; + for (var element in workspaces.values) { + res.add(Shallow(id: element.id ?? "", name: element.name ?? "")); + } + return res; + } + + static List byWorkspace(String id) { + List> d = []; + var w = workspaces[id]!; + d = [ ...d, ...w.datas.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ]; + d = [ ...d, ...w.datacenters.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ]; + d = [ ...d, ...w.processings.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ]; + d = [ ...d, ...w.storages.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ]; + d = [ ...d, ...w.workflows.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ]; + return d; + } static List byTopic(String topic, bool all) { if (all) { diff --git a/lib/core/sections/end_drawer.dart b/lib/core/sections/end_drawer.dart index 1b3ea89..65960d9 100644 --- a/lib/core/sections/end_drawer.dart +++ b/lib/core/sections/end_drawer.dart @@ -1,11 +1,14 @@ import 'package:flutter/material.dart'; import 'package:oc_front/core/models/shared_workspace_local.dart'; +import 'package:oc_front/core/services/specialized_services/shared_service.dart'; +import 'package:oc_front/core/services/specialized_services/workspace_service.dart'; +import 'package:oc_front/models/response.dart'; import 'package:oc_front/models/search.dart'; import 'package:oc_front/pages/catalog.dart'; import 'package:oc_front/core/models/workspace_local.dart'; +import 'package:oc_front/pages/shared.dart'; import 'package:oc_front/widgets/items/item_row.dart'; -import 'package:oc_front/widgets/menu_clipper/shared_workspace_menu.dart'; -import 'package:oc_front/widgets/menu_clipper/workspace_menu.dart'; +import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart'; GlobalKey endDrawerKey = GlobalKey(); class EndDrawerWidget extends StatefulWidget { @@ -17,8 +20,6 @@ class EndDrawerWidgetState extends State { @override Widget build(BuildContext context) { List itemRows = WorkspaceLocal.items.map( (e) => ItemRowWidget(contextWidth: 400, item: e, keys: [endDrawerKey, CatalogFactory.key],)).toList(); - print(WorkspaceLocal.workspaces[WorkspaceLocal.current]!.shared); - print(SharedWorkspaceLocal.workspaces); return Stack( children: [ Container( color: Colors.white, @@ -38,7 +39,19 @@ class EndDrawerWidgetState extends State { ), ), - MenuWorkspaceWidget(simpliest: true, width: 400), + ShallowDropdownInputWidget( + current: WorkspaceLocal.current, + width: 400, + all: () async => WorkspaceLocal.getWorkspacesShallow(), + canRemove: (p0) => p0 != null, + remove: (p0) async { + await WorkspaceService().delete(context, p0, {}).then( (e) => WorkspaceLocal.deleteWorkspace(p0)); + }, + type: SharedWorkspaceType.workspace, + change: (String? change) { + WorkspaceLocal.changeWorkspace(change.toString()); + } + ), Column( children: [ itemRows.isEmpty ? Container( height: MediaQuery.of(context).size.height - 100, color: Colors.grey.shade300, @@ -51,12 +64,29 @@ class EndDrawerWidgetState extends State { ]) ]) ), - Positioned( bottom: 0, left: 0, - child: SharedMenuWorkspaceWidget( width: 400, - excluded: const [], - before: WorkspaceLocal.workspaces[WorkspaceLocal.current]!.shared, - selected: WorkspaceLocal.workspaces[WorkspaceLocal.current]!.shared, - reverse: itemRows.isEmpty, contextID: WorkspaceLocal.current ?? "",) ) + itemRows.isEmpty ? Container() : Positioned( bottom: 0, left: 0, + child: ShallowDropdownInputWidget( + type: SharedWorkspaceType.workspace, + all: () async => SharedWorkspaceLocal.workspaces.values.map( + (e) => Shallow(id: e.id ?? "", name: e.name ?? "") ).toList(), + current: WorkspaceLocal.workspaces[WorkspaceLocal.current]?.shared, + width: 400, + filled: Colors.grey.shade300, + hintColor: Colors.grey, + color: Colors.black, + canLoad: (String? change) => SharedWorkspaceLocal.workspaces[change] == null + || !SharedWorkspaceLocal.workspaces[change]!.workspaces.map( (e) => e.id ).contains(WorkspaceLocal.current), + canRemove: (String? change) => SharedWorkspaceLocal.workspaces[change] == null + || SharedWorkspaceLocal.workspaces[change]!.workspaces.map( (e) => e.id ).contains(WorkspaceLocal.current), + load: (String val) async { + await SharedService().addWorkspace(context, val, WorkspaceLocal.current ?? ""); + SharedWorkspaceLocal.init(context, false); + }, + remove: (String val) async { + await SharedService().removeWorkspace(context, val, WorkspaceLocal.current ?? ""); + SharedWorkspaceLocal.init(context, false); + }) + ) ] ); } diff --git a/lib/core/sections/header/header.dart b/lib/core/sections/header/header.dart index 26187c9..64f93c2 100644 --- a/lib/core/sections/header/header.dart +++ b/lib/core/sections/header/header.dart @@ -2,27 +2,40 @@ import 'package:flutter/material.dart'; import 'package:oc_front/core/sections/header/menu.dart'; import 'package:oc_front/core/sections/header/search.dart'; import 'package:oc_front/core/services/router.dart'; -import 'package:oc_front/widgets/menu_clipper/clipper_menu.dart'; +import 'package:oc_front/widgets/menus/clipper_menu.dart'; class HeaderConstants { static final List noHeader = [ AppRouter.workflowItem, + AppRouter.workflowIDItem, ]; + static HeaderWidgetState? headerWidget; static double height = 200; + static String? title; + static String? description; + + static setTitle(String? v) { + title = v; + HeaderConstants.headerWidget?.setState(() {}); + } + + static setDescription(String? v) { + description = v; + HeaderConstants.headerWidget?.setState(() {}); + } static bool isNoHeader(String route) { return noHeader.where((element) => element.route == route).isNotEmpty; } } -GlobalKey headerWidgetKey = GlobalKey(); class HeaderWidget extends StatefulWidget { - HeaderWidget () : super(key: headerWidgetKey); + HeaderWidget () : super(key: null); @override HeaderWidgetState createState() => HeaderWidgetState(); } class HeaderWidgetState extends State { @override Widget build(BuildContext context) { - headerWidgetKey = GlobalKey(); + HeaderConstants.headerWidget = this; headerMenuKey.currentState?.closeMenu(); HeaderConstants.height = HeaderConstants.isNoHeader(AppRouter.currentRoute.route) ? 50 : 200; return Column( children: [ diff --git a/lib/core/sections/header/menu.dart b/lib/core/sections/header/menu.dart index 67f9f91..c2d7483 100644 --- a/lib/core/sections/header/menu.dart +++ b/lib/core/sections/header/menu.dart @@ -1,6 +1,6 @@ import 'package:oc_front/main.dart'; import 'package:flutter/material.dart'; -import 'package:oc_front/widgets/menu_clipper/clipper_menu.dart'; +import 'package:oc_front/widgets/menus/clipper_menu.dart'; import 'package:oc_front/widgets/dialog/login.dart'; class HeaderMenuWidget extends StatefulWidget{ @@ -17,8 +17,6 @@ class HeaderMenuWidgetState extends State { ), child: Padding(padding: const EdgeInsets.only(top: 5, bottom: 5, left: 50, right: 50), child: Stack(children: [ - /*...(searchWidgetKey.currentState == null ? [Positioned( left: -20, top: -5, - child: SvgPicture.asset("assets/images/icon.svg", height: 70, semanticsLabel: 'OpenCloud Logo'))] : []),*/ Row(crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisAlignment: MainAxisAlignment.end, children: [ diff --git a/lib/core/sections/header/search.dart b/lib/core/sections/header/search.dart index 47749c9..609e725 100644 --- a/lib/core/sections/header/search.dart +++ b/lib/core/sections/header/search.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:oc_front/core/sections/header/header.dart'; import 'package:oc_front/core/services/router.dart'; +import 'package:oc_front/pages/shared.dart'; +import 'package:oc_front/widgets/inputs/shallow_text_input.dart'; class SearchConstants { static final Map _searchHost = {}; @@ -34,21 +37,22 @@ class SearchWidgetState extends State { ) ) )] : []), - AppRouter.currentRoute.description != null ? + AppRouter.currentRoute.description != null + || (AppRouter.currentRoute.route.contains("shared") && HeaderConstants.title != null) ? Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - Text(AppRouter.currentRoute.description!, style: const TextStyle( + Text(HeaderConstants.title ?? AppRouter.currentRoute.description!, style: const TextStyle( color: Color.fromRGBO(38, 166, 154, 1), fontSize: 24, fontWeight: FontWeight.w600 )), Row(children: [ - ...(AppRouter.currentRoute.help == null || AppRouter.currentRoute.help!.isEmpty ? [] + ...(HeaderConstants.description == null && (AppRouter.currentRoute.help == null || AppRouter.currentRoute.help!.isEmpty) ? [] : [ const Padding( padding: EdgeInsets.only(right: 10), child: Icon(Icons.help_outline, color: Colors.grey, size: 20)), - Text(AppRouter.currentRoute.help ?? "", style: const TextStyle( + Text(HeaderConstants.description ?? AppRouter.currentRoute.help ?? "", style: const TextStyle( color: Colors.grey, fontSize: 14, fontWeight: FontWeight.w400 @@ -58,58 +62,25 @@ class SearchWidgetState extends State { ], ) : Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: MediaQuery.of(context).size.width - 400 > 0 ? MediaQuery.of(context).size.width - 300 - 100 : 200, - height: 50, - color: Colors.white, - child: TextField( - onChanged: (value) => SearchConstants.set(value), - decoration: InputDecoration( - hintText: "Search in ${AppRouter.currentRoute.route}...", - contentPadding: const EdgeInsets.symmetric(horizontal: 30), - hintStyle: const TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w300 - ), - border: InputBorder.none - ) - ) - ), - Tooltip( - message: 'search', - child: InkWell( - onTap: () { + mainAxisAlignment: MainAxisAlignment.center, + children: [ ShallowTextInputWidget( + width: MediaQuery.of(context).size.width - 400 > 0 ? MediaQuery.of(context).size.width - 300 - 100 : 200, + type: SharedWorkspaceType.workspace, + hint: "search in resources...", + iconLoad: Icons.search, + iconRemove: Icons.screen_search_desktop_outlined, + tooltipLoad: "search", + tooltipRemove: "distributed search", + canLoad: (String? str) => str != null && str.isNotEmpty, + canRemove: (String? str) => str != null && str.isNotEmpty, + change: (value) => SearchConstants.set(value), + loadStr: (String val) async { AppRouter.currentRoute.factory.search(context); }, - child: Container( - width: 50, - height: 50, - decoration: BoxDecoration( - color: Colors.black, - border: Border(right: BorderSide(color: Colors.white)), - ), - child: const Icon(Icons.search, color: Colors.white) - ) - ) - ), - Tooltip( - message: 'distributed search', - child: InkWell( - onTap: () { + remove: (String val) async { AppRouter.currentRoute.factory.search(context); }, - child: Container( - width: 50, - height: 50, - color: Colors.black, - child: const Icon(Icons.screen_search_desktop_outlined, color: Colors.white) - ) - ) - ) - ]) + ) ]), ]; return Container( width: MediaQuery.of(context).size.width, diff --git a/lib/core/services/router.dart b/lib/core/services/router.dart index ed3abfb..e4e4eee 100644 --- a/lib/core/services/router.dart +++ b/lib/core/services/router.dart @@ -1,3 +1,4 @@ +import 'package:oc_front/core/sections/header/header.dart'; import 'package:oc_front/main.dart'; import 'package:oc_front/pages/abstract_page.dart'; import 'package:oc_front/pages/catalog.dart'; @@ -43,6 +44,10 @@ class RouterItem { AppRouter.currentRoute = this; var newPath = "$path"; for (var arg in args) { newPath = newPath.replaceAll(":$arg", params[arg] ?? ""); } + Future.delayed( const Duration(seconds: 1), () { + HeaderConstants.setTitle(null); + HeaderConstants.setDescription(null); + }); context.go(newPath); } } @@ -50,8 +55,8 @@ class RouterItem { class AppRouter { static const String home = "catalog"; static final RouterItem workflowItem = RouterItem(icon: Icons.rebase_edit, label: "workflow manager", route: "workflow", - description: "View to select & create new workflow.", help: "Workflow only access to your workspace datas. If a an element of your flow is missing, perhaps means it's missing in workspace.", factory: WorkflowFactory()); + static final RouterItem workflowIDItem = RouterItem(description: "", help: "", route: "workflow/:id", factory: WorkflowFactory(), args: ["id"]); static final RouterItem catalogItem = RouterItem(description: "", help: "", route: "catalog/:id", factory: CatalogItemFactory(), args: ["id"]); static final RouterItem catalog= RouterItem(icon: Icons.book_outlined, label: "catalog searcher", route: home, factory: CatalogFactory()); @@ -64,6 +69,7 @@ class AppRouter { factory: DatacenterFactory()), RouterItem(icon: Icons.public_outlined, label: "localisations", route: "map", factory: MapFactory()), RouterItem(icon: Icons.share_rounded, label: "shared spaces", route: "shared", factory: SharedFactory()), + workflowIDItem, catalogItem, ]; static List history = []; diff --git a/lib/core/services/specialized_services/abstract_service.dart b/lib/core/services/specialized_services/abstract_service.dart index 9ad9d6d..814618d 100644 --- a/lib/core/services/specialized_services/abstract_service.dart +++ b/lib/core/services/specialized_services/abstract_service.dart @@ -15,6 +15,7 @@ abstract class AbstractService { return service.get("$subPath$id", true, context); } Future> post(BuildContext? context, Map body, Map params) { + print("Creating workspace $body"); return service.post(subPath, body, context); } Future> put(BuildContext? context, String id, Map body, Map params) { diff --git a/lib/core/services/specialized_services/peer_service.dart b/lib/core/services/specialized_services/peer_service.dart new file mode 100644 index 0000000..2548f02 --- /dev/null +++ b/lib/core/services/specialized_services/peer_service.dart @@ -0,0 +1,10 @@ +import 'package:oc_front/core/services/api_service.dart'; +import 'package:oc_front/core/services/specialized_services/abstract_service.dart'; +import 'package:oc_front/models/shared.dart'; + +class PeerService extends AbstractService { + @override APIService service = APIService( + baseURL: const String.fromEnvironment('PEER_HOST', defaultValue: 'http://localhost:8093') + ); + @override String subPath = "/oc/peer/"; +} \ No newline at end of file diff --git a/lib/models/response.dart b/lib/models/response.dart index fa19e49..87b0d6b 100644 --- a/lib/models/response.dart +++ b/lib/models/response.dart @@ -58,3 +58,17 @@ class RawData extends SerializerDeserializer { @override deserialize(dynamic json) { return RawData(values: json ?? []); } @override Map serialize() => { }; } + +abstract class ShallowData { + String getID(); + String getName(); +} + +class Shallow { + String id; + String name; + Shallow({ + this.id = "", + this.name = "", + }); +} \ No newline at end of file diff --git a/lib/models/shared.dart b/lib/models/shared.dart index e77d1ac..81567dc 100644 --- a/lib/models/shared.dart +++ b/lib/models/shared.dart @@ -1,5 +1,5 @@ -import 'package:flutter/material.dart'; import 'package:oc_front/models/abstract.dart'; +import 'package:oc_front/models/response.dart'; import 'package:oc_front/models/workflow.dart'; import 'package:oc_front/models/workspace.dart'; @@ -35,7 +35,7 @@ class SharedWorkspace extends SerializerDeserializer { id: json.containsKey("id") ? json["id"] : null, name: json.containsKey("name") ? json["name"] : null, description: json.containsKey("description") ? json["description"] : null, - creatorID: json.containsKey("creator_id") ? json["creator_id"] : null, + creatorID: json.containsKey("peer_id") ? json["peer_id"] : null, version: json.containsKey("version") ? json["version"] : null, attributes: json.containsKey("attributes") ? json["attributes"] : {}, workspaces: json.containsKey("shared_workspaces") ? fromListJson(json["shared_workspaces"], Workspace()) : [], @@ -87,13 +87,16 @@ class Rule extends SerializerDeserializer { }; } -class Peer extends SerializerDeserializer { +class Peer extends SerializerDeserializer implements ShallowData { String? id; String? name; Peer( {this.id, this.name,}); + + @override String getID() => id ?? ""; + @override String getName() => name ?? ""; @override deserialize(dynamic json) { diff --git a/lib/models/workflow.dart b/lib/models/workflow.dart index f78d9a9..a644831 100644 --- a/lib/models/workflow.dart +++ b/lib/models/workflow.dart @@ -4,6 +4,7 @@ import 'package:flutter_flow_chart/flutter_flow_chart.dart'; import 'package:oc_front/core/models/workspace_local.dart'; import 'package:oc_front/models/abstract.dart'; import 'package:oc_front/models/logs.dart'; +import 'package:oc_front/models/response.dart'; import 'package:oc_front/models/search.dart'; class Check extends SerializerDeserializer { @@ -98,7 +99,7 @@ class WorkflowExecution extends SerializerDeserializer { } -class Workflow extends SerializerDeserializer { +class Workflow extends SerializerDeserializer implements ShallowData { String? id; String? name; List data; @@ -125,9 +126,9 @@ class Workflow extends SerializerDeserializer { this.shared = const [], }); - String getID() { - return id ?? ""; - } + @override String getID() => id ?? ""; + @override String getName() => name ?? ""; + @override String getDescription() => ""; @override deserialize(dynamic json) { try { json = json as Map; @@ -210,20 +211,24 @@ class Scheduler extends SerializerDeserializer { id = j["id"]; name = j["name"]; cron = j["cron"]; + mode =j["mode"]; try { - start = DateTime.parse(j["start"]); + start = j["start"] != null ? DateTime.parse(j["start"]) : DateTime.now().add( const Duration(minutes: 1)).toUtc(); + if (start == DateTime.utc(0)) { + start = DateTime.now().add( const Duration(minutes: 1)).toUtc(); + } if (j.containsKey("end") && j["end"] != null) { end = DateTime.parse(j["end"]); } + } catch (e) {} - mode = int.parse(j["mode"].toString()); } Map toDashboard() { return { "id": id, "name": name, "cron": cron, - "mode": int.parse(mode.toString()), + "mode": mode ?? 1, "start": start?.toIso8601String(), "end": end?.toIso8601String(), }; @@ -241,15 +246,28 @@ class Scheduler extends SerializerDeserializer { end: json.containsKey("end") && json["end"] != null ? DateTime.parse(json["end"]) : null, ); } - @override Map serialize() => { - "id": id, - "name": name, - "cron": cron ?? "", - "mode": int.parse(mode.toString()), - "start": start?.toIso8601String(), - "end": end?.toIso8601String(), - }; + @override Map serialize() { + try { + return { + "id": id, + "name": name, + "cron": cron ?? "", + "mode": mode ?? 1, + "start": start?.toIso8601String(), + "end": end?.toIso8601String(), + }; + } catch (e) { + return { + "id": id, + "name": name, + "cron": cron ?? "", + "start": start?.toIso8601String(), + "end": end?.toIso8601String(), + }; + } + } } + class Graph extends SerializerDeserializer { double zoom; Map items = {}; @@ -505,24 +523,32 @@ class GraphItem extends SerializerDeserializer { if (abs.topic == "data") { data = DataItem().deserialize(abs.serialize()); data!.model = ResourceModel().deserialize(j["element"]["resource_model"]); - } - if (abs.topic == "processing") { + } else if (abs.topic == "processing") { processing = ProcessingItem().deserialize(abs.serialize()); processing!.model = ResourceModel().deserialize(j["element"]["resource_model"]); - } - if (abs.topic == "datacenter") { + } else if (abs.topic == "datacenter") { datacenter = DataCenterItem().deserialize(abs.serialize()); datacenter!.model = ResourceModel().deserialize(j["element"]["resource_model"]); - } - if (abs.topic == "storage") { + } else if (abs.topic == "storage") { storage = StorageItem().deserialize(abs.serialize()); storage!.model = ResourceModel().deserialize(j["element"]["resource_model"]); - } - if (abs.topic == "workflow") { + } else if (abs.topic == "workflow") { workflow = WorkflowItem().deserialize(abs.serialize()); workflow!.model = ResourceModel().deserialize(j["element"]["resource_model"]); + } else { + datacenter = null; + data = null; + processing = null; + storage = null; + workflow = null; } - } + } else { + datacenter = null; + data = null; + processing = null; + storage = null; + workflow = null; + } } Map toDashboard() { @@ -595,7 +621,7 @@ class Position extends SerializerDeserializer { } @override Map serialize() => { "id": id, - "x": x, + "x": x , "y": y, }; } \ No newline at end of file diff --git a/lib/models/workspace.dart b/lib/models/workspace.dart index 6727a35..144721a 100644 --- a/lib/models/workspace.dart +++ b/lib/models/workspace.dart @@ -1,7 +1,9 @@ +import 'package:flutter_flow_chart/flutter_flow_chart.dart'; import 'package:oc_front/models/abstract.dart'; +import 'package:oc_front/models/response.dart'; import 'package:oc_front/models/search.dart'; -class Workspace extends SerializerDeserializer { +class Workspace extends SerializerDeserializer implements ShallowData { String? id; String? name; bool? active; @@ -24,10 +26,12 @@ class Workspace extends SerializerDeserializer { this.shared, }); + @override String getID() => id ?? ""; + @override String getName() => name ?? ""; + @override deserialize(dynamic json) { try { json = json as Map; } catch (e) { return Workspace(); } - print(json); return Workspace( id: json.containsKey("id") ? json["id"] : null, shared: json["shared"], diff --git a/lib/pages/abstract_page.dart b/lib/pages/abstract_page.dart index f5de51d..39b605f 100644 --- a/lib/pages/abstract_page.dart +++ b/lib/pages/abstract_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; + abstract class AbstractFactory { Widget factory(GoRouterState state, List args); bool searchFill(); diff --git a/lib/pages/catalog.dart b/lib/pages/catalog.dart index 3bef792..02c9a58 100644 --- a/lib/pages/catalog.dart +++ b/lib/pages/catalog.dart @@ -1,12 +1,19 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:oc_front/core/models/shared_workspace_local.dart'; +import 'package:oc_front/core/models/workspace_local.dart'; import 'package:oc_front/core/sections/header/header.dart'; import 'package:oc_front/core/services/specialized_services/resource_service.dart'; +import 'package:oc_front/core/services/specialized_services/shared_service.dart'; +import 'package:oc_front/core/services/specialized_services/workspace_service.dart'; +import 'package:oc_front/models/response.dart'; import 'package:oc_front/models/search.dart'; +import 'package:oc_front/pages/shared.dart'; import 'package:oc_front/widgets/catalog.dart'; import 'package:oc_front/pages/abstract_page.dart'; import 'package:oc_front/core/sections/header/search.dart'; -import 'package:oc_front/widgets/menu_clipper/workspace_menu.dart'; +import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart'; +import 'package:oc_front/widgets/inputs/shallow_text_input.dart'; class CatalogFactory implements AbstractFactory { static List items = []; @@ -25,18 +32,84 @@ class CatalogFactory implements AbstractFactory { } class CatalogPageWidget extends StatefulWidget { + double? itemWidth; final ResourceService search = ResourceService(); - CatalogPageWidget (): super(key: CatalogFactory.key); + CatalogPageWidget ({ + this.itemWidth, + }): super(key: CatalogFactory.key); @override CatalogPageWidgetState createState() => CatalogPageWidgetState(); } class CatalogPageWidgetState extends State { @override Widget build(BuildContext context) { return Column( children : [ - CatalogFactory.items.isEmpty ? Container() : MenuWorkspaceWidget(), + CatalogFactory.items.isEmpty ? Container() : + Row( children: [ + ShallowDropdownInputWidget( + current: WorkspaceLocal.current, + width: MediaQuery.of(context).size.width / 3, + all: () async => WorkspaceLocal.getWorkspacesShallow(), + type: SharedWorkspaceType.workspace, + change: (String? change) { + WorkspaceLocal.changeWorkspace(change.toString()); + }, + color: Colors.white, + filled: const Color.fromRGBO(38, 166, 154, 1), + hintColor: Colors.grey.shade300, + canRemove: (String? remove) => remove != null, + remove: (String? remove) async { + if (remove == null) { return; } + WorkspaceLocal.deleteWorkspace(remove); + }, + ), + ShallowTextInputWidget( + width: MediaQuery.of(context).size.width / 3, + type: SharedWorkspaceType.workspace, + color: Colors.white, + filled: const Color.fromRGBO(38, 166, 154, 1), + hintColor: Colors.grey.shade300, + canRemove: (p0) => p0 != null, + remove: (p0) async { + await WorkspaceService().delete(context, p0, {}).then( (e) => WorkspaceLocal.deleteWorkspace(p0)); + }, + canLoad: (String? remove) => remove != null, + load: (Map add) async { + if (add["name"] == null) { return; } + WorkspaceLocal.createWorkspace(add["name"], context); + }, + ), + ShallowDropdownInputWidget( + iconLoad: Icons.share_rounded, + tooltipLoad: 'share', + tooltipRemove: 'unshare', + color: Colors.white, + filled: const Color.fromRGBO(38, 166, 154, 1), + hintColor: Colors.grey.shade300, + type: SharedWorkspaceType.workspace, + all: () async => SharedWorkspaceLocal.workspaces.values.map( + (e) => Shallow(id: e.id ?? "", name: e.name ?? "") ).toList(), + current: WorkspaceLocal.workspaces[WorkspaceLocal.current]?.shared, + width: (MediaQuery.of(context).size.width / 3), + canLoad: (String? change) => SharedWorkspaceLocal.workspaces[change] == null + || !SharedWorkspaceLocal.workspaces[change]!.workspaces.map( + (e) => e.id ).contains(WorkspaceLocal.current), + canRemove: (String? change) => SharedWorkspaceLocal.workspaces[change] == null + || SharedWorkspaceLocal.workspaces[change]!.workspaces.map( + (e) => e.id ).contains(WorkspaceLocal.current), + load: (String val) async { + await SharedService().addWorkspace(context, val, WorkspaceLocal.current ?? ""); + // ignore: use_build_context_synchronously + SharedWorkspaceLocal.init(context, false); + }, + remove: (String val) async { + await SharedService().removeWorkspace(context, val, WorkspaceLocal.current ?? ""); + // ignore: use_build_context_synchronously + SharedWorkspaceLocal.init(context, false); + }) + ]), SizedBox( width: MediaQuery.of(context).size.width, height: CatalogFactory.items.isEmpty ? 0 : MediaQuery.of(context).size.height - HeaderConstants.height - 50, - child: CatalogWidget(items: CatalogFactory.items) ) + child: SingleChildScrollView( child: CatalogWidget(items: CatalogFactory.items, itemWidth: widget.itemWidth) )), ] ); } diff --git a/lib/pages/scheduler.dart b/lib/pages/scheduler.dart index 3ee4350..91678e3 100644 --- a/lib/pages/scheduler.dart +++ b/lib/pages/scheduler.dart @@ -36,7 +36,6 @@ class SchedulerPageWidgetState extends State { "${widget.end.year}-${widget.end.month > 9 ? widget.end.month : "0${widget.end.month}"}-${widget.end.day > 9 ? widget.end.day : "0${widget.end.day}"}"], {}), builder: (ctx, as) { Map> data = {}; - if (as.hasData && as.data!.data != null) { for (var element in as.data!.data!.executions) { if (element.executionData == null) { continue; } @@ -165,7 +164,7 @@ class SchedulerPageWidgetState extends State { ), Container(padding: const EdgeInsets.only(left: 20), child: const Text("TO", style: TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w500))), - Container( padding: const EdgeInsets.only(left: 20), + Container( padding: const EdgeInsets.only(left: 20, right: 20), width: MediaQuery.of(context).size.width / 5, height: 30, child: DateTimeField( @@ -219,6 +218,12 @@ class SchedulerPageWidgetState extends State { contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), ), ) + ), + Tooltip( message: "refresh scheduler", + child: InkWell( + onTap: () => setState(() {}), + child: const Icon(Icons.refresh, color: Colors.white,), + ), ) ])) ), diff --git a/lib/pages/shared.dart b/lib/pages/shared.dart index bbe8220..c53c5a2 100644 --- a/lib/pages/shared.dart +++ b/lib/pages/shared.dart @@ -1,9 +1,24 @@ -import 'package:alert_banner/exports.dart'; +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:oc_front/core/models/shared_workspace_local.dart'; +import 'package:oc_front/core/models/workspace_local.dart'; import 'package:oc_front/core/sections/header/header.dart'; +import 'package:oc_front/core/services/router.dart'; +import 'package:oc_front/core/services/specialized_services/peer_service.dart'; +import 'package:oc_front/core/services/specialized_services/shared_service.dart'; +import 'package:oc_front/core/services/specialized_services/workflow_service.dart'; +import 'package:oc_front/models/response.dart'; +import 'package:oc_front/models/shared.dart'; import 'package:oc_front/pages/abstract_page.dart'; -import 'package:oc_front/widgets/dialog/new_shared_workspace.dart'; +import 'package:oc_front/widgets/catalog.dart'; +import 'package:oc_front/widgets/dialog/shallow_creation.dart'; +import 'package:oc_front/widgets/inputs/shallow_text_input.dart'; +import 'package:oc_front/widgets/items/shallow_item_row.dart'; +import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart'; + +enum SharedWorkspaceType { global, shared_workspace, workspace, workflow, peer, resource } class SharedFactory implements AbstractFactory { static GlobalKey key = GlobalKey(); @@ -13,51 +28,349 @@ class SharedFactory implements AbstractFactory { } class SharedPageWidget extends StatefulWidget { + SharedWorkspaceType type = SharedWorkspaceType.global; SharedPageWidget(): super(key: SharedFactory.key); @override SharedPageWidgetState createState() => SharedPageWidgetState(); static void search(BuildContext context) { } static Widget factory() { return SharedPageWidget(); } } class SharedPageWidgetState extends State { + SharedService service = SharedService(); + Widget getMenuItem(SharedWorkspaceType workspaceType, IconData icon) { + var s = workspaceType == SharedWorkspaceType.global ? "dashboard" : workspaceType == SharedWorkspaceType.workspace ? "shared workspaces" : workspaceType == SharedWorkspaceType.workflow ? "shared workflows" : "peers engaged"; + return Tooltip( message: s, + child: InkWell( mouseCursor: SystemMouseCursors.click, + onTap: () => setState(() { + widget.type = workspaceType; + }), + child: Container( width: 50, height: 50, + color: widget.type == workspaceType ? Color.fromRGBO(38, 166, 154, 1) : Colors.transparent, + padding: const EdgeInsets.symmetric(vertical: 10), + child : Icon(icon, + color: widget.type == workspaceType ? Colors.white : Colors.white, size: 20)))); + } @override Widget build(BuildContext context) { - Future.delayed( const Duration(milliseconds: 100), () { - showDialog(context: context, builder: (BuildContext ctx) => AlertDialog( - titlePadding: EdgeInsets.zero, - insetPadding: EdgeInsets.zero, - backgroundColor: Colors.white, - shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)), - title:NewBoxSharedWidget())); - }); - return Expanded( - child : Column( children: [ + GlobalKey key = GlobalKey(); + if (SharedWorkspaceLocal.current == null) { + Future.delayed( const Duration(microseconds: 100), () { + HeaderConstants.setTitle("Choose a Shared Workspace"); + HeaderConstants.setDescription("select a shared workspace to continue"); + }); + Future.delayed( const Duration(milliseconds: 100), () { + showDialog( + barrierDismissible: false, + context: context, builder: (BuildContext ctx) => AlertDialog( + titlePadding: EdgeInsets.zero, + insetPadding: EdgeInsets.zero, + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)), + title: ShallowCreationDialogWidget( + formKey: key, + canClose: () => SharedWorkspaceLocal.current != null, + context: context, + load: (p0) async { + SharedWorkspaceLocal.current = p0; + HeaderConstants.setTitle("Shared Workspace <${SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current]?.name ?? ""}>"); + HeaderConstants.setDescription(SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current]?.description ?? ""); + Future.delayed(const Duration(seconds: 1), () => SharedFactory.key.currentState?.setState(() {})); + }, + form: [ + ShallowTextInputWidget( + change :(p0) => key.currentState?.setState(() {}), + canLoad: (po) => po != null && po.isNotEmpty, + type: SharedWorkspaceType.shared_workspace, + width: MediaQuery.of(context).size.width <= 540 ? MediaQuery.of(context).size.width - 140 : 400, + attr: "description", + color: Colors.black, + hintColor: Colors.grey, + filled: Colors.grey.shade300, + ) + ], + create: (p0) async => await SharedService().post(context, p0, {}).then((value) { + if (value.data != null) { + SharedWorkspaceLocal.current = value.data!.id; + } + SharedWorkspaceLocal.init(context, true); + + HeaderConstants.setTitle("Shared Workspace <${SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current]?.name ?? ""}>"); + HeaderConstants.setDescription(SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current]?.description ?? ""); + Future.delayed(const Duration(seconds: 1), () => SharedFactory.key.currentState?.setState(() {})); + }), + type: SharedWorkspaceType.shared_workspace, + all: () async => SharedWorkspaceLocal.workspaces.values.map( + (e) => Shallow(id: e.id ?? "", name: e.name ?? "") ).toList(), + ))); + }); + } else { + Future.delayed( const Duration(milliseconds: 100), () { + HeaderConstants.setTitle("Shared Workspace <${SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current]?.name ?? ""}>"); + HeaderConstants.setDescription(SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current]?.description ?? ""); + }); + } + Widget w = WorkspaceSharedPageWidget(type: widget.type); + List addMenu = []; + SharedWorkspace? current = SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current ?? ""]; + if (widget.type == SharedWorkspaceType.workspace) { + addMenu.add( Row( mainAxisAlignment: MainAxisAlignment.end, + children : [ ShallowDropdownInputWidget( + tooltipLoad: "share", + tooltipRemove: "unshare", + iconLoad: Icons.share, + type: widget.type, + current: WorkspaceLocal.current, + all: () async => WorkspaceLocal.getWorkspacesShallow(), + width: MediaQuery.of(context).size.width / 3, + canLoad: (String? change) => current == null || !current.workspaces.map( (e) => e.id ).contains(change), + canRemove: (String? change) => current == null || current.workspaces.map( (e) => e.id ).contains(change), + load: (String val) async { + await service.addWorkspace(context, SharedWorkspaceLocal.current ?? "", val); + SharedWorkspaceLocal.init(context, false); + }, + remove: (String val) async { + await service.removeWorkspace(context, SharedWorkspaceLocal.current ?? "", val); + SharedWorkspaceLocal.init(context, false); + }) + ])); + } + if (widget.type == SharedWorkspaceType.workflow) { + addMenu.add( Row( mainAxisAlignment: MainAxisAlignment.end, + children : [ ShallowDropdownInputWidget( + tooltipLoad: "share", + tooltipRemove: "unshare", + iconLoad: Icons.share, + type: widget.type, all: () async { + List shals = []; + await WorflowService().all(context).then((value) { + if (value.data != null) { + shals = value.data!.values.map((e) => Shallow(id: e["id"], name: e["name"])).toList(); + } + }); + return shals; + }, + width: MediaQuery.of(context).size.width / 3, + canLoad: (String? change) => current == null || !current.workflows.map( (e) => e.id ).contains(change), + canRemove: (String? change) => current == null || current.workflows.map( (e) => e.id ).contains(change), + load: (String change) async { + await service.addWorkflow(context, SharedWorkspaceLocal.current ?? "", change); + }, + remove: (String change) async { + await service.removeWorkflow(context, SharedWorkspaceLocal.current ?? "", change); + }) + ])); + } + if (widget.type == SharedWorkspaceType.peer) { + addMenu.add( Row( mainAxisAlignment: MainAxisAlignment.end, + children : [ ShallowDropdownInputWidget( + tooltipLoad: "add", + iconLoad: Icons.add, + type: widget.type, all: () async { + List shals = []; + await PeerService().all(context).then((value) { + if (value.data != null) { + shals = value.data!.values.map((e) => Shallow(id: e["id"], name: e["name"])).toList(); + } + }); + return shals; + }, + width: MediaQuery.of(context).size.width / 3, + canLoad: (String? change) => current == null || !current.peers.map( (e) => e.id ).contains(change), + canRemove: (String? change) => current == null || current.peers.map( (e) => e.id ).contains(change), + load: (String change) async { + await service.addPeer(context, SharedWorkspaceLocal.current ?? "", change); + }, + remove: (String change) async { + await service.removePeer(context, SharedWorkspaceLocal.current ?? "", change); + }) + ])); + } + return Column( children: [ Container( height: 50, color: const Color.fromRGBO(38, 166, 154, 1), width: MediaQuery.of(context).size.width, - ), + padding: const EdgeInsets.only(left: 50), + child: Stack( alignment: Alignment.centerLeft, children: [ + Tooltip( message: "open file", child: Padding( padding: const EdgeInsets.only(right: 15), + child: InkWell( mouseCursor: SystemMouseCursors.click, + onTap: () { + showDialog( + barrierDismissible: false, + context: context, builder: (context) { + return AlertDialog( + titlePadding: EdgeInsets.zero, + insetPadding: EdgeInsets.zero, + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)), + title: ShallowCreationDialogWidget( + formKey: key, + canClose: () => SharedWorkspaceLocal.current != null, + context: context, + load: (p0) async { + SharedWorkspaceLocal.current = p0; + HeaderConstants.setTitle("Shared Workspace <${SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current]?.name ?? ""}>"); + HeaderConstants.setDescription(SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current]?.description ?? ""); + Future.delayed(const Duration(seconds: 1), () => SharedFactory.key.currentState?.setState(() {})); + }, + form: [ + ShallowTextInputWidget( + change :(p0) => key.currentState?.setState(() {}), + type: SharedWorkspaceType.shared_workspace, + width: MediaQuery.of(context).size.width <= 540 ? MediaQuery.of(context).size.width - 140 : 400, + attr: "description", + color: Colors.black, + hintColor: Colors.grey, + filled: Colors.grey.shade300, + ) + ], + create: (p0) async => await SharedService().post(context, p0, {}).then( (e) { + if (e.data != null) { + SharedWorkspaceLocal.current = e.data!.id; + } + SharedWorkspaceLocal.init(context, true); + + HeaderConstants.setTitle("Shared Workspace <${SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current]?.name ?? ""}>"); + HeaderConstants.setDescription(SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current]?.description ?? ""); + Future.delayed(const Duration(seconds: 1), () => SharedFactory.key.currentState?.setState(() {})); + }), + type: SharedWorkspaceType.shared_workspace, + all: () async => SharedWorkspaceLocal.workspaces.values.map( + (e) => Shallow(id: e.id ?? "", name: e.name ?? "") ).toList(), + )); + }); + }, + child: const Icon(Icons.folder_open, color: Colors.white)))), + Positioned( right: 0, child: Row( children: addMenu)) + ])), Row( children: [ Container( - color: Colors.grey, + color: Colors.black, height: MediaQuery.of(context).size.height - HeaderConstants.height - 50, width: 50, - padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 10), - child: const Column( + child: Column( children: [ - Tooltip( message: "dashboard", - child: InkWell( mouseCursor: SystemMouseCursors.click, - child: Padding( padding: EdgeInsets.symmetric(vertical: 10), child : Icon(Icons.dashboard, color: Colors.white, size: 20)))), - Tooltip( message: "shared workspaces", - child: InkWell( mouseCursor: SystemMouseCursors.click, - child: Padding( padding: EdgeInsets.symmetric(vertical: 10), child : Icon(Icons.workspaces, color: Colors.white, size: 20)))), - Tooltip( message: "shared workflows", - child: InkWell( mouseCursor: SystemMouseCursors.click, - child: Padding( padding: EdgeInsets.symmetric(vertical: 10), child : Icon(Icons.rebase_edit, color: Colors.white, size: 20)))), + getMenuItem(SharedWorkspaceType.global, Icons.dashboard), + getMenuItem(SharedWorkspaceType.workspace, Icons.workspaces), + getMenuItem(SharedWorkspaceType.workflow, Icons.rebase_edit), + getMenuItem(SharedWorkspaceType.peer, Icons.group), ]) ), + Container( + height: MediaQuery.of(context).size.height - HeaderConstants.height - 50, + width: MediaQuery.of(context).size.width -50, + color: widget.type == SharedWorkspaceType.workflow || widget.type == SharedWorkspaceType.peer ? Colors.grey.shade300 : Colors.white, + child: SingleChildScrollView( child: w )) ] - ) ]) + ) ] ); } +} + +class WorkspaceSharedPageWidget extends StatefulWidget { + SharedWorkspaceType type = SharedWorkspaceType.global; + WorkspaceSharedPageWidget({ required this.type }): super(key: null); + @override WorkspaceSharedPageWidgetState createState() => WorkspaceSharedPageWidgetState(); +} +class WorkspaceSharedPageWidgetState extends State { + + @override Widget build(BuildContext context) { + if (SharedWorkspaceLocal.current == null) { + return Container(); + } + var space = SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current ?? ""]!; + List items = []; + List data = []; + if (widget.type == SharedWorkspaceType.global) { + } else if (widget.type == SharedWorkspaceType.workspace) { + data = space.workspaces; + } else if (widget.type == SharedWorkspaceType.workflow) { + data = space.workflows; + } else if (widget.type == SharedWorkspaceType.peer) { + data = space.peers; + } + var current = SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current]; + for (var w in data) { + if (widget.type == SharedWorkspaceType.workspace) { + if (WorkspaceLocal.workspaces[w.getID()] == null) { continue; } + items.add(WorkspaceSharedItemPageWidget(name: "Workspace <${WorkspaceLocal.workspaces[w.getID()]!.name}>", id: w.getID())); + } else if (widget.type == SharedWorkspaceType.workflow || widget.type == SharedWorkspaceType.peer) { + List badges = []; + if (widget.type == SharedWorkspaceType.peer && w.getID() == current?.creatorID) { + badges.add(Icons.star); + } + items.add(ShallowItemRowWidget( + item: w, badges: badges, + edit: widget.type == SharedWorkspaceType.workflow ? (String? change) { + if (change != null) { + WorkspaceLocal.changeWorkspaceByName(change.split("~")[1]); + } + Future.delayed(const Duration(seconds: 1), () => AppRouter.workflowIDItem.go(context, { "id": change ?? "" })); + } : null, + delete: (String? change) async { + if (change == null) { return; } + if (widget.type == SharedWorkspaceType.peer) { + await SharedService().removePeer(context, SharedWorkspaceLocal.current ?? "", change); + } else { + await SharedService().removePeer(context, SharedWorkspaceLocal.current ?? "", change); + } + }, + icon: widget.type == SharedWorkspaceType.workflow ? Icons.work_history_rounded : Icons.person, + contextWidth: 200) + ); + } + } + if (items.isEmpty) { + return Container( + color: Colors.grey.shade300, + height: MediaQuery.of(context).size.height - HeaderConstants.height - 50, + width: MediaQuery.of(context).size.width - 50, + alignment: Alignment.center, + padding: const EdgeInsets.all(50), + child: Text("NO ITEMS SHARED", style: const TextStyle(fontSize: 25, fontWeight: FontWeight.w600, color: Colors.white))); + } + if (widget.type == SharedWorkspaceType.workflow || widget.type == SharedWorkspaceType.peer) { + return Padding( + padding: const EdgeInsets.all(50), + child: Stack( alignment: Alignment.topLeft, children : items)); + } + return Column( mainAxisAlignment: MainAxisAlignment.start, children : items); + } +} + +class WorkspaceSharedItemPageWidget extends StatefulWidget { + bool open = true; + String id = ""; + String name = ""; + WorkspaceSharedItemPageWidget({ this.name = "", this.id = "" }): super(key: null); + @override WorkspaceSharedItemPageWidgetState createState() => WorkspaceSharedItemPageWidgetState(); +} +class WorkspaceSharedItemPageWidgetState extends State { + @override Widget build(BuildContext context) { + return Column( children: [ + Container( + width: MediaQuery.of(context).size.width - 50, + height: 50, + padding: const EdgeInsets.symmetric(horizontal: 20), + decoration: BoxDecoration(color: Colors.grey.shade300), + child: Stack( + alignment: Alignment.centerLeft, + children: [ + Row( + children: [ + const Padding(padding: EdgeInsets.only(right: 20), child: Icon(Icons.shopping_cart_outlined, size: 18, color: Colors.white)), + Text(widget.name, + style: const TextStyle(fontSize: 15, color: Colors.white, fontWeight: FontWeight.w600)), + ] + ), + Positioned( right: 0, top: 5, child: IconButton( icon: Icon(widget.open ? Icons.arrow_drop_up : Icons.arrow_drop_down, color: Colors.white), + onPressed: () => setState(() { + widget.open = !widget.open; + }))) + ]) + ), + widget.open ? CatalogWidget(itemWidth: MediaQuery.of(context).size.width - 50, + items: WorkspaceLocal.byWorkspace(widget.id)) : Container() + ]); + } } \ No newline at end of file diff --git a/lib/pages/workflow.dart b/lib/pages/workflow.dart index 010573f..b149e1b 100644 --- a/lib/pages/workflow.dart +++ b/lib/pages/workflow.dart @@ -1,32 +1,40 @@ import 'package:flutter/material.dart'; import 'package:flutter_flow_chart/flutter_flow_chart.dart'; import 'package:go_router/go_router.dart'; +import 'package:oc_front/core/models/shared_workspace_local.dart'; import 'package:oc_front/core/models/workspace_local.dart'; import 'package:oc_front/core/sections/header/header.dart'; +import 'package:oc_front/core/services/specialized_services/shared_service.dart'; import 'package:oc_front/core/services/specialized_services/workflow_service.dart'; import 'package:oc_front/models/response.dart'; import 'package:oc_front/models/search.dart'; import 'package:oc_front/models/workflow.dart'; import 'package:oc_front/pages/abstract_page.dart'; -import 'package:oc_front/widgets/dialog/new_workflow.dart'; -import 'package:oc_front/widgets/forms/proxy_forms.dart'; +import 'package:oc_front/pages/shared.dart'; +import 'package:oc_front/widgets/dialog/shallow_creation.dart'; +import 'package:oc_front/widgets/forms/processing_forms.dart'; import 'package:oc_front/widgets/forms/scheduler_forms.dart'; import 'package:oc_front/widgets/items/item_row.dart'; -import 'package:oc_front/widgets/menu_clipper/shared_workspace_menu.dart'; -import 'package:oc_front/widgets/menu_clipper/workspace_menu.dart'; +import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart'; Dashboard dash = Dashboard( name: "workflow_${DateTime.now().toString().replaceAll(" ", "_").substring(0, DateTime.now().toString().length - 7)}"); class WorkflowFactory implements AbstractFactory { static GlobalKey key = GlobalKey(); @override bool searchFill() { return false; } - @override Widget factory(GoRouterState state, List args) { return WorkflowPageWidget(); } + @override Widget factory(GoRouterState state, List args) { + String? id; + try { id = state.pathParameters[args.first]; + } catch (e) { } + return WorkflowPageWidget(id: id); + } @override void search(BuildContext context) { } } bool getAll = true; class WorkflowPageWidget extends StatefulWidget { - WorkflowPageWidget () : super(key: WorkflowFactory.key); + String? id; + WorkflowPageWidget ({ this.id }) : super(key: WorkflowFactory.key); @override WorkflowPageWidgetState createState() => WorkflowPageWidgetState(); static void search(BuildContext context) { } static Widget factory() { return WorkflowPageWidget(); } @@ -42,23 +50,11 @@ final WorflowService _service = WorflowService(); } Widget itemTooltipBuild(Object item) { var e = item as AbstractItem; - return Container(color: Colors.white, child: ItemRowWidget(low: true, contextWidth: 300, item: e)); - } - List getItems(Object? data) { - data = data as APIResponse?; - if (data != null && data.data != null && data.data!.values.isNotEmpty) { - return data.data!.values.map((dynamic value) { - return DropdownMenuItem( - value: "${value["id"] ?? ""}~${value["name"] ?? ""}", - child: Text(value["name"]?.toString() ?? ""), - ); - }).toList(); - } - return []; + return Container(color: Colors.white, child: ItemRowWidget(low: true, contextWidth: 290, item: e)); } List getForms(FlowData? obj) { return obj == null ? [] : [ - ProxyFormsWidget(item: obj as AbstractItem, dash: dash), + ProcessingFormsWidget(item: obj as AbstractItem, dash: dash), ]; } @@ -69,6 +65,7 @@ final WorflowService _service = WorflowService(); } Future loadDash(String selected) async { + dash.shouldSave = false; if (selected.split("~").length > 1) { dash.name = selected.split("~")[1]; dash.id = selected.split("~")[0]; @@ -77,13 +74,15 @@ final WorflowService _service = WorflowService(); } await _service.get(context, dash.id ?? "").then((value) { if (value.data != null) { + dash.clear(); dash.deserialize(value.data!.toDashboard()); + Future.delayed(const Duration(seconds: 1), () => dash.shouldSave = true); } }); } Future saveDash(String? id) async { - if (id == null || !dash.isOpened) { return; } + if (id == null || !dash.isOpened || !dash.shouldSave) { return; } var datas = WorkspaceLocal.byTopic("data", true).where( (element) => dash.elements.map( (e) => e.element?.getID()).contains(element.id) ); var dataCenter = WorkspaceLocal.byTopic("datacenter", true).where( @@ -106,11 +105,27 @@ final WorflowService _service = WorflowService(); updateW.fromDashboard(dash.serialize()); for (var item in (updateW.graph?.items.values ?? [] as List)) { if (item.position == null) { continue; } - item.position?.x = (item.position?.x ?? 0) + 52.5; - item.position?.y = (item.position?.y ?? 0) + 52.5; + item.position?.x = (item.position?.x ?? 0) + (item.width! / 2) + 7.5; + item.position?.y = (item.position?.y ?? 0) + (item.height! / 2) + 7.5; + for (var i in (updateW.graph?.links ?? [] as List).where((element) => id == element.source?.id)) { + i.source?.x = (i.source?.x ?? 0) + (item.width! / 2) + 7; + i.source?.y = (i.source?.y ?? 0) + (item.width! / 2) + 7; + } + for (var i in (updateW.graph?.links ?? [] as List).where((element) => id == element.destination?.id)) { + i.destination?.x = (i.destination?.x ?? 0) + (item.width! / 2) + 7.5; + i.destination?.y = (i.destination?.y ?? 0) + (item.width! / 2) + 7.5; + } } updateW.graph?.zoom = dash.getZoomFactor(); - await _service.put(context, id, updateW.serialize(), {}); + print("SAVE DASH"); + dash.addToHistory(); + await _service.put(context, id, updateW.serialize(), {}).then( (e) { + if (dash.addChange) { + dash.addChange = false; + WorkspaceLocal.init(context, false); + dash.selectedLeftMenuKey.currentState?.setState(() { }); + } + }); } FlowData? transformToData(Map data) { @@ -125,24 +140,88 @@ final WorflowService _service = WorflowService(); return null; } - + Widget onDashboardMenu(Dashboard dash) { + return ShallowDropdownInputWidget( + iconLoad: Icons.share, + tooltipLoad: 'share', + tooltipRemove: 'unshare', + filled: const Color.fromRGBO(38,166, 154, 1), + color: Colors.white, + hintColor: Colors.grey.shade300, + type: SharedWorkspaceType.workflow, + all: () async => SharedWorkspaceLocal.workspaces.values.map( + (e) => Shallow(id: e.id ?? "", name: e.name ?? "") ).toList(), + current: WorkspaceLocal.workspaces[WorkspaceLocal.current]?.shared, + width: (MediaQuery.of(context).size.width / 3), + canLoad: (String? change) { + return SharedWorkspaceLocal.workspaces[change] == null + || !SharedWorkspaceLocal.workspaces[change]!.workflows.map( (e) => e.id + ).contains(dash.id); + }, + canRemove: (String? change) => SharedWorkspaceLocal.workspaces[change] == null + || SharedWorkspaceLocal.workspaces[change]!.workflows.map( (e) => e.id + ).contains(dash.id), + load: (String val) async { + await SharedService().addWorkflow(context, val, dash.id ?? ""); + SharedWorkspaceLocal.init(context, false); + dash.selectedMenuKey.currentState?.setState(() { }); + }, + remove: (String val) async { + await SharedService().removeWorkflow(context, val, dash.id ?? ""); + SharedWorkspaceLocal.init(context, false); + dash.selectedMenuKey.currentState?.setState(() { }); + }); + } Widget menuExtension() { var quart = MediaQuery.of(context).size.width / 6; - return MenuWorkspaceWidget(simpliest: true, width: quart > 80 ? quart : 80, - onWorkspaceChange: () { - dash.selectedLeftMenuKey.currentState?.setState(() { }); - }); + return ShallowDropdownInputWidget( + current: WorkspaceLocal.current, + width: quart > 80 ? quart : 80, + all: () async => WorkspaceLocal.getWorkspacesShallow(), + type: SharedWorkspaceType.workspace, + change: (String? change) { + WorkspaceLocal.changeWorkspace(change.toString()); + } + ); } - - Widget onDashboardMenu(Dashboard dash) { - return SharedMenuWorkspaceWidget(width: MediaQuery.of(context).size.width / 2.5, contextID: dash.id ?? "", - excluded: dash.info["shared"] ?? [], - topMargin: 0, inner: true, type: SharedMenuWorkspaceType.workflow); - } - Widget onDashboardAlertOpened(BuildContext context, Dashboard dash) { - return NewBoxWidget(service: _service, dash: dash, - getItems: getItems); + return ShallowCreationDialogWidget( + canClose: () => dash.isOpened, + context: context, + load: (p0) async { + dash.isOpened = true; + if (dash.load != null) { + WorkspaceLocal.changeWorkspaceByName(p0.split("~")[1]); + await dash.load!(p0); + } + dash.notifyListeners(); + }, + create: (p0) async => await _service.post(context, p0, {}).then( (value) { + dash.clear(); + dash.id = value.data?.getID() ?? ""; + dash.name = value.data?.getName() ?? ""; + dash.notifyListeners(); + WorkspaceLocal.init(context, true); + dash.isOpened = true; + } + ), + maptoDropdown: (e) => DropdownMenuItem( + value: "${e.id}~${e.name}", + child: Text(e.name), + ), + type: SharedWorkspaceType.workflow, + all: () async { + List res = []; + await _service.all(context).then( + (e) { + if (e.data != null) { + res = e.data!.values.map((e) => Shallow(id: e["id"] ?? "", name: e["name"] ?? "")).toList(); + } + } + ); + return res; + } + ); } @override Widget build(BuildContext context) { dash.load = loadDash; @@ -151,20 +230,22 @@ final WorflowService _service = WorflowService(); dash.infoItemWidget = getForms; dash.infoWidget = getDashInfoForms; var quart = MediaQuery.of(context).size.width / 6; - dash.defaultName = "workflow_${DateTime.now().toString().replaceAll(" ", "_").substring(0, DateTime.now().toString().length - 7)}"; + dash.defaultName = "workflow_${DateTime.now().toString().replaceAll(" ", "_" + ).substring(0, DateTime.now().toString().length - 7)}"; return FlowChart( onDashboardAlertOpened: onDashboardAlertOpened, dashboard: dash, + current: widget.id, itemWidget: itemBuild, menuWidget: onDashboardMenu, categories: const ["processing", "data", "datacenter", "storage", "workflows"], draggableItemBuilder: (cat) => WorkspaceLocal.byTopic(cat, false), itemWidgetTooltip: itemTooltipBuild, innerMenuWidth: quart > 80 ? quart : 80, - menuExtension: MediaQuery.of(context).size.width > 600 ? menuExtension : null, width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height - HeaderConstants.height, onNewConnection: (p1, p2) { }, + menuExtension: menuExtension, onDashboardTapped: (context, position) { }, onScaleUpdate: (newScale) { }, onDashboardSecondaryTapped: (context, position) { }, diff --git a/lib/widgets/catalog.dart b/lib/widgets/catalog.dart index 1fb7aef..6de4892 100644 --- a/lib/widgets/catalog.dart +++ b/lib/widgets/catalog.dart @@ -4,14 +4,18 @@ import 'package:oc_front/models/search.dart'; import 'package:oc_front/widgets/items/item_row.dart'; class CatalogWidget extends StatefulWidget { + double? itemWidth; + bool readOnly = false; final List? items; - CatalogWidget ({ Key? key, this.items }): super(key: key); + CatalogWidget ({ Key? key, this.items, this.itemWidth, this.readOnly = false }): super(key: key); @override CatalogWidgetState createState() => CatalogWidgetState(); } class CatalogWidgetState extends State { @override Widget build(BuildContext context) { var items = widget.items ?? WorkspaceLocal.items; - List itemRows = items.map((e) => ItemRowWidget(contextWidth: MediaQuery.of(context).size.width, item: e)).toList(); - return SingleChildScrollView( child: Column( children: itemRows) ); + List itemRows = items.map((e) => ItemRowWidget( + readOnly: widget.readOnly, + contextWidth: widget.itemWidth ?? MediaQuery.of(context).size.width, item: e)).toList(); + return Column( children: itemRows); } } \ No newline at end of file diff --git a/lib/widgets/dialog/login.dart b/lib/widgets/dialog/login.dart index ad08ad6..be1ce72 100644 --- a/lib/widgets/dialog/login.dart +++ b/lib/widgets/dialog/login.dart @@ -17,7 +17,10 @@ class LoginWidgetState extends State { child: Icon(Icons.person_search, size: 80, color: Colors.grey,))), const Center(child: Text("WELCOME ON OPENCLOUD", style: TextStyle(fontSize: 25, fontWeight: FontWeight.w600, color: Color.fromRGBO(38, 166, 154, 1)),)), - Padding(padding: const EdgeInsets.symmetric(vertical: 20), child: Divider(color: Colors.grey.shade300,),), + Container( + padding: const EdgeInsets.symmetric(vertical: 20), + decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Colors.black))), + ), Container( margin: const EdgeInsets.only(bottom: 10), child: Center(child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( diff --git a/lib/widgets/dialog/new_shared_workspace.dart b/lib/widgets/dialog/new_shared_workspace.dart deleted file mode 100644 index 06feeea..0000000 --- a/lib/widgets/dialog/new_shared_workspace.dart +++ /dev/null @@ -1,207 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:oc_front/models/response.dart'; -import 'package:oc_front/core/services/router.dart'; -import 'package:oc_front/core/services/specialized_services/shared_service.dart'; - -class NewBoxSharedWidget extends StatefulWidget { - String? _selected; - SharedService service = SharedService(); - final TextEditingController _ctrl = TextEditingController(); - final TextEditingController _ctrlDescr = TextEditingController(); - NewBoxSharedWidget ({ super.key, }); - @override NewBoxSharedWidgetState createState() => NewBoxSharedWidgetState(); -} -class NewBoxSharedWidgetState extends State { - GlobalKey key = GlobalKey(); - GlobalKey key2 = GlobalKey(); - @override Widget build(BuildContext context) { - return Container( - color: Colors.white, - padding: const EdgeInsets.only( top: 0, bottom: 20, left: 20, right: 20), - child: Column( - children: [ - Container( - alignment: Alignment.centerRight, - height: 50, - child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: - Text("load or create a new shared workspace", style: TextStyle(color: Colors.grey, fontSize: 15) - )), - Padding ( padding: const EdgeInsets.symmetric(horizontal: 10), child: - Tooltip( message: "back", child: InkWell( - mouseCursor: SystemMouseCursors.click, - onTap: () { - AppRouter.catalog.go(context, {}); - }, - child: const Icon(Icons.arrow_back, color: Colors.black))), - ), - Row ( mainAxisAlignment: MainAxisAlignment.end, children: [ - Tooltip( message: "close", child: InkWell( - mouseCursor: SystemMouseCursors.click, - onTap: () { Navigator.pop(context); }, - child: const Icon(Icons.close, color: Colors.black))), - ]), - ],), - ), - FutureBuilder>( - future: widget.service.all(context), - builder: (context, snapshot) { - List items = []; - if (snapshot.data != null && snapshot.data!.data != null) { - for (var item in snapshot.data!.data!.values) { - items.add(DropdownMenuItem( - value: item["id"].toString(), - child: Text(item["name"].toString()), - )); - } - } - if (widget._selected != null - && !items.where((element) => element.value == widget._selected).isNotEmpty) { - items.add(DropdownMenuItem( - value: widget._selected.toString(), - child: Text(widget._selected.toString()), - )); - } - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children : [ - SizedBox( width: MediaQuery.of(context).size.width <= 540 ? MediaQuery.of(context).size.width - 140 : 400, height: 50, - child: DropdownButtonFormField( - value: widget._selected, - isExpanded: true, - hint: const Text("load shared workspace...", style: TextStyle(color: Colors.grey, fontSize: 15)), - decoration: InputDecoration( - filled: true, - focusedBorder: const OutlineInputBorder( borderRadius: BorderRadius.zero, - borderSide: BorderSide(color: Color.fromARGB(38, 166, 154, 1), width: 0), - ), - fillColor: Colors.grey.shade300, - contentPadding: const EdgeInsets.only(left: 30, right: 30, top: 10, bottom: 30), - enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.zero, - borderSide: BorderSide(color: Colors.grey.shade300, width: 0), - ), - border: OutlineInputBorder( borderRadius: BorderRadius.zero, - borderSide: BorderSide(color: Colors.grey.shade300, width: 0)), - ), - items: items, - onChanged: (value) { - setState(() { - widget._selected = value.toString(); - }); - })), - Tooltip( - message: 'empty selection', - child: InkWell( - mouseCursor: widget._selected == null || widget._selected!.isEmpty ? MouseCursor.defer : SystemMouseCursors.click, - onTap: () { - if (widget._selected == null || widget._selected!.isEmpty) { return; } - setState(() { widget._selected = null; }); - }, - child: Container( - width: 50, height: 50, - decoration: const BoxDecoration( color: Colors.black, - border: Border(right: BorderSide(color: Colors.white))), - child: Icon(Icons.refresh, color: widget._selected == null || widget._selected!.isEmpty ? Colors.grey : Colors.white), - ) - ) - ), - Tooltip( - message: 'load shared workspace selected', - child: InkWell( - mouseCursor: widget._selected == null || widget._selected!.isEmpty - ? MouseCursor.defer : SystemMouseCursors.click, - onTap: () async { - }, - child: Container( - width: 50, height: 50, - color: Colors.black, - child: Icon(Icons.open_in_browser_outlined, - color: widget._selected == null || widget._selected!.isEmpty ? Colors.grey : Colors.white), - ) - ) - ) - ]);}), - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - margin: const EdgeInsets.only(top: 10), - width: MediaQuery.of(context).size.width <= 540 ? MediaQuery.of(context).size.width - 90 : 450, - height: 50, - child: TextFormField( key: key, - expands: true, - maxLines: null, - minLines: null, - cursorColor: const Color.fromARGB(38, 166, 154, 1), - controller: widget._ctrl, - onChanged: (value) { widget._ctrl.text = value; }, - validator: (value) => value == null || value.isEmpty ? "name is required" : null, - decoration: InputDecoration( - hintText: "name a new shared workspace...", - fillColor: Colors.grey.shade300, - filled: true, - errorStyle: const TextStyle(fontSize: 0), - contentPadding: const EdgeInsets.only(left: 30, right: 30, top: 15, bottom: 5), - hintStyle: const TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w300 - ), - border: InputBorder.none - ) - ) - ), - Tooltip( - message: 'add', - child:InkWell( - mouseCursor: widget._ctrl.value.text.isEmpty ? MouseCursor.defer : SystemMouseCursors.click, - onTap: () async { - if (key.currentState!.validate() && key2.currentState!.validate()) { - await widget.service.post(context, { - "name" :widget._ctrl.value.text, - "description" : widget._ctrlDescr.value.text }, {}); - } - }, - child: Container( - margin: const EdgeInsets.only(top: 10), - width: 50, - height: 50, - color: Colors.black, - child: Icon(Icons.add, color: widget._ctrl.value.text.isEmpty ? Colors.grey : Colors.white) - ) - ) - ) - ]), - Container( - margin: const EdgeInsets.only(top: 10), - width: MediaQuery.of(context).size.width <= 540 ? MediaQuery.of(context).size.width - 40 : 500, - height: 50, - child: TextFormField( key: key2, - expands: true, - maxLines: null, - minLines: null, - cursorColor: const Color.fromARGB(38, 166, 154, 1), - controller: widget._ctrlDescr, - onChanged: (value) { widget._ctrlDescr.text = value; }, - validator: (value) => value == null || value.isEmpty ? "name is required" : null, - decoration: InputDecoration( - hintText: "description of a new shared workspace...", - fillColor: Colors.grey.shade300, - filled: true, - errorStyle: const TextStyle(fontSize: 0), - contentPadding: const EdgeInsets.only(left: 30, right: 30, top: 15, bottom: 5), - hintStyle: const TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w300 - ), - border: InputBorder.none - ) - ) - ), - ] - ) - ); - } -} \ No newline at end of file diff --git a/lib/widgets/dialog/new_workflow.dart b/lib/widgets/dialog/new_workflow.dart deleted file mode 100644 index d5988ec..0000000 --- a/lib/widgets/dialog/new_workflow.dart +++ /dev/null @@ -1,199 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_flow_chart/flutter_flow_chart.dart'; -import 'package:oc_front/core/models/workspace_local.dart'; -import 'package:oc_front/core/services/router.dart'; -import 'package:oc_front/core/services/specialized_services/abstract_service.dart'; -import 'package:oc_front/models/abstract.dart'; -import 'package:oc_front/models/response.dart'; - -abstract class New { - String name = ""; -} - -class NewBoxWidget> extends StatefulWidget { - String? _selected; - Dashboard dash; - final TextEditingController _ctrl = TextEditingController(); - AbstractService service; - Function validate = () {}; - NewBoxWidget ({ super.key, required this.service, required this.dash, this.getItems }); - @override NewBoxWidgetState createState() => NewBoxWidgetState(); - - List Function(APIResponse? data)? getItems; -} -class NewBoxWidgetState> extends State { - @override Widget build(BuildContext context) { - widget._ctrl.value = TextEditingValue(text: widget.dash.defaultName); - return Container( - color: Colors.white, - padding: const EdgeInsets.only( top: 0, bottom: 20, left: 20, right: 20), - child: Column( - children: [ - Container( - alignment: Alignment.centerRight, - height: 50, - child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - Padding(padding: const EdgeInsets.symmetric(horizontal: 10), child: - Text("load or create a new workflow", style: const TextStyle(color: Colors.grey, fontSize: 15) - )), - Padding ( padding: const EdgeInsets.symmetric(horizontal: 10), child: - Tooltip( message: "back", child: InkWell( - mouseCursor: SystemMouseCursors.click, - onTap: () { - AppRouter.catalog.go(context, {}); - }, - child: const Icon(Icons.arrow_back, color: Colors.black))), - ), - widget.dash.isOpened ? Row ( mainAxisAlignment: MainAxisAlignment.end, children: [ - Tooltip( message: "close", child: InkWell( - mouseCursor: SystemMouseCursors.click, - onTap: () { Navigator.pop(context); }, - child: const Icon(Icons.close, color: Colors.black))), - ]) : Container(), - ],), - ), - FutureBuilder>( - future: (widget.service as AbstractService).all(context), - builder: (context, snapshot) { - List items = widget.getItems != null ? widget.getItems!(snapshot.data) : []; - if (widget._selected != null - && !items.where((element) => element.value == widget._selected).isNotEmpty) { - items.add(DropdownMenuItem( - value: widget._selected.toString(), - child: Text(widget._selected.toString()), - )); - } - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children : [ - SizedBox( width: MediaQuery.of(context).size.width <= 540 ? MediaQuery.of(context).size.width - 140 : 400, height: 50, - child: DropdownButtonFormField( - value: widget._selected, - isExpanded: true, - hint: const Text("load workflow...", style: TextStyle(color: Colors.grey, fontSize: 15)), - decoration: InputDecoration( - filled: true, - focusedBorder: const OutlineInputBorder( borderRadius: BorderRadius.zero, - borderSide: BorderSide(color: Color.fromARGB(38, 166, 154, 1), width: 0), - ), - fillColor: Colors.grey.shade300, - contentPadding: const EdgeInsets.only(left: 30, right: 30, top: 10, bottom: 30), - enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.zero, - borderSide: BorderSide(color: Colors.grey.shade300, width: 0), - ), - border: OutlineInputBorder( borderRadius: BorderRadius.zero, - borderSide: BorderSide(color: Colors.grey.shade300, width: 0)), - ), - items: items, - onChanged: (value) { - setState(() { - widget._selected = value.toString(); - }); - })), - Tooltip( - message: 'empty selection', - child: InkWell( - mouseCursor: widget._selected == null || widget._selected!.isEmpty ? MouseCursor.defer : SystemMouseCursors.click, - onTap: () { - if (widget._selected == null || widget._selected!.isEmpty) { return; } - setState(() { widget._selected = null; }); - }, - child: Container( - width: 50, height: 50, - decoration: const BoxDecoration( color: Colors.black, - border: Border(right: BorderSide(color: Colors.white))), - child: Icon(Icons.refresh, color: widget._selected == null || widget._selected!.isEmpty ? Colors.grey : Colors.white), - ) - ) - ), - Tooltip( - message: 'load workflow selected', - child: InkWell( - mouseCursor: widget._selected == null || widget._selected!.isEmpty - ? MouseCursor.defer : SystemMouseCursors.click, - onTap: () async { - if (widget._selected == null || widget._selected!.isEmpty) { return; } - widget.dash.isOpened = true; - if (widget._selected != null && widget.dash.load != null) { - WorkspaceLocal.changeWorkspaceByName(widget._selected!.split("~")[1]); - await widget.dash.load!(widget._selected ?? ""); - } - widget.dash.notifyListeners(); - Navigator.pop(context); - }, - child: Container( - width: 50, height: 50, - color: Colors.black, - child: Icon(Icons.open_in_browser_outlined, - color: widget._selected == null || widget._selected!.isEmpty ? Colors.grey : Colors.white), - ) - ) - ) - ]);}), - - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - margin: const EdgeInsets.only(top: 10), - width: MediaQuery.of(context).size.width <= 540 ? MediaQuery.of(context).size.width - 90 : 450, - height: 50, - child: TextFormField( - expands: true, - maxLines: null, - minLines: null, - cursorColor: const Color.fromARGB(38, 166, 154, 1), - controller: widget._ctrl, - onChanged: (value) {}, - validator: (value) => value == null || value.isEmpty ? "name is required" : null, - decoration: InputDecoration( - hintText: "name a new workflow...", - fillColor: Colors.grey.shade300, - filled: true, - contentPadding: const EdgeInsets.only(left: 30, right: 30, top: 15, bottom: 5), - hintStyle: const TextStyle( - color: Colors.black, - fontSize: 14, - fontWeight: FontWeight.w300 - ), - border: InputBorder.none - ) - ) - ), - Tooltip( - message: 'add', - child:InkWell( - mouseCursor: widget._ctrl.value.text.isEmpty ? MouseCursor.defer : SystemMouseCursors.click, - onTap: () async { - if (widget._ctrl.value.text.isNotEmpty) { - await widget.service.post(context, { "name" : widget._ctrl.value.text }, {}).then( - (value) { - widget._selected = widget._ctrl.value.text; - widget._ctrl.value = const TextEditingValue(text: ""); - widget.dash.id = value.data?.serialize()["id"]; - widget.dash.name = widget._selected ?? ""; - widget.dash.notifyListeners(); - WorkspaceLocal.init(context, true); - widget.dash.isOpened = true; - // ignore: use_build_context_synchronously - Navigator.pop(context); - } - ); - } - }, - child: Container( - margin: const EdgeInsets.only(top: 10), - width: 50, - height: 50, - color: Colors.black, - child: Icon(Icons.add, color: widget._ctrl.value.text.isEmpty ? Colors.grey : Colors.white) - ) - ) - ) - ]) - ] - ) - ); - } -} \ No newline at end of file diff --git a/lib/widgets/dialog/shallow_creation.dart b/lib/widgets/dialog/shallow_creation.dart new file mode 100644 index 0000000..3c0ae8c --- /dev/null +++ b/lib/widgets/dialog/shallow_creation.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:oc_front/models/response.dart'; +import 'package:oc_front/core/services/router.dart'; +import 'package:oc_front/pages/shared.dart'; +import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart'; +import 'package:oc_front/widgets/inputs/shallow_text_input.dart'; + +class ShallowCreationDialogWidget extends StatefulWidget { + GlobalKey? formKey; + BuildContext context; + bool Function()? canClose; + SharedWorkspaceType type = SharedWorkspaceType.workspace; + Future> Function()? all; + Future Function(String)? load; + Future Function(Map)? create; + bool Function(String?)? canLoad; + DropdownMenuItem Function(Shallow)? maptoDropdown; + List form = []; + + ShallowCreationDialogWidget ({ super.key, required this.type, required this.all, this.load, this.formKey, + required this.create, this.form = const [], this.maptoDropdown, required this.context, this.canClose }) ; + @override ShallowCreationDialogState createState() => ShallowCreationDialogState(); +} +class ShallowCreationDialogState extends State { + GlobalKey key = GlobalKey(); + GlobalKey key2 = GlobalKey(); + @override Widget build(BuildContext context) { + var t = widget.type == SharedWorkspaceType.workspace ? "workspace" : (widget.type == SharedWorkspaceType.workflow ? "workflow" : (widget.type == SharedWorkspaceType.shared_workspace ? "shared workspace" :"peer")); + return Container( + color: Colors.white, + padding: const EdgeInsets.only( top: 0, bottom: 20, left: 20, right: 20), + child: Column( + children: [ + Container( + alignment: Alignment.centerRight, + height: 50, + child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ + Padding(padding: const EdgeInsets.symmetric(horizontal: 10), child: + Text("load or create a new $t", style: const TextStyle(color: Colors.grey, fontSize: 15) + )), + Padding ( padding: const EdgeInsets.symmetric(horizontal: 10), child: + Tooltip( message: "back", child: InkWell( + mouseCursor: SystemMouseCursors.click, + onTap: () { + AppRouter.catalog.go(context, {}); + }, + child: const Icon(Icons.arrow_back, color: Colors.black))), + ), + widget.canClose != null && !widget.canClose!() ? Container() : Row ( mainAxisAlignment: MainAxisAlignment.end, children: [ + Tooltip( message: "close", child: InkWell( + mouseCursor: SystemMouseCursors.click, + onTap: () { Navigator.pop(context); }, + child: const Icon(Icons.close, color: Colors.black))), + ]), + ],), + ), + ShallowDropdownInputWidget( + all: widget.all, + type: widget.type, + width: MediaQuery.of(context).size.width <= 540 ? MediaQuery.of(context).size.width - 140 : 400, + load: (e) async { + await widget.load!(e); + Navigator.pop(widget.context); + }, + iconLoad: Icons.open_in_browser_outlined, + iconRemove: Icons.refresh, + maptoDropdown: widget.maptoDropdown, + canLoad: (p0) => p0 != null && p0.isNotEmpty, + canRemove: (p0) => p0 != null && p0.isNotEmpty, + tooltipRemove: "refresh selection", + deletion: true, + color: Colors.black, + hintColor: Colors.grey, + filled: Colors.grey.shade300, + ), + Container( height: 10), + ShallowTextInputWidget( + key: widget.formKey, + type: widget.type, + width: MediaQuery.of(context).size.width <= 540 ? MediaQuery.of(context).size.width - 140 : 400, + load: (e) async { + await widget.create!(e); + Navigator.pop(widget.context); + }, + forms: widget.form, + canLoad: (p0) => p0 != null && p0.isNotEmpty, + color: Colors.black, + hintColor: Colors.grey, + filled: Colors.grey.shade300, + ), + ...widget.form.map( (e) => Container( margin: const EdgeInsets.only(top: 10), child: e)), + ] + ) + ); + } +} \ No newline at end of file diff --git a/lib/widgets/forms/proxy_forms.dart b/lib/widgets/forms/processing_forms.dart similarity index 96% rename from lib/widgets/forms/proxy_forms.dart rename to lib/widgets/forms/processing_forms.dart index 61c7059..57b4c62 100644 --- a/lib/widgets/forms/proxy_forms.dart +++ b/lib/widgets/forms/processing_forms.dart @@ -6,13 +6,13 @@ import 'package:oc_front/pages/workflow.dart'; Map> proxyWfItem = {}; -class ProxyFormsWidget extends StatefulWidget { +class ProcessingFormsWidget extends StatefulWidget { AbstractItem item; Dashboard dash; - ProxyFormsWidget ({ super.key, required this.item, required this.dash }); - @override ProxyFormsWidgetState createState() => ProxyFormsWidgetState(); + ProcessingFormsWidget ({ super.key, required this.item, required this.dash }); + @override ProcessingFormsWidgetState createState() => ProcessingFormsWidgetState(); } -class ProxyFormsWidgetState extends State { +class ProcessingFormsWidgetState extends State { @override Widget build(BuildContext context) { List children = []; var l = widget.item.model?.model?.keys ?? []; diff --git a/lib/widgets/forms/scheduler_forms.dart b/lib/widgets/forms/scheduler_forms.dart index aee8628..e3d2a56 100644 --- a/lib/widgets/forms/scheduler_forms.dart +++ b/lib/widgets/forms/scheduler_forms.dart @@ -1,15 +1,18 @@ import 'package:alert_banner/exports.dart'; import 'package:cron/cron.dart'; -import 'package:flutter/widgets.dart'; import 'package:intl/intl.dart' as intl; import 'package:flutter/material.dart'; import 'package:flutter_flow_chart/flutter_flow_chart.dart'; import 'package:flutter_advanced_switch/flutter_advanced_switch.dart'; import 'package:datetime_picker_formfield/datetime_picker_formfield.dart'; import 'package:oc_front/core/models/shared_workspace_local.dart'; +import 'package:oc_front/core/services/specialized_services/workflow_service.dart'; + import 'package:oc_front/core/services/specialized_services/check_service.dart'; +import 'package:oc_front/pages/shared.dart'; import 'package:oc_front/pages/workflow.dart'; import 'package:oc_front/widgets/dialog/alert.dart'; +import 'package:oc_front/widgets/inputs/shallow_text_input.dart'; class SchedulerFormsWidget extends StatefulWidget { Dashboard item; @@ -45,6 +48,26 @@ class SchedulerFormsWidgetState extends State { } List> formKeys = [GlobalKey(), GlobalKey(), GlobalKey(), GlobalKey()]; + var shallow = ShallowTextInputWidget( + width: 250 - 1, + current: dash.name, + type: SharedWorkspaceType.workflow, + canRemove: (p0) => p0 != null && p0.isEmpty, + remove: (p0) async { + await WorflowService().delete(context, widget.item.id ?? "", {}).then((value) { + dash.clear(); + dash.isOpened = false; + dash.chartKey.currentState?.widget.flowChart.setState(() { }); + }); + }, + ); + shallow.change =(p0) => Future.delayed( const Duration(seconds: 2), () async { + if (shallow.compare == p0) { + await WorflowService().put(context, widget.item.id ?? "", { "name" : p0 }, {}); + } else { + shallow.compare = p0; + } + }); return Column( children: [ Container( padding: const EdgeInsets.all(10), width: 250, height: 60, decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))), @@ -52,9 +75,12 @@ class SchedulerFormsWidgetState extends State { Text("WORKFLOW INFO", style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), textAlign: TextAlign.center), Text("", style: TextStyle(fontSize: 12), textAlign: TextAlign.center), ])), - Container(height: 20, - width: 250, - ), + Container( + decoration: BoxDecoration( border: Border( + left: BorderSide(color: Colors.grey.shade300, width: 1), + bottom: const BorderSide(color: Colors.grey))), + child: shallow ), + const SizedBox(height: 20, width: 250 ), AdvancedSwitch( width: 140, initialValue: widget.item.scheduler["mode"] == 1, @@ -67,6 +93,7 @@ class SchedulerFormsWidgetState extends State { setState(() { widget.item.scheduler["mode"] = value == true ? 1 : 0; if ((widget.item.scheduler["mode"] == 1 )) { widget.item.scheduler.remove("cron"); } + widget.item.save!(widget.item.id); })); },), Container(height: 5), @@ -334,7 +361,11 @@ class SchedulerFormsWidgetState extends State { Container( width: 250, height: 20, - decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.black, width: 1))), + decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))), + ), + const SizedBox( + width: 250, + height: 10, ), Tooltip( message: "check booking", child: InkWell( mouseCursor: SystemMouseCursors.click, @@ -367,13 +398,13 @@ class SchedulerFormsWidgetState extends State { setState(() {}); } ); - }, child: Container( margin: const EdgeInsets.all(10), + }, child: Container( margin: const EdgeInsets.only(bottom: 5), decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), - border: Border.all(color: widget.booking == null && !dash.scheduleActive ? Colors.grey : (widget.booking == true || dash.scheduleActive ? Colors.green : Colors.red), width: 1)), + border: Border.all(color: widget.booking == null && !dash.scheduleActive ? Colors.black : (widget.booking == true || dash.scheduleActive ? Colors.green : Colors.red), width: 1)), width: 200, height: 30, child: Icon( Icons.verified_outlined, - color: widget.booking == null && !dash.scheduleActive ? Colors.grey : (widget.booking == true || dash.scheduleActive ? Colors.green : Colors.red)), + color: widget.booking == null && !dash.scheduleActive ? Colors.black : (widget.booking == true || dash.scheduleActive ? Colors.green : Colors.red)), )) ), Tooltip( message: dash.scheduleActive ? "unbook" : "book", @@ -387,7 +418,7 @@ class SchedulerFormsWidgetState extends State { } else { k.currentState!.save();} } } - DateTime now = DateTime.now().add(const Duration(minutes: 5)); + DateTime now = DateTime.now().add(const Duration(minutes: 1)); if (dash.scheduler["start"] == null || DateTime.parse(dash.scheduler["start"]!).isBefore(now)) { dash.scheduler["start"] = now.toUtc().toIso8601String(); if (dash.scheduler["end"] != null) { @@ -396,7 +427,7 @@ class SchedulerFormsWidgetState extends State { } widget.item.save!(widget.item.id); setState(() { }); - }, child: Container( margin: const EdgeInsets.all(10), + }, child: Container( margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: dash.scheduleActive ? Colors.green : Colors.black)), width: 200, height: 30, @@ -411,16 +442,18 @@ class SchedulerFormsWidgetState extends State { decoration: const BoxDecoration(border: Border(top: BorderSide(color: Colors.black))), ), Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 20), - child: Text( textAlign: TextAlign.center, + child:Text( textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 14, color: Colors.black, fontWeight: FontWeight.bold), "Workflow is shared in ${(widget.item.info["shared"] as List).length} workspace(s)")), ...(widget.item.info["shared"] as List).where( (e) => SharedWorkspaceLocal.getSharedWorkspace(e) != null ).map((e) { + var sw = SharedWorkspaceLocal.getSharedWorkspace(e); return Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), child: Row( children: [ const Padding(padding: EdgeInsets.only(right: 10), child: Icon(Icons.work, color: Colors.grey, size: 15)), Text(style: const TextStyle(fontSize: 12, color: Colors.grey), - "Workspace: ${SharedWorkspaceLocal.getSharedWorkspace(e)!.name}") ])); + "Workspace: ${sw != null && sw.name != null ? + "${sw.name!.substring(0, sw.name!.length > 15 ? 12 : sw.name!.length)}${sw.name!.length > 15 ? "..." : ""}" : ""}") ])); },) ]) : Container() ]); diff --git a/lib/widgets/inputs/shallow_dropdown_input.dart b/lib/widgets/inputs/shallow_dropdown_input.dart new file mode 100644 index 0000000..587fe5f --- /dev/null +++ b/lib/widgets/inputs/shallow_dropdown_input.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:oc_front/models/response.dart'; +import 'package:oc_front/pages/shared.dart'; +class ShallowDropdownInputWidget extends StatefulWidget { + double? width; + SharedWorkspaceType type = SharedWorkspaceType.workspace; + Future> Function()? all; + Future Function(String)? load; + Future Function(String)? remove; + bool Function(String?)? canLoad; + bool Function(String?)? canRemove; + void Function(String?)? change; + DropdownMenuItem Function(Shallow)? maptoDropdown; + String? current; + + IconData? iconLoad; + IconData? iconRemove; + + String? hint; + + String? tooltipLoad; + String? tooltipRemove; + + Color? filled; + Color? hintColor; + Color? color; + + bool deletion = false; + + ShallowDropdownInputWidget ({ Key? key, this.width, this.current, required this.all, + this.iconLoad, this.iconRemove, this.hint, this.filled, this.hintColor, this.color, + this.tooltipLoad, this.tooltipRemove, this.deletion = false, this.maptoDropdown, + required this.type, this.canLoad, this.canRemove, this.load, this.remove, this.change }): super(key: key); + @override ShallowDropdownInputWidgetState createState() => ShallowDropdownInputWidgetState(); +} +class ShallowDropdownInputWidgetState extends State { + @override Widget build(BuildContext context) { + if (widget.deletion && widget.remove == null) { + widget.remove =(p0) async => widget.current = null; + } + var t = widget.type == SharedWorkspaceType.workspace ? "workspace" : (widget.type == SharedWorkspaceType.workflow ? "workflow" : "peer"); + return FutureBuilder(future: widget.all!(), builder: (b, s) { + List items = []; + if (widget.maptoDropdown != null) { + items = s.data?.map((e) => widget.maptoDropdown!(e)).toList() ?? []; + } else { + items = s.data?.map((e) => DropdownMenuItem(value: e.id,child: Text(e.name),)).toList() ?? []; + } + return Row( children: [ + Tooltip( message: widget.hint ?? "current $t", child: + Theme( + data: Theme.of(context).copyWith( + canvasColor: Colors.grey.shade300, + ), + child: Container( height: 50, width: (widget.width ?? MediaQuery.of(context).size.width) - (widget.load == null ? 0 : 50) - (widget.remove == null ? 0 : 50), + decoration: const BoxDecoration( + color: Colors.white, + ), + child: DropdownButtonFormField( + onChanged: (value) { + setState(() { + widget.current = value; + if (widget.change != null) { + widget.change!(value); + } + }); + }, + value: widget.current != null && !items.map( ((e) => e.value)).contains(widget.current) ? null : widget.current, + isExpanded: true, + style: TextStyle(color: widget.color ?? Colors.black, fontSize: 15), + hint: Text(widget.hint ?? "load $t...", + style: TextStyle(color: widget.hintColor ??Colors.grey.shade300, fontSize: 14)), + icon: Icon( // Add this + Icons.arrow_drop_down, // Add this + color: widget.hintColor ?? Colors.grey , // Add this + ), + decoration: InputDecoration( + filled: true, + suffixIconColor: widget.hintColor ?? Colors.grey , + focusedBorder: InputBorder.none, + fillColor: widget.filled ??Colors.white, + enabledBorder: InputBorder.none, + border: InputBorder.none, + contentPadding: EdgeInsets.only(left: (widget.width ?? 400) < 200 ? 0 : 30, right: (widget.width ?? 400) < 200 ? 0 : 30, top: 18, bottom: 18), + ), + items: items, + )))), + widget.load == null ? Container() : Tooltip( + message: widget.tooltipLoad ?? "load $t", + child:InkWell( + mouseCursor: SystemMouseCursors.click, + onTap: () async { + if (widget.canLoad == null || !widget.canLoad!(widget.current) || widget.load == null || widget.current == null) { + return; + } + await widget.load!(widget.current!); + setState(() { }); + }, + child: Container( width: 50,height: 50, + color: Colors.black, + child: Icon( widget.iconLoad ?? Icons.add, color: widget.current == null || widget.canLoad == null || !widget.canLoad!(widget.current) ? Colors.grey : Colors.white) + ) + ) + ), + widget.remove == null ? Container() : Tooltip( + message: widget.tooltipRemove ?? "remove $t", + child: InkWell( mouseCursor: SystemMouseCursors.click, + onTap: () async { + if (widget.canRemove == null || !widget.canRemove!(widget.current) || widget.remove == null || widget.current == null) { + return; + } + await widget.remove!(widget.current!); + setState(() { }); + }, + child: Container( width: 50,height: 50, + decoration: BoxDecoration(color: Colors.black, border: Border(left: BorderSide(color: Colors.grey.shade300))), + child: Icon(widget.iconRemove ?? Icons.delete, color: widget.current == null || widget.canRemove == null || !widget.canRemove!(widget.current) ? Colors.grey : Colors.white)) ) + ), + ]); }); + } +} \ No newline at end of file diff --git a/lib/widgets/inputs/shallow_text_input.dart b/lib/widgets/inputs/shallow_text_input.dart new file mode 100644 index 0000000..820a8c1 --- /dev/null +++ b/lib/widgets/inputs/shallow_text_input.dart @@ -0,0 +1,137 @@ +import 'package:flutter/material.dart'; +import 'package:oc_front/models/response.dart'; +import 'package:oc_front/pages/shared.dart'; +class ShallowTextInputWidget extends StatefulWidget { + double? width; + SharedWorkspaceType type = SharedWorkspaceType.workspace; + Future Function(Map)? load; + Future Function(String)? loadStr; + Future Function(String)? remove; + bool Function(String?)? canLoad; + bool Function(String?)? canRemove; + void Function(String?)? change; + String? current; + String? compare; + + IconData? iconLoad; + IconData? iconRemove; + + String? hint; + + String? tooltipLoad; + String? tooltipRemove; + + Color? filled; + Color? hintColor; + Color? color; + List forms = []; + + String? attr; + + ShallowTextInputWidget ({ Key? key, this.width, this.current, this.attr, + this.iconLoad, this.iconRemove, this.hint, this.forms = const [], this.loadStr, + this.tooltipLoad = "", this.tooltipRemove = "", + this.filled, this.hintColor, this.color, + required this.type, this.canLoad, this.canRemove, this.load, this.remove, this.change }): super(key: key); + @override ShallowTextInputWidgetState createState() => ShallowTextInputWidgetState(); + + Map serialize() { + Map map = { "name" : current }; + for (var form in forms) { + if (form.attr == null) { + continue; + } + map[form.attr!] = form.current; + } + return map; + } + + bool validate() { + return canLoad == null ? true : canLoad!(current); + } +} +class ShallowTextInputWidgetState extends State { + bool validForms() { + for (var form in widget.forms) { + if (!form.validate()) { + return false; + } + } + return true; + } + + @override Widget build(BuildContext context) { + var t = widget.type == SharedWorkspaceType.workspace ? "workspace" : (widget.type == SharedWorkspaceType.workflow ? "workflow" : "peer"); + return Row( children: [ + Tooltip( message: widget.hint ?? "current $t", child: + Theme( + data: Theme.of(context).copyWith( + canvasColor: Colors.grey.shade300, + ), + child: Container( height: 50, width: (widget.width ?? MediaQuery.of(context).size.width) - (widget.load == null ? 0 : 50) - (widget.remove == null ? 0 : 50), + decoration: BoxDecoration( + color: Colors.white, + ), + child: TextFormField( + onChanged: (value) { + setState(() { + widget.current = value; + if (widget.change != null) { + widget.change!(value); + } + }); + }, + initialValue: widget.current, + style: TextStyle(color: widget.color ?? Colors.black, fontSize: 15), + decoration: InputDecoration( + filled: true, + border: InputBorder.none, + focusedBorder: InputBorder.none, + disabledBorder: InputBorder.none, + enabledBorder: InputBorder.none, + fillColor: widget.filled ?? Colors.white, + hintText: widget.hint ?? "enter $t ${widget.attr ?? "name"}...", + contentPadding: const EdgeInsets.only(left: 30, right: 30, top: 18, bottom: 18), + hintStyle: TextStyle(color: widget.hintColor ??const Color.fromARGB(255, 85, 44, 44), fontSize: 14)), + ), + ))), + widget.load == null && widget.loadStr == null ? Container() : Tooltip( + message: widget.tooltipLoad ?? "add $t", + child:InkWell( + mouseCursor: SystemMouseCursors.click, + onTap: () async { + if (widget.canLoad == null || !widget.canLoad!(widget.current) || !validForms() + || (widget.load == null && widget.loadStr == null) || widget.current == null) { + return; + } + if (widget.loadStr != null) { + await widget.loadStr!(widget.current!); + } else { + await widget.load!(widget.serialize()); + } + setState(() { }); + }, + child: Container( width: 50,height: 50, + color: Colors.black, + child: Icon( widget.iconLoad ?? Icons.add, color: widget.current == null || !validForms() + || widget.canLoad == null || !widget.canLoad!(widget.current) ? Colors.grey : Colors.white) + ) + ) + ), + widget.remove == null ? Container() : Tooltip( + message: widget.tooltipRemove ?? "refresh entry", + child: InkWell( mouseCursor: SystemMouseCursors.click, + onTap: () async { + if (widget.canRemove == null || !widget.canRemove!(widget.current) || widget.remove == null || widget.current == null) { + return; + } + await widget.remove!(widget.current!); + setState(() { }); + }, + child: Container( width: 50,height: 50, + decoration: BoxDecoration(color: Colors.black, border: Border(left: BorderSide(color: Colors.grey.shade300))), + child: Icon(widget.iconRemove ?? Icons.delete, color: widget.current == null || widget.canRemove == null || !widget.canRemove!(widget.current) ? Colors.grey : Colors.white)) ) + ), + ]); + } +} \ No newline at end of file diff --git a/lib/widgets/items/item_row.dart b/lib/widgets/items/item_row.dart index b258bcb..33316ef 100644 --- a/lib/widgets/items/item_row.dart +++ b/lib/widgets/items/item_row.dart @@ -18,7 +18,7 @@ class ItemRowWidget extends StatefulWidget { } class ItemRowWidgetState extends State { @override Widget build(BuildContext context) { - double imageSize = MediaQuery.of(context).size.width != widget.contextWidth ? 0 : 80; + double imageSize = widget.contextWidth <= 400 ? 0 : 80; var ratio = MediaQuery.of(context).size.width != widget.contextWidth ? 0.5 : 1; // 2; var itemWidth = (((widget.contextWidth - imageSize) / 3) - 80) / ratio; itemWidth = itemWidth > 100 ? 100 : ( itemWidth < 40 ? 40 : itemWidth ); @@ -30,6 +30,7 @@ class ItemRowWidgetState extends State { Widget w = Container( width: widget.contextWidth, height: 100, + padding: EdgeInsets.only(left: imageSize == 0 ? 20 : 0), decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.grey.shade300)) ), child: Row( children: [ widget.low ? Container( padding: const EdgeInsets.only(left: 10),) : Container( padding: const EdgeInsets.all(10), diff --git a/lib/widgets/items/shallow_item_row.dart b/lib/widgets/items/shallow_item_row.dart new file mode 100644 index 0000000..72d5207 --- /dev/null +++ b/lib/widgets/items/shallow_item_row.dart @@ -0,0 +1,74 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:oc_front/models/response.dart'; + +const List> _empty = []; +// ignore: must_be_immutable +class ShallowItemRowWidget extends StatefulWidget { + bool readOnly = false; + double contextWidth = 0; + ShallowData item; + IconData? icon; + bool low = false; + bool show = false; + List badges = []; + void Function(String?)? delete; + void Function(String?)? edit; + List> keys = []; + ShallowItemRowWidget ({ super.key, this.low = false, this.icon, this.delete, this.edit, this.badges = const [], + required this.contextWidth, this.readOnly = false, required this.item, this.keys = _empty }); + @override ShallowItemRowWidgetState createState() => ShallowItemRowWidgetState(); +} +class ShallowItemRowWidgetState extends State { + @override Widget build(BuildContext context) { + Widget w = Tooltip( message: widget.item.getName(), child: MouseRegion( + onHover: (e) => setState(() { + widget.show = true; + }), + onExit: (e) => setState(() { + widget.show = false; + }), + child: Container( + height: widget.contextWidth, width: widget.contextWidth, + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), color: Colors.white, + boxShadow: const [BoxShadow(color: Colors.grey, spreadRadius: 1, blurRadius: 1, offset: Offset(0, 1))]), + child: Stack( children: [ + widget.show ? Positioned( left: 0, top: 0, + child: Row( children: [ + Padding( padding: const EdgeInsets.only(right: 5), + child: widget.edit == null ? Container() : InkWell( mouseCursor: SystemMouseCursors.click, + onTap: () => widget.edit!(widget.item.getID() + "~" + widget.item.getName()), child: const Icon(Icons.edit, color: Colors.grey,))), + Padding( padding: const EdgeInsets.only(right: 5), + child: widget.delete == null ? Container() : InkWell( mouseCursor: SystemMouseCursors.click, + onTap: () => widget.delete!(widget.item.getID()), child: const Icon(Icons.delete, color: Colors.grey,))), + ] )) : Container(), + Positioned( right: 0, top: 0, + child: Row( children: widget.badges.map( (e) => Padding( padding: const EdgeInsets.only(left: 5), child: Icon(e, color: Colors.orange.shade300,))).toList() )), + Column( children: [ + widget.low || widget.icon == null ? Container( padding: const EdgeInsets.only(left: 10),) : Container( padding: const EdgeInsets.all(10), + constraints: BoxConstraints(maxWidth: widget.contextWidth, minWidth: widget.contextWidth), + child: Icon( widget.icon!, size: widget.contextWidth / 1.9, color: const Color(0xFFF67C0B9),)), + Container( + child: Padding(padding: widget.contextWidth != MediaQuery.of(context).size.width ? + const EdgeInsets.symmetric(horizontal: 10) : const EdgeInsets.symmetric(horizontal: 20), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, children: [ + Row( children: [ + Expanded( child: Center( child: Text(widget.item.getName().toUpperCase(), + style: const TextStyle(fontSize: 15, + overflow: TextOverflow.ellipsis, + fontWeight: FontWeight.w600, color: Colors.grey)), + )) + ]), + ],) + ) + ), + ]) + ])))); + return widget.readOnly || widget.low ? w : InkWell( mouseCursor: SystemMouseCursors.click, + onTap: () { }, + child: w ); + } +} \ No newline at end of file diff --git a/lib/widgets/logs.dart b/lib/widgets/logs.dart index 0a450aa..ff0057d 100644 --- a/lib/widgets/logs.dart +++ b/lib/widgets/logs.dart @@ -60,7 +60,7 @@ class LogWidgetState extends State { child: Icon( widget.expanded ? Icons.keyboard_arrow_down_outlined : Icons.arrow_forward_ios, size: widget.expanded ? 25 : 15, color: map.isEmpty ? Colors.grey : Colors.black, weight: widget.expanded ? 100 : 1000,)))), Padding( padding: const EdgeInsets.only(right: 10), - child: Text("${widget.item.timestamp?.toString()}", + child: Text("${widget.item.timestamp?.toLocal().toString().substring(3)}", style: const TextStyle(fontSize: 13, color: Colors.black, fontWeight: FontWeight.w500))), Tooltip( message : "copy to clipboard", child: InkWell( child: const Icon(Icons.copy, size: 15, color: Colors.grey), onTap: () { if (widget.item.message != null) { diff --git a/lib/widgets/menu_clipper/shared_workspace_menu.dart b/lib/widgets/menu_clipper/shared_workspace_menu.dart deleted file mode 100644 index 5151f8b..0000000 --- a/lib/widgets/menu_clipper/shared_workspace_menu.dart +++ /dev/null @@ -1,142 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:oc_front/core/models/shared_workspace_local.dart'; -import 'package:oc_front/core/services/specialized_services/shared_service.dart'; -import 'package:oc_front/models/shared.dart'; -import 'package:oc_front/pages/catalog.dart'; -import 'package:oc_front/pages/catalog_item.dart'; -import 'package:oc_front/pages/workflow.dart'; - -enum SharedMenuWorkspaceType { workspace, workflow, peer } - -class SharedMenuWorkspaceWidget extends StatefulWidget { - List excluded = []; - SharedMenuWorkspaceType type = SharedMenuWorkspaceType.workspace; - double? width; - String contextID; - SharedService service = SharedService(); - String? selected; - bool inner = false; - bool reverse = false; - double? topMargin; - String? before; - TextEditingController ctrl = TextEditingController(); - SharedMenuWorkspaceWidget ({ Key? key, this.width, required this.contextID, this.selected, - this.type = SharedMenuWorkspaceType.workspace, this.topMargin, this.excluded = const [], - this.inner = false, this.reverse = false, this.before}): super(key: key); - @override SharedMenuWorkspaceWidgetState createState() => SharedMenuWorkspaceWidgetState(); -} -class SharedMenuWorkspaceWidgetState extends State { - @override Widget build(BuildContext context) { - return Row( children: [ - Tooltip( message: "current workspace", child: - Theme( - data: Theme.of(context).copyWith( - canvasColor: Colors.grey, - ), - child: Container( - margin: EdgeInsets.only(top: widget.topMargin ?? (widget.inner ? 0 : 10)), - height: 50, width: (widget.width ?? MediaQuery.of(context).size.width) - 100, - decoration: BoxDecoration( - color: widget.reverse ? Colors.white : ( widget.inner ? Color.fromRGBO(38, 166, 154, 1) : Colors.grey.shade300 ), - border: const Border(bottom: BorderSide(color: Colors.transparent, width: 1)) - ), - padding: EdgeInsets.only(left: (widget.width ?? 400) < 400 ? 20 : 50, right: (widget.width ?? 400) < 400 ? 20 : 0), - child: DropdownButtonFormField( - value: widget.selected, - isExpanded: true, - style: const TextStyle(color:Colors.black, fontSize: 15), - hint: Text("choose shared workspace...", style: TextStyle(color: widget.inner ? Colors.grey.shade300 : Colors.grey, fontSize: 15)), - icon: Icon( // Add this - Icons.arrow_drop_down, // Add this - color: widget.inner ? Colors.white : Colors.grey, // Add this - ), - decoration: InputDecoration( - filled: true, - suffixIconColor: widget.inner ? Colors.grey.shade300 : Colors.grey, - focusedBorder: const OutlineInputBorder( borderRadius: BorderRadius.zero, - borderSide: BorderSide(color: Colors.transparent, width: 0), - ), - fillColor: widget.reverse ? Colors.white : (widget.inner ? const Color.fromRGBO(38, 166, 154, 1) : Colors.grey.shade300), - contentPadding: EdgeInsets.only(left: 0 , right: (widget.width ?? 400) < 400 ? 0 : 30, top: 10, bottom: 30), - enabledBorder: const OutlineInputBorder( borderRadius: BorderRadius.zero, - borderSide: BorderSide(color: Colors.transparent, width: 0), - ), - border: const OutlineInputBorder( borderRadius: BorderRadius.zero, - borderSide: BorderSide(color: Colors.transparent, width: 0)), - ), - items: SharedWorkspaceLocal.workspaces.values.where((element) => !widget.excluded.contains(element.id) - ).map((e) => DropdownMenuItem( - value: e.id ,child: Text(e.name ?? ""),)).toList(), - onChanged: (value) { - setState(() { - widget.before = widget.selected; - widget.selected = value.toString(); - }); - })))), - Tooltip( - message: 'share', - child:InkWell( - mouseCursor: widget.selected == null || widget.selected == widget.before ? MouseCursor.defer : SystemMouseCursors.click, - onTap: () async { - if (widget.selected != null && widget.contextID != "" && widget.selected != widget.before) { - if (widget.type == SharedMenuWorkspaceType.peer) { - await widget.service.addPeer(context, widget.selected!, widget.contextID); - } else if (widget.type == SharedMenuWorkspaceType.workflow) { - await widget.service.addWorkflow(context, widget.selected!, widget.contextID); - } else { - await widget.service.addWorkspace(context, widget.selected!, widget.contextID); - } - SharedWorkspaceLocal.init(context, false); - setState(() {}); - dash.selectedLeftMenuKey.currentState?.setState(() { }); - CatalogFactory.key.currentState?.setState(() {}); - CatalogItemFactory.key.currentState?.setState(() {}); - WorkflowFactory.key.currentState?.setState(() {}); - } - }, - child: Container( - margin: EdgeInsets.only(top: widget.topMargin ?? (widget.inner ? 0 : 10)), - width: 50, - height: 50, - color: Colors.black, - child: Icon(Icons.share_rounded, - color: widget.selected == null || widget.selected == widget.before ? Colors.grey : Colors.white) - ) - ) - ), - Tooltip( - message: 'unshare', - child:InkWell( - mouseCursor: widget.selected == null || widget.selected == widget.before ? MouseCursor.defer : SystemMouseCursors.click, - onTap: () async { - if (widget.selected != null && widget.contextID != "" && widget.selected != widget.before) { - if (widget.type == SharedMenuWorkspaceType.peer) { - await widget.service.removePeer(context, widget.selected!, widget.contextID); - } else if (widget.type == SharedMenuWorkspaceType.workflow) { - await widget.service.removeWorkflow(context, widget.selected!, widget.contextID); - } else { - await widget.service.removeWorkspace(context, widget.selected!, widget.contextID); - } - SharedWorkspaceLocal.init(context, false); - setState(() {}); - dash.selectedLeftMenuKey.currentState?.setState(() { }); - CatalogFactory.key.currentState?.setState(() {}); - CatalogItemFactory.key.currentState?.setState(() {}); - WorkflowFactory.key.currentState?.setState(() {}); - } - }, - child: Container( - decoration: const BoxDecoration( - color: Colors.black, - border: Border(left: BorderSide(color: Colors.white, width: 1)) - ), - margin: EdgeInsets.only( top: widget.topMargin ?? (widget.inner ? 0 : 10 )), - width: 50, - height: 50, - child: Icon(Icons.delete, color: widget.selected == null || widget.selected == widget.before ? Colors.grey : Colors.white) - ) - ) - ) - ]); - } -} \ No newline at end of file diff --git a/lib/widgets/menu_clipper/workspace_menu.dart b/lib/widgets/menu_clipper/workspace_menu.dart deleted file mode 100644 index 2b734a3..0000000 --- a/lib/widgets/menu_clipper/workspace_menu.dart +++ /dev/null @@ -1,119 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:oc_front/core/models/workspace_local.dart'; -import 'package:oc_front/widgets/menu_clipper/shared_workspace_menu.dart'; -class MenuWorkspaceWidget extends StatefulWidget { - bool simpliest = false; - double? width; - void Function()? onWorkspaceChange; - TextEditingController ctrl = TextEditingController(); - MenuWorkspaceWidget ({ Key? key, this.simpliest = false, this.width, this.onWorkspaceChange }): super(key: key); - @override MenuWorkspaceWidgetState createState() => MenuWorkspaceWidgetState(); -} -class MenuWorkspaceWidgetState extends State { - @override Widget build(BuildContext context) { - - return Row( children: [ - Tooltip( message: "current workspace", child: - Theme( - data: Theme.of(context).copyWith( - canvasColor: widget.simpliest ? Colors.grey.shade300 : Colors.grey, - ), - child: Container( height: 50, width: widget.width ?? MediaQuery.of(context).size.width / ( widget.simpliest ? 1 : 3), - decoration: BoxDecoration( - color: widget.simpliest ? Colors.white : const Color.fromRGBO(38, 166, 154, 1), - border: Border(bottom: BorderSide(color: widget.simpliest ? Colors.grey.shade300 : Colors.transparent, width: 1)) - ), - padding: EdgeInsets.only(left: (widget.width ?? 400) < 400 ? 20 : 50, right: (widget.width ?? 400) < 400 ? 20 : 0), - child: DropdownButtonFormField( - value: WorkspaceLocal.getCurrentWorkspace()?.id, - isExpanded: true, - style: TextStyle(color: widget.simpliest ? Colors.black : Colors.white, fontSize: 15), - hint: Text("load workspace...", style: TextStyle(color: Colors.grey.shade300, fontSize: 15)), - icon: Icon( // Add this - Icons.arrow_drop_down, // Add this - color: widget.simpliest ? Colors.grey : Colors.white, // Add this - ), - decoration: InputDecoration( - filled: true, - prefixIconColor: widget.simpliest ? Colors.grey : Colors.white, - icon: Icon(Icons.shopping_cart, color: Colors.grey.shade300), - suffixIconColor: widget.simpliest ? Colors.grey : Colors.white, - focusedBorder: const OutlineInputBorder( borderRadius: BorderRadius.zero, - borderSide: BorderSide(color: Colors.transparent, width: 0), - ), - fillColor: widget.simpliest ? Colors.white : const Color.fromRGBO(38, 166, 154, 1), - contentPadding: EdgeInsets.only(left: (widget.width ?? 400) < 400 ? 0 : 30, right: (widget.width ?? 400) < 400 ? 0 : 30, top: 10, bottom: 30), - enabledBorder: const OutlineInputBorder( borderRadius: BorderRadius.zero, - borderSide: BorderSide(color: Colors.transparent, width: 0), - ), - border: const OutlineInputBorder( borderRadius: BorderRadius.zero, - borderSide: BorderSide(color: Colors.transparent, width: 0)), - ), - items: WorkspaceLocal.getWorkspacesIDS().map((e) => DropdownMenuItem( - value: e.id,child: Text(e.name ?? ""),)).toList(), - onChanged: (value) { - setState(() { - WorkspaceLocal.changeWorkspace(value.toString()); - if (widget.onWorkspaceChange != null) { - widget.onWorkspaceChange!(); - } - }); - })))), - - widget.simpliest ? Container() : Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - width: (MediaQuery.of(context).size.width / 3) - 50, - height: 50, - decoration: const BoxDecoration(border: Border(left: BorderSide(color: Colors.white))), - child: TextFormField( - expands: true, - maxLines: null, - minLines: null, - style: const TextStyle(color: Colors.white, fontSize: 15), - cursorColor: const Color.fromARGB(38, 166, 154, 1), - controller: widget.ctrl, - onChanged: (value) { setState(() { }); }, - validator: (value) => value == null || value.isEmpty ? "name is required" : null, - decoration: InputDecoration( - hintText: "name a new workspace...", - fillColor: const Color.fromRGBO(38, 166, 154, 1), - filled: true, - contentPadding: const EdgeInsets.only(left: 30, right: 30, top: 15, bottom: 5), - hintStyle: TextStyle( - color: Colors.grey.shade300, - fontSize: 15, - fontWeight: FontWeight.w400 - ), - border: InputBorder.none - ) - ) - ), - Tooltip( - message: 'add', - child:InkWell( - mouseCursor: widget.ctrl.value.text.isEmpty ? MouseCursor.defer : SystemMouseCursors.click, - onTap: () async { - if (widget.ctrl.value.text.isNotEmpty) { - WorkspaceLocal.createWorkspace(widget.ctrl.value.text, context); - } - }, - child: Container( - width: 50, - height: 50, - color: Colors.black, - child: Icon(Icons.add, color: widget.ctrl.value.text.isEmpty ? Colors.grey : Colors.white) - ) - ) - ) - ]), - widget.simpliest ? Container() : SharedMenuWorkspaceWidget( inner: true, - excluded: const[], - before: WorkspaceLocal.workspaces[WorkspaceLocal.current]!.shared, - selected: WorkspaceLocal.workspaces[WorkspaceLocal.current]!.shared, - contextID: WorkspaceLocal.current ?? "", width: (MediaQuery.of(context).size.width / 3)) - ]); - } -} \ No newline at end of file diff --git a/lib/widgets/menu_clipper/arrow_clipper.dart b/lib/widgets/menus/arrow_clipper.dart similarity index 100% rename from lib/widgets/menu_clipper/arrow_clipper.dart rename to lib/widgets/menus/arrow_clipper.dart diff --git a/lib/widgets/menu_clipper/clipper_menu.dart b/lib/widgets/menus/clipper_menu.dart similarity index 99% rename from lib/widgets/menu_clipper/clipper_menu.dart rename to lib/widgets/menus/clipper_menu.dart index 918342b..eef2782 100644 --- a/lib/widgets/menu_clipper/clipper_menu.dart +++ b/lib/widgets/menus/clipper_menu.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:oc_front/core/services/router.dart'; -import 'package:oc_front/widgets/menu_clipper/arrow_clipper.dart'; +import 'package:oc_front/widgets/menus/arrow_clipper.dart'; class TooltipWidget extends StatefulWidget { int index = -1; diff --git a/lib/widgets/sheduler_items/schedule.dart b/lib/widgets/sheduler_items/schedule.dart index 242bdd1..30e9880 100644 --- a/lib/widgets/sheduler_items/schedule.dart +++ b/lib/widgets/sheduler_items/schedule.dart @@ -43,7 +43,7 @@ class ScheduleWidgetState extends State { List children = []; if (selected != null) { for (var wf in widget.data[selected!] ?? ([])) { - DateTime d2 = DateTime.parse(wf.executionData!); + DateTime d2 = DateTime.parse(wf.executionData!).toLocal(); children.add( InkWell( onTap: () => setState(() { selectedReal = wf.executionData; }), child: Container( margin: const EdgeInsets.all(10), diff --git a/lib/widgets/sheduler_items/scheduler_item.dart b/lib/widgets/sheduler_items/scheduler_item.dart index be5a863..644f5b2 100644 --- a/lib/widgets/sheduler_items/scheduler_item.dart +++ b/lib/widgets/sheduler_items/scheduler_item.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_box_transform/flutter_box_transform.dart'; import 'package:oc_front/core/sections/header/header.dart'; import 'package:oc_front/models/workflow.dart'; import 'package:oc_front/widgets/sheduler_items/schedule.dart'; @@ -25,9 +26,9 @@ class SchedulerItemWidgetState extends State { List widgets = []; for (var ev in widget.data[element] ?? ([] as List)) { widget.keys[ev.executionData!] = GlobalKey(); - var d2 = DateTime.parse(ev.executionData!); + var d2 = DateTime.parse(ev.executionData!).toLocal(); DateTime? d3; - try { d3 = DateTime.parse(ev.endDate!); + try { d3 = DateTime.parse(ev.endDate!).toLocal(); } catch (e) { /* */ } widgets.add(InkWell( onTap: () => widget.parent?.setState(() { @@ -79,7 +80,11 @@ class SchedulerItemWidgetState extends State { ))); } var date = DateTime.parse(element); - children.add(Column( children: [ExpansionTile( + children.add(Container( + decoration: const BoxDecoration( + border: Border(bottom: BorderSide(color: Colors.black)), + ), + child: Column( children: [ExpansionTile( enabled: widget.enabled, shape: const ContinuousRectangleBorder(), iconColor: Colors.grey, @@ -97,9 +102,7 @@ class SchedulerItemWidgetState extends State { ), collapsedIconColor: Colors.grey, children: widgets, - ), - Divider(color: Colors.grey.shade300, height: 1) - ])); + )]))); } Future.delayed( const Duration(milliseconds: 100), () { if (selectedReal != null) { diff --git a/library/flutter_flow_chart/lib/src/dashboard.dart b/library/flutter_flow_chart/lib/src/dashboard.dart index 58bef45..b369246 100755 --- a/library/flutter_flow_chart/lib/src/dashboard.dart +++ b/library/flutter_flow_chart/lib/src/dashboard.dart @@ -23,6 +23,7 @@ typedef ConnectionListener = void Function( /// It notifies changes to [FlowChart] // class Dashboard extends ChangeNotifier { + bool shouldSave = true; GlobalKey selectedMenuKey = GlobalKey(); GlobalKey selectedLeftMenuKey = GlobalKey(); GlobalKey chartMenuKey = GlobalKey(); @@ -49,6 +50,7 @@ class Dashboard extends ChangeNotifier { List Function(FlowData? obj)? infoItemWidget; List Function()? infoWidget; FlowData? Function(Map json)? transformToData; + bool addChange = false; /// Dashboard({ this.id, @@ -91,7 +93,6 @@ class Dashboard extends ChangeNotifier { } tempHistory = []; history = []; - addToHistory(); } Future Function(String cat)? load; @@ -267,6 +268,7 @@ class Dashboard extends ChangeNotifier { } selectedMenuKey.currentState?.setState(() { }); chartMenuKey.currentState?.setState(() { }); + addToHistory(); } /// add listener called when a new connection is created @@ -291,21 +293,62 @@ class Dashboard extends ChangeNotifier { handlerFeedbackOffset = offset; } + void addArrows(ArrowPainter f) { + arrows.add(f); + addChange = true; + if (save != null) { + save!(id); + } + } + + void removeArrows(bool Function(ArrowPainter) f) { + arrows.removeWhere((element) => f(element)); + if (save != null) { + save!(id); + } + } + + void removeElements(bool Function(FlowElement) f) { + elements.removeWhere((element) => f(element)); + if (save != null) { + save!(id); + } + } + + void clear() { + elements.clear(); + arrows.clear(); + tempHistory = []; + history = []; + scheduler = {}; + info = {}; + scheduleActive = false; + notifyListeners(); + } + bool noHistory = false; void addToHistory() { + if (noHistory) { + Future.delayed(Duration(seconds: 2), () { + noHistory = false; + }); + return; + } if (tempHistory.length >= 50) { tempHistory.removeAt(0); } tempHistory.add(toMap()); - if (save != null) { save!(id); } history = tempHistory.map((e) => e).toList(); - chartKey.currentState?.setState(() { }); chartMenuKey.currentState?.setState(() { }); } bool isBack = false; void back() { - tempHistory.removeLast(); - if (tempHistory.length == 0) return; - copyFromMap(tempHistory.last); - chartKey.currentState?.setState(() { }); - chartMenuKey.currentState?.setState(() { }); + if (canBack()) { + noHistory = true; + tempHistory.removeLast(); + if (tempHistory.length > 0) { + copyFromMap(tempHistory.last); + } + chartKey.currentState?.setState(() { }); + chartMenuKey.currentState?.setState(() { }); + } } bool canBack() { @@ -318,6 +361,7 @@ class Dashboard extends ChangeNotifier { void forward() { if (canForward()) { + noHistory = true; tempHistory.add(history[tempHistory.length]); copyFromMap(tempHistory.last); chartKey.currentState?.setState(() { }); @@ -332,6 +376,9 @@ class Dashboard extends ChangeNotifier { bool notify = true, }) { element.isResizing = resizable; + if (save != null) { + save!(id); + } if (notify) notifyListeners(); } @@ -347,6 +394,10 @@ class Dashboard extends ChangeNotifier { } element.setScale(1, gridBackgroundParams.scale); elements.add(element); + addChange = true; + if (save != null) { + save!(id); + } if (notify) { notifyListeners(); } @@ -361,7 +412,6 @@ class Dashboard extends ChangeNotifier { @override void notifyListeners() { - addToHistory(); super.notifyListeners(); } diff --git a/library/flutter_flow_chart/lib/src/elements/flow_element.dart b/library/flutter_flow_chart/lib/src/elements/flow_element.dart index 2fed2de..53d9a69 100755 --- a/library/flutter_flow_chart/lib/src/elements/flow_element.dart +++ b/library/flutter_flow_chart/lib/src/elements/flow_element.dart @@ -107,6 +107,7 @@ class FlowElement extends ChangeNotifier { return false; } factory FlowElement.fromMap(Dashboard dashboard, Map map) { + print("FlowElement.fromMap ${map}"); final e = FlowElement( element: (dashboard.transformToData != null ? dashboard.transformToData!(map['element'] ?? {}) @@ -341,6 +342,7 @@ class FlowElement extends ChangeNotifier { Map toMap() { return { 'widget': widget, + 'element': element?.serialize(), 'positionDx': position.dx, 'positionDy': position.dy, 'size.width': size.width, diff --git a/library/flutter_flow_chart/lib/src/flow_chart.dart b/library/flutter_flow_chart/lib/src/flow_chart.dart index e26b6f3..a432d17 100755 --- a/library/flutter_flow_chart/lib/src/flow_chart.dart +++ b/library/flutter_flow_chart/lib/src/flow_chart.dart @@ -56,8 +56,10 @@ class FlowChart extends StatefulWidget { required this.draggableItemBuilder, this.onDashboardAlertOpened, this.menuWidget, + this.current, this.menuExtension, }) {} + final String? current; final List categories; final double width; final double height; @@ -303,18 +305,25 @@ class FlowChartState extends State { @override Widget build(BuildContext context) { if (!widget.dashboard.isOpened && widget.onDashboardAlertOpened != null ) { - Future.delayed(Duration(milliseconds: 100), () { - showDialog( - barrierDismissible: false, - context: context, builder: (context) { - return AlertDialog( - titlePadding: EdgeInsets.zero, - insetPadding: EdgeInsets.zero, - backgroundColor: Colors.white, - shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)), - title: widget.onDashboardAlertOpened!(context, widget.dashboard)); - - }); }); + if (widget.dashboard.id != null) { + widget.dashboard.isOpened = true; + Future.delayed(Duration(milliseconds: 100), () => widget.dashboard.load!(widget.dashboard.id!) ); + } else { + Future.delayed(Duration(milliseconds: 100), () { + showDialog( + barrierDismissible: false, + context: context, builder: (context) { + return AlertDialog( + titlePadding: EdgeInsets.zero, + insetPadding: EdgeInsets.zero, + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)), + title: widget.onDashboardAlertOpened!(context, widget.dashboard)); + + }); }); + } + } else { + widget.dashboard.isOpened = true; } /// get dashboard position after first frame is drawn WidgetsBinding.instance.addPostFrameCallback((timeStamp) { @@ -371,7 +380,7 @@ class FlowChartState extends State { if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.add) { change = true; for (var el in widget.dashboard.elementSelected) { - widget.dashboard.elements.add(FlowElement( + widget.dashboard.addElement(FlowElement( element: el.element as T, dashboard: widget.dashboard, id: const Uuid().v4(), @@ -387,13 +396,13 @@ class FlowChartState extends State { } if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.delete) { change = true; - widget.dashboard.elements.removeWhere( (el) => el.isSelected ); + widget.dashboard.removeElements( (el) => el.isSelected ); for (var arrow in widget.dashboard.arrowsSelected) { for (var el in widget.dashboard.elements.where((element) => element.id == arrow.fromID.split("_")[0])) { el.next.removeAt(int.parse(arrow.fromID.split("_")[1])); } } - widget.dashboard.arrows.removeWhere( (el) => el.isSelected ); + widget.dashboard.removeArrows( (el) => el.isSelected ); } if (change) { DrawingArrow.instance.notifyListeners(); @@ -466,11 +475,11 @@ class FlowChartState extends State { widget.dashboard.isMenu ? Positioned(top: 50, child: FlowChartLeftMenu( key: widget.dashboard.selectedLeftMenuKey, dashboard: widget.dashboard, + menuExtension: widget.menuExtension, categories: widget.categories, height: widget.height, innerMenuWidth: widget.innerMenuWidth, itemWidth: widget.itemWidth, - menuExtension: widget.menuExtension, draggableItemBuilder: widget.draggableItemBuilder as List Function(String cat), getDraggable: getDraggable, ) ) @@ -745,7 +754,7 @@ class ChartWidgetState extends State { if (!hoverImportant) { for (var sel in widget.dashboard.elements) { sel.isSelected = false; } for (var sel in widget.dashboard.arrows) { sel.isSelected = false; } - Future.delayed(Duration(seconds: 1), () { + Future.delayed(Duration(milliseconds: 100), () { widget.dashboard.selectedMenuKey.currentState?.setState(() {}); DrawingArrow.instance.notifyListeners(); }); diff --git a/library/flutter_flow_chart/lib/src/flow_chart_left_menu.dart b/library/flutter_flow_chart/lib/src/flow_chart_left_menu.dart index 2a14ae7..409f865 100644 --- a/library/flutter_flow_chart/lib/src/flow_chart_left_menu.dart +++ b/library/flutter_flow_chart/lib/src/flow_chart_left_menu.dart @@ -69,7 +69,9 @@ class FlowChartLeftMenuState extends State { width: 250, height: widget.height, color: Colors.grey.shade300, - child: Column( children: [ ...widget.dashboard.infoItemWidget != null ? + child: SingleChildScrollView( child: Column( children: [ ...widget.dashboard.infoItemWidget != null ? widget.dashboard.infoItemWidget!(widget.dashboard.elementSelected.first.element) : [], widget.dashboard.arrowsSelected.isNotEmpty || widget.dashboard.elementSelected.isNotEmpty ? Container( @@ -33,13 +33,13 @@ class FlowChartSelectedMenuState extends State { Tooltip( message: "remove", child: InkWell( mouseCursor: SystemMouseCursors.click, onTap: () { - widget.dashboard.arrows.removeWhere((element) { + widget.dashboard.removeArrows((element) { if (element.isSelected && element.elementIndex != null && element.connIndex != null) { widget.dashboard.elements[element.elementIndex!].next.removeAt(element.connIndex!); } return element.isSelected; }); - widget.dashboard.elements.removeWhere((element) => element.isSelected); + widget.dashboard.removeElements((element) => element.isSelected); Future.delayed(Duration(milliseconds: 100), () { widget.dashboard.chartKey.currentState?.setState(() { }); }); @@ -53,13 +53,13 @@ class FlowChartSelectedMenuState extends State { child: InkWell( mouseCursor: SystemMouseCursors.click, onTap: () { for (var sel in widget.dashboard.elementSelected) { - widget.dashboard.elements.add(FlowElement.fromMap(widget.dashboard, sel.toMap())); + widget.dashboard.addElement(FlowElement.fromMap(widget.dashboard, sel.toMap())); widget.dashboard.elements.last.position += Offset(50, 50); } Future.delayed(Duration(milliseconds: 100), () { widget.dashboard.chartKey.currentState?.setState(() { }); }); - }, child: Container( margin: EdgeInsets.all(10), + }, child: Container( margin: EdgeInsets.only(left: 10, right: 10), decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.black, width: 1)), width: 200, height: 30, child: Icon(Icons.copy, color: Colors.black), @@ -68,7 +68,7 @@ class FlowChartSelectedMenuState extends State { ]) ) : Container() ]) - ); + )); } else if (widget.isDashboardInfo && widget.dashboard.infoWidget != null) { w = Container( width: 250, @@ -100,7 +100,7 @@ class FlowChartSelectedMenuState extends State { constraints: BoxConstraints(maxWidth: 100), child: Row( children: [ MySeparator( - width: 45, + width: 65, dashWidth: widget.dashboard.defaultDashWidth, dashSpace: widget.dashboard.defaultDashSpace, color: Colors.black @@ -193,7 +193,7 @@ class FlowChartSelectedMenuState extends State { Tooltip( message: "stroke width", child: Container( margin: EdgeInsets.only(left: 10), - width: 45, height: 25, + width: 75, height: 25, child: TextFormField( textAlign: TextAlign.center, readOnly: widget.dashboard.defaultDashWidth <= 0, initialValue: "${widget.dashboard.defaultStroke}", @@ -280,7 +280,7 @@ class FlowChartSelectedMenuState extends State { Tooltip( message: "space dash", child: Container( margin: EdgeInsets.only(top: 10), - width: 105 / 2, height: 25, + width: 155 / 2, height: 25, child: TextFormField( textAlign: TextAlign.center, readOnly: widget.dashboard.defaultDashWidth <= 0, initialValue: "${widget.dashboard.defaultDashWidth}", @@ -318,7 +318,7 @@ class FlowChartSelectedMenuState extends State { Tooltip( message: "space width", child: Container( margin: EdgeInsets.only(left: 10, top: 10), - width: 105 / 2, height: 25, + width: 155 / 2, height: 25, child: TextFormField( textAlign: TextAlign.center, initialValue: "${widget.dashboard.defaultDashSpace}", onChanged: (value) { @@ -414,7 +414,7 @@ class FlowChartSelectedMenuState extends State { Tooltip( message: "forward size", child: Container( margin: EdgeInsets.only(left: 10, top: 10), - width: 135, height: 25, + width: 185, height: 25, child: TextFormField( textAlign: TextAlign.center, initialValue: "${widget.dashboard.defaultForwardWidth}", onChanged: (value) { @@ -466,7 +466,7 @@ class FlowChartSelectedMenuState extends State { Tooltip( message: "back size", child: Container( margin: EdgeInsets.only(left: 10, top: 10), - width: 135, height: 25, + width: 185, height: 25, child: TextFormField( textAlign: TextAlign.center, initialValue: "${widget.dashboard.defaultBackWidth}", onChanged: (value) { @@ -522,13 +522,13 @@ class FlowChartSelectedMenuState extends State { Tooltip( message: "remove", child: InkWell( mouseCursor: SystemMouseCursors.click, onTap: () { - widget.dashboard.arrows.removeWhere((element) { + widget.dashboard.removeArrows((element) { if (element.isSelected && element.elementIndex != null && element.connIndex != null) { widget.dashboard.elements[element.elementIndex!].next.removeAt(element.connIndex!); } return element.isSelected; }); - widget.dashboard.elements.removeWhere((element) => element.isSelected); + widget.dashboard.removeElements((element) => element.isSelected); Future.delayed(Duration(milliseconds: 100), () { widget.dashboard.chartKey.currentState?.setState(() { }); }); @@ -542,7 +542,7 @@ class FlowChartSelectedMenuState extends State { child: InkWell( mouseCursor: SystemMouseCursors.click, onTap: () { for (var sel in widget.dashboard.elementSelected) { - widget.dashboard.elements.add(FlowElement.fromMap(widget.dashboard, sel.toMap())); + widget.dashboard.addElement(FlowElement.fromMap(widget.dashboard, sel.toMap())); widget.dashboard.elements.last.position += Offset(50, 50); } Future.delayed(Duration(milliseconds: 100), () { diff --git a/library/flutter_flow_chart/lib/src/ui/draw_arrow.dart b/library/flutter_flow_chart/lib/src/ui/draw_arrow.dart index 74e83f4..8cad41b 100755 --- a/library/flutter_flow_chart/lib/src/ui/draw_arrow.dart +++ b/library/flutter_flow_chart/lib/src/ui/draw_arrow.dart @@ -226,6 +226,7 @@ class DrawingArrow extends ChangeNotifier { /// void setFrom(Offset from) { this.from = from; + notifyListeners(); } @@ -350,7 +351,7 @@ class DrawArrowState extends State { ); if ( widget.flow.widget.dashboard.arrows.where( (element) => element.fromID == "${widget.srcElement.id}_${widget.index}").isEmpty) { - widget.flow.widget.dashboard.arrows.add(painter); + widget.flow.widget.dashboard.addArrows(painter); widget.flow.widget.dashboard.save!(widget.flow.widget.dashboard.id); } else { var i = widget.flow.widget.dashboard.arrows.indexWhere( @@ -481,9 +482,9 @@ class ArrowPainter extends CustomPainter { final e = ArrowPainter( connIndex: map['connIndex'] as int, elementIndex: map['elementIndex'] as int, - toID: map['toID'] as String, + toID: map['toID'] != null ? map['toID'] as String : "", fromID: map['fromID'] as String, - isSelected: map['isSelected'] as bool, + isSelected: map['isSelected'] != null ? map['isSelected'] as bool : false, params: ArrowParams.fromMap(map['params']), from: Offset( map['fromDx'] as double, @@ -500,14 +501,17 @@ class ArrowPainter extends CustomPainter { Map toMap() { return { + 'toID': toID, 'fromID': fromID, 'elementIndex': elementIndex, 'connIndex': connIndex, - 'isSelected': isSelected.toString(), - 'params': params.toJson(), - 'from': from.toString(), - 'to': to.toString(), - 'pivots': json.encode(pivots.map((e) => e.toMap()).toList()), + 'isSelected': isSelected, + 'params': params.toMap(), + "fromDx" : from.dx, + "fromDy" : from.dy, + "toDx" : to.dx, + "toDy" : to.dy, + 'pivots': pivots.map((e) => e.toMap()).toList(), }; }