Remade frontend dashboard as flutter dashboard, still WIP
							
								
								
									
										26
									
								
								flake.nix
									
									
									
									
									
								
							
							
						
						@ -1,26 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  description = "Simple dart flake";
 | 
			
		||||
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
 | 
			
		||||
  inputs.flake-utils.url = "github:numtide/flake-utils";
 | 
			
		||||
 | 
			
		||||
  outputs = {
 | 
			
		||||
    flake-utils,
 | 
			
		||||
    nixpkgs,
 | 
			
		||||
    ...
 | 
			
		||||
  }:
 | 
			
		||||
    flake-utils.lib.eachDefaultSystem (system: let
 | 
			
		||||
      pkgs = import nixpkgs {
 | 
			
		||||
        inherit system;
 | 
			
		||||
      };
 | 
			
		||||
    in {
 | 
			
		||||
      devShell = pkgs.mkShell {
 | 
			
		||||
          buildInputs = with pkgs; [
 | 
			
		||||
            dart
 | 
			
		||||
            sqlite
 | 
			
		||||
          ];
 | 
			
		||||
          shellHook = ''
 | 
			
		||||
            export LD_LIBRARY_PATH="${pkgs.sqlite.out}/lib:$LD_LIBRARY_PATH"
 | 
			
		||||
          '';
 | 
			
		||||
        };
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										45
									
								
								xp_dashboard/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,45 @@
 | 
			
		||||
# Miscellaneous
 | 
			
		||||
*.class
 | 
			
		||||
*.log
 | 
			
		||||
*.pyc
 | 
			
		||||
*.swp
 | 
			
		||||
.DS_Store
 | 
			
		||||
.atom/
 | 
			
		||||
.build/
 | 
			
		||||
.buildlog/
 | 
			
		||||
.history
 | 
			
		||||
.svn/
 | 
			
		||||
.swiftpm/
 | 
			
		||||
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
 | 
			
		||||
.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
 | 
			
		||||
							
								
								
									
										5
									
								
								xp_dashboard/.ignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,5 @@
 | 
			
		||||
ios/
 | 
			
		||||
android/
 | 
			
		||||
windows/
 | 
			
		||||
macos/
 | 
			
		||||
linux/
 | 
			
		||||
							
								
								
									
										45
									
								
								xp_dashboard/.metadata
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,45 @@
 | 
			
		||||
# This file tracks properties of this Flutter project.
 | 
			
		||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
 | 
			
		||||
#
 | 
			
		||||
# This file should be version controlled and should not be manually edited.
 | 
			
		||||
 | 
			
		||||
version:
 | 
			
		||||
  revision: "nixpkgs000000000000000000000000000000000"
 | 
			
		||||
  channel: "stable"
 | 
			
		||||
 | 
			
		||||
project_type: app
 | 
			
		||||
 | 
			
		||||
# Tracks metadata for the flutter migrate command
 | 
			
		||||
migration:
 | 
			
		||||
  platforms:
 | 
			
		||||
    - platform: root
 | 
			
		||||
      create_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
      base_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
    - platform: android
 | 
			
		||||
      create_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
      base_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
    - platform: ios
 | 
			
		||||
      create_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
      base_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
    - platform: linux
 | 
			
		||||
      create_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
      base_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
    - platform: macos
 | 
			
		||||
      create_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
      base_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
    - platform: web
 | 
			
		||||
      create_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
      base_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
    - platform: windows
 | 
			
		||||
      create_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
      base_revision: nixpkgs000000000000000000000000000000000
 | 
			
		||||
 | 
			
		||||
  # User provided section
 | 
			
		||||
 | 
			
		||||
  # List of Local paths (relative to this file) that should be
 | 
			
		||||
  # ignored by the migrate tool.
 | 
			
		||||
  #
 | 
			
		||||
  # Files that are not part of the templates will be ignored by default.
 | 
			
		||||
  unmanaged_files:
 | 
			
		||||
    - 'lib/main.dart'
 | 
			
		||||
    - 'ios/Runner.xcodeproj/project.pbxproj'
 | 
			
		||||
							
								
								
									
										16
									
								
								xp_dashboard/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,16 @@
 | 
			
		||||
# xp_dashboard
 | 
			
		||||
 | 
			
		||||
A new Flutter project.
 | 
			
		||||
 | 
			
		||||
## Getting Started
 | 
			
		||||
 | 
			
		||||
This project is a starting point for a Flutter application.
 | 
			
		||||
 | 
			
		||||
A few resources to get you started if this is your first Flutter project:
 | 
			
		||||
 | 
			
		||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
 | 
			
		||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
 | 
			
		||||
 | 
			
		||||
For help getting started with Flutter development, view the
 | 
			
		||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
 | 
			
		||||
samples, guidance on mobile development, and a full API reference.
 | 
			
		||||
							
								
								
									
										28
									
								
								xp_dashboard/analysis_options.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,28 @@
 | 
			
		||||
# This file configures the analyzer, which statically analyzes Dart code to
 | 
			
		||||
# check for errors, warnings, and lints.
 | 
			
		||||
#
 | 
			
		||||
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
 | 
			
		||||
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
 | 
			
		||||
# invoked from the command line by running `flutter analyze`.
 | 
			
		||||
 | 
			
		||||
# The following line activates a set of recommended lints for Flutter apps,
 | 
			
		||||
# packages, and plugins designed to encourage good coding practices.
 | 
			
		||||
include: package:flutter_lints/flutter.yaml
 | 
			
		||||
 | 
			
		||||
linter:
 | 
			
		||||
  # The lint rules applied to this project can be customized in the
 | 
			
		||||
  # section below to disable rules from the `package:flutter_lints/flutter.yaml`
 | 
			
		||||
  # included above or to enable additional rules. A list of all available lints
 | 
			
		||||
  # and their documentation is published at https://dart.dev/lints.
 | 
			
		||||
  #
 | 
			
		||||
  # Instead of disabling a lint rule for the entire project in the
 | 
			
		||||
  # section below, it can also be suppressed for a single line of code
 | 
			
		||||
  # or a specific dart file by using the `// ignore: name_of_lint` and
 | 
			
		||||
  # `// ignore_for_file: name_of_lint` syntax on the line or in the file
 | 
			
		||||
  # producing the lint.
 | 
			
		||||
  rules:
 | 
			
		||||
    # avoid_print: false  # Uncomment to disable the `avoid_print` rule
 | 
			
		||||
    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule
 | 
			
		||||
 | 
			
		||||
# Additional information about this file can be found at
 | 
			
		||||
# https://dart.dev/guides/language/analysis-options
 | 
			
		||||
							
								
								
									
										14
									
								
								xp_dashboard/android/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,14 @@
 | 
			
		||||
gradle-wrapper.jar
 | 
			
		||||
/.gradle
 | 
			
		||||
/captures/
 | 
			
		||||
/gradlew
 | 
			
		||||
/gradlew.bat
 | 
			
		||||
/local.properties
 | 
			
		||||
GeneratedPluginRegistrant.java
 | 
			
		||||
.cxx/
 | 
			
		||||
 | 
			
		||||
# Remember to never publicly share your keystore.
 | 
			
		||||
# See https://flutter.dev/to/reference-keystore
 | 
			
		||||
key.properties
 | 
			
		||||
**/*.keystore
 | 
			
		||||
**/*.jks
 | 
			
		||||
							
								
								
									
										44
									
								
								xp_dashboard/android/app/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,44 @@
 | 
			
		||||
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.xp_dashboard"
 | 
			
		||||
    compileSdk = flutter.compileSdkVersion
 | 
			
		||||
    ndkVersion = flutter.ndkVersion
 | 
			
		||||
 | 
			
		||||
    compileOptions {
 | 
			
		||||
        sourceCompatibility = JavaVersion.VERSION_11
 | 
			
		||||
        targetCompatibility = JavaVersion.VERSION_11
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    kotlinOptions {
 | 
			
		||||
        jvmTarget = JavaVersion.VERSION_11.toString()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
 | 
			
		||||
        applicationId = "com.example.xp_dashboard"
 | 
			
		||||
        // 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
 | 
			
		||||
        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.getByName("debug")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
flutter {
 | 
			
		||||
    source = "../.."
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								xp_dashboard/android/app/src/debug/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -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>
 | 
			
		||||
							
								
								
									
										45
									
								
								xp_dashboard/android/app/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,45 @@
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
 | 
			
		||||
    <application
 | 
			
		||||
        android:label="xp_dashboard"
 | 
			
		||||
        android:name="${applicationName}"
 | 
			
		||||
        android:icon="@mipmap/ic_launcher">
 | 
			
		||||
        <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>
 | 
			
		||||
        </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>
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
package com.example.xp_dashboard
 | 
			
		||||
 | 
			
		||||
import io.flutter.embedding.android.FlutterActivity
 | 
			
		||||
 | 
			
		||||
class MainActivity : FlutterActivity()
 | 
			
		||||
@ -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>
 | 
			
		||||
@ -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>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 544 B  | 
| 
		 After Width: | Height: | Size: 442 B  | 
| 
		 After Width: | Height: | Size: 721 B  | 
| 
		 After Width: | Height: | Size: 1.0 KiB  | 
| 
		 After Width: | Height: | Size: 1.4 KiB  | 
@ -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
									
								
								xp_dashboard/android/app/src/main/res/values/styles.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -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
									
								
								xp_dashboard/android/app/src/profile/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -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>
 | 
			
		||||
							
								
								
									
										21
									
								
								xp_dashboard/android/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,21 @@
 | 
			
		||||
allprojects {
 | 
			
		||||
    repositories {
 | 
			
		||||
        google()
 | 
			
		||||
        mavenCentral()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
 | 
			
		||||
rootProject.layout.buildDirectory.value(newBuildDir)
 | 
			
		||||
 | 
			
		||||
subprojects {
 | 
			
		||||
    val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
 | 
			
		||||
    project.layout.buildDirectory.value(newSubprojectBuildDir)
 | 
			
		||||
}
 | 
			
		||||
subprojects {
 | 
			
		||||
    project.evaluationDependsOn(":app")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tasks.register<Delete>("clean") {
 | 
			
		||||
    delete(rootProject.layout.buildDirectory)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								xp_dashboard/android/gradle.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,3 @@
 | 
			
		||||
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
 | 
			
		||||
android.useAndroidX=true
 | 
			
		||||
android.enableJetifier=true
 | 
			
		||||
							
								
								
									
										5
									
								
								xp_dashboard/android/gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -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.12-all.zip
 | 
			
		||||
							
								
								
									
										25
									
								
								xp_dashboard/android/settings.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,25 @@
 | 
			
		||||
pluginManagement {
 | 
			
		||||
    val flutterSdkPath = run {
 | 
			
		||||
        val properties = java.util.Properties()
 | 
			
		||||
        file("local.properties").inputStream().use { properties.load(it) }
 | 
			
		||||
        val flutterSdkPath = properties.getProperty("flutter.sdk")
 | 
			
		||||
        require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
 | 
			
		||||
        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.7.3" apply false
 | 
			
		||||
    id("org.jetbrains.kotlin.android") version "2.1.0" apply false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
include(":app")
 | 
			
		||||
							
								
								
									
										34
									
								
								xp_dashboard/ios/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,34 @@
 | 
			
		||||
**/dgph
 | 
			
		||||
*.mode1v3
 | 
			
		||||
*.mode2v3
 | 
			
		||||
*.moved-aside
 | 
			
		||||
*.pbxuser
 | 
			
		||||
*.perspectivev3
 | 
			
		||||
**/*sync/
 | 
			
		||||
.sconsign.dblite
 | 
			
		||||
.tags*
 | 
			
		||||
**/.vagrant/
 | 
			
		||||
**/DerivedData/
 | 
			
		||||
Icon?
 | 
			
		||||
**/Pods/
 | 
			
		||||
**/.symlinks/
 | 
			
		||||
profile
 | 
			
		||||
xcuserdata
 | 
			
		||||
**/.generated/
 | 
			
		||||
Flutter/App.framework
 | 
			
		||||
Flutter/Flutter.framework
 | 
			
		||||
Flutter/Flutter.podspec
 | 
			
		||||
Flutter/Generated.xcconfig
 | 
			
		||||
Flutter/ephemeral/
 | 
			
		||||
Flutter/app.flx
 | 
			
		||||
Flutter/app.zip
 | 
			
		||||
Flutter/flutter_assets/
 | 
			
		||||
Flutter/flutter_export_environment.sh
 | 
			
		||||
ServiceDefinitions.json
 | 
			
		||||
Runner/GeneratedPluginRegistrant.*
 | 
			
		||||
 | 
			
		||||
# Exceptions to above rules.
 | 
			
		||||
!default.mode1v3
 | 
			
		||||
!default.mode2v3
 | 
			
		||||
!default.pbxuser
 | 
			
		||||
!default.perspectivev3
 | 
			
		||||
							
								
								
									
										26
									
								
								xp_dashboard/ios/Flutter/AppFrameworkInfo.plist
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,26 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
<dict>
 | 
			
		||||
  <key>CFBundleDevelopmentRegion</key>
 | 
			
		||||
  <string>en</string>
 | 
			
		||||
  <key>CFBundleExecutable</key>
 | 
			
		||||
  <string>App</string>
 | 
			
		||||
  <key>CFBundleIdentifier</key>
 | 
			
		||||
  <string>io.flutter.flutter.app</string>
 | 
			
		||||
  <key>CFBundleInfoDictionaryVersion</key>
 | 
			
		||||
  <string>6.0</string>
 | 
			
		||||
  <key>CFBundleName</key>
 | 
			
		||||
  <string>App</string>
 | 
			
		||||
  <key>CFBundlePackageType</key>
 | 
			
		||||
  <string>FMWK</string>
 | 
			
		||||
  <key>CFBundleShortVersionString</key>
 | 
			
		||||
  <string>1.0</string>
 | 
			
		||||
  <key>CFBundleSignature</key>
 | 
			
		||||
  <string>????</string>
 | 
			
		||||
  <key>CFBundleVersion</key>
 | 
			
		||||
  <string>1.0</string>
 | 
			
		||||
  <key>MinimumOSVersion</key>
 | 
			
		||||
  <string>12.0</string>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
							
								
								
									
										1
									
								
								xp_dashboard/ios/Flutter/Debug.xcconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1 @@
 | 
			
		||||
#include "Generated.xcconfig"
 | 
			
		||||
							
								
								
									
										1
									
								
								xp_dashboard/ios/Flutter/Release.xcconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1 @@
 | 
			
		||||
#include "Generated.xcconfig"
 | 
			
		||||
							
								
								
									
										616
									
								
								xp_dashboard/ios/Runner.xcodeproj/project.pbxproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,616 @@
 | 
			
		||||
// !$*UTF8*$!
 | 
			
		||||
{
 | 
			
		||||
	archiveVersion = 1;
 | 
			
		||||
	classes = {
 | 
			
		||||
	};
 | 
			
		||||
	objectVersion = 54;
 | 
			
		||||
	objects = {
 | 
			
		||||
 | 
			
		||||
/* Begin PBXBuildFile section */
 | 
			
		||||
		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
 | 
			
		||||
		331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
 | 
			
		||||
		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
 | 
			
		||||
		74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
 | 
			
		||||
		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
 | 
			
		||||
		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
 | 
			
		||||
		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
 | 
			
		||||
/* End PBXBuildFile section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXContainerItemProxy section */
 | 
			
		||||
		331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
 | 
			
		||||
			isa = PBXContainerItemProxy;
 | 
			
		||||
			containerPortal = 97C146E61CF9000F007C117D /* Project object */;
 | 
			
		||||
			proxyType = 1;
 | 
			
		||||
			remoteGlobalIDString = 97C146ED1CF9000F007C117D;
 | 
			
		||||
			remoteInfo = Runner;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXContainerItemProxy section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXCopyFilesBuildPhase section */
 | 
			
		||||
		9705A1C41CF9048500538489 /* Embed Frameworks */ = {
 | 
			
		||||
			isa = PBXCopyFilesBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			dstPath = "";
 | 
			
		||||
			dstSubfolderSpec = 10;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			name = "Embed Frameworks";
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXCopyFilesBuildPhase section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXFileReference section */
 | 
			
		||||
		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
 | 
			
		||||
		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
 | 
			
		||||
		331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
 | 
			
		||||
		331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
			
		||||
		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
 | 
			
		||||
		74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
 | 
			
		||||
		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 | 
			
		||||
		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
 | 
			
		||||
		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
 | 
			
		||||
		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
 | 
			
		||||
		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
			
		||||
		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
 | 
			
		||||
		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 | 
			
		||||
		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
 | 
			
		||||
		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 | 
			
		||||
/* End PBXFileReference section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXFrameworksBuildPhase section */
 | 
			
		||||
		97C146EB1CF9000F007C117D /* Frameworks */ = {
 | 
			
		||||
			isa = PBXFrameworksBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXFrameworksBuildPhase section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXGroup section */
 | 
			
		||||
		331C8082294A63A400263BE5 /* RunnerTests */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				331C807B294A618700263BE5 /* RunnerTests.swift */,
 | 
			
		||||
			);
 | 
			
		||||
			path = RunnerTests;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		9740EEB11CF90186004384FC /* Flutter */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
 | 
			
		||||
				9740EEB21CF90195004384FC /* Debug.xcconfig */,
 | 
			
		||||
				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
 | 
			
		||||
				9740EEB31CF90195004384FC /* Generated.xcconfig */,
 | 
			
		||||
			);
 | 
			
		||||
			name = Flutter;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		97C146E51CF9000F007C117D = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				9740EEB11CF90186004384FC /* Flutter */,
 | 
			
		||||
				97C146F01CF9000F007C117D /* Runner */,
 | 
			
		||||
				97C146EF1CF9000F007C117D /* Products */,
 | 
			
		||||
				331C8082294A63A400263BE5 /* RunnerTests */,
 | 
			
		||||
			);
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		97C146EF1CF9000F007C117D /* Products */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				97C146EE1CF9000F007C117D /* Runner.app */,
 | 
			
		||||
				331C8081294A63A400263BE5 /* RunnerTests.xctest */,
 | 
			
		||||
			);
 | 
			
		||||
			name = Products;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		97C146F01CF9000F007C117D /* Runner */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				97C146FA1CF9000F007C117D /* Main.storyboard */,
 | 
			
		||||
				97C146FD1CF9000F007C117D /* Assets.xcassets */,
 | 
			
		||||
				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
 | 
			
		||||
				97C147021CF9000F007C117D /* Info.plist */,
 | 
			
		||||
				1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
 | 
			
		||||
				1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
 | 
			
		||||
				74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
 | 
			
		||||
				74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
 | 
			
		||||
			);
 | 
			
		||||
			path = Runner;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXGroup section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXNativeTarget section */
 | 
			
		||||
		331C8080294A63A400263BE5 /* RunnerTests */ = {
 | 
			
		||||
			isa = PBXNativeTarget;
 | 
			
		||||
			buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
 | 
			
		||||
			buildPhases = (
 | 
			
		||||
				331C807D294A63A400263BE5 /* Sources */,
 | 
			
		||||
				331C807F294A63A400263BE5 /* Resources */,
 | 
			
		||||
			);
 | 
			
		||||
			buildRules = (
 | 
			
		||||
			);
 | 
			
		||||
			dependencies = (
 | 
			
		||||
				331C8086294A63A400263BE5 /* PBXTargetDependency */,
 | 
			
		||||
			);
 | 
			
		||||
			name = RunnerTests;
 | 
			
		||||
			productName = RunnerTests;
 | 
			
		||||
			productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
 | 
			
		||||
			productType = "com.apple.product-type.bundle.unit-test";
 | 
			
		||||
		};
 | 
			
		||||
		97C146ED1CF9000F007C117D /* Runner */ = {
 | 
			
		||||
			isa = PBXNativeTarget;
 | 
			
		||||
			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
 | 
			
		||||
			buildPhases = (
 | 
			
		||||
				9740EEB61CF901F6004384FC /* Run Script */,
 | 
			
		||||
				97C146EA1CF9000F007C117D /* Sources */,
 | 
			
		||||
				97C146EB1CF9000F007C117D /* Frameworks */,
 | 
			
		||||
				97C146EC1CF9000F007C117D /* Resources */,
 | 
			
		||||
				9705A1C41CF9048500538489 /* Embed Frameworks */,
 | 
			
		||||
				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
 | 
			
		||||
			);
 | 
			
		||||
			buildRules = (
 | 
			
		||||
			);
 | 
			
		||||
			dependencies = (
 | 
			
		||||
			);
 | 
			
		||||
			name = Runner;
 | 
			
		||||
			productName = Runner;
 | 
			
		||||
			productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
 | 
			
		||||
			productType = "com.apple.product-type.application";
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXNativeTarget section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXProject section */
 | 
			
		||||
		97C146E61CF9000F007C117D /* Project object */ = {
 | 
			
		||||
			isa = PBXProject;
 | 
			
		||||
			attributes = {
 | 
			
		||||
				BuildIndependentTargetsInParallel = YES;
 | 
			
		||||
				LastUpgradeCheck = 1510;
 | 
			
		||||
				ORGANIZATIONNAME = "";
 | 
			
		||||
				TargetAttributes = {
 | 
			
		||||
					331C8080294A63A400263BE5 = {
 | 
			
		||||
						CreatedOnToolsVersion = 14.0;
 | 
			
		||||
						TestTargetID = 97C146ED1CF9000F007C117D;
 | 
			
		||||
					};
 | 
			
		||||
					97C146ED1CF9000F007C117D = {
 | 
			
		||||
						CreatedOnToolsVersion = 7.3.1;
 | 
			
		||||
						LastSwiftMigration = 1100;
 | 
			
		||||
					};
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
 | 
			
		||||
			compatibilityVersion = "Xcode 9.3";
 | 
			
		||||
			developmentRegion = en;
 | 
			
		||||
			hasScannedForEncodings = 0;
 | 
			
		||||
			knownRegions = (
 | 
			
		||||
				en,
 | 
			
		||||
				Base,
 | 
			
		||||
			);
 | 
			
		||||
			mainGroup = 97C146E51CF9000F007C117D;
 | 
			
		||||
			productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
 | 
			
		||||
			projectDirPath = "";
 | 
			
		||||
			projectRoot = "";
 | 
			
		||||
			targets = (
 | 
			
		||||
				97C146ED1CF9000F007C117D /* Runner */,
 | 
			
		||||
				331C8080294A63A400263BE5 /* RunnerTests */,
 | 
			
		||||
			);
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXProject section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXResourcesBuildPhase section */
 | 
			
		||||
		331C807F294A63A400263BE5 /* Resources */ = {
 | 
			
		||||
			isa = PBXResourcesBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
		97C146EC1CF9000F007C117D /* Resources */ = {
 | 
			
		||||
			isa = PBXResourcesBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
				97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
 | 
			
		||||
				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
 | 
			
		||||
				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
 | 
			
		||||
				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXResourcesBuildPhase section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXShellScriptBuildPhase section */
 | 
			
		||||
		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
 | 
			
		||||
			isa = PBXShellScriptBuildPhase;
 | 
			
		||||
			alwaysOutOfDate = 1;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			inputPaths = (
 | 
			
		||||
				"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
 | 
			
		||||
			);
 | 
			
		||||
			name = "Thin Binary";
 | 
			
		||||
			outputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
			shellPath = /bin/sh;
 | 
			
		||||
			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
 | 
			
		||||
		};
 | 
			
		||||
		9740EEB61CF901F6004384FC /* Run Script */ = {
 | 
			
		||||
			isa = PBXShellScriptBuildPhase;
 | 
			
		||||
			alwaysOutOfDate = 1;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			inputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			name = "Run Script";
 | 
			
		||||
			outputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
			shellPath = /bin/sh;
 | 
			
		||||
			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXShellScriptBuildPhase section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXSourcesBuildPhase section */
 | 
			
		||||
		331C807D294A63A400263BE5 /* Sources */ = {
 | 
			
		||||
			isa = PBXSourcesBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
				331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
		97C146EA1CF9000F007C117D /* Sources */ = {
 | 
			
		||||
			isa = PBXSourcesBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
				74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
 | 
			
		||||
				1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXSourcesBuildPhase section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXTargetDependency section */
 | 
			
		||||
		331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
 | 
			
		||||
			isa = PBXTargetDependency;
 | 
			
		||||
			target = 97C146ED1CF9000F007C117D /* Runner */;
 | 
			
		||||
			targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXTargetDependency section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXVariantGroup section */
 | 
			
		||||
		97C146FA1CF9000F007C117D /* Main.storyboard */ = {
 | 
			
		||||
			isa = PBXVariantGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				97C146FB1CF9000F007C117D /* Base */,
 | 
			
		||||
			);
 | 
			
		||||
			name = Main.storyboard;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
 | 
			
		||||
			isa = PBXVariantGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				97C147001CF9000F007C117D /* Base */,
 | 
			
		||||
			);
 | 
			
		||||
			name = LaunchScreen.storyboard;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXVariantGroup section */
 | 
			
		||||
 | 
			
		||||
/* Begin XCBuildConfiguration section */
 | 
			
		||||
		249021D3217E4FDB00AE95B9 /* Profile */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				ALWAYS_SEARCH_USER_PATHS = NO;
 | 
			
		||||
				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
 | 
			
		||||
				CLANG_ANALYZER_NONNULL = YES;
 | 
			
		||||
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
 | 
			
		||||
				CLANG_CXX_LIBRARY = "libc++";
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CLANG_ENABLE_OBJC_ARC = YES;
 | 
			
		||||
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 | 
			
		||||
				CLANG_WARN_BOOL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_COMMA = YES;
 | 
			
		||||
				CLANG_WARN_CONSTANT_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 | 
			
		||||
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 | 
			
		||||
				CLANG_WARN_EMPTY_BODY = YES;
 | 
			
		||||
				CLANG_WARN_ENUM_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_INFINITE_RECURSION = YES;
 | 
			
		||||
				CLANG_WARN_INT_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 | 
			
		||||
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 | 
			
		||||
				CLANG_WARN_STRICT_PROTOTYPES = YES;
 | 
			
		||||
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 | 
			
		||||
				CLANG_WARN_UNREACHABLE_CODE = YES;
 | 
			
		||||
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 | 
			
		||||
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 | 
			
		||||
				COPY_PHASE_STRIP = NO;
 | 
			
		||||
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 | 
			
		||||
				ENABLE_NS_ASSERTIONS = NO;
 | 
			
		||||
				ENABLE_STRICT_OBJC_MSGSEND = YES;
 | 
			
		||||
				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 | 
			
		||||
				GCC_C_LANGUAGE_STANDARD = gnu99;
 | 
			
		||||
				GCC_NO_COMMON_BLOCKS = YES;
 | 
			
		||||
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 | 
			
		||||
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
 | 
			
		||||
				GCC_WARN_UNDECLARED_SELECTOR = YES;
 | 
			
		||||
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
			
		||||
				GCC_WARN_UNUSED_FUNCTION = YES;
 | 
			
		||||
				GCC_WARN_UNUSED_VARIABLE = YES;
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 | 
			
		||||
				MTL_ENABLE_DEBUG_INFO = NO;
 | 
			
		||||
				SDKROOT = iphoneos;
 | 
			
		||||
				SUPPORTED_PLATFORMS = iphoneos;
 | 
			
		||||
				TARGETED_DEVICE_FAMILY = "1,2";
 | 
			
		||||
				VALIDATE_PRODUCT = YES;
 | 
			
		||||
			};
 | 
			
		||||
			name = Profile;
 | 
			
		||||
		};
 | 
			
		||||
		249021D4217E4FDB00AE95B9 /* Profile */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
 | 
			
		||||
				ENABLE_BITCODE = NO;
 | 
			
		||||
				INFOPLIST_FILE = Runner/Info.plist;
 | 
			
		||||
				LD_RUNPATH_SEARCH_PATHS = (
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
					"@executable_path/Frameworks",
 | 
			
		||||
				);
 | 
			
		||||
				PRODUCT_BUNDLE_IDENTIFIER = com.example.xpDashboard;
 | 
			
		||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
			
		||||
				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
				VERSIONING_SYSTEM = "apple-generic";
 | 
			
		||||
			};
 | 
			
		||||
			name = Profile;
 | 
			
		||||
		};
 | 
			
		||||
		331C8088294A63A400263BE5 /* Debug */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				BUNDLE_LOADER = "$(TEST_HOST)";
 | 
			
		||||
				CODE_SIGN_STYLE = Automatic;
 | 
			
		||||
				CURRENT_PROJECT_VERSION = 1;
 | 
			
		||||
				GENERATE_INFOPLIST_FILE = YES;
 | 
			
		||||
				MARKETING_VERSION = 1.0;
 | 
			
		||||
				PRODUCT_BUNDLE_IDENTIFIER = com.example.xpDashboard.RunnerTests;
 | 
			
		||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
			
		||||
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
 | 
			
		||||
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
 | 
			
		||||
			};
 | 
			
		||||
			name = Debug;
 | 
			
		||||
		};
 | 
			
		||||
		331C8089294A63A400263BE5 /* Release */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				BUNDLE_LOADER = "$(TEST_HOST)";
 | 
			
		||||
				CODE_SIGN_STYLE = Automatic;
 | 
			
		||||
				CURRENT_PROJECT_VERSION = 1;
 | 
			
		||||
				GENERATE_INFOPLIST_FILE = YES;
 | 
			
		||||
				MARKETING_VERSION = 1.0;
 | 
			
		||||
				PRODUCT_BUNDLE_IDENTIFIER = com.example.xpDashboard.RunnerTests;
 | 
			
		||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
 | 
			
		||||
			};
 | 
			
		||||
			name = Release;
 | 
			
		||||
		};
 | 
			
		||||
		331C808A294A63A400263BE5 /* Profile */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				BUNDLE_LOADER = "$(TEST_HOST)";
 | 
			
		||||
				CODE_SIGN_STYLE = Automatic;
 | 
			
		||||
				CURRENT_PROJECT_VERSION = 1;
 | 
			
		||||
				GENERATE_INFOPLIST_FILE = YES;
 | 
			
		||||
				MARKETING_VERSION = 1.0;
 | 
			
		||||
				PRODUCT_BUNDLE_IDENTIFIER = com.example.xpDashboard.RunnerTests;
 | 
			
		||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
 | 
			
		||||
			};
 | 
			
		||||
			name = Profile;
 | 
			
		||||
		};
 | 
			
		||||
		97C147031CF9000F007C117D /* Debug */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				ALWAYS_SEARCH_USER_PATHS = NO;
 | 
			
		||||
				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
 | 
			
		||||
				CLANG_ANALYZER_NONNULL = YES;
 | 
			
		||||
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
 | 
			
		||||
				CLANG_CXX_LIBRARY = "libc++";
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CLANG_ENABLE_OBJC_ARC = YES;
 | 
			
		||||
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 | 
			
		||||
				CLANG_WARN_BOOL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_COMMA = YES;
 | 
			
		||||
				CLANG_WARN_CONSTANT_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 | 
			
		||||
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 | 
			
		||||
				CLANG_WARN_EMPTY_BODY = YES;
 | 
			
		||||
				CLANG_WARN_ENUM_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_INFINITE_RECURSION = YES;
 | 
			
		||||
				CLANG_WARN_INT_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 | 
			
		||||
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 | 
			
		||||
				CLANG_WARN_STRICT_PROTOTYPES = YES;
 | 
			
		||||
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 | 
			
		||||
				CLANG_WARN_UNREACHABLE_CODE = YES;
 | 
			
		||||
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 | 
			
		||||
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 | 
			
		||||
				COPY_PHASE_STRIP = NO;
 | 
			
		||||
				DEBUG_INFORMATION_FORMAT = dwarf;
 | 
			
		||||
				ENABLE_STRICT_OBJC_MSGSEND = YES;
 | 
			
		||||
				ENABLE_TESTABILITY = YES;
 | 
			
		||||
				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 | 
			
		||||
				GCC_C_LANGUAGE_STANDARD = gnu99;
 | 
			
		||||
				GCC_DYNAMIC_NO_PIC = NO;
 | 
			
		||||
				GCC_NO_COMMON_BLOCKS = YES;
 | 
			
		||||
				GCC_OPTIMIZATION_LEVEL = 0;
 | 
			
		||||
				GCC_PREPROCESSOR_DEFINITIONS = (
 | 
			
		||||
					"DEBUG=1",
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
				);
 | 
			
		||||
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 | 
			
		||||
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
 | 
			
		||||
				GCC_WARN_UNDECLARED_SELECTOR = YES;
 | 
			
		||||
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
			
		||||
				GCC_WARN_UNUSED_FUNCTION = YES;
 | 
			
		||||
				GCC_WARN_UNUSED_VARIABLE = YES;
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 | 
			
		||||
				MTL_ENABLE_DEBUG_INFO = YES;
 | 
			
		||||
				ONLY_ACTIVE_ARCH = YES;
 | 
			
		||||
				SDKROOT = iphoneos;
 | 
			
		||||
				TARGETED_DEVICE_FAMILY = "1,2";
 | 
			
		||||
			};
 | 
			
		||||
			name = Debug;
 | 
			
		||||
		};
 | 
			
		||||
		97C147041CF9000F007C117D /* Release */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				ALWAYS_SEARCH_USER_PATHS = NO;
 | 
			
		||||
				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
 | 
			
		||||
				CLANG_ANALYZER_NONNULL = YES;
 | 
			
		||||
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
 | 
			
		||||
				CLANG_CXX_LIBRARY = "libc++";
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CLANG_ENABLE_OBJC_ARC = YES;
 | 
			
		||||
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 | 
			
		||||
				CLANG_WARN_BOOL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_COMMA = YES;
 | 
			
		||||
				CLANG_WARN_CONSTANT_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 | 
			
		||||
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 | 
			
		||||
				CLANG_WARN_EMPTY_BODY = YES;
 | 
			
		||||
				CLANG_WARN_ENUM_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_INFINITE_RECURSION = YES;
 | 
			
		||||
				CLANG_WARN_INT_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 | 
			
		||||
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 | 
			
		||||
				CLANG_WARN_STRICT_PROTOTYPES = YES;
 | 
			
		||||
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 | 
			
		||||
				CLANG_WARN_UNREACHABLE_CODE = YES;
 | 
			
		||||
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 | 
			
		||||
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
 | 
			
		||||
				COPY_PHASE_STRIP = NO;
 | 
			
		||||
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 | 
			
		||||
				ENABLE_NS_ASSERTIONS = NO;
 | 
			
		||||
				ENABLE_STRICT_OBJC_MSGSEND = YES;
 | 
			
		||||
				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 | 
			
		||||
				GCC_C_LANGUAGE_STANDARD = gnu99;
 | 
			
		||||
				GCC_NO_COMMON_BLOCKS = YES;
 | 
			
		||||
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 | 
			
		||||
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
 | 
			
		||||
				GCC_WARN_UNDECLARED_SELECTOR = YES;
 | 
			
		||||
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
			
		||||
				GCC_WARN_UNUSED_FUNCTION = YES;
 | 
			
		||||
				GCC_WARN_UNUSED_VARIABLE = YES;
 | 
			
		||||
				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 | 
			
		||||
				MTL_ENABLE_DEBUG_INFO = NO;
 | 
			
		||||
				SDKROOT = iphoneos;
 | 
			
		||||
				SUPPORTED_PLATFORMS = iphoneos;
 | 
			
		||||
				SWIFT_COMPILATION_MODE = wholemodule;
 | 
			
		||||
				SWIFT_OPTIMIZATION_LEVEL = "-O";
 | 
			
		||||
				TARGETED_DEVICE_FAMILY = "1,2";
 | 
			
		||||
				VALIDATE_PRODUCT = YES;
 | 
			
		||||
			};
 | 
			
		||||
			name = Release;
 | 
			
		||||
		};
 | 
			
		||||
		97C147061CF9000F007C117D /* Debug */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
 | 
			
		||||
				ENABLE_BITCODE = NO;
 | 
			
		||||
				INFOPLIST_FILE = Runner/Info.plist;
 | 
			
		||||
				LD_RUNPATH_SEARCH_PATHS = (
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
					"@executable_path/Frameworks",
 | 
			
		||||
				);
 | 
			
		||||
				PRODUCT_BUNDLE_IDENTIFIER = com.example.xpDashboard;
 | 
			
		||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
			
		||||
				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
 | 
			
		||||
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
				VERSIONING_SYSTEM = "apple-generic";
 | 
			
		||||
			};
 | 
			
		||||
			name = Debug;
 | 
			
		||||
		};
 | 
			
		||||
		97C147071CF9000F007C117D /* Release */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
 | 
			
		||||
				ENABLE_BITCODE = NO;
 | 
			
		||||
				INFOPLIST_FILE = Runner/Info.plist;
 | 
			
		||||
				LD_RUNPATH_SEARCH_PATHS = (
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
					"@executable_path/Frameworks",
 | 
			
		||||
				);
 | 
			
		||||
				PRODUCT_BUNDLE_IDENTIFIER = com.example.xpDashboard;
 | 
			
		||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
			
		||||
				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
				VERSIONING_SYSTEM = "apple-generic";
 | 
			
		||||
			};
 | 
			
		||||
			name = Release;
 | 
			
		||||
		};
 | 
			
		||||
/* End XCBuildConfiguration section */
 | 
			
		||||
 | 
			
		||||
/* Begin XCConfigurationList section */
 | 
			
		||||
		331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
 | 
			
		||||
			isa = XCConfigurationList;
 | 
			
		||||
			buildConfigurations = (
 | 
			
		||||
				331C8088294A63A400263BE5 /* Debug */,
 | 
			
		||||
				331C8089294A63A400263BE5 /* Release */,
 | 
			
		||||
				331C808A294A63A400263BE5 /* Profile */,
 | 
			
		||||
			);
 | 
			
		||||
			defaultConfigurationIsVisible = 0;
 | 
			
		||||
			defaultConfigurationName = Release;
 | 
			
		||||
		};
 | 
			
		||||
		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
 | 
			
		||||
			isa = XCConfigurationList;
 | 
			
		||||
			buildConfigurations = (
 | 
			
		||||
				97C147031CF9000F007C117D /* Debug */,
 | 
			
		||||
				97C147041CF9000F007C117D /* Release */,
 | 
			
		||||
				249021D3217E4FDB00AE95B9 /* Profile */,
 | 
			
		||||
			);
 | 
			
		||||
			defaultConfigurationIsVisible = 0;
 | 
			
		||||
			defaultConfigurationName = Release;
 | 
			
		||||
		};
 | 
			
		||||
		97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
 | 
			
		||||
			isa = XCConfigurationList;
 | 
			
		||||
			buildConfigurations = (
 | 
			
		||||
				97C147061CF9000F007C117D /* Debug */,
 | 
			
		||||
				97C147071CF9000F007C117D /* Release */,
 | 
			
		||||
				249021D4217E4FDB00AE95B9 /* Profile */,
 | 
			
		||||
			);
 | 
			
		||||
			defaultConfigurationIsVisible = 0;
 | 
			
		||||
			defaultConfigurationName = Release;
 | 
			
		||||
		};
 | 
			
		||||
/* End XCConfigurationList section */
 | 
			
		||||
	};
 | 
			
		||||
	rootObject = 97C146E61CF9000F007C117D /* Project object */;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								xp_dashboard/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,7 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<Workspace
 | 
			
		||||
   version = "1.0">
 | 
			
		||||
   <FileRef
 | 
			
		||||
      location = "self:">
 | 
			
		||||
   </FileRef>
 | 
			
		||||
</Workspace>
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
<dict>
 | 
			
		||||
	<key>IDEDidComputeMac32BitWarning</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
<dict>
 | 
			
		||||
	<key>PreviewsEnabled</key>
 | 
			
		||||
	<false/>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
@ -0,0 +1,101 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<Scheme
 | 
			
		||||
   LastUpgradeVersion = "1510"
 | 
			
		||||
   version = "1.3">
 | 
			
		||||
   <BuildAction
 | 
			
		||||
      parallelizeBuildables = "YES"
 | 
			
		||||
      buildImplicitDependencies = "YES">
 | 
			
		||||
      <BuildActionEntries>
 | 
			
		||||
         <BuildActionEntry
 | 
			
		||||
            buildForTesting = "YES"
 | 
			
		||||
            buildForRunning = "YES"
 | 
			
		||||
            buildForProfiling = "YES"
 | 
			
		||||
            buildForArchiving = "YES"
 | 
			
		||||
            buildForAnalyzing = "YES">
 | 
			
		||||
            <BuildableReference
 | 
			
		||||
               BuildableIdentifier = "primary"
 | 
			
		||||
               BlueprintIdentifier = "97C146ED1CF9000F007C117D"
 | 
			
		||||
               BuildableName = "Runner.app"
 | 
			
		||||
               BlueprintName = "Runner"
 | 
			
		||||
               ReferencedContainer = "container:Runner.xcodeproj">
 | 
			
		||||
            </BuildableReference>
 | 
			
		||||
         </BuildActionEntry>
 | 
			
		||||
      </BuildActionEntries>
 | 
			
		||||
   </BuildAction>
 | 
			
		||||
   <TestAction
 | 
			
		||||
      buildConfiguration = "Debug"
 | 
			
		||||
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
 | 
			
		||||
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
 | 
			
		||||
      customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
 | 
			
		||||
      shouldUseLaunchSchemeArgsEnv = "YES">
 | 
			
		||||
      <MacroExpansion>
 | 
			
		||||
         <BuildableReference
 | 
			
		||||
            BuildableIdentifier = "primary"
 | 
			
		||||
            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
 | 
			
		||||
            BuildableName = "Runner.app"
 | 
			
		||||
            BlueprintName = "Runner"
 | 
			
		||||
            ReferencedContainer = "container:Runner.xcodeproj">
 | 
			
		||||
         </BuildableReference>
 | 
			
		||||
      </MacroExpansion>
 | 
			
		||||
      <Testables>
 | 
			
		||||
         <TestableReference
 | 
			
		||||
            skipped = "NO"
 | 
			
		||||
            parallelizable = "YES">
 | 
			
		||||
            <BuildableReference
 | 
			
		||||
               BuildableIdentifier = "primary"
 | 
			
		||||
               BlueprintIdentifier = "331C8080294A63A400263BE5"
 | 
			
		||||
               BuildableName = "RunnerTests.xctest"
 | 
			
		||||
               BlueprintName = "RunnerTests"
 | 
			
		||||
               ReferencedContainer = "container:Runner.xcodeproj">
 | 
			
		||||
            </BuildableReference>
 | 
			
		||||
         </TestableReference>
 | 
			
		||||
      </Testables>
 | 
			
		||||
   </TestAction>
 | 
			
		||||
   <LaunchAction
 | 
			
		||||
      buildConfiguration = "Debug"
 | 
			
		||||
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
 | 
			
		||||
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
 | 
			
		||||
      customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
 | 
			
		||||
      launchStyle = "0"
 | 
			
		||||
      useCustomWorkingDirectory = "NO"
 | 
			
		||||
      ignoresPersistentStateOnLaunch = "NO"
 | 
			
		||||
      debugDocumentVersioning = "YES"
 | 
			
		||||
      debugServiceExtension = "internal"
 | 
			
		||||
      enableGPUValidationMode = "1"
 | 
			
		||||
      allowLocationSimulation = "YES">
 | 
			
		||||
      <BuildableProductRunnable
 | 
			
		||||
         runnableDebuggingMode = "0">
 | 
			
		||||
         <BuildableReference
 | 
			
		||||
            BuildableIdentifier = "primary"
 | 
			
		||||
            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
 | 
			
		||||
            BuildableName = "Runner.app"
 | 
			
		||||
            BlueprintName = "Runner"
 | 
			
		||||
            ReferencedContainer = "container:Runner.xcodeproj">
 | 
			
		||||
         </BuildableReference>
 | 
			
		||||
      </BuildableProductRunnable>
 | 
			
		||||
   </LaunchAction>
 | 
			
		||||
   <ProfileAction
 | 
			
		||||
      buildConfiguration = "Profile"
 | 
			
		||||
      shouldUseLaunchSchemeArgsEnv = "YES"
 | 
			
		||||
      savedToolIdentifier = ""
 | 
			
		||||
      useCustomWorkingDirectory = "NO"
 | 
			
		||||
      debugDocumentVersioning = "YES">
 | 
			
		||||
      <BuildableProductRunnable
 | 
			
		||||
         runnableDebuggingMode = "0">
 | 
			
		||||
         <BuildableReference
 | 
			
		||||
            BuildableIdentifier = "primary"
 | 
			
		||||
            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
 | 
			
		||||
            BuildableName = "Runner.app"
 | 
			
		||||
            BlueprintName = "Runner"
 | 
			
		||||
            ReferencedContainer = "container:Runner.xcodeproj">
 | 
			
		||||
         </BuildableReference>
 | 
			
		||||
      </BuildableProductRunnable>
 | 
			
		||||
   </ProfileAction>
 | 
			
		||||
   <AnalyzeAction
 | 
			
		||||
      buildConfiguration = "Debug">
 | 
			
		||||
   </AnalyzeAction>
 | 
			
		||||
   <ArchiveAction
 | 
			
		||||
      buildConfiguration = "Release"
 | 
			
		||||
      revealArchiveInOrganizer = "YES">
 | 
			
		||||
   </ArchiveAction>
 | 
			
		||||
</Scheme>
 | 
			
		||||
							
								
								
									
										7
									
								
								xp_dashboard/ios/Runner.xcworkspace/contents.xcworkspacedata
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,7 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<Workspace
 | 
			
		||||
   version = "1.0">
 | 
			
		||||
   <FileRef
 | 
			
		||||
      location = "group:Runner.xcodeproj">
 | 
			
		||||
   </FileRef>
 | 
			
		||||
</Workspace>
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
<dict>
 | 
			
		||||
	<key>IDEDidComputeMac32BitWarning</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
<dict>
 | 
			
		||||
	<key>PreviewsEnabled</key>
 | 
			
		||||
	<false/>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
							
								
								
									
										13
									
								
								xp_dashboard/ios/Runner/AppDelegate.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,13 @@
 | 
			
		||||
import Flutter
 | 
			
		||||
import UIKit
 | 
			
		||||
 | 
			
		||||
@main
 | 
			
		||||
@objc class AppDelegate: FlutterAppDelegate {
 | 
			
		||||
  override func application(
 | 
			
		||||
    _ application: UIApplication,
 | 
			
		||||
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
 | 
			
		||||
  ) -> Bool {
 | 
			
		||||
    GeneratedPluginRegistrant.register(with: self)
 | 
			
		||||
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,122 @@
 | 
			
		||||
{
 | 
			
		||||
  "images" : [
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "20x20",
 | 
			
		||||
      "idiom" : "iphone",
 | 
			
		||||
      "filename" : "Icon-App-20x20@2x.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "20x20",
 | 
			
		||||
      "idiom" : "iphone",
 | 
			
		||||
      "filename" : "Icon-App-20x20@3x.png",
 | 
			
		||||
      "scale" : "3x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "29x29",
 | 
			
		||||
      "idiom" : "iphone",
 | 
			
		||||
      "filename" : "Icon-App-29x29@1x.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "29x29",
 | 
			
		||||
      "idiom" : "iphone",
 | 
			
		||||
      "filename" : "Icon-App-29x29@2x.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "29x29",
 | 
			
		||||
      "idiom" : "iphone",
 | 
			
		||||
      "filename" : "Icon-App-29x29@3x.png",
 | 
			
		||||
      "scale" : "3x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "40x40",
 | 
			
		||||
      "idiom" : "iphone",
 | 
			
		||||
      "filename" : "Icon-App-40x40@2x.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "40x40",
 | 
			
		||||
      "idiom" : "iphone",
 | 
			
		||||
      "filename" : "Icon-App-40x40@3x.png",
 | 
			
		||||
      "scale" : "3x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "60x60",
 | 
			
		||||
      "idiom" : "iphone",
 | 
			
		||||
      "filename" : "Icon-App-60x60@2x.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "60x60",
 | 
			
		||||
      "idiom" : "iphone",
 | 
			
		||||
      "filename" : "Icon-App-60x60@3x.png",
 | 
			
		||||
      "scale" : "3x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "20x20",
 | 
			
		||||
      "idiom" : "ipad",
 | 
			
		||||
      "filename" : "Icon-App-20x20@1x.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "20x20",
 | 
			
		||||
      "idiom" : "ipad",
 | 
			
		||||
      "filename" : "Icon-App-20x20@2x.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "29x29",
 | 
			
		||||
      "idiom" : "ipad",
 | 
			
		||||
      "filename" : "Icon-App-29x29@1x.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "29x29",
 | 
			
		||||
      "idiom" : "ipad",
 | 
			
		||||
      "filename" : "Icon-App-29x29@2x.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "40x40",
 | 
			
		||||
      "idiom" : "ipad",
 | 
			
		||||
      "filename" : "Icon-App-40x40@1x.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "40x40",
 | 
			
		||||
      "idiom" : "ipad",
 | 
			
		||||
      "filename" : "Icon-App-40x40@2x.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "76x76",
 | 
			
		||||
      "idiom" : "ipad",
 | 
			
		||||
      "filename" : "Icon-App-76x76@1x.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "76x76",
 | 
			
		||||
      "idiom" : "ipad",
 | 
			
		||||
      "filename" : "Icon-App-76x76@2x.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "83.5x83.5",
 | 
			
		||||
      "idiom" : "ipad",
 | 
			
		||||
      "filename" : "Icon-App-83.5x83.5@2x.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "1024x1024",
 | 
			
		||||
      "idiom" : "ios-marketing",
 | 
			
		||||
      "filename" : "Icon-App-1024x1024@1x.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "info" : {
 | 
			
		||||
    "version" : 1,
 | 
			
		||||
    "author" : "xcode"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
		 After Width: | Height: | Size: 11 KiB  | 
| 
		 After Width: | Height: | Size: 295 B  | 
| 
		 After Width: | Height: | Size: 406 B  | 
| 
		 After Width: | Height: | Size: 450 B  | 
| 
		 After Width: | Height: | Size: 282 B  | 
| 
		 After Width: | Height: | Size: 462 B  | 
| 
		 After Width: | Height: | Size: 704 B  | 
| 
		 After Width: | Height: | Size: 406 B  | 
| 
		 After Width: | Height: | Size: 586 B  | 
| 
		 After Width: | Height: | Size: 862 B  | 
| 
		 After Width: | Height: | Size: 862 B  | 
| 
		 After Width: | Height: | Size: 1.6 KiB  | 
| 
		 After Width: | Height: | Size: 762 B  | 
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
| 
		 After Width: | Height: | Size: 1.4 KiB  | 
							
								
								
									
										23
									
								
								xp_dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,23 @@
 | 
			
		||||
{
 | 
			
		||||
  "images" : [
 | 
			
		||||
    {
 | 
			
		||||
      "idiom" : "universal",
 | 
			
		||||
      "filename" : "LaunchImage.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "idiom" : "universal",
 | 
			
		||||
      "filename" : "LaunchImage@2x.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "idiom" : "universal",
 | 
			
		||||
      "filename" : "LaunchImage@3x.png",
 | 
			
		||||
      "scale" : "3x"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "info" : {
 | 
			
		||||
    "version" : 1,
 | 
			
		||||
    "author" : "xcode"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								xp_dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 68 B  | 
							
								
								
									
										
											BIN
										
									
								
								xp_dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 68 B  | 
							
								
								
									
										
											BIN
										
									
								
								xp_dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 68 B  | 
							
								
								
									
										5
									
								
								xp_dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,5 @@
 | 
			
		||||
# Launch Screen Assets
 | 
			
		||||
 | 
			
		||||
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
 | 
			
		||||
 | 
			
		||||
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
 | 
			
		||||
							
								
								
									
										37
									
								
								xp_dashboard/ios/Runner/Base.lproj/LaunchScreen.storyboard
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,37 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
 | 
			
		||||
    <dependencies>
 | 
			
		||||
        <deployment identifier="iOS"/>
 | 
			
		||||
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
    <scenes>
 | 
			
		||||
        <!--View Controller-->
 | 
			
		||||
        <scene sceneID="EHf-IW-A2E">
 | 
			
		||||
            <objects>
 | 
			
		||||
                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
 | 
			
		||||
                    <layoutGuides>
 | 
			
		||||
                        <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
 | 
			
		||||
                        <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
 | 
			
		||||
                    </layoutGuides>
 | 
			
		||||
                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
 | 
			
		||||
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
 | 
			
		||||
                        <subviews>
 | 
			
		||||
                            <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
 | 
			
		||||
                            </imageView>
 | 
			
		||||
                        </subviews>
 | 
			
		||||
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
 | 
			
		||||
                        <constraints>
 | 
			
		||||
                            <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
 | 
			
		||||
                            <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
 | 
			
		||||
                        </constraints>
 | 
			
		||||
                    </view>
 | 
			
		||||
                </viewController>
 | 
			
		||||
                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
 | 
			
		||||
            </objects>
 | 
			
		||||
            <point key="canvasLocation" x="53" y="375"/>
 | 
			
		||||
        </scene>
 | 
			
		||||
    </scenes>
 | 
			
		||||
    <resources>
 | 
			
		||||
        <image name="LaunchImage" width="168" height="185"/>
 | 
			
		||||
    </resources>
 | 
			
		||||
</document>
 | 
			
		||||
							
								
								
									
										26
									
								
								xp_dashboard/ios/Runner/Base.lproj/Main.storyboard
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,26 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 | 
			
		||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
 | 
			
		||||
    <dependencies>
 | 
			
		||||
        <deployment identifier="iOS"/>
 | 
			
		||||
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
 | 
			
		||||
    </dependencies>
 | 
			
		||||
    <scenes>
 | 
			
		||||
        <!--Flutter View Controller-->
 | 
			
		||||
        <scene sceneID="tne-QT-ifu">
 | 
			
		||||
            <objects>
 | 
			
		||||
                <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
 | 
			
		||||
                    <layoutGuides>
 | 
			
		||||
                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
 | 
			
		||||
                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
 | 
			
		||||
                    </layoutGuides>
 | 
			
		||||
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
 | 
			
		||||
                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
 | 
			
		||||
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
 | 
			
		||||
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
 | 
			
		||||
                    </view>
 | 
			
		||||
                </viewController>
 | 
			
		||||
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
 | 
			
		||||
            </objects>
 | 
			
		||||
        </scene>
 | 
			
		||||
    </scenes>
 | 
			
		||||
</document>
 | 
			
		||||
							
								
								
									
										49
									
								
								xp_dashboard/ios/Runner/Info.plist
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,49 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
<dict>
 | 
			
		||||
	<key>CFBundleDevelopmentRegion</key>
 | 
			
		||||
	<string>$(DEVELOPMENT_LANGUAGE)</string>
 | 
			
		||||
	<key>CFBundleDisplayName</key>
 | 
			
		||||
	<string>Xp Dashboard</string>
 | 
			
		||||
	<key>CFBundleExecutable</key>
 | 
			
		||||
	<string>$(EXECUTABLE_NAME)</string>
 | 
			
		||||
	<key>CFBundleIdentifier</key>
 | 
			
		||||
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
 | 
			
		||||
	<key>CFBundleInfoDictionaryVersion</key>
 | 
			
		||||
	<string>6.0</string>
 | 
			
		||||
	<key>CFBundleName</key>
 | 
			
		||||
	<string>xp_dashboard</string>
 | 
			
		||||
	<key>CFBundlePackageType</key>
 | 
			
		||||
	<string>APPL</string>
 | 
			
		||||
	<key>CFBundleShortVersionString</key>
 | 
			
		||||
	<string>$(FLUTTER_BUILD_NAME)</string>
 | 
			
		||||
	<key>CFBundleSignature</key>
 | 
			
		||||
	<string>????</string>
 | 
			
		||||
	<key>CFBundleVersion</key>
 | 
			
		||||
	<string>$(FLUTTER_BUILD_NUMBER)</string>
 | 
			
		||||
	<key>LSRequiresIPhoneOS</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
	<key>UILaunchStoryboardName</key>
 | 
			
		||||
	<string>LaunchScreen</string>
 | 
			
		||||
	<key>UIMainStoryboardFile</key>
 | 
			
		||||
	<string>Main</string>
 | 
			
		||||
	<key>UISupportedInterfaceOrientations</key>
 | 
			
		||||
	<array>
 | 
			
		||||
		<string>UIInterfaceOrientationPortrait</string>
 | 
			
		||||
		<string>UIInterfaceOrientationLandscapeLeft</string>
 | 
			
		||||
		<string>UIInterfaceOrientationLandscapeRight</string>
 | 
			
		||||
	</array>
 | 
			
		||||
	<key>UISupportedInterfaceOrientations~ipad</key>
 | 
			
		||||
	<array>
 | 
			
		||||
		<string>UIInterfaceOrientationPortrait</string>
 | 
			
		||||
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
 | 
			
		||||
		<string>UIInterfaceOrientationLandscapeLeft</string>
 | 
			
		||||
		<string>UIInterfaceOrientationLandscapeRight</string>
 | 
			
		||||
	</array>
 | 
			
		||||
	<key>CADisableMinimumFrameDurationOnPhone</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
	<key>UIApplicationSupportsIndirectInputEvents</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
							
								
								
									
										1
									
								
								xp_dashboard/ios/Runner/Runner-Bridging-Header.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1 @@
 | 
			
		||||
#import "GeneratedPluginRegistrant.h"
 | 
			
		||||
							
								
								
									
										12
									
								
								xp_dashboard/ios/RunnerTests/RunnerTests.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,12 @@
 | 
			
		||||
import Flutter
 | 
			
		||||
import UIKit
 | 
			
		||||
import XCTest
 | 
			
		||||
 | 
			
		||||
class RunnerTests: XCTestCase {
 | 
			
		||||
 | 
			
		||||
  func testExample() {
 | 
			
		||||
    // If you add code to the Runner application, consider adding tests here.
 | 
			
		||||
    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								xp_dashboard/lib/main.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,39 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:provider/provider.dart';
 | 
			
		||||
import 'src/services/api_service.dart';
 | 
			
		||||
import 'src/services/dashboard_provider.dart';
 | 
			
		||||
import 'src/screens/dashboard_screen.dart';
 | 
			
		||||
import 'src/theme/app_theme.dart';
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
  runApp(const XPDashboardApp());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class XPDashboardApp extends StatelessWidget {
 | 
			
		||||
  const XPDashboardApp({super.key});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return MultiProvider(
 | 
			
		||||
      providers: [
 | 
			
		||||
        Provider<ApiService>(
 | 
			
		||||
          create: (_) => ApiService(),
 | 
			
		||||
          dispose: (_, apiService) => apiService.dispose(),
 | 
			
		||||
        ),
 | 
			
		||||
        ChangeNotifierProxyProvider<ApiService, DashboardProvider>(
 | 
			
		||||
          create: (context) => DashboardProvider(context.read<ApiService>()),
 | 
			
		||||
          update: (context, apiService, previous) =>
 | 
			
		||||
              previous ?? DashboardProvider(apiService),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
      child: MaterialApp(
 | 
			
		||||
        title: 'XP Nix Dashboard',
 | 
			
		||||
        theme: AppTheme.lightTheme,
 | 
			
		||||
        darkTheme: AppTheme.darkTheme,
 | 
			
		||||
        themeMode: ThemeMode.system,
 | 
			
		||||
        home: const DashboardScreen(),
 | 
			
		||||
        debugShowCheckedModeBanner: false,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										353
									
								
								xp_dashboard/lib/src/screens/dashboard_screen.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,353 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:provider/provider.dart';
 | 
			
		||||
import 'package:xp_models/xp_models.dart';
 | 
			
		||||
import '../services/dashboard_provider.dart';
 | 
			
		||||
import '../widgets/stats_header.dart';
 | 
			
		||||
import '../widgets/progress_card.dart';
 | 
			
		||||
import '../widgets/xp_chart.dart';
 | 
			
		||||
import '../widgets/recent_activity_card.dart';
 | 
			
		||||
import '../widgets/xp_breakdown_card.dart';
 | 
			
		||||
import '../widgets/achievements_card.dart';
 | 
			
		||||
import '../widgets/config_card.dart';
 | 
			
		||||
import '../widgets/classification_card.dart';
 | 
			
		||||
import '../widgets/logs_card.dart';
 | 
			
		||||
 | 
			
		||||
class DashboardScreen extends StatefulWidget {
 | 
			
		||||
  const DashboardScreen({super.key});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<DashboardScreen> createState() => _DashboardScreenState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _DashboardScreenState extends State<DashboardScreen> {
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    WidgetsBinding.instance.addPostFrameCallback((_) {
 | 
			
		||||
      context.read<DashboardProvider>().initialize();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        title: const Text('🎮 XP Nix Dashboard'),
 | 
			
		||||
        actions: [
 | 
			
		||||
          Consumer<DashboardProvider>(
 | 
			
		||||
            builder: (context, provider, child) {
 | 
			
		||||
              return Row(
 | 
			
		||||
                mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                children: [
 | 
			
		||||
                  // WebSocket connection indicator
 | 
			
		||||
                  Container(
 | 
			
		||||
                    width: 12,
 | 
			
		||||
                    height: 12,
 | 
			
		||||
                    margin: const EdgeInsets.only(right: 8),
 | 
			
		||||
                    decoration: BoxDecoration(
 | 
			
		||||
                      shape: BoxShape.circle,
 | 
			
		||||
                      color: provider.isWebSocketConnected
 | 
			
		||||
                          ? Colors.green
 | 
			
		||||
                          : Colors.red,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  Text(
 | 
			
		||||
                    provider.isWebSocketConnected ? 'Live' : 'Offline',
 | 
			
		||||
                    style: Theme.of(context).textTheme.bodySmall?.copyWith(
 | 
			
		||||
                      color: Colors.white70,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  const SizedBox(width: 16),
 | 
			
		||||
                  // Refresh button
 | 
			
		||||
                  IconButton(
 | 
			
		||||
                    icon: provider.isLoading
 | 
			
		||||
                        ? const SizedBox(
 | 
			
		||||
                            width: 20,
 | 
			
		||||
                            height: 20,
 | 
			
		||||
                            child: CircularProgressIndicator(
 | 
			
		||||
                              strokeWidth: 2,
 | 
			
		||||
                              valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
 | 
			
		||||
                            ),
 | 
			
		||||
                          )
 | 
			
		||||
                        : const Icon(Icons.refresh),
 | 
			
		||||
                    onPressed: provider.isLoading
 | 
			
		||||
                        ? null
 | 
			
		||||
                        : () => provider.refresh(),
 | 
			
		||||
                    tooltip: 'Refresh Dashboard',
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      body: Consumer<DashboardProvider>(
 | 
			
		||||
        builder: (context, provider, child) {
 | 
			
		||||
          if (provider.isLoading && provider.stats == null) {
 | 
			
		||||
            return const Center(
 | 
			
		||||
              child: Column(
 | 
			
		||||
                mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
                children: [
 | 
			
		||||
                  CircularProgressIndicator(),
 | 
			
		||||
                  SizedBox(height: 16),
 | 
			
		||||
                  Text('Loading dashboard...'),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (provider.error != null && provider.stats == null) {
 | 
			
		||||
            return Center(
 | 
			
		||||
              child: Column(
 | 
			
		||||
                mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
                children: [
 | 
			
		||||
                  const Icon(
 | 
			
		||||
                    Icons.error_outline,
 | 
			
		||||
                    size: 64,
 | 
			
		||||
                    color: Colors.red,
 | 
			
		||||
                  ),
 | 
			
		||||
                  const SizedBox(height: 16),
 | 
			
		||||
                  Text(
 | 
			
		||||
                    'Failed to load dashboard',
 | 
			
		||||
                    style: Theme.of(context).textTheme.headlineSmall,
 | 
			
		||||
                  ),
 | 
			
		||||
                  const SizedBox(height: 8),
 | 
			
		||||
                  Text(
 | 
			
		||||
                    provider.error!,
 | 
			
		||||
                    style: Theme.of(context).textTheme.bodyMedium,
 | 
			
		||||
                    textAlign: TextAlign.center,
 | 
			
		||||
                  ),
 | 
			
		||||
                  const SizedBox(height: 16),
 | 
			
		||||
                  ElevatedButton(
 | 
			
		||||
                    onPressed: () => provider.refresh(),
 | 
			
		||||
                    child: const Text('Retry'),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return RefreshIndicator(
 | 
			
		||||
            onRefresh: () => provider.refresh(),
 | 
			
		||||
            child: SingleChildScrollView(
 | 
			
		||||
              physics: const AlwaysScrollableScrollPhysics(),
 | 
			
		||||
              padding: const EdgeInsets.all(16),
 | 
			
		||||
              child: Column(
 | 
			
		||||
                crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                children: [
 | 
			
		||||
                  // Stats Header
 | 
			
		||||
                  StatsHeader(stats: provider.stats),
 | 
			
		||||
                  const SizedBox(height: 24),
 | 
			
		||||
                  
 | 
			
		||||
                  // Dashboard Grid
 | 
			
		||||
                  LayoutBuilder(
 | 
			
		||||
                    builder: (context, constraints) {
 | 
			
		||||
                      final isWide = constraints.maxWidth > 1200;
 | 
			
		||||
                      final isMedium = constraints.maxWidth > 800;
 | 
			
		||||
                      
 | 
			
		||||
                      if (isWide) {
 | 
			
		||||
                        return _buildWideLayout(provider);
 | 
			
		||||
                      } else if (isMedium) {
 | 
			
		||||
                        return _buildMediumLayout(provider);
 | 
			
		||||
                      } else {
 | 
			
		||||
                        return _buildNarrowLayout(provider);
 | 
			
		||||
                      }
 | 
			
		||||
                    },
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildWideLayout(DashboardProvider provider) {
 | 
			
		||||
    return Column(
 | 
			
		||||
      children: [
 | 
			
		||||
        // First row: Progress, Chart, Recent Activity
 | 
			
		||||
        Row(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Expanded(
 | 
			
		||||
              flex: 1,
 | 
			
		||||
              child: ProgressCard(stats: provider.stats),
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(width: 16),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              flex: 2,
 | 
			
		||||
              child: XPChart(history: provider.statsHistory),
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(width: 16),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              flex: 1,
 | 
			
		||||
              child: RecentActivityCard(activities: provider.stats?.recentActivity ?? []),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        const SizedBox(height: 16),
 | 
			
		||||
        
 | 
			
		||||
        // Second row: XP Breakdown, Achievements
 | 
			
		||||
        Row(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Expanded(
 | 
			
		||||
              flex: 1,
 | 
			
		||||
              child: XPBreakdownCard(breakdown: provider.xpBreakdown),
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(width: 16),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              flex: 1,
 | 
			
		||||
              child: AchievementsCard(achievements: provider.achievements),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        const SizedBox(height: 16),
 | 
			
		||||
        
 | 
			
		||||
        // Third row: Config, Classifications, Logs
 | 
			
		||||
        Row(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Expanded(
 | 
			
		||||
              flex: 1,
 | 
			
		||||
              child: ConfigCard(
 | 
			
		||||
                config: provider.config,
 | 
			
		||||
                onConfigUpdate: provider.updateConfig,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(width: 16),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              flex: 1,
 | 
			
		||||
              child: ClassificationCard(
 | 
			
		||||
                classifications: provider.classifications,
 | 
			
		||||
                unclassified: provider.unclassified,
 | 
			
		||||
                onSaveClassification: provider.saveClassification,
 | 
			
		||||
                onDeleteClassification: provider.deleteClassification,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(width: 16),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              flex: 1,
 | 
			
		||||
              child: LogsCard(
 | 
			
		||||
                logs: provider.logs,
 | 
			
		||||
                onRefresh: provider.loadLogs,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildMediumLayout(DashboardProvider provider) {
 | 
			
		||||
    return Column(
 | 
			
		||||
      children: [
 | 
			
		||||
        // First row: Progress, Chart
 | 
			
		||||
        Row(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Expanded(
 | 
			
		||||
              flex: 1,
 | 
			
		||||
              child: ProgressCard(stats: provider.stats),
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(width: 16),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              flex: 2,
 | 
			
		||||
              child: XPChart(history: provider.statsHistory),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        const SizedBox(height: 16),
 | 
			
		||||
        
 | 
			
		||||
        // Second row: Recent Activity, XP Breakdown
 | 
			
		||||
        Row(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: RecentActivityCard(activities: provider.stats?.recentActivity ?? []),
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(width: 16),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: XPBreakdownCard(breakdown: provider.xpBreakdown),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        const SizedBox(height: 16),
 | 
			
		||||
        
 | 
			
		||||
        // Third row: Achievements, Config
 | 
			
		||||
        Row(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: AchievementsCard(achievements: provider.achievements),
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(width: 16),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: ConfigCard(
 | 
			
		||||
                config: provider.config,
 | 
			
		||||
                onConfigUpdate: provider.updateConfig,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        const SizedBox(height: 16),
 | 
			
		||||
        
 | 
			
		||||
        // Fourth row: Classifications, Logs
 | 
			
		||||
        Row(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: ClassificationCard(
 | 
			
		||||
                classifications: provider.classifications,
 | 
			
		||||
                unclassified: provider.unclassified,
 | 
			
		||||
                onSaveClassification: provider.saveClassification,
 | 
			
		||||
                onDeleteClassification: provider.deleteClassification,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(width: 16),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: LogsCard(
 | 
			
		||||
                logs: provider.logs,
 | 
			
		||||
                onRefresh: provider.loadLogs,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildNarrowLayout(DashboardProvider provider) {
 | 
			
		||||
    return Column(
 | 
			
		||||
      children: [
 | 
			
		||||
        ProgressCard(stats: provider.stats),
 | 
			
		||||
        const SizedBox(height: 16),
 | 
			
		||||
        XPChart(history: provider.statsHistory),
 | 
			
		||||
        const SizedBox(height: 16),
 | 
			
		||||
        RecentActivityCard(activities: provider.stats?.recentActivity ?? []),
 | 
			
		||||
        const SizedBox(height: 16),
 | 
			
		||||
        XPBreakdownCard(breakdown: provider.xpBreakdown),
 | 
			
		||||
        const SizedBox(height: 16),
 | 
			
		||||
        AchievementsCard(achievements: provider.achievements),
 | 
			
		||||
        const SizedBox(height: 16),
 | 
			
		||||
        ConfigCard(
 | 
			
		||||
          config: provider.config,
 | 
			
		||||
          onConfigUpdate: provider.updateConfig,
 | 
			
		||||
        ),
 | 
			
		||||
        const SizedBox(height: 16),
 | 
			
		||||
        ClassificationCard(
 | 
			
		||||
          classifications: provider.classifications,
 | 
			
		||||
          unclassified: provider.unclassified,
 | 
			
		||||
          onSaveClassification: provider.saveClassification,
 | 
			
		||||
          onDeleteClassification: provider.deleteClassification,
 | 
			
		||||
        ),
 | 
			
		||||
        const SizedBox(height: 16),
 | 
			
		||||
        LogsCard(
 | 
			
		||||
          logs: provider.logs,
 | 
			
		||||
          onRefresh: provider.loadLogs,
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										244
									
								
								xp_dashboard/lib/src/services/api_service.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,244 @@
 | 
			
		||||
import 'dart:convert';
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
import 'package:http/http.dart' as http;
 | 
			
		||||
import 'package:web_socket_channel/web_socket_channel.dart';
 | 
			
		||||
import 'package:xp_models/xp_models.dart';
 | 
			
		||||
 | 
			
		||||
class ApiService {
 | 
			
		||||
  static const String _baseUrl = 'http://[::1]:8080';
 | 
			
		||||
  static const String _wsUrl = 'ws://[::1]:8080/ws';
 | 
			
		||||
  
 | 
			
		||||
  final http.Client _httpClient;
 | 
			
		||||
  WebSocketChannel? _wsChannel;
 | 
			
		||||
  
 | 
			
		||||
  ApiService({http.Client? httpClient}) : _httpClient = httpClient ?? http.Client();
 | 
			
		||||
 | 
			
		||||
  // HTTP API Methods
 | 
			
		||||
  Future<DashboardStats> getStats() async {
 | 
			
		||||
    final response = await _httpClient.get(
 | 
			
		||||
      Uri.parse('$_baseUrl/api/stats'),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode == 200) {
 | 
			
		||||
      final json = jsonDecode(response.body) as Map<String, dynamic>;
 | 
			
		||||
      return DashboardStats.fromJson(json);
 | 
			
		||||
    } else {
 | 
			
		||||
      throw ApiException('Failed to get stats: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<List<StatsHistory>> getStatsHistory({int days = 7}) async {
 | 
			
		||||
    final response = await _httpClient.get(
 | 
			
		||||
      Uri.parse('$_baseUrl/api/stats/history?days=$days'),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode == 200) {
 | 
			
		||||
      final json = jsonDecode(response.body) as List<dynamic>;
 | 
			
		||||
      return json.map((item) => StatsHistory.fromJson(item as Map<String, dynamic>)).toList();
 | 
			
		||||
    } else {
 | 
			
		||||
      throw ApiException('Failed to get stats history: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<List<Achievement>> getAchievements({int limit = 5}) async {
 | 
			
		||||
    final response = await _httpClient.get(
 | 
			
		||||
      Uri.parse('$_baseUrl/api/achievements?limit=$limit'),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode == 200) {
 | 
			
		||||
      final json = jsonDecode(response.body) as List<dynamic>;
 | 
			
		||||
      return json.map((item) => Achievement.fromJson(item as Map<String, dynamic>)).toList();
 | 
			
		||||
    } else {
 | 
			
		||||
      throw ApiException('Failed to get achievements: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<List<Activity>> getActivities({int limit = 100}) async {
 | 
			
		||||
    final response = await _httpClient.get(
 | 
			
		||||
      Uri.parse('$_baseUrl/api/activities?limit=$limit'),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode == 200) {
 | 
			
		||||
      final json = jsonDecode(response.body) as List<dynamic>;
 | 
			
		||||
      return json.map((item) => Activity.fromJson(item as Map<String, dynamic>)).toList();
 | 
			
		||||
    } else {
 | 
			
		||||
      throw ApiException('Failed to get activities: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<List<FocusSession>> getFocusSessions({int limit = 50}) async {
 | 
			
		||||
    final response = await _httpClient.get(
 | 
			
		||||
      Uri.parse('$_baseUrl/api/focus-sessions?limit=$limit'),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode == 200) {
 | 
			
		||||
      final json = jsonDecode(response.body) as List<dynamic>;
 | 
			
		||||
      return json.map((item) => FocusSession.fromJson(item as Map<String, dynamic>)).toList();
 | 
			
		||||
    } else {
 | 
			
		||||
      throw ApiException('Failed to get focus sessions: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<Map<String, int>> getXPBreakdown({String? date}) async {
 | 
			
		||||
    final url = date != null 
 | 
			
		||||
        ? '$_baseUrl/api/xp-breakdown?date=$date'
 | 
			
		||||
        : '$_baseUrl/api/xp-breakdown';
 | 
			
		||||
        
 | 
			
		||||
    final response = await _httpClient.get(
 | 
			
		||||
      Uri.parse(url),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode == 200) {
 | 
			
		||||
      final json = jsonDecode(response.body) as Map<String, dynamic>;
 | 
			
		||||
      return json.map((key, value) => MapEntry(key, value as int));
 | 
			
		||||
    } else {
 | 
			
		||||
      throw ApiException('Failed to get XP breakdown: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<SystemLogResponse> getLogs({int count = 100, LogLevel? level}) async {
 | 
			
		||||
    final url = level != null 
 | 
			
		||||
        ? '$_baseUrl/api/logs?count=$count&level=${level.name}'
 | 
			
		||||
        : '$_baseUrl/api/logs?count=$count';
 | 
			
		||||
        
 | 
			
		||||
    final response = await _httpClient.get(
 | 
			
		||||
      Uri.parse(url),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode == 200) {
 | 
			
		||||
      final json = jsonDecode(response.body) as Map<String, dynamic>;
 | 
			
		||||
      return SystemLogResponse.fromJson(json);
 | 
			
		||||
    } else {
 | 
			
		||||
      throw ApiException('Failed to get logs: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<Map<String, dynamic>> getConfig() async {
 | 
			
		||||
    final response = await _httpClient.get(
 | 
			
		||||
      Uri.parse('$_baseUrl/api/config'),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode == 200) {
 | 
			
		||||
      return jsonDecode(response.body) as Map<String, dynamic>;
 | 
			
		||||
    } else {
 | 
			
		||||
      throw ApiException('Failed to get config: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> updateConfig(Map<String, dynamic> updates) async {
 | 
			
		||||
    final response = await _httpClient.post(
 | 
			
		||||
      Uri.parse('$_baseUrl/api/config'),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
      body: jsonEncode(updates),
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode != 200) {
 | 
			
		||||
      throw ApiException('Failed to update config: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<List<ApplicationClassification>> getClassifications() async {
 | 
			
		||||
    final response = await _httpClient.get(
 | 
			
		||||
      Uri.parse('$_baseUrl/api/classifications'),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode == 200) {
 | 
			
		||||
      final json = jsonDecode(response.body) as List<dynamic>;
 | 
			
		||||
      return json.map((item) => ApplicationClassification.fromJson(item as Map<String, dynamic>)).toList();
 | 
			
		||||
    } else {
 | 
			
		||||
      throw ApiException('Failed to get classifications: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> saveClassification(ClassificationRequest request) async {
 | 
			
		||||
    final response = await _httpClient.post(
 | 
			
		||||
      Uri.parse('$_baseUrl/api/classifications'),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
      body: jsonEncode(request.toJson()),
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode != 200) {
 | 
			
		||||
      throw ApiException('Failed to save classification: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> deleteClassification(String applicationName) async {
 | 
			
		||||
    final encodedName = Uri.encodeComponent(applicationName);
 | 
			
		||||
    final response = await _httpClient.delete(
 | 
			
		||||
      Uri.parse('$_baseUrl/api/classifications/$encodedName'),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode != 200) {
 | 
			
		||||
      throw ApiException('Failed to delete classification: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<List<UnclassifiedApplication>> getUnclassified() async {
 | 
			
		||||
    final response = await _httpClient.get(
 | 
			
		||||
      Uri.parse('$_baseUrl/api/unclassified'),
 | 
			
		||||
      headers: {'Content-Type': 'application/json'},
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    if (response.statusCode == 200) {
 | 
			
		||||
      final json = jsonDecode(response.body) as List<dynamic>;
 | 
			
		||||
      return json.map((item) => UnclassifiedApplication.fromJson(item as Map<String, dynamic>)).toList();
 | 
			
		||||
    } else {
 | 
			
		||||
      throw ApiException('Failed to get unclassified applications: ${response.statusCode}');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // WebSocket Methods
 | 
			
		||||
  WebSocketChannel connectWebSocket() {
 | 
			
		||||
    _wsChannel?.sink.close();
 | 
			
		||||
    _wsChannel = WebSocketChannel.connect(Uri.parse(_wsUrl));
 | 
			
		||||
    return _wsChannel!;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void disconnectWebSocket() {
 | 
			
		||||
    _wsChannel?.sink.close();
 | 
			
		||||
    _wsChannel = null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Stream<WebSocketMessage> get webSocketStream {
 | 
			
		||||
    if (_wsChannel == null) {
 | 
			
		||||
      throw StateError('WebSocket not connected. Call connectWebSocket() first.');
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return _wsChannel!.stream.map((data) {
 | 
			
		||||
      final json = jsonDecode(data as String) as Map<String, dynamic>;
 | 
			
		||||
      return WebSocketMessage.fromJson(json);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void sendWebSocketMessage(WebSocketMessage message) {
 | 
			
		||||
    if (_wsChannel == null) {
 | 
			
		||||
      throw StateError('WebSocket not connected. Call connectWebSocket() first.');
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    _wsChannel!.sink.add(jsonEncode(message.toJson()));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void dispose() {
 | 
			
		||||
    _httpClient.close();
 | 
			
		||||
    disconnectWebSocket();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ApiException implements Exception {
 | 
			
		||||
  final String message;
 | 
			
		||||
  
 | 
			
		||||
  ApiException(this.message);
 | 
			
		||||
  
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() => 'ApiException: $message';
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										420
									
								
								xp_dashboard/lib/src/services/dashboard_provider.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,420 @@
 | 
			
		||||
import 'dart:async';
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:xp_models/xp_models.dart';
 | 
			
		||||
import 'api_service.dart';
 | 
			
		||||
 | 
			
		||||
class DashboardProvider extends ChangeNotifier {
 | 
			
		||||
  final ApiService _apiService;
 | 
			
		||||
  
 | 
			
		||||
  // State variables
 | 
			
		||||
  DashboardStats? _stats;
 | 
			
		||||
  List<StatsHistory> _statsHistory = [];
 | 
			
		||||
  List<Achievement> _achievements = [];
 | 
			
		||||
  List<Activity> _activities = [];
 | 
			
		||||
  List<FocusSession> _focusSessions = [];
 | 
			
		||||
  Map<String, int> _xpBreakdown = {};
 | 
			
		||||
  SystemLogResponse? _logs;
 | 
			
		||||
  Map<String, dynamic> _config = {};
 | 
			
		||||
  List<ApplicationClassification> _classifications = [];
 | 
			
		||||
  List<UnclassifiedApplication> _unclassified = [];
 | 
			
		||||
  
 | 
			
		||||
  // Loading states
 | 
			
		||||
  bool _isLoading = false;
 | 
			
		||||
  bool _isStatsLoading = false;
 | 
			
		||||
  bool _isHistoryLoading = false;
 | 
			
		||||
  bool _isAchievementsLoading = false;
 | 
			
		||||
  bool _isActivitiesLoading = false;
 | 
			
		||||
  bool _isFocusSessionsLoading = false;
 | 
			
		||||
  bool _isXpBreakdownLoading = false;
 | 
			
		||||
  bool _isLogsLoading = false;
 | 
			
		||||
  bool _isConfigLoading = false;
 | 
			
		||||
  bool _isClassificationsLoading = false;
 | 
			
		||||
  bool _isUnclassifiedLoading = false;
 | 
			
		||||
  
 | 
			
		||||
  // Error states
 | 
			
		||||
  String? _error;
 | 
			
		||||
  String? _statsError;
 | 
			
		||||
  String? _historyError;
 | 
			
		||||
  String? _achievementsError;
 | 
			
		||||
  String? _activitiesError;
 | 
			
		||||
  String? _focusSessionsError;
 | 
			
		||||
  String? _xpBreakdownError;
 | 
			
		||||
  String? _logsError;
 | 
			
		||||
  String? _configError;
 | 
			
		||||
  String? _classificationsError;
 | 
			
		||||
  String? _unclassifiedError;
 | 
			
		||||
  
 | 
			
		||||
  // WebSocket
 | 
			
		||||
  StreamSubscription<WebSocketMessage>? _wsSubscription;
 | 
			
		||||
  bool _isWebSocketConnected = false;
 | 
			
		||||
  
 | 
			
		||||
  DashboardProvider(this._apiService);
 | 
			
		||||
  
 | 
			
		||||
  // Getters
 | 
			
		||||
  DashboardStats? get stats => _stats;
 | 
			
		||||
  List<StatsHistory> get statsHistory => _statsHistory;
 | 
			
		||||
  List<Achievement> get achievements => _achievements;
 | 
			
		||||
  List<Activity> get activities => _activities;
 | 
			
		||||
  List<FocusSession> get focusSessions => _focusSessions;
 | 
			
		||||
  Map<String, int> get xpBreakdown => _xpBreakdown;
 | 
			
		||||
  SystemLogResponse? get logs => _logs;
 | 
			
		||||
  Map<String, dynamic> get config => _config;
 | 
			
		||||
  List<ApplicationClassification> get classifications => _classifications;
 | 
			
		||||
  List<UnclassifiedApplication> get unclassified => _unclassified;
 | 
			
		||||
  
 | 
			
		||||
  // Loading state getters
 | 
			
		||||
  bool get isLoading => _isLoading;
 | 
			
		||||
  bool get isStatsLoading => _isStatsLoading;
 | 
			
		||||
  bool get isHistoryLoading => _isHistoryLoading;
 | 
			
		||||
  bool get isAchievementsLoading => _isAchievementsLoading;
 | 
			
		||||
  bool get isActivitiesLoading => _isActivitiesLoading;
 | 
			
		||||
  bool get isFocusSessionsLoading => _isFocusSessionsLoading;
 | 
			
		||||
  bool get isXpBreakdownLoading => _isXpBreakdownLoading;
 | 
			
		||||
  bool get isLogsLoading => _isLogsLoading;
 | 
			
		||||
  bool get isConfigLoading => _isConfigLoading;
 | 
			
		||||
  bool get isClassificationsLoading => _isClassificationsLoading;
 | 
			
		||||
  bool get isUnclassifiedLoading => _isUnclassifiedLoading;
 | 
			
		||||
  
 | 
			
		||||
  // Error getters
 | 
			
		||||
  String? get error => _error;
 | 
			
		||||
  String? get statsError => _statsError;
 | 
			
		||||
  String? get historyError => _historyError;
 | 
			
		||||
  String? get achievementsError => _achievementsError;
 | 
			
		||||
  String? get activitiesError => _activitiesError;
 | 
			
		||||
  String? get focusSessionsError => _focusSessionsError;
 | 
			
		||||
  String? get xpBreakdownError => _xpBreakdownError;
 | 
			
		||||
  String? get logsError => _logsError;
 | 
			
		||||
  String? get configError => _configError;
 | 
			
		||||
  String? get classificationsError => _classificationsError;
 | 
			
		||||
  String? get unclassifiedError => _unclassifiedError;
 | 
			
		||||
  
 | 
			
		||||
  bool get isWebSocketConnected => _isWebSocketConnected;
 | 
			
		||||
  
 | 
			
		||||
  // Initialize dashboard data
 | 
			
		||||
  Future<void> initialize() async {
 | 
			
		||||
    _isLoading = true;
 | 
			
		||||
    _error = null;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      await Future.wait([
 | 
			
		||||
        loadStats(),
 | 
			
		||||
        loadStatsHistory(),
 | 
			
		||||
        loadAchievements(),
 | 
			
		||||
        loadActivities(),
 | 
			
		||||
        loadFocusSessions(),
 | 
			
		||||
        loadXPBreakdown(),
 | 
			
		||||
        loadConfig(),
 | 
			
		||||
        loadClassifications(),
 | 
			
		||||
        loadUnclassified(),
 | 
			
		||||
      ]);
 | 
			
		||||
      
 | 
			
		||||
      // Connect WebSocket after initial data load
 | 
			
		||||
      connectWebSocket();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _error = e.toString();
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isLoading = false;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // Load individual data sections
 | 
			
		||||
  Future<void> loadStats() async {
 | 
			
		||||
    _isStatsLoading = true;
 | 
			
		||||
    _statsError = null;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      _stats = await _apiService.getStats();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _statsError = e.toString();
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isStatsLoading = false;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<void> loadStatsHistory({int days = 7}) async {
 | 
			
		||||
    _isHistoryLoading = true;
 | 
			
		||||
    _historyError = null;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      _statsHistory = await _apiService.getStatsHistory(days: days);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _historyError = e.toString();
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isHistoryLoading = false;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<void> loadAchievements({int limit = 5}) async {
 | 
			
		||||
    _isAchievementsLoading = true;
 | 
			
		||||
    _achievementsError = null;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      _achievements = await _apiService.getAchievements(limit: limit);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _achievementsError = e.toString();
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isAchievementsLoading = false;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<void> loadActivities({int limit = 100}) async {
 | 
			
		||||
    _isActivitiesLoading = true;
 | 
			
		||||
    _activitiesError = null;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      _activities = await _apiService.getActivities(limit: limit);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _activitiesError = e.toString();
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isActivitiesLoading = false;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<void> loadFocusSessions({int limit = 50}) async {
 | 
			
		||||
    _isFocusSessionsLoading = true;
 | 
			
		||||
    _focusSessionsError = null;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      _focusSessions = await _apiService.getFocusSessions(limit: limit);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _focusSessionsError = e.toString();
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isFocusSessionsLoading = false;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<void> loadXPBreakdown({String? date}) async {
 | 
			
		||||
    _isXpBreakdownLoading = true;
 | 
			
		||||
    _xpBreakdownError = null;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      _xpBreakdown = await _apiService.getXPBreakdown(date: date);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _xpBreakdownError = e.toString();
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isXpBreakdownLoading = false;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<void> loadLogs({int count = 100, LogLevel? level}) async {
 | 
			
		||||
    _isLogsLoading = true;
 | 
			
		||||
    _logsError = null;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      _logs = await _apiService.getLogs(count: count, level: level);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _logsError = e.toString();
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isLogsLoading = false;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<void> loadConfig() async {
 | 
			
		||||
    _isConfigLoading = true;
 | 
			
		||||
    _configError = null;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      _config = await _apiService.getConfig();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _configError = e.toString();
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isConfigLoading = false;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<void> updateConfig(Map<String, dynamic> updates) async {
 | 
			
		||||
    try {
 | 
			
		||||
      await _apiService.updateConfig(updates);
 | 
			
		||||
      await loadConfig(); // Reload config after update
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _configError = e.toString();
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
      rethrow;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<void> loadClassifications() async {
 | 
			
		||||
    _isClassificationsLoading = true;
 | 
			
		||||
    _classificationsError = null;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      _classifications = await _apiService.getClassifications();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _classificationsError = e.toString();
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isClassificationsLoading = false;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<void> loadUnclassified() async {
 | 
			
		||||
    _isUnclassifiedLoading = true;
 | 
			
		||||
    _unclassifiedError = null;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
      _unclassified = await _apiService.getUnclassified();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _unclassifiedError = e.toString();
 | 
			
		||||
    } finally {
 | 
			
		||||
      _isUnclassifiedLoading = false;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<void> saveClassification(String applicationName, String categoryId) async {
 | 
			
		||||
    try {
 | 
			
		||||
      final request = ClassificationRequest(
 | 
			
		||||
        applicationName: applicationName,
 | 
			
		||||
        categoryId: categoryId,
 | 
			
		||||
      );
 | 
			
		||||
      await _apiService.saveClassification(request);
 | 
			
		||||
      
 | 
			
		||||
      // Reload classifications and unclassified after saving
 | 
			
		||||
      await Future.wait([
 | 
			
		||||
        loadClassifications(),
 | 
			
		||||
        loadUnclassified(),
 | 
			
		||||
      ]);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _classificationsError = e.toString();
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
      rethrow;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  Future<void> deleteClassification(String applicationName) async {
 | 
			
		||||
    try {
 | 
			
		||||
      await _apiService.deleteClassification(applicationName);
 | 
			
		||||
      
 | 
			
		||||
      // Reload classifications and unclassified after deletion
 | 
			
		||||
      await Future.wait([
 | 
			
		||||
        loadClassifications(),
 | 
			
		||||
        loadUnclassified(),
 | 
			
		||||
      ]);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      _classificationsError = e.toString();
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
      rethrow;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // WebSocket methods
 | 
			
		||||
  void connectWebSocket() {
 | 
			
		||||
    try {
 | 
			
		||||
      _apiService.connectWebSocket();
 | 
			
		||||
      _wsSubscription = _apiService.webSocketStream.listen(
 | 
			
		||||
        _handleWebSocketMessage,
 | 
			
		||||
        onError: _handleWebSocketError,
 | 
			
		||||
        onDone: _handleWebSocketDone,
 | 
			
		||||
      );
 | 
			
		||||
      _isWebSocketConnected = true;
 | 
			
		||||
      notifyListeners();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      debugPrint('Failed to connect WebSocket: $e');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  void disconnectWebSocket() {
 | 
			
		||||
    _wsSubscription?.cancel();
 | 
			
		||||
    _wsSubscription = null;
 | 
			
		||||
    _apiService.disconnectWebSocket();
 | 
			
		||||
    _isWebSocketConnected = false;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  void _handleWebSocketMessage(WebSocketMessage message) {
 | 
			
		||||
    switch (message.type) {
 | 
			
		||||
      case WebSocketMessageType.statsUpdate:
 | 
			
		||||
        if (message.data != null) {
 | 
			
		||||
          _stats = DashboardStats.fromJson(message.data!);
 | 
			
		||||
          notifyListeners();
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case WebSocketMessageType.xpBreakdownUpdate:
 | 
			
		||||
        if (message.data != null) {
 | 
			
		||||
          _xpBreakdown = message.data!.map((key, value) => MapEntry(key, value as int));
 | 
			
		||||
          notifyListeners();
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case WebSocketMessageType.achievementUnlocked:
 | 
			
		||||
        // Reload achievements when a new one is unlocked
 | 
			
		||||
        loadAchievements();
 | 
			
		||||
        break;
 | 
			
		||||
      case WebSocketMessageType.levelUp:
 | 
			
		||||
        // Reload stats when level changes
 | 
			
		||||
        loadStats();
 | 
			
		||||
        break;
 | 
			
		||||
      case WebSocketMessageType.focusSessionComplete:
 | 
			
		||||
        // Reload focus sessions and stats
 | 
			
		||||
        Future.wait([
 | 
			
		||||
          loadFocusSessions(),
 | 
			
		||||
          loadStats(),
 | 
			
		||||
        ]);
 | 
			
		||||
        break;
 | 
			
		||||
      case WebSocketMessageType.ping:
 | 
			
		||||
        // Respond to ping with pong
 | 
			
		||||
        _apiService.sendWebSocketMessage(WebSocketMessage.pong());
 | 
			
		||||
        break;
 | 
			
		||||
      case WebSocketMessageType.pong:
 | 
			
		||||
        // Handle pong response (optional)
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  void _handleWebSocketError(error) {
 | 
			
		||||
    debugPrint('WebSocket error: $error');
 | 
			
		||||
    _isWebSocketConnected = false;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    // Try to reconnect after a delay
 | 
			
		||||
    Timer(const Duration(seconds: 5), () {
 | 
			
		||||
      if (!_isWebSocketConnected) {
 | 
			
		||||
        connectWebSocket();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  void _handleWebSocketDone() {
 | 
			
		||||
    debugPrint('WebSocket connection closed');
 | 
			
		||||
    _isWebSocketConnected = false;
 | 
			
		||||
    notifyListeners();
 | 
			
		||||
    
 | 
			
		||||
    // Try to reconnect after a delay
 | 
			
		||||
    Timer(const Duration(seconds: 5), () {
 | 
			
		||||
      if (!_isWebSocketConnected) {
 | 
			
		||||
        connectWebSocket();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // Refresh all data
 | 
			
		||||
  Future<void> refresh() async {
 | 
			
		||||
    await initialize();
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  @override
 | 
			
		||||
  void dispose() {
 | 
			
		||||
    disconnectWebSocket();
 | 
			
		||||
    _apiService.dispose();
 | 
			
		||||
    super.dispose();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										248
									
								
								xp_dashboard/lib/src/theme/app_theme.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,248 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
class AppTheme {
 | 
			
		||||
  static const Color primaryColor = Color(0xFF667eea);
 | 
			
		||||
  static const Color secondaryColor = Color(0xFF764ba2);
 | 
			
		||||
  static const Color accentColor = Color(0xFF4facfe);
 | 
			
		||||
  static const Color successColor = Color(0xFF4CAF50);
 | 
			
		||||
  static const Color warningColor = Color(0xFFFF9800);
 | 
			
		||||
  static const Color errorColor = Color(0xFFF44336);
 | 
			
		||||
  static const Color infoColor = Color(0xFF2196F3);
 | 
			
		||||
  
 | 
			
		||||
  static const Color backgroundLight = Color(0xFFF5F7FA);
 | 
			
		||||
  static const Color surfaceLight = Color(0xFFFFFFFF);
 | 
			
		||||
  static const Color cardLight = Color(0xFFFFFFFF);
 | 
			
		||||
  
 | 
			
		||||
  static const Color backgroundDark = Color(0xFF121212);
 | 
			
		||||
  static const Color surfaceDark = Color(0xFF1E1E1E);
 | 
			
		||||
  static const Color cardDark = Color(0xFF2D2D2D);
 | 
			
		||||
  
 | 
			
		||||
  static ThemeData lightTheme = ThemeData(
 | 
			
		||||
    useMaterial3: true,
 | 
			
		||||
    brightness: Brightness.light,
 | 
			
		||||
    colorScheme: ColorScheme.fromSeed(
 | 
			
		||||
      seedColor: primaryColor,
 | 
			
		||||
      brightness: Brightness.light,
 | 
			
		||||
    ),
 | 
			
		||||
    scaffoldBackgroundColor: backgroundLight,
 | 
			
		||||
    cardTheme: CardThemeData(
 | 
			
		||||
      color: cardLight,
 | 
			
		||||
      elevation: 2,
 | 
			
		||||
      shadowColor: Colors.black.withOpacity(0.1),
 | 
			
		||||
      shape: RoundedRectangleBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(12),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
    appBarTheme: const AppBarTheme(
 | 
			
		||||
      backgroundColor: primaryColor,
 | 
			
		||||
      foregroundColor: Colors.white,
 | 
			
		||||
      elevation: 0,
 | 
			
		||||
      centerTitle: true,
 | 
			
		||||
    ),
 | 
			
		||||
    elevatedButtonTheme: ElevatedButtonThemeData(
 | 
			
		||||
      style: ElevatedButton.styleFrom(
 | 
			
		||||
        backgroundColor: primaryColor,
 | 
			
		||||
        foregroundColor: Colors.white,
 | 
			
		||||
        shape: RoundedRectangleBorder(
 | 
			
		||||
          borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        ),
 | 
			
		||||
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
    outlinedButtonTheme: OutlinedButtonThemeData(
 | 
			
		||||
      style: OutlinedButton.styleFrom(
 | 
			
		||||
        foregroundColor: primaryColor,
 | 
			
		||||
        side: const BorderSide(color: primaryColor),
 | 
			
		||||
        shape: RoundedRectangleBorder(
 | 
			
		||||
          borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        ),
 | 
			
		||||
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
    textButtonTheme: TextButtonThemeData(
 | 
			
		||||
      style: TextButton.styleFrom(
 | 
			
		||||
        foregroundColor: primaryColor,
 | 
			
		||||
        shape: RoundedRectangleBorder(
 | 
			
		||||
          borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        ),
 | 
			
		||||
        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
    inputDecorationTheme: InputDecorationTheme(
 | 
			
		||||
      border: OutlineInputBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        borderSide: BorderSide(color: Colors.grey.shade300),
 | 
			
		||||
      ),
 | 
			
		||||
      enabledBorder: OutlineInputBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        borderSide: BorderSide(color: Colors.grey.shade300),
 | 
			
		||||
      ),
 | 
			
		||||
      focusedBorder: OutlineInputBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        borderSide: const BorderSide(color: primaryColor, width: 2),
 | 
			
		||||
      ),
 | 
			
		||||
      errorBorder: OutlineInputBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        borderSide: const BorderSide(color: errorColor),
 | 
			
		||||
      ),
 | 
			
		||||
      focusedErrorBorder: OutlineInputBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        borderSide: const BorderSide(color: errorColor, width: 2),
 | 
			
		||||
      ),
 | 
			
		||||
      filled: true,
 | 
			
		||||
      fillColor: Colors.grey.shade50,
 | 
			
		||||
      contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
 | 
			
		||||
    ),
 | 
			
		||||
    chipTheme: ChipThemeData(
 | 
			
		||||
      backgroundColor: Colors.grey.shade100,
 | 
			
		||||
      selectedColor: primaryColor.withOpacity(0.2),
 | 
			
		||||
      labelStyle: const TextStyle(color: Colors.black87),
 | 
			
		||||
      shape: RoundedRectangleBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(20),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
  );
 | 
			
		||||
  
 | 
			
		||||
  static ThemeData darkTheme = ThemeData(
 | 
			
		||||
    useMaterial3: true,
 | 
			
		||||
    brightness: Brightness.dark,
 | 
			
		||||
    colorScheme: ColorScheme.fromSeed(
 | 
			
		||||
      seedColor: primaryColor,
 | 
			
		||||
      brightness: Brightness.dark,
 | 
			
		||||
    ),
 | 
			
		||||
    scaffoldBackgroundColor: backgroundDark,
 | 
			
		||||
    cardTheme: CardThemeData(
 | 
			
		||||
      color: cardDark,
 | 
			
		||||
      elevation: 4,
 | 
			
		||||
      shadowColor: Colors.black.withOpacity(0.3),
 | 
			
		||||
      shape: RoundedRectangleBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(12),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
    appBarTheme: const AppBarTheme(
 | 
			
		||||
      backgroundColor: surfaceDark,
 | 
			
		||||
      foregroundColor: Colors.white,
 | 
			
		||||
      elevation: 0,
 | 
			
		||||
      centerTitle: true,
 | 
			
		||||
    ),
 | 
			
		||||
    elevatedButtonTheme: ElevatedButtonThemeData(
 | 
			
		||||
      style: ElevatedButton.styleFrom(
 | 
			
		||||
        backgroundColor: primaryColor,
 | 
			
		||||
        foregroundColor: Colors.white,
 | 
			
		||||
        shape: RoundedRectangleBorder(
 | 
			
		||||
          borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        ),
 | 
			
		||||
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
    outlinedButtonTheme: OutlinedButtonThemeData(
 | 
			
		||||
      style: OutlinedButton.styleFrom(
 | 
			
		||||
        foregroundColor: primaryColor,
 | 
			
		||||
        side: const BorderSide(color: primaryColor),
 | 
			
		||||
        shape: RoundedRectangleBorder(
 | 
			
		||||
          borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        ),
 | 
			
		||||
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
    textButtonTheme: TextButtonThemeData(
 | 
			
		||||
      style: TextButton.styleFrom(
 | 
			
		||||
        foregroundColor: primaryColor,
 | 
			
		||||
        shape: RoundedRectangleBorder(
 | 
			
		||||
          borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        ),
 | 
			
		||||
        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
    inputDecorationTheme: InputDecorationTheme(
 | 
			
		||||
      border: OutlineInputBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        borderSide: BorderSide(color: Colors.grey.shade600),
 | 
			
		||||
      ),
 | 
			
		||||
      enabledBorder: OutlineInputBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        borderSide: BorderSide(color: Colors.grey.shade600),
 | 
			
		||||
      ),
 | 
			
		||||
      focusedBorder: OutlineInputBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        borderSide: const BorderSide(color: primaryColor, width: 2),
 | 
			
		||||
      ),
 | 
			
		||||
      errorBorder: OutlineInputBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        borderSide: const BorderSide(color: errorColor),
 | 
			
		||||
      ),
 | 
			
		||||
      focusedErrorBorder: OutlineInputBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        borderSide: const BorderSide(color: errorColor, width: 2),
 | 
			
		||||
      ),
 | 
			
		||||
      filled: true,
 | 
			
		||||
      fillColor: Colors.grey.shade800,
 | 
			
		||||
      contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
 | 
			
		||||
    ),
 | 
			
		||||
    chipTheme: ChipThemeData(
 | 
			
		||||
      backgroundColor: Colors.grey.shade800,
 | 
			
		||||
      selectedColor: primaryColor.withOpacity(0.3),
 | 
			
		||||
      labelStyle: const TextStyle(color: Colors.white70),
 | 
			
		||||
      shape: RoundedRectangleBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(20),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
  );
 | 
			
		||||
  
 | 
			
		||||
  // Custom gradient decorations
 | 
			
		||||
  static const LinearGradient primaryGradient = LinearGradient(
 | 
			
		||||
    colors: [primaryColor, secondaryColor],
 | 
			
		||||
    begin: Alignment.topLeft,
 | 
			
		||||
    end: Alignment.bottomRight,
 | 
			
		||||
  );
 | 
			
		||||
  
 | 
			
		||||
  static const LinearGradient accentGradient = LinearGradient(
 | 
			
		||||
    colors: [accentColor, primaryColor],
 | 
			
		||||
    begin: Alignment.topLeft,
 | 
			
		||||
    end: Alignment.bottomRight,
 | 
			
		||||
  );
 | 
			
		||||
  
 | 
			
		||||
  // XP source colors
 | 
			
		||||
  static const Map<String, Color> xpSourceColors = {
 | 
			
		||||
    'coding': Color(0xFF4CAF50),
 | 
			
		||||
    'focused_browsing': Color(0xFF2196F3),
 | 
			
		||||
    'collaboration': Color(0xFFFF9800),
 | 
			
		||||
    'meetings': Color(0xFF9C27B0),
 | 
			
		||||
    'misc': Color(0xFF607D8B),
 | 
			
		||||
    'uncategorized': Color(0xFF795548),
 | 
			
		||||
    'focus_session': Color(0xFFE91E63),
 | 
			
		||||
    'achievement': Color(0xFFFFD700),
 | 
			
		||||
    'manual_boost': Color(0xFF00BCD4),
 | 
			
		||||
  };
 | 
			
		||||
  
 | 
			
		||||
  static Color getXPSourceColor(String source) {
 | 
			
		||||
    return xpSourceColors[source] ?? Colors.grey;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // Activity type colors
 | 
			
		||||
  static Color getActivityTypeColor(String type) {
 | 
			
		||||
    switch (type.toLowerCase()) {
 | 
			
		||||
      case 'coding':
 | 
			
		||||
        return const Color(0xFF4CAF50);
 | 
			
		||||
      case 'focused_browsing':
 | 
			
		||||
        return const Color(0xFF2196F3);
 | 
			
		||||
      case 'collaboration':
 | 
			
		||||
        return const Color(0xFFFF9800);
 | 
			
		||||
      case 'meetings':
 | 
			
		||||
        return const Color(0xFF9C27B0);
 | 
			
		||||
      case 'misc':
 | 
			
		||||
        return const Color(0xFF607D8B);
 | 
			
		||||
      default:
 | 
			
		||||
        return Colors.grey;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  // Log level colors
 | 
			
		||||
  static Color getLogLevelColor(String logEntry) {
 | 
			
		||||
    if (logEntry.contains('[ERROR]')) return errorColor;
 | 
			
		||||
    if (logEntry.contains('[WARN]')) return warningColor;
 | 
			
		||||
    if (logEntry.contains('[INFO]')) return infoColor;
 | 
			
		||||
    if (logEntry.contains('[DEBUG]')) return Colors.grey;
 | 
			
		||||
    return Colors.grey;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										114
									
								
								xp_dashboard/lib/src/widgets/achievements_card.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,114 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:xp_models/xp_models.dart';
 | 
			
		||||
import '../theme/app_theme.dart';
 | 
			
		||||
 | 
			
		||||
class AchievementsCard extends StatelessWidget {
 | 
			
		||||
  final List<Achievement> achievements;
 | 
			
		||||
 | 
			
		||||
  const AchievementsCard({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.achievements,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Card(
 | 
			
		||||
      child: Padding(
 | 
			
		||||
        padding: const EdgeInsets.all(20),
 | 
			
		||||
        child: Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Row(
 | 
			
		||||
              children: [
 | 
			
		||||
                const Icon(Icons.emoji_events, color: AppTheme.primaryColor),
 | 
			
		||||
                const SizedBox(width: 8),
 | 
			
		||||
                Text(
 | 
			
		||||
                  'Recent Achievements',
 | 
			
		||||
                  style: Theme.of(context).textTheme.titleLarge?.copyWith(
 | 
			
		||||
                    fontWeight: FontWeight.bold,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(height: 16),
 | 
			
		||||
            if (achievements.isEmpty)
 | 
			
		||||
              const Center(
 | 
			
		||||
                child: Padding(
 | 
			
		||||
                  padding: EdgeInsets.all(20),
 | 
			
		||||
                  child: Text('No achievements yet'),
 | 
			
		||||
                ),
 | 
			
		||||
              )
 | 
			
		||||
            else
 | 
			
		||||
              ...achievements.take(3).map((achievement) => _AchievementItem(achievement: achievement)),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _AchievementItem extends StatelessWidget {
 | 
			
		||||
  final Achievement achievement;
 | 
			
		||||
 | 
			
		||||
  const _AchievementItem({required this.achievement});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final achievedDate = achievement.achievedAt != null 
 | 
			
		||||
        ? DateTime.parse(achievement.achievedAt!)
 | 
			
		||||
        : null;
 | 
			
		||||
 | 
			
		||||
    return Padding(
 | 
			
		||||
      padding: const EdgeInsets.symmetric(vertical: 8),
 | 
			
		||||
      child: Row(
 | 
			
		||||
        children: [
 | 
			
		||||
          Container(
 | 
			
		||||
            width: 40,
 | 
			
		||||
            height: 40,
 | 
			
		||||
            decoration: BoxDecoration(
 | 
			
		||||
              color: AppTheme.successColor.withOpacity(0.1),
 | 
			
		||||
              borderRadius: BorderRadius.circular(20),
 | 
			
		||||
            ),
 | 
			
		||||
            child: const Icon(
 | 
			
		||||
              Icons.emoji_events,
 | 
			
		||||
              color: AppTheme.successColor,
 | 
			
		||||
              size: 20,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          const SizedBox(width: 12),
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: Column(
 | 
			
		||||
              crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
              children: [
 | 
			
		||||
                Text(
 | 
			
		||||
                  achievement.name,
 | 
			
		||||
                  style: Theme.of(context).textTheme.bodyMedium?.copyWith(
 | 
			
		||||
                    fontWeight: FontWeight.w500,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                Text(
 | 
			
		||||
                  achievement.description,
 | 
			
		||||
                  style: Theme.of(context).textTheme.bodySmall?.copyWith(
 | 
			
		||||
                    color: Colors.grey.shade600,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                if (achievedDate != null)
 | 
			
		||||
                  Text(
 | 
			
		||||
                    '+${achievement.xpReward} XP • ${_formatDate(achievedDate)}',
 | 
			
		||||
                    style: Theme.of(context).textTheme.bodySmall?.copyWith(
 | 
			
		||||
                      color: AppTheme.successColor,
 | 
			
		||||
                      fontWeight: FontWeight.w500,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String _formatDate(DateTime date) {
 | 
			
		||||
    return '${date.month}/${date.day}/${date.year}';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										299
									
								
								xp_dashboard/lib/src/widgets/classification_card.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,299 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:xp_models/xp_models.dart';
 | 
			
		||||
import '../theme/app_theme.dart';
 | 
			
		||||
 | 
			
		||||
class ClassificationCard extends StatefulWidget {
 | 
			
		||||
  final List<ApplicationClassification> classifications;
 | 
			
		||||
  final List<UnclassifiedApplication> unclassified;
 | 
			
		||||
  final Future<void> Function(String, String) onSaveClassification;
 | 
			
		||||
  final Future<void> Function(String) onDeleteClassification;
 | 
			
		||||
 | 
			
		||||
  const ClassificationCard({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.classifications,
 | 
			
		||||
    required this.unclassified,
 | 
			
		||||
    required this.onSaveClassification,
 | 
			
		||||
    required this.onDeleteClassification,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<ClassificationCard> createState() => _ClassificationCardState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ClassificationCardState extends State<ClassificationCard> with SingleTickerProviderStateMixin {
 | 
			
		||||
  late TabController _tabController;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    _tabController = TabController(length: 2, vsync: this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void dispose() {
 | 
			
		||||
    _tabController.dispose();
 | 
			
		||||
    super.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Card(
 | 
			
		||||
      child: Padding(
 | 
			
		||||
        padding: const EdgeInsets.all(20),
 | 
			
		||||
        child: Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Row(
 | 
			
		||||
              children: [
 | 
			
		||||
                const Icon(Icons.category, color: AppTheme.primaryColor),
 | 
			
		||||
                const SizedBox(width: 8),
 | 
			
		||||
                Text(
 | 
			
		||||
                  'App Classifications',
 | 
			
		||||
                  style: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(height: 16),
 | 
			
		||||
            TabBar(
 | 
			
		||||
              controller: _tabController,
 | 
			
		||||
              labelColor: AppTheme.primaryColor,
 | 
			
		||||
              unselectedLabelColor: Colors.grey,
 | 
			
		||||
              indicatorColor: AppTheme.primaryColor,
 | 
			
		||||
              tabs: [
 | 
			
		||||
                Tab(text: 'Classified (${widget.classifications.length})'),
 | 
			
		||||
                Tab(text: 'Unclassified (${widget.unclassified.length})'),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(height: 16),
 | 
			
		||||
            SizedBox(
 | 
			
		||||
              height: 400,
 | 
			
		||||
              child: TabBarView(controller: _tabController, children: [_buildClassifiedTab(), _buildUnclassifiedTab()]),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildClassifiedTab() {
 | 
			
		||||
    if (widget.classifications.isEmpty) {
 | 
			
		||||
      return const Center(child: Text('No classified applications yet'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ListView.builder(
 | 
			
		||||
      itemCount: widget.classifications.length,
 | 
			
		||||
      itemBuilder: (context, index) {
 | 
			
		||||
        final classification = widget.classifications[index];
 | 
			
		||||
        return _ClassifiedItem(classification: classification, onDelete: widget.onDeleteClassification);
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildUnclassifiedTab() {
 | 
			
		||||
    if (widget.unclassified.isEmpty) {
 | 
			
		||||
      return const Center(child: Text('No unclassified applications'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ListView.builder(
 | 
			
		||||
      itemCount: widget.unclassified.length,
 | 
			
		||||
      itemBuilder: (context, index) {
 | 
			
		||||
        final app = widget.unclassified[index];
 | 
			
		||||
        return _UnclassifiedItem(app: app, onClassify: widget.onSaveClassification);
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ClassifiedItem extends StatelessWidget {
 | 
			
		||||
  final ApplicationClassification classification;
 | 
			
		||||
  final Future<void> Function(String) onDelete;
 | 
			
		||||
 | 
			
		||||
  const _ClassifiedItem({required this.classification, required this.onDelete});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final categoryIcon = _getCategoryIcon(classification.categoryId);
 | 
			
		||||
    final categoryName = _formatCategoryName(classification.categoryId);
 | 
			
		||||
 | 
			
		||||
    return Card(
 | 
			
		||||
      margin: const EdgeInsets.symmetric(vertical: 4),
 | 
			
		||||
      child: ListTile(
 | 
			
		||||
        leading: Text(categoryIcon, style: const TextStyle(fontSize: 20)),
 | 
			
		||||
        title: Text(classification.applicationName),
 | 
			
		||||
        subtitle: Text(categoryName),
 | 
			
		||||
        trailing: IconButton(
 | 
			
		||||
          icon: const Icon(Icons.delete, color: AppTheme.errorColor),
 | 
			
		||||
          onPressed: () => _showDeleteDialog(context),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _showDeleteDialog(BuildContext context) {
 | 
			
		||||
    showDialog(
 | 
			
		||||
      context: context,
 | 
			
		||||
      builder:
 | 
			
		||||
          (context) => AlertDialog(
 | 
			
		||||
            title: const Text('Delete Classification'),
 | 
			
		||||
            content: Text('Are you sure you want to remove the classification for ${classification.applicationName}?'),
 | 
			
		||||
            actions: [
 | 
			
		||||
              TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text('Cancel')),
 | 
			
		||||
              TextButton(
 | 
			
		||||
                onPressed: () {
 | 
			
		||||
                  Navigator.of(context).pop();
 | 
			
		||||
                  onDelete(classification.applicationName);
 | 
			
		||||
                },
 | 
			
		||||
                child: const Text('Delete'),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String _getCategoryIcon(String categoryId) {
 | 
			
		||||
    const icons = {
 | 
			
		||||
      'coding': '💻',
 | 
			
		||||
      'focused_browsing': '🔍',
 | 
			
		||||
      'collaboration': '🤝',
 | 
			
		||||
      'meetings': '📅',
 | 
			
		||||
      'misc': '📝',
 | 
			
		||||
      'uncategorized': '❓',
 | 
			
		||||
    };
 | 
			
		||||
    return icons[categoryId] ?? '📊';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String _formatCategoryName(String categoryId) {
 | 
			
		||||
    const names = {
 | 
			
		||||
      'coding': 'Coding',
 | 
			
		||||
      'focused_browsing': 'Focused Browsing',
 | 
			
		||||
      'collaboration': 'Collaboration',
 | 
			
		||||
      'meetings': 'Meetings',
 | 
			
		||||
      'misc': 'Miscellaneous',
 | 
			
		||||
      'uncategorized': 'Uncategorized',
 | 
			
		||||
    };
 | 
			
		||||
    return names[categoryId] ?? categoryId;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _UnclassifiedItem extends StatefulWidget {
 | 
			
		||||
  final UnclassifiedApplication app;
 | 
			
		||||
  final Future<void> Function(String, String) onClassify;
 | 
			
		||||
 | 
			
		||||
  const _UnclassifiedItem({required this.app, required this.onClassify});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<_UnclassifiedItem> createState() => _UnclassifiedItemState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _UnclassifiedItemState extends State<_UnclassifiedItem> {
 | 
			
		||||
  String? _selectedCategory;
 | 
			
		||||
  bool _isLoading = false;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final lastSeen = DateTime.parse(widget.app.lastSeen);
 | 
			
		||||
 | 
			
		||||
    return Card(
 | 
			
		||||
      margin: const EdgeInsets.symmetric(vertical: 4),
 | 
			
		||||
      child: Padding(
 | 
			
		||||
        padding: const EdgeInsets.all(16),
 | 
			
		||||
        child: Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Row(
 | 
			
		||||
              children: [
 | 
			
		||||
                Expanded(
 | 
			
		||||
                  child: Column(
 | 
			
		||||
                    crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
                    children: [
 | 
			
		||||
                      Text(
 | 
			
		||||
                        widget.app.applicationName,
 | 
			
		||||
                        style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16),
 | 
			
		||||
                      ),
 | 
			
		||||
                      Text(
 | 
			
		||||
                        '${widget.app.occurrenceCount} times • Last: ${_formatDate(lastSeen)}',
 | 
			
		||||
                        style: TextStyle(color: Colors.grey.shade600, fontSize: 12),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(height: 12),
 | 
			
		||||
            Row(
 | 
			
		||||
              children: [
 | 
			
		||||
                Expanded(
 | 
			
		||||
                  child: DropdownButtonFormField<String>(
 | 
			
		||||
                    value: _selectedCategory,
 | 
			
		||||
                    decoration: const InputDecoration(
 | 
			
		||||
                      labelText: 'Select category',
 | 
			
		||||
                      border: OutlineInputBorder(),
 | 
			
		||||
                      contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
 | 
			
		||||
                    ),
 | 
			
		||||
                    items: const [
 | 
			
		||||
                      DropdownMenuItem(value: 'coding', child: Text('💻 Coding')),
 | 
			
		||||
                      DropdownMenuItem(value: 'focused_browsing', child: Text('🔍 Focused Browsing')),
 | 
			
		||||
                      DropdownMenuItem(value: 'collaboration', child: Text('🤝 Collaboration')),
 | 
			
		||||
                      DropdownMenuItem(value: 'meetings', child: Text('📅 Meetings')),
 | 
			
		||||
                      DropdownMenuItem(value: 'misc', child: Text('📝 Miscellaneous')),
 | 
			
		||||
                      DropdownMenuItem(value: 'uncategorized', child: Text('❓ Uncategorized')),
 | 
			
		||||
                    ],
 | 
			
		||||
                    onChanged: (value) {
 | 
			
		||||
                      setState(() {
 | 
			
		||||
                        _selectedCategory = value;
 | 
			
		||||
                      });
 | 
			
		||||
                    },
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                const SizedBox(width: 12),
 | 
			
		||||
                ElevatedButton(
 | 
			
		||||
                  onPressed: _selectedCategory == null || _isLoading ? null : _classifyApp,
 | 
			
		||||
                  child:
 | 
			
		||||
                      _isLoading
 | 
			
		||||
                          ? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2))
 | 
			
		||||
                          : const Text('Classify'),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _classifyApp() async {
 | 
			
		||||
    if (_selectedCategory == null) return;
 | 
			
		||||
 | 
			
		||||
    setState(() {
 | 
			
		||||
      _isLoading = true;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      await widget.onClassify(widget.app.applicationName, _selectedCategory!);
 | 
			
		||||
      if (mounted) {
 | 
			
		||||
        ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
          SnackBar(
 | 
			
		||||
            content: Text('${widget.app.applicationName} classified successfully'),
 | 
			
		||||
            backgroundColor: AppTheme.successColor,
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      if (mounted) {
 | 
			
		||||
        ScaffoldMessenger.of(
 | 
			
		||||
          context,
 | 
			
		||||
        ).showSnackBar(SnackBar(content: Text('Failed to classify: $e'), backgroundColor: AppTheme.errorColor));
 | 
			
		||||
      }
 | 
			
		||||
    } finally {
 | 
			
		||||
      if (mounted) {
 | 
			
		||||
        setState(() {
 | 
			
		||||
          _isLoading = false;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String _formatDate(DateTime date) {
 | 
			
		||||
    return '${date.month}/${date.day}/${date.year}';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										219
									
								
								xp_dashboard/lib/src/widgets/config_card.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,219 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter/services.dart';
 | 
			
		||||
import '../theme/app_theme.dart';
 | 
			
		||||
 | 
			
		||||
class ConfigCard extends StatefulWidget {
 | 
			
		||||
  final Map<String, dynamic> config;
 | 
			
		||||
  final Future<void> Function(Map<String, dynamic>) onConfigUpdate;
 | 
			
		||||
 | 
			
		||||
  const ConfigCard({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.config,
 | 
			
		||||
    required this.onConfigUpdate,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<ConfigCard> createState() => _ConfigCardState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ConfigCardState extends State<ConfigCard> {
 | 
			
		||||
  final _formKey = GlobalKey<FormState>();
 | 
			
		||||
  late TextEditingController _codingXpController;
 | 
			
		||||
  late TextEditingController _researchXpController;
 | 
			
		||||
  late TextEditingController _meetingXpController;
 | 
			
		||||
  late TextEditingController _focusBonusController;
 | 
			
		||||
  bool _isLoading = false;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    _initializeControllers();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _initializeControllers() {
 | 
			
		||||
    final xpRewards = widget.config['xp_rewards'] as Map<String, dynamic>? ?? {};
 | 
			
		||||
    final baseMultipliers = xpRewards['base_multipliers'] as Map<String, dynamic>? ?? {};
 | 
			
		||||
    final focusBonuses = xpRewards['focus_session_bonuses'] as Map<String, dynamic>? ?? {};
 | 
			
		||||
 | 
			
		||||
    _codingXpController = TextEditingController(
 | 
			
		||||
      text: (baseMultipliers['coding'] ?? 10).toString(),
 | 
			
		||||
    );
 | 
			
		||||
    _researchXpController = TextEditingController(
 | 
			
		||||
      text: (baseMultipliers['research'] ?? 8).toString(),
 | 
			
		||||
    );
 | 
			
		||||
    _meetingXpController = TextEditingController(
 | 
			
		||||
      text: (baseMultipliers['meeting'] ?? 3).toString(),
 | 
			
		||||
    );
 | 
			
		||||
    _focusBonusController = TextEditingController(
 | 
			
		||||
      text: (focusBonuses['base_xp_per_minute'] ?? 5).toString(),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void didUpdateWidget(ConfigCard oldWidget) {
 | 
			
		||||
    super.didUpdateWidget(oldWidget);
 | 
			
		||||
    if (widget.config != oldWidget.config) {
 | 
			
		||||
      _initializeControllers();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void dispose() {
 | 
			
		||||
    _codingXpController.dispose();
 | 
			
		||||
    _researchXpController.dispose();
 | 
			
		||||
    _meetingXpController.dispose();
 | 
			
		||||
    _focusBonusController.dispose();
 | 
			
		||||
    super.dispose();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> _saveConfig() async {
 | 
			
		||||
    if (!_formKey.currentState!.validate()) return;
 | 
			
		||||
 | 
			
		||||
    setState(() {
 | 
			
		||||
      _isLoading = true;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      final updates = {
 | 
			
		||||
        'xp_rewards.base_multipliers.coding': int.parse(_codingXpController.text),
 | 
			
		||||
        'xp_rewards.base_multipliers.research': int.parse(_researchXpController.text),
 | 
			
		||||
        'xp_rewards.base_multipliers.meeting': int.parse(_meetingXpController.text),
 | 
			
		||||
        'xp_rewards.focus_session_bonuses.base_xp_per_minute': int.parse(_focusBonusController.text),
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      await widget.onConfigUpdate(updates);
 | 
			
		||||
 | 
			
		||||
      if (mounted) {
 | 
			
		||||
        ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
          const SnackBar(
 | 
			
		||||
            content: Text('Configuration saved successfully!'),
 | 
			
		||||
            backgroundColor: AppTheme.successColor,
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      if (mounted) {
 | 
			
		||||
        ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
          SnackBar(
 | 
			
		||||
            content: Text('Failed to save configuration: $e'),
 | 
			
		||||
            backgroundColor: AppTheme.errorColor,
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    } finally {
 | 
			
		||||
      if (mounted) {
 | 
			
		||||
        setState(() {
 | 
			
		||||
          _isLoading = false;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Card(
 | 
			
		||||
      child: Padding(
 | 
			
		||||
        padding: const EdgeInsets.all(20),
 | 
			
		||||
        child: Form(
 | 
			
		||||
          key: _formKey,
 | 
			
		||||
          child: Column(
 | 
			
		||||
            crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
            children: [
 | 
			
		||||
              Row(
 | 
			
		||||
                children: [
 | 
			
		||||
                  const Icon(Icons.settings, color: AppTheme.primaryColor),
 | 
			
		||||
                  const SizedBox(width: 8),
 | 
			
		||||
                  Text(
 | 
			
		||||
                    'XP Configuration',
 | 
			
		||||
                    style: Theme.of(context).textTheme.titleLarge?.copyWith(
 | 
			
		||||
                      fontWeight: FontWeight.bold,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
              const SizedBox(height: 16),
 | 
			
		||||
              _ConfigField(
 | 
			
		||||
                label: 'Coding XP Multiplier',
 | 
			
		||||
                controller: _codingXpController,
 | 
			
		||||
                icon: Icons.code,
 | 
			
		||||
              ),
 | 
			
		||||
              const SizedBox(height: 12),
 | 
			
		||||
              _ConfigField(
 | 
			
		||||
                label: 'Research XP Multiplier',
 | 
			
		||||
                controller: _researchXpController,
 | 
			
		||||
                icon: Icons.search,
 | 
			
		||||
              ),
 | 
			
		||||
              const SizedBox(height: 12),
 | 
			
		||||
              _ConfigField(
 | 
			
		||||
                label: 'Meeting XP Multiplier',
 | 
			
		||||
                controller: _meetingXpController,
 | 
			
		||||
                icon: Icons.groups,
 | 
			
		||||
              ),
 | 
			
		||||
              const SizedBox(height: 12),
 | 
			
		||||
              _ConfigField(
 | 
			
		||||
                label: 'Focus Bonus (XP/min)',
 | 
			
		||||
                controller: _focusBonusController,
 | 
			
		||||
                icon: Icons.psychology,
 | 
			
		||||
              ),
 | 
			
		||||
              const SizedBox(height: 20),
 | 
			
		||||
              SizedBox(
 | 
			
		||||
                width: double.infinity,
 | 
			
		||||
                child: ElevatedButton(
 | 
			
		||||
                  onPressed: _isLoading ? null : _saveConfig,
 | 
			
		||||
                  child: _isLoading
 | 
			
		||||
                      ? const SizedBox(
 | 
			
		||||
                          height: 20,
 | 
			
		||||
                          width: 20,
 | 
			
		||||
                          child: CircularProgressIndicator(
 | 
			
		||||
                            strokeWidth: 2,
 | 
			
		||||
                            valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
 | 
			
		||||
                          ),
 | 
			
		||||
                        )
 | 
			
		||||
                      : const Text('Save Configuration'),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ConfigField extends StatelessWidget {
 | 
			
		||||
  final String label;
 | 
			
		||||
  final TextEditingController controller;
 | 
			
		||||
  final IconData icon;
 | 
			
		||||
 | 
			
		||||
  const _ConfigField({
 | 
			
		||||
    required this.label,
 | 
			
		||||
    required this.controller,
 | 
			
		||||
    required this.icon,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return TextFormField(
 | 
			
		||||
      controller: controller,
 | 
			
		||||
      decoration: InputDecoration(
 | 
			
		||||
        labelText: label,
 | 
			
		||||
        prefixIcon: Icon(icon, color: AppTheme.primaryColor),
 | 
			
		||||
        border: const OutlineInputBorder(),
 | 
			
		||||
      ),
 | 
			
		||||
      keyboardType: TextInputType.number,
 | 
			
		||||
      inputFormatters: [
 | 
			
		||||
        FilteringTextInputFormatter.digitsOnly,
 | 
			
		||||
      ],
 | 
			
		||||
      validator: (value) {
 | 
			
		||||
        if (value == null || value.isEmpty) {
 | 
			
		||||
          return 'Please enter a value';
 | 
			
		||||
        }
 | 
			
		||||
        final intValue = int.tryParse(value);
 | 
			
		||||
        if (intValue == null || intValue < 0) {
 | 
			
		||||
          return 'Please enter a valid positive number';
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										150
									
								
								xp_dashboard/lib/src/widgets/logs_card.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,150 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:xp_models/xp_models.dart';
 | 
			
		||||
import '../theme/app_theme.dart';
 | 
			
		||||
 | 
			
		||||
class LogsCard extends StatefulWidget {
 | 
			
		||||
  final SystemLogResponse? logs;
 | 
			
		||||
  final Future<void> Function({int count, LogLevel? level}) onRefresh;
 | 
			
		||||
 | 
			
		||||
  const LogsCard({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.logs,
 | 
			
		||||
    required this.onRefresh,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<LogsCard> createState() => _LogsCardState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _LogsCardState extends State<LogsCard> {
 | 
			
		||||
  LogLevel? _selectedLevel;
 | 
			
		||||
  bool _isRefreshing = false;
 | 
			
		||||
 | 
			
		||||
  Future<void> _refreshLogs() async {
 | 
			
		||||
    setState(() {
 | 
			
		||||
      _isRefreshing = true;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      await widget.onRefresh(count: 50, level: _selectedLevel);
 | 
			
		||||
    } finally {
 | 
			
		||||
      if (mounted) {
 | 
			
		||||
        setState(() {
 | 
			
		||||
          _isRefreshing = false;
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Card(
 | 
			
		||||
      child: Padding(
 | 
			
		||||
        padding: const EdgeInsets.all(20),
 | 
			
		||||
        child: Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Row(
 | 
			
		||||
              children: [
 | 
			
		||||
                const Icon(Icons.article, color: AppTheme.primaryColor),
 | 
			
		||||
                const SizedBox(width: 8),
 | 
			
		||||
                Expanded(
 | 
			
		||||
                  child: Text(
 | 
			
		||||
                    'System Logs',
 | 
			
		||||
                    style: Theme.of(context).textTheme.titleLarge?.copyWith(
 | 
			
		||||
                      fontWeight: FontWeight.bold,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                DropdownButton<LogLevel?>(
 | 
			
		||||
                  value: _selectedLevel,
 | 
			
		||||
                  hint: const Text('All'),
 | 
			
		||||
                  items: [
 | 
			
		||||
                    const DropdownMenuItem<LogLevel?>(
 | 
			
		||||
                      value: null,
 | 
			
		||||
                      child: Text('All'),
 | 
			
		||||
                    ),
 | 
			
		||||
                    ...LogLevel.values.map((level) => DropdownMenuItem<LogLevel?>(
 | 
			
		||||
                      value: level,
 | 
			
		||||
                      child: Text(level.name.toUpperCase()),
 | 
			
		||||
                    )),
 | 
			
		||||
                  ],
 | 
			
		||||
                  onChanged: (LogLevel? newLevel) {
 | 
			
		||||
                    setState(() {
 | 
			
		||||
                      _selectedLevel = newLevel;
 | 
			
		||||
                    });
 | 
			
		||||
                    _refreshLogs();
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
                const SizedBox(width: 8),
 | 
			
		||||
                IconButton(
 | 
			
		||||
                  icon: _isRefreshing
 | 
			
		||||
                      ? const SizedBox(
 | 
			
		||||
                          width: 20,
 | 
			
		||||
                          height: 20,
 | 
			
		||||
                          child: CircularProgressIndicator(strokeWidth: 2),
 | 
			
		||||
                        )
 | 
			
		||||
                      : const Icon(Icons.refresh),
 | 
			
		||||
                  onPressed: _isRefreshing ? null : _refreshLogs,
 | 
			
		||||
                  tooltip: 'Refresh Logs',
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(height: 16),
 | 
			
		||||
            Container(
 | 
			
		||||
              height: 300,
 | 
			
		||||
              decoration: BoxDecoration(
 | 
			
		||||
                color: Colors.grey.shade50,
 | 
			
		||||
                borderRadius: BorderRadius.circular(8),
 | 
			
		||||
                border: Border.all(color: Colors.grey.shade300),
 | 
			
		||||
              ),
 | 
			
		||||
              child: widget.logs == null
 | 
			
		||||
                  ? const Center(child: CircularProgressIndicator())
 | 
			
		||||
                  : widget.logs!.logs.isEmpty
 | 
			
		||||
                      ? const Center(child: Text('No logs available'))
 | 
			
		||||
                      : ListView.builder(
 | 
			
		||||
                          padding: const EdgeInsets.all(8),
 | 
			
		||||
                          itemCount: widget.logs!.logs.length,
 | 
			
		||||
                          itemBuilder: (context, index) {
 | 
			
		||||
                            final log = widget.logs!.logs[index];
 | 
			
		||||
                            return _LogEntry(log: log);
 | 
			
		||||
                          },
 | 
			
		||||
                        ),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _LogEntry extends StatelessWidget {
 | 
			
		||||
  final String log;
 | 
			
		||||
 | 
			
		||||
  const _LogEntry({required this.log});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final color = _getLogColor(log);
 | 
			
		||||
    
 | 
			
		||||
    return Padding(
 | 
			
		||||
      padding: const EdgeInsets.symmetric(vertical: 2),
 | 
			
		||||
      child: Text(
 | 
			
		||||
        log,
 | 
			
		||||
        style: TextStyle(
 | 
			
		||||
          fontFamily: 'monospace',
 | 
			
		||||
          fontSize: 12,
 | 
			
		||||
          color: color,
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Color _getLogColor(String logEntry) {
 | 
			
		||||
    if (logEntry.contains('[ERROR]')) return AppTheme.errorColor;
 | 
			
		||||
    if (logEntry.contains('[WARN]')) return AppTheme.warningColor;
 | 
			
		||||
    if (logEntry.contains('[INFO]')) return AppTheme.infoColor;
 | 
			
		||||
    if (logEntry.contains('[DEBUG]')) return Colors.grey;
 | 
			
		||||
    return Colors.black87;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										162
									
								
								xp_dashboard/lib/src/widgets/progress_card.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,162 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:xp_models/xp_models.dart';
 | 
			
		||||
import '../theme/app_theme.dart';
 | 
			
		||||
 | 
			
		||||
class ProgressCard extends StatelessWidget {
 | 
			
		||||
  final DashboardStats? stats;
 | 
			
		||||
 | 
			
		||||
  const ProgressCard({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.stats,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Card(
 | 
			
		||||
      child: Padding(
 | 
			
		||||
        padding: const EdgeInsets.all(20),
 | 
			
		||||
        child: Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Row(
 | 
			
		||||
              children: [
 | 
			
		||||
                const Icon(Icons.analytics, color: AppTheme.primaryColor),
 | 
			
		||||
                const SizedBox(width: 8),
 | 
			
		||||
                Text(
 | 
			
		||||
                  'Today\'s Progress',
 | 
			
		||||
                  style: Theme.of(context).textTheme.titleLarge?.copyWith(
 | 
			
		||||
                    fontWeight: FontWeight.bold,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(height: 24),
 | 
			
		||||
            if (stats != null) ...[
 | 
			
		||||
              _ProgressItem(
 | 
			
		||||
                label: 'Focus Time',
 | 
			
		||||
                value: _formatDuration(stats!.today.focusTime),
 | 
			
		||||
                progress: _calculateProgress(stats!.today.focusTime, 8 * 3600), // 8 hours target
 | 
			
		||||
                color: AppTheme.successColor,
 | 
			
		||||
                icon: Icons.psychology,
 | 
			
		||||
              ),
 | 
			
		||||
              const SizedBox(height: 16),
 | 
			
		||||
              _ProgressItem(
 | 
			
		||||
                label: 'Meeting Time',
 | 
			
		||||
                value: _formatDuration(stats!.today.meetingTime),
 | 
			
		||||
                progress: _calculateProgress(stats!.today.meetingTime, 4 * 3600), // 4 hours target
 | 
			
		||||
                color: AppTheme.infoColor,
 | 
			
		||||
                icon: Icons.groups,
 | 
			
		||||
              ),
 | 
			
		||||
              const SizedBox(height: 16),
 | 
			
		||||
              _ProgressItem(
 | 
			
		||||
                label: 'Focus Sessions',
 | 
			
		||||
                value: stats!.today.focusSessions.toString(),
 | 
			
		||||
                progress: _calculateProgress(stats!.today.focusSessions, 8), // 8 sessions target
 | 
			
		||||
                color: AppTheme.warningColor,
 | 
			
		||||
                icon: Icons.timer,
 | 
			
		||||
                isCount: true,
 | 
			
		||||
              ),
 | 
			
		||||
            ] else ...[
 | 
			
		||||
              const Center(
 | 
			
		||||
                child: CircularProgressIndicator(),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String _formatDuration(int seconds) {
 | 
			
		||||
    final hours = seconds ~/ 3600;
 | 
			
		||||
    final minutes = (seconds % 3600) ~/ 60;
 | 
			
		||||
    return '${hours}h ${minutes}m';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  double _calculateProgress(int current, int target) {
 | 
			
		||||
    if (target == 0) return 0.0;
 | 
			
		||||
    return (current / target).clamp(0.0, 1.0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ProgressItem extends StatelessWidget {
 | 
			
		||||
  final String label;
 | 
			
		||||
  final String value;
 | 
			
		||||
  final double progress;
 | 
			
		||||
  final Color color;
 | 
			
		||||
  final IconData icon;
 | 
			
		||||
  final bool isCount;
 | 
			
		||||
 | 
			
		||||
  const _ProgressItem({
 | 
			
		||||
    required this.label,
 | 
			
		||||
    required this.value,
 | 
			
		||||
    required this.progress,
 | 
			
		||||
    required this.color,
 | 
			
		||||
    required this.icon,
 | 
			
		||||
    this.isCount = false,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Column(
 | 
			
		||||
      crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
      children: [
 | 
			
		||||
        Row(
 | 
			
		||||
          children: [
 | 
			
		||||
            Icon(icon, size: 20, color: color),
 | 
			
		||||
            const SizedBox(width: 8),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: Text(
 | 
			
		||||
                label,
 | 
			
		||||
                style: Theme.of(context).textTheme.bodyMedium?.copyWith(
 | 
			
		||||
                  fontWeight: FontWeight.w500,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            Text(
 | 
			
		||||
              value,
 | 
			
		||||
              style: Theme.of(context).textTheme.bodyLarge?.copyWith(
 | 
			
		||||
                fontWeight: FontWeight.bold,
 | 
			
		||||
                color: color,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        const SizedBox(height: 8),
 | 
			
		||||
        Stack(
 | 
			
		||||
          children: [
 | 
			
		||||
            Container(
 | 
			
		||||
              height: 8,
 | 
			
		||||
              decoration: BoxDecoration(
 | 
			
		||||
                color: Colors.grey.shade200,
 | 
			
		||||
                borderRadius: BorderRadius.circular(4),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            AnimatedContainer(
 | 
			
		||||
              duration: const Duration(milliseconds: 800),
 | 
			
		||||
              curve: Curves.easeInOut,
 | 
			
		||||
              height: 8,
 | 
			
		||||
              width: MediaQuery.of(context).size.width * progress * 0.8, // Approximate width
 | 
			
		||||
              decoration: BoxDecoration(
 | 
			
		||||
                color: color,
 | 
			
		||||
                borderRadius: BorderRadius.circular(4),
 | 
			
		||||
                gradient: LinearGradient(
 | 
			
		||||
                  colors: [color, color.withOpacity(0.7)],
 | 
			
		||||
                  begin: Alignment.centerLeft,
 | 
			
		||||
                  end: Alignment.centerRight,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        const SizedBox(height: 4),
 | 
			
		||||
        Text(
 | 
			
		||||
          '${(progress * 100).toInt()}% of target',
 | 
			
		||||
          style: Theme.of(context).textTheme.bodySmall?.copyWith(
 | 
			
		||||
            color: Colors.grey.shade600,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										115
									
								
								xp_dashboard/lib/src/widgets/recent_activity_card.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,115 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:xp_models/xp_models.dart';
 | 
			
		||||
import '../theme/app_theme.dart';
 | 
			
		||||
 | 
			
		||||
class RecentActivityCard extends StatelessWidget {
 | 
			
		||||
  final List<RecentActivity> activities;
 | 
			
		||||
 | 
			
		||||
  const RecentActivityCard({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.activities,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Card(
 | 
			
		||||
      child: Padding(
 | 
			
		||||
        padding: const EdgeInsets.all(20),
 | 
			
		||||
        child: Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Row(
 | 
			
		||||
              children: [
 | 
			
		||||
                const Icon(Icons.history, color: AppTheme.primaryColor),
 | 
			
		||||
                const SizedBox(width: 8),
 | 
			
		||||
                Text(
 | 
			
		||||
                  'Recent Activity',
 | 
			
		||||
                  style: Theme.of(context).textTheme.titleLarge?.copyWith(
 | 
			
		||||
                    fontWeight: FontWeight.bold,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(height: 16),
 | 
			
		||||
            if (activities.isEmpty)
 | 
			
		||||
              const Center(
 | 
			
		||||
                child: Padding(
 | 
			
		||||
                  padding: EdgeInsets.all(20),
 | 
			
		||||
                  child: Text('No recent activity'),
 | 
			
		||||
                ),
 | 
			
		||||
              )
 | 
			
		||||
            else
 | 
			
		||||
              ...activities.take(5).map((activity) => _ActivityItem(activity: activity)),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _ActivityItem extends StatelessWidget {
 | 
			
		||||
  final RecentActivity activity;
 | 
			
		||||
 | 
			
		||||
  const _ActivityItem({required this.activity});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final timestamp = DateTime.parse(activity.timestamp);
 | 
			
		||||
    final duration = Duration(seconds: activity.durationSeconds);
 | 
			
		||||
    
 | 
			
		||||
    return Padding(
 | 
			
		||||
      padding: const EdgeInsets.symmetric(vertical: 8),
 | 
			
		||||
      child: Row(
 | 
			
		||||
        children: [
 | 
			
		||||
          Container(
 | 
			
		||||
            width: 8,
 | 
			
		||||
            height: 8,
 | 
			
		||||
            decoration: BoxDecoration(
 | 
			
		||||
              shape: BoxShape.circle,
 | 
			
		||||
              color: AppTheme.getActivityTypeColor(activity.type),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          const SizedBox(width: 12),
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: Column(
 | 
			
		||||
              crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
              children: [
 | 
			
		||||
                Text(
 | 
			
		||||
                  activity.application,
 | 
			
		||||
                  style: Theme.of(context).textTheme.bodyMedium?.copyWith(
 | 
			
		||||
                    fontWeight: FontWeight.w500,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                Text(
 | 
			
		||||
                  '${_capitalizeFirst(activity.type)} • ${_formatDuration(duration)} • ${_formatTime(timestamp)}',
 | 
			
		||||
                  style: Theme.of(context).textTheme.bodySmall?.copyWith(
 | 
			
		||||
                    color: Colors.grey.shade600,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String _capitalizeFirst(String text) {
 | 
			
		||||
    if (text.isEmpty) return text;
 | 
			
		||||
    return text[0].toUpperCase() + text.substring(1);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String _formatDuration(Duration duration) {
 | 
			
		||||
    if (duration.inHours > 0) {
 | 
			
		||||
      return '${duration.inHours}h ${duration.inMinutes % 60}m';
 | 
			
		||||
    } else if (duration.inMinutes > 0) {
 | 
			
		||||
      return '${duration.inMinutes}m';
 | 
			
		||||
    } else {
 | 
			
		||||
      return '${duration.inSeconds}s';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String _formatTime(DateTime time) {
 | 
			
		||||
    return '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										118
									
								
								xp_dashboard/lib/src/widgets/stats_header.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,118 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:xp_models/xp_models.dart';
 | 
			
		||||
import '../theme/app_theme.dart';
 | 
			
		||||
 | 
			
		||||
class StatsHeader extends StatelessWidget {
 | 
			
		||||
  final DashboardStats? stats;
 | 
			
		||||
 | 
			
		||||
  const StatsHeader({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.stats,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    if (stats == null) {
 | 
			
		||||
      return const SizedBox.shrink();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Container(
 | 
			
		||||
      padding: const EdgeInsets.all(24),
 | 
			
		||||
      decoration: BoxDecoration(
 | 
			
		||||
        gradient: AppTheme.primaryGradient,
 | 
			
		||||
        borderRadius: BorderRadius.circular(16),
 | 
			
		||||
        boxShadow: [
 | 
			
		||||
          BoxShadow(
 | 
			
		||||
            color: AppTheme.primaryColor.withOpacity(0.3),
 | 
			
		||||
            blurRadius: 20,
 | 
			
		||||
            offset: const Offset(0, 10),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      child: Row(
 | 
			
		||||
        children: [
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: _StatCard(
 | 
			
		||||
              icon: Icons.trending_up,
 | 
			
		||||
              label: 'Level',
 | 
			
		||||
              value: stats!.today.level.toString(),
 | 
			
		||||
              color: Colors.white,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          const SizedBox(width: 16),
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: _StatCard(
 | 
			
		||||
              icon: Icons.stars,
 | 
			
		||||
              label: 'XP',
 | 
			
		||||
              value: _formatNumber(stats!.today.xp),
 | 
			
		||||
              color: Colors.white,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          const SizedBox(width: 16),
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: _StatCard(
 | 
			
		||||
              icon: Icons.local_fire_department,
 | 
			
		||||
              label: 'Streak',
 | 
			
		||||
              value: stats!.streaks.currentStreak.toString(),
 | 
			
		||||
              color: Colors.white,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String _formatNumber(int number) {
 | 
			
		||||
    if (number >= 1000000) {
 | 
			
		||||
      return '${(number / 1000000).toStringAsFixed(1)}M';
 | 
			
		||||
    } else if (number >= 1000) {
 | 
			
		||||
      return '${(number / 1000).toStringAsFixed(1)}K';
 | 
			
		||||
    }
 | 
			
		||||
    return number.toString();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _StatCard extends StatelessWidget {
 | 
			
		||||
  final IconData icon;
 | 
			
		||||
  final String label;
 | 
			
		||||
  final String value;
 | 
			
		||||
  final Color color;
 | 
			
		||||
 | 
			
		||||
  const _StatCard({
 | 
			
		||||
    required this.icon,
 | 
			
		||||
    required this.label,
 | 
			
		||||
    required this.value,
 | 
			
		||||
    required this.color,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Column(
 | 
			
		||||
      children: [
 | 
			
		||||
        Icon(
 | 
			
		||||
          icon,
 | 
			
		||||
          size: 32,
 | 
			
		||||
          color: color.withOpacity(0.8),
 | 
			
		||||
        ),
 | 
			
		||||
        const SizedBox(height: 8),
 | 
			
		||||
        Text(
 | 
			
		||||
          label,
 | 
			
		||||
          style: TextStyle(
 | 
			
		||||
            color: color.withOpacity(0.8),
 | 
			
		||||
            fontSize: 14,
 | 
			
		||||
            fontWeight: FontWeight.w500,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        const SizedBox(height: 4),
 | 
			
		||||
        Text(
 | 
			
		||||
          value,
 | 
			
		||||
          style: TextStyle(
 | 
			
		||||
            color: color,
 | 
			
		||||
            fontSize: 24,
 | 
			
		||||
            fontWeight: FontWeight.bold,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										179
									
								
								xp_dashboard/lib/src/widgets/xp_breakdown_card.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,179 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import '../theme/app_theme.dart';
 | 
			
		||||
 | 
			
		||||
class XPBreakdownCard extends StatelessWidget {
 | 
			
		||||
  final Map<String, int> breakdown;
 | 
			
		||||
 | 
			
		||||
  const XPBreakdownCard({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.breakdown,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Card(
 | 
			
		||||
      child: Padding(
 | 
			
		||||
        padding: const EdgeInsets.all(20),
 | 
			
		||||
        child: Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Row(
 | 
			
		||||
              children: [
 | 
			
		||||
                const Icon(Icons.pie_chart, color: AppTheme.primaryColor),
 | 
			
		||||
                const SizedBox(width: 8),
 | 
			
		||||
                Text(
 | 
			
		||||
                  'XP Sources Today',
 | 
			
		||||
                  style: Theme.of(context).textTheme.titleLarge?.copyWith(
 | 
			
		||||
                    fontWeight: FontWeight.bold,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(height: 16),
 | 
			
		||||
            if (breakdown.isEmpty)
 | 
			
		||||
              const Center(
 | 
			
		||||
                child: Padding(
 | 
			
		||||
                  padding: EdgeInsets.all(20),
 | 
			
		||||
                  child: Text('No XP earned today'),
 | 
			
		||||
                ),
 | 
			
		||||
              )
 | 
			
		||||
            else
 | 
			
		||||
              ..._buildXPSourceItems(),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  List<Widget> _buildXPSourceItems() {
 | 
			
		||||
    final totalXP = breakdown.values.fold(0, (sum, xp) => sum + xp);
 | 
			
		||||
    
 | 
			
		||||
    final sortedEntries = breakdown.entries
 | 
			
		||||
        .where((entry) => entry.value > 0)
 | 
			
		||||
        .toList();
 | 
			
		||||
    
 | 
			
		||||
    sortedEntries.sort((a, b) => b.value.compareTo(a.value));
 | 
			
		||||
    
 | 
			
		||||
    return sortedEntries
 | 
			
		||||
        .take(5)
 | 
			
		||||
        .map((entry) => _XPSourceItem(
 | 
			
		||||
              source: entry.key,
 | 
			
		||||
              xp: entry.value,
 | 
			
		||||
              totalXP: totalXP,
 | 
			
		||||
            ))
 | 
			
		||||
        .toList();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _XPSourceItem extends StatelessWidget {
 | 
			
		||||
  final String source;
 | 
			
		||||
  final int xp;
 | 
			
		||||
  final int totalXP;
 | 
			
		||||
 | 
			
		||||
  const _XPSourceItem({
 | 
			
		||||
    required this.source,
 | 
			
		||||
    required this.xp,
 | 
			
		||||
    required this.totalXP,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final percentage = totalXP > 0 ? (xp / totalXP * 100) : 0.0;
 | 
			
		||||
    final color = AppTheme.getXPSourceColor(source);
 | 
			
		||||
 | 
			
		||||
    return Padding(
 | 
			
		||||
      padding: const EdgeInsets.symmetric(vertical: 8),
 | 
			
		||||
      child: Column(
 | 
			
		||||
        crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
        children: [
 | 
			
		||||
          Row(
 | 
			
		||||
            children: [
 | 
			
		||||
              Container(
 | 
			
		||||
                width: 12,
 | 
			
		||||
                height: 12,
 | 
			
		||||
                decoration: BoxDecoration(
 | 
			
		||||
                  color: color,
 | 
			
		||||
                  borderRadius: BorderRadius.circular(2),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              const SizedBox(width: 8),
 | 
			
		||||
              Expanded(
 | 
			
		||||
                child: Text(
 | 
			
		||||
                  _formatSourceName(source),
 | 
			
		||||
                  style: Theme.of(context).textTheme.bodyMedium?.copyWith(
 | 
			
		||||
                    fontWeight: FontWeight.w500,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              Text(
 | 
			
		||||
                '+$xp XP',
 | 
			
		||||
                style: Theme.of(context).textTheme.bodyMedium?.copyWith(
 | 
			
		||||
                  fontWeight: FontWeight.bold,
 | 
			
		||||
                  color: color,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
          const SizedBox(height: 4),
 | 
			
		||||
          Row(
 | 
			
		||||
            children: [
 | 
			
		||||
              Expanded(
 | 
			
		||||
                child: Container(
 | 
			
		||||
                  height: 4,
 | 
			
		||||
                  decoration: BoxDecoration(
 | 
			
		||||
                    color: Colors.grey.shade200,
 | 
			
		||||
                    borderRadius: BorderRadius.circular(2),
 | 
			
		||||
                  ),
 | 
			
		||||
                  child: FractionallySizedBox(
 | 
			
		||||
                    alignment: Alignment.centerLeft,
 | 
			
		||||
                    widthFactor: percentage / 100,
 | 
			
		||||
                    child: Container(
 | 
			
		||||
                      decoration: BoxDecoration(
 | 
			
		||||
                        color: color,
 | 
			
		||||
                        borderRadius: BorderRadius.circular(2),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              const SizedBox(width: 8),
 | 
			
		||||
              Text(
 | 
			
		||||
                '${percentage.toStringAsFixed(1)}%',
 | 
			
		||||
                style: Theme.of(context).textTheme.bodySmall?.copyWith(
 | 
			
		||||
                  color: Colors.grey.shade600,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String _formatSourceName(String source) {
 | 
			
		||||
    switch (source) {
 | 
			
		||||
      case 'coding':
 | 
			
		||||
        return 'Coding';
 | 
			
		||||
      case 'focused_browsing':
 | 
			
		||||
        return 'Focused Browsing';
 | 
			
		||||
      case 'collaboration':
 | 
			
		||||
        return 'Collaboration';
 | 
			
		||||
      case 'meetings':
 | 
			
		||||
        return 'Meetings';
 | 
			
		||||
      case 'misc':
 | 
			
		||||
        return 'Miscellaneous';
 | 
			
		||||
      case 'uncategorized':
 | 
			
		||||
        return 'Uncategorized';
 | 
			
		||||
      case 'focus_session':
 | 
			
		||||
        return 'Focus Sessions';
 | 
			
		||||
      case 'achievement':
 | 
			
		||||
        return 'Achievements';
 | 
			
		||||
      case 'manual_boost':
 | 
			
		||||
        return 'Manual Boosts';
 | 
			
		||||
      default:
 | 
			
		||||
        return source.replaceAll('_', ' ').split(' ').map((word) => 
 | 
			
		||||
          word.isEmpty ? word : word[0].toUpperCase() + word.substring(1)
 | 
			
		||||
        ).join(' ');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										359
									
								
								xp_dashboard/lib/src/widgets/xp_chart.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,359 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:fl_chart/fl_chart.dart';
 | 
			
		||||
import 'package:xp_models/xp_models.dart';
 | 
			
		||||
import '../theme/app_theme.dart';
 | 
			
		||||
 | 
			
		||||
class XPChart extends StatelessWidget {
 | 
			
		||||
  final List<StatsHistory> history;
 | 
			
		||||
 | 
			
		||||
  const XPChart({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.history,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Card(
 | 
			
		||||
      child: Padding(
 | 
			
		||||
        padding: const EdgeInsets.all(20),
 | 
			
		||||
        child: Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Row(
 | 
			
		||||
              children: [
 | 
			
		||||
                const Icon(Icons.trending_up, color: AppTheme.primaryColor),
 | 
			
		||||
                const SizedBox(width: 8),
 | 
			
		||||
                Text(
 | 
			
		||||
                  'XP Progress (7 Days)',
 | 
			
		||||
                  style: Theme.of(context).textTheme.titleLarge?.copyWith(
 | 
			
		||||
                    fontWeight: FontWeight.bold,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
            const SizedBox(height: 16),
 | 
			
		||||
            _buildLegend(),
 | 
			
		||||
            const SizedBox(height: 16),
 | 
			
		||||
            SizedBox(
 | 
			
		||||
              height: 300,
 | 
			
		||||
              child: history.isEmpty
 | 
			
		||||
                  ? const Center(
 | 
			
		||||
                      child: CircularProgressIndicator(),
 | 
			
		||||
                    )
 | 
			
		||||
                  : LineChart(
 | 
			
		||||
                      _buildChartData(),
 | 
			
		||||
                    ),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LineChartData _buildChartData() {
 | 
			
		||||
    final xpSpots = <FlSpot>[];
 | 
			
		||||
    final levelSpots = <FlSpot>[];
 | 
			
		||||
    
 | 
			
		||||
    for (int i = 0; i < history.length; i++) {
 | 
			
		||||
      xpSpots.add(FlSpot(i.toDouble(), history[i].xp.toDouble()));
 | 
			
		||||
      levelSpots.add(FlSpot(i.toDouble(), history[i].level.toDouble()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final maxXP = history.map((h) => h.xp).reduce((a, b) => a > b ? a : b).toDouble();
 | 
			
		||||
    final maxLevel = history.map((h) => h.level).reduce((a, b) => a > b ? a : b).toDouble();
 | 
			
		||||
 | 
			
		||||
    return LineChartData(
 | 
			
		||||
      gridData: FlGridData(
 | 
			
		||||
        show: true,
 | 
			
		||||
        drawVerticalLine: true,
 | 
			
		||||
        horizontalInterval: maxXP / 5,
 | 
			
		||||
        verticalInterval: 1,
 | 
			
		||||
        getDrawingHorizontalLine: (value) {
 | 
			
		||||
          return FlLine(
 | 
			
		||||
            color: Colors.grey.shade300,
 | 
			
		||||
            strokeWidth: 1,
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
        getDrawingVerticalLine: (value) {
 | 
			
		||||
          return FlLine(
 | 
			
		||||
            color: Colors.grey.shade300,
 | 
			
		||||
            strokeWidth: 1,
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
      ),
 | 
			
		||||
      titlesData: FlTitlesData(
 | 
			
		||||
        show: true,
 | 
			
		||||
        rightTitles: AxisTitles(
 | 
			
		||||
          sideTitles: SideTitles(
 | 
			
		||||
            showTitles: true,
 | 
			
		||||
            reservedSize: 40,
 | 
			
		||||
            interval: maxLevel / 4,
 | 
			
		||||
            getTitlesWidget: (value, meta) {
 | 
			
		||||
              return Text(
 | 
			
		||||
                'L${value.toInt()}',
 | 
			
		||||
                style: const TextStyle(
 | 
			
		||||
                  color: AppTheme.secondaryColor,
 | 
			
		||||
                  fontWeight: FontWeight.bold,
 | 
			
		||||
                  fontSize: 12,
 | 
			
		||||
                ),
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        topTitles: const AxisTitles(
 | 
			
		||||
          sideTitles: SideTitles(showTitles: false),
 | 
			
		||||
        ),
 | 
			
		||||
        bottomTitles: AxisTitles(
 | 
			
		||||
          sideTitles: SideTitles(
 | 
			
		||||
            showTitles: true,
 | 
			
		||||
            reservedSize: 30,
 | 
			
		||||
            interval: 1,
 | 
			
		||||
            getTitlesWidget: (value, meta) {
 | 
			
		||||
              final index = value.toInt();
 | 
			
		||||
              if (index >= 0 && index < history.length) {
 | 
			
		||||
                final date = DateTime.parse(history[index].date);
 | 
			
		||||
                return Padding(
 | 
			
		||||
                  padding: const EdgeInsets.only(top: 8.0),
 | 
			
		||||
                  child: Text(
 | 
			
		||||
                    '${date.month}/${date.day}',
 | 
			
		||||
                    style: const TextStyle(
 | 
			
		||||
                      color: Colors.grey,
 | 
			
		||||
                      fontWeight: FontWeight.bold,
 | 
			
		||||
                      fontSize: 12,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                );
 | 
			
		||||
              }
 | 
			
		||||
              return const Text('');
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        leftTitles: AxisTitles(
 | 
			
		||||
          sideTitles: SideTitles(
 | 
			
		||||
            showTitles: true,
 | 
			
		||||
            interval: maxXP / 4,
 | 
			
		||||
            reservedSize: 50,
 | 
			
		||||
            getTitlesWidget: (value, meta) {
 | 
			
		||||
              return Text(
 | 
			
		||||
                _formatXP(value.toInt()),
 | 
			
		||||
                style: const TextStyle(
 | 
			
		||||
                  color: AppTheme.primaryColor,
 | 
			
		||||
                  fontWeight: FontWeight.bold,
 | 
			
		||||
                  fontSize: 12,
 | 
			
		||||
                ),
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
      borderData: FlBorderData(
 | 
			
		||||
        show: true,
 | 
			
		||||
        border: Border.all(color: Colors.grey.shade300),
 | 
			
		||||
      ),
 | 
			
		||||
      minX: 0,
 | 
			
		||||
      maxX: (history.length - 1).toDouble(),
 | 
			
		||||
      minY: 0,
 | 
			
		||||
      maxY: maxXP * 1.1,
 | 
			
		||||
      lineBarsData: [
 | 
			
		||||
        // XP Line
 | 
			
		||||
        LineChartBarData(
 | 
			
		||||
          spots: xpSpots,
 | 
			
		||||
          isCurved: true,
 | 
			
		||||
          gradient: const LinearGradient(
 | 
			
		||||
            colors: [AppTheme.primaryColor, AppTheme.accentColor],
 | 
			
		||||
          ),
 | 
			
		||||
          barWidth: 3,
 | 
			
		||||
          isStrokeCapRound: true,
 | 
			
		||||
          dotData: FlDotData(
 | 
			
		||||
            show: true,
 | 
			
		||||
            getDotPainter: (spot, percent, barData, index) {
 | 
			
		||||
              return FlDotCirclePainter(
 | 
			
		||||
                radius: 4,
 | 
			
		||||
                color: AppTheme.primaryColor,
 | 
			
		||||
                strokeWidth: 2,
 | 
			
		||||
                strokeColor: Colors.white,
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
          belowBarData: BarAreaData(
 | 
			
		||||
            show: true,
 | 
			
		||||
            gradient: LinearGradient(
 | 
			
		||||
              colors: [
 | 
			
		||||
                AppTheme.primaryColor.withOpacity(0.3),
 | 
			
		||||
                AppTheme.primaryColor.withOpacity(0.1),
 | 
			
		||||
              ],
 | 
			
		||||
              begin: Alignment.topCenter,
 | 
			
		||||
              end: Alignment.bottomCenter,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        // Level Line (scaled to fit)
 | 
			
		||||
        LineChartBarData(
 | 
			
		||||
          spots: levelSpots.map((spot) => FlSpot(spot.x, spot.y * (maxXP / maxLevel))).toList(),
 | 
			
		||||
          isCurved: true,
 | 
			
		||||
          color: AppTheme.secondaryColor,
 | 
			
		||||
          barWidth: 2,
 | 
			
		||||
          isStrokeCapRound: true,
 | 
			
		||||
          dotData: FlDotData(
 | 
			
		||||
            show: true,
 | 
			
		||||
            getDotPainter: (spot, percent, barData, index) {
 | 
			
		||||
              return FlDotCirclePainter(
 | 
			
		||||
                radius: 3,
 | 
			
		||||
                color: AppTheme.secondaryColor,
 | 
			
		||||
                strokeWidth: 2,
 | 
			
		||||
                strokeColor: Colors.white,
 | 
			
		||||
              );
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
          dashArray: [5, 5],
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
      lineTouchData: LineTouchData(
 | 
			
		||||
        enabled: true,
 | 
			
		||||
        touchTooltipData: LineTouchTooltipData(
 | 
			
		||||
          tooltipBgColor: Colors.blueGrey.withOpacity(0.8),
 | 
			
		||||
          getTooltipItems: (List<LineBarSpot> touchedBarSpots) {
 | 
			
		||||
            return touchedBarSpots.map((barSpot) {
 | 
			
		||||
              final flSpot = barSpot;
 | 
			
		||||
              final index = flSpot.x.toInt();
 | 
			
		||||
              if (index >= 0 && index < history.length) {
 | 
			
		||||
                final historyItem = history[index];
 | 
			
		||||
                final date = DateTime.parse(historyItem.date);
 | 
			
		||||
                
 | 
			
		||||
                if (barSpot.barIndex == 0) {
 | 
			
		||||
                  // XP line
 | 
			
		||||
                  return LineTooltipItem(
 | 
			
		||||
                    '${date.month}/${date.day}\nXP: ${historyItem.xp}',
 | 
			
		||||
                    const TextStyle(
 | 
			
		||||
                      color: Colors.white,
 | 
			
		||||
                      fontWeight: FontWeight.bold,
 | 
			
		||||
                    ),
 | 
			
		||||
                  );
 | 
			
		||||
                } else {
 | 
			
		||||
                  // Level line
 | 
			
		||||
                  return LineTooltipItem(
 | 
			
		||||
                    'Level: ${historyItem.level}',
 | 
			
		||||
                    const TextStyle(
 | 
			
		||||
                      color: Colors.white,
 | 
			
		||||
                      fontWeight: FontWeight.bold,
 | 
			
		||||
                    ),
 | 
			
		||||
                  );
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
              return null;
 | 
			
		||||
            }).toList();
 | 
			
		||||
          },
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildLegend() {
 | 
			
		||||
    return Container(
 | 
			
		||||
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
 | 
			
		||||
      decoration: BoxDecoration(
 | 
			
		||||
        color: Colors.grey.shade50,
 | 
			
		||||
        borderRadius: BorderRadius.circular(8),
 | 
			
		||||
        border: Border.all(color: Colors.grey.shade200),
 | 
			
		||||
      ),
 | 
			
		||||
      child: Row(
 | 
			
		||||
        mainAxisSize: MainAxisSize.min,
 | 
			
		||||
        children: [
 | 
			
		||||
          _buildLegendItem(
 | 
			
		||||
            color: AppTheme.primaryColor,
 | 
			
		||||
            label: 'Experience Points',
 | 
			
		||||
            isDashed: false,
 | 
			
		||||
            hasGradient: true,
 | 
			
		||||
          ),
 | 
			
		||||
          const SizedBox(width: 24),
 | 
			
		||||
          _buildLegendItem(
 | 
			
		||||
            color: AppTheme.secondaryColor,
 | 
			
		||||
            label: 'Level',
 | 
			
		||||
            isDashed: true,
 | 
			
		||||
            hasGradient: false,
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget _buildLegendItem({
 | 
			
		||||
    required Color color,
 | 
			
		||||
    required String label,
 | 
			
		||||
    required bool isDashed,
 | 
			
		||||
    required bool hasGradient,
 | 
			
		||||
  }) {
 | 
			
		||||
    return Row(
 | 
			
		||||
      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
      children: [
 | 
			
		||||
        Container(
 | 
			
		||||
          width: 24,
 | 
			
		||||
          height: 3,
 | 
			
		||||
          decoration: BoxDecoration(
 | 
			
		||||
            gradient: hasGradient
 | 
			
		||||
                ? const LinearGradient(
 | 
			
		||||
                    colors: [AppTheme.primaryColor, AppTheme.accentColor],
 | 
			
		||||
                  )
 | 
			
		||||
                : null,
 | 
			
		||||
            color: hasGradient ? null : color,
 | 
			
		||||
            borderRadius: BorderRadius.circular(2),
 | 
			
		||||
          ),
 | 
			
		||||
          child: isDashed
 | 
			
		||||
              ? CustomPaint(
 | 
			
		||||
                  painter: DashedLinePainter(color: color),
 | 
			
		||||
                  size: const Size(24, 3),
 | 
			
		||||
                )
 | 
			
		||||
              : null,
 | 
			
		||||
        ),
 | 
			
		||||
        const SizedBox(width: 8),
 | 
			
		||||
        Text(
 | 
			
		||||
          label,
 | 
			
		||||
          style: const TextStyle(
 | 
			
		||||
            fontSize: 12,
 | 
			
		||||
            fontWeight: FontWeight.w500,
 | 
			
		||||
            color: Colors.black87,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String _formatXP(int xp) {
 | 
			
		||||
    if (xp >= 1000000) {
 | 
			
		||||
      return '${(xp / 1000000).toStringAsFixed(1)}M';
 | 
			
		||||
    } else if (xp >= 1000) {
 | 
			
		||||
      return '${(xp / 1000).toStringAsFixed(1)}K';
 | 
			
		||||
    }
 | 
			
		||||
    return xp.toString();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DashedLinePainter extends CustomPainter {
 | 
			
		||||
  final Color color;
 | 
			
		||||
 | 
			
		||||
  DashedLinePainter({required this.color});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void paint(Canvas canvas, Size size) {
 | 
			
		||||
    final paint = Paint()
 | 
			
		||||
      ..color = color
 | 
			
		||||
      ..strokeWidth = 2
 | 
			
		||||
      ..style = PaintingStyle.stroke;
 | 
			
		||||
 | 
			
		||||
    const dashWidth = 3.0;
 | 
			
		||||
    const dashSpace = 2.0;
 | 
			
		||||
    double startX = 0;
 | 
			
		||||
 | 
			
		||||
    while (startX < size.width) {
 | 
			
		||||
      canvas.drawLine(
 | 
			
		||||
        Offset(startX, size.height / 2),
 | 
			
		||||
        Offset(startX + dashWidth, size.height / 2),
 | 
			
		||||
        paint,
 | 
			
		||||
      );
 | 
			
		||||
      startX += dashWidth + dashSpace;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool shouldRepaint(CustomPainter oldDelegate) => false;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								xp_dashboard/linux/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1 @@
 | 
			
		||||
flutter/ephemeral
 | 
			
		||||
							
								
								
									
										128
									
								
								xp_dashboard/linux/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,128 @@
 | 
			
		||||
# Project-level configuration.
 | 
			
		||||
cmake_minimum_required(VERSION 3.13)
 | 
			
		||||
project(runner LANGUAGES CXX)
 | 
			
		||||
 | 
			
		||||
# The name of the executable created for the application. Change this to change
 | 
			
		||||
# the on-disk name of your application.
 | 
			
		||||
set(BINARY_NAME "xp_dashboard")
 | 
			
		||||
# The unique GTK application identifier for this application. See:
 | 
			
		||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
 | 
			
		||||
set(APPLICATION_ID "com.example.xp_dashboard")
 | 
			
		||||
 | 
			
		||||
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
 | 
			
		||||
# versions of CMake.
 | 
			
		||||
cmake_policy(SET CMP0063 NEW)
 | 
			
		||||
 | 
			
		||||
# Load bundled libraries from the lib/ directory relative to the binary.
 | 
			
		||||
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
 | 
			
		||||
 | 
			
		||||
# Root filesystem for cross-building.
 | 
			
		||||
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
 | 
			
		||||
  set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
 | 
			
		||||
  set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
 | 
			
		||||
  set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
 | 
			
		||||
  set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
 | 
			
		||||
  set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
 | 
			
		||||
  set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Define build configuration options.
 | 
			
		||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
 | 
			
		||||
  set(CMAKE_BUILD_TYPE "Debug" CACHE
 | 
			
		||||
    STRING "Flutter build mode" FORCE)
 | 
			
		||||
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
 | 
			
		||||
    "Debug" "Profile" "Release")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Compilation settings that should be applied to most targets.
 | 
			
		||||
#
 | 
			
		||||
# Be cautious about adding new options here, as plugins use this function by
 | 
			
		||||
# default. In most cases, you should add new options to specific targets instead
 | 
			
		||||
# of modifying this function.
 | 
			
		||||
function(APPLY_STANDARD_SETTINGS TARGET)
 | 
			
		||||
  target_compile_features(${TARGET} PUBLIC cxx_std_14)
 | 
			
		||||
  target_compile_options(${TARGET} PRIVATE -Wall -Werror)
 | 
			
		||||
  target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
 | 
			
		||||
  target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
 | 
			
		||||
endfunction()
 | 
			
		||||
 | 
			
		||||
# Flutter library and tool build rules.
 | 
			
		||||
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
 | 
			
		||||
add_subdirectory(${FLUTTER_MANAGED_DIR})
 | 
			
		||||
 | 
			
		||||
# System-level dependencies.
 | 
			
		||||
find_package(PkgConfig REQUIRED)
 | 
			
		||||
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
 | 
			
		||||
 | 
			
		||||
# Application build; see runner/CMakeLists.txt.
 | 
			
		||||
add_subdirectory("runner")
 | 
			
		||||
 | 
			
		||||
# Run the Flutter tool portions of the build. This must not be removed.
 | 
			
		||||
add_dependencies(${BINARY_NAME} flutter_assemble)
 | 
			
		||||
 | 
			
		||||
# Only the install-generated bundle's copy of the executable will launch
 | 
			
		||||
# correctly, since the resources must in the right relative locations. To avoid
 | 
			
		||||
# people trying to run the unbundled copy, put it in a subdirectory instead of
 | 
			
		||||
# the default top-level location.
 | 
			
		||||
set_target_properties(${BINARY_NAME}
 | 
			
		||||
  PROPERTIES
 | 
			
		||||
  RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Generated plugin build rules, which manage building the plugins and adding
 | 
			
		||||
# them to the application.
 | 
			
		||||
include(flutter/generated_plugins.cmake)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# === Installation ===
 | 
			
		||||
# By default, "installing" just makes a relocatable bundle in the build
 | 
			
		||||
# directory.
 | 
			
		||||
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
 | 
			
		||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
 | 
			
		||||
  set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Start with a clean build bundle directory every time.
 | 
			
		||||
install(CODE "
 | 
			
		||||
  file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
 | 
			
		||||
  " COMPONENT Runtime)
 | 
			
		||||
 | 
			
		||||
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
 | 
			
		||||
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
 | 
			
		||||
 | 
			
		||||
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
 | 
			
		||||
  COMPONENT Runtime)
 | 
			
		||||
 | 
			
		||||
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
 | 
			
		||||
  COMPONENT Runtime)
 | 
			
		||||
 | 
			
		||||
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
 | 
			
		||||
  COMPONENT Runtime)
 | 
			
		||||
 | 
			
		||||
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
 | 
			
		||||
  install(FILES "${bundled_library}"
 | 
			
		||||
    DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
 | 
			
		||||
    COMPONENT Runtime)
 | 
			
		||||
endforeach(bundled_library)
 | 
			
		||||
 | 
			
		||||
# Copy the native assets provided by the build.dart from all packages.
 | 
			
		||||
set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
 | 
			
		||||
install(DIRECTORY "${NATIVE_ASSETS_DIR}"
 | 
			
		||||
   DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
 | 
			
		||||
   COMPONENT Runtime)
 | 
			
		||||
 | 
			
		||||
# Fully re-copy the assets directory on each build to avoid having stale files
 | 
			
		||||
# from a previous install.
 | 
			
		||||
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
 | 
			
		||||
install(CODE "
 | 
			
		||||
  file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
 | 
			
		||||
  " COMPONENT Runtime)
 | 
			
		||||
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
 | 
			
		||||
  DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
 | 
			
		||||
 | 
			
		||||
# Install the AOT library on non-Debug builds only.
 | 
			
		||||
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
 | 
			
		||||
  install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
 | 
			
		||||
    COMPONENT Runtime)
 | 
			
		||||
endif()
 | 
			
		||||
							
								
								
									
										88
									
								
								xp_dashboard/linux/flutter/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,88 @@
 | 
			
		||||
# This file controls Flutter-level build steps. It should not be edited.
 | 
			
		||||
cmake_minimum_required(VERSION 3.10)
 | 
			
		||||
 | 
			
		||||
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
 | 
			
		||||
 | 
			
		||||
# Configuration provided via flutter tool.
 | 
			
		||||
include(${EPHEMERAL_DIR}/generated_config.cmake)
 | 
			
		||||
 | 
			
		||||
# TODO: Move the rest of this into files in ephemeral. See
 | 
			
		||||
# https://github.com/flutter/flutter/issues/57146.
 | 
			
		||||
 | 
			
		||||
# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
 | 
			
		||||
# which isn't available in 3.10.
 | 
			
		||||
function(list_prepend LIST_NAME PREFIX)
 | 
			
		||||
    set(NEW_LIST "")
 | 
			
		||||
    foreach(element ${${LIST_NAME}})
 | 
			
		||||
        list(APPEND NEW_LIST "${PREFIX}${element}")
 | 
			
		||||
    endforeach(element)
 | 
			
		||||
    set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
 | 
			
		||||
endfunction()
 | 
			
		||||
 | 
			
		||||
# === Flutter Library ===
 | 
			
		||||
# System-level dependencies.
 | 
			
		||||
find_package(PkgConfig REQUIRED)
 | 
			
		||||
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
 | 
			
		||||
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
 | 
			
		||||
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
 | 
			
		||||
 | 
			
		||||
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
 | 
			
		||||
 | 
			
		||||
# Published to parent scope for install step.
 | 
			
		||||
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
 | 
			
		||||
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
 | 
			
		||||
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
 | 
			
		||||
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
 | 
			
		||||
 | 
			
		||||
list(APPEND FLUTTER_LIBRARY_HEADERS
 | 
			
		||||
  "fl_basic_message_channel.h"
 | 
			
		||||
  "fl_binary_codec.h"
 | 
			
		||||
  "fl_binary_messenger.h"
 | 
			
		||||
  "fl_dart_project.h"
 | 
			
		||||
  "fl_engine.h"
 | 
			
		||||
  "fl_json_message_codec.h"
 | 
			
		||||
  "fl_json_method_codec.h"
 | 
			
		||||
  "fl_message_codec.h"
 | 
			
		||||
  "fl_method_call.h"
 | 
			
		||||
  "fl_method_channel.h"
 | 
			
		||||
  "fl_method_codec.h"
 | 
			
		||||
  "fl_method_response.h"
 | 
			
		||||
  "fl_plugin_registrar.h"
 | 
			
		||||
  "fl_plugin_registry.h"
 | 
			
		||||
  "fl_standard_message_codec.h"
 | 
			
		||||
  "fl_standard_method_codec.h"
 | 
			
		||||
  "fl_string_codec.h"
 | 
			
		||||
  "fl_value.h"
 | 
			
		||||
  "fl_view.h"
 | 
			
		||||
  "flutter_linux.h"
 | 
			
		||||
)
 | 
			
		||||
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
 | 
			
		||||
add_library(flutter INTERFACE)
 | 
			
		||||
target_include_directories(flutter INTERFACE
 | 
			
		||||
  "${EPHEMERAL_DIR}"
 | 
			
		||||
)
 | 
			
		||||
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
 | 
			
		||||
target_link_libraries(flutter INTERFACE
 | 
			
		||||
  PkgConfig::GTK
 | 
			
		||||
  PkgConfig::GLIB
 | 
			
		||||
  PkgConfig::GIO
 | 
			
		||||
)
 | 
			
		||||
add_dependencies(flutter flutter_assemble)
 | 
			
		||||
 | 
			
		||||
# === Flutter tool backend ===
 | 
			
		||||
# _phony_ is a non-existent file to force this command to run every time,
 | 
			
		||||
# since currently there's no way to get a full input/output list from the
 | 
			
		||||
# flutter tool.
 | 
			
		||||
add_custom_command(
 | 
			
		||||
  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
 | 
			
		||||
    ${CMAKE_CURRENT_BINARY_DIR}/_phony_
 | 
			
		||||
  COMMAND ${CMAKE_COMMAND} -E env
 | 
			
		||||
    ${FLUTTER_TOOL_ENVIRONMENT}
 | 
			
		||||
    "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
 | 
			
		||||
      ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}
 | 
			
		||||
  VERBATIM
 | 
			
		||||
)
 | 
			
		||||
add_custom_target(flutter_assemble DEPENDS
 | 
			
		||||
  "${FLUTTER_LIBRARY}"
 | 
			
		||||
  ${FLUTTER_LIBRARY_HEADERS}
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										11
									
								
								xp_dashboard/linux/flutter/generated_plugin_registrant.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,11 @@
 | 
			
		||||
//
 | 
			
		||||
//  Generated file. Do not edit.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
 | 
			
		||||
#include "generated_plugin_registrant.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void fl_register_plugins(FlPluginRegistry* registry) {
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								xp_dashboard/linux/flutter/generated_plugin_registrant.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,15 @@
 | 
			
		||||
//
 | 
			
		||||
//  Generated file. Do not edit.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
 | 
			
		||||
#ifndef GENERATED_PLUGIN_REGISTRANT_
 | 
			
		||||
#define GENERATED_PLUGIN_REGISTRANT_
 | 
			
		||||
 | 
			
		||||
#include <flutter_linux/flutter_linux.h>
 | 
			
		||||
 | 
			
		||||
// Registers Flutter plugins.
 | 
			
		||||
void fl_register_plugins(FlPluginRegistry* registry);
 | 
			
		||||
 | 
			
		||||
#endif  // GENERATED_PLUGIN_REGISTRANT_
 | 
			
		||||
							
								
								
									
										23
									
								
								xp_dashboard/linux/flutter/generated_plugins.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,23 @@
 | 
			
		||||
#
 | 
			
		||||
# Generated file, do not edit.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
list(APPEND FLUTTER_PLUGIN_LIST
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
set(PLUGIN_BUNDLED_LIBRARIES)
 | 
			
		||||
 | 
			
		||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
 | 
			
		||||
  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
 | 
			
		||||
  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
 | 
			
		||||
  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
 | 
			
		||||
  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
 | 
			
		||||
endforeach(plugin)
 | 
			
		||||
 | 
			
		||||
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
 | 
			
		||||
  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
 | 
			
		||||
  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
 | 
			
		||||
endforeach(ffi_plugin)
 | 
			
		||||
							
								
								
									
										26
									
								
								xp_dashboard/linux/runner/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,26 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.13)
 | 
			
		||||
project(runner LANGUAGES CXX)
 | 
			
		||||
 | 
			
		||||
# Define the application target. To change its name, change BINARY_NAME in the
 | 
			
		||||
# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer
 | 
			
		||||
# work.
 | 
			
		||||
#
 | 
			
		||||
# Any new source files that you add to the application should be added here.
 | 
			
		||||
add_executable(${BINARY_NAME}
 | 
			
		||||
  "main.cc"
 | 
			
		||||
  "my_application.cc"
 | 
			
		||||
  "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Apply the standard set of build settings. This can be removed for applications
 | 
			
		||||
# that need different build settings.
 | 
			
		||||
apply_standard_settings(${BINARY_NAME})
 | 
			
		||||
 | 
			
		||||
# Add preprocessor definitions for the application ID.
 | 
			
		||||
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
 | 
			
		||||
 | 
			
		||||
# Add dependency libraries. Add any application-specific dependencies here.
 | 
			
		||||
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
 | 
			
		||||
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
 | 
			
		||||
 | 
			
		||||
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
 | 
			
		||||
							
								
								
									
										6
									
								
								xp_dashboard/linux/runner/main.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,6 @@
 | 
			
		||||
#include "my_application.h"
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv) {
 | 
			
		||||
  g_autoptr(MyApplication) app = my_application_new();
 | 
			
		||||
  return g_application_run(G_APPLICATION(app), argc, argv);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										130
									
								
								xp_dashboard/linux/runner/my_application.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,130 @@
 | 
			
		||||
#include "my_application.h"
 | 
			
		||||
 | 
			
		||||
#include <flutter_linux/flutter_linux.h>
 | 
			
		||||
#ifdef GDK_WINDOWING_X11
 | 
			
		||||
#include <gdk/gdkx.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "flutter/generated_plugin_registrant.h"
 | 
			
		||||
 | 
			
		||||
struct _MyApplication {
 | 
			
		||||
  GtkApplication parent_instance;
 | 
			
		||||
  char** dart_entrypoint_arguments;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
 | 
			
		||||
 | 
			
		||||
// Implements GApplication::activate.
 | 
			
		||||
static void my_application_activate(GApplication* application) {
 | 
			
		||||
  MyApplication* self = MY_APPLICATION(application);
 | 
			
		||||
  GtkWindow* window =
 | 
			
		||||
      GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
 | 
			
		||||
 | 
			
		||||
  // Use a header bar when running in GNOME as this is the common style used
 | 
			
		||||
  // by applications and is the setup most users will be using (e.g. Ubuntu
 | 
			
		||||
  // desktop).
 | 
			
		||||
  // If running on X and not using GNOME then just use a traditional title bar
 | 
			
		||||
  // in case the window manager does more exotic layout, e.g. tiling.
 | 
			
		||||
  // If running on Wayland assume the header bar will work (may need changing
 | 
			
		||||
  // if future cases occur).
 | 
			
		||||
  gboolean use_header_bar = TRUE;
 | 
			
		||||
#ifdef GDK_WINDOWING_X11
 | 
			
		||||
  GdkScreen* screen = gtk_window_get_screen(window);
 | 
			
		||||
  if (GDK_IS_X11_SCREEN(screen)) {
 | 
			
		||||
    const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
 | 
			
		||||
    if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
 | 
			
		||||
      use_header_bar = FALSE;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  if (use_header_bar) {
 | 
			
		||||
    GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
 | 
			
		||||
    gtk_widget_show(GTK_WIDGET(header_bar));
 | 
			
		||||
    gtk_header_bar_set_title(header_bar, "xp_dashboard");
 | 
			
		||||
    gtk_header_bar_set_show_close_button(header_bar, TRUE);
 | 
			
		||||
    gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
 | 
			
		||||
  } else {
 | 
			
		||||
    gtk_window_set_title(window, "xp_dashboard");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  gtk_window_set_default_size(window, 1280, 720);
 | 
			
		||||
  gtk_widget_show(GTK_WIDGET(window));
 | 
			
		||||
 | 
			
		||||
  g_autoptr(FlDartProject) project = fl_dart_project_new();
 | 
			
		||||
  fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
 | 
			
		||||
 | 
			
		||||
  FlView* view = fl_view_new(project);
 | 
			
		||||
  gtk_widget_show(GTK_WIDGET(view));
 | 
			
		||||
  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
 | 
			
		||||
 | 
			
		||||
  fl_register_plugins(FL_PLUGIN_REGISTRY(view));
 | 
			
		||||
 | 
			
		||||
  gtk_widget_grab_focus(GTK_WIDGET(view));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implements GApplication::local_command_line.
 | 
			
		||||
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
 | 
			
		||||
  MyApplication* self = MY_APPLICATION(application);
 | 
			
		||||
  // Strip out the first argument as it is the binary name.
 | 
			
		||||
  self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
 | 
			
		||||
 | 
			
		||||
  g_autoptr(GError) error = nullptr;
 | 
			
		||||
  if (!g_application_register(application, nullptr, &error)) {
 | 
			
		||||
     g_warning("Failed to register: %s", error->message);
 | 
			
		||||
     *exit_status = 1;
 | 
			
		||||
     return TRUE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  g_application_activate(application);
 | 
			
		||||
  *exit_status = 0;
 | 
			
		||||
 | 
			
		||||
  return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implements GApplication::startup.
 | 
			
		||||
static void my_application_startup(GApplication* application) {
 | 
			
		||||
  //MyApplication* self = MY_APPLICATION(object);
 | 
			
		||||
 | 
			
		||||
  // Perform any actions required at application startup.
 | 
			
		||||
 | 
			
		||||
  G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implements GApplication::shutdown.
 | 
			
		||||
static void my_application_shutdown(GApplication* application) {
 | 
			
		||||
  //MyApplication* self = MY_APPLICATION(object);
 | 
			
		||||
 | 
			
		||||
  // Perform any actions required at application shutdown.
 | 
			
		||||
 | 
			
		||||
  G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implements GObject::dispose.
 | 
			
		||||
static void my_application_dispose(GObject* object) {
 | 
			
		||||
  MyApplication* self = MY_APPLICATION(object);
 | 
			
		||||
  g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
 | 
			
		||||
  G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void my_application_class_init(MyApplicationClass* klass) {
 | 
			
		||||
  G_APPLICATION_CLASS(klass)->activate = my_application_activate;
 | 
			
		||||
  G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
 | 
			
		||||
  G_APPLICATION_CLASS(klass)->startup = my_application_startup;
 | 
			
		||||
  G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
 | 
			
		||||
  G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void my_application_init(MyApplication* self) {}
 | 
			
		||||
 | 
			
		||||
MyApplication* my_application_new() {
 | 
			
		||||
  // Set the program name to the application ID, which helps various systems
 | 
			
		||||
  // like GTK and desktop environments map this running application to its
 | 
			
		||||
  // corresponding .desktop file. This ensures better integration by allowing
 | 
			
		||||
  // the application to be recognized beyond its binary name.
 | 
			
		||||
  g_set_prgname(APPLICATION_ID);
 | 
			
		||||
 | 
			
		||||
  return MY_APPLICATION(g_object_new(my_application_get_type(),
 | 
			
		||||
                                     "application-id", APPLICATION_ID,
 | 
			
		||||
                                     "flags", G_APPLICATION_NON_UNIQUE,
 | 
			
		||||
                                     nullptr));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								xp_dashboard/linux/runner/my_application.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,18 @@
 | 
			
		||||
#ifndef FLUTTER_MY_APPLICATION_H_
 | 
			
		||||
#define FLUTTER_MY_APPLICATION_H_
 | 
			
		||||
 | 
			
		||||
#include <gtk/gtk.h>
 | 
			
		||||
 | 
			
		||||
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
 | 
			
		||||
                     GtkApplication)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * my_application_new:
 | 
			
		||||
 *
 | 
			
		||||
 * Creates a new Flutter-based application.
 | 
			
		||||
 *
 | 
			
		||||
 * Returns: a new #MyApplication.
 | 
			
		||||
 */
 | 
			
		||||
MyApplication* my_application_new();
 | 
			
		||||
 | 
			
		||||
#endif  // FLUTTER_MY_APPLICATION_H_
 | 
			
		||||
							
								
								
									
										7
									
								
								xp_dashboard/macos/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,7 @@
 | 
			
		||||
# Flutter-related
 | 
			
		||||
**/Flutter/ephemeral/
 | 
			
		||||
**/Pods/
 | 
			
		||||
 | 
			
		||||
# Xcode-related
 | 
			
		||||
**/dgph
 | 
			
		||||
**/xcuserdata/
 | 
			
		||||
							
								
								
									
										1
									
								
								xp_dashboard/macos/Flutter/Flutter-Debug.xcconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1 @@
 | 
			
		||||
#include "ephemeral/Flutter-Generated.xcconfig"
 | 
			
		||||
							
								
								
									
										1
									
								
								xp_dashboard/macos/Flutter/Flutter-Release.xcconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1 @@
 | 
			
		||||
#include "ephemeral/Flutter-Generated.xcconfig"
 | 
			
		||||
							
								
								
									
										10
									
								
								xp_dashboard/macos/Flutter/GeneratedPluginRegistrant.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,10 @@
 | 
			
		||||
//
 | 
			
		||||
//  Generated file. Do not edit.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
import FlutterMacOS
 | 
			
		||||
import Foundation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										705
									
								
								xp_dashboard/macos/Runner.xcodeproj/project.pbxproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,705 @@
 | 
			
		||||
// !$*UTF8*$!
 | 
			
		||||
{
 | 
			
		||||
	archiveVersion = 1;
 | 
			
		||||
	classes = {
 | 
			
		||||
	};
 | 
			
		||||
	objectVersion = 54;
 | 
			
		||||
	objects = {
 | 
			
		||||
 | 
			
		||||
/* Begin PBXAggregateTarget section */
 | 
			
		||||
		33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
 | 
			
		||||
			isa = PBXAggregateTarget;
 | 
			
		||||
			buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
 | 
			
		||||
			buildPhases = (
 | 
			
		||||
				33CC111E2044C6BF0003C045 /* ShellScript */,
 | 
			
		||||
			);
 | 
			
		||||
			dependencies = (
 | 
			
		||||
			);
 | 
			
		||||
			name = "Flutter Assemble";
 | 
			
		||||
			productName = FLX;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXAggregateTarget section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXBuildFile section */
 | 
			
		||||
		331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
 | 
			
		||||
		335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
 | 
			
		||||
		33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
 | 
			
		||||
		33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
 | 
			
		||||
		33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
 | 
			
		||||
		33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
 | 
			
		||||
/* End PBXBuildFile section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXContainerItemProxy section */
 | 
			
		||||
		331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {
 | 
			
		||||
			isa = PBXContainerItemProxy;
 | 
			
		||||
			containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
 | 
			
		||||
			proxyType = 1;
 | 
			
		||||
			remoteGlobalIDString = 33CC10EC2044A3C60003C045;
 | 
			
		||||
			remoteInfo = Runner;
 | 
			
		||||
		};
 | 
			
		||||
		33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
 | 
			
		||||
			isa = PBXContainerItemProxy;
 | 
			
		||||
			containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
 | 
			
		||||
			proxyType = 1;
 | 
			
		||||
			remoteGlobalIDString = 33CC111A2044C6BA0003C045;
 | 
			
		||||
			remoteInfo = FLX;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXContainerItemProxy section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXCopyFilesBuildPhase section */
 | 
			
		||||
		33CC110E2044A8840003C045 /* Bundle Framework */ = {
 | 
			
		||||
			isa = PBXCopyFilesBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			dstPath = "";
 | 
			
		||||
			dstSubfolderSpec = 10;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			name = "Bundle Framework";
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXCopyFilesBuildPhase section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXFileReference section */
 | 
			
		||||
		331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
			
		||||
		331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
 | 
			
		||||
		333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
 | 
			
		||||
		335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
 | 
			
		||||
		33CC10ED2044A3C60003C045 /* xp_dashboard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "xp_dashboard.app"; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
			
		||||
		33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 | 
			
		||||
		33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
 | 
			
		||||
		33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
 | 
			
		||||
		33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
 | 
			
		||||
		33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
 | 
			
		||||
		33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
 | 
			
		||||
		33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
 | 
			
		||||
		33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
 | 
			
		||||
		33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
 | 
			
		||||
		33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
 | 
			
		||||
		33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
 | 
			
		||||
		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
 | 
			
		||||
		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
 | 
			
		||||
/* End PBXFileReference section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXFrameworksBuildPhase section */
 | 
			
		||||
		331C80D2294CF70F00263BE5 /* Frameworks */ = {
 | 
			
		||||
			isa = PBXFrameworksBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
		33CC10EA2044A3C60003C045 /* Frameworks */ = {
 | 
			
		||||
			isa = PBXFrameworksBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXFrameworksBuildPhase section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXGroup section */
 | 
			
		||||
		331C80D6294CF71000263BE5 /* RunnerTests */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				331C80D7294CF71000263BE5 /* RunnerTests.swift */,
 | 
			
		||||
			);
 | 
			
		||||
			path = RunnerTests;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		33BA886A226E78AF003329D5 /* Configs */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				33E5194F232828860026EE4D /* AppInfo.xcconfig */,
 | 
			
		||||
				9740EEB21CF90195004384FC /* Debug.xcconfig */,
 | 
			
		||||
				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
 | 
			
		||||
				333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
 | 
			
		||||
			);
 | 
			
		||||
			path = Configs;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		33CC10E42044A3C60003C045 = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				33FAB671232836740065AC1E /* Runner */,
 | 
			
		||||
				33CEB47122A05771004F2AC0 /* Flutter */,
 | 
			
		||||
				331C80D6294CF71000263BE5 /* RunnerTests */,
 | 
			
		||||
				33CC10EE2044A3C60003C045 /* Products */,
 | 
			
		||||
				D73912EC22F37F3D000D13A0 /* Frameworks */,
 | 
			
		||||
			);
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		33CC10EE2044A3C60003C045 /* Products */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				33CC10ED2044A3C60003C045 /* xp_dashboard.app */,
 | 
			
		||||
				331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
 | 
			
		||||
			);
 | 
			
		||||
			name = Products;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		33CC11242044D66E0003C045 /* Resources */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				33CC10F22044A3C60003C045 /* Assets.xcassets */,
 | 
			
		||||
				33CC10F42044A3C60003C045 /* MainMenu.xib */,
 | 
			
		||||
				33CC10F72044A3C60003C045 /* Info.plist */,
 | 
			
		||||
			);
 | 
			
		||||
			name = Resources;
 | 
			
		||||
			path = ..;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		33CEB47122A05771004F2AC0 /* Flutter */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
 | 
			
		||||
				33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
 | 
			
		||||
				33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
 | 
			
		||||
				33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
 | 
			
		||||
			);
 | 
			
		||||
			path = Flutter;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		33FAB671232836740065AC1E /* Runner */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				33CC10F02044A3C60003C045 /* AppDelegate.swift */,
 | 
			
		||||
				33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
 | 
			
		||||
				33E51913231747F40026EE4D /* DebugProfile.entitlements */,
 | 
			
		||||
				33E51914231749380026EE4D /* Release.entitlements */,
 | 
			
		||||
				33CC11242044D66E0003C045 /* Resources */,
 | 
			
		||||
				33BA886A226E78AF003329D5 /* Configs */,
 | 
			
		||||
			);
 | 
			
		||||
			path = Runner;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
		D73912EC22F37F3D000D13A0 /* Frameworks */ = {
 | 
			
		||||
			isa = PBXGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
			);
 | 
			
		||||
			name = Frameworks;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXGroup section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXNativeTarget section */
 | 
			
		||||
		331C80D4294CF70F00263BE5 /* RunnerTests */ = {
 | 
			
		||||
			isa = PBXNativeTarget;
 | 
			
		||||
			buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
 | 
			
		||||
			buildPhases = (
 | 
			
		||||
				331C80D1294CF70F00263BE5 /* Sources */,
 | 
			
		||||
				331C80D2294CF70F00263BE5 /* Frameworks */,
 | 
			
		||||
				331C80D3294CF70F00263BE5 /* Resources */,
 | 
			
		||||
			);
 | 
			
		||||
			buildRules = (
 | 
			
		||||
			);
 | 
			
		||||
			dependencies = (
 | 
			
		||||
				331C80DA294CF71000263BE5 /* PBXTargetDependency */,
 | 
			
		||||
			);
 | 
			
		||||
			name = RunnerTests;
 | 
			
		||||
			productName = RunnerTests;
 | 
			
		||||
			productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;
 | 
			
		||||
			productType = "com.apple.product-type.bundle.unit-test";
 | 
			
		||||
		};
 | 
			
		||||
		33CC10EC2044A3C60003C045 /* Runner */ = {
 | 
			
		||||
			isa = PBXNativeTarget;
 | 
			
		||||
			buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
 | 
			
		||||
			buildPhases = (
 | 
			
		||||
				33CC10E92044A3C60003C045 /* Sources */,
 | 
			
		||||
				33CC10EA2044A3C60003C045 /* Frameworks */,
 | 
			
		||||
				33CC10EB2044A3C60003C045 /* Resources */,
 | 
			
		||||
				33CC110E2044A8840003C045 /* Bundle Framework */,
 | 
			
		||||
				3399D490228B24CF009A79C7 /* ShellScript */,
 | 
			
		||||
			);
 | 
			
		||||
			buildRules = (
 | 
			
		||||
			);
 | 
			
		||||
			dependencies = (
 | 
			
		||||
				33CC11202044C79F0003C045 /* PBXTargetDependency */,
 | 
			
		||||
			);
 | 
			
		||||
			name = Runner;
 | 
			
		||||
			productName = Runner;
 | 
			
		||||
			productReference = 33CC10ED2044A3C60003C045 /* xp_dashboard.app */;
 | 
			
		||||
			productType = "com.apple.product-type.application";
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXNativeTarget section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXProject section */
 | 
			
		||||
		33CC10E52044A3C60003C045 /* Project object */ = {
 | 
			
		||||
			isa = PBXProject;
 | 
			
		||||
			attributes = {
 | 
			
		||||
				BuildIndependentTargetsInParallel = YES;
 | 
			
		||||
				LastSwiftUpdateCheck = 0920;
 | 
			
		||||
				LastUpgradeCheck = 1510;
 | 
			
		||||
				ORGANIZATIONNAME = "";
 | 
			
		||||
				TargetAttributes = {
 | 
			
		||||
					331C80D4294CF70F00263BE5 = {
 | 
			
		||||
						CreatedOnToolsVersion = 14.0;
 | 
			
		||||
						TestTargetID = 33CC10EC2044A3C60003C045;
 | 
			
		||||
					};
 | 
			
		||||
					33CC10EC2044A3C60003C045 = {
 | 
			
		||||
						CreatedOnToolsVersion = 9.2;
 | 
			
		||||
						LastSwiftMigration = 1100;
 | 
			
		||||
						ProvisioningStyle = Automatic;
 | 
			
		||||
						SystemCapabilities = {
 | 
			
		||||
							com.apple.Sandbox = {
 | 
			
		||||
								enabled = 1;
 | 
			
		||||
							};
 | 
			
		||||
						};
 | 
			
		||||
					};
 | 
			
		||||
					33CC111A2044C6BA0003C045 = {
 | 
			
		||||
						CreatedOnToolsVersion = 9.2;
 | 
			
		||||
						ProvisioningStyle = Manual;
 | 
			
		||||
					};
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
			buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
 | 
			
		||||
			compatibilityVersion = "Xcode 9.3";
 | 
			
		||||
			developmentRegion = en;
 | 
			
		||||
			hasScannedForEncodings = 0;
 | 
			
		||||
			knownRegions = (
 | 
			
		||||
				en,
 | 
			
		||||
				Base,
 | 
			
		||||
			);
 | 
			
		||||
			mainGroup = 33CC10E42044A3C60003C045;
 | 
			
		||||
			productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
 | 
			
		||||
			projectDirPath = "";
 | 
			
		||||
			projectRoot = "";
 | 
			
		||||
			targets = (
 | 
			
		||||
				33CC10EC2044A3C60003C045 /* Runner */,
 | 
			
		||||
				331C80D4294CF70F00263BE5 /* RunnerTests */,
 | 
			
		||||
				33CC111A2044C6BA0003C045 /* Flutter Assemble */,
 | 
			
		||||
			);
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXProject section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXResourcesBuildPhase section */
 | 
			
		||||
		331C80D3294CF70F00263BE5 /* Resources */ = {
 | 
			
		||||
			isa = PBXResourcesBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
		33CC10EB2044A3C60003C045 /* Resources */ = {
 | 
			
		||||
			isa = PBXResourcesBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
				33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
 | 
			
		||||
				33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXResourcesBuildPhase section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXShellScriptBuildPhase section */
 | 
			
		||||
		3399D490228B24CF009A79C7 /* ShellScript */ = {
 | 
			
		||||
			isa = PBXShellScriptBuildPhase;
 | 
			
		||||
			alwaysOutOfDate = 1;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			inputFileListPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			inputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			outputFileListPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			outputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
			shellPath = /bin/sh;
 | 
			
		||||
			shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
 | 
			
		||||
		};
 | 
			
		||||
		33CC111E2044C6BF0003C045 /* ShellScript */ = {
 | 
			
		||||
			isa = PBXShellScriptBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
			);
 | 
			
		||||
			inputFileListPaths = (
 | 
			
		||||
				Flutter/ephemeral/FlutterInputs.xcfilelist,
 | 
			
		||||
			);
 | 
			
		||||
			inputPaths = (
 | 
			
		||||
				Flutter/ephemeral/tripwire,
 | 
			
		||||
			);
 | 
			
		||||
			outputFileListPaths = (
 | 
			
		||||
				Flutter/ephemeral/FlutterOutputs.xcfilelist,
 | 
			
		||||
			);
 | 
			
		||||
			outputPaths = (
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
			shellPath = /bin/sh;
 | 
			
		||||
			shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXShellScriptBuildPhase section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXSourcesBuildPhase section */
 | 
			
		||||
		331C80D1294CF70F00263BE5 /* Sources */ = {
 | 
			
		||||
			isa = PBXSourcesBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
				331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
		33CC10E92044A3C60003C045 /* Sources */ = {
 | 
			
		||||
			isa = PBXSourcesBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
				33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
 | 
			
		||||
				33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
 | 
			
		||||
				335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
 | 
			
		||||
			);
 | 
			
		||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXSourcesBuildPhase section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXTargetDependency section */
 | 
			
		||||
		331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {
 | 
			
		||||
			isa = PBXTargetDependency;
 | 
			
		||||
			target = 33CC10EC2044A3C60003C045 /* Runner */;
 | 
			
		||||
			targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;
 | 
			
		||||
		};
 | 
			
		||||
		33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
 | 
			
		||||
			isa = PBXTargetDependency;
 | 
			
		||||
			target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
 | 
			
		||||
			targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXTargetDependency section */
 | 
			
		||||
 | 
			
		||||
/* Begin PBXVariantGroup section */
 | 
			
		||||
		33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
 | 
			
		||||
			isa = PBXVariantGroup;
 | 
			
		||||
			children = (
 | 
			
		||||
				33CC10F52044A3C60003C045 /* Base */,
 | 
			
		||||
			);
 | 
			
		||||
			name = MainMenu.xib;
 | 
			
		||||
			path = Runner;
 | 
			
		||||
			sourceTree = "<group>";
 | 
			
		||||
		};
 | 
			
		||||
/* End PBXVariantGroup section */
 | 
			
		||||
 | 
			
		||||
/* Begin XCBuildConfiguration section */
 | 
			
		||||
		331C80DB294CF71000263BE5 /* Debug */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				BUNDLE_LOADER = "$(TEST_HOST)";
 | 
			
		||||
				CURRENT_PROJECT_VERSION = 1;
 | 
			
		||||
				GENERATE_INFOPLIST_FILE = YES;
 | 
			
		||||
				MARKETING_VERSION = 1.0;
 | 
			
		||||
				PRODUCT_BUNDLE_IDENTIFIER = com.example.xpDashboard.RunnerTests;
 | 
			
		||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/xp_dashboard.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/xp_dashboard";
 | 
			
		||||
			};
 | 
			
		||||
			name = Debug;
 | 
			
		||||
		};
 | 
			
		||||
		331C80DC294CF71000263BE5 /* Release */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				BUNDLE_LOADER = "$(TEST_HOST)";
 | 
			
		||||
				CURRENT_PROJECT_VERSION = 1;
 | 
			
		||||
				GENERATE_INFOPLIST_FILE = YES;
 | 
			
		||||
				MARKETING_VERSION = 1.0;
 | 
			
		||||
				PRODUCT_BUNDLE_IDENTIFIER = com.example.xpDashboard.RunnerTests;
 | 
			
		||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/xp_dashboard.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/xp_dashboard";
 | 
			
		||||
			};
 | 
			
		||||
			name = Release;
 | 
			
		||||
		};
 | 
			
		||||
		331C80DD294CF71000263BE5 /* Profile */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				BUNDLE_LOADER = "$(TEST_HOST)";
 | 
			
		||||
				CURRENT_PROJECT_VERSION = 1;
 | 
			
		||||
				GENERATE_INFOPLIST_FILE = YES;
 | 
			
		||||
				MARKETING_VERSION = 1.0;
 | 
			
		||||
				PRODUCT_BUNDLE_IDENTIFIER = com.example.xpDashboard.RunnerTests;
 | 
			
		||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/xp_dashboard.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/xp_dashboard";
 | 
			
		||||
			};
 | 
			
		||||
			name = Profile;
 | 
			
		||||
		};
 | 
			
		||||
		338D0CE9231458BD00FA5F75 /* Profile */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				ALWAYS_SEARCH_USER_PATHS = NO;
 | 
			
		||||
				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
 | 
			
		||||
				CLANG_ANALYZER_NONNULL = YES;
 | 
			
		||||
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 | 
			
		||||
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
 | 
			
		||||
				CLANG_CXX_LIBRARY = "libc++";
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CLANG_ENABLE_OBJC_ARC = YES;
 | 
			
		||||
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 | 
			
		||||
				CLANG_WARN_BOOL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_CONSTANT_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 | 
			
		||||
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 | 
			
		||||
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 | 
			
		||||
				CLANG_WARN_EMPTY_BODY = YES;
 | 
			
		||||
				CLANG_WARN_ENUM_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_INFINITE_RECURSION = YES;
 | 
			
		||||
				CLANG_WARN_INT_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 | 
			
		||||
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 | 
			
		||||
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 | 
			
		||||
				CODE_SIGN_IDENTITY = "-";
 | 
			
		||||
				COPY_PHASE_STRIP = NO;
 | 
			
		||||
				DEAD_CODE_STRIPPING = YES;
 | 
			
		||||
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 | 
			
		||||
				ENABLE_NS_ASSERTIONS = NO;
 | 
			
		||||
				ENABLE_STRICT_OBJC_MSGSEND = YES;
 | 
			
		||||
				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 | 
			
		||||
				GCC_C_LANGUAGE_STANDARD = gnu11;
 | 
			
		||||
				GCC_NO_COMMON_BLOCKS = YES;
 | 
			
		||||
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 | 
			
		||||
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
 | 
			
		||||
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
			
		||||
				GCC_WARN_UNUSED_FUNCTION = YES;
 | 
			
		||||
				GCC_WARN_UNUSED_VARIABLE = YES;
 | 
			
		||||
				MACOSX_DEPLOYMENT_TARGET = 10.14;
 | 
			
		||||
				MTL_ENABLE_DEBUG_INFO = NO;
 | 
			
		||||
				SDKROOT = macosx;
 | 
			
		||||
				SWIFT_COMPILATION_MODE = wholemodule;
 | 
			
		||||
				SWIFT_OPTIMIZATION_LEVEL = "-O";
 | 
			
		||||
			};
 | 
			
		||||
			name = Profile;
 | 
			
		||||
		};
 | 
			
		||||
		338D0CEA231458BD00FA5F75 /* Profile */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
 | 
			
		||||
				CODE_SIGN_STYLE = Automatic;
 | 
			
		||||
				COMBINE_HIDPI_IMAGES = YES;
 | 
			
		||||
				INFOPLIST_FILE = Runner/Info.plist;
 | 
			
		||||
				LD_RUNPATH_SEARCH_PATHS = (
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
					"@executable_path/../Frameworks",
 | 
			
		||||
				);
 | 
			
		||||
				PROVISIONING_PROFILE_SPECIFIER = "";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
			};
 | 
			
		||||
			name = Profile;
 | 
			
		||||
		};
 | 
			
		||||
		338D0CEB231458BD00FA5F75 /* Profile */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				CODE_SIGN_STYLE = Manual;
 | 
			
		||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
			
		||||
			};
 | 
			
		||||
			name = Profile;
 | 
			
		||||
		};
 | 
			
		||||
		33CC10F92044A3C60003C045 /* Debug */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				ALWAYS_SEARCH_USER_PATHS = NO;
 | 
			
		||||
				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
 | 
			
		||||
				CLANG_ANALYZER_NONNULL = YES;
 | 
			
		||||
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 | 
			
		||||
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
 | 
			
		||||
				CLANG_CXX_LIBRARY = "libc++";
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CLANG_ENABLE_OBJC_ARC = YES;
 | 
			
		||||
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 | 
			
		||||
				CLANG_WARN_BOOL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_CONSTANT_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 | 
			
		||||
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 | 
			
		||||
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 | 
			
		||||
				CLANG_WARN_EMPTY_BODY = YES;
 | 
			
		||||
				CLANG_WARN_ENUM_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_INFINITE_RECURSION = YES;
 | 
			
		||||
				CLANG_WARN_INT_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 | 
			
		||||
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 | 
			
		||||
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 | 
			
		||||
				CODE_SIGN_IDENTITY = "-";
 | 
			
		||||
				COPY_PHASE_STRIP = NO;
 | 
			
		||||
				DEAD_CODE_STRIPPING = YES;
 | 
			
		||||
				DEBUG_INFORMATION_FORMAT = dwarf;
 | 
			
		||||
				ENABLE_STRICT_OBJC_MSGSEND = YES;
 | 
			
		||||
				ENABLE_TESTABILITY = YES;
 | 
			
		||||
				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 | 
			
		||||
				GCC_C_LANGUAGE_STANDARD = gnu11;
 | 
			
		||||
				GCC_DYNAMIC_NO_PIC = NO;
 | 
			
		||||
				GCC_NO_COMMON_BLOCKS = YES;
 | 
			
		||||
				GCC_OPTIMIZATION_LEVEL = 0;
 | 
			
		||||
				GCC_PREPROCESSOR_DEFINITIONS = (
 | 
			
		||||
					"DEBUG=1",
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
				);
 | 
			
		||||
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 | 
			
		||||
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
 | 
			
		||||
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
			
		||||
				GCC_WARN_UNUSED_FUNCTION = YES;
 | 
			
		||||
				GCC_WARN_UNUSED_VARIABLE = YES;
 | 
			
		||||
				MACOSX_DEPLOYMENT_TARGET = 10.14;
 | 
			
		||||
				MTL_ENABLE_DEBUG_INFO = YES;
 | 
			
		||||
				ONLY_ACTIVE_ARCH = YES;
 | 
			
		||||
				SDKROOT = macosx;
 | 
			
		||||
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
 | 
			
		||||
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
			
		||||
			};
 | 
			
		||||
			name = Debug;
 | 
			
		||||
		};
 | 
			
		||||
		33CC10FA2044A3C60003C045 /* Release */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				ALWAYS_SEARCH_USER_PATHS = NO;
 | 
			
		||||
				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
 | 
			
		||||
				CLANG_ANALYZER_NONNULL = YES;
 | 
			
		||||
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
 | 
			
		||||
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
 | 
			
		||||
				CLANG_CXX_LIBRARY = "libc++";
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CLANG_ENABLE_OBJC_ARC = YES;
 | 
			
		||||
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
 | 
			
		||||
				CLANG_WARN_BOOL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_CONSTANT_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
 | 
			
		||||
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
 | 
			
		||||
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
 | 
			
		||||
				CLANG_WARN_EMPTY_BODY = YES;
 | 
			
		||||
				CLANG_WARN_ENUM_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_INFINITE_RECURSION = YES;
 | 
			
		||||
				CLANG_WARN_INT_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
 | 
			
		||||
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
 | 
			
		||||
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
 | 
			
		||||
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
 | 
			
		||||
				CODE_SIGN_IDENTITY = "-";
 | 
			
		||||
				COPY_PHASE_STRIP = NO;
 | 
			
		||||
				DEAD_CODE_STRIPPING = YES;
 | 
			
		||||
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 | 
			
		||||
				ENABLE_NS_ASSERTIONS = NO;
 | 
			
		||||
				ENABLE_STRICT_OBJC_MSGSEND = YES;
 | 
			
		||||
				ENABLE_USER_SCRIPT_SANDBOXING = NO;
 | 
			
		||||
				GCC_C_LANGUAGE_STANDARD = gnu11;
 | 
			
		||||
				GCC_NO_COMMON_BLOCKS = YES;
 | 
			
		||||
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 | 
			
		||||
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
 | 
			
		||||
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 | 
			
		||||
				GCC_WARN_UNUSED_FUNCTION = YES;
 | 
			
		||||
				GCC_WARN_UNUSED_VARIABLE = YES;
 | 
			
		||||
				MACOSX_DEPLOYMENT_TARGET = 10.14;
 | 
			
		||||
				MTL_ENABLE_DEBUG_INFO = NO;
 | 
			
		||||
				SDKROOT = macosx;
 | 
			
		||||
				SWIFT_COMPILATION_MODE = wholemodule;
 | 
			
		||||
				SWIFT_OPTIMIZATION_LEVEL = "-O";
 | 
			
		||||
			};
 | 
			
		||||
			name = Release;
 | 
			
		||||
		};
 | 
			
		||||
		33CC10FC2044A3C60003C045 /* Debug */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
 | 
			
		||||
				CODE_SIGN_STYLE = Automatic;
 | 
			
		||||
				COMBINE_HIDPI_IMAGES = YES;
 | 
			
		||||
				INFOPLIST_FILE = Runner/Info.plist;
 | 
			
		||||
				LD_RUNPATH_SEARCH_PATHS = (
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
					"@executable_path/../Frameworks",
 | 
			
		||||
				);
 | 
			
		||||
				PROVISIONING_PROFILE_SPECIFIER = "";
 | 
			
		||||
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
			};
 | 
			
		||||
			name = Debug;
 | 
			
		||||
		};
 | 
			
		||||
		33CC10FD2044A3C60003C045 /* Release */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
 | 
			
		||||
				CODE_SIGN_STYLE = Automatic;
 | 
			
		||||
				COMBINE_HIDPI_IMAGES = YES;
 | 
			
		||||
				INFOPLIST_FILE = Runner/Info.plist;
 | 
			
		||||
				LD_RUNPATH_SEARCH_PATHS = (
 | 
			
		||||
					"$(inherited)",
 | 
			
		||||
					"@executable_path/../Frameworks",
 | 
			
		||||
				);
 | 
			
		||||
				PROVISIONING_PROFILE_SPECIFIER = "";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
			};
 | 
			
		||||
			name = Release;
 | 
			
		||||
		};
 | 
			
		||||
		33CC111C2044C6BA0003C045 /* Debug */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				CODE_SIGN_STYLE = Manual;
 | 
			
		||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
			
		||||
			};
 | 
			
		||||
			name = Debug;
 | 
			
		||||
		};
 | 
			
		||||
		33CC111D2044C6BA0003C045 /* Release */ = {
 | 
			
		||||
			isa = XCBuildConfiguration;
 | 
			
		||||
			buildSettings = {
 | 
			
		||||
				CODE_SIGN_STYLE = Automatic;
 | 
			
		||||
				PRODUCT_NAME = "$(TARGET_NAME)";
 | 
			
		||||
			};
 | 
			
		||||
			name = Release;
 | 
			
		||||
		};
 | 
			
		||||
/* End XCBuildConfiguration section */
 | 
			
		||||
 | 
			
		||||
/* Begin XCConfigurationList section */
 | 
			
		||||
		331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
 | 
			
		||||
			isa = XCConfigurationList;
 | 
			
		||||
			buildConfigurations = (
 | 
			
		||||
				331C80DB294CF71000263BE5 /* Debug */,
 | 
			
		||||
				331C80DC294CF71000263BE5 /* Release */,
 | 
			
		||||
				331C80DD294CF71000263BE5 /* Profile */,
 | 
			
		||||
			);
 | 
			
		||||
			defaultConfigurationIsVisible = 0;
 | 
			
		||||
			defaultConfigurationName = Release;
 | 
			
		||||
		};
 | 
			
		||||
		33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
 | 
			
		||||
			isa = XCConfigurationList;
 | 
			
		||||
			buildConfigurations = (
 | 
			
		||||
				33CC10F92044A3C60003C045 /* Debug */,
 | 
			
		||||
				33CC10FA2044A3C60003C045 /* Release */,
 | 
			
		||||
				338D0CE9231458BD00FA5F75 /* Profile */,
 | 
			
		||||
			);
 | 
			
		||||
			defaultConfigurationIsVisible = 0;
 | 
			
		||||
			defaultConfigurationName = Release;
 | 
			
		||||
		};
 | 
			
		||||
		33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
 | 
			
		||||
			isa = XCConfigurationList;
 | 
			
		||||
			buildConfigurations = (
 | 
			
		||||
				33CC10FC2044A3C60003C045 /* Debug */,
 | 
			
		||||
				33CC10FD2044A3C60003C045 /* Release */,
 | 
			
		||||
				338D0CEA231458BD00FA5F75 /* Profile */,
 | 
			
		||||
			);
 | 
			
		||||
			defaultConfigurationIsVisible = 0;
 | 
			
		||||
			defaultConfigurationName = Release;
 | 
			
		||||
		};
 | 
			
		||||
		33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
 | 
			
		||||
			isa = XCConfigurationList;
 | 
			
		||||
			buildConfigurations = (
 | 
			
		||||
				33CC111C2044C6BA0003C045 /* Debug */,
 | 
			
		||||
				33CC111D2044C6BA0003C045 /* Release */,
 | 
			
		||||
				338D0CEB231458BD00FA5F75 /* Profile */,
 | 
			
		||||
			);
 | 
			
		||||
			defaultConfigurationIsVisible = 0;
 | 
			
		||||
			defaultConfigurationName = Release;
 | 
			
		||||
		};
 | 
			
		||||
/* End XCConfigurationList section */
 | 
			
		||||
	};
 | 
			
		||||
	rootObject = 33CC10E52044A3C60003C045 /* Project object */;
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
<dict>
 | 
			
		||||
	<key>IDEDidComputeMac32BitWarning</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
@ -0,0 +1,99 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<Scheme
 | 
			
		||||
   LastUpgradeVersion = "1510"
 | 
			
		||||
   version = "1.3">
 | 
			
		||||
   <BuildAction
 | 
			
		||||
      parallelizeBuildables = "YES"
 | 
			
		||||
      buildImplicitDependencies = "YES">
 | 
			
		||||
      <BuildActionEntries>
 | 
			
		||||
         <BuildActionEntry
 | 
			
		||||
            buildForTesting = "YES"
 | 
			
		||||
            buildForRunning = "YES"
 | 
			
		||||
            buildForProfiling = "YES"
 | 
			
		||||
            buildForArchiving = "YES"
 | 
			
		||||
            buildForAnalyzing = "YES">
 | 
			
		||||
            <BuildableReference
 | 
			
		||||
               BuildableIdentifier = "primary"
 | 
			
		||||
               BlueprintIdentifier = "33CC10EC2044A3C60003C045"
 | 
			
		||||
               BuildableName = "xp_dashboard.app"
 | 
			
		||||
               BlueprintName = "Runner"
 | 
			
		||||
               ReferencedContainer = "container:Runner.xcodeproj">
 | 
			
		||||
            </BuildableReference>
 | 
			
		||||
         </BuildActionEntry>
 | 
			
		||||
      </BuildActionEntries>
 | 
			
		||||
   </BuildAction>
 | 
			
		||||
   <TestAction
 | 
			
		||||
      buildConfiguration = "Debug"
 | 
			
		||||
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
 | 
			
		||||
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
 | 
			
		||||
      shouldUseLaunchSchemeArgsEnv = "YES">
 | 
			
		||||
      <MacroExpansion>
 | 
			
		||||
         <BuildableReference
 | 
			
		||||
            BuildableIdentifier = "primary"
 | 
			
		||||
            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
 | 
			
		||||
            BuildableName = "xp_dashboard.app"
 | 
			
		||||
            BlueprintName = "Runner"
 | 
			
		||||
            ReferencedContainer = "container:Runner.xcodeproj">
 | 
			
		||||
         </BuildableReference>
 | 
			
		||||
      </MacroExpansion>
 | 
			
		||||
      <Testables>
 | 
			
		||||
         <TestableReference
 | 
			
		||||
            skipped = "NO"
 | 
			
		||||
            parallelizable = "YES">
 | 
			
		||||
            <BuildableReference
 | 
			
		||||
               BuildableIdentifier = "primary"
 | 
			
		||||
               BlueprintIdentifier = "331C80D4294CF70F00263BE5"
 | 
			
		||||
               BuildableName = "RunnerTests.xctest"
 | 
			
		||||
               BlueprintName = "RunnerTests"
 | 
			
		||||
               ReferencedContainer = "container:Runner.xcodeproj">
 | 
			
		||||
            </BuildableReference>
 | 
			
		||||
         </TestableReference>
 | 
			
		||||
      </Testables>
 | 
			
		||||
   </TestAction>
 | 
			
		||||
   <LaunchAction
 | 
			
		||||
      buildConfiguration = "Debug"
 | 
			
		||||
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
 | 
			
		||||
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
 | 
			
		||||
      launchStyle = "0"
 | 
			
		||||
      useCustomWorkingDirectory = "NO"
 | 
			
		||||
      ignoresPersistentStateOnLaunch = "NO"
 | 
			
		||||
      debugDocumentVersioning = "YES"
 | 
			
		||||
      debugServiceExtension = "internal"
 | 
			
		||||
      enableGPUValidationMode = "1"
 | 
			
		||||
      allowLocationSimulation = "YES">
 | 
			
		||||
      <BuildableProductRunnable
 | 
			
		||||
         runnableDebuggingMode = "0">
 | 
			
		||||
         <BuildableReference
 | 
			
		||||
            BuildableIdentifier = "primary"
 | 
			
		||||
            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
 | 
			
		||||
            BuildableName = "xp_dashboard.app"
 | 
			
		||||
            BlueprintName = "Runner"
 | 
			
		||||
            ReferencedContainer = "container:Runner.xcodeproj">
 | 
			
		||||
         </BuildableReference>
 | 
			
		||||
      </BuildableProductRunnable>
 | 
			
		||||
   </LaunchAction>
 | 
			
		||||
   <ProfileAction
 | 
			
		||||
      buildConfiguration = "Profile"
 | 
			
		||||
      shouldUseLaunchSchemeArgsEnv = "YES"
 | 
			
		||||
      savedToolIdentifier = ""
 | 
			
		||||
      useCustomWorkingDirectory = "NO"
 | 
			
		||||
      debugDocumentVersioning = "YES">
 | 
			
		||||
      <BuildableProductRunnable
 | 
			
		||||
         runnableDebuggingMode = "0">
 | 
			
		||||
         <BuildableReference
 | 
			
		||||
            BuildableIdentifier = "primary"
 | 
			
		||||
            BlueprintIdentifier = "33CC10EC2044A3C60003C045"
 | 
			
		||||
            BuildableName = "xp_dashboard.app"
 | 
			
		||||
            BlueprintName = "Runner"
 | 
			
		||||
            ReferencedContainer = "container:Runner.xcodeproj">
 | 
			
		||||
         </BuildableReference>
 | 
			
		||||
      </BuildableProductRunnable>
 | 
			
		||||
   </ProfileAction>
 | 
			
		||||
   <AnalyzeAction
 | 
			
		||||
      buildConfiguration = "Debug">
 | 
			
		||||
   </AnalyzeAction>
 | 
			
		||||
   <ArchiveAction
 | 
			
		||||
      buildConfiguration = "Release"
 | 
			
		||||
      revealArchiveInOrganizer = "YES">
 | 
			
		||||
   </ArchiveAction>
 | 
			
		||||
</Scheme>
 | 
			
		||||
							
								
								
									
										7
									
								
								xp_dashboard/macos/Runner.xcworkspace/contents.xcworkspacedata
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,7 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<Workspace
 | 
			
		||||
   version = "1.0">
 | 
			
		||||
   <FileRef
 | 
			
		||||
      location = "group:Runner.xcodeproj">
 | 
			
		||||
   </FileRef>
 | 
			
		||||
</Workspace>
 | 
			
		||||
@ -0,0 +1,8 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
<dict>
 | 
			
		||||
	<key>IDEDidComputeMac32BitWarning</key>
 | 
			
		||||
	<true/>
 | 
			
		||||
</dict>
 | 
			
		||||
</plist>
 | 
			
		||||
							
								
								
									
										13
									
								
								xp_dashboard/macos/Runner/AppDelegate.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,13 @@
 | 
			
		||||
import Cocoa
 | 
			
		||||
import FlutterMacOS
 | 
			
		||||
 | 
			
		||||
@main
 | 
			
		||||
class AppDelegate: FlutterAppDelegate {
 | 
			
		||||
  override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
 | 
			
		||||
    return true
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,68 @@
 | 
			
		||||
{
 | 
			
		||||
  "images" : [
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "16x16",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_16.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "16x16",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_32.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "32x32",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_32.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "32x32",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_64.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "128x128",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_128.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "128x128",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_256.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "256x256",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_256.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "256x256",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_512.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "512x512",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_512.png",
 | 
			
		||||
      "scale" : "1x"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "size" : "512x512",
 | 
			
		||||
      "idiom" : "mac",
 | 
			
		||||
      "filename" : "app_icon_1024.png",
 | 
			
		||||
      "scale" : "2x"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "info" : {
 | 
			
		||||
    "version" : 1,
 | 
			
		||||
    "author" : "xcode"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
		 After Width: | Height: | Size: 101 KiB  |