UI debugging + git ignore
This commit is contained in:
parent
ceeebfc964
commit
1db9ef0794
44
.gitignore
vendored
Normal file
44
.gitignore
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.packages
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
@ -9,6 +9,8 @@ ARG WORKSPACE_HOST="https://localhost:8089"
|
|||||||
ARG WORKFLOW_HOST="https://localhost:8088"
|
ARG WORKFLOW_HOST="https://localhost:8088"
|
||||||
ARG SEARCH_HOST="https://localhost:49618"
|
ARG SEARCH_HOST="https://localhost:49618"
|
||||||
ARG ITEM_HOST="https://localhost:8087"
|
ARG ITEM_HOST="https://localhost:8087"
|
||||||
|
ARG SCHEDULER_HOST="http://localhost:8090"
|
||||||
|
ARG LOGS_HOST="http://localhost:3100"
|
||||||
|
|
||||||
# define variables
|
# define variables
|
||||||
ARG FLUTTER_SDK=/usr/local/flutter
|
ARG FLUTTER_SDK=/usr/local/flutter
|
||||||
|
@ -34,6 +34,7 @@ class WorkspaceLocal {
|
|||||||
_service.put(context, ws.id!, { "active" : true }, {});
|
_service.put(context, ws.id!, { "active" : true }, {});
|
||||||
}
|
}
|
||||||
if (ws.active == true && changeCurrent) {
|
if (ws.active == true && changeCurrent) {
|
||||||
|
print(ws.serialize());
|
||||||
current = ws.id;
|
current = ws.id;
|
||||||
}
|
}
|
||||||
fill();
|
fill();
|
||||||
@ -95,7 +96,13 @@ class WorkspaceLocal {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void changeWorkspaceByName(String name) {
|
||||||
|
var id = workspaces.entries.firstWhere((element) => element.value.name == "${name}_workspace").key;
|
||||||
|
changeWorkspace(id);
|
||||||
|
}
|
||||||
|
|
||||||
static void changeWorkspace(String id) {
|
static void changeWorkspace(String id) {
|
||||||
|
_service.put(null, id, { "active" : true }, {});
|
||||||
current = id;
|
current = id;
|
||||||
fill();
|
fill();
|
||||||
endDrawerKey.currentState?.setState(() {});
|
endDrawerKey.currentState?.setState(() {});
|
||||||
|
@ -61,7 +61,7 @@ class SearchWidgetState extends State<SearchWidget> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: MediaQuery.of(context).size.width - 400 > 0 ? MediaQuery.of(context).size.width - 300 - 100 : MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width - 400 > 0 ? MediaQuery.of(context).size.width - 300 - 100 : 200,
|
||||||
height: 50,
|
height: 50,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
|
@ -74,15 +74,10 @@ class APIService<T extends SerializerDeserializer> {
|
|||||||
Future<APIResponse<T>> _main(String url, dynamic body, String method, String succeed, bool force,
|
Future<APIResponse<T>> _main(String url, dynamic body, String method, String succeed, bool force,
|
||||||
BuildContext? context, Options? options) async {
|
BuildContext? context, Options? options) async {
|
||||||
var err = "";
|
var err = "";
|
||||||
|
|
||||||
if ((!force) && cache.containsKey(url) && cache[url] != null ) {
|
|
||||||
return cache[url]! as APIResponse<T>;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
_dio.options.headers["authorization"] = auth;
|
_dio.options.headers["authorization"] = auth;
|
||||||
_dio.interceptors.clear();
|
_dio.interceptors.clear();
|
||||||
var response = await _request(url, method, body, options);
|
var response = await _request(url, method, body, options);
|
||||||
print(response);
|
|
||||||
if (response.statusCode != null && response.statusCode! < 400) {
|
if (response.statusCode != null && response.statusCode! < 400) {
|
||||||
if (method == "delete") { cache.remove(url); return APIResponse<T>(); }
|
if (method == "delete") { cache.remove(url); return APIResponse<T>(); }
|
||||||
APIResponse<T> resp = APIResponse<T>().deserialize(response.data);
|
APIResponse<T> resp = APIResponse<T>().deserialize(response.data);
|
||||||
@ -117,6 +112,7 @@ class APIService<T extends SerializerDeserializer> {
|
|||||||
try {
|
try {
|
||||||
_dio.options.headers["authorization"] = auth;
|
_dio.options.headers["authorization"] = auth;
|
||||||
_dio.interceptors.clear();
|
_dio.interceptors.clear();
|
||||||
|
|
||||||
var response = await _request(url, method, body, null);
|
var response = await _request(url, method, body, null);
|
||||||
if (response.statusCode != null && response.statusCode! < 400) {
|
if (response.statusCode != null && response.statusCode! < 400) {
|
||||||
if (method == "delete") { cache.remove(url); return APIResponse<RawData>(); }
|
if (method == "delete") { cache.remove(url); return APIResponse<RawData>(); }
|
||||||
|
36
lib/core/services/specialized_services/check_service.dart
Normal file
36
lib/core/services/specialized_services/check_service.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:flutter/widgets.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 CheckService extends AbstractService<Check> {
|
||||||
|
@override APIService<Check> service = APIService<Check>(
|
||||||
|
baseURL: const String.fromEnvironment('WORKFLOW_HOST', defaultValue: 'http://localhost:8088')
|
||||||
|
);
|
||||||
|
@override String subPath = "/oc/workflow/check/";
|
||||||
|
|
||||||
|
Future<APIResponse<Check>> search(BuildContext? context, List<String> words, Map<String, dynamic> params) {
|
||||||
|
return service.get("$subPath${words.join("/")}", true, context);
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Future<APIResponse<RawData>> all(BuildContext? context) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Future<APIResponse<Check>> get(BuildContext? context, String id) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Future<APIResponse<Check>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Future<APIResponse<Check>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Future<APIResponse<Check>> delete(BuildContext? context, String id, Map<String, String> params) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
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/abstract.dart';
|
|
||||||
import 'package:oc_front/models/search.dart';
|
|
||||||
|
|
||||||
class ItemService<S extends AbstractItem, T extends SerializerDeserializer<S>> extends AbstractService<T> {
|
|
||||||
@override APIService<T> service = APIService<T>(
|
|
||||||
baseURL: const String.fromEnvironment('ITEM_HOST', defaultValue: 'http://localhost:8087')
|
|
||||||
);
|
|
||||||
@override String subPath = "/oc/${getTopic(S)}/";
|
|
||||||
}
|
|
@ -4,31 +4,31 @@ import 'package:oc_front/core/services/specialized_services/abstract_service.dar
|
|||||||
import 'package:oc_front/models/logs.dart';
|
import 'package:oc_front/models/logs.dart';
|
||||||
import 'package:oc_front/models/response.dart';
|
import 'package:oc_front/models/response.dart';
|
||||||
|
|
||||||
class LogsService extends AbstractService<LogResults> {
|
class LogsService extends AbstractService<LogsResult> {
|
||||||
@override APIService<LogResults> service = APIService<LogResults>(
|
@override APIService<LogsResult> service = APIService<LogsResult>(
|
||||||
baseURL: const String.fromEnvironment('SCHEDULER_HOST', defaultValue: 'http://localhost:3100')
|
baseURL: const String.fromEnvironment('LOGS_HOST', defaultValue: 'http://localhost:3100')
|
||||||
);
|
);
|
||||||
@override String subPath = "/loki/api/v1/";
|
@override String subPath = "/loki/api/v1/";
|
||||||
|
|
||||||
@override Future<APIResponse<LogResults>> search(BuildContext? context, List<String> words, Map<String, dynamic> params) {
|
@override Future<APIResponse<LogsResult>> search(BuildContext? context, List<String> words, Map<String, dynamic> params) {
|
||||||
List<String> v = [];
|
List<String> v = [];
|
||||||
for (var p in params.keys) {
|
for (var p in params.keys) {
|
||||||
if (p == "start" || p == "end") { continue; }
|
if (p == "start" || p == "end") { continue; }
|
||||||
v.add("$p=\"${params[p]}\"");
|
v.add("$p=\"${params[p]}\"");
|
||||||
}
|
}
|
||||||
return service.get("${subPath}query_range?query={${v.join(", ")}}&start=${params["start"]}&end=${params["end"]}", false, context);
|
return service.get("${subPath}query_range?query={${v.join(", ")}}&start=${params["start"].toString().substring(0, 10)}&end=${params["end"].toString().substring(0, 10)}", false, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override Future<APIResponse<LogResults>> get(BuildContext? context, String id) {
|
@override Future<APIResponse<LogsResult>> get(BuildContext? context, String id) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
@override Future<APIResponse<LogResults>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params) {
|
@override Future<APIResponse<LogsResult>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
@override Future<APIResponse<LogResults>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) {
|
@override Future<APIResponse<LogsResult>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
@override Future<APIResponse<LogResults>> delete(BuildContext? context, String id, Map<String, String> params) {
|
@override Future<APIResponse<LogsResult>> delete(BuildContext? context, String id, Map<String, String> params) {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,6 @@
|
|||||||
import 'package:oc_front/models/abstract.dart';
|
import 'package:oc_front/models/abstract.dart';
|
||||||
import 'package:json_string/json_string.dart';
|
import 'package:json_string/json_string.dart';
|
||||||
|
|
||||||
class LogResults extends SerializerDeserializer<LogResults> {
|
|
||||||
String? status;
|
|
||||||
LogsResult? data;
|
|
||||||
|
|
||||||
LogResults({
|
|
||||||
this.status,
|
|
||||||
this.data,
|
|
||||||
});
|
|
||||||
|
|
||||||
String getID() {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@override deserialize(dynamic json) {
|
|
||||||
try { json = json as Map<String, dynamic>;
|
|
||||||
} catch (e) { return LogResults(); }
|
|
||||||
return LogResults(
|
|
||||||
status: json.containsKey("status") ? json["status"] : "",
|
|
||||||
data: json.containsKey("data") ? LogsResult().deserialize(json["data"]) : null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@override Map<String, dynamic> serialize() {
|
|
||||||
return { };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LogsResult extends SerializerDeserializer<LogsResult> {
|
class LogsResult extends SerializerDeserializer<LogsResult> {
|
||||||
List<Logs> result;
|
List<Logs> result;
|
||||||
LogsResult({
|
LogsResult({
|
||||||
@ -45,7 +19,9 @@ class LogsResult extends SerializerDeserializer<LogsResult> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
@override Map<String, dynamic> serialize() {
|
@override Map<String, dynamic> serialize() {
|
||||||
return { };
|
return {
|
||||||
|
"result": toListJson(result),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +30,7 @@ class Logs extends SerializerDeserializer<Logs> {
|
|||||||
List<Log> logs = [];
|
List<Log> logs = [];
|
||||||
Logs({
|
Logs({
|
||||||
this.level,
|
this.level,
|
||||||
|
this.logs = const [],
|
||||||
});
|
});
|
||||||
|
|
||||||
String getID() {
|
String getID() {
|
||||||
@ -65,11 +42,13 @@ class Logs extends SerializerDeserializer<Logs> {
|
|||||||
} catch (e) { return Logs(); }
|
} catch (e) { return Logs(); }
|
||||||
return Logs(
|
return Logs(
|
||||||
level: json.containsKey("stream") && (json["stream"] as Map<String, dynamic>).containsKey("level") ? json["stream"]["level"] : "",
|
level: json.containsKey("stream") && (json["stream"] as Map<String, dynamic>).containsKey("level") ? json["stream"]["level"] : "",
|
||||||
|
logs: json.containsKey("values") ? fromListJson(json["values"], Log()) : [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@override Map<String, dynamic> serialize() {
|
@override Map<String, dynamic> serialize() {
|
||||||
return { };
|
return {
|
||||||
|
"level": level,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,10 +57,12 @@ class Log extends SerializerDeserializer<Log> {
|
|||||||
String? message;
|
String? message;
|
||||||
|
|
||||||
String? level;
|
String? level;
|
||||||
|
String? rawMessage;
|
||||||
Map<String, dynamic> map = {};
|
Map<String, dynamic> map = {};
|
||||||
Log({
|
Log({
|
||||||
this.timestamp,
|
this.timestamp,
|
||||||
this.message,
|
this.message,
|
||||||
|
this.rawMessage,
|
||||||
this.level
|
this.level
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -97,7 +78,7 @@ class Log extends SerializerDeserializer<Log> {
|
|||||||
if (j["Status"] == "Pending") {
|
if (j["Status"] == "Pending") {
|
||||||
jsonString = "${j["Name"]} : [${j["Namespace"]}] Status: ${j["Status"]}... \nCreated at ${j["Created"].toString().replaceAllMapped(RegExp(r'\(\w+\)'), (match) { return ''; }).replaceAllMapped(RegExp(r'\+\w+'), (match) { return ''; })}";
|
jsonString = "${j["Name"]} : [${j["Namespace"]}] Status: ${j["Status"]}... \nCreated at ${j["Created"].toString().replaceAllMapped(RegExp(r'\(\w+\)'), (match) { return ''; }).replaceAllMapped(RegExp(r'\+\w+'), (match) { return ''; })}";
|
||||||
} else {
|
} else {
|
||||||
jsonString = "${j["Name"]} : [${j["Namespace"]}] ${j["Status"]} ${j["Progress"]} (${j["Duration"].toString().replaceAll("seconds", "s")})\nStarted at ${j["Created"].toString().replaceAllMapped(RegExp(r'\(\w+\)'), (match) { return ''; }).replaceAllMapped(RegExp(r'\+\w+'), (match) { return ''; })}";
|
jsonString = "${j["Name"]} : [${j["Namespace"]}] ${j["Status"]} ${j["Progress"]} (${j["Duration"].toString()})\nCreated at ${j["Created"].toString().replaceAllMapped(RegExp(r'\(\w+\)'), (match) { return ''; }).replaceAllMapped(RegExp(r'\+\w+'), (match) { return ''; })}; Started at ${j["Created"].toString().replaceAllMapped(RegExp(r'\(\w+\)'), (match) { return ''; }).replaceAllMapped(RegExp(r'\+\w+'), (match) { return ''; })}";
|
||||||
}
|
}
|
||||||
} on JsonFormatException catch (e) { /* */ }
|
} on JsonFormatException catch (e) { /* */ }
|
||||||
message = jsonString;
|
message = jsonString;
|
||||||
@ -107,10 +88,13 @@ class Log extends SerializerDeserializer<Log> {
|
|||||||
@override deserialize(dynamic json) {
|
@override deserialize(dynamic json) {
|
||||||
try { json = json as List<dynamic>;
|
try { json = json as List<dynamic>;
|
||||||
} catch (e) { return Log(); }
|
} catch (e) { return Log(); }
|
||||||
return Log(
|
var l = Log(
|
||||||
timestamp: json.isNotEmpty ? DateTime.parse(json[0]) : null,
|
timestamp: json.isNotEmpty ? DateTime.fromMillisecondsSinceEpoch(int.parse(json[0]) ~/ 1000) : null,
|
||||||
message: json.length > 1 ? getMessage(json[1].toString()) : null,
|
message: json.length > 1 ? getMessage(json[1].toString()) : null,
|
||||||
|
rawMessage : json.length > 1 ? json[1].toString() : null,
|
||||||
);
|
);
|
||||||
|
l.getMessage(l.message ?? "");
|
||||||
|
return l;
|
||||||
}
|
}
|
||||||
@override Map<String, dynamic> serialize() { return { }; }
|
@override Map<String, dynamic> serialize() { return { }; }
|
||||||
}
|
}
|
@ -15,7 +15,8 @@ Map<Type, SerializerDeserializer> refs = <Type, SerializerDeserializer> {
|
|||||||
Workflow: Workflow(),
|
Workflow: Workflow(),
|
||||||
Resource: Resource(),
|
Resource: Resource(),
|
||||||
WorkflowExecutions: WorkflowExecutions(),
|
WorkflowExecutions: WorkflowExecutions(),
|
||||||
LogResults: LogResults(),
|
LogsResult: LogsResult(),
|
||||||
|
Check: Check(),
|
||||||
};
|
};
|
||||||
|
|
||||||
class APIResponse<T extends SerializerDeserializer> {
|
class APIResponse<T extends SerializerDeserializer> {
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
|
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
|
||||||
import 'package:oc_front/models/abstract.dart';
|
import 'package:oc_front/models/abstract.dart';
|
||||||
|
|
||||||
@ -248,10 +251,21 @@ class ProcessingItem extends SerializerDeserializer<ProcessingItem> implements A
|
|||||||
@override String getName() {
|
@override String getName() {
|
||||||
return name ?? "";
|
return name ?? "";
|
||||||
}
|
}
|
||||||
|
double? width;
|
||||||
|
double? height;
|
||||||
|
@override
|
||||||
|
double? getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
double? getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
@override deserialize(dynamic json) {
|
@override deserialize(dynamic json) {
|
||||||
try { json = json as Map<String, dynamic>;
|
try { json = json as Map<String, dynamic>;
|
||||||
} catch (e) { return ProcessingItem(); }
|
} catch (e) { return ProcessingItem(); }
|
||||||
return ProcessingItem(
|
var w = ProcessingItem(
|
||||||
id: json.containsKey("id") ? json["id"] : null,
|
id: json.containsKey("id") ? json["id"] : null,
|
||||||
name: json.containsKey("name") ? json["name"] : null,
|
name: json.containsKey("name") ? json["name"] : null,
|
||||||
logo: json.containsKey("logo") ? json["logo"] : null,
|
logo: json.containsKey("logo") ? json["logo"] : null,
|
||||||
@ -274,6 +288,19 @@ class ProcessingItem extends SerializerDeserializer<ProcessingItem> implements A
|
|||||||
scallingModel: json.containsKey("scalling_model") ? json["scalling_model"] : null,
|
scallingModel: json.containsKey("scalling_model") ? json["scalling_model"] : null,
|
||||||
diskIO: json.containsKey("disk_io") ? json["disk_io"] : null,
|
diskIO: json.containsKey("disk_io") ? json["disk_io"] : null,
|
||||||
);
|
);
|
||||||
|
if (w.logo != null) {
|
||||||
|
//
|
||||||
|
var image = Image.network(w.logo!);
|
||||||
|
image.image
|
||||||
|
.resolve(const ImageConfiguration())
|
||||||
|
.addListener(
|
||||||
|
ImageStreamListener(
|
||||||
|
(ImageInfo info, bool _) {
|
||||||
|
w.width = info.image.width.toDouble();
|
||||||
|
w.height = info.image.height.toDouble();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return w;
|
||||||
}
|
}
|
||||||
@override Map<String, dynamic> serialize() => {
|
@override Map<String, dynamic> serialize() => {
|
||||||
"id": id,
|
"id": id,
|
||||||
@ -336,13 +363,24 @@ class WorkflowItem extends SerializerDeserializer<WorkflowItem> implements Abstr
|
|||||||
return id ?? "";
|
return id ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double? width;
|
||||||
|
double? height;
|
||||||
|
@override
|
||||||
|
double? getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
double? getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
@override String getName() {
|
@override String getName() {
|
||||||
return name ?? "";
|
return name ?? "";
|
||||||
}
|
}
|
||||||
@override deserialize(dynamic json) {
|
@override deserialize(dynamic json) {
|
||||||
try { json = json as Map<String, dynamic>;
|
try { json = json as Map<String, dynamic>;
|
||||||
} catch (e) { return WorkflowItem(); }
|
} catch (e) { return WorkflowItem(); }
|
||||||
return WorkflowItem(
|
var w = WorkflowItem(
|
||||||
id: json.containsKey("id") ? json["id"] : null,
|
id: json.containsKey("id") ? json["id"] : null,
|
||||||
name: json.containsKey("name") ? json["name"] : null,
|
name: json.containsKey("name") ? json["name"] : null,
|
||||||
logo: json.containsKey("logo") ? json["logo"] : null,
|
logo: json.containsKey("logo") ? json["logo"] : null,
|
||||||
@ -358,6 +396,19 @@ class WorkflowItem extends SerializerDeserializer<WorkflowItem> implements Abstr
|
|||||||
model: json.containsKey("resource_model") ? ResourceModel().deserialize(json["resource_model"]) : null,
|
model: json.containsKey("resource_model") ? ResourceModel().deserialize(json["resource_model"]) : null,
|
||||||
workflowID: json.containsKey("workflow_id") ? json["workflow_id"] : null,
|
workflowID: json.containsKey("workflow_id") ? json["workflow_id"] : null,
|
||||||
);
|
);
|
||||||
|
if (w.logo != null) {
|
||||||
|
//
|
||||||
|
var image = Image.network(w.logo!);
|
||||||
|
image.image
|
||||||
|
.resolve(const ImageConfiguration())
|
||||||
|
.addListener(
|
||||||
|
ImageStreamListener(
|
||||||
|
(ImageInfo info, bool _) {
|
||||||
|
w.width = info.image.width.toDouble();
|
||||||
|
w.height = info.image.height.toDouble();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return w;
|
||||||
}
|
}
|
||||||
@override Map<String, dynamic> serialize() => {
|
@override Map<String, dynamic> serialize() => {
|
||||||
"id": id,
|
"id": id,
|
||||||
@ -420,10 +471,21 @@ class DataItem extends SerializerDeserializer<DataItem> implements AbstractItem<
|
|||||||
@override String getID() {
|
@override String getID() {
|
||||||
return id ?? "";
|
return id ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double? width;
|
||||||
|
double? height;
|
||||||
|
@override
|
||||||
|
double? getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
double? getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
@override deserialize(dynamic json) {
|
@override deserialize(dynamic json) {
|
||||||
try { json = json as Map<String, dynamic>;
|
try { json = json as Map<String, dynamic>;
|
||||||
} catch (e) { return DataItem(); }
|
} catch (e) { return DataItem(); }
|
||||||
return DataItem(
|
var w = DataItem(
|
||||||
id: json.containsKey("id") ? json["id"] : null,
|
id: json.containsKey("id") ? json["id"] : null,
|
||||||
name: json.containsKey("name") ? json["name"] : null,
|
name: json.containsKey("name") ? json["name"] : null,
|
||||||
logo: json.containsKey("logo") ? json["logo"] : null,
|
logo: json.containsKey("logo") ? json["logo"] : null,
|
||||||
@ -441,6 +503,19 @@ class DataItem extends SerializerDeserializer<DataItem> implements AbstractItem<
|
|||||||
dataType: json.containsKey("data_type") ? json["data_type"] : null,
|
dataType: json.containsKey("data_type") ? json["data_type"] : null,
|
||||||
exemple: json.containsKey("exemple") ? json["exemple"] : null,
|
exemple: json.containsKey("exemple") ? json["exemple"] : null,
|
||||||
);
|
);
|
||||||
|
if (w.logo != null) {
|
||||||
|
//
|
||||||
|
var image = Image.network(w.logo!);
|
||||||
|
image.image
|
||||||
|
.resolve(const ImageConfiguration())
|
||||||
|
.addListener(
|
||||||
|
ImageStreamListener(
|
||||||
|
(ImageInfo info, bool _) {
|
||||||
|
w.width = info.image.width.toDouble();
|
||||||
|
w.height = info.image.height.toDouble();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return w;
|
||||||
}
|
}
|
||||||
@override Map<String, dynamic> serialize() => {
|
@override Map<String, dynamic> serialize() => {
|
||||||
"id": id,
|
"id": id,
|
||||||
@ -505,10 +580,21 @@ class DataCenterItem extends SerializerDeserializer<DataCenterItem> implements A
|
|||||||
@override String getName() {
|
@override String getName() {
|
||||||
return name ?? "";
|
return name ?? "";
|
||||||
}
|
}
|
||||||
|
double? width;
|
||||||
|
double? height;
|
||||||
|
@override
|
||||||
|
double? getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
double? getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
@override deserialize(dynamic json) {
|
@override deserialize(dynamic json) {
|
||||||
try { json = json as Map<String, dynamic>;
|
try { json = json as Map<String, dynamic>;
|
||||||
} catch (e) { return DataCenterItem(); }
|
} catch (e) { return DataCenterItem(); }
|
||||||
return DataCenterItem(
|
|
||||||
|
var w = DataCenterItem(
|
||||||
id: json.containsKey("id") ? json["id"] : null,
|
id: json.containsKey("id") ? json["id"] : null,
|
||||||
name: json.containsKey("name") ? json["name"] : null,
|
name: json.containsKey("name") ? json["name"] : null,
|
||||||
logo: json.containsKey("logo") ? json["logo"] : null,
|
logo: json.containsKey("logo") ? json["logo"] : null,
|
||||||
@ -526,6 +612,19 @@ class DataCenterItem extends SerializerDeserializer<DataCenterItem> implements A
|
|||||||
gpus: json.containsKey("gpus") ? fromListJson(json["gpus"], GPU()) : [],
|
gpus: json.containsKey("gpus") ? fromListJson(json["gpus"], GPU()) : [],
|
||||||
ram: json.containsKey("ram") ? RAM().deserialize(json["ram"]) : null,
|
ram: json.containsKey("ram") ? RAM().deserialize(json["ram"]) : null,
|
||||||
);
|
);
|
||||||
|
if (w.logo != null) {
|
||||||
|
//
|
||||||
|
var image = Image.network(w.logo!);
|
||||||
|
image.image
|
||||||
|
.resolve(const ImageConfiguration())
|
||||||
|
.addListener(
|
||||||
|
ImageStreamListener(
|
||||||
|
(ImageInfo info, bool _) {
|
||||||
|
w.width = info.image.width.toDouble();
|
||||||
|
w.height = info.image.height.toDouble();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return w;
|
||||||
}
|
}
|
||||||
@override Map<String, dynamic> serialize() => {
|
@override Map<String, dynamic> serialize() => {
|
||||||
"id": id,
|
"id": id,
|
||||||
@ -680,10 +779,20 @@ class StorageItem extends SerializerDeserializer<StorageItem> implements Abstrac
|
|||||||
@override String getID() {
|
@override String getID() {
|
||||||
return id ?? "";
|
return id ?? "";
|
||||||
}
|
}
|
||||||
|
double? width;
|
||||||
|
double? height;
|
||||||
|
@override
|
||||||
|
double? getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
double? getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
@override deserialize(dynamic json) {
|
@override deserialize(dynamic json) {
|
||||||
try { json = json as Map<String, dynamic>;
|
try { json = json as Map<String, dynamic>;
|
||||||
} catch (e) { return StorageItem(); }
|
} catch (e) { return StorageItem(); }
|
||||||
return StorageItem(
|
var w = StorageItem(
|
||||||
id: json.containsKey("id") ? json["id"] : null,
|
id: json.containsKey("id") ? json["id"] : null,
|
||||||
name: json.containsKey("name") ? json["name"] : null,
|
name: json.containsKey("name") ? json["name"] : null,
|
||||||
logo: json.containsKey("logo") ? json["logo"] : null,
|
logo: json.containsKey("logo") ? json["logo"] : null,
|
||||||
@ -705,6 +814,19 @@ class StorageItem extends SerializerDeserializer<StorageItem> implements Abstrac
|
|||||||
redundancy: json.containsKey("redundancy") ? json["redundancy"] : null,
|
redundancy: json.containsKey("redundancy") ? json["redundancy"] : null,
|
||||||
throughput: json.containsKey("throughput") ? json["throughput"] : null,
|
throughput: json.containsKey("throughput") ? json["throughput"] : null,
|
||||||
);
|
);
|
||||||
|
if (w.logo != null) {
|
||||||
|
//
|
||||||
|
var image = Image.network(w.logo!);
|
||||||
|
image.image
|
||||||
|
.resolve(const ImageConfiguration())
|
||||||
|
.addListener(
|
||||||
|
ImageStreamListener(
|
||||||
|
(ImageInfo info, bool _) {
|
||||||
|
w.width = info.image.width.toDouble();
|
||||||
|
w.height = info.image.height.toDouble();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return w;
|
||||||
}
|
}
|
||||||
@override Map<String, dynamic> serialize() => {
|
@override Map<String, dynamic> serialize() => {
|
||||||
"id": id,
|
"id": id,
|
||||||
|
@ -3,8 +3,31 @@ import 'package:flutter_colorpicker/flutter_colorpicker.dart';
|
|||||||
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
|
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
|
||||||
import 'package:oc_front/core/models/workspace_local.dart';
|
import 'package:oc_front/core/models/workspace_local.dart';
|
||||||
import 'package:oc_front/models/abstract.dart';
|
import 'package:oc_front/models/abstract.dart';
|
||||||
|
import 'package:oc_front/models/logs.dart';
|
||||||
import 'package:oc_front/models/search.dart';
|
import 'package:oc_front/models/search.dart';
|
||||||
|
|
||||||
|
class Check extends SerializerDeserializer<Check> {
|
||||||
|
bool is_available = false;
|
||||||
|
|
||||||
|
Check({
|
||||||
|
this.is_available = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override deserialize(dynamic json) {
|
||||||
|
try { json = json as Map<String, dynamic>;
|
||||||
|
} catch (e) { return Check(); }
|
||||||
|
return Check(
|
||||||
|
is_available: json.containsKey("is_available") ? json["is_available"] : false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@override Map<String, dynamic> serialize() {
|
||||||
|
return {
|
||||||
|
"is_available": is_available,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class WorkflowExecutions extends SerializerDeserializer<WorkflowExecutions> {
|
class WorkflowExecutions extends SerializerDeserializer<WorkflowExecutions> {
|
||||||
List<WorkflowExecution> executions = [];
|
List<WorkflowExecution> executions = [];
|
||||||
String? executionData;
|
String? executionData;
|
||||||
@ -37,6 +60,8 @@ class WorkflowExecution extends SerializerDeserializer<WorkflowExecution> {
|
|||||||
int? status;
|
int? status;
|
||||||
String? workflowId;
|
String? workflowId;
|
||||||
|
|
||||||
|
List<Log>? logs;
|
||||||
|
|
||||||
|
|
||||||
WorkflowExecution({
|
WorkflowExecution({
|
||||||
this.id,
|
this.id,
|
||||||
@ -83,6 +108,7 @@ class Workflow extends SerializerDeserializer<Workflow> {
|
|||||||
List<dynamic> workflows;
|
List<dynamic> workflows;
|
||||||
Graph? graph;
|
Graph? graph;
|
||||||
Scheduler? schedule;
|
Scheduler? schedule;
|
||||||
|
bool scheduleActive = false;
|
||||||
|
|
||||||
Workflow({
|
Workflow({
|
||||||
this.id,
|
this.id,
|
||||||
@ -94,6 +120,7 @@ class Workflow extends SerializerDeserializer<Workflow> {
|
|||||||
this.workflows = const [],
|
this.workflows = const [],
|
||||||
this.graph,
|
this.graph,
|
||||||
this.schedule,
|
this.schedule,
|
||||||
|
this.scheduleActive = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
String getID() {
|
String getID() {
|
||||||
@ -110,6 +137,7 @@ class Workflow extends SerializerDeserializer<Workflow> {
|
|||||||
processing: json.containsKey("processings") ? json["processings"] : [],
|
processing: json.containsKey("processings") ? json["processings"] : [],
|
||||||
datacenter: json.containsKey("datacenters") ? json["datacenters"] : [],
|
datacenter: json.containsKey("datacenters") ? json["datacenters"] : [],
|
||||||
data: json.containsKey("datas") ? json["datas"] : [],
|
data: json.containsKey("datas") ? json["datas"] : [],
|
||||||
|
scheduleActive: json.containsKey("schedule_active") ? json["schedule_active"] : false,
|
||||||
storage: json.containsKey("storages") ? json["storages"] : [],
|
storage: json.containsKey("storages") ? json["storages"] : [],
|
||||||
graph: json.containsKey("graph") ? Graph().deserialize(json["graph"]) : null,
|
graph: json.containsKey("graph") ? Graph().deserialize(json["graph"]) : null,
|
||||||
schedule: json.containsKey("schedule") ? Scheduler().deserialize(json["schedule"]) : null,
|
schedule: json.containsKey("schedule") ? Scheduler().deserialize(json["schedule"]) : null,
|
||||||
@ -124,6 +152,7 @@ class Workflow extends SerializerDeserializer<Workflow> {
|
|||||||
"storages": storage,
|
"storages": storage,
|
||||||
"processings": processing,
|
"processings": processing,
|
||||||
"workflows": workflows,
|
"workflows": workflows,
|
||||||
|
"schedule_active": scheduleActive,
|
||||||
"schedule": schedule?.serialize(),
|
"schedule": schedule?.serialize(),
|
||||||
};
|
};
|
||||||
if (graph != null) {
|
if (graph != null) {
|
||||||
@ -135,6 +164,7 @@ class Workflow extends SerializerDeserializer<Workflow> {
|
|||||||
void fromDashboard(Map<String, dynamic> j) {
|
void fromDashboard(Map<String, dynamic> j) {
|
||||||
id = j["id"];
|
id = j["id"];
|
||||||
name = j["name"];
|
name = j["name"];
|
||||||
|
scheduleActive = j["schedule_active"];
|
||||||
if (j.containsKey("graph")) {
|
if (j.containsKey("graph")) {
|
||||||
graph = Graph();
|
graph = Graph();
|
||||||
graph!.fromDashboard(j["graph"]);
|
graph!.fromDashboard(j["graph"]);
|
||||||
@ -149,6 +179,7 @@ class Workflow extends SerializerDeserializer<Workflow> {
|
|||||||
"id": id,
|
"id": id,
|
||||||
"name": name,
|
"name": name,
|
||||||
"graph": graph?.toDashboard(),
|
"graph": graph?.toDashboard(),
|
||||||
|
"schedule_active": scheduleActive,
|
||||||
"schedule": schedule?.toDashboard(),
|
"schedule": schedule?.toDashboard(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -160,13 +191,15 @@ class Scheduler extends SerializerDeserializer<Scheduler> {
|
|||||||
String? cron;
|
String? cron;
|
||||||
DateTime? start;
|
DateTime? start;
|
||||||
DateTime? end;
|
DateTime? end;
|
||||||
|
int? mode;
|
||||||
|
|
||||||
Scheduler({
|
Scheduler({
|
||||||
this.id,
|
this.id,
|
||||||
this.name,
|
this.name,
|
||||||
this.cron,
|
this.cron,
|
||||||
this.start,
|
this.start,
|
||||||
this.end
|
this.end,
|
||||||
|
this.mode,
|
||||||
});
|
});
|
||||||
|
|
||||||
void fromDashboard(Map<String, dynamic> j) {
|
void fromDashboard(Map<String, dynamic> j) {
|
||||||
@ -177,13 +210,14 @@ class Scheduler extends SerializerDeserializer<Scheduler> {
|
|||||||
if (j.containsKey("end") && j["end"] != null) {
|
if (j.containsKey("end") && j["end"] != null) {
|
||||||
end = DateTime.parse(j["end"]);
|
end = DateTime.parse(j["end"]);
|
||||||
}
|
}
|
||||||
|
mode = int.parse(j["mode"].toString());
|
||||||
}
|
}
|
||||||
Map<String, dynamic> toDashboard() {
|
Map<String, dynamic> toDashboard() {
|
||||||
return {
|
return {
|
||||||
"id": id,
|
"id": id,
|
||||||
"name": name,
|
"name": name,
|
||||||
"cron": cron,
|
"cron": cron,
|
||||||
|
"mode": int.parse(mode.toString()),
|
||||||
"start": start?.toIso8601String(),
|
"start": start?.toIso8601String(),
|
||||||
"end": end?.toIso8601String(),
|
"end": end?.toIso8601String(),
|
||||||
};
|
};
|
||||||
@ -196,6 +230,7 @@ class Scheduler extends SerializerDeserializer<Scheduler> {
|
|||||||
id: json.containsKey("id") ? json["id"] : null,
|
id: json.containsKey("id") ? json["id"] : null,
|
||||||
name: json.containsKey("name") ? json["name"] : "",
|
name: json.containsKey("name") ? json["name"] : "",
|
||||||
cron: json.containsKey("cron") ? json["cron"] : "",
|
cron: json.containsKey("cron") ? json["cron"] : "",
|
||||||
|
mode: json.containsKey("mode") ? json["mode"] : "",
|
||||||
start: json.containsKey("start") ? DateTime.parse(json["start"]) : null,
|
start: json.containsKey("start") ? DateTime.parse(json["start"]) : null,
|
||||||
end: json.containsKey("end") ? DateTime.parse(json["end"]) : null,
|
end: json.containsKey("end") ? DateTime.parse(json["end"]) : null,
|
||||||
);
|
);
|
||||||
@ -204,6 +239,7 @@ class Scheduler extends SerializerDeserializer<Scheduler> {
|
|||||||
"id": id,
|
"id": id,
|
||||||
"name": name,
|
"name": name,
|
||||||
"cron": cron ?? "",
|
"cron": cron ?? "",
|
||||||
|
"mode": int.parse(mode.toString()),
|
||||||
"start": start?.toIso8601String(),
|
"start": start?.toIso8601String(),
|
||||||
"end": end?.toIso8601String(),
|
"end": end?.toIso8601String(),
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@ import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart' as intl;
|
import 'package:intl/intl.dart' as intl;
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:oc_front/core/services/specialized_services/logs_service.dart';
|
||||||
import 'package:oc_front/core/services/specialized_services/workflow_execution_service.dart';
|
import 'package:oc_front/core/services/specialized_services/workflow_execution_service.dart';
|
||||||
import 'package:oc_front/models/workflow.dart';
|
import 'package:oc_front/models/workflow.dart';
|
||||||
import 'package:oc_front/pages/abstract_page.dart';
|
import 'package:oc_front/pages/abstract_page.dart';
|
||||||
@ -35,6 +36,7 @@ class SchedulerPageWidgetState extends State<SchedulerPageWidget> {
|
|||||||
"${widget.end.year}-${widget.end.month > 9 ? widget.end.month : "0${widget.end.month}"}-${widget.end.day > 9 ? widget.end.day : "0${widget.end.day}"}"], {}),
|
"${widget.end.year}-${widget.end.month > 9 ? widget.end.month : "0${widget.end.month}"}-${widget.end.day > 9 ? widget.end.day : "0${widget.end.day}"}"], {}),
|
||||||
builder: (ctx, as) {
|
builder: (ctx, as) {
|
||||||
Map<String, List<WorkflowExecution>> data = {};
|
Map<String, List<WorkflowExecution>> data = {};
|
||||||
|
|
||||||
if (as.hasData && as.data!.data != null) {
|
if (as.hasData && as.data!.data != null) {
|
||||||
for (var element in as.data!.data!.executions) {
|
for (var element in as.data!.data!.executions) {
|
||||||
if (element.executionData == null) { continue; }
|
if (element.executionData == null) { continue; }
|
||||||
@ -47,6 +49,49 @@ class SchedulerPageWidgetState extends State<SchedulerPageWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
GlobalKey<ScheduleWidgetState> k = GlobalKey<ScheduleWidgetState>();
|
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; });
|
||||||
|
LogsService().search(context, [], {
|
||||||
|
"workflow_execution_id": exec.id,
|
||||||
|
"start": start,
|
||||||
|
"end": end
|
||||||
|
}).then((value) {
|
||||||
|
if (value.data != null) {
|
||||||
|
var d = value.data!;
|
||||||
|
for( var r in d.result) {
|
||||||
|
for (var element in r.logs) {
|
||||||
|
element.level = r.level;
|
||||||
|
exec.logs ??= [];
|
||||||
|
exec.logs!.add(element);
|
||||||
|
}
|
||||||
|
exec.logs?.sort((a, b) => a.timestamp!.compareTo(b.timestamp!));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
k.currentState?.setState(() { k.currentState?.widget.loading = false; });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
return Column( children: [
|
return Column( children: [
|
||||||
Container( color: const Color.fromRGBO(38, 166, 154, 1),
|
Container( color: const Color.fromRGBO(38, 166, 154, 1),
|
||||||
height: 50, width: MediaQuery.of(context).size.width,
|
height: 50, width: MediaQuery.of(context).size.width,
|
||||||
@ -69,7 +114,7 @@ class SchedulerPageWidgetState extends State<SchedulerPageWidget> {
|
|||||||
validator: (value) {
|
validator: (value) {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
resetIcon: const Icon(Icons.close, size: 15),
|
resetIcon: null,
|
||||||
onShowPicker: (context, currentValue) async {
|
onShowPicker: (context, currentValue) async {
|
||||||
var date = await showDatePicker(
|
var date = await showDatePicker(
|
||||||
builder: (BuildContext context, Widget? child) {
|
builder: (BuildContext context, Widget? child) {
|
||||||
@ -94,6 +139,7 @@ class SchedulerPageWidgetState extends State<SchedulerPageWidget> {
|
|||||||
);
|
);
|
||||||
return date;
|
return date;
|
||||||
},
|
},
|
||||||
|
|
||||||
format: intl.DateFormat('y-M-dd hh:mm:ss'),
|
format: intl.DateFormat('y-M-dd hh:mm:ss'),
|
||||||
initialValue: widget.start,
|
initialValue: widget.start,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -126,7 +172,7 @@ class SchedulerPageWidgetState extends State<SchedulerPageWidget> {
|
|||||||
validator: (value) {
|
validator: (value) {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
resetIcon: const Icon(Icons.close, size: 15),
|
resetIcon: null,
|
||||||
onShowPicker: (context, currentValue) async {
|
onShowPicker: (context, currentValue) async {
|
||||||
var date = await showDatePicker(
|
var date = await showDatePicker(
|
||||||
builder: (BuildContext context, Widget? child) {
|
builder: (BuildContext context, Widget? child) {
|
||||||
@ -176,7 +222,7 @@ class SchedulerPageWidgetState extends State<SchedulerPageWidget> {
|
|||||||
)
|
)
|
||||||
]))
|
]))
|
||||||
),
|
),
|
||||||
ScheduleWidget( key: k, data: data, start: widget.start, end : widget.end, isList: widget.isList )
|
ScheduleWidget( key: k, data: data, start: widget.start, end : widget.end, isList: widget.isList, )
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ class WorkflowFactory implements AbstractFactory {
|
|||||||
@override Widget factory(GoRouterState state, List<String> args) { return WorkflowPageWidget(); }
|
@override Widget factory(GoRouterState state, List<String> args) { return WorkflowPageWidget(); }
|
||||||
@override void search(BuildContext context) { }
|
@override void search(BuildContext context) { }
|
||||||
}
|
}
|
||||||
|
bool getAll = true;
|
||||||
|
|
||||||
class WorkflowPageWidget extends StatefulWidget {
|
class WorkflowPageWidget extends StatefulWidget {
|
||||||
WorkflowPageWidget () : super(key: WorkflowFactory.key);
|
WorkflowPageWidget () : super(key: WorkflowFactory.key);
|
||||||
@ -107,7 +108,6 @@ final WorflowService _service = WorflowService();
|
|||||||
item.position?.x = (item.position?.x ?? 0) + 52.5;
|
item.position?.x = (item.position?.x ?? 0) + 52.5;
|
||||||
item.position?.y = (item.position?.y ?? 0) + 52.5;
|
item.position?.y = (item.position?.y ?? 0) + 52.5;
|
||||||
}
|
}
|
||||||
print(dash.getZoomFactor());
|
|
||||||
updateW.graph?.zoom = dash.getZoomFactor();
|
updateW.graph?.zoom = dash.getZoomFactor();
|
||||||
await _service.put(context, id, updateW.serialize(), {});
|
await _service.put(context, id, updateW.serialize(), {});
|
||||||
}
|
}
|
||||||
@ -128,7 +128,9 @@ final WorflowService _service = WorflowService();
|
|||||||
Widget menuExtension() {
|
Widget menuExtension() {
|
||||||
var quart = MediaQuery.of(context).size.width / 6;
|
var quart = MediaQuery.of(context).size.width / 6;
|
||||||
return MenuWorkspaceWidget(simpliest: true, width: quart > 80 ? quart : 80,
|
return MenuWorkspaceWidget(simpliest: true, width: quart > 80 ? quart : 80,
|
||||||
onWorkspaceChange: () { dash.selectedLeftMenuKey.currentState?.setState(() { }); });
|
onWorkspaceChange: () {
|
||||||
|
dash.selectedLeftMenuKey.currentState?.setState(() { });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget onDashboardAlertOpened(BuildContext context, Dashboard dash) {
|
Widget onDashboardAlertOpened(BuildContext context, Dashboard dash) {
|
||||||
@ -148,7 +150,7 @@ final WorflowService _service = WorflowService();
|
|||||||
dashboard: dash,
|
dashboard: dash,
|
||||||
itemWidget: itemBuild,
|
itemWidget: itemBuild,
|
||||||
categories: const ["processing", "data", "datacenter", "storage", "workflows"],
|
categories: const ["processing", "data", "datacenter", "storage", "workflows"],
|
||||||
draggableItemBuilder: (cat) => WorkspaceLocal.byTopic(cat, true).toList(),
|
draggableItemBuilder: (cat) => WorkspaceLocal.byTopic(cat, false),
|
||||||
itemWidgetTooltip: itemTooltipBuild,
|
itemWidgetTooltip: itemTooltipBuild,
|
||||||
innerMenuWidth: quart > 80 ? quart : 80,
|
innerMenuWidth: quart > 80 ? quart : 80,
|
||||||
menuExtension: menuExtension,
|
menuExtension: menuExtension,
|
||||||
|
@ -113,11 +113,11 @@ class NewBoxWidgetState<T extends SerializerDeserializer<dynamic>> extends State
|
|||||||
? MouseCursor.defer : SystemMouseCursors.click,
|
? MouseCursor.defer : SystemMouseCursors.click,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
if (widget._selected == null || widget._selected!.isEmpty) { return; }
|
if (widget._selected == null || widget._selected!.isEmpty) { return; }
|
||||||
if (widget._selected != null && widget.dash.load != null) {
|
|
||||||
await widget.dash.load!(widget._selected ?? "");
|
|
||||||
WorkspaceLocal.init(context, true);
|
|
||||||
}
|
|
||||||
widget.dash.isOpened = true;
|
widget.dash.isOpened = true;
|
||||||
|
if (widget._selected != null && widget.dash.load != null) {
|
||||||
|
WorkspaceLocal.changeWorkspaceByName(widget._selected!.split("~")[1]);
|
||||||
|
await widget.dash.load!(widget._selected ?? "");
|
||||||
|
}
|
||||||
widget.dash.notifyListeners();
|
widget.dash.notifyListeners();
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
|
@ -1,20 +1,46 @@
|
|||||||
|
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:intl/intl.dart' as intl;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
|
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
|
||||||
import 'package:flutter_advanced_switch/flutter_advanced_switch.dart';
|
import 'package:flutter_advanced_switch/flutter_advanced_switch.dart';
|
||||||
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
|
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
|
||||||
|
import 'package:oc_front/core/services/specialized_services/check_service.dart';
|
||||||
|
import 'package:oc_front/pages/workflow.dart';
|
||||||
|
import 'package:oc_front/widgets/dialog/alert.dart';
|
||||||
|
|
||||||
class SchedulerFormsWidget extends StatefulWidget {
|
class SchedulerFormsWidget extends StatefulWidget {
|
||||||
Dashboard item;
|
Dashboard item;
|
||||||
String purpose = "";
|
String purpose = "";
|
||||||
|
bool? booking;
|
||||||
Function validate = () {};
|
Function validate = () {};
|
||||||
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();
|
||||||
@override Widget build(BuildContext context) {
|
@override Widget build(BuildContext context) {
|
||||||
if (widget.item.schedulerState["service"] == null) { widget.item.schedulerState["service"] = true; }
|
try {
|
||||||
|
if (widget.item.scheduler["mode"] == null) { widget.item.scheduler["mode"] = 1; }
|
||||||
|
} catch (e) {
|
||||||
|
widget.item.scheduler = { "mode": 1 };
|
||||||
|
}
|
||||||
|
DateTime? start;
|
||||||
|
DateTime? end;
|
||||||
|
if (widget.item.scheduler["start"] != null) {
|
||||||
|
start = DateTime.parse(widget.item.scheduler["start"]!);
|
||||||
|
if (start.isBefore(DateTime.now()) && !dash.scheduleActive) {
|
||||||
|
start = DateTime.now().add(const Duration(minutes: 5));
|
||||||
|
widget.item.scheduler["start"] = start.toUtc().toIso8601String();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (widget.item.scheduler["end"] != null) {
|
||||||
|
end = DateTime.parse(widget.item.scheduler["end"]!);
|
||||||
|
if (end.isBefore(DateTime.now()) && !dash.scheduleActive) {
|
||||||
|
end = DateTime.now().add(const Duration(minutes: 5));
|
||||||
|
widget.item.scheduler["end"] = end.toUtc().toIso8601String();
|
||||||
|
}
|
||||||
|
}
|
||||||
List<GlobalKey<FormFieldState>> formKeys = [GlobalKey<FormFieldState>(), GlobalKey<FormFieldState>(),
|
List<GlobalKey<FormFieldState>> formKeys = [GlobalKey<FormFieldState>(), GlobalKey<FormFieldState>(),
|
||||||
GlobalKey<FormFieldState>(), GlobalKey<FormFieldState>()];
|
GlobalKey<FormFieldState>(), GlobalKey<FormFieldState>()];
|
||||||
return Column( children: [
|
return Column( children: [
|
||||||
@ -27,7 +53,7 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
|
|||||||
Container(height: 20),
|
Container(height: 20),
|
||||||
AdvancedSwitch(
|
AdvancedSwitch(
|
||||||
width: 140,
|
width: 140,
|
||||||
initialValue: widget.item.schedulerState["service"] == true,
|
initialValue: widget.item.scheduler["mode"] == 1,
|
||||||
activeColor: Colors.green, inactiveColor: Colors.green,
|
activeColor: Colors.green, inactiveColor: Colors.green,
|
||||||
activeChild: const Text("service", style: TextStyle(color: Colors.white)),
|
activeChild: const Text("service", style: TextStyle(color: Colors.white)),
|
||||||
inactiveChild: const Text("cron task", style: TextStyle(color: Colors.white)),
|
inactiveChild: const Text("cron task", style: TextStyle(color: Colors.white)),
|
||||||
@ -35,8 +61,8 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
|
|||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
Future.delayed(const Duration(milliseconds: 100), () =>
|
Future.delayed(const Duration(milliseconds: 100), () =>
|
||||||
setState(() {
|
setState(() {
|
||||||
widget.item.schedulerState["service"] = value;
|
widget.item.scheduler["mode"] = value == true ? 1 : 0;
|
||||||
if ((widget.item.schedulerState["service"] == true )) { widget.item.scheduler.remove("cron"); }
|
if ((widget.item.scheduler["mode"] == 1 )) { widget.item.scheduler.remove("cron"); }
|
||||||
}));
|
}));
|
||||||
},),
|
},),
|
||||||
Container(height: 5),
|
Container(height: 5),
|
||||||
@ -44,12 +70,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: 20),
|
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
|
||||||
child: TextFormField( key: formKeys[0],
|
child: TextFormField( key: formKeys[0],
|
||||||
initialValue: "${widget.item.schedulerState["service"] == true ? "" : "cron_"}${widget.item.scheduler["name"] ?? "${widget.item.name}_event"}",
|
initialValue: "${widget.item.scheduler["mode"] == 1 ? "" : "cron_"}${widget.item.scheduler["name"] ?? "${widget.item.name}_event"}",
|
||||||
|
enabled: !dash.scheduleActive,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
|
Future.delayed(const Duration(seconds: 100), () {
|
||||||
|
if (widget.item.scheduler["name"] == value) {
|
||||||
|
widget.item.save!(widget.item.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
widget.item.scheduler["name"] = value;
|
widget.item.scheduler["name"] = value;
|
||||||
},
|
},
|
||||||
onSaved: (value) {
|
onSaved: (value) {
|
||||||
widget.item.scheduler["name"] = value ?? "${widget.item.schedulerState["service"] == true ? "" : "cron_"}${widget.item.scheduler["name"] ?? "${widget.item.name}_event"}";
|
widget.item.scheduler["name"] = value ?? "${widget.item.scheduler["mode"] == 1 ? "" : "cron_"}${widget.item.scheduler["name"] ?? "${widget.item.name}_event"}";
|
||||||
},
|
},
|
||||||
validator: (value) => value == null || value.isEmpty ? "not empty" : null,
|
validator: (value) => value == null || value.isEmpty ? "not empty" : null,
|
||||||
style: const TextStyle(fontSize: 12),
|
style: const TextStyle(fontSize: 12),
|
||||||
@ -72,10 +104,8 @@ 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: 20),
|
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
|
||||||
child: DateTimeField( key: formKeys[1],
|
child: DateTimeField( key: formKeys[1],
|
||||||
resetIcon: const Icon(Icons.close, size: 15),
|
enabled: !dash.scheduleActive,
|
||||||
onSaved: (value) {
|
resetIcon: null,
|
||||||
widget.item.scheduler["start"] = "${(value ?? DateTime.now()).toIso8601String()}Z";
|
|
||||||
},
|
|
||||||
onShowPicker: (context, currentValue) async {
|
onShowPicker: (context, currentValue) async {
|
||||||
var date = await showDatePicker(
|
var date = await showDatePicker(
|
||||||
builder: (BuildContext context, Widget? child) {
|
builder: (BuildContext context, Widget? child) {
|
||||||
@ -94,12 +124,24 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
|
|||||||
return w;
|
return w;
|
||||||
},
|
},
|
||||||
context: context,
|
context: context,
|
||||||
firstDate: DateTime(1900),
|
firstDate: dash.scheduleActive ? DateTime(1900) : DateTime.now().add(const Duration(minutes: 5)),
|
||||||
initialDate: DateTime.parse(widget.item.scheduler["start"] ?? currentValue?.toIso8601String() ?? ""),
|
initialDate: DateTime.parse( start?.toLocal().toIso8601String()
|
||||||
|
?? currentValue?.toIso8601String()
|
||||||
|
?? DateTime.now().add(const Duration(minutes: 5)).toUtc().toIso8601String()).toLocal(),
|
||||||
lastDate: DateTime(2100)
|
lastDate: DateTime(2100)
|
||||||
);
|
);
|
||||||
if (date != null) {
|
if (date != null) {
|
||||||
var time = await showTimePicker(context: context,
|
var n = TimeOfDay.now();
|
||||||
|
TimeOfDay? time = n;
|
||||||
|
var count = 0;
|
||||||
|
while(((time?.hour ?? 0) + ((time?.minute ?? 0) / 100)) <= (n.hour + ((n.minute + 1) / 100)) ) {
|
||||||
|
if (count > 0 && time != null) {
|
||||||
|
showAlertBanner( context, () {},
|
||||||
|
const AlertAlertBannerChild(
|
||||||
|
text: "must be at least 1 minute from now to let system check info"),// <-- Put any widget here you want!
|
||||||
|
alertBannerLocation: AlertBannerLocation.bottom,);
|
||||||
|
}
|
||||||
|
time = await showTimePicker(context: context,
|
||||||
initialTime: TimeOfDay(hour: date.hour, minute: date.minute),
|
initialTime: TimeOfDay(hour: date.hour, minute: date.minute),
|
||||||
builder: (BuildContext context, Widget? child) {
|
builder: (BuildContext context, Widget? child) {
|
||||||
Widget w = Theme(
|
Widget w = Theme(
|
||||||
@ -117,15 +159,18 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
|
|||||||
return w;
|
return w;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if (time == null) { return DateTime.now().add( const Duration(minutes: 1)); }
|
||||||
|
count++;
|
||||||
|
}
|
||||||
date = date.add(Duration(hours: time?.hour ?? 0, minutes: time?.minute ?? 0));
|
date = date.add(Duration(hours: time?.hour ?? 0, minutes: time?.minute ?? 0));
|
||||||
|
widget.item.scheduler["start"] = date.toUtc().toIso8601String();
|
||||||
|
widget.item.save!(widget.item.id);
|
||||||
}
|
}
|
||||||
return date;
|
return date;
|
||||||
},
|
},
|
||||||
format: intl.DateFormat('y-M-dd hh:mm:ss'),
|
format: intl.DateFormat('y-M-dd HH:mm:ss'),
|
||||||
initialValue: DateTime.parse(widget.item.scheduler["start"] ?? DateTime.now().toIso8601String()),
|
initialValue: start?.toLocal() ?? DateTime.now(),
|
||||||
onChanged: (value) {
|
onChanged: (value) { },
|
||||||
widget.item.scheduler["start"] = "${(value ?? DateTime.now()).toIso8601String()}Z";
|
|
||||||
},
|
|
||||||
validator: (value) => value == null ? "not empty" : null,
|
validator: (value) => value == null ? "not empty" : null,
|
||||||
style: const TextStyle(fontSize: 12),
|
style: const TextStyle(fontSize: 12),
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
@ -147,8 +192,14 @@ 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: 20),
|
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
|
||||||
child: DateTimeField( key: formKeys[2],
|
child: DateTimeField( key: formKeys[2],
|
||||||
|
enabled: !dash.scheduleActive,
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
return value == null && !(widget.item.schedulerState["service"] == true ) ? "not empty" : null;
|
return value == null && !(widget.item.scheduler["mode"] == 1 ) ? "not empty" : null;
|
||||||
|
},
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value == null) {
|
||||||
|
widget.item.scheduler.remove("end");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
resetIcon: const Icon(Icons.close, size: 15),
|
resetIcon: const Icon(Icons.close, size: 15),
|
||||||
onShowPicker: (context, currentValue) async {
|
onShowPicker: (context, currentValue) async {
|
||||||
@ -169,12 +220,24 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
|
|||||||
return w;
|
return w;
|
||||||
},
|
},
|
||||||
context: context,
|
context: context,
|
||||||
firstDate: DateTime(1900),
|
firstDate: dash.scheduleActive ? DateTime(1900) : DateTime.now().add(const Duration(minutes: 5)),
|
||||||
initialDate: DateTime.parse(widget.item.scheduler["start"] ?? currentValue?.toIso8601String() ?? ""),
|
initialDate: DateTime.parse( end?.toLocal().toIso8601String()
|
||||||
|
?? currentValue?.toIso8601String()
|
||||||
|
?? DateTime.now().add(const Duration(minutes: 5)).toUtc().toIso8601String()).toLocal(),
|
||||||
lastDate: DateTime(2100)
|
lastDate: DateTime(2100)
|
||||||
);
|
);
|
||||||
if (date != null) {
|
if (date != null) {
|
||||||
var time = await showTimePicker(context: context,
|
// ignore: use_build_context_synchronously
|
||||||
|
var n = TimeOfDay.now();
|
||||||
|
TimeOfDay? time = TimeOfDay(hour: date.hour, minute: date.minute);
|
||||||
|
var count = 0;
|
||||||
|
while(((time?.hour ?? 0) + (time?.minute ?? 0 / 100)) <= (n.hour + ((n.minute + 1) / 100)) ) {
|
||||||
|
if (count > 0 && time != null) {
|
||||||
|
showAlertBanner( context, () {},
|
||||||
|
const AlertAlertBannerChild(text: "must be at least 1 minute from now to let system check info"),// <-- Put any widget here you want!
|
||||||
|
alertBannerLocation: AlertBannerLocation.bottom,);
|
||||||
|
}
|
||||||
|
time = await showTimePicker(context: context,
|
||||||
initialTime: TimeOfDay(hour: date.hour, minute: date.minute),
|
initialTime: TimeOfDay(hour: date.hour, minute: date.minute),
|
||||||
builder: (BuildContext context, Widget? child) {
|
builder: (BuildContext context, Widget? child) {
|
||||||
Widget w = Theme(
|
Widget w = Theme(
|
||||||
@ -192,22 +255,17 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
|
|||||||
return w;
|
return w;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
if (time == null) { return null; }
|
||||||
|
count++;
|
||||||
|
}
|
||||||
date = date.add(Duration(hours: time?.hour ?? 0, minutes: time?.minute ?? 0));
|
date = date.add(Duration(hours: time?.hour ?? 0, minutes: time?.minute ?? 0));
|
||||||
|
widget.item.scheduler["end"] = date.toUtc().toIso8601String();
|
||||||
|
widget.item.save!(widget.item.id);
|
||||||
}
|
}
|
||||||
return date;
|
return date;
|
||||||
},
|
},
|
||||||
|
format: intl.DateFormat('y-M-dd HH:mm:ss'),
|
||||||
format: intl.DateFormat('y-M-dd hh:mm:ss'),
|
initialValue: end?.toLocal(),
|
||||||
initialValue: widget.item.scheduler["end"] != null ? DateTime.parse(widget.item.scheduler["end"]!) : null,
|
|
||||||
onSaved: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
widget.item.scheduler["end"] = "${(value).toIso8601String()}Z";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value == null) { return; }
|
|
||||||
widget.item.scheduler["end"] = "${value.toIso8601String()}Z";
|
|
||||||
},
|
|
||||||
style: const TextStyle(fontSize: 12),
|
style: const TextStyle(fontSize: 12),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
fillColor: Colors.white,
|
fillColor: Colors.white,
|
||||||
@ -215,7 +273,7 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
|
|||||||
filled: true,
|
filled: true,
|
||||||
alignLabelWithHint: false,
|
alignLabelWithHint: false,
|
||||||
hintText: "enter end event...",
|
hintText: "enter end event...",
|
||||||
labelText: "end event${!(widget.item.schedulerState["service"] == true) ? "*" : ""}",
|
labelText: "end event${!(widget.item.scheduler["mode"] == 1) ? "*" : ""}",
|
||||||
errorStyle: const TextStyle(fontSize: 0),
|
errorStyle: const TextStyle(fontSize: 0),
|
||||||
hintStyle: const TextStyle(fontSize: 10),
|
hintStyle: const TextStyle(fontSize: 10),
|
||||||
labelStyle: const TextStyle(fontSize: 10),
|
labelStyle: const TextStyle(fontSize: 10),
|
||||||
@ -224,12 +282,18 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
|
|||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||||
),
|
),
|
||||||
))),
|
))),
|
||||||
widget.item.schedulerState["service"] == true ? Container() : Tooltip( message: "schedule",
|
widget.item.scheduler["mode"] == 1 ? Container() : Tooltip( message: "schedule",
|
||||||
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: 20),
|
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
|
||||||
child: TextFormField( key: formKeys[3],
|
child: TextFormField( key: formKeys[3],
|
||||||
|
enabled: !dash.scheduleActive,
|
||||||
initialValue: widget.item.scheduler["cron"],
|
initialValue: widget.item.scheduler["cron"],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
|
Future.delayed(const Duration(seconds: 100), () {
|
||||||
|
if (widget.item.scheduler["cron"] == value) {
|
||||||
|
widget.item.save!(widget.item.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
widget.item.scheduler["cron"] = value;
|
widget.item.scheduler["cron"] = value;
|
||||||
},
|
},
|
||||||
onSaved: (value) {
|
onSaved: (value) {
|
||||||
@ -263,9 +327,48 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
|
|||||||
),
|
),
|
||||||
))),
|
))),
|
||||||
const Divider(color: Colors.grey),
|
const Divider(color: Colors.grey),
|
||||||
Tooltip( message: "save",
|
Tooltip( message: "check booking",
|
||||||
child: InkWell( mouseCursor: SystemMouseCursors.click,
|
child: InkWell( mouseCursor: SystemMouseCursors.click,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
if (dash.scheduler["start"] == null ) {
|
||||||
|
DateTime now = DateTime.now().add(const Duration(minutes: 5));
|
||||||
|
dash.scheduler["start"] = now.toUtc().toIso8601String();
|
||||||
|
}
|
||||||
|
var s = DateTime.parse(dash.scheduler["start"]).toUtc().toIso8601String();
|
||||||
|
var e = "";
|
||||||
|
if (dash.scheduler["end"] == null) {
|
||||||
|
e = DateTime.parse(dash.scheduler["start"]).add(const Duration(seconds: 5)).toUtc().toIso8601String();
|
||||||
|
} else {
|
||||||
|
e = DateTime.parse(dash.scheduler["end"]).toUtc().toIso8601String();
|
||||||
|
}
|
||||||
|
check.search(context, [widget.item.id ?? "", s.substring(0, 19), e.substring(0, 19)], {}).then(
|
||||||
|
(v) {
|
||||||
|
if (v.data == null) { return; }
|
||||||
|
widget.booking = v.data!.is_available;
|
||||||
|
if (v.data!.is_available) {
|
||||||
|
showAlertBanner( context, () {},
|
||||||
|
const InfoAlertBannerChild(text: "no booking found at this date"),// <-- Put any widget here you want!
|
||||||
|
alertBannerLocation: AlertBannerLocation.bottom,);
|
||||||
|
} else {
|
||||||
|
showAlertBanner( context, () {},
|
||||||
|
const AlertAlertBannerChild(text: "booking found at this date"),// <-- Put any widget here you want!
|
||||||
|
alertBannerLocation: AlertBannerLocation.bottom,);
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, child: Container( margin: const EdgeInsets.all(10),
|
||||||
|
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5),
|
||||||
|
border: Border.all(color: widget.booking == null ? Colors.black : (widget.booking == true ? Colors.green : Colors.red), width: 1)),
|
||||||
|
width: 140, height: 30,
|
||||||
|
child: Icon(
|
||||||
|
Icons.verified_outlined, color:widget.booking == null ? Colors.black : (widget.booking == true? Colors.green : Colors.red)),
|
||||||
|
))
|
||||||
|
),
|
||||||
|
Tooltip( message: dash.scheduleActive ? "unbook" : "book",
|
||||||
|
child: InkWell( mouseCursor: SystemMouseCursors.click,
|
||||||
|
onTap: () {
|
||||||
|
dash.scheduleActive = !dash.scheduleActive;
|
||||||
for (var k in formKeys) {
|
for (var k in formKeys) {
|
||||||
if (k.currentState != null) {
|
if (k.currentState != null) {
|
||||||
if (!k.currentState!.validate()) {
|
if (!k.currentState!.validate()) {
|
||||||
@ -273,12 +376,18 @@ class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
|
|||||||
} else { k.currentState!.save();}
|
} else { k.currentState!.save();}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
widget.item.schedulerSave = true;
|
if (dash.scheduler["start"] == null ) {
|
||||||
|
DateTime now = DateTime.now().add(const Duration(minutes: 5));
|
||||||
|
dash.scheduler["start"] = now.toUtc().toIso8601String();
|
||||||
|
}
|
||||||
widget.item.save!(widget.item.id);
|
widget.item.save!(widget.item.id);
|
||||||
|
setState(() { });
|
||||||
}, child: Container( margin: const EdgeInsets.all(10),
|
}, child: Container( margin: const 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: dash.scheduleActive ? Colors.green : Colors.black, width: 1)),
|
||||||
width: 140, height: 30,
|
width: 140, height: 30,
|
||||||
child: const Icon(Icons.save_outlined, color: Colors.black),
|
child: Icon(
|
||||||
|
dash.scheduleActive ? Icons.cancel_schedule_send : Icons.schedule_send, color: dash.scheduleActive ? Colors.green : Colors.black),
|
||||||
))
|
))
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
@ -32,7 +32,8 @@ class ItemRowWidgetState extends State<ItemRowWidget> {
|
|||||||
height: 100,
|
height: 100,
|
||||||
decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.grey.shade300)) ),
|
decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.grey.shade300)) ),
|
||||||
child: Row( children: [
|
child: Row( children: [
|
||||||
widget.low ? Container( padding: const EdgeInsets.only(left: 10),) : Padding( padding: const EdgeInsets.all(10),
|
widget.low ? Container( padding: const EdgeInsets.only(left: 10),) : Container( padding: const EdgeInsets.all(10),
|
||||||
|
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(
|
Container(
|
||||||
|
824
lib/widgets/lib/tranformablebox.dart
Normal file
824
lib/widgets/lib/tranformablebox.dart
Normal file
@ -0,0 +1,824 @@
|
|||||||
|
/*
|
||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2023, Birju Vachhani
|
||||||
|
*/
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:box_transform/box_transform.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_box_transform/flutter_box_transform.dart';
|
||||||
|
|
||||||
|
/// A widget that allows you to resize and drag a box around a widget.
|
||||||
|
class TransformableBox extends StatefulWidget {
|
||||||
|
/// If you need more control over the [TransformableBox] you can pass a
|
||||||
|
/// custom [TransformableBoxController] instance through the [controller]
|
||||||
|
/// parameter.
|
||||||
|
///
|
||||||
|
/// If you do not specify one, a default [TransformableBoxController] instance
|
||||||
|
/// will be created internally, along with its lifecycle.
|
||||||
|
final TransformableBoxController? controller;
|
||||||
|
|
||||||
|
/// A builder function that is used to build the content of the
|
||||||
|
/// [TransformableBox]. This is the physical widget you wish to show resizable
|
||||||
|
/// handles on. It's most commonly something like an image widget, but it
|
||||||
|
/// could be anything you want to have resizable & draggable box handles on.
|
||||||
|
final TransformableChildBuilder contentBuilder;
|
||||||
|
|
||||||
|
/// A builder function that is used to build the corners handles of the
|
||||||
|
/// [TransformableBox]. If you don't specify it, the default handles will be
|
||||||
|
/// used.
|
||||||
|
///
|
||||||
|
/// Note that this will build for all four corners of the rectangle.
|
||||||
|
final HandleBuilder cornerHandleBuilder;
|
||||||
|
|
||||||
|
/// A builder function that is used to build the side handles of the
|
||||||
|
/// [TransformableBox]. If you don't specify it, the default handles will be
|
||||||
|
/// used.
|
||||||
|
///
|
||||||
|
/// Note that this will build for all four sides of the rectangle.
|
||||||
|
final HandleBuilder sideHandleBuilder;
|
||||||
|
|
||||||
|
/// The size of the gesture response area of the handles. If you don't
|
||||||
|
/// specify it, the default value will be used.
|
||||||
|
///
|
||||||
|
/// This is similar to Flutter's [MaterialTapTargetSize] property, in which
|
||||||
|
/// the actual handle size is smaller than the gesture response area. This is
|
||||||
|
/// done to improve accessibility and usability of the handles; users will not
|
||||||
|
/// need cursor precision over the handle's pixels to be able to perform
|
||||||
|
/// operations with them, they need only to be able to reach the handle's
|
||||||
|
/// gesture response area to make it forgiving.
|
||||||
|
///
|
||||||
|
/// The default value is 24 pixels in diameter.
|
||||||
|
final double handleTapSize;
|
||||||
|
|
||||||
|
/// A set containing handles that are enabled. This is different from
|
||||||
|
/// [visibleHandles].
|
||||||
|
///
|
||||||
|
/// [enabledHandles] determines which handles are
|
||||||
|
/// interactive and can be used to resize the box. [visibleHandles]
|
||||||
|
/// determines which handles are visible. If a handle is visible but not
|
||||||
|
/// enabled, it will not be interactive. If a handle is enabled but not
|
||||||
|
/// visible, it will not be shown and will not be interactive.
|
||||||
|
final Set<HandlePosition> enabledHandles;
|
||||||
|
|
||||||
|
/// A set containing which handles to show. This is different from
|
||||||
|
/// [enabledHandles].
|
||||||
|
///
|
||||||
|
/// [enabledHandles] determines which handles are
|
||||||
|
/// interactive and can be used to resize the box. [visibleHandles]
|
||||||
|
/// determines which handles are visible. If a handle is visible but not
|
||||||
|
/// enabled, it will not be interactive. If a handle is enabled but not
|
||||||
|
/// visible, it will not be shown and will not be interactive.
|
||||||
|
final Set<HandlePosition> visibleHandles;
|
||||||
|
|
||||||
|
/// The initial box that will be used to position set the initial size of
|
||||||
|
/// the [TransformableBox] widget.
|
||||||
|
///
|
||||||
|
/// This initial box will be mutated by the [TransformableBoxController] through
|
||||||
|
/// different dragging, panning, and resizing operations.
|
||||||
|
///
|
||||||
|
/// [Rect] is immutable, so a new [Rect] instance will be created every time
|
||||||
|
/// the [TransformableBoxController] mutates the box. You can acquire your
|
||||||
|
/// updated box through the [onChanged] callback or through an externally
|
||||||
|
/// provided [TransformableBoxController] instance.
|
||||||
|
final Rect rect;
|
||||||
|
|
||||||
|
/// The initial flip that will be used to set the initial flip of the
|
||||||
|
/// [TransformableBox] widget. Normally, flipping is done by the user through
|
||||||
|
/// the handles, but you can set the initial flip through this parameter in
|
||||||
|
/// case the initial state of the box is in a flipped state.
|
||||||
|
///
|
||||||
|
/// This utility cannot predicate if a box is flipped or not, so you will
|
||||||
|
/// need to provide the correct initial flip state.
|
||||||
|
///
|
||||||
|
/// Note that the flip is optional, if you're resizing an image, for example,
|
||||||
|
/// you might want to allow flipping of the image when the user drags the
|
||||||
|
/// handles to opposite corners of the box. This flip behavior is entirely
|
||||||
|
/// optional and will allow handling such cases.
|
||||||
|
///
|
||||||
|
/// You can leave it at the default [Flip.none] if flipping is not desired.
|
||||||
|
/// Note that this will not prevent the drag handles from crossing to
|
||||||
|
/// opposite corners of the box, it will only give oyu a lack of information
|
||||||
|
/// on the state of the box if flipping were to occur.
|
||||||
|
final Flip flip;
|
||||||
|
|
||||||
|
/// A box that will contain the [rect] inside of itself, forcing [rect] to
|
||||||
|
/// be clamped inside of this [clampingRect].
|
||||||
|
final Rect clampingRect;
|
||||||
|
|
||||||
|
/// A set of constraints that will be applied to the [rect] when it is
|
||||||
|
/// resized by the [TransformableBoxController].
|
||||||
|
final BoxConstraints constraints;
|
||||||
|
|
||||||
|
/// Whether the box is resizable or not. Setting this to false will disable
|
||||||
|
/// all resizing operations. This is a convenience parameter that will ignore
|
||||||
|
/// the [enabledHandles] parameter and set all handles to disabled.
|
||||||
|
final bool resizable;
|
||||||
|
|
||||||
|
/// Whether the box is movable or not. Setting this to false will disable
|
||||||
|
/// all moving operations.
|
||||||
|
final bool draggable;
|
||||||
|
|
||||||
|
/// Whether to allow flipping of the box while resizing. If this is set to
|
||||||
|
/// true, the box will flip when the user drags the handles to opposite
|
||||||
|
/// corners of the rect.
|
||||||
|
final bool allowFlippingWhileResizing;
|
||||||
|
|
||||||
|
/// Decides whether to flip the contents of the box when the box is flipped.
|
||||||
|
/// If this is set to true, the contents will be flipped when the box is
|
||||||
|
/// flipped.
|
||||||
|
final bool allowContentFlipping;
|
||||||
|
|
||||||
|
/// How to align the handles.
|
||||||
|
final HandleAlignment handleAlignment;
|
||||||
|
|
||||||
|
/// The callback function that is used to resolve the [ResizeMode] based on
|
||||||
|
/// the pressed keys on the keyboard.
|
||||||
|
final ValueGetter<ResizeMode> resizeModeResolver;
|
||||||
|
|
||||||
|
/// A callback that is called every time the [TransformableBox] is updated.
|
||||||
|
/// This is called every time the [TransformableBoxController] mutates the box
|
||||||
|
/// or the flip.
|
||||||
|
final RectChangeEvent? onChanged;
|
||||||
|
|
||||||
|
/// A callback that is called when [TransformableBox] triggers a pointer down
|
||||||
|
/// event to begin a drag operation.
|
||||||
|
final RectDragStartEvent? onDragStart;
|
||||||
|
|
||||||
|
/// A callback that is called every time the [TransformableBox] is moved.
|
||||||
|
/// This is called every time the [TransformableBoxController] mutates the
|
||||||
|
/// box through a drag operation.
|
||||||
|
///
|
||||||
|
/// This is different from [onChanged] in that it is only called when the
|
||||||
|
/// box is moved, not when the box is resized.
|
||||||
|
final RectDragUpdateEvent? onDragUpdate;
|
||||||
|
|
||||||
|
/// A callback that is called every time the [TransformableBox] is completes
|
||||||
|
/// its drag operation via the pan end event.
|
||||||
|
final RectDragEndEvent? onDragEnd;
|
||||||
|
|
||||||
|
/// A callback that is called every time the [TransformableBox] cancels
|
||||||
|
/// its drag operation via the pan cancel event.
|
||||||
|
final RectDragCancelEvent? onDragCancel;
|
||||||
|
|
||||||
|
/// A callback function that triggers when the box is about to start resizing.
|
||||||
|
final RectResizeStart? onResizeStart;
|
||||||
|
|
||||||
|
/// A callback that is called every time the [TransformableBox] is resized.
|
||||||
|
/// This is called every time the [TransformableBoxController] mutates the
|
||||||
|
/// box.
|
||||||
|
///
|
||||||
|
/// This is different from [onChanged] in that it is only called when the box
|
||||||
|
/// is resized, not when the box is moved.
|
||||||
|
final RectResizeUpdateEvent? onResizeUpdate;
|
||||||
|
|
||||||
|
/// A callback function that triggers when the box is about to end resizing.
|
||||||
|
final RectResizeEnd? onResizeEnd;
|
||||||
|
|
||||||
|
/// A callback function that triggers when the box cancels resizing.
|
||||||
|
final RectResizeCancel? onResizeCancel;
|
||||||
|
|
||||||
|
/// A callback function that triggers when the box reaches its minimum width
|
||||||
|
/// when resizing.
|
||||||
|
final TerminalEdgeEvent? onMinWidthReached;
|
||||||
|
|
||||||
|
/// A callback function that triggers when the box reaches its maximum width
|
||||||
|
/// when resizing.
|
||||||
|
final TerminalEdgeEvent? onMaxWidthReached;
|
||||||
|
|
||||||
|
/// A callback function that triggers when the box reaches its minimum height
|
||||||
|
/// when resizing.
|
||||||
|
final TerminalEdgeEvent? onMinHeightReached;
|
||||||
|
|
||||||
|
/// A callback function that triggers when the box reaches its maximum height
|
||||||
|
/// when resizing.
|
||||||
|
final TerminalEdgeEvent? onMaxHeightReached;
|
||||||
|
|
||||||
|
/// A callback function that triggers when the box reaches a terminal width
|
||||||
|
/// when resizing. A terminal width is a width that is either the minimum or
|
||||||
|
/// maximum width of the box.
|
||||||
|
///
|
||||||
|
/// This function combines both [onMinWidthReached] and [onMaxWidthReached]
|
||||||
|
/// into one callback function.
|
||||||
|
final TerminalAxisEvent? onTerminalWidthReached;
|
||||||
|
|
||||||
|
/// A callback function that triggers when the box reaches a terminal height
|
||||||
|
/// when resizing. A terminal height is a height that is either the minimum or
|
||||||
|
/// maximum height of the box.
|
||||||
|
///
|
||||||
|
/// This function combines both [onMinHeightReached] and [onMaxHeightReached]
|
||||||
|
/// into one callback function.
|
||||||
|
final TerminalAxisEvent? onTerminalHeightReached;
|
||||||
|
|
||||||
|
/// A callback function that triggers when the box reaches a terminal size
|
||||||
|
/// when resizing. A terminal size is a size that is either the minimum or
|
||||||
|
/// maximum size of the box on either axis.
|
||||||
|
///
|
||||||
|
/// This function combines both [onTerminalWidthReached] and
|
||||||
|
/// [onTerminalHeightReached] into one callback function.
|
||||||
|
final TerminalEvent? onTerminalSizeReached;
|
||||||
|
|
||||||
|
/// Whether to paint the handle's bounds for debugging purposes.
|
||||||
|
final bool debugPaintHandleBounds;
|
||||||
|
final double handleTapLeftSize;
|
||||||
|
/// Creates a [TransformableBox] widget.
|
||||||
|
const TransformableBox({
|
||||||
|
super.key,
|
||||||
|
required this.contentBuilder,
|
||||||
|
this.controller,
|
||||||
|
this.cornerHandleBuilder = _defaultCornerHandleBuilder,
|
||||||
|
this.sideHandleBuilder = _defaultSideHandleBuilder,
|
||||||
|
this.handleTapSize = 24,
|
||||||
|
this.handleTapLeftSize = 24,
|
||||||
|
this.allowContentFlipping = true,
|
||||||
|
this.handleAlignment = HandleAlignment.center,
|
||||||
|
this.enabledHandles = const {...HandlePosition.values},
|
||||||
|
this.visibleHandles = const {...HandlePosition.values},
|
||||||
|
|
||||||
|
// Raw values.
|
||||||
|
Rect? rect,
|
||||||
|
Flip? flip,
|
||||||
|
Rect? clampingRect,
|
||||||
|
BoxConstraints? constraints,
|
||||||
|
ValueGetter<ResizeMode>? resizeModeResolver,
|
||||||
|
|
||||||
|
// Additional controls.
|
||||||
|
this.resizable = true,
|
||||||
|
this.draggable = true,
|
||||||
|
this.allowFlippingWhileResizing = true,
|
||||||
|
|
||||||
|
// Either resize or drag triggers.
|
||||||
|
this.onChanged,
|
||||||
|
|
||||||
|
// Resize events
|
||||||
|
this.onResizeStart,
|
||||||
|
this.onResizeUpdate,
|
||||||
|
this.onResizeEnd,
|
||||||
|
this.onResizeCancel,
|
||||||
|
|
||||||
|
// Drag Events.
|
||||||
|
this.onDragStart,
|
||||||
|
this.onDragUpdate,
|
||||||
|
this.onDragEnd,
|
||||||
|
this.onDragCancel,
|
||||||
|
|
||||||
|
// Terminal update events.
|
||||||
|
this.onMinWidthReached,
|
||||||
|
this.onMaxWidthReached,
|
||||||
|
this.onMinHeightReached,
|
||||||
|
this.onMaxHeightReached,
|
||||||
|
this.onTerminalWidthReached,
|
||||||
|
this.onTerminalHeightReached,
|
||||||
|
this.onTerminalSizeReached,
|
||||||
|
this.debugPaintHandleBounds = false,
|
||||||
|
}) : assert(
|
||||||
|
(controller == null) ||
|
||||||
|
((rect == null) &&
|
||||||
|
(flip == null) &&
|
||||||
|
(clampingRect == null) &&
|
||||||
|
(constraints == null) &&
|
||||||
|
(resizeModeResolver == null)),
|
||||||
|
'If a controller is provided, the raw values should not be provided.',
|
||||||
|
),
|
||||||
|
rect = rect ?? Rect.zero,
|
||||||
|
flip = flip ?? Flip.none,
|
||||||
|
clampingRect = clampingRect ?? Rect.largest,
|
||||||
|
constraints = constraints ?? const BoxConstraints.expand(),
|
||||||
|
resizeModeResolver = resizeModeResolver ?? defaultResizeModeResolver;
|
||||||
|
|
||||||
|
/// Returns the [TransformableBox] of the closest ancestor.
|
||||||
|
static TransformableBox? widgetOf(BuildContext context) {
|
||||||
|
return context.findAncestorWidgetOfExactType<TransformableBox>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the [TransformableBoxController] of the closest ancestor.
|
||||||
|
static TransformableBoxController? controllerOf(BuildContext context) {
|
||||||
|
return context
|
||||||
|
.findAncestorStateOfType<_TransformableBoxState>()
|
||||||
|
?.controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<TransformableBox> createState() => _TransformableBoxState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TransformableBoxState extends State<TransformableBox> {
|
||||||
|
late TransformableBoxController controller;
|
||||||
|
|
||||||
|
bool isLegalGesture = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
if (widget.controller != null) {
|
||||||
|
controller = widget.controller!;
|
||||||
|
// We only want to listen to the controller if it is provided externally.
|
||||||
|
controller.addListener(onControllerUpdate);
|
||||||
|
} else {
|
||||||
|
// If it is provided internally, we should not listen to it.
|
||||||
|
controller = TransformableBoxController(
|
||||||
|
rect: widget.rect,
|
||||||
|
flip: widget.flip,
|
||||||
|
clampingRect: widget.clampingRect,
|
||||||
|
constraints: widget.constraints,
|
||||||
|
resizeModeResolver: widget.resizeModeResolver,
|
||||||
|
allowFlippingWhileResizing: widget.allowFlippingWhileResizing,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant TransformableBox oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
if (widget.controller != null && oldWidget.controller == null ||
|
||||||
|
widget.controller != oldWidget.controller) {
|
||||||
|
// New explicit controller provided or explicit controller changed.
|
||||||
|
controller.removeListener(onControllerUpdate);
|
||||||
|
controller = widget.controller!;
|
||||||
|
controller.addListener(onControllerUpdate);
|
||||||
|
} else if (oldWidget.controller != null && widget.controller == null) {
|
||||||
|
// Explicit controller removed.
|
||||||
|
controller.removeListener(onControllerUpdate);
|
||||||
|
controller = TransformableBoxController(
|
||||||
|
rect: widget.rect,
|
||||||
|
flip: widget.flip,
|
||||||
|
clampingRect: widget.clampingRect,
|
||||||
|
constraints: widget.constraints,
|
||||||
|
resizeModeResolver: widget.resizeModeResolver,
|
||||||
|
allowFlippingWhileResizing: widget.allowFlippingWhileResizing,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if the controller is external.
|
||||||
|
if (widget.controller != null) return;
|
||||||
|
|
||||||
|
// Below code should only be executed if the controller is internal.
|
||||||
|
bool shouldRecalculatePosition = false;
|
||||||
|
bool shouldRecalculateSize = false;
|
||||||
|
|
||||||
|
if (oldWidget.rect != widget.rect) {
|
||||||
|
controller.setRect(widget.rect, notify: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldWidget.flip != widget.flip) {
|
||||||
|
controller.setFlip(widget.flip, notify: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldWidget.resizeModeResolver != widget.resizeModeResolver) {
|
||||||
|
controller.setResizeModeResolver(
|
||||||
|
widget.resizeModeResolver,
|
||||||
|
notify: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldWidget.clampingRect != widget.clampingRect) {
|
||||||
|
controller.setClampingRect(widget.clampingRect, notify: false);
|
||||||
|
shouldRecalculatePosition = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldWidget.constraints != widget.constraints) {
|
||||||
|
controller.setConstraints(widget.constraints, notify: false);
|
||||||
|
shouldRecalculateSize = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldWidget.allowFlippingWhileResizing !=
|
||||||
|
widget.allowFlippingWhileResizing) {
|
||||||
|
controller.setAllowFlippingWhileResizing(
|
||||||
|
widget.allowFlippingWhileResizing,
|
||||||
|
notify: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldRecalculatePosition) {
|
||||||
|
controller.recalculatePosition(notify: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldRecalculateSize) {
|
||||||
|
controller.recalculateSize(notify: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
controller.removeListener(onControllerUpdate);
|
||||||
|
if (widget.controller == null) controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the controller is updated.
|
||||||
|
void onControllerUpdate() {
|
||||||
|
if (widget.rect != controller.rect || widget.flip != controller.flip) {
|
||||||
|
if (mounted) setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the handle drag starts.
|
||||||
|
void onHandlePanStart(DragStartDetails event, HandlePosition handle) {
|
||||||
|
// Two fingers were used to start the drag. This produces issues with
|
||||||
|
// the box drag event. Therefore, we ignore it.
|
||||||
|
if (event.kind == PointerDeviceKind.trackpad) {
|
||||||
|
isLegalGesture = false;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
isLegalGesture = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.onResizeStart(event.localPosition);
|
||||||
|
widget.onResizeStart?.call(handle, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the handle drag updates.
|
||||||
|
void onHandlePanUpdate(DragUpdateDetails event, HandlePosition handle) {
|
||||||
|
if (!isLegalGesture) return;
|
||||||
|
|
||||||
|
final UIResizeResult result = controller.onResizeUpdate(
|
||||||
|
event.localPosition,
|
||||||
|
handle,
|
||||||
|
);
|
||||||
|
|
||||||
|
widget.onChanged?.call(result, event);
|
||||||
|
widget.onResizeUpdate?.call(result, event);
|
||||||
|
widget.onMinWidthReached?.call(result.minWidthReached);
|
||||||
|
widget.onMaxWidthReached?.call(result.maxWidthReached);
|
||||||
|
widget.onMinHeightReached?.call(result.minHeightReached);
|
||||||
|
widget.onMaxHeightReached?.call(result.maxHeightReached);
|
||||||
|
widget.onTerminalWidthReached?.call(
|
||||||
|
result.minWidthReached,
|
||||||
|
result.maxWidthReached,
|
||||||
|
);
|
||||||
|
widget.onTerminalHeightReached?.call(
|
||||||
|
result.minHeightReached,
|
||||||
|
result.maxHeightReached,
|
||||||
|
);
|
||||||
|
widget.onTerminalSizeReached?.call(
|
||||||
|
result.minWidthReached,
|
||||||
|
result.maxWidthReached,
|
||||||
|
result.minHeightReached,
|
||||||
|
result.maxHeightReached,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the handle drag ends.
|
||||||
|
void onHandlePanEnd(DragEndDetails event, HandlePosition handle) {
|
||||||
|
if (!isLegalGesture) return;
|
||||||
|
|
||||||
|
controller.onResizeEnd();
|
||||||
|
widget.onResizeEnd?.call(handle, event);
|
||||||
|
widget.onMinWidthReached?.call(false);
|
||||||
|
widget.onMaxWidthReached?.call(false);
|
||||||
|
widget.onMinHeightReached?.call(false);
|
||||||
|
widget.onMaxHeightReached?.call(false);
|
||||||
|
widget.onTerminalWidthReached?.call(false, false);
|
||||||
|
widget.onTerminalHeightReached?.call(false, false);
|
||||||
|
widget.onTerminalSizeReached?.call(false, false, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onHandlePanCancel(HandlePosition handle) {
|
||||||
|
if (!isLegalGesture) return;
|
||||||
|
|
||||||
|
controller.onResizeEnd();
|
||||||
|
widget.onResizeCancel?.call(handle);
|
||||||
|
widget.onMinWidthReached?.call(false);
|
||||||
|
widget.onMaxWidthReached?.call(false);
|
||||||
|
widget.onMinHeightReached?.call(false);
|
||||||
|
widget.onMaxHeightReached?.call(false);
|
||||||
|
widget.onTerminalWidthReached?.call(false, false);
|
||||||
|
widget.onTerminalHeightReached?.call(false, false);
|
||||||
|
widget.onTerminalSizeReached?.call(false, false, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the box drag event starts.
|
||||||
|
void onDragPanStart(DragStartDetails event) {
|
||||||
|
// Two fingers were used to start the drag. This produces issues with
|
||||||
|
// the box drag event. Therefore, we ignore it.
|
||||||
|
if (event.kind == PointerDeviceKind.trackpad) {
|
||||||
|
isLegalGesture = false;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
isLegalGesture = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.onDragStart(event.localPosition);
|
||||||
|
widget.onDragStart?.call(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the box drag event updates.
|
||||||
|
void onDragPanUpdate(DragUpdateDetails event) {
|
||||||
|
if (!isLegalGesture) return;
|
||||||
|
|
||||||
|
final UIMoveResult result = controller.onDragUpdate(
|
||||||
|
event.localPosition,
|
||||||
|
);
|
||||||
|
|
||||||
|
widget.onChanged?.call(result, event);
|
||||||
|
widget.onDragUpdate?.call(result, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the box drag event ends.
|
||||||
|
void onDragPanEnd(DragEndDetails event) {
|
||||||
|
if (!isLegalGesture) return;
|
||||||
|
|
||||||
|
controller.onDragEnd();
|
||||||
|
widget.onDragEnd?.call(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDragPanCancel() {
|
||||||
|
if (!isLegalGesture) return;
|
||||||
|
|
||||||
|
controller.onDragEnd();
|
||||||
|
widget.onDragCancel?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final Flip flip = controller.flip;
|
||||||
|
final Rect rect = controller.rect;
|
||||||
|
|
||||||
|
Widget content = Transform.scale(
|
||||||
|
scaleX: widget.allowContentFlipping && flip.isHorizontal ? -1 : 1,
|
||||||
|
scaleY: widget.allowContentFlipping && flip.isVertical ? -1 : 1,
|
||||||
|
child: widget.contentBuilder(context, rect, flip),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (widget.draggable) {
|
||||||
|
content = GestureDetector(
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onPanStart: onDragPanStart,
|
||||||
|
onPanUpdate: onDragPanUpdate,
|
||||||
|
onPanEnd: onDragPanEnd,
|
||||||
|
onPanCancel: onDragPanCancel,
|
||||||
|
child: content,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
|
child: Stack(
|
||||||
|
clipBehavior: Clip.none,
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
Positioned(
|
||||||
|
left: widget.handleAlignment.offset(widget.handleTapLeftSize),
|
||||||
|
top: widget.handleAlignment.offset(widget.handleTapSize),
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
|
child: content,
|
||||||
|
),
|
||||||
|
if (widget.resizable)
|
||||||
|
for (final handle in HandlePosition.corners.where((handle) =>
|
||||||
|
widget.visibleHandles.contains(handle) ||
|
||||||
|
widget.enabledHandles.contains(handle)))
|
||||||
|
CornerHandleWidget(
|
||||||
|
key: ValueKey(handle),
|
||||||
|
handlePosition: handle,
|
||||||
|
handleTapSize: widget.handleTapSize,
|
||||||
|
enabled: widget.enabledHandles.contains(handle),
|
||||||
|
visible: widget.visibleHandles.contains(handle),
|
||||||
|
onPanStart: (event) => onHandlePanStart(event, handle),
|
||||||
|
onPanUpdate: (event) => onHandlePanUpdate(event, handle),
|
||||||
|
onPanEnd: (event) => onHandlePanEnd(event, handle),
|
||||||
|
onPanCancel: () => onHandlePanCancel(handle),
|
||||||
|
builder: widget.cornerHandleBuilder,
|
||||||
|
),
|
||||||
|
if (widget.resizable)
|
||||||
|
for (final handle in HandlePosition.sides.where((handle) =>
|
||||||
|
widget.visibleHandles.contains(handle) ||
|
||||||
|
widget.enabledHandles.contains(handle)))
|
||||||
|
SideHandleWidget(
|
||||||
|
key: ValueKey(handle),
|
||||||
|
handlePosition: handle,
|
||||||
|
handleTapSize: widget.handleTapSize,
|
||||||
|
enabled: widget.enabledHandles.contains(handle),
|
||||||
|
visible: widget.visibleHandles.contains(handle),
|
||||||
|
onPanStart: (event) => onHandlePanStart(event, handle),
|
||||||
|
onPanUpdate: (event) => onHandlePanUpdate(event, handle),
|
||||||
|
onPanEnd: (event) => onHandlePanEnd(event, handle),
|
||||||
|
onPanCancel: () => onHandlePanCancel(handle),
|
||||||
|
builder: widget.sideHandleBuilder,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
) ,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A default implementation of the corner [HandleBuilder] callback.
|
||||||
|
Widget _defaultCornerHandleBuilder(
|
||||||
|
BuildContext context,
|
||||||
|
HandlePosition handle,
|
||||||
|
) =>
|
||||||
|
DefaultCornerHandle(handle: handle);
|
||||||
|
|
||||||
|
/// A default implementation of the side [HandleBuilder] callback.
|
||||||
|
Widget _defaultSideHandleBuilder(
|
||||||
|
BuildContext context,
|
||||||
|
HandlePosition handle,
|
||||||
|
) =>
|
||||||
|
DefaultSideHandle(handle: handle);
|
||||||
|
|
||||||
|
|
||||||
|
@protected
|
||||||
|
class CornerHandleWidget extends StatelessWidget {
|
||||||
|
/// The position of the handle.
|
||||||
|
final HandlePosition handlePosition;
|
||||||
|
|
||||||
|
/// The builder that is used to build the handle widget.
|
||||||
|
final HandleBuilder builder;
|
||||||
|
|
||||||
|
/// The size of the handle's gesture response area.
|
||||||
|
final double handleTapSize;
|
||||||
|
|
||||||
|
/// Called when the handle dragging starts.
|
||||||
|
final GestureDragStartCallback? onPanStart;
|
||||||
|
|
||||||
|
/// Called when the handle dragging is updated.
|
||||||
|
final GestureDragUpdateCallback? onPanUpdate;
|
||||||
|
|
||||||
|
/// Called when the handle dragging ends.
|
||||||
|
final GestureDragEndCallback? onPanEnd;
|
||||||
|
|
||||||
|
/// Called when the handle dragging is canceled.
|
||||||
|
final GestureDragCancelCallback? onPanCancel;
|
||||||
|
|
||||||
|
/// Whether the handle is resizable.
|
||||||
|
final bool enabled;
|
||||||
|
|
||||||
|
/// Whether the handle is visible.
|
||||||
|
final bool visible;
|
||||||
|
|
||||||
|
/// Whether to paint the handle's bounds for debugging purposes.
|
||||||
|
final bool debugPaintHandleBounds;
|
||||||
|
|
||||||
|
/// Creates a new handle widget.
|
||||||
|
CornerHandleWidget({
|
||||||
|
super.key,
|
||||||
|
required this.handlePosition,
|
||||||
|
required this.handleTapSize,
|
||||||
|
required this.builder,
|
||||||
|
this.onPanStart,
|
||||||
|
this.onPanUpdate,
|
||||||
|
this.onPanEnd,
|
||||||
|
this.onPanCancel,
|
||||||
|
this.enabled = true,
|
||||||
|
this.visible = true,
|
||||||
|
this.debugPaintHandleBounds = false,
|
||||||
|
}) : assert(handlePosition.isDiagonal, 'A corner handle must be diagonal.');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget child =
|
||||||
|
visible ? builder(context, handlePosition) : const SizedBox.shrink();
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
child = GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onPanStart: onPanStart,
|
||||||
|
onPanUpdate: onPanUpdate,
|
||||||
|
onPanEnd: onPanEnd,
|
||||||
|
onPanCancel: onPanCancel,
|
||||||
|
child: MouseRegion(
|
||||||
|
cursor: getCursorForHandle(handlePosition),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Positioned(
|
||||||
|
left: handlePosition.influencesLeft ? 0 : null,
|
||||||
|
right: handlePosition.influencesRight ? 0 : null,
|
||||||
|
top: handlePosition.influencesTop ? 0 : null,
|
||||||
|
bottom: handlePosition.influencesBottom ? 0 : null,
|
||||||
|
width: handleTapSize,
|
||||||
|
height: handleTapSize,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the cursor for the given handle position.
|
||||||
|
MouseCursor getCursorForHandle(HandlePosition handle) {
|
||||||
|
switch (handle) {
|
||||||
|
case HandlePosition.topLeft:
|
||||||
|
case HandlePosition.bottomRight:
|
||||||
|
return SystemMouseCursors.resizeUpLeftDownRight;
|
||||||
|
case HandlePosition.topRight:
|
||||||
|
case HandlePosition.bottomLeft:
|
||||||
|
return SystemMouseCursors.resizeUpRightDownLeft;
|
||||||
|
default:
|
||||||
|
throw Exception('Invalid handle position.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new cardinal handle widget, with its appropriate gesture splash
|
||||||
|
/// zone.
|
||||||
|
@protected
|
||||||
|
class SideHandleWidget extends StatelessWidget {
|
||||||
|
/// The position of the handle.
|
||||||
|
final HandlePosition handlePosition;
|
||||||
|
|
||||||
|
/// The builder that is used to build the handle widget.
|
||||||
|
final HandleBuilder builder;
|
||||||
|
|
||||||
|
/// The thickness of the handle that is used for gesture detection.
|
||||||
|
final double handleTapSize;
|
||||||
|
|
||||||
|
/// Called when the handle dragging starts.
|
||||||
|
final GestureDragStartCallback? onPanStart;
|
||||||
|
|
||||||
|
/// Called when the handle dragging is updated.
|
||||||
|
final GestureDragUpdateCallback? onPanUpdate;
|
||||||
|
|
||||||
|
/// Called when the handle dragging ends.
|
||||||
|
final GestureDragEndCallback? onPanEnd;
|
||||||
|
|
||||||
|
/// Called when the handle dragging is canceled.
|
||||||
|
final GestureDragCancelCallback? onPanCancel;
|
||||||
|
|
||||||
|
/// Whether the handle is resizable.
|
||||||
|
final bool enabled;
|
||||||
|
|
||||||
|
/// Whether the handle is visible.
|
||||||
|
final bool visible;
|
||||||
|
|
||||||
|
/// Whether to paint the handle's bounds for debugging purposes.
|
||||||
|
final bool debugPaintHandleBounds;
|
||||||
|
|
||||||
|
/// Creates a new handle widget.
|
||||||
|
SideHandleWidget({
|
||||||
|
super.key,
|
||||||
|
required this.handlePosition,
|
||||||
|
required this.handleTapSize,
|
||||||
|
required this.builder,
|
||||||
|
this.onPanStart,
|
||||||
|
this.onPanUpdate,
|
||||||
|
this.onPanEnd,
|
||||||
|
this.onPanCancel,
|
||||||
|
this.enabled = true,
|
||||||
|
this.visible = true,
|
||||||
|
this.debugPaintHandleBounds = false,
|
||||||
|
}) : assert(handlePosition.isSide, 'A cardinal handle must be cardinal.');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget child =
|
||||||
|
visible ? builder(context, handlePosition) : const SizedBox.shrink();
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
child = GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onPanStart: onPanStart,
|
||||||
|
onPanUpdate: onPanUpdate,
|
||||||
|
onPanEnd: onPanEnd,
|
||||||
|
onPanCancel: onPanCancel,
|
||||||
|
child: MouseRegion(
|
||||||
|
cursor: getCursorForHandle(handlePosition),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Positioned(
|
||||||
|
left: handlePosition.isVertical
|
||||||
|
? handleTapSize
|
||||||
|
: handlePosition.influencesLeft
|
||||||
|
? 0
|
||||||
|
: null,
|
||||||
|
right: handlePosition.isVertical
|
||||||
|
? handleTapSize
|
||||||
|
: handlePosition.influencesRight
|
||||||
|
? 0
|
||||||
|
: null,
|
||||||
|
top: handlePosition.isHorizontal
|
||||||
|
? handleTapSize
|
||||||
|
: handlePosition.influencesTop
|
||||||
|
? 0
|
||||||
|
: null,
|
||||||
|
bottom: handlePosition.isHorizontal
|
||||||
|
? handleTapSize
|
||||||
|
: handlePosition.influencesBottom
|
||||||
|
? 0
|
||||||
|
: null,
|
||||||
|
width: handlePosition.isHorizontal ? handleTapSize : null,
|
||||||
|
height: handlePosition.isVertical ? handleTapSize : null,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the cursor for the given handle position.
|
||||||
|
MouseCursor getCursorForHandle(HandlePosition handle) {
|
||||||
|
switch (handle) {
|
||||||
|
case HandlePosition.left:
|
||||||
|
case HandlePosition.right:
|
||||||
|
return SystemMouseCursors.resizeLeftRight;
|
||||||
|
case HandlePosition.top:
|
||||||
|
case HandlePosition.bottom:
|
||||||
|
return SystemMouseCursors.resizeUpDown;
|
||||||
|
default:
|
||||||
|
throw Exception('Invalid handle position.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,28 @@
|
|||||||
import 'package:alert_banner/exports.dart';
|
import 'package:alert_banner/exports.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:json_string/json_string.dart';
|
||||||
|
import 'package:oc_front/core/sections/header/header.dart';
|
||||||
import 'package:oc_front/models/logs.dart';
|
import 'package:oc_front/models/logs.dart';
|
||||||
import 'package:oc_front/widgets/dialog/alert.dart';
|
import 'package:oc_front/widgets/dialog/alert.dart';
|
||||||
|
|
||||||
class LogsWidget extends StatefulWidget {
|
class LogsWidget extends StatefulWidget {
|
||||||
final List<Log> items;
|
final List<Log> items;
|
||||||
LogsWidget ({ Key? key, required this.items }): super(key: key);
|
String? level;
|
||||||
|
String search = "";
|
||||||
|
LogsWidget ({ Key? key, this.search = "", required this.items, this.level }): super(key: key);
|
||||||
@override LogsWidgetState createState() => LogsWidgetState();
|
@override LogsWidgetState createState() => LogsWidgetState();
|
||||||
}
|
}
|
||||||
class LogsWidgetState extends State<LogsWidget> {
|
class LogsWidgetState extends State<LogsWidget> {
|
||||||
@override Widget build(BuildContext context) {
|
@override Widget build(BuildContext context) {
|
||||||
List<LogWidget> itemRows = widget.items.map((e) => LogWidget(item: e)).toList();
|
List<LogWidget> itemRows = widget.items.where((element) => (element.message?.toLowerCase().contains(widget.search.toLowerCase()) ?? true)
|
||||||
return SingleChildScrollView( child: Column( children: itemRows ) );
|
&& (widget.level?.contains(element.level ?? "") ?? true) ).map((e) => LogWidget(item: e)).toList();
|
||||||
|
return Stack( children: [
|
||||||
|
SingleChildScrollView( child: itemRows.isEmpty ?
|
||||||
|
Container( height: MediaQuery.of(context).size.height - 100 - HeaderConstants.height,
|
||||||
|
child: const Center( child: Text("no log registered", style: TextStyle(color: Colors.grey, fontSize: 25 ),)))
|
||||||
|
: Column( children: [...itemRows, Container(height: 50,) ] ) ),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,23 +34,31 @@ class LogWidget extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
class LogWidgetState extends State<LogWidget> {
|
class LogWidgetState extends State<LogWidget> {
|
||||||
@override Widget build(BuildContext context) {
|
@override Widget build(BuildContext context) {
|
||||||
return Padding( padding: const EdgeInsets.only(top: 10, left: 30, right: 30), child: Wrap( children: [
|
Map<String, dynamic> map = {};
|
||||||
|
try { map = JsonString(widget.item.rawMessage?.replaceAll("\\", "") ?? "").decodedValue as Map<String, dynamic>;
|
||||||
|
} catch (e) { /* */}
|
||||||
|
return Container(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
border: Border(bottom: BorderSide(color: Colors.white)),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.only(top: 10, left: 30, right: 30, bottom: 10),
|
||||||
|
child: Wrap( children: [
|
||||||
Row( mainAxisAlignment: MainAxisAlignment.start,
|
Row( mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Container( width: 10, height: 15, color: widget.item.level?.toLowerCase() == "info" ? Colors.green :
|
Container( width: 10, height: 15, color: widget.item.level?.toLowerCase() == "info" ? Colors.green :
|
||||||
( widget.item.level?.toLowerCase() == "error" ? Colors.red : (
|
( widget.item.level?.toLowerCase() == "error" ? Colors.red : (
|
||||||
widget.item.level?.toLowerCase() == "warning" ? Colors.orange : Colors.blue))),
|
widget.item.level?.toLowerCase() == "warning" ? Colors.orange : Colors.blue))),
|
||||||
InkWell( mouseCursor: widget.item.map.isEmpty ? MouseCursor.defer : SystemMouseCursors.click, onTap: () {
|
InkWell( mouseCursor: map.isEmpty ? MouseCursor.defer : SystemMouseCursors.click, onTap: () {
|
||||||
if (widget.item.map.isNotEmpty ) {
|
if (map.isNotEmpty ) {
|
||||||
setState(() {
|
setState(() {
|
||||||
widget.expanded = !widget.expanded;
|
widget.expanded = !widget.expanded;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, child: Container( height: 20,
|
}, child: SizedBox( height: 20,
|
||||||
child: Padding( padding: EdgeInsets.symmetric(horizontal: widget.expanded ? 0 : 5),
|
child: Padding( padding: EdgeInsets.symmetric(horizontal: widget.expanded ? 0 : 5),
|
||||||
child: Icon( widget.expanded ? Icons.keyboard_arrow_down_outlined : Icons.arrow_forward_ios, size: widget.expanded ? 25 : 15,
|
child: Icon( widget.expanded ? Icons.keyboard_arrow_down_outlined : Icons.arrow_forward_ios, size: widget.expanded ? 25 : 15,
|
||||||
color: widget.item.map.isEmpty ? Colors.grey : Colors.black, weight: widget.expanded ? 100 : 1000,)))),
|
color: map.isEmpty ? Colors.grey : Colors.black, weight: widget.expanded ? 100 : 1000,)))),
|
||||||
Padding( padding: const EdgeInsets.only(right: 10),
|
Padding( padding: const EdgeInsets.only(right: 10),
|
||||||
child: Text("${widget.item.timestamp?.toString()}",
|
child: Text("${widget.item.timestamp?.toString()}",
|
||||||
style: const TextStyle(fontSize: 13, color: Colors.black, fontWeight: FontWeight.w500))),
|
style: const TextStyle(fontSize: 13, color: Colors.black, fontWeight: FontWeight.w500))),
|
||||||
@ -57,10 +75,10 @@ class LogWidgetState extends State<LogWidget> {
|
|||||||
decoration: BoxDecoration( color: Colors.grey,
|
decoration: BoxDecoration( color: Colors.grey,
|
||||||
borderRadius: BorderRadius.circular(4)),
|
borderRadius: BorderRadius.circular(4)),
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: Column( children: widget.item.map.keys.map((e) =>
|
child: Column( children: map.keys.map((e) =>
|
||||||
Padding( padding: const EdgeInsets.all(2), child: Row( mainAxisAlignment: MainAxisAlignment.start,
|
Padding( padding: const EdgeInsets.all(2), child: Row( mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [Flexible( child:Text("$e: \"${widget.item.map[e]}\"",
|
children: [Flexible( child:Text("$e: \"${map[e]}\"",
|
||||||
style: const TextStyle(fontSize: 11, color: Colors.white))), ])
|
style: const TextStyle(fontSize: 11, color: Colors.white))), ])
|
||||||
)).toList()
|
)).toList()
|
||||||
)) : Container(),
|
)) : Container(),
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
import 'dart:ffi';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:oc_front/core/models/workspace_local.dart';
|
import 'package:oc_front/core/models/workspace_local.dart';
|
||||||
class MenuWorkspaceWidget extends StatefulWidget {
|
class MenuWorkspaceWidget extends StatefulWidget {
|
||||||
|
@ -1,26 +1,34 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_box_transform/flutter_box_transform.dart';
|
||||||
import 'package:oc_front/core/sections/header/header.dart';
|
import 'package:oc_front/core/sections/header/header.dart';
|
||||||
import 'package:oc_front/core/services/specialized_services/logs_service.dart';
|
import 'package:oc_front/core/services/specialized_services/logs_service.dart';
|
||||||
import 'package:oc_front/models/logs.dart';
|
import 'package:oc_front/models/logs.dart';
|
||||||
import 'package:oc_front/models/workflow.dart';
|
import 'package:oc_front/models/workflow.dart';
|
||||||
import 'package:oc_front/widgets/logs.dart';
|
import 'package:oc_front/widgets/logs.dart';
|
||||||
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
|
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_calendar.dart';
|
||||||
import 'package:oc_front/widgets/sheduler_items/scheduler_item.dart';
|
import 'package:oc_front/widgets/sheduler_items/scheduler_item.dart';
|
||||||
|
|
||||||
|
double menuSize = 300;
|
||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
class ScheduleWidget extends StatefulWidget {
|
class ScheduleWidget extends StatefulWidget {
|
||||||
DateTime start;
|
DateTime start;
|
||||||
DateTime end;
|
DateTime end;
|
||||||
bool isDayPlanner = true;
|
bool isDayPlanner = true;
|
||||||
|
bool loading = true;
|
||||||
Map<String, List<WorkflowExecution>> data;
|
Map<String, List<WorkflowExecution>> data;
|
||||||
bool isList = true;
|
bool isList = true;
|
||||||
ScheduleWidget ({ super.key, required this.data, required this.start, required this.end, this.isList = true });
|
ScheduleWidget ({ super.key, required this.data, required this.start, required this.end, this.isList = true, this.loading = false});
|
||||||
@override ScheduleWidgetState createState() => ScheduleWidgetState();
|
@override ScheduleWidgetState createState() => ScheduleWidgetState();
|
||||||
}
|
}
|
||||||
|
String? selected;
|
||||||
|
String? selectedReal;
|
||||||
class ScheduleWidgetState extends State<ScheduleWidget> {
|
class ScheduleWidgetState extends State<ScheduleWidget> {
|
||||||
LogsService _service = LogsService();
|
LogsService _service = LogsService();
|
||||||
String? selected;
|
String search = "";
|
||||||
String? selectedReal;
|
String? level;
|
||||||
List<Color> colors = [Colors.blue, Colors.orange, Colors.red, Colors.green];
|
List<Color> colors = [Colors.blue, Colors.orange, Colors.red, Colors.green];
|
||||||
List<String> titles = ["SCHEDULED", "RUNNING", "FAILURE", "SUCCESS"];
|
List<String> titles = ["SCHEDULED", "RUNNING", "FAILURE", "SUCCESS"];
|
||||||
|
|
||||||
@ -31,7 +39,7 @@ class ScheduleWidgetState extends State<ScheduleWidget> {
|
|||||||
|
|
||||||
@override Widget build(BuildContext context) {
|
@override Widget build(BuildContext context) {
|
||||||
bool isInfo = MediaQuery.of(context).size.width <= 600 && selected != null;
|
bool isInfo = MediaQuery.of(context).size.width <= 600 && selected != null;
|
||||||
double w = selected != null ? MediaQuery.of(context).size.width - 300 : MediaQuery.of(context).size.width;
|
double w = selected != null ? MediaQuery.of(context).size.width - menuSize : MediaQuery.of(context).size.width;
|
||||||
List<Widget> children = [];
|
List<Widget> children = [];
|
||||||
if (selected != null) {
|
if (selected != null) {
|
||||||
for (var wf in widget.data[selected!] ?? (<WorkflowExecution>[])) {
|
for (var wf in widget.data[selected!] ?? (<WorkflowExecution>[])) {
|
||||||
@ -40,7 +48,8 @@ class ScheduleWidgetState extends State<ScheduleWidget> {
|
|||||||
onTap: () => setState(() { selectedReal = wf.executionData; }),
|
onTap: () => setState(() { selectedReal = wf.executionData; }),
|
||||||
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 ? const Color.fromRGBO(38, 166, 154, 1) : Colors.transparent, width: 2),
|
border: Border.all(color: selectedReal != null && selectedReal == wf.executionData ?
|
||||||
|
const Color.fromRGBO(38, 166, 154, 1) : Colors.transparent, width: 2),
|
||||||
borderRadius: BorderRadius.circular(4), color: Colors.white
|
borderRadius: BorderRadius.circular(4), color: Colors.white
|
||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -52,13 +61,13 @@ class ScheduleWidgetState extends State<ScheduleWidget> {
|
|||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container( width: (400 - 250),
|
SizedBox( width: (menuSize - 140),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 20),
|
padding: const EdgeInsets.only(left: 20),
|
||||||
child: Text(wf.name?.toUpperCase() ?? "", overflow: TextOverflow.ellipsis,
|
child: Text(wf.name?.toUpperCase() ?? "", overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(color: Colors.black, fontSize: 12, fontWeight: FontWeight.w500)),
|
style: const TextStyle(color: Colors.black, fontSize: 12, fontWeight: FontWeight.w500)),
|
||||||
)),
|
)),
|
||||||
Container(
|
SizedBox(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 20),
|
padding: const EdgeInsets.only(left: 20),
|
||||||
child: Text("${d2.hour > 9 ? d2.hour : "0${d2.hour}"}:${d2.minute > 9 ? d2.minute : "0${d2.minute}"}", overflow: TextOverflow.ellipsis,
|
child: Text("${d2.hour > 9 ? d2.hour : "0${d2.hour}"}:${d2.minute > 9 ? d2.minute : "0${d2.minute}"}", overflow: TextOverflow.ellipsis,
|
||||||
@ -70,48 +79,45 @@ class ScheduleWidgetState extends State<ScheduleWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
List<Log> logs = [];
|
||||||
String? selectedID;
|
String? selectedID;
|
||||||
String? start;
|
|
||||||
String? end;
|
|
||||||
if (selectedReal != null) {
|
if (selectedReal != null) {
|
||||||
try {
|
try {
|
||||||
var sel = widget.data[selected!]!.firstWhere((element) => element.executionData == selectedReal);
|
var sel = widget.data[selected!]!.firstWhere((element) => element.executionData == selectedReal);
|
||||||
selectedID = sel.id;
|
selectedID = sel.id;
|
||||||
print(sel.endDate);
|
logs = sel.logs ?? [];
|
||||||
if (sel.endDate != null && sel.endDate != "") {
|
|
||||||
var startD = DateTime.parse(sel.executionData!);
|
|
||||||
var endD = DateTime.parse(sel.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(sel.executionData!).subtract( const Duration(days: 14)).microsecondsSinceEpoch).toString();
|
|
||||||
end = (DateTime.parse(sel.executionData!).add( const Duration(days: 14)).microsecondsSinceEpoch).toString();
|
|
||||||
}
|
|
||||||
} catch(e) { /* */ }
|
} catch(e) { /* */ }
|
||||||
}
|
}
|
||||||
|
menuSize = isInfo ? MediaQuery.of(context).size.width : (menuSize > MediaQuery.of(context).size.width / 2 ? MediaQuery.of(context).size.width / 2 : menuSize);
|
||||||
|
Rect rect = Rect.fromCenter( center: MediaQuery.of(context).size.center(Offset.zero),
|
||||||
|
width: selected != null ? menuSize : 0, height: (MediaQuery.of(context).size.height - HeaderConstants.height - 50) > 0 ? (MediaQuery.of(context).size.height - HeaderConstants.height - 50) : 0);
|
||||||
return Row( children: [
|
return Row( children: [
|
||||||
isInfo ? Container() : SizedBox( width: w,
|
isInfo ? Container() : SizedBox( width: w,
|
||||||
child: widget.isList ? SchedulerItemWidget(data: widget.data, parent: this, focusedDay: getFocusedDay(), width: w)
|
child: widget.isList ? SchedulerItemWidget(data: widget.data, parent: this, focusedDay: getFocusedDay(), width: w)
|
||||||
: SchedulerCalendarWidget(data: widget.data, start: widget.start,
|
: SchedulerCalendarWidget(data: widget.data, start: widget.start,
|
||||||
end: widget.end, parent: this, focusedDay: getFocusedDay(),)
|
end: widget.end, parent: this, focusedDay: getFocusedDay(),)
|
||||||
),
|
),
|
||||||
Container(
|
fork.TransformableBox(
|
||||||
|
rect: rect, constraints: BoxConstraints(
|
||||||
|
maxWidth: isInfo ? MediaQuery.of(context).size.width : (selected != null ? MediaQuery.of(context).size.width / 2 : 0),
|
||||||
|
minWidth: selected != null ? 300 : 0),
|
||||||
|
handleTapSize: 1, handleTapLeftSize: 0, allowFlippingWhileResizing: false, draggable: false, flip: null,
|
||||||
|
resizeModeResolver: () => ResizeMode.freeform,
|
||||||
|
visibleHandles: const {HandlePosition.left},
|
||||||
|
enabledHandles: const {HandlePosition.left},
|
||||||
|
clampingRect: Offset.zero & MediaQuery.sizeOf(context),
|
||||||
|
handleAlignment: HandleAlignment.inside,
|
||||||
|
onChanged: (result, event) { setState(() { menuSize = result.rect.width; }); },
|
||||||
|
contentBuilder: (context, rect, flip) { return Container(
|
||||||
height: MediaQuery.of(context).size.height - HeaderConstants.height - 50,
|
height: MediaQuery.of(context).size.height - HeaderConstants.height - 50,
|
||||||
width: isInfo ? MediaQuery.of(context).size.width : (selected != null ? 300 : 0),
|
width: isInfo ? MediaQuery.of(context).size.width : (selected != null ? menuSize : 0),
|
||||||
color: Colors.grey.shade300,
|
color: Colors.grey.shade300,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Row( children: [
|
Row( children: [
|
||||||
InkWell( onTap: () => setState(() { widget.isDayPlanner = true; }),
|
InkWell( onTap: () => setState(() { widget.isDayPlanner = true; }),
|
||||||
child: Tooltip( message: "day planning", child:
|
child: Tooltip( message: "day planning", child:
|
||||||
Container( height: 50, width: (isInfo ? MediaQuery.of(context).size.width : (selected != null ? 300 : 0)) / (selectedReal != null ? 2 : 1 ),
|
Container( height: 50, width: (isInfo ? MediaQuery.of(context).size.width : (selected != null ? menuSize : 0)) / (selectedReal != null ? 2 : 1 ),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: widget.isDayPlanner ? Colors.grey : Colors.transparent,
|
color: widget.isDayPlanner ? Colors.grey : Colors.transparent,
|
||||||
@ -121,7 +127,8 @@ class ScheduleWidgetState extends State<ScheduleWidget> {
|
|||||||
)),
|
)),
|
||||||
InkWell( onTap: () => setState(() { widget.isDayPlanner = false; }),
|
InkWell( onTap: () => setState(() { widget.isDayPlanner = false; }),
|
||||||
child: Tooltip( message: "monitor task", child:
|
child: Tooltip( message: "monitor task", child:
|
||||||
Container( height: 50, width: selectedReal == null ? 0 : ((isInfo ? MediaQuery.of(context).size.width : (selected != null ? 300 : 0)) / 2),
|
Container( height: 50, width: selectedReal == null ? 0 : (
|
||||||
|
(isInfo ? MediaQuery.of(context).size.width : (selected != null ? menuSize : 0)) / 2),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: !widget.isDayPlanner ? Colors.grey : Colors.transparent,
|
color: !widget.isDayPlanner ? Colors.grey : Colors.transparent,
|
||||||
@ -132,48 +139,84 @@ class ScheduleWidgetState extends State<ScheduleWidget> {
|
|||||||
)
|
)
|
||||||
))
|
))
|
||||||
]),
|
]),
|
||||||
Container( width: isInfo ? MediaQuery.of(context).size.width : (selected != null ? 300 : 0), height: MediaQuery.of(context).size.height - HeaderConstants.height - 100,
|
SizedBox( width: isInfo ? MediaQuery.of(context).size.width : (selected != null ? menuSize : 0), height: MediaQuery.of(context).size.height - HeaderConstants.height - (!widget.isDayPlanner && !widget.loading ? 150 : 100 ),
|
||||||
child: SingleChildScrollView( child: Column(
|
child: Stack( children: [
|
||||||
mainAxisAlignment: children.isEmpty ? MainAxisAlignment.center : MainAxisAlignment.start,
|
SingleChildScrollView( child: Column(
|
||||||
|
mainAxisAlignment: children.isEmpty || widget.loading ? MainAxisAlignment.center : MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
...( widget.isDayPlanner ? children : ( selectedID != null ? [
|
...( widget.isDayPlanner ? children : ( selectedID != null ? [
|
||||||
FutureBuilder(future: _service.search(context, [], {
|
widget.loading ? const SpinKitCircle(color: Colors.white,) : LogsWidget(items: logs, search: search, level: level)
|
||||||
"workflow_execution_id": selectedID,
|
|
||||||
"start": start,
|
|
||||||
"end": end
|
|
||||||
}), builder: (ctx, as) {
|
|
||||||
var speLog = Log(level: "error", timestamp: DateTime.now());
|
|
||||||
speLog.getMessage("{\"Name\":\"oc-monitor-unonip-fauta9hswg\",\"Namespace\":\"argo\",\"Status\":\"Pending\",\"PodRunning\":false,\"Completed\":false,\"Created\":\"Tue Aug 06 11:33:52 +0200 (now)\",\"Started\":\"\",\"Duration\":\"\",\"Progress\":\"\"}");
|
|
||||||
var speLog2 = Log(level: "warning", timestamp: DateTime.now());
|
|
||||||
speLog2.getMessage("{\"Name\":\"oc-monitor-unonip-fauta9hswg\",\"Namespace\":\"argo\",\"Status\":\"Running\",\"PodRunning\":false,\"Completed\":false,\"Created\":\"Tue Aug 06 11:33:52 +0200 (now)\",\"Started\":\"Tue Aug 06 11:33:52 +0200 (now)\",\"Duration\":\"0 seconds\",\"Progress\":\"0/1\"}");
|
|
||||||
List<Log> logs = [
|
|
||||||
Log(
|
|
||||||
level: "info",
|
|
||||||
message: "No logs found",
|
|
||||||
timestamp: DateTime.now()
|
|
||||||
),
|
|
||||||
speLog,
|
|
||||||
speLog2
|
|
||||||
];
|
|
||||||
if (as.hasData && as.data!.data != null) {
|
|
||||||
var d = as.data!.data!;
|
|
||||||
for( var r in d.data?.result ?? <Logs> []) {
|
|
||||||
for (var element in r.logs) {
|
|
||||||
element.level = r.level;
|
|
||||||
logs.add(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logs.sort((a, b) => a.timestamp!.compareTo(b.timestamp!));
|
|
||||||
return LogsWidget(items: logs);
|
|
||||||
})
|
|
||||||
] : [])),
|
] : [])),
|
||||||
children.isEmpty ? Container( height: 100, alignment: Alignment.center, child: const Text("No event found", style: const TextStyle(color: Colors.grey, fontSize: 20))) : Container()
|
children.isEmpty ? Container( height: 100, alignment: Alignment.center, child: const Text("No event found", style: TextStyle(color: Colors.grey, fontSize: 20))) : Container()
|
||||||
]))
|
]),
|
||||||
|
) ])
|
||||||
|
),
|
||||||
|
!widget.isDayPlanner && !widget.loading ?
|
||||||
|
Row( children: [
|
||||||
|
Container(
|
||||||
|
width: 150,
|
||||||
|
height: 50,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(left: BorderSide(color: Colors.grey.shade300)),
|
||||||
|
),
|
||||||
|
child: DropdownButtonFormField(
|
||||||
|
isExpanded: true,
|
||||||
|
value: level,
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
hint: const Text("by level...", style: TextStyle(fontSize: 12)),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
filled: true,
|
||||||
|
focusedBorder: const OutlineInputBorder( borderRadius: BorderRadius.zero,
|
||||||
|
borderSide: BorderSide(color: Color.fromARGB(38, 166, 154, 1), width: 0),
|
||||||
|
),
|
||||||
|
fillColor: Colors.white,
|
||||||
|
contentPadding: const EdgeInsets.only(left: 30, right: 30, top: 10, bottom: 30),
|
||||||
|
enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.zero,
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300, width: 0),
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder( borderRadius: BorderRadius.zero,
|
||||||
|
borderSide: BorderSide(color: Colors.grey.shade300, width: 0)),
|
||||||
|
),
|
||||||
|
items: [
|
||||||
|
DropdownMenuItem(value: "debug,warning,error,info", child: Row( children: [ Container( width: 10, height: 15, color: Colors.grey), Text(" all", style: TextStyle(fontSize: 12, color: Colors.black)) ])),
|
||||||
|
DropdownMenuItem(value: "debug", child: Row( children: [ Container( width: 10, height: 15, color: Colors.blue), Text(" debug", style: TextStyle(fontSize: 12, color: Colors.black)) ])),
|
||||||
|
DropdownMenuItem(value: "warning", child: Row( children: [ Container( width: 10, height: 15, color: Colors.orange), Text(" warning", style: TextStyle(fontSize: 12, color: Colors.black)) ])),
|
||||||
|
DropdownMenuItem(value: "error", child: Row( children: [ Container( width: 10, height: 15, color: Colors.red), Text(" error", style: TextStyle(fontSize: 12, color: Colors.black)) ])),
|
||||||
|
DropdownMenuItem(value: "info", child: Row( children: [ Container( width: 10, height: 15, color: Colors.green), Text(" info", style: TextStyle(fontSize: 12, color: Colors.black)) ])),
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
level = value;
|
||||||
|
});
|
||||||
|
})),
|
||||||
|
Container(
|
||||||
|
width: menuSize - 150,
|
||||||
|
height: 50,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(left: BorderSide(color: Colors.grey.shade300)),
|
||||||
|
),
|
||||||
|
child: TextField(
|
||||||
|
onChanged: (value) { setState(() {
|
||||||
|
search = value;
|
||||||
|
});},
|
||||||
|
style: const TextStyle(fontSize: 12),
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: "by logs...",
|
||||||
|
fillColor: Colors.white,
|
||||||
|
filled: true,
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 30),
|
||||||
|
hintStyle: TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w300
|
||||||
|
),
|
||||||
|
border: InputBorder.none
|
||||||
)
|
)
|
||||||
|
)) ]
|
||||||
|
) : Container( ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
); })
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -50,13 +50,14 @@ class SchedulerCalendarWidgetState extends State<SchedulerCalendarWidget> {
|
|||||||
markerBuilder: (context, day, events) {
|
markerBuilder: (context, day, events) {
|
||||||
List<Widget> children = [];
|
List<Widget> children = [];
|
||||||
for (var ev in events) {
|
for (var ev in events) {
|
||||||
if (children.length == 2 && events.length > 3) {
|
if (children.length == 1 && events.length > 2) {
|
||||||
children.add( InkWell( onTap: () => widget.parent!.setState(() {
|
children.add( InkWell( onTap: () => widget.parent!.setState(() {
|
||||||
widget.parent!.selected = day.toIso8601String();
|
selected = day.toIso8601String();
|
||||||
widget.parent!.selectedReal = null;
|
selectedReal = null;
|
||||||
widget.parent!.widget.isDayPlanner = true;
|
widget.parent!.widget.isDayPlanner = true;
|
||||||
}),
|
}),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(bottom: 5),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
@ -67,13 +68,14 @@ class SchedulerCalendarWidgetState extends State<SchedulerCalendarWidget> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
children.add(InkWell( onTap: () => widget.parent!.setState(() {
|
children.add(InkWell( onTap: () => widget.parent!.setState(() {
|
||||||
widget.parent!.selected = day.toIso8601String();
|
selected = day.toIso8601String();
|
||||||
widget.parent!.selectedReal = ev.executionData;
|
selectedReal = ev.executionData;
|
||||||
if (widget.parent!.selectedReal == null) {
|
if (selectedReal == null) {
|
||||||
widget.parent!.widget.isDayPlanner = true;
|
widget.parent!.widget.isDayPlanner = true;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
margin: const EdgeInsets.only(bottom: 2.5, top: 25),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 2),
|
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
@ -146,8 +148,8 @@ class SchedulerCalendarWidgetState extends State<SchedulerCalendarWidget> {
|
|||||||
}),
|
}),
|
||||||
onDaySelected: (selectedDay, focusedDay) {
|
onDaySelected: (selectedDay, focusedDay) {
|
||||||
widget.parent!.setState(() {
|
widget.parent!.setState(() {
|
||||||
widget.parent!.selected = selectedDay.toIso8601String();
|
selected = selectedDay.toIso8601String();
|
||||||
widget.parent!.selectedReal = null;
|
selectedReal = null;
|
||||||
widget.parent!.widget.isDayPlanner = true;
|
widget.parent!.widget.isDayPlanner = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -27,21 +27,21 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
|
|||||||
widget.keys[ev.executionData!] = GlobalKey();
|
widget.keys[ev.executionData!] = GlobalKey();
|
||||||
var d2 = DateTime.parse(ev.executionData!);
|
var d2 = DateTime.parse(ev.executionData!);
|
||||||
DateTime? d3;
|
DateTime? d3;
|
||||||
try {
|
try { d3 = DateTime.parse(ev.endDate!);
|
||||||
d3 = DateTime.parse(ev.endDate!);
|
|
||||||
} catch (e) { /* */ }
|
} catch (e) { /* */ }
|
||||||
widgets.add(InkWell(
|
widgets.add(InkWell(
|
||||||
onTap: () => widget.parent?.setState(() {
|
onTap: () => widget.parent?.setState(() {
|
||||||
widget.parent?.selected = widget.parent?.selected != element ? element : null;
|
selected = selected != element || ev.executionData != selectedReal ? element : null;
|
||||||
widget.parent?.selectedReal = widget.parent?.selected == null ? null : ev.executionData;
|
selectedReal = selected == null ? null : ev.executionData;
|
||||||
if (widget.parent!.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.executionData!],
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50),
|
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: widget.parent?.selected == element ? Border.all(color: const Color.fromRGBO(38, 166, 154, 1), width: 2)
|
border: selectedReal == ev.executionData ?
|
||||||
|
Border.all(color: const Color.fromRGBO(38, 166, 154, 1), width: 2)
|
||||||
: Border(top: BorderSide(color: Colors.grey.shade300)),
|
: Border(top: BorderSide(color: Colors.grey.shade300)),
|
||||||
),
|
),
|
||||||
child: Row(children: [
|
child: Row(children: [
|
||||||
@ -79,10 +79,9 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
var date = DateTime.parse(element);
|
var date = DateTime.parse(element);
|
||||||
children.add(Column( children: [Container(
|
children.add(Column( children: [ExpansionTile(
|
||||||
child: ExpansionTile(
|
|
||||||
enabled: widget.enabled,
|
enabled: widget.enabled,
|
||||||
shape: ContinuousRectangleBorder(),
|
shape: const ContinuousRectangleBorder(),
|
||||||
iconColor: Colors.grey,
|
iconColor: Colors.grey,
|
||||||
initiallyExpanded: true,
|
initiallyExpanded: true,
|
||||||
title: SizedBox(
|
title: SizedBox(
|
||||||
@ -92,24 +91,27 @@ class SchedulerItemWidgetState extends State<SchedulerItemWidget> {
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(right: 5),
|
padding: const EdgeInsets.only(right: 5),
|
||||||
child: Text("${date.day > 9 ? date.day : "0${date.day}"}-${date.hour > 9 ? date.hour : "0${date.hour}"}-${date.year}".toUpperCase(), overflow: TextOverflow.ellipsis,
|
child: Text("${date.day > 9 ? date.day : "0${date.day}"}-${date.month > 9 ? date.month : "0${date.month}"}-${date.year}".toUpperCase(), overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(color: Colors.black, fontWeight: FontWeight.w500))))
|
style: const TextStyle(color: Colors.black, fontWeight: FontWeight.w500))))
|
||||||
])
|
])
|
||||||
),
|
),
|
||||||
collapsedIconColor: Colors.grey,
|
collapsedIconColor: Colors.grey,
|
||||||
children: widgets,
|
children: widgets,
|
||||||
)),
|
),
|
||||||
Divider(color: Colors.grey.shade300, height: 1)
|
Divider(color: Colors.grey.shade300, height: 1)
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
Future.delayed( const Duration(milliseconds: 100), () {
|
Future.delayed( const Duration(milliseconds: 100), () {
|
||||||
if (widget.parent?.selectedReal != null) {
|
if (selectedReal != null) {
|
||||||
widget.keys[widget.parent!.selectedReal!]?.currentContext?.findRenderObject()?.showOnScreen();
|
widget.keys[selectedReal!]?.currentContext?.findRenderObject()?.showOnScreen();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return SingleChildScrollView( child: Container(
|
return Container(
|
||||||
|
alignment: children.isNotEmpty ? Alignment.topLeft : Alignment.center,
|
||||||
|
color: children.isNotEmpty ? Colors.transparent : Colors.grey.shade300,
|
||||||
|
width: children.isNotEmpty ? MediaQuery.of(context).size.width : null,
|
||||||
height: MediaQuery.of(context).size.height - HeaderConstants.height - 50,
|
height: MediaQuery.of(context).size.height - HeaderConstants.height - 50,
|
||||||
child: Column( children: children))
|
child: children.isNotEmpty ? SingleChildScrollView( child: Column( children: children)) : const Text("NO DATA FOUND", style: TextStyle(color: Colors.grey, fontSize: 30))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -30,8 +30,7 @@ class Dashboard extends ChangeNotifier {
|
|||||||
List<Map<String, dynamic>> tempHistory = [];
|
List<Map<String, dynamic>> tempHistory = [];
|
||||||
List<Map<String, dynamic>> history = [];
|
List<Map<String, dynamic>> history = [];
|
||||||
Map<String, dynamic> scheduler = {};
|
Map<String, dynamic> scheduler = {};
|
||||||
Map<String, bool> schedulerState = {};
|
bool scheduleActive = false;
|
||||||
bool schedulerSave = false;
|
|
||||||
String? id;
|
String? id;
|
||||||
String name;
|
String name;
|
||||||
String defaultName = "";
|
String defaultName = "";
|
||||||
@ -108,6 +107,7 @@ class Dashboard extends ChangeNotifier {
|
|||||||
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>),
|
||||||
@ -137,6 +137,7 @@ class Dashboard extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void copyFromMap(Map<String, dynamic> map) {
|
void copyFromMap(Map<String, dynamic> map) {
|
||||||
|
scheduleActive = map['schedule_active'] as bool? ?? false;
|
||||||
scheduler = map['schedule'] as Map<String, String>? ?? {};
|
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;
|
||||||
@ -222,17 +223,16 @@ class Dashboard extends ChangeNotifier {
|
|||||||
d["id"]=id;
|
d["id"]=id;
|
||||||
d["name"]=name;
|
d["name"]=name;
|
||||||
d["graph"]=graph;
|
d["graph"]=graph;
|
||||||
if (schedulerSave) {
|
|
||||||
d["schedule"]=scheduler;
|
d["schedule"]=scheduler;
|
||||||
}
|
d["schedule_active"]=scheduleActive;
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
void deserialize(Map<String, dynamic> graph) {
|
void deserialize(Map<String, dynamic> graph) {
|
||||||
elements = [];
|
elements = [];
|
||||||
arrows = [];
|
arrows = [];
|
||||||
print(graph['schedule']);
|
|
||||||
scheduler = graph['schedule'] ?? {};
|
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 = [];
|
||||||
|
@ -4,7 +4,6 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
|
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
|
||||||
import 'package:flutter_flow_chart/src/dashboard.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
/// Kinf od element
|
/// Kinf od element
|
||||||
|
@ -19,6 +19,9 @@ import 'package:uuid/uuid.dart';
|
|||||||
abstract class FlowData {
|
abstract class FlowData {
|
||||||
String getID();
|
String getID();
|
||||||
String getName();
|
String getName();
|
||||||
|
double? getWidth();
|
||||||
|
double? getHeight();
|
||||||
|
|
||||||
Map<String, dynamic> serialize();
|
Map<String, dynamic> serialize();
|
||||||
FlowData deserialize(Map<String, dynamic> data);
|
FlowData deserialize(Map<String, dynamic> data);
|
||||||
}
|
}
|
||||||
@ -297,6 +300,20 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (!widget.dashboard.isOpened && widget.onDashboardAlertOpened != null ) {
|
||||||
|
Future.delayed(Duration(milliseconds: 100), () {
|
||||||
|
showDialog(
|
||||||
|
barrierDismissible: false,
|
||||||
|
context: context, builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
titlePadding: EdgeInsets.zero,
|
||||||
|
insetPadding: EdgeInsets.zero,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)),
|
||||||
|
title: widget.onDashboardAlertOpened!(context, widget.dashboard));
|
||||||
|
|
||||||
|
}); });
|
||||||
|
}
|
||||||
/// get dashboard position after first frame is drawn
|
/// get dashboard position after first frame is drawn
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@ -379,9 +396,6 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
|
|||||||
if (change) {
|
if (change) {
|
||||||
DrawingArrow.instance.notifyListeners();
|
DrawingArrow.instance.notifyListeners();
|
||||||
widget.dashboard.chartKey.currentState?.setState(() { });
|
widget.dashboard.chartKey.currentState?.setState(() { });
|
||||||
/*Future.delayed(Duration(milliseconds: 10), () {
|
|
||||||
node.requestFocus();
|
|
||||||
});*/
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: ClipRect(
|
child: ClipRect(
|
||||||
@ -421,12 +435,16 @@ class FlowChartState<T extends FlowData> extends State<FlowChart> {
|
|||||||
onAcceptWithDetails: (DragTargetDetails<T> details) {
|
onAcceptWithDetails: (DragTargetDetails<T> details) {
|
||||||
var e = details.data;
|
var e = details.data;
|
||||||
String newID = const Uuid().v4();
|
String newID = const Uuid().v4();
|
||||||
|
double ratio = 1;
|
||||||
|
if (e.getWidth() != null && e.getHeight() != null) {
|
||||||
|
ratio = (e.getHeight()! / (e.getWidth()! - 30));
|
||||||
|
}
|
||||||
FlowElement<T> el = FlowElement<T>(
|
FlowElement<T> el = FlowElement<T>(
|
||||||
dashboard: widget.dashboard,
|
dashboard: widget.dashboard,
|
||||||
id: newID,
|
id: newID,
|
||||||
element: e,
|
element: e,
|
||||||
position: details.offset,
|
position: details.offset,
|
||||||
size: const Size(100, 100),
|
size: Size(100, 100 * ratio),
|
||||||
text: '${widget.dashboard.elements.length}',
|
text: '${widget.dashboard.elements.length}',
|
||||||
handlerSize: 15,
|
handlerSize: 15,
|
||||||
widget: widget.itemWidget(e),
|
widget: widget.itemWidget(e),
|
||||||
@ -477,33 +495,22 @@ 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( height: realSize - 20, child: widget.itemWidget(e) ))),
|
child: Container( child: widget.itemWidget(e),
|
||||||
feedback: Container( height: realSize, child: widget.itemWidget(e) ),
|
constraints: BoxConstraints(maxHeight: realSize - 20, maxWidth: realSize - 20), ))),
|
||||||
|
feedback: Container( constraints: BoxConstraints(maxHeight: realSize, maxWidth: realSize),
|
||||||
|
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),
|
||||||
child: widget.itemWidgetTooltip != null ? HoverMenu( key: hoverKey, width: 400, title: Container(
|
child: widget.itemWidgetTooltip != null ? HoverMenu( key: hoverKey, width: 400, title: Container(
|
||||||
height: realSize - 20, child: widget.itemWidget(e) ),
|
constraints: BoxConstraints( maxHeight: realSize - 20, maxWidth: realSize - 20),
|
||||||
|
child: widget.itemWidget(e) ),
|
||||||
items: [
|
items: [
|
||||||
Container(child: widget.itemWidgetTooltip!(e)),
|
Container(child: widget.itemWidgetTooltip!(e)),
|
||||||
]
|
]
|
||||||
) : Container(
|
) : Container(
|
||||||
height: realSize - 20, child: widget.itemWidget(e)
|
constraints: BoxConstraints(maxHeight: realSize - 20, maxWidth: realSize - 20), child: widget.itemWidget(e)
|
||||||
)
|
)
|
||||||
) )));
|
) )));
|
||||||
}
|
}
|
||||||
if (!widget.dashboard.isOpened && widget.onDashboardAlertOpened != null ) {
|
|
||||||
Future.delayed(Duration(milliseconds: 100), () {
|
|
||||||
showDialog(
|
|
||||||
barrierDismissible: false,
|
|
||||||
context: context, builder: (context) {
|
|
||||||
return AlertDialog(
|
|
||||||
titlePadding: EdgeInsets.zero,
|
|
||||||
insetPadding: EdgeInsets.zero,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)),
|
|
||||||
title: widget.onDashboardAlertOpened!(context, widget.dashboard));
|
|
||||||
|
|
||||||
}); });
|
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user