27 Commits

Author SHA1 Message Date
mr
3d6b4bf3b3 change 2025-02-17 09:35:56 +01:00
mr
313ef43e9c debug 2025-02-05 16:06:07 +01:00
mr
058633742e debug 2025-02-05 16:04:57 +01:00
mr
e1968e14b0 debug 2025-02-05 15:02:35 +01:00
mr
6b362d77f0 Debug 2025-02-05 14:48:23 +01:00
mr
57c6d74ff5 info on resource item 2025-02-05 12:22:19 +01:00
mr
bed48b4cb4 missing files 2025-02-05 09:07:39 +01:00
mr
05854c84d8 Major Change Inputs & Co 2025-02-04 17:02:49 +01:00
mr
2c86e90b76 login debug 2024-11-27 10:54:10 +01:00
mr
e73ca6b532 Style Dashboard by rules 2024-11-22 09:39:04 +01:00
mr
7fea931b63 Rules on dashboard + Permissions 2024-11-21 11:00:44 +01:00
mr
2ceab090fd Autoload yay 2024-11-19 16:41:47 +01:00
mr
d522a44029 Shared improved with rules 2024-11-19 16:16:53 +01:00
mr
06b13853db Shared debug 2024-11-19 15:33:55 +01:00
mr
6be0fbac7d Shared space 2024-11-19 15:06:22 +01:00
mr
062042b590 draft booking scheduler page 2024-11-13 08:12:37 +01:00
mr
1ca77b6611 test 2024-11-08 13:59:22 +01:00
mr
685badc59a New local front deployment + new args 2024-10-15 11:28:29 +02:00
mr
dacda3b3a6 test 2024-09-24 11:42:12 +02:00
mr
40a44848a8 Change for demo 2024-09-23 16:59:21 +02:00
mr
0b294a782c Latest Front with debug 2024-08-30 12:52:32 +02:00
mr
8beddba367 ws 2024-08-27 15:38:50 +02:00
mr
6ba32a7dfa Shared WS 2024-08-27 15:38:21 +02:00
mr
36a70db69f Booking forms + Scheduler adaptation 2024-08-26 17:37:23 +02:00
mr
8f91a10331 missing pubspec 2024-08-22 15:47:25 +02:00
mr
1db9ef0794 UI debugging + git ignore 2024-08-22 15:46:16 +02:00
mr
ceeebfc964 intermediate 2024-08-08 08:42:32 +02:00
126 changed files with 10950 additions and 2123 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
["/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/libflutter_linux_gtk.so","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/icudtl.dat","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_dart_project.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_texture.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_event_channel.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_message_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_message_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registry.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_method_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_string_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_pixel_buffer_texture.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_texture_gl.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/flutter_linux.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_value.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_view.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_channel.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_engine.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_messenger.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_message_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_method_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_texture_registrar.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_response.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registrar.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_basic_message_channel.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_call.h","/home/mr/Documents/OC/oc-front/build/flutter_assets/kernel_blob.bin","/home/mr/Documents/OC/oc-front/build/flutter_assets/assets/images/logo.svg","/home/mr/Documents/OC/oc-front/build/flutter_assets/assets/images/icon.svg","/home/mr/Documents/OC/oc-front/build/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf","/home/mr/Documents/OC/oc-front/build/flutter_assets/packages/flutter_map/lib/assets/flutter_map_logo.png","/home/mr/Documents/OC/oc-front/build/flutter_assets/fonts/MaterialIcons-Regular.otf","/home/mr/Documents/OC/oc-front/build/flutter_assets/shaders/ink_sparkle.frag","/home/mr/Documents/OC/oc-front/build/flutter_assets/AssetManifest.json","/home/mr/Documents/OC/oc-front/build/flutter_assets/AssetManifest.bin","/home/mr/Documents/OC/oc-front/build/flutter_assets/FontManifest.json","/home/mr/Documents/OC/oc-front/build/flutter_assets/NOTICES.Z","/home/mr/Documents/OC/oc-front/build/flutter_assets/version.json"]
["/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/libflutter_linux_gtk.so","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/icudtl.dat","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_dart_project.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_texture.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_event_channel.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_message_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_message_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registry.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_method_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_string_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_pixel_buffer_texture.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_texture_gl.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/flutter_linux.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_value.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_view.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_channel.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_engine.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_messenger.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_message_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_method_codec.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_texture_registrar.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_response.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registrar.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_basic_message_channel.h","/home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_call.h","/home/mr/Documents/OC/oc-front/build/flutter_assets/kernel_blob.bin","/home/mr/Documents/OC/oc-front/build/flutter_assets/assets/images/logo.svg","/home/mr/Documents/OC/oc-front/build/flutter_assets/assets/images/icon.svg","/home/mr/Documents/OC/oc-front/build/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf","/home/mr/Documents/OC/oc-front/build/flutter_assets/packages/flutter_map/lib/assets/flutter_map_logo.png","/home/mr/Documents/OC/oc-front/build/flutter_assets/packages/timezone/data/latest_all.tzf","/home/mr/Documents/OC/oc-front/build/flutter_assets/fonts/MaterialIcons-Regular.otf","/home/mr/Documents/OC/oc-front/build/flutter_assets/shaders/ink_sparkle.frag","/home/mr/Documents/OC/oc-front/build/flutter_assets/AssetManifest.json","/home/mr/Documents/OC/oc-front/build/flutter_assets/AssetManifest.bin","/home/mr/Documents/OC/oc-front/build/flutter_assets/FontManifest.json","/home/mr/Documents/OC/oc-front/build/flutter_assets/NOTICES.Z","/home/mr/Documents/OC/oc-front/build/flutter_assets/version.json"]

View File

@@ -8,8 +8,10 @@
import 'dart:io'; // flutter_ignore: dart_io_import.
import 'package:path_provider_android/path_provider_android.dart';
import 'package:shared_preferences_android/shared_preferences_android.dart';
import 'package:webview_flutter_android/webview_flutter_android.dart';
import 'package:path_provider_foundation/path_provider_foundation.dart';
import 'package:shared_preferences_foundation/shared_preferences_foundation.dart';
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:path_provider_linux/path_provider_linux.dart';
import 'package:shared_preferences_linux/shared_preferences_linux.dart';
@@ -43,6 +45,15 @@ class _PluginRegistrant {
);
}
try {
AndroidWebViewPlatform.registerWith();
} catch (err) {
print(
'`webview_flutter_android` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
} else if (Platform.isIOS) {
try {
PathProviderFoundation.registerWith();
@@ -62,6 +73,15 @@ class _PluginRegistrant {
);
}
try {
WebKitWebViewPlatform.registerWith();
} catch (err) {
print(
'`webview_flutter_wkwebview` threw an error: $err. '
'The app may not function as expected until you remove this plugin from pubspec.yaml'
);
}
} else if (Platform.isLinux) {
try {
DeviceInfoPlusLinuxPlugin.registerWith();

View File

@@ -61,6 +61,12 @@
"packageUri": "lib/",
"languageVersion": "2.18"
},
{
"name": "cron",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/cron-0.6.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "crypto",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/crypto-3.0.3",
@@ -85,6 +91,12 @@
"packageUri": "lib/",
"languageVersion": "2.19"
},
{
"name": "datetime_picker_formfield",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/datetime_picker_formfield-2.0.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "desktop_window",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/desktop_window-0.4.0",
@@ -157,6 +169,12 @@
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "flutter_advanced_switch",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/flutter_advanced_switch-3.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_box_transform",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/flutter_box_transform-0.4.4",
@@ -169,6 +187,12 @@
"packageUri": "lib/",
"languageVersion": "2.14"
},
{
"name": "flutter_event_calendar",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/flutter_event_calendar-1.0.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_flow_chart",
"rootUri": "../library/flutter_flow_chart",
@@ -259,6 +283,18 @@
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "json_string",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/json_string-3.0.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "json_util",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/json_util-2.0.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "latlong2",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/latlong2-0.9.1",
@@ -421,6 +457,18 @@
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "scoped_model",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/scoped_model-2.0.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "shamsi_date",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/shamsi_date-0.15.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "shared_preferences",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/shared_preferences-2.2.3",
@@ -529,6 +577,24 @@
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "syncfusion_flutter_calendar",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/syncfusion_flutter_calendar-26.2.7",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "syncfusion_flutter_core",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-26.2.7",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "syncfusion_flutter_datepicker",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/syncfusion_flutter_datepicker-26.2.7",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "table_calendar",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/table_calendar-3.1.2",
@@ -547,6 +613,12 @@
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "timezone",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/timezone-0.9.3",
"packageUri": "lib/",
"languageVersion": "2.19"
},
{
"name": "typed_data",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/typed_data-1.3.2",
@@ -601,6 +673,30 @@
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "webview_flutter",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/webview_flutter-4.8.0",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "webview_flutter_android",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/webview_flutter_android-3.16.3",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "webview_flutter_platform_interface",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/webview_flutter_platform_interface-2.10.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "webview_flutter_wkwebview",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/webview_flutter_wkwebview-3.14.0",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "win32",
"rootUri": "file:///home/mr/.pub-cache/hosted/pub.dev/win32-5.5.0",
@@ -638,7 +734,7 @@
"languageVersion": "3.3"
}
],
"generated": "2024-07-17T08:10:14.640895Z",
"generated": "2024-08-07T09:57:10.081261Z",
"generator": "pub",
"generatorVersion": "3.3.4"
}

View File

@@ -38,6 +38,10 @@ convert
2.18
file:///home/mr/.pub-cache/hosted/pub.dev/convert-3.1.1/
file:///home/mr/.pub-cache/hosted/pub.dev/convert-3.1.1/lib/
cron
2.12
file:///home/mr/.pub-cache/hosted/pub.dev/cron-0.6.1/
file:///home/mr/.pub-cache/hosted/pub.dev/cron-0.6.1/lib/
crypto
2.19
file:///home/mr/.pub-cache/hosted/pub.dev/crypto-3.0.3/
@@ -54,6 +58,10 @@ dashed_path
2.19
file:///home/mr/.pub-cache/hosted/pub.dev/dashed_path-1.0.1/
file:///home/mr/.pub-cache/hosted/pub.dev/dashed_path-1.0.1/lib/
datetime_picker_formfield
2.12
file:///home/mr/.pub-cache/hosted/pub.dev/datetime_picker_formfield-2.0.1/
file:///home/mr/.pub-cache/hosted/pub.dev/datetime_picker_formfield-2.0.1/lib/
desktop_window
2.12
file:///home/mr/.pub-cache/hosted/pub.dev/desktop_window-0.4.0/
@@ -98,6 +106,10 @@ fixnum
2.19
file:///home/mr/.pub-cache/hosted/pub.dev/fixnum-1.1.0/
file:///home/mr/.pub-cache/hosted/pub.dev/fixnum-1.1.0/lib/
flutter_advanced_switch
2.12
file:///home/mr/.pub-cache/hosted/pub.dev/flutter_advanced_switch-3.1.0/
file:///home/mr/.pub-cache/hosted/pub.dev/flutter_advanced_switch-3.1.0/lib/
flutter_box_transform
3.0
file:///home/mr/.pub-cache/hosted/pub.dev/flutter_box_transform-0.4.4/
@@ -106,6 +118,10 @@ flutter_colorpicker
2.14
file:///home/mr/.pub-cache/hosted/pub.dev/flutter_colorpicker-1.1.0/
file:///home/mr/.pub-cache/hosted/pub.dev/flutter_colorpicker-1.1.0/lib/
flutter_event_calendar
2.12
file:///home/mr/.pub-cache/hosted/pub.dev/flutter_event_calendar-1.0.0/
file:///home/mr/.pub-cache/hosted/pub.dev/flutter_event_calendar-1.0.0/lib/
flutter_lints
3.1
file:///home/mr/.pub-cache/hosted/pub.dev/flutter_lints-3.0.2/
@@ -154,6 +170,14 @@ irondash_message_channel
3.0
file:///home/mr/.pub-cache/hosted/pub.dev/irondash_message_channel-0.7.0/
file:///home/mr/.pub-cache/hosted/pub.dev/irondash_message_channel-0.7.0/lib/
json_string
2.12
file:///home/mr/.pub-cache/hosted/pub.dev/json_string-3.0.1/
file:///home/mr/.pub-cache/hosted/pub.dev/json_string-3.0.1/lib/
json_util
2.12
file:///home/mr/.pub-cache/hosted/pub.dev/json_util-2.0.0/
file:///home/mr/.pub-cache/hosted/pub.dev/json_util-2.0.0/lib/
latlong2
3.0
file:///home/mr/.pub-cache/hosted/pub.dev/latlong2-0.9.1/
@@ -262,6 +286,14 @@ proj4dart
2.12
file:///home/mr/.pub-cache/hosted/pub.dev/proj4dart-2.1.0/
file:///home/mr/.pub-cache/hosted/pub.dev/proj4dart-2.1.0/lib/
scoped_model
2.12
file:///home/mr/.pub-cache/hosted/pub.dev/scoped_model-2.0.0/
file:///home/mr/.pub-cache/hosted/pub.dev/scoped_model-2.0.0/lib/
shamsi_date
2.12
file:///home/mr/.pub-cache/hosted/pub.dev/shamsi_date-0.15.0/
file:///home/mr/.pub-cache/hosted/pub.dev/shamsi_date-0.15.0/lib/
shared_preferences
3.1
file:///home/mr/.pub-cache/hosted/pub.dev/shared_preferences-2.2.3/
@@ -330,6 +362,18 @@ super_native_extensions
3.3
file:///home/mr/.pub-cache/hosted/pub.dev/super_native_extensions-0.8.17/
file:///home/mr/.pub-cache/hosted/pub.dev/super_native_extensions-0.8.17/lib/
syncfusion_flutter_calendar
2.17
file:///home/mr/.pub-cache/hosted/pub.dev/syncfusion_flutter_calendar-26.2.7/
file:///home/mr/.pub-cache/hosted/pub.dev/syncfusion_flutter_calendar-26.2.7/lib/
syncfusion_flutter_core
2.17
file:///home/mr/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-26.2.7/
file:///home/mr/.pub-cache/hosted/pub.dev/syncfusion_flutter_core-26.2.7/lib/
syncfusion_flutter_datepicker
2.17
file:///home/mr/.pub-cache/hosted/pub.dev/syncfusion_flutter_datepicker-26.2.7/
file:///home/mr/.pub-cache/hosted/pub.dev/syncfusion_flutter_datepicker-26.2.7/lib/
table_calendar
3.0
file:///home/mr/.pub-cache/hosted/pub.dev/table_calendar-3.1.2/
@@ -342,6 +386,10 @@ test_api
3.0
file:///home/mr/.pub-cache/hosted/pub.dev/test_api-0.6.1/
file:///home/mr/.pub-cache/hosted/pub.dev/test_api-0.6.1/lib/
timezone
2.19
file:///home/mr/.pub-cache/hosted/pub.dev/timezone-0.9.3/
file:///home/mr/.pub-cache/hosted/pub.dev/timezone-0.9.3/lib/
typed_data
2.17
file:///home/mr/.pub-cache/hosted/pub.dev/typed_data-1.3.2/
@@ -378,6 +426,22 @@ web
3.3
file:///home/mr/.pub-cache/hosted/pub.dev/web-0.5.1/
file:///home/mr/.pub-cache/hosted/pub.dev/web-0.5.1/lib/
webview_flutter
3.2
file:///home/mr/.pub-cache/hosted/pub.dev/webview_flutter-4.8.0/
file:///home/mr/.pub-cache/hosted/pub.dev/webview_flutter-4.8.0/lib/
webview_flutter_android
3.2
file:///home/mr/.pub-cache/hosted/pub.dev/webview_flutter_android-3.16.3/
file:///home/mr/.pub-cache/hosted/pub.dev/webview_flutter_android-3.16.3/lib/
webview_flutter_platform_interface
3.0
file:///home/mr/.pub-cache/hosted/pub.dev/webview_flutter_platform_interface-2.10.0/
file:///home/mr/.pub-cache/hosted/pub.dev/webview_flutter_platform_interface-2.10.0/lib/
webview_flutter_wkwebview
3.2
file:///home/mr/.pub-cache/hosted/pub.dev/webview_flutter_wkwebview-3.14.0/
file:///home/mr/.pub-cache/hosted/pub.dev/webview_flutter_wkwebview-3.14.0/lib/
win32
3.3
file:///home/mr/.pub-cache/hosted/pub.dev/win32-5.5.0/

View File

@@ -1,16 +1,16 @@
# This is a generated file; do not edit or check into version control.
desktop_window=/home/mr/.pub-cache/hosted/pub.dev/desktop_window-0.4.0/
device_info_plus=/home/mr/.pub-cache/hosted/pub.dev/device_info_plus-10.1.0/
desktop_window=/home/mr/.pub-cache/hosted/pub.dev/desktop_window-0.4.1/
device_info_plus=/home/mr/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/
irondash_engine_context=/home/mr/.pub-cache/hosted/pub.dev/irondash_engine_context-0.5.4/
path_provider=/home/mr/.pub-cache/hosted/pub.dev/path_provider-2.1.3/
path_provider=/home/mr/.pub-cache/hosted/pub.dev/path_provider-2.1.4/
path_provider_android=/home/mr/.pub-cache/hosted/pub.dev/path_provider_android-2.2.4/
path_provider_foundation=/home/mr/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.0/
path_provider_linux=/home/mr/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/
path_provider_windows=/home/mr/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/
path_provider_windows=/home/mr/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/
shared_preferences=/home/mr/.pub-cache/hosted/pub.dev/shared_preferences-2.2.3/
shared_preferences_android=/home/mr/.pub-cache/hosted/pub.dev/shared_preferences_android-2.2.2/
shared_preferences_foundation=/home/mr/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.4.0/
shared_preferences_linux=/home/mr/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.3.2/
shared_preferences_web=/home/mr/.pub-cache/hosted/pub.dev/shared_preferences_web-2.3.0/
shared_preferences_windows=/home/mr/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.3.2/
super_native_extensions=/home/mr/.pub-cache/hosted/pub.dev/super_native_extensions-0.8.17/
shared_preferences_foundation=/home/mr/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/
shared_preferences_linux=/home/mr/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/
shared_preferences_web=/home/mr/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.1/
shared_preferences_windows=/home/mr/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/
super_native_extensions=/home/mr/.pub-cache/hosted/pub.dev/super_native_extensions-0.8.19/

File diff suppressed because one or more lines are too long

45
.gitignore vendored Normal file
View File

@@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
build/
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

View File

@@ -5,13 +5,17 @@ FROM debian:latest AS build-env
RUN apt-get update
RUN apt-get install -y curl git unzip
ARG WORKSPACE_HOST="https://localhost:49618"
ARG WORKFLOW_HOST="https://localhost:49618"
ARG SEARCH_HOST="https://localhost:49618"
ARG ITEM_HOST="https://localhost:49618"
ARG WORKSPACE_HOST="http://localhost:8089/oc"
ARG WORKFLOW_HOST="http://localhost:8088/oc"
ARG ITEM_HOST="http://localhost:8087/oc"
ARG SCHEDULER_HOST="http://localhost:8090/oc"
ARG LOGS_HOST="http://localhost:3100"
ARG PEER_HOST="http://localhost:8093/oc"
ARG DATACENTER_HOST="http://localhost:8092/oc"
ARG COLLABORATIVE_AREA_HOST="http://localhost:8091/oc"
ARG AUTH_MODE=true
# define variables
ARG FLUTTER_SDK=/usr/local/flutter
ARG FLUTTER_SDK=/usr/local/flutter
ARG FLUTTER_VERSION=3.19.6
ARG APP=/app/
@@ -38,10 +42,15 @@ WORKDIR $APP
RUN flutter clean
RUN flutter pub get
RUN flutter build web \
--dart-define=AUTH_MODE=$AUTH_MODE \
--dart-define=WORKSPACE_HOST=$WORKSPACE_HOST \
--dart-define=WORKFLOW_HOST=$WORKFLOW_HOST \
--dart-define=SEARCH_HOST=$SEARCH_HOST \
--dart-define=ITEM_HOST=$ITEM_HOST
--dart-define=PEER_HOST=$PEER_HOST \
--dart-define=COLLABORATIVE_AREA_HOST=$COLLABORATIVE_AREA_HOST \
--dart-define=SCHEDULER_HOST=$SCHEDULER_HOST \
--dart-define=LOGS_HOST=$LOGS_HOST \
--dart-define=ITEM_HOST=$ITEM_HOST \
--dart-define=DATACENTER_HOST=$DATACENTER_HOST
# once heare the app will be compiled and ready to deploy
# use nginx to deploy

View File

@@ -43,4 +43,7 @@ At the root of the project :
- `docker build . -t oc-front`
if localisation services change : `docker build -t oc-front --build-arg WORKSPACE_HOST=<SERVICE URL> --build-arg WORKFLOW_HOST=<SERVICE URL> --build-arg SEARCH_HOST=<SERVICE URL> --build-arg ITEM_HOST=<SERVICE URL> .`
- `docker-compose up -d --build --force-recreate`
- `docker-compose up -d --build --force-recreate`
## HELP
sudo apt install libwebkit2gtk-4.0-dev

View File

@@ -1 +1 @@

View File

@@ -1 +1 @@
{"assets/images/icon.svg":["assets/images/icon.svg"],"assets/images/logo.svg":["assets/images/logo.svg"],"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"],"packages/flutter_map/lib/assets/flutter_map_logo.png":["packages/flutter_map/lib/assets/flutter_map_logo.png"]}
{"assets/images/icon.svg":["assets/images/icon.svg"],"assets/images/logo.svg":["assets/images/logo.svg"],"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"],"packages/flutter_map/lib/assets/flutter_map_logo.png":["packages/flutter_map/lib/assets/flutter_map_logo.png"],"packages/timezone/data/latest_all.tzf":["packages/timezone/data/latest_all.tzf"]}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,65 +1,123 @@
# ninja log v5
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_call.h df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registry.h df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_string_codec.h df34b103123784f1
4 320 1721230626649119048 plugins/super_native_extensions/_phony_ 74762aee97d0d662
5895 6038 1720171596542283915 plugins/desktop_window/libdesktop_window_plugin.so 26c8f2b838605d38
3 5644 0 flutter/_phony_ df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_method_codec.h df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_channel.h df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_engine.h df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_dart_project.h df34b103123784f1
13608 16421 1720171474184250688 plugins/desktop_window/CMakeFiles/desktop_window_plugin.dir/desktop_window_plugin.cc.o 259dba9caad978aa
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_messenger.h df34b103123784f1
6240 6392 0 CMakeFiles/install.util 558f353ee1373233
7221 7661 1720190160103160034 CMakeFiles/oc_front.dir/main.cc.o b1d36a8b3fb9d7f7
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_response.h df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/libflutter_linux_gtk.so df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_message_codec.h df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/flutter_linux.h df34b103123784f1
5644 6116 1721230632457031904 CMakeFiles/oc_front.dir/my_application.cc.o 3fd042b328f87769
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_basic_message_channel.h df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_codec.h df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_method_codec.h df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_view.h df34b103123784f1
4 320 1721230626649119048 plugins/super_native_extensions/libsuper_native_extensions.so 74762aee97d0d662
6116 6240 1721230632585029984 intermediates_do_not_run/oc_front 4e630cc1de7b670a
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_codec.h df34b103123784f1
7223 7702 1720190160147159452 plugins/super_native_extensions/CMakeFiles/super_native_extensions_plugin.dir/super_native_extensions_plugin.cc.o e4e228e87b2f98cc
5645 6080 1721230632425032384 CMakeFiles/oc_front.dir/flutter/generated_plugin_registrant.cc.o 79b3e2ad61c9feb4
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_message_codec.h df34b103123784f1
6105 7479 1720190159919162468 plugins/irondash_engine_context/CMakeFiles/irondash_engine_context_plugin.dir/irondash_engine_context_plugin.cc.o 439d26d301366f5a
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_message_codec.h df34b103123784f1
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registrar.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_channel.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_codec.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_basic_message_channel.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_engine.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_dart_project.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_messenger.h df34b103123784f1
19318 20273 0 CMakeFiles/install.util 558f353ee1373233
43 412 1723042867477311806 plugins/super_native_extensions/libsuper_native_extensions.so 74762aee97d0d662
18891 19318 1723042886400968232 intermediates_do_not_run/oc_front 4e630cc1de7b670a
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_value.h df34b103123784f1
7479 7597 1720190160043160828 plugins/irondash_engine_context/libirondash_engine_context_plugin.so a89d3772b0fb0935
3 5644 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_value.h df34b103123784f1
5645 5781 1721230632125036886 plugins/super_native_extensions/libsuper_native_extensions_plugin.so 87d9259f3dbf9022
3 326 1721231108669887541 plugins/super_native_extensions/libsuper_native_extensions.so 74762aee97d0d662
3 326 1721231108669887541 plugins/super_native_extensions/_phony_ 74762aee97d0d662
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/libflutter_linux_gtk.so df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_basic_message_channel.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_codec.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_messenger.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_dart_project.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_engine.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_message_codec.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_method_codec.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_message_codec.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_call.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_channel.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_codec.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_response.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registrar.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registry.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_message_codec.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_method_codec.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_string_codec.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_value.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_view.h df34b103123784f1
3 5916 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/flutter_linux.h df34b103123784f1
3 5916 0 flutter/_phony_ df34b103123784f1
5919 6044 1721231114401801556 plugins/super_native_extensions/libsuper_native_extensions_plugin.so 87d9259f3dbf9022
5918 6335 1721231114689797236 CMakeFiles/oc_front.dir/flutter/generated_plugin_registrant.cc.o 79b3e2ad61c9feb4
5917 6380 1721231114733796575 CMakeFiles/oc_front.dir/my_application.cc.o 3fd042b328f87769
6380 6517 1721231114873794475 intermediates_do_not_run/oc_front 4e630cc1de7b670a
6517 6648 0 CMakeFiles/install.util 558f353ee1373233
43 7102 0 flutter/_phony_ df34b103123784f1
13608 16421 1720171474184250688 plugins/desktop_window/CMakeFiles/desktop_window_plugin.dir/desktop_window_plugin.cc.o 259dba9caad978aa
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registry.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_call.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registrar.h df34b103123784f1
6105 7479 1720190159919162468 plugins/irondash_engine_context/CMakeFiles/irondash_engine_context_plugin.dir/irondash_engine_context_plugin.cc.o 439d26d301366f5a
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_message_codec.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/libflutter_linux_gtk.so df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_message_codec.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/flutter_linux.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_string_codec.h df34b103123784f1
5895 6038 1720171596542283915 plugins/desktop_window/libdesktop_window_plugin.so 26c8f2b838605d38
43 412 1723042867477311806 plugins/super_native_extensions/_phony_ 74762aee97d0d662
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_response.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_codec.h df34b103123784f1
7223 7702 1720190160147159452 plugins/super_native_extensions/CMakeFiles/super_native_extensions_plugin.dir/super_native_extensions_plugin.cc.o e4e228e87b2f98cc
7117 18885 1723042885952976327 CMakeFiles/oc_front.dir/flutter/generated_plugin_registrant.cc.o 79b3e2ad61c9feb4
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_message_codec.h df34b103123784f1
7117 16523 1723042883325023855 plugins/super_native_extensions/libsuper_native_extensions_plugin.so 87d9259f3dbf9022
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_method_codec.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_view.h df34b103123784f1
43 7102 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_method_codec.h df34b103123784f1
7221 7661 1720190160103160034 CMakeFiles/oc_front.dir/main.cc.o b1d36a8b3fb9d7f7
7116 18891 1723042885956976255 CMakeFiles/oc_front.dir/my_application.cc.o 3fd042b328f87769
58 466 1723042916288432161 plugins/super_native_extensions/libsuper_native_extensions.so 74762aee97d0d662
58 466 1723042916288432161 plugins/super_native_extensions/_phony_ 74762aee97d0d662
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/libflutter_linux_gtk.so df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_basic_message_channel.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_codec.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_messenger.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_dart_project.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_engine.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_message_codec.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_method_codec.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_message_codec.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_call.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_channel.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_codec.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_response.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registrar.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registry.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_message_codec.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_method_codec.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_string_codec.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_value.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_view.h df34b103123784f1
57 12620 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/flutter_linux.h df34b103123784f1
57 12620 0 flutter/_phony_ df34b103123784f1
12638 21600 1723042936588072438 plugins/super_native_extensions/libsuper_native_extensions_plugin.so 87d9259f3dbf9022
12638 24688 1723042940476003931 CMakeFiles/oc_front.dir/flutter/generated_plugin_registrant.cc.o 79b3e2ad61c9feb4
12638 24698 1723042940524003086 CMakeFiles/oc_front.dir/my_application.cc.o 3fd042b328f87769
24698 25496 1723042941331988865 intermediates_do_not_run/oc_front 4e630cc1de7b670a
25496 27380 0 CMakeFiles/install.util 558f353ee1373233
93 17067 1723043023530568429 plugins/super_native_extensions/libsuper_native_extensions.so 74762aee97d0d662
93 17067 1723043023530568429 plugins/super_native_extensions/_phony_ 74762aee97d0d662
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/libflutter_linux_gtk.so df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_basic_message_channel.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_codec.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_messenger.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_dart_project.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_engine.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_message_codec.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_method_codec.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_message_codec.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_call.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_channel.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_codec.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_response.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registrar.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registry.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_message_codec.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_method_codec.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_string_codec.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_value.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_view.h df34b103123784f1
91 25502 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/flutter_linux.h df34b103123784f1
91 25502 0 flutter/_phony_ df34b103123784f1
25503 26894 1723043048582145188 plugins/super_native_extensions/libsuper_native_extensions_plugin.so 87d9259f3dbf9022
25503 28785 1723043050470113462 CMakeFiles/oc_front.dir/flutter/generated_plugin_registrant.cc.o 79b3e2ad61c9feb4
25502 28787 1723043050470113462 CMakeFiles/oc_front.dir/my_application.cc.o 3fd042b328f87769
28787 28990 1723043050674110035 intermediates_do_not_run/oc_front 4e630cc1de7b670a
28990 29315 0 CMakeFiles/install.util 558f353ee1373233
26 384 1723043130512788850 plugins/super_native_extensions/libsuper_native_extensions.so 74762aee97d0d662
26 384 1723043130512788850 plugins/super_native_extensions/_phony_ 74762aee97d0d662
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/libflutter_linux_gtk.so df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_basic_message_channel.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_codec.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_binary_messenger.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_dart_project.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_engine.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_message_codec.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_json_method_codec.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_message_codec.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_call.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_channel.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_codec.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_method_response.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registrar.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_plugin_registry.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_message_codec.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_standard_method_codec.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_string_codec.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_value.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/fl_view.h df34b103123784f1
26 6081 0 /home/mr/Documents/OC/oc-front/linux/flutter/ephemeral/flutter_linux/flutter_linux.h df34b103123784f1
26 6081 0 flutter/_phony_ df34b103123784f1
6084 6259 1723043136404692840 plugins/super_native_extensions/libsuper_native_extensions_plugin.so 87d9259f3dbf9022
6083 6556 1723043136700688022 CMakeFiles/oc_front.dir/flutter/generated_plugin_registrant.cc.o 79b3e2ad61c9feb4
6082 6587 1723043136728687566 CMakeFiles/oc_front.dir/my_application.cc.o 3fd042b328f87769
6587 6802 1723043136948683986 intermediates_do_not_run/oc_front 4e630cc1de7b670a
6802 6962 0 CMakeFiles/install.util 558f353ee1373233

View File

@@ -1 +1 @@
{"assets/images/icon.svg":["assets/images/icon.svg"],"assets/images/logo.svg":["assets/images/logo.svg"],"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"],"packages/flutter_map/lib/assets/flutter_map_logo.png":["packages/flutter_map/lib/assets/flutter_map_logo.png"]}
{"assets/images/icon.svg":["assets/images/icon.svg"],"assets/images/logo.svg":["assets/images/logo.svg"],"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"],"packages/flutter_map/lib/assets/flutter_map_logo.png":["packages/flutter_map/lib/assets/flutter_map_logo.png"],"packages/timezone/data/latest_all.tzf":["packages/timezone/data/latest_all.tzf"]}

View File

@@ -14,6 +14,7 @@
/home/mr/Documents/OC/oc-front/build/linux/x64/debug/bundle/data/flutter_assets/NOTICES.Z
/home/mr/Documents/OC/oc-front/build/linux/x64/debug/bundle/data/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf
/home/mr/Documents/OC/oc-front/build/linux/x64/debug/bundle/data/flutter_assets/packages/flutter_map/lib/assets/flutter_map_logo.png
/home/mr/Documents/OC/oc-front/build/linux/x64/debug/bundle/data/flutter_assets/packages/timezone/data/latest_all.tzf
/home/mr/Documents/OC/oc-front/build/linux/x64/debug/bundle/data/flutter_assets/version.json
/home/mr/Documents/OC/oc-front/build/linux/x64/debug/bundle/data/flutter_assets/FontManifest.json
/home/mr/Documents/OC/oc-front/build/linux/x64/debug/bundle/data/flutter_assets/AssetManifest.bin

View File

@@ -1,29 +0,0 @@
import 'package:oc_front/models/search.dart';
class Graphs {
static const Map<String, Graph> graphs = {};
static Graph getGraph(String id) {
return graphs[id]!;
}
static void addGraph(String id, Graph graph) {
graphs[id] = graph;
}
static void removeGraph(String id) {
graphs.remove(id);
}
static void clear() {
graphs.clear();
}
}
class Graph {
String id;
List<AbstractItem> items = [];
Graph({ required this.id, required this.items }) {
}
}

View File

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

View File

@@ -1,84 +1,227 @@
import 'package:flutter/material.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/core/sections/end_drawer.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/models/resources/compute.dart';
import 'package:oc_front/models/resources/data.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/models/resources/storage.dart';
import 'package:oc_front/models/resources/workflow.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/models/workspace.dart';
import 'package:oc_front/core/services/specialized_services/item_service.dart';
import 'package:oc_front/core/services/specialized_services/workspace_service.dart';
import 'package:oc_front/pages/catalog.dart';
import 'package:oc_front/pages/catalog_item.dart';
import 'package:oc_front/pages/workflow.dart';
class WorkSpaceItem {
String? id;
String? name;
WorkSpaceItem({this.id, this.name});
}
class WorkspaceLocal {
static Workspace? workspace;
static String? current;
static Map<String, Workspace> workspaces = {};
static final WorkspaceService _service = WorkspaceService();
static List<AbstractItem> items = [];
static void init(BuildContext context) {
_service.all(context).then((value) {
workspace = value.data;
if (workspace != null) {
if (workspace!.data.isNotEmpty) {
ItemService<DataItem, DataItem> dataService = ItemService<DataItem, DataItem>();
for (var element in workspace!.data) {
if (WorkspaceLocal.hasItemByID(element)) { continue; }
dataService.get(context, element).then(
(value) { if (value.data != null) { items.add(value.data!); } }
);
}
static Future<void> init(BuildContext? context, bool changeCurrent) async {
var value = await _service.all(context);
if (value.data != null && value.data!.values.isNotEmpty ) {
var vals = value.data!.values;
for (var element in vals) {
var ws = Workspace().deserialize(element);
if (ws.id == null) { continue; }
workspaces[ws.id!] = ws;
if (current == null) {
current ??= ws.id;
workspaces[current!] = ws;
_service.put(context, ws.id!, { "active" : true }, {});
}
if (workspace!.computing.isNotEmpty) {
ItemService<ComputingItem, ComputingItem> computingService = ItemService<ComputingItem, ComputingItem>();
for (var element in workspace!.computing) {
if (WorkspaceLocal.hasItemByID(element)) { continue; }
computingService.get(context, element).then(
(value) { if (value.data != null) { items.add(value.data!); } }
);
}
}
if (workspace!.datacenter.isNotEmpty) {
ItemService<DataCenterItem, DataCenterItem> dataCenterService = ItemService<DataCenterItem, DataCenterItem>();
for (var element in workspace!.datacenter) {
if (WorkspaceLocal.hasItemByID(element)) { continue; }
dataCenterService.get(context, element).then(
(value) { if (value.data != null) { items.add(value.data!); } }
);
}
}
if (workspace!.storage.isNotEmpty) {
ItemService<StorageItem, StorageItem> storageService = ItemService<StorageItem, StorageItem>();
for (var element in workspace!.storage) {
if (WorkspaceLocal.hasItemByID(element)) { continue; }
storageService.get(context, element).then(
(value) { if (value.data != null) { items.add(value.data!); } }
);
}
if (ws.active == true && changeCurrent) {
current = ws.id;
}
fill();
HeaderConstants.headerKey.currentState?.setState(() {});
}
} else {
await WorkspaceLocal.createWorkspace("default workspace", null);
}
}
static void fill() {
items = [];
if (workspaces[current] != null) {
for (var element in workspaces[current]!.datas) {
if (WorkspaceLocal.hasItemByID(element.getID())) { continue; }
items.add(element);
}
for (var element in workspaces[current]!.processings) {
if (WorkspaceLocal.hasItemByID(element.getID())) { continue; }
items.add(element); }
for (var element in workspaces[current]!.computes) {
if (WorkspaceLocal.hasItemByID(element.getID())) { continue; }
items.add(element);
}
for (var element in workspaces[current]!.storages) {
if (WorkspaceLocal.hasItemByID(element.getID())) { continue; }
items.add(element);
}
for (var element in workspaces[current]!.workflows) {
if (WorkspaceLocal.hasItemByID(element.getID())) { continue; }
items.add(element);
}
}
}
static Workspace? getCurrentWorkspace() {
return workspaces[current];
}
static bool hasWorkspace(String workspaceName) {
for (var element in workspaces.values) {
if (element.name == workspaceName) { return true; }
}
return false;
}
static Future<void> deleteWorkspace(String id) async {
if (workspaces.containsKey(id) && workspaces.length == 1) { return; }
workspaces.remove(id);
await _service.delete(null, id, {});
current = workspaces.keys.first;
}
static Future<void> createWorkspace(String name, BuildContext? context) async {
Workspace n = Workspace(name: name);
await _service.post(null, n.serialize(), {}).then((value) {
if (value.data != null) {
workspaces[value.data!.id!] = value.data!;
current = value.data!.id;
fill();
endDrawerKey.currentState?.setState(() {});
CatalogFactory.key.currentState?.setState(() {});
CatalogItemFactory.key.currentState?.setState(() {});
WorkflowFactory.key.currentState?.setState(() {});
HeaderConstants.headerKey.currentState?.setState(() {});
}
});
}
static List<AbstractItem> byTopic(String topic) {
static void changeWorkspaceByName(String name) {
try {
var id = workspaces.entries.firstWhere((element) => element.value.name == "${name}_workspace").key;
changeWorkspace(id);
} catch (e) {}
}
static void changeWorkspace(String id) {
_service.put(null, id, { "active" : true }, {});
current = id;
fill();
HeaderConstants.headerKey.currentState?.setState(() {});
endDrawerKey.currentState?.setState(() {});
CatalogFactory.key.currentState?.setState(() {});
CatalogItemFactory.key.currentState?.setState(() {});
WorkflowFactory.key.currentState?.setState(() {});
}
static String getWorkspaceName(String id) {
return workspaces[id]?.name ?? "";
}
static List<WorkSpaceItem> getWorkspacesIDS() {
List<WorkSpaceItem> res = [];
for (var element in workspaces.entries) {
res.add(WorkSpaceItem(id: element.key, name: element.value.name));
}
return res;
}
static List<Shallow> getWorkspacesShallow() {
List<Shallow> res = [];
for (var element in workspaces.values) {
res.add(Shallow(id: element.id ?? "", name: element.name ?? ""));
}
return res;
}
static List<AbstractItem> byWorkspace(String id) {
List<AbstractItem> d = [];
var w = workspaces[id]!;
d = [ ...d, ...w.datas.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
d = [ ...d, ...w.computes.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
d = [ ...d, ...w.processings.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
d = [ ...d, ...w.storages.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
d = [ ...d, ...w.workflows.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
return d;
}
static List<AbstractItem> byTopic(String topic, bool all) {
if (all) {
List<AbstractItem> d = [];
for (var w in workspaces.values) {
d = [ ...d, ...w.datas.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
d = [ ...d, ...w.computes.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
d = [ ...d, ...w.processings.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
d = [ ...d, ...w.storages.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
d = [ ...d, ...w.workflows.where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
}
return d.where((element) => element.topic.toString() == topic).toList();
}
return WorkspaceLocal.items.where((element) => element.topic.toString() == topic).toList();
}
static AbstractItem? getItem(String id) {
static AbstractItem? getItem(String id, bool all) {
if (all) {
List<AbstractItem> d = [];
for (var w in workspaces.values) {
d = [ ...d, ...w.datas.where((element) => element.id.toString() == id).where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
d = [ ...d, ...w.computes.where((element) => element.id.toString() == id).where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
d = [ ...d, ...w.processings.where((element) => element.id.toString() == id).where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
d = [ ...d, ...w.storages.where((element) => element.id.toString() == id).where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
d = [ ...d, ...w.workflows.where((element) => element.id.toString() == id).where((element) => !d.map( (d2) => d2.id).contains(element.id)) ];
}
return d.isEmpty ? null : d.first;
}
var found = WorkspaceLocal.items.where((element) => element.id.toString() == id);
return found.isEmpty ? null : found.first;
}
static void addItem(AbstractItem item) {
if (!WorkspaceLocal.hasItem(item)) {
if (!WorkspaceLocal.hasItem(item) && workspaces[current] != null && workspaces[current]!.id != null) {
items.add(item);
try {
_service.post(null, {}, { "id" : item.id.toString(), "rtype" : item.type.toString() });
} catch (e) { /* */ }
if (item.topic == "data") { workspaces[current]!.datas.add(item as DataItem); }
if (item.topic == "processing") { workspaces[current]!.processings.add(item as ProcessingItem); }
if (item.topic == "compute") { workspaces[current]!.computes.add(item as ComputeItem); }
if (item.topic == "storage") { workspaces[current]!.storages.add(item as StorageItem); }
if (item.topic == "workflow") { workspaces[current]!.workflows.add(item as WorkflowItem); }
try {
_service.put(null, workspaces[current]!.id!, workspaces[current]!.serialize(), {});
} catch (e) {
_service.put(null, item.id ?? "", workspaces[current]!.serialize(), {});
}
}
}
static void removeItem(AbstractItem item) {
items = items.where((element) => element.name != item.name).toList();
try { _service.delete(null, { "id" : item.id.toString(), "rtype" : item.type.toString() });
} catch (e) { /* */ }
if (item.topic == "data") { workspaces[current]!.datas.removeWhere( (e) => e.id == item.id); }
if (item.topic == "processing") { workspaces[current]!.processings.removeWhere( (e) => e.id == item.id); }
if (item.topic == "compute") { workspaces[current]!.computes.removeWhere( (e) => e.id == item.id); }
if (item.topic == "storage") { workspaces[current]!.storages.removeWhere( (e) => e.id == item.id); }
if (item.topic == "workflow") { workspaces[current]!.workflows.removeWhere( (e) => e.id == item.id); }
try {
_service.put(null, workspaces[current]!.id!, workspaces[current]!.serialize(), {});
} catch (e) {
_service.put(null, item.id ?? "", workspaces[current]!.serialize(), {});
}
}
static bool hasItemByID(String id) {
return items.where((element) => element.id == id).isNotEmpty;
}
static bool hasItem(AbstractItem item) {
return items.where((element) => element.name == item.name).isNotEmpty;
return items.where((element) => element.id == item.id).isNotEmpty;
}
static void clear() => items.clear();
}

View File

@@ -1,8 +1,14 @@
import 'package:oc_front/main.dart';
import 'package:flutter/material.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/pages/shared.dart';
import 'package:oc_front/pages/catalog.dart';
import 'package:oc_front/core/models/workspace_local.dart';
import 'package:oc_front/widgets/items/item_row.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/core/models/workspace_local.dart';
import 'package:oc_front/widgets/inputs/shallow_text_input.dart';
import 'package:oc_front/core/models/shared_workspace_local.dart';
import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart';
import 'package:oc_front/core/services/specialized_services/workspace_service.dart';
GlobalKey<EndDrawerWidgetState> endDrawerKey = GlobalKey<EndDrawerWidgetState>();
class EndDrawerWidget extends StatefulWidget {
@@ -14,35 +20,83 @@ class EndDrawerWidgetState extends State<EndDrawerWidget> {
@override Widget build(BuildContext context) {
List<ItemRowWidget> itemRows = WorkspaceLocal.items.map(
(e) => ItemRowWidget(contextWidth: 400, item: e, keys: [endDrawerKey, CatalogFactory.key],)).toList();
return Container(
color: Colors.white,
width: 400,
height: MediaQuery.of(context).size.height,
child: SingleChildScrollView(
return Stack( children: [
Container(
color: Colors.white,
width: 400,
height: getHeight(context),
child: Column( children: [
Container(
width: 400,
height: 50,
decoration: const BoxDecoration(color: Color.fromRGBO(38, 166, 154, 1)),
child: const Center(
child: Row( mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(padding: EdgeInsets.only(right: 20), child: Icon(Icons.shopping_cart_outlined, size: 18, color: Colors.white)),
Text("Workspace", style: TextStyle(fontSize: 18, color: Colors.white, fontWeight: FontWeight.w600))
])
Container(
width: 400,
height: 50,
decoration: BoxDecoration(color: lightColor ),
child: const Center(
child: Row( mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(padding: EdgeInsets.only(right: 20), child: Icon(Icons.shopping_cart_outlined, size: 18, color: Colors.white)),
Text("Workspace", style: TextStyle(fontSize: 18, color: Colors.white, fontWeight: FontWeight.w600))
])
),
),
),
itemRows.isEmpty ? Container( height: MediaQuery.of(context).size.height - 50,
color: Colors.grey.shade300,
child: const Center(child: Text("WORKSPACE IS EMPTY",
style: TextStyle(fontSize: 25, fontWeight: FontWeight.w600, color: Colors.white))))
: Container( child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column( children: itemRows)
)),
])
)
ShallowDropdownInputWidget(
current: WorkspaceLocal.current,
filled: Colors.white,
width: 400,
all: () async => WorkspaceLocal.getWorkspacesShallow(),
canRemove: (p0) => p0 != null,
remove: (p0) async {
await WorkspaceService().delete(context, p0, {}).then( (e) => WorkspaceLocal.deleteWorkspace(p0));
},
type: CollaborativeAreaType.workspace,
change: (String? change) {
WorkspaceLocal.changeWorkspace(change.toString());
}
),
Container(
width: 400,
height: 40,
decoration: BoxDecoration(
color: Colors.white,
border: Border(bottom: BorderSide(color: midColor), top: BorderSide(color: midColor)),
),
child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [
Padding(padding: EdgeInsets.only(right: 5), child: Icon(Icons.share, size: 12, color: Colors.grey)),
Text( (WorkspaceLocal.workspaces[WorkspaceLocal.current]?.shared) == null ?
"actually not shared" : "share with ${CollaborativeAreaLocal.workspaces[WorkspaceLocal.workspaces[WorkspaceLocal.current]!.shared!]}",
style: TextStyle( fontSize: 14, color: Colors.grey ),),
]),
),
Column( children: [
itemRows.isEmpty ? Container( height: getHeight(context) - 140,
color: Colors.white,
child: Center(child: Text("WORKSPACE IS EMPTY",
style: TextStyle(fontSize: 25, fontWeight: FontWeight.w600, color: midColor))))
: Container( height: getHeight(context) - 140, child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column( children: [ ...itemRows, Container(height: 50)])
)),
])
])
),
Positioned( bottom: 0, left: 0,
child: Tooltip( message: "create workspace", child: ShallowTextInputWidget(
width: 400,
tooltipLoad: "create workspace",
iconLoad: Icons.create_new_folder_sharp,
type: CollaborativeAreaType.workspace,
color: Colors.white,
filled: midColor,
hint: "enter workspace name",
hintColor: Colors.grey,
canLoad: (String? remove) => remove != null,
load: (Map<String?, dynamic> add) async {
if (add["name"] == null) { return; }
WorkspaceLocal.createWorkspace(add["name"], context);
},
),
))
]
);
}
}

View File

@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/core/services/perms_service.dart';
import 'package:oc_front/core/services/router.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/pages/shared.dart';
import 'package:oc_front/widgets/inputs/shallow_text_input.dart';
class DefaultWidget extends StatefulWidget{
const DefaultWidget (): super(key: null);
@override DefaultWidgetState createState() => DefaultWidgetState();
}
class DefaultWidgetState extends State<DefaultWidget> {
@override Widget build(BuildContext context) {
return Container(
width: getMainWidth(context),
height: getHeight(context) - 50,
color: midColor,
padding: EdgeInsets.only(top: 50),
child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: getMainHeight(context) < 600 ? MainAxisAlignment.center : MainAxisAlignment.start,
children: [ getMainHeight(context) < 600 ? Container() : Container(
margin: EdgeInsets.only(left: 40),
child: SvgPicture.asset("assets/images/logo.svg", width: 300, height: (getMainHeight(context) / (1.8) ), semanticsLabel: 'OpenCloud Logo')
),
ShallowTextInputWidget(
alignment: MainAxisAlignment.center,
width: getMainWidth(context) / 1.5,
type: CollaborativeAreaType.workspace,
hint: "search in resources...",
iconLoad: PermsService.getPerm(Perms.SEARCH_INTERNAL) ? Icons.search : null,
iconRemove: PermsService.getPerm(Perms.SEARCH_EXTERNAL) ? Icons.screen_search_desktop_outlined : null,
tooltipLoad: PermsService.getPerm(Perms.SEARCH_INTERNAL) ? "search" : null,
tooltipRemove: PermsService.getPerm(Perms.SEARCH_EXTERNAL) ? "distributed search" : null,
canLoad: PermsService.getPerm(Perms.SEARCH_INTERNAL) ? (String? str) => str != null && str.isNotEmpty : null,
canRemove: PermsService.getPerm(Perms.SEARCH_EXTERNAL) ? (String? str) => str != null && str.isNotEmpty : null,
change: (value) => SearchConstants.set(value),
loadStr: PermsService.getPerm(Perms.SEARCH_INTERNAL) ? (String val) async {
AppRouter.currentRoute.factory.search(context, false);
} : null,
remove: PermsService.getPerm(Perms.SEARCH_EXTERNAL) ? (String val) async {
AppRouter.currentRoute.factory.search(context, true);
} : null,
)
])
);
}
}

View File

@@ -1,33 +1,140 @@
import 'package:flutter/material.dart';
import 'package:localstorage/localstorage.dart';
import 'package:oc_front/core/models/workspace_local.dart';
import 'package:oc_front/core/sections/header/default.dart';
import 'package:oc_front/core/sections/header/menu.dart';
import 'package:oc_front/core/sections/header/search.dart';
import 'package:oc_front/core/services/perms_service.dart';
import 'package:oc_front/core/services/router.dart';
import 'package:oc_front/widgets/menu_clipper/clipper_menu.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/pages/shared.dart';
import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart';
import 'package:oc_front/widgets/inputs/shallow_text_input.dart';
class SearchConstants {
static final Map<String, String?> _searchHost = {};
static String? get() { return _searchHost[AppRouter.currentRoute.route]; }
static void set(String? search) { _searchHost[AppRouter.currentRoute.route] = search; }
static void remove() { _searchHost.remove(AppRouter.currentRoute.route); }
static void clear() {
localStorage.setItem("search", "");
_searchHost.clear();
}
}
class HeaderConstants {
static GlobalKey<HeaderMenuWidgetState> headerKey = GlobalKey<HeaderMenuWidgetState>();
static final List<RouterItem> noHeader = [
AppRouter.scheduler,
AppRouter.compute,
AppRouter.shared,
AppRouter.workflowItem,
AppRouter.workflowIDItem,
];
static HeaderWidgetState? headerWidget;
static double height = 200;
static String? title;
static String? description;
static getKey() {
headerKey = GlobalKey<HeaderMenuWidgetState>();
return headerKey;
}
static setTitle(String? v) {
title = v;
Future.delayed(Duration(milliseconds: 100), () {
HeaderConstants.headerWidget?.setState(() {});
});
}
static setDescription(String? v) {
description = v;
Future.delayed(Duration(milliseconds: 100), () {
HeaderConstants.headerWidget?.setState(() {});
});
}
static bool isNoHeader(String route) {
return noHeader.where((element) => element.route == route).isNotEmpty;
}
}
GlobalKey<HeaderWidgetState> headerWidgetKey = GlobalKey<HeaderWidgetState>();
class HeaderWidget extends StatefulWidget {
HeaderWidget () : super(key: headerWidgetKey);
const HeaderWidget () : super(key: null);
@override HeaderWidgetState createState() => HeaderWidgetState();
}
class HeaderWidgetState extends State<HeaderWidget> {
@override Widget build(BuildContext context) {
headerWidgetKey = GlobalKey<HeaderWidgetState>();
headerMenuKey.currentState?.closeMenu();
HeaderConstants.height = HeaderConstants.isNoHeader(AppRouter.currentRoute.route) ? 50 : 200;
HeaderConstants.headerWidget = this;
if (HeaderConstants.isNoHeader(AppRouter.currentRoute.route)) {
return Container();
}
if (AppRouter.currentRoute.factory.searchFill()) {
return DefaultWidget();
}
HeaderConstants.height = HeaderConstants.isNoHeader(AppRouter.currentRoute.route) || !AppRouter.currentRoute.factory.searchFill() ? 50 : 100;
return Column( children: [
const HeaderMenuWidget(),
HeaderConstants.isNoHeader(AppRouter.currentRoute.route) ? Container() : SearchWidget()
AppRouter.currentRoute.factory.searchFill() ? Container() : Container(
height: 50, width: getMainWidth(context),
decoration: BoxDecoration(
color: midColor,
border: Border(bottom: BorderSide(color: Colors.white, width: 1),)
),
child: Row(crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell( onTap: () {
AppRouter.currentRoute.factory.back(context);
},
child:
Container(width: 50, height: 50,
color: Colors.black,
child: Center(child: Icon(Icons.keyboard_backspace, color: Colors.white))
),
),
ShallowTextInputWidget(
filled: midColor,
current: AppRouter.currentRoute.factory.getSearch(),
width: getMainWidth(context) - 501,
type: CollaborativeAreaType.workspace,
hint: "search in resources...",
iconLoad: PermsService.getPerm(Perms.SEARCH_INTERNAL) ? Icons.search : null,
iconRemove: PermsService.getPerm(Perms.SEARCH_EXTERNAL) ? Icons.screen_search_desktop_outlined : null,
tooltipLoad: PermsService.getPerm(Perms.SEARCH_INTERNAL) ? "search" : null,
tooltipRemove: PermsService.getPerm(Perms.SEARCH_EXTERNAL) ? "distributed search" : null,
canLoad: PermsService.getPerm(Perms.SEARCH_INTERNAL) ? (String? str) => str != null && str.isNotEmpty : null,
canRemove: PermsService.getPerm(Perms.SEARCH_EXTERNAL) ? (String? str) => str != null && str.isNotEmpty : null,
change: (value) => SearchConstants.set(value),
loadStr: PermsService.getPerm(Perms.SEARCH_INTERNAL) ? (String val) async {
AppRouter.currentRoute.factory.search(context, false);
} : null,
remove: PermsService.getPerm(Perms.SEARCH_EXTERNAL) ? (String val) async {
AppRouter.currentRoute.factory.search(context, true);
} : null,
),
Container( padding: EdgeInsets.only(left: 50),
decoration: BoxDecoration( border: Border( left: BorderSide( color: Colors.white ))),
child: ShallowDropdownInputWidget(
prefixIcon: Padding( padding: EdgeInsets.only(right: 10), child: Icon(Icons.shopping_cart, color: Colors.grey)),
current: WorkspaceLocal.current,
width: 350,
all: () async => WorkspaceLocal.getWorkspacesShallow(),
type: CollaborativeAreaType.workspace,
change: (String? change) {
WorkspaceLocal.changeWorkspace(change.toString());
},
canLoad: (p0) => true,
load: (p0) async {
scaffoldKey.currentState?.openEndDrawer();
},
tooltipLoad: "open workspace manager",
iconLoad: Icons.remove_red_eye,
color: Colors.black,
filled: midColor,
hintColor: Colors.grey,
))
])
),
],);
}
}

View File

@@ -1,52 +1,61 @@
import 'package:flutter_svg/svg.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/core/services/auth.service.dart';
import 'package:oc_front/core/services/router.dart';
import 'package:oc_front/main.dart';
import 'package:flutter/material.dart';
import 'package:oc_front/widgets/menu_clipper/clipper_menu.dart';
import 'package:oc_front/widgets/dialog/login.dart';
import 'package:oc_front/pages/catalog.dart';
class HeaderMenuWidget extends StatefulWidget{
const HeaderMenuWidget ({ super.key });
HeaderMenuWidget (): super(key: HeaderConstants.getKey());
@override HeaderMenuWidgetState createState() => HeaderMenuWidgetState();
}
class HeaderMenuWidgetState extends State<HeaderMenuWidget> {
@override Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
width: getWidth(context),
height: 50,
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.grey.shade300))
color: Colors.white,
border: Border(bottom: BorderSide(color: midColor))
),
child: Padding(padding: const EdgeInsets.only(top: 5, bottom: 5, left: 50, right: 50),
child: Stack(children: [
/*...(searchWidgetKey.currentState == null ? [Positioned( left: -20, top: -5,
child: SvgPicture.asset("assets/images/icon.svg", height: 70, semanticsLabel: 'OpenCloud Logo'))] : []),*/
Row(crossAxisAlignment: CrossAxisAlignment.stretch,
child: Stack(children: [
AppRouter.currentRoute.factory.searchFill() ? Container() : Positioned( top: 0, left: 30,
child: InkWell( onTap: () {
CatalogFactory.key.currentState?.widget.isSearch = true;
CatalogFactory.key.currentState?.widget.items = [];
AppRouter.zones.first.go(context, {});
AppRouter.zones.first.factory.getKey().currentState?.setState(() {});
HeaderConstants.headerKey.currentState?.setState(() {});
},
child: Wrap( alignment: WrapAlignment.center, children: [
SvgPicture.asset("assets/images/icon.svg", width: 70, height: 70, semanticsLabel: 'OpenCloud Logo'),
Container( height: 50, alignment: Alignment.centerLeft, margin: const EdgeInsets.only(left: 20),
child: Text(AppRouter.currentRoute.label ?? "Where am I ?", style: TextStyle( color: Colors.grey)))
]))),
Padding( padding: const EdgeInsets.only(left: 50),
child: Row(crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Tooltip( message: "workspace/cart", child: Padding(padding: const EdgeInsets.only(left: 10),
child: IconButton(
icon: const Icon(Icons.shopping_cart_outlined),
onPressed: () {
headerMenuKey.currentState?.closeMenu();
scaffoldKey.currentState?.openEndDrawer();
},
)
Center(child: Text("hello \"${AuthService.getUsername() ?? ""}\"", style: const TextStyle(color: Colors.grey, fontSize: 15))),
Padding(padding: const EdgeInsets.only(top: 5, bottom: 5, right: 50, left: 20),
child: Row(crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Tooltip( message: "logout", child: Padding(padding: const EdgeInsets.only(left: 10),
child: IconButton(
icon: const Icon(FontAwesomeIcons.powerOff),
onPressed: () async {
await AuthService.logout();
mainKey?.currentState?.setState(() {});
},
)
)),
Tooltip( message: "login", child: Padding(padding: const EdgeInsets.only(left: 10),
child: IconButton(
icon: const Icon(Icons.login_outlined),
onPressed: () { showDialog(context: context, builder: (context) => LoginWidget()); },
)
)),
Tooltip( message: "navigation", child: Padding(padding: const EdgeInsets.only(left: 10),
child: ClipperMenuWidget(
borderRadius: BorderRadius.circular(4),
iconColor: Colors.white,
)
))
]
)
))
])
)
)])
);
}
}

View File

@@ -1,125 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:oc_front/core/services/router.dart';
class SearchConstants {
static final Map<String, String?> _searchHost = {};
static String? get() { return _searchHost[AppRouter.currentRoute.route]; }
static void set(String? search) { _searchHost[AppRouter.currentRoute.route] = search; }
static void remove() { _searchHost.remove(AppRouter.currentRoute.route); }
static void clear() { _searchHost.clear(); }
}
GlobalKey<SearchWidgetState> searchWidgetKey = GlobalKey<SearchWidgetState>();
class SearchWidget extends StatefulWidget {
SearchWidget (): super(key: GlobalKey<SearchWidgetState>());
@override SearchWidgetState createState() => SearchWidgetState();
}
class SearchWidgetState extends State<SearchWidget> {
@override Widget build(BuildContext context) {
searchWidgetKey = widget.key as GlobalKey<SearchWidgetState>;
List<Widget> widgets = [
...(MediaQuery.of(context).size.width > 600 || AppRouter.currentRoute.factory.searchFill()
? [InkWell(
mouseCursor: SystemMouseCursors.click,
onTap: () => AppRouter.zones.first.go(context, {}),
child: Container(
margin: EdgeInsets.only(top: 20, left: AppRouter.currentRoute.factory.searchFill() ? 40 : 0),
child: SvgPicture.asset(
width: 300,
height: AppRouter.currentRoute.factory.searchFill() ?
((MediaQuery.of(context).size.height - 50) / 2) : 150,
"assets/images/logo.svg",
semanticsLabel: 'OpenCloud Logo'
)
)
)] : []),
AppRouter.currentRoute.description != null ?
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(AppRouter.currentRoute.description!, style: const TextStyle(
color: Color.fromRGBO(38, 166, 154, 1),
fontSize: 24,
fontWeight: FontWeight.w600
)),
Row(children: [
...(AppRouter.currentRoute.help == null || AppRouter.currentRoute.help!.isEmpty ? []
: [ const Padding( padding: EdgeInsets.only(right: 10),
child: Icon(Icons.help_outline, color: Colors.grey, size: 20)),
Text(AppRouter.currentRoute.help ?? "", style: const TextStyle(
color: Colors.grey,
fontSize: 14,
fontWeight: FontWeight.w400
)) ])
],)
],
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width - 300 - 100,
height: 50,
color: Colors.white,
child: TextField(
onChanged: (value) => SearchConstants.set(value),
decoration: InputDecoration(
hintText: "Search in ${AppRouter.currentRoute.route}...",
contentPadding: const EdgeInsets.symmetric(horizontal: 30),
hintStyle: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w300
),
border: InputBorder.none
)
)
),
Tooltip(
message: 'search',
child: InkWell(
onTap: () {
AppRouter.currentRoute.factory.search(context);
},
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.black,
border: Border(right: BorderSide(color: Colors.white)),
),
child: const Icon(Icons.search, color: Colors.white)
)
)
),
Tooltip(
message: 'distributed search',
child: InkWell(
onTap: () {
AppRouter.currentRoute.factory.search(context);
},
child: Container(
width: 50,
height: 50,
color: Colors.black,
child: const Icon(Icons.screen_search_desktop_outlined, color: Colors.white)
)
)
)
])
];
return Container(
width: MediaQuery.of(context).size.width,
height: AppRouter.currentRoute.factory.searchFill() ? (MediaQuery.of(context).size.height - 50) : 150,
color: Colors.grey.shade300,
child: AppRouter.currentRoute.factory.searchFill() ? Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: widgets)
: Row( mainAxisAlignment: MediaQuery.of(context).size.width < 600
? MainAxisAlignment.center : MainAxisAlignment.start, children: widgets)
);
}
}

View File

@@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/services/router.dart';
class LeftMenuWidget extends StatefulWidget {
const LeftMenuWidget({Key? key}): super(key: key);
@override LeftMenuWidgetState createState() => LeftMenuWidgetState();
}
class LeftMenuWidgetState extends State<LeftMenuWidget> {
@override Widget build(BuildContext context) {
var routes = AppRouter.zones.where( (e) => e.label != null && e.icon != null).toList();
List<Widget> widgets = routes.map( (e) => Opacity( opacity: AppRouter.currentRoute.route == e.route ? 1 : .5,
child: Tooltip( message: e.label, child: SizedBox( width: 50, height: 50, child: InkWell(
onTap: () { e.go(context, {}); },
child: Center( child: Icon(e.icon, color:Colors.white) ),
),
)))).toList();
return Column( children: widgets);
}
}

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:alert_banner/exports.dart';
import 'package:localstorage/localstorage.dart';
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/widgets/dialog/alert.dart';
@@ -11,19 +12,18 @@ import 'package:oc_front/core/services/html.dart' if (kIsWeb) 'dart:html' as htt
class APIService<T extends SerializerDeserializer> {
static bool forceRequest = false;
static Map<String, APIResponse<dynamic>> cache = <String, APIResponse<dynamic>>{};
static String auth = "";
Dio _dio = Dio(
BaseOptions(
baseUrl: const String.fromEnvironment('HOST', defaultValue: 'http://localhost:8080'), // you can keep this blank
headers: { 'Content-Type': 'application/json; charset=UTF-8' },
headers: { 'Content-Type': 'application/json; charset=UTF-8', 'Access-Control-Allow-Origin': '*' },
),
)..interceptors.add(LogInterceptor( requestHeader: true, ),);
)..interceptors.add(LogInterceptor( requestHeader: true, ));
APIService({ required String baseURL }) {
APIService({ required String baseURL}) {
_dio = Dio(
BaseOptions(
baseUrl: baseURL, // you can keep this blank
headers: { 'Content-Type': 'application/json; charset=UTF-8' },
headers: { 'Content-Type': 'application/json; charset=UTF-8', 'Access-Control-Allow-Origin': '*' },
),
)..interceptors.add(LogInterceptor( requestHeader: true, ),);
}
@@ -51,7 +51,6 @@ class APIService<T extends SerializerDeserializer> {
Future _mainDownload(String url, String method, bool isFilter, String? extend, String savePath, bool isWeb, BuildContext context) async {
try {
downloadProgressNotifier.value = 0;
// dio.options.headers["authorization"] = auth;
if (isWeb) {
_dio.get("$url${extend ?? ""}").then((value) {
var url = http.Url.createObjectUrlFromBlob(http.Blob([value.data]));
@@ -74,40 +73,40 @@ class APIService<T extends SerializerDeserializer> {
Future<APIResponse<T>> _main(String url, dynamic body, String method, String succeed, bool force,
BuildContext? context, Options? options) async {
var err = "";
if ((!force) && cache.containsKey(url) && cache[url] != null ) {
return cache[url]! as APIResponse<T>;
}
try {
_dio.options.headers["authorization"] = auth;
var type = localStorage.getItem('tokenType') ?? "bearer";
_dio.options.headers["Authorization"] = "${type[0].toUpperCase() + type.substring(1)} ${localStorage.getItem('accessToken') ?? ""}";
_dio.interceptors.clear();
var response = await _request(url, method, body, options);
if (response.statusCode != null && response.statusCode! < 400) {
if (method == "delete") { cache.remove(url); return APIResponse<T>(); }
APIResponse<T> resp = APIResponse<T>().deserialize(response.data);
if (resp.error == "") {
if (method == "get") { cache[url]=resp; }
if (context != null && succeed != "") {
// ignore: use_build_context_synchronously
showAlertBanner(context, () {}, InfoAlertBannerChild(text: succeed), // <-- Put any widget here you want!
try {
showAlertBanner(context, () {}, InfoAlertBannerChild(text: succeed), // <-- Put any widget here you want!
alertBannerLocation: AlertBannerLocation.bottom,);
} catch (e) { /* */ }
}
try { return cache[url] as APIResponse<T>;
} catch (e) { return APIResponse(); }
return resp;
}
err = resp.error ?? "internal error";
}
if (response.statusCode == 401) { err = "not authorized"; }
} else if (response.statusCode == 401) { err = "not authorized"; }
} catch(e, s) {
print(e);
print(s);
err = e.toString();
if (!e.toString().contains("context")) {
print(e);
print(s);
err = e.toString();
}
}
//if (err.contains("token") && err.contains("expired")) { AuthService().unAuthenticate(); }
if (context != null) {
// ignore: use_build_context_synchronously
showAlertBanner( context, () {}, AlertAlertBannerChild(text: err),// <-- Put any widget here you want!
try {
showAlertBanner( context, () {}, AlertAlertBannerChild(text: err),// <-- Put any widget here you want!
alertBannerLocation: AlertBannerLocation.bottom,);
} catch (e) { /* */ }
}
throw Exception(err);
}
@@ -116,7 +115,8 @@ class APIService<T extends SerializerDeserializer> {
var err = "";
if (url != "") {
try {
_dio.options.headers["authorization"] = auth;
var type = localStorage.getItem('tokenType') ?? "bearer";
_dio.options.headers["Authorization"] = "${type[0].toUpperCase() + type.substring(1)} ${localStorage.getItem('accessToken') ?? ""}";
_dio.interceptors.clear();
var response = await _request(url, method, body, null);
if (response.statusCode != null && response.statusCode! < 400) {
@@ -129,7 +129,6 @@ class APIService<T extends SerializerDeserializer> {
} catch(e, s) { print(e); print(s);
err = "${e.toString()} ${const String.fromEnvironment('HOST', defaultValue: 'http://localhost:8080')}"; }
} else { err = "no url"; }
// if (err.contains("token") && err.contains("expired")) { AuthService().unAuthenticate(); }
throw Exception(err);
}

View File

@@ -0,0 +1,99 @@
import 'package:localstorage/localstorage.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/core/services/api_service.dart';
import 'package:oc_front/core/services/perms_service.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/response.dart';
class AuthService {
static var isAuth = const bool.fromEnvironment('AUTH_MODE', defaultValue: false);
static final _clientID = const String.fromEnvironment('CLIENT_ID', defaultValue: 'test-client');
static APIService<SimpleData> service = APIService(
baseURL: const String.fromEnvironment('AUTH_HOST', defaultValue: 'http://localhost:8080/auth'),
);
static Future<void> init() async {
if (!isAuth) {
return;
}
PermsService.init(localStorage.getItem('accessToken') ?? "");
bool ok = await introspect().catchError( (e) => false );
if (ok) {
var now = DateTime.now();
var expires = DateTime.parse(localStorage.getItem('expiresIn') ?? DateTime.now().toIso8601String());
var duration = expires.difference(now);
refresh(localStorage.getItem('accessToken') ?? "", localStorage.getItem('username') ?? "", duration);
} else {
localStorage.setItem('accessToken', '');
localStorage.setItem('username', '');
localStorage.setItem('expiresIn', '');
}
}
static bool isConnected() {
if (!isAuth) {
return true;
}
return (localStorage.getItem('accessToken') ?? "") != ""
&& localStorage.getItem('username') != "";
}
static String? getUsername() {
if (!isAuth) {
return "no auth user";
}
return localStorage.getItem('username') ?? "unknown";
}
static Future<void> login(String username, String password) async {
var token = await service.post("/login?client_id=$_clientID", <String, dynamic> {
"username": username,
"password": password
}, null);
if (token.code == 200 && token.data != null) {
localStorage.setItem('accessToken', token.data?.value['access_token']);
localStorage.setItem('tokenType', token.data?.value['token_type']);
localStorage.setItem('username', username);
localStorage.setItem('expiresIn', DateTime.now().add(
Duration(seconds: token.data?.value['expires_in'])).toIso8601String());
mainKey?.currentState?.setState(() {});
PermsService.init(token.data?.value['access_token']);
refresh(token.data?.value['access_token'] ?? "", username, Duration(seconds: token.data?.value['expires_in']));
}
}
static Future<void> logout() async {
var token = await service.delete("/logout?client_id=$_clientID", null);
if (token.code == 200) {
localStorage.setItem('accessToken', '');
localStorage.setItem('username', '');
localStorage.setItem('expiresIn', '');
PermsService.clear();
}
}
static Future<bool> introspect() async {
if (!isConnected()) {
return false;
}
// ignore: invalid_return_type_for_catch_error
var isIntrospected = await service.get("/introspect", true, null).catchError((e) => mainKey?.currentState?.setState(() {}));
return isIntrospected.code == 200;
}
static Future<void> refresh(String accessToken, String username, Duration duration) async {
Future.delayed(duration, () {
service.post("/refresh?client_id=$_clientID", <String, dynamic> {
"access_token": accessToken,
"username": username
}, null).then((token) {
if (token.code == 200 && token.data != null) {
PermsService.init(token.data?.value['access_token']);
localStorage.setItem('accessToken', token.data?.value['access_token']);
localStorage.setItem('username', username);
localStorage.setItem('expiresIn',
DateTime.now().add(Duration(seconds: token.data?.value['expires_in']) - Duration(seconds: 10)).toIso8601String());
}
});
});
}
}

View File

@@ -0,0 +1,49 @@
import 'package:oc_front/models/response.dart';
import 'package:oc_front/core/services/api_service.dart';
class EnumService {
static final APIService<EnumData> _service = APIService<EnumData>(
baseURL: const String.fromEnvironment('ITEM_HOST',
defaultValue: 'http://localhost:8080/catalog')
);
static String subPath = "/enum/";
static Map<String, Map<String,dynamic>> enums = {};
static int? get(String path, dynamic name) {
var n = enums[path];
if (n == null) {
return null;
}
var names = "$name";
for (var nn in n.keys) {
if (n[nn] == names || nn == names) {
return int.parse(nn);
}
}
return null;
}
static void init() {
_load("infrastructure");
_load("storage/type");
_load("storage/size");
_load("resource/type");
_load("booking/status");
_load("status");
_load("pricing/strategy/buy");
_load("pricing/strategy/data");
_load("pricing/strategy/time");
_load("pricing/strategy/storage");
_load("pricing/strategy/privilege/storage");
_load("pricing/strategy/privilege");
_load("pricing/refund/type");
}
static void _load(String name) {
_service.get("$subPath$name", false, null).then((response) {
if (response.code == 200) {
enums[name] = response.data!.value;
}
});
}
}

View File

@@ -0,0 +1,99 @@
import 'dart:convert';
import 'package:oc_front/main.dart';
enum Perms {
SEARCH_INTERNAL,// ignore: constant_identifier_names
SEARCH_EXTERNAL, // ignore: constant_identifier_names
WORKSPACE_SHARE,// ignore: constant_identifier_names
WORKSPACE_UNSHARE,// ignore: constant_identifier_names
WORKFLOW_CREATE, // ignore: constant_identifier_names
WORKFLOW_EDIT, // ignore: constant_identifier_names
WORKFLOW_DELETE, // ignore: constant_identifier_names
WORKFLOW_BOOKING, // ignore: constant_identifier_names
WORKFLOW_SHARE, // ignore: constant_identifier_names
WORKFLOW_UNSHARE, // ignore: constant_identifier_names
PEER_SHARE, // ignore: constant_identifier_names
PEER_UNSHARE, // ignore: constant_identifier_names
COLLABORATIVE_AREA_CREATE, // ignore: constant_identifier_names
COLLABORATIVE_AREA_EDIT, // ignore: constant_identifier_names
COLLABORATIVE_AREA_DELETE, // ignore: constant_identifier_names
}
Map<Perms, String> perms = {
Perms.SEARCH_INTERNAL: 'GET__CATALOG_COMPUTE_SEARCH_SEARCH'.toUpperCase(),
Perms.SEARCH_EXTERNAL: 'Search External'.toUpperCase(),
Perms.WORKSPACE_SHARE: 'POST__SHARED_COLLABORATIVE_AREA_ID_WORKSPACE_ID2'.toUpperCase(),
Perms.WORKFLOW_CREATE: 'POST__WORKFLOW_'.toUpperCase(),
Perms.WORKFLOW_UNSHARE: 'DELETE__SHARED_COLLABORATIVE_AREA_ID_WORKFLOW_ID2'.toUpperCase(),
Perms.PEER_SHARE: 'POST__SHARED_COLLABORATIVE_AREA_ID_PEER_ID2'.toUpperCase(),
Perms.PEER_UNSHARE: 'DELETE__SHARED_COLLABORATIVE_AREA_ID_PEER_ID2'.toUpperCase(),
Perms.COLLABORATIVE_AREA_CREATE: 'POST__SHARED_COLLABORATIVE_AREA_'.toUpperCase(),
Perms.COLLABORATIVE_AREA_EDIT: 'PUT__SHARED_COLLABORATIVE_AREA_ID'.toUpperCase(),
Perms.COLLABORATIVE_AREA_DELETE: 'DELETE__SHARED_COLLABORATIVE_AREA_ID'.toUpperCase(),
Perms.WORKSPACE_UNSHARE: 'DELETE__SHARED_COLLABORATIVE_AREA_ID_WORKSPACE_ID2'.toUpperCase(),
Perms.WORKFLOW_EDIT: 'PUT__WORKFLOW_ID'.toUpperCase(),
Perms.WORKFLOW_DELETE: 'DELETE__WORKFLOW_ID'.toUpperCase(),
Perms.WORKFLOW_BOOKING: 'POST__DATACENTER_BOOKING_'.toUpperCase(),
Perms.WORKFLOW_SHARE: 'POST__SHARED_COLLABORATIVE_AREA_ID_WORKFLOW_ID2'.toUpperCase(),
};
class PermsService {
static final Map<Perms, bool> _perms = {
Perms.SEARCH_INTERNAL: false,
Perms.SEARCH_EXTERNAL: false,
Perms.WORKSPACE_SHARE: false,
Perms.WORKSPACE_UNSHARE: false,
Perms.WORKFLOW_CREATE: false,
Perms.WORKFLOW_EDIT: false,
Perms.WORKFLOW_DELETE: false,
Perms.WORKFLOW_BOOKING: false,
Perms.WORKFLOW_SHARE: false,
Perms.WORKFLOW_UNSHARE: false,
Perms.PEER_SHARE: false,
Perms.PEER_UNSHARE: false,
Perms.COLLABORATIVE_AREA_CREATE: false,
Perms.COLLABORATIVE_AREA_EDIT: false,
Perms.COLLABORATIVE_AREA_DELETE: false,
};
static final PermsService _instance = PermsService._internal();
factory PermsService() => _instance;
PermsService._internal();
/* should decode claims such as in oc-auth */
static Future<void> init(String token ) async {
var claims = token.split(".").last;
var decoded = base64.decode(claims);
String foo = utf8.decode(decoded);
try {
var what = json.decode(foo);
what = what["session"]["access_token"] as Map<String, dynamic>;
for (var w in perms.keys) {
if (what.keys.contains(perms[w])) {
_perms[w] = true;
} else {
_perms[w] = false;
}
}
mainKey?.currentState?.setState(() {});
} catch (e) {/**/}
}
static void clear() {
_perms.forEach((key, value) {
_perms[key] = false;
});
}
static bool getPerm(Perms perm) {
return _perms[perm] ?? false;
}
static void setPerm(Perms perm, bool value) {
_perms[perm] = value;
}
}

View File

@@ -1,3 +1,4 @@
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/pages/abstract_page.dart';
import 'package:oc_front/pages/catalog.dart';
@@ -5,6 +6,7 @@ import 'package:oc_front/pages/catalog_item.dart';
import 'package:oc_front/pages/datacenter.dart';
import 'package:oc_front/pages/map.dart';
import 'package:oc_front/pages/scheduler.dart';
import 'package:oc_front/pages/shared.dart';
import 'package:oc_front/pages/workflow.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/material.dart';
@@ -42,25 +44,33 @@ class RouterItem {
AppRouter.currentRoute = this;
var newPath = "$path";
for (var arg in args) { newPath = newPath.replaceAll(":$arg", params[arg] ?? ""); }
Future.delayed( const Duration(seconds: 1), () {
HeaderConstants.setTitle(null);
HeaderConstants.setDescription(null);
});
context.go(newPath);
}
}
class AppRouter {
static const String home = "catalog";
static String home = "catalog";
static final RouterItem workflowItem = RouterItem(icon: Icons.rebase_edit, label: "workflow manager", route: "workflow",
description: "View to select & create new workflow.", help: "Workflow only access to your workspace datas. If a an element of your flow is missing, perhaps means it's missing in workspace.",
factory: WorkflowFactory());
static final RouterItem catalogItem = RouterItem(description: "", help: "", route: "catalog/:id", factory: CatalogItemFactory(), args: ["id"]);
static final RouterItem workflowIDItem = RouterItem(description: "", help: "", route: "workflow/:id", factory: WorkflowFactory(), args: ["id"]);
static final RouterItem catalogItem = RouterItem(label: "resource", description: "", help: "", route: "catalog/:id", factory: CatalogItemFactory(), args: ["id"]);
static final RouterItem catalog= RouterItem(icon: Icons.book_outlined, label: "catalog searcher", route: "catalog", factory: CatalogFactory());
static final RouterItem scheduler = RouterItem(icon: Icons.schedule, label: "scheduled tasks", route: "scheduler", factory: SchedulerFactory());
static final RouterItem compute = RouterItem(icon: Icons.dns_outlined, label: "my compute", route: "compute", factory: DatacenterFactory());
static final RouterItem shared = RouterItem(icon: Icons.share_rounded, label: "collaborative area", route: "shared", factory: SharedFactory());
static List<RouterItem> zones = [
RouterItem(icon: Icons.book_outlined, label: "catalog searcher", route: home, factory: CatalogFactory()),
catalog,
workflowItem,
RouterItem(icon: Icons.schedule, label: "scheduled tasks", route: "scheduler", factory: SchedulerFactory()),
RouterItem(icon: Icons.dns_outlined, label: "my datacenter", route: "datacenter",
description: "Manage & monitor your datacenter.", help: "not implemented for now",
factory: DatacenterFactory()),
scheduler,
compute,
RouterItem(icon: Icons.public_outlined, label: "localisations", route: "map", factory: MapFactory()),
shared,
workflowIDItem,
catalogItem,
];
static List<String> history = [];

View File

@@ -6,10 +6,22 @@ import 'package:oc_front/core/services/api_service.dart';
abstract class AbstractService<T extends SerializerDeserializer> {
abstract APIService<T> service;
abstract String subPath;
Future<APIResponse<T>> search(BuildContext? context, List<String> words, Map<String, dynamic> params) { throw UnimplementedError(); }
Future<APIResponse<RawData>> all(BuildContext? context) {
return service.raw(subPath, null, "get");
}
Future<APIResponse<T>> get(BuildContext? context, String id) {
return service.get("$subPath$id", true, context);
}
Future<APIResponse<T>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params) {
return service.post(subPath, body, context);
}
Future<APIResponse<T>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) {
return service.put("$subPath$id", body, context);
}
Future<APIResponse<T>> delete(BuildContext? context, String id, Map<String, String> params) {
return service.delete("$subPath$id", context);
}
}
Future<APIResponse<T>> all(BuildContext? context) { throw UnimplementedError(); }
Future<APIResponse<T>> get(BuildContext? context, String id);
Future<APIResponse<T>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params);
Future<APIResponse<T>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) { throw UnimplementedError(); }
Future<APIResponse<T>> delete(BuildContext? context, Map<String, String> params) { throw UnimplementedError(); }
}

View File

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

View 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:8080/workflow')
);
@override String subPath = "/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();
}
}

View File

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

View File

@@ -1,24 +0,0 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/services/api_service.dart';
import 'package:oc_front/core/services/specialized_services/abstract_service.dart';
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/response.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:49618')
);
@override String subPath = "/v1/${getTopic(S)}";
@override Future<APIResponse<T>> all(BuildContext? context) { throw UnimplementedError(); }
@override Future<APIResponse<T>> get(BuildContext? context, String id) {
if (id.contains(",")) { return service.get("$subPath/multi/$id", true, context); }
return service.get("$subPath/$id", true, context);
}
@override Future<APIResponse<T>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params) {
return service.post("$subPath/", body, context);
}
@override Future<APIResponse<T>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) { throw UnimplementedError(); }
@override Future<APIResponse<T>> delete(BuildContext? context, Map<String, String> params) { throw UnimplementedError(); }
}

View File

@@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/services/api_service.dart';
import 'package:oc_front/core/services/specialized_services/abstract_service.dart';
import 'package:oc_front/models/logs.dart';
import 'package:oc_front/models/response.dart';
class LogsService extends AbstractService<LogsResult> {
@override APIService<LogsResult> service = APIService<LogsResult>(
baseURL: const String.fromEnvironment('LOGS_HOST', defaultValue: 'http://localhost:3100')
);
@override String subPath = "/loki/api/v1/";
@override Future<APIResponse<LogsResult>> search(BuildContext? context, List<String> words, Map<String, dynamic> params) {
List<String> v = [];
for (var p in params.keys) {
if (p == "start" || p == "end") { continue; }
v.add("$p=\"${params[p]}\"");
}
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<LogsResult>> get(BuildContext? context, String id) {
throw UnimplementedError();
}
@override Future<APIResponse<LogsResult>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params) {
throw UnimplementedError();
}
@override Future<APIResponse<LogsResult>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) {
throw UnimplementedError();
}
@override Future<APIResponse<LogsResult>> delete(BuildContext? context, String id, Map<String, String> params) {
throw UnimplementedError();
}
}

View File

@@ -0,0 +1,10 @@
import 'package:oc_front/core/services/api_service.dart';
import 'package:oc_front/core/services/specialized_services/abstract_service.dart';
import 'package:oc_front/models/shared.dart';
class PeerService extends AbstractService<Peer> {
@override APIService<Peer> service = APIService<Peer>(
baseURL: const String.fromEnvironment('PEER_HOST', defaultValue: 'http://localhost:8080/peer')
);
@override String subPath = "/";
}

View File

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

View File

@@ -1,19 +0,0 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/services/api_service.dart';
import 'package:oc_front/core/services/specialized_services/abstract_service.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/models/search.dart';
class SearchService extends AbstractService<Search> {
@override APIService<Search> service = APIService<Search>(
baseURL: const String.fromEnvironment('SEARCH_HOST', defaultValue: 'http://localhost:49618')
);
@override String subPath = "/v1/search";
@override Future<APIResponse<Search>> all(BuildContext? context) { throw UnimplementedError(); }
@override Future<APIResponse<Search>> get(BuildContext? context, String id) {
return service.get("$subPath/byWord?word=$id", true, context);
}
@override Future<APIResponse<Search>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params) { throw UnimplementedError(); }
@override Future<APIResponse<Search>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) { throw UnimplementedError(); }
@override Future<APIResponse<Search>> delete(BuildContext? context, Map<String, String> params) { throw UnimplementedError(); }
}

View File

@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/services/api_service.dart';
import 'package:oc_front/core/services/specialized_services/abstract_service.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/models/shared.dart';
class SharedService extends AbstractService<CollaborativeArea> {
@override APIService<CollaborativeArea> service = APIService<CollaborativeArea>(
baseURL: const String.fromEnvironment('COLLABORATIVE_AREA_HOST', defaultValue: 'http://localhost:8080/shared')
);
@override String subPath = "/collaborative_area/";
Future<APIResponse<CollaborativeArea>> addWorkspace(BuildContext? context, String id, String id2) {
return service.post("$subPath$id/workspace/$id2", {}, context);
}
Future<APIResponse<CollaborativeArea>> addWorkflow(BuildContext? context, String id, String id2) {
return service.post("$subPath$id/workflow/$id2", {}, context);
}
Future<APIResponse<CollaborativeArea>> addPeer(BuildContext? context, String id, String id2) {
return service.post("$subPath$id/peer/$id2", {}, context);
}
Future<APIResponse<CollaborativeArea>> removeWorkspace(BuildContext? context, String id, String id2) {
return service.delete("$subPath$id/workspace/$id2", context);
}
Future<APIResponse<CollaborativeArea>> removeWorkflow(BuildContext? context, String id, String id2) {
return service.delete("$subPath$id/workflow/$id2", context);
}
Future<APIResponse<CollaborativeArea>> removePeer(BuildContext? context, String id, String id2) {
return service.delete("$subPath$id/peer/$id2", context);
}
}

View File

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

View File

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

View File

@@ -1,26 +1,10 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/services/api_service.dart';
import 'package:oc_front/core/services/specialized_services/abstract_service.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/models/workflow.dart';
class WorflowService extends AbstractService<RawData> {
@override APIService<RawData> service = APIService<RawData>(
baseURL: const String.fromEnvironment('WORKFLOW_HOST', defaultValue: 'http://localhost:49618')
class WorflowService extends AbstractService<Workflow> {
@override APIService<Workflow> service = APIService<Workflow>(
baseURL: const String.fromEnvironment('WORKFLOW_HOST', defaultValue: 'http://localhost:8080/workflow')
);
@override String subPath = "/v1/workflow/";
@override Future<APIResponse<RawData>> all(BuildContext? context) {
return service.get(subPath, true, context);
}
@override Future<APIResponse<RawData>> get(BuildContext? context, String id) { throw UnimplementedError(); }
@override Future<APIResponse<RawData>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params) {
String path = "?";
for (var key in params.keys) { path += "$key=${params[key]}&"; }
return service.post("$subPath$path", body, context);
}
@override Future<APIResponse<RawData>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) {
throw UnimplementedError();
}
@override Future<APIResponse<RawData>> delete(BuildContext? context, Map<String, String> params) {
throw UnimplementedError();
}
@override String subPath = "/";
}

View File

@@ -1,30 +1,10 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/services/api_service.dart';
import 'package:oc_front/core/services/specialized_services/abstract_service.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/models/workspace.dart';
class WorkspaceService extends AbstractService<Workspace> {
@override APIService<Workspace> service = APIService<Workspace>(
baseURL: const String.fromEnvironment('WORKSPACE_HOST', defaultValue: 'http://localhost:49618')
baseURL: const String.fromEnvironment('WORKSPACE_HOST', defaultValue: 'http://localhost:8080/workspace')
);
@override String subPath = "/v1/workspace/";
@override Future<APIResponse<Workspace>> all(BuildContext? context) {
return service.get("$subPath/list", true, context);
}
@override Future<APIResponse<Workspace>> get(BuildContext? context, String id) { throw UnimplementedError(); }
@override Future<APIResponse<Workspace>> post(BuildContext? context, Map<String, dynamic> body, Map<String, String> params) {
String path = "?";
for (var key in params.keys) { path += "$key=${params[key]}&"; }
return service.post("$subPath$path", body, context);
}
@override Future<APIResponse<Workspace>> put(BuildContext? context, String id, Map<String, dynamic> body, Map<String, String> params) {
throw UnimplementedError();
}
@override Future<APIResponse<Workspace>> delete(BuildContext? context, Map<String, String> params) {
String path = "?";
for (var key in params.keys) { path += "$key=${params[key]}&"; }
return service.delete("$subPath$path", context);
}
@override String subPath = "/";
}

View File

@@ -1,32 +1,42 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
import 'package:localstorage/localstorage.dart';
import 'package:oc_front/core/models/shared_workspace_local.dart';
import 'package:oc_front/core/models/workspace_local.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/core/sections/header/menu.dart';
import 'package:oc_front/core/sections/left_menu.dart';
import 'package:oc_front/core/services/auth.service.dart';
import 'package:oc_front/core/services/enum_service.dart';
import 'package:oc_front/core/services/router.dart';
import 'package:oc_front/core/sections/end_drawer.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:desktop_window/desktop_window.dart' if (kIsWeb) '';
import 'package:oc_front/widgets/dialog/login.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
void main() {
// Run `LinuxWebViewPlugin.initialize()` first before creating a WebView.
await initLocalStorage();
runApp(const MyApp());
}
GlobalKey<MainPageState>? mainKey;
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
if (!kIsWeb) { DesktopWindow.setMinWindowSize(const Size(400, 400)); }
return MaterialApp.router(
routerConfig: GoRouter( routes: AppRouter.routes ),
);
AuthService.init();
EnumService.init();
SearchConstants.clear();
return MaterialApp.router( routerConfig: GoRouter( routes: AppRouter.routes ) );
}
}
// ignore: must_be_immutable
class MainPage extends StatefulWidget {
Widget page;
MainPage({super.key, required this.page});
Widget? page;
MainPage({Key? key, required this.page}) : super(key: GlobalKey<MainPageState>());
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
@@ -38,10 +48,40 @@ class MainPage extends StatefulWidget {
// always marked "final".
@override
State<MainPage> createState() => _MainPageState();
State<MainPage> createState() => MainPageState();
}
class _MainPageState extends State<MainPage> {
var darkColor = Color.fromRGBO(26, 83, 92, 1);
var lightColor = Color.fromRGBO(78, 205, 196, 1);
var darkMidColor = Color.fromRGBO(44, 83, 100, 1);
var midColor = Colors.grey.shade300;
var redColor = Color.fromRGBO(255, 107, 107, 1);
double getWidth(BuildContext context) {
return MediaQuery.of(context).size.width <= 800 ? 800 : MediaQuery.of(context).size.width;
}
double getHeight(BuildContext context) {
return MediaQuery.of(context).size.height <= 400 ? 400 : MediaQuery.of(context).size.height;
}
double getMainHeight(BuildContext context) {
return getHeight(context) - HeaderConstants.height;
}
double getMainWidth(BuildContext context) {
return getWidth(context) - 50;
}
bool loginIsSet = false;
class MainPageState extends State<MainPage> {
bool isCtrl = false;
final FocusNode node = FocusNode();
@override
void initState() {
mainKey = widget.key as GlobalKey<MainPageState>?;
node.requestFocus();
super.initState();
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
@@ -49,32 +89,63 @@ class _MainPageState extends State<MainPage> {
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
WorkspaceLocal.init(context);
// than having to individually change instances of widgets.i
scaffoldKey = GlobalKey<ScaffoldState>();
return Scaffold(
key: scaffoldKey,
endDrawer: EndDrawerWidget(),
body: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
//
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
// action in the IDE, or press "p" in the console), to see the
// wireframe for each widget.
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
HeaderWidget(),
widget.page // CatalogPageWidget(),
],
),
);
isCtrl = false;
if (!AuthService.isConnected() && !loginIsSet) {
Future.delayed(const Duration(milliseconds: 500), () {
loginIsSet = true;
showDialog(
barrierDismissible: false,
context: context ?? context, builder: (context) {
return AlertDialog(
insetPadding: EdgeInsets.zero,
backgroundColor: Colors.white,
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)),
title: LoginWidget());
});
});
}
return FutureBuilder(future: AuthService.init(),
builder: (e, s) {
WorkspaceLocal.init(context, false);
HeaderConstants.height = HeaderConstants.isNoHeader(AppRouter.currentRoute.route) || AppRouter.currentRoute.factory.searchFill() ? 50 : 100;
return Scaffold( key: scaffoldKey, endDrawer: EndDrawerWidget(), body:
SingleChildScrollView(
controller: ScrollController(),
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
child: Column( children: [
HeaderMenuWidget(),
Row( children : [
Container( padding: const EdgeInsets.symmetric(vertical: 30),
decoration: BoxDecoration( color: darkColor),
width: 50, height: getHeight(context) - 50,
child: SingleChildScrollView( child: LeftMenuWidget() )),
SizedBox( width: getMainWidth(context), height: getHeight(context) - 50,
child: KeyboardListener(
focusNode: node,
onKeyEvent: (event) async {
if ( event.logicalKey == LogicalKeyboardKey.controlLeft ) {
isCtrl = (event is KeyDownEvent);
node.requestFocus();
} else if( (event is KeyDownEvent) && event.logicalKey == LogicalKeyboardKey.enter) {
AppRouter.currentRoute.factory.search(context, isCtrl);
node.requestFocus();
}
},
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
HeaderWidget(),
widget.page ?? Container() // CatalogPageWidget(),
],
),
)),
])
])
)
));
});
}
}

View File

@@ -1,5 +1,4 @@
import 'dart:convert';
import 'dart:developer' as developer;
abstract class SerializerDeserializer<T> {
T deserialize(dynamic json);

100
lib/models/logs.dart Normal file
View File

@@ -0,0 +1,100 @@
import 'package:oc_front/models/abstract.dart';
import 'package:json_string/json_string.dart';
class LogsResult extends SerializerDeserializer<LogsResult> {
List<Logs> result;
LogsResult({
this.result = const [],
});
String getID() {
return "";
}
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return LogsResult(); }
return LogsResult(
result: json.containsKey("result") ? fromListJson(json["result"], Logs()) : [],
);
}
@override Map<String, dynamic> serialize() {
return {
"result": toListJson(result),
};
}
}
class Logs extends SerializerDeserializer<Logs> {
String? level;
List<Log> logs = [];
Logs({
this.level,
this.logs = const [],
});
String getID() {
return "";
}
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Logs(); }
return Logs(
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() {
return {
"level": level,
};
}
}
class Log extends SerializerDeserializer<Log> {
DateTime? timestamp;
String? message;
String? level;
String? rawMessage;
Map<String, dynamic> map = {};
Log({
this.timestamp,
this.message,
this.rawMessage,
this.level
});
String getID() {
return "";
}
String getMessage(String mess) {
var jsonString = mess;
try {
var j = JsonString(mess.replaceAll("\\", "")).decodedValue as Map<String, dynamic>;
map = j;
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 ''; })}";
} else {
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) { /* */ }
message = jsonString;
return jsonString;
}
@override deserialize(dynamic json) {
try { json = json as List<dynamic>;
} catch (e) { return Log(); }
var l = Log(
timestamp: json.isNotEmpty ? DateTime.fromMillisecondsSinceEpoch(int.parse(json[0]) ~/ 1000) : 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 { }; }
}

View File

@@ -0,0 +1,222 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/services/enum_service.dart';
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/resources/resources.dart';
class ComputeItem extends AbstractItem<ComputePricing, ComputePartnership, ComputeInstance, ComputeItem> {
// special attributes
int? infrastructureEnum;
String? architecture;
ComputeItem({
this.infrastructureEnum,
this.architecture
}): super();
@override String get topic => "compute";
@override deserialize(dynamic data) {
try { data = data as Map<String, dynamic>;
} catch (e) { return ComputeItem(); }
var w = ComputeItem(
infrastructureEnum: data.containsKey("infrastructure") ? EnumService.get("infrastructure", data["infrastructure"]) : null,
architecture: data.containsKey("architecture") && data["architecture"] != null ? data["architecture"] : null,
);
w.mapFromJSON(data, ComputeInstance());
if (w.logo != null) { // get image dimensions
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> infos() {
return {
"infrastructure": EnumService.enums["infrastructure"] != null
&& EnumService.enums["infrastructure"]!["$infrastructureEnum"] != null ? EnumService.enums["infrastructure"]!["$infrastructureEnum"] : infrastructureEnum,
"architecture": architecture,
};
}
@override Map<String, dynamic> serialize() {
Map<String, dynamic> obj = infos();
obj["infrastructure"] = infrastructureEnum;
obj.addAll(toJSON());
return obj;
}
}
class ComputeInstance extends AbstractInstance<ComputePricing, ComputePartnership> {
String? securityLevel;
List<String>? powerSources = [];
double? annualEnergyConsumption;
Map<String,CPU> cpus = {};
Map<String,GPU> gpus = {};
List<ComputeNode> nodes = [];
ComputeInstance({
this.securityLevel,
this.powerSources = const [],
this.annualEnergyConsumption,
this.cpus = const {},
this.gpus = const {},
this.nodes = const [],
}): super();
@override
Map<String, dynamic> infos() {
return {
"security_level": securityLevel,
"power_sources": powerSources,
"annual_co2_emissions": annualEnergyConsumption,
"cpus": toMapJson(cpus),
"gpus": toMapJson(gpus),
"nodes": toListJson(nodes),
"inputs": toListJson(inputs),
"outputs": toListJson(outputs),
};
}
@override
ComputeInstance deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return ComputeInstance(); }
var w = ComputeInstance(
securityLevel: json.containsKey("security_level") && json["security_level"] != null ? json["security_level"] : null,
powerSources: json.containsKey("power_sources") && json["power_sources"] != null ? List<String>.from(json["power_sources"]) : [],
annualEnergyConsumption: json.containsKey("annual_co2_emissions") && json["annual_co2_emissions"] != null ? json["annual_co2_emissions"] : null,
//cpus: json.containsKey("cpus") && json["cpus"] != null ? fromMapJson(json["cpus"], CPU()) : {},
// gpus: json.containsKey("gpus") && json["gpus"] != null ? fromMapJson(json["gpus"], GPU()) : {},
//nodes: json.containsKey("nodes") && json["nodes"] != null ? fromListJson(json["nodes"], ComputeNode()) : [],
);
w.mapFromJSON(json, ComputePartnership());
return w;
}
@override
Map<String, dynamic> serialize() {
var obj = infos();
obj.addAll(toJSON());
return obj;
}
}
class ComputePartnership extends AbstractPartnerShip<ComputePricing> {
Map<String, dynamic> maxAllowedCPUsCores = {};
Map<String, dynamic> maxAllowedGPUsMemoryGB = {};
double? maxAllowedRAM;
ComputePartnership({
this.maxAllowedCPUsCores = const {},
this.maxAllowedGPUsMemoryGB = const {},
this.maxAllowedRAM,
}): super();
@override
ComputePartnership deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return ComputePartnership(); }
var w = ComputePartnership(
maxAllowedCPUsCores: json.containsKey("allowed_cpus") && json["allowed_cpus"] != null ? json["allowed_cpus"] : {},
maxAllowedGPUsMemoryGB: json.containsKey("allowed_gpus") && json["allowed_gpus"] != null ? json["allowed_gpus"] : {},
maxAllowedRAM: json.containsKey("allowed_ram") && json["allowed_ram"] != null ? double.parse("${json["allowed_ram"]}") : null,
);
w.mapFromJSON(json, ComputePricing());
return w;
}
@override
Map<String, dynamic> serialize() {
Map<String, dynamic> obj = {
"allowed_cpus": maxAllowedCPUsCores,
"allowed_gpus": maxAllowedGPUsMemoryGB,
"allowed_ram": maxAllowedRAM,
};
obj.addAll(toJSON());
return obj;
}
}
class ComputePricing extends AbstractPricing {
Map<String, dynamic> cpusPrice = {};
Map<String, dynamic> gpusPrice = {};
double? ramPrice;
ComputePricing({
this.cpusPrice = const {},
this.gpusPrice = const {},
this.ramPrice,
});
@override ComputePricing deserialize(json) {
var w = ComputePricing(
cpusPrice: json.containsKey("cpus") && json["cpus"] != null ? json["cpus"] : {},
gpusPrice: json.containsKey("gpus") && json["gpus"] != null ? json["gpus"] : {},
ramPrice: json.containsKey("ram") && json["ram"] != null ? json["ram"] : null,
);
w.mapFromJSON(json);
return w;
}
@override
Map<String, dynamic> serialize() {
var obj = {
"cpus": cpusPrice,
"gpus": gpusPrice,
"ram": ramPrice,
};
obj.addAll(toJSON());
return obj;
}
}
class ComputeNode extends SerializerDeserializer<ComputeNode> {
String? name;
int? quantity;
Map<String,dynamic> cpus = {};
Map<String,dynamic> gpus = {};
RAM? ram;
ComputeNode({
this.cpus = const {},
this.gpus = const {},
this.ram,
this.name,
this.quantity,
});
@override
ComputeNode deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return ComputeNode(); }
return ComputeNode(
name: json.containsKey("name") && json["name"] != null ? json["name"] : null,
quantity: json.containsKey("quantity") && json["quantity"] != null ? json["quantity"] : null,
cpus: json.containsKey("cpus") && json["cpus"] != null ? fromMapJson(json["cpus"], CPU()) : {},
gpus: json.containsKey("gpus") && json["gpus"] != null ? fromMapJson(json["gpus"], GPU()) : {},
ram: json.containsKey("ram") && json["ram"] != null ? RAM().deserialize(json["ram"]) : null,
);
}
@override
Map<String, dynamic> serialize() {
return {
"name": name,
"quantity": quantity,
"cpus": cpus,
"gpus": gpus,
"ram": ram!.serialize(),
};
}
}

View File

@@ -0,0 +1,166 @@
import 'package:flutter/material.dart';
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/resources/resources.dart';
class DataItem extends AbstractItem<DataPricing, DataPartnership, DataInstance, DataItem> {
// special attributes
String? type;
String? source;
String? quality;
bool openData = false;
bool static = false;
bool personalData = false;
bool anonymizedPersonalData = false;
double? size;
String? example;
DateTime? updatePeriod;
DataItem({
this.type,
this.source,
this.quality,
this.openData = false,
this.static = false,
this.personalData = false,
this.anonymizedPersonalData = false,
this.size,
this.example,
this.updatePeriod,
}): super();
@override String get topic => "data";
@override deserialize(dynamic data) {
try { data = data as Map<String, dynamic>;
} catch (e) { return DataItem(); }
var w = DataItem(
type: data.containsKey("type") && data["type"] != null ? data["type"] : null,
source: data.containsKey("source") && data["source"] != null ? data["source"] : null,
quality: data.containsKey("quality") && data["quality"] != null ? data["quality"] : null,
openData: data.containsKey("open_data") && data["open_data"] != null ? data["open_data"] : false,
static: data.containsKey("static") && data["static"] != null ? data["static"] : false,
personalData: data.containsKey("personal_data") && data["l"] != null ? data["personal_data"] : false,
anonymizedPersonalData: data.containsKey("anonymized_personal_data") && data["anonymized_personal_data"] != null ? data["anonymized_personal_data"] : false,
size: data.containsKey("size") && data["size"] != null ? data["size"] : null,
example: data.containsKey("example") && data["example"] != null ? data["example"] : null,
updatePeriod: data.containsKey("update_period") && data["update_period"] != null ? DateTime.parse(data["update_period"]) : null,
);
w.mapFromJSON(data, DataInstance());
if (w.logo != null) { // get image dimensions
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> infos() {
return {
"type": type,
"quality": quality,
"open_data": openData,
"static": static,
"personal_data": personalData,
"anonymized_personal_data": anonymizedPersonalData,
"size": size,
"example": example,
"update_period": updatePeriod?.toIso8601String(),
};
}
@override Map<String, dynamic> serialize() {
var obj = infos();
obj.addAll(toJSON());
return obj;
}
}
class DataInstance extends AbstractInstance<DataPricing, DataPartnership> {
String? source;
DataInstance(
{this.source}
): super();
@override
Map<String, dynamic> infos() {
return {
"inputs": toListJson(inputs),
"outputs": toListJson(outputs),
};
}
@override
DataInstance deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return DataInstance(); }
var w = DataInstance(
source: json.containsKey("source") && json["source"] != null ? json["source"] : null,
);
w.mapFromJSON(json, DataPartnership());
return w;
}
@override
Map<String, dynamic> serialize() {
var obj = toJSON();
obj["source"] = source;
obj.addAll(infos());
return obj;
}
}
class DataPartnership extends AbstractPartnerShip<DataPricing> {
double? maxDownloadableGBAllowed;
bool personalDataAllowed = false;
bool anonymizedPersonalDataAllowed = false;
DataPartnership({
this.maxDownloadableGBAllowed,
this.personalDataAllowed = false,
this.anonymizedPersonalDataAllowed = false,
}): super();
@override
DataPartnership deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return DataPartnership(); }
var w = DataPartnership(
maxDownloadableGBAllowed: json.containsKey("max_downloadable_gb_allowed") && json["max_downloadable_gb_allowed"] != null ? json["max_downloadable_gb_allowed"] : null,
personalDataAllowed: json.containsKey("personal_data_allowed") && json["personal_data_allowed"] != null ? json["personal_data_allowed"] : false,
anonymizedPersonalDataAllowed: json.containsKey("anonymized_personal_data_allowed") && json["anonymized_personal_data_allowed"] != null ? json["anonymized_personal_data_allowed"] : false,
);
w.mapFromJSON(json, DataPricing());
return w;
}
@override
Map<String, dynamic> serialize() {
Map<String, dynamic> obj = {
"max_downloadable_gb_allowed": maxDownloadableGBAllowed,
"personal_data_allowed": personalDataAllowed,
"anonymized_personal_data_allowed": anonymizedPersonalDataAllowed,
};
obj.addAll(toJSON());
return obj;
}
}
class DataPricing extends AbstractPricing {
@override DataPricing deserialize(json) {
var w = DataPricing();
w.mapFromJSON(json);
return w;
}
@override
Map<String, dynamic> serialize() {
return toJSON();
}
}

View File

@@ -0,0 +1,357 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/services/enum_service.dart';
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/resources/resources.dart';
class ProcessingItem extends AbstractItem<ProcessingPricing, ProcessingPartnership, ProcessingInstance, ProcessingItem> {
// special attributes
int? infrastructureEnum;
bool isService = false;
bool openSource = false;
String? license;
String? maturity;
ProcessingUsage? usage;
ProcessingItem({
this.infrastructureEnum,
this.isService = false,
this.openSource = false,
this.license,
this.maturity,
this.usage,
}): super();
@override String get topic => "processing";
@override deserialize(dynamic data) {
try { data = data as Map<String, dynamic>;
} catch (e) { return ProcessingItem(); }
var w = ProcessingItem(
infrastructureEnum: data.containsKey("infrastructure") ? EnumService.get("infrastructure", data["infrastructure"]) : null,
isService: data.containsKey("is_service") && data["is_service"] != null ? data["is_service"] : false,
openSource: data.containsKey("open_source") && data["open_source"] != null ? data["open_source"] : false,
license: data.containsKey("license") && data["license"] != null ? data["license"] : null,
maturity: data.containsKey("maturity") && data["maturity"] != null ? data["maturity"] : null,
usage: data.containsKey("usage") && data["usage"] != null ? ProcessingUsage().deserialize(data["usage"]) : null,
);
w.mapFromJSON(data, ProcessingInstance());
if (w.logo != null) { // get image dimensions
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> infos() {
return {
"infrastructure": EnumService.enums["infrastructure"] != null
&& EnumService.enums["infrastructure"]!["$infrastructureEnum"] != null ?
EnumService.enums["infrastructure"]!["$infrastructureEnum"] : infrastructureEnum,
"is_service": isService,
"open_source": openSource,
"license": license,
"maturity": maturity,
};
}
@override Map<String, dynamic> serialize() {
var obj = infos();
obj["infrastructure"] = infrastructureEnum;
obj["usage"] = usage?.serialize();
obj.addAll(toJSON());
return obj;
}
}
class ProcessingAccess extends SerializerDeserializer<ProcessingAccess> {
Containered? container;
ProcessingAccess({
this.container,
});
@override ProcessingAccess deserialize(dynamic json) {
try {
json = json as Map<String, dynamic>;
} catch (e) {
return ProcessingAccess();
}
return ProcessingAccess(
container: json.containsKey("container") && json["container"] != null ? Containered().deserialize(json["container"]) : null,
);
}
@override Map<String, dynamic> serialize() {
return {
"container": container?.serialize(),
};
}
}
class ProcessingInstance extends AbstractInstance<ProcessingPricing, ProcessingPartnership> {
ProcessingAccess? access;
ProcessingInstance(
{this.access}
): super();
@override
ProcessingInstance deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return ProcessingInstance(); }
var w = ProcessingInstance();
w.access = json.containsKey("access") && json["access"] != null ? ProcessingAccess().deserialize(json['access']) : null;
w.mapFromJSON(json, ProcessingPartnership());
return w;
}
@override
Map<String, dynamic> serialize() {
var obj = toJSON();
obj["access"] = access?.serialize();
return obj;
}
@override
Map<String, dynamic> infos() {
return {
"inputs": toListJson(inputs),
"outputs": toListJson(outputs),
};
}
}
class ProcessingPartnership extends AbstractPartnerShip<ProcessingPricing> {
ProcessingPartnership(): super();
@override
ProcessingPartnership deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return ProcessingPartnership(); }
var w = ProcessingPartnership();
w.mapFromJSON(json, ProcessingPricing());
return w;
}
@override
Map<String, dynamic> serialize() {
return toJSON();
}
}
class ProcessingPricing extends AbstractPricing {
@override ProcessingPricing deserialize(json) {
var w = ProcessingPricing();
w.mapFromJSON(json);
return w;
}
@override
Map<String, dynamic> serialize() {
return toJSON();
}
}
class ProcessingUsage extends SerializerDeserializer<ProcessingUsage> {
Map<String,CPU> cpus = {};
Map<String,GPU> gpus = {};
RAM? ram;
double? storageSize;
String? hypothesis;
String? scalingModel;
ProcessingUsage({
this.cpus = const {},
this.gpus = const {},
this.ram,
this.storageSize,
this.hypothesis,
this.scalingModel,
});
@override
ProcessingUsage deserialize(json) {
return ProcessingUsage(
cpus: json.containsKey("cpus") && json["cpus"] != null ? fromMapJson(json["cpus"], CPU()) : {},
gpus: json.containsKey("gpus") && json["gpus"] != null ? fromMapJson(json["gpus"], GPU()) : {},
ram: json.containsKey("ram") && json["ram"] != null ? RAM().deserialize(json["ram"]) : null,
storageSize: json.containsKey("storage_size") && json["storage_size"] != null ? json["storage_size"]?.toDouble() : null,
hypothesis: json.containsKey("hypothesis") && json["hypothesis"] != null ? json["hypothesis"] : null,
scalingModel: json.containsKey("scaling_model") && json["scaling_model"] != null ? json["scaling_model"] : null,
);
}
@override
Map<String, dynamic> serialize() {
return {
"cpus": toMapJson(cpus),
"gpus": toMapJson(gpus),
"ram": ram?.serialize(),
"storage_size": storageSize,
"hypothesis": hypothesis,
"scaling_model": scalingModel,
};
}
}
class CPU extends SerializerDeserializer<CPU> {
CPU({
this.cores,
this.platform,
this.architecture,
this.minimumMemory,
this.shared = false,
});
double? cores;
String? platform;
bool shared = false;
String? architecture;
double? minimumMemory;
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return CPU(); }
return CPU(
cores: json.containsKey("cores") && json["cores"] != null ? json["cores"]?.toDouble() : null,
platform: json.containsKey("platform") && json["platform"] != null ? json["platform"] : null,
architecture: json.containsKey("architecture") && json["architecture"] != null ? json["architecture"] : null,
minimumMemory: json.containsKey("minimumMemory") && json["minimumMemory"] != null ? json["minimumMemory"]?.toDouble() : null,
shared: json.containsKey("shared") && json["shared"] != null ? json["shared"] : false,
);
}
@override Map<String, dynamic> serialize() => {
"cores": cores,
"platform": platform,
"architecture": architecture,
"minimumMemory": minimumMemory,
"shared": shared,
};
}
class GPU extends SerializerDeserializer<GPU> {
GPU({
this.cudaCores,
this.memory,
this.model,
this.tensorCores,
});
double? cudaCores;
double? memory;
String? model;
double? tensorCores;
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return GPU(); }
return GPU(
cudaCores: json.containsKey("cuda_cores") && json["cuda_cores"] != null ? json["cuda_cores"]?.toDouble() : null,
memory: json.containsKey("memory") && json["memory"] != null ? json["memory"]?.toDouble() : null,
model: json.containsKey("model") && json["model"] != null ? json["model"] : null,
tensorCores: json.containsKey("tensor_cores") && json["tensor_cores"] != null ? json["tensor_cores"]?.toDouble() : null,
);
}
@override Map<String, dynamic> serialize() => {
"cuda_cores": cudaCores,
"memory": memory,
"model": model,
"tensor_cores": tensorCores,
};
}
class RAM extends SerializerDeserializer<RAM> {
RAM({
this.ecc = false,
this.size,
});
bool ecc = false;
double? size;
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return RAM(); }
return RAM(
ecc: json.containsKey("ecc") && json["ecc"] != null ? json["ecc"] : false,
size: json.containsKey("size") && json["size"] != null ? json["size"]?.toDouble() : null,
);
}
@override Map<String, dynamic> serialize() => {
"ecc": ecc,
"size": size,
};
}
class Expose extends SerializerDeserializer<Expose> {
Expose({
this.PAT,
this.port,
this.path,
});
int? port;
int? PAT;
String? path;
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Expose(); }
return Expose(
port: json.containsKey("port") && json["port"] != null ? json["port"] : null,
PAT: json.containsKey("PAT") && json["PAT"] != null ? json["PAT"] : null,
path: json.containsKey("reverse") && json["reverse"] != null ? json["reverse"] : null,
);
}
@override Map<String, dynamic> serialize() => {
"port": port,
"PAT": PAT,
"reverse": path,
};
}
class Containered extends SerializerDeserializer<Containered> {
Containered({
this.image,
this.args,
this.command,
this.env,
this.volumes,
this.exposes = const [],
});
String? args;
String? image;
String? command;
Map<String, dynamic>? env;
Map<String, dynamic>? volumes;
List<Expose> exposes = [];
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Containered(); }
return Containered(
args: json.containsKey("args") && json["args"] != null ? json["args"] : null,
image: json.containsKey("image") && json["image"] != null ? json["image"] : null,
command: json.containsKey("command") && json["command"] != null ? json["command"] : null,
env: json.containsKey("env") && json["env"] != null ? json["env"] : null,
volumes: json.containsKey("volumes") && json["volumes"] != null ? json["volumes"] : null,
exposes: json.containsKey("exposes") && json["exposes"] != null ? fromListJson(json["exposes"], Expose()) : [],
);
}
@override Map<String, dynamic> serialize() {
var w = {
"args": args,
"image": image,
"command": command,
"env": env,
"volumes": volumes,
"exposes": toListJson(exposes),
};
return w;
}
}

View File

@@ -0,0 +1,529 @@
import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/resources/compute.dart';
import 'package:oc_front/models/resources/data.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/resources/storage.dart';
import 'package:oc_front/models/resources/workflow.dart';
class Resource implements SerializerDeserializer<Resource> {
List<DataItem> datas = [];
List<ProcessingItem> processings = [];
List<StorageItem> storages = [];
List<ComputeItem> computes = [];
List<WorkflowItem> workflows = [];
Resource({
this.datas = const [],
this.processings = const [],
this.storages = const [],
this.computes = const [],
this.workflows = const [],
});
@override Resource deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Resource(); }
return Resource(
computes: json.containsKey("compute_resource") ? fromListJson(json["compute_resource"], ComputeItem()) : [],
datas: json.containsKey("data_resource") ? fromListJson(json["data_resource"], DataItem()) : [],
processings: json.containsKey("processing_resource") ? fromListJson(json["processing_resource"], ProcessingItem()) : [],
storages: json.containsKey("storage_resource") ? fromListJson(json["storage_resource"], StorageItem()) : [],
workflows: json.containsKey("workflow_resource") ? fromListJson(json["workflow_resource"], WorkflowItem()) : [],
);
}
@override Map<String, dynamic> serialize() {
return {
"compute_resource": toListJson<ComputeItem>(computes),
"data_resource": toListJson<DataItem>(datas),
"processing_resource": toListJson<ProcessingItem>(processings),
"storage_resource": toListJson<StorageItem>(storages),
"workflow_resource": toListJson<WorkflowItem>(workflows),
};
}
}
class Owner extends SerializerDeserializer<Owner> {
String? name;
String? logo;
Owner({
this.name,
this.logo,
});
@override Owner deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Owner(); }
return Owner(
name: json.containsKey("name") ? json["name"] : null,
logo: json.containsKey("logo") ? json["logo"] : null,
);
}
@override Map<String, dynamic> serialize() {
return {
"name": name,
"logo": logo,
};
}
}
abstract class Infos {
Map<String, dynamic> infos();
}
class Artifact extends SerializerDeserializer<Artifact> {
String? attrPath;
String? attrFrom;
bool readOnly = true;
Artifact({
this.attrPath,
this.attrFrom,
this.readOnly = true,
});
@override Artifact deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Artifact(); }
return Artifact(
attrPath: json.containsKey("attr_path") ? json["attr_path"] : null,
attrFrom: json.containsKey("attr_from") ? json["attr_from"] : null,
readOnly: json.containsKey("readonly") ? json["readonly"] : true,
);
}
@override Map<String, dynamic> serialize() {
return {
"attr_path": attrPath,
"attr_from": attrFrom,
"readonly": readOnly,
};
}
}
class Param extends SerializerDeserializer<Param> {
String? name;
String? attr;
dynamic value;
String? origin;
bool optionnal = false;
bool readOnly = true;
Param({
this.name,
this.attr,
this.value,
this.origin,
this.optionnal = false,
this.readOnly = true,
});
@override Param deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Param(); }
return Param(
name: json.containsKey("name") ? json["name"] : null,
attr: json.containsKey("attr") ? json["attr"] : null,
value: json.containsKey("value") ? json["value"] : null,
origin: json.containsKey("origin") ? json["origin"] : null,
optionnal: json.containsKey("optionnal") ? json["optionnal"] : false,
readOnly: json.containsKey("readonly") ? json["readonly"] : false,
);
}
@override Map<String, dynamic> serialize() {
return {
"name": name,
"attr": attr,
"value": value,
"origin": origin,
"optionnal": optionnal,
"readonly": readOnly,
};
}
}
class Credential extends SerializerDeserializer<Credential> {
String? login;
String? password;
Credential({
this.login,
this.password,
});
@override
Credential deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Credential(); }
return Credential(
login: json.containsKey("login") ? json["login"] : null,
password: json.containsKey("password") ? json["password"] : null,
);
}
@override
Map<String, dynamic> serialize() {
return {
"login": login,
"password": password,
};
}
}
abstract class AbstractItem<X extends AbstractPricing, Y extends AbstractPartnerShip<X>, S extends AbstractInstance<X,Y>, T extends FlowData> extends FlowData implements SerializerDeserializer<T>, Infos {
String? id;
String? name;
String? logo;
String? type;
String? creatorID;
String? updaterID;
DateTime? createdAt;
DateTime? updatedAt;
List<Owner> owners;
String? description;
String? restrictions;
String? shortDescription;
int selectedInstance = 0;
List<AbstractInstance<X,Y>> instances = [];
String get topic => "";
AbstractItem({
this.id,
this.type,
this.name,
this.logo,
this.creatorID,
this.updaterID,
this.createdAt,
this.updatedAt,
this.description,
this.shortDescription,
this.owners = const [],
this.selectedInstance = 0,
});
void addEnv(List<dynamic> infos) {
var inst = getSelectedInstance();
if (inst == null) { return; }
inst.env = [];
for (var info in infos) {
inst.env.add(Param(name: info["name"], attr: info["attr"], value: info["value"],
origin: info["origin"], optionnal: info["optionnal"], readOnly: info["readonly"]));
}
}
AbstractInstance<X,Y>? getSelectedInstance() {
if (selectedInstance == -1) { return instances.isEmpty ? null : instances[0]; }
return instances.isNotEmpty ? instances[selectedInstance] : null;
}
@override String getID() {
return id ?? "";
}
@override String getType() {
return type ?? "";
}
@override String getName() {
return name ?? "";
}
@override
Map<String, dynamic> infos();
double? width;
double? height;
@override
double? getWidth() {
return width;
}
@override
double? getHeight() {
return height;
}
Map<String, dynamic> setVariable(String key, dynamic value, Map<String, dynamic> map) {
map[key] = value;
return map;
}
dynamic getVariable(String key, Map<String, dynamic> map) {
return map[key];
}
Map<String, dynamic> toJSON() {
return {
"id": id,
"type": type ?? topic,
"name": name,
"logo": logo,
"owners": toListJson(owners),
"creator_id": creatorID,
"updater_id": updaterID,
"creation_date": createdAt?.toIso8601String(),
"update_date": updatedAt?.toIso8601String(),
"description": description,
"short_description": shortDescription,
"selected_instance": selectedInstance,
"instances": instances.map((e) => e.serialize()).toList(),
};
}
void mapFromJSON(dynamic json, S ex) {
try { json = json as Map<String, dynamic>;
} catch (e) { return; }
this.id = json.containsKey("id") ? json["id"] : null;
this.type = json.containsKey("type") ? json["type"] : topic;
this.name = json.containsKey("name") ? json["name"] : null;
this.logo = json.containsKey("logo") ? json["logo"] : null;
this.creatorID = json.containsKey("creator_id") ? json["creator_id"] : null;
this.updaterID = json.containsKey("updater_id") ? json["updater_id"] : null;
this.description = json.containsKey("description") ? json["description"] : null;
this.owners = json.containsKey("owners") ? fromListJson(json["owners"], Owner()) : [];
this.instances = json.containsKey("instances") ? fromListJson(json["instances"], ex) : [];
this.updatedAt = json.containsKey("update_date") ? DateTime.parse(json["update_date"]) : null;
this.selectedInstance = json.containsKey("selected_instance") ? json["selected_instance"] : 0;
this.shortDescription = json.containsKey("short_description") ? json["short_description"] : null;
this.createdAt = json.containsKey("creation_date") ? DateTime.parse(json["creation_date"]) : null;
}
}
class Location extends SerializerDeserializer<Location> {
double? latitude;
double? longitude;
Location({
this.latitude,
this.longitude,
});
@override
Location deserialize(json) {
return Location(
latitude: json.containsKey("latitude") ? json["latitude"] : null,
longitude: json.containsKey("longitude") ? json["longitude"] : null,
);
}
@override
Map<String, dynamic> serialize() {
return {
"latitude": latitude,
"longitude": longitude,
};
}
}
abstract class AbstractInstance<X extends AbstractPricing, S extends AbstractPartnerShip<X>> extends SerializerDeserializer<AbstractInstance<X,S>> implements Infos {
String? id;
String? name;
int? countryCode;
Location? location;
List<S> partnerships = [];
List<Param> env = [];
List<Param> inputs = [];
List<Param> outputs = [];
Credential? credential;
bool isEnv(String key) {
for (var e in env) {
if (e.name?.contains(key) ?? false || key.contains(e.name ?? "none")) { return true; }
}
return false;
}
bool isEnvAttr(String attr, String origin, bool isOrigin) {
for (var e in env) {
if (e.attr == attr && ((isOrigin && e.origin != null) || (!isOrigin && e.origin == origin))) { return true; }
}
return false;
}
@override
Map<String, dynamic> infos();
void mapFromJSON(dynamic json, S ex) {
try { json = json as Map<String, dynamic>;
} catch (e) { return; }
this.countryCode = json.containsKey("country_code") ? json["country_code"] : null;
this.id = json.containsKey("id") ? json["id"] : null;
this.name = json.containsKey("name") ? json["name"] : null;
this.env = json.containsKey("env") ? fromListJson(json["env"], Param()) : [];
this.inputs = json.containsKey("inputs") ? fromListJson(json["inputs"], Param()) : [];
this.outputs = json.containsKey("outputs") ? fromListJson(json["outputs"], Param()) : [];
this.location = json.containsKey("location") ? Location().deserialize(json["location"]) : null;
this.credential = json.containsKey("credential") ? Credential().deserialize(json["credential"]) : null;
this.partnerships = json.containsKey("partnerships") ? fromListJson(json["partnerships"], ex) : [];
}
Map<String, dynamic> toJSON() {
return {
"country_code": countryCode,
"id": id,
"name": name,
"location": location?.serialize(),
"env": toListJson(env),
"inputs": toListJson(inputs),
"outputs": toListJson(outputs),
"credential": credential?.serialize(), // TODO CREDENTIAL FORM
"partnerships": partnerships.map((e) => e.serialize()).toList(),
};
}
}
abstract class AbstractPartnerShip<S extends AbstractPricing> extends SerializerDeserializer<AbstractPartnerShip<S>> {
String? namespace;
List<AbstractPricing> pricings = [];
void mapFromJSON(dynamic json, S ex) {
try { json = json as Map<String, dynamic>;
} catch (e) { return; }
this.namespace = json.containsKey("namespace") ? json["namespace"] : null;
this.pricings = json.containsKey("pricings") ? fromListJson(json["pricings"], ex) : [];
}
Map<String, dynamic> toJSON() {
return {
"namespace": namespace,
"pricings": pricings,
};
}
}
abstract class AbstractPricing extends SerializerDeserializer<AbstractPricing> {
PricingStrategy? pricing;
int? refundTypeEnum;
int? refundRatio;
List<dynamic> additionnalRefundTypeEnum = [];
int? privilegeStrategyEnum;
int? garantedDelaySecond;
bool exceeding = false;
int? exceedingRatio;
void mapFromJSON(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return; }
pricing = json.containsKey("pricing") ? PricingStrategy().deserialize(json["pricing"]) : null;
refundTypeEnum = json.containsKey("refund_type") ? json["refund_type"] : null;
refundRatio = json.containsKey("refund_ratio") ? json["refund_ratio"] : null;
additionnalRefundTypeEnum = json.containsKey("additionnal_refund_type") ? json["additionnal_refund_type"] : [];
privilegeStrategyEnum = json.containsKey("privilege_strategy") ? json["privilege_strategy"] : null;
garantedDelaySecond = json.containsKey("garanted_delay") ? json["garanted_delay"] : null;
exceeding = json.containsKey("exceeding") ? json["exceeding"] : false;
exceedingRatio = json.containsKey("exceeding_ratio") ? json["exceeding_ratio"] : null;
}
Map<String, dynamic> toJSON() {
return {
"pricing": pricing?.serialize(),
"refund_type": refundTypeEnum,
"refund_ratio": refundRatio,
"additionnal_refund_type": additionnalRefundTypeEnum,
"privilege_strategy": privilegeStrategyEnum,
"garanted_delay": garantedDelaySecond,
"exceeding": exceeding,
"exceeding_ratio": exceedingRatio,
};
}
}
class PricingStrategy extends SerializerDeserializer<PricingStrategy> {
double? price;
String? currency;
int? buyingStrategyEnum;
int? timeStrategyEnum;
int? overrideStrategyEnum;
PricingStrategy({
this.price,
this.currency,
this.buyingStrategyEnum,
this.timeStrategyEnum,
this.overrideStrategyEnum,
});
@override
PricingStrategy deserialize(dynamic json) {
return PricingStrategy(
price: json.containsKey("price") && json["price"] != null ? json["price"] : null,
currency: json.containsKey("currency") && json["currency"] != null ? json["currency"] : null,
buyingStrategyEnum: json.containsKey("buying_strategy") ? json["buying_strategy"] : null,
timeStrategyEnum: json.containsKey("time_strategy") ? json["time_strategy"] : null,
overrideStrategyEnum: json.containsKey("override_strategy") ? json["override_strategy"] : null,
);
}
@override
Map<String, dynamic> serialize() {
return {
"price": price,
"currency": currency,
"buying_strategy": buyingStrategyEnum,
"time_strategy": timeStrategyEnum,
"override_strategy": overrideStrategyEnum,
};
}
}
class Model extends SerializerDeserializer<Model> {
dynamic value;
String? type;
bool readonly = false;
Model({
this.value,
this.type,
this.readonly = false,
});
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Model(); }
return Model(
value: json.containsKey("value") ? json["value"] : null,
type: json.containsKey("type") ? json["type"] : null,
readonly: json.containsKey("readonly") ? json["readonly"] : false,
);
}
@override Map<String, dynamic> serialize() => {
"value": value,
"type": type,
"readonly": readonly,
};
}
Type? getTopicType(String topic) {
if (topic == "processing") { return ProcessingItem; }
else if (topic == "data") { return DataItem; }
else if (topic == "compute") { return ComputeItem; }
else if (topic == "storage") { return StorageItem; }
else if (topic == "workflow") { return WorkflowItem; }
else { return null; }
}
String getTopic(Type type) {
if (type == AbstractItem) { return "resource"; }
if (type == ProcessingItem) { return "processing"; }
if (type == DataItem) { return "data"; }
if (type == ComputeItem) { return "compute"; }
if (type == StorageItem) { return "storage"; }
if (type == WorkflowItem) { return "workflow"; }
return "";
}
bool isComputing(String topic) => topic == "processing";
bool isData(String topic) => topic == "data";
bool isCompute(String topic) => topic == "compute";
bool isStorage(String topic) => topic == "storage";
bool isWorkflow(String topic) => topic == "workflow";
Color getColor(String topic) => isData(topic) ? Colors.blue : isComputing(topic) ? Colors.green :
isCompute(topic) ? Colors.orange : isStorage(topic) ? redColor : Colors.grey;

View File

@@ -0,0 +1,166 @@
import 'package:flutter/material.dart';
import 'package:oc_front/core/services/enum_service.dart';
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/resources/resources.dart';
class StorageItem extends AbstractItem<StoragePricing, StoragePartnership, StorageInstance, StorageItem> {
StorageItem({
this.acronym,
this.typeEnum,
}): super();
@override String get topic => "storage";
// special attributes
String? acronym;
int? typeEnum;
@override deserialize(dynamic data) {
try { data = data as Map<String, dynamic>;
} catch (e) { return StorageItem(); }
var w = StorageItem(
acronym: data.containsKey("acronym") && data["acronym"] != null ? data["acronym"] : null,
typeEnum: data.containsKey("storage_type") && data["storage_type"] != null ? EnumService.get("storage/type", data["storage_type"]) : null,
);
w.mapFromJSON(data, StorageInstance());
if (w.logo != null) { // get image dimensions
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> infos() {
return {
"acronym": acronym,
"storage_type": EnumService.enums["storage/type"] != null
&& EnumService.enums["storage/type"]!["$typeEnum"] != null ?
EnumService.enums["storage/type"]!["$typeEnum"] : typeEnum,
};
}
@override Map<String, dynamic> serialize() {
var obj = infos();
obj.addAll(toJSON());
return obj;
}
}
class StorageInstance extends AbstractInstance<StoragePricing, StoragePartnership> {
String? source;
bool local = false;
String? securityLevel;
int? storageSizeEnum;
int? size;
bool encryption = false;
String? redundancy;
String? throughput;
StorageInstance({
this.source,
this.local = false,
this.securityLevel,
this.storageSizeEnum,
this.size,
this.encryption = false,
this.redundancy,
this.throughput,
}): super();
@override
StorageInstance deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return StorageInstance(); }
var w = StorageInstance(
source: json.containsKey("source") && json["source"] != null ? json["source"] : null,
local: json.containsKey("local") && json["local"] != null ? json["local"] : false,
securityLevel: json.containsKey("security_level") && json["security_level"] != null ? json["security_level"] : null,
storageSizeEnum: json.containsKey("size_type") ? EnumService.get("storage/size", json["size_type"]) : null,
size: json.containsKey("size") && json["size"] != null ? json["size"] : null,
encryption: json.containsKey("encryption") && json["encryption"] != null ? json["encryption"] : false,
redundancy: json.containsKey("redundancy") && json["redundancy"] != null ? json["redundancy"] : null,
throughput: json.containsKey("throughput") && json["throughput"] != null ? json["throughput"] : null,
);
w.mapFromJSON(json, StoragePartnership());
return w;
}
@override
Map<String, dynamic> infos() {
return {
"local": local,
"security_level": securityLevel,
"size_type": EnumService.enums["storage/size"] != null
&& EnumService.enums["storage/size"]!["$storageSizeEnum"] != null ?
EnumService.enums["storage/size"]!["$storageSizeEnum"] : storageSizeEnum,
"size": size,
"encryption": encryption,
"redundancy": redundancy,
"throughput": throughput,
"inputs": toListJson(inputs),
"outputs": toListJson(outputs),
};
}
@override
Map<String, dynamic> serialize() {
var obj = infos();
obj["source"] = source;
obj["size_type"] = storageSizeEnum;
obj.addAll(toJSON());
return obj;
}
}
class StoragePartnership extends AbstractPartnerShip<StoragePricing> {
double? maxSizeGBAllowed;
bool onlyEncryptedAllowed = false;
StoragePartnership({
this.maxSizeGBAllowed,
this.onlyEncryptedAllowed = false,
}): super();
@override
StoragePartnership deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return StoragePartnership(); }
var w = StoragePartnership(
maxSizeGBAllowed: json.containsKey("allowed_gb") && json["allowed_gb"] != null ? json["allowed_gb"] : null,
onlyEncryptedAllowed: json.containsKey("personal_data_allowed") && json["personal_data_allowed"] != null ? json["personal_data_allowed"] : false,
);
w.mapFromJSON(json, StoragePricing());
return w;
}
@override
Map<String, dynamic> serialize() {
Map<String, dynamic> obj = {
"allowed_gb": maxSizeGBAllowed,
"personal_data_allowed": onlyEncryptedAllowed,
};
obj.addAll(toJSON());
return obj;
}
}
class StoragePricing extends AbstractPricing {
@override StoragePricing deserialize(json) {
var w = StoragePricing();
w.mapFromJSON(json);
return w;
}
@override
Map<String, dynamic> serialize() {
return toJSON();
}
}

View File

@@ -0,0 +1,101 @@
import 'package:flutter/material.dart';
import 'package:oc_front/models/resources/resources.dart';
class WorkflowItem extends AbstractItem<WorkflowPricing, WorkflowPartnership, WorkflowInstance, WorkflowItem> {
// special attributes
String? workflowID;
WorkflowItem({
this.workflowID,
}): super();
@override String get topic => "workflow";
@override deserialize(dynamic data) {
try { data = data as Map<String, dynamic>;
} catch (e) { return WorkflowItem(); }
var w = WorkflowItem(
workflowID: data.containsKey("workflow_id") && data["workflow_id"] != null ? data["workflow_id"] : null,
);
w.mapFromJSON(data, WorkflowInstance());
if (w.logo != null) { // get image dimensions
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> infos() {
return {};
}
@override Map<String, dynamic> serialize() {
Map<String, dynamic> obj ={
"workflow_id": workflowID,
};
obj.addAll(toJSON());
return obj;
}
}
class WorkflowInstance extends AbstractInstance<WorkflowPricing, WorkflowPartnership> {
WorkflowInstance(): super();
@override
WorkflowInstance deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return WorkflowInstance(); }
var w = WorkflowInstance();
w.mapFromJSON(json, WorkflowPartnership());
return w;
}
@override
Map<String, dynamic> infos() {
return {};
}
@override
Map<String, dynamic> serialize() {
return toJSON();
}
}
class WorkflowPartnership extends AbstractPartnerShip<WorkflowPricing> {
WorkflowPartnership(): super();
@override
WorkflowPartnership deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return WorkflowPartnership(); }
var w = WorkflowPartnership();
w.mapFromJSON(json, WorkflowPricing());
return w;
}
@override
Map<String, dynamic> serialize() {
return toJSON();
}
}
class WorkflowPricing extends AbstractPricing {
@override WorkflowPricing deserialize(json) {
var w = WorkflowPricing();
w.mapFromJSON(json);
return w;
}
@override
Map<String, dynamic> serialize() {
return toJSON();
}
}

View File

@@ -1,5 +1,13 @@
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/logs.dart';
import 'package:oc_front/models/resources/compute.dart';
import 'package:oc_front/models/resources/data.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/models/resources/storage.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/models/shared.dart';
import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/models/workspace.dart';
Map<Type, SerializerDeserializer> refs = <Type, SerializerDeserializer> {
@@ -7,17 +15,27 @@ Map<Type, SerializerDeserializer> refs = <Type, SerializerDeserializer> {
Search: Search(),
Workspace: Workspace(),
DataItem: DataItem(),
DataCenterItem: DataCenterItem(),
ComputeItem: ComputeItem(),
StorageItem: StorageItem(),
ComputingItem: ComputingItem(),
ProcessingItem: ProcessingItem(),
Workflow: Workflow(),
Resource: Resource(),
WorkflowExecutions: WorkflowExecutions(),
LogsResult: LogsResult(),
Check: Check(),
CollaborativeArea: CollaborativeArea(),
SimpleData: SimpleData(),
EnumData: EnumData(),
};
class APIResponse<T extends SerializerDeserializer> {
APIResponse({
this.data,
this.code = 200,
this.error = "",
this.offset = 0,
});
int code = 200;
int offset = 0;
T? data ;
String? error = "";
@@ -28,18 +46,69 @@ class APIResponse<T extends SerializerDeserializer> {
}
APIResponse<T> deserialize(dynamic j) {
dynamic data;
try {
if (j["data"] == null) { data = j; }
else {
data = j["data"];
}
} catch (e) { data = j; }
try {
return APIResponse<T>(
data: refs[T]!.deserialize(j),
data: refs[T]!.deserialize(data),
code: j.containsKey("code") && j["code"] != null ? j["code"] : 200,
error: j.containsKey("error") && j["error"] != null ? j["error"] : "",
);
} catch (e) { return APIResponse<T>( data: refs[T]!.deserialize(j), ); }
} catch (e) {
return APIResponse<T>( data: refs[T]?.deserialize(data), );
}
}
}
class RawData extends SerializerDeserializer<RawData> {
RawData({ this.values = const []});
dynamic values;
@override deserialize(dynamic json) { return RawData(values: json); }
class SimpleData extends SerializerDeserializer<SimpleData> {
SimpleData({ this.value });
dynamic value;
@override deserialize(dynamic json) {
return SimpleData(value: json);
}
@override Map<String, dynamic> serialize() => { };
}
class EnumData extends SerializerDeserializer<EnumData> {
EnumData({ this.value = const {} });
Map<String, dynamic> value = {};
@override deserialize(dynamic json) {
return EnumData(value: json as Map<String, dynamic>);
}
@override Map<String, dynamic> serialize() => { };
}
class RawData extends SerializerDeserializer<RawData> {
RawData({ this.values = const []});
List<dynamic> values = [];
@override deserialize(dynamic json) {
try {
return RawData(values: json ?? []);
} catch (e) {
print(e);
print(json);
return RawData();
}
}
@override Map<String, dynamic> serialize() => { };
}
abstract class ShallowData {
String getID();
String getName();
Map<String, dynamic> serialize();
}
class Shallow {
String id;
String name;
Shallow({
this.id = "",
this.name = "",
});
}

37
lib/models/schedule.dart Normal file
View File

@@ -0,0 +1,37 @@
import 'package:oc_front/models/abstract.dart';
class Scheduler extends SerializerDeserializer<Scheduler> {
DateTime? start;
DateTime? end;
double? duration;
String? cron;
Scheduler({
this.start,
this.end,
this.duration,
this.cron,
});
@override
Scheduler deserialize(json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Scheduler(); }
var w = Scheduler(
start: json.containsKey("start") && json["start"] != null ? DateTime.parse(json["start"]) : null,
end: json.containsKey("end") && json["end"] != null ? DateTime.parse(json["end"]) : null,
duration: json.containsKey("duration_s") && json["duration_s"] != null ? json["duration_s"] : -1,
cron: json.containsKey("cron") && json["cron"] != null ? json["cron"] : null,
);
return w;
}
@override Map<String, dynamic> serialize() {
return {
"start": start?.toIso8601String(),
"end": end?.toIso8601String(),
"duration_s": duration,
"cron": cron,
};
}
}

View File

@@ -1,381 +1,33 @@
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/resources/compute.dart';
import 'package:oc_front/models/resources/data.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/resources/storage.dart';
const List<ComputingItem> _emptyComputing = [];
const List<DataItem> _emptyData = [];
const List<DataCenterItem> _emptyDataCenter = [];
const List<StorageItem> _emptyStorage = [];
class Search extends SerializerDeserializer<Search> {
Search({
this.computing = _emptyComputing,
this.datacenter = _emptyDataCenter,
this.data = _emptyData,
this.storage = _emptyStorage,
this.computing = const [],
this.compute = const [],
this.data = const [],
this.storage = const [],
});
List<ComputingItem> computing;
List<DataCenterItem> datacenter;
List<ProcessingItem> computing;
List<ComputeItem> compute;
List<DataItem> data;
List<StorageItem> storage;
@override deserialize(dynamic json) {
json = json as Map<String, dynamic>;
return Search(
computing: json.containsKey("computing") ? fromListJson(json["computing"], ComputingItem()) : [],
datacenter: json.containsKey("datacenter") ? fromListJson(json["datacenter"], DataCenterItem()) : [],
computing: json.containsKey("processing") ? fromListJson(json["processing"], ProcessingItem()) : [],
compute: json.containsKey("compute") ? fromListJson(json["compute"], ComputeItem()) : [],
data: json.containsKey("data") ? fromListJson(json["data"], DataItem()) : [],
storage: json.containsKey("storage") ? fromListJson(json["storage"], StorageItem()) : [],
);
}
@override Map<String, dynamic> serialize() => {};
@override Map<String, dynamic> serialize() => {
"processing": toListJson<ProcessingItem>(computing),
"compute": toListJson<ComputeItem>(compute),
"data": toListJson<DataItem>(data),
"storage": toListJson<StorageItem>(storage),
};
}
const List<String> _empty = [];
abstract class AbstractItem {
String? id;
String? name;
String? logo;
String? type;
String? owner;
String? description;
String? shortDescription;
String topic = "";
}
Type? getTopicType(String topic) {
if (topic == "computing") { return ComputingItem; }
else if (topic == "data") { return DataItem; }
else if (topic == "datacenter") { return DataCenterItem; }
else if (topic == "storage") { return StorageItem; }
else { return null; }
}
String getTopic(Type type) {
if (type == ComputingItem) { return "computing"; }
if (type == DataItem) { return "data"; }
if (type == DataCenterItem) { return "datacenter"; }
if (type == StorageItem) { return "storage"; }
return "";
}
bool isComputing(String topic) => topic == "computing";
bool isData(String topic) => topic == "data";
bool isDataCenter(String topic) => topic == "datacenter";
bool isStorage(String topic) => topic == "storage";
class ComputingItem extends SerializerDeserializer<ComputingItem> implements AbstractItem {
ComputingItem({
this.id,
this.name,
this.logo,
this.type,
this.owner,
this.price,
this.image,
this.command,
this.licence,
this.description,
this.requirements,
this.ports = _empty,
this.shortDescription,
this.dinputs = _empty,
this.doutputs = _empty,
this.arguments = _empty,
this.environment = _empty,
});
@override String? id;
@override String? name;
@override String? logo;
@override String? type;
@override String? owner;
@override String topic = "computing";
double? price;
String? image;
String? command;
String? licence;
List<dynamic> ports;
List<dynamic> dinputs;
List<dynamic> doutputs;
List<dynamic> arguments;
@override String? description;
@override String? shortDescription;
List<dynamic> environment;
ExecRequirements? requirements;
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return ComputingItem(); }
return ComputingItem(
id: json.containsKey("ID") ? json["ID"] : null,
name: json.containsKey("name") ? json["name"] : null,
logo: json.containsKey("logo") ? json["logo"] : null,
type: json.containsKey("type") ? json["type"] : null,
owner: json.containsKey("owner") ? json["owner"] : null,
price: json.containsKey("price") ? json["price"]?.toDouble() : null,
image: json.containsKey("image") ? json["image"] : null,
command: json.containsKey("command") ? json["command"] : null,
licence: json.containsKey("licence") ? json["licence"] : null,
description: json.containsKey("description") ? json["description"] : null,
ports: json["ports"] ?? [],
shortDescription: json.containsKey("short_description") ? json["short_description"] : null,
dinputs: json["dinputs"] ?? [],
doutputs: json["doutputs"] ?? [],
arguments: json["arguments"] ?? [],
environment: json["environment"] ?? [],
requirements: json.containsKey("requirements") ? ExecRequirements().deserialize(json["execution_requirements"]) : null,
);
}
@override Map<String, dynamic> serialize() => {};
}
class ExecRequirements extends SerializerDeserializer<ExecRequirements> {
ExecRequirements({
this.ram,
this.cpus,
this.gpus,
this.diskIO,
this.scallingModel,
this.parallel = false,
});
double? ram;
double? cpus;
double? gpus;
String? diskIO;
bool parallel = false;
double? scallingModel;
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return ExecRequirements(); }
return ExecRequirements(
ram: json.containsKey("ram") ? json["ram"]?.toDouble() : null,
cpus: json.containsKey("cpus") ? json["cpus"]?.toDouble() : null,
gpus: json.containsKey("gpus") ? json["gpus"]?.toDouble() : null,
diskIO: json.containsKey("disk_io") ? json["disk_io"] : null,
scallingModel: json.containsKey("scaling_model") ? json["scaling_model"]?.toDouble() : null,
parallel: json.containsKey("parallel") ? json["parallel"] : false,
);
}
@override Map<String, dynamic> serialize() => {};
}
class DataItem extends SerializerDeserializer<DataItem> implements AbstractItem {
DataItem({
this.id,
this.name,
this.logo,
this.type,
this.dtype,
this.owner,
this.example,
this.location,
this.description,
this.protocol = _empty,
this.shortDescription,
});
@override String? id;
@override String? name;
@override String? logo;
@override String? type;
@override String topic = "data";
String? dtype;
String? example;
String? location;
@override String? description;
List<dynamic> protocol;
@override String? shortDescription;
@override String? owner;
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return DataItem(); }
return DataItem(
id: json.containsKey("ID") ? json["ID"] : null,
name: json.containsKey("name") ? json["name"] : null,
logo: json.containsKey("logo") ? json["logo"] : null,
type: json.containsKey("type") ? json["type"] : null,
owner: json.containsKey("owner") ? json["owner"] : null,
dtype: json.containsKey("dtype") ? json["dtype"] : null,
example: json.containsKey("example") ? json["example"] : null,
location: json.containsKey("location") ? json["location"] : null,
description: json.containsKey("description") ? json["description"] : null,
protocol: json["protocol"] ?? [],
shortDescription: json.containsKey("short_description") ? json["short_description"] : null
);
}
@override Map<String, dynamic> serialize() => {};
}
const List<GPU> _emptyGPU = [];
class DataCenterItem extends SerializerDeserializer<DataCenterItem> implements AbstractItem {
DataCenterItem({
this.id,
this.cpu,
this.ram,
this.name,
this.logo,
this.type,
this.owner,
this.acronym,
this.bookingPrice,
this.description,
this.hosts = _empty,
this.gpus = _emptyGPU,
this.shortDescription,
});
CPU? cpu;
RAM? ram;
@override String? id;
@override String? name;
@override String? logo;
@override String? type;
@override String? owner;
@override String topic = "datacenter";
String? acronym;
List<GPU> gpus = [];
@override String? description;
List<dynamic> hosts;
double? bookingPrice;
@override String? shortDescription;
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return DataCenterItem(); }
return DataCenterItem(
id: json.containsKey("ID") ? json["ID"] : null,
ram: json.containsKey("ram") ? RAM().deserialize(json["ram"]) : null,
cpu: json.containsKey("cpu") ? CPU().deserialize(json["cpu"]) : null,
acronym: json.containsKey("acronym") ? json["acronym"] : null,
name: json.containsKey("name") ? json["name"] : null,
logo: json.containsKey("logo") ? json["logo"] : null,
type: json.containsKey("type") ? json["type"] : null,
owner: json.containsKey("owner") ? json["owner"] : null,
bookingPrice: json.containsKey("bookingPrice") ? json["bookingPrice"]?.toDouble() : null,
description: json.containsKey("description") ? json["description"] : null,
hosts: json["hosts"] ?? [],
shortDescription: json.containsKey("short_description") ? json["short_description"] : null,
gpus: json.containsKey("gpus") ? fromListJson(json["gpus"] ?? [], GPU()) : [],
);
}
@override Map<String, dynamic> serialize() => {};
}
class CPU extends SerializerDeserializer<CPU> {
CPU({
this.cores,
this.platform,
this.architecture,
this.minimumMemory,
this.shared = false,
});
double? cores;
String? platform;
bool shared = false;
String? architecture;
double? minimumMemory;
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return CPU(); }
return CPU(
cores: json.containsKey("cores") ? json["cores"]?.toDouble() : null,
platform: json.containsKey("platform") ? json["platform"] : null,
architecture: json.containsKey("architecture") ? json["architecture"] : null,
minimumMemory: json.containsKey("minimumMemory") ? json["minimumMemory"]?.toDouble() : null,
shared: json.containsKey("shared") ? json["shared"] : false,
);
}
@override Map<String, dynamic> serialize() => {};
}
class GPU extends SerializerDeserializer<GPU> {
GPU({
this.cudaCores,
this.memory,
this.model,
this.tensorCores,
});
double? cudaCores;
double? memory;
String? model;
double? tensorCores;
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return GPU(); }
return GPU(
cudaCores: json.containsKey("cuda_cores") ? json["cuda_cores"]?.toDouble() : null,
memory: json.containsKey("memory") ? json["memory"]?.toDouble() : null,
model: json.containsKey("model") ? json["model"] : null,
tensorCores: json.containsKey("tensor_cores") ? json["tensor_cores"]?.toDouble() : null,
);
}
@override Map<String, dynamic> serialize() => {};
}
class RAM extends SerializerDeserializer<RAM> {
RAM({
this.ecc = false,
this.size,
});
bool ecc = false;
double? size;
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return RAM(); }
return RAM(
ecc: json.containsKey("ecc") ? json["ecc"] : false,
size: json.containsKey("size") ? json["size"]?.toDouble() : null,
);
}
@override Map<String, dynamic> serialize() => {};
}
class StorageItem extends SerializerDeserializer<StorageItem> implements AbstractItem {
StorageItem({
this.id,
this.url,
this.size,
this.name,
this.logo,
this.type,
this.owner,
this.acronym,
this.throughput,
this.redundancy,
this.description,
this.bookingPrice,
this.shortDescription,
this.encryption = false,
});
@override String? id;
String? url;
@override String? name;
@override String? logo;
@override String? type;
@override String topic = "storage";
double? size;
@override String? owner;
String? acronym;
String? redundancy;
String? throughput;
@override String? description;
double? bookingPrice;
bool encryption = false;
@override String? shortDescription;
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return StorageItem(); }
return StorageItem(
id: json.containsKey("ID") ? json["ID"] : null,
url: json.containsKey("URL") ? json["URL"] : null,
size: json.containsKey("size") ? json["size"]?.toDouble() : null,
name: json.containsKey("name") ? json["name"] : null,
logo: json.containsKey("logo") ? json["logo"] : null,
type: json.containsKey("type") ? json["type"] : null,
owner: json.containsKey("owner") ? json["owner"] : null,
acronym: json.containsKey("DCacronym") ? json["DCacronym"] : null,
bookingPrice: json.containsKey("bookingPrice") ? json["bookingPrice"]?.toDouble() : null,
description: json.containsKey("description") ? json["description"] : null,
throughput: json.containsKey("throughput") ? json["throughput"] : [],
shortDescription: json.containsKey("short_description") ? json["short_description"] : null,
redundancy: json.containsKey("redundancy") ? json["redundancy"] : [],
encryption: json.containsKey("encryption") ? json["encryption"] : false,
);
}
@override Map<String, dynamic> serialize() => {};
}

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

@@ -0,0 +1,161 @@
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/models/workspace.dart';
/*
based on :
type CollaborativeAreaRule struct {
ShareMode string `json:"share_mode,omitempty" bson:"share_mode,omitempty"` // Share is the share of the rule
CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"` // CreatedAt is the time the rule was created
Creator string `json:"creator,omitempty" bson:"creator,omitempty"` // Creator is the creator of the rule
ExploitedBy string `json:"exploited_by,omitempty" bson:"exploited_by,omitempty"` // ExploitedBy is the exploited by of the rule
}
*/
class CollaborativeAreaRule extends SerializerDeserializer<CollaborativeAreaRule> {
String? shareMode;
DateTime? createdAt;
String? creator;
String? exploitedBy;
CollaborativeAreaRule(
{this.shareMode,
this.createdAt,
this.creator,
this.exploitedBy,});
@override
CollaborativeAreaRule deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return CollaborativeAreaRule(); }
return CollaborativeAreaRule(
shareMode: json.containsKey("share_mode") ? json["share_mode"] : null,
createdAt: json.containsKey("created_at") ? DateTime.parse(json["created_at"]) : null,
creator: json.containsKey("creator") ? json["creator"] : null,
exploitedBy: json.containsKey("exploited_by") ? json["exploited_by"] : null,
);
}
@override
Map<String, dynamic> serialize() => {
"share_mode": shareMode,
"created_at": createdAt?.toIso8601String(),
"creator": creator,
"exploited_by": exploitedBy,
};
}
class CollaborativeArea extends SerializerDeserializer<CollaborativeArea> {
String? id;
String? name;
String? description;
String? creatorID;
String? version;
Map<String, dynamic> attributes = {};
List<Workspace> workspaces = [];
List<Workflow> workflows = [];
List<Peer> peers = [];
List<dynamic> rules = [];
CollaborativeAreaRule? rule;
CollaborativeArea(
{this.id,
this.name,
this.description,
this.creatorID,
this.version,
this.attributes = const {},
this.workspaces = const [],
this.workflows = const [],
this.peers = const [],
this.rule,
this.rules = const []});
@override
deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return CollaborativeArea(); }
return CollaborativeArea(
rule : json.containsKey("collaborative_area") ? CollaborativeAreaRule().deserialize(json["collaborative_area"]) : CollaborativeAreaRule(),
id: json.containsKey("id") ? json["id"] : null,
name: json.containsKey("name") ? json["name"] : null,
creatorID: json.containsKey("creator_id") ? json["creator_id"] : null,
description: json.containsKey("description") ? json["description"] : null,
version: json.containsKey("version") ? json["version"] : null,
attributes: json.containsKey("attributes") ? json["attributes"] : {},
workspaces: json.containsKey("shared_workspaces") && json["shared_workspaces"] != null ? fromListJson(json["shared_workspaces"], Workspace()) : [],
workflows: json.containsKey("shared_workflows") && json["shared_workflows"] != null ? fromListJson(json["shared_workflows"], Workflow()) : [],
peers: json.containsKey("shared_peers") && json["shared_peers"] != null ? fromListJson(json["shared_peers"], Peer()) : [],
rules: json.containsKey("shared_rules") && json["shared_rules"] != null ? json["shared_rules"] : [],
);
}
@override
Map<String, dynamic> serialize() => {
"id": id,
"name": name,
"description": description,
"creator_id": creatorID,
"version": version,
"attributes": attributes,
"rule": rule?.serialize(),
"workspaces": workspaces.map((e) => e.id).toList(),
"workflows": workflows.map((e) => e.id).toList(),
"peers": peers.map((e) => e.id).toList(),
"rules": rules.map((e) => e.id).toList(),
};
}
class Rule extends SerializerDeserializer<Rule> {
String? id;
String? name;
String? description;
Rule(
{this.id,
this.name,
this.description,});
@override
deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Rule(); }
return Rule(
id: json.containsKey("id") ? json["id"] : null,
name: json.containsKey("name") ? json["name"] : null,
description: json.containsKey("description") ? json["description"] : null,
);
}
@override
Map<String, dynamic> serialize() => {
"id": id,
"name": name,
"description": description,
};
}
class Peer extends SerializerDeserializer<Peer> implements ShallowData {
String? id;
String? name;
Peer(
{this.id,
this.name,});
@override String getID() => id ?? "";
@override String getName() => name ?? "";
@override
deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Peer(); }
return Peer(
id: json.containsKey("id") ? json["id"] : null,
name: json.containsKey("name") ? json["name"] : null,
);
}
@override
Map<String, dynamic> serialize() => {
"id": id,
"name": name,
};
}

818
lib/models/workflow.dart Normal file
View File

@@ -0,0 +1,818 @@
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/logs.dart';
import 'package:oc_front/models/resources/compute.dart';
import 'package:oc_front/models/resources/data.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/models/resources/storage.dart';
import 'package:oc_front/models/resources/workflow.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/widgets/forms/sub_keys_forms.dart';
class Check extends SerializerDeserializer<Check> {
bool isAvailable = false;
Check({
this.isAvailable = false,
});
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Check(); }
return Check(
isAvailable: json.containsKey("is_available") ? json["is_available"] : false,
);
}
@override Map<String, dynamic> serialize() {
return {
"is_available": isAvailable,
};
}
}
class WorkflowExecutions extends SerializerDeserializer<WorkflowExecutions> {
List<WorkflowExecution> executions = [];
WorkflowExecutions({
this.executions = const [],
});
@override deserialize(dynamic json) {
try { json = json as List<dynamic>;
} catch (e) { return WorkflowExecutions(); }
return WorkflowExecutions(
executions: fromListJson(json, WorkflowExecution()),
);
}
@override Map<String, dynamic> serialize() {
return {};
}
}
class WorkflowExecution extends SerializerDeserializer<WorkflowExecution> {
String? id;
String? name;
String? startDate;
String? endDate;
int? status;
String? workflowId;
List<Log>? logs;
WorkflowExecution({
this.id,
this.startDate,
this.status,
this.workflowId,
this.name,
this.endDate,
});
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return WorkflowExecution(); }
return WorkflowExecution(
id: json.containsKey("id") ? json["id"] : "",
endDate: json.containsKey("end_date") ? json["end_date"] : "",
startDate: json.containsKey("execution_date") ? json["execution_date"] : "",
status: json.containsKey("state") ? json["state"] : 1,
workflowId: json.containsKey("workflow_id") ? json["workflow_id"] : "",
name: json.containsKey("name") ? json["name"] : "",
);
}
@override Map<String, dynamic> serialize() {
return {
"id": id,
"name": name,
"end_date": endDate,
"execution_data": startDate,
"status": status,
"workflow_id": workflowId,
};
}
}
class Workflow extends SerializerDeserializer<Workflow> implements ShallowData {
String? id;
String? name;
List<dynamic> data;
List<dynamic> compute;
List<dynamic> storage;
List<dynamic> processing;
List<dynamic> workflows;
Graph? graph;
List<dynamic> shared;
Workflow({
this.id,
this.name = "",
this.data = const [],
this.compute = const [],
this.storage = const [],
this.processing = const [],
this.workflows = const [],
this.graph,
this.shared = const [],
});
@override String getID() => id ?? "";
@override String getName() => name ?? "";
String getDescription() => "";
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Workflow(); }
return Workflow(
id: json.containsKey("id") ? json["id"] : "",
name: json.containsKey("name") ? json["name"] : "",
workflows: json.containsKey("workflows") ? json["workflows"] : [],
processing: json.containsKey("processings") ? json["processings"] : [],
compute: json.containsKey("computes") ? json["computes"] : [],
data: json.containsKey("datas") ? json["datas"] : [],
storage: json.containsKey("storages") ? json["storages"] : [],
shared: json.containsKey("shared") ? json["shared"] : [],
graph: json.containsKey("graph") ? Graph().deserialize(json["graph"]) : null,
);
}
@override Map<String, dynamic> serialize() {
var obj = {
"id": id,
"name": name,
"datas": data,
"storages": storage,
"computes" : compute,
"workflows": workflows,
"processings": processing,
};
if (graph != null) {
obj["graph"] = graph!.serialize();
}
return obj;
}
void fromDashboard(Map<String, dynamic> j) {
id = j["id"];
name = j["name"];
if (j.containsKey("graph")) {
graph = Graph();
graph!.fromDashboard(j["graph"]);
}
}
Map<String, dynamic> toDashboard() {
return {
"id": id,
"name": name,
"shared": shared,
"graph": graph?.toDashboard(),
};
}
}
class Scheduler extends SerializerDeserializer<Scheduler> {
String? id;
String? name;
String? cron;
DateTime? start;
DateTime? end;
int? mode;
Scheduler({
this.id,
this.name,
this.cron,
this.start,
this.end,
this.mode,
});
void fromDashboard(Map<String, dynamic> j) {
id = j["id"];
name = j["name"];
cron = j["cron"];
mode =j["mode"];
try {
start = j["start"] != null ? DateTime.parse(j["start"]) : DateTime.now().add( const Duration(minutes: 1)).toUtc();
if (start == DateTime.utc(0)) {
start = DateTime.now().add( const Duration(minutes: 1)).toUtc();
}
if (j.containsKey("end") && j["end"] != null) {
end = DateTime.parse(j["end"]);
}
} catch (e) { /**/ }
}
Map<String, dynamic> toDashboard() {
return {
"id": id,
"name": name,
"cron": cron,
"mode": mode ?? 1,
"start": start?.toIso8601String(),
"end": end?.toIso8601String(),
};
}
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Scheduler(); }
return Scheduler(
id: json.containsKey("id") ? json["id"] : null,
name: json.containsKey("name") ? json["name"] : "",
cron: json.containsKey("cron") ? json["cron"] : "",
mode: json.containsKey("mode") ? json["mode"] : "",
start: json.containsKey("start") && json["start"] != null ? DateTime.parse(json["start"]) : null,
end: json.containsKey("end") && json["end"] != null ? DateTime.parse(json["end"]) : null,
);
}
@override Map<String, dynamic> serialize() {
try {
return {
"id": id,
"name": name,
"cron": cron ?? "",
"mode": mode ?? 1,
"start": start?.toIso8601String(),
"end": end?.toIso8601String(),
};
} catch (e) {
return {
"id": id,
"name": name,
"cron": cron ?? "",
"start": start?.toIso8601String(),
"end": end?.toIso8601String(),
};
}
}
}
class Graph extends SerializerDeserializer<Graph> {
double zoom;
Map<String, GraphItem> items = {};
List<GraphLink> links = [];
Graph({
this.zoom = 1,
this.items = const {},
this.links = const [],
});
Map<String, List<dynamic>> getInfosToUpdate(SubMapFormsType type) {
Map<String, List<dynamic>> infos = {};
for (var item in items.values) {
var inst = item.getElement()?.getSelectedInstance();
if (inst == null) { return infos; }
infos[item.id ?? ""] = (type == SubMapFormsType.INPUT ? inst.inputs : (
type == SubMapFormsType.OUTPUT ? inst.outputs : inst.env)).map( (e) => e.serialize()).toList();
}
return infos;
}
Map<String, List<dynamic>> getEnvToUpdate() {
return getInfosToUpdate(SubMapFormsType.ENV);
}
Map<String, List<dynamic>> getInputToUpdate() {
return getInfosToUpdate(SubMapFormsType.INPUT);
}
Map<String, List<dynamic>> getOutputToUpdate() {
return getInfosToUpdate(SubMapFormsType.OUTPUT);
}
GraphItem getItemByElementID(String id) {
return items[id] ?? GraphItem();
}
Map<String,GraphItem?> getArrowByElementID(String id) {
Map<String,GraphItem?> arr = {};
for (var link in links) {
var nested = false;
var reverse = false;
String? from = link.source?.id;
String? to = link.destination?.id;
bool isInLink = (from?.contains(id) ?? false) || (to?.contains(id) ?? false);
if (isInLink && (["storage", "compute"].contains(getItemByElementID(from ?? "").getElement()?.getType())
|| ["storage", "compute"].contains(getItemByElementID(to ?? "").getElement()?.getType()))) {
nested = true;
reverse = link.source?.id?.contains(id) ?? false;
}
if (nested || isInLink) {
arr[reverse ? (to ?? "") : (from ?? "") ] = getItemByElementID(reverse ? (to ?? "") : (from ?? "") );
}
}
return arr;
}
void fillEnv(AbstractItem? mainItem, GraphItem? item, SubMapFormsType type, Map<String,int> alreadySeen) {
if (mainItem == null || item == null) {
return;
}
AbstractItem? el = item.getElement();
if (el?.getSelectedInstance() == null) {
return;
}
var inst = el!.getSelectedInstance()!;
var inst2 = mainItem.getSelectedInstance()!;
var what = type == SubMapFormsType.INPUT ? inst.inputs : (
type == SubMapFormsType.OUTPUT ? inst.outputs : inst.env);
var what2 = type == SubMapFormsType.INPUT ? inst2.inputs : (
type == SubMapFormsType.OUTPUT ? inst2.outputs : inst2.env);
for (var e in what2) {
if (e.attr != null && e.value == null) {
e.value = inst2.serialize()[e.attr!];
}
}
// should find arrow env info and add it to the env
List<Param> extParams = [];
var arrows = links.where( (e) => (e.source?.id?.contains(item.id ?? "") ?? false) || (e.destination?.id?.contains(item.id ?? "") ?? false));
for (var arrow in arrows) {
for (var info in arrow.infos) {
var i = info as Map<String, dynamic>;
for (var entry in i.entries) {
if (entry.value == null) { continue; }
var varName = "LINK_${el.getName().toUpperCase().replaceAll(" ", "_")}_${entry.key.toUpperCase()}";
/*alreadySeen[varName] = (alreadySeen[varName] ?? -1) + 1;
if ((alreadySeen[varName] ?? 0) > 1) {
varName = "${varName}_${alreadySeen[varName]}";
}*/
if ((entry.value is String) && !isEnvAttr(entry.value, what2)) {
extParams.add(Param( name: varName,
attr: entry.key, value: entry.value, origin: item.id, readOnly: true));
}
}
}
}
for ( var param in what) {
if (param.attr == null) { continue; }
var varName = param.name != null && (param.name!.contains("LINK_")
|| param.name!.contains("DATA_")
|| param.name!.contains("PROCESSING_")
|| param.name!.contains("STORAGE_")
|| param.name!.contains("WORKFLOW_")
|| param.name!.contains("COMPUTE") ) ? param.name : "${el.topic.toUpperCase()}_${el.getName().toUpperCase().replaceAll(" ", "_")}_${param.attr!.toUpperCase()}";
/*alreadySeen[varName] = (alreadySeen[varName] ?? -1) + 1;
if ((alreadySeen[varName] ?? 0) > 0) {
varName = "${varName}_${alreadySeen[varName]}";
}*/
dynamic env;
if (param.value == null) {
var s = el.serialize();
s.addAll(el.getSelectedInstance()?.serialize() ?? {});
env = s[param.attr!];
} else {
env = param.value;
}
// bool isSrc = param.origin == null;
//if (isSrc) { continue; }
if (!isEnvAttr(env, what2)) {
extParams.add(Param( name: varName,
attr: param.attr, value: env, origin: item.id, readOnly: true));
}
}
for (var param in extParams) {
what2.add(param);
}
}
bool isEnvAttr(String value, List<Param> params) {
for (var e in params) {
if (value == e.value) { return true; }
}
return false;
}
void fill() {
Map<String, int> alreadySeen = {};
var its = items.values.toList();
for (var type in [SubMapFormsType.INPUT, SubMapFormsType.OUTPUT, SubMapFormsType.ENV]) {
for (var item in its) {
var envs = type == SubMapFormsType.INPUT ? item.getElement()?.getSelectedInstance()?.inputs : (
type == SubMapFormsType.OUTPUT ? item.getElement()?.getSelectedInstance()?.outputs : item.getElement()?.getSelectedInstance()?.env);
if (envs != null) {
envs.removeWhere( (e) => e.readOnly && e.name != null
&& (e.name!.contains("LINK_")
|| e.name!.contains("DATA_")
|| e.name!.contains("PROCESSING_")
|| e.name!.contains("STORAGE_")
|| e.name!.contains("WORKFLOW_")
|| e.name!.contains("COMPUTE") ));
}
}
for (var item in [...its, ...its.reversed]) {
var itss = getArrowByElementID(item.id ?? "");
for (var i in itss.values) {
fillEnv(item.getElement(), i, type, alreadySeen);
}
}
}
}
void fromDashboard(Map<String, dynamic> j) {
items = {};
for (var el in (j["elements"] as Map<dynamic, dynamic>).values) {
var d = GraphItem();
d.fromDashboard(el as Map<String, dynamic>);
items[d.id ?? ""] = d;
}
links = (j["arrows"] as List<dynamic>).map( (el) {
var d = GraphLink();
d.fromDashboard(el);
return d;
}).toList();
j["zoom"] = zoom;
fill();
}
Map<String, dynamic> toDashboard() {
fill();
List<Map<String, dynamic>> elements = [];
List<Map<String, dynamic>> arrows = [];
for (var el in items.values) {
elements.add(el.toDashboard());
}
for (var l in links) {
arrows.add(l.toDashboard());
}
return {
"zoom": zoom,
"elements": elements,
"arrows": arrows,
};
}
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Graph(); }
var g = Graph(
zoom: json.containsKey("zoom") ? double.parse(json["zoom"].toString()) : 0,
links: json.containsKey("links") ? fromListJson(json["links"], GraphLink()) : [],
items: json.containsKey("items") ? fromMapJson<GraphItem>(json["items"], GraphItem()) : {},
);
g.fill();
return g;
}
@override Map<String, dynamic> serialize() {
fill();
return {
"zoom": zoom,
"items": toMapJson(items),
"links": toListJson(links),
};
}
}
class StorageProcessingGraphLink extends SerializerDeserializer<StorageProcessingGraphLink> {
bool write = false;
String? source;
String? destination;
String? filename;
StorageProcessingGraphLink({
this.write = false,
this.source,
this.destination,
this.filename,
});
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return StorageProcessingGraphLink(); }
return StorageProcessingGraphLink(
write: json.containsKey("write") ? json["write"] : false,
source: json.containsKey("source") ? json["source"] : "",
destination: json.containsKey("destination") ? json["destination"] : "",
filename: json.containsKey("filename") && json["filename"] != null ? json["filename"] : "",
);
}
@override Map<String, dynamic> serialize() {
return {
"write": write,
"source": source,
"destination": destination,
"filename": filename,
};
}
}
class GraphLink extends SerializerDeserializer<GraphLink> {
Position? source;
Position? destination;
GraphLinkStyle? style;
List<StorageProcessingGraphLink> infos = [];
List<Param> env = [];
GraphLink({
this.source,
this.destination,
this.style,
this.infos = const [],
this.env = const [],
});
void fromDashboard(Map<String, dynamic> j) {
source = Position(id: j["from"]["id"], x: j["from"]["x"], y: j["from"]["y"]);
destination = Position(id: j["to"]["id"], x: j["to"]["x"], y: j["to"]["y"]);
style = GraphLinkStyle();
style!.fromDashboard(j["params"]);
infos = fromListJson(j["infos"], StorageProcessingGraphLink());
env = fromListJson(j["env"], Param());
}
Map<String, dynamic> toDashboard() {
return {
"from": {
"id": source?.id ?? "",
"x" : source?.x ?? 0,
"y": source?.y ?? 0,
},
"to": {
"id": destination?.id ?? "",
"x" : destination?.x ?? 0,
"y": destination?.y ?? 0,
},
"params": style?.toDashboard(),
"infos": toListJson(infos),
"env": env.map( (el) => el.serialize()).toList(),
};
}
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return GraphLink(); }
return GraphLink(
source: json.containsKey("source") && json["source"] != null ? Position().deserialize(json["source"]) : null,
destination: json.containsKey("destination") && json["destination"] != null ? Position().deserialize(json["destination"]) : null,
style: json.containsKey("style") && json["style"] != null ? GraphLinkStyle().deserialize(json["style"]) : null,
infos: json.containsKey("storage_link_infos") && json["storage_link_infos"] != null ? fromListJson(json["storage_link_infos"], StorageProcessingGraphLink()) : [],
env: json.containsKey("env") && json["env"] != null ? fromListJson(json["env"], Param()) : [],
);
}
@override Map<String, dynamic> serialize() {
var obj = <String, dynamic>{};
if (source != null) {
obj["source"] = source!.serialize();
}
if (destination != null) {
obj["destination"] = destination!.serialize();
}
if (style != null) {
obj["style"] = style!.serialize();
}
obj["storage_link_infos"] = toListJson(infos);
obj["env"] = toListJson(env);
return obj;
}
}
class GraphLinkStyle extends SerializerDeserializer<GraphLinkStyle> {
Color color = Colors.black;
double stroke = 1;
double tension = 23;
double headRadius = 10;
double dashWidth = 0;
double dashSpace = 0;
double startArrowWidth = 1;
double endArrowWidth = 10;
Position? startArrow;
Position? endArrow;
ArrowStyle arrowStyle = ArrowStyle.curve;
ArrowDirection arrowDirection = ArrowDirection.forward;
GraphLinkStyle({
this.color = Colors.black,
this.stroke = 1,
this.tension = 23,
this.headRadius = 10,
this.dashWidth = 0,
this.dashSpace = 0,
this.startArrowWidth = 10,
this.endArrowWidth = 10,
this.startArrow,
this.endArrow,
this.arrowStyle = ArrowStyle.curve,
this.arrowDirection = ArrowDirection.forward
});
void fromDashboard(Map<String, dynamic> j) {
dashSpace = j["dash_space"];
dashWidth = j["dash_width"];
endArrowWidth = j["backward_arrow_width"];
startArrowWidth = j["forward_arrow_width"];
stroke = j["thickness"];
arrowDirection = ArrowDirection.values.firstWhere((element) => element.index == j["direction"]);
headRadius = j["head_radius"];
color = Color(j["color"]);
arrowStyle = ArrowStyle.values.firstWhere((element) => element.index == j["arrow_style"]);
tension = j["tension"];
startArrow = Position(x: j["start_arrow_position_x"], y: j["start_arrow_position_y"]);
endArrow = Position(x: j["end_arrow_position_x"], y: j["end_arrow_position_y"]);
}
Map<String, dynamic> toDashboard() {
return {
"dash_space": dashSpace,
"dash_width": dashWidth,
"backward_arrow_width": endArrowWidth,
"forward_arrow_width": startArrowWidth,
"thickness": stroke,
"direction": arrowDirection.index,
"head_radius": headRadius,
"tail_length": 25.0,
"color": int.parse(color.toHexString(), radix: 16),
"arrow_style" : arrowStyle.index,
"tension" : tension,
"start_arrow_position_x": startArrow?.x ?? 0,
"start_arrow_position_y": startArrow?.y ?? 0,
"end_arrow_position_x": endArrow?.x ?? 0,
"end_arrow_position_y": endArrow?.y ?? 0,
};
}
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return GraphLinkStyle(); }
return GraphLinkStyle(
color: json.containsKey("color") ? Color(json["color"] as int) : Colors.black,
stroke: json.containsKey("stroke") ? double.parse(json["stroke"].toString()) : 0,
tension: json.containsKey("tension") ? double.parse(json["tension"].toString()) : 0,
headRadius: json.containsKey("head_radius") ? double.parse(json["head_radius"].toString()) : 0,
dashWidth: json.containsKey("dash_width") ? double.parse(json["dash_width"].toString()) : 0,
dashSpace: json.containsKey("dash_space") ? double.parse(json["dash_space"].toString()) : 0,
startArrow: json.containsKey("start_arrow") ? Position().deserialize(json["start_arrow"]) : null,
endArrow: json.containsKey("end_arrow") ? Position().deserialize(json["end_arrow"]) : null,
arrowDirection: json.containsKey("arrow_direction") ? ArrowDirection.values.firstWhere((el) => el.index == json["arrow_direction"]) : ArrowDirection.forward,
arrowStyle: json.containsKey("arrow_style") ? ArrowStyle.values.firstWhere((el) => el.index == json["arrow_style"]) : ArrowStyle.curve,
startArrowWidth: json.containsKey("start_arrow_width") ? double.parse(json["start_arrow_width"].toString()) : 0,
endArrowWidth: json.containsKey("end_arrow_width") ? double.parse(json["end_arrow_width"].toString()) : 0,
);
}
@override Map<String, dynamic> serialize() {
var obj = <String, dynamic> {
"color" : int.parse(color.toHexString(), radix: 16),
"stroke" : stroke,
"tension" : tension,
"head_radius" : headRadius,
"dash_width" : dashWidth,
"dash_space" : dashSpace,
"arrow_direction" : arrowDirection.index,
"arrow_style" : arrowStyle.index,
"start_arrow_width" : startArrowWidth,
"end_arrow_width" : endArrowWidth,
};
if (startArrow != null) {
obj["start_arrow"] = startArrow!.serialize();
}
if (endArrow != null) {
obj["end_arrow"] = endArrow!.serialize();
}
return obj;
}
}
class GraphItem extends SerializerDeserializer<GraphItem> {
String? id;
double? width;
double? height;
Position? position;
DataItem? data;
ProcessingItem? processing;
StorageItem? storage;
ComputeItem? compute;
WorkflowItem? workflow;
GraphItem({
this.id,
this.width,
this.height,
this.position,
this.data,
this.processing,
this.storage,
this.compute,
this.workflow,
});
AbstractItem? getElement() {
if (data != null) { return data!; }
if (processing != null) { return processing!; }
if (storage != null) { return storage!; }
if (compute != null) { return compute!; }
if (workflow != null) { return workflow!; }
return null;
}
void fromDashboard(Map<String, dynamic> j) {
id = j["id"];
position = Position(x: j["x"], y: j["y"]);
width = j["width"];
height = j["height"];
if (j["element"] != null) {
if (j["element"]["type"] == "data") { data = DataItem().deserialize(j["element"]);
} else if (j["element"]["type"] == "processing") { processing = ProcessingItem().deserialize(j["element"]);
} else if (j["element"]["type"] == "compute") { compute = ComputeItem().deserialize(j["element"]);
} else if (j["element"]["type"] == "storage") { storage = StorageItem().deserialize(j["element"]);
} else if (j["element"]["type"] == "workflow") { workflow = WorkflowItem().deserialize(j["element"]);
} else {
compute = null;
data = null;
processing = null;
storage = null;
workflow = null;
}
} else {
compute = null;
data = null;
processing = null;
storage = null;
workflow = null;
}
}
Map<String, dynamic> toDashboard() {
Map<String, dynamic> element = {};
List<AbstractItem?> items = [data, processing, storage, compute, workflow];
for(var el in items) {
if (el != null && el.getID() != "") {
element = el.serialize();
break;
}
}
return {
"id": id,
"x": (position?.x ?? 0),
"y": (position?.y ?? 0),
"width": width ?? 0,
"height": height ?? 0,
"element": element,
};
}
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return GraphItem(); }
return GraphItem(
id: json.containsKey("id") ? json["id"] : null,
width: json.containsKey("width") ? double.parse(json["width"].toString()) : null,
height: json.containsKey("height") ? double.parse(json["height"].toString()) : null,
position: json.containsKey("position") ? Position().deserialize(json["position"]) : null,
data: json.containsKey("data") ? DataItem().deserialize(json["data"]) : null,
processing: json.containsKey("processing") ? ProcessingItem().deserialize(json["processing"]) : null,
storage: json.containsKey("storage") ? StorageItem().deserialize(json["storage"]) : null,
compute: json.containsKey("compute") ? ComputeItem().deserialize(json["compute"]) : null,
workflow: json.containsKey("workflow") ? WorkflowItem().deserialize(json["workflow"]) : null,
);
}
@override Map<String, dynamic> serialize() {
return {
"id": id,
"width": width,
"height": height,
"data": data?.serialize(),
"processing": processing?.serialize(),
"storage": storage?.serialize(),
"compute": compute?.serialize(),
"workflow": workflow?.serialize(),
"position": position?.serialize(),
};
}
}
class Position extends SerializerDeserializer<Position> {
String? id;
double? x;
double? y;
Position({
this.id,
this.x,
this.y,
});
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Position(); }
return Position(
id: json.containsKey("id") ? json["id"] : null,
x: json.containsKey("x") ? double.parse(json["x"].toString()) : null,
y: json.containsKey("y") ? double.parse(json["y"].toString()) : null,
);
}
@override Map<String, dynamic> serialize() => {
"id": id,
"x": x ,
"y": y,
};
}

View File

@@ -1,29 +1,61 @@
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/resources/compute.dart';
import 'package:oc_front/models/resources/data.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/resources/storage.dart';
import 'package:oc_front/models/resources/workflow.dart';
import 'package:oc_front/models/response.dart';
class Workspace extends SerializerDeserializer<Workspace> {
class Workspace extends SerializerDeserializer<Workspace> implements ShallowData {
String? id;
List<dynamic> data;
List<dynamic> datacenter;
List<dynamic> storage;
List<dynamic> computing;
String? name;
bool? active;
List<DataItem> datas;
List<ComputeItem> computes;
List<StorageItem> storages;
List<ProcessingItem> processings;
List<WorkflowItem> workflows;
String? shared;
Workspace({
this.id,
this.computing = const [],
this.data = const [],
this.datacenter = const [],
this.storage = const [],
this.name,
this.active = false,
this.workflows = const [],
this.datas = const [],
this.computes = const [],
this.storages = const [],
this.processings = const [],
this.shared,
});
@override String getID() => id ?? "";
@override String getName() => name ?? "";
@override deserialize(dynamic json) {
try { json = json as Map<String, dynamic>;
} catch (e) { return Workspace(); }
return Workspace(
computing: json.containsKey("computing") ? json["computing"] : [],
datacenter: json.containsKey("datacenter") ? json["datacenter"] : [],
data: json.containsKey("data") ? json["data"] : [],
storage: json.containsKey("storage") ? json["storage"] : [],
id: json.containsKey("id") ? json["id"] : null,
shared: json["shared"],
name: json.containsKey("name") ? json["name"] : null,
active: json.containsKey("active") ? json["active"] : false,
processings: json.containsKey("processing_resources") ? fromListJson(json["processing_resources"], ProcessingItem()) : [],
storages: json.containsKey("storage_resources") ? fromListJson(json["storage_resources"], StorageItem()) : [],
computes: json.containsKey("compute_resources") ? fromListJson(json["compute_resources"], ComputeItem()) : [],
datas: json.containsKey("data_resources") ? fromListJson(json["data_resources"], DataItem()) : [],
workflows: json.containsKey("workflow_resources") ? fromListJson(json["workflow_resources"], WorkflowItem()) : []
);
}
@override Map<String, dynamic> serialize() => {};
@override Map<String, dynamic> serialize() {
return {
"id": id,
"name": name,
"processings": processings.map((e) => e.id).toList(),
"storages": storages.map((e) => e.id).toList(),
"computes": computes.map((e) => e.id).toList(),
"datas": datas.map((e) => e.id).toList(),
"workflows": workflows.map((e) => e.id).toList(),
};
}
}

View File

@@ -1,7 +1,11 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
abstract class AbstractFactory {
GlobalKey getKey();
Widget factory(GoRouterState state, List<String> args);
bool searchFill();
void search(BuildContext context);
void search(BuildContext context, bool special);
String? getSearch();
void back(BuildContext context);
}

View File

@@ -1,41 +1,103 @@
import 'package:oc_front/main.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/widgets/catalog.dart';
import 'package:localstorage/localstorage.dart';
import 'package:oc_front/pages/abstract_page.dart';
import 'package:oc_front/core/sections/header/search.dart';
import 'package:oc_front/core/services/specialized_services/search_service.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/core/services/specialized_services/resource_service.dart';
class CatalogFactory implements AbstractFactory {
static List<AbstractItem> items = [];
@override GlobalKey getKey() { return key; }
static GlobalKey<CatalogPageWidgetState> key = GlobalKey<CatalogPageWidgetState>();
@override bool searchFill() { return CatalogFactory.items.isEmpty; }
@override Widget factory(GoRouterState state, List<String> args) { return CatalogPageWidget(); }
@override void search(BuildContext context) {
CatalogFactory.key.currentState?.widget.search.get(context, SearchConstants.get()!).then((value) {
if (value.data == null) { return; }
CatalogFactory.items = [
...value.data!.computing, ...value.data!.data, ...value.data!.storage, ...value.data!.datacenter,];
searchWidgetKey.currentState?.setState(() {});
@override void back(BuildContext context) {
var s = (localStorage.getItem("search") ?? "");
if (s != "") {
localStorage.setItem("search", s.split(",").sublist(1).join(","));
SearchConstants.set(s.split(",").sublist(1).join(","));
if ((localStorage.getItem("search") ?? "") == "") {
SearchConstants.remove();
key.currentState?.widget.isSearch = true;
key.currentState?.widget.items = [];
HeaderConstants.headerKey.currentState?.setState(() {});
HeaderConstants.headerWidget?.setState(() {});
CatalogFactory.key.currentState?.setState(() {});
} else {
CatalogFactory().search(context, false);
}
} else {
SearchConstants.remove();
key.currentState?.widget.isSearch = true;
key.currentState?.widget.items = [];
HeaderConstants.headerKey.currentState?.setState(() {});
HeaderConstants.headerWidget?.setState(() {});
CatalogFactory.key.currentState?.setState(() {});
}
}
@override bool searchFill() { return (key.currentState?.widget.items.isEmpty ?? true) && (key.currentState?.widget.isSearch ?? true); }
@override Widget factory(GoRouterState state, List<String> args) { return CatalogPageWidget(); }
@override String? getSearch() {
if ((localStorage.getItem("search") ?? "") == "") { return null; }
return localStorage.getItem("search")!.split(",")[0];
}
@override void search(BuildContext context, bool special) {
if (special) { return; } // T
key.currentState?.widget.isSearch = true;
var s = (localStorage.getItem("search") ?? "");
if (s != "") {
if (SearchConstants.get() == null) {
localStorage.setItem("search", s);
} else if (s.split(",")[0] != SearchConstants.get()) {
localStorage.setItem("search", "${SearchConstants.get()!},$s");
}
} else if ((SearchConstants.get() ?? "") == "") { return;
} else { localStorage.setItem("search", SearchConstants.get()!); }
CatalogFactory.key.currentState?.widget.search.search(context, [
localStorage.getItem("search")!.split(",")[0] ], {}).then((value) {
if (value.data == null) {
key.currentState?.widget.items = [];
} else {
key.currentState?.widget.isSearch = false;
key.currentState?.widget.items = [ ...value.data!.workflows,
...value.data!.processings, ...value.data!.datas, ...value.data!.storages, ...value.data!.computes,];
}
HeaderConstants.headerKey.currentState?.setState(() {});
HeaderConstants.headerWidget?.setState(() {});
CatalogFactory.key.currentState?.setState(() {}); // ignore: invalid_use_of_protected_member
});
}
}
// ignore: must_be_immutable
class CatalogPageWidget extends StatefulWidget {
final SearchService search = SearchService();
CatalogPageWidget (): super(key: CatalogFactory.key);
double? itemWidth;
bool isSearch = true;
List<AbstractItem> items = [];
final ResourceService search = ResourceService();
CatalogPageWidget ({
this.itemWidth,
}): super(key: CatalogFactory.key);
@override CatalogPageWidgetState createState() => CatalogPageWidgetState();
}
class CatalogPageWidgetState extends State<CatalogPageWidget> {
@override Widget build(BuildContext context) {
if (widget.items.isEmpty) {
if (widget.isSearch) { return Container(); }
return Container(
width: getMainWidth(context),
height: getMainHeight(context) - 50,
color: Colors.grey.shade300,
child: const Center(child: Text("NO RESOURCES FOUND",
style: TextStyle(fontSize: 30, color: Colors.grey))
),
); }
return Column( children : [
SizedBox(
width: MediaQuery.of(context).size.width,
height: CatalogFactory.items.isEmpty ? 0 : MediaQuery.of(context).size.height - HeaderConstants.height,
child: CatalogWidget(items: CatalogFactory.items) )
SizedBox( width: getMainWidth(context),
height: getMainHeight(context) - 50,
child: SingleChildScrollView( child: CatalogWidget(items: CatalogFactory.key.currentState?.widget.items, itemWidth: widget.itemWidth) )),
]
);
}
}
}

View File

@@ -1,7 +1,11 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:localstorage/localstorage.dart';
import 'package:oc_front/core/models/workspace_local.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/core/services/router.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/pages/abstract_page.dart';
import 'package:oc_front/pages/catalog.dart';
import 'package:oc_front/widgets/items/item.dart';
@@ -9,21 +13,38 @@ import 'package:oc_front/widgets/items/item_row.dart';
class CatalogItemFactory implements AbstractFactory {
static GlobalKey<CatalogItemPageWidgetState> key = GlobalKey<CatalogItemPageWidgetState>();
@override GlobalKey getKey() { return key; }
@override void back(BuildContext context) {
search(context, false);
}
@override String? getSearch() {
if ((localStorage.getItem("search") ?? "") == "") { return null; }
return localStorage.getItem("search")!.split(",")[0];
}
@override bool searchFill() { return false; }
@override Widget factory(GoRouterState state, List<String> args) {
var id = state.pathParameters[args.first];
try {
var item = CatalogFactory.items.firstWhere( (element) => element.id == id );
return CatalogItemPageWidget(item : item);
var item = CatalogFactory.key.currentState?.widget.items.firstWhere( (element) => element.id == id );
return CatalogItemPageWidget(item : item!);
} catch (e) {
var item = WorkspaceLocal.getItem(id ?? "");
var item = WorkspaceLocal.getItem(id ?? "", false);
if (item != null) { return CatalogItemPageWidget(item : item); }
return Container();
}
}
@override void search(BuildContext context) { }
@override void search(BuildContext context, bool special) {
if (special) { return; } // T
var s = SearchConstants.get();
AppRouter.catalog.go(context, {});
Future.delayed(Duration(milliseconds: 10), () {
SearchConstants.set(s);
CatalogFactory().search(context, false);
});
}
}
// ignore: must_be_immutable
class CatalogItemPageWidget extends StatefulWidget {
AbstractItem item;
CatalogItemPageWidget ({ required this.item }) : super(key: CatalogItemFactory.key);
@@ -32,8 +53,8 @@ class CatalogItemPageWidget extends StatefulWidget {
class CatalogItemPageWidgetState extends State<CatalogItemPageWidget> {
@override Widget build(BuildContext context) {
return Column( children: [
ItemRowWidget(contextWidth: MediaQuery.of(context).size.width, item: widget.item, readOnly: true,),
ItemWidget(item: widget.item,),
ItemRowWidget(contextWidth: getMainWidth(context), item: widget.item, readOnly: true,),
ItemWidget(item: widget.item),
]);
}
}

View File

@@ -1,22 +1,177 @@
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:oc_front/core/services/specialized_services/booking_service.dart';
import 'package:oc_front/main.dart';
import 'package:intl/intl.dart' as intl;
import 'package:oc_front/pages/abstract_page.dart';
import 'package:oc_front/widgets/sheduler_items/schedule.dart';
class DatacenterFactory implements AbstractFactory {
static GlobalKey<DataCenterPageWidgetState> key = GlobalKey<DataCenterPageWidgetState>();
@override GlobalKey getKey() { return key; }
@override String? getSearch() { return ""; }
@override void back(BuildContext context) { }
static GlobalKey<ComputePageWidgetState> key = GlobalKey<ComputePageWidgetState>();
@override bool searchFill() { return false; }
@override Widget factory(GoRouterState state, List<String> args) { return DataCenterPageWidget(); }
@override void search(BuildContext context) { }
@override Widget factory(GoRouterState state, List<String> args) { return ComputePageWidget(); }
@override void search(BuildContext context, bool special) { }
}
class DataCenterPageWidget extends StatefulWidget {
DataCenterPageWidget () : super(key: DatacenterFactory.key);
@override DataCenterPageWidgetState createState() => DataCenterPageWidgetState();
// ignore: must_be_immutable
class ComputePageWidget extends StatefulWidget {
bool isList = true;
DateTime start = DateTime.now();
DateTime end = DateTime.now().add(const Duration(days: 180));
final BookingExecutionService _service = BookingExecutionService();
static Widget factory() { return DataCenterPageWidget(); }
ComputePageWidget () : super(key: DatacenterFactory.key);
@override ComputePageWidgetState createState() => ComputePageWidgetState();
static Widget factory() { return ComputePageWidget(); }
}
class DataCenterPageWidgetState extends State<DataCenterPageWidget> {
class ComputePageWidgetState extends State<ComputePageWidget> {
List<Color> colors = [Colors.blue, Colors.orange, redColor, Colors.green, redColor];
List<String> titles = ["SCHEDULED", "RUNNING", "FAILURE", "SUCCESS", "MISSED"];
@override Widget build(BuildContext context) {
return Column( children: []);
GlobalKey<ScheduleWidgetState> k = GlobalKey<ScheduleWidgetState>();
return Column( children: [
Container( color: lightColor,
height: 50, width: getMainWidth(context),
child: Padding(padding: const EdgeInsets.symmetric(horizontal: 50),
child: Row( children: [
Padding(padding: const EdgeInsets.only(right: 30),
child: Tooltip( message: widget.isList ? "calendar view" : "list view",
child: InkWell( child: Icon( widget.isList ? Icons.calendar_month : Icons.list, color: Colors.white
, size: 25 ),
onTap: () {
widget.isList = !widget.isList;
k.currentState?.setState(() { k.currentState?.widget.isList = widget.isList; });
})
),
),
Container(padding: const EdgeInsets.only(left: 20),
width: getMainWidth(context) / 5,
height: 30,
child: DateTimeField(
validator: (value) {
return null;
},
resetIcon: null,
onShowPicker: (context, currentValue) async {
var date = await showDatePicker(
builder: (BuildContext context, Widget? child) {
Widget w = Theme(
data: ThemeData(
cardTheme: CardTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
dialogTheme: DialogTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
colorScheme: ColorScheme.light(
background: midColor,
tertiary: Colors.grey,
secondary: Colors.grey,
primary: Colors.black),
),
child: child ?? Container(),
);
return w;
},
context: context,
firstDate: DateTime(1900),
initialDate: widget.start,
lastDate: DateTime(2100)
);
return date;
},
format: intl.DateFormat('y-M-dd hh:mm:ss'),
initialValue: widget.start,
onChanged: (value) {
if (value == null) { return; }
setState(() { widget.start = value; });
},
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
fillColor: Colors.white,
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
alignLabelWithHint: false,
hintText: "enter start date...",
labelText: "",
errorStyle: TextStyle(fontSize: 0),
hintStyle: TextStyle(fontSize: 10),
labelStyle: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w500),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
)
),
Container(padding: const EdgeInsets.only(left: 20),
child: const Text("TO", style: TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w500))),
Container( padding: const EdgeInsets.only(left: 20, right: 20),
width: getMainWidth(context) / 5,
height: 30,
child: DateTimeField(
validator: (value) {
return null;
},
resetIcon: null,
onShowPicker: (context, currentValue) async {
var date = await showDatePicker(
builder: (BuildContext context, Widget? child) {
Widget w = Theme(
data: ThemeData(
cardTheme: CardTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
dialogTheme: DialogTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
colorScheme: ColorScheme.light(
background: midColor,
tertiary: Colors.grey,
secondary: Colors.grey,
primary: Colors.black),
),
child: child ?? Container(),
);
return w;
},
context: context,
firstDate: DateTime(1900),
initialDate: widget.end,
lastDate: DateTime(2100)
);
return date;
},
format: intl.DateFormat('y-M-dd hh:mm:ss'),
initialValue: widget.end,
onChanged: (value) {
if (value == null) { return; }
setState(() { widget.start = value; });
},
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
fillColor: Colors.white,
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
alignLabelWithHint: false,
hintText: "enter end date...",
labelText: "",
errorStyle: TextStyle(fontSize: 0),
hintStyle: TextStyle(fontSize: 10),
labelStyle: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w500),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
)
),
Tooltip( message: "refresh scheduler",
child: InkWell(
onTap: () => setState(() {}),
child: const Icon(Icons.refresh, color: Colors.white,),
),
)
]))
),
ScheduleWidget( key: k, start: widget.start, end : widget.end, isList: widget.isList, isBox: false)
]);
}
}

View File

@@ -1,17 +1,32 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:go_router/go_router.dart';
import 'package:latlong2/latlong.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:oc_front/core/services/specialized_services/datacenter_service.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/resources/compute.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/models/resources/storage.dart';
import 'package:oc_front/pages/abstract_page.dart';
import 'package:oc_front/widgets/items/item_row.dart';
class MapFactory implements AbstractFactory {
@override GlobalKey getKey() { return key; }
@override String? getSearch() { return ""; }
@override void back(BuildContext context) { }
static GlobalKey<MapPageWidgetState> key = GlobalKey<MapPageWidgetState>();
@override bool searchFill() { return false; }
@override Widget factory(GoRouterState state, List<String> args) { return MapPageWidget(); }
@override void search(BuildContext context) { }
@override void search(BuildContext context, bool special) { }
}
double menuSize = 0;
class MapPageWidget extends StatefulWidget {
bool isShowed = false;
final DatacenterService _service = DatacenterService();
MapPageWidget(): super(key: MapFactory.key);
@override MapPageWidgetState createState() => MapPageWidgetState();
static void search(BuildContext context) { }
@@ -21,26 +36,193 @@ class MapPageWidgetState extends State<MapPageWidget> {
double currentZoom = 2.0;
LatLng currentCenter = const LatLng(51.5, -0.09);
static final MapController _mapController = MapController();
void _zoom() {
currentZoom = currentZoom - 1;
_mapController.move(currentCenter, currentZoom);
}
bool selected = true;
@override Widget build(BuildContext context) {
return Expanded(
child : FlutterMap(
mapController: _mapController,
options: MapOptions(
initialCenter: currentCenter,
initialZoom: currentZoom,
return FutureBuilder(future: widget._service.all(context), builder: (BuildContext context, AsyncSnapshot snapshot) {
Map<String, Map<AbstractItem, LatLng>> coordinates = {};
List<Marker> markerCoordinates = [];
if (snapshot.data != null&& snapshot.data!.data != null && snapshot.data!.data!.values.isNotEmpty) {
for (var element in snapshot.data!.data!.values) {
if (element["type"] == "storage") {
StorageItem resource = StorageItem().deserialize(element);
var instance = resource.getSelectedInstance();
if (instance == null || instance.location == null) { continue; }
if (coordinates[resource.topic] == null) { coordinates[resource.topic] = {}; }
coordinates[resource.topic]![resource] = LatLng(instance.location!.latitude ?? 0, instance.location!.longitude ?? 0);
} else if (element["type"] == "compute") {
ComputeItem resource = ComputeItem().deserialize(element);
var instance = resource.getSelectedInstance();
if (instance == null || instance.location == null) { continue; }
if (coordinates[resource.topic] == null) { coordinates[resource.topic] = {}; }
var lr = Random().nextInt(max(1, 100)) / 10000;
var lfr = Random().nextInt(max(1, 100)) / 10000;
coordinates[resource.topic]![resource] = LatLng((instance.location!.latitude ?? 0) + lr, (instance.location!.longitude ?? 0) + lfr);
}
}
for (var topic in coordinates.keys) {
for (var coord in coordinates[topic]!.keys) {
markerCoordinates.add(Marker(
width: 25,
height: 30,
point: coordinates[topic]![coord]!,
child: HoverMenu( width: 110, title: Container( alignment: Alignment.center,
constraints: BoxConstraints( maxHeight: 100, maxWidth: 100 ),
child: Icon(FontAwesomeIcons.locationDot,
shadows: <Shadow>[Shadow(color: Color.fromRGBO(0, 0, 0, 1), blurRadius: 10.0)],
color: getColor(topic)) ),
items: [ Container(color: Colors.white,
child: ItemRowWidget(low: true, contextWidth: 290, item: coord)) ]
),
));
}
}
Rect rect = Rect.fromCenter( center: MediaQuery.of(context).size.center(Offset.zero),
width: selected ? menuSize : 0, height: (getMainHeight(context) - 50) > 0 ? (getMainHeight(context) - 50) : 0);
return Expanded(
child : FlutterMap(
mapController: _mapController,
options: MapOptions(
initialCenter: currentCenter,
initialZoom: currentZoom,
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'dev.fleaflet.flutter_map.example',
),
MarkerLayer(
markers: markerCoordinates,
),
],
),
);
} else {
return const CircularProgressIndicator();
}
}
);
}
}
class HoverMenuController {
HoverMenuState? currentState;
void hideSubMenu() {
currentState?.hideSubMenu();
}
}
class HoverMenu extends StatefulWidget {
final Widget title;
final double? width;
final List<Widget> items;
final HoverMenuController? controller;
bool isHovered = false;
HoverMenu({
Key? key,
required this.title,
this.items = const [],
this.width,
this.controller,
}) : super(key: key);
@override
HoverMenuState createState() => HoverMenuState();
}
class HoverMenuState extends State<HoverMenu> {
OverlayEntry? _overlayEntry;
final _focusNode = FocusNode();
@override
void initState() {
super.initState();
_focusNode.addListener(_onFocusChanged);
if (widget.controller != null) {
widget.controller?.currentState = this;
}
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
void _onFocusChanged() {
if (_focusNode.hasFocus) {
_overlayEntry = _createOverlayEntry();
Overlay.of(context).insert(_overlayEntry!);
} else {
_overlayEntry?.remove();
_removeOverlay();
}
}
void _removeOverlay() {
widget.isHovered = false;
}
void hideSubMenu() {
_focusNode.unfocus();
}
@override
Widget build(BuildContext context) {
return TextButton(
style: TextButton.styleFrom(
elevation: 0.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)),
splashFactory: NoSplash.splashFactory,
padding: EdgeInsets.zero,
foregroundColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'dev.fleaflet.flutter_map.example',
)
],
)
focusNode: _focusNode,
onHover: (isHovered) {
if (isHovered && !widget.isHovered) {
_focusNode.requestFocus();
isHovered = true;
} else {
_focusNode.unfocus();
widget.isHovered = false;
}
},
onPressed: () {},
child: widget.title,
);
}
OverlayEntry _createOverlayEntry() {
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
maintainState: true,
builder: (context) => Positioned(
left: offset.dx - 300,
top: offset.dy + size.height,
width: 300,
child: TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)),
splashFactory: NoSplash.splashFactory,
backgroundColor: Colors.transparent,
),
onPressed: () {},
child: ListView(
padding: EdgeInsets.zero,
shrinkWrap: true,
children: widget.items)),
),
);
}
}

View File

@@ -1,28 +1,173 @@
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart' as intl;
import 'package:go_router/go_router.dart';
import 'package:oc_front/core/services/specialized_services/workflow_execution_service.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/pages/abstract_page.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:oc_front/widgets/sheduler_items/schedule.dart';
class SchedulerFactory implements AbstractFactory {
@override GlobalKey getKey() { return key; }
@override String? getSearch() { return ""; }
@override void back(BuildContext context) { }
static GlobalKey<SchedulerPageWidgetState> key = GlobalKey<SchedulerPageWidgetState>();
@override bool searchFill() { return false; }
@override Widget factory(GoRouterState state, List<String> args) { return SchedulerPageWidget(); }
@override void search(BuildContext context) { }
@override void search(BuildContext context, bool special) { }
}
// ignore: must_be_immutable
class SchedulerPageWidget extends StatefulWidget {
bool isList = true;
DateTime start = DateTime.now();
DateTime end = DateTime.now().add(const Duration(days: 180));
final WorkflowExecutionService _service = WorkflowExecutionService();
SchedulerPageWidget(): super(key: SchedulerFactory.key);
@override SchedulerPageWidgetState createState() => SchedulerPageWidgetState();
static void search(BuildContext context) { }
static Widget factory() { return SchedulerPageWidget(); }
}
class SchedulerPageWidgetState extends State<SchedulerPageWidget> {
class SchedulerPageWidgetState extends State<SchedulerPageWidget> {
@override Widget build(BuildContext context) {
return TableCalendar(
firstDay: DateTime.utc(2010, 10, 16),
lastDay: DateTime.utc(2030, 3, 14),
focusedDay: DateTime.now(),
);
GlobalKey<ScheduleWidgetState> k = GlobalKey<ScheduleWidgetState>();
return Column( children: [
Container( color: lightColor,
height: 50, width: getMainWidth(context),
child: Padding(padding: const EdgeInsets.symmetric(horizontal: 50),
child: Row( children: [
Padding(padding: const EdgeInsets.only(right: 30),
child: Tooltip( message: widget.isList ? "calendar view" : "list view",
child: InkWell( child: Icon( widget.isList ? Icons.calendar_month : Icons.list, color: Colors.white
, size: 25 ),
onTap: () {
widget.isList = !widget.isList;
k.currentState?.setState(() { k.currentState?.widget.isList = widget.isList; });
})
),
),
Container(padding: const EdgeInsets.only(left: 20),
width: getMainWidth(context) / 5,
height: 30,
child: DateTimeField(
validator: (value) {
return null;
},
resetIcon: null,
onShowPicker: (context, currentValue) async {
var date = await showDatePicker(
builder: (BuildContext context, Widget? child) {
Widget w = Theme(
data: ThemeData(
cardTheme: CardTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
dialogTheme: DialogTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
colorScheme: ColorScheme.light(
background: midColor,
tertiary: Colors.grey,
secondary: Colors.grey,
primary: Colors.black),
),
child: child ?? Container(),
);
return w;
},
context: context,
firstDate: DateTime(1900),
initialDate: widget.start,
lastDate: DateTime(2100)
);
return date;
},
format: intl.DateFormat('y-M-dd hh:mm:ss'),
initialValue: widget.start,
onChanged: (value) {
if (value == null) { return; }
setState(() { widget.start = value; });
},
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
fillColor: Colors.white,
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
alignLabelWithHint: false,
hintText: "enter start date...",
labelText: "",
errorStyle: TextStyle(fontSize: 0),
hintStyle: TextStyle(fontSize: 10),
labelStyle: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w500),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
)
),
Container(padding: const EdgeInsets.only(left: 20),
child: const Text("TO", style: TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w500))),
Container( padding: const EdgeInsets.only(left: 20, right: 20),
width: getMainWidth(context) / 5,
height: 30,
child: DateTimeField(
validator: (value) {
return null;
},
resetIcon: null,
onShowPicker: (context, currentValue) async {
var date = await showDatePicker(
builder: (BuildContext context, Widget? child) {
Widget w = Theme(
data: ThemeData(
cardTheme: CardTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
dialogTheme: DialogTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
colorScheme: ColorScheme.light(
background: midColor,
tertiary: Colors.grey,
secondary: Colors.grey,
primary: Colors.black),
),
child: child ?? Container(),
);
return w;
},
context: context,
firstDate: DateTime(1900),
initialDate: widget.end,
lastDate: DateTime(2100)
);
return date;
},
format: intl.DateFormat('y-M-dd hh:mm:ss'),
initialValue: widget.end,
onChanged: (value) {
if (value == null) { return; }
setState(() { widget.start = value; });
},
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
fillColor: Colors.white,
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
alignLabelWithHint: false,
hintText: "enter end date...",
labelText: "",
errorStyle: TextStyle(fontSize: 0),
hintStyle: TextStyle(fontSize: 10),
labelStyle: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w500),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent)),
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
)
),
Tooltip( message: "refresh scheduler",
child: InkWell(
onTap: () => setState(() {}),
child: const Icon(Icons.refresh, color: Colors.white,),
),
)
]))
),
ScheduleWidget( key: k, start: widget.start, end : widget.end, isList: widget.isList, )
]);
}
}
}

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

@@ -0,0 +1,619 @@
import 'dart:async';
import 'package:intl/intl.dart';
import 'package:oc_front/main.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:oc_front/models/shared.dart';
import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/widgets/catalog.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/pages/abstract_page.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/core/models/workspace_local.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/core/services/perms_service.dart';
import 'package:oc_front/widgets/items/shallow_item_row.dart';
import 'package:oc_front/widgets/dialog/shallow_creation.dart';
import 'package:oc_front/widgets/inputs/shallow_text_input.dart';
import 'package:oc_front/core/models/shared_workspace_local.dart';
import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart';
import 'package:oc_front/core/services/specialized_services/peer_service.dart';
import 'package:oc_front/core/services/specialized_services/shared_service.dart';
import 'package:oc_front/core/services/specialized_services/workflow_service.dart';
enum CollaborativeAreaType { global, collaborative_area, workspace, workflow, peer, resource }
class SharedFactory implements AbstractFactory {
@override GlobalKey getKey() { return key; }
@override String? getSearch() { return ""; }
@override void back(BuildContext context) { }
static GlobalKey<SharedPageWidgetState> key = GlobalKey<SharedPageWidgetState>();
@override bool searchFill() { return false; }
@override Widget factory(GoRouterState state, List<String> args) { return SharedPageWidget(); }
@override void search(BuildContext context, bool special) { }
}
class SharedPageWidget extends StatefulWidget {
CollaborativeAreaType type = CollaborativeAreaType.global;
bool showDialog = false;
SharedPageWidget(): super(key: SharedFactory.key);
@override SharedPageWidgetState createState() => SharedPageWidgetState();
static void search(BuildContext context) { }
static Widget factory() { return SharedPageWidget(); }
}
class SharedPageWidgetState extends State<SharedPageWidget> {
SharedService service = SharedService();
Widget getMenuItem(CollaborativeAreaType workspaceType, IconData icon) {
var s = workspaceType == CollaborativeAreaType.global ? "dashboard" : workspaceType == CollaborativeAreaType.workspace ? "shared workspaces" : workspaceType == CollaborativeAreaType.workflow ? "shared workflows" : "peers engaged";
return Tooltip( message: s,
child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () => setState(() {
widget.type = workspaceType;
}),
child: Container( width: 50, height: 50,
color: widget.type == workspaceType ? darkColor : Colors.transparent,
padding: const EdgeInsets.symmetric(vertical: 10),
child : Icon(icon,
color: widget.type == workspaceType ? Colors.white : Colors.white, size: 20))));
}
@override Widget build(BuildContext context) {
GlobalKey<ShallowTextInputWidgetState> key = GlobalKey<ShallowTextInputWidgetState>();
if (CollaborativeAreaLocal.current == null) {
Future.delayed( const Duration(microseconds: 100), () {
HeaderConstants.setTitle("Choose a Collaborative Area");
HeaderConstants.setDescription("select a shared workspace to continue");
});
if (!widget.showDialog) {
Future.delayed( const Duration(milliseconds: 100), () {
widget.showDialog = true;
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext ctx) => AlertDialog(
titlePadding: EdgeInsets.zero,
insetPadding: EdgeInsets.zero,
backgroundColor: Colors.white,
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)),
title: ShallowCreationDialogWidget(
formKey: key,
canClose: () => CollaborativeAreaLocal.current != null,
context: context,
load: (p0) async {
CollaborativeAreaLocal.current = p0;
HeaderConstants.setTitle("Collaborative Area <${CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current]?.name ?? ""}>");
HeaderConstants.setDescription(CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current]?.description ?? "");
Future.delayed(const Duration(seconds: 1), () => SharedFactory.key.currentState?.setState(() { widget.showDialog = false; }));
},
form: [
ShallowTextInputWidget(
change :(p0) => key.currentState?.setState(() {}),
canLoad: (po) => po != null && po.isNotEmpty,
type: CollaborativeAreaType.collaborative_area,
width: getMainWidth(context) <= 540 ? getMainWidth(context) - 140 : 400,
attr: "description",
color: Colors.black,
hintColor: Colors.grey,
hint: "enter collaborative area description...",
filled: midColor,
)
],
create: PermsService.getPerm(Perms.COLLABORATIVE_AREA_CREATE) ? (p0) async => await SharedService().post(context, p0, {}).then((value) {
if (value.data != null) {
CollaborativeAreaLocal.current = value.data!.id;
}
CollaborativeAreaLocal.init(context, true);
HeaderConstants.setTitle("Collaborative Area <${CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current]?.name ?? ""}>");
HeaderConstants.setDescription(CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current]?.description ?? "");
Future.delayed(const Duration(seconds: 1), () => SharedFactory.key.currentState?.setState(() { widget.showDialog = false; }));
}) : null,
type: CollaborativeAreaType.collaborative_area,
all: () async {
await CollaborativeAreaLocal.init(context, true);
return CollaborativeAreaLocal.workspaces.values.map(
(e) => Shallow(id: e.id ?? "", name: e.name ?? "") ).toList();
}
)));
});
}
} else {
Future.delayed( const Duration(milliseconds: 100), () {
HeaderConstants.setTitle("Collaborative Area <${CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current]?.name ?? ""}>");
HeaderConstants.setDescription(CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current]?.description ?? "");
});
}
Widget w = WorkspaceSharedPageWidget(type: widget.type);
List<Widget> addMenu = [];
CollaborativeArea? current = CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current ?? ""];
addMenu.add(getDropdown(widget.type, current, this, context, false));
return Column( children: [
Container(
height: 50,
color: lightColor,
width: getMainWidth(context),
padding: const EdgeInsets.only(left: 50),
child: Stack( alignment: Alignment.centerLeft, children: [
Tooltip( message: "open file", child: Padding( padding: const EdgeInsets.only(right: 15),
child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () {
showDialog(
barrierDismissible: false,
context: context, builder: (context) {
return AlertDialog(
titlePadding: EdgeInsets.zero,
insetPadding: EdgeInsets.zero,
backgroundColor: Colors.white,
shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0)),
title: ShallowCreationDialogWidget(
formKey: key,
canClose: () => CollaborativeAreaLocal.current != null,
context: context,
load: (p0) async {
CollaborativeAreaLocal.current = p0;
HeaderConstants.setTitle("Collaborative Area <${CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current]?.name ?? ""}>");
HeaderConstants.setDescription(CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current]?.description ?? "");
Future.delayed(const Duration(seconds: 1), () => SharedFactory.key.currentState?.setState(() {}));
},
form: [
ShallowTextInputWidget(
change :(p0) => key.currentState?.setState(() {}),
type: CollaborativeAreaType.collaborative_area,
width: getMainWidth(context) <= 540 ? getMainWidth(context) - 140 : 400,
attr: "description",
color: Colors.black,
hintColor: Colors.grey,
filled: midColor,
)
],
create: PermsService.getPerm(Perms.COLLABORATIVE_AREA_CREATE) ? (p0) async => await SharedService().post(context, p0, {}).then( (e) async {
if (e.data != null) {
CollaborativeAreaLocal.current = e.data!.id;
}
await CollaborativeAreaLocal.init(context, true);
setState(() {});
}) : null,
type: CollaborativeAreaType.collaborative_area,
all: () async => CollaborativeAreaLocal.workspaces.values.map(
(e) => Shallow(id: e.id ?? "", name: e.name ?? "") ).toList(),
));
});
},
child: const Icon(Icons.folder_open, color: Colors.white)))),
Positioned( right: 0, child: Row( children: addMenu))
])),
Row(
children: [
Container(
decoration: BoxDecoration(
color: Colors.black,
border: Border(left: BorderSide(color: lightColor))
),
height: getMainHeight(context) - 50,
width: 50,
child: Column(
children: [
getMenuItem(CollaborativeAreaType.global, Icons.dashboard),
getMenuItem(CollaborativeAreaType.workspace, Icons.workspaces),
getMenuItem(CollaborativeAreaType.workflow, Icons.rebase_edit),
getMenuItem(CollaborativeAreaType.peer, Icons.group),
])
),
Container(
height: getMainHeight(context) - 50,
width: getMainWidth(context) -50,
color: widget.type == CollaborativeAreaType.workflow || widget.type == CollaborativeAreaType.peer
|| widget.type == CollaborativeAreaType.global ? midColor : Colors.white,
child: SingleChildScrollView( child: w ))
]
) ]
);
}
}
class WorkspaceSharedPageWidget extends StatefulWidget {
CollaborativeAreaType type = CollaborativeAreaType.global;
WorkspaceSharedPageWidget({ required this.type }): super(key: null);
@override WorkspaceSharedPageWidgetState createState() => WorkspaceSharedPageWidgetState();
}
class WorkspaceSharedPageWidgetState extends State<WorkspaceSharedPageWidget> {
List<Widget> getCardWorkspaceItems(Map<String, List<AbstractItem>> data) {
List<Widget> items = [];
for (var w1 in data.keys) {
for (var w in data[w1]!) {
List<Widget> badges = [];
List<Widget> bbadges = [];
badges.add(Container( margin: const EdgeInsets.only(left: 5), padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: getColor(w.topic)
),
child:Text(w.topic, style: TextStyle( color: Colors.white, fontSize: 11) )));
bbadges.add(Container( margin: const EdgeInsets.only(left: 5),
decoration: BoxDecoration( color: Colors.grey, borderRadius: BorderRadius.circular(4) ),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), child: Text(w1, style: TextStyle( color: Colors.white, fontSize: 11) )));
items.add(ShallowItemFlowDataRowWidget(
color: Colors.grey.shade200,
item: w, badges: badges, bottomBadges: bbadges,
icon: Icons.shopping_cart,
contextWidth: 200
));
}}
return items;
}
List<Widget> getCardItems(List<ShallowData> data) {
List<Widget> items = [];
List<ShallowData> neoData = [];
for (var d in data) {
try { neoData.firstWhere( (e) => e.getID() == d.getID());
} catch (e) { neoData.add(d); }
}
for (var w in neoData) {
List<Widget> badges = [];
if (w is Peer && (
w.getID() != CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current ?? ""]?.rule?.creator
|| w.getID() != CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current ?? ""]?.creatorID)) {
badges.add(Padding( padding: const EdgeInsets.only(left: 5), child: Icon(Icons.star, color: Colors.orange.shade300 )));
} else if (widget.type == CollaborativeAreaType.workspace) {
badges.add(Container( margin: const EdgeInsets.only(left: 5), padding: EdgeInsets.all(5), color: Colors.grey.shade200,
child: Icon(Icons.star, color: Colors.orange.shade300,)));
}
items.add(ShallowItemRowWidget(
color: Colors.grey.shade200,
item: w, badges: badges,
delete: w is Peer ? (PermsService.getPerm(Perms.PEER_UNSHARE) && (
w.getID() != CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current ?? ""]?.rule?.creator
&& w.getID() != CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current ?? ""]?.creatorID) ? (String? change) async {
await SharedService().removePeer(context, CollaborativeAreaLocal.current ?? "", change ?? "");
await CollaborativeAreaLocal.init(context, false);
setState(() {});
} : null) : (w is Workflow && PermsService.getPerm(Perms.WORKSPACE_UNSHARE)) ? (String? change) async {
await SharedService().removeWorkflow(context, CollaborativeAreaLocal.current ?? "", change ?? "");
await CollaborativeAreaLocal.init(context, false);
setState(() {});
} : null,
icon: w is Workflow ? Icons.work_history_rounded : Icons.person,
contextWidth: 200
));
}
return items;
}
@override Widget build(BuildContext context) {
var space = CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current ?? ""];
if (widget.type == CollaborativeAreaType.global) {
if (space == null) {
return Container(
color: midColor,
height: getMainHeight(context) - 50,
width: getMainWidth(context) - 50,
alignment: Alignment.center,
padding: const EdgeInsets.all(50)
);
}
final DateFormat formatter = DateFormat('dd-MM-yyyy');
Peer? creator;
SharedService service = SharedService();
try { creator = space.peers.firstWhere( (e) => (space.rule?.creator ?? "") == e.id);
} catch (e) { /**/ }
Map<String, List<AbstractItem>> datas = {};
for (var w in space.workspaces) {
datas[w.getName()] =<AbstractItem> [
...w.computes,
...w.datas,
...w.processings,
...w.storages,
...w.workflows
];
}
return Padding( padding: EdgeInsets.all(30),
child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [
Row( mainAxisAlignment: MainAxisAlignment.center, children: [
Text((CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current]?.name ?? "").toUpperCase(),
style: TextStyle(color: Colors.grey, fontSize: 25, fontWeight: FontWeight.bold), textAlign: TextAlign.center,
softWrap: true, overflow: TextOverflow.ellipsis,),
]),
Container(
width: getMainWidth(context) - 50,
margin: EdgeInsets.symmetric(vertical: 20),
color: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50),
child: Text(space.description ?? "",
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w400, color: Colors.grey))
),
Padding( padding: EdgeInsets.only(left: 30), child: Text("COLLABORATIVE AREA ACCESS RULES", textAlign: TextAlign.start,
style: TextStyle(color: darkColor, fontSize: 20, fontWeight: FontWeight.bold), overflow: TextOverflow.clip)),
Container(
width: getMainWidth(context) - 50,
margin: EdgeInsets.symmetric(vertical: 20),
color: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50),
child: Column(
children: [
Row(children: [ Text("Share mode : "), Text( space.rule?.shareMode ?? "closed", style: TextStyle( fontWeight: FontWeight.bold ) )],),
Row(children: [ Text("Created : "), Text( formatter.format(space.rule?.createdAt ?? DateTime.now()), style: TextStyle( fontWeight: FontWeight.bold )) ],),
creator != null ? Row(children: [ Text("Area master : "), Text( creator.getName(), style: TextStyle( fontWeight: FontWeight.bold ) ) ],)
: Container(),
Row(children: [ Text("User count : "), Text( "${space.peers.length}", style: TextStyle( fontWeight: FontWeight.bold )) ],),
Row(children: [ Text("Users profile : "), Text( space.rule?.exploitedBy ?? "any", style: TextStyle( fontWeight: FontWeight.bold ) ) ],),
],
)
),
Row( children: [
Padding( padding: EdgeInsets.only(left: 30), child: Text("COLLABORATIVE AREA PEERS", textAlign: TextAlign.start,
style: TextStyle(color: darkColor, fontSize: 20, fontWeight: FontWeight.bold), overflow: TextOverflow.clip)),
PermsService.getPerm(Perms.PEER_SHARE) ? Container(
margin: EdgeInsets.only(left: 20), padding: EdgeInsets.only(left: 15),
decoration: BoxDecoration(
border: Border(left: BorderSide(color: Colors.grey))
), child: getDropdown(CollaborativeAreaType.peer, CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current], this, context, true)
) : Container()]),
Container(
width: getMainWidth(context) - 50,
margin: EdgeInsets.symmetric(vertical: 20),
color: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50),
child: Wrap( alignment: WrapAlignment.center, children: getCardItems(space.peers)),
),
space.rules.isNotEmpty ? Padding( padding: EdgeInsets.only(left: 30), child: Text("RULES", textAlign: TextAlign.start,
style: TextStyle(color: darkColor, fontSize: 20, fontWeight: FontWeight.bold), overflow: TextOverflow.clip)) : Container(),
space.rules.isNotEmpty ? Container(
width: getMainWidth(context) - 50,
margin: EdgeInsets.symmetric(vertical: 20),
color: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50),
child: Column( children: space.rules.map( (e) {
return Row( children: [
Padding( padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child:Text("-", style: TextStyle(color: Colors.black, fontSize: 15, fontWeight: FontWeight.bold), overflow: TextOverflow.clip)),
Text(e ?? "", style: TextStyle(color: Colors.grey, fontSize: 15), overflow: TextOverflow.clip)
]);
}).toList() )
) : Container(),
Row( children: [
Padding( padding: EdgeInsets.only(left: 30), child: Text("WORKSPACES / RESOURCES", textAlign: TextAlign.start,
style: TextStyle(color: darkColor, fontSize: 20, fontWeight: FontWeight.bold), overflow: TextOverflow.clip)),
PermsService.getPerm(Perms.WORKSPACE_SHARE) ? Container(
margin: EdgeInsets.only(left: 20), padding: EdgeInsets.only(left: 15),
decoration: BoxDecoration(
border: Border(left: BorderSide(color: Colors.grey))
), child: getDropdown(CollaborativeAreaType.workspace, CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current], this, context, true)) : Container() ]),
Container(
width: getMainWidth(context) - 50,
margin: EdgeInsets.symmetric(vertical: 20),
color: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50),
child: Wrap( alignment: WrapAlignment.center, children: getCardWorkspaceItems(datas))
),
Row( children: [
Padding( padding: EdgeInsets.only(left: 30), child: Text("WORKFLOWS", textAlign: TextAlign.start,
style: TextStyle(color: darkColor, fontSize: 20, fontWeight: FontWeight.bold), overflow: TextOverflow.clip)),
PermsService.getPerm(Perms.WORKFLOW_SHARE) ? Container(
margin: EdgeInsets.only(left: 20), padding: EdgeInsets.only(left: 15),
decoration: BoxDecoration(
border: Border(left: BorderSide(color: Colors.grey))
), child: getDropdown(CollaborativeAreaType.workflow, CollaborativeAreaLocal.workspaces[CollaborativeAreaLocal.current], this, context, true)) : Container()]),
Container(
width: getMainWidth(context) - 50,
margin: EdgeInsets.symmetric(vertical: 20),
color: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 50),
child: Wrap( alignment: WrapAlignment.center, children: getCardItems(space.workflows))
),
],)
);
}
if (CollaborativeAreaLocal.current == null) {
return Container(
color: midColor,
height: getMainHeight(context) - 50,
width: getMainWidth(context) - 50,
alignment: Alignment.center,
padding: const EdgeInsets.all(50)
);
}
List<Widget> items = [];
List<ShallowData> data = [];
if (widget.type == CollaborativeAreaType.workspace) {
data = space?.workspaces ?? [];
} else if (widget.type == CollaborativeAreaType.workflow) {
data = space?.workflows ?? [];
} else if (widget.type == CollaborativeAreaType.peer) {
data = space?.peers ?? [];
}
if (widget.type == CollaborativeAreaType.workspace) {
List<ShallowData> neoData = [];
for (var d in data) {
try { neoData.firstWhere( (e) => e.getID() == d.getID());
} catch (e) { neoData.add(d); }
}
for (var w in neoData) {
if (WorkspaceLocal.workspaces[w.getID()] == null) { continue; }
items.add(WorkspaceSharedItemPageWidget(name: "Workspace <${WorkspaceLocal.workspaces[w.getID()]!.name}>", id: w.getID()));
}
} else {
items = getCardItems(data);
}
if (items.isEmpty) {
return Container(
color: midColor,
height: getMainHeight(context) - 50,
width: getMainWidth(context) - 50,
alignment: Alignment.center,
padding: const EdgeInsets.all(50),
child: Text("NO ITEMS SHARED", style: const TextStyle(fontSize: 25, fontWeight: FontWeight.w600, color: Colors.white)));
}
if (widget.type == CollaborativeAreaType.workflow || widget.type == CollaborativeAreaType.peer) {
return Padding(
padding: const EdgeInsets.all(50),
child: Stack( alignment: Alignment.topLeft, children : items));
}
return Column( mainAxisAlignment: MainAxisAlignment.start, children : items);
}
}
// ignore: must_be_immutable
class WorkspaceSharedItemPageWidget extends StatefulWidget {
bool open = true;
String id = "";
String name = "";
WorkspaceSharedItemPageWidget({ this.name = "", this.id = "" }): super(key: null);
@override WorkspaceSharedItemPageWidgetState createState() => WorkspaceSharedItemPageWidgetState();
}
class WorkspaceSharedItemPageWidgetState extends State<WorkspaceSharedItemPageWidget> {
@override Widget build(BuildContext context) {
return Column( children: [
Container(
width: getMainWidth(context) - 50,
height: 50,
padding: const EdgeInsets.symmetric(horizontal: 20),
decoration: BoxDecoration(color: midColor, border: Border(bottom: BorderSide(color: Colors.white))),
child: Stack(
alignment: Alignment.centerLeft,
children: [
Row(
children: [
const Padding(padding: EdgeInsets.only(right: 20), child: Icon(Icons.shopping_cart_outlined, size: 18, color: Colors.grey)),
Text(widget.name,
style: const TextStyle(fontSize: 15, color: Colors.grey, fontWeight: FontWeight.w600)),
]
),
Positioned( right: 0, top: 5, child: IconButton( icon: Icon(widget.open ? Icons.arrow_drop_up : Icons.arrow_drop_down, color: Colors.grey),
onPressed: () => setState(() {
widget.open = !widget.open;
})))
])
),
widget.open ? CatalogWidget(itemWidth: getMainWidth(context) - 50,
items: WorkspaceLocal.byWorkspace(widget.id)) : Container()
]);
}
}
Widget getDropdown(CollaborativeAreaType type, CollaborativeArea? current, State<dynamic> state, BuildContext context, bool mainPage ) {
if (type == CollaborativeAreaType.workspace) {
return Row( mainAxisAlignment: MainAxisAlignment.end,
children : [ Container( padding: EdgeInsets.only(left: mainPage ? 0 : 20), decoration: BoxDecoration(
border: mainPage ? null : Border(left: BorderSide(color: Colors.white))
), child: ShallowDropdownInputWidget(
tooltipLoad: "share",
tooltipRemove: "unshare",
iconLoad: Icons.share,
type: type,
filled: mainPage ? Colors.white : lightColor,
hintColor: mainPage ? Colors.grey : midColor,
color: mainPage ? Colors.black : Colors.white,
prefixIcon: Icon(Icons.shopping_cart, color: Colors.white),
current: WorkspaceLocal.current,
all: () async => WorkspaceLocal.getWorkspacesShallow(),
width: getMainWidth(context) / 3,
canLoad: (String? change) => current != null && !current.workspaces.map( (e) => e.id ).contains(change),
canRemove: (String? change) => current != null && current.workspaces.map( (e) => e.id ).contains(change),
load: PermsService.getPerm(Perms.WORKSPACE_SHARE) ? (String val) async {
await SharedService().addWorkspace(context, CollaborativeAreaLocal.current ?? "", val);
await CollaborativeAreaLocal.init(context, false);
state.setState(() {});
if (mainPage) {
SharedFactory.key.currentState?.setState(() {});
}
} : null,
remove: PermsService.getPerm(Perms.WORKSPACE_UNSHARE) ? (String val) async {
await SharedService().removeWorkspace(context, CollaborativeAreaLocal.current ?? "", val);
await CollaborativeAreaLocal.init(context, false);
state.setState(() {});
if (mainPage) {
SharedFactory.key.currentState?.setState(() {});
}
} : null))
]);
}
if (type == CollaborativeAreaType.workflow) {
return Row( mainAxisAlignment: MainAxisAlignment.end,
children : [ Container( padding: EdgeInsets.only(left: mainPage ? 0 : 20), decoration: BoxDecoration(
border: mainPage ? null : Border(left: BorderSide(color: Colors.white))
), child: ShallowDropdownInputWidget(
tooltipLoad: "share",
tooltipRemove: "unshare",
iconLoad: Icons.share,
filled: mainPage ? Colors.white : lightColor,
hintColor: mainPage ? Colors.grey : midColor,
color: mainPage ? Colors.black : Colors.white,
type: type, all: () async {
List<Shallow> shals = [];
await WorflowService().all(context).then((value) {
if (value.data != null) {
shals = value.data!.values.map((e) => Shallow(id: e["id"], name: e["name"])).toList();
}
});
return shals;
},
width: getMainWidth(context) / 3,
canLoad: (String? change) => current != null && !current.workflows.map( (e) => e.id ).contains(change),
canRemove: (String? change) => current != null && current.workflows.map( (e) => e.id ).contains(change),
load: PermsService.getPerm(Perms.WORKFLOW_SHARE) ? (String change) async {
await SharedService().addWorkflow(context, CollaborativeAreaLocal.current ?? "", change);
await CollaborativeAreaLocal.init(context, false);
state.setState(() {});
if (mainPage) {
SharedFactory.key.currentState?.setState(() {});
}
} : null,
remove: PermsService.getPerm(Perms.WORKFLOW_UNSHARE) ? (String change) async {
await SharedService().removeWorkflow(context, CollaborativeAreaLocal.current ?? "", change);
await CollaborativeAreaLocal.init(context, false);
state.setState(() {});
if (mainPage) {
SharedFactory.key.currentState?.setState(() {});
}
} : null))
]);
}
if (type == CollaborativeAreaType.peer) {
return Row( mainAxisAlignment: MainAxisAlignment.end,
children : [
Container( padding: EdgeInsets.only(left: mainPage ? 0 : 20), decoration: BoxDecoration(
border: mainPage ? null : Border(left: BorderSide(color : Colors.white))
), child: ShallowDropdownInputWidget(
tooltipLoad: "add",
iconLoad: Icons.add,
filled: mainPage ? Colors.white : lightColor,
hintColor: mainPage ? Colors.grey : midColor,
color: mainPage ? Colors.black : Colors.white,
type: type, all: () async {
List<Shallow> shals = [];
await PeerService().all(context).then((value) {
if (value.data != null) {
shals = value.data!.values.where( (e) {
return e["id"] != current?.creatorID;
}
).map((e) => Shallow(id: e["id"], name: e["name"])).toList();
}
});
return shals;
},
width: getMainWidth(context) / 3,
canLoad: (String? change) => current != null && !current.peers.map( (e) => e.id ).contains(change),
canRemove: (String? change) => current != null && current.peers.map( (e) => e.id ).contains(change),
load: PermsService.getPerm(Perms.PEER_SHARE) ? (String change) async {
await SharedService().addPeer(context, CollaborativeAreaLocal.current ?? "", change);
await CollaborativeAreaLocal.init(context, false);
state.setState(() {});
if (mainPage) {
SharedFactory.key.currentState?.setState(() {});
}
} : null,
remove: PermsService.getPerm(Perms.PEER_UNSHARE) ? (String change) async {
await SharedService().removePeer(context, CollaborativeAreaLocal.current ?? "", change);
await CollaborativeAreaLocal.init(context, false);
state.setState(() {});
if (mainPage) {
SharedFactory.key.currentState?.setState(() {});
}
} : null))
]);
}
return Container();
}

View File

@@ -1,26 +1,49 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:go_router/go_router.dart';
import 'package:oc_front/core/models/workspace_local.dart';
import 'package:oc_front/core/sections/header/header.dart';
import 'package:oc_front/core/services/perms_service.dart';
import 'package:oc_front/core/services/specialized_services/workflow_service.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/resources/compute.dart';
import 'package:oc_front/models/resources/data.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/models/resources/storage.dart';
import 'package:oc_front/models/resources/workflow.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/pages/abstract_page.dart';
import 'package:oc_front/widgets/dialog/new_box.dart';
import 'package:oc_front/pages/shared.dart';
import 'package:oc_front/widgets/dialog/shallow_creation.dart';
import 'package:oc_front/widgets/forms/resource_forms.dart';
import 'package:oc_front/widgets/forms/scheduler_forms.dart';
import 'package:oc_front/widgets/forms/storage_processing_link_forms.dart';
import 'package:oc_front/widgets/items/item_row.dart';
Dashboard dash = Dashboard(name: "workflow_${DateTime.now().toString().replaceAll(" ", "_").substring(0, DateTime.now().toString().length - 7)}");
import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart';
Dashboard dash = Dashboard(name: "");
class WorkflowFactory implements AbstractFactory {
@override GlobalKey getKey() { return key; }
@override String? getSearch() { return ""; }
@override void back(BuildContext context) { }
static GlobalKey<WorkflowPageWidgetState> key = GlobalKey<WorkflowPageWidgetState>();
@override bool searchFill() { return false; }
@override Widget factory(GoRouterState state, List<String> args) { return WorkflowPageWidget(); }
@override void search(BuildContext context) { }
@override Widget factory(GoRouterState state, List<String> args) {
String? id;
try { id = state.pathParameters[args.first];
} catch (e) { /**/ }
return WorkflowPageWidget(id: id);
}
@override void search(BuildContext context, bool special) { }
}
bool getAll = true;
// ignore: must_be_immutable
class WorkflowPageWidget extends StatefulWidget {
WorkflowPageWidget () : super(key: WorkflowFactory.key);
String? id;
WorkflowPageWidget ({ this.id }) : super(key: WorkflowFactory.key);
@override WorkflowPageWidgetState createState() => WorkflowPageWidgetState();
static void search(BuildContext context) { }
static Widget factory() { return WorkflowPageWidget(); }
@@ -29,42 +52,298 @@ class WorkflowPageWidgetState extends State<WorkflowPageWidget> {
final WorflowService _service = WorflowService();
Widget itemBuild(Object item) {
var e = item as AbstractItem;
return e.logo != null ? Image.memory(base64Decode(e.logo ?? ""), fit: BoxFit.fill)
: Image.network('https://get-picto.com/wp-content/uploads/2024/01/logo-instagram-png.webp', fit: BoxFit.fill);
return Tooltip( message: item.name ?? "",
child: e.logo != null ? Image.network(e.logo ?? "", fit: BoxFit.fill)
: Image.network('https://get-picto.com/wp-content/uploads/2024/01/logo-instagram-png.webp',
fit: BoxFit.fill));
}
Widget itemTooltipBuild(Object item) {
var e = item as AbstractItem;
return Container(color: Colors.white, child: ItemRowWidget(low: true, contextWidth: 300, item: e));
return Container(color: Colors.white, child: ItemRowWidget(low: true, contextWidth: 290, item: e));
}
List<DropdownMenuItem> getItems(Object? data) {
data = data as APIResponse<RawData>?;
if (data != null && data.data != null && data.data!.values.isNotEmpty) {
return (data.data!.values as List<dynamic>).map((dynamic value) {
return DropdownMenuItem<String>(
value: value.toString(),
child: Text(value.toString()),
);
}).toList();
Widget getArrowForms(ArrowPainter? arrow) {
if (arrow == null) { return Container(); }
var from = dash.getElement(arrow.fromID);
var to = dash.getElement(arrow.toID);
if ((from?.element?.getType() == "storage" && to?.element?.getType() == "processing")
|| (from?.element?.getType() == "processing" && to?.element?.getType() == "storage")) {
return StorageProcessingLinkFormsWidget( dash: dash, item: arrow);
}
return [];
return Container();
}
List<Widget> getForms(FlowData? obj, String id) {
var objAbs = obj as AbstractItem?;
if (objAbs == null) { return []; }
List<Widget> res = [ ResourceFormsWidget(item: objAbs, dash: dash, elementID: id) ];
return [ Wrap(
alignment: WrapAlignment.center,
children: [
Container( padding: const EdgeInsets.all(10), width: 250, height: 60,
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))),
child: const Column( mainAxisAlignment: MainAxisAlignment.center, children: [
Text("ELEMENT INFO", style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
Text("<general>", style: TextStyle(fontSize: 12), textAlign: TextAlign.center),
])),
...res
]) ];
}
Widget? getTopRight(FlowData? obj) {
var objAbs = obj as AbstractItem?;
if (objAbs == null) { return null; }
if (objAbs.topic == "compute") {
var instance = objAbs as ComputeItem;
if (instance.infrastructureEnum != null) {
if (instance.infrastructureEnum == 0) {
return Icon(FontAwesomeIcons.docker, size: 16);
} else if (instance.infrastructureEnum == 1) {
return Icon(FontAwesomeIcons.lifeRing, size: 16);
} else if (instance.infrastructureEnum == 2) {
return Icon(FontAwesomeIcons.cubes, size: 16);
} else if (instance.infrastructureEnum == 3) {
return Icon(FontAwesomeIcons.hardDrive, size: 16);
} else if (instance.infrastructureEnum == 4) {
return Icon(FontAwesomeIcons.v, size: 16);
}
}
}
return null;
}
Widget? getBottomLeftBadge(FlowData? obj) {
var objAbs = obj as AbstractItem?;
if (objAbs == null) { return null; }
if (objAbs.topic == "processing" ) {
return Icon(FontAwesomeIcons.gear, size: 16);
} else if (objAbs.topic == "data" ) {
return Icon(FontAwesomeIcons.file, size: 16);
} else if (objAbs.topic == "storage" ) {
return Icon(FontAwesomeIcons.database, size: 16);
} else if (objAbs.topic == "compute" ) {
return Icon(FontAwesomeIcons.microchip, size: 16);
} else if (objAbs.topic == "workflows" ) {
return Icon(FontAwesomeIcons.diagramProject, size: 16);
}
return null;
}
List<Widget> getDashInfoForms() {
return [
SchedulerFormsWidget(item: dash),
];
}
Future<void> loadDash(String selected) async {
dash.shouldSave = false;
dash.name = "";
var name = "";
if (selected.split("~").length > 1) {
name = selected.split("~")[1];
dash.id = selected.split("~")[0];
} else {
name = selected;
}
await _service.get(context, dash.id ?? "").then((value) async {
if (value.data != null) {
// ignore: use_build_context_synchronously
await WorkspaceLocal.init(context, false);
WorkspaceLocal.changeWorkspaceByName("${value.data?.name ?? ""}_workspace");
dash.clear();
dash.deserialize(value.data!.toDashboard(), false);
Future.delayed(const Duration(seconds: 1), () {
dash.name = name;
dash.shouldSave = true;
dash.selectedMenuKey.currentState?.setState(() { });
});
}
});
}
Future<void> saveDash(String? id, BuildContext? context) async {
if (id == null || !dash.isOpened || !dash.shouldSave || !PermsService.getPerm(Perms.WORKFLOW_EDIT)) { return; }
var datas = dash.elements.where( (e) => e.element?.serialize()["type"] == "data");
var compute = dash.elements.where( (e) => e.element?.serialize()["type"] == "compute");
var storage = dash.elements.where( (e) => e.element?.serialize()["type"] == "storage");
var processing = dash.elements.where( (e) => e.element?.serialize()["type"] == "processing");
var workflows = dash.elements.where( (e) => e.element?.serialize()["type"] == "workflow");
var updateW = Workflow(
name: dash.name,
graph: Graph(),
data: datas.map((e) => e.id).toSet().toList(),
compute: compute.map((e) => e.id).toSet().toList(),
storage: storage.map((e) => e.id).toSet().toList(),
processing: processing.map((e) => e.id).toSet().toList(),
workflows: workflows.map((e) => e.id).toSet().toList(),
);
updateW.fromDashboard(dash.serialize());
for (var item in (updateW.graph?.items.values ?? [] as List<GraphItem>)) {
if (item.position == null) { continue; }
item.position?.x = (item.position?.x ?? 0) + (item.width! / 2) + 7.5;
item.position?.y = (item.position?.y ?? 0) + (item.height! / 2) + 7.5;
for (var i in (updateW.graph?.links ?? [] as List<GraphLink>).where((element) => id == element.source?.id)) {
i.source?.x = (i.source?.x ?? 0) + (item.width! / 2) + 7.5;
i.source?.y = (i.source?.y ?? 0) + (item.width! / 2) + 7.5;
}
for (var i in (updateW.graph?.links ?? [] as List<GraphLink>).where((element) => id == element.destination?.id)) {
i.destination?.x = (i.destination?.x ?? 0) + (item.width! / 2) + 7.5;
i.destination?.y = (i.destination?.y ?? 0) + (item.width! / 2) + 7.5;
}
}
updateW.graph?.zoom = dash.getZoomFactor();
dash.addToHistory();
_service.put(null, id, updateW.serialize(), {}).then( (e) async {
dash.applyInfos(updateW.graph?.getEnvToUpdate() ?? {}, updateW.toDashboard());
if (dash.addChange) {
dash.addChange = false;
// ignore: use_build_context_synchronously
await WorkspaceLocal.init(context, false);
WorkspaceLocal.changeWorkspaceByName("${dash.name}_workspace");
dash.selectedLeftMenuKey.currentState?.setState(() { });
}
});
}
FlowData? transformToData(Map<String, dynamic> data) {
if (data["type"] == "data") { return DataItem().deserialize(data); }
if (data["type"] == "compute") { return ComputeItem().deserialize(data); }
if (data["type"] == "storage") { return StorageItem().deserialize(data); }
if (data["type"] == "processing") { return ProcessingItem().deserialize(data); }
if (data["type"] == "workflows") { return WorkflowItem().deserialize(data); }
return null;
}
Widget onDashboardMenu(Dashboard dash) {
return Container( padding: EdgeInsets.only(left: 50),
decoration: BoxDecoration( border: Border( left: BorderSide( color: Colors.white ))),
child: ShallowDropdownInputWidget(
filled: lightColor,
hintColor: Colors.grey.shade200,
color: Colors.white,
prefixIcon: Padding( padding: EdgeInsets.only(right: 10), child: Icon(Icons.shopping_cart, color: Colors.grey.shade200)),
current: WorkspaceLocal.current,
width: 300,
all: () async => WorkspaceLocal.getWorkspacesShallow(),
type: CollaborativeAreaType.workspace,
change: (String? change) {
WorkspaceLocal.changeWorkspace(change.toString());
},
canLoad: (p0) => true,
load: (p0) async {
dash.isInfo = !dash.isInfo;
dash.flutterChartKey.currentState?.setState(() { });
},
tooltipLoad: "open workspace manager",
iconLoad: dash.isInfo ? Icons.remove_red_eye_outlined : Icons.remove_red_eye,
));
}
Widget onDashboardAlertOpened(BuildContext context, Dashboard dash) {
return NewBoxWidget<RawData>(service: _service, dash: dash,
getItems: getItems);
return ShallowCreationDialogWidget(
canClose: () => dash.isOpened,
context: context,
load: (p0) async {
dash.isOpened = true;
if (dash.load != null) {
WorkspaceLocal.changeWorkspaceByName(p0.split("~")[1]);
await dash.load!(p0);
}
dash.inDialog = false;
dash.notifyListeners();
},
create: PermsService.getPerm(Perms.WORKFLOW_CREATE) ? (p0) async => await _service.post(context, p0, {}).then( (value) async {
dash.clear();
dash.id = value.data?.getID() ?? "";
dash.name = value.data?.getName() ?? "";
dash.inDialog = false;
dash.notifyListeners();
await WorkspaceLocal.init(context, true);
dash.isOpened = true;
Future.delayed(const Duration(seconds: 1), () {
dash.load!("${dash.id}~${dash.name}");
});
}
) : null,
maptoDropdown: (e) => DropdownMenuItem<String>(
value: "${e.id}~${e.name}",
child: Text(e.name),
),
type: CollaborativeAreaType.workflow,
all: () async {
List<Shallow> res = [];
await _service.all(context).then(
(e) {
if (e.data != null) {
res = e.data!.values.map((e) => Shallow(id: e["id"] ?? "", name: e["name"] ?? "")).toList();
}
}
);
return res;
}
);
}
@override Widget build(BuildContext context) {
var quart = MediaQuery.of(context).size.width / 6;
dash.defaultName = "workflow_${DateTime.now().toString().replaceAll(" ", "_").substring(0, DateTime.now().toString().length - 7)}";
dash.load = loadDash;
dash.save = saveDash;
dash.dashColor = lightColor;
dash.midDashColor = midColor;
dash.transformToData = transformToData;
dash.infoItemWidget = getForms;
dash.infoLinkWidget = getArrowForms;
dash.infoWidget = getDashInfoForms;
dash.widthOffset = 50;
dash.arrowStyleRules = [
(dash) {
for (var arrow in dash.arrows) {
try {
var from = dash.elements.firstWhere((element) => arrow.fromID.split("_")[0] == element.id).element;
var to = dash.elements.firstWhere((element) => arrow.toID.split("_")[0] == element.id).element;
if ((from is ProcessingItem && to is ComputeItem) || (to is ProcessingItem && from is ComputeItem)) {
arrow.params.color = Colors.orange;
arrow.params.dashSpace = 2;
arrow.params.dashWidth = 2;
} else if ((from is ProcessingItem && to is StorageItem) || (to is ProcessingItem && from is StorageItem)) {
arrow.params.color = redColor;
arrow.params.dashSpace = 2;
arrow.params.dashWidth = 2;
} else if ((from is ProcessingItem && to is DataItem) || (to is ProcessingItem && from is DataItem)) {
arrow.params.color = Colors.blue;
arrow.params.dashSpace = 2;
arrow.params.dashWidth = 2;
} else {
arrow.params.color = Colors.black;
arrow.params.dashSpace = 0;
arrow.params.dashWidth = 0;
}
} catch (e) {
arrow.params.color = Colors.black;
arrow.params.dashSpace = 0;
arrow.params.dashWidth = 0;
}
}
return dash.arrows;
}
];
dash.saveRules = [
(dash) {
dash.error = null;
return true;
}
];
return FlowChart<AbstractItem>(
key: dash.flutterChartKey,
itemLeftBottomBadges: getBottomLeftBadge,
itemrightTopBadges: getTopRight,
onDashboardAlertOpened: onDashboardAlertOpened,
dashboard: dash,
current: widget.id,
itemWidget: itemBuild,
categories: const ["computing", "data", "datacenter", "storage"],
draggableItemBuilder: (cat) => WorkspaceLocal.byTopic(cat).toList(),
menuWidget: onDashboardMenu,
categories: const ["processing", "data", "compute", "storage", "workflows"],
draggableItemBuilder: (cat) => WorkspaceLocal.byTopic(cat, false),
itemWidgetTooltip: itemTooltipBuild,
innerMenuWidth: quart > 80 ? quart : 80,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height - HeaderConstants.height,
innerMenuWidth: 350,
width: getMainWidth(context),
height: getMainHeight(context),
onNewConnection: (p1, p2) { },
onDashboardTapped: (context, position) { },
onScaleUpdate: (newScale) { },

View File

@@ -1,17 +1,22 @@
import 'package:oc_front/main.dart';
import 'package:flutter/material.dart';
import 'package:oc_front/core/models/workspace_local.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/widgets/items/item_row.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/core/models/workspace_local.dart';
class CatalogWidget extends StatefulWidget {
double? itemWidth;
bool readOnly = false;
final List<AbstractItem>? items;
CatalogWidget ({ Key? key, this.items }): super(key: key);
CatalogWidget ({ super.key, this.items, this.itemWidth, this.readOnly = false });
@override CatalogWidgetState createState() => CatalogWidgetState();
}
class CatalogWidgetState extends State<CatalogWidget> {
@override Widget build(BuildContext context) {
var items = widget.items ?? WorkspaceLocal.items;
List<ItemRowWidget> itemRows = items.map((e) => ItemRowWidget(contextWidth: MediaQuery.of(context).size.width, item: e)).toList();
return SingleChildScrollView( child: Column( children: itemRows) );
List<ItemRowWidget> itemRows = items.map((e) => ItemRowWidget(
readOnly: widget.readOnly,
contextWidth: widget.itemWidth ?? getMainWidth(context), item: e)).toList();
return Column( children: itemRows);
}
}

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:oc_front/main.dart';
class InfoAlertBannerChild extends StatelessWidget {
final String text;
@@ -9,7 +10,7 @@ class InfoAlertBannerChild extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
width: double.infinity,
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.8),
constraints: BoxConstraints(maxWidth: getMainWidth(context) * 0.8),
decoration: const BoxDecoration(
color: Colors.greenAccent,
borderRadius: BorderRadius.all(Radius.circular(5)),
@@ -38,9 +39,9 @@ class AlertAlertBannerChild extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
width: double.infinity,
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.8),
decoration: const BoxDecoration(
color: Colors.redAccent,
constraints: BoxConstraints(maxWidth: getMainWidth(context) * 0.8),
decoration: BoxDecoration(
color: redColor,
borderRadius: BorderRadius.all(
Radius.circular(5),
),

View File

@@ -1,71 +1,140 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/services.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:go_router/go_router.dart';
import 'package:oc_front/core/services/auth.service.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/pages/workflow.dart';
class LoginWidget extends StatefulWidget {
LoginWidget ({ Key? key }): super(key: key);
@override LoginWidgetState createState() => LoginWidgetState();
}
class LoginWidgetState extends State<LoginWidget> {
TextEditingController usernameCtrl = TextEditingController();
TextEditingController passwordCtrl = TextEditingController();
String? error;
bool loading = false;
FocusNode focusNode = FocusNode();
@override Widget build(BuildContext context) {
return AlertDialog(
backgroundColor: Colors.white,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(0))),
content: Padding(padding: const EdgeInsets.all(20), child: Column(mainAxisSize: MainAxisSize.min, children: [
const Center(child: Padding( padding: EdgeInsets.only(bottom: 10),
child: Icon(Icons.person_search, size: 80, color: Colors.grey,))),
const Center(child: Text("WELCOME ON OPENCLOUD", style: TextStyle(fontSize: 25, fontWeight: FontWeight.w600,
color: Color.fromRGBO(38, 166, 154, 1)),)),
Padding(padding: const EdgeInsets.symmetric(vertical: 20), child: Divider(color: Colors.grey.shade300,),),
Container( margin: const EdgeInsets.only(bottom: 10), child: Center(child: Row( mainAxisAlignment: MainAxisAlignment.center,
return KeyboardListener(focusNode: focusNode,
onKeyEvent: (value) async {
if (value is KeyDownEvent && value.logicalKey == LogicalKeyboardKey.enter) {
if (usernameCtrl.text == "" || passwordCtrl.text == "") { return; }
error = null;
setState(() {
loading = true;
});
await AuthService.login(usernameCtrl.text, passwordCtrl.text).catchError( (e) {
setState(() {
loading = false;
error = "Invalid username or password";
});
}).then( (e) {
if (error == null) {
// ignore: use_build_context_synchronously
setState(() {
loading = false;
});
loginIsSet = false;
dash.inDialog = false;
context.pop();
mainKey?.currentState?.setState(() {});
}
});
}
},
child: Container( padding: const EdgeInsets.all(50), child: Column(children: [
getMainHeight(context) < 600 ? Container() : SizedBox( width: getMainWidth(context) / 4, height: getMainHeight(context) / 4,
child: FittedBox(
child:const Center(child: Icon(Icons.person_search, size: 150, color: Colors.grey,)))),
Center(child: Padding( padding: const EdgeInsets.only(top: 5, bottom: 20),
child: Text("WELCOME ON OPENCLOUD", style: TextStyle(fontSize: 25, fontWeight: FontWeight.w600,
color: lightColor ), overflow: TextOverflow.ellipsis, ))),
Container( margin: const EdgeInsets.only(bottom: 10),
child: Center(child: Row( mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width / 3,
alignment : Alignment.center,
child: TextField(
controller: usernameCtrl,
onChanged: (v) => setState(() {}),
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.shade300), borderRadius: BorderRadius.zero),
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.shade300), borderRadius: BorderRadius.zero),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.shade300), borderRadius: BorderRadius.zero),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: midColor), borderRadius: BorderRadius.zero),
border: OutlineInputBorder(borderSide: BorderSide(color: midColor), borderRadius: BorderRadius.zero),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: midColor), borderRadius: BorderRadius.zero),
hintText: "username...",
contentPadding: const EdgeInsets.symmetric(horizontal: 20),
fillColor: Colors.grey.shade300,
fillColor: midColor,
filled: true,
hintStyle: const TextStyle(fontSize: 12.5, color: Colors.grey)),
style: const TextStyle(fontSize: 12.5, color: Colors.grey)),),
style: const TextStyle(fontSize: 12.5, color: Colors.black)),),
Container(width: 50, height: 50, color: Colors.black, child: const Icon(Icons.person, color: Colors.white))
]))),
Container( margin: const EdgeInsets.only(bottom: 20), child: Center(child: Row( mainAxisAlignment: MainAxisAlignment.center,
Center(child: Row( mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width / 3,
alignment : Alignment.center,
child: TextField(
controller: passwordCtrl,
obscureText: true,
onChanged: (value) {
setState(() {});
},
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.shade300), borderRadius: BorderRadius.zero),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.shade300), borderRadius: BorderRadius.zero),
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey.shade300), borderRadius: BorderRadius.zero),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: midColor), borderRadius: BorderRadius.zero),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: midColor), borderRadius: BorderRadius.zero),
border: OutlineInputBorder(borderSide: BorderSide(color: midColor), borderRadius: BorderRadius.zero),
hintText: "password...",
contentPadding: const EdgeInsets.symmetric(horizontal: 20),
fillColor: Colors.grey.shade300,
fillColor: midColor,
filled: true,
hintStyle: const TextStyle(fontSize: 12.5, color: Colors.grey)),
style: const TextStyle(fontSize: 12.5, color: Colors.grey)),),
style: const TextStyle(fontSize: 12.5, color: Colors.black)),),
Container(width: 50, height: 50, color: Colors.black, child: const Icon(Icons.password, color: Colors.white))
]))),
Row( mainAxisAlignment: MainAxisAlignment.center, children: [
Padding( padding: const EdgeInsets.only(right: 10), child:
InkWell(onTap: () { context.pop(); },
mouseCursor: SystemMouseCursors.click,
child: Container(
margin: const EdgeInsets.only(top: 20),
width: MediaQuery.of(context).size.width / 3,
padding: const EdgeInsets.symmetric(vertical: 20),
color: const Color.fromRGBO(38, 166, 154, 1),
child: const Center( child: Text("LOGIN", style: TextStyle(color: Colors.white, fontSize: 15),))))),
])
])),
Column( children: [
Center( child: Padding( padding: EdgeInsets.only(bottom: 10, top: error == null ? 27 : 10), child:
error == null ? Container() : Text(error ?? "", style: TextStyle(color: redColor, fontSize: 12.5)))),
Row( mainAxisAlignment: MainAxisAlignment.center, children: [
Padding( padding: const EdgeInsets.only(right: 10, bottom: 30), child:
InkWell(onTap: () async {
if (usernameCtrl.text == "" || passwordCtrl.text == "") { return; }
error = null;
setState(() {
loading = true;
});
await AuthService.login(usernameCtrl.text, passwordCtrl.text).catchError( (e) {
setState(() {
loading = false;
error = "Invalid username or password";
});
}).then( (e) {
if (error == null) {
// ignore: use_build_context_synchronously
setState(() {
loading = false;
});
loginIsSet = false;
dash.inDialog = false;
context.pop();
mainKey?.currentState?.setState(() {});
}
});
},
mouseCursor: SystemMouseCursors.click,
child: Container(
width: MediaQuery.of(context).size.width / 3,
padding: const EdgeInsets.symmetric(vertical: 20),
color: usernameCtrl.text == "" || passwordCtrl.text == "" ? Colors.grey : lightColor,
child: Center( child: loading ? SpinKitWave(color: Colors.white, size: 20) : Text("LOGIN", style: TextStyle(
color: usernameCtrl.text == "" || passwordCtrl.text == "" ? midColor :Colors.white,
fontSize: 15) ))))),
])
]),
],)));
}
}

View File

@@ -1,163 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/core/services/specialized_services/abstract_service.dart';
import 'package:oc_front/models/abstract.dart';
import 'package:oc_front/models/response.dart';
abstract class New {
String name = "";
}
class NewBoxWidget<T extends SerializerDeserializer<dynamic>> extends StatefulWidget {
String? _selected;
Dashboard dash;
final TextEditingController _ctrl = TextEditingController();
AbstractService<T> service;
Function validate = () {};
NewBoxWidget ({ super.key, required this.service, required this.dash, this.getItems });
@override NewBoxWidgetState<T> createState() => NewBoxWidgetState<T>();
List<DropdownMenuItem> Function(APIResponse<T>? data)? getItems;
}
class NewBoxWidgetState<T extends SerializerDeserializer<dynamic>> extends State<NewBoxWidget> {
@override Widget build(BuildContext context) {
widget._ctrl.value = TextEditingValue(text: widget.dash.defaultName);
return Container(
color: Colors.white,
padding: const EdgeInsets.all(20),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children : [
FutureBuilder<APIResponse<T>>(
future: (widget.service as AbstractService<T>).all(context),
builder: (context, snapshot) {
List<DropdownMenuItem> items = widget.getItems != null ? widget.getItems!(snapshot.data) : [];
if (widget._selected != null
&& !items.where((element) => element.value == widget._selected).isNotEmpty) {
items.add(DropdownMenuItem<String>(
value: widget._selected.toString(),
child: Text(widget._selected.toString()),
));
}
return SizedBox( width: MediaQuery.of(context).size.width <= 540 ? MediaQuery.of(context).size.width - 140 : 400, height: 50,
child: DropdownButtonFormField(
value: widget._selected,
isExpanded: true,
hint: const Text("load workflow...", style: TextStyle(color: Colors.grey, fontSize: 15)),
decoration: InputDecoration(
filled: true,
focusedBorder: const OutlineInputBorder( borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: Color.fromARGB(38, 166, 154, 1), width: 0),
),
fillColor: Colors.grey.shade300,
contentPadding: const EdgeInsets.only(left: 30, right: 30, top: 10, bottom: 30),
enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: Colors.grey.shade300, width: 0),
),
border: OutlineInputBorder( borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: Colors.grey.shade300, width: 0)),
),
items: items, onChanged: (value) {
setState(() {
widget._selected = value.toString();
});
}));
}),
Tooltip(
message: 'empty selection',
child: InkWell(
mouseCursor: widget._selected == null || widget._selected!.isEmpty ? MouseCursor.defer : SystemMouseCursors.click,
onTap: () {
if (widget._selected == null || widget._selected!.isEmpty) { return; }
setState(() { widget._selected = null; });
},
child: Container(
width: 50, height: 50,
decoration: const BoxDecoration( color: Colors.black,
border: Border(right: BorderSide(color: Colors.white))),
child: Icon(Icons.refresh, color: widget._selected == null || widget._selected!.isEmpty ? Colors.grey : Colors.white),
)
)
),
Tooltip(
message: 'load workflow selected',
child: InkWell(
mouseCursor: widget._selected == null || widget._selected!.isEmpty
? MouseCursor.defer : SystemMouseCursors.click,
onTap: () {
if (widget._selected == null || widget._selected!.isEmpty) { return; }
widget.dash.name = widget._selected ?? widget.dash.name;
widget.dash.notifyListeners();
Navigator.pop(context);
},
child: Container(
width: 50, height: 50,
color: Colors.black,
child: Icon(Icons.open_in_browser_outlined,
color: widget._selected == null || widget._selected!.isEmpty ? Colors.grey : Colors.white),
)
)
)
]),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
margin: const EdgeInsets.only(top: 10),
width: MediaQuery.of(context).size.width <= 540 ? MediaQuery.of(context).size.width - 90 : 450,
height: 50,
child: TextFormField(
expands: true,
maxLines: null,
minLines: null,
cursorColor: const Color.fromARGB(38, 166, 154, 1),
controller: widget._ctrl,
onChanged: (value) => setState(() { widget._ctrl.value = TextEditingValue(text: value); }),
validator: (value) => value == null || value.isEmpty ? "name is required" : null,
decoration: InputDecoration(
hintText: "name a new workflow...",
fillColor: Colors.grey.shade300,
filled: true,
contentPadding: const EdgeInsets.only(left: 30, right: 30, top: 15, bottom: 5),
hintStyle: const TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w300
),
border: InputBorder.none
)
)
),
Tooltip(
message: 'add',
child:InkWell(
mouseCursor: widget._ctrl.value.text.isEmpty ? MouseCursor.defer : SystemMouseCursors.click,
onTap: () async {
if (widget._ctrl.value.text.isNotEmpty) {
await widget.service.post(context, {}, { "workflowName" : widget._ctrl.value.text });
widget._selected = widget._ctrl.value.text;
widget._ctrl.value = const TextEditingValue(text: "");
widget.dash.name = widget._selected ?? widget.dash.name;
widget.dash.notifyListeners();
// ignore: use_build_context_synchronously
Navigator.pop(context);
}
},
child: Container(
margin: const EdgeInsets.only(top: 10),
width: 50,
height: 50,
color: Colors.black,
child: Icon(Icons.add, color: widget._ctrl.value.text.isEmpty ? Colors.grey : Colors.white)
)
)
)
])
]
)
);
}
}

View File

@@ -0,0 +1,107 @@
import 'package:flutter/material.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/core/services/router.dart';
import 'package:oc_front/pages/shared.dart';
import 'package:oc_front/pages/workflow.dart';
import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart';
import 'package:oc_front/widgets/inputs/shallow_text_input.dart';
// ignore: must_be_immutable
class ShallowCreationDialogWidget extends StatefulWidget {
GlobalKey<ShallowTextInputWidgetState>? formKey;
BuildContext context;
bool Function()? canClose;
CollaborativeAreaType type = CollaborativeAreaType.workspace;
Future<List<Shallow>> Function()? all;
Future<void> Function(String)? load;
Future<void> Function(Map<String,dynamic>)? create;
bool Function(String?)? canLoad;
DropdownMenuItem Function(Shallow)? maptoDropdown;
List<ShallowTextInputWidget> form = [];
ShallowCreationDialogWidget ({ super.key, required this.type, required this.all, this.load, this.formKey,
required this.create, this.form = const [], this.maptoDropdown, required this.context, this.canClose }) ;
@override ShallowCreationDialogState createState() => ShallowCreationDialogState();
}
class ShallowCreationDialogState extends State<ShallowCreationDialogWidget> {
GlobalKey<FormFieldState> key = GlobalKey<FormFieldState>();
GlobalKey<FormFieldState> key2 = GlobalKey<FormFieldState>();
@override Widget build(BuildContext context) {
var t = widget.type == CollaborativeAreaType.workspace ? "workspace" : (widget.type == CollaborativeAreaType.workflow ? "workflow" : (widget.type == CollaborativeAreaType.collaborative_area ? "collaborative area" :"peer"));
return Container(
color: Colors.white,
padding: const EdgeInsets.only( top: 0, bottom: 20, left: 20, right: 20),
child: Column(
children: [
Container(
alignment: Alignment.centerRight,
height: 50,
child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [
Padding(padding: const EdgeInsets.symmetric(horizontal: 10), child:
Text("load or create a new $t", style: const TextStyle(color: Colors.grey, fontSize: 15)
)),
Padding ( padding: const EdgeInsets.symmetric(horizontal: 10), child:
Tooltip( message: "back", child: InkWell(
mouseCursor: SystemMouseCursors.click,
onTap: () {
dash.inDialog = false;
AppRouter.catalog.go(context, {});
},
child: const Icon(Icons.arrow_back, color: Colors.black))),
),
widget.canClose != null && !widget.canClose!() ? Container() : Row ( mainAxisAlignment: MainAxisAlignment.end, children: [
Tooltip( message: "close", child: InkWell(
mouseCursor: SystemMouseCursors.click,
onTap: () {
dash.inDialog = false;
Navigator.pop(context);
},
child: const Icon(Icons.close, color: Colors.black))),
]),
],),
),
ShallowDropdownInputWidget(
all: widget.all,
type: widget.type,
hint: "select a $t",
width: getMainWidth(context) <= 540 ? getMainWidth(context) - 140 : 400,
load: (e) async {
await widget.load!(e);
dash.inDialog = false;
Navigator.pop(widget.context);
},
iconLoad: Icons.open_in_browser_outlined,
iconRemove: Icons.refresh,
maptoDropdown: widget.maptoDropdown,
canLoad: (p0) => p0 != null && p0.isNotEmpty,
canRemove: (p0) => p0 != null && p0.isNotEmpty,
tooltipRemove: "refresh selection",
deletion: true,
color: Colors.black,
hintColor: Colors.grey,
filled: midColor,
),
Container( height: 10),
widget.create != null ? ShallowTextInputWidget(
key: widget.formKey,
type: widget.type,
hint: "create a new $t",
width: getMainWidth(context) <= 540 ? getMainWidth(context) - 140 : 400,
load: (e) async {
await widget.create!(e);
dash.inDialog = false;
Navigator.pop(widget.context);
},
forms: widget.form,
canLoad: (p0) => p0 != null && p0.isNotEmpty,
color: Colors.black,
hintColor: Colors.grey,
filled: midColor,
) : Container(),
...(widget.create != null ? widget.form.map( (e) => Container( margin: const EdgeInsets.only(top: 10), child: e)) : []),
]
)
);
}
}

View File

@@ -0,0 +1,105 @@
import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/widgets/forms/sub_expose_forms.dart';
import 'package:oc_front/widgets/inputs/sub_text_input.dart';
// ignore: must_be_immutable
class ContainerFormsWidget extends StatefulWidget {
int instanceID = 0;
Dashboard dash;
AbstractItem item;
String elementID;
ContainerFormsWidget({ super.key, required this.item, required this.dash, required this.elementID });
@override ContainerFormsWidgetState createState() => ContainerFormsWidgetState();
}
class ContainerFormsWidgetState extends State<ContainerFormsWidget> {
@override Widget build(BuildContext context) {
List<Widget> widgets = [];
var instance = widget.item.getSelectedInstance();
if (instance != null && instance is ProcessingInstance && instance.access?.container != null) {
var container = instance.access!.container!;
widgets.add(Container(
padding: const EdgeInsets.only(bottom: 10),
width: 180,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("<CONTAINER>", style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
SubTextInputWidget(subkey: "image", width: 180, empty: false, change: (value) { },
initialValue: container.image, readOnly: true),
SubTextInputWidget(subkey: "command", width: 180, empty: false, change: (value) {
container.command = value;
for (var el in widget.dash.elements) {
if (el.id == widget.elementID) {
el.element = widget.item;
break;
}
}
widget.dash.saveDash(widget.dash.id, context);
}, initialValue: container.command, readOnly: false,),
SubTextInputWidget(subkey: "args", width: 180, empty: false, change: (value) {
container.args = value;
for (var el in widget.dash.elements) {
if (el.id == widget.elementID) {
el.element = widget.item;
break;
}
}
widget.dash.saveDash(widget.dash.id, context);
},
initialValue: container.args, readOnly: false,)
],)
));
widgets.add(Container(
width: 200, decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.grey))),
));
widgets.add(Container(
padding: const EdgeInsets.symmetric(vertical: 10),
width: 180,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Padding(padding: EdgeInsets.only(bottom: 5), child: Text("<EXPOSE>",
style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold), textAlign: TextAlign.center)),
Row( children: [
InkWell( onTap: () {
container.exposes.add(Expose());
var el = widget.dash.getElement(widget.elementID);
el!.element = widget.item as dynamic;
setState(() {});
}, child:
Container( margin: const EdgeInsets.only(top: 5),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.grey, width: 1)),
width: 125, height: 30,
child: const Row( mainAxisAlignment: MainAxisAlignment.center,
children: [ Padding( padding: EdgeInsets.only(right: 5), child: Icon(Icons.add)), Text("add expose")]),
)
),
InkWell( onTap: () {
if (container.exposes.isEmpty) { return; }
container.exposes = container.exposes.sublist(0, container.exposes.length - 1);
var el = widget.dash.getElement(widget.elementID);
el!.element = widget.item as dynamic;
setState(() {});
}, child:
Container( margin: const EdgeInsets.only(left: 5, top: 5),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.grey, width: 1)),
width: 45, height: 30,
child: const Row( mainAxisAlignment: MainAxisAlignment.center,
children: [ Icon(Icons.delete, color: Colors.black) ]),
)
)
]),
],)
));
for (var expose in container.exposes) {
widgets.add(SubExposeFormsWidget( readOnly: true, width: 180, dash: widget.dash, empty: widgets.isEmpty,
item: expose, elementID: widget.elementID));
}
}
return Column(children: widgets);
}
}

View File

@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/widgets/inputs/sub_text_input.dart';
// ignore: must_be_immutable
class CredentialsFormsWidget extends StatefulWidget {
int instanceID = 0;
Dashboard dash;
AbstractItem item;
String elementID;
CredentialsFormsWidget({ super.key, required this.item, required this.dash, required this.elementID });
@override CredentialsFormsWidgetState createState() => CredentialsFormsWidgetState();
}
class CredentialsFormsWidgetState extends State<CredentialsFormsWidget> {
@override Widget build(BuildContext context) {
List<Widget> widgets = [];
var instance = widget.item.getSelectedInstance();
if (instance != null && instance.credential != null) {
var creds = instance.credential!;
widgets.add(Container( margin: EdgeInsets.only(bottom: 15),
width: 200, decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.grey))),
));
widgets.add(Container(
padding: const EdgeInsets.only(bottom: 10),
width: 180,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("<CREDENTIALS>", style: const TextStyle(fontSize: 13, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
SubTextInputWidget(subkey: "login", width: 180, empty: false, change: (value) {
creds.password = value;
for (var el in widget.dash.elements) {
if (el.id == widget.elementID) {
el.element = widget.item;
break;
}
}
widget.dash.saveDash(widget.dash.id, context);
}, initialValue: creds.login),
SubTextInputWidget(subkey: "password", width: 180, empty: false, change: (value) {
creds.password = value;
for (var el in widget.dash.elements) {
if (el.id == widget.elementID) {
el.element = widget.item;
break;
}
}
widget.dash.saveDash(widget.dash.id, context);
}, initialValue: creds.password, readOnly: false,),
],)
));
widgets.add(Container( padding: EdgeInsets.only(bottom: 15), width: 200 ));
}
return Column(children: widgets);
}
}

View File

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

View File

@@ -0,0 +1,554 @@
import 'package:cron/cron.dart';
import 'package:oc_front/core/services/specialized_services/workflow_execution_service.dart';
import 'package:oc_front/core/services/specialized_services/workflow_scheduler_service.dart';
import 'package:oc_front/main.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart' as intl;
import 'package:alert_banner/exports.dart';
import 'package:oc_front/models/resources/compute.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:oc_front/models/workflow.dart';
import 'package:oc_front/pages/shared.dart';
import 'package:oc_front/pages/workflow.dart';
import 'package:oc_front/widgets/dialog/alert.dart';
import 'package:oc_front/widgets/dialog/confirm_box.dart';
import 'package:oc_front/core/services/perms_service.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/widgets/inputs/shallow_text_input.dart';
import 'package:oc_front/core/models/shared_workspace_local.dart';
import 'package:datetime_picker_formfield/datetime_picker_formfield.dart';
import 'package:oc_front/core/services/specialized_services/check_service.dart';
import 'package:oc_front/core/services/specialized_services/workflow_service.dart';
// ignore: must_be_immutable
class SchedulerFormsWidget extends StatefulWidget {
Scheduler schedule = Scheduler();
Dashboard item;
bool? booking;
bool valid = false;
bool shouldSearch = true;
int scheduleFutureCount = 0;
int scheduleBeforeCount = 0;
String? error;
String? errorEndDate;
String? errorCron;
Function validate = () {};
final SchedulerService _schedulerService = SchedulerService();
final WorkflowExecutionService _executionService = WorkflowExecutionService();
SchedulerFormsWidget ({ super.key, required this.item, });
@override SchedulerFormsWidgetState createState() => SchedulerFormsWidgetState();
}
class SchedulerFormsWidgetState extends State<SchedulerFormsWidget> {
CheckService check = CheckService();
void save(List<GlobalKey<FormFieldState>> formKeys) {
print("save");
widget.error = null;
widget.errorEndDate = null;
widget.errorCron = null;
if (dash.elements.isEmpty || dash.elements.where((element) => element.element is ProcessingItem).isEmpty) {
dash.error = "You need at least one processing element";
}
var processings = dash.elements.where((element) => element.element is ProcessingItem);
var computes = dash.elements.where((element) => element.element is ComputeItem);
for (var p in processings) {
var links = dash.arrows.where((element) => element.fromID.contains(p.id) || element.toID.contains(p.id));
try {
List<ComputeItem> c = [];
for (var link in links) {
c.addAll(computes.where( (e) => link.toID.contains(e.id) || link.fromID.contains(e.id)).map( (e) => e.element as ComputeItem));
}
if (c.isEmpty) { throw Exception("no compute element linked"); }
} catch (e) {
dash.error = "You need to link each processing element to a compute element";
}
}
if (dash.error != null) {
showAlertBanner( context, () {}, AlertAlertBannerChild(text: dash.error.toString()),// <-- Put any widget here you want!
alertBannerLocation: AlertBannerLocation.bottom,);
setState(() {});
return;
}
for (var k in formKeys) {
if (k.currentState != null) {
if (!k.currentState!.validate()) {
return;
} else { k.currentState!.save();}
}
}
DateTime now = DateTime.now().add(const Duration(minutes: 1));
if (widget.schedule.start == null || widget.schedule.start!.isBefore(now)) {
widget.schedule.start = now.toUtc();
if (widget.schedule.end != null) {
widget.schedule.end = now.add(const Duration(minutes: 1)).toUtc();
}
}
Duration durationBefore = widget.schedule.start!.difference(DateTime.now().toUtc()) + Duration(seconds: 5);
widget._schedulerService.schedule(context, widget.item.id ?? "", widget.schedule.serialize(), {}).then((value) {
setState(() { widget.valid = true; });
Future.delayed(durationBefore, () {
try {
setState(() {});
} catch (e) { /* */ }
});
Future.delayed(const Duration(seconds: 10), () {
try {
setState(() { widget.valid = false; });
} catch (e) { /* */ }
});
});
}
void checkBooking(List<GlobalKey<FormFieldState>> formKeys, void Function(List<GlobalKey<FormFieldState>> )? f){
if (widget.schedule.start == null) {
DateTime now = DateTime.now().add(const Duration(minutes: 5));
widget.schedule.start = now.toUtc();
}
var s = widget.schedule.start!.toUtc().toIso8601String();
var e = "";
if (widget.schedule.end == null) {
e = widget.schedule.start!.add(const Duration(seconds: 5)).toUtc().toIso8601String();
} else {
e = widget.schedule.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!.isAvailable;
if (v.data!.isAvailable) {
if (f != null) { f(formKeys);
} else {
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(() {});
}
);
}
@override Widget build(BuildContext context) {
if (widget.shouldSearch && widget.item.name != "") {
widget.shouldSearch = false;
widget._executionService.search(null, [widget.item.name], {}).then((value) {
if (value.data != null) {
try {
setState(() {
widget.scheduleFutureCount = 0;
widget.scheduleBeforeCount = 0;
for (var exec in value.data!.executions) {
if (exec.startDate != null && DateTime.parse(exec.startDate!).isAfter(DateTime.now().toUtc())) {
widget.scheduleFutureCount++;
} else {
widget.scheduleBeforeCount++;
}
}
});
} catch (e) { /* */ }
}
});
} else {
Future.delayed(const Duration(milliseconds: 100), () {
widget.shouldSearch = true;
});
}
bool isService = true;
try {
widget.item.elements.firstWhere((element) => (element.element?.serialize()["is_service"] ?? false) == true);
} catch (e) {
isService = false;
}
bool readOnly = !PermsService.getPerm(Perms.WORKFLOW_EDIT);
DateTime? start;
DateTime? end;
Duration delayed = const Duration(minutes: 5);
if (widget.schedule.start != null) {
start = widget.schedule.start!;
if (start.isBefore(DateTime.now())) {
start = DateTime.now().add(const Duration(minutes: 5));
widget.schedule.start = start.toUtc();
}
if (start.isBefore(DateTime.now())) {
// get difference between now and start
delayed = start.difference(DateTime.now());
}
}
if (widget.schedule.end != null) {
end = widget.schedule.end!;
if (end.isBefore(DateTime.now())) {
end = DateTime.now().add(const Duration(minutes: 5));
widget.schedule.end = end.toUtc();
}
if (end.isBefore(DateTime.now())) {
// get difference between now and start
delayed = end.difference(DateTime.now());
Future.delayed(delayed, () {
WorkflowFactory.key.currentState?.setState(() { });
});
}
}
List<GlobalKey<FormFieldState>> formKeys = [GlobalKey<FormFieldState>(), GlobalKey<FormFieldState>(), GlobalKey<FormFieldState>(), GlobalKey<FormFieldState>()];
var shallow = ShallowTextInputWidget(
width: 200 - 1, readOnly: readOnly,
current: widget.item.name,
type: CollaborativeAreaType.workflow,
canRemove: PermsService.getPerm(Perms.WORKFLOW_DELETE) ? (p0) => p0 != null && p0.isNotEmpty : null,
remove: PermsService.getPerm(Perms.WORKFLOW_DELETE) ? (p0) async {
showDialog(context: context, builder: (context) {
return ConfirmBoxWidget(
purpose: "Are you sure you want to delete this workflow?",
validate: () async {
await WorflowService().delete(context, widget.item.id ?? "", {}).then((value) {
dash.id = null;
dash.name = "";
dash.isOpened = false;
dash.clear();
dash.chartKey.currentState?.widget.flowChart.setState(() { });
});
},
);
});
} : null,
);
shallow.change =(p0) => Future.delayed( const Duration(seconds: 2), () async {
if (shallow.compare == p0) {
await WorflowService().put(context, widget.item.id ?? "", { "name" : p0 }, {});
} else {
shallow.compare = p0;
}
});
return Column( children: [
Container( padding: const EdgeInsets.all(10), width: 200, height: 60,
decoration: const BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey, width: 1))),
child: const Column( mainAxisAlignment: MainAxisAlignment.center, children: [
Text("WORKFLOW INFO", style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), textAlign: TextAlign.center),
Text("<general>", style: TextStyle(fontSize: 12), textAlign: TextAlign.center),
])),
widget.item.name == "" ? Container() : Container(
decoration: BoxDecoration( border: Border(
left: BorderSide(color: midColor, width: 1),
bottom: const BorderSide(color: Colors.grey))),
child: shallow ),
const SizedBox(height: 20, width: 200 ),
isService ? Text("Warning a processing is a service, if no end execution it will run forever.") : Container(),
Tooltip( message: "start executions",
child: Container( height: 40, margin: const EdgeInsets.only(top: 5),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
child: DateTimeField( key: formKeys[1],
enabled: !readOnly,
resetIcon: null,
onShowPicker: (context, currentValue) async {
var date = await showDatePicker(
builder: (BuildContext context, Widget? child) {
Widget w = Theme(
data: ThemeData(
cardTheme: CardTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
dialogTheme: DialogTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
colorScheme: ColorScheme.light(
background: midColor,
tertiary: Colors.grey,
secondary: Colors.grey,
primary: Colors.black),
),
child: child ?? Container(),
);
return w;
},
context: context,
firstDate: DateTime.now().add(const Duration(minutes: 5)),
initialDate: DateTime.parse( start?.toLocal().toIso8601String()
?? currentValue?.toIso8601String()
?? DateTime.now().add(const Duration(minutes: 5)).toUtc().toIso8601String()).toLocal(),
lastDate: DateTime(2100)
);
if (date != null) {
TimeOfDay? time;
var count = 0;
while(date!.microsecondsSinceEpoch <= (DateTime.now().microsecondsSinceEpoch) || time == null) {
if (count > 0) {
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),
builder: (BuildContext context, Widget? child) {
Widget w = Theme(
data: ThemeData(
cardTheme: CardTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
dialogTheme: DialogTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
colorScheme: ColorScheme.light(
background: midColor,
tertiary: Colors.grey,
secondary: Colors.grey,
primary: Colors.black),
),
child: child ?? Container(),
);
return w;
},
);
if (time == null) { return DateTime.now().add( const Duration(minutes: 1)); }
count++;
date = DateTime(date.year, date.month, date.day, time.hour, time.minute);
widget.schedule.start = date.toUtc();
}
widget.item.saveDash(widget.item.id, context);
}
return date;
},
format: intl.DateFormat('y-M-dd HH:mm:ss'),
initialValue: start?.toLocal() ?? DateTime.now(),
onChanged: (value) { },
validator: (value) {
return value == null ? "not empty" : null;
},
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.always,
fillColor: Colors.white,
filled: true,
alignLabelWithHint: false,
hintText: "enter start executions...",
labelText: "start executions*",
hintStyle: TextStyle(fontSize: 10),
labelStyle: TextStyle(fontSize: 10),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
))),
Tooltip( message: "end executions",
child: Container( height: 40, margin: const EdgeInsets.only(top: 5),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
child: DateTimeField( key: formKeys[2],
enabled: !readOnly,
validator: (value) {
if (value == null && widget.schedule.cron != null && widget.schedule.cron!.isNotEmpty) {
setState(() {
widget.errorEndDate = 'missing start date';
});
}
return value == null && widget.schedule.cron != null && widget.schedule.cron!.isNotEmpty ? "not empty" : null;
},
onChanged: (value) {
if (value == null) {
widget.schedule.end = null;
}
},
resetIcon: const Icon(Icons.close, size: 15),
onShowPicker: (context, currentValue) async {
var date = await showDatePicker(
builder: (BuildContext context, Widget? child) {
Widget w = Theme(
data: ThemeData(
cardTheme: CardTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
dialogTheme: DialogTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
colorScheme: ColorScheme.light(
background: midColor,
tertiary: Colors.grey,
secondary: Colors.grey,
primary: Colors.black),
),
child: child ?? Container(),
);
return w;
},
context: context,
firstDate: DateTime.now().add(const Duration(minutes: 5)),
initialDate: DateTime.parse( end?.toLocal().toIso8601String()
?? currentValue?.toIso8601String()
?? DateTime.now().add(const Duration(minutes: 5)).toUtc().toIso8601String()).toLocal(),
lastDate: DateTime(2100)
);
if (date != null) {
// ignore: use_build_context_synchronously
date = DateTime(date.year, date.month, date.day, date.hour, date.minute);
TimeOfDay? time;
var count = 0;
while(date!.microsecondsSinceEpoch <= (DateTime.now().microsecondsSinceEpoch)
|| time == null
|| (date.microsecondsSinceEpoch) <= (widget.schedule.start ?? DateTime.now()).microsecondsSinceEpoch) {
if (count > 0) {
showAlertBanner( context, () {}, // ignore: use_build_context_synchronously
const AlertAlertBannerChild(text: "must be at least 1 minute from now to let system check info && upper starting date"),// <-- Put any widget here you want!
alertBannerLocation: AlertBannerLocation.bottom,);
}
// ignore: use_build_context_synchronously
time = await showTimePicker(context: context,
initialTime: TimeOfDay(hour: date.hour, minute: date.minute),
builder: (BuildContext context, Widget? child) {
Widget w = Theme(
data: ThemeData(
cardTheme: CardTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
dialogTheme: DialogTheme(elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0))),
colorScheme: ColorScheme.light(
background: midColor,
tertiary: Colors.grey,
secondary: Colors.grey,
primary: Colors.black),
),
child: child ?? Container(),
);
return w;
},
);
if (time == null) { return null; }
count++;
date = DateTime(date.year, date.month, date.day, time.hour, time.minute);
widget.schedule.end = date.toUtc();
}
widget.item.saveDash(widget.item.id, context);
}
return date;
},
format: intl.DateFormat('y-M-dd HH:mm:ss'),
initialValue: end?.toLocal(),
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
fillColor: Colors.white,
floatingLabelBehavior: FloatingLabelBehavior.always,
filled: true,
alignLabelWithHint: false,
hintText: "enter end executions...",
labelText: "end executions",
hintStyle: const TextStyle(fontSize: 10),
labelStyle: const TextStyle(fontSize: 10),
focusedErrorBorder: OutlineInputBorder(borderSide: BorderSide(color: widget.errorEndDate != null ? Colors.red : Colors.black)),
errorBorder: OutlineInputBorder(borderSide: BorderSide(color: widget.errorEndDate != null ? Colors.red : Colors.black)),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: widget.errorEndDate != null ? Colors.red : Colors.black)),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: widget.errorEndDate != null ? Colors.red : Colors.grey)),
border: OutlineInputBorder(borderSide: BorderSide(color: widget.errorEndDate != null ? Colors.red : Colors.grey)),
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
))),
Tooltip( message: "cron command",
child: Container( height: 40, margin: const EdgeInsets.only(top: 5),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
child: TextFormField( key: formKeys[3],
enabled: !readOnly,
initialValue: widget.schedule.cron,
onChanged: (value) {
Future.delayed(const Duration(seconds: 100), () {
if (widget.schedule.cron == value) {
widget.item.saveDash(widget.item.id, context);
}
});
widget.schedule.cron = value;
},
onSaved: (value) {
if (value != null) {
widget.schedule.cron = value;
}
},
validator: (value) {
var cron = Cron();
if (value != null && value.isNotEmpty) {
try {
cron.schedule(Schedule.parse(value), () {});
} catch (e) {
setState(() {
widget.errorCron = e.toString();
});
return "invalid cron";
}
}
return null;
},
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
floatingLabelBehavior: FloatingLabelBehavior.always,
fillColor: Colors.white,
filled: true,
hintText: "enter cron command...",
labelText: "cron",
errorStyle: TextStyle(height: 0),
hintStyle: TextStyle(fontSize: 10),
labelStyle: TextStyle(fontSize: 10),
focusedErrorBorder: OutlineInputBorder(borderSide: BorderSide(color: widget.errorCron != null ? Colors.red : Colors.black)),
errorBorder: OutlineInputBorder(borderSide: BorderSide(color: widget.errorCron != null ? Colors.red : Colors.black)),
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: widget.errorCron != null ? Colors.red : Colors.black)),
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: widget.errorCron != null ? Colors.red : Colors.grey)),
border: OutlineInputBorder(borderSide: BorderSide(color: widget.errorCron != null ? Colors.red : Colors.grey)),
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
))),
Container(
width: 200,
height: 20,
decoration: BoxDecoration(border: Border(bottom: BorderSide(color:
PermsService.getPerm(Perms.WORKFLOW_BOOKING)? Colors.grey : Colors.transparent, width: 1))),
),
const SizedBox(
width: 200,
height: 10,
),
PermsService.getPerm(Perms.WORKFLOW_BOOKING)? Tooltip( message: "check booking", child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () { PermsService.getPerm(Perms.WORKFLOW_BOOKING) ? checkBooking(formKeys, null) : null;
}, child: Container( margin: const EdgeInsets.only(bottom: 5, left: 10, right: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5),
border: Border.all(color: widget.booking == null ? (
PermsService.getPerm(Perms.WORKFLOW_BOOKING) && PermsService.getPerm(Perms.WORKFLOW_EDIT) ? Colors.black : Colors.grey) : (
widget.booking == true ? Colors.green : redColor), width: 1)),
width: 200, height: 30,
child: Icon( Icons.verified_outlined,
color: widget.booking == null ? Colors.black : (widget.booking == true ? Colors.green : redColor)),
))
): Container(),
PermsService.getPerm(Perms.WORKFLOW_BOOKING) ? Tooltip( message: "book", child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () {
setState(() { save(formKeys); });
}, child: Container( margin: const EdgeInsets.only(top: 5, bottom: 10, left: 10, right: 10),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5),
border: Border.all(color: dash.error != null ? Colors.red : ( PermsService.getPerm(Perms.WORKFLOW_BOOKING) ?(widget.valid ? Colors.green : Colors.black) : Colors.grey ))),
width: 200, height: 30,
child: Icon(Icons.schedule_send, color: dash.error != null ? Colors.red : (widget.valid ? Colors.green : Colors.black)),
))
) : Container(),
Column( children: [
Container(
height: 15, width: 200,
decoration: const BoxDecoration(border: Border(top: BorderSide(color: Colors.grey))),
),
Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 10),
child:Text( textAlign: TextAlign.center, overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 13, color: Colors.grey ),
"Was booked ${widget.scheduleBeforeCount} times.")),
Container( alignment: Alignment.center, padding: const EdgeInsets.only(left: 10, right: 10, bottom: 15),
child:Text( textAlign: TextAlign.center, overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 13, color: Colors.grey ),
"Is booked ${widget.scheduleFutureCount} times.")),
]),
widget.item.info["shared"] != null && (widget.item.info["shared"] as List<dynamic>).isNotEmpty ? Column( children: [
Container(
height: 30,
width: 200,
decoration: const BoxDecoration(border: Border(top: BorderSide(color: Colors.grey))),
),
Container( alignment: Alignment.center,
child:Text( overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 14, color: Colors.black, fontWeight: FontWeight.bold),
"Is shared ${(widget.item.info["shared"] as List<dynamic>).length} time(s).")),
...(widget.item.info["shared"] as List<dynamic>).where( (e) => CollaborativeAreaLocal.getCollaborativeArea(e) != null
).map((e) {
var sw = CollaborativeAreaLocal.getCollaborativeArea(e);
return Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: Row( children: [
const Padding(padding: EdgeInsets.only( left: 10, right: 10), child: Icon(Icons.work, color: Colors.grey, size: 15)),
Text(style: const TextStyle(fontSize: 12, color: Colors.grey),
"Workspace: ${sw != null && sw.name != null ?
"${sw.name!.substring(0, sw.name!.length > 15 ? 12 : sw.name!.length)}${sw.name!.length > 15 ? "..." : ""}" : ""}") ]));
},)
]) : Container()
]);
}
}

View File

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

View File

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

View File

@@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:oc_front/models/resources/processing.dart';
import 'package:flutter_flow_chart/flutter_flow_chart.dart';
import 'package:oc_front/widgets/inputs/sub_text_input.dart';
// ignore: must_be_immutable
class SubExposeFormsWidget extends StatefulWidget {
bool readOnly;
Expose item;
Dashboard dash;
String elementID = "";
double width = 180;
bool empty = false;
SubExposeFormsWidget ({ super.key, required this.dash, this.readOnly = false,
this.empty = false, required this.item, required this.elementID, required this.width });
@override SubExposeFormsWidgetState createState() => SubExposeFormsWidgetState();
}
class SubExposeFormsWidgetState extends State<SubExposeFormsWidget> {
@override Widget build(BuildContext context) {
return Column( children : [
Container( margin: const EdgeInsets.only(left: 10, right: 10, top: 5),
decoration: BoxDecoration(border: Border.all(color: Colors.grey, width: 1)),
width: 180
),
SubTextInputWidget(subkey: "reference port", readOnly: widget.readOnly,
initialValue: widget.item.port != null ? '${widget.item.port}' : null,
width: widget.width, empty: widget.empty, change: (value) {
try {
widget.item.port = int.parse(value);
Future.delayed(const Duration(seconds: 2), () {
if (widget.item.port == int.parse(value) && int.parse(value) != 0) {
widget.dash.saveDash(widget.dash.id, context);
}
});
} catch (e) { widget.item.port = null; }
var el = widget.dash.getElement(widget.elementID);
el!.element = widget.item as dynamic;
}),
SubTextInputWidget(
readOnly: widget.readOnly,
subkey: "PAT", initialValue: widget.item.PAT != null ? '${widget.item.PAT}' : null,
width: widget.width, empty: widget.empty, change: (value) {
try {
widget.item.PAT = int.parse(value);
Future.delayed(const Duration(seconds: 2), () {
if (widget.item.PAT == int.parse(value) && int.parse(value) != 0) {
widget.dash.saveDash(widget.dash.id, context);
}
});
} catch (e) { widget.item.PAT = null; }
var el = widget.dash.getElement(widget.elementID);
el!.element = widget.item as dynamic;
}),
SubTextInputWidget(subkey: "reverse path", initialValue: widget.item.PAT != null ? '${widget.item.PAT}' : null,
readOnly: widget.readOnly,
width: widget.width, empty: widget.empty, change: (value) {
try {
widget.item.path = value;
Future.delayed(const Duration(seconds: 2), () {
if (widget.item.path == value) { widget.dash.saveDash(widget.dash.id, context); }
});
} catch (e) { widget.item.path = null; }
var el = widget.dash.getElement(widget.elementID);
el!.element = widget.item as dynamic;
}),
]);
}
}

View File

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

View File

@@ -0,0 +1,130 @@
import 'package:flutter/material.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/pages/shared.dart';
class ShallowDropdownInputWidget extends StatefulWidget {
double? width;
double? height;
CollaborativeAreaType type = CollaborativeAreaType.workspace;
Future<List<Shallow>> Function()? all;
Future<void> Function(String)? load;
Future<void> Function(String)? remove;
bool Function(String?)? canLoad;
bool Function(String?)? canRemove;
void Function(String?)? change;
DropdownMenuItem Function(Shallow)? maptoDropdown;
String? current;
Widget? prefixIcon;
IconData? iconLoad;
IconData? iconRemove;
String? label;
String? hint;
String? tooltipLoad;
String? tooltipRemove;
Color? filled;
Color? hintColor;
Color? color;
bool deletion = false;
ShallowDropdownInputWidget ({ Key? key, this.width, this.current, required this.all, this.prefixIcon,
this.iconLoad, this.iconRemove, this.hint, this.filled, this.hintColor, this.color, this.height,
this.tooltipLoad, this.tooltipRemove, this.deletion = false, this.maptoDropdown, this.label,
required this.type, this.canLoad, this.canRemove, this.load, this.remove, this.change }): super(key: key);
@override ShallowDropdownInputWidgetState createState() => ShallowDropdownInputWidgetState();
}
class ShallowDropdownInputWidgetState extends State<ShallowDropdownInputWidget> {
@override Widget build(BuildContext context) {
if (widget.deletion && widget.remove == null) {
widget.remove =(p0) async => widget.current = null;
}
var t = widget.type == CollaborativeAreaType.workspace ? "workspace" : (widget.type == CollaborativeAreaType.workflow ? "workflow" : "peer");
return FutureBuilder(future: widget.all!(), builder: (b, s) {
List<DropdownMenuItem> items = [];
if (widget.maptoDropdown != null) {
items = s.data?.map((e) => widget.maptoDropdown!(e)).toList() ?? <DropdownMenuItem>[];
} else {
items = s.data?.map((e) => DropdownMenuItem(value: e.id,child: Text(e.name),)).toList() ?? <DropdownMenuItem>[];
}
return Row( children: [
Tooltip( message: widget.hint ?? "current $t", child:
Theme(
data: Theme.of(context).copyWith(
canvasColor: widget.filled ??Colors.white,
),
child: Container( height: widget.height ?? 50,
width: (widget.width ?? getMainWidth(context)) - (widget.load == null ? 0 : 50) - (widget.remove == null ? 0 : 50),
decoration: const BoxDecoration(
color: Colors.white,
),
child: Center(child: DropdownButtonFormField(
onChanged: (value) {
setState(() {
widget.current = value;
if (widget.change != null) {
widget.change!(value);
}
});
},
value: widget.current != null && !items.map( ((e) => e.value)).contains(widget.current) ? null : widget.current,
isExpanded: true,
style: TextStyle(color: widget.color ?? Colors.black, fontSize: 15),
hint: Text(widget.hint ?? "load $t...",
style: TextStyle(color: widget.hintColor ??midColor, fontSize: 14)),
icon: Icon( // Add this
Icons.arrow_drop_down, // Add this
color: widget.hintColor ?? Colors.grey , // Add thisprefixIcon
),
decoration: InputDecoration(
filled: true,
label: widget.label == null ? null : Text(widget.label!),
alignLabelWithHint: false,
prefixIconColor: Colors.grey,
prefixIcon: widget.prefixIcon,
suffixIconColor: widget.hintColor ?? Colors.grey ,
focusedBorder: InputBorder.none,
fillColor: widget.filled ??Colors.white,
enabledBorder: InputBorder.none,
border: InputBorder.none,
contentPadding: EdgeInsets.only(left: (widget.width ?? 400) < 200 ? 0 : 30, right: (widget.width ?? 400) < 200 ? 0 : 30, top: 18, bottom: 18),
),
items: items,
))))),
widget.load == null ? Container() : Tooltip(
message: widget.tooltipLoad ?? "load $t",
child:InkWell(
mouseCursor: SystemMouseCursors.click,
onTap: () async {
if (widget.canLoad == null || !widget.canLoad!(widget.current)
|| widget.load == null || widget.current == null) {
return;
}
await widget.load!(widget.current!);
setState(() { });
},
child: Container( width: 50,height: 50,
color: Colors.black,
child: Icon( widget.iconLoad ?? Icons.add, color: widget.current == null || widget.canLoad == null || !widget.canLoad!(widget.current) ? Colors.grey : Colors.white)
)
)
),
widget.remove == null ? Container() : Tooltip(
message: widget.tooltipRemove ?? "remove $t",
child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () async {
if (widget.canRemove == null || !widget.canRemove!(widget.current) || widget.remove == null || widget.current == null) {
return;
}
await widget.remove!(widget.current!);
setState(() { });
},
child: Container( width: 50,height: 50,
decoration: BoxDecoration(color: Colors.black, border: Border(left: BorderSide(color: midColor))),
child: Icon(widget.iconRemove ?? Icons.delete, color: widget.current == null || widget.canRemove == null || !widget.canRemove!(widget.current) ? Colors.grey : Colors.white)) )
),
]); });
}
}

View File

@@ -0,0 +1,142 @@
import 'package:flutter/material.dart';
import 'package:oc_front/main.dart';
import 'package:oc_front/pages/shared.dart';
class ShallowTextInputWidget extends StatefulWidget {
double? width;
CollaborativeAreaType type = CollaborativeAreaType.workspace;
Future<void> Function(Map<String,dynamic>)? load;
Future<void> Function(String)? loadStr;
Future<void> Function(String)? remove;
bool Function(String?)? canLoad;
bool Function(String?)? canRemove;
void Function(String?)? change;
String? current;
String? compare;
bool readOnly;
IconData? iconLoad;
IconData? iconRemove;
MainAxisAlignment alignment = MainAxisAlignment.start;
String? hint;
String? tooltipLoad;
String? tooltipRemove;
Color? filled;
Color? hintColor;
Color? color;
List<ShallowTextInputWidget> forms = [];
String? attr;
ShallowTextInputWidget ({ Key? key, this.width, this.current, this.attr, this.readOnly = false, this.alignment = MainAxisAlignment.start,
this.iconLoad, this.iconRemove, this.hint, this.forms = const [], this.loadStr,
this.tooltipLoad = "", this.tooltipRemove = "",
this.filled, this.hintColor, this.color,
required this.type, this.canLoad, this.canRemove, this.load, this.remove, this.change }): super(key: key);
@override ShallowTextInputWidgetState createState() => ShallowTextInputWidgetState();
Map<String, dynamic> serialize() {
Map<String, dynamic> map = { "name" : current };
for (var form in forms) {
if (form.attr == null) {
continue;
}
map[form.attr!] = form.current;
}
return map;
}
bool validate() {
return canLoad == null ? true : canLoad!(current);
}
}
class ShallowTextInputWidgetState extends State<ShallowTextInputWidget> {
TextEditingController ctrl = TextEditingController();
bool validForms() {
for (var form in widget.forms) {
if (!form.validate()) {
return false;
}
}
return true;
}
@override Widget build(BuildContext context) {
var t = widget.type == CollaborativeAreaType.workspace ? "workspace" : (widget.type == CollaborativeAreaType.workflow ? "workflow" : "peer");
ctrl.text = widget.current ?? "";
return Row( mainAxisAlignment: widget.alignment, children: [
Tooltip( message: widget.hint ?? "current $t", child:
Theme(
data: Theme.of(context).copyWith(
canvasColor: midColor,
),
child: Container( height: 50, width: (widget.width ?? getMainWidth(context)) - (widget.load == null ? 0 : 50) - (widget.remove == null ? 0 : 50),
decoration: const BoxDecoration( color: Colors.white ),
child: TextFormField(
enabled: !widget.readOnly,
onChanged: (value) {
setState(() {
widget.current = value;
if (widget.change != null) {
widget.change!(value);
}
});
},
controller: ctrl,
style: TextStyle(color: widget.color ?? Colors.black, fontSize: 15),
decoration: InputDecoration(
filled: true,
border: InputBorder.none,
focusedBorder: InputBorder.none,
disabledBorder: InputBorder.none,
enabledBorder: InputBorder.none,
fillColor: widget.filled ?? Colors.white,
hintText: widget.hint ?? "enter $t ${widget.attr ?? "name"}...",
contentPadding: const EdgeInsets.only(left: 30, right: 30, top: 18, bottom: 18),
hintStyle: TextStyle(color: widget.hintColor ??const Color.fromARGB(255, 85, 44, 44), fontSize: 14)),
),
))),
widget.load == null && widget.loadStr == null ? Container() : Tooltip(
message: widget.tooltipLoad ?? "add $t",
child:InkWell(
mouseCursor: SystemMouseCursors.click,
onTap: () async {
if (widget.canLoad == null || !widget.canLoad!(widget.current) || !validForms()
|| (widget.load == null && widget.loadStr == null) || widget.current == null) {
return;
}
if (widget.loadStr != null) {
await widget.loadStr!(widget.current!);
} else {
await widget.load!(widget.serialize());
}
setState(() { });
},
child: Container( width: 50,height: 50,
color: Colors.black,
child: Icon( widget.iconLoad ?? Icons.add, color: widget.current == null || !validForms()
|| widget.canLoad == null || !widget.canLoad!(widget.current) ? Colors.grey : Colors.white)
)
)
),
widget.remove == null ? Container() : Tooltip(
message: widget.tooltipRemove ?? "refresh entry",
child: InkWell( mouseCursor: SystemMouseCursors.click,
onTap: () async {
if (widget.canRemove == null || !widget.canRemove!(widget.current) || widget.remove == null || widget.current == null) {
return;
}
await widget.remove!(widget.current!);
setState(() { });
},
child: Container( width: 50,height: 50,
decoration: BoxDecoration(color: Colors.black, border: Border(left: BorderSide(color: midColor))),
child: Icon(widget.iconRemove ?? Icons.delete, color: widget.current == null || widget.canRemove == null || !widget.canRemove!(widget.current) ? Colors.grey : Colors.white)) )
),
]);
}
}

View File

@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
class SubDropdownInputWidget extends StatefulWidget {
String subkey;
double width;
bool empty;
String? initialValue;
List<DropdownMenuItem<String>> dropdownMenuEntries = [];
void Function(String?)? change = (value) {};
SubDropdownInputWidget ({ super.key, required this.dropdownMenuEntries, this.initialValue,
required this.subkey, required this.width, required this.empty, required this.change });
@override SubDropdownInputWidgetState createState() => SubDropdownInputWidgetState();
}
class SubDropdownInputWidgetState extends State<SubDropdownInputWidget> {
@override Widget build(BuildContext context) {
return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Tooltip( message: widget.subkey,
child: Container( margin: EdgeInsets.only(top: widget.empty ? 0 : 15),
width: widget.width, height: 30,
child: DropdownButtonFormField(
isExpanded: true,
items: widget.dropdownMenuEntries,
value: widget.initialValue,
onChanged: widget.change,
style: const TextStyle(fontSize: 12,color: Colors.black, overflow: TextOverflow.ellipsis),
decoration: InputDecoration(
hintText: "select ${widget.subkey}...",
fillColor: Colors.white,
filled: true,
labelText: widget.subkey,
alignLabelWithHint: false,
errorStyle: const TextStyle(fontSize: 0),
hintStyle: const TextStyle(fontSize: 10),
labelStyle: const TextStyle(fontSize: 10),
floatingLabelBehavior: FloatingLabelBehavior.always,
enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
border: const OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
))),
]);
}
}

View File

@@ -0,0 +1,62 @@
import 'package:alert_banner/exports.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:oc_front/widgets/dialog/alert.dart';
class SubTextInputWidget extends StatefulWidget {
String subkey;
String? initialValue;
double width;
bool empty;
bool noLabel;
bool readOnly = false;
bool copyLabel = false;
void Function(String) change = (value) {};
SubTextInputWidget ({ Key? key,
required this.subkey, this.readOnly = false, this.noLabel = false, this.copyLabel = false,
this.initialValue, required this.width, required this.empty, required this.change }):
super(key: key);
@override SubTextInputWidgetState createState() => SubTextInputWidgetState();
}
class SubTextInputWidgetState extends State<SubTextInputWidget> {
@override Widget build(BuildContext context) {
if (widget.readOnly && widget.initialValue == null) {
return Container();
}
return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Tooltip( message: widget.subkey,
child: Container( margin: EdgeInsets.only(top: widget.empty ? 0 : 15),
width: widget.width - (widget.readOnly ? 40 : 0), height: 30,
child: TextFormField( textAlign: TextAlign.start,
enabled: !widget.readOnly,
initialValue: widget.initialValue,
onChanged: widget.change,
style: const TextStyle(fontSize: 12),
decoration: InputDecoration(
hintText: "enter ${widget.subkey}...",
fillColor: Colors.white,
filled: true,
labelText: widget.noLabel ? "" : widget.subkey,
alignLabelWithHint: false,
errorStyle: const TextStyle(fontSize: 0),
hintStyle: const TextStyle(fontSize: 10),
labelStyle: const TextStyle(fontSize: 12),
floatingLabelBehavior: FloatingLabelBehavior.always,
enabledBorder: const OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
border: const OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
))),
widget.readOnly ? InkWell( onTap: () {
Clipboard.setData(ClipboardData(text: widget.copyLabel ? "\$${widget.subkey}" : widget.initialValue!));
showAlertBanner(context, () {}, const InfoAlertBannerChild(text: "successfully add to clipboard"), // <-- Put any widget here you want!
alertBannerLocation: AlertBannerLocation.bottom,);
}, child: Container( margin: EdgeInsets.only(left: 5, top: widget.empty ? 0 : 15),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(5), border: Border.all(color: Colors.grey, width: 1)),
width: 35, height: 30,
child: const Row( mainAxisAlignment: MainAxisAlignment.center,
children: [ Icon(Icons.copy, color: Colors.black, size: 15,) ]),
)) : Container()
]);
}
}

View File

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

View File

@@ -1,8 +1,12 @@
import 'package:oc_front/main.dart';
import 'package:flutter/material.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/widgets/items/items_details/data_item.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/models/response.dart';
import 'package:oc_front/pages/shared.dart';
import 'package:oc_front/widgets/inputs/shallow_dropdown_input.dart';
import 'package:oc_front/widgets/inputs/sub_dropdown_input%20.dart';
// ignore: must_be_immutable
class ItemWidget extends StatefulWidget {
AbstractItem item;
ItemWidget ({ super.key, required this.item });
@@ -10,28 +14,88 @@ class ItemWidget extends StatefulWidget {
}
class ItemWidgetState extends State<ItemWidget> {
@override Widget build(BuildContext context) {
Widget w = Container();
/* if (isData(widget.item.topic)) { w = DataItemWidget(item: widget.item as DataItem); }
else if (isComputing(widget.item.topic)) { w = DataItemWidget(item: widget.item as DataItem); }
else if (isDataCenter(widget.item.topic)) { w = DataItemWidget(item: widget.item as DataItem); }
else if (isStorage(widget.item.topic)) { w = DataItemWidget(item: widget.item as DataItem); } */
return Container(
height: MediaQuery.of(context).size.height - 300,
List<Widget> widgets = [
Container( margin: EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(border: Border(bottom: BorderSide(color: midColor))),
width: getMainWidth(context) / 2,
child: Center(child: Padding( padding: EdgeInsets.only(bottom: 20),
child: Text("RESOURCE INFORMATIONS", style: TextStyle(fontSize: 18, color: Colors.grey, fontWeight: FontWeight.w500)))))
];
var infos = widget.item.infos();
var count = 0;
for (var info in infos.keys) {
count++;
widgets.add(Padding( padding: EdgeInsets.symmetric(vertical: 5), child : Row(children: [
Padding( padding: EdgeInsets.only(left: 50, right: 10), child : Text("${info.toUpperCase().replaceAll("_", " ")} :", style: TextStyle(fontSize: 15, color: Colors.grey))),
Text("${infos[info] is bool ? (infos[info] == true ? "yes" : "no") : infos[info] ?? "unknown"}",
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w500))
])));
}
if (count == 0 ) {
widgets.add(Center(child: Padding( padding: EdgeInsets.symmetric(vertical: 5), child : Row(children: [
Padding( padding: EdgeInsets.only(left: 50, right: 10), child : Text("NO INFORMATION", style: TextStyle(fontSize: 15, color: Colors.grey))),
]))));
}
List<Widget> widgetsInstance = [];
List<Shallow> dpItems = [];
for (var (i, instance) in widget.item.instances.indexed) {
dpItems.add(Shallow(id: "$i", name: instance.name ?? ""));
}
if (dpItems.isNotEmpty) {
widgetsInstance.add(Center( child: Padding(padding: EdgeInsets.only(bottom: 15), child:
ShallowDropdownInputWidget( all: () async => dpItems, width: (getWidth(context) / 2) - 25,
label: "instances", type: CollaborativeAreaType.resource, height: 65,
current: "${widget.item.selectedInstance}", change: (value) {
if (value != null) { setState(() { widget.item.selectedInstance = int.parse(value); }); }
},
))
));
}
if (widget.item.instances.length > widget.item.selectedInstance) {
var instance = widget.item.instances[widget.item.selectedInstance];
widgetsInstance.add(Container(height: 20, width: getWidth(context) / 2,));
var count = 0;
for (var info in instance.infos().keys) {
if (instance.infos()[info] == null || instance.infos()[info] is List || instance.infos()[info] is Map) { continue; }
count++;
widgetsInstance.add(Center(child: Padding( padding: EdgeInsets.symmetric(vertical: 5), child : Row(children: [
Padding( padding: EdgeInsets.only(left: 50, right: 10), child : Text("${info.toUpperCase().replaceAll("_", " ")} :", style: TextStyle(fontSize: 15, color: Colors.grey))),
Text("${instance.infos()[info] is bool ? (instance.infos()[info] == true ? "yes" : "no") : instance.infos()[info] ?? "unknown"}",
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 15, color: Colors.black, fontWeight: FontWeight.w500))
]))));
}
if (count == 0 ) {
widgetsInstance.add(Padding( padding: EdgeInsets.symmetric(vertical: 5), child : Row(children: [
Padding( padding: EdgeInsets.only(left: 50, right: 10), child : Text("NO INSTANCE INFORMATION", style: TextStyle(fontSize: 15, color: Colors.grey))),
])));
}
}
Widget w = Column( mainAxisSize: MainAxisSize.max, children: widgets );
Widget w2 = Column( mainAxisSize: MainAxisSize.max, children: widgetsInstance );
return SizedBox(
height: getHeight(context) - 200,
child: SingleChildScrollView(
child: Column( children: [
child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [
widget.item.description == null ? Container() : Container(
width: MediaQuery.of(context).size.width,
width: getMainWidth(context),
alignment: Alignment.center,
decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Colors.grey.shade300))),
padding: const EdgeInsets.all(30),
child: Text(widget.item.description!,
height: 100,
decoration: BoxDecoration(
color: Colors.white,
border: Border(bottom: BorderSide(color: midColor))),
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 50),
child: Text(widget.item.description!.length > 350 ? "${widget.item.description!.substring(0, 347)}..." : widget.item.description!,
style: TextStyle(fontSize: 15, color: Colors.grey, fontWeight: FontWeight.w500))),
Container(padding: const EdgeInsets.all(30),
color: Colors.grey.shade300,
width: MediaQuery.of(context).size.width / 2,
child: w
)
Row( children: [
Container(padding: const EdgeInsets.symmetric(vertical: 20),
height: getHeight(context) - 300,
decoration: BoxDecoration(border: Border(right: BorderSide(color: midColor))),
alignment: Alignment.topLeft, width: getMainWidth(context) / 2, child: w),
Container(height: getHeight(context) - 300,
alignment: Alignment.topRight, width: getMainWidth(context) / 2, child: w2)
])
]
)
)

View File

@@ -1,8 +1,8 @@
import 'dart:convert';
import 'package:oc_front/main.dart';
import 'package:flutter/material.dart';
import 'package:oc_front/models/search.dart';
import 'package:oc_front/core/models/workspace_local.dart';
import 'package:oc_front/core/services/router.dart';
import 'package:oc_front/models/resources/resources.dart';
import 'package:oc_front/core/models/workspace_local.dart';
const List<GlobalKey<State>> _empty = [];
// ignore: must_be_immutable
@@ -18,26 +18,28 @@ class ItemRowWidget extends StatefulWidget {
}
class ItemRowWidgetState extends State<ItemRowWidget> {
@override Widget build(BuildContext context) {
double imageSize = MediaQuery.of(context).size.width != widget.contextWidth ? 0 : 80;
var ratio = MediaQuery.of(context).size.width != widget.contextWidth ? 0.5 : 1; // 2;
double imageSize = widget.contextWidth <= 400 ? 0 : 80;
var ratio = getMainWidth(context) != widget.contextWidth ? 0.5 : 1; // 2;
var itemWidth = (((widget.contextWidth - imageSize) / 3) - 80) / ratio;
itemWidth = itemWidth > 100 ? 100 : ( itemWidth < 40 ? 40 : itemWidth );
var endWidth = (itemWidth * ratio) + 80;
Image? image;
if (widget.item.logo != null) {
image = Image.memory(base64Decode(widget.item.logo ?? ""), width: imageSize, height: imageSize);
image = Image.network(widget.item.logo ?? "", width: imageSize, height: imageSize);
}
Widget w = Container(
width: widget.contextWidth,
height: 100,
decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.grey.shade300)) ),
padding: EdgeInsets.only(left: imageSize == 0 ? 20 : 0),
decoration: BoxDecoration( border: Border(bottom: BorderSide(color: midColor)) ),
child: Row( children: [
widget.low ? Container( padding: 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',
height: imageSize, width: imageSize)),
Container(
SizedBox(
width: widget.low ? widget.contextWidth - 20 : widget.contextWidth - (imageSize + 20) - endWidth,
child: Padding(padding: widget.contextWidth != MediaQuery.of(context).size.width ?
child: Padding(padding: widget.contextWidth != getMainWidth(context) ?
const EdgeInsets.symmetric(horizontal: 10) : const EdgeInsets.symmetric(horizontal: 20),
child: Column(crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center, children: [
@@ -45,26 +47,23 @@ class ItemRowWidgetState extends State<ItemRowWidget> {
widget.low ? Container() : Container(padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
margin: const EdgeInsets.only(right: 20),
decoration: BoxDecoration(
color: isData(widget.item.topic) ? Colors.blue :
isComputing(widget.item.topic) ? Colors.green :
isDataCenter(widget.item.topic) ? Colors.orange :
isStorage(widget.item.topic) ? Colors.red : Colors.grey,
color: getColor(widget.item.topic),
borderRadius: BorderRadius.circular(4),
),
child: Text( MediaQuery.of(context).size.width < 600 ? "" : widget.item.type.toString(),
child: Text( getMainWidth(context) < 600 ? "" : widget.item.topic.toString(),
style: const TextStyle(fontSize: 10, color: Colors.white, fontWeight: FontWeight.w600)),
),
Expanded( child: Text(widget.item.name?.toUpperCase() ?? "",
style: TextStyle(fontSize: widget.low ? 14 : 20, overflow: TextOverflow.ellipsis, fontWeight: FontWeight.w600, color: Color(0xFFF67C0B9))),
)
]),
Text( "From ${widget.item.owner ?? "unknown owner"}",
style: TextStyle(fontSize: 14, color: Colors.grey, overflow: TextOverflow.ellipsis)),
widget.item.owners.isEmpty ? Container() : Text( "From ${widget.item.owners.map( (e) => e.name).join(", ")}",
style: const TextStyle(fontSize: 14, color: Colors.grey, overflow: TextOverflow.ellipsis)),
Text(widget.item.shortDescription ?? "", style: const TextStyle(fontSize: 12, overflow: TextOverflow.ellipsis)),
],)
)
),
widget.low ? Container() : Container(
widget.low ? Container() : SizedBox(
width: endWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
@@ -83,12 +82,13 @@ class ItemRowWidgetState extends State<ItemRowWidget> {
constraints: const BoxConstraints(maxWidth: 80),
width: itemWidth,
decoration: BoxDecoration(
boxShadow: [BoxShadow(color: Colors.grey.shade300, spreadRadius: 1, blurRadius: 1, offset: const Offset(0, 1))],
color: (WorkspaceLocal.hasItem(widget.item) ? Colors.red : const Color.fromRGBO(38, 166, 154, 1)),
boxShadow: [BoxShadow(color: midColor, spreadRadius: 1, blurRadius: 1, offset: const Offset(0, 1))],
color: (WorkspaceLocal.hasItem(widget.item) ? redColor : lightColor ),
borderRadius: BorderRadius.circular(4),
),
child: Icon(WorkspaceLocal.hasItem(widget.item) ? Icons.remove_shopping_cart : Icons.add_shopping_cart,
color: Colors.white, size: 20 ))
color: Colors.white, size: 20 )
)
),
...(ratio > 1 ? [Padding( padding: const EdgeInsets.only(left: 20),
child: InkWell(
@@ -99,8 +99,8 @@ class ItemRowWidgetState extends State<ItemRowWidget> {
constraints: const BoxConstraints(maxWidth: 80, minWidth: 40),
width: itemWidth,
decoration: BoxDecoration(
boxShadow: [BoxShadow(color: Colors.grey.shade300, spreadRadius: 1, blurRadius: 1, offset: const Offset(0, 1))],
color: Colors.grey.shade300,
boxShadow: [BoxShadow(color: midColor, spreadRadius: 1, blurRadius: 1, offset: const Offset(0, 1))],
color: midColor,
borderRadius: BorderRadius.circular(4),
),
child: const Icon(Icons.favorite_border, color: Colors.white, size: 20 ))

Some files were not shown because too many files have changed in this diff Show More