Compare commits

...

4 Commits

Author SHA1 Message Date
mr
0b294a782c Latest Front with debug 2024-08-30 12:52:32 +02:00
mr
8beddba367 ws 2024-08-27 15:38:50 +02:00
mr
6ba32a7dfa Shared WS 2024-08-27 15:38:21 +02:00
mr
36a70db69f Booking forms + Scheduler adaptation 2024-08-26 17:37:23 +02:00
44 changed files with 1892 additions and 645 deletions

View File

@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/sections/end_drawer.dart';
import 'package:oc_front/core/services/specialized_services/shared_service.dart';
import 'package:oc_front/models/shared.dart';
import 'package:oc_front/models/workspace.dart';
import 'package:oc_front/pages/catalog.dart';
import 'package:oc_front/pages/catalog_item.dart';
import 'package:oc_front/pages/workflow.dart';
class WorkSpaceItem {
String? id;
String? name;
WorkSpaceItem({this.id, this.name});
}
class SharedWorkspaceLocal {
static String? current;
static Map<String, SharedWorkspace> workspaces = {};
static final SharedService _service = SharedService();
static void init(BuildContext context, bool changeCurrent) {
_service.all(context).then((value) {
if (value.data != null && value.data!.values.isNotEmpty ) {
var vals = value.data!.values;
for (var element in vals) {
var ws = SharedWorkspace().deserialize(element);
if (ws.id == null) { continue; }
workspaces[ws.id!] = ws;
}
}
});
}
static SharedWorkspace? getSharedWorkspace(String id) {
return workspaces[id];
}
static Future<void> deleteSharedWorkspace(String id) async {
if (workspaces.containsKey(id) && workspaces.length == 1) { return; }
workspaces.remove(id);
await _service.delete(null, id, {});
}
static Future<void> createSharedWorkspace(String name, BuildContext? context) async {
Workspace n = Workspace(name: name);
await _service.post(context, n.serialize(), {}).then((value) {
if (value.data != null) {
workspaces[value.data!.id!] = value.data!;
endDrawerKey.currentState?.setState(() {});
CatalogFactory.key.currentState?.setState(() {});
CatalogItemFactory.key.currentState?.setState(() {});
WorkflowFactory.key.currentState?.setState(() {});
}
});
}
static void changeWorkspaceByName(String name) {
var id = workspaces.entries.firstWhere((element) => element.value.name == "${name}_workspace").key;
changeWorkspace(id);
}
static void changeWorkspace(String id) {
_service.put(null, id, { "active" : true }, {});
endDrawerKey.currentState?.setState(() {});
CatalogFactory.key.currentState?.setState(() {});
CatalogItemFactory.key.currentState?.setState(() {});
WorkflowFactory.key.currentState?.setState(() {});
}
static List<WorkSpaceItem> getSharedWorkspacesIDS() {
List<WorkSpaceItem> res = [];
for (var element in workspaces.entries) {
res.add(WorkSpaceItem(id: element.key, name: element.value.name));
}
return res;
}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart'; import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/core/sections/end_drawer.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/search.dart';
import 'package:oc_front/models/workspace.dart'; import 'package:oc_front/models/workspace.dart';
import 'package:oc_front/core/services/specialized_services/workspace_service.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 }, {}); _service.put(context, ws.id!, { "active" : true }, {});
} }
if (ws.active == true && changeCurrent) { if (ws.active == true && changeCurrent) {
print(ws.serialize());
current = ws.id; current = ws.id;
} }
fill(); fill();
} }
} else { } else {
WorkspaceLocal.createWorkspace("main", context); WorkspaceLocal.createWorkspace("main", null);
} }
print("qsdqsd $current");
}); });
} }
@ -83,7 +84,7 @@ class WorkspaceLocal {
static Future<void> createWorkspace(String name, BuildContext? context) async { static Future<void> createWorkspace(String name, BuildContext? context) async {
Workspace n = Workspace(name: name); 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) { if (value.data != null) {
workspaces[value.data!.id!] = value.data!; workspaces[value.data!.id!] = value.data!;
current = value.data!.id; current = value.data!.id;
@ -102,6 +103,7 @@ class WorkspaceLocal {
} }
static void changeWorkspace(String id) { static void changeWorkspace(String id) {
print("WORKSPACES ID $id");
_service.put(null, id, { "active" : true }, {}); _service.put(null, id, { "active" : true }, {});
current = id; current = id;
fill(); fill();
@ -119,6 +121,25 @@ class WorkspaceLocal {
return res; return res;
} }
static List<Shallow> getWorkspacesShallow() {
List<Shallow> res = [];
for (var element in workspaces.values) {
res.add(Shallow(id: element.id ?? "", name: element.name ?? ""));
}
return res;
}
static List<AbstractItem> byWorkspace(String id) {
List<AbstractItem<FlowData>> 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<AbstractItem> byTopic(String topic, bool all) { static List<AbstractItem> byTopic(String topic, bool all) {
if (all) { if (all) {
List<AbstractItem<FlowData>> d = []; List<AbstractItem<FlowData>> d = [];

View File

@ -1,9 +1,14 @@
import 'package:flutter/material.dart'; 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/models/search.dart';
import 'package:oc_front/pages/catalog.dart'; import 'package:oc_front/pages/catalog.dart';
import 'package:oc_front/core/models/workspace_local.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/items/item_row.dart';
import 'package:oc_front/widgets/menu_clipper/workspace_menu.dart'; import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart';
GlobalKey<EndDrawerWidgetState> endDrawerKey = GlobalKey<EndDrawerWidgetState>(); GlobalKey<EndDrawerWidgetState> endDrawerKey = GlobalKey<EndDrawerWidgetState>();
class EndDrawerWidget extends StatefulWidget { class EndDrawerWidget extends StatefulWidget {
@ -15,11 +20,11 @@ class EndDrawerWidgetState extends State<EndDrawerWidget> {
@override Widget build(BuildContext context) { @override Widget build(BuildContext context) {
List<ItemRowWidget> itemRows = WorkspaceLocal.items.map( List<ItemRowWidget> itemRows = WorkspaceLocal.items.map(
(e) => ItemRowWidget(contextWidth: 400, item: e, keys: [endDrawerKey, CatalogFactory.key],)).toList(); (e) => ItemRowWidget(contextWidth: 400, item: e, keys: [endDrawerKey, CatalogFactory.key],)).toList();
return Container( return Stack( children: [
Container(
color: Colors.white, color: Colors.white,
width: 400, width: 400,
height: MediaQuery.of(context).size.height, height: MediaQuery.of(context).size.height,
child: SingleChildScrollView(
child: Column( children: [ child: Column( children: [
Container( Container(
width: 400, width: 400,
@ -34,17 +39,55 @@ class EndDrawerWidgetState extends State<EndDrawerWidget> {
), ),
), ),
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, itemRows.isEmpty ? Container( height: MediaQuery.of(context).size.height - 100,
color: Colors.grey.shade300, color: Colors.grey.shade300,
child: const Center(child: Text("WORKSPACE IS EMPTY", child: const Center(child: Text("WORKSPACE IS EMPTY",
style: TextStyle(fontSize: 25, fontWeight: FontWeight.w600, color: Colors.white)))) style: TextStyle(fontSize: 25, fontWeight: FontWeight.w600, color: Colors.white))))
: Container( child: SingleChildScrollView( : Container( height: MediaQuery.of(context).size.height - 100, child: SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.vertical,
child: Column( children: itemRows) child: Column( children: [ ...itemRows, Container(height: 50)])
)), )),
]) ])
])
),
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);
})
) )
]
); );
} }
} }

View File

@ -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/menu.dart';
import 'package:oc_front/core/sections/header/search.dart'; import 'package:oc_front/core/sections/header/search.dart';
import 'package:oc_front/core/services/router.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 { class HeaderConstants {
static final List<RouterItem> noHeader = [ static final List<RouterItem> noHeader = [
AppRouter.workflowItem, AppRouter.workflowItem,
AppRouter.workflowIDItem,
]; ];
static HeaderWidgetState? headerWidget;
static double height = 200; 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) { static bool isNoHeader(String route) {
return noHeader.where((element) => element.route == route).isNotEmpty; return noHeader.where((element) => element.route == route).isNotEmpty;
} }
} }
GlobalKey<HeaderWidgetState> headerWidgetKey = GlobalKey<HeaderWidgetState>();
class HeaderWidget extends StatefulWidget { class HeaderWidget extends StatefulWidget {
HeaderWidget () : super(key: headerWidgetKey); HeaderWidget () : super(key: null);
@override HeaderWidgetState createState() => HeaderWidgetState(); @override HeaderWidgetState createState() => HeaderWidgetState();
} }
class HeaderWidgetState extends State<HeaderWidget> { class HeaderWidgetState extends State<HeaderWidget> {
@override Widget build(BuildContext context) { @override Widget build(BuildContext context) {
headerWidgetKey = GlobalKey<HeaderWidgetState>(); HeaderConstants.headerWidget = this;
headerMenuKey.currentState?.closeMenu(); headerMenuKey.currentState?.closeMenu();
HeaderConstants.height = HeaderConstants.isNoHeader(AppRouter.currentRoute.route) ? 50 : 200; HeaderConstants.height = HeaderConstants.isNoHeader(AppRouter.currentRoute.route) ? 50 : 200;
return Column( children: [ return Column( children: [

View File

@ -1,6 +1,6 @@
import 'package:oc_front/main.dart'; import 'package:oc_front/main.dart';
import 'package:flutter/material.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'; import 'package:oc_front/widgets/dialog/login.dart';
class HeaderMenuWidget extends StatefulWidget{ class HeaderMenuWidget extends StatefulWidget{
@ -17,8 +17,6 @@ class HeaderMenuWidgetState extends State<HeaderMenuWidget> {
), ),
child: Padding(padding: const EdgeInsets.only(top: 5, bottom: 5, left: 50, right: 50), child: Padding(padding: const EdgeInsets.only(top: 5, bottom: 5, left: 50, right: 50),
child: Stack(children: [ 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, Row(crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
@ -34,7 +32,8 @@ class HeaderMenuWidgetState extends State<HeaderMenuWidget> {
Tooltip( message: "login", child: Padding(padding: const EdgeInsets.only(left: 10), Tooltip( message: "login", child: Padding(padding: const EdgeInsets.only(left: 10),
child: IconButton( child: IconButton(
icon: const Icon(Icons.login_outlined), icon: const Icon(Icons.login_outlined),
onPressed: () { showDialog(context: context, builder: (context) => LoginWidget()); }, onPressed: () { showDialog(context: context, builder: (context) =>
LoginWidget()); },
) )
)), )),
Tooltip( message: "navigation", child: Padding(padding: const EdgeInsets.only(left: 10), Tooltip( message: "navigation", child: Padding(padding: const EdgeInsets.only(left: 10),

View File

@ -1,6 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.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/core/services/router.dart';
import 'package:oc_front/pages/shared.dart';
import 'package:oc_front/widgets/inputs/shallow_text_input.dart';
class SearchConstants { class SearchConstants {
static final Map<String, String?> _searchHost = {}; static final Map<String, String?> _searchHost = {};
@ -34,21 +37,22 @@ class SearchWidgetState extends State<SearchWidget> {
) )
) )
)] : []), )] : []),
AppRouter.currentRoute.description != null ? AppRouter.currentRoute.description != null
|| (AppRouter.currentRoute.route.contains("shared") && HeaderConstants.title != null) ?
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text(AppRouter.currentRoute.description!, style: const TextStyle( Text(HeaderConstants.title ?? AppRouter.currentRoute.description!, style: const TextStyle(
color: Color.fromRGBO(38, 166, 154, 1), color: Color.fromRGBO(38, 166, 154, 1),
fontSize: 24, fontSize: 24,
fontWeight: FontWeight.w600 fontWeight: FontWeight.w600
)), )),
Row(children: [ 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), : [ const Padding( padding: EdgeInsets.only(right: 10),
child: Icon(Icons.help_outline, color: Colors.grey, size: 20)), 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, color: Colors.grey,
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w400 fontWeight: FontWeight.w400
@ -59,57 +63,24 @@ class SearchWidgetState extends State<SearchWidget> {
) )
: Row( : Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [ ShallowTextInputWidget(
Container(
width: MediaQuery.of(context).size.width - 400 > 0 ? MediaQuery.of(context).size.width - 300 - 100 : 200, width: MediaQuery.of(context).size.width - 400 > 0 ? MediaQuery.of(context).size.width - 300 - 100 : 200,
height: 50, type: SharedWorkspaceType.workspace,
color: Colors.white, hint: "search in resources...",
child: TextField( iconLoad: Icons.search,
onChanged: (value) => SearchConstants.set(value), iconRemove: Icons.screen_search_desktop_outlined,
decoration: InputDecoration( tooltipLoad: "search",
hintText: "Search in ${AppRouter.currentRoute.route}...", tooltipRemove: "distributed search",
contentPadding: const EdgeInsets.symmetric(horizontal: 30), canLoad: (String? str) => str != null && str.isNotEmpty,
hintStyle: const TextStyle( canRemove: (String? str) => str != null && str.isNotEmpty,
color: Colors.black, change: (value) => SearchConstants.set(value),
fontSize: 14, loadStr: (String val) async {
fontWeight: FontWeight.w300
),
border: InputBorder.none
)
)
),
Tooltip(
message: 'search',
child: InkWell(
onTap: () {
AppRouter.currentRoute.factory.search(context); AppRouter.currentRoute.factory.search(context);
}, },
child: Container( remove: (String val) async {
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: () {
AppRouter.currentRoute.factory.search(context); 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( return Container(
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,

View File

@ -1,3 +1,4 @@
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/main.dart'; import 'package:oc_front/main.dart';
import 'package:oc_front/pages/abstract_page.dart'; import 'package:oc_front/pages/abstract_page.dart';
import 'package:oc_front/pages/catalog.dart'; import 'package:oc_front/pages/catalog.dart';
@ -5,6 +6,7 @@ import 'package:oc_front/pages/catalog_item.dart';
import 'package:oc_front/pages/datacenter.dart'; import 'package:oc_front/pages/datacenter.dart';
import 'package:oc_front/pages/map.dart'; import 'package:oc_front/pages/map.dart';
import 'package:oc_front/pages/scheduler.dart'; import 'package:oc_front/pages/scheduler.dart';
import 'package:oc_front/pages/shared.dart';
import 'package:oc_front/pages/workflow.dart'; import 'package:oc_front/pages/workflow.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -42,6 +44,10 @@ class RouterItem {
AppRouter.currentRoute = this; AppRouter.currentRoute = this;
var newPath = "$path"; var newPath = "$path";
for (var arg in args) { newPath = newPath.replaceAll(":$arg", params[arg] ?? ""); } 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); context.go(newPath);
} }
} }
@ -49,8 +55,8 @@ class RouterItem {
class AppRouter { class AppRouter {
static const String home = "catalog"; static const String home = "catalog";
static final RouterItem workflowItem = RouterItem(icon: Icons.rebase_edit, label: "workflow manager", route: "workflow", 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()); 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 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()); static final RouterItem catalog= RouterItem(icon: Icons.book_outlined, label: "catalog searcher", route: home, factory: CatalogFactory());
@ -62,6 +68,8 @@ class AppRouter {
description: "Manage & monitor your datacenter.", help: "not implemented for now", description: "Manage & monitor your datacenter.", help: "not implemented for now",
factory: DatacenterFactory()), factory: DatacenterFactory()),
RouterItem(icon: Icons.public_outlined, label: "localisations", route: "map", factory: MapFactory()), 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, catalogItem,
]; ];
static List<String> history = []; static List<String> history = [];

View File

@ -15,6 +15,7 @@ abstract class AbstractService<T extends SerializerDeserializer> {
return service.get("$subPath$id", true, context); return service.get("$subPath$id", true, context);
} }
Future<APIResponse<T>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params) { Future<APIResponse<T>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params) {
print("Creating workspace $body");
return service.post(subPath, body, context); return service.post(subPath, body, context);
} }
Future<APIResponse<T>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) { Future<APIResponse<T>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) {

View File

@ -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<Peer> {
@override APIService<Peer> service = APIService<Peer>(
baseURL: const String.fromEnvironment('PEER_HOST', defaultValue: 'http://localhost:8093')
);
@override String subPath = "/oc/peer/";
}

View File

@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
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/response.dart';
import 'package:oc_front/models/shared.dart';
class SharedService extends AbstractService<SharedWorkspace> {
@override APIService<SharedWorkspace> service = APIService<SharedWorkspace>(
baseURL: const String.fromEnvironment('SHAREDWORKSPACE_HOST', defaultValue: 'http://localhost:8091')
);
@override String subPath = "/oc/shared/workspace/";
Future<APIResponse<SharedWorkspace>> addWorkspace(BuildContext? context, String id, String id2) {
return service.post("$subPath$id/workspace/$id2", {}, context);
}
Future<APIResponse<SharedWorkspace>> addWorkflow(BuildContext? context, String id, String id2) {
return service.post("$subPath$id/workflow/$id2", {}, context);
}
Future<APIResponse<SharedWorkspace>> addPeer(BuildContext? context, String id, String id2) {
return service.post("$subPath$id/peer/$id2", {}, context);
}
Future<APIResponse<SharedWorkspace>> removeWorkspace(BuildContext? context, String id, String id2) {
return service.delete("$subPath$id/workspace/$id2", context);
}
Future<APIResponse<SharedWorkspace>> removeWorkflow(BuildContext? context, String id, String id2) {
return service.delete("$subPath$id/workflow/$id2", context);
}
Future<APIResponse<SharedWorkspace>> removePeer(BuildContext? context, String id, String id2) {
return service.delete("$subPath$id/peer/$id2", context);
}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:go_router/go_router.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/models/workspace_local.dart';
import 'package:oc_front/core/services/router.dart'; import 'package:oc_front/core/services/router.dart';
import 'package:oc_front/core/sections/end_drawer.dart'; import 'package:oc_front/core/sections/end_drawer.dart';
@ -51,6 +52,7 @@ class _MainPageState extends State<MainPage> {
// fast, so that you can just rebuild anything that needs updating rather // fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets. // than having to individually change instances of widgets.
WorkspaceLocal.init(context, false); WorkspaceLocal.init(context, false);
SharedWorkspaceLocal.init(context, false);
scaffoldKey = GlobalKey<ScaffoldState>(); scaffoldKey = GlobalKey<ScaffoldState>();
return Scaffold( return Scaffold(
key: scaffoldKey, key: scaffoldKey,

View File

@ -1,6 +1,7 @@
import 'package:oc_front/models/abstract.dart'; import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/logs.dart'; import 'package:oc_front/models/logs.dart';
import 'package:oc_front/models/search.dart'; import 'package:oc_front/models/search.dart';
import 'package:oc_front/models/shared.dart';
import 'package:oc_front/models/workflow.dart'; import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/models/workspace.dart'; import 'package:oc_front/models/workspace.dart';
@ -17,6 +18,7 @@ Map<Type, SerializerDeserializer> refs = <Type, SerializerDeserializer> {
WorkflowExecutions: WorkflowExecutions(), WorkflowExecutions: WorkflowExecutions(),
LogsResult: LogsResult(), LogsResult: LogsResult(),
Check: Check(), Check: Check(),
SharedWorkspace: SharedWorkspace(),
}; };
class APIResponse<T extends SerializerDeserializer> { class APIResponse<T extends SerializerDeserializer> {
@ -56,3 +58,17 @@ class RawData extends SerializerDeserializer<RawData> {
@override deserialize(dynamic json) { return RawData(values: json ?? []); } @override deserialize(dynamic json) { return RawData(values: json ?? []); }
@override Map<String, dynamic> serialize() => { }; @override Map<String, dynamic> serialize() => { };
} }
abstract class ShallowData {
String getID();
String getName();
}
class Shallow {
String id;
String name;
Shallow({
this.id = "",
this.name = "",
});
}

115
lib/models/shared.dart Normal file
View File

@ -0,0 +1,115 @@
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';
class SharedWorkspace extends SerializerDeserializer<SharedWorkspace> {
String? id;
String? name;
String? description;
String? creatorID;
String? version;
Map<String, dynamic> attributes = {};
List<Workspace> workspaces = [];
List<Workflow> workflows = [];
List<Peer> peers = [];
List<Rule> rules = [];
SharedWorkspace(
{this.id,
this.name,
this.description,
this.creatorID,
this.version,
this.attributes = const {},
this.workspaces = const [],
this.workflows = const [],
this.peers = const [],
this.rules = const []});
@override
deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return SharedWorkspace(); }
return SharedWorkspace(
id: json.containsKey("id") ? json["id"] : null,
name: json.containsKey("name") ? json["name"] : null,
description: json.containsKey("description") ? json["description"] : 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()) : [],
workflows: json.containsKey("shared_workflows") ? fromListJson(json["shared_workflows"], Workflow()) : [],
peers: json.containsKey("shared_peers") ? fromListJson(json["shared_peers"], Peer()) : [],
rules: json.containsKey("shared_rules") ? fromListJson(json["shared_rules"], Rule()) : [],
);
}
@override
Map<String, dynamic> serialize() => {
"id": id,
"name": name,
"description": description,
"creator_id": creatorID,
"version": version,
"attributes": attributes,
"workspaces": workspaces.map((e) => e.id).toList(),
"workflows": workflows.map((e) => e.id).toList(),
"peers": peers.map((e) => e.id).toList(),
"rules": rules.map((e) => e.id).toList(),
};
}
class Rule extends SerializerDeserializer<Rule> {
String? id;
String? name;
String? description;
Rule(
{this.id,
this.name,
this.description,});
@override
deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Rule(); }
return Rule(
id: json.containsKey("id") ? json["id"] : null,
name: json.containsKey("name") ? json["name"] : null,
description: json.containsKey("description") ? json["description"] : null,
);
}
@override
Map<String, dynamic> serialize() => {
"id": id,
"name": name,
"description": description,
};
}
class Peer extends SerializerDeserializer<Peer> implements ShallowData {
String? id;
String? name;
Peer(
{this.id,
this.name,});
@override String getID() => id ?? "";
@override String getName() => name ?? "";
@override
deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Peer(); }
return Peer(
id: json.containsKey("id") ? json["id"] : null,
name: json.containsKey("name") ? json["name"] : null,
);
}
@override
Map<String, dynamic> serialize() => {
"id": id,
"name": name,
};
}

View File

@ -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/core/models/workspace_local.dart';
import 'package:oc_front/models/abstract.dart'; import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/logs.dart'; import 'package:oc_front/models/logs.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/models/search.dart'; import 'package:oc_front/models/search.dart';
class Check extends SerializerDeserializer<Check> { class Check extends SerializerDeserializer<Check> {
@ -98,7 +99,7 @@ class WorkflowExecution extends SerializerDeserializer<WorkflowExecution> {
} }
class Workflow extends SerializerDeserializer<Workflow> { class Workflow extends SerializerDeserializer<Workflow> implements ShallowData {
String? id; String? id;
String? name; String? name;
List<dynamic> data; List<dynamic> data;
@ -109,6 +110,7 @@ class Workflow extends SerializerDeserializer<Workflow> {
Graph? graph; Graph? graph;
Scheduler? schedule; Scheduler? schedule;
bool scheduleActive = false; bool scheduleActive = false;
List<dynamic> shared;
Workflow({ Workflow({
this.id, this.id,
@ -121,11 +123,12 @@ class Workflow extends SerializerDeserializer<Workflow> {
this.graph, this.graph,
this.schedule, this.schedule,
this.scheduleActive = false, this.scheduleActive = false,
this.shared = const [],
}); });
String getID() { @override String getID() => id ?? "";
return id ?? ""; @override String getName() => name ?? "";
} @override String getDescription() => "";
@override deserialize(dynamic json) { @override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>; try { json = json as Map<String, dynamic>;
@ -139,6 +142,7 @@ class Workflow extends SerializerDeserializer<Workflow> {
data: json.containsKey("datas") ? json["datas"] : [], data: json.containsKey("datas") ? json["datas"] : [],
scheduleActive: json.containsKey("schedule_active") ? json["schedule_active"] : false, scheduleActive: json.containsKey("schedule_active") ? json["schedule_active"] : false,
storage: json.containsKey("storages") ? json["storages"] : [], storage: json.containsKey("storages") ? json["storages"] : [],
shared: json.containsKey("shared") ? json["shared"] : [],
graph: json.containsKey("graph") ? Graph().deserialize(json["graph"]) : null, graph: json.containsKey("graph") ? Graph().deserialize(json["graph"]) : null,
schedule: json.containsKey("schedule") ? Scheduler().deserialize(json["schedule"]) : null, schedule: json.containsKey("schedule") ? Scheduler().deserialize(json["schedule"]) : null,
); );
@ -181,6 +185,7 @@ class Workflow extends SerializerDeserializer<Workflow> {
"graph": graph?.toDashboard(), "graph": graph?.toDashboard(),
"schedule_active": scheduleActive, "schedule_active": scheduleActive,
"schedule": schedule?.toDashboard(), "schedule": schedule?.toDashboard(),
"shared": shared,
}; };
} }
} }
@ -206,18 +211,24 @@ class Scheduler extends SerializerDeserializer<Scheduler> {
id = j["id"]; id = j["id"];
name = j["name"]; name = j["name"];
cron = j["cron"]; cron = j["cron"];
start = DateTime.parse(j["start"]); mode =j["mode"];
try {
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) { if (j.containsKey("end") && j["end"] != null) {
end = DateTime.parse(j["end"]); end = DateTime.parse(j["end"]);
} }
mode = int.parse(j["mode"].toString());
} catch (e) {}
} }
Map<String, dynamic> toDashboard() { Map<String, dynamic> toDashboard() {
return { return {
"id": id, "id": id,
"name": name, "name": name,
"cron": cron, "cron": cron,
"mode": int.parse(mode.toString()), "mode": mode ?? 1,
"start": start?.toIso8601String(), "start": start?.toIso8601String(),
"end": end?.toIso8601String(), "end": end?.toIso8601String(),
}; };
@ -231,19 +242,32 @@ class Scheduler extends SerializerDeserializer<Scheduler> {
name: json.containsKey("name") ? json["name"] : "", name: json.containsKey("name") ? json["name"] : "",
cron: json.containsKey("cron") ? json["cron"] : "", cron: json.containsKey("cron") ? json["cron"] : "",
mode: json.containsKey("mode") ? json["mode"] : "", mode: json.containsKey("mode") ? json["mode"] : "",
start: json.containsKey("start") ? DateTime.parse(json["start"]) : null, start: json.containsKey("start") && json["start"] != null ? DateTime.parse(json["start"]) : null,
end: json.containsKey("end") ? DateTime.parse(json["end"]) : null, end: json.containsKey("end") && json["end"] != null ? DateTime.parse(json["end"]) : null,
); );
} }
@override Map<String, dynamic> serialize() => { @override Map<String, dynamic> serialize() {
try {
return {
"id": id, "id": id,
"name": name, "name": name,
"cron": cron ?? "", "cron": cron ?? "",
"mode": int.parse(mode.toString()), "mode": mode ?? 1,
"start": start?.toIso8601String(), "start": start?.toIso8601String(),
"end": end?.toIso8601String(), "end": end?.toIso8601String(),
}; };
} catch (e) {
return {
"id": id,
"name": name,
"cron": cron ?? "",
"start": start?.toIso8601String(),
"end": end?.toIso8601String(),
};
}
}
} }
class Graph extends SerializerDeserializer<Graph> { class Graph extends SerializerDeserializer<Graph> {
double zoom; double zoom;
Map<String, GraphItem> items = {}; Map<String, GraphItem> items = {};
@ -499,23 +523,31 @@ class GraphItem extends SerializerDeserializer<GraphItem> {
if (abs.topic == "data") { if (abs.topic == "data") {
data = DataItem().deserialize(abs.serialize()); data = DataItem().deserialize(abs.serialize());
data!.model = ResourceModel().deserialize(j["element"]["resource_model"]); data!.model = ResourceModel().deserialize(j["element"]["resource_model"]);
} } else if (abs.topic == "processing") {
if (abs.topic == "processing") {
processing = ProcessingItem().deserialize(abs.serialize()); processing = ProcessingItem().deserialize(abs.serialize());
processing!.model = ResourceModel().deserialize(j["element"]["resource_model"]); processing!.model = ResourceModel().deserialize(j["element"]["resource_model"]);
} } else if (abs.topic == "datacenter") {
if (abs.topic == "datacenter") {
datacenter = DataCenterItem().deserialize(abs.serialize()); datacenter = DataCenterItem().deserialize(abs.serialize());
datacenter!.model = ResourceModel().deserialize(j["element"]["resource_model"]); datacenter!.model = ResourceModel().deserialize(j["element"]["resource_model"]);
} } else if (abs.topic == "storage") {
if (abs.topic == "storage") {
storage = StorageItem().deserialize(abs.serialize()); storage = StorageItem().deserialize(abs.serialize());
storage!.model = ResourceModel().deserialize(j["element"]["resource_model"]); storage!.model = ResourceModel().deserialize(j["element"]["resource_model"]);
} } else if (abs.topic == "workflow") {
if (abs.topic == "workflow") {
workflow = WorkflowItem().deserialize(abs.serialize()); workflow = WorkflowItem().deserialize(abs.serialize());
workflow!.model = ResourceModel().deserialize(j["element"]["resource_model"]); 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;
} }
} }
@ -589,7 +621,7 @@ class Position extends SerializerDeserializer<Position> {
} }
@override Map<String, dynamic> serialize() => { @override Map<String, dynamic> serialize() => {
"id": id, "id": id,
"x": x, "x": x ,
"y": y, "y": y,
}; };
} }

View File

@ -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/abstract.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/models/search.dart'; import 'package:oc_front/models/search.dart';
class Workspace extends SerializerDeserializer<Workspace> { class Workspace extends SerializerDeserializer<Workspace> implements ShallowData {
String? id; String? id;
String? name; String? name;
bool? active; bool? active;
@ -10,6 +12,7 @@ class Workspace extends SerializerDeserializer<Workspace> {
List<StorageItem> storages; List<StorageItem> storages;
List<ProcessingItem> processings; List<ProcessingItem> processings;
List<WorkflowItem> workflows; List<WorkflowItem> workflows;
String? shared;
Workspace({ Workspace({
this.id, this.id,
@ -20,13 +23,18 @@ class Workspace extends SerializerDeserializer<Workspace> {
this.datacenters = const [], this.datacenters = const [],
this.storages = const [], this.storages = const [],
this.processings = const [], this.processings = const [],
this.shared,
}); });
@override String getID() => id ?? "";
@override String getName() => name ?? "";
@override deserialize(dynamic json) { @override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>; try { json = json as Map<String, dynamic>;
} catch (e) { return Workspace(); } } catch (e) { return Workspace(); }
return Workspace( return Workspace(
id: json.containsKey("id") ? json["id"] : null, id: json.containsKey("id") ? json["id"] : null,
shared: json["shared"],
name: json.containsKey("name") ? json["name"] : null, name: json.containsKey("name") ? json["name"] : null,
active: json.containsKey("active") ? json["active"] : false, active: json.containsKey("active") ? json["active"] : false,
processings: json.containsKey("processing_resources") ? fromListJson(json["processing_resources"], ProcessingItem()) : [], processings: json.containsKey("processing_resources") ? fromListJson(json["processing_resources"], ProcessingItem()) : [],

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
abstract class AbstractFactory { abstract class AbstractFactory {
Widget factory(GoRouterState state, List<String> args); Widget factory(GoRouterState state, List<String> args);
bool searchFill(); bool searchFill();

View File

@ -1,12 +1,19 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.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/sections/header/header.dart';
import 'package:oc_front/core/services/specialized_services/resource_service.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/models/search.dart';
import 'package:oc_front/pages/shared.dart';
import 'package:oc_front/widgets/catalog.dart'; import 'package:oc_front/widgets/catalog.dart';
import 'package:oc_front/pages/abstract_page.dart'; import 'package:oc_front/pages/abstract_page.dart';
import 'package:oc_front/core/sections/header/search.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 { class CatalogFactory implements AbstractFactory {
static List<AbstractItem> items = []; static List<AbstractItem> items = [];
@ -25,18 +32,84 @@ class CatalogFactory implements AbstractFactory {
} }
class CatalogPageWidget extends StatefulWidget { class CatalogPageWidget extends StatefulWidget {
double? itemWidth;
final ResourceService search = ResourceService(); final ResourceService search = ResourceService();
CatalogPageWidget (): super(key: CatalogFactory.key); CatalogPageWidget ({
this.itemWidth,
}): super(key: CatalogFactory.key);
@override CatalogPageWidgetState createState() => CatalogPageWidgetState(); @override CatalogPageWidgetState createState() => CatalogPageWidgetState();
} }
class CatalogPageWidgetState extends State<CatalogPageWidget> { class CatalogPageWidgetState extends State<CatalogPageWidget> {
@override Widget build(BuildContext context) { @override Widget build(BuildContext context) {
return Column( children : [ 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<String?, dynamic> 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( SizedBox(
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
height: CatalogFactory.items.isEmpty ? 0 : MediaQuery.of(context).size.height - HeaderConstants.height - 50, 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) )),
] ]
); );
} }

View File

@ -36,7 +36,6 @@ class SchedulerPageWidgetState extends State<SchedulerPageWidget> {
"${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}"}"], {}), "${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) { builder: (ctx, as) {
Map<String, List<WorkflowExecution>> data = {}; Map<String, List<WorkflowExecution>> data = {};
if (as.hasData && as.data!.data != null) { if (as.hasData && as.data!.data != null) {
for (var element in as.data!.data!.executions) { for (var element in as.data!.data!.executions) {
if (element.executionData == null) { continue; } if (element.executionData == null) { continue; }
@ -165,7 +164,7 @@ class SchedulerPageWidgetState extends State<SchedulerPageWidget> {
), ),
Container(padding: const EdgeInsets.only(left: 20), Container(padding: const EdgeInsets.only(left: 20),
child: const Text("TO", style: TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w500))), 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, width: MediaQuery.of(context).size.width / 5,
height: 30, height: 30,
child: DateTimeField( child: DateTimeField(
@ -219,6 +218,12 @@ class SchedulerPageWidgetState extends State<SchedulerPageWidget> {
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
), ),
) )
),
Tooltip( message: "refresh scheduler",
child: InkWell(
onTap: () => setState(() {}),
child: const Icon(Icons.refresh, color: Colors.white,),
),
) )
])) ]))
), ),

376
lib/pages/shared.dart Normal file
View File

@ -0,0 +1,376 @@
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/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<SharedPageWidgetState> key = GlobalKey<SharedPageWidgetState>();
@override bool searchFill() { return false; }
@override Widget factory(GoRouterState state, List<String> args) { return SharedPageWidget(); }
@override void search(BuildContext context) { }
}
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<SharedPageWidget> {
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) {
GlobalKey<ShallowTextInputWidgetState> key = GlobalKey<ShallowTextInputWidgetState>();
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<Widget> 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<Shallow> 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<Shallow> 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.black,
height: MediaQuery.of(context).size.height - HeaderConstants.height - 50,
width: 50,
child: Column(
children: [
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<WorkspaceSharedPageWidget> {
@override Widget build(BuildContext context) {
if (SharedWorkspaceLocal.current == null) {
return Container();
}
var space = SharedWorkspaceLocal.workspaces[SharedWorkspaceLocal.current ?? ""]!;
List<Widget> items = [];
List<ShallowData> 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<IconData> 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<WorkspaceSharedItemPageWidget> {
@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()
]);
}
}

View File

@ -1,31 +1,40 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart'; import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:go_router/go_router.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/models/workspace_local.dart';
import 'package:oc_front/core/sections/header/header.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/core/services/specialized_services/workflow_service.dart';
import 'package:oc_front/models/response.dart'; import 'package:oc_front/models/response.dart';
import 'package:oc_front/models/search.dart'; import 'package:oc_front/models/search.dart';
import 'package:oc_front/models/workflow.dart'; import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/pages/abstract_page.dart'; import 'package:oc_front/pages/abstract_page.dart';
import 'package:oc_front/widgets/dialog/new_box.dart'; import 'package:oc_front/pages/shared.dart';
import 'package:oc_front/widgets/forms/proxy_forms.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/forms/scheduler_forms.dart';
import 'package:oc_front/widgets/items/item_row.dart'; import 'package:oc_front/widgets/items/item_row.dart';
import 'package:oc_front/widgets/menu_clipper/workspace_menu.dart'; import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart';
Dashboard dash = Dashboard( Dashboard dash = Dashboard(
name: "workflow_${DateTime.now().toString().replaceAll(" ", "_").substring(0, DateTime.now().toString().length - 7)}"); name: "workflow_${DateTime.now().toString().replaceAll(" ", "_").substring(0, DateTime.now().toString().length - 7)}");
class WorkflowFactory implements AbstractFactory { class WorkflowFactory implements AbstractFactory {
static GlobalKey<WorkflowPageWidgetState> key = GlobalKey<WorkflowPageWidgetState>(); static GlobalKey<WorkflowPageWidgetState> key = GlobalKey<WorkflowPageWidgetState>();
@override bool searchFill() { return false; } @override bool searchFill() { return false; }
@override Widget factory(GoRouterState state, List<String> args) { return WorkflowPageWidget(); } @override Widget factory(GoRouterState state, List<String> args) {
String? id;
try { id = state.pathParameters[args.first];
} catch (e) { }
return WorkflowPageWidget(id: id);
}
@override void search(BuildContext context) { } @override void search(BuildContext context) { }
} }
bool getAll = true; bool getAll = true;
class WorkflowPageWidget extends StatefulWidget { class WorkflowPageWidget extends StatefulWidget {
WorkflowPageWidget () : super(key: WorkflowFactory.key); String? id;
WorkflowPageWidget ({ this.id }) : super(key: WorkflowFactory.key);
@override WorkflowPageWidgetState createState() => WorkflowPageWidgetState(); @override WorkflowPageWidgetState createState() => WorkflowPageWidgetState();
static void search(BuildContext context) { } static void search(BuildContext context) { }
static Widget factory() { return WorkflowPageWidget(); } static Widget factory() { return WorkflowPageWidget(); }
@ -41,23 +50,11 @@ final WorflowService _service = WorflowService();
} }
Widget itemTooltipBuild(Object item) { Widget itemTooltipBuild(Object item) {
var e = item as AbstractItem; var e = item as AbstractItem;
return Container(color: Colors.white, child: ItemRowWidget(low: true, contextWidth: 300, item: e)); return Container(color: Colors.white, child: ItemRowWidget(low: true, contextWidth: 290, item: e));
}
List<DropdownMenuItem> getItems(Object? data) {
data = data as APIResponse<RawData>?;
if (data != null && data.data != null && data.data!.values.isNotEmpty) {
return data.data!.values.map((dynamic value) {
return DropdownMenuItem<String>(
value: "${value["id"] ?? ""}~${value["name"] ?? ""}",
child: Text(value["name"]?.toString() ?? ""),
);
}).toList();
}
return [];
} }
List<Widget> getForms(FlowData? obj) { List<Widget> getForms(FlowData? obj) {
return obj == null ? [] : [ return obj == null ? [] : [
ProxyFormsWidget(item: obj as AbstractItem), ProcessingFormsWidget(item: obj as AbstractItem, dash: dash),
]; ];
} }
@ -68,6 +65,7 @@ final WorflowService _service = WorflowService();
} }
Future<void> loadDash(String selected) async { Future<void> loadDash(String selected) async {
dash.shouldSave = false;
if (selected.split("~").length > 1) { if (selected.split("~").length > 1) {
dash.name = selected.split("~")[1]; dash.name = selected.split("~")[1];
dash.id = selected.split("~")[0]; dash.id = selected.split("~")[0];
@ -76,13 +74,15 @@ final WorflowService _service = WorflowService();
} }
await _service.get(context, dash.id ?? "").then((value) { await _service.get(context, dash.id ?? "").then((value) {
if (value.data != null) { if (value.data != null) {
dash.clear();
dash.deserialize(value.data!.toDashboard()); dash.deserialize(value.data!.toDashboard());
Future.delayed(const Duration(seconds: 1), () => dash.shouldSave = true);
} }
}); });
} }
Future<void> saveDash(String? id) async { Future<void> 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( var datas = WorkspaceLocal.byTopic("data", true).where(
(element) => dash.elements.map( (e) => e.element?.getID()).contains(element.id) ); (element) => dash.elements.map( (e) => e.element?.getID()).contains(element.id) );
var dataCenter = WorkspaceLocal.byTopic("datacenter", true).where( var dataCenter = WorkspaceLocal.byTopic("datacenter", true).where(
@ -105,11 +105,27 @@ final WorflowService _service = WorflowService();
updateW.fromDashboard(dash.serialize()); updateW.fromDashboard(dash.serialize());
for (var item in (updateW.graph?.items.values ?? [] as List<GraphItem>)) { for (var item in (updateW.graph?.items.values ?? [] as List<GraphItem>)) {
if (item.position == null) { continue; } if (item.position == null) { continue; }
item.position?.x = (item.position?.x ?? 0) + 52.5; item.position?.x = (item.position?.x ?? 0) + (item.width! / 2) + 7.5;
item.position?.y = (item.position?.y ?? 0) + 52.5; item.position?.y = (item.position?.y ?? 0) + (item.height! / 2) + 7.5;
for (var i in (updateW.graph?.links ?? [] as List<GraphLink>).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<GraphLink>).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(); 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<String, dynamic> data) { FlowData? transformToData(Map<String, dynamic> data) {
@ -124,18 +140,88 @@ final WorflowService _service = WorflowService();
return null; return null;
} }
Widget onDashboardMenu(Dashboard dash) {
Widget menuExtension() { return ShallowDropdownInputWidget(
var quart = MediaQuery.of(context).size.width / 6; iconLoad: Icons.share,
return MenuWorkspaceWidget(simpliest: true, width: quart > 80 ? quart : 80, tooltipLoad: 'share',
onWorkspaceChange: () { tooltipRemove: 'unshare',
dash.selectedLeftMenuKey.currentState?.setState(() { }); 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 ShallowDropdownInputWidget(
current: WorkspaceLocal.current,
width: quart > 80 ? quart : 80,
all: () async => WorkspaceLocal.getWorkspacesShallow(),
type: SharedWorkspaceType.workspace,
change: (String? change) {
WorkspaceLocal.changeWorkspace(change.toString());
}
);
}
Widget onDashboardAlertOpened(BuildContext context, Dashboard dash) { Widget onDashboardAlertOpened(BuildContext context, Dashboard dash) {
return NewBoxWidget<Workflow>(service: _service, dash: dash, return ShallowCreationDialogWidget(
getItems: getItems); 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<String>(
value: "${e.id}~${e.name}",
child: Text(e.name),
),
type: SharedWorkspaceType.workflow,
all: () async {
List<Shallow> 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) { @override Widget build(BuildContext context) {
dash.load = loadDash; dash.load = loadDash;
@ -144,19 +230,22 @@ final WorflowService _service = WorflowService();
dash.infoItemWidget = getForms; dash.infoItemWidget = getForms;
dash.infoWidget = getDashInfoForms; dash.infoWidget = getDashInfoForms;
var quart = MediaQuery.of(context).size.width / 6; 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<AbstractItem>( return FlowChart<AbstractItem>(
onDashboardAlertOpened: onDashboardAlertOpened, onDashboardAlertOpened: onDashboardAlertOpened,
dashboard: dash, dashboard: dash,
current: widget.id,
itemWidget: itemBuild, itemWidget: itemBuild,
menuWidget: onDashboardMenu,
categories: const ["processing", "data", "datacenter", "storage", "workflows"], categories: const ["processing", "data", "datacenter", "storage", "workflows"],
draggableItemBuilder: (cat) => WorkspaceLocal.byTopic(cat, false), draggableItemBuilder: (cat) => WorkspaceLocal.byTopic(cat, false),
itemWidgetTooltip: itemTooltipBuild, itemWidgetTooltip: itemTooltipBuild,
innerMenuWidth: quart > 80 ? quart : 80, innerMenuWidth: quart > 80 ? quart : 80,
menuExtension: menuExtension,
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height - HeaderConstants.height, height: MediaQuery.of(context).size.height - HeaderConstants.height,
onNewConnection: (p1, p2) { }, onNewConnection: (p1, p2) { },
menuExtension: menuExtension,
onDashboardTapped: (context, position) { }, onDashboardTapped: (context, position) { },
onScaleUpdate: (newScale) { }, onScaleUpdate: (newScale) { },
onDashboardSecondaryTapped: (context, position) { }, onDashboardSecondaryTapped: (context, position) { },

View File

@ -4,14 +4,18 @@ import 'package:oc_front/models/search.dart';
import 'package:oc_front/widgets/items/item_row.dart'; import 'package:oc_front/widgets/items/item_row.dart';
class CatalogWidget extends StatefulWidget { class CatalogWidget extends StatefulWidget {
double? itemWidth;
bool readOnly = false;
final List<AbstractItem>? items; final List<AbstractItem>? 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(); @override CatalogWidgetState createState() => CatalogWidgetState();
} }
class CatalogWidgetState extends State<CatalogWidget> { class CatalogWidgetState extends State<CatalogWidget> {
@override Widget build(BuildContext context) { @override Widget build(BuildContext context) {
var items = widget.items ?? WorkspaceLocal.items; var items = widget.items ?? WorkspaceLocal.items;
List<ItemRowWidget> itemRows = items.map((e) => ItemRowWidget(contextWidth: MediaQuery.of(context).size.width, item: e)).toList(); List<ItemRowWidget> itemRows = items.map((e) => ItemRowWidget(
return SingleChildScrollView( child: Column( children: itemRows) ); readOnly: widget.readOnly,
contextWidth: widget.itemWidth ?? MediaQuery.of(context).size.width, item: e)).toList();
return Column( children: itemRows);
} }
} }

View File

@ -17,7 +17,10 @@ class LoginWidgetState extends State<LoginWidget> {
child: Icon(Icons.person_search, size: 80, color: Colors.grey,))), child: Icon(Icons.person_search, size: 80, color: Colors.grey,))),
const Center(child: Text("WELCOME ON OPENCLOUD", style: TextStyle(fontSize: 25, fontWeight: FontWeight.w600, const Center(child: Text("WELCOME ON OPENCLOUD", style: TextStyle(fontSize: 25, fontWeight: FontWeight.w600,
color: Color.fromRGBO(38, 166, 154, 1)),)), 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, Container( margin: const EdgeInsets.only(bottom: 10), child: Center(child: Row( mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Container( Container(

View File

@ -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<T extends SerializerDeserializer<dynamic>> extends StatefulWidget {
String? _selected;
Dashboard dash;
final TextEditingController _ctrl = TextEditingController();
AbstractService<T> service;
Function validate = () {};
NewBoxWidget ({ super.key, required this.service, required this.dash, this.getItems });
@override NewBoxWidgetState<T> createState() => NewBoxWidgetState<T>();
List<DropdownMenuItem> Function(APIResponse<T>? data)? getItems;
}
class NewBoxWidgetState<T extends SerializerDeserializer<dynamic>> extends State<NewBoxWidget> {
@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<APIResponse<RawData>>(
future: (widget.service as AbstractService<T>).all(context),
builder: (context, snapshot) {
List<DropdownMenuItem> items = widget.getItems != null ? widget.getItems!(snapshot.data) : [];
if (widget._selected != null
&& !items.where((element) => element.value == widget._selected).isNotEmpty) {
items.add(DropdownMenuItem<String>(
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)
)
)
)
])
]
)
);
}
}

View File

@ -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<ShallowTextInputWidgetState>? formKey;
BuildContext context;
bool Function()? canClose;
SharedWorkspaceType type = SharedWorkspaceType.workspace;
Future<List<Shallow>> Function()? all;
Future<void> Function(String)? load;
Future<void> Function(Map<String,dynamic>)? create;
bool Function(String?)? canLoad;
DropdownMenuItem Function(Shallow)? maptoDropdown;
List<ShallowTextInputWidget> 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<ShallowCreationDialogWidget> {
GlobalKey<FormFieldState> key = GlobalKey<FormFieldState>();
GlobalKey<FormFieldState> key2 = GlobalKey<FormFieldState>();
@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)),
]
)
);
}
}

View File

@ -0,0 +1,136 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/pages/workflow.dart';
Map<String, Map<String, AbstractItem>> proxyWfItem = {};
class ProcessingFormsWidget extends StatefulWidget {
AbstractItem item;
Dashboard dash;
ProcessingFormsWidget ({ super.key, required this.item, required this.dash });
@override ProcessingFormsWidgetState createState() => ProcessingFormsWidgetState();
}
class ProcessingFormsWidgetState extends State<ProcessingFormsWidget> {
@override Widget build(BuildContext context) {
List<Widget> children = [];
var l = widget.item.model?.model?.keys ?? [];
for (var child in l) {
if (widget.item.model!.model![child]!.type != "string") { continue; }
children.add(
Tooltip( message: child,
child: Container( margin: EdgeInsets.only(top: children.isEmpty ? 0 : 15),
width: 200, height: 30,
child: TextFormField( textAlign: TextAlign.start,
initialValue: widget.item.model?.model?[child]?.value,
onChanged: (value) {
widget.item.model ?? Model();
Future.delayed(const Duration(seconds: 2), () {
if (widget.item.model!.model?[child]?.value == value) {
dash.save!(dash.id);
}
});
widget.item.model?.model?[child]?.value = value;
},
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
hintText: "enter $child...",
fillColor: Colors.white,
filled: true,
labelText: child,
alignLabelWithHint: false,
errorStyle: const TextStyle(fontSize: 0),
hintStyle: const TextStyle(fontSize: 10),
labelStyle: const TextStyle(fontSize: 10),
floatingLabelBehavior: FloatingLabelBehavior.always,
enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
border: const OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
)))
);
}
if (widget.dash.scheduler["mode"] != null && widget.dash.scheduler["mode"] == 1) {
children.add(
Tooltip( message: "hostname",
child: Container( margin: EdgeInsets.only(top: children.isEmpty ? 0 : 15),
width: 200, height: 30,
child: TextFormField( textAlign: TextAlign.start,
onChanged: (value) {
widget.item.model ?? Model();
Future.delayed(const Duration(seconds: 2), () {
if (widget.item.model!.model?["hostname"]?.value == value) {
dash.save!(dash.id);
}
});
widget.item.model?.model?["hostname"]?.value = value;
},
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
hintText: "enter hostname...",
fillColor: Colors.white,
filled: true,
labelText: "hostname",
alignLabelWithHint: false,
errorStyle: TextStyle(fontSize: 0),
hintStyle: TextStyle(fontSize: 10),
labelStyle: TextStyle(fontSize: 10),
floatingLabelBehavior: FloatingLabelBehavior.always,
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
)))
);
children.add(
Tooltip( message: "port",
child: Container( margin: EdgeInsets.only(top: children.isEmpty ? 0 : 15),
width: 200, height: 50,
child: TextFormField( textAlign: TextAlign.start,
onChanged: (value) {
widget.item.model ?? Model();
Future.delayed(const Duration(seconds: 2), () {
if (widget.item.model!.model?["port"]?.value == value) {
dash.save!(dash.id);
}
});
try {
widget.item.model?.model?["port"]?.value = int.parse(value);
} catch (e) { /**/ }
},
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
], // Only numbers can be entered
maxLength: 4,
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
hintText: "enter port...",
fillColor: Colors.white,
filled: true,
labelText: "port",
alignLabelWithHint: false,
errorStyle: TextStyle(fontSize: 0),
hintStyle: TextStyle(fontSize: 10),
labelStyle: TextStyle(fontSize: 10),
floatingLabelBehavior: FloatingLabelBehavior.always,
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
)))
);
}
return Column( children: [
Container( padding: const EdgeInsets.all(10), width: 250, height: 60, margin: const EdgeInsets.only(bottom: 15),
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))),
child: const Column( mainAxisAlignment: MainAxisAlignment.center, children: [
Text("ELEMENT INFO", style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
Text("<general>", style: TextStyle(fontSize: 12), textAlign: TextAlign.center),
])),
...children
]);
}
}

View File

@ -1,61 +0,0 @@
import 'package:flutter/material.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/pages/workflow.dart';
Map<String, Map<String, AbstractItem>> proxyWfItem = {};
class ProxyFormsWidget extends StatefulWidget {
AbstractItem item;
ProxyFormsWidget ({ super.key, required this.item });
@override ProxyFormsWidgetState createState() => ProxyFormsWidgetState();
}
class ProxyFormsWidgetState extends State<ProxyFormsWidget> {
@override Widget build(BuildContext context) {
List<Widget> children = [];
var l = widget.item.model?.model?.keys ?? [];
for (var child in l) {
if (widget.item.model!.model![child]!.type != "string") { continue; }
children.add(
Tooltip( message: child,
child: Container( margin: EdgeInsets.only(top: children.isEmpty ? 0 : 15),
width: 160, height: 30,
child: TextFormField( textAlign: TextAlign.start,
initialValue: widget.item.model?.model?[child]?.value,
onChanged: (value) {
widget.item.model ?? Model();
Future.delayed(const Duration(seconds: 2), () {
if (widget.item.model!.model?[child]?.value == value) {
dash.save!(dash.id);
}
});
widget.item.model?.model?[child]?.value = value;
},
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
hintText: "enter $child...",
fillColor: Colors.white,
filled: true,
labelText: child,
alignLabelWithHint: false,
errorStyle: const TextStyle(fontSize: 0),
hintStyle: const TextStyle(fontSize: 10),
labelStyle: const TextStyle(fontSize: 10),
floatingLabelBehavior: FloatingLabelBehavior.always,
enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
border: const OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
)))
);
}
return Column( children: [
Container( padding: const EdgeInsets.all(10), width: 200, height: 60, margin: const EdgeInsets.only(bottom: 15),
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))),
child: const Column( mainAxisAlignment: MainAxisAlignment.center, children: [
Text("ELEMENT INFO", style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
Text("<general>", style: TextStyle(fontSize: 12), textAlign: TextAlign.center),
])),
...children
]);
}
}

View File

@ -5,9 +5,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart'; import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:flutter_advanced_switch/flutter_advanced_switch.dart'; import 'package:flutter_advanced_switch/flutter_advanced_switch.dart';
import 'package:datetime_picker_formfield/datetime_picker_formfield.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/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/pages/workflow.dart';
import 'package:oc_front/widgets/dialog/alert.dart'; import 'package:oc_front/widgets/dialog/alert.dart';
import 'package:oc_front/widgets/inputs/shallow_text_input.dart';
class SchedulerFormsWidget extends StatefulWidget { class SchedulerFormsWidget extends StatefulWidget {
Dashboard item; Dashboard item;
@ -43,14 +48,39 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
} }
List<GlobalKey<FormFieldState>> formKeys = [GlobalKey<FormFieldState>(), GlobalKey<FormFieldState>(), List<GlobalKey<FormFieldState>> formKeys = [GlobalKey<FormFieldState>(), GlobalKey<FormFieldState>(),
GlobalKey<FormFieldState>(), GlobalKey<FormFieldState>()]; GlobalKey<FormFieldState>(), GlobalKey<FormFieldState>()];
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: [ return Column( children: [
Container( padding: const EdgeInsets.all(10), width: 200, height: 60, Container( padding: const EdgeInsets.all(10), width: 250, height: 60,
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))), decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))),
child: const Column( mainAxisAlignment: MainAxisAlignment.center, children: [ child: const Column( mainAxisAlignment: MainAxisAlignment.center, children: [
Text("WORKFLOW INFO", style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), textAlign: TextAlign.center), Text("WORKFLOW INFO", style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
Text("<general>", style: TextStyle(fontSize: 12), textAlign: TextAlign.center), Text("<general>", style: TextStyle(fontSize: 12), textAlign: TextAlign.center),
])), ])),
Container(height: 20), 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( AdvancedSwitch(
width: 140, width: 140,
initialValue: widget.item.scheduler["mode"] == 1, initialValue: widget.item.scheduler["mode"] == 1,
@ -63,6 +93,7 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
setState(() { setState(() {
widget.item.scheduler["mode"] = value == true ? 1 : 0; widget.item.scheduler["mode"] = value == true ? 1 : 0;
if ((widget.item.scheduler["mode"] == 1 )) { widget.item.scheduler.remove("cron"); } if ((widget.item.scheduler["mode"] == 1 )) { widget.item.scheduler.remove("cron"); }
widget.item.save!(widget.item.id);
})); }));
},), },),
Container(height: 5), Container(height: 5),
@ -131,11 +162,10 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
lastDate: DateTime(2100) lastDate: DateTime(2100)
); );
if (date != null) { if (date != null) {
var n = TimeOfDay.now(); TimeOfDay? time;
TimeOfDay? time = n;
var count = 0; var count = 0;
while(((time?.hour ?? 0) + ((time?.minute ?? 0) / 100)) <= (n.hour + ((n.minute + 1) / 100)) ) { while(date!.microsecondsSinceEpoch <= (DateTime.now().microsecondsSinceEpoch) || time == null) {
if (count > 0 && time != null) { if (count > 0) {
showAlertBanner( context, () {}, showAlertBanner( context, () {},
const AlertAlertBannerChild( const AlertAlertBannerChild(
text: "must be at least 1 minute from now to let system check info"),// <-- Put any widget here you want! text: "must be at least 1 minute from now to let system check info"),// <-- Put any widget here you want!
@ -161,9 +191,9 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
); );
if (time == null) { return DateTime.now().add( const Duration(minutes: 1)); } if (time == null) { return DateTime.now().add( const Duration(minutes: 1)); }
count++; count++;
} date = DateTime(date.year, date.month, date.day, time.hour, time.minute);
date = date.add(Duration(hours: time?.hour ?? 0, minutes: time?.minute ?? 0));
widget.item.scheduler["start"] = date.toUtc().toIso8601String(); widget.item.scheduler["start"] = date.toUtc().toIso8601String();
}
widget.item.save!(widget.item.id); widget.item.save!(widget.item.id);
} }
return date; return date;
@ -228,13 +258,15 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
); );
if (date != null) { if (date != null) {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
var n = TimeOfDay.now(); date = DateTime(date.year, date.month, date.day, date.hour, date.minute);
TimeOfDay? time = TimeOfDay(hour: date.hour, minute: date.minute); TimeOfDay? time;
var count = 0; var count = 0;
while(((time?.hour ?? 0) + (time?.minute ?? 0 / 100)) <= (n.hour + ((n.minute + 1) / 100)) ) { while(date!.microsecondsSinceEpoch <= (DateTime.now().microsecondsSinceEpoch)
if (count > 0 && time != null) { || time == null
|| (date.microsecondsSinceEpoch) <= (DateTime.parse(widget.item.scheduler["start"] ?? DateTime.now().toIso8601String()).microsecondsSinceEpoch)) {
if (count > 0) {
showAlertBanner( context, () {}, showAlertBanner( context, () {},
const AlertAlertBannerChild(text: "must be at least 1 minute from now to let system check info"),// <-- Put any widget here you want! const AlertAlertBannerChild(text: "must be at least 1 minute from now to let system check info && upper starting date"),// <-- Put any widget here you want!
alertBannerLocation: AlertBannerLocation.bottom,); alertBannerLocation: AlertBannerLocation.bottom,);
} }
time = await showTimePicker(context: context, time = await showTimePicker(context: context,
@ -257,9 +289,9 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
); );
if (time == null) { return null; } if (time == null) { return null; }
count++; count++;
} date = DateTime(date.year, date.month, date.day, time.hour, time.minute);
date = date.add(Duration(hours: time?.hour ?? 0, minutes: time?.minute ?? 0));
widget.item.scheduler["end"] = date.toUtc().toIso8601String(); widget.item.scheduler["end"] = date.toUtc().toIso8601String();
}
widget.item.save!(widget.item.id); widget.item.save!(widget.item.id);
} }
return date; return date;
@ -326,10 +358,19 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
), ),
))), ))),
const Divider(color: Colors.grey), Container(
width: 250,
height: 20,
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))),
),
const SizedBox(
width: 250,
height: 10,
),
Tooltip( message: "check booking", Tooltip( message: "check booking",
child: InkWell( mouseCursor: SystemMouseCursors.click, child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () { onTap: () {
if (dash.scheduleActive) { return; }
if (dash.scheduler["start"] == null ) { if (dash.scheduler["start"] == null ) {
DateTime now = DateTime.now().add(const Duration(minutes: 5)); DateTime now = DateTime.now().add(const Duration(minutes: 5));
dash.scheduler["start"] = now.toUtc().toIso8601String(); dash.scheduler["start"] = now.toUtc().toIso8601String();
@ -357,12 +398,13 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
setState(() {}); setState(() {});
} }
); );
}, child: Container( margin: const EdgeInsets.all(10), }, child: Container( margin: const EdgeInsets.only(bottom: 5),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), decoration: BoxDecoration(borderRadius: BorderRadius.circular(5),
border: Border.all(color: widget.booking == null ? Colors.black : (widget.booking == true ? 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: 140, height: 30, width: 200, height: 30,
child: Icon( child: Icon(
Icons.verified_outlined, color:widget.booking == null ? Colors.black : (widget.booking == true? Colors.green : Colors.red)), Icons.verified_outlined,
color: widget.booking == null && !dash.scheduleActive ? Colors.black : (widget.booking == true || dash.scheduleActive ? Colors.green : Colors.red)),
)) ))
), ),
Tooltip( message: dash.scheduleActive ? "unbook" : "book", Tooltip( message: dash.scheduleActive ? "unbook" : "book",
@ -376,20 +418,44 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
} else { k.currentState!.save();} } else { k.currentState!.save();}
} }
} }
if (dash.scheduler["start"] == null ) { DateTime now = DateTime.now().add(const Duration(minutes: 1));
DateTime now = DateTime.now().add(const Duration(minutes: 5)); if (dash.scheduler["start"] == null || DateTime.parse(dash.scheduler["start"]!).isBefore(now)) {
dash.scheduler["start"] = now.toUtc().toIso8601String(); dash.scheduler["start"] = now.toUtc().toIso8601String();
if (dash.scheduler["end"] != null) {
dash.scheduler["end"] = now.add(const Duration(minutes: 1)).toUtc().toIso8601String();
}
} }
widget.item.save!(widget.item.id); widget.item.save!(widget.item.id);
setState(() { }); setState(() { });
}, child: Container( margin: const EdgeInsets.all(10), }, child: Container( margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), decoration: BoxDecoration(borderRadius: BorderRadius.circular(5),
border: Border.all(color: dash.scheduleActive ? Colors.green : Colors.black, width: 1)), border: Border.all(color: dash.scheduleActive ? Colors.green : Colors.black)),
width: 140, height: 30, width: 200, height: 30,
child: Icon( child: Icon(
dash.scheduleActive ? Icons.cancel_schedule_send : Icons.schedule_send, color: dash.scheduleActive ? Colors.green : Colors.black), dash.scheduleActive ? Icons.cancel_schedule_send : Icons.schedule_send, color: dash.scheduleActive ? Colors.green : Colors.black),
)) ))
), ),
widget.item.info["shared"] != null && (widget.item.info["shared"] as List<dynamic>).isNotEmpty ? Column( children: [
Container(
height: 20,
width: 250,
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, overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 14, color: Colors.black, fontWeight: FontWeight.bold),
"Workflow is shared in ${(widget.item.info["shared"] as List<dynamic>).length} workspace(s)")),
...(widget.item.info["shared"] as List<dynamic>).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: ${sw != null && sw.name != null ?
"${sw.name!.substring(0, sw.name!.length > 15 ? 12 : sw.name!.length)}${sw.name!.length > 15 ? "..." : ""}" : ""}") ]));
},)
]) : Container()
]); ]);
} }
} }

View File

@ -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<List<Shallow>> Function()? all;
Future<void> Function(String)? load;
Future<void> 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<ShallowDropdownInputWidget> {
@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<DropdownMenuItem> items = [];
if (widget.maptoDropdown != null) {
items = s.data?.map((e) => widget.maptoDropdown!(e)).toList() ?? <DropdownMenuItem>[];
} else {
items = s.data?.map((e) => DropdownMenuItem(value: e.id,child: Text(e.name),)).toList() ?? <DropdownMenuItem>[];
}
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)) )
),
]); });
}
}

View File

@ -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<void> Function(Map<String,dynamic>)? load;
Future<void> Function(String)? loadStr;
Future<void> 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<ShallowTextInputWidget> 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<String, dynamic> serialize() {
Map<String, dynamic> 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<ShallowTextInputWidget> {
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)) )
),
]);
}
}

View File

@ -18,7 +18,7 @@ class ItemRowWidget extends StatefulWidget {
} }
class ItemRowWidgetState extends State<ItemRowWidget> { class ItemRowWidgetState extends State<ItemRowWidget> {
@override Widget build(BuildContext context) { @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 ratio = MediaQuery.of(context).size.width != widget.contextWidth ? 0.5 : 1; // 2;
var itemWidth = (((widget.contextWidth - imageSize) / 3) - 80) / ratio; var itemWidth = (((widget.contextWidth - imageSize) / 3) - 80) / ratio;
itemWidth = itemWidth > 100 ? 100 : ( itemWidth < 40 ? 40 : itemWidth ); itemWidth = itemWidth > 100 ? 100 : ( itemWidth < 40 ? 40 : itemWidth );
@ -30,6 +30,7 @@ class ItemRowWidgetState extends State<ItemRowWidget> {
Widget w = Container( Widget w = Container(
width: widget.contextWidth, width: widget.contextWidth,
height: 100, height: 100,
padding: EdgeInsets.only(left: imageSize == 0 ? 20 : 0),
decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.grey.shade300)) ), decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.grey.shade300)) ),
child: Row( children: [ child: Row( children: [
widget.low ? Container( padding: const EdgeInsets.only(left: 10),) : Container( padding: const EdgeInsets.all(10), widget.low ? Container( padding: const EdgeInsets.only(left: 10),) : Container( padding: const EdgeInsets.all(10),

View File

@ -0,0 +1,74 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:oc_front/models/response.dart';
const List<GlobalKey<State>> _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<IconData> badges = [];
void Function(String?)? delete;
void Function(String?)? edit;
List<GlobalKey<State>> 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<ShallowItemRowWidget> {
@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 );
}
}

View File

@ -60,7 +60,7 @@ class LogWidgetState extends State<LogWidget> {
child: Icon( widget.expanded ? Icons.keyboard_arrow_down_outlined : Icons.arrow_forward_ios, size: widget.expanded ? 25 : 15, 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,)))), color: map.isEmpty ? Colors.grey : Colors.black, weight: widget.expanded ? 100 : 1000,)))),
Padding( padding: const EdgeInsets.only(right: 10), 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))), 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: () { Tooltip( message : "copy to clipboard", child: InkWell( child: const Icon(Icons.copy, size: 15, color: Colors.grey), onTap: () {
if (widget.item.message != null) { if (widget.item.message != null) {

View File

@ -1,112 +0,0 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/models/workspace_local.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<MenuWorkspaceWidget> {
@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 : 2),
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 / 2) - 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)
)
)
)
])
]);
}
}

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:oc_front/core/sections/header/menu.dart';
import 'package:oc_front/core/services/router.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 { class TooltipWidget extends StatefulWidget {
int index = -1; int index = -1;

View File

@ -43,7 +43,7 @@ class ScheduleWidgetState extends State<ScheduleWidget> {
List<Widget> children = []; List<Widget> children = [];
if (selected != null) { if (selected != null) {
for (var wf in widget.data[selected!] ?? (<WorkflowExecution>[])) { for (var wf in widget.data[selected!] ?? (<WorkflowExecution>[])) {
DateTime d2 = DateTime.parse(wf.executionData!); DateTime d2 = DateTime.parse(wf.executionData!).toLocal();
children.add( InkWell( children.add( InkWell(
onTap: () => setState(() { selectedReal = wf.executionData; }), onTap: () => setState(() { selectedReal = wf.executionData; }),
child: Container( margin: const EdgeInsets.all(10), child: Container( margin: const EdgeInsets.all(10),

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; 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/core/sections/header/header.dart';
import 'package:oc_front/models/workflow.dart'; import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/widgets/sheduler_items/schedule.dart'; import 'package:oc_front/widgets/sheduler_items/schedule.dart';
@ -25,9 +26,9 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
List<Widget> widgets = []; List<Widget> widgets = [];
for (var ev in widget.data[element] ?? ([] as List<WorkflowExecution>)) { for (var ev in widget.data[element] ?? ([] as List<WorkflowExecution>)) {
widget.keys[ev.executionData!] = GlobalKey(); widget.keys[ev.executionData!] = GlobalKey();
var d2 = DateTime.parse(ev.executionData!); var d2 = DateTime.parse(ev.executionData!).toLocal();
DateTime? d3; DateTime? d3;
try { d3 = DateTime.parse(ev.endDate!); try { d3 = DateTime.parse(ev.endDate!).toLocal();
} catch (e) { /* */ } } catch (e) { /* */ }
widgets.add(InkWell( widgets.add(InkWell(
onTap: () => widget.parent?.setState(() { onTap: () => widget.parent?.setState(() {
@ -79,7 +80,11 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
))); )));
} }
var date = DateTime.parse(element); 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, enabled: widget.enabled,
shape: const ContinuousRectangleBorder(), shape: const ContinuousRectangleBorder(),
iconColor: Colors.grey, iconColor: Colors.grey,
@ -97,9 +102,7 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
), ),
collapsedIconColor: Colors.grey, collapsedIconColor: Colors.grey,
children: widgets, children: widgets,
), )])));
Divider(color: Colors.grey.shade300, height: 1)
]));
} }
Future.delayed( const Duration(milliseconds: 100), () { Future.delayed( const Duration(milliseconds: 100), () {
if (selectedReal != null) { if (selectedReal != null) {

View File

@ -23,6 +23,7 @@ typedef ConnectionListener = void Function(
/// It notifies changes to [FlowChart] /// It notifies changes to [FlowChart]
// //
class Dashboard extends ChangeNotifier { class Dashboard extends ChangeNotifier {
bool shouldSave = true;
GlobalKey<FlowChartSelectedMenuState> selectedMenuKey = GlobalKey<FlowChartSelectedMenuState>(); GlobalKey<FlowChartSelectedMenuState> selectedMenuKey = GlobalKey<FlowChartSelectedMenuState>();
GlobalKey<FlowChartLeftMenuState> selectedLeftMenuKey = GlobalKey<FlowChartLeftMenuState>(); GlobalKey<FlowChartLeftMenuState> selectedLeftMenuKey = GlobalKey<FlowChartLeftMenuState>();
GlobalKey<FlowChartMenuState> chartMenuKey = GlobalKey<FlowChartMenuState>(); GlobalKey<FlowChartMenuState> chartMenuKey = GlobalKey<FlowChartMenuState>();
@ -30,6 +31,7 @@ class Dashboard extends ChangeNotifier {
List<Map<String, dynamic>> tempHistory = []; List<Map<String, dynamic>> tempHistory = [];
List<Map<String, dynamic>> history = []; List<Map<String, dynamic>> history = [];
Map<String, dynamic> scheduler = {}; Map<String, dynamic> scheduler = {};
Map<String, dynamic> info = {};
bool scheduleActive = false; bool scheduleActive = false;
String? id; String? id;
String name; String name;
@ -48,6 +50,7 @@ class Dashboard extends ChangeNotifier {
List<Widget> Function(FlowData? obj)? infoItemWidget; List<Widget> Function(FlowData? obj)? infoItemWidget;
List<Widget> Function()? infoWidget; List<Widget> Function()? infoWidget;
FlowData? Function(Map<String, dynamic> json)? transformToData; FlowData? Function(Map<String, dynamic> json)? transformToData;
bool addChange = false;
/// ///
Dashboard({ Dashboard({
this.id, this.id,
@ -90,7 +93,6 @@ class Dashboard extends ChangeNotifier {
} }
tempHistory = []; tempHistory = [];
history = []; history = [];
addToHistory();
} }
Future<void> Function(String cat)? load; Future<void> Function(String cat)? load;
@ -231,6 +233,7 @@ class Dashboard extends ChangeNotifier {
void deserialize(Map<String, dynamic> graph) { void deserialize(Map<String, dynamic> graph) {
elements = []; elements = [];
arrows = []; arrows = [];
info["shared"] = graph["shared"] ?? [];
scheduler = graph['schedule'] ?? {}; scheduler = graph['schedule'] ?? {};
scheduleActive = graph['schedule_active'] ?? false; scheduleActive = graph['schedule_active'] ?? false;
setZoomFactor(graph["graph"]?["zoom"] ?? 1.0); setZoomFactor(graph["graph"]?["zoom"] ?? 1.0);
@ -265,6 +268,7 @@ class Dashboard extends ChangeNotifier {
} }
selectedMenuKey.currentState?.setState(() { }); selectedMenuKey.currentState?.setState(() { });
chartMenuKey.currentState?.setState(() { }); chartMenuKey.currentState?.setState(() { });
addToHistory();
} }
/// add listener called when a new connection is created /// add listener called when a new connection is created
@ -289,22 +293,63 @@ class Dashboard extends ChangeNotifier {
handlerFeedbackOffset = offset; 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<FlowData>) 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() { void addToHistory() {
if (noHistory) {
Future.delayed(Duration(seconds: 2), () {
noHistory = false;
});
return;
}
if (tempHistory.length >= 50) { tempHistory.removeAt(0); } if (tempHistory.length >= 50) { tempHistory.removeAt(0); }
tempHistory.add(toMap()); tempHistory.add(toMap());
if (save != null) { save!(id); }
history = tempHistory.map((e) => e).toList(); history = tempHistory.map((e) => e).toList();
chartKey.currentState?.setState(() { });
chartMenuKey.currentState?.setState(() { }); chartMenuKey.currentState?.setState(() { });
} }
bool isBack = false; bool isBack = false;
void back() { void back() {
if (canBack()) {
noHistory = true;
tempHistory.removeLast(); tempHistory.removeLast();
if (tempHistory.length == 0) return; if (tempHistory.length > 0) {
copyFromMap(tempHistory.last); copyFromMap(tempHistory.last);
}
chartKey.currentState?.setState(() { }); chartKey.currentState?.setState(() { });
chartMenuKey.currentState?.setState(() { }); chartMenuKey.currentState?.setState(() { });
} }
}
bool canBack() { bool canBack() {
return tempHistory.length > 1; return tempHistory.length > 1;
@ -316,6 +361,7 @@ class Dashboard extends ChangeNotifier {
void forward() { void forward() {
if (canForward()) { if (canForward()) {
noHistory = true;
tempHistory.add(history[tempHistory.length]); tempHistory.add(history[tempHistory.length]);
copyFromMap(tempHistory.last); copyFromMap(tempHistory.last);
chartKey.currentState?.setState(() { }); chartKey.currentState?.setState(() { });
@ -330,6 +376,9 @@ class Dashboard extends ChangeNotifier {
bool notify = true, bool notify = true,
}) { }) {
element.isResizing = resizable; element.isResizing = resizable;
if (save != null) {
save!(id);
}
if (notify) notifyListeners(); if (notify) notifyListeners();
} }
@ -345,6 +394,10 @@ class Dashboard extends ChangeNotifier {
} }
element.setScale(1, gridBackgroundParams.scale); element.setScale(1, gridBackgroundParams.scale);
elements.add(element); elements.add(element);
addChange = true;
if (save != null) {
save!(id);
}
if (notify) { if (notify) {
notifyListeners(); notifyListeners();
} }
@ -359,7 +412,6 @@ class Dashboard extends ChangeNotifier {
@override @override
void notifyListeners() { void notifyListeners() {
addToHistory();
super.notifyListeners(); super.notifyListeners();
} }

View File

@ -107,6 +107,7 @@ class FlowElement<T extends FlowData> extends ChangeNotifier {
return false; return false;
} }
factory FlowElement.fromMap(Dashboard dashboard, Map<String, dynamic> map) { factory FlowElement.fromMap(Dashboard dashboard, Map<String, dynamic> map) {
print("FlowElement.fromMap ${map}");
final e = FlowElement<T>( final e = FlowElement<T>(
element: (dashboard.transformToData != null element: (dashboard.transformToData != null
? dashboard.transformToData!(map['element'] ?? {}) ? dashboard.transformToData!(map['element'] ?? {})
@ -341,6 +342,7 @@ class FlowElement<T extends FlowData> extends ChangeNotifier {
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return <String, dynamic>{ return <String, dynamic>{
'widget': widget, 'widget': widget,
'element': element?.serialize(),
'positionDx': position.dx, 'positionDx': position.dx,
'positionDy': position.dy, 'positionDy': position.dy,
'size.width': size.width, 'size.width': size.width,

View File

@ -55,14 +55,18 @@ class FlowChart<T extends FlowData> extends StatefulWidget {
this.categories = const [], this.categories = const [],
required this.draggableItemBuilder, required this.draggableItemBuilder,
this.onDashboardAlertOpened, this.onDashboardAlertOpened,
this.menuWidget,
this.current,
this.menuExtension, this.menuExtension,
}) {} }) {}
final String? current;
final List<String> categories; final List<String> categories;
final double width; final double width;
final double height; final double height;
final double innerMenuWidth; final double innerMenuWidth;
Widget Function()? menuExtension; Widget Function()? menuExtension;
Widget Function(Dashboard data)? menuWidget;
double itemWidth = 80; double itemWidth = 80;
double zoom = 1; double zoom = 1;
@ -301,6 +305,10 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!widget.dashboard.isOpened && widget.onDashboardAlertOpened != null ) { if (!widget.dashboard.isOpened && widget.onDashboardAlertOpened != null ) {
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), () { Future.delayed(Duration(milliseconds: 100), () {
showDialog( showDialog(
barrierDismissible: false, barrierDismissible: false,
@ -314,6 +322,9 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
}); }); }); });
} }
} else {
widget.dashboard.isOpened = true;
}
/// get dashboard position after first frame is drawn /// get dashboard position after first frame is drawn
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (mounted) { if (mounted) {
@ -369,7 +380,7 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.add) { if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.add) {
change = true; change = true;
for (var el in widget.dashboard.elementSelected) { for (var el in widget.dashboard.elementSelected) {
widget.dashboard.elements.add(FlowElement<T>( widget.dashboard.addElement(FlowElement<T>(
element: el.element as T, element: el.element as T,
dashboard: widget.dashboard, dashboard: widget.dashboard,
id: const Uuid().v4(), id: const Uuid().v4(),
@ -385,13 +396,13 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
} }
if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.delete) { if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.delete) {
change = true; change = true;
widget.dashboard.elements.removeWhere( (el) => el.isSelected ); widget.dashboard.removeElements( (el) => el.isSelected );
for (var arrow in widget.dashboard.arrowsSelected) { for (var arrow in widget.dashboard.arrowsSelected) {
for (var el in widget.dashboard.elements.where((element) => element.id == arrow.fromID.split("_")[0])) { for (var el in widget.dashboard.elements.where((element) => element.id == arrow.fromID.split("_")[0])) {
el.next.removeAt(int.parse(arrow.fromID.split("_")[1])); el.next.removeAt(int.parse(arrow.fromID.split("_")[1]));
} }
} }
widget.dashboard.arrows.removeWhere( (el) => el.isSelected ); widget.dashboard.removeArrows( (el) => el.isSelected );
} }
if (change) { if (change) {
DrawingArrow.instance.notifyListeners(); DrawingArrow.instance.notifyListeners();
@ -416,6 +427,7 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
child: ChartWidget<T>( child: ChartWidget<T>(
key: widget.dashboard.chartKey, key: widget.dashboard.chartKey,
flowChart: this, flowChart: this,
menuWidget: widget.menuWidget,
dashboard: widget.dashboard, dashboard: widget.dashboard,
onNewConnection: widget.onNewConnection, onNewConnection: widget.onNewConnection,
onDashboardTapped: widget.onDashboardLongTapped, onDashboardTapped: widget.onDashboardLongTapped,
@ -437,7 +449,7 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
String newID = const Uuid().v4(); String newID = const Uuid().v4();
double ratio = 1; double ratio = 1;
if (e.getWidth() != null && e.getHeight() != null) { if (e.getWidth() != null && e.getHeight() != null) {
ratio = (e.getHeight()! / (e.getWidth()! - 30)); ratio = (e.getHeight()! / (e.getWidth()!));
} }
FlowElement<T> el = FlowElement<T>( FlowElement<T> el = FlowElement<T>(
dashboard: widget.dashboard, dashboard: widget.dashboard,
@ -463,11 +475,11 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
widget.dashboard.isMenu ? Positioned(top: 50, child: FlowChartLeftMenu<T>( widget.dashboard.isMenu ? Positioned(top: 50, child: FlowChartLeftMenu<T>(
key: widget.dashboard.selectedLeftMenuKey, key: widget.dashboard.selectedLeftMenuKey,
dashboard: widget.dashboard, dashboard: widget.dashboard,
menuExtension: widget.menuExtension,
categories: widget.categories, categories: widget.categories,
height: widget.height, height: widget.height,
innerMenuWidth: widget.innerMenuWidth, innerMenuWidth: widget.innerMenuWidth,
itemWidth: widget.itemWidth, itemWidth: widget.itemWidth,
menuExtension: widget.menuExtension,
draggableItemBuilder: widget.draggableItemBuilder as List<T> Function(String cat), draggableItemBuilder: widget.draggableItemBuilder as List<T> Function(String cat),
getDraggable: getDraggable, getDraggable: getDraggable,
) ) ) )
@ -579,10 +591,13 @@ class ChartWidget<T extends FlowData> extends StatefulWidget {
this.onScaleUpdate, this.onScaleUpdate,
required this.dashboard, required this.dashboard,
this.onNewConnection, this.onNewConnection,
this.menuWidget,
}) : super(key: key); }) : super(key: key);
FlowChartState flowChart; FlowChartState flowChart;
Widget Function(Dashboard dash)? menuWidget;
final void Function(BuildContext context, Offset position)? onDashboardTapped; final void Function(BuildContext context, Offset position)? onDashboardTapped;
/// callback for long tap on dashboard /// callback for long tap on dashboard
@ -739,7 +754,7 @@ class ChartWidgetState<T extends FlowData> extends State<ChartWidget> {
if (!hoverImportant) { if (!hoverImportant) {
for (var sel in widget.dashboard.elements) { sel.isSelected = false; } for (var sel in widget.dashboard.elements) { sel.isSelected = false; }
for (var sel in widget.dashboard.arrows) { 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(() {}); widget.dashboard.selectedMenuKey.currentState?.setState(() {});
DrawingArrow.instance.notifyListeners(); DrawingArrow.instance.notifyListeners();
}); });
@ -905,6 +920,7 @@ class ChartWidgetState<T extends FlowData> extends State<ChartWidget> {
Positioned(top: 0, right: 0, child: FlowChartMenu( Positioned(top: 0, right: 0, child: FlowChartMenu(
key: widget.dashboard.chartMenuKey, key: widget.dashboard.chartMenuKey,
chart: this, chart: this,
menuExtension: widget.menuWidget != null ? widget.menuWidget!(widget.dashboard) : null,
dashboard: widget.dashboard, dashboard: widget.dashboard,
width: MediaQuery.of(context).size.width) width: MediaQuery.of(context).size.width)
), ),

View File

@ -69,7 +69,9 @@ class FlowChartLeftMenuState<T extends FlowData> extends State<FlowChartLeftMenu
Container( Container(
width: widget.innerMenuWidth, width: widget.innerMenuWidth,
height: 50, height: 50,
decoration: const BoxDecoration(border: Border(left: BorderSide(color: Colors.white))), decoration: BoxDecoration(border: Border(
right: BorderSide(color: Colors.grey.shade300),
left: BorderSide(color: Colors.white))),
child: TextFormField( child: TextFormField(
style: const TextStyle(color: Colors.black, fontSize: 15), style: const TextStyle(color: Colors.black, fontSize: 15),
cursorColor: const Color.fromARGB(38, 166, 154, 1), cursorColor: const Color.fromARGB(38, 166, 154, 1),

View File

@ -11,8 +11,9 @@ class FlowChartMenu extends StatefulWidget {
ChartWidgetState chart; ChartWidgetState chart;
Dashboard dashboard; Dashboard dashboard;
double width = 100; double width = 100;
Widget? menuExtension;
FlowChartMenu ({ super.key, required this.chart, required this.dashboard, this.width = 100 }); FlowChartMenu ({ super.key, required this.chart, required this.dashboard, this.width = 100, this.menuExtension });
@override FlowChartMenuState createState() => FlowChartMenuState(); @override FlowChartMenuState createState() => FlowChartMenuState();
} }
class FlowChartMenuState extends State<FlowChartMenu> { class FlowChartMenuState extends State<FlowChartMenu> {
@ -22,7 +23,7 @@ class FlowChartMenuState extends State<FlowChartMenu> {
return Container( // SHORTCUT return Container( // SHORTCUT
width: widget.width, width: widget.width,
height: 50, height: 50,
padding: EdgeInsets.symmetric(horizontal: 20), padding: EdgeInsets.only(left: 20),
color: const Color.fromRGBO(38, 166, 154, 1), color: const Color.fromRGBO(38, 166, 154, 1),
child: Row( children : [ Expanded(flex: 2, child: Row( children: [ child: Row( children : [ Expanded(flex: 2, child: Row( children: [
widget.chart.widget.flowChart.widget.onDashboardAlertOpened == null ? Container() : Container( widget.chart.widget.flowChart.widget.onDashboardAlertOpened == null ? Container() : Container(
@ -240,6 +241,12 @@ class FlowChartMenuState extends State<FlowChartMenu> {
style: TextStyle(color: Colors.white, fontSize: 14), textAlign: TextAlign.start), style: TextStyle(color: Colors.white, fontSize: 14), textAlign: TextAlign.start),
padding: EdgeInsets.symmetric(horizontal: 20))), padding: EdgeInsets.symmetric(horizontal: 20))),
])), ])),
widget.menuExtension != null && MediaQuery.of(context).size.width > 600 ? Container(
decoration: BoxDecoration(
border: Border(left: BorderSide(color: Colors.white, width: 1))
),
child: widget.menuExtension
) : Container()
]) ])
); );
} }

View File

@ -17,14 +17,14 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
Widget? w; Widget? w;
if (widget.isDashboardInfo && widget.dashboard.elementSelected.length == 1) { if (widget.isDashboardInfo && widget.dashboard.elementSelected.length == 1) {
w = Container( w = Container(
width: 200, width: 250,
height: widget.height, height: widget.height,
color: Colors.grey.shade300, 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.infoItemWidget!(widget.dashboard.elementSelected.first.element)
: [], : [],
widget.dashboard.arrowsSelected.isNotEmpty || widget.dashboard.elementSelected.isNotEmpty ? Container( widget.dashboard.arrowsSelected.isNotEmpty || widget.dashboard.elementSelected.isNotEmpty ? Container(
width: 200, width: 250,
margin: EdgeInsets.only(top: 15), margin: EdgeInsets.only(top: 15),
decoration: BoxDecoration(border: Border( decoration: BoxDecoration(border: Border(
top: BorderSide(color: Colors.grey, width: 1), top: BorderSide(color: Colors.grey, width: 1),
@ -33,19 +33,19 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
Tooltip( message: "remove", Tooltip( message: "remove",
child: InkWell( mouseCursor: SystemMouseCursors.click, child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () { onTap: () {
widget.dashboard.arrows.removeWhere((element) { widget.dashboard.removeArrows((element) {
if (element.isSelected && element.elementIndex != null && element.connIndex != null) { if (element.isSelected && element.elementIndex != null && element.connIndex != null) {
widget.dashboard.elements[element.elementIndex!].next.removeAt(element.connIndex!); widget.dashboard.elements[element.elementIndex!].next.removeAt(element.connIndex!);
} }
return element.isSelected; return element.isSelected;
}); });
widget.dashboard.elements.removeWhere((element) => element.isSelected); widget.dashboard.removeElements((element) => element.isSelected);
Future.delayed(Duration(milliseconds: 100), () { Future.delayed(Duration(milliseconds: 100), () {
widget.dashboard.chartKey.currentState?.setState(() { }); widget.dashboard.chartKey.currentState?.setState(() { });
}); });
}, child: Container( margin: EdgeInsets.all(10), }, child: Container( margin: EdgeInsets.all(10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.black, width: 1)), decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.black, width: 1)),
width: 140, height: 30, width: 200, height: 30,
child: Icon(Icons.delete_outline, color: Colors.black), child: Icon(Icons.delete_outline, color: Colors.black),
)) ))
), ),
@ -53,42 +53,42 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
child: InkWell( mouseCursor: SystemMouseCursors.click, child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () { onTap: () {
for (var sel in widget.dashboard.elementSelected) { 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); widget.dashboard.elements.last.position += Offset(50, 50);
} }
Future.delayed(Duration(milliseconds: 100), () { Future.delayed(Duration(milliseconds: 100), () {
widget.dashboard.chartKey.currentState?.setState(() { }); 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)), decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.black, width: 1)),
width: 140, height: 30, width: 200, height: 30,
child: Icon(Icons.copy, color: Colors.black), child: Icon(Icons.copy, color: Colors.black),
)) ))
), ),
]) ])
) : Container() ) : Container()
]) ])
); ));
} else if (widget.isDashboardInfo && widget.dashboard.infoWidget != null) { } else if (widget.isDashboardInfo && widget.dashboard.infoWidget != null) {
w = Container( w = Container(
width: 200, width: 250,
height: widget.height, height: widget.height,
color: Colors.grey.shade300, color: Colors.grey.shade300,
child: Column( children: widget.dashboard.infoWidget != null ? widget.dashboard.infoWidget!() : []) child: Column( children: widget.dashboard.infoWidget != null ? widget.dashboard.infoWidget!() : [])
); );
} else { } else {
w = Container( // SHORTCUT w = Container( // SHORTCUT
width: 200, width: 250,
height: widget.height, height: widget.height,
color: Colors.grey.shade300, color: Colors.grey.shade300,
child: Column( children: [ child: Column( children: [
Container( padding: EdgeInsets.all(10), width: 200, height: 60, Container( padding: EdgeInsets.all(10), width: 250, height: 60,
decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))), decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))),
child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [
Text("STYLE ${widget.dashboard.elementSelected.isNotEmpty ? "ELEMENT" : "ARROW"}", style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), textAlign: TextAlign.center), Text("STYLE ${widget.dashboard.elementSelected.isNotEmpty ? "ELEMENT" : "ARROW"}", style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
Text("<${widget.dashboard.arrowsSelected.isEmpty && widget.dashboard.elementSelected.isEmpty ? "general" : "selected"}>", style: TextStyle(fontSize: 12), textAlign: TextAlign.center), Text("<${widget.dashboard.arrowsSelected.isEmpty && widget.dashboard.elementSelected.isEmpty ? "general" : "selected"}>", style: TextStyle(fontSize: 12), textAlign: TextAlign.center),
])), ])),
Container( width: 200, height: widget.height - 60, child: SingleChildScrollView( child: Column( children: [ Container( width: 250, height: widget.height - 60, child: SingleChildScrollView( child: Column( children: [
widget.dashboard.elementSelected.isNotEmpty ? Container() : Container( padding: EdgeInsets.symmetric(horizontal: 10, vertical: 20), widget.dashboard.elementSelected.isNotEmpty ? Container() : Container( padding: EdgeInsets.symmetric(horizontal: 10, vertical: 20),
decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))), decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))),
child: Column( children: [ child: Column( children: [
@ -100,7 +100,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
constraints: BoxConstraints(maxWidth: 100), constraints: BoxConstraints(maxWidth: 100),
child: Row( children: [ child: Row( children: [
MySeparator( MySeparator(
width: 45, width: 65,
dashWidth: widget.dashboard.defaultDashWidth, dashWidth: widget.dashboard.defaultDashWidth,
dashSpace: widget.dashboard.defaultDashSpace, dashSpace: widget.dashboard.defaultDashSpace,
color: Colors.black color: Colors.black
@ -193,7 +193,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
Tooltip( message: "stroke width", Tooltip( message: "stroke width",
child: Container( child: Container(
margin: EdgeInsets.only(left: 10), margin: EdgeInsets.only(left: 10),
width: 45, height: 25, width: 75, height: 25,
child: TextFormField( textAlign: TextAlign.center, child: TextFormField( textAlign: TextAlign.center,
readOnly: widget.dashboard.defaultDashWidth <= 0, readOnly: widget.dashboard.defaultDashWidth <= 0,
initialValue: "${widget.dashboard.defaultStroke}", initialValue: "${widget.dashboard.defaultStroke}",
@ -280,7 +280,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
Tooltip( message: "space dash", Tooltip( message: "space dash",
child: Container( child: Container(
margin: EdgeInsets.only(top: 10), margin: EdgeInsets.only(top: 10),
width: 105 / 2, height: 25, width: 155 / 2, height: 25,
child: TextFormField( textAlign: TextAlign.center, child: TextFormField( textAlign: TextAlign.center,
readOnly: widget.dashboard.defaultDashWidth <= 0, readOnly: widget.dashboard.defaultDashWidth <= 0,
initialValue: "${widget.dashboard.defaultDashWidth}", initialValue: "${widget.dashboard.defaultDashWidth}",
@ -293,7 +293,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
}, },
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
fillColor: widget.dashboard.defaultDashWidth <= 0 ? Colors.grey.shade200 : Colors.white, fillColor: widget.dashboard.defaultDashWidth <= 0 ? Colors.grey.shade300 : Colors.white,
filled: true, filled: true,
labelText: "dash", labelText: "dash",
labelStyle: TextStyle(fontSize: 10), labelStyle: TextStyle(fontSize: 10),
@ -318,7 +318,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
Tooltip( message: "space width", Tooltip( message: "space width",
child: Container( child: Container(
margin: EdgeInsets.only(left: 10, top: 10), margin: EdgeInsets.only(left: 10, top: 10),
width: 105 / 2, height: 25, width: 155 / 2, height: 25,
child: TextFormField( textAlign: TextAlign.center, child: TextFormField( textAlign: TextAlign.center,
initialValue: "${widget.dashboard.defaultDashSpace}", initialValue: "${widget.dashboard.defaultDashSpace}",
onChanged: (value) { onChanged: (value) {
@ -330,7 +330,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
}, },
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
fillColor: widget.dashboard.defaultDashWidth <= 0 ? Colors.grey.shade200 : Colors.white, fillColor: widget.dashboard.defaultDashWidth <= 0 ? Colors.grey.shade300 : Colors.white,
filled: true, filled: true,
labelText: "space", labelText: "space",
labelStyle: TextStyle(fontSize: 10), labelStyle: TextStyle(fontSize: 10),
@ -414,7 +414,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
Tooltip( message: "forward size", Tooltip( message: "forward size",
child: Container( child: Container(
margin: EdgeInsets.only(left: 10, top: 10), margin: EdgeInsets.only(left: 10, top: 10),
width: 135, height: 25, width: 185, height: 25,
child: TextFormField( textAlign: TextAlign.center, child: TextFormField( textAlign: TextAlign.center,
initialValue: "${widget.dashboard.defaultForwardWidth}", initialValue: "${widget.dashboard.defaultForwardWidth}",
onChanged: (value) { onChanged: (value) {
@ -438,7 +438,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
}, },
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
fillColor: widget.dashboard.defaultDashWidth <= 0 ? Colors.grey.shade200 : Colors.white, fillColor: widget.dashboard.defaultDashWidth <= 0 ? Colors.grey.shade300 : Colors.white,
filled: true, filled: true,
labelText: "forward size", labelText: "forward size",
labelStyle: TextStyle(fontSize: 10), labelStyle: TextStyle(fontSize: 10),
@ -466,7 +466,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
Tooltip( message: "back size", Tooltip( message: "back size",
child: Container( child: Container(
margin: EdgeInsets.only(left: 10, top: 10), margin: EdgeInsets.only(left: 10, top: 10),
width: 135, height: 25, width: 185, height: 25,
child: TextFormField( textAlign: TextAlign.center, child: TextFormField( textAlign: TextAlign.center,
initialValue: "${widget.dashboard.defaultBackWidth}", initialValue: "${widget.dashboard.defaultBackWidth}",
onChanged: (value) { onChanged: (value) {
@ -490,7 +490,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
}, },
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
decoration: InputDecoration( decoration: InputDecoration(
fillColor: widget.dashboard.defaultDashWidth <= 0 ? Colors.grey.shade200 : Colors.white, fillColor: widget.dashboard.defaultDashWidth <= 0 ? Colors.grey.shade300 : Colors.white,
filled: true, filled: true,
labelText: "back size", labelText: "back size",
labelStyle: TextStyle(fontSize: 10), labelStyle: TextStyle(fontSize: 10),
@ -516,19 +516,19 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
]) ])
])), ])),
widget.dashboard.arrowsSelected.isNotEmpty || widget.dashboard.elementSelected.isNotEmpty ? Container( widget.dashboard.arrowsSelected.isNotEmpty || widget.dashboard.elementSelected.isNotEmpty ? Container(
width: 200, width: 250,
decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))), decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))),
child: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ child: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
Tooltip( message: "remove", Tooltip( message: "remove",
child: InkWell( mouseCursor: SystemMouseCursors.click, child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () { onTap: () {
widget.dashboard.arrows.removeWhere((element) { widget.dashboard.removeArrows((element) {
if (element.isSelected && element.elementIndex != null && element.connIndex != null) { if (element.isSelected && element.elementIndex != null && element.connIndex != null) {
widget.dashboard.elements[element.elementIndex!].next.removeAt(element.connIndex!); widget.dashboard.elements[element.elementIndex!].next.removeAt(element.connIndex!);
} }
return element.isSelected; return element.isSelected;
}); });
widget.dashboard.elements.removeWhere((element) => element.isSelected); widget.dashboard.removeElements((element) => element.isSelected);
Future.delayed(Duration(milliseconds: 100), () { Future.delayed(Duration(milliseconds: 100), () {
widget.dashboard.chartKey.currentState?.setState(() { }); widget.dashboard.chartKey.currentState?.setState(() { });
}); });
@ -542,7 +542,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
child: InkWell( mouseCursor: SystemMouseCursors.click, child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () { onTap: () {
for (var sel in widget.dashboard.elementSelected) { 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); widget.dashboard.elements.last.position += Offset(50, 50);
} }
Future.delayed(Duration(milliseconds: 100), () { Future.delayed(Duration(milliseconds: 100), () {
@ -562,7 +562,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
} }
return Column( children: [ return Column( children: [
Container( // SHORTCUT Container( // SHORTCUT
width: 200, width: 250,
height: 50, height: 50,
decoration: BoxDecoration(color: Colors.grey.shade300, border: Border(bottom: BorderSide(color: Colors.grey, width: 1))), decoration: BoxDecoration(color: Colors.grey.shade300, border: Border(bottom: BorderSide(color: Colors.grey, width: 1))),
child: Row( children: [ child: Row( children: [
@ -573,7 +573,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
child: Container( alignment: Alignment.center, child: Container( alignment: Alignment.center,
padding: EdgeInsets.symmetric(vertical: 10), padding: EdgeInsets.symmetric(vertical: 10),
color: widget.isDashboardInfo ? Colors.grey : Colors.grey.shade300, color: widget.isDashboardInfo ? Colors.grey : Colors.grey.shade300,
width: 100, child: Icon(Icons.info, color: Colors.white)) width: 125, child: Icon(Icons.info, color: Colors.white))
) )
), ),
Tooltip( Tooltip(
@ -583,7 +583,7 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
child: Container( alignment: Alignment.center, child: Container( alignment: Alignment.center,
padding: EdgeInsets.symmetric(vertical: 10), padding: EdgeInsets.symmetric(vertical: 10),
color: !widget.isDashboardInfo ? Colors.grey : Colors.grey.shade300, color: !widget.isDashboardInfo ? Colors.grey : Colors.grey.shade300,
width: 100, child: Icon(Icons.format_paint, color: Colors.white)), width: 125, child: Icon(Icons.format_paint, color: Colors.white)),
)) ))
])), ])),
w w

View File

@ -226,6 +226,7 @@ class DrawingArrow extends ChangeNotifier {
/// ///
void setFrom(Offset from) { void setFrom(Offset from) {
this.from = from; this.from = from;
notifyListeners(); notifyListeners();
} }
@ -350,7 +351,7 @@ class DrawArrowState extends State<DrawArrow> {
); );
if ( widget.flow.widget.dashboard.arrows.where( if ( widget.flow.widget.dashboard.arrows.where(
(element) => element.fromID == "${widget.srcElement.id}_${widget.index}").isEmpty) { (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); widget.flow.widget.dashboard.save!(widget.flow.widget.dashboard.id);
} else { } else {
var i = widget.flow.widget.dashboard.arrows.indexWhere( var i = widget.flow.widget.dashboard.arrows.indexWhere(
@ -481,9 +482,9 @@ class ArrowPainter extends CustomPainter {
final e = ArrowPainter( final e = ArrowPainter(
connIndex: map['connIndex'] as int, connIndex: map['connIndex'] as int,
elementIndex: map['elementIndex'] 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, fromID: map['fromID'] as String,
isSelected: map['isSelected'] as bool, isSelected: map['isSelected'] != null ? map['isSelected'] as bool : false,
params: ArrowParams.fromMap(map['params']), params: ArrowParams.fromMap(map['params']),
from: Offset( from: Offset(
map['fromDx'] as double, map['fromDx'] as double,
@ -500,14 +501,17 @@ class ArrowPainter extends CustomPainter {
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return { return {
'toID': toID,
'fromID': fromID, 'fromID': fromID,
'elementIndex': elementIndex, 'elementIndex': elementIndex,
'connIndex': connIndex, 'connIndex': connIndex,
'isSelected': isSelected.toString(), 'isSelected': isSelected,
'params': params.toJson(), 'params': params.toMap(),
'from': from.toString(), "fromDx" : from.dx,
'to': to.toString(), "fromDy" : from.dy,
'pivots': json.encode(pivots.map((e) => e.toMap()).toList()), "toDx" : to.dx,
"toDy" : to.dy,
'pivots': pivots.map((e) => e.toMap()).toList(),
}; };
} }