Fares 10 mesi fa
parent
commit
185222916f
48 ha cambiato i file con 1243 aggiunte e 154 eliminazioni
  1. 6 6
      .metadata
  2. 25 0
      .vscode/launch.json
  3. 14 0
      .vscode/sftp.json
  4. 1 2
      android/app/build.gradle
  5. 1 9
      android/app/src/main/AndroidManifest.xml
  6. 4 4
      android/app/src/profile/AndroidManifest.xml
  7. 13 0
      androidold/.gitignore
  8. 45 0
      androidold/app/build.gradle
  9. 7 0
      androidold/app/src/debug/AndroidManifest.xml
  10. 57 0
      androidold/app/src/main/AndroidManifest.xml
  11. 5 0
      androidold/app/src/main/kotlin/com/example/tp5/MainActivity.kt
  12. 12 0
      androidold/app/src/main/res/drawable-v21/launch_background.xml
  13. 12 0
      androidold/app/src/main/res/drawable/launch_background.xml
  14. 0 0
      androidold/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml
  15. BIN
      androidold/app/src/main/res/mipmap-hdpi/ic_launcher.png
  16. 0 0
      androidold/app/src/main/res/mipmap-hdpi/launcher_icon.png
  17. BIN
      androidold/app/src/main/res/mipmap-mdpi/ic_launcher.png
  18. 0 0
      androidold/app/src/main/res/mipmap-mdpi/launcher_icon.png
  19. BIN
      androidold/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  20. 0 0
      androidold/app/src/main/res/mipmap-xhdpi/launcher_icon.png
  21. BIN
      androidold/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  22. 0 0
      androidold/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
  23. BIN
      androidold/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  24. 0 0
      androidold/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
  25. 18 0
      androidold/app/src/main/res/values-night/styles.xml
  26. 18 0
      androidold/app/src/main/res/values/styles.xml
  27. 7 0
      androidold/app/src/profile/AndroidManifest.xml
  28. 18 0
      androidold/build.gradle
  29. 3 0
      androidold/gradle.properties
  30. 5 0
      androidold/gradle/wrapper/gradle-wrapper.properties
  31. 25 0
      androidold/settings.gradle
  32. 2 29
      lib/core/routes.dart
  33. 18 15
      lib/csv/data.dart
  34. 1 1
      lib/fltinfo/view/fltinfo_page.dart
  35. 9 8
      lib/lido/lido_api.dart
  36. 25 7
      lib/lido/view/lido_form.dart
  37. 2 2
      lib/main.dart
  38. 1 1
      lib/roster/api/crewlink_api.dart
  39. 48 0
      lib/settings/settings.dart
  40. 126 6
      lib/settings/settings_page.dart
  41. 5 0
      lib/widgets/nav_drawer.dart
  42. 68 63
      pubspec.lock
  43. 3 1
      pubspec.yaml
  44. 271 0
      serverinit/migrate.sql
  45. 271 0
      tp5/migrate.sql
  46. 17 0
      uploaddb/docker-compose.yml
  47. 80 0
      uploaddb/upload.sh
  48. BIN
      windows/runner/resources/app_icon.ico

+ 6 - 6
.metadata

@@ -4,7 +4,7 @@
 # This file should be version controlled and should not be manually edited.
 
 version:
-  revision: "ee624bc4fd41413cbb89099b0701a42287643d9a"
+  revision: "fc011960a2b3260d49b64823b90fe292440c78ee"
   channel: "beta"
 
 project_type: app
@@ -13,11 +13,11 @@ project_type: app
 migration:
   platforms:
     - platform: root
-      create_revision: ee624bc4fd41413cbb89099b0701a42287643d9a
-      base_revision: ee624bc4fd41413cbb89099b0701a42287643d9a
-    - platform: android
-      create_revision: ee624bc4fd41413cbb89099b0701a42287643d9a
-      base_revision: ee624bc4fd41413cbb89099b0701a42287643d9a
+      create_revision: fc011960a2b3260d49b64823b90fe292440c78ee
+      base_revision: fc011960a2b3260d49b64823b90fe292440c78ee
+    - platform: windows
+      create_revision: fc011960a2b3260d49b64823b90fe292440c78ee
+      base_revision: fc011960a2b3260d49b64823b90fe292440c78ee
 
   # User provided section
 

+ 25 - 0
.vscode/launch.json

@@ -0,0 +1,25 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "tp5",
+            "request": "launch",
+            "type": "dart"
+        },
+        {
+            "name": "tp5 (profile mode)",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "profile"
+        },
+        {
+            "name": "tp5 (release mode)",
+            "request": "launch",
+            "type": "dart",
+            "flutterMode": "release"
+        }
+    ]
+}

+ 14 - 0
.vscode/sftp.json

@@ -0,0 +1,14 @@
+[{
+    "name": "ubuntu",
+    "host": "tarpilot.com",
+    "protocol": "sftp",
+    "port": 22,
+    "username": "ubuntu",
+    "password": "Farrous08",
+    "remotePath": "/home/docks",
+    "uploadOnSave": true,
+    "useTempFile": true,
+    "openSsh": false
+}
+
+]

+ 1 - 2
android/app/build.gradle

@@ -24,8 +24,7 @@ android {
         applicationId = "com.example.tp5"
         // You can update the following values to match your application needs.
         // For more information, see: https://flutter.dev/to/review-gradle-config.
-        // minSdk = flutter.minSdkVersion
-        minSdk = 29
+        minSdk = flutter.minSdkVersion
         targetSdk = flutter.targetSdkVersion
         versionCode = flutter.versionCode
         versionName = flutter.versionName

+ 1 - 9
android/app/src/main/AndroidManifest.xml

@@ -3,12 +3,10 @@
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
     <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
-
-   
     <application
         android:label="tp5"
         android:name="${applicationName}"
-        android:icon="@mipmap/launcher_icon">
+        android:icon="@mipmap/ic_launcher">
         <activity
             android:name=".MainActivity"
             android:exported="true"
@@ -30,12 +28,6 @@
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
-            <intent-filter android:autoVerify="true">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <data android:scheme="com.example.tp5" android:host="magic-link" />
-            </intent-filter>
         </activity>
         <!-- Don't delete the meta-data below.
              This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->

+ 4 - 4
android/app/src/profile/AndroidManifest.xml

@@ -1,7 +1,7 @@
-
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- The INTERNET permission is required for development. Specifically,
+         the Flutter tool needs it to communicate with the running application
+         to allow setting breakpoints, to provide hot reload, etc.
+    -->
     <uses-permission android:name="android.permission.INTERNET"/>
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
-    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
 </manifest>

+ 13 - 0
androidold/.gitignore

@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/to/reference-keystore
+key.properties
+**/*.keystore
+**/*.jks

+ 45 - 0
androidold/app/build.gradle

@@ -0,0 +1,45 @@
+plugins {
+    id "com.android.application"
+    id "kotlin-android"
+    // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+    id "dev.flutter.flutter-gradle-plugin"
+}
+
+android {
+    namespace = "com.example.tp5"
+    compileSdk = flutter.compileSdkVersion
+    ndkVersion = flutter.ndkVersion
+
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_1_8
+        targetCompatibility = JavaVersion.VERSION_1_8
+    }
+
+    kotlinOptions {
+        jvmTarget = JavaVersion.VERSION_1_8
+    }
+
+    defaultConfig {
+        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+        applicationId = "com.example.tp5"
+        // You can update the following values to match your application needs.
+        // For more information, see: https://flutter.dev/to/review-gradle-config.
+        // minSdk = flutter.minSdkVersion
+        minSdk = 33
+        targetSdk = flutter.targetSdkVersion
+        versionCode = flutter.versionCode
+        versionName = flutter.versionName
+    }
+
+    buildTypes {
+        release {
+            // TODO: Add your own signing config for the release build.
+            // Signing with the debug keys for now, so `flutter run --release` works.
+            signingConfig = signingConfigs.debug
+        }
+    }
+}
+
+flutter {
+    source = "../.."
+}

+ 7 - 0
androidold/app/src/debug/AndroidManifest.xml

@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- The INTERNET permission is required for development. Specifically,
+         the Flutter tool needs it to communicate with the running application
+         to allow setting breakpoints, to provide hot reload, etc.
+    -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>

+ 57 - 0
androidold/app/src/main/AndroidManifest.xml

@@ -0,0 +1,57 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
+    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
+
+   
+    <application
+        android:label="tp5"
+        android:name="${applicationName}"
+        android:icon="@mipmap/launcher_icon">
+        <activity
+            android:name=".MainActivity"
+            android:exported="true"
+            android:launchMode="singleTop"
+            android:taskAffinity=""
+            android:theme="@style/LaunchTheme"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+            android:hardwareAccelerated="true"
+            android:windowSoftInputMode="adjustResize">
+            <!-- Specifies an Android theme to apply to this Activity as soon as
+                 the Android process has started. This theme is visible to the user
+                 while the Flutter UI initializes. After that, this theme continues
+                 to determine the Window background behind the Flutter UI. -->
+            <meta-data
+              android:name="io.flutter.embedding.android.NormalTheme"
+              android:resource="@style/NormalTheme"
+              />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+            <intent-filter android:autoVerify="true">
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="com.example.tp5" android:host="magic-link" />
+            </intent-filter>
+        </activity>
+        <!-- Don't delete the meta-data below.
+             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+        <meta-data
+            android:name="flutterEmbedding"
+            android:value="2" />
+    </application>
+    <!-- Required to query activities that can process text, see:
+         https://developer.android.com/training/package-visibility and
+         https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
+
+         In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
+    <queries>
+        <intent>
+            <action android:name="android.intent.action.PROCESS_TEXT"/>
+            <data android:mimeType="text/plain"/>
+        </intent>
+    </queries>
+</manifest>

+ 5 - 0
androidold/app/src/main/kotlin/com/example/tp5/MainActivity.kt

@@ -0,0 +1,5 @@
+package com.example.tp5
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity()

+ 12 - 0
androidold/app/src/main/res/drawable-v21/launch_background.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="?android:colorBackground" />
+
+    <!-- You can insert your own image assets here -->
+    <!-- <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@mipmap/launch_image" />
+    </item> -->
+</layer-list>

+ 12 - 0
androidold/app/src/main/res/drawable/launch_background.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@android:color/white" />
+
+    <!-- You can insert your own image assets here -->
+    <!-- <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@mipmap/launch_image" />
+    </item> -->
+</layer-list>

+ 0 - 0
android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml → androidold/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml


BIN
androidold/app/src/main/res/mipmap-hdpi/ic_launcher.png


+ 0 - 0
android/app/src/main/res/mipmap-hdpi/launcher_icon.png → androidold/app/src/main/res/mipmap-hdpi/launcher_icon.png


BIN
androidold/app/src/main/res/mipmap-mdpi/ic_launcher.png


+ 0 - 0
android/app/src/main/res/mipmap-mdpi/launcher_icon.png → androidold/app/src/main/res/mipmap-mdpi/launcher_icon.png


BIN
androidold/app/src/main/res/mipmap-xhdpi/ic_launcher.png


+ 0 - 0
android/app/src/main/res/mipmap-xhdpi/launcher_icon.png → androidold/app/src/main/res/mipmap-xhdpi/launcher_icon.png


BIN
androidold/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


+ 0 - 0
android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png → androidold/app/src/main/res/mipmap-xxhdpi/launcher_icon.png


BIN
androidold/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


+ 0 - 0
android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png → androidold/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png


+ 18 - 0
androidold/app/src/main/res/values-night/styles.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
+    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <!-- Show a splash screen on the activity. Automatically removed when
+             the Flutter engine draws its first frame -->
+        <item name="android:windowBackground">@drawable/launch_background</item>
+    </style>
+    <!-- Theme applied to the Android Window as soon as the process has started.
+         This theme determines the color of the Android Window while your
+         Flutter UI initializes, as well as behind your Flutter UI while its
+         running.
+
+         This Theme is only used starting with V2 of Flutter's Android embedding. -->
+    <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <item name="android:windowBackground">?android:colorBackground</item>
+    </style>
+</resources>

+ 18 - 0
androidold/app/src/main/res/values/styles.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
+    <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
+        <!-- Show a splash screen on the activity. Automatically removed when
+             the Flutter engine draws its first frame -->
+        <item name="android:windowBackground">@drawable/launch_background</item>
+    </style>
+    <!-- Theme applied to the Android Window as soon as the process has started.
+         This theme determines the color of the Android Window while your
+         Flutter UI initializes, as well as behind your Flutter UI while its
+         running.
+
+         This Theme is only used starting with V2 of Flutter's Android embedding. -->
+    <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
+        <item name="android:windowBackground">?android:colorBackground</item>
+    </style>
+</resources>

+ 7 - 0
androidold/app/src/profile/AndroidManifest.xml

@@ -0,0 +1,7 @@
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
+    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
+</manifest>

+ 18 - 0
androidold/build.gradle

@@ -0,0 +1,18 @@
+allprojects {
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+
+rootProject.buildDir = "../build"
+subprojects {
+    project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+    project.evaluationDependsOn(":app")
+}
+
+tasks.register("clean", Delete) {
+    delete rootProject.buildDir
+}

+ 3 - 0
androidold/gradle.properties

@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
+android.useAndroidX=true
+android.enableJetifier=true

+ 5 - 0
androidold/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip

+ 25 - 0
androidold/settings.gradle

@@ -0,0 +1,25 @@
+pluginManagement {
+    def flutterSdkPath = {
+        def properties = new Properties()
+        file("local.properties").withInputStream { properties.load(it) }
+        def flutterSdkPath = properties.getProperty("flutter.sdk")
+        assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+        return flutterSdkPath
+    }()
+
+    includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+    repositories {
+        google()
+        mavenCentral()
+        gradlePluginPortal()
+    }
+}
+
+plugins {
+    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+    id "com.android.application" version "8.1.0" apply false
+    id "org.jetbrains.kotlin.android" version "1.8.22" apply false
+}
+
+include ":app"

+ 2 - 29
lib/core/routes.dart

@@ -24,6 +24,7 @@ import 'package:tp5/roster/view/roster_page.dart';
 import 'package:tp5/rosters/rosters_page.dart';
 import 'package:supabase_flutter/supabase_flutter.dart';
 import 'package:tp5/settings/settings_page.dart';
+import 'package:tp5/widgets/nav_drawer.dart';
 
 final routeProvider = Provider<RouterConfig<Object>?>((ref) {
   return _routes;
@@ -73,35 +74,7 @@ final _routes = GoRouter(
         }),
     GoRoute(
       path: '/test',
-      builder: (context, state) => BasicPage(
-        appBar: AppBar(
-          automaticallyImplyLeading: false,
-          title: const Text("TAR Pilot v5"),
-        ),
-        drawer: ListView(),
-        body: Column(
-          children: [
-            Text("${Jiffy.parseFromList([
-                  2024,
-                  9,
-                  1,
-                  20,
-                  0
-                ], isUtc: true).setTz(ap: "TUN").toUtc().dateTime}"),
-            Text(DTInterval.fromHm(
-                    apartir:
-                        Jiffy.parseFromList([2024, 9, 1, 1, 1], isUtc: true)
-                            .setTz(ap: "TUN"),
-                    h: 2,
-                    m: 0,
-                    duration: const Duration(hours: 2, minutes: 59),
-                    ap: "TUN")
-                .start
-                .dateTime
-                .toIso8601String())
-          ],
-        ),
-      ),
+      builder: (context, state) => Text("test"),
     ),
     GoRoute(
         path: "/auth",

+ 18 - 15
lib/csv/data.dart

@@ -424,29 +424,32 @@ class DataNotifier extends StateNotifier<DataState> {
     print(
         "data: datastate: resettimer: timer reseteed ${dur.inMinutes}min ${Jiffy.now().Hms}");
     _timer?.cancel();
-    _timer = Timer.periodic(dur, (timer) async {
-      print("data: datastate: timer: activated ${Jiffy.now().Hms}");
-      await downloadModifiedFiles();
-    });
+    // _timer = Timer.periodic(dur, (timer) async {
+    //   print("data: datastate: timer: activated ${Jiffy.now().Hms}");
+    //   await downloadModifiedFiles();
+    // });
   }
 
   DataNotifier() : super(DataState()) {
     Future.delayed(Duration.zero).then((x) async {
-      await downloadModifiedFiles();
       await loadAll();
+      await downloadModifiedFiles();
+
+      final csvhich = Supabase.instance.client.channel('csvhichstorage');
 
-      realtimeCsvupdates = Supabase.instance.client
-          .channel('public:csvupdates')
-          .onPostgresChanges(
-              event: PostgresChangeEvent.insert,
-              schema: 'public',
-              table: 'csvupdates',
+      csvhich
+          .onBroadcast(
+              event: 'upload',
               callback: (payload) async {
-                print(
-                    "data: datanotifier: realtime: ${payload.newRecord["filename"]}");
-                await downloadFile(payload.newRecord["filename"]);
+                final pay = payload; //["payload"] ?? payload;
+                print("data: datanotifier: realtime: $pay");
+                if (pay is Map && pay.keys.contains("filename")) {
+                  await downloadFile(pay["filename"] ?? "unknown.file");
+                }
               })
-          .subscribe();
+          .subscribe((st, ob) {
+        print("Data: datanotifier: Subscription status: $st | object: $ob");
+      });
     });
   }
 

+ 1 - 1
lib/fltinfo/view/fltinfo_page.dart

@@ -122,7 +122,7 @@ class _FltinfoPageState extends ConsumerState<FltinfoPage> {
         lido.add(LidoapiList.fromJson(e));
       }
     }
-    print(res);
+    //print(res);
     setState(() {
       _loadingLido = false;
     });

+ 9 - 8
lib/lido/lido_api.dart

@@ -137,13 +137,12 @@ class LidoApi {
       log("api lido 4d login errorcode: $errorcode");
       //invalide username or password
       if (['"LAS_112"', '"LAS_113"'].contains(errorcode)) {
-        print("lidoapi: login: invalidate user & pass");
-        await Hive.box("settings").delete("lido_user");
-        await Hive.box("settings").delete("lido_pass");
-        if (username != "" && password != "") {
-          await Hive.box("settings").put("lido_user_old", username);
-          await Hive.box("settings").put("lido_pass_old", password);
-        } //username = "";
+        if (user != null) {
+          print("lidoapi: login: invalidate user & pass");
+          await Hive.box("settings").delete("lido_user");
+          await Hive.box("settings").delete("lido_pass");
+        }
+        //username = "";
         password = "";
       }
       final res = extractCallbackData(response.body);
@@ -155,6 +154,9 @@ class LidoApi {
         await Hive.box("settings").put("lido_user", username);
         await Hive.box("settings").put("lido_pass", newpass);
         log("lidoapi: login: newpass: $newpass");
+      } else if (logged && newpass == null) {
+        await Hive.box("settings").put("lido_user", username);
+        await Hive.box("settings").put("lido_pass", password);
       }
       return {
         'error': logged ? null : message,
@@ -397,7 +399,6 @@ class LidoApi {
 
       return res;
     } catch (e) {
-      print("lidoapi: list:");
       final res = {
         'error': e.toString(),
         'data': {},

+ 25 - 7
lib/lido/view/lido_form.dart

@@ -9,6 +9,7 @@ import 'package:tp5/core/basic_page.dart';
 import 'package:tp5/core/core.dart';
 import 'package:tp5/core/utils.dart';
 import 'package:tp5/lido/lido_api.dart';
+import 'package:tp5/settings/settings.dart';
 
 class LidoForm extends ConsumerStatefulWidget {
   const LidoForm({super.key});
@@ -129,12 +130,26 @@ class _LidoFormState extends ConsumerState<LidoForm> {
                       padding: const EdgeInsets.only(left: 1.0),
                       child: InkWell(
                           onTap: () async {
-                            final x = await ref.read(lidoapiProvider).login(
-                                user: _userCtrl.text,
-                                pass: _passCtrl.text,
-                                newpass: "FaresWiem08__w");
-                            print("lidoform: loginnewpass: x= $x");
-                            // await ref.read(lidoapiProvider).forgotpass();
+                            // final x = await ref.read(lidoapiProvider).login(
+                            //     user: _userCtrl.text,
+                            //     pass: _passCtrl.text,
+                            //     newpass: "FaresWiem08__w");
+                            // print("lidoform: loginnewpass: x= $x");
+
+                            if (_userCtrl.text != "") {
+                              final res = await ref
+                                  .read(lidoapiProvider)
+                                  .forgotpass(user: _userCtrl.text);
+                              if (res["error"] == null) {
+                                context.showSuccess(
+                                    "Check your mail, we sent you a link to reset your password.");
+                              } else {
+                                context.showError(res["error"].toString());
+                              }
+                            } else {
+                              context.showError(
+                                  "Please enter your username, then try again.");
+                            }
                           },
                           child: const Text(
                             'Click here.',
@@ -197,11 +212,14 @@ class _LidoFormState extends ConsumerState<LidoForm> {
       setState(() {
         _isSubmitting = false;
       });
-      print("lidoform: login: out= $out");
+      // print("lidoform: login: out= $out");
       if (out["data"]?["logged"] == true) {
         await Hive.box("settings").put("lido_user", _userCtrl.text);
         await Hive.box("settings").put("lido_pass", _passCtrl.text);
 
+        Settings.put("lido_user", _userCtrl.text);
+        Settings.put("lido_pass", _passCtrl.text);
+
         context.showSuccess(
             "Your Credentials are correct and saved. Now you're connected to Lido!!!");
 

+ 2 - 2
lib/main.dart

@@ -70,7 +70,7 @@ main() async {
 
   try {
     Supabase.initialize(
-        url: 'https://baas.fares.cyou',
+        url: 'http://baas.fares.cyou:8000',
         anonKey:
             'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJhbm9uIiwKICAgICJpc3MiOiAic3VwYWJhc2UtZGVtbyIsCiAgICAiaWF0IjogMTY0MTc2OTIwMCwKICAgICJleHAiOiAxNzk5NTM1NjAwCn0.dc_X5iR_VP_qT0zsiyj_I_OZ2T9FtRU2BBNWN8Bu4GE',
         debug: true);
@@ -89,7 +89,7 @@ class MyApp extends ConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
     final routes = ref.read(routeProvider);
     return MaterialApp.router(
-      title: 'Memories App',
+      title: 'TAR Pilot',
       debugShowCheckedModeBanner: false,
       theme: myTheme(context, false),
       darkTheme: myTheme(context, true),

+ 1 - 1
lib/roster/api/crewlink_api.dart

@@ -666,7 +666,7 @@ class CrewlinkApi {
     const al = "[A-Z]{2}";
     const fnum = "\\d{1,4}[A-Z^R]?";
     const actype = "\\d{2}[A-Z0-9]";
-    const label = "(OFF|[A-Z]{2,}|[A-Z]+([0-9?]))";
+    const label = "(OFF|[A-Z]{2,}|[A-Z]+([0-9?])|[A-Z]+/[A-Z]+[0-9]?)";
     const time = "[0-2]\\d[0-5]\\d";
 
     PdfDocument document =

+ 48 - 0
lib/settings/settings.dart

@@ -0,0 +1,48 @@
+import 'package:hive_flutter/hive_flutter.dart';
+import 'package:supabase_flutter/supabase_flutter.dart';
+
+class Settings {
+  static const box = "settings";
+  static get(String key, {String? defaultValue}) {
+    Hive.box(box).get(key, defaultValue: defaultValue);
+  }
+
+  Future<Map<String, dynamic>?> getFresh(String key, {String? userId}) async {
+    SupabaseClient supabase = Supabase.instance.client;
+    final session = supabase.auth.currentSession;
+    if (session != null) {
+      final response = await Supabase.instance.client
+          .from('settings')
+          .select()
+          .eq('user_id', userId ?? session.user.id)
+          .eq('field', key)
+          .single();
+      return response as Map<String, dynamic>?;
+    }
+  }
+
+  static put(String key, value) {
+    Hive.box(box).put(key, value);
+    SupabaseClient supabase = Supabase.instance.client;
+    if (supabase.auth.currentSession != null) {
+      upsertSettings(
+          userid: supabase.auth.currentSession!.user.id,
+          field: key,
+          value: value);
+    }
+  }
+
+  static Future<void> upsertSettings({userid, field, value}) async {
+    final response = await Supabase.instance.client.from('settings').upsert(
+      {'user_id': userid, 'field': field, 'value': value},
+      onConflict: 'user_id,field',
+    ).select();
+    print('settings: upsertsettings: response: $response');
+  }
+
+  static delete(String key) => Hive.box(box).delete(key);
+  static clear() => Hive.box(box).clear();
+  static close() => Hive.box(box).close();
+  static isOpen() => Hive.box(box).isOpen;
+  static openBox() => Hive.openBox(box);
+}

+ 126 - 6
lib/settings/settings_page.dart

@@ -1,7 +1,13 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:form_builder_validators/form_builder_validators.dart';
+import 'package:gap/gap.dart';
+import 'package:hive_flutter/hive_flutter.dart';
 import 'package:tp5/core/basic_page.dart';
 
+import 'package:flutter_form_builder/flutter_form_builder.dart';
+import 'package:tp5/fltinfo/widget/w_cadre.dart';
+
 class SettingsPage extends ConsumerStatefulWidget {
   const SettingsPage({super.key, required this.params});
   final SettingsParams params;
@@ -11,15 +17,129 @@ class SettingsPage extends ConsumerStatefulWidget {
 }
 
 class _SettingsPageState extends ConsumerState<SettingsPage> {
+  final _formKey = GlobalKey<FormBuilderState>();
   @override
   Widget build(BuildContext context) {
     return BasicPage(
-      title: "Settings & Profile",
-      body: Form(
-          child: Column(
-        children: [FormField(builder: (x) => TextField())],
-      )),
-    );
+        title: "Settings & Profile",
+        body: Column(
+          mainAxisSize: MainAxisSize.min,
+          children: [
+            Padding(
+              padding: const EdgeInsets.all(8.0),
+              child: FormBuilder(
+                key: _formKey,
+                child: Column(
+                  children: [
+                    WCadre(
+                      title: "Crewlink Settings",
+                      child: Column(
+                        children: [
+                          FormBuilderTextField(
+                              name: 'crewlink_user',
+                              decoration: InputDecoration(
+                                labelText: 'Crewlink Username',
+                              )),
+                          FormBuilderTextField(
+                            name: 'crewlink_pass',
+                            decoration: InputDecoration(
+                              labelText: 'Crewlink Password',
+                            ),
+                          ),
+                          SizedBox(height: 20),
+                          Row(
+                            mainAxisAlignment: MainAxisAlignment.end,
+                            children: [
+                              ElevatedButton(
+                                onPressed: () {},
+                                child: Text(
+                                  'Test Connection to Crewlink',
+                                ),
+                              ),
+                              ElevatedButton(
+                                onPressed: () {},
+                                style: ElevatedButton.styleFrom(
+                                    backgroundColor: Colors.red),
+                                child: Text("Delete"),
+                              )
+                            ],
+                          ),
+                          const Gap(10)
+                        ],
+                      ),
+                    ),
+                    const Gap(10),
+                    WCadre(
+                      title: "Lido Settings",
+                      child: Column(
+                        children: [
+                          FormBuilderTextField(
+                              name: 'lido_user',
+                              decoration: InputDecoration(
+                                labelText: 'Lido Username',
+                              )),
+                          FormBuilderTextField(
+                            name: 'lido_pass',
+                            decoration: InputDecoration(
+                              labelText: 'Lido Password',
+                            ),
+                          ),
+                          SizedBox(height: 20),
+                          Row(
+                            mainAxisAlignment: MainAxisAlignment.end,
+                            children: [
+                              ElevatedButton(
+                                onPressed: () {},
+                                child: Text(
+                                  'Test Connection to Lido',
+                                ),
+                              ),
+                              ElevatedButton(
+                                onPressed: () {},
+                                style: ElevatedButton.styleFrom(
+                                    backgroundColor: Colors.red),
+                                child: Text("Delete"),
+                              )
+                            ],
+                          ),
+                          const Gap(10)
+                        ],
+                      ),
+                    ),
+                  ],
+                ),
+              ),
+            ),
+            Row(
+              children: [
+                Expanded(
+                  child: ElevatedButton(
+                    onPressed: () {
+                      if (_formKey.currentState!.saveAndValidate()) {
+                        print(_formKey.currentState!.value);
+                      } else {
+                        print('Validation failed');
+                      }
+                    },
+                    style: ElevatedButton.styleFrom(
+                      backgroundColor: Colors.green[800],
+                      foregroundColor: Colors.white,
+                    ),
+                    child: SizedBox(
+                      height: 44,
+                      child: Center(
+                        child: const Text(
+                          'Save & Sync',
+                          style: TextStyle(color: Colors.white, fontSize: 22),
+                        ),
+                      ),
+                    ),
+                  ),
+                ),
+              ],
+            ),
+          ],
+        ));
   }
 }
 

+ 5 - 0
lib/widgets/nav_drawer.dart

@@ -70,6 +70,11 @@ class NavDrawer extends StatelessWidget {
             title: const Text('Feedback'),
             onTap: () => {Navigator.of(context).pop()},
           ),
+          ListTile(
+            leading: const Icon(Icons.border_color),
+            title: const Text('Test'),
+            onTap: () => context.go('/test'),
+          ),
           ListTile(
             leading: const Icon(Icons.exit_to_app),
             title: const Text('Logout'),

+ 68 - 63
pubspec.lock

@@ -371,14 +371,14 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
-  flutter_launcher_icons:
-    dependency: "direct dev"
+  flutter_form_builder:
+    dependency: "direct main"
     description:
-      name: flutter_launcher_icons
-      sha256: "619817c4b65b322b5104b6bb6dfe6cda62d9729bd7ad4303ecc8b4e690a67a77"
+      name: flutter_form_builder
+      sha256: c278ef69b08957d484f83413f0e77b656a39b7a7bb4eb8a295da3a820ecc6545
       url: "https://pub.dev"
     source: hosted
-    version: "0.14.1"
+    version: "9.5.0"
   flutter_lints:
     dependency: "direct dev"
     description:
@@ -387,6 +387,11 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "5.0.0"
+  flutter_localizations:
+    dependency: transitive
+    description: flutter
+    source: sdk
+    version: "0.0.0"
   flutter_riverpod:
     dependency: "direct main"
     description:
@@ -405,6 +410,14 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  form_builder_validators:
+    dependency: "direct main"
+    description:
+      name: form_builder_validators
+      sha256: c61ed7b1deecf0e1ebe49e2fa79e3283937c5a21c7e48e3ed9856a4a14e1191a
+      url: "https://pub.dev"
+    source: hosted
+    version: "11.0.0"
   freezed_annotation:
     dependency: transitive
     description:
@@ -449,10 +462,10 @@ packages:
     dependency: "direct main"
     description:
       name: go_router
-      sha256: "8ae664a70174163b9f65ea68dd8673e29db8f9095de7b5cd00e167c621f4fef5"
+      sha256: "2fd11229f59e23e967b0775df8d5948a519cd7e1e8b6e849729e010587b46539"
       url: "https://pub.dev"
     source: hosted
-    version: "14.6.0"
+    version: "14.6.2"
   google_fonts:
     dependency: "direct main"
     description:
@@ -465,10 +478,10 @@ packages:
     dependency: transitive
     description:
       name: gotrue
-      sha256: "74b29f10ef7239e254847d52ecce1eac2a08c2cfced6a78280859391f5157e8b"
+      sha256: b9541c62edc0ee1fddf2c95364251b8076a93bcdbc3ad27f14a633d187e89ccc
       url: "https://pub.dev"
     source: hosted
-    version: "2.10.0"
+    version: "2.11.0"
   graphs:
     dependency: transitive
     description:
@@ -541,14 +554,6 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "4.1.1"
-  image:
-    dependency: transitive
-    description:
-      name: image
-      sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d
-      url: "https://pub.dev"
-    source: hosted
-    version: "4.3.0"
   intl:
     dependency: "direct main"
     description:
@@ -569,10 +574,10 @@ packages:
     dependency: "direct main"
     description:
       name: jiffy
-      sha256: "3497caaa36d36a29033e66803c9739ce6bccbc7e241ca46070f76ee9e6f6eb0c"
+      sha256: "1c1b86459969ff9f32dc5b0ffe392f1e08181e66396cf9dd8fa7c90552a691af"
       url: "https://pub.dev"
     source: hosted
-    version: "6.3.1"
+    version: "6.3.2"
   js:
     dependency: transitive
     description:
@@ -737,18 +742,18 @@ packages:
     dependency: transitive
     description:
       name: path_provider_android
-      sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a
+      sha256: "8c4967f8b7cb46dc914e178daa29813d83ae502e0529d7b0478330616a691ef7"
       url: "https://pub.dev"
     source: hosted
-    version: "2.2.12"
+    version: "2.2.14"
   path_provider_foundation:
     dependency: transitive
     description:
       name: path_provider_foundation
-      sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
+      sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
       url: "https://pub.dev"
     source: hosted
-    version: "2.4.0"
+    version: "2.4.1"
   path_provider_linux:
     dependency: transitive
     description:
@@ -801,10 +806,10 @@ packages:
     dependency: transitive
     description:
       name: permission_handler_html
-      sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851
+      sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
       url: "https://pub.dev"
     source: hosted
-    version: "0.1.3+2"
+    version: "0.1.3+5"
   permission_handler_platform_interface:
     dependency: transitive
     description:
@@ -857,10 +862,10 @@ packages:
     dependency: transitive
     description:
       name: postgrest
-      sha256: c6ddc0a2c238c5a686b00094edad728a49645579bf5868a06e1ad95c4918fe2c
+      sha256: "9f759ac497a24839addbed69d9569ea6d51d2e4834c672b8c2a73752fb6945c8"
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.0"
+    version: "2.4.0"
   pub_semver:
     dependency: transitive
     description:
@@ -905,10 +910,10 @@ packages:
     dependency: transitive
     description:
       name: riverpod_analyzer_utils
-      sha256: dc53a659cb543b203cdc35cd4e942ed08ea893eb6ef12029301323bdf18c5d95
+      sha256: c6b8222b2b483cb87ae77ad147d6408f400c64f060df7a225b127f4afef4f8c8
       url: "https://pub.dev"
     source: hosted
-    version: "0.5.7"
+    version: "0.5.8"
   riverpod_annotation:
     dependency: "direct dev"
     description:
@@ -921,18 +926,18 @@ packages:
     dependency: "direct dev"
     description:
       name: riverpod_generator
-      sha256: "54458dac2fea976990dc9ed379060db6ae5c8790143f1963fedd0fb99980a326"
+      sha256: "63546d70952015f0981361636bf8f356d9cfd9d7f6f0815e3c07789a41233188"
       url: "https://pub.dev"
     source: hosted
-    version: "2.6.2"
+    version: "2.6.3"
   riverpod_lint:
     dependency: "direct dev"
     description:
       name: riverpod_lint
-      sha256: "326efc199b87f21053b9a2afbf2aea26c41b3bf6f8ba346ce69126ee17d16ebd"
+      sha256: "83e4caa337a9840469b7b9bd8c2351ce85abad80f570d84146911b32086fbd99"
       url: "https://pub.dev"
     source: hosted
-    version: "2.6.2"
+    version: "2.6.3"
   rxdart:
     dependency: transitive
     description:
@@ -961,10 +966,10 @@ packages:
     dependency: transitive
     description:
       name: shared_preferences_android
-      sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab"
+      sha256: "7f172d1b06de5da47b6264c2692ee2ead20bbbc246690427cdb4fc301cd0c549"
       url: "https://pub.dev"
     source: hosted
-    version: "2.3.3"
+    version: "2.3.4"
   shared_preferences_foundation:
     dependency: transitive
     description:
@@ -1017,10 +1022,10 @@ packages:
     dependency: transitive
     description:
       name: shelf_web_socket
-      sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
+      sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
       url: "https://pub.dev"
     source: hosted
-    version: "2.0.0"
+    version: "2.0.1"
   sky_engine:
     dependency: transitive
     description: flutter
@@ -1102,18 +1107,18 @@ packages:
     dependency: transitive
     description:
       name: supabase
-      sha256: dccda29b5bda0a04f6725480e180353486dfd5d9dd2b8b17a30a7a7d094f7042
+      sha256: ecdfb226c483f05fd10425304de744144dfdda2f33d14151d2604cebe8c6d077
       url: "https://pub.dev"
     source: hosted
-    version: "2.5.0"
+    version: "2.6.0"
   supabase_flutter:
     dependency: "direct main"
     description:
       name: supabase_flutter
-      sha256: "8c056c83e9163c287e031cdd6fa34a452bad4ec7342a27b12190613f77c358e9"
+      sha256: bdbc6770e0e91db0b9d931f41ea4ee311d8819f539743f47e8cc795b492be75a
       url: "https://pub.dev"
     source: hosted
-    version: "2.8.0"
+    version: "2.8.1"
   super_sliver_list:
     dependency: "direct main"
     description:
@@ -1126,66 +1131,66 @@ packages:
     dependency: transitive
     description:
       name: syncfusion_flutter_core
-      sha256: "225b1cc135549bb4eef096d63b7323c30ee61c4b095c7e8a14bf9333e243d84b"
+      sha256: "325f519ce4ad8edd81811c21b853d72018529e353584490824da0555156ba076"
       url: "https://pub.dev"
     source: hosted
-    version: "27.2.2"
+    version: "27.2.5"
   syncfusion_flutter_pdf:
     dependency: "direct main"
     description:
       name: syncfusion_flutter_pdf
-      sha256: a1d9191fe72015977437b677ef57a44b860c8f9d23d62d2c41c431892ccfff5c
+      sha256: da7fb9d156fafdce7099dc711c1a2dc522c48361188bd4be78520b22c99bbafd
       url: "https://pub.dev"
     source: hosted
-    version: "27.2.2"
+    version: "27.2.5"
   syncfusion_flutter_pdfviewer:
     dependency: "direct main"
     description:
       name: syncfusion_flutter_pdfviewer
-      sha256: "98ce45bc769411a960a85f9b988afe303682830f4bbcd934fb3a11056ebe989d"
+      sha256: "0d51a728a2982bdad43bb6bf39da25ca9322e4b196f5335b2aed58ff85c7a7bf"
       url: "https://pub.dev"
     source: hosted
-    version: "27.2.2"
+    version: "27.2.5"
   syncfusion_flutter_signaturepad:
     dependency: transitive
     description:
       name: syncfusion_flutter_signaturepad
-      sha256: "9b81ad7e3a12f0306355bda761070065f849d40f664ceef5d35b1bcc33a709a5"
+      sha256: "84990394d4af761701b11d2764de52559a1ebbd2fcc0745a3ed34b563ac5b054"
       url: "https://pub.dev"
     source: hosted
-    version: "27.2.2"
+    version: "27.2.5"
   syncfusion_pdfviewer_macos:
     dependency: transitive
     description:
       name: syncfusion_pdfviewer_macos
-      sha256: bf9d0371506e44e02427e082ec673a5ed4a0030d30e679c05750917393f10e08
+      sha256: "25ae3ce2c2ef5b6ec7eb305fdfaff6049dc7d77d667e372fcdd497cf0d872f70"
       url: "https://pub.dev"
     source: hosted
-    version: "27.2.2"
+    version: "27.2.5"
   syncfusion_pdfviewer_platform_interface:
     dependency: transitive
     description:
       name: syncfusion_pdfviewer_platform_interface
-      sha256: b4358d5ccdfa83d9aed1547529078423e5d41731dc1c37faedb51bc5373e178a
+      sha256: "680f2f1fc9499214bd368209028e27b4c3060e2c9333baf692ee8e8b41a8fda9"
       url: "https://pub.dev"
     source: hosted
-    version: "27.2.2"
+    version: "27.2.5"
   syncfusion_pdfviewer_web:
     dependency: transitive
     description:
       name: syncfusion_pdfviewer_web
-      sha256: "89e9f5f6fdb220f3bc6a9c5b08bd77f967c21ef273114f97f10e62582ab553e1"
+      sha256: a2b840167434a023335f1ef8240a5f206003e3b2c33888e772150a9bc5e9662d
       url: "https://pub.dev"
     source: hosted
-    version: "27.2.2"
+    version: "27.2.5"
   syncfusion_pdfviewer_windows:
     dependency: transitive
     description:
       name: syncfusion_pdfviewer_windows
-      sha256: e698c992d9463a66f345e9b81744360345635a4c7b2cf66458a914de898621d5
+      sha256: "14b7ce1a86f893888e80cabf8ae4b5f62fd8aeb406868ec3c2101a2d211f10f6"
       url: "https://pub.dev"
     source: hosted
-    version: "27.2.2"
+    version: "27.2.5"
   term_glyph:
     dependency: transitive
     description:
@@ -1254,10 +1259,10 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_ios
-      sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e
+      sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626"
       url: "https://pub.dev"
     source: hosted
-    version: "6.3.1"
+    version: "6.3.2"
   url_launcher_linux:
     dependency: transitive
     description:
@@ -1270,10 +1275,10 @@ packages:
     dependency: transitive
     description:
       name: url_launcher_macos
-      sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672"
+      sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2"
       url: "https://pub.dev"
     source: hosted
-    version: "3.2.1"
+    version: "3.2.2"
   url_launcher_platform_interface:
     dependency: transitive
     description:
@@ -1358,10 +1363,10 @@ packages:
     dependency: transitive
     description:
       name: win32
-      sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2"
+      sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69"
       url: "https://pub.dev"
     source: hosted
-    version: "5.8.0"
+    version: "5.9.0"
   win32_registry:
     dependency: transitive
     description:

+ 3 - 1
pubspec.yaml

@@ -57,6 +57,8 @@ dependencies:
   linear_progress_bar: #^1.1.2
   #supabase_auth_ui: ^0.5.4
   multi_select_flutter: #^4.1.3.0.0
+  flutter_form_builder: #^9.5.0
+  form_builder_validators: #^11.0.0
 
 dev_dependencies:
   flutter_test:
@@ -70,7 +72,7 @@ dev_dependencies:
   #json_serializable: #^6.6.1
   custom_lint:
   riverpod_lint: #^2.3.1
-  flutter_launcher_icons: #^0.14.1
+  #flutter_launcher_icons: #^0.14.1
 
 flutter_launcher_icons:
   android: "launcher_icon"

+ 271 - 0
serverinit/migrate.sql

@@ -0,0 +1,271 @@
+DROP TABLE IF exists settings;
+-- Create the "settings" table
+CREATE TABLE settings (
+    id UUID PRIMARY KEY DEFAULT gen_random_uuid() UNIQUE,
+    user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
+    field TEXT NOT NULL,
+    value TEXT NOT NULL,
+    updated_at TIMESTAMPTZ DEFAULT now() NOT NULL,
+    created_at TIMESTAMPTZ DEFAULT now() NOT NULL
+);
+ALTER TABLE settings
+ADD CONSTRAINT unique_user_field UNIQUE (user_id, field);
+
+
+-- Enable Row-Level Security (RLS) on the "settings" table
+ALTER TABLE settings ENABLE ROW LEVEL SECURITY;
+
+-- Create a policy to allow each user to access only their data
+CREATE POLICY select_policy ON settings
+    FOR SELECT
+    USING (auth.uid() = user_id);
+
+-- Create a policy to allow each user to insert their data
+CREATE POLICY insert_policy ON settings
+    FOR INSERT
+    WITH CHECK (auth.uid() = user_id);
+
+-- Create a policy to allow each user to update only their data
+CREATE POLICY update_policy ON settings
+    FOR UPDATE
+    USING (auth.uid() = user_id);
+
+-- Create a policy to allow each user to delete only their data
+CREATE POLICY delete_policy ON settings
+    FOR DELETE
+    USING (auth.uid() = user_id);
+
+-- Add a rule to automatically update the "updated_at" column on update
+CREATE OR REPLACE FUNCTION update_updated_at_column()
+RETURNS TRIGGER AS $$
+BEGIN
+    NEW.updated_at = now();
+    RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE TRIGGER update_updated_at
+BEFORE UPDATE ON settings
+FOR EACH ROW
+EXECUTE FUNCTION update_updated_at_column();
+
+
+-- csvhichupdate
+-- Create the csvhichupdates table
+CREATE TABLE csvhichupdates (
+
+    id UUID PRIMARY KEY DEFAULT gen_random_uuid() UNIQUE,
+--    id SERIAL PRIMARY KEY,  -- Auto-incrementing unique identifier
+    filename TEXT NOT NULL,  -- Filename as text
+    updated_at TIMESTAMPTZ DEFAULT NOW()  -- Timestamp with timezone, default to current time
+);
+
+-- Enable Realtime on the csvhichupdates table
+-- SELECT pg_create_logical_replication_slot('csvhichupdates_slot', 'pgoutput');
+
+-- Grant permissions for RLS
+ALTER TABLE csvhichupdates ENABLE ROW LEVEL SECURITY;
+
+-- Create policies to allow access for anon, service_key, and authenticated users
+CREATE POLICY select_policy ON csvhichupdates
+    FOR SELECT
+    USING (auth.role() IN ('anon', 'service_key', 'authenticated'));
+
+--create buckets
+INSERT INTO storage.buckets (id, name)
+VALUES ('csv', 'csv');
+INSERT INTO storage.buckets (id, name)
+VALUES ('csvhich', 'csvhich');
+INSERT INTO storage.buckets (id, name)
+VALUES ('csvhich_archive', 'csvhich_archive');
+
+--create bucket policies
+CREATE POLICY "read_csv 2492_0" ON "storage"."objects" USING (((bucket_id = 'csv'::text) OR (bucket_id = 'csvhich'::text) ));
+CREATE POLICY "insert_in_csv 2492_0" ON storage.objects FOR INSERT TO service_role, authenticated WITH CHECK (bucket_id = 'csv' OR bucket_id = 'csvhich' );
+CREATE POLICY "insert_in_csv 2492_1" ON storage.objects FOR UPDATE TO service_role, authenticated USING (bucket_id = 'csv' OR bucket_id = 'csvhich' );
+
+
+-- tables csv
+create table IF NOT EXISTS public.aclegs_csv (
+	"leg_no" TEXT,
+	"fn_carrier" TEXT,
+	"fn_number" TEXT,
+	"fn_suffix" TEXT,
+	"day_of_origin" TEXT,
+	"ac_owner" TEXT,
+	"ac_subtype" TEXT,
+	"ac_version" TEXT,
+	"ac_registration" TEXT,
+	"dep_ap_actual" TEXT,
+	"dep_ap_sched" TEXT,
+	"dep_dt_est" TEXT,
+	"dep_sched_dt" TEXT,
+	"arr_ap_actual" TEXT,
+	"arr_ap_sched" TEXT,
+	"arr_dt_est" TEXT,
+	"arr_sched_dt" TEXT,
+	"slot_time_actual" TEXT,
+	"leg_type" TEXT,
+	"status" TEXT,
+	"employer_cockpit" TEXT,
+	"employer_cabin" TEXT,
+	"cycles" TEXT,
+	"delay_code_01" TEXT,
+	"delay_code_02" TEXT,
+	"delay_code_03" TEXT,
+	"delay_code_04" TEXT,
+	"delay_time_01" TEXT,
+	"delay_time_02" TEXT,
+	"delay_time_03" TEXT,
+	"delay_time_04" TEXT,
+	"subdelay_code_01" TEXT,
+	"subdelay_code_02" TEXT,
+	"subdelay_code_03" TEXT,
+	"subdelay_code_04" TEXT,
+	"pax_booked_c" TEXT,
+	"pax_booked_y" TEXT,
+	"pax_booked_trs_c" TEXT,
+	"pax_booked_trs_y" TEXT,
+	"pad_booked_c" TEXT,
+	"pad_booked_y" TEXT,
+	"offblock_dt_a" TEXT,
+	"airborne_dt_a" TEXT,
+	"landing_dt_a" TEXT,
+	"onblock_dt_a" TEXT,
+	"offblock_dt_f" TEXT,
+	"airborne_dt_f" TEXT,
+	"landing_dt_f" TEXT,
+	"onblock_dt_f" TEXT,
+	"offblock_dt_m" TEXT,
+	"airborne_dt_m" TEXT,
+	"landing_dt_m" TEXT,
+	"onblock_dt_m" TEXT,
+	"eet" TEXT,
+    id SERIAL PRIMARY KEY
+);
+
+create table IF NOT EXISTS public.pnlegs_csv (
+date TEXT,
+tlc TEXT,
+actype TEXT,
+al TEXT,
+fnum TEXT,
+ddep TEXT,
+hdep TEXT,
+ddes TEXT,
+hdes TEXT,
+dep TEXT,
+des TEXT,
+label TEXT,
+type TEXT,
+id SERIAL PRIMARY KEY
+);
+
+create table IF NOT EXISTS public.pnlegsmois_csv (
+date TEXT,
+tlc TEXT,
+actype TEXT,
+al TEXT,
+fnum TEXT,
+ddep TEXT,
+hdep TEXT,
+ddes TEXT,
+hdes TEXT,
+dep TEXT,
+des TEXT,
+label TEXT,
+type TEXT,
+id SERIAL PRIMARY KEY
+);
+
+create table IF NOT EXISTS public.licences_csv (
+tlc TEXT,
+fname TEXT,
+mname TEXT,
+lname TEXT,
+expire TEXT,
+ac TEXT,
+college TEXT,
+base TEXT,
+id SERIAL PRIMARY KEY
+);
+
+
+
+
+
+
+CREATE OR REPLACE VIEW pnlegs_csv_clean AS
+select TO_DATE(DATE,'DD/MM/YYYY') as date,
+       tlc,
+       actype,
+       al,
+       fnum,
+       TO_TIMESTAMP(ddep || COALESCE(hdep, '0000'), 'DD/MM/YYYYHH24MI') AT TIME ZONE 'UTC' as hdep,
+       TO_TIMESTAMP(ddes || COALESCE(hdes, '0000'), 'DD/MM/YYYYHH24MI') AT TIME ZONE 'UTC' as hdes, 
+       dep,
+       des,
+       label,
+       TYPE
+       FROM pnlegs_csv;
+
+
+CREATE OR REPLACE VIEW aclegs_csv_clean AS
+	SELECT 
+		fn_carrier AS al  ,
+		fn_number AS fnum  ,
+		DATE(TO_TIMESTAMP(DAY_OF_ORIGIN,'YYYY-MM-DD HH24:MI:SS') AT TIME ZONE 'UTC' ) AS date ,
+		json_build_object('owner',ac_owner,'subtype',ac_subtype,'version',ac_version) AS ac  ,
+		ac_registration AS reg,
+		COALESCE(dep_ap_actual,dep_ap_sched,null) AS dep  ,
+		COALESCE(arr_ap_actual,arr_ap_sched,null) AS des  ,
+		TO_TIMESTAMP(dep_sched_dt,'YYYY-MM-DD HH24:MI:SS') AT TIME ZONE 'UTC' AS hdep_sched  ,
+		TO_TIMESTAMP(arr_sched_dt,'YYYY-MM-DD HH24:MI:SS') AT TIME ZONE 'UTC'  AS hdes_sched  ,
+		COALESCE(COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,NULL),dep_dt_est,dep_sched_dt)::timestamp AS hdep  ,
+
+		COALESCE(
+			COALESCE(onblock_dt_m,onblock_dt_a,onblock_dt_f,NULL)::timestamp,
+			(CASE
+				WHEN COALESCE(landing_dt_m,landing_dt_a,landing_dt_f,null) IS NOT NULL THEN 
+					COALESCE(landing_dt_m,landing_dt_a,landing_dt_f,NULL)::timestamp+taxi_in(arr_ap_actual)::INTERVAL 
+				WHEN COALESCE(airborne_dt_m,airborne_dt_a,airborne_dt_f,null) IS NOT NULL AND EET IS NOT NULL THEN 
+					COALESCE(airborne_dt_m,airborne_dt_a,airborne_dt_f,NULL)::TIMESTAMP+(EET||' minutes')::INTERVAL +taxi_in(arr_ap_actual)::INTERVAL
+				WHEN COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,null) IS NOT NULL AND EET IS NOT NULL THEN 
+					COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,NULL)::timestamp+taxi_out(dep_ap_actual)::INTERVAL+(EET||' minutes')::INTERVAL +taxi_in(arr_ap_actual)::INTERVAL
+				WHEN COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,null) IS NOT NULL AND EET IS NULL THEN
+					COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,NULL)::timestamp+(arr_sched_dt::TIMESTAMP-dep_sched_dt::TIMESTAMP)
+				WHEN ( (dep_dt_est IS NOT NULL) AND (EET IS NOT NULL) ) THEN
+					dep_dt_est::TIMESTAMP + taxi_out(dep_ap_actual)::INTERVAL + (EET||' minutes')::INTERVAL + taxi_in(arr_ap_actual)::INTERVAL  
+				WHEN ( (dep_dt_est IS NOT NULL) AND (EET IS NULL) ) THEN 
+					dep_dt_est::TIMESTAMP + (arr_sched_dt::TIMESTAMP-dep_sched_dt::TIMESTAMP)  
+				WHEN (  (EET IS NOT NULL) ) THEN
+					dep_sched_dt::TIMESTAMP + taxi_out(dep_ap_actual)::INTERVAL + (EET||' minutes')::INTERVAL + taxi_in(arr_ap_actual)::INTERVAL  
+
+				ELSE 
+					arr_sched_dt::timestamp
+			END)::timestamp
+		)::timestamp
+		AS hdes  ,
+		
+		nullif(array_remove(ARRAY [delay_code_01 || '|' || subdelay_code_01 || '|' || delay_time_01,delay_code_02 || '|' || subdelay_code_02 || '|' || delay_time_02,delay_code_03 || '|' || subdelay_code_03 || '|' || delay_time_03,delay_code_04 || '|' || subdelay_code_04 || '|' || delay_time_04],'||'),'{}') AS delay  ,
+	
+		pax_booked_c AS pax_c  ,
+		pax_booked_y AS pax_y  ,
+		COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,NULL)::timestamp AS block_off  ,
+		COALESCE(airborne_dt_m,airborne_dt_a,airborne_dt_f,null)::timestamp AS block_air  ,
+		COALESCE(landing_dt_m,landing_dt_a,landing_dt_f,null)::timestamp AS block_ldg  ,
+		COALESCE(onblock_dt_m,onblock_dt_a,onblock_dt_f,null)::timestamp AS block_on  ,
+		slot_time_actual::timestamp AS ctot  ,
+		'1 minutes'::interval * eet::integer  AS EET  ,
+			CASE
+				WHEN COALESCE(onblock_dt_m,onblock_dt_a,onblock_dt_f,null) IS NOT NULL THEN 'ARRIVED'
+				WHEN COALESCE(landing_dt_m,landing_dt_a,landing_dt_f,null) IS NOT NULL THEN 'LANDED'
+				WHEN COALESCE(airborne_dt_m,airborne_dt_a,airborne_dt_f,null) IS NOT NULL THEN 'AIRBORN'
+				WHEN COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,null) IS NOT NULL THEN 'DEPARTED'
+				WHEN ( (dep_dt_est IS NOT NULL) AND (dep_sched_dt <> dep_dt_est) ) THEN 'DELAYED'
+				else
+				'SCHED'
+			END
+		 AS status ,
+		json_build_object('FN_CARRIER',FN_CARRIER,'FN_NUMBER',FN_NUMBER,'DAY_OF_ORIGIN',DAY_OF_ORIGIN,'AC_OWNER',AC_OWNER,'AC_SUBTYPE',AC_SUBTYPE,'AC_VERSION',AC_VERSION,'AC_REGISTRATION',AC_REGISTRATION,'DEP_AP_ACTUAL',DEP_AP_ACTUAL,'DEP_AP_SCHED',DEP_AP_SCHED,'DEP_DT_EST',DEP_DT_EST,'DEP_SCHED_DT',DEP_SCHED_DT,'ARR_AP_ACTUAL',ARR_AP_ACTUAL,'ARR_AP_SCHED',ARR_AP_SCHED,'ARR_DT_EST',ARR_DT_EST,'ARR_SCHED_DT',ARR_SCHED_DT,'SLOT_TIME_ACTUAL',SLOT_TIME_ACTUAL,'LEG_TYPE',LEG_TYPE,'EMPLOYER_COCKPIT',EMPLOYER_COCKPIT,'EMPLOYER_CABIN',EMPLOYER_CABIN,'DELAY_CODE_01',DELAY_CODE_01,'DELAY_CODE_02',DELAY_CODE_02,'DELAY_CODE_03',DELAY_CODE_03,'DELAY_CODE_04',DELAY_CODE_04,'DELAY_TIME_01',DELAY_TIME_01,'DELAY_TIME_02',DELAY_TIME_02,'DELAY_TIME_03',DELAY_TIME_03,'DELAY_TIME_04',DELAY_TIME_04,'SUBDELAY_CODE_01',SUBDELAY_CODE_01,'SUBDELAY_CODE_02',SUBDELAY_CODE_02,'SUBDELAY_CODE_03',SUBDELAY_CODE_03,'SUBDELAY_CODE_04',SUBDELAY_CODE_04,'PAX_BOOKED_C',PAX_BOOKED_C,'PAX_BOOKED_Y',PAX_BOOKED_Y,'OFFBLOCK_DT_A',OFFBLOCK_DT_A,'AIRBORNE_DT_A',AIRBORNE_DT_A,'LANDING_DT_A',LANDING_DT_A,'ONBLOCK_DT_A',ONBLOCK_DT_A,'OFFBLOCK_DT_F',OFFBLOCK_DT_F,'AIRBORNE_DT_F',AIRBORNE_DT_F,'LANDING_DT_F',LANDING_DT_F,'ONBLOCK_DT_F',ONBLOCK_DT_F,'OFFBLOCK_DT_M',OFFBLOCK_DT_M,'AIRBORNE_DT_M',AIRBORNE_DT_M,'LANDING_DT_M',LANDING_DT_M,'ONBLOCK_DT_M',ONBLOCK_DT_M,'EET',EET) AS DATA  
+	FROM aclegs_csv;

+ 271 - 0
tp5/migrate.sql

@@ -0,0 +1,271 @@
+DROP TABLE IF exists settings;
+-- Create the "settings" table
+CREATE TABLE settings (
+    id UUID PRIMARY KEY DEFAULT gen_random_uuid() UNIQUE,
+    user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
+    field TEXT NOT NULL,
+    value TEXT NOT NULL,
+    updated_at TIMESTAMPTZ DEFAULT now() NOT NULL,
+    created_at TIMESTAMPTZ DEFAULT now() NOT NULL
+);
+ALTER TABLE settings
+ADD CONSTRAINT unique_user_field UNIQUE (user_id, field);
+
+
+-- Enable Row-Level Security (RLS) on the "settings" table
+ALTER TABLE settings ENABLE ROW LEVEL SECURITY;
+
+-- Create a policy to allow each user to access only their data
+CREATE POLICY select_policy ON settings
+    FOR SELECT
+    USING (auth.uid() = user_id);
+
+-- Create a policy to allow each user to insert their data
+CREATE POLICY insert_policy ON settings
+    FOR INSERT
+    WITH CHECK (auth.uid() = user_id);
+
+-- Create a policy to allow each user to update only their data
+CREATE POLICY update_policy ON settings
+    FOR UPDATE
+    USING (auth.uid() = user_id);
+
+-- Create a policy to allow each user to delete only their data
+CREATE POLICY delete_policy ON settings
+    FOR DELETE
+    USING (auth.uid() = user_id);
+
+-- Add a rule to automatically update the "updated_at" column on update
+CREATE OR REPLACE FUNCTION update_updated_at_column()
+RETURNS TRIGGER AS $$
+BEGIN
+    NEW.updated_at = now();
+    RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE TRIGGER update_updated_at
+BEFORE UPDATE ON settings
+FOR EACH ROW
+EXECUTE FUNCTION update_updated_at_column();
+
+
+-- csvhichupdate
+-- Create the csvhichupdates table
+CREATE TABLE csvhichupdates (
+
+    id UUID PRIMARY KEY DEFAULT gen_random_uuid() UNIQUE,
+--    id SERIAL PRIMARY KEY,  -- Auto-incrementing unique identifier
+    filename TEXT NOT NULL,  -- Filename as text
+    updated_at TIMESTAMPTZ DEFAULT NOW()  -- Timestamp with timezone, default to current time
+);
+
+-- Enable Realtime on the csvhichupdates table
+-- SELECT pg_create_logical_replication_slot('csvhichupdates_slot', 'pgoutput');
+
+-- Grant permissions for RLS
+ALTER TABLE csvhichupdates ENABLE ROW LEVEL SECURITY;
+
+-- Create policies to allow access for anon, service_key, and authenticated users
+CREATE POLICY select_policy ON csvhichupdates
+    FOR SELECT
+    USING (auth.role() IN ('anon', 'service_key', 'authenticated'));
+
+--create buckets
+INSERT INTO storage.buckets (id, name)
+VALUES ('csv', 'csv');
+INSERT INTO storage.buckets (id, name)
+VALUES ('csvhich', 'csvhich');
+INSERT INTO storage.buckets (id, name)
+VALUES ('csvhich_archive', 'csvhich_archive');
+
+--create bucket policies
+CREATE POLICY "read_csv 2492_0" ON "storage"."objects" USING (((bucket_id = 'csv'::text) OR (bucket_id = 'csvhich'::text) ));
+CREATE POLICY "insert_in_csv 2492_0" ON storage.objects FOR INSERT TO service_role, authenticated WITH CHECK (bucket_id = 'csv' OR bucket_id = 'csvhich' );
+CREATE POLICY "insert_in_csv 2492_1" ON storage.objects FOR UPDATE TO service_role, authenticated USING (bucket_id = 'csv' OR bucket_id = 'csvhich' );
+
+
+-- tables csv
+create table IF NOT EXISTS public.aclegs_csv (
+	"leg_no" TEXT,
+	"fn_carrier" TEXT,
+	"fn_number" TEXT,
+	"fn_suffix" TEXT,
+	"day_of_origin" TEXT,
+	"ac_owner" TEXT,
+	"ac_subtype" TEXT,
+	"ac_version" TEXT,
+	"ac_registration" TEXT,
+	"dep_ap_actual" TEXT,
+	"dep_ap_sched" TEXT,
+	"dep_dt_est" TEXT,
+	"dep_sched_dt" TEXT,
+	"arr_ap_actual" TEXT,
+	"arr_ap_sched" TEXT,
+	"arr_dt_est" TEXT,
+	"arr_sched_dt" TEXT,
+	"slot_time_actual" TEXT,
+	"leg_type" TEXT,
+	"status" TEXT,
+	"employer_cockpit" TEXT,
+	"employer_cabin" TEXT,
+	"cycles" TEXT,
+	"delay_code_01" TEXT,
+	"delay_code_02" TEXT,
+	"delay_code_03" TEXT,
+	"delay_code_04" TEXT,
+	"delay_time_01" TEXT,
+	"delay_time_02" TEXT,
+	"delay_time_03" TEXT,
+	"delay_time_04" TEXT,
+	"subdelay_code_01" TEXT,
+	"subdelay_code_02" TEXT,
+	"subdelay_code_03" TEXT,
+	"subdelay_code_04" TEXT,
+	"pax_booked_c" TEXT,
+	"pax_booked_y" TEXT,
+	"pax_booked_trs_c" TEXT,
+	"pax_booked_trs_y" TEXT,
+	"pad_booked_c" TEXT,
+	"pad_booked_y" TEXT,
+	"offblock_dt_a" TEXT,
+	"airborne_dt_a" TEXT,
+	"landing_dt_a" TEXT,
+	"onblock_dt_a" TEXT,
+	"offblock_dt_f" TEXT,
+	"airborne_dt_f" TEXT,
+	"landing_dt_f" TEXT,
+	"onblock_dt_f" TEXT,
+	"offblock_dt_m" TEXT,
+	"airborne_dt_m" TEXT,
+	"landing_dt_m" TEXT,
+	"onblock_dt_m" TEXT,
+	"eet" TEXT,
+    id SERIAL PRIMARY KEY
+);
+
+create table IF NOT EXISTS public.pnlegs_csv (
+date TEXT,
+tlc TEXT,
+actype TEXT,
+al TEXT,
+fnum TEXT,
+ddep TEXT,
+hdep TEXT,
+ddes TEXT,
+hdes TEXT,
+dep TEXT,
+des TEXT,
+label TEXT,
+type TEXT,
+id SERIAL PRIMARY KEY
+);
+
+create table IF NOT EXISTS public.pnlegsmois_csv (
+date TEXT,
+tlc TEXT,
+actype TEXT,
+al TEXT,
+fnum TEXT,
+ddep TEXT,
+hdep TEXT,
+ddes TEXT,
+hdes TEXT,
+dep TEXT,
+des TEXT,
+label TEXT,
+type TEXT,
+id SERIAL PRIMARY KEY
+);
+
+create table IF NOT EXISTS public.licences_csv (
+tlc TEXT,
+fname TEXT,
+mname TEXT,
+lname TEXT,
+expire TEXT,
+ac TEXT,
+college TEXT,
+base TEXT,
+id SERIAL PRIMARY KEY
+);
+
+
+
+
+
+
+CREATE OR REPLACE VIEW pnlegs_csv_clean AS
+select TO_DATE(DATE,'DD/MM/YYYY') as date,
+       tlc,
+       actype,
+       al,
+       fnum,
+       TO_TIMESTAMP(ddep || COALESCE(hdep, '0000'), 'DD/MM/YYYYHH24MI') AT TIME ZONE 'UTC' as hdep,
+       TO_TIMESTAMP(ddes || COALESCE(hdes, '0000'), 'DD/MM/YYYYHH24MI') AT TIME ZONE 'UTC' as hdes, 
+       dep,
+       des,
+       label,
+       TYPE
+       FROM pnlegs_csv;
+
+
+CREATE OR REPLACE VIEW aclegs_csv_clean AS
+	SELECT 
+		fn_carrier AS al  ,
+		fn_number AS fnum  ,
+		DATE(TO_TIMESTAMP(DAY_OF_ORIGIN,'YYYY-MM-DD HH24:MI:SS') AT TIME ZONE 'UTC' ) AS date ,
+		json_build_object('owner',ac_owner,'subtype',ac_subtype,'version',ac_version) AS ac  ,
+		ac_registration AS reg,
+		COALESCE(dep_ap_actual,dep_ap_sched,null) AS dep  ,
+		COALESCE(arr_ap_actual,arr_ap_sched,null) AS des  ,
+		TO_TIMESTAMP(dep_sched_dt,'YYYY-MM-DD HH24:MI:SS') AT TIME ZONE 'UTC' AS hdep_sched  ,
+		TO_TIMESTAMP(arr_sched_dt,'YYYY-MM-DD HH24:MI:SS') AT TIME ZONE 'UTC'  AS hdes_sched  ,
+		COALESCE(COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,NULL),dep_dt_est,dep_sched_dt)::timestamp AS hdep  ,
+
+		COALESCE(
+			COALESCE(onblock_dt_m,onblock_dt_a,onblock_dt_f,NULL)::timestamp,
+			(CASE
+				WHEN COALESCE(landing_dt_m,landing_dt_a,landing_dt_f,null) IS NOT NULL THEN 
+					COALESCE(landing_dt_m,landing_dt_a,landing_dt_f,NULL)::timestamp+taxi_in(arr_ap_actual)::INTERVAL 
+				WHEN COALESCE(airborne_dt_m,airborne_dt_a,airborne_dt_f,null) IS NOT NULL AND EET IS NOT NULL THEN 
+					COALESCE(airborne_dt_m,airborne_dt_a,airborne_dt_f,NULL)::TIMESTAMP+(EET||' minutes')::INTERVAL +taxi_in(arr_ap_actual)::INTERVAL
+				WHEN COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,null) IS NOT NULL AND EET IS NOT NULL THEN 
+					COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,NULL)::timestamp+taxi_out(dep_ap_actual)::INTERVAL+(EET||' minutes')::INTERVAL +taxi_in(arr_ap_actual)::INTERVAL
+				WHEN COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,null) IS NOT NULL AND EET IS NULL THEN
+					COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,NULL)::timestamp+(arr_sched_dt::TIMESTAMP-dep_sched_dt::TIMESTAMP)
+				WHEN ( (dep_dt_est IS NOT NULL) AND (EET IS NOT NULL) ) THEN
+					dep_dt_est::TIMESTAMP + taxi_out(dep_ap_actual)::INTERVAL + (EET||' minutes')::INTERVAL + taxi_in(arr_ap_actual)::INTERVAL  
+				WHEN ( (dep_dt_est IS NOT NULL) AND (EET IS NULL) ) THEN 
+					dep_dt_est::TIMESTAMP + (arr_sched_dt::TIMESTAMP-dep_sched_dt::TIMESTAMP)  
+				WHEN (  (EET IS NOT NULL) ) THEN
+					dep_sched_dt::TIMESTAMP + taxi_out(dep_ap_actual)::INTERVAL + (EET||' minutes')::INTERVAL + taxi_in(arr_ap_actual)::INTERVAL  
+
+				ELSE 
+					arr_sched_dt::timestamp
+			END)::timestamp
+		)::timestamp
+		AS hdes  ,
+		
+		nullif(array_remove(ARRAY [delay_code_01 || '|' || subdelay_code_01 || '|' || delay_time_01,delay_code_02 || '|' || subdelay_code_02 || '|' || delay_time_02,delay_code_03 || '|' || subdelay_code_03 || '|' || delay_time_03,delay_code_04 || '|' || subdelay_code_04 || '|' || delay_time_04],'||'),'{}') AS delay  ,
+	
+		pax_booked_c AS pax_c  ,
+		pax_booked_y AS pax_y  ,
+		COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,NULL)::timestamp AS block_off  ,
+		COALESCE(airborne_dt_m,airborne_dt_a,airborne_dt_f,null)::timestamp AS block_air  ,
+		COALESCE(landing_dt_m,landing_dt_a,landing_dt_f,null)::timestamp AS block_ldg  ,
+		COALESCE(onblock_dt_m,onblock_dt_a,onblock_dt_f,null)::timestamp AS block_on  ,
+		slot_time_actual::timestamp AS ctot  ,
+		'1 minutes'::interval * eet::integer  AS EET  ,
+			CASE
+				WHEN COALESCE(onblock_dt_m,onblock_dt_a,onblock_dt_f,null) IS NOT NULL THEN 'ARRIVED'
+				WHEN COALESCE(landing_dt_m,landing_dt_a,landing_dt_f,null) IS NOT NULL THEN 'LANDED'
+				WHEN COALESCE(airborne_dt_m,airborne_dt_a,airborne_dt_f,null) IS NOT NULL THEN 'AIRBORN'
+				WHEN COALESCE(offblock_dt_m,offblock_dt_a,offblock_dt_f,null) IS NOT NULL THEN 'DEPARTED'
+				WHEN ( (dep_dt_est IS NOT NULL) AND (dep_sched_dt <> dep_dt_est) ) THEN 'DELAYED'
+				else
+				'SCHED'
+			END
+		 AS status ,
+		json_build_object('FN_CARRIER',FN_CARRIER,'FN_NUMBER',FN_NUMBER,'DAY_OF_ORIGIN',DAY_OF_ORIGIN,'AC_OWNER',AC_OWNER,'AC_SUBTYPE',AC_SUBTYPE,'AC_VERSION',AC_VERSION,'AC_REGISTRATION',AC_REGISTRATION,'DEP_AP_ACTUAL',DEP_AP_ACTUAL,'DEP_AP_SCHED',DEP_AP_SCHED,'DEP_DT_EST',DEP_DT_EST,'DEP_SCHED_DT',DEP_SCHED_DT,'ARR_AP_ACTUAL',ARR_AP_ACTUAL,'ARR_AP_SCHED',ARR_AP_SCHED,'ARR_DT_EST',ARR_DT_EST,'ARR_SCHED_DT',ARR_SCHED_DT,'SLOT_TIME_ACTUAL',SLOT_TIME_ACTUAL,'LEG_TYPE',LEG_TYPE,'EMPLOYER_COCKPIT',EMPLOYER_COCKPIT,'EMPLOYER_CABIN',EMPLOYER_CABIN,'DELAY_CODE_01',DELAY_CODE_01,'DELAY_CODE_02',DELAY_CODE_02,'DELAY_CODE_03',DELAY_CODE_03,'DELAY_CODE_04',DELAY_CODE_04,'DELAY_TIME_01',DELAY_TIME_01,'DELAY_TIME_02',DELAY_TIME_02,'DELAY_TIME_03',DELAY_TIME_03,'DELAY_TIME_04',DELAY_TIME_04,'SUBDELAY_CODE_01',SUBDELAY_CODE_01,'SUBDELAY_CODE_02',SUBDELAY_CODE_02,'SUBDELAY_CODE_03',SUBDELAY_CODE_03,'SUBDELAY_CODE_04',SUBDELAY_CODE_04,'PAX_BOOKED_C',PAX_BOOKED_C,'PAX_BOOKED_Y',PAX_BOOKED_Y,'OFFBLOCK_DT_A',OFFBLOCK_DT_A,'AIRBORNE_DT_A',AIRBORNE_DT_A,'LANDING_DT_A',LANDING_DT_A,'ONBLOCK_DT_A',ONBLOCK_DT_A,'OFFBLOCK_DT_F',OFFBLOCK_DT_F,'AIRBORNE_DT_F',AIRBORNE_DT_F,'LANDING_DT_F',LANDING_DT_F,'ONBLOCK_DT_F',ONBLOCK_DT_F,'OFFBLOCK_DT_M',OFFBLOCK_DT_M,'AIRBORNE_DT_M',AIRBORNE_DT_M,'LANDING_DT_M',LANDING_DT_M,'ONBLOCK_DT_M',ONBLOCK_DT_M,'EET',EET) AS DATA  
+	FROM aclegs_csv;

+ 17 - 0
uploaddb/docker-compose.yml

@@ -0,0 +1,17 @@
+#version: '3.8'
+
+services:
+  upload-db:
+    build: .
+    volumes:
+      - ../tp4/src/storage/app/ftp:/app/watched
+      - ./upload.sh:/app/upload.sh
+    restart: unless-stopped
+    environment:
+      - SUPABASE_URL=https://your-supabase-url.supabase.co/storage/v1/object/csv
+      - SUPABASE_KEY=your-supabase-key
+    logging:
+      driver: "json-file"
+      options:
+        max-size: "5m"  # Maximum size of a log file
+        max-file: "3"     # Maximum number of log files to keep

+ 80 - 0
uploaddb/upload.sh

@@ -0,0 +1,80 @@
+#!/bin/bash
+
+# Set your Supabase project URL and API key
+SUPABASE_URL="https://v5.fares.cyou"
+SUPABASE_API_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
+BUCKET_NAME="csv"
+#FOLDER="/ftphich"
+
+# Directory to watch
+WATCHED_DIR="/app/watched"
+
+# Files to monitor
+FILES_TO_MONITOR=("ExportPGRGPNmois.zip" "secondprgtype.zip" "exportPRG.zip" "exportPGRGPN.zip" "exportlicence.txt")
+
+# Declare an associative array to hold the last modified timestamps
+declare -A last_modified
+
+# Initialize the last modified timestamps
+#"for file in "${FILES_TO_MONITOR[@]}"; do
+#   last_modified["$file"]=$(stat -c %Y "$WATCHED_DIR/$file" 2>/dev/null)
+#done
+
+
+
+# Function to upload file to Supabase
+upload_file() {
+    local file="$1"
+    local basename_file=$(basename "$file")
+    
+    #echo "Uploading $basename_file to Supabase storage..."
+
+    # Use curl to upload the file
+    response=$(curl --fail-with-body -X POST "${SUPABASE_URL}/upload" \
+        -H "Content-Type: application/octet-stream" \
+        -H "Authorization: Bearer ${SUPABASE_API_KEY}" \
+        --data-binary @"$file" 2>/dev/null)
+
+    response=$(curl -F "file=@$file;type=application/octet-stream" \
+     -H "Authorization: Bearer  ${SUPABASE_API_KEY}" \
+     -X POST "${SUPABASE_URL}/upload" 2>/dev/null)
+
+    if [ $? -eq 0 ]; then
+        echo "File uploaded successfully: $response"
+        # Check if the upload was successful
+        if echo "$response" | grep -q '"Key":';  then
+        #echo "$response"
+            echo "$(date '+%d%b%y %H:%M:%S') +++ Successfully uploaded $basename_file."
+        else
+            echo "$(date '+%d%b%y %H:%M:%S') --- Failed to upload $basename_file. Response: $response"
+        fi
+
+    else 
+        echo "$(date '+%d%b%y %H:%M:%S') --- Failed to upload file. Response: $response"
+    fi
+
+}
+
+# Polling loop
+while true; do
+    for file in "${FILES_TO_MONITOR[@]}"; do
+        full_path="$WATCHED_DIR/$file"
+        # Get the current modified timestamp
+        current_modified=$(stat -c %Y "$full_path" 2>/dev/null)
+        #echo "file $file timestamp $current_modified"
+        # Check if the file has been modified
+        if [[ "$current_modified" != "${last_modified[$file]}" ]]; then
+            formatted_lastdate=$(date -d @"${last_modified[$file]}" '+%d%b%y %H%M')
+            formatted_curdate=$(date -d @"$current_modified" '+%d%b%y %H%M')
+            echo "=================================================== $file"
+            echo "$(date '+%d%b%y %H:%M:%S') ... file has been modified. last: $formatted_lastdate >>> current: $formatted_curdate"
+            upload_file "$full_path"
+            # Update the last modified timestamp
+            last_modified["$file"]=$current_modified
+        fi
+    done
+    # Sleep for a specified interval (e.g., 5 seconds)
+    sleep 5
+done
+
+

BIN
windows/runner/resources/app_icon.ico