draft booking scheduler page

This commit is contained in:
mr 2024-11-13 08:12:37 +01:00
parent 1ca77b6611
commit 062042b590
15 changed files with 288 additions and 72 deletions

View File

@ -11,6 +11,7 @@ ARG ITEM_HOST="http://localhost:8087/oc"
ARG SCHEDULER_HOST="http://localhost:8090/oc"
ARG LOGS_HOST="http://localhost:3100"
ARG PEER_HOST="http://localhost:8093/oc"
ARG DATACENTER_HOST="http://localhost:8092/oc"
ARG COLLABORATIVE_AREA_HOST="http://localhost:8091/oc"
# define variables
ARG FLUTTER_SDK=/usr/local/flutter
@ -47,6 +48,7 @@ RUN flutter build web \
--dart-define=SCHEDULER_HOST=$SCHEDULER_HOST \
--dart-define=LOGS_HOST=$LOGS_HOST \
--dart-define=ITEM_HOST=$ITEM_HOST \
--dart-define=DATACENTER_HOST=$DATACENTER_HOST
# once heare the app will be compiled and ready to deploy
# use nginx to deploy

View File

@ -21,6 +21,7 @@ class HeaderConstants {
static GlobalKey<HeaderMenuWidgetState> headerKey = GlobalKey<HeaderMenuWidgetState>();
static final List<RouterItem> noHeader = [
AppRouter.scheduler,
AppRouter.compute,
AppRouter.shared,
AppRouter.workflowItem,
AppRouter.workflowIDItem,
@ -37,12 +38,16 @@ class HeaderConstants {
static setTitle(String? v) {
title = v;
Future.delayed(Duration(milliseconds: 100), () {
HeaderConstants.headerWidget?.setState(() {});
});
}
static setDescription(String? v) {
description = v;
Future.delayed(Duration(milliseconds: 100), () {
HeaderConstants.headerWidget?.setState(() {});
});
}
static bool isNoHeader(String route) {

View File

@ -63,13 +63,11 @@ class AuthService {
}
static Future<void> refresh(String accessToken, String username, Duration duration) async {
print("REFRESH ${duration}");
Future.delayed(duration, () {
service.post("/refresh", <String, dynamic> {
"access_token": accessToken,
"username": username
}, null).then((token) {
print("REFRESH ${token.data}");
if (token.code == 200 && token.data != null) {
localStorage.setItem('accessToken', token.data?.value['access_token']);
localStorage.setItem('username', username);

View File

@ -60,16 +60,14 @@ class AppRouter {
static final RouterItem catalogItem = RouterItem(description: "", help: "", route: "catalog/:id", factory: CatalogItemFactory(), args: ["id"]);
static final RouterItem catalog= RouterItem(icon: Icons.book_outlined, label: "catalog searcher", route: "catalog", factory: CatalogFactory());
static final RouterItem scheduler = RouterItem(icon: Icons.schedule, label: "scheduled tasks", route: "scheduler", factory: SchedulerFactory());
static final RouterItem compute = RouterItem(icon: Icons.dns_outlined, label: "my compute", route: "compute", factory: DatacenterFactory());
static final RouterItem shared = RouterItem(icon: Icons.share_rounded, label: "collaborative area", route: "shared", factory: SharedFactory());
static List<RouterItem> zones = [
catalog,
workflowItem,
scheduler,
RouterItem(icon: Icons.dns_outlined, label: "my compute", route: "compute",
description: "Manage & monitor your compute.", help: "not implemented for now",
factory: DatacenterFactory()),
compute,
RouterItem(icon: Icons.public_outlined, label: "localisations", route: "map", factory: MapFactory()),
shared,
workflowIDItem,

View File

@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/services/api_service.dart';
import 'package:oc_front/core/services/specialized_services/abstract_service.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/models/workflow.dart';
class BookingExecutionService extends AbstractService<WorkflowExecutions> {
@override APIService<WorkflowExecutions> service = APIService<WorkflowExecutions>(
baseURL: const String.fromEnvironment('DATACENTER_HOST', defaultValue: 'http://localhost:8080/datacenter')
);
@override String subPath = "/booking/";
@override Future<APIResponse<WorkflowExecutions>> search(BuildContext? context, List<String> words, Map<String, dynamic> params) {
print("${subPath}search/${words.join("/")}");
return service.get("${subPath}search/${words.join("/")}", false, context);
}
@override Future<APIResponse<WorkflowExecutions>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params) {
return throw UnimplementedError();
}
@override Future<APIResponse<WorkflowExecutions>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) {
return throw UnimplementedError();
}
}

View File

@ -69,7 +69,8 @@ class SimpleData extends SerializerDeserializer<SimpleData> {
class RawData extends SerializerDeserializer<RawData> {
RawData({ this.values = const []});
List<dynamic> values = [];
@override deserialize(dynamic json) { return RawData(values: json ?? []); }
@override deserialize(dynamic json) {
return RawData(values: json ?? []); }
@override Map<String, dynamic> serialize() => { };
}

View File

@ -817,7 +817,6 @@ class ComputeItem extends SerializerDeserializer<ComputeItem> implements Abstrac
gpus: json.containsKey("gpus") ? fromListJson(json["gpus"], GPU()) : [],
ram: json.containsKey("ram") ? RAM().deserialize(json["ram"]) : null,
);
print(w.technology);
if (w.logo != null) {
//
var image = Image.network(w.logo!);

View File

@ -1,6 +1,12 @@
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:oc_front/core/services/specialized_services/booking_service.dart';
import 'package:oc_front/main.dart';
import 'package:intl/intl.dart' as intl;
import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/pages/abstract_page.dart';
import 'package:oc_front/widgets/sheduler_items/schedule.dart';
class DatacenterFactory implements AbstractFactory {
@override GlobalKey getKey() { return key; }
@ -12,13 +18,202 @@ class DatacenterFactory implements AbstractFactory {
}
class ComputePageWidget extends StatefulWidget {
bool isList = true;
DateTime start = DateTime.now();
DateTime end = DateTime.now().add(const Duration(days: 180));
final BookingExecutionService _service = BookingExecutionService();
ComputePageWidget () : super(key: DatacenterFactory.key);
@override ComputePageWidgetState createState() => ComputePageWidgetState();
static Widget factory() { return ComputePageWidget(); }
}
class ComputePageWidgetState extends State<ComputePageWidget> {
List<Color> colors = [Colors.blue, Colors.orange, redColor, Colors.green, redColor];
List<String> titles = ["SCHEDULED", "RUNNING", "FAILURE", "SUCCESS", "FORGOTTEN"];
@override Widget build(BuildContext context) {
return Column( children: []);
return FutureBuilder(
future: widget._service.search(context, [
"${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}"}"], {}),
builder: (ctx, as) {
Map<String, List<WorkflowExecution>> data = {};
if (as.hasData && as.data!.data != null) {
for (var element in as.data!.data!.executions) {
if (element.executionData == null) { continue; }
DateTime dateTime = DateTime.parse(element.executionData!);
DateTime date = DateTime(dateTime.year, dateTime.month, dateTime.day);
var str = "${date.toIso8601String()}Z";
if (data[str] == null) { data[str] = []; }
data[str]!.add(element);
data[str]!.sort((a, b) => DateTime.parse(a.executionData!).compareTo(DateTime.parse(b.executionData!)));
}
}
GlobalKey<ScheduleWidgetState> k = GlobalKey<ScheduleWidgetState>();
for (var da in data.keys) {
for (var exec in data[da]!) {
String start = "";
String end = "";
try {
if (exec.endDate != null && exec.endDate != "") {
var startD = DateTime.parse(exec.executionData!);
var endD = DateTime.parse(exec.endDate!);
var diff = endD.difference(startD);
if (diff.inDays < 30) {
var rest = ((30 - diff.inDays) ~/ 2) - 1;
start = (startD.subtract(Duration(days: rest)).microsecondsSinceEpoch).toString();
end = (endD.add(Duration(days: rest)).microsecondsSinceEpoch).toString();
} else {
start = (startD.microsecondsSinceEpoch).toString();
end = (startD.add( const Duration(days: 29)).microsecondsSinceEpoch).toString();
}
} else {
start = (DateTime.parse(exec.executionData!).subtract( const Duration(days: 14)).microsecondsSinceEpoch).toString();
end = (DateTime.parse(exec.executionData!).add( const Duration(days: 14)).microsecondsSinceEpoch).toString();
}
} catch(e) { /* */ }
k.currentState?.setState(() { k.currentState?.widget.loading = true; });
}
}
return Column( children: [
Container( color: lightColor,
height: 50, width: getMainWidth(context),
child: Padding(padding: const EdgeInsets.symmetric(horizontal: 50),
child: Row( children: [
Padding(padding: const EdgeInsets.only(right: 30),
child: Tooltip( message: widget.isList ? "calendar view" : "list view",
child: InkWell( child: Icon( widget.isList ? Icons.calendar_month : Icons.list, color: Colors.white
, size: 25 ),
onTap: () {
widget.isList = !widget.isList;
k.currentState?.setState(() { k.currentState?.widget.isList = widget.isList; });
})
),
),
Container(padding: const EdgeInsets.only(left: 20),
width: getMainWidth(context) / 5,
height: 30,
child: DateTimeField(
validator: (value) {
return null;
},
resetIcon: null,
onShowPicker: (context, currentValue) async {
var date = await showDatePicker(
builder: (BuildContext context, Widget? child) {
Widget w = Theme(
data: ThemeData(
cardTheme: CardTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
dialogTheme: DialogTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
colorScheme: ColorScheme.light(
background: midColor,
tertiary: Colors.grey,
secondary: Colors.grey,
primary: Colors.black),
),
child: child ?? Container(),
);
return w;
},
context: context,
firstDate: DateTime(1900),
initialDate: widget.start,
lastDate: DateTime(2100)
);
return date;
},
format: intl.DateFormat('y-M-dd hh:mm:ss'),
initialValue: widget.start,
onChanged: (value) {
if (value == null) { return; }
setState(() { widget.start = value; });
},
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
fillColor: Colors.white,
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
alignLabelWithHint: false,
hintText: "enter start date...",
labelText: "",
errorStyle: TextStyle(fontSize: 0),
hintStyle: TextStyle(fontSize: 10),
labelStyle: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w500),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
)
),
Container(padding: const EdgeInsets.only(left: 20),
child: const Text("TO", style: TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w500))),
Container( padding: const EdgeInsets.only(left: 20, right: 20),
width: getMainWidth(context) / 5,
height: 30,
child: DateTimeField(
validator: (value) {
return null;
},
resetIcon: null,
onShowPicker: (context, currentValue) async {
var date = await showDatePicker(
builder: (BuildContext context, Widget? child) {
Widget w = Theme(
data: ThemeData(
cardTheme: CardTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
dialogTheme: DialogTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
colorScheme: ColorScheme.light(
background: midColor,
tertiary: Colors.grey,
secondary: Colors.grey,
primary: Colors.black),
),
child: child ?? Container(),
);
return w;
},
context: context,
firstDate: DateTime(1900),
initialDate: widget.end,
lastDate: DateTime(2100)
);
return date;
},
format: intl.DateFormat('y-M-dd hh:mm:ss'),
initialValue: widget.end,
onChanged: (value) {
if (value == null) { return; }
setState(() { widget.start = value; });
},
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
fillColor: Colors.white,
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
alignLabelWithHint: false,
hintText: "enter end date...",
labelText: "",
errorStyle: TextStyle(fontSize: 0),
hintStyle: TextStyle(fontSize: 10),
labelStyle: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w500),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
)
),
Tooltip( message: "refresh scheduler",
child: InkWell(
onTap: () => setState(() {}),
child: const Icon(Icons.refresh, color: Colors.white,),
),
)
]))
),
ScheduleWidget( key: k, data: data, start: widget.start, end : widget.end, isList: widget.isList, isBox: false)
]);
});
}
}

View File

@ -29,8 +29,8 @@ class SchedulerPageWidget extends StatefulWidget {
static Widget factory() { return SchedulerPageWidget(); }
}
class SchedulerPageWidgetState extends State<SchedulerPageWidget> {
List<Color> colors = [Colors.blue, Colors.orange, redColor, Colors.green];
List<String> titles = ["SCHEDULED", "RUNNING", "FAILURE", "SUCCESS"];
List<Color> colors = [Colors.blue, Colors.orange, redColor, Colors.green, redColor];
List<String> titles = ["SCHEDULED", "RUNNING", "FAILURE", "SUCCESS", "FORGOTTEN"];
@override Widget build(BuildContext context) {
return FutureBuilder(

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_box_transform/flutter_box_transform.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/logs.dart';
import 'package:oc_front/models/workflow.dart';
@ -19,7 +18,9 @@ class ScheduleWidget extends StatefulWidget {
bool loading = true;
Map<String, List<WorkflowExecution>> data;
bool isList = true;
ScheduleWidget ({ super.key, required this.data, required this.start, required this.end, this.isList = true, this.loading = false});
bool isBox = true;
ScheduleWidget ({ super.key, required this.data, required this.start, required this.end,
this.isBox =true, this.isList = true, this.loading = false});
@override ScheduleWidgetState createState() => ScheduleWidgetState();
}
String? selected;
@ -27,8 +28,8 @@ String? selectedReal;
class ScheduleWidgetState extends State<ScheduleWidget> {
String search = "";
String? level;
List<Color> colors = [Colors.blue, Colors.orange, redColor, Colors.green];
List<String> titles = ["SCHEDULED", "RUNNING", "FAILURE", "SUCCESS"];
List<Color> colors = [Colors.blue, Colors.orange, redColor, Colors.green, redColor];
List<String> titles = ["SCHEDULED", "RUNNING", "FAILURE", "SUCCESS", "FORGOTTEN"];
DateTime getFocusedDay() {
if (selected != null) { return DateTime.parse(selected!); }
@ -36,8 +37,8 @@ class ScheduleWidgetState extends State<ScheduleWidget> {
}
@override Widget build(BuildContext context) {
bool isInfo = getMainWidth(context) <= 600 && selected != null;
double w = selected != null ? getMainWidth(context) - menuSize : getMainWidth(context);
bool isInfo = getMainWidth(context) <= 600 && selected != null && widget.isBox;
double w = selected != null && widget.isBox ? getMainWidth(context) - menuSize : getMainWidth(context);
List<Widget> children = [];
if (selected != null) {
for (var wf in widget.data[selected!] ?? (<WorkflowExecution>[])) {
@ -94,7 +95,7 @@ class ScheduleWidgetState extends State<ScheduleWidget> {
: SchedulerCalendarWidget(data: widget.data, start: widget.start,
end: widget.end, parent: this, focusedDay: getFocusedDay(),)
),
fork.TransformableBox(
!widget.isBox ? Container() : fork.TransformableBox(
rect: rect, constraints: BoxConstraints(
maxWidth: isInfo ? getMainWidth(context) : (selected != null ? getMainWidth(context) / 2 : 0),
minWidth: selected != null ? 300 : 0),

View File

@ -24,8 +24,8 @@ class SchedulerCalendarWidget extends StatefulWidget {
@override SchedulerCalendarWidgetState createState() => SchedulerCalendarWidgetState();
}
class SchedulerCalendarWidgetState extends State<SchedulerCalendarWidget> {
List<Color> colors = [Colors.blue, Colors.orange, redColor, Colors.green];
List<String> titles = ["SCHEDULED", "RUNNING", "FAILURE", "SUCCESS"];
List<Color> colors = [Colors.blue, Colors.orange, redColor, Colors.green, redColor];
List<String> titles = ["SCHEDULED", "RUNNING", "FAILURE", "SUCCESS", "FORGOTTEN"];
bool isEvent(Map<String, List<WorkflowExecution>> data, DateTime day) {
if (data[day.toIso8601String()] == null || data[day.toIso8601String()]!.isEmpty) { return false; }
return true;

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_box_transform/flutter_box_transform.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/widgets/sheduler_items/schedule.dart';
@ -17,8 +18,8 @@ class SchedulerItemWidget extends StatefulWidget {
@override SchedulerItemWidgetState createState() => SchedulerItemWidgetState();
}
class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
List<Color> colors = [Colors.blue, Colors.orange, Colors.red, Colors.green];
List<String> titles = ["SCHEDULED", "RUNNING", "FAILURE", "SUCCESS"];
List<Color> colors = [Colors.blue, Colors.orange, redColor, Colors.green, redColor];
List<String> titles = ["SCHEDULED", "RUNNING", "FAILURE", "SUCCESS", "FORGOTTEN"];
@override Widget build(BuildContext context) {
List<Widget> children = [];
@ -42,8 +43,7 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50),
decoration: BoxDecoration(
border: selectedReal == ev.executionData ?
Border.all(color: const Color.fromRGBO(38, 166, 154, 1), width: 2)
: Border(top: BorderSide(color: Colors.grey.shade300)),
Border.all(color: lightColor, width: 2) : Border(top: BorderSide(color: midColor)),
),
child: Row(children: [
Container( width: 110,
@ -64,7 +64,7 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
)),
SizedBox( width: (widget.width - 340) / 2,
child: Padding(
padding: const EdgeInsets.only(left: 20),
padding: const EdgeInsets.only(left: 17),
child: Container( padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(d3 != null ? "killed at ${d3.day}/${d3.month}/${d3.year} ${d3.hour}:${d3.minute}:${d3.second}"
: "infinite run till process end", overflow: TextOverflow.ellipsis,
@ -72,7 +72,7 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
)),
SizedBox(
child: Padding(
padding: const EdgeInsets.only(left: 20),
padding: const EdgeInsets.only(left: 17),
child: Text("${d2.hour > 9 ? d2.hour : "0${d2.hour}"}:${d2.minute > 9 ? d2.minute : "0${d2.minute}"}:${d2.second > 9 ? d2.second : "0${d2.second}"}", overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 25,
color: Colors.grey, fontWeight: FontWeight.w500))))
@ -81,12 +81,12 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
}
var date = DateTime.parse(element);
children.add(Container(
decoration: const BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.black)),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.grey.shade300)),
),
child: Column( children: [ ExpansionTile(
enabled: widget.enabled,
shape: const ContinuousRectangleBorder(),
shape: const ContinuousRectangleBorder(side : BorderSide(color: Colors.grey)),
iconColor: Colors.grey,
initiallyExpanded: true,
title: SizedBox(
@ -110,11 +110,15 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
}
});
return Container(
decoration: BoxDecoration(
color: children.isNotEmpty ? Colors.transparent : midColor,
border: Border( right: BorderSide( color: children.isNotEmpty ? Colors.transparent : Colors.white) ),
),
alignment: children.isNotEmpty ? Alignment.topLeft : Alignment.center,
color: children.isNotEmpty ? Colors.transparent : Colors.white,
width: children.isNotEmpty ? MediaQuery.of(context).size.width : null,
height: MediaQuery.of(context).size.height - HeaderConstants.height - 50,
child: children.isNotEmpty ? SingleChildScrollView( child: Column( children: children)) : const Text("NO DATA FOUND", style: TextStyle(color: Colors.grey, fontSize: 30))
width: children.isNotEmpty ? getMainWidth(context) : null,
height: getMainHeight(context) - 50,
child: children.isNotEmpty ? SingleChildScrollView( child: Column( children: children)) : const Text(
"NO DATA FOUND", style: TextStyle(color: Colors.grey, fontSize: 30))
);
}
}

View File

@ -298,16 +298,21 @@ class HoverMenuState extends State<HoverMenu> {
}
}
var node = FocusNode();
class FlowChartState<T extends FlowData> extends State<FlowChart> {
var node = FocusNode();
bool isCtrl = false;
@override
void initState() {
node.requestFocus();
super.initState();
}
@override
void dispose() {
node.requestFocus();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (!widget.dashboard.isOpened && widget.onDashboardAlertOpened != null ) {
@ -331,7 +336,7 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
} else {
widget.dashboard.isOpened = true;
}
/// get dashboard position after first frame is drawn
/// get dashboard positi(event is KeyDownEvent || event is KeyRepeatEvent) &&on after first frame is drawn
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if (mounted) {
final object = context.findRenderObject() as RenderBox?;
@ -348,41 +353,18 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
if (kIsWeb) BrowserContextMenu.disableContextMenu();
return KeyboardListener(
focusNode: node,
autofocus: true,
onKeyEvent: (event) {
bool change = false;
/*if ((event is KeyDownEvent || event is KeyRepeatEvent) && event.logicalKey == LogicalKeyboardKey.arrowUp) {
change = true;
for (var el in widget.dashboard.elements) {
if (el.isSelected) {
el.position = Offset(el.position.dx, el.position.dy - 10);
if (event.logicalKey == LogicalKeyboardKey.controlLeft) {
isCtrl = event is KeyDownEvent || event is KeyRepeatEvent;
}
if (event is KeyDownEvent || event.logicalKey == LogicalKeyboardKey.keyZ && isCtrl) {
widget.dashboard.back();
}
if (event is KeyDownEvent || event.logicalKey == LogicalKeyboardKey.keyY && isCtrl) {
widget.dashboard.forward();
}
if ((event is KeyDownEvent || event is KeyRepeatEvent) && event.logicalKey == LogicalKeyboardKey.arrowDown) {
change = true;
for (var el in widget.dashboard.elements) {
if (el.isSelected) {
el.position = Offset(el.position.dx, el.position.dy + 10);
}
}
}
if ((event is KeyDownEvent || event is KeyRepeatEvent) && event.logicalKey == LogicalKeyboardKey.arrowLeft) {
change = true;
for (var el in widget.dashboard.elements) {
if (el.isSelected) {
el.position = Offset(el.position.dx - 10, el.position.dy);
}
}
}
if ((event is KeyDownEvent || event is KeyRepeatEvent) && event.logicalKey == LogicalKeyboardKey.arrowRight) {
change = true;
for (var el in widget.dashboard.elements) {
if (el.isSelected) {
el.position = Offset(el.position.dx + 10, el.position.dy);
}
}
}*/
if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.add) {
change = true;
for (var el in widget.dashboard.elementSelected) {
@ -413,6 +395,7 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
if (change) {
DrawingArrow.instance.notifyListeners();
widget.dashboard.chartKey.currentState?.setState(() { });
node.requestFocus();
}
},
child: ClipRect(
@ -455,7 +438,6 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
String newID = const Uuid().v4();
double ratio = 1;
if (e.getWidth() != null && e.getHeight() != null) {
print("${e.getWidth()} ${e.getHeight()}");
var h = e.getHeight()! < e.getWidth()! ? e.getHeight()! + 20 : e.getHeight()!;
var w = e.getWidth()! < e.getHeight()! ? e.getWidth()! + 20 : e.getWidth()!;
ratio = (h / w);

View File

@ -147,6 +147,12 @@ class ElementWidgetState<T extends FlowData> extends State<ElementWidget> {
secondaryTapDownPos = details.globalPosition,
onTap: () {
setState(() {
if (!(widget.dashboard.flutterChartKey.currentState?.isCtrl ?? true)) {
for (var element in widget.dashboard.elements) {
element.isSelected = false;
element.dashboard.chartKey.currentState?. setState(() { });
}
}
widget.element.isSelected = !widget.element.isSelected;
for (var sel in widget.dashboard.arrows) { sel.isSelected = false; }
widget.dashboard.selectedMenuKey.currentState?. setState(() { });

View File

@ -8,5 +8,6 @@ LOGS_HOST='http://localhost:3100'
PEER_HOST='http://localhost:8093/oc'
COLLABORATIVE_AREA_HOST='http://localhost:8091/oc'
AUTH_HOST='http://localhost:8094/oc'
DATACENTER_HOST='http://localhost:8092/oc'
flutter run -d linux --dart-define=AUTH_HOST=$AUTH_HOST --dart-define=WORKSPACE_HOST=$WORKSPACE_HOST --dart-define=WORKFLOW_HOST=$WORKFLOW_HOST --dart-define=PEER_HOST=$PEER_HOST --dart-define=COLLABORATIVE_AREA_HOST=$COLLABORATIVE_AREA_HOST --dart-define=SCHEDULER_HOST=$SCHEDULER_HOST --dart-define=LOGS_HOST=$LOGS_HOST --dart-define=ITEM_HOST=$ITEM_HOST
flutter run -d linux --dart-define=DATACENTER_HOST=$DATACENTER_HOST --dart-define=AUTH_HOST=$AUTH_HOST --dart-define=WORKSPACE_HOST=$WORKSPACE_HOST --dart-define=WORKFLOW_HOST=$WORKFLOW_HOST --dart-define=PEER_HOST=$PEER_HOST --dart-define=COLLABORATIVE_AREA_HOST=$COLLABORATIVE_AREA_HOST --dart-define=SCHEDULER_HOST=$SCHEDULER_HOST --dart-define=LOGS_HOST=$LOGS_HOST --dart-define=ITEM_HOST=$ITEM_HOST