missing files

This commit is contained in:
mr 2025-02-05 09:07:39 +01:00
parent 05854c84d8
commit bed48b4cb4
31 changed files with 947 additions and 681 deletions

View File

@ -1,147 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/core/services/perms_service.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/pages/workflow.dart';
import 'package:oc_front/widgets/forms/sub_keys_forms.dart';
import 'package:oc_front/widgets/forms/sub_expose_forms.dart';
import 'package:oc_front/widgets/forms/sub_map_forms.dart';
import 'package:oc_front/widgets/inputs/sub_text_input.dart';
Map<String, Map<String, AbstractItem>> proxyWfItem = {};
// ADD EXPOSE
class ProcessingFormsWidget extends StatefulWidget {
String elementID;
ProcessingItem item;
Dashboard dash;
ProcessingFormsWidget ({ super.key, required this.item, required this.dash, required this.elementID });
@override ProcessingFormsWidgetState createState() => ProcessingFormsWidgetState();
}
class ProcessingFormsWidgetState extends State<ProcessingFormsWidget> {
Widget getInputAndOutputVariableForms(bool readOnly) {
var inList = widget.dash.GetArrowByElementID(widget.elementID, true);
var outList = widget.dash.GetArrowByElementID(widget.elementID, false);
List<Widget> res = [];
List<GraphItem> inItems = [];
List<GraphItem> outItems = [];
for (var inItem in inList) {
var element = widget.dash.getElement(inItem.fromID.substring(0,36));
if (element == null) { continue; }
var g = GraphItem();
g.fromDashboard(element.serialize());
inItems.add(g);
}
for (var outItem in outList) {
var element = widget.dash.getElement(outItem.toID.substring(0,36));
if (element == null) { continue; }
var g = GraphItem();
g.fromDashboard(element.serialize());
outItems.add(g);
}
if (inItems.isNotEmpty) {
res.add(SubKeysMapFormsWidget(dash: widget.dash, isInput: true, item: widget.item, elementID: widget.elementID,
categoryKey: "container", varKey: "env", graphItems: inItems, readOnly: readOnly));
}
if (outItems.isNotEmpty) {
res.add(SubKeysMapFormsWidget(dash: widget.dash, isInput: false, item: widget.item, elementID: widget.elementID,
categoryKey: "container", varKey: "env", graphItems: outItems, readOnly: readOnly));
}
return Column( children: res );
}
@override Widget build(BuildContext context) {
bool readOnly = !PermsService.getPerm(Perms.WORKFLOW_EDIT);
List<Widget> categories = [];
var l = widget.item.model?.model?.keys ?? [];
for (var child in l) {
var sub = widget.item.model!.model![child]!;
List<Widget> children = [];
for (var st in sub.keys) {
if (sub[st]!.type?.contains("map") ?? false) {
children.add(
SubMapFormsWidget(dash: dash, empty: children.isEmpty, item: widget.item,
readOnly: readOnly,
elementID: widget.elementID, categoryKey: child, varKey: st)
);
} else if (sub[st]!.type == "string") {
children.add(SubTextInputWidget(subkey: st,
readOnly: readOnly,
initialValue: widget.item.getVariable([child, st], widget.item.serialize()),
width: 180, empty: children.isEmpty, change: (value) {
widget.item.model ?? Model();
Future.delayed(const Duration(seconds: 2), () {
if (widget.item.getVariable([child, st], widget.item.serialize()) == value) {
dash.saveDash(dash.id);
}
});
var el = dash.getElement(widget.elementID);
widget.item = widget.item.deserialize(widget.item.setVariable([child, st], value, widget.item.serialize())) as dynamic;
el!.element = widget.item as dynamic;
})
);
}
}
categories.add(Container(
padding: const EdgeInsets.symmetric(vertical: 10),
width: 180,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("<${child.toUpperCase()}>", style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
Padding(padding: const EdgeInsets.only(bottom: 10), child: SubTextInputWidget(subkey: "image", width: 180, empty: false, change: (value) {},
initialValue: widget.item.container?.image, readOnly: true,)),
...children,
getInputAndOutputVariableForms(readOnly),
],)
));
}
// EXPOSE
categories.add(Container(
padding: const EdgeInsets.symmetric(vertical: 10),
width: 180,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Padding(padding: EdgeInsets.only(bottom: 5), child: Text("<EXPOSE>",
style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold), textAlign: TextAlign.center)),
readOnly ? Container() : Row( children: [
InkWell( onTap: () {
widget.item.expose.add(Expose());
var el = dash.getElement(widget.elementID);
el!.element = widget.item as dynamic;
setState(() {});
}, child:
Container( margin: const EdgeInsets.only(top: 5),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.grey, width: 1)),
width: 125, height: 30,
child: const Row( mainAxisAlignment: MainAxisAlignment.center,
children: [ Padding( padding: EdgeInsets.only(right: 5), child: Icon(Icons.add)), Text("add expose")]),
)
),
InkWell( onTap: () {
if (widget.item.expose.isEmpty) { return; }
widget.item.expose = widget.item.expose.sublist(0, widget.item.expose.length - 1);
var el = dash.getElement(widget.elementID);
el!.element = widget.item as dynamic;
setState(() {});
}, child:
Container( margin: const EdgeInsets.only(left: 5, top: 5),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.grey, width: 1)),
width: 45, height: 30,
child: const Row( mainAxisAlignment: MainAxisAlignment.center,
children: [ Icon(Icons.delete, color: Colors.black) ]),
)
)
]),
],)
));
for (var expose in widget.item.expose) {
categories.add(SubExposeFormsWidget( readOnly: readOnly, width: 180, dash: dash, empty: categories.isEmpty,
item: expose, elementID: widget.elementID));
}
return SizedBox( height: getHeight(context) - 330, child: SingleChildScrollView( child: Column(
children: categories )) );
}
}

View File

@ -0,0 +1,91 @@
import 'package:oc_front/main.dart';
import 'package:flutter/material.dart';
import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/core/services/perms_service.dart';
import 'package:oc_front/widgets/forms/sub_keys_forms.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/widgets/inputs/sub_text_input.dart';
import 'package:oc_front/widgets/forms/container_forms.dart';
import 'package:oc_front/widgets/inputs/sub_dropdown_input%20.dart';
// ignore: must_be_immutable
class ResourceFormsWidget extends StatefulWidget {
int instanceID = 0;
AbstractItem item;
Dashboard dash;
String elementID;
ResourceFormsWidget ({ super.key, required this.item, required this.dash, required this.elementID });
@override ResourceFormsWidgetState createState() => ResourceFormsWidgetState();
}
class ResourceFormsWidgetState extends State<ResourceFormsWidget> {
List<Widget> getWidgets(Map<String, dynamic> infos) {
List<Widget> widgets = [];
for (var key in infos.keys) {
if (infos[key] == null && infos[key] != 0) { continue; }
if (infos[key] is Map) { widgets.addAll(getWidgets(infos[key]));
} if (infos[key] is List) {
if (infos[key].isEmpty) { continue; }
widgets.add(SubTextInputWidget(subkey: key.replaceAll("_", " "), width: 180, empty: false, readOnly: true,
change: (value) {}, initialValue: (infos[key] as List<dynamic>).join(",") ));
} else if (infos[key] is Map) {
widgets.addAll(getWidgets(infos[key] as Map<String, dynamic>));
} else {
widgets.add(SubTextInputWidget(subkey: key.replaceAll("_", " "), width: 180, empty: false, readOnly: true,
change: (value) {}, initialValue: infos[key] is List ? infos[key].join(",") : "${infos[key]}" ));
}
}
if (widgets.isNotEmpty) {
widgets.add(SizedBox( width: 200, height: 15) );
}
return widgets;
}
Widget getInputAndOutputVariableForms(bool readOnly) {
List<Widget> res = [];
res.add(SubKeysMapFormsWidget(dash: widget.dash, type: SubMapFormsType.INPUT, item: widget.item, elementID: widget.elementID,
readOnly: readOnly));
res.add(SubKeysMapFormsWidget(dash: widget.dash, type: SubMapFormsType.OUTPUT, item: widget.item, elementID: widget.elementID,
readOnly: readOnly));
res.add(SubKeysMapFormsWidget(dash: widget.dash, type: SubMapFormsType.ENV, item: widget.item, elementID: widget.elementID,
readOnly: readOnly));
return Column( children: res );
}
@override Widget build(BuildContext context) {
List<Widget> instancesCat = [];
List<Widget> childrenReadOnly = getWidgets(widget.item.infos());
List<DropdownMenuItem<String>> dpItems = [];
for (var (i, instance) in widget.item.instances.indexed) {
dpItems.add(DropdownMenuItem(value: '$i', child: Text('${instance.name}', overflow: TextOverflow.ellipsis,)));
}
if (dpItems.isNotEmpty) {
childrenReadOnly.add(Padding( padding: EdgeInsets.only(top: 20),
child : Container(
width: 200, decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.grey))),
)));
childrenReadOnly.add(Padding(padding: EdgeInsets.only(bottom: 15), child:
SubDropdownInputWidget( dropdownMenuEntries: dpItems, subkey: "", width: 180, empty: false,
initialValue: "${widget.instanceID}", change: (value) {
if (value != null) { setState(() { widget.instanceID = int.parse(value); }); }
},
))
);
if (widget.item.instances.length > widget.instanceID) {
childrenReadOnly.addAll(getWidgets(widget.item.instances[widget.instanceID].infos()));
}
}
instancesCat.add(ContainerFormsWidget(dash: widget.dash, item: widget.item, elementID: widget.elementID));
if (instancesCat.isNotEmpty) {
instancesCat.add(Container(
width: 200, decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.grey))),
));
}
bool readOnly = !PermsService.getPerm(Perms.WORKFLOW_EDIT);
// missing env input and output variables
return SizedBox( height: getHeight(context) - 330, child: SingleChildScrollView( child: Column(
children: [ getInputAndOutputVariableForms(readOnly), ...childrenReadOnly, ...instancesCat, ])));
}
}

View File

@ -1,72 +1,117 @@
import 'package:alert_banner/exports.dart';
import 'package:cron/cron.dart'; import 'package:cron/cron.dart';
import 'package:intl/intl.dart' as intl; import 'package:oc_front/core/services/specialized_services/workflow_execution_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.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/perms_service.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/main.dart'; import 'package:oc_front/main.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart' as intl;
import 'package:alert_banner/exports.dart';
import 'package:oc_front/models/resources/compute.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/pages/shared.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/dialog/confirm_box.dart'; import 'package:oc_front/widgets/dialog/confirm_box.dart';
import 'package:oc_front/core/services/perms_service.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/widgets/inputs/shallow_text_input.dart'; import 'package:oc_front/widgets/inputs/shallow_text_input.dart';
import 'package:oc_front/core/models/shared_workspace_local.dart';
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
import 'package:oc_front/core/services/specialized_services/check_service.dart';
import 'package:oc_front/core/services/specialized_services/workflow_service.dart';
// ignore: must_be_immutable
class SchedulerFormsWidget extends StatefulWidget { class SchedulerFormsWidget extends StatefulWidget {
Scheduler schedule = Scheduler();
Dashboard item; Dashboard item;
String purpose = "";
bool? booking; bool? booking;
bool valid = false;
bool shouldSearch = true;
int scheduleFutureCount = 0;
int scheduleBeforeCount = 0;
String? error; String? error;
String? errorEndDate; String? errorEndDate;
String? errorCron; String? errorCron;
Function validate = () {}; Function validate = () {};
final WorkflowExecutionService _service = WorkflowExecutionService();
SchedulerFormsWidget ({ super.key, required this.item, }); SchedulerFormsWidget ({ super.key, required this.item, });
@override SchedulerFormsWidgetState createState() => SchedulerFormsWidgetState(); @override SchedulerFormsWidgetState createState() => SchedulerFormsWidgetState();
} }
class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> { class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
CheckService check = CheckService(); CheckService check = CheckService();
void save(List<GlobalKey<FormFieldState>> formKeys) { void save(List<GlobalKey<FormFieldState>> formKeys) {
dash.scheduleActive = !dash.scheduleActive;
widget.error = null; widget.error = null;
widget.errorEndDate = null; widget.errorEndDate = null;
widget.errorCron = null; widget.errorCron = null;
if (dash.elements.isEmpty || dash.elements.where((element) => element.element is ProcessingItem).isEmpty) {
dash.error = "You need at least one processing element";
}
var processings = dash.elements.where((element) => element.element is ProcessingItem);
var computes = dash.elements.where((element) => element.element is ComputeItem);
for (var p in processings) {
var links = dash.arrows.where((element) => element.fromID.contains(p.id) || element.toID.contains(p.id));
try {
List<ComputeItem> c = [];
for (var link in links) {
c.addAll(computes.where( (e) => link.toID.contains(e.id) || link.fromID.contains(e.id)).map( (e) => e.element as ComputeItem));
}
if (c.isEmpty) { throw Exception("no compute element linked"); }
} catch (e) {
dash.error = "You need to link each processing element to a compute element";
}
}
if (dash.error != null) {
showAlertBanner( context, () {}, AlertAlertBannerChild(text: dash.error.toString()),// <-- Put any widget here you want!
alertBannerLocation: AlertBannerLocation.bottom,);
setState(() {});
return;
}
for (var k in formKeys) { for (var k in formKeys) {
if (k.currentState != null) { if (k.currentState != null) {
if (!k.currentState!.validate() && dash.scheduleActive) { if (!k.currentState!.validate()) {
dash.scheduleActive = !dash.scheduleActive;
return; return;
} else { k.currentState!.save();} } else { k.currentState!.save();}
} }
} }
DateTime now = DateTime.now().add(const Duration(minutes: 1)); DateTime now = DateTime.now().add(const Duration(minutes: 1));
if (dash.scheduler["start"] == null || DateTime.parse(dash.scheduler["start"]!).isBefore(now)) { if (widget.schedule.start == null || widget.schedule.start!.isBefore(now)) {
dash.scheduler["start"] = now.toUtc().toIso8601String(); widget.schedule.start = now.toUtc();
if (dash.scheduler["end"] != null) { if (widget.schedule.end != null) {
dash.scheduler["end"] = now.add(const Duration(minutes: 1)).toUtc().toIso8601String(); widget.schedule.end = now.add(const Duration(minutes: 1)).toUtc();
} }
} }
widget.item.saveDash(widget.item.id); Duration durationBefore = widget.schedule.start!.difference(DateTime.now().toUtc()) + Duration(seconds: 5);
widget._service.schedule(context, widget.item.id ?? "", widget.schedule.serialize(), {}).then((value) {
setState(() { widget.valid = true; });
Future.delayed(durationBefore, () {
try {
setState(() {});
} catch (e) { /* */ }
});
Future.delayed(const Duration(seconds: 10), () {
try {
setState(() { widget.valid = false; });
} catch (e) { /* */ }
});
});
} }
void checkBooking(List<GlobalKey<FormFieldState>> formKeys, void Function(List<GlobalKey<FormFieldState>> )? f){ void checkBooking(List<GlobalKey<FormFieldState>> formKeys, void Function(List<GlobalKey<FormFieldState>> )? f){
if (widget.item.scheduler["start"] == null) { if (widget.schedule.start == null) {
DateTime now = DateTime.now().add(const Duration(minutes: 5)); DateTime now = DateTime.now().add(const Duration(minutes: 5));
widget.item.scheduler["start"] = now.toUtc().toIso8601String(); widget.schedule.start = now.toUtc();
} }
var s = DateTime.parse(widget.item.scheduler["start"]).toUtc().toIso8601String(); var s = widget.schedule.start!.toUtc().toIso8601String();
var e = ""; var e = "";
if (widget.item.scheduler["end"] == null) { if (widget.schedule.end == null) {
e = DateTime.parse(widget.item.scheduler["start"]).add(const Duration(seconds: 5)).toUtc().toIso8601String(); e = widget.schedule.start!.add(const Duration(seconds: 5)).toUtc().toIso8601String();
} else { } else {
e = DateTime.parse(widget.item.scheduler["end"]).toUtc().toIso8601String(); e = widget.schedule.end!.toUtc().toIso8601String();
} }
check.search(context, [widget.item.id ?? "", s.substring(0, 19), e.substring(0, 19)], {}).then( check.search(context, [widget.item.id ?? "", s.substring(0, 19), e.substring(0, 19)], {}).then(
(v) { (v) {
if (v.data == null) { return; } if (v.data == null) { return; }
widget.booking = v.data!.is_available; widget.booking = v.data!.isAvailable;
if (v.data!.is_available) { if (v.data!.isAvailable) {
if (f != null) { f(formKeys); if (f != null) { f(formKeys);
} else { } else {
showAlertBanner( context, () {}, showAlertBanner( context, () {},
@ -84,6 +129,31 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
} }
@override Widget build(BuildContext context) { @override Widget build(BuildContext context) {
if (widget.shouldSearch && widget.item.name != "") {
widget.shouldSearch = false;
widget._service.search(null, [widget.item.name], {}).then((value) {
if (value.data != null) {
try {
setState(() {
widget.scheduleFutureCount = 0;
widget.scheduleBeforeCount = 0;
for (var exec in value.data!.executions) {
if (exec.startDate != null && DateTime.parse(exec.startDate!).isAfter(DateTime.now().toUtc())) {
widget.scheduleFutureCount++;
} else {
widget.scheduleBeforeCount++;
}
}
});
} catch (e) { /* */ }
}
});
} else {
Future.delayed(const Duration(milliseconds: 100), () {
widget.shouldSearch = true;
});
}
bool isService = true; bool isService = true;
try { try {
widget.item.elements.firstWhere((element) => (element.element?.serialize()["is_service"] ?? false) == true); widget.item.elements.firstWhere((element) => (element.element?.serialize()["is_service"] ?? false) == true);
@ -95,22 +165,22 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
DateTime? end; DateTime? end;
Duration delayed = const Duration(minutes: 5); Duration delayed = const Duration(minutes: 5);
if (widget.item.scheduler["start"] != null) { if (widget.schedule.start != null) {
start = DateTime.parse(widget.item.scheduler["start"]!); start = widget.schedule.start!;
if (start.isBefore(DateTime.now()) && !dash.scheduleActive) { if (start.isBefore(DateTime.now())) {
start = DateTime.now().add(const Duration(minutes: 5)); start = DateTime.now().add(const Duration(minutes: 5));
widget.item.scheduler["start"] = start.toUtc().toIso8601String(); widget.schedule.start = start.toUtc();
} }
if (start.isBefore(DateTime.now())) { if (start.isBefore(DateTime.now())) {
// get difference between now and start // get difference between now and start
delayed = start.difference(DateTime.now()); delayed = start.difference(DateTime.now());
} }
} }
if (widget.item.scheduler["end"] != null) { if (widget.schedule.end != null) {
end = DateTime.parse(widget.item.scheduler["end"]!); end = widget.schedule.end!;
if (end.isBefore(DateTime.now()) && !dash.scheduleActive) { if (end.isBefore(DateTime.now())) {
end = DateTime.now().add(const Duration(minutes: 5)); end = DateTime.now().add(const Duration(minutes: 5));
widget.item.scheduler["end"] = end.toUtc().toIso8601String(); widget.schedule.end = end.toUtc();
} }
if (end.isBefore(DateTime.now())) { if (end.isBefore(DateTime.now())) {
// get difference between now and start // get difference between now and start
@ -164,51 +234,11 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
child: shallow ), child: shallow ),
const SizedBox(height: 20, width: 200 ), const SizedBox(height: 20, width: 200 ),
isService ? Text("Warning a processing is a service, if no end execution it will run forever.") : Container(), isService ? Text("Warning a processing is a service, if no end execution it will run forever.") : Container(),
Tooltip( message: "executions name",
child: Container( height: 40, margin: const EdgeInsets.only(top: 5),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
child: TextFormField( key: formKeys[0], readOnly: readOnly,
initialValue: "${widget.item.scheduler["name"] ?? "${widget.item.name}_executions"}",
enabled: !dash.scheduleActive && !readOnly,
onChanged: (value) {
Future.delayed(const Duration(seconds: 100), () {
if (widget.item.scheduler["name"] == value) {
widget.item.saveDash(widget.item.id);
}
});
widget.item.scheduler["name"] = value;
},
onSaved: (value) {
widget.item.scheduler["name"] = value ?? "${widget.item.scheduler["name"] ?? "${widget.item.name}_executions"}";
},
validator: (value) {
if (value == null || value.isEmpty) {
setState(() { widget.error = 'missing name'; });
}
return value == null || value.isEmpty ? "not empty" : null;
},
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.always,
fillColor: Colors.white,
filled: true,
hintText: "enter executions name...",
labelText: "executions name*",
hintStyle: TextStyle(fontSize: 10),
labelStyle: TextStyle(fontSize: 10),
focusedErrorBorder: OutlineInputBorder(borderSide: BorderSide(color: widget.item.error != null || widget.error != null ? Colors.red : Colors.black)),
errorBorder: OutlineInputBorder(borderSide: BorderSide(color: widget.item.error != null || widget.error != null ? Colors.red : Colors.black)),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: widget.item.error != null || widget.error != null ? Colors.red : Colors.black)),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: widget.item.error != null || widget.error != null ? Colors.red : Colors.grey)),
border: OutlineInputBorder(borderSide: BorderSide(color: widget.item.error != null || widget.error != null ? Colors.red : Colors.grey)),
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
))),
Tooltip( message: "start executions", Tooltip( message: "start executions",
child: Container( height: 40, margin: const EdgeInsets.only(top: 5), child: Container( height: 40, margin: const EdgeInsets.only(top: 5),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
child: DateTimeField( key: formKeys[1], child: DateTimeField( key: formKeys[1],
enabled: !dash.scheduleActive && !readOnly, enabled: !readOnly,
resetIcon: null, resetIcon: null,
onShowPicker: (context, currentValue) async { onShowPicker: (context, currentValue) async {
var date = await showDatePicker( var date = await showDatePicker(
@ -228,7 +258,7 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
return w; return w;
}, },
context: context, context: context,
firstDate: dash.scheduleActive ? DateTime(1900) : DateTime.now().add(const Duration(minutes: 5)), firstDate: DateTime.now().add(const Duration(minutes: 5)),
initialDate: DateTime.parse( start?.toLocal().toIso8601String() initialDate: DateTime.parse( start?.toLocal().toIso8601String()
?? currentValue?.toIso8601String() ?? currentValue?.toIso8601String()
?? DateTime.now().add(const Duration(minutes: 5)).toUtc().toIso8601String()).toLocal(), ?? DateTime.now().add(const Duration(minutes: 5)).toUtc().toIso8601String()).toLocal(),
@ -265,9 +295,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 = DateTime(date.year, date.month, date.day, time.hour, time.minute);
widget.item.scheduler["start"] = date.toUtc().toIso8601String(); widget.schedule.start = date.toUtc();
} }
widget.item.saveDash(widget.item.id); widget.item.saveDash(widget.item.id, context);
} }
return date; return date;
}, },
@ -296,18 +326,18 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
child: Container( height: 40, margin: const EdgeInsets.only(top: 5), child: Container( height: 40, margin: const EdgeInsets.only(top: 5),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
child: DateTimeField( key: formKeys[2], child: DateTimeField( key: formKeys[2],
enabled: !dash.scheduleActive && !readOnly, enabled: !readOnly,
validator: (value) { validator: (value) {
if (value == null && widget.item.scheduler["cron"] != null && widget.item.scheduler["cron"].isNotEmpty) { if (value == null && widget.schedule.cron != null && widget.schedule.cron!.isNotEmpty) {
setState(() { setState(() {
widget.errorEndDate = 'missing start date'; widget.errorEndDate = 'missing start date';
}); });
} }
return value == null && widget.item.scheduler["cron"] != null && widget.item.scheduler["cron"].isNotEmpty ? "not empty" : null; return value == null && widget.schedule.cron != null && widget.schedule.cron!.isNotEmpty ? "not empty" : null;
}, },
onChanged: (value) { onChanged: (value) {
if (value == null) { if (value == null) {
widget.item.scheduler.remove("end"); widget.schedule.end = null;
} }
}, },
resetIcon: const Icon(Icons.close, size: 15), resetIcon: const Icon(Icons.close, size: 15),
@ -329,7 +359,7 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
return w; return w;
}, },
context: context, context: context,
firstDate: dash.scheduleActive ? DateTime(1900) : DateTime.now().add(const Duration(minutes: 5)), firstDate: DateTime.now().add(const Duration(minutes: 5)),
initialDate: DateTime.parse( end?.toLocal().toIso8601String() initialDate: DateTime.parse( end?.toLocal().toIso8601String()
?? currentValue?.toIso8601String() ?? currentValue?.toIso8601String()
?? DateTime.now().add(const Duration(minutes: 5)).toUtc().toIso8601String()).toLocal(), ?? DateTime.now().add(const Duration(minutes: 5)).toUtc().toIso8601String()).toLocal(),
@ -342,7 +372,7 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
var count = 0; var count = 0;
while(date!.microsecondsSinceEpoch <= (DateTime.now().microsecondsSinceEpoch) while(date!.microsecondsSinceEpoch <= (DateTime.now().microsecondsSinceEpoch)
|| time == null || time == null
|| (date.microsecondsSinceEpoch) <= (DateTime.parse(widget.item.scheduler["start"] ?? DateTime.now().toIso8601String()).microsecondsSinceEpoch)) { || (date.microsecondsSinceEpoch) <= (widget.schedule.start ?? DateTime.now()).microsecondsSinceEpoch) {
if (count > 0) { if (count > 0) {
showAlertBanner( context, () {}, // ignore: use_build_context_synchronously showAlertBanner( context, () {}, // ignore: use_build_context_synchronously
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! 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!
@ -370,9 +400,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 = DateTime(date.year, date.month, date.day, time.hour, time.minute);
widget.item.scheduler["end"] = date.toUtc().toIso8601String(); widget.schedule.end = date.toUtc();
} }
widget.item.saveDash(widget.item.id); widget.item.saveDash(widget.item.id, context);
} }
return date; return date;
}, },
@ -400,19 +430,19 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
child: Container( height: 40, margin: const EdgeInsets.only(top: 5), child: Container( height: 40, margin: const EdgeInsets.only(top: 5),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
child: TextFormField( key: formKeys[3], child: TextFormField( key: formKeys[3],
enabled: !dash.scheduleActive && !readOnly, enabled: !readOnly,
initialValue: widget.item.scheduler["cron"], initialValue: widget.schedule.cron,
onChanged: (value) { onChanged: (value) {
Future.delayed(const Duration(seconds: 100), () { Future.delayed(const Duration(seconds: 100), () {
if (widget.item.scheduler["cron"] == value) { if (widget.schedule.cron == value) {
widget.item.saveDash(widget.item.id); widget.item.saveDash(widget.item.id, context);
} }
}); });
widget.item.scheduler["cron"] = value; widget.schedule.cron = value;
}, },
onSaved: (value) { onSaved: (value) {
if (value != null) { if (value != null) {
widget.item.scheduler["cron"] = value; widget.schedule.cron = value;
} }
}, },
validator: (value) { validator: (value) {
@ -456,49 +486,58 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
width: 200, width: 200,
height: 10, height: 10,
), ),
Tooltip( message: "check booking", Tooltip( message: "check booking", child: InkWell( mouseCursor: SystemMouseCursors.click,
child: InkWell( mouseCursor: SystemMouseCursors.click, onTap: () { PermsService.getPerm(Perms.WORKFLOW_BOOKING) ? checkBooking(formKeys, null) : null;
onTap: () { }, child: Container( margin: const EdgeInsets.only(bottom: 5, left: 10, right: 10),
PermsService.getPerm(Perms.WORKFLOW_BOOKING) ? checkBooking(formKeys, null) : null; decoration: BoxDecoration(borderRadius: BorderRadius.circular(5),
}, child: Container( margin: const EdgeInsets.only(bottom: 5, left: 10, right: 10), border: Border.all(color: widget.booking == null ? (
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5),
border: Border.all(color: widget.booking == null && !dash.scheduleActive ? (
PermsService.getPerm(Perms.WORKFLOW_BOOKING) && PermsService.getPerm(Perms.WORKFLOW_EDIT) ? Colors.black : Colors.grey) : ( PermsService.getPerm(Perms.WORKFLOW_BOOKING) && PermsService.getPerm(Perms.WORKFLOW_EDIT) ? Colors.black : Colors.grey) : (
widget.booking == true || dash.scheduleActive ? Colors.green : redColor), width: 1)), widget.booking == true ? Colors.green : redColor), width: 1)),
width: 200, height: 30, width: 200, height: 30,
child: Icon( child: Icon( Icons.verified_outlined,
Icons.verified_outlined, color: widget.booking == null ? Colors.black : (widget.booking == true ? Colors.green : redColor)),
color: widget.booking == null && !dash.scheduleActive ? Colors.black : (widget.booking == true || dash.scheduleActive ? Colors.green : redColor)),
)) ))
), ),
Tooltip( message: dash.scheduleActive ? "unbook" : "book", Tooltip( message: "book", child: InkWell( mouseCursor: SystemMouseCursors.click,
child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () { onTap: () {
PermsService.getPerm(Perms.WORKFLOW_BOOKING) && PermsService.getPerm(Perms.WORKFLOW_EDIT) ? (dash.scheduleActive ? setState(() { save(formKeys); }) : checkBooking(formKeys, save)) : null; PermsService.getPerm(Perms.WORKFLOW_BOOKING) && PermsService.getPerm(Perms.WORKFLOW_EDIT) ? setState(() { save(formKeys); }) : null;
}, child: Container( margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), }, child: Container( margin: const EdgeInsets.only(top: 5, bottom: 10, left: 10, right: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), decoration: BoxDecoration(borderRadius: BorderRadius.circular(5),
border: Border.all(color: dash.scheduleActive ? Colors.green : ( PermsService.getPerm(Perms.WORKFLOW_BOOKING) ?Colors.black : Colors.grey ))), border: Border.all(color: dash.error != null ? Colors.red : ( PermsService.getPerm(Perms.WORKFLOW_BOOKING) ?(widget.valid ? Colors.green : Colors.black) : Colors.grey ))),
width: 200, height: 30, width: 200, height: 30,
child: Icon( child: Icon(Icons.schedule_send, color: dash.error != null ? Colors.red : (widget.valid ? Colors.green : Colors.black)),
dash.scheduleActive ? Icons.cancel_schedule_send : Icons.schedule_send, color: dash.scheduleActive ? Colors.green : Colors.black),
)) ))
), ),
Column( children: [
Container(
height: 15, width: 200,
decoration: const BoxDecoration(border: Border(top: BorderSide(color: Colors.grey))),
),
Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 10),
child:Text( textAlign: TextAlign.center, overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 13, color: Colors.grey ),
"Was booked ${widget.scheduleBeforeCount} times.")),
Container( alignment: Alignment.center, padding: const EdgeInsets.only(left: 10, right: 10, bottom: 15),
child:Text( textAlign: TextAlign.center, overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 13, color: Colors.grey ),
"Is booked ${widget.scheduleFutureCount} times.")),
]),
widget.item.info["shared"] != null && (widget.item.info["shared"] as List<dynamic>).isNotEmpty ? Column( children: [ widget.item.info["shared"] != null && (widget.item.info["shared"] as List<dynamic>).isNotEmpty ? Column( children: [
Container( Container(
height: 30, height: 30,
width: 200, width: 200,
decoration: const BoxDecoration(border: Border(top: BorderSide(color: Colors.grey))), decoration: const BoxDecoration(border: Border(top: BorderSide(color: Colors.grey))),
), ),
Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 10), Container( alignment: Alignment.center,
child:Text( textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, child:Text( overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 14, color: Colors.black, fontWeight: FontWeight.bold), 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)")), "Is shared ${(widget.item.info["shared"] as List<dynamic>).length} time(s).")),
...(widget.item.info["shared"] as List<dynamic>).where( (e) => CollaborativeAreaLocal.getCollaborativeArea(e) != null ...(widget.item.info["shared"] as List<dynamic>).where( (e) => CollaborativeAreaLocal.getCollaborativeArea(e) != null
).map((e) { ).map((e) {
var sw = CollaborativeAreaLocal.getCollaborativeArea(e); var sw = CollaborativeAreaLocal.getCollaborativeArea(e);
return Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), return Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: Row( children: [ child: Row( children: [
const Padding(padding: EdgeInsets.only(right: 10), child: Icon(Icons.work, color: Colors.grey, size: 15)), const Padding(padding: EdgeInsets.only( left: 10, right: 10), child: Icon(Icons.work, color: Colors.grey, size: 15)),
Text(style: const TextStyle(fontSize: 12, color: Colors.grey), Text(style: const TextStyle(fontSize: 12, color: Colors.grey),
"Workspace: ${sw != null && sw.name != null ? "Workspace: ${sw != null && sw.name != null ?
"${sw.name!.substring(0, sw.name!.length > 15 ? 12 : sw.name!.length)}${sw.name!.length > 15 ? "..." : ""}" : ""}") ])); "${sw.name!.substring(0, sw.name!.length > 15 ? 12 : sw.name!.length)}${sw.name!.length > 15 ? "..." : ""}" : ""}") ]));

View File

@ -1,18 +0,0 @@
import 'package:flutter/material.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/widgets/forms/web_reference_forms.dart';
class StorageFormsWidget extends StatefulWidget {
StorageItem item;
String purpose = "";
Function validate = () {};
StorageFormsWidget ({ super.key, required this.item });
@override StorageFormsWidgetState createState() => StorageFormsWidgetState();
}
class StorageFormsWidgetState extends State<StorageFormsWidget> {
@override Widget build(BuildContext context) {
return Column( children: [
WebReferenceFormsWidget(item: widget.item),
]);
}
}

View File

@ -0,0 +1,154 @@
import 'package:flutter/material.dart';
import 'package:flutter_advanced_switch/flutter_advanced_switch.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/widgets/inputs/sub_text_input.dart';
// ignore: must_be_immutable
class StorageProcessingLinkFormsWidget extends StatefulWidget {
Dashboard dash;
ArrowPainter item;
bool readOnly = false;
StorageProcessingLinkFormsWidget ({ super.key, required this.dash, required this.item, this.readOnly = false });
@override StorageProcessingLinkFormsWidgetState createState() => StorageProcessingLinkFormsWidgetState();
}
class StorageProcessingLinkFormsWidgetState extends State<StorageProcessingLinkFormsWidget> {
List<Param> getParams(String fromID, String toID) {
List<Param> arr = [];
var els = widget.dash.elements.where( (e) => fromID.contains(e.id) || toID.contains(e.id));
for (var element in els) {
var g = GraphItem();
g.fromDashboard(element.serialize());
var e = g.getElement();
if (e == null) { continue; }
if (e.getSelectedInstance() != null) {
for (var env in e.getSelectedInstance()!.env) {
if (env.name?.contains("LINK") ?? true) { continue; }
arr.add(env);
}
}
}
return arr.where( (e) => e.name != null && e.value != null).toList();
}
@override Widget build(BuildContext context) {
List<Widget> children = [
Padding( padding: const EdgeInsets.only(top: 10),
child: Text("<ENV VARIABLES>",
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold), textAlign: TextAlign.center)),
];
var params = getParams(widget.item.fromID, widget.item.toID);
widget.item.env = params.map( (e) => e.serialize()).toList();
for (var param in params) {
children.add(SubTextInputWidget(subkey: param.name ?? "", width: 180, empty: false, readOnly: true,
change: (value) {}, initialValue: param.value ?? "", copyLabel: true));
}
children.add(Container(width: 200, padding: const EdgeInsets.only(top: 10, bottom: 10),
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1)))));
List<Widget> inf = [];
int count = 0;
for(var info in widget.item.infos) {
count++;
inf.add(Padding( padding: EdgeInsets.only(top: 10, bottom: 5),
child : Row( crossAxisAlignment: CrossAxisAlignment.center, children: [
Padding(padding: EdgeInsets.only(left: 10, right: 10), child: Text("$count")),
Container(width: 140, decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))))
])));
for (var key in (info as Map<String, dynamic>).keys) {
if (info[key] is bool) {
inf.add(AdvancedSwitch(
width: 150.0,
height: 25.0,
initialValue: info[key] as bool? ?? false,
enabled: !widget.readOnly,
activeChild: Text(key, style: const TextStyle(color: Colors.white)),
inactiveChild: Text(key == "write" ? "read" : "no $key", style: const TextStyle(color: Colors.white)),
activeColor: Colors.green,
inactiveColor: redColor,
onChanged: (v) {
try {
info[key] = v;
Future.delayed(const Duration(seconds: 1), () {
for (var a in widget.dash.arrows) {
if (a.fromID == widget.item.fromID && a.toID == widget.item.toID) {
a.infos = widget.item.infos;
}
}
widget.dash.saveDash(widget.dash.id, context);
});
} catch (e) { /* */ }
},
));
} else if (info[key] is String?) {
inf.add(SubTextInputWidget(subkey: key, width: 180, empty: false, change: (v) {
try {
info[key] = v;
Future.delayed(const Duration(seconds: 1), () {
if (info[key] != v) { return; }
for (var a in widget.dash.arrows) {
if (a.fromID == widget.item.fromID && a.toID == widget.item.toID) {
a.infos = widget.item.infos;
}
}
widget.dash.saveDash(widget.dash.id, context);
});
} catch (e) {
print(e);
}
}, initialValue: "${info[key] ?? ""}", readOnly: false, noLabel: false));
}
}
}
return SizedBox( height: getHeight(context) - 230, child: SingleChildScrollView( child: Column(children: [
...children,
Row( mainAxisAlignment: MainAxisAlignment.center, children: [
InkWell( onTap: () {
widget.item.infos.add({
"write": false,
"source": null,
"destination": null,
"filename": null,
});
setState(() {
widget.dash.saveDash(widget.dash.id, context);
});
}, child: Container( margin: const EdgeInsets.only(top: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5),
border: Border.all(color: Colors.grey, width: 1)),
width: 125, height: 30,
child: Row( mainAxisAlignment: MainAxisAlignment.center,
children: [ Padding( padding: EdgeInsets.only(right: 5), child: Icon(Icons.add, color: Colors.black,)),
Text("link infos",
style: TextStyle( color: Colors.black))]),
),
),
InkWell( onTap: () {
if (widget.item.infos.isEmpty) { return; }
setState(() {
widget.item.infos.removeLast();
widget.dash.saveDash(widget.dash.id, context);
});
}, child:
Container( margin: const EdgeInsets.only(left: 5, top: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5),
border: Border.all(color: widget.item.infos.isEmpty ? Colors.grey : Colors.black , width: 1)),
width: 50, height: 30,
child: Row( mainAxisAlignment: MainAxisAlignment.center,
children: [ Icon( Icons.delete, size: 18,
color: widget.item.infos.isEmpty ? Colors.grey : Colors.black ) ]),
)
),
]),
...inf
] ) ));
}
}

View File

@ -0,0 +1,174 @@
import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/widgets/forms/sub_keys_forms.dart';
import 'package:oc_front/widgets/inputs/sub_text_input.dart';
// ignore: must_be_immutable
class SubAddFormsWidget extends StatefulWidget {
bool readOnly;
SubMapFormsType type;
String elementID = "";
AbstractItem item;
Dashboard dash;
bool empty = false;
List<Param> forms = [];
SubAddFormsWidget ({ super.key, required this.dash, this.readOnly = false,
this.empty = false, required this.item, required this.elementID,
required this.type });
@override SubAddFormsWidgetState createState() => SubAddFormsWidgetState();
}
class SubAddFormsWidgetState extends State<SubAddFormsWidget> {
Param? addedparam;
@override Widget build(BuildContext context) {
AbstractInstance instance = widget.item.getSelectedInstance()!;
var f = (widget.type == SubMapFormsType.INPUT ? instance.inputs : (
widget.type == SubMapFormsType.OUTPUT ? instance.outputs : instance.env)).where( (e) => !e.readOnly).toList();
widget.forms = f;
if (addedparam != null) {
widget.forms.add(addedparam!);
}
List<Widget> children = [];
for (var param in widget.forms) {
children.add(Row( mainAxisAlignment: MainAxisAlignment.center, children: [
SubTextInputWidget(subkey: "key", readOnly: widget.readOnly,
initialValue: param.name, width: 81, empty: widget.empty,
change: (value) {
setState(() {
param.name = value;
param.attr = value;
if (addedparam?.value != null && addedparam?.name != null) {
addedparam = null;
}
var sel = widget.item.getSelectedInstance();
if (sel != null) {
if (widget.type == SubMapFormsType.INPUT) { sel.inputs = [
...sel.inputs.where( (e) => e.readOnly),
...widget.forms.where((e) => e.name != null && e.value != null)
];
} else if (widget.type == SubMapFormsType.OUTPUT) {
sel.outputs = [
...sel.outputs.where( (e) => e.readOnly),
...widget.forms.where((e) => e.name != null && e.value != null)
];
} else {
sel.env = [
...sel.env.where( (e) => e.readOnly),
...widget.forms.where((e) => e.name != null && e.value != null)
];
}
}
Future.delayed(const Duration(milliseconds: 100), () {
if (param.name == value) {
widget.dash.saveDash(widget.dash.id, context);
}
});
var el = widget.dash.getElement(widget.elementID);
el!.element = widget.item as dynamic;
setState(() {});
});
}),
const Padding(padding: EdgeInsets.only(left: 5, right: 5, top: 15), child: Text("=", textAlign: TextAlign.center,)),
SubTextInputWidget(subkey: "value", initialValue: param.value, width: 81, empty: widget.empty,
readOnly: widget.readOnly, change: (value) {
param.value = value;
if (addedparam?.value != null && addedparam?.name != null) {
addedparam = null;
}
var sel = widget.item.getSelectedInstance();
if (sel != null) {
if (widget.type == SubMapFormsType.INPUT) { sel.inputs = [
...sel.inputs.where( (e) => e.readOnly),
...widget.forms.where((e) => e.name != null && e.value != null)
];
} else if (widget.type == SubMapFormsType.OUTPUT) {
sel.outputs = [
...sel.outputs.where( (e) => e.readOnly),
...widget.forms.where((e) => e.name != null && e.value != null)
];
} else {
sel.env = [
...sel.env.where( (e) => e.readOnly ),
...widget.forms.where((e) => e.name != null && e.value != null)
];
}
}
var el = widget.dash.getElement(widget.elementID);
el!.element = widget.item as dynamic;
setState(() {});
Future.delayed(const Duration(milliseconds: 100), () {
if (param.value == value) {
widget.dash.saveDash(widget.dash.id, context);
}
});
}),
]));
}
return Column( crossAxisAlignment: CrossAxisAlignment.center, children : [
widget.readOnly ? Container() : Row( mainAxisAlignment: MainAxisAlignment.center, children: [
InkWell( onTap: () {
if (widget.forms.isNotEmpty && (widget.forms[widget.forms.length - 1].name == null
|| widget.forms[widget.forms.length - 1].value == null)) { return;}
widget.forms.add(Param(readOnly: false));
addedparam = widget.forms.last;
var el = widget.dash.getElement(widget.elementID);
el!.element = widget.item as dynamic;
setState(() {});
}, child: Container( margin: const EdgeInsets.only(top: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5),
border: Border.all(color: widget.forms.isNotEmpty && (widget.forms[widget.forms.length - 1].name == null
|| widget.forms[widget.forms.length - 1].value == null) ? Colors.grey : Colors.grey, width: 1)),
width: 125, height: 30,
child: Row( mainAxisAlignment: MainAxisAlignment.center,
children: [ Padding( padding: EdgeInsets.only(right: 5), child: Icon(Icons.add, color: widget.forms.isNotEmpty && (widget.forms[widget.forms.length - 1].name == null
|| widget.forms[widget.forms.length - 1].value == null) ? Colors.grey : Colors.black,)),
Text(widget.type == SubMapFormsType.ENV ? "env" : (widget.type == SubMapFormsType.INPUT ? "input" : "output"),
style: TextStyle( color: widget.forms.isNotEmpty && (widget.forms[widget.forms.length - 1].name == null
|| widget.forms[widget.forms.length - 1].value == null) ? Colors.grey : Colors.black))]),
),
),
InkWell( onTap: () {
if (addedparam != null) {
addedparam = null;
return setState(() {});
}
if (widget.forms.isEmpty) { return; }
widget.forms.sublist(0, widget.forms.length - 1);
var el = widget.dash.getElement(widget.elementID);
var sel = widget.item.getSelectedInstance();
if (sel != null) {
widget.forms.removeLast();
if (widget.type == SubMapFormsType.INPUT) { sel.inputs = [
...sel.inputs.where( (e) => e.readOnly),
...widget.forms.where((e) => e.name != null && e.value != null)
];
} else if (widget.type == SubMapFormsType.OUTPUT) {
sel.outputs = [
...sel.outputs.where( (e) => e.readOnly),
...widget.forms.where((e) => e.name != null && e.value != null)
];
} else {
sel.env = [
...sel.env.where( (e) => e.readOnly),
...widget.forms.where((e) => e.name != null && e.value != null)
];
}
}
el!.element = widget.item as dynamic;
setState(() { widget.dash.saveDash(widget.dash.id, context); });
}, child:
Container( margin: const EdgeInsets.only(left: 5, top: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5),
border: Border.all(color: widget.forms.isEmpty ? Colors.grey : Colors.black , width: 1)),
width: 50, height: 30,
child: Row( mainAxisAlignment: MainAxisAlignment.center,
children: [ Icon( Icons.delete, size: 18,
color: widget.forms.isEmpty ? Colors.grey : Colors.black ) ]),
)
),
]),
...children
]);
}
}

View File

@ -1,9 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart'; import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/widgets/inputs/sub_text_input.dart'; import 'package:oc_front/widgets/inputs/sub_text_input.dart';
// ignore: must_be_immutable
class SubExposeFormsWidget extends StatefulWidget { class SubExposeFormsWidget extends StatefulWidget {
bool readOnly; bool readOnly;
Expose item; Expose item;
@ -29,7 +30,7 @@ class SubExposeFormsWidgetState extends State<SubExposeFormsWidget> {
widget.item.port = int.parse(value); widget.item.port = int.parse(value);
Future.delayed(const Duration(seconds: 2), () { Future.delayed(const Duration(seconds: 2), () {
if (widget.item.port == int.parse(value) && int.parse(value) != 0) { if (widget.item.port == int.parse(value) && int.parse(value) != 0) {
widget.dash.saveDash(widget.dash.id); widget.dash.saveDash(widget.dash.id, context);
} }
}); });
} catch (e) { widget.item.port = null; } } catch (e) { widget.item.port = null; }
@ -44,7 +45,7 @@ class SubExposeFormsWidgetState extends State<SubExposeFormsWidget> {
widget.item.PAT = int.parse(value); widget.item.PAT = int.parse(value);
Future.delayed(const Duration(seconds: 2), () { Future.delayed(const Duration(seconds: 2), () {
if (widget.item.PAT == int.parse(value) && int.parse(value) != 0) { if (widget.item.PAT == int.parse(value) && int.parse(value) != 0) {
widget.dash.saveDash(widget.dash.id); widget.dash.saveDash(widget.dash.id, context);
} }
}); });
} catch (e) { widget.item.PAT = null; } } catch (e) { widget.item.PAT = null; }
@ -57,7 +58,7 @@ class SubExposeFormsWidgetState extends State<SubExposeFormsWidget> {
try { try {
widget.item.path = value; widget.item.path = value;
Future.delayed(const Duration(seconds: 2), () { Future.delayed(const Duration(seconds: 2), () {
if (widget.item.path == value) { widget.dash.saveDash(widget.dash.id); } if (widget.item.path == value) { widget.dash.saveDash(widget.dash.id, context); }
}); });
} catch (e) { widget.item.path = null; } } catch (e) { widget.item.path = null; }
var el = widget.dash.getElement(widget.elementID); var el = widget.dash.getElement(widget.elementID);

View File

@ -1,62 +1,97 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_advanced_switch/flutter_advanced_switch.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart'; import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/models/workflow.dart'; import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/widgets/forms/sub_add_forms.dart';
import 'package:oc_front/widgets/inputs/sub_text_input.dart'; import 'package:oc_front/widgets/inputs/sub_text_input.dart';
enum SubMapFormsType {
ENV,
INPUT,
OUTPUT
}
// ignore: must_be_immutable
class SubKeysMapFormsWidget extends StatefulWidget { class SubKeysMapFormsWidget extends StatefulWidget {
bool readOnly = false; bool readOnly = false;
FlowData item; AbstractItem item;
Dashboard dash; Dashboard dash;
String varKey = "";
bool empty = false; bool empty = false;
bool isInput = true; SubMapFormsType type;
String elementID = ""; String elementID = "";
String categoryKey = ""; SubKeysMapFormsWidget({ super.key, required this.dash, required this.type, this.readOnly = false,
List<GraphItem> graphItems = []; this.empty = false, required this.item, required this.elementID });
SubKeysMapFormsWidget({ super.key, required this.dash, required this.isInput, this.readOnly = false,
this.empty = false, required this.item, required this.elementID, required this.graphItems,
required this.categoryKey, required this.varKey });
@override SubKeysMapFormsWidgetState createState() => SubKeysMapFormsWidgetState(); @override SubKeysMapFormsWidgetState createState() => SubKeysMapFormsWidgetState();
} }
class SubKeysMapFormsWidgetState extends State<SubKeysMapFormsWidget> { class SubKeysMapFormsWidgetState extends State<SubKeysMapFormsWidget> {
bool _save = false;
List<Param> getParams(String fromID, String toID) {
List<Param> arr = [];
var els = widget.dash.elements.where( (e) => fromID.contains(e.id) || toID.contains(e.id));
for (var element in els) {
var g = GraphItem();
g.fromDashboard(element.serialize());
var e = g.getElement();
if (e != null && e.getSelectedInstance() != null) {
arr = [...arr, ...e.getSelectedInstance()!.env];
}
}
return arr;
}
List<Widget> getInstanceInOutput(AbstractItem? item, SubMapFormsType type) {
List<Widget> children = [];
if (item == null || item.getSelectedInstance() == null) {
return [];
}
AbstractInstance instance = item.getSelectedInstance()!;
List<Param> params = widget.type == SubMapFormsType.INPUT ? instance.inputs : ( widget.type == SubMapFormsType.OUTPUT ? instance.outputs : instance.env);
for ( var param in params) {
if (!param.readOnly) { continue; }
dynamic env;
if (param.value == null && param.attr != null) {
var s = item.serialize();
s.addAll(item.getSelectedInstance()?.serialize() ?? {});
env = s[param.attr!];
} else { env = param.value; }
_save = true;
if (env is bool) {
var ctrl = ValueNotifier<bool>(env);
children.add(AdvancedSwitch(
width: 150.0,
height: 25.0,
controller: ctrl,
enabled: false,
activeChild: Text(param.name!, style: const TextStyle(color: Colors.white)),
inactiveChild: Text("no ${param.name!}", style: const TextStyle(color: Colors.white)),
activeColor: Colors.green,
inactiveColor: redColor,
onChanged: (value) {},
));
} else {
children.add(SubTextInputWidget( subkey: param.name!, width: 180, empty: false, change: (value) { },
initialValue: env, readOnly: true, noLabel: false, copyLabel: true ));
}
}
return children;
}
@override Widget build(BuildContext context) { @override Widget build(BuildContext context) {
List<Widget> children = []; List<Widget> children = [];
var newwidget = getInstanceInOutput(widget.item, widget.type);
bool save = false; children.add(Column( children : [
for (var graphItem in widget.graphItems) { Padding( padding: const EdgeInsets.only(top: 10),
int count = 0; child: Text("<${widget.type == SubMapFormsType.INPUT ? "INPUT" : (widget.type == SubMapFormsType.OUTPUT ? "OUTPUT" : "")} ENV VARIABLES>",
var el = graphItem.getElement(); style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold), textAlign: TextAlign.center)),
if (el == null || el.model == null) { continue; } ...newwidget,
for ( var r in el.model!.refs.keys) { SubAddFormsWidget(dash: widget.dash, item: widget.item, elementID: widget.elementID, type: widget.type),
var env = widget.item.getVariable(["container", "env"], widget.item.serialize()); Container(width: 200, padding: const EdgeInsets.only(top: 10, bottom: 10),
if (env == null || env is Map<String, dynamic>) { continue; } decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1)))),
var n = "${el.topic.toUpperCase()}_${el.getName().toUpperCase().replaceAll(" ", "_")}_${r.toUpperCase()}_$count"; ]));
if (env[n] == null) { if (_save) {
save = true; widget.dash.saveDash(widget.dash.id, context);
}
env[n]= "{{ ${ widget.isInput ? "in" : "out" }_${graphItem.id}_$r }}";
widget.item.setVariable(["container", "env"], env, el.serialize());
children.add( Padding(padding: const EdgeInsets.only(bottom: 10),
child: SubTextInputWidget(subkey: n, width: 180, empty: false, change: (value) {},
initialValue: n, readOnly: true, noLabel: true)));
count++;
}
} }
if (save) { return Column( children : children );
widget.dash.saveDash(widget.dash.id);
}
if (children.isEmpty) {
return Container();
}
return Column( children : [
Container(width: 200, padding: const EdgeInsets.only(top: 10, bottom: 10),
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1)))),
Padding( padding: const EdgeInsets.only(top: 10),
child: Text("<${widget.isInput ? "INPUT ENV VARIABLE" : "OUTPUT ENV VARIABLE"}>", style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold),
textAlign: TextAlign.center)),
Column( children: children),
]);
} }
} }

View File

@ -1,115 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/widgets/inputs/sub_text_input.dart';
class MapForm {
String key = "";
String value = "";
MapForm({ required this.key, required this.value });
}
class SubMapFormsWidget extends StatefulWidget {
bool readOnly;
String categoryKey = "";
String varKey = "";
String elementID = "";
FlowData item;
Dashboard dash;
bool empty = false;
List<MapForm> forms = [];
SubMapFormsWidget ({ super.key, required this.dash, this.readOnly = false,
this.empty = false, required this.item, required this.elementID,
required this.categoryKey, required this.varKey });
@override SubMapFormsWidgetState createState() => SubMapFormsWidgetState();
}
class SubMapFormsWidgetState extends State<SubMapFormsWidget> {
Map<String, dynamic> toMap() {
Map<String, dynamic> m = {};
for (var form in widget.forms) {
m[form.key] = form.value;
}
return m;
}
@override Widget build(BuildContext context) {
Map<String, dynamic>? m = widget.item.getVariable([widget.categoryKey, widget.varKey], widget.item.serialize());
var l = [widget.categoryKey, widget.varKey];
List<Widget> children = [];
List<String> empty = [];
widget.forms = [];
var i = 0;
for (var key in (m?.keys.toList() ?? empty)) {
if (((m![key] as String?)?.contains('{{') ?? false)) {
continue;
}
widget.forms.add(MapForm(key: key, value: m[key]));
children.add(Row( children: [
SubTextInputWidget(subkey: "key", readOnly: widget.readOnly,
initialValue: key, width: 77.5, empty: widget.empty,
change: (value) {
setState(() {
widget.forms[i].key = value;
Future.delayed(const Duration(seconds: 2), () {
widget.dash.saveDash(widget.dash.id);
});
var el = widget.dash.getElement(widget.elementID);
widget.item = widget.item.deserialize(widget.item.setVariable(l, toMap(), widget.item.serialize())) as dynamic;
el!.element = widget.item as dynamic;
});
}),
const Padding(padding: EdgeInsets.only(left: 5, right: 5, top: 15), child: Text("=", textAlign: TextAlign.center,)),
SubTextInputWidget(subkey: "value", initialValue: widget.forms[i].value, width: 77.5, empty: widget.empty,
readOnly: widget.readOnly, change: (value) {
Future.delayed(const Duration(seconds: 2), () {
widget.dash.saveDash(widget.dash.id);
});
widget.forms[i].value = value;
var el = widget.dash.getElement(widget.elementID);
widget.item = widget.item.deserialize(
widget.item.setVariable(l, toMap(), widget.item.serialize())) as dynamic;
el!.element = widget.item as dynamic;
}),
]));
i++;
}
return Column( children : [
Container(width: 200, padding: const EdgeInsets.only(top: 10),
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))),),
widget.readOnly ? Container() : Row( children: [
InkWell( onTap: () {
widget.forms.add(MapForm(key: "", value: ""));
var el = widget.dash.getElement(widget.elementID);
widget.item = widget.item.deserialize(
widget.item.setVariable(l, toMap(), widget.item.serialize())) as dynamic;
el!.element = widget.item as dynamic;
setState(() {});
}, child: Container( margin: const EdgeInsets.only(top: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.grey, width: 1)),
width: 125, height: 30,
child: Row( mainAxisAlignment: MainAxisAlignment.center,
children: [ const Padding( padding: EdgeInsets.only(right: 5), child: Icon(Icons.add)), Text("add ${widget.varKey} vars")]),
),
),
InkWell( onTap: () {
if (widget.forms.isEmpty) { return;}
widget.forms.sublist(0, widget.forms.length - 1);
widget.item = widget.item.deserialize(
widget.item.setVariable(l, toMap(), widget.item.serialize())) as dynamic;
var el = widget.dash.getElement(widget.elementID);
el!.element = widget.item as dynamic;
setState(() { widget.dash.saveDash(widget.dash.id); });
}, child:
Container( margin: const EdgeInsets.only(left: 5, top: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.grey, width: 1)),
width: 45, height: 30,
child: const Row( mainAxisAlignment: MainAxisAlignment.center,
children: [ Icon( Icons.delete, color: Colors.black ) ]),
)
),
]),
...children
]);
}
}

View File

@ -1,23 +0,0 @@
import 'package:flutter/material.dart';
import 'package:oc_front/widgets/inputs/sub_text_input.dart';
class WebReferenceFormsWidget extends StatefulWidget {
dynamic item;
WebReferenceFormsWidget ({ super.key, required this.item });
@override WebReferenceFormsWidgetState createState() => WebReferenceFormsWidgetState();
}
class WebReferenceFormsWidgetState extends State<WebReferenceFormsWidget> {
@override Widget build(BuildContext context) {
return Column( children: [
SubTextInputWidget(subkey: "path", width: 180, empty: false, change: (value) {
widget.item.path = value.split(",");
}, initialValue: widget.item.path, readOnly: true,),
SubTextInputWidget(subkey: "protocol", width: 180, empty: false, change: (value) {
widget.item.protocol = value.split(",");
}, initialValue: widget.item.protocol, readOnly: true,),
SubTextInputWidget(subkey: "type", width: 180, empty: false, change: (value) {
widget.item.type = value.split(",");
}, initialValue: widget.item.type, readOnly: true,),
]);
}
}

View File

@ -94,7 +94,9 @@ class ShallowDropdownInputWidgetState extends State<ShallowDropdownInputWidget>
child:InkWell( child:InkWell(
mouseCursor: SystemMouseCursors.click, mouseCursor: SystemMouseCursors.click,
onTap: () async { onTap: () async {
if (widget.canLoad == null || !widget.canLoad!(widget.current) || widget.load == null || widget.current == null) { print("load ${widget.current}");
if (widget.canLoad == null || !widget.canLoad!(widget.current)
|| widget.load == null || widget.current == null) {
return; return;
} }
await widget.load!(widget.current!); await widget.load!(widget.current!);

View File

@ -54,6 +54,8 @@ class ShallowTextInputWidget extends StatefulWidget {
} }
} }
class ShallowTextInputWidgetState extends State<ShallowTextInputWidget> { class ShallowTextInputWidgetState extends State<ShallowTextInputWidget> {
TextEditingController ctrl = TextEditingController();
bool validForms() { bool validForms() {
for (var form in widget.forms) { for (var form in widget.forms) {
if (!form.validate()) { if (!form.validate()) {
@ -65,6 +67,7 @@ class ShallowTextInputWidgetState extends State<ShallowTextInputWidget> {
@override Widget build(BuildContext context) { @override Widget build(BuildContext context) {
var t = widget.type == CollaborativeAreaType.workspace ? "workspace" : (widget.type == CollaborativeAreaType.workflow ? "workflow" : "peer"); var t = widget.type == CollaborativeAreaType.workspace ? "workspace" : (widget.type == CollaborativeAreaType.workflow ? "workflow" : "peer");
ctrl.text = widget.current ?? "";
return Row( mainAxisAlignment: widget.alignment, children: [ return Row( mainAxisAlignment: widget.alignment, children: [
Tooltip( message: widget.hint ?? "current $t", child: Tooltip( message: widget.hint ?? "current $t", child:
Theme( Theme(
@ -83,7 +86,7 @@ class ShallowTextInputWidgetState extends State<ShallowTextInputWidget> {
} }
}); });
}, },
initialValue: widget.current, controller: ctrl,
style: TextStyle(color: widget.color ?? Colors.black, fontSize: 15), style: TextStyle(color: widget.color ?? Colors.black, fontSize: 15),
decoration: InputDecoration( decoration: InputDecoration(
filled: true, filled: true,

View File

@ -4,10 +4,11 @@ class SubDropdownInputWidget extends StatefulWidget {
String subkey; String subkey;
double width; double width;
bool empty; bool empty;
String? initialValue;
List<DropdownMenuItem<String>> dropdownMenuEntries = []; List<DropdownMenuItem<String>> dropdownMenuEntries = [];
void Function(String?)? change = (value) {}; void Function(String?)? change = (value) {};
SubDropdownInputWidget ({ Key? key, required this.dropdownMenuEntries, SubDropdownInputWidget ({ super.key, required this.dropdownMenuEntries, this.initialValue,
required this.subkey, required this.width, required this.empty, required this.change }): super(key: key); required this.subkey, required this.width, required this.empty, required this.change });
@override SubDropdownInputWidgetState createState() => SubDropdownInputWidgetState(); @override SubDropdownInputWidgetState createState() => SubDropdownInputWidgetState();
} }
class SubDropdownInputWidgetState extends State<SubDropdownInputWidget> { class SubDropdownInputWidgetState extends State<SubDropdownInputWidget> {
@ -17,9 +18,11 @@ class SubDropdownInputWidgetState extends State<SubDropdownInputWidget> {
child: Container( margin: EdgeInsets.only(top: widget.empty ? 0 : 15), child: Container( margin: EdgeInsets.only(top: widget.empty ? 0 : 15),
width: widget.width, height: 30, width: widget.width, height: 30,
child: DropdownButtonFormField( child: DropdownButtonFormField(
isExpanded: true,
items: widget.dropdownMenuEntries, items: widget.dropdownMenuEntries,
value: widget.initialValue,
onChanged: widget.change, onChanged: widget.change,
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12,color: Colors.black, overflow: TextOverflow.ellipsis),
decoration: InputDecoration( decoration: InputDecoration(
hintText: "select ${widget.subkey}...", hintText: "select ${widget.subkey}...",
fillColor: Colors.white, fillColor: Colors.white,

View File

@ -10,9 +10,10 @@ class SubTextInputWidget extends StatefulWidget {
bool empty; bool empty;
bool noLabel; bool noLabel;
bool readOnly = false; bool readOnly = false;
bool copyLabel = false;
void Function(String) change = (value) {}; void Function(String) change = (value) {};
SubTextInputWidget ({ Key? key, SubTextInputWidget ({ Key? key,
required this.subkey, this.readOnly = false, this.noLabel = false, required this.subkey, this.readOnly = false, this.noLabel = false, this.copyLabel = false,
this.initialValue, required this.width, required this.empty, required this.change }): this.initialValue, required this.width, required this.empty, required this.change }):
super(key: key); super(key: key);
@override SubTextInputWidgetState createState() => SubTextInputWidgetState(); @override SubTextInputWidgetState createState() => SubTextInputWidgetState();
@ -47,7 +48,7 @@ class SubTextInputWidgetState extends State<SubTextInputWidget> {
), ),
))), ))),
widget.readOnly ? InkWell( onTap: () { widget.readOnly ? InkWell( onTap: () {
Clipboard.setData(ClipboardData(text: widget.initialValue!)); Clipboard.setData(ClipboardData(text: widget.copyLabel ? "\$${widget.subkey}" : widget.initialValue!));
showAlertBanner(context, () {}, const InfoAlertBannerChild(text: "successfully add to clipboard"), // <-- Put any widget here you want! showAlertBanner(context, () {}, const InfoAlertBannerChild(text: "successfully add to clipboard"), // <-- Put any widget here you want!
alertBannerLocation: AlertBannerLocation.bottom,); alertBannerLocation: AlertBannerLocation.bottom,);
}, child: Container( margin: EdgeInsets.only(left: 5, top: widget.empty ? 0 : 15), }, child: Container( margin: EdgeInsets.only(left: 5, top: widget.empty ? 0 : 15),

View File

@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:oc_front/models/resources/resources.dart';
// ignore: must_be_immutable
class InfosWidget extends StatefulWidget {
AbstractItem item;
InfosWidget ({ super.key, required this.item });
@override InfosWidgetState createState() => InfosWidgetState();
}
class InfosWidgetState extends State<InfosWidget> {
@override Widget build(BuildContext context) {
List<Widget> children = [];
var obj = widget.item.infos();
for (var key in obj.keys) {
children.add(
Padding(padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 100),
child: Text("$key : ${obj[key] ?? "no $key"}",
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600))),
);
}
return Wrap( children: children);
}
}

View File

@ -1,9 +1,8 @@
import 'package:flutter/material.dart';
import 'package:oc_front/main.dart'; import 'package:oc_front/main.dart';
import 'package:oc_front/models/search.dart'; import 'package:flutter/material.dart';
import 'package:oc_front/widgets/items/items_details/data_item.dart'; import 'package:oc_front/models/resources/resources.dart';
// ignore: must_be_immutable
class ItemWidget extends StatefulWidget { class ItemWidget extends StatefulWidget {
AbstractItem item; AbstractItem item;
ItemWidget ({ super.key, required this.item }); ItemWidget ({ super.key, required this.item });
@ -12,15 +11,10 @@ class ItemWidget extends StatefulWidget {
class ItemWidgetState extends State<ItemWidget> { class ItemWidgetState extends State<ItemWidget> {
@override Widget build(BuildContext context) { @override Widget build(BuildContext context) {
Widget w = Container(); Widget w = Container();
/* if (isData(widget.item.topic)) { w = DataItemWidget(item: widget.item as DataItem); } return SizedBox(
else if (isComputing(widget.item.topic)) { w = DataItemWidget(item: widget.item as DataItem); }
else if (isCompute(widget.item.topic)) { w = DataItemWidget(item: widget.item as DataItem); }
else if (isStorage(widget.item.topic)) { w = DataItemWidget(item: widget.item as DataItem); } */
return Container(
height: getHeight(context) - 300, height: getHeight(context) - 300,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( children: [ child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [
widget.item.description == null ? Container() : Container( widget.item.description == null ? Container() : Container(
width: getMainWidth(context), width: getMainWidth(context),
alignment: Alignment.center, alignment: Alignment.center,
@ -29,10 +23,8 @@ class ItemWidgetState extends State<ItemWidget> {
child: Text(widget.item.description!, child: Text(widget.item.description!,
style: TextStyle(fontSize: 15, color: Colors.grey, fontWeight: FontWeight.w500))), style: TextStyle(fontSize: 15, color: Colors.grey, fontWeight: FontWeight.w500))),
Container(padding: const EdgeInsets.all(30), Container(padding: const EdgeInsets.all(30),
color: midColor, alignment: Alignment.topLeft,
width: getMainWidth(context) / 2, color: midColor, width: getMainWidth(context) / 2, child: w)
child: w
)
] ]
) )
) )

View File

@ -1,9 +1,8 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:oc_front/main.dart'; import 'package:oc_front/main.dart';
import 'package:oc_front/models/search.dart'; import 'package:flutter/material.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/models/resources/resources.dart';
import 'package:oc_front/core/models/workspace_local.dart';
const List<GlobalKey<State>> _empty = []; const List<GlobalKey<State>> _empty = [];
// ignore: must_be_immutable // ignore: must_be_immutable
@ -38,7 +37,7 @@ class ItemRowWidgetState extends State<ItemRowWidget> {
constraints: BoxConstraints(maxWidth: imageSize, minWidth: imageSize), constraints: BoxConstraints(maxWidth: imageSize, minWidth: imageSize),
child: image ?? Image.network('https://get-picto.com/wp-content/uploads/2024/01/logo-instagram-png.webp', child: image ?? Image.network('https://get-picto.com/wp-content/uploads/2024/01/logo-instagram-png.webp',
height: imageSize, width: imageSize)), height: imageSize, width: imageSize)),
Container( SizedBox(
width: widget.low ? widget.contextWidth - 20 : widget.contextWidth - (imageSize + 20) - endWidth, width: widget.low ? widget.contextWidth - 20 : widget.contextWidth - (imageSize + 20) - endWidth,
child: Padding(padding: widget.contextWidth != getMainWidth(context) ? child: Padding(padding: widget.contextWidth != getMainWidth(context) ?
const EdgeInsets.symmetric(horizontal: 10) : const EdgeInsets.symmetric(horizontal: 20), const EdgeInsets.symmetric(horizontal: 10) : const EdgeInsets.symmetric(horizontal: 20),
@ -48,10 +47,7 @@ class ItemRowWidgetState extends State<ItemRowWidget> {
widget.low ? Container() : Container(padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), widget.low ? Container() : Container(padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
margin: const EdgeInsets.only(right: 20), margin: const EdgeInsets.only(right: 20),
decoration: BoxDecoration( decoration: BoxDecoration(
color: isData(widget.item.topic) ? Colors.blue : color: getColor(widget.item.topic),
isComputing(widget.item.topic) ? Colors.green :
isCompute(widget.item.topic) ? Colors.orange :
isStorage(widget.item.topic) ? redColor : Colors.grey,
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
), ),
child: Text( getMainWidth(context) < 600 ? "" : widget.item.topic.toString(), child: Text( getMainWidth(context) < 600 ? "" : widget.item.topic.toString(),
@ -61,13 +57,13 @@ class ItemRowWidgetState extends State<ItemRowWidget> {
style: TextStyle(fontSize: widget.low ? 14 : 20, overflow: TextOverflow.ellipsis, fontWeight: FontWeight.w600, color: Color(0xFFF67C0B9))), style: TextStyle(fontSize: widget.low ? 14 : 20, overflow: TextOverflow.ellipsis, fontWeight: FontWeight.w600, color: Color(0xFFF67C0B9))),
) )
]), ]),
Text( "From ${widget.item.owner ?? "unknown owner"}", widget.item.owners.isEmpty ? Container() : Text( "From ${widget.item.owners.map( (e) => e.name).join(", ")}",
style: const TextStyle(fontSize: 14, color: Colors.grey, overflow: TextOverflow.ellipsis)), style: const TextStyle(fontSize: 14, color: Colors.grey, overflow: TextOverflow.ellipsis)),
Text(widget.item.shortDescription ?? "", style: const TextStyle(fontSize: 12, overflow: TextOverflow.ellipsis)), Text(widget.item.shortDescription ?? "", style: const TextStyle(fontSize: 12, overflow: TextOverflow.ellipsis)),
],) ],)
) )
), ),
widget.low ? Container() : Container( widget.low ? Container() : SizedBox(
width: endWidth, width: endWidth,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -91,7 +87,8 @@ class ItemRowWidgetState extends State<ItemRowWidget> {
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
), ),
child: Icon(WorkspaceLocal.hasItem(widget.item) ? Icons.remove_shopping_cart : Icons.add_shopping_cart, child: Icon(WorkspaceLocal.hasItem(widget.item) ? Icons.remove_shopping_cart : Icons.add_shopping_cart,
color: Colors.white, size: 20 )) color: Colors.white, size: 20 )
)
), ),
...(ratio > 1 ? [Padding( padding: const EdgeInsets.only(left: 20), ...(ratio > 1 ? [Padding( padding: const EdgeInsets.only(left: 20),
child: InkWell( child: InkWell(

View File

@ -1,24 +0,0 @@
import 'package:flutter/material.dart';
import 'package:oc_front/models/search.dart';
class DataItemWidget extends StatefulWidget {
DataItem item;
DataItemWidget ({ super.key, required this.item });
@override DataItemWidgetState createState() => DataItemWidgetState();
}
class DataItemWidgetState extends State<DataItemWidget> {
@override Widget build(BuildContext context) {
return Wrap( children: [
Padding(padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 100),
child: Text("type : ${widget.item.type ?? "unknown type"}",
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600))),
Padding(padding: const EdgeInsets.symmetric(horizontal: 100, vertical: 20),
child: Text("protocol : ${widget.item.protocols.isEmpty ? "no protocol founded" : widget.item.protocols.join(",")}",
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600))),
Padding(padding: const EdgeInsets.symmetric(horizontal: 100, vertical: 20),
child: Text("ex : ${widget.item.exemple ?? "no example"}",
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600))),
]);
}
}

View File

@ -1,14 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter_box_transform/flutter_box_transform.dart';
import 'package:oc_front/core/services/specialized_services/abstract_service.dart';
import 'package:oc_front/core/services/specialized_services/booking_service.dart';
import 'package:oc_front/main.dart'; import 'package:oc_front/main.dart';
import 'package:oc_front/models/workflow.dart'; import 'package:flutter/material.dart';
import 'package:oc_front/widgets/logs.dart'; import 'package:oc_front/widgets/logs.dart';
import 'package:oc_front/models/workflow.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_box_transform/flutter_box_transform.dart';
import 'package:oc_front/widgets/lib/tranformablebox.dart' as fork; import 'package:oc_front/widgets/lib/tranformablebox.dart' as fork;
import 'package:oc_front/widgets/sheduler_items/scheduler_calendar.dart';
import 'package:oc_front/widgets/sheduler_items/scheduler_item.dart'; import 'package:oc_front/widgets/sheduler_items/scheduler_item.dart';
import 'package:oc_front/widgets/sheduler_items/scheduler_calendar.dart';
import 'package:oc_front/core/services/specialized_services/abstract_service.dart';
import 'package:oc_front/core/services/specialized_services/workflow_execution_service.dart';
double menuSize = 300; double menuSize = 300;
// ignore: must_be_immutable // ignore: must_be_immutable
@ -19,8 +19,8 @@ class ScheduleWidget extends StatefulWidget {
bool loading = true; bool loading = true;
bool isList = true; bool isList = true;
bool isBox = true; bool isBox = true;
AbstractService<WorkflowExecutions> service; AbstractService<WorkflowExecutions> service = WorkflowExecutionService();
ScheduleWidget ({ super.key, required this.start, required this.end, required this.service, ScheduleWidget ({ super.key, required this.start, required this.end,
this.isBox =true, this.isList = true, this.loading = false}); this.isBox =true, this.isList = true, this.loading = false});
@override ScheduleWidgetState createState() => ScheduleWidgetState(); @override ScheduleWidgetState createState() => ScheduleWidgetState();
} }
@ -43,19 +43,16 @@ class ScheduleWidgetState extends State<ScheduleWidget> {
"${widget.start.year}-${widget.start.month > 9 ? widget.start.month : "0${widget.start.month}"}-${widget.start.day > 9 ? widget.start.day : "0${widget.start.day}"}", "${widget.start.year}-${widget.start.month > 9 ? widget.start.month : "0${widget.start.month}"}-${widget.start.day > 9 ? widget.start.day : "0${widget.start.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}"}"], {}), "${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) {
Future.delayed(const Duration(minutes: 1), () {
setState(() {});
});
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.startDate == null) { continue; }
DateTime dateTime = DateTime.parse(element.executionData!); DateTime dateTime = DateTime.parse(element.startDate!);
DateTime date = DateTime(dateTime.year, dateTime.month, dateTime.day); DateTime date = DateTime(dateTime.year, dateTime.month, dateTime.day);
var str = "${date.toIso8601String()}Z"; var str = "${date.toIso8601String()}Z";
if (data[str] == null) { data[str] = []; } if (data[str] == null) { data[str] = []; }
data[str]!.add(element); data[str]!.add(element);
data[str]!.sort((a, b) => DateTime.parse(a.executionData!).compareTo(DateTime.parse(b.executionData!))); data[str]!.sort((a, b) => DateTime.parse(a.startDate!).compareTo(DateTime.parse(b.startDate!)));
} }
} }
bool isInfo = getMainWidth(context) <= 600 && selected != null && widget.isBox; bool isInfo = getMainWidth(context) <= 600 && selected != null && widget.isBox;
@ -63,12 +60,12 @@ class ScheduleWidgetState extends State<ScheduleWidget> {
List<Widget> children = []; List<Widget> children = [];
if (selected != null) { if (selected != null) {
for (var wf in data[selected!] ?? (<WorkflowExecution>[])) { for (var wf in data[selected!] ?? (<WorkflowExecution>[])) {
DateTime d2 = DateTime.parse(wf.executionData!).toLocal(); DateTime d2 = DateTime.parse(wf.startDate!).toLocal();
children.add( InkWell( children.add( InkWell(
onTap: () => setState(() { selectedReal = wf.executionData; }), onTap: () => setState(() { selectedReal = wf.startDate; }),
child: Container( margin: const EdgeInsets.all(10), child: Container( margin: const EdgeInsets.all(10),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: selectedReal != null && selectedReal == wf.executionData ? lightColor : Colors.transparent, width: 2), border: Border.all(color: selectedReal != null && selectedReal == wf.startDate ? lightColor : Colors.transparent, width: 2),
borderRadius: BorderRadius.circular(4), color: Colors.white borderRadius: BorderRadius.circular(4), color: Colors.white
), ),
child: Container( child: Container(
@ -96,13 +93,12 @@ class ScheduleWidgetState extends State<ScheduleWidget> {
)) ))
)); ));
} }
} }
String? selectedID; String? selectedID;
WorkflowExecution? sel; WorkflowExecution? sel;
if (selectedReal != null) { if (selectedReal != null) {
try { try {
sel = data[selected!]!.firstWhere((element) => element.executionData == selectedReal); sel = data[selected!]!.firstWhere((element) => element.startDate == selectedReal);
selectedID = sel.id; selectedID = sel.id;
} catch(e) { /* */ } } catch(e) { /* */ }
} }

View File

@ -70,7 +70,7 @@ class SchedulerCalendarWidgetState extends State<SchedulerCalendarWidget> {
} }
children.add(InkWell( onTap: () => widget.parent!.setState(() { children.add(InkWell( onTap: () => widget.parent!.setState(() {
selected = day.toIso8601String(); selected = day.toIso8601String();
selectedReal = ev.executionData; selectedReal = ev.startDate;
if (selectedReal == null) { if (selectedReal == null) {
widget.parent!.widget.isDayPlanner = true; widget.parent!.widget.isDayPlanner = true;
} }
@ -158,9 +158,9 @@ class SchedulerCalendarWidgetState extends State<SchedulerCalendarWidget> {
shouldFillViewport: true, shouldFillViewport: true,
eventLoader: (day) { eventLoader: (day) {
return widget.data[day.toIso8601String()] != null ? widget.data[day.toIso8601String()]!.map((e) { return widget.data[day.toIso8601String()] != null ? widget.data[day.toIso8601String()]!.map((e) {
DateTime dateTime = DateTime.parse(e.executionData!); DateTime dateTime = DateTime.parse(e.startDate!);
return Event("[${dateTime.hour > 9 ? dateTime.hour : "0${dateTime.hour}"}:${dateTime.minute > 9 ? dateTime.minute : "0${dateTime.minute}"}:${dateTime.second > 9 ? dateTime.second : "0${dateTime.second}"}] ${e.name}", return Event("[${dateTime.hour > 9 ? dateTime.hour : "0${dateTime.hour}"}:${dateTime.minute > 9 ? dateTime.minute : "0${dateTime.minute}"}:${dateTime.second > 9 ? dateTime.second : "0${dateTime.second}"}] ${e.name}",
colors[(e.status ?? 1) - 1], e.executionData ); colors[(e.status ?? 1) - 1], e.startDate );
}).toList() : []; }).toList() : [];
}, },
)); ));
@ -169,10 +169,10 @@ class SchedulerCalendarWidgetState extends State<SchedulerCalendarWidget> {
class Event { class Event {
final String title; final String title;
String? executionData; String? startDate;
Color color; Color color;
Event(this.title, this.color, this.executionData); Event(this.title, this.color, this.startDate);
@override @override
String toString() => title; String toString() => title;

View File

@ -26,23 +26,23 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
for (var element in widget.data.keys.toList()..sort((a, b) => DateTime.parse(a).compareTo(DateTime.parse(b)))) { for (var element in widget.data.keys.toList()..sort((a, b) => DateTime.parse(a).compareTo(DateTime.parse(b)))) {
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.startDate!] = GlobalKey();
var d2 = DateTime.parse(ev.executionData!).toLocal(); var d2 = DateTime.parse(ev.startDate!).toLocal();
DateTime? d3; DateTime? d3;
try { d3 = DateTime.parse(ev.endDate!).toLocal(); 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(() {
selected = selected != element || ev.executionData != selectedReal ? element : null; selected = selected != element || ev.startDate != selectedReal ? element : null;
selectedReal = selected == null ? null : ev.executionData; selectedReal = selected == null ? null : ev.startDate;
if (selectedReal == null) { if (selectedReal == null) {
widget.parent!.widget.isDayPlanner = true; widget.parent!.widget.isDayPlanner = true;
} }
}), }),
child: Container( key: widget.keys[ev.executionData!], child: Container( key: widget.keys[ev.startDate!],
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50), padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50),
decoration: BoxDecoration( decoration: BoxDecoration(
border: selectedReal == ev.executionData ? border: selectedReal == ev.startDate ?
Border.all(color: lightColor, width: 2) : Border(top: BorderSide(color: midColor)), Border.all(color: lightColor, width: 2) : Border(top: BorderSide(color: midColor)),
), ),
child: Row(children: [ child: Row(children: [
@ -59,7 +59,7 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
SizedBox( width: (widget.width - 330) / 2, SizedBox( width: (widget.width - 330) / 2,
child: Padding( child: Padding(
padding: const EdgeInsets.only(left: 20), padding: const EdgeInsets.only(left: 20),
child: Text(ev.name?.toUpperCase() ?? "", overflow: TextOverflow.ellipsis, child: Text("${ev.name?.toUpperCase().split("EXECUTION")[0].replaceAll("_", "") ?? ""} EXECUTION", overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.black, fontWeight: FontWeight.w500)), style: const TextStyle(color: Colors.black, fontWeight: FontWeight.w500)),
)), )),
SizedBox( width: (widget.width - 340) / 2, SizedBox( width: (widget.width - 340) / 2,

View File

@ -1 +1 @@
3.19.6 3.24.3

View File

@ -5,5 +5,5 @@ export 'src/dashboard.dart';
export 'src/elements/connection_params.dart'; export 'src/elements/connection_params.dart';
export 'src/elements/flow_element.dart'; export 'src/elements/flow_element.dart';
export 'src/flow_chart.dart'; export 'src/flow_chart.dart';
export 'src/ui/draw_arrow.dart' show ArrowParams, ArrowStyle, ArrowDirection; export 'src/ui/draw_arrow.dart' show ArrowParams, ArrowStyle, ArrowDirection, ArrowPainter;
export 'src/ui/grid_background.dart' show GridBackgroundParams; export 'src/ui/grid_background.dart' show GridBackgroundParams;

View File

@ -28,13 +28,11 @@ class Dashboard extends ChangeNotifier {
GlobalKey<FlowChartMenuState> chartMenuKey = GlobalKey<FlowChartMenuState>(); GlobalKey<FlowChartMenuState> chartMenuKey = GlobalKey<FlowChartMenuState>();
GlobalKey<ChartWidgetState> chartKey = GlobalKey<ChartWidgetState>(); GlobalKey<ChartWidgetState> chartKey = GlobalKey<ChartWidgetState>();
GlobalKey<FlowChartState> flutterChartKey = GlobalKey<FlowChartState>(); GlobalKey<FlowChartState> flutterChartKey = GlobalKey<FlowChartState>();
bool inDialog = false;
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> info = {}; Map<String, dynamic> info = {};
bool scheduleActive = false;
String? id; String? id;
String name = ""; String name = "";
bool isMenu = true; bool isMenu = true;
@ -47,8 +45,9 @@ class Dashboard extends ChangeNotifier {
double defaultDashWidth = 0; double defaultDashWidth = 0;
double defaultBackWidth = 10; double defaultBackWidth = 10;
double defaultForwardWidth = 10; double defaultForwardWidth = 10;
Future<void> Function(String? id)? save; Future<void> Function(String? id, BuildContext? context)? save;
List<Widget> Function(FlowData? obj, String id)? infoItemWidget; List<Widget> Function(FlowData? obj, String id)? infoItemWidget;
Widget Function(ArrowPainter item)? infoLinkWidget;
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; bool addChange = false;
@ -74,7 +73,6 @@ class Dashboard extends ChangeNotifier {
this.save, this.save,
this.dashColor = Colors.grey, this.dashColor = Colors.grey,
this.midDashColor = Colors.blueGrey, this.midDashColor = Colors.blueGrey,
this.scheduler = const {},
Offset? handlerFeedbackOffset, Offset? handlerFeedbackOffset,
this.isMenu = true, this.isMenu = true,
this.defaultDashSpace = 0, this.defaultDashSpace = 0,
@ -93,7 +91,7 @@ class Dashboard extends ChangeNotifier {
// This is a workaround to set the handlerFeedbackOffset // This is a workaround to set the handlerFeedbackOffset
// to improve the user experience on devices with touch screens // to improve the user experience on devices with touch screens
// This will prevent the handler being covered by user's finger // This will prevent the handler being covered by user's finger
if (loadedGraph != null) { deserialize(loadedGraph!); } if (loadedGraph != null) { deserialize(loadedGraph!, false); }
if (handlerFeedbackOffset != null) { if (handlerFeedbackOffset != null) {
this.handlerFeedbackOffset = handlerFeedbackOffset; this.handlerFeedbackOffset = handlerFeedbackOffset;
} else { } else {
@ -110,18 +108,15 @@ class Dashboard extends ChangeNotifier {
tempHistory = []; tempHistory = [];
history = []; history = [];
} }
Future<void> saveDash(String? id) async { Future<void> saveDash(String? id, BuildContext? context) async {
shouldSave = true;
for (var element in saveRules) { for (var element in saveRules) {
if (element(this)) { if (!element(this)) {
shouldSave = true;
} else {
shouldSave = false; shouldSave = false;
break; break;
} }
} }
if (save != null && shouldSave) { if (save != null && shouldSave) {
save!(id); save!(id, context);
} }
} }
Future<void> Function(String cat)? load; Future<void> Function(String cat)? load;
@ -131,14 +126,12 @@ class Dashboard extends ChangeNotifier {
final d = Dashboard( final d = Dashboard(
name: map['name'] as String, name: map['name'] as String,
isMenu: map['isMenu'] as bool, isMenu: map['isMenu'] as bool,
scheduler: map['schedule'] as Map<String, String>? ?? {},
defaultDashSpace: map['defaultDashSpace'] as double? ?? 0, defaultDashSpace: map['defaultDashSpace'] as double? ?? 0,
defaultDashWidth: map['defaultDashWidth'] as double? ?? 0, defaultDashWidth: map['defaultDashWidth'] as double? ?? 0,
defaultArrowDirection: ArrowDirection.values[ defaultArrowDirection: ArrowDirection.values[
map['defaultArrowDirection'] as int? ?? 0], map['defaultArrowDirection'] as int? ?? 0],
defaultArrowStyle: ArrowStyle.values[map['arrowStyle'] as int? ?? 0], defaultArrowStyle: ArrowStyle.values[map['arrowStyle'] as int? ?? 0],
); );
d..scheduleActive = map['schedule_active'] as bool? ?? false;
d..arrows = List<ArrowPainter>.from( d..arrows = List<ArrowPainter>.from(
(map['arrows'] as List<dynamic>).map<ArrowPainter>( (map['arrows'] as List<dynamic>).map<ArrowPainter>(
(x) => ArrowPainter.fromMap(x as Map<String, dynamic>), (x) => ArrowPainter.fromMap(x as Map<String, dynamic>),
@ -170,15 +163,14 @@ class Dashboard extends ChangeNotifier {
} }
void copyFromMap(Map<String, dynamic> map) { void copyFromMap(Map<String, dynamic> map) {
scheduleActive = map['schedule_active'] as bool? ?? false; debugPrintStack(stackTrace: StackTrace.current, label: 'my_label', maxFrames: 5);
scheduler = map['schedule'] as Map<String, String>? ?? {};
defaultArrowStyle = ArrowStyle.values[map['arrowStyle'] as int? ?? 0]; defaultArrowStyle = ArrowStyle.values[map['arrowStyle'] as int? ?? 0];
defaultDashSpace = map['defaultDashSpace'] as double? ?? 0; defaultDashSpace = map['defaultDashSpace'] as double? ?? 0;
defaultDashWidth = map['defaultDashWidth'] as double? ?? 0; defaultDashWidth = map['defaultDashWidth'] as double? ?? 0;
defaultArrowDirection = ArrowDirection.values[ defaultArrowDirection = ArrowDirection.values[
map['defaultArrowDirection'] as int? ?? 0]; map['defaultArrowDirection'] as int? ?? 0];
arrows = List<ArrowPainter>.from( arrows = List<ArrowPainter>.from(
(map['arrows'] as List<dynamic>).map<ArrowPainter>( (map['arrows'] as List<dynamic>).map<ArrowPainter>(
(x) => ArrowPainter.fromMap(x as Map<String, dynamic>), (x) => ArrowPainter.fromMap(x as Map<String, dynamic>),
), ),
); );
@ -257,26 +249,25 @@ class Dashboard extends ChangeNotifier {
d["id"]=id; d["id"]=id;
d["name"]=name; d["name"]=name;
d["graph"]=graph; d["graph"]=graph;
d["schedule"]=scheduler;
d["schedule_active"]=scheduleActive;
return d; return d;
} }
void deserialize(Map<String, dynamic> graph) { void deserialize(Map<String, dynamic> graph, bool noHistory) {
elements = []; elements = [];
arrows = []; arrows = [];
info["shared"] = graph["shared"] ?? []; info["shared"] = graph["shared"] ?? [];
scheduler = graph['schedule'] ?? {};
scheduleActive = graph['schedule_active'] ?? false;
setZoomFactor(graph["graph"]?["zoom"] ?? 1.0); setZoomFactor(graph["graph"]?["zoom"] ?? 1.0);
for(var el in graph['graph']?['elements'] ?? []) { for(var el in graph['graph']?['elements'] ?? []) {
List<ConnectionParams> nexts = []; List<ConnectionParams> nexts = [];
var flow = FlowElement.deserialize(this, el); var flow = FlowElement.deserialize(this, el);
for(var ar in graph['graph']['arrows']) { for(var ar in graph['graph']['arrows']) {
if (ar['from']['id'] != flow.id) { continue; } if (ar['from']['id'] != flow.id) { continue; }
nexts.add(ConnectionParams( nexts.add(ConnectionParams(
srcElementId: ar['from']['id'], srcElementId: ar['from']['id'],
destElementId: ar['to']['id'], destElementId: ar['to']['id'],
infos: ar['infos'],
env: ar['env'],
arrowParams: ArrowParams.fromMap(ar["params"]), arrowParams: ArrowParams.fromMap(ar["params"]),
pivots: [ pivots: [
Pivot(Offset(ar['from']['x'], (ar['from']['y']))), Pivot(Offset(ar['from']['x'], (ar['from']['y']))),
@ -291,16 +282,35 @@ class Dashboard extends ChangeNotifier {
arr.add(build); arr.add(build);
} }
} }
try { if (flow.element != null) {
FlowData data = arr.firstWhere((element) => element.getID() == flow.element?.getID());
flow.kind = ElementKind.widget; flow.kind = ElementKind.widget;
flow.widget = chartKey.currentState?.widget.flowChart.widget.itemWidget(data); flow.widget = chartKey.currentState?.widget.flowChart.widget.itemWidget(flow.element!);
} catch (e) { print(e); } }
elements.add(flow); elements.add(flow);
} }
selectedMenuKey.currentState?.setState(() { }); selectedMenuKey.currentState?.setState(() { });
chartMenuKey.currentState?.setState(() { }); chartMenuKey.currentState?.setState(() { });
addToHistory(); if (!noHistory) {
addToHistory();
}
}
void applyInfos(Map<String, List<dynamic>> elInfos, Map<String, dynamic> graph) {
for(var id in elInfos.keys) {
try {
var element = elements.firstWhere((element) => element.id == id);
element.element?.addEnv(elInfos[id] ?? []);
} catch (e) { print("THERE ??? ${id} ${e}"); }
}
for(var ar in graph['graph']['arrows']) {
var arr = arrows.where((a) => a.fromID == ar['from']['id'] && a.toID == ar['to']['id']).toList();
for (var a in arr) {
a.infos = ar['infos'];
a.env = ar['env'];
}
}
selectedLeftMenuKey.currentState?.setState(() { });
} }
/// add listener called when a new connection is created /// add listener called when a new connection is created
@ -325,30 +335,31 @@ class Dashboard extends ChangeNotifier {
handlerFeedbackOffset = offset; handlerFeedbackOffset = offset;
} }
List<ArrowPainter> GetArrowByElementID(String id, bool isinput) { List<ArrowPainter> getArrowByElementID(String id, bool isinput) {
return arrows.where((element) => (isinput && element.toID.contains(id)) || (!isinput && element.fromID.contains(id))).toList(); return arrows.where((element) =>
(isinput && element.toID.contains(id)) || (!isinput && element.fromID.contains(id))).toList();
} }
void addArrows(ArrowPainter f) { void addArrows(ArrowPainter f, BuildContext? context) {
arrows.add(f); arrows.add(f);
for (var f in arrowStyleRules) { for (var f in arrowStyleRules) {
arrows = f(this); arrows = f(this);
} }
addChange = true; addChange = true;
saveDash(id); saveDash(id, context);
} }
void removeArrows(bool Function(ArrowPainter) f) { void removeArrows(bool Function(ArrowPainter) f, BuildContext? context) {
arrows.removeWhere((element) => f(element)); arrows.removeWhere((element) => f(element));
for (var f in arrowStyleRules) { for (var f in arrowStyleRules) {
arrows = f(this); arrows = f(this);
} }
saveDash(id); saveDash(id, context);
} }
void removeElements(bool Function(FlowElement<FlowData>) f) { void removeElements(bool Function(FlowElement<FlowData>) f, BuildContext? context) {
elements.removeWhere((element) => f(element)); elements.removeWhere((element) => f(element));
saveDash(id); saveDash(id, context);
} }
void clear() { void clear() {
@ -356,9 +367,7 @@ class Dashboard extends ChangeNotifier {
arrows.clear(); arrows.clear();
tempHistory = []; tempHistory = [];
history = []; history = [];
scheduler = {};
info = {}; info = {};
scheduleActive = false;
notifyListeners(); notifyListeners();
} }
bool noHistory = false; bool noHistory = false;
@ -375,14 +384,13 @@ class Dashboard extends ChangeNotifier {
Future.delayed(Duration(seconds: 1), () { Future.delayed(Duration(seconds: 1), () {
chartMenuKey.currentState?.setState(() { }); chartMenuKey.currentState?.setState(() { });
}); });
;
} }
bool isBack = false; bool isBack = false;
void back() { void back() {
if (canBack()) { if (canBack()) {
noHistory = true; noHistory = true;
tempHistory.removeLast(); tempHistory.removeLast();
if (tempHistory.length > 0) { if (tempHistory.isNotEmpty) {
copyFromMap(tempHistory.last); copyFromMap(tempHistory.last);
} }
chartKey.currentState?.setState(() { }); chartKey.currentState?.setState(() { });
@ -413,28 +421,29 @@ class Dashboard extends ChangeNotifier {
FlowElement element, FlowElement element,
bool resizable, { bool resizable, {
bool notify = true, bool notify = true,
BuildContext? context
}) { }) {
element.isResizing = resizable; element.isResizing = resizable;
saveDash(id); saveDash(id, context);
if (notify) notifyListeners(); if (notify) notifyListeners();
} }
FlowElement? getElement(String id, {bool notify = true}) { FlowElement? getElement(String id, {bool notify = true}) {
try { return elements.firstWhere((element) { try { return elements.firstWhere((element) {
return element.id == id; return id.contains(element.id);
}); } }); }
catch (e) { return null; } catch (e) { return null; }
} }
/// add a [FlowElement] to the dashboard /// add a [FlowElement] to the dashboard
void addElement(FlowElement element, {bool notify = true}) { void addElement(FlowElement element, BuildContext? context, {bool notify = true}) {
if (element.id.isEmpty) { if (element.id.isEmpty) {
element.id = const Uuid().v4(); element.id = const Uuid().v4();
} }
element.setScale(1, gridBackgroundParams.scale); element.setScale(1, gridBackgroundParams.scale);
elements.add(element); elements.add(element);
addChange = true; addChange = true;
saveDash(id); saveDash(id, context);
if (notify) { if (notify) {
notifyListeners(); notifyListeners();
} }
@ -554,16 +563,16 @@ class Dashboard extends ChangeNotifier {
} }
/// remove all elements /// remove all elements
void removeAllElements({bool notify = true}) { void removeAllElements(BuildContext? context, {bool notify = true}) {
elements.clear(); elements.clear();
if (notify) notifyListeners(); if (notify) notifyListeners();
saveDash(id); saveDash(id, context);
} }
/// remove the [handler] connection of [element] /// remove the [handler] connection of [element]
void removeElementConnection( void removeElementConnection(
FlowElement element, FlowElement element,
Handler handler, { Handler handler, BuildContext? context, {
bool notify = true, bool notify = true,
}) { }) {
Alignment alignment; Alignment alignment;
@ -600,7 +609,7 @@ class Dashboard extends ChangeNotifier {
} }
} }
if (notify) notifyListeners(); if (notify) notifyListeners();
saveDash(id); saveDash(id, context);
} }
/// dissect an element connection /// dissect an element connection
@ -690,14 +699,14 @@ class Dashboard extends ChangeNotifier {
} }
/// remove all the connection from the [element] /// remove all the connection from the [element]
void removeElementConnections(FlowElement element, {bool notify = true}) { void removeElementConnections(FlowElement element, BuildContext? context, {bool notify = true}) {
element.next.clear(); element.next.clear();
if (notify) notifyListeners(); if (notify) notifyListeners();
saveDash(id); saveDash(id, context);
} }
/// remove all the elements with [id] from the dashboard /// remove all the elements with [id] from the dashboard
void removeElementById(String id, {bool notify = true}) { void removeElementById(String id, BuildContext? context, {bool notify = true}) {
// remove the element // remove the element
var elementId = ''; var elementId = '';
elements.removeWhere((element) { elements.removeWhere((element) {
@ -714,12 +723,12 @@ class Dashboard extends ChangeNotifier {
}); });
} }
if (notify) notifyListeners(); if (notify) notifyListeners();
saveDash(id); saveDash(id, context);
} }
/// remove element /// remove element
/// return true if it has been removed /// return true if it has been removed
bool removeElement(FlowElement element, {bool notify = true}) { bool removeElement(FlowElement element, BuildContext? context, {bool notify = true}) {
// remove the element // remove the element
var found = false; var found = false;
final elementId = element.id; final elementId = element.id;
@ -735,7 +744,7 @@ class Dashboard extends ChangeNotifier {
); );
} }
if (notify) notifyListeners(); if (notify) notifyListeners();
saveDash(id); saveDash(id, context);
return found; return found;
} }

View File

@ -11,14 +11,19 @@ class ConnectionParams {
required this.srcElementId, required this.srcElementId,
required this.destElementId, required this.destElementId,
required this.arrowParams, required this.arrowParams,
this.env = const [],
this.infos = const [],
List<Pivot>? pivots, List<Pivot>? pivots,
}) : pivots = pivots ?? []; }) : pivots = pivots ?? [];
List<dynamic> env = [];
List<dynamic> infos = [];
/// ///
factory ConnectionParams.fromMap(Map<String, dynamic> map) { factory ConnectionParams.fromMap(Map<String, dynamic> map) {
return ConnectionParams( return ConnectionParams(
srcElementId: map['srcElementId'] as String, srcElementId: map['srcElementId'] as String,
destElementId: map['destElementId'] as String, destElementId: map['destElementId'] as String,
infos: map['infos'] as List<dynamic>? ?? [],
env: map['env'] as List<dynamic>? ?? [],
arrowParams: ArrowParams.fromMap(map['arrowParams'] as Map<String, dynamic>), arrowParams: ArrowParams.fromMap(map['arrowParams'] as Map<String, dynamic>),
pivots: (map['pivots'] as List?) pivots: (map['pivots'] as List?)
?.map<Pivot>( ?.map<Pivot>(
@ -37,7 +42,6 @@ class ConnectionParams {
final String destElementId; final String destElementId;
final String srcElementId; final String srcElementId;
/// Arrow parameters. /// Arrow parameters.
final ArrowParams arrowParams; final ArrowParams arrowParams;

View File

@ -374,6 +374,7 @@ class FlowElement<T extends FlowData> extends ChangeNotifier {
graphElement['width'] = size.width; graphElement['width'] = size.width;
graphElement['height'] = size.height; graphElement['height'] = size.height;
graphElement['element']=element?.serialize(); graphElement['element']=element?.serialize();
graphElement['next'] = next.map((x) => x.toMap()).toList();
return graphElement; return graphElement;
} }
@ -385,6 +386,13 @@ class FlowElement<T extends FlowData> extends ChangeNotifier {
position: Offset(double.parse("${map['x']}"), double.parse("${map['y']}")), position: Offset(double.parse("${map['x']}"), double.parse("${map['y']}")),
size: Size(double.parse("${map['width']}"), double.parse("${map['height']}")), size: Size(double.parse("${map['width']}"), double.parse("${map['height']}")),
element: (dashboard.transformToData != null ? dashboard.transformToData!(map['element'] ?? {}) : null) as T?, element: (dashboard.transformToData != null ? dashboard.transformToData!(map['element'] ?? {}) : null) as T?,
/*next: map['next'] != null && (map['next'] as List).isNotEmpty
? List<ConnectionParams>.from(
(map['next'] as List<dynamic>).map<dynamic>(
(x) => ConnectionParams.fromMap(x as Map<String, dynamic>),
),
)
: []*/
); );
} }
} }

View File

@ -1,29 +1,27 @@
// ignore: directives_ordering // ignore: directives_ordering
import 'dart:developer'; import 'package:uuid/uuid.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:flutter_flow_chart/src/flow_chart_left_menu.dart';
import 'package:flutter_flow_chart/src/flow_chart_menu.dart';
import 'package:flutter_flow_chart/src/flow_chart_selected_menu.dart';
import 'package:flutter_flow_chart/src/ui/draw_arrow.dart'; import 'package:flutter_flow_chart/src/ui/draw_arrow.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:flutter_flow_chart/src/flow_chart_menu.dart';
import 'package:flutter_flow_chart/src/ui/element_widget.dart'; import 'package:flutter_flow_chart/src/ui/element_widget.dart';
import 'package:flutter_flow_chart/src/ui/grid_background.dart'; import 'package:flutter_flow_chart/src/ui/grid_background.dart';
import 'package:flutter_flow_chart/src/ui/segment_handler.dart'; import 'package:flutter_flow_chart/src/ui/segment_handler.dart';
import 'package:uuid/uuid.dart'; import 'package:flutter_flow_chart/src/flow_chart_left_menu.dart';
import 'package:flutter_flow_chart/src/flow_chart_selected_menu.dart';
/// Main flow chart Widget. /// Main flow chart Widget.
/// It displays the background grid, all the elements and connection lines /// It displays the background grid, all the elements and connection lines
abstract class FlowData { abstract class FlowData {
String getID(); String getID();
String getName(); String getName();
String getType();
double? getWidth(); double? getWidth();
double? getHeight(); double? getHeight();
void addEnv(List<dynamic> infos);
Map<String, dynamic> setVariable(List<String> keys, dynamic value, Map<String, dynamic> map);
dynamic getVariable(List<String> keys, Map<String, dynamic> map);
Map<String, dynamic> serialize(); Map<String, dynamic> serialize();
FlowData deserialize(Map<String, dynamic> data); FlowData deserialize(Map<String, dynamic> data);
} }
@ -321,17 +319,21 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
Future.delayed(Duration(milliseconds: 100), () => widget.dashboard.load!(widget.dashboard.id!) ); Future.delayed(Duration(milliseconds: 100), () => widget.dashboard.load!(widget.dashboard.id!) );
} else { } else {
Future.delayed(Duration(milliseconds: 100), () { Future.delayed(Duration(milliseconds: 100), () {
showDialog( if (!widget.dashboard.inDialog) {
barrierDismissible: false, widget.dashboard.inDialog = true;
context: context, builder: (context) { showDialog(
return AlertDialog( barrierDismissible: false,
titlePadding: EdgeInsets.zero, context: context, builder: (context) {
insetPadding: EdgeInsets.zero, return AlertDialog(
backgroundColor: Colors.white, titlePadding: EdgeInsets.zero,
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)), insetPadding: EdgeInsets.zero,
title: widget.onDashboardAlertOpened!(context, widget.dashboard)); backgroundColor: Colors.white,
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)),
}); }); title: widget.onDashboardAlertOpened!(context, widget.dashboard));
});
}
});
} }
} else { } else {
widget.dashboard.isOpened = true; widget.dashboard.isOpened = true;
@ -357,14 +359,14 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
onKeyEvent: (event) { onKeyEvent: (event) {
bool change = false; bool change = false;
if (event.logicalKey == LogicalKeyboardKey.controlLeft) { if (event.logicalKey == LogicalKeyboardKey.controlLeft) {
isCtrl = event is KeyDownEvent || event is KeyRepeatEvent; isCtrl = event is KeyDownEvent || event is KeyRepeatEvent;
} }
if (event is KeyDownEvent || event.logicalKey == LogicalKeyboardKey.keyZ && isCtrl) { /*if ((event is KeyDownEvent || event.logicalKey == LogicalKeyboardKey.keyZ) && isCtrl) {
widget.dashboard.back(); widget.dashboard.back();
} }
if (event is KeyDownEvent || event.logicalKey == LogicalKeyboardKey.keyY && isCtrl) { if ((event is KeyDownEvent || event.logicalKey == LogicalKeyboardKey.keyY) && isCtrl) {
widget.dashboard.forward(); widget.dashboard.forward();
} }*/
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) {
@ -379,18 +381,18 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
kind: el.kind, kind: el.kind,
handlers: el.handlers, handlers: el.handlers,
handlerSize: el.handlerSize, handlerSize: el.handlerSize,
)); ), context);
} }
} }
if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.delete) { if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.delete) {
change = true; change = true;
widget.dashboard.removeElements( (el) => el.isSelected ); widget.dashboard.removeElements( (el) => el.isSelected, context );
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.removeArrows( (el) => el.isSelected ); widget.dashboard.removeArrows( (el) => el.isSelected, context );
} }
if (change) { if (change) {
DrawingArrow.instance.notifyListeners(); DrawingArrow.instance.notifyListeners();
@ -459,7 +461,7 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
Handler.rightCenter, Handler.rightCenter,
], ],
); );
widget.dashboard.addElement(el); widget.dashboard.addElement(el, context);
}, },
))] ))]
), ),
@ -495,8 +497,8 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
onDragEnd: (d) => node.requestFocus(), onDragEnd: (d) => node.requestFocus(),
childWhenDragging: Opacity(opacity: .5, childWhenDragging: Opacity(opacity: .5,
child: Padding( padding: const EdgeInsets.all(10), child: Padding( padding: const EdgeInsets.all(10),
child: Container( child: widget.itemWidget(e), alignment: Alignment.center, child: Container( alignment: Alignment.center,
constraints: BoxConstraints(maxHeight: realSize - 10, maxWidth: realSize - 10), ))), constraints: BoxConstraints(maxHeight: realSize - 10, maxWidth: realSize - 10), child: widget.itemWidget(e), ))),
feedback: Container( alignment: Alignment.center, constraints: BoxConstraints(maxHeight: realSize, maxWidth: realSize), feedback: Container( alignment: Alignment.center, constraints: BoxConstraints(maxHeight: realSize, maxWidth: realSize),
child: widget.itemWidget(e) ), child: widget.itemWidget(e) ),
child: InkWell( mouseCursor: SystemMouseCursors.grab, child: Padding( padding: const EdgeInsets.all(10), child: InkWell( mouseCursor: SystemMouseCursors.grab, child: Padding( padding: const EdgeInsets.all(10),
@ -548,6 +550,8 @@ class _DrawingArrowWidgetState extends State<DrawingArrowWidget> {
painter: ArrowPainter( painter: ArrowPainter(
fromID: DrawingArrow.instance.fromID, fromID: DrawingArrow.instance.fromID,
toID: "", toID: "",
env: DrawingArrow.instance.env,
infos: DrawingArrow.instance.infos,
params: DrawingArrow.instance.params, params: DrawingArrow.instance.params,
from: DrawingArrow.instance.from, from: DrawingArrow.instance.from,
to: DrawingArrow.instance.to, to: DrawingArrow.instance.to,
@ -878,9 +882,7 @@ class ChartWidgetState<T extends FlowData> extends State<ChartWidget> {
), ),
// Draw arrows // Draw arrows
for (int i = 0; i < widget.dashboard.elements.length; i++) for (int i = 0; i < widget.dashboard.elements.length; i++)
for (int n = 0; n < widget.dashboard.elements[i].next.length; n++) for (int n = 0; n < widget.dashboard.elements[i].next.length; n++)
DrawArrow( DrawArrow(
flow: this, flow: this,
key: UniqueKey(), key: UniqueKey(),
@ -889,6 +891,8 @@ class ChartWidgetState<T extends FlowData> extends State<ChartWidget> {
destElement: widget.dashboard.elements[widget.dashboard.findElementIndexById( destElement: widget.dashboard.elements[widget.dashboard.findElementIndexById(
widget.dashboard.elements[i].next[n].destElementId, widget.dashboard.elements[i].next[n].destElementId,
)], )],
env: widget.dashboard.elements[i].next[n].env,
infos: widget.dashboard.elements[i].next[n].infos,
arrowParams: widget.dashboard.elements[i].next[n].arrowParams, arrowParams: widget.dashboard.elements[i].next[n].arrowParams,
pivots: widget.dashboard.elements[i].next[n].pivots, pivots: widget.dashboard.elements[i].next[n].pivots,
), ),

View File

@ -37,17 +37,20 @@ class FlowChartMenuState extends State<FlowChartMenu> {
onTap: () { onTap: () {
widget.dashboard.name = "graph_${DateTime.now().toString().replaceAll(" ", "_").substring(0, DateTime.now().toString().length - 7)}"; widget.dashboard.name = "graph_${DateTime.now().toString().replaceAll(" ", "_").substring(0, DateTime.now().toString().length - 7)}";
widget.dashboard.isOpened = true; widget.dashboard.isOpened = true;
showDialog( if (!widget.dashboard.inDialog) {
barrierDismissible: false, widget.dashboard.inDialog = true;
context: context, builder: (context) { showDialog(
return AlertDialog( barrierDismissible: false,
titlePadding: EdgeInsets.zero, context: context, builder: (context) {
insetPadding: EdgeInsets.zero, return AlertDialog(
backgroundColor: Colors.white, titlePadding: EdgeInsets.zero,
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)), insetPadding: EdgeInsets.zero,
title: widget.chart.widget.flowChart.widget.onDashboardAlertOpened!( backgroundColor: Colors.white,
context, widget.dashboard)); shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)),
}); title: widget.chart.widget.flowChart.widget.onDashboardAlertOpened!(
context, widget.dashboard));
});
}
}, },
child: Icon(Icons.folder, color: Colors.white))))), child: Icon(Icons.folder, color: Colors.white))))),
])), ])),
@ -57,8 +60,6 @@ class FlowChartMenuState extends State<FlowChartMenu> {
), ),
child: Padding( padding: EdgeInsets.symmetric(horizontal: 10), child: Padding( padding: EdgeInsets.symmetric(horizontal: 10),
child: PopupMenuButton<DisplayEnum>( child: PopupMenuButton<DisplayEnum>(
child:
Row( children: [ Icon(Icons.fullscreen, color: Colors.white), Icon(Icons.arrow_drop_down, size: 10, color: Colors.white) ]),
initialValue: null, initialValue: null,
onSelected: (DisplayEnum value) { onSelected: (DisplayEnum value) {
if (value == DisplayEnum.MENU) { widget.dashboard.isMenu = !widget.dashboard.isMenu; } if (value == DisplayEnum.MENU) { widget.dashboard.isMenu = !widget.dashboard.isMenu; }
@ -83,7 +84,9 @@ class FlowChartMenuState extends State<FlowChartMenu> {
child: Text(widget.dashboard.isInfo ? 'hide info' : 'show info', textAlign: TextAlign.center,)) child: Text(widget.dashboard.isInfo ? 'hide info' : 'show info', textAlign: TextAlign.center,))
]), ]),
), ),
] ],
child:
Row( children: [ Icon(Icons.fullscreen, color: Colors.white), Icon(Icons.arrow_drop_down, size: 10, color: Colors.white) ])
),) ),)
) )
), ),

View File

@ -36,10 +36,10 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
widget.dashboard.elements[element.elementIndex!].next.removeAt(element.connIndex!); widget.dashboard.elements[element.elementIndex!].next.removeAt(element.connIndex!);
} }
return element.isSelected; return element.isSelected;
}); }, context);
widget.dashboard.removeElements((element) => element.isSelected); widget.dashboard.removeElements((element) => element.isSelected, context);
Future.delayed(Duration(milliseconds: 100), () { Future.delayed(Duration(milliseconds: 100), () {
widget.dashboard.chartKey.currentState?.setState(() { }); widget.dashboard.flutterChartKey.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)),
@ -51,7 +51,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.addElement(FlowElement.fromMap(widget.dashboard, sel.toMap())); widget.dashboard.addElement(FlowElement.fromMap(widget.dashboard, sel.toMap()), context);
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), () {
@ -67,6 +67,42 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
) : Container() ) : Container()
]) ])
)); ));
} else if (widget.isDashboardInfo && widget.dashboard.arrowsSelected.length == 1) {
w = Container(
width: 200,
height: widget.height,
color: widget.dashboard.midDashColor,
child: SingleChildScrollView( child: Column( children: [
widget.dashboard.infoItemWidget != null ?
widget.dashboard.infoLinkWidget!(widget.dashboard.arrowsSelected.first) : Container(),
widget.dashboard.arrowsSelected.isNotEmpty || widget.dashboard.elementSelected.isNotEmpty ? Container(
width: 200,
margin: EdgeInsets.only(top: 15),
decoration: BoxDecoration(border: Border(
top: BorderSide(color: Colors.grey, width: 1))),
child: Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
Tooltip( message: "remove",
child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () {
widget.dashboard.removeArrows((element) {
if (element.isSelected && element.elementIndex != null && element.connIndex != null) {
widget.dashboard.elements[element.elementIndex!].next.removeAt(element.connIndex!);
}
return element.isSelected;
}, context);
Future.delayed(Duration(milliseconds: 100), () {
widget.dashboard.flutterChartKey.currentState?.setState(() { });
});
}, child: Container( margin: EdgeInsets.all(10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.black, width: 1)),
width: 200, height: 30,
child: Icon(Icons.delete_outline, color: Colors.black),
))
),
])
) : Container()
])
));
} else if (widget.isDashboardInfo && widget.dashboard.infoWidget != null) { } else if (widget.isDashboardInfo && widget.dashboard.infoWidget != null) {
w = Container( w = Container(
width: 200, width: 200,
@ -520,10 +556,10 @@ class FlowChartSelectedMenuState extends State<FlowChartSelectedMenu> {
widget.dashboard.elements[element.elementIndex!].next.removeAt(element.connIndex!); widget.dashboard.elements[element.elementIndex!].next.removeAt(element.connIndex!);
} }
return element.isSelected; return element.isSelected;
}); }, context);
widget.dashboard.removeElements((element) => element.isSelected); widget.dashboard.removeElements((element) => element.isSelected, context);
Future.delayed(Duration(milliseconds: 100), () { Future.delayed(Duration(milliseconds: 100), () {
widget.dashboard.chartKey.currentState?.setState(() { }); widget.dashboard.flutterChartKey.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)),
@ -535,7 +571,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.addElement(FlowElement.fromMap(widget.dashboard, sel.toMap())); widget.dashboard.addElement(FlowElement.fromMap(widget.dashboard, sel.toMap()), context);
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), () {

View File

@ -211,6 +211,8 @@ class DrawingArrow extends ChangeNotifier {
/// Arrow parameters. /// Arrow parameters.
ArrowParams params = ArrowParams(); ArrowParams params = ArrowParams();
List<dynamic> env = [];
List<dynamic> infos = [];
String fromID = ""; String fromID = "";
String toID = ""; String toID = "";
@ -264,13 +266,16 @@ class DrawArrow extends StatefulWidget {
required this.srcElement, required this.srcElement,
required this.destElement, required this.destElement,
required List<Pivot> pivots, required List<Pivot> pivots,
this.infos = const [],
this.env = const [],
super.key, super.key,
ArrowParams? arrowParams, ArrowParams? arrowParams,
}) : arrowParams = arrowParams ?? ArrowParams(), }) : arrowParams = arrowParams ?? ArrowParams(),
pivots = PivotsNotifier(pivots); pivots = PivotsNotifier(pivots);
final int index; final int index;
List<dynamic> env = [];
List<dynamic> infos = [];
/// ///
final ArrowParams arrowParams; final ArrowParams arrowParams;
@ -340,6 +345,8 @@ class DrawArrowState extends State<DrawArrow> {
builder: (context) { builder: (context) {
var painter = ArrowPainter( var painter = ArrowPainter(
connIndex: widget.index, connIndex: widget.index,
infos: widget.infos,
env: widget.env,
elementIndex: widget.flow.widget.dashboard.elements.indexOf(widget.srcElement), elementIndex: widget.flow.widget.dashboard.elements.indexOf(widget.srcElement),
fromID: "${widget.srcElement.id}_${widget.index}", fromID: "${widget.srcElement.id}_${widget.index}",
toID: "${widget.destElement.id}_${widget.index}", toID: "${widget.destElement.id}_${widget.index}",
@ -351,8 +358,8 @@ 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.addArrows(painter); widget.flow.widget.dashboard.addArrows(painter, context);
widget.flow.widget.dashboard.saveDash(widget.flow.widget.dashboard.id); widget.flow.widget.dashboard.saveDash(widget.flow.widget.dashboard.id, context);
} else { } else {
var i = widget.flow.widget.dashboard.arrows.indexWhere( var i = widget.flow.widget.dashboard.arrows.indexWhere(
(element) => element.fromID == "${widget.srcElement.id}_${widget.index}"); (element) => element.fromID == "${widget.srcElement.id}_${widget.index}");
@ -390,7 +397,7 @@ class GraphParamsWidgetState extends State<GraphParamsWidget> {
child: Row(children: [ child: Row(children: [
IconButton(onPressed: () { IconButton(onPressed: () {
widget.comp.setState(() { widget.comp.setState(() {
widget.comp.widget.dashboard.removeArrows((el) => el.fromID == "${widget.element.id}${widget.index}"); widget.comp.widget.dashboard.removeArrows((el) => el.fromID == "${widget.element.id}${widget.index}", context);
widget.element.next.removeAt(widget.index); widget.element.next.removeAt(widget.index);
}); });
}, icon: Icon(Icons.delete)) }, icon: Icon(Icons.delete))
@ -421,13 +428,13 @@ class ArrowInfoWidgetState extends State<ArrowInfoWidget> {
/// [ArrowParams.startArrowPosition] and /// [ArrowParams.startArrowPosition] and
/// [ArrowParams.endArrowPosition] alignment. /// [ArrowParams.endArrowPosition] alignment.
class ArrowPainter extends CustomPainter { class ArrowPainter extends CustomPainter {
///
ArrowPainter({ ArrowPainter({
this.elementIndex, this.elementIndex,
this.connIndex, this.connIndex,
this.toID = "", this.toID = "",
this.fromID = "", this.fromID = "",
this.infos = const [],
this.env = const [],
this.isSelected = false, this.isSelected = false,
required this.params, required this.params,
this.from = Offset.zero, this.from = Offset.zero,
@ -435,9 +442,9 @@ class ArrowPainter extends CustomPainter {
List<Pivot>? pivots, List<Pivot>? pivots,
}) : pivots = pivots ?? []; }) : pivots = pivots ?? [];
/// List<dynamic> env;
List<dynamic> infos;
ArrowParams params; ArrowParams params;
///
String toID; String toID;
String fromID; String fromID;
Offset to; Offset to;
@ -462,6 +469,8 @@ class ArrowPainter extends CustomPainter {
final arrowAngle= 25 * math.pi / 180; final arrowAngle= 25 * math.pi / 180;
ArrowPainter deserialize(Map<String, dynamic> map) { ArrowPainter deserialize(Map<String, dynamic> map) {
infos = map['infos'];
env = map['env'];
params = ArrowParams.fromMap(map['params']); params = ArrowParams.fromMap(map['params']);
fromID = map['from']['id']; fromID = map['from']['id'];
toID = map['to']['id']; toID = map['to']['id'];
@ -469,9 +478,10 @@ class ArrowPainter extends CustomPainter {
to = Offset(map['to']['x'], map['to']['y']); to = Offset(map['to']['x'], map['to']['y']);
return this; return this;
} }
Map<String, dynamic> serialize() { Map<String, dynamic> serialize() {
Map<String, dynamic> graphElement = {}; Map<String, dynamic> graphElement = {};
graphElement['infos'] = infos;
graphElement['env'] = env;
graphElement['from'] = { "id" : fromID.split("_")[0], "x" : from.dx, "y" : from.dy }; graphElement['from'] = { "id" : fromID.split("_")[0], "x" : from.dx, "y" : from.dy };
graphElement['to'] = { "id" : toID.split("_")[0], "x" : to.dx, "y" : to.dy }; graphElement['to'] = { "id" : toID.split("_")[0], "x" : to.dx, "y" : to.dy };
graphElement['params'] = params.toMap(); graphElement['params'] = params.toMap();
@ -484,6 +494,8 @@ class ArrowPainter extends CustomPainter {
elementIndex: map['elementIndex'] as int, elementIndex: map['elementIndex'] as int,
toID: map['toID'] != null ? map['toID'] as String : "", toID: map['toID'] != null ? map['toID'] as String : "",
fromID: map['fromID'] as String, fromID: map['fromID'] as String,
infos: map['infos'] as List,
env: map['env'] as List,
isSelected: map['isSelected'] != null ? map['isSelected'] as bool : false, isSelected: map['isSelected'] != null ? map['isSelected'] as bool : false,
params: ArrowParams.fromMap(map['params']), params: ArrowParams.fromMap(map['params']),
from: Offset( from: Offset(
@ -511,6 +523,8 @@ class ArrowPainter extends CustomPainter {
"fromDy" : from.dy, "fromDy" : from.dy,
"toDx" : to.dx, "toDx" : to.dx,
"toDy" : to.dy, "toDy" : to.dy,
"infos" : infos,
"env" : env,
'pivots': pivots.map((e) => e.toMap()).toList(), 'pivots': pivots.map((e) => e.toMap()).toList(),
}; };
} }

View File

@ -151,12 +151,15 @@ class ElementWidgetState<T extends FlowData> extends State<ElementWidget> {
for (var element in widget.dashboard.elements) { for (var element in widget.dashboard.elements) {
element.isSelected = false; element.isSelected = false;
element.dashboard.chartKey.currentState?. setState(() { }); element.dashboard.chartKey.currentState?. setState(() { });
widget.dashboard.selectedMenuKey.currentState?. setState(() { });
} }
} }
widget.element.isSelected = !widget.element.isSelected; Future.delayed(Duration(milliseconds: 100), () {
for (var sel in widget.dashboard.arrows) { sel.isSelected = false; } DrawingArrow.instance.notifyListeners();
widget.dashboard.selectedMenuKey.currentState?. setState(() { }); widget.element.isSelected = !widget.element.isSelected;
Future.delayed(Duration(seconds: 1), () { DrawingArrow.instance.notifyListeners(); }); for (var sel in widget.dashboard.arrows) { sel.isSelected = false; }
widget.dashboard.selectedMenuKey.currentState?. setState(() { });
});
}); });
widget.onElementPressed?.call(context, tapLocation); widget.onElementPressed?.call(context, tapLocation);
}, },
@ -234,7 +237,7 @@ class ElementWidgetState<T extends FlowData> extends State<ElementWidget> {
sel.changePosition(sel.position + diff); sel.changePosition(sel.position + diff);
} }
} }
widget.dashboard.save!(widget.dashboard.id); widget.dashboard.saveDash(widget.dashboard.id, context);
}, },
), ),
), ),
@ -263,7 +266,7 @@ class ElementWidgetState<T extends FlowData> extends State<ElementWidget> {
child: Row( mainAxisAlignment: MainAxisAlignment.center, child: Row( mainAxisAlignment: MainAxisAlignment.center,
children: (!widget.isHovered ? [] : [ children: (!widget.isHovered ? [] : [
IconButton(tooltip: "remove element", onPressed: () { IconButton(tooltip: "remove element", onPressed: () {
widget.dashboard.removeElement(widget.element); widget.dashboard.removeElement(widget.element, context);
}, icon: Icon(Icons.delete_outline)), }, icon: Icon(Icons.delete_outline)),
IconButton(tooltip: "copy element", onPressed: () { IconButton(tooltip: "copy element", onPressed: () {
FlowElement<T> newElement = FlowElement<T>( FlowElement<T> newElement = FlowElement<T>(
@ -274,7 +277,7 @@ class ElementWidgetState<T extends FlowData> extends State<ElementWidget> {
size: widget.element.size, size: widget.element.size,
widget: widget.element.widget, widget: widget.element.widget,
); );
widget.dashboard.addElement(newElement); widget.dashboard.addElement(newElement, context);
}, icon: Icon(Icons.copy, size: 20)), }, icon: Icon(Icons.copy, size: 20)),
])) ]))
), ),