Delete apps/mobile/prototypes directory
@@ -1,45 +0,0 @@
|
|||||||
# 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-dependencies
|
|
||||||
.pub-cache/
|
|
||||||
.pub/
|
|
||||||
/build/
|
|
||||||
/coverage/
|
|
||||||
|
|
||||||
# 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
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
# 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: "b45fa18946ecc2d9b4009952c636ba7e2ffbb787"
|
|
||||||
channel: "stable"
|
|
||||||
|
|
||||||
project_type: app
|
|
||||||
|
|
||||||
# Tracks metadata for the flutter migrate command
|
|
||||||
migration:
|
|
||||||
platforms:
|
|
||||||
- platform: root
|
|
||||||
create_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
base_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
- platform: android
|
|
||||||
create_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
base_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
- platform: ios
|
|
||||||
create_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
base_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
- platform: linux
|
|
||||||
create_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
base_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
- platform: macos
|
|
||||||
create_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
base_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
- platform: web
|
|
||||||
create_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
base_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
- platform: windows
|
|
||||||
create_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
base_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
|
|
||||||
|
|
||||||
# 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'
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# client_app_mvp
|
|
||||||
|
|
||||||
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.
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
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")
|
|
||||||
id("com.google.gms.google-services")
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "com.example.client_app_mvp"
|
|
||||||
compileSdk = flutter.compileSdkVersion
|
|
||||||
ndkVersion = flutter.ndkVersion
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = JavaVersion.VERSION_17.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
|
||||||
applicationId = "com.example.client_app_mvp"
|
|
||||||
// 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 = "../.."
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
{
|
|
||||||
"project_info": {
|
|
||||||
"project_number": "717206318340",
|
|
||||||
"project_id": "krow-apps",
|
|
||||||
"storage_bucket": "krow-apps.firebasestorage.app"
|
|
||||||
},
|
|
||||||
"client": [
|
|
||||||
{
|
|
||||||
"client_info": {
|
|
||||||
"mobilesdk_app_id": "1:717206318340:android:b0bff06f9967d8678af451",
|
|
||||||
"android_client_info": {
|
|
||||||
"package_name": "com.example.client_app_mvp"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "717206318340-9c24vluvsda8gh0pt8gk9sd7vj2nptn2.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"api_key": [
|
|
||||||
{
|
|
||||||
"current_key": "AIzaSyCXKJ5yME2a4FlrAzZA5LzSt97JwEwn9qE"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"services": {
|
|
||||||
"appinvite_service": {
|
|
||||||
"other_platform_oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "717206318340-9c24vluvsda8gh0pt8gk9sd7vj2nptn2.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"client_info": {
|
|
||||||
"mobilesdk_app_id": "1:717206318340:android:d3eac8c3774905e08af451",
|
|
||||||
"android_client_info": {
|
|
||||||
"package_name": "com.example.staff_app_mvp"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "717206318340-9c24vluvsda8gh0pt8gk9sd7vj2nptn2.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"api_key": [
|
|
||||||
{
|
|
||||||
"current_key": "AIzaSyCXKJ5yME2a4FlrAzZA5LzSt97JwEwn9qE"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"services": {
|
|
||||||
"appinvite_service": {
|
|
||||||
"other_platform_oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "717206318340-9c24vluvsda8gh0pt8gk9sd7vj2nptn2.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"configuration_version": "1"
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<application
|
|
||||||
android:label="Krow Client App MVP"
|
|
||||||
android:name="${applicationName}"
|
|
||||||
android:icon="@mipmap/launcher_icon">
|
|
||||||
<activity
|
|
||||||
android:name=".MainActivity"
|
|
||||||
android:exported="true"
|
|
||||||
android:launchMode="singleTop"
|
|
||||||
android:taskAffinity=""
|
|
||||||
android:theme="@style/LaunchTheme"
|
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
|
||||||
android:hardwareAccelerated="true"
|
|
||||||
android:windowSoftInputMode="adjustResize">
|
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
|
||||||
the Android process has started. This theme is visible to the user
|
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
|
||||||
to determine the Window background behind the Flutter UI. -->
|
|
||||||
<meta-data
|
|
||||||
android:name="io.flutter.embedding.android.NormalTheme"
|
|
||||||
android:resource="@style/NormalTheme"
|
|
||||||
/>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
|
||||||
</intent-filter>
|
|
||||||
</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>
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
|
||||||
</manifest>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.example.client_app_mvp
|
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
|
||||||
|
|
||||||
class MainActivity : FlutterActivity()
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<?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>
|
|
||||||
|
Before Width: | Height: | Size: 544 B |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 442 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 721 B |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
@@ -1,18 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
|
||||||
android.useAndroidX=true
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
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.11.1" apply false
|
|
||||||
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
|
|
||||||
id("com.google.gms.google-services") version "4.4.2" apply false
|
|
||||||
}
|
|
||||||
|
|
||||||
include(":app")
|
|
||||||
|
Before Width: | Height: | Size: 8.4 KiB |
@@ -1,34 +0,0 @@
|
|||||||
**/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
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?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>13.0</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
|
||||||
#include "Generated.xcconfig"
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
|
||||||
#include "Generated.xcconfig"
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# Uncomment this line to define a global platform for your project
|
|
||||||
# platform :ios, '13.0'
|
|
||||||
|
|
||||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
|
||||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
|
||||||
|
|
||||||
project 'Runner', {
|
|
||||||
'Debug' => :debug,
|
|
||||||
'Profile' => :release,
|
|
||||||
'Release' => :release,
|
|
||||||
}
|
|
||||||
|
|
||||||
def flutter_root
|
|
||||||
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
|
||||||
unless File.exist?(generated_xcode_build_settings_path)
|
|
||||||
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
|
||||||
end
|
|
||||||
|
|
||||||
File.foreach(generated_xcode_build_settings_path) do |line|
|
|
||||||
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
|
||||||
return matches[1].strip if matches
|
|
||||||
end
|
|
||||||
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
|
||||||
end
|
|
||||||
|
|
||||||
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
|
||||||
|
|
||||||
flutter_ios_podfile_setup
|
|
||||||
|
|
||||||
target 'Runner' do
|
|
||||||
use_frameworks!
|
|
||||||
|
|
||||||
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
|
||||||
target 'RunnerTests' do
|
|
||||||
inherit! :search_paths
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
post_install do |installer|
|
|
||||||
installer.pods_project.targets.each do |target|
|
|
||||||
flutter_additional_ios_build_settings(target)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,728 +0,0 @@
|
|||||||
// !$*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 */; };
|
|
||||||
59A242D46CCC2B41A5A2C541 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD5AC650E4AD66A8AC1F1833 /* Pods_Runner.framework */; };
|
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
|
||||||
76284B4CE5310321526352FD /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93FA99887D920E203CE2CDBC /* Pods_RunnerTests.framework */; };
|
|
||||||
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>"; };
|
|
||||||
50734491C8F4091FC4215298 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
6B4A6F53289C89C876C48A46 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
6D7BF6C00FBA636F126BBD33 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; 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>"; };
|
|
||||||
93FA99887D920E203CE2CDBC /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
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>"; };
|
|
||||||
B701E0489709E5092E6A038C /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
BD5AC650E4AD66A8AC1F1833 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
C4A6F6F8B46779D11DEEA3E7 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
F4AA850DEDC9D26C59B80E5F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
59A242D46CCC2B41A5A2C541 /* Pods_Runner.framework in Frameworks */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
F86625E7251DC5F34D5E3557 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
76284B4CE5310321526352FD /* Pods_RunnerTests.framework in Frameworks */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXFrameworksBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
|
||||||
09914133214C1E3120D3D9EE /* Frameworks */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
BD5AC650E4AD66A8AC1F1833 /* Pods_Runner.framework */,
|
|
||||||
93FA99887D920E203CE2CDBC /* Pods_RunnerTests.framework */,
|
|
||||||
);
|
|
||||||
name = Frameworks;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
172627A77D8C2EB68F8D116E /* Pods */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
F4AA850DEDC9D26C59B80E5F /* Pods-Runner.debug.xcconfig */,
|
|
||||||
6B4A6F53289C89C876C48A46 /* Pods-Runner.release.xcconfig */,
|
|
||||||
C4A6F6F8B46779D11DEEA3E7 /* Pods-Runner.profile.xcconfig */,
|
|
||||||
6D7BF6C00FBA636F126BBD33 /* Pods-RunnerTests.debug.xcconfig */,
|
|
||||||
50734491C8F4091FC4215298 /* Pods-RunnerTests.release.xcconfig */,
|
|
||||||
B701E0489709E5092E6A038C /* Pods-RunnerTests.profile.xcconfig */,
|
|
||||||
);
|
|
||||||
name = Pods;
|
|
||||||
path = Pods;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
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 */,
|
|
||||||
172627A77D8C2EB68F8D116E /* Pods */,
|
|
||||||
09914133214C1E3120D3D9EE /* Frameworks */,
|
|
||||||
);
|
|
||||||
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 = (
|
|
||||||
AE1567838935F36DA48C52B1 /* [CP] Check Pods Manifest.lock */,
|
|
||||||
331C807D294A63A400263BE5 /* Sources */,
|
|
||||||
331C807F294A63A400263BE5 /* Resources */,
|
|
||||||
F86625E7251DC5F34D5E3557 /* Frameworks */,
|
|
||||||
);
|
|
||||||
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 = (
|
|
||||||
A06687346091B014AF063603 /* [CP] Check Pods Manifest.lock */,
|
|
||||||
9740EEB61CF901F6004384FC /* Run Script */,
|
|
||||||
97C146EA1CF9000F007C117D /* Sources */,
|
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
|
||||||
97C146EC1CF9000F007C117D /* Resources */,
|
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
|
||||||
4F27457BBFA344CA19B6713C /* [CP] Embed Pods Frameworks */,
|
|
||||||
);
|
|
||||||
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";
|
|
||||||
};
|
|
||||||
4F27457BBFA344CA19B6713C /* [CP] Embed Pods Frameworks */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
|
||||||
);
|
|
||||||
name = "[CP] Embed Pods Frameworks";
|
|
||||||
outputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
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";
|
|
||||||
};
|
|
||||||
A06687346091B014AF063603 /* [CP] Check Pods Manifest.lock */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
|
||||||
"${PODS_ROOT}/Manifest.lock",
|
|
||||||
);
|
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
|
||||||
outputFileListPaths = (
|
|
||||||
);
|
|
||||||
outputPaths = (
|
|
||||||
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
AE1567838935F36DA48C52B1 /* [CP] Check Pods Manifest.lock */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
|
||||||
"${PODS_ROOT}/Manifest.lock",
|
|
||||||
);
|
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
|
||||||
outputFileListPaths = (
|
|
||||||
);
|
|
||||||
outputPaths = (
|
|
||||||
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
/* 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 = 13.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.clientAppMvp;
|
|
||||||
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;
|
|
||||||
baseConfigurationReference = 6D7BF6C00FBA636F126BBD33 /* Pods-RunnerTests.debug.xcconfig */;
|
|
||||||
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.clientAppMvp.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;
|
|
||||||
baseConfigurationReference = 50734491C8F4091FC4215298 /* Pods-RunnerTests.release.xcconfig */;
|
|
||||||
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.clientAppMvp.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;
|
|
||||||
baseConfigurationReference = B701E0489709E5092E6A038C /* Pods-RunnerTests.profile.xcconfig */;
|
|
||||||
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.clientAppMvp.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 = AppIcon;
|
|
||||||
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 = 13.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 = AppIcon;
|
|
||||||
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 = 13.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.clientAppMvp;
|
|
||||||
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.clientAppMvp;
|
|
||||||
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 */;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Workspace
|
|
||||||
version = "1.0">
|
|
||||||
<FileRef
|
|
||||||
location = "self:">
|
|
||||||
</FileRef>
|
|
||||||
</Workspace>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Workspace
|
|
||||||
version = "1.0">
|
|
||||||
<FileRef
|
|
||||||
location = "group:Runner.xcodeproj">
|
|
||||||
</FileRef>
|
|
||||||
<FileRef
|
|
||||||
location = "group:Pods/Pods.xcodeproj">
|
|
||||||
</FileRef>
|
|
||||||
</Workspace>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"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":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"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":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@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"}}
|
|
||||||
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 506 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 765 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 68 B |
|
Before Width: | Height: | Size: 68 B |
|
Before Width: | Height: | Size: 68 B |
@@ -1,5 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<?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>Client App Mvp</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>client_app_mvp</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 +0,0 @@
|
|||||||
#import "GeneratedPluginRegistrant.h"
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
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.
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"description": "A set of guides for interacting with the generated firebase dataconnect sdk",
|
|
||||||
"mcpServers": {
|
|
||||||
"firebase": {
|
|
||||||
"command": "npx",
|
|
||||||
"args": ["-y", "firebase-tools@latest", "experimental:mcp"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Setup
|
|
||||||
|
|
||||||
This guide will walk you through setting up your environment to use the Firebase Data Connect SDK. Mostly using
|
|
||||||
documentation listed [here](https://firebase.google.com/docs/flutter/setup?platform=ios#install-cli-tools).
|
|
||||||
|
|
||||||
1. Make sure you have the latest Firebase CLI tools installed. Follow the instructions [here](https://firebase.google.com/docs/cli#setup_update_cli) to install.
|
|
||||||
2. Log into your Firebase account:
|
|
||||||
```sh
|
|
||||||
firebase login
|
|
||||||
```
|
|
||||||
3. Install the FlutterFire CLI by running the following command from any directory:
|
|
||||||
```sh
|
|
||||||
dart pub global activate flutterfire_cli
|
|
||||||
```
|
|
||||||
4. Make sure the user has initialized Firebase already based on the instructions [here](https://firebase.google.com/docs/flutter/setup?platform=ios#initialize-firebase).
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# Basic Usage
|
|
||||||
|
|
||||||
```dart
|
|
||||||
ExampleConnector.instance.CreateMovie(createMovieVariables).execute();
|
|
||||||
ExampleConnector.instance.UpsertUser(upsertUserVariables).execute();
|
|
||||||
ExampleConnector.instance.AddReview(addReviewVariables).execute();
|
|
||||||
ExampleConnector.instance.DeleteReview(deleteReviewVariables).execute();
|
|
||||||
ExampleConnector.instance.ListMovies().execute();
|
|
||||||
ExampleConnector.instance.ListUsers().execute();
|
|
||||||
ExampleConnector.instance.ListUserReviews().execute();
|
|
||||||
ExampleConnector.instance.GetMovieById(getMovieByIdVariables).execute();
|
|
||||||
ExampleConnector.instance.SearchMovie(searchMovieVariables).execute();
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## Optional Fields
|
|
||||||
|
|
||||||
Some operations may have optional fields. In these cases, the Flutter SDK exposes a builder method, and will have to be set separately.
|
|
||||||
|
|
||||||
Optional fields can be discovered based on classes that have `Optional` object types.
|
|
||||||
|
|
||||||
This is an example of a mutation with an optional field:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
await ExampleConnector.instance.SearchMovie({ ... })
|
|
||||||
.titleInput(...)
|
|
||||||
.execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: the above example is a mutation, but the same logic applies to query operations as well. Additionally, `createMovie` is an example, and may not be available to the user.
|
|
||||||
|
|
||||||
@@ -1,446 +0,0 @@
|
|||||||
# dataconnect_generated SDK
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
```sh
|
|
||||||
flutter pub get firebase_data_connect
|
|
||||||
flutterfire configure
|
|
||||||
```
|
|
||||||
For more information, see [Flutter for Firebase installation documentation](https://firebase.google.com/docs/data-connect/flutter-sdk#use-core).
|
|
||||||
|
|
||||||
## Data Connect instance
|
|
||||||
Each connector creates a static class, with an instance of the `DataConnect` class that can be used to connect to your Data Connect backend and call operations.
|
|
||||||
|
|
||||||
### Connecting to the emulator
|
|
||||||
|
|
||||||
```dart
|
|
||||||
String host = 'localhost'; // or your host name
|
|
||||||
int port = 9399; // or your port number
|
|
||||||
ExampleConnector.instance.dataConnect.useDataConnectEmulator(host, port);
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also call queries and mutations by using the connector class.
|
|
||||||
## Queries
|
|
||||||
|
|
||||||
### ListMovies
|
|
||||||
#### Required Arguments
|
|
||||||
```dart
|
|
||||||
// No required arguments
|
|
||||||
ExampleConnector.instance.listMovies().execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Return Type
|
|
||||||
`execute()` returns a `QueryResult<ListMoviesData, void>`
|
|
||||||
```dart
|
|
||||||
/// Result of an Operation Request (query/mutation).
|
|
||||||
class OperationResult<Data, Variables> {
|
|
||||||
OperationResult(this.dataConnect, this.data, this.ref);
|
|
||||||
Data data;
|
|
||||||
OperationRef<Data, Variables> ref;
|
|
||||||
FirebaseDataConnect dataConnect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Result of a query request. Created to hold extra variables in the future.
|
|
||||||
class QueryResult<Data, Variables> extends OperationResult<Data, Variables> {
|
|
||||||
QueryResult(super.dataConnect, super.data, super.ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
final result = await ExampleConnector.instance.listMovies();
|
|
||||||
ListMoviesData data = result.data;
|
|
||||||
final ref = result.ref;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Getting the Ref
|
|
||||||
Each builder returns an `execute` function, which is a helper function that creates a `Ref` object, and executes the underlying operation.
|
|
||||||
An example of how to use the `Ref` object is shown below:
|
|
||||||
```dart
|
|
||||||
final ref = ExampleConnector.instance.listMovies().ref();
|
|
||||||
ref.execute();
|
|
||||||
|
|
||||||
ref.subscribe(...);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### ListUsers
|
|
||||||
#### Required Arguments
|
|
||||||
```dart
|
|
||||||
// No required arguments
|
|
||||||
ExampleConnector.instance.listUsers().execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Return Type
|
|
||||||
`execute()` returns a `QueryResult<ListUsersData, void>`
|
|
||||||
```dart
|
|
||||||
/// Result of an Operation Request (query/mutation).
|
|
||||||
class OperationResult<Data, Variables> {
|
|
||||||
OperationResult(this.dataConnect, this.data, this.ref);
|
|
||||||
Data data;
|
|
||||||
OperationRef<Data, Variables> ref;
|
|
||||||
FirebaseDataConnect dataConnect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Result of a query request. Created to hold extra variables in the future.
|
|
||||||
class QueryResult<Data, Variables> extends OperationResult<Data, Variables> {
|
|
||||||
QueryResult(super.dataConnect, super.data, super.ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
final result = await ExampleConnector.instance.listUsers();
|
|
||||||
ListUsersData data = result.data;
|
|
||||||
final ref = result.ref;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Getting the Ref
|
|
||||||
Each builder returns an `execute` function, which is a helper function that creates a `Ref` object, and executes the underlying operation.
|
|
||||||
An example of how to use the `Ref` object is shown below:
|
|
||||||
```dart
|
|
||||||
final ref = ExampleConnector.instance.listUsers().ref();
|
|
||||||
ref.execute();
|
|
||||||
|
|
||||||
ref.subscribe(...);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### ListUserReviews
|
|
||||||
#### Required Arguments
|
|
||||||
```dart
|
|
||||||
// No required arguments
|
|
||||||
ExampleConnector.instance.listUserReviews().execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Return Type
|
|
||||||
`execute()` returns a `QueryResult<ListUserReviewsData, void>`
|
|
||||||
```dart
|
|
||||||
/// Result of an Operation Request (query/mutation).
|
|
||||||
class OperationResult<Data, Variables> {
|
|
||||||
OperationResult(this.dataConnect, this.data, this.ref);
|
|
||||||
Data data;
|
|
||||||
OperationRef<Data, Variables> ref;
|
|
||||||
FirebaseDataConnect dataConnect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Result of a query request. Created to hold extra variables in the future.
|
|
||||||
class QueryResult<Data, Variables> extends OperationResult<Data, Variables> {
|
|
||||||
QueryResult(super.dataConnect, super.data, super.ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
final result = await ExampleConnector.instance.listUserReviews();
|
|
||||||
ListUserReviewsData data = result.data;
|
|
||||||
final ref = result.ref;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Getting the Ref
|
|
||||||
Each builder returns an `execute` function, which is a helper function that creates a `Ref` object, and executes the underlying operation.
|
|
||||||
An example of how to use the `Ref` object is shown below:
|
|
||||||
```dart
|
|
||||||
final ref = ExampleConnector.instance.listUserReviews().ref();
|
|
||||||
ref.execute();
|
|
||||||
|
|
||||||
ref.subscribe(...);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### GetMovieById
|
|
||||||
#### Required Arguments
|
|
||||||
```dart
|
|
||||||
String id = ...;
|
|
||||||
ExampleConnector.instance.getMovieById(
|
|
||||||
id: id,
|
|
||||||
).execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Return Type
|
|
||||||
`execute()` returns a `QueryResult<GetMovieByIdData, GetMovieByIdVariables>`
|
|
||||||
```dart
|
|
||||||
/// Result of an Operation Request (query/mutation).
|
|
||||||
class OperationResult<Data, Variables> {
|
|
||||||
OperationResult(this.dataConnect, this.data, this.ref);
|
|
||||||
Data data;
|
|
||||||
OperationRef<Data, Variables> ref;
|
|
||||||
FirebaseDataConnect dataConnect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Result of a query request. Created to hold extra variables in the future.
|
|
||||||
class QueryResult<Data, Variables> extends OperationResult<Data, Variables> {
|
|
||||||
QueryResult(super.dataConnect, super.data, super.ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
final result = await ExampleConnector.instance.getMovieById(
|
|
||||||
id: id,
|
|
||||||
);
|
|
||||||
GetMovieByIdData data = result.data;
|
|
||||||
final ref = result.ref;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Getting the Ref
|
|
||||||
Each builder returns an `execute` function, which is a helper function that creates a `Ref` object, and executes the underlying operation.
|
|
||||||
An example of how to use the `Ref` object is shown below:
|
|
||||||
```dart
|
|
||||||
String id = ...;
|
|
||||||
|
|
||||||
final ref = ExampleConnector.instance.getMovieById(
|
|
||||||
id: id,
|
|
||||||
).ref();
|
|
||||||
ref.execute();
|
|
||||||
|
|
||||||
ref.subscribe(...);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### SearchMovie
|
|
||||||
#### Required Arguments
|
|
||||||
```dart
|
|
||||||
// No required arguments
|
|
||||||
ExampleConnector.instance.searchMovie().execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Optional Arguments
|
|
||||||
We return a builder for each query. For SearchMovie, we created `SearchMovieBuilder`. For queries and mutations with optional parameters, we return a builder class.
|
|
||||||
The builder pattern allows Data Connect to distinguish between fields that haven't been set and fields that have been set to null. A field can be set by calling its respective setter method like below:
|
|
||||||
```dart
|
|
||||||
class SearchMovieVariablesBuilder {
|
|
||||||
...
|
|
||||||
|
|
||||||
SearchMovieVariablesBuilder titleInput(String? t) {
|
|
||||||
_titleInput.value = t;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
SearchMovieVariablesBuilder genre(String? t) {
|
|
||||||
_genre.value = t;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
...
|
|
||||||
}
|
|
||||||
ExampleConnector.instance.searchMovie()
|
|
||||||
.titleInput(titleInput)
|
|
||||||
.genre(genre)
|
|
||||||
.execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Return Type
|
|
||||||
`execute()` returns a `QueryResult<SearchMovieData, SearchMovieVariables>`
|
|
||||||
```dart
|
|
||||||
/// Result of an Operation Request (query/mutation).
|
|
||||||
class OperationResult<Data, Variables> {
|
|
||||||
OperationResult(this.dataConnect, this.data, this.ref);
|
|
||||||
Data data;
|
|
||||||
OperationRef<Data, Variables> ref;
|
|
||||||
FirebaseDataConnect dataConnect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Result of a query request. Created to hold extra variables in the future.
|
|
||||||
class QueryResult<Data, Variables> extends OperationResult<Data, Variables> {
|
|
||||||
QueryResult(super.dataConnect, super.data, super.ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
final result = await ExampleConnector.instance.searchMovie();
|
|
||||||
SearchMovieData data = result.data;
|
|
||||||
final ref = result.ref;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Getting the Ref
|
|
||||||
Each builder returns an `execute` function, which is a helper function that creates a `Ref` object, and executes the underlying operation.
|
|
||||||
An example of how to use the `Ref` object is shown below:
|
|
||||||
```dart
|
|
||||||
final ref = ExampleConnector.instance.searchMovie().ref();
|
|
||||||
ref.execute();
|
|
||||||
|
|
||||||
ref.subscribe(...);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Mutations
|
|
||||||
|
|
||||||
### CreateMovie
|
|
||||||
#### Required Arguments
|
|
||||||
```dart
|
|
||||||
String title = ...;
|
|
||||||
String genre = ...;
|
|
||||||
String imageUrl = ...;
|
|
||||||
ExampleConnector.instance.createMovie(
|
|
||||||
title: title,
|
|
||||||
genre: genre,
|
|
||||||
imageUrl: imageUrl,
|
|
||||||
).execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Return Type
|
|
||||||
`execute()` returns a `OperationResult<CreateMovieData, CreateMovieVariables>`
|
|
||||||
```dart
|
|
||||||
/// Result of an Operation Request (query/mutation).
|
|
||||||
class OperationResult<Data, Variables> {
|
|
||||||
OperationResult(this.dataConnect, this.data, this.ref);
|
|
||||||
Data data;
|
|
||||||
OperationRef<Data, Variables> ref;
|
|
||||||
FirebaseDataConnect dataConnect;
|
|
||||||
}
|
|
||||||
|
|
||||||
final result = await ExampleConnector.instance.createMovie(
|
|
||||||
title: title,
|
|
||||||
genre: genre,
|
|
||||||
imageUrl: imageUrl,
|
|
||||||
);
|
|
||||||
CreateMovieData data = result.data;
|
|
||||||
final ref = result.ref;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Getting the Ref
|
|
||||||
Each builder returns an `execute` function, which is a helper function that creates a `Ref` object, and executes the underlying operation.
|
|
||||||
An example of how to use the `Ref` object is shown below:
|
|
||||||
```dart
|
|
||||||
String title = ...;
|
|
||||||
String genre = ...;
|
|
||||||
String imageUrl = ...;
|
|
||||||
|
|
||||||
final ref = ExampleConnector.instance.createMovie(
|
|
||||||
title: title,
|
|
||||||
genre: genre,
|
|
||||||
imageUrl: imageUrl,
|
|
||||||
).ref();
|
|
||||||
ref.execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### UpsertUser
|
|
||||||
#### Required Arguments
|
|
||||||
```dart
|
|
||||||
String username = ...;
|
|
||||||
ExampleConnector.instance.upsertUser(
|
|
||||||
username: username,
|
|
||||||
).execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Return Type
|
|
||||||
`execute()` returns a `OperationResult<UpsertUserData, UpsertUserVariables>`
|
|
||||||
```dart
|
|
||||||
/// Result of an Operation Request (query/mutation).
|
|
||||||
class OperationResult<Data, Variables> {
|
|
||||||
OperationResult(this.dataConnect, this.data, this.ref);
|
|
||||||
Data data;
|
|
||||||
OperationRef<Data, Variables> ref;
|
|
||||||
FirebaseDataConnect dataConnect;
|
|
||||||
}
|
|
||||||
|
|
||||||
final result = await ExampleConnector.instance.upsertUser(
|
|
||||||
username: username,
|
|
||||||
);
|
|
||||||
UpsertUserData data = result.data;
|
|
||||||
final ref = result.ref;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Getting the Ref
|
|
||||||
Each builder returns an `execute` function, which is a helper function that creates a `Ref` object, and executes the underlying operation.
|
|
||||||
An example of how to use the `Ref` object is shown below:
|
|
||||||
```dart
|
|
||||||
String username = ...;
|
|
||||||
|
|
||||||
final ref = ExampleConnector.instance.upsertUser(
|
|
||||||
username: username,
|
|
||||||
).ref();
|
|
||||||
ref.execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### AddReview
|
|
||||||
#### Required Arguments
|
|
||||||
```dart
|
|
||||||
String movieId = ...;
|
|
||||||
int rating = ...;
|
|
||||||
String reviewText = ...;
|
|
||||||
ExampleConnector.instance.addReview(
|
|
||||||
movieId: movieId,
|
|
||||||
rating: rating,
|
|
||||||
reviewText: reviewText,
|
|
||||||
).execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Return Type
|
|
||||||
`execute()` returns a `OperationResult<AddReviewData, AddReviewVariables>`
|
|
||||||
```dart
|
|
||||||
/// Result of an Operation Request (query/mutation).
|
|
||||||
class OperationResult<Data, Variables> {
|
|
||||||
OperationResult(this.dataConnect, this.data, this.ref);
|
|
||||||
Data data;
|
|
||||||
OperationRef<Data, Variables> ref;
|
|
||||||
FirebaseDataConnect dataConnect;
|
|
||||||
}
|
|
||||||
|
|
||||||
final result = await ExampleConnector.instance.addReview(
|
|
||||||
movieId: movieId,
|
|
||||||
rating: rating,
|
|
||||||
reviewText: reviewText,
|
|
||||||
);
|
|
||||||
AddReviewData data = result.data;
|
|
||||||
final ref = result.ref;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Getting the Ref
|
|
||||||
Each builder returns an `execute` function, which is a helper function that creates a `Ref` object, and executes the underlying operation.
|
|
||||||
An example of how to use the `Ref` object is shown below:
|
|
||||||
```dart
|
|
||||||
String movieId = ...;
|
|
||||||
int rating = ...;
|
|
||||||
String reviewText = ...;
|
|
||||||
|
|
||||||
final ref = ExampleConnector.instance.addReview(
|
|
||||||
movieId: movieId,
|
|
||||||
rating: rating,
|
|
||||||
reviewText: reviewText,
|
|
||||||
).ref();
|
|
||||||
ref.execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### DeleteReview
|
|
||||||
#### Required Arguments
|
|
||||||
```dart
|
|
||||||
String movieId = ...;
|
|
||||||
ExampleConnector.instance.deleteReview(
|
|
||||||
movieId: movieId,
|
|
||||||
).execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Return Type
|
|
||||||
`execute()` returns a `OperationResult<DeleteReviewData, DeleteReviewVariables>`
|
|
||||||
```dart
|
|
||||||
/// Result of an Operation Request (query/mutation).
|
|
||||||
class OperationResult<Data, Variables> {
|
|
||||||
OperationResult(this.dataConnect, this.data, this.ref);
|
|
||||||
Data data;
|
|
||||||
OperationRef<Data, Variables> ref;
|
|
||||||
FirebaseDataConnect dataConnect;
|
|
||||||
}
|
|
||||||
|
|
||||||
final result = await ExampleConnector.instance.deleteReview(
|
|
||||||
movieId: movieId,
|
|
||||||
);
|
|
||||||
DeleteReviewData data = result.data;
|
|
||||||
final ref = result.ref;
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Getting the Ref
|
|
||||||
Each builder returns an `execute` function, which is a helper function that creates a `Ref` object, and executes the underlying operation.
|
|
||||||
An example of how to use the `Ref` object is shown below:
|
|
||||||
```dart
|
|
||||||
String movieId = ...;
|
|
||||||
|
|
||||||
final ref = ExampleConnector.instance.deleteReview(
|
|
||||||
movieId: movieId,
|
|
||||||
).ref();
|
|
||||||
ref.execute();
|
|
||||||
```
|
|
||||||
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
part of 'generated.dart';
|
|
||||||
|
|
||||||
class AddReviewVariablesBuilder {
|
|
||||||
String movieId;
|
|
||||||
int rating;
|
|
||||||
String reviewText;
|
|
||||||
|
|
||||||
final FirebaseDataConnect _dataConnect;
|
|
||||||
AddReviewVariablesBuilder(this._dataConnect, {required this.movieId,required this.rating,required this.reviewText,});
|
|
||||||
Deserializer<AddReviewData> dataDeserializer = (dynamic json) => AddReviewData.fromJson(jsonDecode(json));
|
|
||||||
Serializer<AddReviewVariables> varsSerializer = (AddReviewVariables vars) => jsonEncode(vars.toJson());
|
|
||||||
Future<OperationResult<AddReviewData, AddReviewVariables>> execute() {
|
|
||||||
return ref().execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
MutationRef<AddReviewData, AddReviewVariables> ref() {
|
|
||||||
AddReviewVariables vars= AddReviewVariables(movieId: movieId,rating: rating,reviewText: reviewText,);
|
|
||||||
return _dataConnect.mutation("AddReview", dataDeserializer, varsSerializer, vars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class AddReviewReviewUpsert {
|
|
||||||
final String userId;
|
|
||||||
final String movieId;
|
|
||||||
AddReviewReviewUpsert.fromJson(dynamic json):
|
|
||||||
|
|
||||||
userId = nativeFromJson<String>(json['userId']),
|
|
||||||
movieId = nativeFromJson<String>(json['movieId']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final AddReviewReviewUpsert otherTyped = other as AddReviewReviewUpsert;
|
|
||||||
return userId == otherTyped.userId &&
|
|
||||||
movieId == otherTyped.movieId;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([userId.hashCode, movieId.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['userId'] = nativeToJson<String>(userId);
|
|
||||||
json['movieId'] = nativeToJson<String>(movieId);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddReviewReviewUpsert({
|
|
||||||
required this.userId,
|
|
||||||
required this.movieId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class AddReviewData {
|
|
||||||
final AddReviewReviewUpsert review_upsert;
|
|
||||||
AddReviewData.fromJson(dynamic json):
|
|
||||||
|
|
||||||
review_upsert = AddReviewReviewUpsert.fromJson(json['review_upsert']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final AddReviewData otherTyped = other as AddReviewData;
|
|
||||||
return review_upsert == otherTyped.review_upsert;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => review_upsert.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['review_upsert'] = review_upsert.toJson();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddReviewData({
|
|
||||||
required this.review_upsert,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class AddReviewVariables {
|
|
||||||
final String movieId;
|
|
||||||
final int rating;
|
|
||||||
final String reviewText;
|
|
||||||
@Deprecated('fromJson is deprecated for Variable classes as they are no longer required for deserialization.')
|
|
||||||
AddReviewVariables.fromJson(Map<String, dynamic> json):
|
|
||||||
|
|
||||||
movieId = nativeFromJson<String>(json['movieId']),
|
|
||||||
rating = nativeFromJson<int>(json['rating']),
|
|
||||||
reviewText = nativeFromJson<String>(json['reviewText']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final AddReviewVariables otherTyped = other as AddReviewVariables;
|
|
||||||
return movieId == otherTyped.movieId &&
|
|
||||||
rating == otherTyped.rating &&
|
|
||||||
reviewText == otherTyped.reviewText;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([movieId.hashCode, rating.hashCode, reviewText.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['movieId'] = nativeToJson<String>(movieId);
|
|
||||||
json['rating'] = nativeToJson<int>(rating);
|
|
||||||
json['reviewText'] = nativeToJson<String>(reviewText);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddReviewVariables({
|
|
||||||
required this.movieId,
|
|
||||||
required this.rating,
|
|
||||||
required this.reviewText,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
part of 'generated.dart';
|
|
||||||
|
|
||||||
class CreateMovieVariablesBuilder {
|
|
||||||
String title;
|
|
||||||
String genre;
|
|
||||||
String imageUrl;
|
|
||||||
|
|
||||||
final FirebaseDataConnect _dataConnect;
|
|
||||||
CreateMovieVariablesBuilder(this._dataConnect, {required this.title,required this.genre,required this.imageUrl,});
|
|
||||||
Deserializer<CreateMovieData> dataDeserializer = (dynamic json) => CreateMovieData.fromJson(jsonDecode(json));
|
|
||||||
Serializer<CreateMovieVariables> varsSerializer = (CreateMovieVariables vars) => jsonEncode(vars.toJson());
|
|
||||||
Future<OperationResult<CreateMovieData, CreateMovieVariables>> execute() {
|
|
||||||
return ref().execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
MutationRef<CreateMovieData, CreateMovieVariables> ref() {
|
|
||||||
CreateMovieVariables vars= CreateMovieVariables(title: title,genre: genre,imageUrl: imageUrl,);
|
|
||||||
return _dataConnect.mutation("CreateMovie", dataDeserializer, varsSerializer, vars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class CreateMovieMovieInsert {
|
|
||||||
final String id;
|
|
||||||
CreateMovieMovieInsert.fromJson(dynamic json):
|
|
||||||
|
|
||||||
id = nativeFromJson<String>(json['id']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final CreateMovieMovieInsert otherTyped = other as CreateMovieMovieInsert;
|
|
||||||
return id == otherTyped.id;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => id.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['id'] = nativeToJson<String>(id);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateMovieMovieInsert({
|
|
||||||
required this.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class CreateMovieData {
|
|
||||||
final CreateMovieMovieInsert movie_insert;
|
|
||||||
CreateMovieData.fromJson(dynamic json):
|
|
||||||
|
|
||||||
movie_insert = CreateMovieMovieInsert.fromJson(json['movie_insert']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final CreateMovieData otherTyped = other as CreateMovieData;
|
|
||||||
return movie_insert == otherTyped.movie_insert;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => movie_insert.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['movie_insert'] = movie_insert.toJson();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateMovieData({
|
|
||||||
required this.movie_insert,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class CreateMovieVariables {
|
|
||||||
final String title;
|
|
||||||
final String genre;
|
|
||||||
final String imageUrl;
|
|
||||||
@Deprecated('fromJson is deprecated for Variable classes as they are no longer required for deserialization.')
|
|
||||||
CreateMovieVariables.fromJson(Map<String, dynamic> json):
|
|
||||||
|
|
||||||
title = nativeFromJson<String>(json['title']),
|
|
||||||
genre = nativeFromJson<String>(json['genre']),
|
|
||||||
imageUrl = nativeFromJson<String>(json['imageUrl']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final CreateMovieVariables otherTyped = other as CreateMovieVariables;
|
|
||||||
return title == otherTyped.title &&
|
|
||||||
genre == otherTyped.genre &&
|
|
||||||
imageUrl == otherTyped.imageUrl;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([title.hashCode, genre.hashCode, imageUrl.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['title'] = nativeToJson<String>(title);
|
|
||||||
json['genre'] = nativeToJson<String>(genre);
|
|
||||||
json['imageUrl'] = nativeToJson<String>(imageUrl);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateMovieVariables({
|
|
||||||
required this.title,
|
|
||||||
required this.genre,
|
|
||||||
required this.imageUrl,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
part of 'generated.dart';
|
|
||||||
|
|
||||||
class DeleteReviewVariablesBuilder {
|
|
||||||
String movieId;
|
|
||||||
|
|
||||||
final FirebaseDataConnect _dataConnect;
|
|
||||||
DeleteReviewVariablesBuilder(this._dataConnect, {required this.movieId,});
|
|
||||||
Deserializer<DeleteReviewData> dataDeserializer = (dynamic json) => DeleteReviewData.fromJson(jsonDecode(json));
|
|
||||||
Serializer<DeleteReviewVariables> varsSerializer = (DeleteReviewVariables vars) => jsonEncode(vars.toJson());
|
|
||||||
Future<OperationResult<DeleteReviewData, DeleteReviewVariables>> execute() {
|
|
||||||
return ref().execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
MutationRef<DeleteReviewData, DeleteReviewVariables> ref() {
|
|
||||||
DeleteReviewVariables vars= DeleteReviewVariables(movieId: movieId,);
|
|
||||||
return _dataConnect.mutation("DeleteReview", dataDeserializer, varsSerializer, vars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class DeleteReviewReviewDelete {
|
|
||||||
final String userId;
|
|
||||||
final String movieId;
|
|
||||||
DeleteReviewReviewDelete.fromJson(dynamic json):
|
|
||||||
|
|
||||||
userId = nativeFromJson<String>(json['userId']),
|
|
||||||
movieId = nativeFromJson<String>(json['movieId']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final DeleteReviewReviewDelete otherTyped = other as DeleteReviewReviewDelete;
|
|
||||||
return userId == otherTyped.userId &&
|
|
||||||
movieId == otherTyped.movieId;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([userId.hashCode, movieId.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['userId'] = nativeToJson<String>(userId);
|
|
||||||
json['movieId'] = nativeToJson<String>(movieId);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteReviewReviewDelete({
|
|
||||||
required this.userId,
|
|
||||||
required this.movieId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class DeleteReviewData {
|
|
||||||
final DeleteReviewReviewDelete? review_delete;
|
|
||||||
DeleteReviewData.fromJson(dynamic json):
|
|
||||||
|
|
||||||
review_delete = json['review_delete'] == null ? null : DeleteReviewReviewDelete.fromJson(json['review_delete']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final DeleteReviewData otherTyped = other as DeleteReviewData;
|
|
||||||
return review_delete == otherTyped.review_delete;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => review_delete.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
if (review_delete != null) {
|
|
||||||
json['review_delete'] = review_delete!.toJson();
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteReviewData({
|
|
||||||
this.review_delete,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class DeleteReviewVariables {
|
|
||||||
final String movieId;
|
|
||||||
@Deprecated('fromJson is deprecated for Variable classes as they are no longer required for deserialization.')
|
|
||||||
DeleteReviewVariables.fromJson(Map<String, dynamic> json):
|
|
||||||
|
|
||||||
movieId = nativeFromJson<String>(json['movieId']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final DeleteReviewVariables otherTyped = other as DeleteReviewVariables;
|
|
||||||
return movieId == otherTyped.movieId;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => movieId.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['movieId'] = nativeToJson<String>(movieId);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteReviewVariables({
|
|
||||||
required this.movieId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
library dataconnect_generated;
|
|
||||||
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
part 'create_movie.dart';
|
|
||||||
|
|
||||||
part 'upsert_user.dart';
|
|
||||||
|
|
||||||
part 'add_review.dart';
|
|
||||||
|
|
||||||
part 'delete_review.dart';
|
|
||||||
|
|
||||||
part 'list_movies.dart';
|
|
||||||
|
|
||||||
part 'list_users.dart';
|
|
||||||
|
|
||||||
part 'list_user_reviews.dart';
|
|
||||||
|
|
||||||
part 'get_movie_by_id.dart';
|
|
||||||
|
|
||||||
part 'search_movie.dart';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ExampleConnector {
|
|
||||||
|
|
||||||
|
|
||||||
CreateMovieVariablesBuilder createMovie ({required String title, required String genre, required String imageUrl, }) {
|
|
||||||
return CreateMovieVariablesBuilder(dataConnect, title: title,genre: genre,imageUrl: imageUrl,);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
UpsertUserVariablesBuilder upsertUser ({required String username, }) {
|
|
||||||
return UpsertUserVariablesBuilder(dataConnect, username: username,);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
AddReviewVariablesBuilder addReview ({required String movieId, required int rating, required String reviewText, }) {
|
|
||||||
return AddReviewVariablesBuilder(dataConnect, movieId: movieId,rating: rating,reviewText: reviewText,);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DeleteReviewVariablesBuilder deleteReview ({required String movieId, }) {
|
|
||||||
return DeleteReviewVariablesBuilder(dataConnect, movieId: movieId,);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ListMoviesVariablesBuilder listMovies () {
|
|
||||||
return ListMoviesVariablesBuilder(dataConnect, );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ListUsersVariablesBuilder listUsers () {
|
|
||||||
return ListUsersVariablesBuilder(dataConnect, );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ListUserReviewsVariablesBuilder listUserReviews () {
|
|
||||||
return ListUserReviewsVariablesBuilder(dataConnect, );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
GetMovieByIdVariablesBuilder getMovieById ({required String id, }) {
|
|
||||||
return GetMovieByIdVariablesBuilder(dataConnect, id: id,);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SearchMovieVariablesBuilder searchMovie () {
|
|
||||||
return SearchMovieVariablesBuilder(dataConnect, );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static ConnectorConfig connectorConfig = ConnectorConfig(
|
|
||||||
'us-central1',
|
|
||||||
'example',
|
|
||||||
'client-krow-poc',
|
|
||||||
);
|
|
||||||
|
|
||||||
ExampleConnector({required this.dataConnect});
|
|
||||||
static ExampleConnector get instance {
|
|
||||||
return ExampleConnector(
|
|
||||||
dataConnect: FirebaseDataConnect.instanceFor(
|
|
||||||
connectorConfig: connectorConfig,
|
|
||||||
sdkType: CallerSDKType.generated));
|
|
||||||
}
|
|
||||||
|
|
||||||
FirebaseDataConnect dataConnect;
|
|
||||||
}
|
|
||||||
@@ -1,297 +0,0 @@
|
|||||||
part of 'generated.dart';
|
|
||||||
|
|
||||||
class GetMovieByIdVariablesBuilder {
|
|
||||||
String id;
|
|
||||||
|
|
||||||
final FirebaseDataConnect _dataConnect;
|
|
||||||
GetMovieByIdVariablesBuilder(this._dataConnect, {required this.id,});
|
|
||||||
Deserializer<GetMovieByIdData> dataDeserializer = (dynamic json) => GetMovieByIdData.fromJson(jsonDecode(json));
|
|
||||||
Serializer<GetMovieByIdVariables> varsSerializer = (GetMovieByIdVariables vars) => jsonEncode(vars.toJson());
|
|
||||||
Future<QueryResult<GetMovieByIdData, GetMovieByIdVariables>> execute() {
|
|
||||||
return ref().execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryRef<GetMovieByIdData, GetMovieByIdVariables> ref() {
|
|
||||||
GetMovieByIdVariables vars= GetMovieByIdVariables(id: id,);
|
|
||||||
return _dataConnect.query("GetMovieById", dataDeserializer, varsSerializer, vars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class GetMovieByIdMovie {
|
|
||||||
final String id;
|
|
||||||
final String title;
|
|
||||||
final String imageUrl;
|
|
||||||
final String? genre;
|
|
||||||
final GetMovieByIdMovieMetadata? metadata;
|
|
||||||
final List<GetMovieByIdMovieReviews> reviews;
|
|
||||||
GetMovieByIdMovie.fromJson(dynamic json):
|
|
||||||
|
|
||||||
id = nativeFromJson<String>(json['id']),
|
|
||||||
title = nativeFromJson<String>(json['title']),
|
|
||||||
imageUrl = nativeFromJson<String>(json['imageUrl']),
|
|
||||||
genre = json['genre'] == null ? null : nativeFromJson<String>(json['genre']),
|
|
||||||
metadata = json['metadata'] == null ? null : GetMovieByIdMovieMetadata.fromJson(json['metadata']),
|
|
||||||
reviews = (json['reviews'] as List<dynamic>)
|
|
||||||
.map((e) => GetMovieByIdMovieReviews.fromJson(e))
|
|
||||||
.toList();
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final GetMovieByIdMovie otherTyped = other as GetMovieByIdMovie;
|
|
||||||
return id == otherTyped.id &&
|
|
||||||
title == otherTyped.title &&
|
|
||||||
imageUrl == otherTyped.imageUrl &&
|
|
||||||
genre == otherTyped.genre &&
|
|
||||||
metadata == otherTyped.metadata &&
|
|
||||||
reviews == otherTyped.reviews;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([id.hashCode, title.hashCode, imageUrl.hashCode, genre.hashCode, metadata.hashCode, reviews.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['id'] = nativeToJson<String>(id);
|
|
||||||
json['title'] = nativeToJson<String>(title);
|
|
||||||
json['imageUrl'] = nativeToJson<String>(imageUrl);
|
|
||||||
if (genre != null) {
|
|
||||||
json['genre'] = nativeToJson<String?>(genre);
|
|
||||||
}
|
|
||||||
if (metadata != null) {
|
|
||||||
json['metadata'] = metadata!.toJson();
|
|
||||||
}
|
|
||||||
json['reviews'] = reviews.map((e) => e.toJson()).toList();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetMovieByIdMovie({
|
|
||||||
required this.id,
|
|
||||||
required this.title,
|
|
||||||
required this.imageUrl,
|
|
||||||
this.genre,
|
|
||||||
this.metadata,
|
|
||||||
required this.reviews,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class GetMovieByIdMovieMetadata {
|
|
||||||
final double? rating;
|
|
||||||
final int? releaseYear;
|
|
||||||
final String? description;
|
|
||||||
GetMovieByIdMovieMetadata.fromJson(dynamic json):
|
|
||||||
|
|
||||||
rating = json['rating'] == null ? null : nativeFromJson<double>(json['rating']),
|
|
||||||
releaseYear = json['releaseYear'] == null ? null : nativeFromJson<int>(json['releaseYear']),
|
|
||||||
description = json['description'] == null ? null : nativeFromJson<String>(json['description']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final GetMovieByIdMovieMetadata otherTyped = other as GetMovieByIdMovieMetadata;
|
|
||||||
return rating == otherTyped.rating &&
|
|
||||||
releaseYear == otherTyped.releaseYear &&
|
|
||||||
description == otherTyped.description;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([rating.hashCode, releaseYear.hashCode, description.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
if (rating != null) {
|
|
||||||
json['rating'] = nativeToJson<double?>(rating);
|
|
||||||
}
|
|
||||||
if (releaseYear != null) {
|
|
||||||
json['releaseYear'] = nativeToJson<int?>(releaseYear);
|
|
||||||
}
|
|
||||||
if (description != null) {
|
|
||||||
json['description'] = nativeToJson<String?>(description);
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetMovieByIdMovieMetadata({
|
|
||||||
this.rating,
|
|
||||||
this.releaseYear,
|
|
||||||
this.description,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class GetMovieByIdMovieReviews {
|
|
||||||
final String? reviewText;
|
|
||||||
final DateTime reviewDate;
|
|
||||||
final int? rating;
|
|
||||||
final GetMovieByIdMovieReviewsUser user;
|
|
||||||
GetMovieByIdMovieReviews.fromJson(dynamic json):
|
|
||||||
|
|
||||||
reviewText = json['reviewText'] == null ? null : nativeFromJson<String>(json['reviewText']),
|
|
||||||
reviewDate = nativeFromJson<DateTime>(json['reviewDate']),
|
|
||||||
rating = json['rating'] == null ? null : nativeFromJson<int>(json['rating']),
|
|
||||||
user = GetMovieByIdMovieReviewsUser.fromJson(json['user']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final GetMovieByIdMovieReviews otherTyped = other as GetMovieByIdMovieReviews;
|
|
||||||
return reviewText == otherTyped.reviewText &&
|
|
||||||
reviewDate == otherTyped.reviewDate &&
|
|
||||||
rating == otherTyped.rating &&
|
|
||||||
user == otherTyped.user;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([reviewText.hashCode, reviewDate.hashCode, rating.hashCode, user.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
if (reviewText != null) {
|
|
||||||
json['reviewText'] = nativeToJson<String?>(reviewText);
|
|
||||||
}
|
|
||||||
json['reviewDate'] = nativeToJson<DateTime>(reviewDate);
|
|
||||||
if (rating != null) {
|
|
||||||
json['rating'] = nativeToJson<int?>(rating);
|
|
||||||
}
|
|
||||||
json['user'] = user.toJson();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetMovieByIdMovieReviews({
|
|
||||||
this.reviewText,
|
|
||||||
required this.reviewDate,
|
|
||||||
this.rating,
|
|
||||||
required this.user,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class GetMovieByIdMovieReviewsUser {
|
|
||||||
final String id;
|
|
||||||
final String username;
|
|
||||||
GetMovieByIdMovieReviewsUser.fromJson(dynamic json):
|
|
||||||
|
|
||||||
id = nativeFromJson<String>(json['id']),
|
|
||||||
username = nativeFromJson<String>(json['username']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final GetMovieByIdMovieReviewsUser otherTyped = other as GetMovieByIdMovieReviewsUser;
|
|
||||||
return id == otherTyped.id &&
|
|
||||||
username == otherTyped.username;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([id.hashCode, username.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['id'] = nativeToJson<String>(id);
|
|
||||||
json['username'] = nativeToJson<String>(username);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetMovieByIdMovieReviewsUser({
|
|
||||||
required this.id,
|
|
||||||
required this.username,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class GetMovieByIdData {
|
|
||||||
final GetMovieByIdMovie? movie;
|
|
||||||
GetMovieByIdData.fromJson(dynamic json):
|
|
||||||
|
|
||||||
movie = json['movie'] == null ? null : GetMovieByIdMovie.fromJson(json['movie']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final GetMovieByIdData otherTyped = other as GetMovieByIdData;
|
|
||||||
return movie == otherTyped.movie;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => movie.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
if (movie != null) {
|
|
||||||
json['movie'] = movie!.toJson();
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetMovieByIdData({
|
|
||||||
this.movie,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class GetMovieByIdVariables {
|
|
||||||
final String id;
|
|
||||||
@Deprecated('fromJson is deprecated for Variable classes as they are no longer required for deserialization.')
|
|
||||||
GetMovieByIdVariables.fromJson(Map<String, dynamic> json):
|
|
||||||
|
|
||||||
id = nativeFromJson<String>(json['id']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final GetMovieByIdVariables otherTyped = other as GetMovieByIdVariables;
|
|
||||||
return id == otherTyped.id;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => id.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['id'] = nativeToJson<String>(id);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetMovieByIdVariables({
|
|
||||||
required this.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
part of 'generated.dart';
|
|
||||||
|
|
||||||
class ListMoviesVariablesBuilder {
|
|
||||||
|
|
||||||
final FirebaseDataConnect _dataConnect;
|
|
||||||
ListMoviesVariablesBuilder(this._dataConnect, );
|
|
||||||
Deserializer<ListMoviesData> dataDeserializer = (dynamic json) => ListMoviesData.fromJson(jsonDecode(json));
|
|
||||||
|
|
||||||
Future<QueryResult<ListMoviesData, void>> execute() {
|
|
||||||
return ref().execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryRef<ListMoviesData, void> ref() {
|
|
||||||
|
|
||||||
return _dataConnect.query("ListMovies", dataDeserializer, emptySerializer, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class ListMoviesMovies {
|
|
||||||
final String id;
|
|
||||||
final String title;
|
|
||||||
final String imageUrl;
|
|
||||||
final String? genre;
|
|
||||||
ListMoviesMovies.fromJson(dynamic json):
|
|
||||||
|
|
||||||
id = nativeFromJson<String>(json['id']),
|
|
||||||
title = nativeFromJson<String>(json['title']),
|
|
||||||
imageUrl = nativeFromJson<String>(json['imageUrl']),
|
|
||||||
genre = json['genre'] == null ? null : nativeFromJson<String>(json['genre']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ListMoviesMovies otherTyped = other as ListMoviesMovies;
|
|
||||||
return id == otherTyped.id &&
|
|
||||||
title == otherTyped.title &&
|
|
||||||
imageUrl == otherTyped.imageUrl &&
|
|
||||||
genre == otherTyped.genre;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([id.hashCode, title.hashCode, imageUrl.hashCode, genre.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['id'] = nativeToJson<String>(id);
|
|
||||||
json['title'] = nativeToJson<String>(title);
|
|
||||||
json['imageUrl'] = nativeToJson<String>(imageUrl);
|
|
||||||
if (genre != null) {
|
|
||||||
json['genre'] = nativeToJson<String?>(genre);
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
ListMoviesMovies({
|
|
||||||
required this.id,
|
|
||||||
required this.title,
|
|
||||||
required this.imageUrl,
|
|
||||||
this.genre,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class ListMoviesData {
|
|
||||||
final List<ListMoviesMovies> movies;
|
|
||||||
ListMoviesData.fromJson(dynamic json):
|
|
||||||
|
|
||||||
movies = (json['movies'] as List<dynamic>)
|
|
||||||
.map((e) => ListMoviesMovies.fromJson(e))
|
|
||||||
.toList();
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ListMoviesData otherTyped = other as ListMoviesData;
|
|
||||||
return movies == otherTyped.movies;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => movies.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['movies'] = movies.map((e) => e.toJson()).toList();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
ListMoviesData({
|
|
||||||
required this.movies,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
part of 'generated.dart';
|
|
||||||
|
|
||||||
class ListUserReviewsVariablesBuilder {
|
|
||||||
|
|
||||||
final FirebaseDataConnect _dataConnect;
|
|
||||||
ListUserReviewsVariablesBuilder(this._dataConnect, );
|
|
||||||
Deserializer<ListUserReviewsData> dataDeserializer = (dynamic json) => ListUserReviewsData.fromJson(jsonDecode(json));
|
|
||||||
|
|
||||||
Future<QueryResult<ListUserReviewsData, void>> execute() {
|
|
||||||
return ref().execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryRef<ListUserReviewsData, void> ref() {
|
|
||||||
|
|
||||||
return _dataConnect.query("ListUserReviews", dataDeserializer, emptySerializer, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class ListUserReviewsUser {
|
|
||||||
final String id;
|
|
||||||
final String username;
|
|
||||||
final List<ListUserReviewsUserReviews> reviews;
|
|
||||||
ListUserReviewsUser.fromJson(dynamic json):
|
|
||||||
|
|
||||||
id = nativeFromJson<String>(json['id']),
|
|
||||||
username = nativeFromJson<String>(json['username']),
|
|
||||||
reviews = (json['reviews'] as List<dynamic>)
|
|
||||||
.map((e) => ListUserReviewsUserReviews.fromJson(e))
|
|
||||||
.toList();
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ListUserReviewsUser otherTyped = other as ListUserReviewsUser;
|
|
||||||
return id == otherTyped.id &&
|
|
||||||
username == otherTyped.username &&
|
|
||||||
reviews == otherTyped.reviews;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([id.hashCode, username.hashCode, reviews.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['id'] = nativeToJson<String>(id);
|
|
||||||
json['username'] = nativeToJson<String>(username);
|
|
||||||
json['reviews'] = reviews.map((e) => e.toJson()).toList();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
ListUserReviewsUser({
|
|
||||||
required this.id,
|
|
||||||
required this.username,
|
|
||||||
required this.reviews,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class ListUserReviewsUserReviews {
|
|
||||||
final int? rating;
|
|
||||||
final DateTime reviewDate;
|
|
||||||
final String? reviewText;
|
|
||||||
final ListUserReviewsUserReviewsMovie movie;
|
|
||||||
ListUserReviewsUserReviews.fromJson(dynamic json):
|
|
||||||
|
|
||||||
rating = json['rating'] == null ? null : nativeFromJson<int>(json['rating']),
|
|
||||||
reviewDate = nativeFromJson<DateTime>(json['reviewDate']),
|
|
||||||
reviewText = json['reviewText'] == null ? null : nativeFromJson<String>(json['reviewText']),
|
|
||||||
movie = ListUserReviewsUserReviewsMovie.fromJson(json['movie']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ListUserReviewsUserReviews otherTyped = other as ListUserReviewsUserReviews;
|
|
||||||
return rating == otherTyped.rating &&
|
|
||||||
reviewDate == otherTyped.reviewDate &&
|
|
||||||
reviewText == otherTyped.reviewText &&
|
|
||||||
movie == otherTyped.movie;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([rating.hashCode, reviewDate.hashCode, reviewText.hashCode, movie.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
if (rating != null) {
|
|
||||||
json['rating'] = nativeToJson<int?>(rating);
|
|
||||||
}
|
|
||||||
json['reviewDate'] = nativeToJson<DateTime>(reviewDate);
|
|
||||||
if (reviewText != null) {
|
|
||||||
json['reviewText'] = nativeToJson<String?>(reviewText);
|
|
||||||
}
|
|
||||||
json['movie'] = movie.toJson();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
ListUserReviewsUserReviews({
|
|
||||||
this.rating,
|
|
||||||
required this.reviewDate,
|
|
||||||
this.reviewText,
|
|
||||||
required this.movie,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class ListUserReviewsUserReviewsMovie {
|
|
||||||
final String id;
|
|
||||||
final String title;
|
|
||||||
ListUserReviewsUserReviewsMovie.fromJson(dynamic json):
|
|
||||||
|
|
||||||
id = nativeFromJson<String>(json['id']),
|
|
||||||
title = nativeFromJson<String>(json['title']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ListUserReviewsUserReviewsMovie otherTyped = other as ListUserReviewsUserReviewsMovie;
|
|
||||||
return id == otherTyped.id &&
|
|
||||||
title == otherTyped.title;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([id.hashCode, title.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['id'] = nativeToJson<String>(id);
|
|
||||||
json['title'] = nativeToJson<String>(title);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
ListUserReviewsUserReviewsMovie({
|
|
||||||
required this.id,
|
|
||||||
required this.title,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class ListUserReviewsData {
|
|
||||||
final ListUserReviewsUser? user;
|
|
||||||
ListUserReviewsData.fromJson(dynamic json):
|
|
||||||
|
|
||||||
user = json['user'] == null ? null : ListUserReviewsUser.fromJson(json['user']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ListUserReviewsData otherTyped = other as ListUserReviewsData;
|
|
||||||
return user == otherTyped.user;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => user.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
if (user != null) {
|
|
||||||
json['user'] = user!.toJson();
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
ListUserReviewsData({
|
|
||||||
this.user,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
part of 'generated.dart';
|
|
||||||
|
|
||||||
class ListUsersVariablesBuilder {
|
|
||||||
|
|
||||||
final FirebaseDataConnect _dataConnect;
|
|
||||||
ListUsersVariablesBuilder(this._dataConnect, );
|
|
||||||
Deserializer<ListUsersData> dataDeserializer = (dynamic json) => ListUsersData.fromJson(jsonDecode(json));
|
|
||||||
|
|
||||||
Future<QueryResult<ListUsersData, void>> execute() {
|
|
||||||
return ref().execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryRef<ListUsersData, void> ref() {
|
|
||||||
|
|
||||||
return _dataConnect.query("ListUsers", dataDeserializer, emptySerializer, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class ListUsersUsers {
|
|
||||||
final String id;
|
|
||||||
final String username;
|
|
||||||
ListUsersUsers.fromJson(dynamic json):
|
|
||||||
|
|
||||||
id = nativeFromJson<String>(json['id']),
|
|
||||||
username = nativeFromJson<String>(json['username']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ListUsersUsers otherTyped = other as ListUsersUsers;
|
|
||||||
return id == otherTyped.id &&
|
|
||||||
username == otherTyped.username;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([id.hashCode, username.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['id'] = nativeToJson<String>(id);
|
|
||||||
json['username'] = nativeToJson<String>(username);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
ListUsersUsers({
|
|
||||||
required this.id,
|
|
||||||
required this.username,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class ListUsersData {
|
|
||||||
final List<ListUsersUsers> users;
|
|
||||||
ListUsersData.fromJson(dynamic json):
|
|
||||||
|
|
||||||
users = (json['users'] as List<dynamic>)
|
|
||||||
.map((e) => ListUsersUsers.fromJson(e))
|
|
||||||
.toList();
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ListUsersData otherTyped = other as ListUsersData;
|
|
||||||
return users == otherTyped.users;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => users.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['users'] = users.map((e) => e.toJson()).toList();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
ListUsersData({
|
|
||||||
required this.users,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
part of 'generated.dart';
|
|
||||||
|
|
||||||
class SearchMovieVariablesBuilder {
|
|
||||||
Optional<String> _titleInput = Optional.optional(nativeFromJson, nativeToJson);
|
|
||||||
Optional<String> _genre = Optional.optional(nativeFromJson, nativeToJson);
|
|
||||||
|
|
||||||
final FirebaseDataConnect _dataConnect;
|
|
||||||
SearchMovieVariablesBuilder titleInput(String? t) {
|
|
||||||
_titleInput.value = t;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
SearchMovieVariablesBuilder genre(String? t) {
|
|
||||||
_genre.value = t;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchMovieVariablesBuilder(this._dataConnect, );
|
|
||||||
Deserializer<SearchMovieData> dataDeserializer = (dynamic json) => SearchMovieData.fromJson(jsonDecode(json));
|
|
||||||
Serializer<SearchMovieVariables> varsSerializer = (SearchMovieVariables vars) => jsonEncode(vars.toJson());
|
|
||||||
Future<QueryResult<SearchMovieData, SearchMovieVariables>> execute() {
|
|
||||||
return ref().execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
QueryRef<SearchMovieData, SearchMovieVariables> ref() {
|
|
||||||
SearchMovieVariables vars= SearchMovieVariables(titleInput: _titleInput,genre: _genre,);
|
|
||||||
return _dataConnect.query("SearchMovie", dataDeserializer, varsSerializer, vars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class SearchMovieMovies {
|
|
||||||
final String id;
|
|
||||||
final String title;
|
|
||||||
final String? genre;
|
|
||||||
final String imageUrl;
|
|
||||||
SearchMovieMovies.fromJson(dynamic json):
|
|
||||||
|
|
||||||
id = nativeFromJson<String>(json['id']),
|
|
||||||
title = nativeFromJson<String>(json['title']),
|
|
||||||
genre = json['genre'] == null ? null : nativeFromJson<String>(json['genre']),
|
|
||||||
imageUrl = nativeFromJson<String>(json['imageUrl']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final SearchMovieMovies otherTyped = other as SearchMovieMovies;
|
|
||||||
return id == otherTyped.id &&
|
|
||||||
title == otherTyped.title &&
|
|
||||||
genre == otherTyped.genre &&
|
|
||||||
imageUrl == otherTyped.imageUrl;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([id.hashCode, title.hashCode, genre.hashCode, imageUrl.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['id'] = nativeToJson<String>(id);
|
|
||||||
json['title'] = nativeToJson<String>(title);
|
|
||||||
if (genre != null) {
|
|
||||||
json['genre'] = nativeToJson<String?>(genre);
|
|
||||||
}
|
|
||||||
json['imageUrl'] = nativeToJson<String>(imageUrl);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchMovieMovies({
|
|
||||||
required this.id,
|
|
||||||
required this.title,
|
|
||||||
this.genre,
|
|
||||||
required this.imageUrl,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class SearchMovieData {
|
|
||||||
final List<SearchMovieMovies> movies;
|
|
||||||
SearchMovieData.fromJson(dynamic json):
|
|
||||||
|
|
||||||
movies = (json['movies'] as List<dynamic>)
|
|
||||||
.map((e) => SearchMovieMovies.fromJson(e))
|
|
||||||
.toList();
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final SearchMovieData otherTyped = other as SearchMovieData;
|
|
||||||
return movies == otherTyped.movies;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => movies.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['movies'] = movies.map((e) => e.toJson()).toList();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchMovieData({
|
|
||||||
required this.movies,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class SearchMovieVariables {
|
|
||||||
late final Optional<String>titleInput;
|
|
||||||
late final Optional<String>genre;
|
|
||||||
@Deprecated('fromJson is deprecated for Variable classes as they are no longer required for deserialization.')
|
|
||||||
SearchMovieVariables.fromJson(Map<String, dynamic> json) {
|
|
||||||
|
|
||||||
|
|
||||||
titleInput = Optional.optional(nativeFromJson, nativeToJson);
|
|
||||||
titleInput.value = json['titleInput'] == null ? null : nativeFromJson<String>(json['titleInput']);
|
|
||||||
|
|
||||||
|
|
||||||
genre = Optional.optional(nativeFromJson, nativeToJson);
|
|
||||||
genre.value = json['genre'] == null ? null : nativeFromJson<String>(json['genre']);
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final SearchMovieVariables otherTyped = other as SearchMovieVariables;
|
|
||||||
return titleInput == otherTyped.titleInput &&
|
|
||||||
genre == otherTyped.genre;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([titleInput.hashCode, genre.hashCode]);
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
if(titleInput.state == OptionalState.set) {
|
|
||||||
json['titleInput'] = titleInput.toJson();
|
|
||||||
}
|
|
||||||
if(genre.state == OptionalState.set) {
|
|
||||||
json['genre'] = genre.toJson();
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchMovieVariables({
|
|
||||||
required this.titleInput,
|
|
||||||
required this.genre,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
part of 'generated.dart';
|
|
||||||
|
|
||||||
class UpsertUserVariablesBuilder {
|
|
||||||
String username;
|
|
||||||
|
|
||||||
final FirebaseDataConnect _dataConnect;
|
|
||||||
UpsertUserVariablesBuilder(this._dataConnect, {required this.username,});
|
|
||||||
Deserializer<UpsertUserData> dataDeserializer = (dynamic json) => UpsertUserData.fromJson(jsonDecode(json));
|
|
||||||
Serializer<UpsertUserVariables> varsSerializer = (UpsertUserVariables vars) => jsonEncode(vars.toJson());
|
|
||||||
Future<OperationResult<UpsertUserData, UpsertUserVariables>> execute() {
|
|
||||||
return ref().execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
MutationRef<UpsertUserData, UpsertUserVariables> ref() {
|
|
||||||
UpsertUserVariables vars= UpsertUserVariables(username: username,);
|
|
||||||
return _dataConnect.mutation("UpsertUser", dataDeserializer, varsSerializer, vars);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class UpsertUserUserUpsert {
|
|
||||||
final String id;
|
|
||||||
UpsertUserUserUpsert.fromJson(dynamic json):
|
|
||||||
|
|
||||||
id = nativeFromJson<String>(json['id']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final UpsertUserUserUpsert otherTyped = other as UpsertUserUserUpsert;
|
|
||||||
return id == otherTyped.id;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => id.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['id'] = nativeToJson<String>(id);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpsertUserUserUpsert({
|
|
||||||
required this.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class UpsertUserData {
|
|
||||||
final UpsertUserUserUpsert user_upsert;
|
|
||||||
UpsertUserData.fromJson(dynamic json):
|
|
||||||
|
|
||||||
user_upsert = UpsertUserUserUpsert.fromJson(json['user_upsert']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final UpsertUserData otherTyped = other as UpsertUserData;
|
|
||||||
return user_upsert == otherTyped.user_upsert;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => user_upsert.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['user_upsert'] = user_upsert.toJson();
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpsertUserData({
|
|
||||||
required this.user_upsert,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
class UpsertUserVariables {
|
|
||||||
final String username;
|
|
||||||
@Deprecated('fromJson is deprecated for Variable classes as they are no longer required for deserialization.')
|
|
||||||
UpsertUserVariables.fromJson(Map<String, dynamic> json):
|
|
||||||
|
|
||||||
username = nativeFromJson<String>(json['username']);
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
if(identical(this, other)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(other.runtimeType != runtimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final UpsertUserVariables otherTyped = other as UpsertUserVariables;
|
|
||||||
return username == otherTyped.username;
|
|
||||||
|
|
||||||
}
|
|
||||||
@override
|
|
||||||
int get hashCode => username.hashCode;
|
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
Map<String, dynamic> json = {};
|
|
||||||
json['username'] = nativeToJson<String>(username);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpsertUserVariables({
|
|
||||||
required this.username,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
||||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
|
||||||
import 'theme.dart';
|
|
||||||
import 'router.dart';
|
|
||||||
import 'widgets/web_mobile_frame.dart';
|
|
||||||
|
|
||||||
void main() async {
|
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
|
||||||
|
|
||||||
const app = AppRoot();
|
|
||||||
|
|
||||||
runApp(ProviderScope(child: kIsWeb ? const WebMobileFrame(child: app) : app));
|
|
||||||
}
|
|
||||||
|
|
||||||
class AppRoot extends ConsumerWidget {
|
|
||||||
const AppRoot({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
return MaterialApp.router(
|
|
||||||
title: 'Krow Client App',
|
|
||||||
theme: AppTheme.lightTheme,
|
|
||||||
routerConfig: router,
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'screens/auth/client_get_started_screen.dart';
|
|
||||||
import 'screens/auth/client_sign_in_screen.dart';
|
|
||||||
import 'screens/auth/client_sign_up_screen.dart';
|
|
||||||
import 'screens/client/client_home_screen.dart';
|
|
||||||
import 'screens/client/client_workers_screen.dart';
|
|
||||||
import 'screens/client/client_timesheets_screen.dart';
|
|
||||||
import 'screens/client/client_shifts_screen.dart';
|
|
||||||
import 'screens/client/client_reports_screen.dart';
|
|
||||||
import 'screens/client/create_order_screen.dart';
|
|
||||||
import 'screens/client/client_settings_screen.dart';
|
|
||||||
import 'screens/client/client_billing_screen.dart';
|
|
||||||
import 'screens/client/client_coverage_screen.dart';
|
|
||||||
import 'screens/client/client_hubs_screen.dart';
|
|
||||||
import 'screens/client/verify_worker_attire_screen.dart';
|
|
||||||
import 'screens/client/reports/daily_ops_report_screen.dart';
|
|
||||||
import 'screens/client/reports/spend_report_screen.dart';
|
|
||||||
import 'screens/client/reports/forecast_report_screen.dart';
|
|
||||||
import 'screens/client/reports/performance_report_screen.dart';
|
|
||||||
import 'screens/client/reports/no_show_report_screen.dart';
|
|
||||||
import 'screens/client/reports/coverage_report_screen.dart';
|
|
||||||
import 'screens/client/create_order_pages/rapid_order_flow_page.dart';
|
|
||||||
import 'screens/client/create_order_pages/one_time_order_flow_page.dart';
|
|
||||||
import 'screens/client/create_order_pages/recurring_order_flow_page.dart';
|
|
||||||
import 'screens/client/create_order_pages/permanent_order_flow_page.dart';
|
|
||||||
import 'widgets/scaffold_with_nav_bar.dart';
|
|
||||||
|
|
||||||
final router = GoRouter(
|
|
||||||
initialLocation: '/client-get-started',
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
path: '/client-get-started',
|
|
||||||
builder: (context, state) => const ClientGetStartedScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/client-sign-in',
|
|
||||||
builder: (context, state) => const ClientSignInScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/client-sign-up',
|
|
||||||
builder: (context, state) => const ClientSignUpScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/create-order',
|
|
||||||
builder: (context, state) => const CreateOrderScreen(),
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
path: 'rapid',
|
|
||||||
builder: (context, state) => const RapidOrderFlowPage(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: 'one-time',
|
|
||||||
builder: (context, state) => const OneTimeOrderFlowPage(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: 'recurring',
|
|
||||||
builder: (context, state) => const RecurringOrderFlowPage(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: 'permanent',
|
|
||||||
builder: (context, state) => const PermanentOrderFlowPage(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/client-settings',
|
|
||||||
builder: (context, state) => const ClientSettingsScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/client-hubs',
|
|
||||||
builder: (context, state) => const ClientHubsScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/verify-worker-attire',
|
|
||||||
builder: (context, state) => const VerifyWorkerAttireScreen(),
|
|
||||||
),
|
|
||||||
// Report Routes
|
|
||||||
GoRoute(
|
|
||||||
path: '/daily-ops-report',
|
|
||||||
builder: (context, state) => const DailyOpsReportScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/spend-report',
|
|
||||||
builder: (context, state) => const SpendReportScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/forecast-report',
|
|
||||||
builder: (context, state) => const ForecastReportScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/performance-report',
|
|
||||||
builder: (context, state) => const PerformanceReportScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/no-show-report',
|
|
||||||
builder: (context, state) => const NoShowReportScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/coverage-report-detail',
|
|
||||||
builder: (context, state) => const CoverageReportScreen(),
|
|
||||||
),
|
|
||||||
// Moved Workers and Timesheets out of bottom nav, accessible as standalone routes
|
|
||||||
GoRoute(
|
|
||||||
path: '/client-workers',
|
|
||||||
builder: (context, state) => const ClientWorkersScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
|
||||||
path: '/client-timesheets',
|
|
||||||
builder: (context, state) => const ClientTimesheetsScreen(),
|
|
||||||
),
|
|
||||||
|
|
||||||
StatefulShellRoute.indexedStack(
|
|
||||||
builder: (context, state, navigationShell) {
|
|
||||||
return ScaffoldWithNavBar(navigationShell: navigationShell);
|
|
||||||
},
|
|
||||||
branches: [
|
|
||||||
// Index 0: Coverage
|
|
||||||
StatefulShellBranch(
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
path: '/client-coverage',
|
|
||||||
builder: (context, state) => const ClientCoverageScreen(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// Index 1: Billing
|
|
||||||
StatefulShellBranch(
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
path: '/client-billing',
|
|
||||||
builder: (context, state) => const ClientBillingScreen(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// Index 2: Home
|
|
||||||
StatefulShellBranch(
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
path: '/client-home',
|
|
||||||
builder: (context, state) => const ClientHomeScreen(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// Index 3: Orders (Shifts)
|
|
||||||
StatefulShellBranch(
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
path: '/client-shifts',
|
|
||||||
builder: (context, state) => const ClientShiftsScreen(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
// Index 4: Reports
|
|
||||||
StatefulShellBranch(
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
path: '/client-reports',
|
|
||||||
builder: (context, state) => const ClientReportsScreen(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
@@ -1,392 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
import '../../theme.dart';
|
|
||||||
|
|
||||||
class ClientGetStartedScreen extends StatelessWidget {
|
|
||||||
const ClientGetStartedScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: AppColors.krowBlue,
|
|
||||||
body: SafeArea(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
// Logo
|
|
||||||
Image.network(
|
|
||||||
'https://qtrypzzcjebvfcihiynt.supabase.co/storage/v1/object/public/base44-prod/public/692e9622b387da7cdcd95980/29a493751_PNG3Krow.png',
|
|
||||||
height: 40,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
|
|
||||||
// Floating Cards Area
|
|
||||||
const Expanded(
|
|
||||||
child: Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
clipBehavior: Clip.none,
|
|
||||||
children: [
|
|
||||||
// Card 1 - Shift Order
|
|
||||||
Positioned(left: 24, top: 32, child: _ShiftOrderCard()),
|
|
||||||
|
|
||||||
// Card 2 - Worker Profile
|
|
||||||
Positioned(right: 24, top: 16, child: _WorkerProfileCard()),
|
|
||||||
|
|
||||||
// Card 3 - Calendar
|
|
||||||
Positioned(top: 112, child: _CalendarCard()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Bottom Content
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 40),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Take Control of Your\nShifts and Events',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 30,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
height: 1.1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
const Text(
|
|
||||||
'Streamline your operations with powerful tools to manage schedules, track performance, and keep your team on the same page—all in one place',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white70,
|
|
||||||
fontSize: 14,
|
|
||||||
height: 1.4,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
|
|
||||||
// Sign In Button
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 56,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: () => context.push('/client-sign-in'),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: AppColors.krowYellow,
|
|
||||||
foregroundColor: AppColors.krowCharcoal,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(28),
|
|
||||||
),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'Sign In',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
|
|
||||||
// Create Account Button
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 56,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: () => context.push('/client-sign-up'),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
foregroundColor: AppColors.krowCharcoal,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(28),
|
|
||||||
),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'Create Account',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ShiftOrderCard extends StatelessWidget {
|
|
||||||
const _ShiftOrderCard();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Transform.rotate(
|
|
||||||
angle: -12 * 3.14159 / 180,
|
|
||||||
child: Container(
|
|
||||||
width: 160,
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.1),
|
|
||||||
blurRadius: 10,
|
|
||||||
offset: const Offset(0, 4),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.green.shade100,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: const Center(
|
|
||||||
child: Icon(Icons.check, size: 14, color: Colors.green),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
const Text(
|
|
||||||
'Confirmed',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.green,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
const Text(
|
|
||||||
'Taste of the Town',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Text(
|
|
||||||
'Event Catering',
|
|
||||||
style: TextStyle(fontSize: 10, color: Colors.grey),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
const Row(
|
|
||||||
children: [
|
|
||||||
Icon(LucideIcons.calendar, size: 12, color: Colors.grey),
|
|
||||||
SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
'01.21.2025, 06:30 pm',
|
|
||||||
style: TextStyle(fontSize: 10, color: Colors.grey),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _WorkerProfileCard extends StatelessWidget {
|
|
||||||
const _WorkerProfileCard();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Transform.rotate(
|
|
||||||
angle: 8 * 3.14159 / 180,
|
|
||||||
child: Container(
|
|
||||||
width: 144,
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.1),
|
|
||||||
blurRadius: 10,
|
|
||||||
offset: const Offset(0, 4),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 48,
|
|
||||||
height: 48,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
gradient: LinearGradient(
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
colors: [Colors.orangeAccent, Colors.deepOrange],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
const Text(
|
|
||||||
'★ Verified',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.green,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 2),
|
|
||||||
const Text(
|
|
||||||
'Jane Johnson',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Text(
|
|
||||||
'Server • 4.9 ★',
|
|
||||||
style: TextStyle(fontSize: 10, color: Colors.grey),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CalendarCard extends StatelessWidget {
|
|
||||||
const _CalendarCard();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
width: 192,
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.1),
|
|
||||||
blurRadius: 10,
|
|
||||||
offset: const Offset(0, 4),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'January 2025',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Icon(LucideIcons.clipboardList, size: 16, color: Colors.grey),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
GridView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
||||||
crossAxisCount: 7,
|
|
||||||
mainAxisSpacing: 2,
|
|
||||||
crossAxisSpacing: 2,
|
|
||||||
childAspectRatio: 1,
|
|
||||||
),
|
|
||||||
itemCount: 7 + 31,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (index < 7) {
|
|
||||||
final days = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
|
|
||||||
return Center(
|
|
||||||
child: Text(
|
|
||||||
days[index],
|
|
||||||
style: const TextStyle(fontSize: 8, color: Colors.grey),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final day = index - 7 + 1;
|
|
||||||
Color? bg;
|
|
||||||
Color? text;
|
|
||||||
|
|
||||||
if (day == 21) {
|
|
||||||
bg = Colors.blue.shade600;
|
|
||||||
text = Colors.white;
|
|
||||||
} else if (day == 15) {
|
|
||||||
// Adjusted to match visual roughly
|
|
||||||
bg = Colors.green.shade100;
|
|
||||||
text = Colors.green.shade600;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: bg,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
'$day',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 8,
|
|
||||||
color: text ?? Colors.grey.shade600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.green.shade100,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'Active Event',
|
|
||||||
style: TextStyle(fontSize: 8, color: Colors.green),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.amber.shade100,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'Assigned Event',
|
|
||||||
style: TextStyle(fontSize: 8, color: Colors.amber),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,384 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
import '../../theme.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
|
|
||||||
class ClientSignInScreen extends StatefulWidget {
|
|
||||||
const ClientSignInScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ClientSignInScreen> createState() => _ClientSignInScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ClientSignInScreenState extends State<ClientSignInScreen> {
|
|
||||||
final _emailController = TextEditingController();
|
|
||||||
final _passwordController = TextEditingController();
|
|
||||||
bool _obscurePassword = true;
|
|
||||||
bool _isLoading = false;
|
|
||||||
|
|
||||||
void _handleSignIn() async {
|
|
||||||
setState(() => _isLoading = true);
|
|
||||||
// Simulate sign in
|
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
|
||||||
if (mounted) {
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
context.go('/client-home');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: AppColors.krowBlue,
|
|
||||||
body: SafeArea(
|
|
||||||
bottom: false,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
// Header
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => context.pop(),
|
|
||||||
child: Container(
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withOpacity(0.1),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
LucideIcons.chevronLeft,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 32),
|
|
||||||
child: Image.network(
|
|
||||||
'https://qtrypzzcjebvfcihiynt.supabase.co/storage/v1/object/public/base44-prod/public/692e9622b387da7cdcd95980/29a493751_PNG3Krow.png',
|
|
||||||
height: 40,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Form Card
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(32),
|
|
||||||
topRight: Radius.circular(32),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.fromLTRB(24, 32, 24, 0),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Welcome Back',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
const Text(
|
|
||||||
'Sign in to manage your shifts and workers',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
|
|
||||||
// Email Field
|
|
||||||
const Text(
|
|
||||||
'Email',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
TextField(
|
|
||||||
controller: _emailController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Enter your email',
|
|
||||||
prefixIcon: const Icon(
|
|
||||||
LucideIcons.mail,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBlue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
// Password Field
|
|
||||||
const Text(
|
|
||||||
'Password',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
TextField(
|
|
||||||
controller: _passwordController,
|
|
||||||
obscureText: _obscurePassword,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Enter your password',
|
|
||||||
prefixIcon: const Icon(
|
|
||||||
LucideIcons.lock,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
_obscurePassword
|
|
||||||
? LucideIcons.eyeOff
|
|
||||||
: LucideIcons.eye,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
onPressed: () => setState(
|
|
||||||
() => _obscurePassword = !_obscurePassword,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBlue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Forgot Password
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: TextButton(
|
|
||||||
onPressed: () {},
|
|
||||||
child: const Text(
|
|
||||||
'Forgot Password?',
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.krowBlue,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
|
|
||||||
// Sign In Button
|
|
||||||
SizedBox(
|
|
||||||
height: 56,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: _isLoading ? null : _handleSignIn,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: AppColors.krowBlue,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
child: _isLoading
|
|
||||||
? const CircularProgressIndicator(
|
|
||||||
color: Colors.white,
|
|
||||||
)
|
|
||||||
: const Text(
|
|
||||||
'Sign In',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Divider
|
|
||||||
const Row(
|
|
||||||
children: [
|
|
||||||
Expanded(child: Divider(color: AppColors.krowBorder)),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
child: Text(
|
|
||||||
'or',
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(child: Divider(color: AppColors.krowBorder)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Social Buttons
|
|
||||||
_SocialButton(
|
|
||||||
text: 'Sign In with Apple',
|
|
||||||
icon: Icons.apple,
|
|
||||||
onPressed: _handleSignIn,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
_SocialButton(
|
|
||||||
text: 'Sign In with Google',
|
|
||||||
icon: Icons
|
|
||||||
.flutter_dash, // Custom Google icon logic if needed
|
|
||||||
isGoogle: true,
|
|
||||||
onPressed: _handleSignIn,
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
|
|
||||||
// Sign Up Link
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
"Don't have an account? ",
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => context.push('/client-sign-up'),
|
|
||||||
child: const Text(
|
|
||||||
'Sign Up',
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.krowBlue,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 40),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SocialButton extends StatelessWidget {
|
|
||||||
final String text;
|
|
||||||
final IconData? icon;
|
|
||||||
final bool isGoogle;
|
|
||||||
final VoidCallback onPressed;
|
|
||||||
|
|
||||||
const _SocialButton({
|
|
||||||
required this.text,
|
|
||||||
this.icon,
|
|
||||||
this.isGoogle = false,
|
|
||||||
required this.onPressed,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SizedBox(
|
|
||||||
height: 56,
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: onPressed,
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
side: const BorderSide(color: AppColors.krowBorder),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
foregroundColor: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
if (text.contains('Apple'))
|
|
||||||
const FaIcon(
|
|
||||||
FontAwesomeIcons.apple,
|
|
||||||
color: Colors.black,
|
|
||||||
size: 20,
|
|
||||||
)
|
|
||||||
else if (isGoogle)
|
|
||||||
const FaIcon(
|
|
||||||
FontAwesomeIcons.google,
|
|
||||||
color: Colors.black,
|
|
||||||
size: 20,
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Icon(icon, color: AppColors.krowCharcoal, size: 20),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(
|
|
||||||
text,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,463 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
import '../../theme.dart';
|
|
||||||
|
|
||||||
class ClientSignUpScreen extends StatefulWidget {
|
|
||||||
const ClientSignUpScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ClientSignUpScreen> createState() => _ClientSignUpScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ClientSignUpScreenState extends State<ClientSignUpScreen> {
|
|
||||||
final _companyController = TextEditingController();
|
|
||||||
final _emailController = TextEditingController();
|
|
||||||
final _passwordController = TextEditingController();
|
|
||||||
final _confirmPasswordController = TextEditingController();
|
|
||||||
bool _obscurePassword = true;
|
|
||||||
bool _isLoading = false;
|
|
||||||
|
|
||||||
void _handleSignUp() async {
|
|
||||||
setState(() => _isLoading = true);
|
|
||||||
// Simulate sign up
|
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
|
||||||
if (mounted) {
|
|
||||||
setState(() => _isLoading = false);
|
|
||||||
context.go('/client-home');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: AppColors.krowBlue,
|
|
||||||
body: SafeArea(
|
|
||||||
bottom: false,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
// Header
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => context.pop(),
|
|
||||||
child: Container(
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withOpacity(0.1),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
LucideIcons.chevronLeft,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 24),
|
|
||||||
child: Image.network(
|
|
||||||
'https://qtrypzzcjebvfcihiynt.supabase.co/storage/v1/object/public/base44-prod/public/692e9622b387da7cdcd95980/29a493751_PNG3Krow.png',
|
|
||||||
height: 40,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Form Card
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(32),
|
|
||||||
topRight: Radius.circular(32),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.fromLTRB(24, 32, 24, 0),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Create Account',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
const Text(
|
|
||||||
'Get started with Krow for your business',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Company Name Field
|
|
||||||
const Text(
|
|
||||||
'Company Name',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
TextField(
|
|
||||||
controller: _companyController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Enter company name',
|
|
||||||
prefixIcon: const Icon(
|
|
||||||
LucideIcons.building2,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBlue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
// Email Field
|
|
||||||
const Text(
|
|
||||||
'Email',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
TextField(
|
|
||||||
controller: _emailController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Enter your email',
|
|
||||||
prefixIcon: const Icon(
|
|
||||||
LucideIcons.mail,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBlue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
// Password Field
|
|
||||||
const Text(
|
|
||||||
'Password',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
TextField(
|
|
||||||
controller: _passwordController,
|
|
||||||
obscureText: _obscurePassword,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Create a password',
|
|
||||||
prefixIcon: const Icon(
|
|
||||||
LucideIcons.lock,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
suffixIcon: IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
_obscurePassword
|
|
||||||
? LucideIcons.eyeOff
|
|
||||||
: LucideIcons.eye,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
onPressed: () => setState(
|
|
||||||
() => _obscurePassword = !_obscurePassword,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBlue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
// Confirm Password Field
|
|
||||||
const Text(
|
|
||||||
'Confirm Password',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
TextField(
|
|
||||||
controller: _confirmPasswordController,
|
|
||||||
obscureText: _obscurePassword,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Confirm your password',
|
|
||||||
prefixIcon: const Icon(
|
|
||||||
LucideIcons.lock,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBorder,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: AppColors.krowBlue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
|
|
||||||
// Create Account Button
|
|
||||||
SizedBox(
|
|
||||||
height: 56,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: _isLoading ? null : _handleSignUp,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: AppColors.krowBlue,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
child: _isLoading
|
|
||||||
? const CircularProgressIndicator(
|
|
||||||
color: Colors.white,
|
|
||||||
)
|
|
||||||
: const Text(
|
|
||||||
'Create Account',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Divider
|
|
||||||
const Row(
|
|
||||||
children: [
|
|
||||||
Expanded(child: Divider(color: AppColors.krowBorder)),
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
child: Text(
|
|
||||||
'or',
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(child: Divider(color: AppColors.krowBorder)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Social Buttons
|
|
||||||
_SocialButton(
|
|
||||||
text: 'Sign Up with Apple',
|
|
||||||
icon: Icons.apple,
|
|
||||||
onPressed: () {},
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
_SocialButton(
|
|
||||||
text: 'Sign Up with Google',
|
|
||||||
icon: null,
|
|
||||||
isGoogle: true,
|
|
||||||
onPressed: () {},
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
|
|
||||||
// Sign In Link
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
"Already have an account? ",
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => context.push('/client-sign-in'),
|
|
||||||
child: const Text(
|
|
||||||
'Sign In',
|
|
||||||
style: TextStyle(
|
|
||||||
color: AppColors.krowBlue,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 40),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SocialButton extends StatelessWidget {
|
|
||||||
final String text;
|
|
||||||
final IconData? icon;
|
|
||||||
final bool isGoogle;
|
|
||||||
final VoidCallback onPressed;
|
|
||||||
|
|
||||||
const _SocialButton({
|
|
||||||
required this.text,
|
|
||||||
this.icon,
|
|
||||||
this.isGoogle = false,
|
|
||||||
required this.onPressed,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SizedBox(
|
|
||||||
height: 56,
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: onPressed,
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
side: const BorderSide(color: AppColors.krowBorder),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
foregroundColor: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
if (isGoogle)
|
|
||||||
Container(
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
decoration: const BoxDecoration(shape: BoxShape.circle),
|
|
||||||
child: Image.network(
|
|
||||||
'https://www.gstatic.com/images/branding/product/1x/gsa_512dp.png',
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else if (text.contains('Apple'))
|
|
||||||
Image.network(
|
|
||||||
'https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_logo_black.svg/480px-Apple_logo_black.svg.png',
|
|
||||||
height: 24,
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Icon(icon, color: AppColors.krowCharcoal, size: 20),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Text(
|
|
||||||
text,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,990 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
import '../../theme.dart';
|
|
||||||
|
|
||||||
class ClientBillingScreen extends StatefulWidget {
|
|
||||||
const ClientBillingScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ClientBillingScreen> createState() => _ClientBillingScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ClientBillingScreenState extends State<ClientBillingScreen>
|
|
||||||
with SingleTickerProviderStateMixin {
|
|
||||||
late TabController _periodTabController;
|
|
||||||
|
|
||||||
// Mock Data
|
|
||||||
final double currentBill = 4250.00;
|
|
||||||
final double savings = 320.00;
|
|
||||||
|
|
||||||
final List<Map<String, dynamic>> pendingInvoices = [
|
|
||||||
{
|
|
||||||
'id': 'INV-PEND-001',
|
|
||||||
'title': 'Server Staff - Downtown Event',
|
|
||||||
'location_address': '123 Main St, City Center',
|
|
||||||
'client_name': 'TechCorp Inc.',
|
|
||||||
'date': '2024-01-24', // Use a recent date
|
|
||||||
'invoiceTotal': 840.00,
|
|
||||||
'workers_count': 3,
|
|
||||||
'total_hours': 24.0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'id': 'INV-PEND-002',
|
|
||||||
'title': 'Bartenders - Private Party',
|
|
||||||
'location_address': '456 High St, West End',
|
|
||||||
'client_name': 'TechCorp Inc.',
|
|
||||||
'date': '2024-01-23',
|
|
||||||
'invoiceTotal': 620.00,
|
|
||||||
'workers_count': 2,
|
|
||||||
'total_hours': 16.0,
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
final List<Map<String, dynamic>> breakdown = [
|
|
||||||
{'category': 'Server Staff', 'hours': 120, 'amount': 2160.00},
|
|
||||||
{'category': 'Bartenders', 'hours': 80, 'amount': 1520.00},
|
|
||||||
{'category': 'Kitchen Staff', 'hours': 40, 'amount': 640.00},
|
|
||||||
{'category': 'Event Staff', 'hours': 25, 'amount': 425.00},
|
|
||||||
];
|
|
||||||
|
|
||||||
final List<Map<String, dynamic>> invoices = [
|
|
||||||
{
|
|
||||||
'id': 'INV-2024-012',
|
|
||||||
'date': '2024-12-15',
|
|
||||||
'amount': 5280.00,
|
|
||||||
'status': 'paid'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'id': 'INV-2024-011',
|
|
||||||
'date': '2024-12-08',
|
|
||||||
'amount': 4850.00,
|
|
||||||
'status': 'paid'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'id': 'INV-2024-010',
|
|
||||||
'date': '2024-12-01',
|
|
||||||
'amount': 4120.00,
|
|
||||||
'status': 'paid'
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_periodTabController = TabController(length: 2, vsync: this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_periodTabController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// Colors matching React
|
|
||||||
const krowBlue = Color(0xFF0A39DF);
|
|
||||||
const krowYellow = Color(0xFFFFED4A);
|
|
||||||
const krowCharcoal = Color(0xFF121826);
|
|
||||||
// const krowMuted = Color(0xFF6A7382); // Already in theme? Using AppColors.krowMuted
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: AppColors.krowBackground,
|
|
||||||
body: Column(
|
|
||||||
children: [
|
|
||||||
// Header
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.fromLTRB(20, 60, 20, 20),
|
|
||||||
color: krowBlue,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
// Top Bar
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => context.go('/client-home'),
|
|
||||||
child: Container(
|
|
||||||
width: 36,
|
|
||||||
height: 36,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withOpacity(0.2),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
LucideIcons.arrowLeft,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
const Text(
|
|
||||||
'Billing',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
// Current Bill
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Current Period',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white.withOpacity(0.7),
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
'\$${currentBill.toStringAsFixed(2)}',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 8,
|
|
||||||
vertical: 4,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: krowYellow,
|
|
||||||
borderRadius: BorderRadius.circular(100),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
LucideIcons.trendingDown,
|
|
||||||
size: 12,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
'\$${savings.toStringAsFixed(0)} saved',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Content
|
|
||||||
Expanded(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
// Pending Invoices
|
|
||||||
if (pendingInvoices.isNotEmpty) ...[
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 6,
|
|
||||||
height: 6,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.orange,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
const Text(
|
|
||||||
'Awaiting Approval',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Container(
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: krowYellow,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
'${pendingInvoices.length}',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
...pendingInvoices.map(
|
|
||||||
(invoice) => Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 8),
|
|
||||||
child: _buildPendingInvoiceCard(invoice),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
],
|
|
||||||
|
|
||||||
// Payment Method
|
|
||||||
_buildPaymentMethodCard(krowBlue, krowYellow, krowCharcoal),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
// Spending Breakdown
|
|
||||||
_buildBreakdownCard(krowCharcoal),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
// Savings Card
|
|
||||||
_buildSavingsCard(krowBlue, krowYellow, krowCharcoal),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Invoice History
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Invoice History',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {},
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
minimumSize: Size.zero,
|
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'View all',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: krowBlue,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Icon(
|
|
||||||
LucideIcons.chevronRight,
|
|
||||||
size: 16,
|
|
||||||
color: krowBlue,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
border: Border.all(color: const Color(0xFFE3E6E9)),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.05),
|
|
||||||
blurRadius: 2,
|
|
||||||
offset: const Offset(0, 1),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: invoices.asMap().entries.map((entry) {
|
|
||||||
final index = entry.key;
|
|
||||||
final invoice = entry.value;
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
if (index > 0)
|
|
||||||
const Divider(
|
|
||||||
height: 1,
|
|
||||||
color: Color(0xFFF1F5F9),
|
|
||||||
),
|
|
||||||
_buildInvoiceItem(invoice),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Export Button
|
|
||||||
OutlinedButton.icon(
|
|
||||||
onPressed: () {},
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
minimumSize: const Size(double.infinity, 44),
|
|
||||||
side: const BorderSide(color: Color(0xFFE3E6E9)),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
foregroundColor: krowCharcoal,
|
|
||||||
),
|
|
||||||
icon: const Icon(LucideIcons.download, size: 16),
|
|
||||||
label: const Text(
|
|
||||||
'Export All Invoices',
|
|
||||||
style: TextStyle(fontSize: 14),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildPendingInvoiceCard(Map<String, dynamic> invoice) {
|
|
||||||
const krowBlue = Color(0xFF0A39DF);
|
|
||||||
const krowCharcoal = Color(0xFF121826);
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
border: Border.all(color: const Color(0xFFE3E6E9)),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.05),
|
|
||||||
blurRadius: 2,
|
|
||||||
offset: const Offset(0, 1),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
// Address
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
LucideIcons.mapPin,
|
|
||||||
size: 14,
|
|
||||||
color: Color(0xFF475569),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
invoice['location_address'],
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF475569),
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
// Title
|
|
||||||
Text(
|
|
||||||
invoice['title'],
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
// Client & Date
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
invoice['client_name'],
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
const Text(
|
|
||||||
'•',
|
|
||||||
style: TextStyle(fontSize: 12, color: Color(0xFF94A3B8)),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
Text(
|
|
||||||
invoice['date'], // Should ideally format date
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF475569),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
// Status
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 6,
|
|
||||||
height: 6,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.orange,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
const Text(
|
|
||||||
'PENDING APPROVAL',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.orange,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
// Grid Stats
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
border: Border.symmetric(
|
|
||||||
horizontal: BorderSide(color: Color(0xFFF1F5F9)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
LucideIcons.dollarSign,
|
|
||||||
size: 14,
|
|
||||||
color: Color(0xFF94A3B8),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 2),
|
|
||||||
Text(
|
|
||||||
'\$${invoice['invoiceTotal'].toStringAsFixed(2)}',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Text(
|
|
||||||
'Total',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(width: 1, height: 30, color: const Color(0xFFF1F5F9)),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
LucideIcons.users,
|
|
||||||
size: 14,
|
|
||||||
color: Color(0xFF94A3B8),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 2),
|
|
||||||
Text(
|
|
||||||
'${invoice['workers_count']}',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Text(
|
|
||||||
'workers',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(width: 1, height: 30, color: const Color(0xFFF1F5F9)),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
LucideIcons.clock,
|
|
||||||
size: 14,
|
|
||||||
color: Color(0xFF94A3B8),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 2),
|
|
||||||
Text(
|
|
||||||
'${invoice['total_hours'].toStringAsFixed(1)}',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Text(
|
|
||||||
'HRS',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
// Approve Button
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {},
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: krowBlue,
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
minimumSize: const Size(double.infinity, 36),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
child: const Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(LucideIcons.checkCircle, size: 16),
|
|
||||||
SizedBox(width: 6),
|
|
||||||
Text(
|
|
||||||
'Review & Approve',
|
|
||||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildPaymentMethodCard(
|
|
||||||
Color krowBlue,
|
|
||||||
Color krowYellow,
|
|
||||||
Color krowCharcoal,
|
|
||||||
) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
border: Border.all(color: const Color(0xFFE3E6E9)),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.05),
|
|
||||||
blurRadius: 2,
|
|
||||||
offset: const Offset(0, 1),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Payment Method',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton.icon(
|
|
||||||
onPressed: () {},
|
|
||||||
icon: Icon(LucideIcons.plus, size: 14, color: krowBlue),
|
|
||||||
label: Text(
|
|
||||||
'Add',
|
|
||||||
style: TextStyle(fontSize: 12, color: krowBlue),
|
|
||||||
),
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
minimumSize: Size.zero,
|
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFF8FAFC),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 40,
|
|
||||||
height: 28,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: krowBlue,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: const Center(
|
|
||||||
child: Text(
|
|
||||||
'VISA',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'•••• 4242',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Text(
|
|
||||||
'Expires 12/25',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 6,
|
|
||||||
vertical: 2,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: krowYellow,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'Default',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildBreakdownCard(Color krowCharcoal) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
border: Border.all(color: const Color(0xFFE3E6E9)),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.05),
|
|
||||||
blurRadius: 2,
|
|
||||||
offset: const Offset(0, 1),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'This Period Breakdown',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
height: 24,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFF1F5F9),
|
|
||||||
borderRadius: BorderRadius.circular(6),
|
|
||||||
),
|
|
||||||
child: TabBar(
|
|
||||||
controller: _periodTabController,
|
|
||||||
isScrollable: true,
|
|
||||||
indicator: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.05),
|
|
||||||
blurRadius: 1,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
labelColor: krowCharcoal,
|
|
||||||
unselectedLabelColor: const Color(0xFF64748B),
|
|
||||||
labelStyle: const TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.all(2),
|
|
||||||
indicatorSize: TabBarIndicatorSize.tab,
|
|
||||||
labelPadding: const EdgeInsets.symmetric(horizontal: 8),
|
|
||||||
dividerColor: Colors.transparent,
|
|
||||||
tabs: const [Tab(text: 'Week'), Tab(text: 'Month')],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
...breakdown.map((item) => _buildBreakdownRow(item, krowCharcoal)),
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 8),
|
|
||||||
child: Divider(height: 1, color: Color(0xFFE2E8F0)),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Total',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'\$${breakdown.fold(0.0, (sum, item) => sum + (item['amount'] as double)).toStringAsFixed(2)}',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildBreakdownRow(Map<String, dynamic> item, Color krowCharcoal) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 6),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
item['category'],
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'${item['hours']} hours',
|
|
||||||
style: const TextStyle(fontSize: 10, color: Color(0xFF64748B)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'\$${(item['amount'] as double).toStringAsFixed(2)}',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildSavingsCard(
|
|
||||||
Color krowBlue,
|
|
||||||
Color krowYellow,
|
|
||||||
Color krowCharcoal,
|
|
||||||
) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: krowYellow.withOpacity(0.2),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
border: Border.all(color: krowYellow),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: krowYellow,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Icon(LucideIcons.trendingDown, size: 16, color: krowCharcoal),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Rate Optimization',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
const Text(
|
|
||||||
'Save \$180/month by switching 3 shifts',
|
|
||||||
style: TextStyle(fontSize: 12, color: Color(0xFF475569)),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
SizedBox(
|
|
||||||
height: 28,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: () {},
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: krowBlue,
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
elevation: 0,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'View Details',
|
|
||||||
style: TextStyle(fontSize: 10),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildInvoiceItem(Map<String, dynamic> invoice) {
|
|
||||||
const krowCharcoal = Color(0xFF121826);
|
|
||||||
const krowBlue = Color(0xFF0A39DF);
|
|
||||||
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFF1F5F9),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
LucideIcons.fileText,
|
|
||||||
size: 16,
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
invoice['id'],
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
invoice['date'], // Should format date
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'\$${(invoice['amount'] as double).toStringAsFixed(2)}',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: krowBlue.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
invoice['status'].toString().toUpperCase(),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: krowBlue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
const Icon(LucideIcons.download, size: 16, color: Color(0xFF94A3B8)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,967 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
|
|
||||||
class ClientCoverageScreen extends StatefulWidget {
|
|
||||||
const ClientCoverageScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ClientCoverageScreen> createState() => _ClientCoverageScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ClientCoverageScreenState extends State<ClientCoverageScreen> {
|
|
||||||
DateTime _selectedDate = DateTime.now();
|
|
||||||
late DateTime _today;
|
|
||||||
|
|
||||||
// Mock Data
|
|
||||||
final List<Map<String, dynamic>> _shifts = [
|
|
||||||
{
|
|
||||||
'id': '1',
|
|
||||||
'title': 'Banquet Server',
|
|
||||||
'location': 'Grand Ballroom',
|
|
||||||
'startTime': '16:00', // 4:00 PM
|
|
||||||
'workersNeeded': 10,
|
|
||||||
'date': DateTime.now().toIso8601String().split('T')[0],
|
|
||||||
'workers': [
|
|
||||||
{
|
|
||||||
'name': 'Sarah Wilson',
|
|
||||||
'status': 'confirmed',
|
|
||||||
'checkInTime': '15:55',
|
|
||||||
},
|
|
||||||
{'name': 'Mike Ross', 'status': 'confirmed', 'checkInTime': '16:00'},
|
|
||||||
{
|
|
||||||
'name': 'Jane Doe',
|
|
||||||
'status': 'confirmed',
|
|
||||||
'checkInTime': null,
|
|
||||||
}, // En route
|
|
||||||
{'name': 'John Smith', 'status': 'late', 'checkInTime': null},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'id': '2',
|
|
||||||
'title': 'Bartender',
|
|
||||||
'location': 'Lobby Bar',
|
|
||||||
'startTime': '17:00', // 5:00 PM
|
|
||||||
'workersNeeded': 4,
|
|
||||||
'date': DateTime.now().toIso8601String().split('T')[0],
|
|
||||||
'workers': [
|
|
||||||
{
|
|
||||||
'name': 'Emily Blunt',
|
|
||||||
'status': 'confirmed',
|
|
||||||
'checkInTime': '16:45',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'Chris Evans',
|
|
||||||
'status': 'confirmed',
|
|
||||||
'checkInTime': '16:50',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'Tom Holland',
|
|
||||||
'status': 'confirmed',
|
|
||||||
'checkInTime': null,
|
|
||||||
}, // En route
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_today = DateTime.now();
|
|
||||||
_today = DateTime(_today.year, _today.month, _today.day);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<DateTime> _getCalendarDays() {
|
|
||||||
final List<DateTime> days = [];
|
|
||||||
final startDate = _selectedDate.subtract(const Duration(days: 3));
|
|
||||||
for (int i = 0; i < 7; i++) {
|
|
||||||
days.add(startDate.add(Duration(days: i)));
|
|
||||||
}
|
|
||||||
return days;
|
|
||||||
}
|
|
||||||
|
|
||||||
String _formatTime(String? time) {
|
|
||||||
if (time == null) return '';
|
|
||||||
final parts = time.split(':');
|
|
||||||
final dt = DateTime(2022, 1, 1, int.parse(parts[0]), int.parse(parts[1]));
|
|
||||||
return DateFormat('h:mm a').format(dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// Process mock data for stats
|
|
||||||
final shiftsToday = _shifts; // In a real app, filter by date
|
|
||||||
final allApps = shiftsToday
|
|
||||||
.expand(
|
|
||||||
(s) => (s['workers'] as List).map((w) => {...w, 'shift_id': s['id']}),
|
|
||||||
)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
final totalNeeded = shiftsToday.fold(
|
|
||||||
0,
|
|
||||||
(sum, s) => sum + (s['workersNeeded'] as int),
|
|
||||||
);
|
|
||||||
final confirmedApps = allApps
|
|
||||||
.where((a) => a['status'] == 'confirmed')
|
|
||||||
.toList();
|
|
||||||
final totalConfirmed = confirmedApps.length;
|
|
||||||
final coveragePercent = totalNeeded > 0
|
|
||||||
? ((totalConfirmed / totalNeeded) * 100).round()
|
|
||||||
: 100;
|
|
||||||
|
|
||||||
final lateWorkers = allApps.where((a) => a['status'] == 'late').toList();
|
|
||||||
final onTimeWorkers = confirmedApps
|
|
||||||
.where((a) => a['checkInTime'] != null)
|
|
||||||
.toList();
|
|
||||||
final enRouteWorkers = confirmedApps
|
|
||||||
.where((a) => a['checkInTime'] == null)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
final calendarDays = _getCalendarDays();
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: const Color(0xFFF8FAFC), // slate-50
|
|
||||||
body: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
// Header
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 60,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
bottom: 24,
|
|
||||||
),
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: [
|
|
||||||
Color(0xFF2563EB), // blue-600
|
|
||||||
Color(0xFF0891B2), // cyan-600
|
|
||||||
],
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => context.go('/client-home'),
|
|
||||||
child: Container(
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withOpacity(0.2),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
LucideIcons.arrowLeft,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
const Text(
|
|
||||||
'Daily Coverage',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
width: 36,
|
|
||||||
height: 36,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.transparent,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
// refresh logic
|
|
||||||
});
|
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
LucideIcons.refreshCw,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
constraints: const BoxConstraints(),
|
|
||||||
style: IconButton.styleFrom(
|
|
||||||
hoverColor: Colors.white.withOpacity(0.2),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
// Calendar Selector
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
_NavButton(
|
|
||||||
text: '← Prev Week',
|
|
||||||
onTap: () {
|
|
||||||
setState(() {
|
|
||||||
_selectedDate = _selectedDate.subtract(
|
|
||||||
const Duration(days: 7),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_NavButton(
|
|
||||||
text: 'Today',
|
|
||||||
onTap: () {
|
|
||||||
setState(() {
|
|
||||||
final now = DateTime.now();
|
|
||||||
_selectedDate = DateTime(
|
|
||||||
now.year,
|
|
||||||
now.month,
|
|
||||||
now.day,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_NavButton(
|
|
||||||
text: 'Next Week →',
|
|
||||||
onTap: () {
|
|
||||||
setState(() {
|
|
||||||
_selectedDate = _selectedDate.add(
|
|
||||||
const Duration(days: 7),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: calendarDays.map((date) {
|
|
||||||
final isSelected =
|
|
||||||
date.year == _selectedDate.year &&
|
|
||||||
date.month == _selectedDate.month &&
|
|
||||||
date.day == _selectedDate.day;
|
|
||||||
final isToday =
|
|
||||||
date.year == _today.year &&
|
|
||||||
date.month == _today.month &&
|
|
||||||
date.day == _today.day;
|
|
||||||
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
setState(() {
|
|
||||||
_selectedDate = date;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
width: 44, // roughly grid-cols-7 spacing
|
|
||||||
height: 60,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: isSelected
|
|
||||||
? Colors.white
|
|
||||||
: Colors.white.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
border: isToday && !isSelected
|
|
||||||
? Border.all(color: Colors.white, width: 2)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
date.day.toString().padLeft(2, '0'),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: isSelected
|
|
||||||
? const Color(0xFF0A39DF)
|
|
||||||
: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
DateFormat('E').format(date),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 9,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: isSelected
|
|
||||||
? const Color(0xFF6A7382)
|
|
||||||
: Colors.white.withOpacity(0.7),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
|
|
||||||
// Compact Coverage Summary
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Coverage Status',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.white.withOpacity(0.7),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'$coveragePercent%',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Workers',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.white.withOpacity(0.7),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'$totalConfirmed/$totalNeeded',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Content Body
|
|
||||||
Transform.translate(
|
|
||||||
offset: const Offset(0, -16),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
// Quick Stats
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: _QuickStatCard(
|
|
||||||
value: '${onTimeWorkers.length}',
|
|
||||||
label: 'Checked In',
|
|
||||||
valueColor: const Color(0xFF059669), // emerald-600
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: _QuickStatCard(
|
|
||||||
value: '${enRouteWorkers.length}',
|
|
||||||
label: 'En Route',
|
|
||||||
valueColor: const Color(0xFFD97706), // amber-600
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: _QuickStatCard(
|
|
||||||
value: '${lateWorkers.length}',
|
|
||||||
label: 'Late',
|
|
||||||
valueColor: const Color(0xFFDC2626), // red-600
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
// Alerts
|
|
||||||
if (lateWorkers.isNotEmpty) ...[
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: const LinearGradient(
|
|
||||||
colors: [
|
|
||||||
Color(0xFFFEF2F2), // red-50
|
|
||||||
Color(0xFFFFF1F2), // rose-50
|
|
||||||
],
|
|
||||||
begin: Alignment.centerLeft,
|
|
||||||
end: Alignment.centerRight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
border: Border.all(
|
|
||||||
color: const Color(0xFFFEE2E2),
|
|
||||||
), // red-100
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.02),
|
|
||||||
blurRadius: 2,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
LucideIcons.alertTriangle,
|
|
||||||
color: Color(0xFFEF4444), // red-500
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'${lateWorkers.length} worker(s) running late',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Color(0xFFB91C1C), // red-700
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
const Text(
|
|
||||||
'Auto-backup system activated - finding replacements',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: Color(0xFFDC2626), // red-600
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
|
|
||||||
// Live Activity Header
|
|
||||||
const Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Live Activity',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF0F172A), // slate-900
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
|
|
||||||
// Shifts List
|
|
||||||
if (shiftsToday.isEmpty)
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(32),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.02),
|
|
||||||
blurRadius: 2,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: const Column(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
LucideIcons.users,
|
|
||||||
size: 48,
|
|
||||||
color: Color(0xFFCBD5E1),
|
|
||||||
), // slate-300
|
|
||||||
SizedBox(height: 12),
|
|
||||||
Text(
|
|
||||||
'No shifts scheduled for this day',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color(0xFF64748B), // slate-500
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
...shiftsToday.map((shift) {
|
|
||||||
final shiftApps = (shift['workers'] as List);
|
|
||||||
final workersNeeded = shift['workersNeeded'] as int;
|
|
||||||
final coverage = workersNeeded > 0
|
|
||||||
? ((shiftApps.length / workersNeeded) * 100).round()
|
|
||||||
: 100;
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.02),
|
|
||||||
blurRadius: 2,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
clipBehavior: Clip.antiAlias,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
// Shift Header
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: [
|
|
||||||
Color(0xFFEFF6FF), // blue-50
|
|
||||||
Color(0xFFECFEFF), // cyan-50
|
|
||||||
],
|
|
||||||
begin: Alignment.centerLeft,
|
|
||||||
end: Alignment.centerRight,
|
|
||||||
),
|
|
||||||
border: Border(
|
|
||||||
bottom: BorderSide(
|
|
||||||
color: Color(0xFFE2E8F0),
|
|
||||||
), // slate-200
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment:
|
|
||||||
CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 8,
|
|
||||||
height: 8,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Color(
|
|
||||||
0xFF3B82F6,
|
|
||||||
), // blue-500
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
shift['title'],
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(
|
|
||||||
0xFF0F172A,
|
|
||||||
), // slate-900
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
LucideIcons.mapPin,
|
|
||||||
size: 12,
|
|
||||||
color: Color(0xFF475569),
|
|
||||||
), // slate-600
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
shift['location'],
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Color(
|
|
||||||
0xFF475569,
|
|
||||||
), // slate-600
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
const Icon(
|
|
||||||
LucideIcons.clock,
|
|
||||||
size: 12,
|
|
||||||
color: Color(0xFF475569),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
_formatTime(
|
|
||||||
shift['startTime'],
|
|
||||||
),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF475569),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_CoverageBadge(
|
|
||||||
current: shiftApps.length,
|
|
||||||
total: workersNeeded,
|
|
||||||
coveragePercent: coverage,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Workers List
|
|
||||||
if (shiftApps.isNotEmpty)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
child: Column(
|
|
||||||
children: shiftApps.map<Widget>((worker) {
|
|
||||||
final isLast = worker == shiftApps.last;
|
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: isLast ? 0 : 8,
|
|
||||||
),
|
|
||||||
child: _WorkerRow(
|
|
||||||
name: worker['name'],
|
|
||||||
status: worker['status'],
|
|
||||||
checkInTime: worker['checkInTime'],
|
|
||||||
formatTime: _formatTime,
|
|
||||||
shiftStartTime: _formatTime(
|
|
||||||
shift['startTime'],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.all(16),
|
|
||||||
child: Text(
|
|
||||||
'No workers assigned yet',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color(0xFF64748B), // slate-500
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
|
|
||||||
const SizedBox(height: 80), // Bottom padding
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NavButton extends StatelessWidget {
|
|
||||||
final String text;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
|
|
||||||
const _NavButton({required this.text, required this.onTap});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withOpacity(0.2),
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
text,
|
|
||||||
style: const TextStyle(color: Colors.white, fontSize: 14),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _QuickStatCard extends StatelessWidget {
|
|
||||||
final String value;
|
|
||||||
final String label;
|
|
||||||
final Color valueColor;
|
|
||||||
|
|
||||||
const _QuickStatCard({
|
|
||||||
required this.value,
|
|
||||||
required this.label,
|
|
||||||
required this.valueColor,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(
|
|
||||||
12,
|
|
||||||
), // rounded-xl check? React uses default which is usually 0.5rem(8px) or 0.75rem(12px) for Card
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.06), // shadow-md
|
|
||||||
blurRadius: 4,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
value,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: valueColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF64748B), // slate-500
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CoverageBadge extends StatelessWidget {
|
|
||||||
final int current;
|
|
||||||
final int total;
|
|
||||||
final int coveragePercent;
|
|
||||||
|
|
||||||
const _CoverageBadge({
|
|
||||||
required this.current,
|
|
||||||
required this.total,
|
|
||||||
required this.coveragePercent,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Color bg;
|
|
||||||
Color text;
|
|
||||||
|
|
||||||
if (coveragePercent >= 100) {
|
|
||||||
bg = const Color(0xFF10B981); // emerald-500
|
|
||||||
text = Colors.white;
|
|
||||||
} else if (coveragePercent >= 80) {
|
|
||||||
bg = const Color(0xFFF59E0B); // amber-500
|
|
||||||
text = Colors.white;
|
|
||||||
} else {
|
|
||||||
bg = const Color(0xFFEF4444); // red-500
|
|
||||||
text = Colors.white;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: bg,
|
|
||||||
borderRadius: BorderRadius.circular(100), // rounded-full
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'$current/$total',
|
|
||||||
style: TextStyle(
|
|
||||||
color: text,
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _WorkerRow extends StatelessWidget {
|
|
||||||
final String name;
|
|
||||||
final String status;
|
|
||||||
final String? checkInTime;
|
|
||||||
final String Function(String?) formatTime;
|
|
||||||
final String shiftStartTime;
|
|
||||||
|
|
||||||
const _WorkerRow({
|
|
||||||
required this.name,
|
|
||||||
required this.status,
|
|
||||||
required this.checkInTime,
|
|
||||||
required this.formatTime,
|
|
||||||
required this.shiftStartTime,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Color bg;
|
|
||||||
Color border;
|
|
||||||
Color textBg;
|
|
||||||
Color textColor;
|
|
||||||
IconData icon;
|
|
||||||
String statusText;
|
|
||||||
Color badgeBg;
|
|
||||||
Color badgeText;
|
|
||||||
String badgeLabel;
|
|
||||||
|
|
||||||
if (status == 'confirmed' && checkInTime != null) {
|
|
||||||
// Checked In
|
|
||||||
bg = const Color(0xFFECFDF5); // emerald-50
|
|
||||||
border = const Color(0xFF10B981); // emerald-500
|
|
||||||
textBg = const Color(0xFFD1FAE5); // emerald-100
|
|
||||||
textColor = const Color(0xFF047857); // emerald-700
|
|
||||||
icon = LucideIcons.checkCircle;
|
|
||||||
statusText = '✓ Checked In at ${formatTime(checkInTime)}';
|
|
||||||
badgeBg = const Color(0xFF10B981); // emerald-500
|
|
||||||
badgeText = Colors.white;
|
|
||||||
badgeLabel = 'On Site';
|
|
||||||
} else if (status == 'confirmed' && checkInTime == null) {
|
|
||||||
// En Route
|
|
||||||
bg = const Color(0xFFFFFBEB); // amber-50
|
|
||||||
border = const Color(0xFFF59E0B); // amber-500
|
|
||||||
textBg = const Color(0xFFFEF3C7); // amber-100
|
|
||||||
textColor = const Color(0xFFB45309); // amber-700
|
|
||||||
icon = LucideIcons.clock;
|
|
||||||
statusText = 'En Route - Expected $shiftStartTime';
|
|
||||||
badgeBg = const Color(0xFFF59E0B); // amber-500
|
|
||||||
badgeText = Colors.white;
|
|
||||||
badgeLabel = 'En Route';
|
|
||||||
} else {
|
|
||||||
// Late
|
|
||||||
bg = const Color(0xFFFEF2F2); // red-50
|
|
||||||
border = const Color(0xFFEF4444); // red-500
|
|
||||||
textBg = const Color(0xFFFEE2E2); // red-100
|
|
||||||
textColor = const Color(0xFFB91C1C); // red-700
|
|
||||||
icon = LucideIcons.alertTriangle;
|
|
||||||
statusText = '⚠ Running Late';
|
|
||||||
badgeBg = const Color(0xFFEF4444); // red-500
|
|
||||||
badgeText = Colors.white;
|
|
||||||
badgeLabel = 'Late';
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: bg,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Stack(
|
|
||||||
clipBehavior: Clip.none,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(color: border, width: 2),
|
|
||||||
),
|
|
||||||
child: CircleAvatar(
|
|
||||||
backgroundColor: textBg,
|
|
||||||
child: Text(
|
|
||||||
name.isNotEmpty ? name[0] : 'W',
|
|
||||||
style: TextStyle(
|
|
||||||
color: textColor,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
bottom: -2,
|
|
||||||
right: -2,
|
|
||||||
child: Container(
|
|
||||||
width: 16,
|
|
||||||
height: 16,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: border,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: Icon(icon, size: 10, color: Colors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
name,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF0F172A), // slate-900
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
statusText,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: textColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: badgeBg,
|
|
||||||
borderRadius: BorderRadius.circular(100), // rounded-full
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
badgeLabel,
|
|
||||||
style: TextStyle(
|
|
||||||
color: badgeText,
|
|
||||||
fontSize: 10, // xs
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,753 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
|
|
||||||
class ClientHubsScreen extends StatefulWidget {
|
|
||||||
const ClientHubsScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ClientHubsScreen> createState() => _ClientHubsScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ClientHubsScreenState extends State<ClientHubsScreen> {
|
|
||||||
// Mock Data
|
|
||||||
final List<Map<String, dynamic>> _hubs = [];
|
|
||||||
bool _showAddHub = false;
|
|
||||||
bool _showIdentifyNFC = false;
|
|
||||||
Map<String, dynamic>? _selectedHub;
|
|
||||||
String? _nfcTagId;
|
|
||||||
|
|
||||||
final _nameController = TextEditingController();
|
|
||||||
final _locationNameController = TextEditingController();
|
|
||||||
final _addressController = TextEditingController();
|
|
||||||
|
|
||||||
void _addHub() {
|
|
||||||
if (_nameController.text.isEmpty) return;
|
|
||||||
setState(() {
|
|
||||||
_hubs.add({
|
|
||||||
'id': DateTime.now().millisecondsSinceEpoch.toString(),
|
|
||||||
'name': _nameController.text,
|
|
||||||
'locationName': _locationNameController.text,
|
|
||||||
'address': _addressController.text,
|
|
||||||
'nfcTagId': null,
|
|
||||||
});
|
|
||||||
_nameController.clear();
|
|
||||||
_locationNameController.clear();
|
|
||||||
_addressController.clear();
|
|
||||||
_showAddHub = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _deleteHub(String id) {
|
|
||||||
setState(() {
|
|
||||||
_hubs.removeWhere((hub) => hub['id'] == id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _simulateNFCScan() {
|
|
||||||
setState(() {
|
|
||||||
_nfcTagId =
|
|
||||||
'NFC-${DateTime.now().millisecondsSinceEpoch.toString().substring(8).toUpperCase()}';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _assignTag() {
|
|
||||||
if (_selectedHub != null && _nfcTagId != null) {
|
|
||||||
setState(() {
|
|
||||||
final index = _hubs.indexWhere((h) => h['id'] == _selectedHub!['id']);
|
|
||||||
if (index != -1) {
|
|
||||||
_hubs[index]['nfcTagId'] = _nfcTagId;
|
|
||||||
}
|
|
||||||
_showIdentifyNFC = false;
|
|
||||||
_nfcTagId = null;
|
|
||||||
_selectedHub = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: const Color(0xFFF8FAFC), // slate-50
|
|
||||||
body: Stack(
|
|
||||||
children: [
|
|
||||||
CustomScrollView(
|
|
||||||
slivers: [
|
|
||||||
SliverAppBar(
|
|
||||||
backgroundColor: const Color(0xFF121826),
|
|
||||||
automaticallyImplyLeading: false,
|
|
||||||
expandedHeight: 140,
|
|
||||||
pinned: true,
|
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
|
||||||
background: Container(
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: [Color(0xFF121826), Color(0xFF1E293B)],
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.fromLTRB(20, 48, 20, 0),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => context.pop(),
|
|
||||||
child: Container(
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withOpacity(0.2),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
LucideIcons.arrowLeft,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Hubs',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'Manage clock-in locations',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color(0xFFCBD5E1), // slate-300
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
ElevatedButton.icon(
|
|
||||||
onPressed: () =>
|
|
||||||
setState(() => _showAddHub = true),
|
|
||||||
icon: const Icon(
|
|
||||||
LucideIcons.plus,
|
|
||||||
size: 16,
|
|
||||||
color: Color(0xFF121826),
|
|
||||||
),
|
|
||||||
label: const Text(
|
|
||||||
'Add Hub',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color(0xFF121826),
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 13,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: const Color(0xFFFFED4A),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
elevation: 4,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16,
|
|
||||||
vertical: 8,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
SliverPadding(
|
|
||||||
padding: const EdgeInsets.fromLTRB(20, 20, 20, 100),
|
|
||||||
sliver: SliverList(
|
|
||||||
delegate: SliverChildListDelegate([
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
..._hubs.map((hub) => _buildHubCard(hub)),
|
|
||||||
|
|
||||||
if (_hubs.isEmpty) _buildEmptyState(),
|
|
||||||
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
_buildInfoCard(),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
if (_showAddHub) _buildAddHubDialog(),
|
|
||||||
|
|
||||||
if (_showIdentifyNFC) _buildIdentifyNFCDialog(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildHubCard(Map<String, dynamic> hub) {
|
|
||||||
final bool hasNfc = hub['nfcTagId'] != null;
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.04),
|
|
||||||
blurRadius: 10,
|
|
||||||
offset: const Offset(0, 4),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 52,
|
|
||||||
height: 52,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFEFF6FF), // blue-50
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
hasNfc ? LucideIcons.checkCircle : LucideIcons.nfc,
|
|
||||||
color: hasNfc
|
|
||||||
? const Color(0xFF16A34A)
|
|
||||||
: const Color(0xFF94A3B8), // green-600 or slate-400
|
|
||||||
size: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
hub['name'],
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 16,
|
|
||||||
color: Color(0xFF0F172A),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (hub['locationName'].isNotEmpty)
|
|
||||||
Text(
|
|
||||||
hub['locationName'],
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (hub['address'].isNotEmpty)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 4),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
LucideIcons.mapPin,
|
|
||||||
size: 12,
|
|
||||||
color: Color(0xFF94A3B8),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
hub['address'],
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (hasNfc)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 4),
|
|
||||||
child: Text(
|
|
||||||
'Tag: ${hub['nfcTagId']}',
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Color(0xFF16A34A),
|
|
||||||
fontSize: 12,
|
|
||||||
fontFamily: 'monospace',
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
_selectedHub = hub;
|
|
||||||
_showIdentifyNFC = true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
LucideIcons.nfc,
|
|
||||||
color: Color(0xFF2563EB),
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
constraints: const BoxConstraints(),
|
|
||||||
splashRadius: 20,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () => _deleteHub(hub['id']),
|
|
||||||
icon: const Icon(
|
|
||||||
LucideIcons.trash2,
|
|
||||||
color: Color(0xFFDC2626),
|
|
||||||
size: 20,
|
|
||||||
), // red-600
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
constraints: const BoxConstraints(),
|
|
||||||
splashRadius: 20,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildEmptyState() {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(32),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.04),
|
|
||||||
blurRadius: 10,
|
|
||||||
offset: const Offset(0, 4),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 64,
|
|
||||||
height: 64,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Color(0xFFF1F5F9), // slate-100
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
LucideIcons.nfc,
|
|
||||||
size: 32,
|
|
||||||
color: Color(0xFF94A3B8),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
const Text(
|
|
||||||
'No hubs yet',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF0F172A),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
const Text(
|
|
||||||
'Create clock-in stations for your locations',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: Color(0xFF64748B), fontSize: 14),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
ElevatedButton.icon(
|
|
||||||
onPressed: () => setState(() => _showAddHub = true),
|
|
||||||
icon: const Icon(
|
|
||||||
LucideIcons.plus,
|
|
||||||
size: 16,
|
|
||||||
color: Color(0xFF121826),
|
|
||||||
),
|
|
||||||
label: const Text(
|
|
||||||
'Add Your First Hub',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color(0xFF121826),
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: const Color(0xFFFFED4A),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
elevation: 4,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildInfoCard() {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFEFF6FF), // blue-50
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: const Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Icon(LucideIcons.nfc, size: 20, color: Color(0xFF2563EB)),
|
|
||||||
SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'About Hubs',
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 14,
|
|
||||||
color: Color(0xFF0F172A),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
'Hubs are clock-in stations at your locations. Assign NFC tags to each hub so workers can quickly clock in/out using their phones.',
|
|
||||||
style: TextStyle(
|
|
||||||
color: Color(0xFF334155),
|
|
||||||
fontSize: 12,
|
|
||||||
height: 1.4,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildAddHubDialog() {
|
|
||||||
return Container(
|
|
||||||
color: Colors.black.withOpacity(0.5),
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
width: MediaQuery.of(context).size.width * 0.9,
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(24),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(color: Colors.black.withOpacity(0.1), blurRadius: 20),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Add New Hub',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF0F172A),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
_buildFieldLabel('Hub Name *'),
|
|
||||||
TextField(
|
|
||||||
controller: _nameController,
|
|
||||||
decoration: _buildInputDecoration(
|
|
||||||
'e.g., Main Kitchen, Front Desk',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
_buildFieldLabel('Location Name'),
|
|
||||||
TextField(
|
|
||||||
controller: _locationNameController,
|
|
||||||
decoration: _buildInputDecoration('e.g., Downtown Restaurant'),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
_buildFieldLabel('Address'),
|
|
||||||
TextField(
|
|
||||||
controller: _addressController,
|
|
||||||
decoration: _buildInputDecoration('Full address'),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: () => setState(() => _showAddHub = false),
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
side: const BorderSide(color: Color(0xFFE2E8F0)),
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'Cancel',
|
|
||||||
style: TextStyle(color: Color(0xFF64748B)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: _addHub,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: const Color(0xFFFFED4A),
|
|
||||||
foregroundColor: const Color(0xFF121826),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'Create Hub',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildIdentifyNFCDialog() {
|
|
||||||
return Container(
|
|
||||||
color: Colors.black.withOpacity(0.5),
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
width: MediaQuery.of(context).size.width * 0.9,
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(24),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(color: Colors.black.withOpacity(0.1), blurRadius: 20),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Identify NFC Tag',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF0F172A),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
Container(
|
|
||||||
width: 80,
|
|
||||||
height: 80,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Color(0xFFEFF6FF), // blue-50
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
LucideIcons.nfc,
|
|
||||||
size: 40,
|
|
||||||
color: Color(0xFF2563EB),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
_selectedHub?['name'] ?? '',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 16,
|
|
||||||
color: Color(0xFF0F172A),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
const Text(
|
|
||||||
'Tap your phone to the NFC tag to identify it',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(color: Color(0xFF64748B), fontSize: 14),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
ElevatedButton.icon(
|
|
||||||
onPressed: _simulateNFCScan,
|
|
||||||
icon: const Icon(LucideIcons.nfc, size: 20),
|
|
||||||
label: const Text(
|
|
||||||
'Scan NFC Tag',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: const Color(0xFF0047FF),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 24,
|
|
||||||
vertical: 14,
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
elevation: 4,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (_nfcTagId != null) ...[
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFF0FDF4), // green-50
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
const Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
LucideIcons.checkCircle,
|
|
||||||
size: 20,
|
|
||||||
color: Color(0xFF16A34A),
|
|
||||||
),
|
|
||||||
SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
'Tag Identified',
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 14,
|
|
||||||
color: Color(0xFF0F172A),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12,
|
|
||||||
vertical: 8,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
border: Border.all(color: const Color(0xFFDCE8E0)),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
_nfcTagId!,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontFamily: 'monospace',
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF334155),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
const SizedBox(height: 32),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
_showIdentifyNFC = false;
|
|
||||||
_nfcTagId = null;
|
|
||||||
_selectedHub = null;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
side: const BorderSide(color: Color(0xFFE2E8F0)),
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'Cancel',
|
|
||||||
style: TextStyle(color: Color(0xFF64748B)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: _nfcTagId != null ? _assignTag : null,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: const Color(0xFFFFED4A),
|
|
||||||
foregroundColor: const Color(0xFF121826),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'Assign Tag',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildFieldLabel(String label) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 6),
|
|
||||||
child: Text(
|
|
||||||
label,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Color(0xFF0F172A),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
InputDecoration _buildInputDecoration(String hint) {
|
|
||||||
return InputDecoration(
|
|
||||||
hintText: hint,
|
|
||||||
hintStyle: const TextStyle(color: Color(0xFF94A3B8), fontSize: 14),
|
|
||||||
filled: true,
|
|
||||||
fillColor: const Color(0xFFF8FAFC),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
borderSide: const BorderSide(color: Color(0xFF2563EB), width: 2),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,544 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
|
|
||||||
class ClientReportsScreen extends StatefulWidget {
|
|
||||||
const ClientReportsScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ClientReportsScreen> createState() => _ClientReportsScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ClientReportsScreenState extends State<ClientReportsScreen>
|
|
||||||
with SingleTickerProviderStateMixin {
|
|
||||||
late TabController _tabController;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_tabController = TabController(length: 4, vsync: this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_tabController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: const Color(0xFFF8FAFC), // slate-50
|
|
||||||
body: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
// Header
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 60,
|
|
||||||
left: 20,
|
|
||||||
right: 20,
|
|
||||||
bottom: 32,
|
|
||||||
),
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: [
|
|
||||||
Color(0xFF0A39DF), // React primary
|
|
||||||
Color(0xFF0830B8), // React darker
|
|
||||||
],
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => context.go('/client-home'),
|
|
||||||
child: Container(
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withOpacity(0.2),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
LucideIcons.arrowLeft,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
const Text(
|
|
||||||
'Workforce Control Tower',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
// Tabs
|
|
||||||
Container(
|
|
||||||
height: 44,
|
|
||||||
padding: const EdgeInsets.all(4),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white.withOpacity(0.2),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: TabBar(
|
|
||||||
controller: _tabController,
|
|
||||||
indicator: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
labelColor: const Color(0xFF0A39DF),
|
|
||||||
unselectedLabelColor: Colors.white,
|
|
||||||
labelStyle: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
indicatorSize: TabBarIndicatorSize.tab,
|
|
||||||
dividerColor: Colors.transparent,
|
|
||||||
tabs: const [
|
|
||||||
Tab(text: 'Today'),
|
|
||||||
Tab(text: 'Week'),
|
|
||||||
Tab(text: 'Month'),
|
|
||||||
Tab(text: 'Quarter'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Content
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
// Key Metrics - 6 items as in React
|
|
||||||
GridView.count(
|
|
||||||
crossAxisCount: 2,
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
mainAxisSpacing: 12,
|
|
||||||
crossAxisSpacing: 12,
|
|
||||||
childAspectRatio: 1.2,
|
|
||||||
children: const [
|
|
||||||
_MetricCard(
|
|
||||||
icon: LucideIcons.clock,
|
|
||||||
label: 'Total Hrs',
|
|
||||||
value: '1,248',
|
|
||||||
badgeText: 'This period',
|
|
||||||
badgeColor: Color(0xFFEEF2FF), // indigo-50
|
|
||||||
badgeTextColor: Color(0xFF4338CA), // indigo-700
|
|
||||||
iconColor: Color(0xFF4F46E5), // indigo-600
|
|
||||||
),
|
|
||||||
_MetricCard(
|
|
||||||
icon: LucideIcons.trendingUp,
|
|
||||||
label: 'OT Hours',
|
|
||||||
value: '64',
|
|
||||||
badgeText: '5.1% of total',
|
|
||||||
badgeColor: Color(0xFFF1F5F9), // slate-100
|
|
||||||
badgeTextColor: Color(0xFF475569), // slate-600
|
|
||||||
iconColor: Color(0xFFD97706), // amber-600
|
|
||||||
),
|
|
||||||
_MetricCard(
|
|
||||||
icon: LucideIcons.dollarSign,
|
|
||||||
label: 'Total Spend',
|
|
||||||
value: '\$17.2k',
|
|
||||||
badgeText: '↓ 8% vs last week',
|
|
||||||
badgeColor: Color(0xFFD1FAE5), // emerald-100
|
|
||||||
badgeTextColor: Color(0xFF047857), // emerald-700
|
|
||||||
iconColor: Color(0xFF10B981), // emerald-600
|
|
||||||
),
|
|
||||||
_MetricCard(
|
|
||||||
icon: LucideIcons.trendingUp,
|
|
||||||
label: 'Fill Rate',
|
|
||||||
value: '96%',
|
|
||||||
badgeText: '↑ 2% improvement',
|
|
||||||
badgeColor: Color(0xFFDBEAFE), // blue-100
|
|
||||||
badgeTextColor: Color(0xFF1D4ED8), // blue-700
|
|
||||||
iconColor: Color(0xFF2563EB), // blue-600
|
|
||||||
),
|
|
||||||
_MetricCard(
|
|
||||||
icon: LucideIcons.clock,
|
|
||||||
label: 'Avg Fill Time',
|
|
||||||
value: '2.4 hrs',
|
|
||||||
badgeText: 'Industry best',
|
|
||||||
badgeColor: Color(0xFFDBEAFE), // blue-100
|
|
||||||
badgeTextColor: Color(0xFF1D4ED8), // blue-700
|
|
||||||
iconColor: Color(0xFF2563EB), // blue-600
|
|
||||||
),
|
|
||||||
_MetricCard(
|
|
||||||
icon: LucideIcons.alertTriangle,
|
|
||||||
label: 'No-Show Rate',
|
|
||||||
value: '2%',
|
|
||||||
badgeText: 'Below avg',
|
|
||||||
badgeColor: Color(0xFFD1FAE5), // emerald-100
|
|
||||||
badgeTextColor: Color(0xFF047857), // emerald-700
|
|
||||||
iconColor: Color(0xFFDC2626), // red-600
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
// Quick Reports
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Quick Reports',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF0F172A), // slate-900
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextButton.icon(
|
|
||||||
onPressed: () {},
|
|
||||||
icon: const Icon(LucideIcons.download, size: 16),
|
|
||||||
label: const Text('Export All'),
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
foregroundColor: const Color(0xFF2563EB), // blue-600
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
minimumSize: Size.zero,
|
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
GridView.count(
|
|
||||||
crossAxisCount: 2,
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
mainAxisSpacing: 12,
|
|
||||||
crossAxisSpacing: 12,
|
|
||||||
childAspectRatio: 1.3,
|
|
||||||
children: const [
|
|
||||||
_ReportCard(
|
|
||||||
icon: LucideIcons.calendar,
|
|
||||||
name: 'Daily Ops Report',
|
|
||||||
iconBgColor: Color(0xFFDBEAFE), // blue-100
|
|
||||||
iconColor: Color(0xFF2563EB), // blue-600
|
|
||||||
route: '/daily-ops-report',
|
|
||||||
),
|
|
||||||
_ReportCard(
|
|
||||||
icon: LucideIcons.dollarSign,
|
|
||||||
name: 'Spend Report',
|
|
||||||
iconBgColor: Color(0xFFD1FAE5), // emerald-100
|
|
||||||
iconColor: Color(0xFF059669), // emerald-600
|
|
||||||
route: '/spend-report',
|
|
||||||
),
|
|
||||||
_ReportCard(
|
|
||||||
icon: LucideIcons.users,
|
|
||||||
name: 'Coverage Report',
|
|
||||||
iconBgColor: Color(0xFFDBEAFE), // blue-100
|
|
||||||
iconColor: Color(0xFF2563EB), // blue-600
|
|
||||||
route: '/coverage-report-detail',
|
|
||||||
),
|
|
||||||
_ReportCard(
|
|
||||||
icon: LucideIcons.alertTriangle,
|
|
||||||
name: 'No-Show Report',
|
|
||||||
iconBgColor: Color(0xFFFEE2E2), // red-100
|
|
||||||
iconColor: Color(0xFFDC2626), // red-600
|
|
||||||
route: '/no-show-report',
|
|
||||||
),
|
|
||||||
_ReportCard(
|
|
||||||
icon: LucideIcons.trendingUp,
|
|
||||||
name: 'Forecast Report',
|
|
||||||
iconBgColor: Color(0xFFFEF3C7), // amber-100
|
|
||||||
iconColor: Color(0xFFD97706), // amber-600
|
|
||||||
route: '/forecast-report',
|
|
||||||
),
|
|
||||||
_ReportCard(
|
|
||||||
icon: LucideIcons.barChart3,
|
|
||||||
name: 'Performance Report',
|
|
||||||
iconBgColor: Color(0xFFDBEAFE), // blue-100
|
|
||||||
iconColor: Color(0xFF2563EB), // blue-600
|
|
||||||
route: '/performance-report',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
// AI Insights
|
|
||||||
Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFEBF5FF), // Light blue as in React
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.02),
|
|
||||||
blurRadius: 2,
|
|
||||||
offset: const Offset(0, 1),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'💡 AI Insights',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF0F172A), // slate-900
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
_InsightRow(
|
|
||||||
children: [
|
|
||||||
const TextSpan(text: 'You could save '),
|
|
||||||
const TextSpan(
|
|
||||||
text: '\$1,200/month',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
const TextSpan(
|
|
||||||
text: ' by booking workers 48hrs in advance',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
_InsightRow(
|
|
||||||
children: [
|
|
||||||
const TextSpan(text: 'Weekend demand is '),
|
|
||||||
const TextSpan(
|
|
||||||
text: '40% higher',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
const TextSpan(
|
|
||||||
text: ' - consider scheduling earlier',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
_InsightRow(
|
|
||||||
children: [
|
|
||||||
const TextSpan(
|
|
||||||
text: 'Your top 5 workers complete ',
|
|
||||||
),
|
|
||||||
const TextSpan(
|
|
||||||
text: '95% of shifts',
|
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
const TextSpan(text: ' - mark them as preferred'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 100), // pb-24
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MetricCard extends StatelessWidget {
|
|
||||||
final IconData icon;
|
|
||||||
final String label;
|
|
||||||
final String value;
|
|
||||||
final String badgeText;
|
|
||||||
final Color badgeColor;
|
|
||||||
final Color badgeTextColor;
|
|
||||||
final Color iconColor;
|
|
||||||
|
|
||||||
const _MetricCard({
|
|
||||||
required this.icon,
|
|
||||||
required this.label,
|
|
||||||
required this.value,
|
|
||||||
required this.badgeText,
|
|
||||||
required this.badgeColor,
|
|
||||||
required this.badgeTextColor,
|
|
||||||
required this.iconColor,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.black.withOpacity(0.06), // shadow-md
|
|
||||||
blurRadius: 4,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Icon(icon, size: 16, color: iconColor),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
label,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF64748B), // slate-500
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
value,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF0F172A), // slate-900
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: badgeColor,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
badgeText,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: badgeTextColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ReportCard extends StatelessWidget {
|
|
||||||
final IconData icon;
|
|
||||||
final String name;
|
|
||||||
final Color iconBgColor;
|
|
||||||
final Color iconColor;
|
|
||||||
final String route;
|
|
||||||
|
|
||||||
const _ReportCard({
|
|
||||||
required this.icon,
|
|
||||||
required this.name,
|
|
||||||
required this.iconBgColor,
|
|
||||||
required this.iconColor,
|
|
||||||
required this.route,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () => context.push(route),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(color: Colors.black.withOpacity(0.02), blurRadius: 2),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: iconBgColor,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
child: Icon(icon, size: 20, color: iconColor),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
name,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF0F172A),
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
const Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
LucideIcons.download,
|
|
||||||
size: 12,
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
SizedBox(width: 4),
|
|
||||||
Text(
|
|
||||||
'2-click export',
|
|
||||||
style: TextStyle(fontSize: 12, color: Color(0xFF64748B)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _InsightRow extends StatelessWidget {
|
|
||||||
final List<InlineSpan> children;
|
|
||||||
|
|
||||||
const _InsightRow({required this.children});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'• ',
|
|
||||||
style: TextStyle(color: Color(0xFF334155), fontSize: 14),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text.rich(
|
|
||||||
TextSpan(
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: Color(0xFF334155),
|
|
||||||
height: 1.4,
|
|
||||||
),
|
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||