Merge pull request #548 from Oloodi/538-be-assign-a-hub-manager-to-an-order
UI changes in the client hub details page
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<application
|
<application
|
||||||
android:label="Krow With Us Client"
|
android:label="KROW With Us Client"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/launcher_icon">
|
android:icon="@mipmap/launcher_icon">
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>Krow With Us Client</string>
|
<string>KROW With Us Client</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>Krow With Us Client</string>
|
<string>KROW With Us Client</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
|
|||||||
@@ -33,7 +33,11 @@ void main() async {
|
|||||||
|
|
||||||
// Initialize session listener for Firebase Auth state changes
|
// Initialize session listener for Firebase Auth state changes
|
||||||
DataConnectService.instance.initializeAuthListener(
|
DataConnectService.instance.initializeAuthListener(
|
||||||
allowedRoles: <String>['CLIENT', 'BUSINESS', 'BOTH'], // Only allow users with CLIENT, BUSINESS, or BOTH roles
|
allowedRoles: <String>[
|
||||||
|
'CLIENT',
|
||||||
|
'BUSINESS',
|
||||||
|
'BOTH',
|
||||||
|
], // Only allow users with CLIENT, BUSINESS, or BOTH roles
|
||||||
);
|
);
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
@@ -52,7 +56,10 @@ class AppModule extends Module {
|
|||||||
@override
|
@override
|
||||||
void routes(RouteManager r) {
|
void routes(RouteManager r) {
|
||||||
// Initial route points to the client authentication flow
|
// Initial route points to the client authentication flow
|
||||||
r.module(ClientPaths.root, module: client_authentication.ClientAuthenticationModule());
|
r.module(
|
||||||
|
ClientPaths.root,
|
||||||
|
module: client_authentication.ClientAuthenticationModule(),
|
||||||
|
);
|
||||||
|
|
||||||
// Client main shell with bottom navigation (includes home as a child)
|
// Client main shell with bottom navigation (includes home as a child)
|
||||||
r.module(ClientPaths.main, module: client_main.ClientMainModule());
|
r.module(ClientPaths.main, module: client_main.ClientMainModule());
|
||||||
@@ -95,7 +102,7 @@ class AppWidget extends StatelessWidget {
|
|||||||
return core_localization.TranslationProvider(
|
return core_localization.TranslationProvider(
|
||||||
child: MaterialApp.router(
|
child: MaterialApp.router(
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
title: "Krow Client",
|
title: "KROW Client",
|
||||||
theme: UiTheme.light,
|
theme: UiTheme.light,
|
||||||
routerConfig: Modular.routerConfig,
|
routerConfig: Modular.routerConfig,
|
||||||
locale: state.locale,
|
locale: state.locale,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: krowwithus_client
|
name: krowwithus_client
|
||||||
description: "Krow Client Application"
|
description: "KROW Client Application"
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
version: 0.0.1-IlianaClientM3
|
version: 0.0.1-IlianaClientM3
|
||||||
resolution: workspace
|
resolution: workspace
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>Krow Design System Viewer</string>
|
<string>KROW Design System Viewer</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<application
|
<application
|
||||||
android:label="Krow With Us Staff"
|
android:label="KROW With Us Staff"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/launcher_icon">
|
android:icon="@mipmap/launcher_icon">
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>Krow With Us Staff</string>
|
<string>KROW With Us Staff</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>Krow With Us Staff</string>
|
<string>KROW With Us Staff</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: krowwithus_staff
|
name: krowwithus_staff
|
||||||
description: "Krow Staff Application"
|
description: "KROW Staff Application"
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 0.0.1-IlianaStaffM3
|
version: 0.0.1-IlianaStaffM3
|
||||||
resolution: workspace
|
resolution: workspace
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ extension StaffNavigator on IModularNavigator {
|
|||||||
// ADDITIONAL FEATURES
|
// ADDITIONAL FEATURES
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
/// Pushes the Krow University page (placeholder).
|
/// Pushes the KROW University page (placeholder).
|
||||||
///
|
///
|
||||||
/// Access training materials and educational courses.
|
/// Access training materials and educational courses.
|
||||||
void toKrowUniversity() {
|
void toKrowUniversity() {
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ class StaffPaths {
|
|||||||
// ADDITIONAL FEATURES (Placeholders)
|
// ADDITIONAL FEATURES (Placeholders)
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
/// Krow University - training and education (placeholder).
|
/// KROW University - training and education (placeholder).
|
||||||
///
|
///
|
||||||
/// Access to training materials and courses.
|
/// Access to training materials and courses.
|
||||||
static const String krowUniversity = '/krow-university';
|
static const String krowUniversity = '/krow-university';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:krow_core/src/services/api_service/inspectors/auth_interceptor.dart';
|
import 'package:krow_core/src/services/api_service/inspectors/auth_interceptor.dart';
|
||||||
|
|
||||||
/// A custom Dio client for the Krow project that includes basic configuration
|
/// A custom Dio client for the KROW project that includes basic configuration
|
||||||
/// and an [AuthInterceptor].
|
/// and an [AuthInterceptor].
|
||||||
class DioClient extends DioMixin implements Dio {
|
class DioClient extends DioMixin implements Dio {
|
||||||
DioClient([BaseOptions? baseOptions]) {
|
DioClient([BaseOptions? baseOptions]) {
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
},
|
},
|
||||||
"sign_up_page": {
|
"sign_up_page": {
|
||||||
"title": "Create Account",
|
"title": "Create Account",
|
||||||
"subtitle": "Get started with Krow for your business",
|
"subtitle": "Get started with KROW for your business",
|
||||||
"company_label": "Company Name",
|
"company_label": "Company Name",
|
||||||
"company_hint": "Enter company name",
|
"company_hint": "Enter company name",
|
||||||
"email_label": "Email",
|
"email_label": "Email",
|
||||||
@@ -544,7 +544,7 @@
|
|||||||
"home": {
|
"home": {
|
||||||
"header": {
|
"header": {
|
||||||
"welcome_back": "Welcome back",
|
"welcome_back": "Welcome back",
|
||||||
"user_name_placeholder": "Krower"
|
"user_name_placeholder": "KROWER"
|
||||||
},
|
},
|
||||||
"banners": {
|
"banners": {
|
||||||
"complete_profile_title": "Complete Your Profile",
|
"complete_profile_title": "Complete Your Profile",
|
||||||
@@ -628,17 +628,17 @@
|
|||||||
"page": "/krow-university"
|
"page": "/krow-university"
|
||||||
},
|
},
|
||||||
"podcast": {
|
"podcast": {
|
||||||
"title": "Krow Podcast",
|
"title": "KROW Podcast",
|
||||||
"description": "Listen to tips from top workers.",
|
"description": "Listen to tips from top workers.",
|
||||||
"page": "/krow-university"
|
"page": "/krow-university"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"more_ways": {
|
"more_ways": {
|
||||||
"title": "More Ways To Use Krow",
|
"title": "More Ways To Use KROW",
|
||||||
"items": {
|
"items": {
|
||||||
"benefits": {
|
"benefits": {
|
||||||
"title": "Krow Benefits",
|
"title": "KROW Benefits",
|
||||||
"page": "/benefits"
|
"page": "/benefits"
|
||||||
},
|
},
|
||||||
"refer": {
|
"refer": {
|
||||||
@@ -684,7 +684,7 @@
|
|||||||
"documents": "Documents",
|
"documents": "Documents",
|
||||||
"certificates": "Certificates",
|
"certificates": "Certificates",
|
||||||
"tax_forms": "Tax Forms",
|
"tax_forms": "Tax Forms",
|
||||||
"krow_university": "Krow University",
|
"krow_university": "KROW University",
|
||||||
"trainings": "Trainings",
|
"trainings": "Trainings",
|
||||||
"leaderboard": "Leaderboard",
|
"leaderboard": "Leaderboard",
|
||||||
"bank_account": "Bank Account",
|
"bank_account": "Bank Account",
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
},
|
},
|
||||||
"sign_up_page": {
|
"sign_up_page": {
|
||||||
"title": "Crear cuenta",
|
"title": "Crear cuenta",
|
||||||
"subtitle": "Comienza con Krow para tu negocio",
|
"subtitle": "Comienza con KROW para tu negocio",
|
||||||
"company_label": "Nombre de la empresa",
|
"company_label": "Nombre de la empresa",
|
||||||
"company_hint": "Ingresa el nombre de la empresa",
|
"company_hint": "Ingresa el nombre de la empresa",
|
||||||
"email_label": "Correo electr\u00f3nico",
|
"email_label": "Correo electr\u00f3nico",
|
||||||
@@ -544,7 +544,7 @@
|
|||||||
"home": {
|
"home": {
|
||||||
"header": {
|
"header": {
|
||||||
"welcome_back": "Bienvenido de nuevo",
|
"welcome_back": "Bienvenido de nuevo",
|
||||||
"user_name_placeholder": "Krower"
|
"user_name_placeholder": "KROWER"
|
||||||
},
|
},
|
||||||
"banners": {
|
"banners": {
|
||||||
"complete_profile_title": "Completa tu Perfil",
|
"complete_profile_title": "Completa tu Perfil",
|
||||||
@@ -628,17 +628,17 @@
|
|||||||
"page": "/krow-university"
|
"page": "/krow-university"
|
||||||
},
|
},
|
||||||
"podcast": {
|
"podcast": {
|
||||||
"title": "Podcast de Krow",
|
"title": "Podcast de KROW",
|
||||||
"description": "Escucha consejos de los mejores trabajadores.",
|
"description": "Escucha consejos de los mejores trabajadores.",
|
||||||
"page": "/krow-university"
|
"page": "/krow-university"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"more_ways": {
|
"more_ways": {
|
||||||
"title": "M\u00e1s Formas de Usar Krow",
|
"title": "M\u00e1s Formas de Usar KROW",
|
||||||
"items": {
|
"items": {
|
||||||
"benefits": {
|
"benefits": {
|
||||||
"title": "Beneficios de Krow",
|
"title": "Beneficios de KROW",
|
||||||
"page": "/benefits"
|
"page": "/benefits"
|
||||||
},
|
},
|
||||||
"refer": {
|
"refer": {
|
||||||
@@ -684,7 +684,7 @@
|
|||||||
"documents": "Documentos",
|
"documents": "Documentos",
|
||||||
"certificates": "Certificados",
|
"certificates": "Certificados",
|
||||||
"tax_forms": "Formularios Fiscales",
|
"tax_forms": "Formularios Fiscales",
|
||||||
"krow_university": "Krow University",
|
"krow_university": "KROW University",
|
||||||
"trainings": "Capacitaciones",
|
"trainings": "Capacitaciones",
|
||||||
"leaderboard": "Tabla de Clasificaci\u00f3n",
|
"leaderboard": "Tabla de Clasificaci\u00f3n",
|
||||||
"bank_account": "Cuenta Bancaria",
|
"bank_account": "Cuenta Bancaria",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// A custom AppBar for the Krow UI design system.
|
/// A custom AppBar for the KROW UI design system.
|
||||||
///
|
///
|
||||||
/// This widget provides a consistent look and feel for top app bars across the application.
|
/// This widget provides a consistent look and feel for top app bars across the application.
|
||||||
class UiAppBar extends StatelessWidget implements PreferredSizeWidget {
|
class UiAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ import '../ui_typography.dart';
|
|||||||
import '../ui_constants.dart';
|
import '../ui_constants.dart';
|
||||||
import '../ui_colors.dart';
|
import '../ui_colors.dart';
|
||||||
|
|
||||||
/// A custom TextField for the Krow UI design system.
|
/// A custom TextField for the KROW UI design system.
|
||||||
///
|
///
|
||||||
/// This widget combines a label and a [TextField] with consistent styling.
|
/// This widget combines a label and a [TextField] with consistent styling.
|
||||||
class UiTextField extends StatelessWidget {
|
class UiTextField extends StatelessWidget {
|
||||||
|
|
||||||
const UiTextField({
|
const UiTextField({
|
||||||
super.key,
|
super.key,
|
||||||
this.semanticsIdentifier,
|
this.semanticsIdentifier,
|
||||||
@@ -30,8 +29,10 @@ class UiTextField extends StatelessWidget {
|
|||||||
this.onTap,
|
this.onTap,
|
||||||
this.validator,
|
this.validator,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Optional semantics identifier for E2E testing (e.g. Maestro).
|
/// Optional semantics identifier for E2E testing (e.g. Maestro).
|
||||||
final String? semanticsIdentifier;
|
final String? semanticsIdentifier;
|
||||||
|
|
||||||
/// The label text to display above the text field.
|
/// The label text to display above the text field.
|
||||||
final String? label;
|
final String? label;
|
||||||
|
|
||||||
@@ -120,10 +121,7 @@ class UiTextField extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (semanticsIdentifier != null) {
|
if (semanticsIdentifier != null) {
|
||||||
return Semantics(
|
return Semantics(identifier: semanticsIdentifier!, child: field);
|
||||||
identifier: semanticsIdentifier!,
|
|
||||||
child: field,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return field;
|
return field;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import '../../domain/repositories/auth_repository_interface.dart';
|
|||||||
/// Production-ready implementation of the [AuthRepositoryInterface] for the client app.
|
/// Production-ready implementation of the [AuthRepositoryInterface] for the client app.
|
||||||
///
|
///
|
||||||
/// This implementation integrates with Firebase Authentication for user
|
/// This implementation integrates with Firebase Authentication for user
|
||||||
/// identity management and Krow's Data Connect SDK for storing user profile data.
|
/// identity management and KROW's Data Connect SDK for storing user profile data.
|
||||||
class AuthRepositoryImpl implements AuthRepositoryInterface {
|
class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||||
/// Creates an [AuthRepositoryImpl] with the real dependencies.
|
/// Creates an [AuthRepositoryImpl] with the real dependencies.
|
||||||
AuthRepositoryImpl({dc.DataConnectService? service})
|
AuthRepositoryImpl({dc.DataConnectService? service})
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import 'presentation/pages/pending_invoices_page.dart';
|
|||||||
class BillingModule extends Module {
|
class BillingModule extends Module {
|
||||||
@override
|
@override
|
||||||
void binds(Injector i) {
|
void binds(Injector i) {
|
||||||
|
|
||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
i.addSingleton<BillingRepository>(BillingRepositoryImpl.new);
|
i.addSingleton<BillingRepository>(BillingRepositoryImpl.new);
|
||||||
|
|
||||||
@@ -54,9 +52,22 @@ class BillingModule extends Module {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void routes(RouteManager r) {
|
void routes(RouteManager r) {
|
||||||
r.child(ClientPaths.childRoute(ClientPaths.billing, ClientPaths.billing), child: (_) => const BillingPage());
|
r.child(
|
||||||
r.child('/completion-review', child: (_) => ShiftCompletionReviewPage(invoice: r.args.data as BillingInvoice?));
|
ClientPaths.childRoute(ClientPaths.billing, ClientPaths.billing),
|
||||||
r.child('/invoice-ready', child: (_) => const InvoiceReadyPage());
|
child: (_) => const BillingPage(),
|
||||||
r.child('/awaiting-approval', child: (_) => const PendingInvoicesPage());
|
);
|
||||||
|
r.child(
|
||||||
|
ClientPaths.childRoute(ClientPaths.billing, ClientPaths.completionReview),
|
||||||
|
child: (_) =>
|
||||||
|
ShiftCompletionReviewPage(invoice: r.args.data as BillingInvoice?),
|
||||||
|
);
|
||||||
|
r.child(
|
||||||
|
ClientPaths.childRoute(ClientPaths.billing, ClientPaths.invoiceReady),
|
||||||
|
child: (_) => const InvoiceReadyPage(),
|
||||||
|
);
|
||||||
|
r.child(
|
||||||
|
ClientPaths.childRoute(ClientPaths.billing, ClientPaths.awaitingApproval),
|
||||||
|
child: (_) => const PendingInvoicesPage(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:design_system/design_system.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:krow_core/core.dart';
|
|
||||||
import '../blocs/billing_bloc.dart';
|
import '../blocs/billing_bloc.dart';
|
||||||
import '../blocs/billing_state.dart';
|
import '../blocs/billing_state.dart';
|
||||||
import '../widgets/pending_invoices_section.dart';
|
import '../widgets/pending_invoices_section.dart';
|
||||||
@@ -13,96 +13,62 @@ class PendingInvoicesPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return BlocBuilder<BillingBloc, BillingState>(
|
||||||
backgroundColor: const Color(0xFFF8FAFC),
|
|
||||||
body: BlocBuilder<BillingBloc, BillingState>(
|
|
||||||
bloc: Modular.get<BillingBloc>(),
|
bloc: Modular.get<BillingBloc>(),
|
||||||
builder: (context, state) {
|
builder: (BuildContext context, BillingState state) {
|
||||||
return CustomScrollView(
|
return Scaffold(
|
||||||
slivers: [
|
appBar: UiAppBar(
|
||||||
_buildHeader(context, state.pendingInvoices.length),
|
title: t.client_billing.awaiting_approval,
|
||||||
if (state.status == BillingStatus.loading)
|
showBackButton: true,
|
||||||
const SliverFillRemaining(
|
),
|
||||||
child: Center(child: CircularProgressIndicator()),
|
body: _buildBody(context, state),
|
||||||
)
|
);
|
||||||
else if (state.pendingInvoices.isEmpty)
|
},
|
||||||
_buildEmptyState()
|
);
|
||||||
else
|
}
|
||||||
SliverPadding(
|
|
||||||
|
Widget _buildBody(BuildContext context, BillingState state) {
|
||||||
|
if (state.status == BillingStatus.loading) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.pendingInvoices.isEmpty) {
|
||||||
|
return _buildEmptyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
padding: const EdgeInsets.fromLTRB(
|
padding: const EdgeInsets.fromLTRB(
|
||||||
UiConstants.space5,
|
UiConstants.space5,
|
||||||
UiConstants.space5,
|
UiConstants.space5,
|
||||||
UiConstants.space5,
|
UiConstants.space5,
|
||||||
100, // Bottom padding for scroll clearance
|
100, // Bottom padding for scroll clearance
|
||||||
),
|
),
|
||||||
sliver: SliverList(
|
itemCount: state.pendingInvoices.length,
|
||||||
delegate: SliverChildBuilderDelegate(
|
itemBuilder: (BuildContext context, int index) {
|
||||||
(context, index) {
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: UiConstants.space4),
|
padding: const EdgeInsets.only(bottom: UiConstants.space4),
|
||||||
child: PendingInvoiceCard(invoice: state.pendingInvoices[index]),
|
child: PendingInvoiceCard(invoice: state.pendingInvoices[index]),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
childCount: state.pendingInvoices.length,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildHeader(BuildContext context, int count) {
|
|
||||||
return SliverAppBar(
|
|
||||||
pinned: true,
|
|
||||||
expandedHeight: 140.0,
|
|
||||||
backgroundColor: UiColors.primary,
|
|
||||||
elevation: 0,
|
|
||||||
leadingWidth: 72,
|
|
||||||
leading: Center(
|
|
||||||
child: UiIconButton(
|
|
||||||
icon: UiIcons.arrowLeft,
|
|
||||||
backgroundColor: UiColors.white.withOpacity(0.15),
|
|
||||||
iconColor: UiColors.white,
|
|
||||||
useBlur: true,
|
|
||||||
size: 40,
|
|
||||||
onTap: () => Navigator.of(context).pop(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
flexibleSpace: FlexibleSpaceBar(
|
|
||||||
centerTitle: true,
|
|
||||||
title: Text(
|
|
||||||
t.client_billing.awaiting_approval,
|
|
||||||
style: UiTypography.headline4b.copyWith(color: UiColors.white),
|
|
||||||
),
|
|
||||||
background: Center(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 40),
|
|
||||||
child: Opacity(
|
|
||||||
opacity: 0.1,
|
|
||||||
child: Icon(UiIcons.clock, size: 100, color: UiColors.white),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEmptyState() {
|
Widget _buildEmptyState() {
|
||||||
return SliverFillRemaining(
|
return Center(
|
||||||
child: Center(
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(UiConstants.space6),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
decoration: BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(UiIcons.checkCircle, size: 48, color: UiColors.success),
|
child: const Icon(
|
||||||
|
UiIcons.checkCircle,
|
||||||
|
size: 48,
|
||||||
|
color: UiColors.success,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space4),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Text(
|
Text(
|
||||||
@@ -115,7 +81,6 @@ class PendingInvoicesPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:design_system/design_system.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:krow_core/core.dart';
|
import 'package:krow_core/core.dart';
|
||||||
|
|
||||||
import '../models/billing_invoice_model.dart';
|
import '../models/billing_invoice_model.dart';
|
||||||
|
|
||||||
/// Section showing a banner for invoices awaiting approval.
|
/// Section showing a banner for invoices awaiting approval.
|
||||||
@@ -56,7 +57,10 @@ class PendingInvoicesSection extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: UiConstants.space2),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8,
|
||||||
|
vertical: 2,
|
||||||
|
),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: UiColors.accent,
|
color: UiColors.accent,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
@@ -104,14 +108,7 @@ class PendingInvoiceCard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border.withOpacity(0.5)),
|
border: Border.all(color: UiColors.border, width: 0.5),
|
||||||
boxShadow: <BoxShadow>[
|
|
||||||
BoxShadow(
|
|
||||||
color: UiColors.black.withValues(alpha: 0.04),
|
|
||||||
blurRadius: 12,
|
|
||||||
offset: const Offset(0, 4),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(UiConstants.space5),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
@@ -187,7 +184,11 @@ class PendingInvoiceCard extends StatelessWidget {
|
|||||||
t.client_billing.stats.total,
|
t.client_billing.stats.total,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 1, height: 32, color: UiColors.border.withOpacity(0.3)),
|
Container(
|
||||||
|
width: 1,
|
||||||
|
height: 32,
|
||||||
|
color: UiColors.border.withOpacity(0.3),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildStatItem(
|
child: _buildStatItem(
|
||||||
UiIcons.users,
|
UiIcons.users,
|
||||||
@@ -195,11 +196,15 @@ class PendingInvoiceCard extends StatelessWidget {
|
|||||||
t.client_billing.stats.workers,
|
t.client_billing.stats.workers,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(width: 1, height: 32, color: UiColors.border.withOpacity(0.3)),
|
Container(
|
||||||
|
width: 1,
|
||||||
|
height: 32,
|
||||||
|
color: UiColors.border.withOpacity(0.3),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildStatItem(
|
child: _buildStatItem(
|
||||||
UiIcons.clock,
|
UiIcons.clock,
|
||||||
'${invoice.totalHours.toStringAsFixed(1)}',
|
invoice.totalHours.toStringAsFixed(1),
|
||||||
t.client_billing.stats.hrs,
|
t.client_billing.stats.hrs,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -210,14 +215,12 @@ class PendingInvoiceCard extends StatelessWidget {
|
|||||||
const SizedBox(height: UiConstants.space5),
|
const SizedBox(height: UiConstants.space5),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: UiButton.primary(
|
child: UiButton.secondary(
|
||||||
text: t.client_billing.review_and_approve,
|
text: t.client_billing.review_and_approve,
|
||||||
leadingIcon: UiIcons.checkCircle,
|
leadingIcon: UiIcons.checkCircle,
|
||||||
onPressed: () => Modular.to.toCompletionReview(arguments: invoice),
|
onPressed: () =>
|
||||||
|
Modular.to.toCompletionReview(arguments: invoice),
|
||||||
size: UiButtonSize.large,
|
size: UiButtonSize.large,
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
textStyle: UiTypography.body1b.copyWith(fontSize: 16),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class _ActionCard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: color,
|
color: color,
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: borderColor),
|
border: Border.all(color: borderColor, width: 0.5),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
/// A dashboard widget that displays today's coverage status.
|
/// A dashboard widget that displays today's coverage status.
|
||||||
class CoverageDashboard extends StatelessWidget {
|
class CoverageDashboard extends StatelessWidget {
|
||||||
|
|
||||||
/// Creates a [CoverageDashboard].
|
/// Creates a [CoverageDashboard].
|
||||||
const CoverageDashboard({
|
const CoverageDashboard({
|
||||||
super.key,
|
super.key,
|
||||||
required this.shifts,
|
required this.shifts,
|
||||||
required this.applications,
|
required this.applications,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The list of shifts for today.
|
/// The list of shifts for today.
|
||||||
final List<dynamic> shifts;
|
final List<dynamic> shifts;
|
||||||
|
|
||||||
@@ -23,7 +23,8 @@ class CoverageDashboard extends StatelessWidget {
|
|||||||
double todayCost = 0;
|
double todayCost = 0;
|
||||||
|
|
||||||
for (final dynamic s in shifts) {
|
for (final dynamic s in shifts) {
|
||||||
final int needed = (s as Map<String, dynamic>)['workersNeeded'] as int? ?? 0;
|
final int needed =
|
||||||
|
(s as Map<String, dynamic>)['workersNeeded'] as int? ?? 0;
|
||||||
final int confirmed = s['filled'] as int? ?? 0;
|
final int confirmed = s['filled'] as int? ?? 0;
|
||||||
final double rate = s['hourlyRate'] as double? ?? 0.0;
|
final double rate = s['hourlyRate'] as double? ?? 0.0;
|
||||||
final double hours = s['hours'] as double? ?? 0.0;
|
final double hours = s['hours'] as double? ?? 0.0;
|
||||||
@@ -39,7 +40,9 @@ class CoverageDashboard extends StatelessWidget {
|
|||||||
final int unfilledPositions = totalNeeded - totalConfirmed;
|
final int unfilledPositions = totalNeeded - totalConfirmed;
|
||||||
|
|
||||||
final int checkedInCount = applications
|
final int checkedInCount = applications
|
||||||
.where((dynamic a) => (a as Map<String, dynamic>)['checkInTime'] != null)
|
.where(
|
||||||
|
(dynamic a) => (a as Map<String, dynamic>)['checkInTime'] != null,
|
||||||
|
)
|
||||||
.length;
|
.length;
|
||||||
final int lateWorkersCount = applications
|
final int lateWorkersCount = applications
|
||||||
.where((dynamic a) => (a as Map<String, dynamic>)['status'] == 'LATE')
|
.where((dynamic a) => (a as Map<String, dynamic>)['status'] == 'LATE')
|
||||||
@@ -58,7 +61,7 @@ class CoverageDashboard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border, width: 0.5),
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withValues(alpha: 0.02),
|
color: UiColors.black.withValues(alpha: 0.02),
|
||||||
@@ -145,7 +148,6 @@ class CoverageDashboard extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _StatusCard extends StatelessWidget {
|
class _StatusCard extends StatelessWidget {
|
||||||
|
|
||||||
const _StatusCard({
|
const _StatusCard({
|
||||||
required this.label,
|
required this.label,
|
||||||
required this.value,
|
required this.value,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
/// A widget that displays the daily coverage metrics.
|
/// A widget that displays the daily coverage metrics.
|
||||||
class CoverageWidget extends StatelessWidget {
|
class CoverageWidget extends StatelessWidget {
|
||||||
|
|
||||||
/// Creates a [CoverageWidget].
|
/// Creates a [CoverageWidget].
|
||||||
const CoverageWidget({
|
const CoverageWidget({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -13,6 +12,7 @@ class CoverageWidget extends StatelessWidget {
|
|||||||
this.coveragePercent = 0,
|
this.coveragePercent = 0,
|
||||||
this.subtitle,
|
this.subtitle,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The total number of shifts needed.
|
/// The total number of shifts needed.
|
||||||
final int totalNeeded;
|
final int totalNeeded;
|
||||||
|
|
||||||
@@ -66,7 +66,9 @@ class CoverageWidget extends StatelessWidget {
|
|||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
t.client_home.dashboard.percent_covered(percent: coveragePercent),
|
t.client_home.dashboard.percent_covered(
|
||||||
|
percent: coveragePercent,
|
||||||
|
),
|
||||||
style: UiTypography.footnote2b.copyWith(color: textColor),
|
style: UiTypography.footnote2b.copyWith(color: textColor),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -115,7 +117,6 @@ class CoverageWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MetricCard extends StatelessWidget {
|
class _MetricCard extends StatelessWidget {
|
||||||
|
|
||||||
const _MetricCard({
|
const _MetricCard({
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.iconColor,
|
required this.iconColor,
|
||||||
@@ -136,7 +137,7 @@ class _MetricCard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.cardViewBackground,
|
color: UiColors.cardViewBackground,
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border, width: 0.5),
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withValues(alpha: 0.02),
|
color: UiColors.black.withValues(alpha: 0.02),
|
||||||
|
|||||||
@@ -56,10 +56,8 @@ class ClientHubsModule extends Module {
|
|||||||
ClientPaths.childRoute(ClientPaths.hubs, ClientPaths.hubDetails),
|
ClientPaths.childRoute(ClientPaths.hubs, ClientPaths.hubDetails),
|
||||||
child: (_) {
|
child: (_) {
|
||||||
final Map<String, dynamic> data = r.args.data as Map<String, dynamic>;
|
final Map<String, dynamic> data = r.args.data as Map<String, dynamic>;
|
||||||
return HubDetailsPage(
|
final Hub hub = data['hub'] as Hub;
|
||||||
hub: data['hub'] as Hub,
|
return HubDetailsPage(hub: hub);
|
||||||
bloc: Modular.get<HubDetailsBloc>(),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
r.child(
|
r.child(
|
||||||
@@ -67,7 +65,8 @@ class ClientHubsModule extends Module {
|
|||||||
transition: TransitionType.custom,
|
transition: TransitionType.custom,
|
||||||
customTransition: CustomTransition(
|
customTransition: CustomTransition(
|
||||||
opaque: false,
|
opaque: false,
|
||||||
transitionBuilder: (
|
transitionBuilder:
|
||||||
|
(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
Animation<double> animation,
|
Animation<double> animation,
|
||||||
Animation<double> secondaryAnimation,
|
Animation<double> secondaryAnimation,
|
||||||
@@ -78,10 +77,7 @@ class ClientHubsModule extends Module {
|
|||||||
),
|
),
|
||||||
child: (_) {
|
child: (_) {
|
||||||
final Map<String, dynamic> data = r.args.data as Map<String, dynamic>;
|
final Map<String, dynamic> data = r.args.data as Map<String, dynamic>;
|
||||||
return EditHubPage(
|
return EditHubPage(hub: data['hub'] as Hub?);
|
||||||
hub: data['hub'] as Hub?,
|
|
||||||
bloc: Modular.get<EditHubBloc>(),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,32 +8,21 @@ import 'package:krow_domain/krow_domain.dart';
|
|||||||
import '../blocs/edit_hub/edit_hub_bloc.dart';
|
import '../blocs/edit_hub/edit_hub_bloc.dart';
|
||||||
import '../blocs/edit_hub/edit_hub_event.dart';
|
import '../blocs/edit_hub/edit_hub_event.dart';
|
||||||
import '../blocs/edit_hub/edit_hub_state.dart';
|
import '../blocs/edit_hub/edit_hub_state.dart';
|
||||||
import '../widgets/hub_form_dialog.dart';
|
import '../widgets/hub_form.dart';
|
||||||
|
|
||||||
/// A wrapper page that shows the hub form in a modal-style layout.
|
/// A wrapper page that shows the hub form in a modal-style layout.
|
||||||
class EditHubPage extends StatefulWidget {
|
class EditHubPage extends StatelessWidget {
|
||||||
const EditHubPage({this.hub, required this.bloc, super.key});
|
const EditHubPage({this.hub, super.key});
|
||||||
|
|
||||||
final Hub? hub;
|
final Hub? hub;
|
||||||
final EditHubBloc bloc;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<EditHubPage> createState() => _EditHubPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _EditHubPageState extends State<EditHubPage> {
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
// Load available cost centers
|
|
||||||
widget.bloc.add(const EditHubCostCentersLoadRequested());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider<EditHubBloc>.value(
|
return BlocProvider<EditHubBloc>(
|
||||||
value: widget.bloc,
|
create: (_) =>
|
||||||
child: BlocListener<EditHubBloc, EditHubState>(
|
Modular.get<EditHubBloc>()
|
||||||
|
..add(const EditHubCostCentersLoadRequested()),
|
||||||
|
child: BlocConsumer<EditHubBloc, EditHubState>(
|
||||||
listenWhen: (EditHubState prev, EditHubState curr) =>
|
listenWhen: (EditHubState prev, EditHubState curr) =>
|
||||||
prev.status != curr.status || prev.successKey != curr.successKey,
|
prev.status != curr.status || prev.successKey != curr.successKey,
|
||||||
listener: (BuildContext context, EditHubState state) {
|
listener: (BuildContext context, EditHubState state) {
|
||||||
@@ -58,28 +47,25 @@ class _EditHubPageState extends State<EditHubPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: BlocBuilder<EditHubBloc, EditHubState>(
|
|
||||||
builder: (BuildContext context, EditHubState state) {
|
builder: (BuildContext context, EditHubState state) {
|
||||||
final bool isSaving = state.status == EditHubStatus.loading;
|
final bool isSaving = state.status == EditHubStatus.loading;
|
||||||
|
final bool isEditing = hub != null;
|
||||||
|
final String title = isEditing
|
||||||
|
? t.client_hubs.edit_hub.title
|
||||||
|
: t.client_hubs.add_hub_dialog.title;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: UiColors.bgOverlay,
|
appBar: UiAppBar(title: title, showBackButton: true),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
// Tap background to dismiss
|
SingleChildScrollView(
|
||||||
GestureDetector(
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
onTap: () => Modular.to.pop(),
|
child: HubForm(
|
||||||
child: Container(color: Colors.transparent),
|
hub: hub,
|
||||||
),
|
|
||||||
|
|
||||||
// Dialog-style content centered
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: HubFormDialog(
|
|
||||||
hub: widget.hub,
|
|
||||||
costCenters: state.costCenters,
|
costCenters: state.costCenters,
|
||||||
onCancel: () => Modular.to.pop(),
|
onCancel: () => Modular.to.pop(),
|
||||||
onSave: ({
|
onSave:
|
||||||
|
({
|
||||||
required String name,
|
required String name,
|
||||||
required String address,
|
required String address,
|
||||||
String? costCenterId,
|
String? costCenterId,
|
||||||
@@ -87,8 +73,8 @@ class _EditHubPageState extends State<EditHubPage> {
|
|||||||
double? latitude,
|
double? latitude,
|
||||||
double? longitude,
|
double? longitude,
|
||||||
}) {
|
}) {
|
||||||
if (widget.hub == null) {
|
if (hub == null) {
|
||||||
widget.bloc.add(
|
BlocProvider.of<EditHubBloc>(context).add(
|
||||||
EditHubAddRequested(
|
EditHubAddRequested(
|
||||||
name: name,
|
name: name,
|
||||||
address: address,
|
address: address,
|
||||||
@@ -99,9 +85,9 @@ class _EditHubPageState extends State<EditHubPage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
widget.bloc.add(
|
BlocProvider.of<EditHubBloc>(context).add(
|
||||||
EditHubUpdateRequested(
|
EditHubUpdateRequested(
|
||||||
id: widget.hub!.id,
|
id: hub!.id,
|
||||||
name: name,
|
name: name,
|
||||||
address: address,
|
address: address,
|
||||||
costCenterId: costCenterId,
|
costCenterId: costCenterId,
|
||||||
@@ -126,7 +112,6 @@ class _EditHubPageState extends State<EditHubPage> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,28 +10,27 @@ import '../blocs/hub_details/hub_details_bloc.dart';
|
|||||||
import '../blocs/hub_details/hub_details_event.dart';
|
import '../blocs/hub_details/hub_details_event.dart';
|
||||||
import '../blocs/hub_details/hub_details_state.dart';
|
import '../blocs/hub_details/hub_details_state.dart';
|
||||||
import '../widgets/hub_details/hub_details_bottom_actions.dart';
|
import '../widgets/hub_details/hub_details_bottom_actions.dart';
|
||||||
import '../widgets/hub_details/hub_details_header.dart';
|
|
||||||
import '../widgets/hub_details/hub_details_item.dart';
|
import '../widgets/hub_details/hub_details_item.dart';
|
||||||
|
|
||||||
/// A read-only details page for a single [Hub].
|
/// A read-only details page for a single [Hub].
|
||||||
///
|
///
|
||||||
/// Shows hub name, address, and NFC tag assignment.
|
/// Shows hub name, address, and NFC tag assignment.
|
||||||
class HubDetailsPage extends StatelessWidget {
|
class HubDetailsPage extends StatelessWidget {
|
||||||
const HubDetailsPage({required this.hub, required this.bloc, super.key});
|
const HubDetailsPage({required this.hub, super.key});
|
||||||
|
|
||||||
final Hub hub;
|
final Hub hub;
|
||||||
final HubDetailsBloc bloc;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider<HubDetailsBloc>.value(
|
return BlocProvider<HubDetailsBloc>(
|
||||||
value: bloc,
|
create: (_) => Modular.get<HubDetailsBloc>(),
|
||||||
child: BlocListener<HubDetailsBloc, HubDetailsState>(
|
child: BlocListener<HubDetailsBloc, HubDetailsState>(
|
||||||
listener: (BuildContext context, HubDetailsState state) {
|
listener: (BuildContext context, HubDetailsState state) {
|
||||||
if (state.status == HubDetailsStatus.deleted) {
|
if (state.status == HubDetailsStatus.deleted) {
|
||||||
final String message = state.successKey == 'deleted'
|
final String message = state.successKey == 'deleted'
|
||||||
? t.client_hubs.hub_details.deleted_success
|
? t.client_hubs.hub_details.deleted_success
|
||||||
: (state.successMessage ?? t.client_hubs.hub_details.deleted_success);
|
: (state.successMessage ??
|
||||||
|
t.client_hubs.hub_details.deleted_success);
|
||||||
UiSnackbar.show(
|
UiSnackbar.show(
|
||||||
context,
|
context,
|
||||||
message: message,
|
message: message,
|
||||||
@@ -53,23 +52,22 @@ class HubDetailsPage extends StatelessWidget {
|
|||||||
final bool isLoading = state.status == HubDetailsStatus.loading;
|
final bool isLoading = state.status == HubDetailsStatus.loading;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: const UiAppBar(showBackButton: true),
|
appBar: UiAppBar(
|
||||||
|
title: hub.name,
|
||||||
|
subtitle: hub.address,
|
||||||
|
showBackButton: true,
|
||||||
|
),
|
||||||
bottomNavigationBar: HubDetailsBottomActions(
|
bottomNavigationBar: HubDetailsBottomActions(
|
||||||
isLoading: isLoading,
|
isLoading: isLoading,
|
||||||
onDelete: () => _confirmDeleteHub(context),
|
onDelete: () => _confirmDeleteHub(context),
|
||||||
onEdit: () => _navigateToEditPage(context),
|
onEdit: () => _navigateToEditPage(context),
|
||||||
),
|
),
|
||||||
backgroundColor: UiColors.bgMenu,
|
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
// ── Header ──────────────────────────────────────────
|
|
||||||
HubDetailsHeader(hub: hub),
|
|
||||||
const Divider(height: 1, thickness: 0.5),
|
|
||||||
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(UiConstants.space5),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -85,11 +83,16 @@ class HubDetailsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space4),
|
const SizedBox(height: UiConstants.space4),
|
||||||
HubDetailsItem(
|
HubDetailsItem(
|
||||||
label: t.client_hubs.hub_details.cost_center_label,
|
label:
|
||||||
|
t.client_hubs.hub_details.cost_center_label,
|
||||||
value: hub.costCenter != null
|
value: hub.costCenter != null
|
||||||
? '${hub.costCenter!.name} (${hub.costCenter!.code})'
|
? '${hub.costCenter!.name} (${hub.costCenter!.code})'
|
||||||
: t.client_hubs.hub_details.cost_center_none,
|
: t
|
||||||
icon: UiIcons.bank, // Using bank icon for cost center
|
.client_hubs
|
||||||
|
.hub_details
|
||||||
|
.cost_center_none,
|
||||||
|
icon: UiIcons
|
||||||
|
.bank, // Using bank icon for cost center
|
||||||
isHighlight: hub.costCenter != null,
|
isHighlight: hub.costCenter != null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -140,7 +143,7 @@ class HubDetailsPage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (confirm == true) {
|
if (confirm == true) {
|
||||||
bloc.add(HubDetailsDeleteRequested(hub.id));
|
Modular.get<HubDetailsBloc>().add(HubDetailsDeleteRequested(hub.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,307 @@
|
|||||||
|
import 'package:design_system/design_system.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:core_localization/core_localization.dart';
|
||||||
|
import 'package:google_places_flutter/model/prediction.dart';
|
||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
|
import 'hub_address_autocomplete.dart';
|
||||||
|
import 'edit_hub/edit_hub_field_label.dart';
|
||||||
|
|
||||||
|
/// A bottom sheet dialog for adding or editing a hub.
|
||||||
|
class HubForm extends StatefulWidget {
|
||||||
|
const HubForm({
|
||||||
|
required this.onSave,
|
||||||
|
required this.onCancel,
|
||||||
|
this.hub,
|
||||||
|
this.costCenters = const <CostCenter>[],
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Hub? hub;
|
||||||
|
final List<CostCenter> costCenters;
|
||||||
|
final void Function({
|
||||||
|
required String name,
|
||||||
|
required String address,
|
||||||
|
String? costCenterId,
|
||||||
|
String? placeId,
|
||||||
|
double? latitude,
|
||||||
|
double? longitude,
|
||||||
|
})
|
||||||
|
onSave;
|
||||||
|
final VoidCallback onCancel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<HubForm> createState() => _HubFormState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HubFormState extends State<HubForm> {
|
||||||
|
late final TextEditingController _nameController;
|
||||||
|
late final TextEditingController _addressController;
|
||||||
|
late final FocusNode _addressFocusNode;
|
||||||
|
String? _selectedCostCenterId;
|
||||||
|
Prediction? _selectedPrediction;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_nameController = TextEditingController(text: widget.hub?.name);
|
||||||
|
_addressController = TextEditingController(text: widget.hub?.address);
|
||||||
|
_addressFocusNode = FocusNode();
|
||||||
|
_selectedCostCenterId = widget.hub?.costCenter?.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_nameController.dispose();
|
||||||
|
_addressController.dispose();
|
||||||
|
_addressFocusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final bool isEditing = widget.hub != null;
|
||||||
|
final String buttonText = isEditing
|
||||||
|
? t.client_hubs.edit_hub.save_button
|
||||||
|
: t.client_hubs.add_hub_dialog.create_button;
|
||||||
|
|
||||||
|
return Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: <Widget>[
|
||||||
|
// ── Hub Name ────────────────────────────────
|
||||||
|
EditHubFieldLabel(t.client_hubs.add_hub_dialog.name_label),
|
||||||
|
const SizedBox(height: UiConstants.space2),
|
||||||
|
TextFormField(
|
||||||
|
controller: _nameController,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
validator: (String? value) {
|
||||||
|
if (value == null || value.trim().isEmpty) {
|
||||||
|
return t.client_hubs.add_hub_dialog.name_required;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
decoration: _buildInputDecoration(
|
||||||
|
t.client_hubs.add_hub_dialog.name_hint,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
|
// ── Cost Center ─────────────────────────────
|
||||||
|
EditHubFieldLabel(t.client_hubs.add_hub_dialog.cost_center_label),
|
||||||
|
const SizedBox(height: UiConstants.space2),
|
||||||
|
InkWell(
|
||||||
|
onTap: _showCostCenterSelector,
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase * 1.5),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space4,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFF8FAFD),
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
UiConstants.radiusBase * 1.5,
|
||||||
|
),
|
||||||
|
border: Border.all(
|
||||||
|
color: _selectedCostCenterId != null
|
||||||
|
? UiColors.primary
|
||||||
|
: UiColors.primary.withValues(alpha: 0.1),
|
||||||
|
width: _selectedCostCenterId != null ? 2 : 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
_selectedCostCenterId != null
|
||||||
|
? _getCostCenterName(_selectedCostCenterId!)
|
||||||
|
: t.client_hubs.add_hub_dialog.cost_center_hint,
|
||||||
|
style: _selectedCostCenterId != null
|
||||||
|
? UiTypography.body1r.textPrimary
|
||||||
|
: UiTypography.body2r.textPlaceholder.copyWith(
|
||||||
|
color: UiColors.textSecondary.withValues(
|
||||||
|
alpha: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Icon(
|
||||||
|
Icons.keyboard_arrow_down,
|
||||||
|
color: UiColors.iconSecondary,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
|
// ── Address ─────────────────────────────────
|
||||||
|
EditHubFieldLabel(t.client_hubs.add_hub_dialog.address_label),
|
||||||
|
const SizedBox(height: UiConstants.space2),
|
||||||
|
HubAddressAutocomplete(
|
||||||
|
controller: _addressController,
|
||||||
|
hintText: t.client_hubs.add_hub_dialog.address_hint,
|
||||||
|
decoration: _buildInputDecoration(
|
||||||
|
t.client_hubs.add_hub_dialog.address_hint,
|
||||||
|
),
|
||||||
|
focusNode: _addressFocusNode,
|
||||||
|
onSelected: (Prediction prediction) {
|
||||||
|
_selectedPrediction = prediction;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: UiConstants.space8),
|
||||||
|
|
||||||
|
// ── Save Button ─────────────────────────────
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: UiButton.primary(
|
||||||
|
onPressed: () {
|
||||||
|
if (_formKey.currentState!.validate()) {
|
||||||
|
if (_addressController.text.trim().isEmpty) {
|
||||||
|
UiSnackbar.show(
|
||||||
|
context,
|
||||||
|
message:
|
||||||
|
t.client_hubs.add_hub_dialog.address_required,
|
||||||
|
type: UiSnackbarType.error,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.onSave(
|
||||||
|
name: _nameController.text.trim(),
|
||||||
|
address: _addressController.text.trim(),
|
||||||
|
costCenterId: _selectedCostCenterId,
|
||||||
|
placeId: _selectedPrediction?.placeId,
|
||||||
|
latitude: double.tryParse(
|
||||||
|
_selectedPrediction?.lat ?? '',
|
||||||
|
),
|
||||||
|
longitude: double.tryParse(
|
||||||
|
_selectedPrediction?.lng ?? '',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text: buttonText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space4),
|
||||||
|
Expanded(
|
||||||
|
child: UiButton.secondary(
|
||||||
|
onPressed: widget.onCancel,
|
||||||
|
text: t.common.cancel,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
InputDecoration _buildInputDecoration(String hint) {
|
||||||
|
return InputDecoration(
|
||||||
|
hintText: hint,
|
||||||
|
hintStyle: UiTypography.body2r.textPlaceholder.copyWith(
|
||||||
|
color: UiColors.textSecondary.withValues(alpha: 0.5),
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: const Color(0xFFF8FAFD),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space4,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase * 1.5),
|
||||||
|
borderSide: BorderSide(color: UiColors.primary.withValues(alpha: 0.1)),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase * 1.5),
|
||||||
|
borderSide: BorderSide(color: UiColors.primary.withValues(alpha: 0.1)),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase * 1.5),
|
||||||
|
borderSide: const BorderSide(color: UiColors.primary, width: 2),
|
||||||
|
),
|
||||||
|
errorStyle: UiTypography.footnote2r.textError,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getCostCenterName(String id) {
|
||||||
|
try {
|
||||||
|
return widget.costCenters.firstWhere((CostCenter cc) => cc.id == id).name;
|
||||||
|
} catch (_) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showCostCenterSelector() async {
|
||||||
|
final CostCenter? selected = await showDialog<CostCenter>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
t.client_hubs.add_hub_dialog.cost_center_label,
|
||||||
|
style: UiTypography.headline3m.textPrimary,
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
content: SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxHeight: 400),
|
||||||
|
child: widget.costCenters.isEmpty
|
||||||
|
? Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
child: Text(
|
||||||
|
t.client_hubs.add_hub_dialog.cost_centers_empty,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: widget.costCenters.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
final CostCenter cc = widget.costCenters[index];
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
cc.name,
|
||||||
|
style: UiTypography.body1m.textPrimary,
|
||||||
|
),
|
||||||
|
subtitle: cc.code != null
|
||||||
|
? Text(
|
||||||
|
cc.code!,
|
||||||
|
style: UiTypography.body2r.textSecondary,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
onTap: () => Navigator.of(context).pop(cc),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selected != null) {
|
||||||
|
setState(() {
|
||||||
|
_selectedCostCenterId = selected.id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,350 +0,0 @@
|
|||||||
import 'package:design_system/design_system.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:core_localization/core_localization.dart';
|
|
||||||
import 'package:google_places_flutter/model/prediction.dart';
|
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
|
||||||
|
|
||||||
import 'hub_address_autocomplete.dart';
|
|
||||||
import 'edit_hub/edit_hub_field_label.dart';
|
|
||||||
|
|
||||||
/// A bottom sheet dialog for adding or editing a hub.
|
|
||||||
class HubFormDialog extends StatefulWidget {
|
|
||||||
/// Creates a [HubFormDialog].
|
|
||||||
const HubFormDialog({
|
|
||||||
required this.onSave,
|
|
||||||
required this.onCancel,
|
|
||||||
this.hub,
|
|
||||||
this.costCenters = const <CostCenter>[],
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// The hub to edit. If null, a new hub is created.
|
|
||||||
final Hub? hub;
|
|
||||||
|
|
||||||
/// Available cost centers for selection.
|
|
||||||
final List<CostCenter> costCenters;
|
|
||||||
|
|
||||||
/// Callback when the "Save" button is pressed.
|
|
||||||
final void Function({
|
|
||||||
required String name,
|
|
||||||
required String address,
|
|
||||||
String? costCenterId,
|
|
||||||
String? placeId,
|
|
||||||
double? latitude,
|
|
||||||
double? longitude,
|
|
||||||
}) onSave;
|
|
||||||
|
|
||||||
/// Callback when the dialog is cancelled.
|
|
||||||
final VoidCallback onCancel;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<HubFormDialog> createState() => _HubFormDialogState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _HubFormDialogState extends State<HubFormDialog> {
|
|
||||||
late final TextEditingController _nameController;
|
|
||||||
late final TextEditingController _addressController;
|
|
||||||
late final FocusNode _addressFocusNode;
|
|
||||||
String? _selectedCostCenterId;
|
|
||||||
Prediction? _selectedPrediction;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_nameController = TextEditingController(text: widget.hub?.name);
|
|
||||||
_addressController = TextEditingController(text: widget.hub?.address);
|
|
||||||
_addressFocusNode = FocusNode();
|
|
||||||
_selectedCostCenterId = widget.hub?.costCenter?.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_nameController.dispose();
|
|
||||||
_addressController.dispose();
|
|
||||||
_addressFocusNode.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final bool isEditing = widget.hub != null;
|
|
||||||
final String title = isEditing
|
|
||||||
? t.client_hubs.edit_hub.title
|
|
||||||
: t.client_hubs.add_hub_dialog.title;
|
|
||||||
|
|
||||||
final String buttonText = isEditing
|
|
||||||
? t.client_hubs.edit_hub.save_button
|
|
||||||
: t.client_hubs.add_hub_dialog.create_button;
|
|
||||||
|
|
||||||
return Center(
|
|
||||||
child: Container(
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: UiConstants.space5),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: UiColors.bgPopup,
|
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase * 3),
|
|
||||||
boxShadow: <BoxShadow>[
|
|
||||||
BoxShadow(
|
|
||||||
color: UiColors.black.withValues(alpha: 0.15),
|
|
||||||
blurRadius: 30,
|
|
||||||
offset: const Offset(0, 10),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.all(UiConstants.space6),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Form(
|
|
||||||
key: _formKey,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
title,
|
|
||||||
style: UiTypography.headline3m.textPrimary.copyWith(
|
|
||||||
fontSize: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space5),
|
|
||||||
|
|
||||||
// ── Hub Name ────────────────────────────────
|
|
||||||
EditHubFieldLabel(t.client_hubs.add_hub_dialog.name_label),
|
|
||||||
const SizedBox(height: UiConstants.space2),
|
|
||||||
TextFormField(
|
|
||||||
controller: _nameController,
|
|
||||||
style: UiTypography.body1r.textPrimary,
|
|
||||||
textInputAction: TextInputAction.next,
|
|
||||||
validator: (String? value) {
|
|
||||||
if (value == null || value.trim().isEmpty) {
|
|
||||||
return t.client_hubs.add_hub_dialog.name_required;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
decoration: _buildInputDecoration(
|
|
||||||
t.client_hubs.add_hub_dialog.name_hint,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: UiConstants.space4),
|
|
||||||
|
|
||||||
// ── Cost Center ─────────────────────────────
|
|
||||||
EditHubFieldLabel(t.client_hubs.add_hub_dialog.cost_center_label),
|
|
||||||
const SizedBox(height: UiConstants.space2),
|
|
||||||
InkWell(
|
|
||||||
onTap: _showCostCenterSelector,
|
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase * 1.5),
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: UiConstants.space4,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFF8FAFD),
|
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase * 1.5),
|
|
||||||
border: Border.all(
|
|
||||||
color: _selectedCostCenterId != null
|
|
||||||
? UiColors.primary
|
|
||||||
: UiColors.primary.withValues(alpha: 0.1),
|
|
||||||
width: _selectedCostCenterId != null ? 2 : 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
_selectedCostCenterId != null
|
|
||||||
? _getCostCenterName(_selectedCostCenterId!)
|
|
||||||
: t.client_hubs.add_hub_dialog.cost_center_hint,
|
|
||||||
style: _selectedCostCenterId != null
|
|
||||||
? UiTypography.body1r.textPrimary
|
|
||||||
: UiTypography.body2r.textPlaceholder.copyWith(
|
|
||||||
color: UiColors.textSecondary.withValues(alpha: 0.5),
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Icon(
|
|
||||||
Icons.keyboard_arrow_down,
|
|
||||||
color: UiColors.iconSecondary,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: UiConstants.space4),
|
|
||||||
|
|
||||||
// ── Address ─────────────────────────────────
|
|
||||||
EditHubFieldLabel(t.client_hubs.add_hub_dialog.address_label),
|
|
||||||
const SizedBox(height: UiConstants.space2),
|
|
||||||
HubAddressAutocomplete(
|
|
||||||
controller: _addressController,
|
|
||||||
hintText: t.client_hubs.add_hub_dialog.address_hint,
|
|
||||||
decoration: _buildInputDecoration(
|
|
||||||
t.client_hubs.add_hub_dialog.address_hint,
|
|
||||||
),
|
|
||||||
focusNode: _addressFocusNode,
|
|
||||||
onSelected: (Prediction prediction) {
|
|
||||||
_selectedPrediction = prediction;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: UiConstants.space8),
|
|
||||||
|
|
||||||
// ── Buttons ─────────────────────────────────
|
|
||||||
Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: UiButton.secondary(
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
side: BorderSide(
|
|
||||||
color: UiColors.primary.withValues(alpha: 0.1),
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(
|
|
||||||
UiConstants.radiusBase * 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: widget.onCancel,
|
|
||||||
text: t.common.cancel,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space3),
|
|
||||||
Expanded(
|
|
||||||
child: UiButton.primary(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: UiColors.accent,
|
|
||||||
foregroundColor: UiColors.accentForeground,
|
|
||||||
elevation: 0,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(
|
|
||||||
UiConstants.radiusBase * 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
if (_formKey.currentState!.validate()) {
|
|
||||||
if (_addressController.text.trim().isEmpty) {
|
|
||||||
UiSnackbar.show(
|
|
||||||
context,
|
|
||||||
message: t.client_hubs.add_hub_dialog.address_required,
|
|
||||||
type: UiSnackbarType.error,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
widget.onSave(
|
|
||||||
name: _nameController.text.trim(),
|
|
||||||
address: _addressController.text.trim(),
|
|
||||||
costCenterId: _selectedCostCenterId,
|
|
||||||
placeId: _selectedPrediction?.placeId,
|
|
||||||
latitude: double.tryParse(
|
|
||||||
_selectedPrediction?.lat ?? '',
|
|
||||||
),
|
|
||||||
longitude: double.tryParse(
|
|
||||||
_selectedPrediction?.lng ?? '',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
text: buttonText,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
InputDecoration _buildInputDecoration(String hint) {
|
|
||||||
return InputDecoration(
|
|
||||||
hintText: hint,
|
|
||||||
hintStyle: UiTypography.body2r.textPlaceholder.copyWith(
|
|
||||||
color: UiColors.textSecondary.withValues(alpha: 0.5),
|
|
||||||
),
|
|
||||||
filled: true,
|
|
||||||
fillColor: const Color(0xFFF8FAFD),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: UiConstants.space4,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase * 1.5),
|
|
||||||
borderSide: BorderSide(color: UiColors.primary.withValues(alpha: 0.1)),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase * 1.5),
|
|
||||||
borderSide: BorderSide(color: UiColors.primary.withValues(alpha: 0.1)),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase * 1.5),
|
|
||||||
borderSide: const BorderSide(color: UiColors.primary, width: 2),
|
|
||||||
),
|
|
||||||
errorStyle: UiTypography.footnote2r.textError,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String _getCostCenterName(String id) {
|
|
||||||
try {
|
|
||||||
return widget.costCenters.firstWhere((CostCenter cc) => cc.id == id).name;
|
|
||||||
} catch (_) {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _showCostCenterSelector() async {
|
|
||||||
final CostCenter? selected = await showDialog<CostCenter>(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
t.client_hubs.add_hub_dialog.cost_center_label,
|
|
||||||
style: UiTypography.headline3m.textPrimary,
|
|
||||||
),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
|
||||||
content: SizedBox(
|
|
||||||
width: double.maxFinite,
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(maxHeight: 400),
|
|
||||||
child: widget.costCenters.isEmpty
|
|
||||||
? Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
child: Text(t.client_hubs.add_hub_dialog.cost_centers_empty),
|
|
||||||
)
|
|
||||||
: ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemCount: widget.costCenters.length,
|
|
||||||
itemBuilder: (BuildContext context, int index) {
|
|
||||||
final CostCenter cc = widget.costCenters[index];
|
|
||||||
return ListTile(
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
title: Text(cc.name, style: UiTypography.body1m.textPrimary),
|
|
||||||
subtitle: cc.code != null ? Text(cc.code!, style: UiTypography.body2r.textSecondary) : null,
|
|
||||||
onTap: () => Navigator.of(context).pop(cc),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (selected != null) {
|
|
||||||
setState(() {
|
|
||||||
_selectedCostCenterId = selected.id;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
@@ -5,7 +5,7 @@ import '../widgets/create_order/create_order_view.dart';
|
|||||||
/// Main entry page for the client create order flow.
|
/// Main entry page for the client create order flow.
|
||||||
///
|
///
|
||||||
/// This page displays the [CreateOrderView].
|
/// This page displays the [CreateOrderView].
|
||||||
/// It follows the Krow Clean Architecture by being a [StatelessWidget] and
|
/// It follows the KROW Clean Architecture by being a [StatelessWidget] and
|
||||||
/// delegating its UI to other components.
|
/// delegating its UI to other components.
|
||||||
class ClientCreateOrderPage extends StatelessWidget {
|
class ClientCreateOrderPage extends StatelessWidget {
|
||||||
/// Creates a [ClientCreateOrderPage].
|
/// Creates a [ClientCreateOrderPage].
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import '../blocs/one_time_order/one_time_order_state.dart';
|
|||||||
/// Users can specify the date, location, and multiple staff positions required.
|
/// Users can specify the date, location, and multiple staff positions required.
|
||||||
///
|
///
|
||||||
/// This page initializes the [OneTimeOrderBloc] and displays the [OneTimeOrderView]
|
/// This page initializes the [OneTimeOrderBloc] and displays the [OneTimeOrderView]
|
||||||
/// from the common orders package. It follows the Krow Clean Architecture by being
|
/// from the common orders package. It follows the KROW Clean Architecture by being
|
||||||
/// a [StatelessWidget] and mapping local BLoC state to generic UI models.
|
/// a [StatelessWidget] and mapping local BLoC state to generic UI models.
|
||||||
class OneTimeOrderPage extends StatelessWidget {
|
class OneTimeOrderPage extends StatelessWidget {
|
||||||
/// Creates a [OneTimeOrderPage].
|
/// Creates a [OneTimeOrderPage].
|
||||||
@@ -70,10 +70,8 @@ class OneTimeOrderPage extends StatelessWidget {
|
|||||||
bloc.add(const OneTimeOrderHubManagerChanged(null));
|
bloc.add(const OneTimeOrderHubManagerChanged(null));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final OneTimeOrderManagerOption original =
|
final OneTimeOrderManagerOption original = state.managers
|
||||||
state.managers.firstWhere(
|
.firstWhere((OneTimeOrderManagerOption m) => m.id == val.id);
|
||||||
(OneTimeOrderManagerOption m) => m.id == val.id,
|
|
||||||
);
|
|
||||||
bloc.add(OneTimeOrderHubManagerChanged(original));
|
bloc.add(OneTimeOrderHubManagerChanged(original));
|
||||||
},
|
},
|
||||||
onPositionAdded: () => bloc.add(const OneTimeOrderPositionAdded()),
|
onPositionAdded: () => bloc.add(const OneTimeOrderPositionAdded()),
|
||||||
@@ -150,4 +148,3 @@ class OneTimeOrderPage extends StatelessWidget {
|
|||||||
return OrderManagerUiModel(id: manager.id, name: manager.name);
|
return OrderManagerUiModel(id: manager.id, name: manager.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import '../widgets/rapid_order/rapid_order_view.dart';
|
|||||||
/// Features voice recognition simulation and quick example selection.
|
/// Features voice recognition simulation and quick example selection.
|
||||||
///
|
///
|
||||||
/// This page initializes the [RapidOrderBloc] and displays the [RapidOrderView].
|
/// This page initializes the [RapidOrderBloc] and displays the [RapidOrderView].
|
||||||
/// It follows the Krow Clean Architecture by being a [StatelessWidget] and
|
/// It follows the KROW Clean Architecture by being a [StatelessWidget] and
|
||||||
/// delegating its state and UI to other components.
|
/// delegating its state and UI to other components.
|
||||||
class RapidOrderPage extends StatelessWidget {
|
class RapidOrderPage extends StatelessWidget {
|
||||||
/// Creates a [RapidOrderPage].
|
/// Creates a [RapidOrderPage].
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
|
|
||||||
/// Widget for displaying more ways to use Krow, using design system tokens.
|
/// Widget for displaying more ways to use KROW, using design system tokens.
|
||||||
class MoreWaysToUseKrowWidget extends StatelessWidget {
|
class MoreWaysToUseKrowWidget extends StatelessWidget {
|
||||||
/// Creates a [MoreWaysToUseKrowWidget].
|
/// Creates a [MoreWaysToUseKrowWidget].
|
||||||
const MoreWaysToUseKrowWidget({super.key});
|
const MoreWaysToUseKrowWidget({super.key});
|
||||||
@@ -15,13 +15,15 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
|
|||||||
{
|
{
|
||||||
'id': 'benefits',
|
'id': 'benefits',
|
||||||
'title': i18n.items.benefits.title,
|
'title': i18n.items.benefits.title,
|
||||||
'image': 'https://images.unsplash.com/photo-1481627834876-b7833e8f5570?w=400&h=300&fit=crop',
|
'image':
|
||||||
|
'https://images.unsplash.com/photo-1481627834876-b7833e8f5570?w=400&h=300&fit=crop',
|
||||||
'page': i18n.items.benefits.page,
|
'page': i18n.items.benefits.page,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'id': 'refer',
|
'id': 'refer',
|
||||||
'title': i18n.items.refer.title,
|
'title': i18n.items.refer.title,
|
||||||
'image': 'https://images.unsplash.com/photo-1529156069898-49953e39b3ac?w=400&h=300&fit=crop',
|
'image':
|
||||||
|
'https://images.unsplash.com/photo-1529156069898-49953e39b3ac?w=400&h=300&fit=crop',
|
||||||
'page': i18n.items.refer.page,
|
'page': i18n.items.refer.page,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -29,10 +31,7 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(i18n.title, style: UiTypography.title1m.textPrimary),
|
||||||
i18n.title,
|
|
||||||
style: UiTypography.title1m.textPrimary,
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space3),
|
const SizedBox(height: UiConstants.space3),
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
@@ -75,10 +74,7 @@ class MoreWaysToUseKrowWidget extends StatelessWidget {
|
|||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
errorBuilder: (context, error, stackTrace) => Container(
|
errorBuilder: (context, error, stackTrace) => Container(
|
||||||
color: UiColors.background,
|
color: UiColors.background,
|
||||||
child: Icon(
|
child: Icon(UiIcons.zap, color: UiColors.mutedForeground),
|
||||||
UiIcons.zap,
|
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Effective Date: February 18, 2026
|
|||||||
|
|
||||||
1. INTRODUCTION
|
1. INTRODUCTION
|
||||||
|
|
||||||
Krow Workforce ("we," "us," "our," or "the App") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and otherwise process your personal information through our mobile application and related services.
|
KROW Workforce ("we," "us," "our," or "the App") is committed to protecting your privacy. This Privacy Policy explains how we collect, use, disclose, and otherwise process your personal information through our mobile application and related services.
|
||||||
|
|
||||||
2. INFORMATION WE COLLECT
|
2. INFORMATION WE COLLECT
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ We may update this Privacy Policy from time to time. We will notify you of signi
|
|||||||
If you have questions about this Privacy Policy or your personal information, please contact us at:
|
If you have questions about this Privacy Policy or your personal information, please contact us at:
|
||||||
|
|
||||||
Email: privacy@krow.com
|
Email: privacy@krow.com
|
||||||
Address: Krow Workforce, [Company Address]
|
Address: KROW Workforce, [Company Address]
|
||||||
Phone: [Support Phone Number]
|
Phone: [Support Phone Number]
|
||||||
|
|
||||||
14. CALIFORNIA PRIVACY RIGHTS (CCPA)
|
14. CALIFORNIA PRIVACY RIGHTS (CCPA)
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ Effective Date: February 18, 2026
|
|||||||
|
|
||||||
1. ACCEPTANCE OF TERMS
|
1. ACCEPTANCE OF TERMS
|
||||||
|
|
||||||
By accessing and using the Krow Workforce application ("the App"), you accept and agree to be bound by the terms and provisions of this agreement. If you do not agree to abide by the above, please do not use this service.
|
By accessing and using the KROW Workforce application ("the App"), you accept and agree to be bound by the terms and provisions of this agreement. If you do not agree to abide by the above, please do not use this service.
|
||||||
|
|
||||||
2. USE LICENSE
|
2. USE LICENSE
|
||||||
|
|
||||||
Permission is granted to temporarily download one copy of the materials (information or software) on Krow Workforce's App for personal, non-commercial transitory viewing only. This is the grant of a license, not a transfer of title, and under this license you may not:
|
Permission is granted to temporarily download one copy of the materials (information or software) on KROW Workforce's App for personal, non-commercial transitory viewing only. This is the grant of a license, not a transfer of title, and under this license you may not:
|
||||||
|
|
||||||
a) Modifying or copying the materials
|
a) Modifying or copying the materials
|
||||||
b) Using the materials for any commercial purpose or for any public display
|
b) Using the materials for any commercial purpose or for any public display
|
||||||
@@ -18,43 +18,43 @@ e) Transferring the materials to another person or "mirroring" the materials on
|
|||||||
|
|
||||||
3. DISCLAIMER
|
3. DISCLAIMER
|
||||||
|
|
||||||
The materials on Krow Workforce's App are provided on an "as is" basis. Krow Workforce makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.
|
The materials on KROW Workforce's App are provided on an "as is" basis. KROW Workforce makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.
|
||||||
|
|
||||||
4. LIMITATIONS
|
4. LIMITATIONS
|
||||||
|
|
||||||
In no event shall Krow Workforce or its suppliers be liable for any damages (including, without limitation, damages for loss of data or profit, or due to business interruption) arising out of the use or inability to use the materials on Krow Workforce's App, even if Krow Workforce or a Krow Workforce authorized representative has been notified orally or in writing of the possibility of such damage.
|
In no event shall KROW Workforce or its suppliers be liable for any damages (including, without limitation, damages for loss of data or profit, or due to business interruption) arising out of the use or inability to use the materials on KROW Workforce's App, even if KROW Workforce or a KROW Workforce authorized representative has been notified orally or in writing of the possibility of such damage.
|
||||||
|
|
||||||
5. ACCURACY OF MATERIALS
|
5. ACCURACY OF MATERIALS
|
||||||
|
|
||||||
The materials appearing on Krow Workforce's App could include technical, typographical, or photographic errors. Krow Workforce does not warrant that any of the materials on its App are accurate, complete, or current. Krow Workforce may make changes to the materials contained on its App at any time without notice.
|
The materials appearing on KROW Workforce's App could include technical, typographical, or photographic errors. KROW Workforce does not warrant that any of the materials on its App are accurate, complete, or current. KROW Workforce may make changes to the materials contained on its App at any time without notice.
|
||||||
|
|
||||||
6. MATERIALS DISCLAIMER
|
6. MATERIALS DISCLAIMER
|
||||||
|
|
||||||
Krow Workforce has not reviewed all of the sites linked to its App and is not responsible for the contents of any such linked site. The inclusion of any link does not imply endorsement by Krow Workforce of the site. Use of any such linked website is at the user's own risk.
|
KROW Workforce has not reviewed all of the sites linked to its App and is not responsible for the contents of any such linked site. The inclusion of any link does not imply endorsement by KROW Workforce of the site. Use of any such linked website is at the user's own risk.
|
||||||
|
|
||||||
7. MODIFICATIONS
|
7. MODIFICATIONS
|
||||||
|
|
||||||
Krow Workforce may revise these terms of service for its App at any time without notice. By using this App, you are agreeing to be bound by the then current version of these terms of service.
|
KROW Workforce may revise these terms of service for its App at any time without notice. By using this App, you are agreeing to be bound by the then current version of these terms of service.
|
||||||
|
|
||||||
8. GOVERNING LAW
|
8. GOVERNING LAW
|
||||||
|
|
||||||
These terms and conditions are governed by and construed in accordance with the laws of the jurisdiction in which Krow Workforce is located, and you irrevocably submit to the exclusive jurisdiction of the courts in that location.
|
These terms and conditions are governed by and construed in accordance with the laws of the jurisdiction in which KROW Workforce is located, and you irrevocably submit to the exclusive jurisdiction of the courts in that location.
|
||||||
|
|
||||||
9. LIMITATION OF LIABILITY
|
9. LIMITATION OF LIABILITY
|
||||||
|
|
||||||
In no case shall Krow Workforce, its staff, or other contributors be liable for any indirect, incidental, consequential, special, or punitive damages arising out of or relating to the use of the App.
|
In no case shall KROW Workforce, its staff, or other contributors be liable for any indirect, incidental, consequential, special, or punitive damages arising out of or relating to the use of the App.
|
||||||
|
|
||||||
10. USER CONTENT
|
10. USER CONTENT
|
||||||
|
|
||||||
You grant Krow Workforce a non-exclusive, royalty-free, perpetual, and irrevocable right to use any content you provide to us, including but not limited to text, images, and information, in any media or format and for any purpose consistent with our business.
|
You grant KROW Workforce a non-exclusive, royalty-free, perpetual, and irrevocable right to use any content you provide to us, including but not limited to text, images, and information, in any media or format and for any purpose consistent with our business.
|
||||||
|
|
||||||
11. INDEMNIFICATION
|
11. INDEMNIFICATION
|
||||||
|
|
||||||
You agree to indemnify and hold harmless Krow Workforce and its staff from any and all claims, damages, losses, costs, and expenses, including attorney's fees, arising out of or resulting from your use of the App or violation of these terms.
|
You agree to indemnify and hold harmless KROW Workforce and its staff from any and all claims, damages, losses, costs, and expenses, including attorney's fees, arising out of or resulting from your use of the App or violation of these terms.
|
||||||
|
|
||||||
12. TERMINATION
|
12. TERMINATION
|
||||||
|
|
||||||
Krow Workforce reserves the right to terminate your account and access to the App at any time, in its sole discretion, for any reason or no reason, with or without notice.
|
KROW Workforce reserves the right to terminate your account and access to the App at any time, in its sole discretion, for any reason or no reason, with or without notice.
|
||||||
|
|
||||||
13. CONTACT INFORMATION
|
13. CONTACT INFORMATION
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Krow-web</title>
|
<title>KROW-web</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ const ForgotPassword: React.FC = () => {
|
|||||||
<div className="absolute top-12 left-12 z-20">
|
<div className="absolute top-12 left-12 z-20">
|
||||||
<img
|
<img
|
||||||
src={logo}
|
src={logo}
|
||||||
alt="Krow Logo"
|
alt="KROW Logo"
|
||||||
className="h-10 w-auto brightness-0 invert"
|
className="h-10 w-auto brightness-0 invert"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -139,7 +139,7 @@ const ForgotPassword: React.FC = () => {
|
|||||||
|
|
||||||
<img
|
<img
|
||||||
src={logo}
|
src={logo}
|
||||||
alt="Krow Logo"
|
alt="KROW Logo"
|
||||||
className="h-10 w-auto lg:hidden mb-2"
|
className="h-10 w-auto lg:hidden mb-2"
|
||||||
/>
|
/>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ const Login: React.FC = () => {
|
|||||||
<div className="absolute top-12 left-12 z-20">
|
<div className="absolute top-12 left-12 z-20">
|
||||||
<img
|
<img
|
||||||
src={logo}
|
src={logo}
|
||||||
alt="Krow Logo"
|
alt="KROW Logo"
|
||||||
className="h-10 w-auto brightness-0 invert"
|
className="h-10 w-auto brightness-0 invert"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -168,7 +168,7 @@ const Login: React.FC = () => {
|
|||||||
<div className="flex flex-col items-center lg:items-start space-y-4">
|
<div className="flex flex-col items-center lg:items-start space-y-4">
|
||||||
<img
|
<img
|
||||||
src={logo}
|
src={logo}
|
||||||
alt="Krow Logo"
|
alt="KROW Logo"
|
||||||
className="h-10 w-auto lg:hidden mb-2"
|
className="h-10 w-auto lg:hidden mb-2"
|
||||||
/>
|
/>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
|
|||||||
@@ -455,7 +455,7 @@ export default function Schedule() {
|
|||||||
<p className="text-[10px] font-bold text-secondary-text uppercase tracking-wider">Business</p>
|
<p className="text-[10px] font-bold text-secondary-text uppercase tracking-wider">Business</p>
|
||||||
<div className="flex items-center gap-2 bg-slate-50 p-2 rounded-lg border border-slate-100">
|
<div className="flex items-center gap-2 bg-slate-50 p-2 rounded-lg border border-slate-100">
|
||||||
<Users className="w-4 h-4 text-blue-600" />
|
<Users className="w-4 h-4 text-blue-600" />
|
||||||
<span className="text-sm font-medium">{selectedOrder.business?.businessName || "Krow Workforce"}</span>
|
<span className="text-sm font-medium">{selectedOrder.business?.businessName || "KROW Workforce"}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ mutation seedAll @transaction {
|
|||||||
data: {
|
data: {
|
||||||
id: "dvpWnaBjT6UksS5lo04hfMTyq1q1"
|
id: "dvpWnaBjT6UksS5lo04hfMTyq1q1"
|
||||||
email: "legendary@krowd.com"
|
email: "legendary@krowd.com"
|
||||||
fullName: "Krow Payements"
|
fullName: "KROW Payements"
|
||||||
role: USER
|
role: USER
|
||||||
userRole: "BUSINESS"
|
userRole: "BUSINESS"
|
||||||
}
|
}
|
||||||
@@ -24,9 +24,9 @@ mutation seedAll @transaction {
|
|||||||
business_1: business_insert(
|
business_1: business_insert(
|
||||||
data: {
|
data: {
|
||||||
id: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
id: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
||||||
businessName: "Krow"
|
businessName: "KROW"
|
||||||
userId: "dvpWnaBjT6UksS5lo04hfMTyq1q1"
|
userId: "dvpWnaBjT6UksS5lo04hfMTyq1q1"
|
||||||
contactName: "Krow Payements"
|
contactName: "KROW Payements"
|
||||||
email: "legendary@krowd.com"
|
email: "legendary@krowd.com"
|
||||||
phone: "+1-818-555-0148"
|
phone: "+1-818-555-0148"
|
||||||
address: "5000 San Jose Street, Granada Hills, CA, USA"
|
address: "5000 San Jose Street, Granada Hills, CA, USA"
|
||||||
@@ -48,9 +48,9 @@ mutation seedAll @transaction {
|
|||||||
team_1: team_insert(
|
team_1: team_insert(
|
||||||
data: {
|
data: {
|
||||||
id: "9508c187-7612-4084-90de-4ece4a63773f"
|
id: "9508c187-7612-4084-90de-4ece4a63773f"
|
||||||
teamName: "Krow"
|
teamName: "KROW"
|
||||||
ownerId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
ownerId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
||||||
ownerName: "Krow"
|
ownerName: "KROW"
|
||||||
ownerRole: "ADMIN"
|
ownerRole: "ADMIN"
|
||||||
totalHubs: 3
|
totalHubs: 3
|
||||||
}
|
}
|
||||||
@@ -335,7 +335,7 @@ mutation seedAll @transaction {
|
|||||||
businessId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
businessId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
||||||
orderType: ONE_TIME
|
orderType: ONE_TIME
|
||||||
status: COMPLETED
|
status: COMPLETED
|
||||||
eventName: "Krow Opening Night"
|
eventName: "KROW Opening Night"
|
||||||
teamHubId: "22a0b119-e6dc-4011-9043-d857cd4c12f3"
|
teamHubId: "22a0b119-e6dc-4011-9043-d857cd4c12f3"
|
||||||
date: "2026-01-26T05:00:00Z"
|
date: "2026-01-26T05:00:00Z"
|
||||||
requested: 1
|
requested: 1
|
||||||
@@ -377,7 +377,7 @@ mutation seedAll @transaction {
|
|||||||
businessId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
businessId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
||||||
orderType: ONE_TIME
|
orderType: ONE_TIME
|
||||||
status: COMPLETED
|
status: COMPLETED
|
||||||
eventName: "Krow Partner Showcase"
|
eventName: "KROW Partner Showcase"
|
||||||
teamHubId: "22a0b119-e6dc-4011-9043-d857cd4c12f3"
|
teamHubId: "22a0b119-e6dc-4011-9043-d857cd4c12f3"
|
||||||
date: "2026-01-28T05:00:00Z"
|
date: "2026-01-28T05:00:00Z"
|
||||||
requested: 1
|
requested: 1
|
||||||
@@ -419,7 +419,7 @@ mutation seedAll @transaction {
|
|||||||
businessId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
businessId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
||||||
orderType: ONE_TIME
|
orderType: ONE_TIME
|
||||||
status: COMPLETED
|
status: COMPLETED
|
||||||
eventName: "Krow Friday Preview"
|
eventName: "KROW Friday Preview"
|
||||||
teamHubId: "22a0b119-e6dc-4011-9043-d857cd4c12f3"
|
teamHubId: "22a0b119-e6dc-4011-9043-d857cd4c12f3"
|
||||||
date: "2026-01-30T05:00:00Z"
|
date: "2026-01-30T05:00:00Z"
|
||||||
requested: 2
|
requested: 2
|
||||||
@@ -613,7 +613,7 @@ mutation seedAll @transaction {
|
|||||||
shift_01: shift_insert(
|
shift_01: shift_insert(
|
||||||
data: {
|
data: {
|
||||||
id: "97475714-44d9-4a52-8486-672977689bc0"
|
id: "97475714-44d9-4a52-8486-672977689bc0"
|
||||||
title: "Krow Opening Night Shift"
|
title: "KROW Opening Night Shift"
|
||||||
orderId: "0e3b8fbb-ffd7-496d-a20a-2375b9205f54"
|
orderId: "0e3b8fbb-ffd7-496d-a20a-2375b9205f54"
|
||||||
date: "2026-01-26T05:00:00Z"
|
date: "2026-01-26T05:00:00Z"
|
||||||
startTime: "2026-01-26T14:00:00Z"
|
startTime: "2026-01-26T14:00:00Z"
|
||||||
@@ -682,7 +682,7 @@ mutation seedAll @transaction {
|
|||||||
shift_04: shift_insert(
|
shift_04: shift_insert(
|
||||||
data: {
|
data: {
|
||||||
id: "dafa7ede-5245-436c-af4a-1d1f20d68ab5"
|
id: "dafa7ede-5245-436c-af4a-1d1f20d68ab5"
|
||||||
title: "Krow Partner Showcase Shift"
|
title: "KROW Partner Showcase Shift"
|
||||||
orderId: "83b7dd83-2223-4585-a75f-b247368ebfcb"
|
orderId: "83b7dd83-2223-4585-a75f-b247368ebfcb"
|
||||||
date: "2026-01-28T05:00:00Z"
|
date: "2026-01-28T05:00:00Z"
|
||||||
startTime: "2026-01-28T14:00:00Z"
|
startTime: "2026-01-28T14:00:00Z"
|
||||||
@@ -751,7 +751,7 @@ mutation seedAll @transaction {
|
|||||||
shift_07: shift_insert(
|
shift_07: shift_insert(
|
||||||
data: {
|
data: {
|
||||||
id: "7dc230cb-5680-4799-b45a-8a8269675a42"
|
id: "7dc230cb-5680-4799-b45a-8a8269675a42"
|
||||||
title: "Krow Friday Preview Shift"
|
title: "KROW Friday Preview Shift"
|
||||||
orderId: "c3c5dca6-c8f9-4948-bb8c-10d8129914b3"
|
orderId: "c3c5dca6-c8f9-4948-bb8c-10d8129914b3"
|
||||||
date: "2026-01-30T05:00:00Z"
|
date: "2026-01-30T05:00:00Z"
|
||||||
startTime: "2026-01-30T14:00:00Z"
|
startTime: "2026-01-30T14:00:00Z"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ mutation seedAll @transaction {
|
|||||||
data: {
|
data: {
|
||||||
id: "dvpWnaBjT6UksS5lo04hfMTyq1q1"
|
id: "dvpWnaBjT6UksS5lo04hfMTyq1q1"
|
||||||
email: "legendary@krowd.com"
|
email: "legendary@krowd.com"
|
||||||
fullName: "Krow Payements"
|
fullName: "KROW Payements"
|
||||||
role: USER
|
role: USER
|
||||||
userRole: "BUSINESS"
|
userRole: "BUSINESS"
|
||||||
}
|
}
|
||||||
@@ -24,9 +24,9 @@ mutation seedAll @transaction {
|
|||||||
business_1: business_insert(
|
business_1: business_insert(
|
||||||
data: {
|
data: {
|
||||||
id: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
id: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
||||||
businessName: "Krow"
|
businessName: "KROW"
|
||||||
userId: "dvpWnaBjT6UksS5lo04hfMTyq1q1"
|
userId: "dvpWnaBjT6UksS5lo04hfMTyq1q1"
|
||||||
contactName: "Krow Payements"
|
contactName: "KROW Payements"
|
||||||
email: "legendary@krowd.com"
|
email: "legendary@krowd.com"
|
||||||
phone: "+1-818-555-0148"
|
phone: "+1-818-555-0148"
|
||||||
address: "5000 San Jose Street, Granada Hills, CA, USA"
|
address: "5000 San Jose Street, Granada Hills, CA, USA"
|
||||||
@@ -42,9 +42,9 @@ mutation seedAll @transaction {
|
|||||||
team_1: team_insert(
|
team_1: team_insert(
|
||||||
data: {
|
data: {
|
||||||
id: "9508c187-7612-4084-90de-4ece4a63773f"
|
id: "9508c187-7612-4084-90de-4ece4a63773f"
|
||||||
teamName: "Krow"
|
teamName: "KROW"
|
||||||
ownerId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
ownerId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
||||||
ownerName: "Krow"
|
ownerName: "KROW"
|
||||||
ownerRole: "ADMIN"
|
ownerRole: "ADMIN"
|
||||||
totalHubs: 3
|
totalHubs: 3
|
||||||
}
|
}
|
||||||
@@ -288,7 +288,7 @@ mutation seedAll @transaction {
|
|||||||
businessId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
businessId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
||||||
orderType: ONE_TIME
|
orderType: ONE_TIME
|
||||||
status: COMPLETED
|
status: COMPLETED
|
||||||
eventName: "Krow Opening Night"
|
eventName: "KROW Opening Night"
|
||||||
teamHubId: "22a0b119-e6dc-4011-9043-d857cd4c12f3"
|
teamHubId: "22a0b119-e6dc-4011-9043-d857cd4c12f3"
|
||||||
date: "2026-01-26T05:00:00Z"
|
date: "2026-01-26T05:00:00Z"
|
||||||
requested: 1
|
requested: 1
|
||||||
@@ -330,7 +330,7 @@ mutation seedAll @transaction {
|
|||||||
businessId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
businessId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
||||||
orderType: ONE_TIME
|
orderType: ONE_TIME
|
||||||
status: COMPLETED
|
status: COMPLETED
|
||||||
eventName: "Krow Partner Showcase"
|
eventName: "KROW Partner Showcase"
|
||||||
teamHubId: "22a0b119-e6dc-4011-9043-d857cd4c12f3"
|
teamHubId: "22a0b119-e6dc-4011-9043-d857cd4c12f3"
|
||||||
date: "2026-01-28T05:00:00Z"
|
date: "2026-01-28T05:00:00Z"
|
||||||
requested: 1
|
requested: 1
|
||||||
@@ -372,7 +372,7 @@ mutation seedAll @transaction {
|
|||||||
businessId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
businessId: "ef69e942-d6e5-48e5-a8bc-69d3faa63b2f"
|
||||||
orderType: ONE_TIME
|
orderType: ONE_TIME
|
||||||
status: COMPLETED
|
status: COMPLETED
|
||||||
eventName: "Krow Friday Preview"
|
eventName: "KROW Friday Preview"
|
||||||
teamHubId: "22a0b119-e6dc-4011-9043-d857cd4c12f3"
|
teamHubId: "22a0b119-e6dc-4011-9043-d857cd4c12f3"
|
||||||
date: "2026-01-30T05:00:00Z"
|
date: "2026-01-30T05:00:00Z"
|
||||||
requested: 2
|
requested: 2
|
||||||
@@ -566,7 +566,7 @@ mutation seedAll @transaction {
|
|||||||
shift_01: shift_insert(
|
shift_01: shift_insert(
|
||||||
data: {
|
data: {
|
||||||
id: "97475714-44d9-4a52-8486-672977689bc0"
|
id: "97475714-44d9-4a52-8486-672977689bc0"
|
||||||
title: "Krow Opening Night Shift"
|
title: "KROW Opening Night Shift"
|
||||||
orderId: "0e3b8fbb-ffd7-496d-a20a-2375b9205f54"
|
orderId: "0e3b8fbb-ffd7-496d-a20a-2375b9205f54"
|
||||||
date: "2026-01-26T05:00:00Z"
|
date: "2026-01-26T05:00:00Z"
|
||||||
startTime: "2026-01-26T14:00:00Z"
|
startTime: "2026-01-26T14:00:00Z"
|
||||||
@@ -635,7 +635,7 @@ mutation seedAll @transaction {
|
|||||||
shift_04: shift_insert(
|
shift_04: shift_insert(
|
||||||
data: {
|
data: {
|
||||||
id: "dafa7ede-5245-436c-af4a-1d1f20d68ab5"
|
id: "dafa7ede-5245-436c-af4a-1d1f20d68ab5"
|
||||||
title: "Krow Partner Showcase Shift"
|
title: "KROW Partner Showcase Shift"
|
||||||
orderId: "83b7dd83-2223-4585-a75f-b247368ebfcb"
|
orderId: "83b7dd83-2223-4585-a75f-b247368ebfcb"
|
||||||
date: "2026-01-28T05:00:00Z"
|
date: "2026-01-28T05:00:00Z"
|
||||||
startTime: "2026-01-28T14:00:00Z"
|
startTime: "2026-01-28T14:00:00Z"
|
||||||
@@ -704,7 +704,7 @@ mutation seedAll @transaction {
|
|||||||
shift_07: shift_insert(
|
shift_07: shift_insert(
|
||||||
data: {
|
data: {
|
||||||
id: "7dc230cb-5680-4799-b45a-8a8269675a42"
|
id: "7dc230cb-5680-4799-b45a-8a8269675a42"
|
||||||
title: "Krow Friday Preview Shift"
|
title: "KROW Friday Preview Shift"
|
||||||
orderId: "c3c5dca6-c8f9-4948-bb8c-10d8129914b3"
|
orderId: "c3c5dca6-c8f9-4948-bb8c-10d8129914b3"
|
||||||
date: "2026-01-30T05:00:00Z"
|
date: "2026-01-30T05:00:00Z"
|
||||||
startTime: "2026-01-30T14:00:00Z"
|
startTime: "2026-01-30T14:00:00Z"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# Krow Platform: System Architecture Overview
|
# KROW Platform: System Architecture Overview
|
||||||
|
|
||||||
## 1. Executive Summary: The Business Purpose
|
## 1. Executive Summary: The Business Purpose
|
||||||
The **Krow Platform** is an end-to-end workforce management ecosystem designed to bridge the gap between businesses that need staff ("Clients") and the temporary workers who fill those roles ("Staff").
|
The **KROW Platform** is an end-to-end workforce management ecosystem designed to bridge the gap between businesses that need staff ("Clients") and the temporary workers who fill those roles ("Staff").
|
||||||
|
|
||||||
Traditionally, this process involves phone calls, paper timesheets, and manual payroll. Krow digitizes the entire lifecycle:
|
Traditionally, this process involves phone calls, paper timesheets, and manual payroll. KROW digitizes the entire lifecycle:
|
||||||
1. **Finding Work:** Clients post shifts instantly; workers claim them via mobile.
|
1. **Finding Work:** Clients post shifts instantly; workers claim them via mobile.
|
||||||
2. **Doing Work:** GPS-verified clock-ins and digital timesheets ensure accuracy.
|
2. **Doing Work:** GPS-verified clock-ins and digital timesheets ensure accuracy.
|
||||||
3. **Managing Business:** A web dashboard provides analytics, billing, and compliance oversight.
|
3. **Managing Business:** A web dashboard provides analytics, billing, and compliance oversight.
|
||||||
@@ -23,7 +23,7 @@ The platform consists of three distinct applications, each tailored to a specifi
|
|||||||
* **Role:** The supply pool. It acts as their personal agency, handling job discovery, schedule management, and instant payouts.
|
* **Role:** The supply pool. It acts as their personal agency, handling job discovery, schedule management, and instant payouts.
|
||||||
* **Key Value:** Flexibility and financial security. Workers choose when they work and get paid faster.
|
* **Key Value:** Flexibility and financial security. Workers choose when they work and get paid faster.
|
||||||
|
|
||||||
### C. Krow Web Application (The "HQ")
|
### C. KROW Web Application (The "HQ")
|
||||||
* **User:** Administrators, HR, Finance, and Client Executives.
|
* **User:** Administrators, HR, Finance, and Client Executives.
|
||||||
* **Role:** The command center. It handles the heavy lifting—complex invoicing, vendor management, compliance audits, and strategic data analysis.
|
* **Role:** The command center. It handles the heavy lifting—complex invoicing, vendor management, compliance audits, and strategic data analysis.
|
||||||
* **Key Value:** Control and insight. It turns operational data into cost-saving strategies.
|
* **Key Value:** Control and insight. It turns operational data into cost-saving strategies.
|
||||||
@@ -51,7 +51,7 @@ To maintain privacy and organization, data is strictly compartmentalized:
|
|||||||
|
|
||||||
* **Worker Data:** Owned by the worker but accessible to the platform. Clients can only see limited details (Name, Rating, Skills) of workers assigned to *their* specific shifts. They cannot see a worker's full financial history or assignments with other clients.
|
* **Worker Data:** Owned by the worker but accessible to the platform. Clients can only see limited details (Name, Rating, Skills) of workers assigned to *their* specific shifts. They cannot see a worker's full financial history or assignments with other clients.
|
||||||
* **Client Data:** Owned by the business. Workers see only what is necessary to do the job (Location, Dress Code, Supervisor Name). They cannot see the client's internal billing or strategic reports.
|
* **Client Data:** Owned by the business. Workers see only what is necessary to do the job (Location, Dress Code, Supervisor Name). They cannot see the client's internal billing or strategic reports.
|
||||||
* **Platform Data:** owned by Krow (Admins). This includes the aggregate data used for "Smart Strategies" and market analysis—e.g., "Average hourly rate for a Bartender in downtown."
|
* **Platform Data:** owned by KROW (Admins). This includes the aggregate data used for "Smart Strategies" and market analysis—e.g., "Average hourly rate for a Bartender in downtown."
|
||||||
|
|
||||||
## 6. Security & Access Control
|
## 6. Security & Access Control
|
||||||
The system operates on a **Role-Based Access Control (RBAC)** model:
|
The system operates on a **Role-Based Access Control (RBAC)** model:
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ This document details the primary business actions and user flows within the **C
|
|||||||
|
|
||||||
### 1.2 Register Business Account (Sign Up)
|
### 1.2 Register Business Account (Sign Up)
|
||||||
* **Actor:** New Business Manager
|
* **Actor:** New Business Manager
|
||||||
* **Description:** Creating a new identity for the business on the Krow platform.
|
* **Description:** Creating a new identity for the business on the KROW platform.
|
||||||
* **Main Flow:**
|
* **Main Flow:**
|
||||||
1. User taps "Sign Up".
|
1. User taps "Sign Up".
|
||||||
2. User enters company details (Name, Industry).
|
2. User enters company details (Name, Industry).
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ This document details the primary business actions available within the **Staff
|
|||||||
|
|
||||||
### 1.2 Onboarding & Registration
|
### 1.2 Onboarding & Registration
|
||||||
* **Actor:** New Worker
|
* **Actor:** New Worker
|
||||||
* **Description:** Creating a new profile to join the Krow network.
|
* **Description:** Creating a new profile to join the KROW network.
|
||||||
* **Main Flow:**
|
* **Main Flow:**
|
||||||
1. Worker enters phone number.
|
1. Worker enters phone number.
|
||||||
2. System sends SMS OTP.
|
2. System sends SMS OTP.
|
||||||
@@ -111,10 +111,10 @@ This document details the primary business actions available within the **Staff
|
|||||||
* **Description:** Submitting legal employment forms.
|
* **Description:** Submitting legal employment forms.
|
||||||
* **Main Flow:** Navigate to "Tax Forms" -> Complete W-4 or I-9 digitally -> Sign and Submit.
|
* **Main Flow:** Navigate to "Tax Forms" -> Complete W-4 or I-9 digitally -> Sign and Submit.
|
||||||
|
|
||||||
### 5.3 Krow University Training
|
### 5.3 KROW University Training
|
||||||
* **Actor:** Temporary Worker
|
* **Actor:** Temporary Worker
|
||||||
* **Description:** Improving skills to unlock better jobs.
|
* **Description:** Improving skills to unlock better jobs.
|
||||||
* **Main Flow:** Navigate to "Krow University" -> Select Module -> Watch Video/Take Quiz -> Earn Badge.
|
* **Main Flow:** Navigate to "KROW University" -> Select Module -> Watch Video/Take Quiz -> Earn Badge.
|
||||||
|
|
||||||
### 5.4 Account Settings
|
### 5.4 Account Settings
|
||||||
* **Actor:** Temporary Worker
|
* **Actor:** Temporary Worker
|
||||||
@@ -193,7 +193,7 @@ flowchart TD
|
|||||||
ComplianceMenu --> UploadDocs[Upload Certificates]
|
ComplianceMenu --> UploadDocs[Upload Certificates]
|
||||||
ComplianceMenu --> TaxForms["Manage Tax Forms (W-4/I-9)"]
|
ComplianceMenu --> TaxForms["Manage Tax Forms (W-4/I-9)"]
|
||||||
|
|
||||||
Profile --> KrowUniversity[Krow University]
|
Profile --> KrowUniversity[KROW University]
|
||||||
KrowUniversity --> StartTraining[Start Training Module]
|
KrowUniversity --> StartTraining[Start Training Module]
|
||||||
|
|
||||||
Profile --> BankAccount[Manage Bank Details]
|
Profile --> BankAccount[Manage Bank Details]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# The Krow Platform System Bible
|
# The KROW Platform System Bible
|
||||||
|
|
||||||
**Status:** Official / Living Document
|
**Status:** Official / Living Document
|
||||||
**Version:** 1.0.0
|
**Version:** 1.0.0
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
## 1. Executive Summary
|
## 1. Executive Summary
|
||||||
|
|
||||||
### What the System Is
|
### What the System Is
|
||||||
The **Krow Platform** is a multi-sided workforce management ecosystem that digitizes the entire lifecycle of temporary staffing. It replaces fragmented, manual processes (phone calls, spreadsheets, paper timesheets) with a unified digital infrastructure connecting businesses ("Clients") directly with temporary workers ("Staff").
|
The **KROW Platform** is a multi-sided workforce management ecosystem that digitizes the entire lifecycle of temporary staffing. It replaces fragmented, manual processes (phone calls, spreadsheets, paper timesheets) with a unified digital infrastructure connecting businesses ("Clients") directly with temporary workers ("Staff").
|
||||||
|
|
||||||
### Why It Exists
|
### Why It Exists
|
||||||
The temporary staffing industry suffers from friction, lack of transparency, and delayed payments. Businesses struggle to find reliable staff quickly, while workers face uncertain schedules and slow wage access. Krow exists to remove this friction, ensuring shifts are filled instantly, work is verified accurately, and payments are processed swiftly.
|
The temporary staffing industry suffers from friction, lack of transparency, and delayed payments. Businesses struggle to find reliable staff quickly, while workers face uncertain schedules and slow wage access. KROW exists to remove this friction, ensuring shifts are filled instantly, work is verified accurately, and payments are processed swiftly.
|
||||||
|
|
||||||
### Who It Serves
|
### Who It Serves
|
||||||
1. **Clients (Businesses):** Venue managers and owners who need on-demand or scheduled staff.
|
1. **Clients (Businesses):** Venue managers and owners who need on-demand or scheduled staff.
|
||||||
@@ -19,7 +19,7 @@ The temporary staffing industry suffers from friction, lack of transparency, and
|
|||||||
3. **Admins (Operations):** Internal teams managing the marketplace, compliance, and financial flows.
|
3. **Admins (Operations):** Internal teams managing the marketplace, compliance, and financial flows.
|
||||||
|
|
||||||
### High-Level Value Proposition
|
### High-Level Value Proposition
|
||||||
Krow transforms labor from a manual logistical headache into a streamlined digital asset. For clients, it provides "staff on tap" with verified compliance. For workers, it offers "freedom and instant pay." For the platform operators, it delivers data-driven oversight of a complex marketplace.
|
KROW transforms labor from a manual logistical headache into a streamlined digital asset. For clients, it provides "staff on tap" with verified compliance. For workers, it offers "freedom and instant pay." For the platform operators, it delivers data-driven oversight of a complex marketplace.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ The ecosystem comprises three distinct applications, each serving a specific use
|
|||||||
* **Responsibility:** Supply fulfillment. Empowering workers to find jobs, manage their schedule, verify their presence (Clock In), and access earnings.
|
* **Responsibility:** Supply fulfillment. Empowering workers to find jobs, manage their schedule, verify their presence (Clock In), and access earnings.
|
||||||
* **Concept:** The worker's "Digital Agency" in their pocket.
|
* **Concept:** The worker's "Digital Agency" in their pocket.
|
||||||
|
|
||||||
### 3. Krow Web Application (The "HQ")
|
### 3. KROW Web Application (The "HQ")
|
||||||
* **Platform:** React (Web)
|
* **Platform:** React (Web)
|
||||||
* **Responsibility:** Ecosystem governance. The command center for high-level analytics, complex financial operations (invoicing/payouts), vendor management, and system administration.
|
* **Responsibility:** Ecosystem governance. The command center for high-level analytics, complex financial operations (invoicing/payouts), vendor management, and system administration.
|
||||||
* **Concept:** The "Mission Control" for the business backend.
|
* **Concept:** The "Mission Control" for the business backend.
|
||||||
@@ -69,7 +69,7 @@ The ecosystem comprises three distinct applications, each serving a specific use
|
|||||||
|
|
||||||
## 4. System Architecture Overview
|
## 4. System Architecture Overview
|
||||||
|
|
||||||
The Krow Platform follows a **Service-Oriented Architecture (SOA)** where multiple front-end clients interface with a shared, monolithic logical backend (exposed via API Gateway).
|
The KROW Platform follows a **Service-Oriented Architecture (SOA)** where multiple front-end clients interface with a shared, monolithic logical backend (exposed via API Gateway).
|
||||||
|
|
||||||
### Architectural Style
|
### Architectural Style
|
||||||
* **Centralized State:** A single backend database serves as the source of truth for all apps.
|
* **Centralized State:** A single backend database serves as the source of truth for all apps.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Web Application: Use Case Overview
|
# Web Application: Use Case Overview
|
||||||
|
|
||||||
This document details the primary business actions and user flows within the **Krow Web Application**. It is organized according to the logical workflows for each primary user role as defined in the system's architecture.
|
This document details the primary business actions and user flows within the **KROW Web Application**. It is organized according to the logical workflows for each primary user role as defined in the system's architecture.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Krow Workforce – Backend Manual
|
# KROW Workforce – Backend Manual
|
||||||
Firebase Data Connect + Cloud SQL (PostgreSQL)
|
Firebase Data Connect + Cloud SQL (PostgreSQL)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
**Client Account (Business User):**
|
**Client Account (Business User):**
|
||||||
- Email: `legendary@krowd.com`
|
- Email: `legendary@krowd.com`
|
||||||
- Password: `Demo2026!`
|
- Password: `Demo2026!`
|
||||||
- Client Name: "Krow"
|
- Client Name: "KROW"
|
||||||
|
|
||||||
**Staff Account (Worker):**
|
**Staff Account (Worker):**
|
||||||
- Phone: `+15557654321`
|
- Phone: `+15557654321`
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
- Suggestion: Use Google Maps to suggest only city names. Currently users can type anything, which will cause misspellings and inconsistent data. Important for the max distance feature.
|
- Suggestion: Use Google Maps to suggest only city names. Currently users can type anything, which will cause misspellings and inconsistent data. Important for the max distance feature.
|
||||||
|
|
||||||
**Home page:**
|
**Home page:**
|
||||||
- Same flickering issue — shows "Krower" briefly before displaying the real name.
|
- Same flickering issue — shows "KROWER" briefly before displaying the real name.
|
||||||
|
|
||||||
**Profile page:**
|
**Profile page:**
|
||||||
- Phone number should be read-only, or require re-verification if changed.
|
- Phone number should be read-only, or require re-verification if changed.
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ This demo showcases the progress of the milestone 3.
|
|||||||
**Client Account (Business User):**
|
**Client Account (Business User):**
|
||||||
- Email: `legendary@krowd.com`
|
- Email: `legendary@krowd.com`
|
||||||
- Password: `Demo2026!`
|
- Password: `Demo2026!`
|
||||||
- Client Name: "Krow"
|
- Client Name: "KROW"
|
||||||
|
|
||||||
**Staff Account (Worker):**
|
**Staff Account (Worker):**
|
||||||
- Phone: `+15557654321`
|
- Phone: `+15557654321`
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ Context:
|
|||||||
3. Legendary uses its own workforce, and can still route overflow to approved vendors.
|
3. Legendary uses its own workforce, and can still route overflow to approved vendors.
|
||||||
|
|
||||||
Actor mapping (text):
|
Actor mapping (text):
|
||||||
1. Tenant: `Legendary Event Staffing and Entertainment` (the company using Krow).
|
1. Tenant: `Legendary Event Staffing and Entertainment` (the company using KROW).
|
||||||
2. User: `Wil` (ops lead), `Maria` (Google client manager), `Omar` (Google procurement approver), `Jose` (vendor scheduler), `Ana` (worker).
|
2. User: `Wil` (ops lead), `Maria` (Google client manager), `Omar` (Google procurement approver), `Jose` (vendor scheduler), `Ana` (worker).
|
||||||
3. TenantMembership:
|
3. TenantMembership:
|
||||||
4. `Wil` is `admin` in Legendary tenant.
|
4. `Wil` is `admin` in Legendary tenant.
|
||||||
@@ -109,7 +109,7 @@ Context:
|
|||||||
3. Peakline primarily fulfills demand through external approved vendors.
|
3. Peakline primarily fulfills demand through external approved vendors.
|
||||||
|
|
||||||
Actor mapping (text):
|
Actor mapping (text):
|
||||||
1. Tenant: `Peakline Events` (another staffing company using Krow).
|
1. Tenant: `Peakline Events` (another staffing company using KROW).
|
||||||
2. User: `Chris` (operations coordinator), `Nina` (client manager), `Sam` (vendor manager), `Leo` (worker).
|
2. User: `Chris` (operations coordinator), `Nina` (client manager), `Sam` (vendor manager), `Leo` (worker).
|
||||||
3. TenantMembership:
|
3. TenantMembership:
|
||||||
4. `Chris` is `admin` in Peakline tenant.
|
4. `Chris` is `admin` in Peakline tenant.
|
||||||
|
|||||||
@@ -13,19 +13,19 @@ This avoids two failure modes:
|
|||||||
|
|
||||||
## 2) Inputs reviewed
|
## 2) Inputs reviewed
|
||||||
All 13 roadmap exports from `/Users/wiel/Downloads`:
|
All 13 roadmap exports from `/Users/wiel/Downloads`:
|
||||||
1. `Krow App – Roadmap - Business App_ Google, Nvidia.csv`
|
1. `KROW App – Roadmap - Business App_ Google, Nvidia.csv`
|
||||||
2. `Krow App – Roadmap - Client_ Google, Nvidia.csv`
|
2. `KROW App – Roadmap - Client_ Google, Nvidia.csv`
|
||||||
3. `Krow App – Roadmap - Compass- The Operator.csv`
|
3. `KROW App – Roadmap - Compass- The Operator.csv`
|
||||||
4. `Krow App – Roadmap - Employee App.csv`
|
4. `KROW App – Roadmap - Employee App.csv`
|
||||||
5. `Krow App – Roadmap - Features.csv`
|
5. `KROW App – Roadmap - Features.csv`
|
||||||
6. `Krow App – Roadmap - FoodBuy- Procurement.csv`
|
6. `KROW App – Roadmap - FoodBuy- Procurement.csv`
|
||||||
7. `Krow App – Roadmap - KROW Dashboard.csv`
|
7. `KROW App – Roadmap - KROW Dashboard.csv`
|
||||||
8. `Krow App – Roadmap - Offenses.csv`
|
8. `KROW App – Roadmap - Offenses.csv`
|
||||||
9. `Krow App – Roadmap - Partner.csv`
|
9. `KROW App – Roadmap - Partner.csv`
|
||||||
10. `Krow App – Roadmap - Roadmap.csv`
|
10. `KROW App – Roadmap - Roadmap.csv`
|
||||||
11. `Krow App – Roadmap - Sectors_ BA, Flik ( The executors).csv`
|
11. `KROW App – Roadmap - Sectors_ BA, Flik ( The executors).csv`
|
||||||
12. `Krow App – Roadmap - The Workforce_ Employees.csv`
|
12. `KROW App – Roadmap - The Workforce_ Employees.csv`
|
||||||
13. `Krow App – Roadmap - Vendor_ Legendary (Staffing).csv`
|
13. `KROW App – Roadmap - Vendor_ Legendary (Staffing).csv`
|
||||||
|
|
||||||
Parsed signal:
|
Parsed signal:
|
||||||
1. 983 non-empty task lines.
|
1. 983 non-empty task lines.
|
||||||
|
|||||||
@@ -211,9 +211,9 @@
|
|||||||
| 5.1 Manage Compliance Documents | View/Manage Identity Documents | ✅ | ✅ | ✅ Completed | `documents_page.dart` with `documents_progress_card.dart`. |
|
| 5.1 Manage Compliance Documents | View/Manage Identity Documents | ✅ | ✅ | ✅ Completed | `documents_page.dart` with `documents_progress_card.dart`. |
|
||||||
| 5.2 Manage Tax Forms | Complete W-4 digitally & submit | ✅ | ✅ | ✅ Completed | `form_w4_page.dart` + `FormW4Cubit` fully implemented. |
|
| 5.2 Manage Tax Forms | Complete W-4 digitally & submit | ✅ | ✅ | ✅ Completed | `form_w4_page.dart` + `FormW4Cubit` fully implemented. |
|
||||||
| 5.2 Manage Tax Forms | Complete I-9 digitally & submit | ✅ | ✅ | ✅ Completed | `form_i9_page.dart` + `FormI9Cubit` fully implemented. |
|
| 5.2 Manage Tax Forms | Complete I-9 digitally & submit | ✅ | ✅ | ✅ Completed | `form_i9_page.dart` + `FormI9Cubit` fully implemented. |
|
||||||
| 5.3 Krow University Training | Navigate to Krow University | ✅ | ❌ | ❌ Not Implemented | `krow_university_screen.dart` exists **only** in prototype. No `krow_university` or training package in real app feature modules. |
|
| 5.3 KROW University Training | Navigate to KROW University | ✅ | ❌ | ❌ Not Implemented | `krow_university_screen.dart` exists **only** in prototype. No `krow_university` or training package in real app feature modules. |
|
||||||
| 5.3 Krow University Training | Select Module → Watch Video / Take Quiz | ✅ | ❌ | ⚠️ Prototype Only | Fully prototyped (courses, categories, XP tracking). Not migrated at all. |
|
| 5.3 KROW University Training | Select Module → Watch Video / Take Quiz | ✅ | ❌ | ⚠️ Prototype Only | Fully prototyped (courses, categories, XP tracking). Not migrated at all. |
|
||||||
| 5.3 Krow University Training | Earn Badge | ✅ | ❌ | ⚠️ Prototype Only | Prototype only. |
|
| 5.3 KROW University Training | Earn Badge | ✅ | ❌ | ⚠️ Prototype Only | Prototype only. |
|
||||||
| 5.4 Account Settings | Update Bank Details | ✅ | ✅ | ✅ Completed | `bank_account_page.dart` + `BankAccountCubit` in `profile_sections/finances/staff_bank_account`. |
|
| 5.4 Account Settings | Update Bank Details | ✅ | ✅ | ✅ Completed | `bank_account_page.dart` + `BankAccountCubit` in `profile_sections/finances/staff_bank_account`. |
|
||||||
| 5.4 Account Settings | View Benefits | ✅ | ❌ | ⚠️ Prototype Only | `benefits_screen.dart` exists only in prototype. No `benefits` package in real app. |
|
| 5.4 Account Settings | View Benefits | ✅ | ❌ | ⚠️ Prototype Only | `benefits_screen.dart` exists only in prototype. No `benefits` package in real app. |
|
||||||
| 5.4 Account Settings | Access Support / FAQs | ✅ | ✅ | ✅ Completed | `faqs_page.dart` with `FAQsBloc` and search in `profile_sections/support/faqs`. |
|
| 5.4 Account Settings | Access Support / FAQs | ✅ | ✅ | ✅ Completed | `faqs_page.dart` with `FAQsBloc` and search in `profile_sections/support/faqs`. |
|
||||||
@@ -263,7 +263,7 @@
|
|||||||
|
|
||||||
The following are **high-priority missing flows** that block core business value:
|
The following are **high-priority missing flows** that block core business value:
|
||||||
|
|
||||||
1. **Staff: Krow University & Benefits**
|
1. **Staff: KROW University & Benefits**
|
||||||
Several modules exist in the prototype but are missing in the real app, including training Modules, XP tracking, and Benefits views.
|
Several modules exist in the prototype but are missing in the real app, including training Modules, XP tracking, and Benefits views.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -341,7 +341,7 @@ The following screens exist **only** in the prototypes and have no real-app equi
|
|||||||
| Screen | Path |
|
| Screen | Path |
|
||||||
|:---|:---|
|
|:---|:---|
|
||||||
| Benefits | `worker/benefits_screen.dart` |
|
| Benefits | `worker/benefits_screen.dart` |
|
||||||
| Krow University | `worker/worker_profile/level_up/krow_university_screen.dart` |
|
| KROW University | `worker/worker_profile/level_up/krow_university_screen.dart` |
|
||||||
| Leaderboard | `worker/worker_profile/level_up/leaderboard_screen.dart` |
|
| Leaderboard | `worker/worker_profile/level_up/leaderboard_screen.dart` |
|
||||||
| Training Modules | `worker/worker_profile/level_up/trainings_screen.dart` |
|
| Training Modules | `worker/worker_profile/level_up/trainings_screen.dart` |
|
||||||
| In-App Messages | `worker/worker_profile/support/messages_screen.dart` |
|
| In-App Messages | `worker/worker_profile/support/messages_screen.dart` |
|
||||||
@@ -352,7 +352,7 @@ The following screens exist **only** in the prototypes and have no real-app equi
|
|||||||
|
|
||||||
### Sprint Focus Areas (Priority Order)
|
### Sprint Focus Areas (Priority Order)
|
||||||
|
|
||||||
| 🟠 P2 | Migrate Krow University training module from prototype | Large |
|
| 🟠 P2 | Migrate KROW University training module from prototype | Large |
|
||||||
| 🟠 P2 | Migrate Benefits view from prototype | Medium |
|
| 🟠 P2 | Migrate Benefits view from prototype | Medium |
|
||||||
| 🟡 P3 | Migrate Workers List to real app (`client/workers`) | Medium |
|
| 🟡 P3 | Migrate Workers List to real app (`client/workers`) | Medium |
|
||||||
| 🟡 P3 | Formally document undocumented features (NFC, History tab, etc.) | Small |
|
| 🟡 P3 | Formally document undocumented features (NFC, History tab, etc.) | Small |
|
||||||
|
|||||||
@@ -1,266 +0,0 @@
|
|||||||
# KROW Workforce API Contracts
|
|
||||||
|
|
||||||
This document captures all API contracts used by the Staff and Client mobile applications. It serves as a single reference document to understand what each endpoint does, its expected inputs, returned outputs, and any non-obvious details.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Staff Application
|
|
||||||
|
|
||||||
### Authentication / Onboarding Pages (Get Started, Intro, Phone Verification, Profile Setup, Personal Info)
|
|
||||||
#### Setup / User Validation API
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/getUserById` |
|
|
||||||
| **Purpose** | Retrieves the base user profile to determine authentication status and role access (e.g., if user is STAFF). |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `id: UUID!` (Firebase UID) |
|
|
||||||
| **Outputs** | `User { id, email, phone, role }` |
|
|
||||||
| **Notes** | Required after OTP verification to route users. |
|
|
||||||
|
|
||||||
#### Create Default User API
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/createUser` |
|
|
||||||
| **Purpose** | Inserts a base user record into the system during initial signup. |
|
|
||||||
| **Operation** | Mutation |
|
|
||||||
| **Inputs** | `id: UUID!`, `role: UserBaseRole` |
|
|
||||||
| **Outputs** | `id` of newly created User |
|
|
||||||
| **Notes** | Used explicitly during the "Sign Up" flow if the user doesn't exist. |
|
|
||||||
|
|
||||||
#### Get Staff Profile API
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/getStaffByUserId` |
|
|
||||||
| **Purpose** | Finds the specific Staff record associated with the base user ID. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `userId: UUID!` |
|
|
||||||
| **Outputs** | `Staffs { id, userId, fullName, email, phone, photoUrl, status }` |
|
|
||||||
| **Notes** | Needed to verify if a complete staff profile exists before fully authenticating. |
|
|
||||||
|
|
||||||
#### Update Staff Profile API
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/updateStaff` |
|
|
||||||
| **Purpose** | Saves onboarding data across Personal Info, Experience, and Preferred Locations pages. |
|
|
||||||
| **Operation** | Mutation |
|
|
||||||
| **Inputs** | `id: UUID!`, `fullName`, `email`, `phone`, `addres`, etc. |
|
|
||||||
| **Outputs** | `id` |
|
|
||||||
| **Notes** | Called incrementally during profile setup wizard. |
|
|
||||||
|
|
||||||
### Home Page (worker_home_page.dart) & Benefits Overview
|
|
||||||
#### Load Today/Tomorrow Shifts
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/getApplicationsByStaffId` |
|
|
||||||
| **Purpose** | Retrieves applications (shifts) assigned to the current staff member within a specific date range. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `staffId: UUID!`, `dayStart: Timestamp`, `dayEnd: Timestamp` |
|
|
||||||
| **Outputs** | `Applications { shift, shiftRole, status, createdAt }` |
|
|
||||||
| **Notes** | The frontend filters the query response for `CONFIRMED` applications to display "Today's" and "Tomorrow's" shifts. |
|
|
||||||
|
|
||||||
#### List Recommended Shifts
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/listShifts` |
|
|
||||||
| **Purpose** | Fetches open shifts that are available for the staff to apply to. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | None directly mapped, but filters OPEN shifts purely on the client side at the time. |
|
|
||||||
| **Outputs** | `Shifts { id, title, orderId, cost, location, startTime, endTime, status }` |
|
|
||||||
| **Notes** | Limits output to 10 on the frontend. Should ideally rely on a `$status: OPEN` parameter. |
|
|
||||||
|
|
||||||
#### Benefits Summary API
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/listBenefitsDataByStaffId` |
|
|
||||||
| **Purpose** | Retrieves accrued benefits (e.g., Sick time, Vacation) to display on the home screen. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `staffId: UUID!` |
|
|
||||||
| **Outputs** | `BenefitsDatas { vendorBenefitPlan { title, total }, current }` |
|
|
||||||
| **Notes** | Calculates `usedHours = total - current`. |
|
|
||||||
|
|
||||||
### Find Shifts / Shift Details Pages (shifts_page.dart)
|
|
||||||
#### List Available Shifts Filtered
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/filterShifts` |
|
|
||||||
| **Purpose** | Used to fetch Open Shifts in specific regions when the worker searches in the "Find Shifts" tab. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `$status: ShiftStatus`, `$dateFrom: Timestamp`, `$dateTo: Timestamp` |
|
|
||||||
| **Outputs** | `Shifts { id, title, location, cost, durationDays, order { business, vendor } }` |
|
|
||||||
| **Notes** | - |
|
|
||||||
|
|
||||||
#### Get Shift Details
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/getShiftById` |
|
|
||||||
| **Purpose** | Gets deeper details for a single shift including exact uniform/managers needed. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `id: UUID!` |
|
|
||||||
| **Outputs** | `Shift { id, title, hours, cost, locationAddress, workersNeeded ... }` |
|
|
||||||
| **Notes** | - |
|
|
||||||
|
|
||||||
#### Apply To Shift
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/createApplication` |
|
|
||||||
| **Purpose** | Worker submits an intent to take an open shift. |
|
|
||||||
| **Operation** | Mutation |
|
|
||||||
| **Inputs** | `shiftId`, `staffId`, `status: APPLIED` |
|
|
||||||
| **Outputs** | `Application ID` |
|
|
||||||
| **Notes** | A shift status will switch to `CONFIRMED` via admin approval. |
|
|
||||||
|
|
||||||
### Availability Page (availability_page.dart)
|
|
||||||
#### Get Default Availability
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/listStaffAvailabilitiesByStaffId` |
|
|
||||||
| **Purpose** | Fetches the standard Mon-Sun recurring availability for a staff member. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `staffId: UUID!` |
|
|
||||||
| **Outputs** | `StaffAvailabilities { dayOfWeek, isAvailable, startTime, endTime }` |
|
|
||||||
| **Notes** | - |
|
|
||||||
|
|
||||||
#### Update Availability
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/updateStaffAvailability` (or `createStaffAvailability`) |
|
|
||||||
| **Purpose** | Upserts availability preferences. |
|
|
||||||
| **Operation** | Mutation |
|
|
||||||
| **Inputs** | `staffId`, `dayOfWeek`, `isAvailable`, `startTime`, `endTime` |
|
|
||||||
| **Outputs** | `id` |
|
|
||||||
| **Notes** | Called individually per day edited. |
|
|
||||||
|
|
||||||
### Payments Page (payments_page.dart)
|
|
||||||
#### Get Recent Payments
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/listRecentPaymentsByStaffId` |
|
|
||||||
| **Purpose** | Loads the history of earnings and timesheets completed by the staff. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `staffId: UUID!` |
|
|
||||||
| **Outputs** | `Payments { amount, processDate, shiftId, status }` |
|
|
||||||
| **Notes** | Displays historical metrics under Earnings tab. |
|
|
||||||
|
|
||||||
### Compliance / Profiles (Agreements, W4, I9, Documents)
|
|
||||||
#### Get Tax Forms
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/getTaxFormsByStaffId` |
|
|
||||||
| **Purpose** | Check the filing status of I9 and W4 forms. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `staffId: UUID!` |
|
|
||||||
| **Outputs** | `TaxForms { formType, isCompleted, updatedDate }` |
|
|
||||||
| **Notes** | Required for staff to be eligible for shifts. |
|
|
||||||
|
|
||||||
#### Update Tax Forms
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/updateTaxForm` |
|
|
||||||
| **Purpose** | Submits state and filing for the given tax form type. |
|
|
||||||
| **Operation** | Mutation |
|
|
||||||
| **Inputs** | `id`, `dataPoints...` |
|
|
||||||
| **Outputs** | `id` |
|
|
||||||
| **Notes** | Updates compliance state. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Client Application
|
|
||||||
|
|
||||||
### Authentication / Intro (Sign In, Get Started)
|
|
||||||
#### Client User Validation API
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/getUserById` |
|
|
||||||
| **Purpose** | Retrieves the base user profile to determine authentication status and role access (e.g., if user is BUSINESS). |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `id: UUID!` (Firebase UID) |
|
|
||||||
| **Outputs** | `User { id, email, phone, userRole }` |
|
|
||||||
| **Notes** | Must check if `userRole == BUSINESS` or `BOTH`. |
|
|
||||||
|
|
||||||
#### Get Business Profile API
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/getBusinessByUserId` |
|
|
||||||
| **Purpose** | Maps the authenticated user to their client business context. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `userId: UUID!` |
|
|
||||||
| **Outputs** | `Business { id, businessName, email, contactName }` |
|
|
||||||
| **Notes** | Used to set the working scopes (Business ID) across the entire app. |
|
|
||||||
|
|
||||||
### Hubs Page (client_hubs_page.dart, edit_hub.dart)
|
|
||||||
#### List Hubs
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/listTeamHubsByBusinessId` |
|
|
||||||
| **Purpose** | Fetches the primary working sites (Hubs) for a client. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `businessId: UUID!` |
|
|
||||||
| **Outputs** | `TeamHubs { id, hubName, address, contact, active }` |
|
|
||||||
| **Notes** | - |
|
|
||||||
|
|
||||||
#### Update / Delete Hub
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/updateTeamHub` / `/deleteTeamHub` |
|
|
||||||
| **Purpose** | Edits or archives a Hub location. |
|
|
||||||
| **Operation** | Mutation |
|
|
||||||
| **Inputs** | `id: UUID!`, `hubName`, `address`, etc (for Update) |
|
|
||||||
| **Outputs** | `id` |
|
|
||||||
| **Notes** | - |
|
|
||||||
|
|
||||||
### Orders Page (create_order, view_orders)
|
|
||||||
#### Create Order
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/createOrder` |
|
|
||||||
| **Purpose** | The client submits a new request for temporary staff (can result in multiple Shifts generated on the backend). |
|
|
||||||
| **Operation** | Mutation |
|
|
||||||
| **Inputs** | `businessId`, `eventName`, `orderType`, `status` |
|
|
||||||
| **Outputs** | `id` (Order ID) |
|
|
||||||
| **Notes** | This creates an order. Shift instances are subsequently created through secondary mutations. |
|
|
||||||
|
|
||||||
#### List Orders
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/getOrdersByBusinessId` |
|
|
||||||
| **Purpose** | Retrieves all ongoing and past staff requests from the client. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `businessId: UUID!` |
|
|
||||||
| **Outputs** | `Orders { id, eventName, shiftCount, status }` |
|
|
||||||
| **Notes** | - |
|
|
||||||
|
|
||||||
### Billing Pages (billing_page.dart, pending_invoices)
|
|
||||||
#### List Invoices
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/listInvoicesByBusinessId` |
|
|
||||||
| **Purpose** | Fetches "Pending", "Paid", and "Disputed" invoices for the client to review. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `businessId: UUID!` |
|
|
||||||
| **Outputs** | `Invoices { id, amountDue, issueDate, status }` |
|
|
||||||
| **Notes** | Used across all Billing view tabs. |
|
|
||||||
|
|
||||||
#### Mark Invoice
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/updateInvoice` |
|
|
||||||
| **Purpose** | Marks an invoice as disputed or pays it (changes status). |
|
|
||||||
| **Operation** | Mutation |
|
|
||||||
| **Inputs** | `id: UUID!`, `status: InvoiceStatus` |
|
|
||||||
| **Outputs** | `id` |
|
|
||||||
| **Notes** | Disputing usually involves setting a memo or flag. |
|
|
||||||
|
|
||||||
### Reports Page (reports_page.dart)
|
|
||||||
#### Get Coverage Stats
|
|
||||||
| Field | Description |
|
|
||||||
|---|---|
|
|
||||||
| **Endpoint name** | `/getCoverageStatsByBusiness` |
|
|
||||||
| **Purpose** | Provides data on fulfillments rates vs actual requests. |
|
|
||||||
| **Operation** | Query |
|
|
||||||
| **Inputs** | `businessId: UUID!` |
|
|
||||||
| **Outputs** | `Stats { totalRequested, totalFilled, percentage }` |
|
|
||||||
| **Notes** | Driven mostly by aggregated backend views. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*This document reflects the current state of Data Connect definitions implemented across the frontend and mapped manually by reviewing Repository and UI logic.*
|
|
||||||
Binary file not shown.
@@ -49,7 +49,7 @@ adb install apps/mobile/apps/staff/build/app/outputs/flutter-apk/app-debug.apk
|
|||||||
# Client login credentials
|
# Client login credentials
|
||||||
export TEST_CLIENT_EMAIL=legendary@krowd.com
|
export TEST_CLIENT_EMAIL=legendary@krowd.com
|
||||||
export TEST_CLIENT_PASSWORD=Demo2026!
|
export TEST_CLIENT_PASSWORD=Demo2026!
|
||||||
export TEST_CLIENT_COMPANY="Krow Demo"
|
export TEST_CLIENT_COMPANY="KROW Demo"
|
||||||
|
|
||||||
# Staff login credentials
|
# Staff login credentials
|
||||||
export TEST_STAFF_PHONE=5557654321
|
export TEST_STAFF_PHONE=5557654321
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ const Layout = () => {
|
|||||||
<aside className="w-72 bg-white border-r border-slate-200 flex flex-col">
|
<aside className="w-72 bg-white border-r border-slate-200 flex flex-col">
|
||||||
<div className="p-6 border-b border-slate-200">
|
<div className="p-6 border-b border-slate-200">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<img src="/logo.svg" alt="Krow Logo" className="h-12 w-12" />
|
<img src="/logo.svg" alt="KROW Logo" className="h-12 w-12" />
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-xl font-bold text-slate-900">KROW</h1>
|
<h1 className="text-xl font-bold text-slate-900">KROW</h1>
|
||||||
<p className="text-xs text-slate-500">API Test Harness ({import.meta.env.VITE_HARNESS_ENVIRONMENT})</p>
|
<p className="text-xs text-slate-500">API Test Harness ({import.meta.env.VITE_HARNESS_ENVIRONMENT})</p>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Krow Workforce – Backend Manual
|
# KROW Workforce – Backend Manual
|
||||||
Firebase Data Connect + Cloud SQL (PostgreSQL)
|
Firebase Data Connect + Cloud SQL (PostgreSQL)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Krow System Architecture Overview
|
# KROW System Architecture Overview
|
||||||
|
|
||||||
## 1. System Purpose: The Digital Workforce Marketplace
|
## 1. System Purpose: The Digital Workforce Marketplace
|
||||||
The **Krow Ecosystem** is a complete digital solution for the temporary staffing industry. Its primary purpose is to seamlessly connect two groups of people:
|
The **KROW Ecosystem** is a complete digital solution for the temporary staffing industry. Its primary purpose is to seamlessly connect two groups of people:
|
||||||
1. **Businesses** (restaurants, event venues) who need temporary staff.
|
1. **Businesses** (restaurants, event venues) who need temporary staff.
|
||||||
2. **Workers** (waiters, security, chefs) who are looking for flexible shifts.
|
2. **Workers** (waiters, security, chefs) who are looking for flexible shifts.
|
||||||
|
|
||||||
Instead of using agencies, phone calls, or spreadsheets, the Krow system automates the entire process: finding staff, verifying their skills, tracking their work hours on-site, and processing payments. It turns a chaotic manual process into a smooth, transparent digital workflow.
|
Instead of using agencies, phone calls, or spreadsheets, the KROW system automates the entire process: finding staff, verifying their skills, tracking their work hours on-site, and processing payments. It turns a chaotic manual process into a smooth, transparent digital workflow.
|
||||||
|
|
||||||
## 2. The Role of Each Application
|
## 2. The Role of Each Application
|
||||||
The system is composed of three distinct applications, each tailored for a specific user:
|
The system is composed of three distinct applications, each tailored for a specific user:
|
||||||
@@ -21,8 +21,8 @@ The system is composed of three distinct applications, each tailored for a speci
|
|||||||
* **Key Analogy:** Like the "Uber Driver" app—accepting jobs and proving the work was done.
|
* **Key Analogy:** Like the "Uber Driver" app—accepting jobs and proving the work was done.
|
||||||
|
|
||||||
### C. The Web Platform (Web & Backend)
|
### C. The Web Platform (Web & Backend)
|
||||||
* **User:** Krow Operations Team (Admins) & System Logic.
|
* **User:** KROW Operations Team (Admins) & System Logic.
|
||||||
* **Role:** The "Brain" and "Control Tower." It stores all the data, enforces the rules (e.g., "You can't work if your visa is expired"), and provides a dashboard for Krow staff to oversee the entire business.
|
* **Role:** The "Brain" and "Control Tower." It stores all the data, enforces the rules (e.g., "You can't work if your visa is expired"), and provides a dashboard for KROW staff to oversee the entire business.
|
||||||
* **Key Analogy:** The Uber HQ dashboard—managing the market, resolving disputes, and ensuring safety.
|
* **Key Analogy:** The Uber HQ dashboard—managing the market, resolving disputes, and ensuring safety.
|
||||||
|
|
||||||
## 3. How They Interact
|
## 3. How They Interact
|
||||||
@@ -50,14 +50,14 @@ To keep the system fast, secure, and reliable, all three apps share a common fou
|
|||||||
The system uses a strict **Role-Based Access Control (RBAC)** model:
|
The system uses a strict **Role-Based Access Control (RBAC)** model:
|
||||||
* **Authentication (Who are you?):** Handled by Firebase. Every request to the server must include a digital "ID Card" (Token) proving the user is logged in.
|
* **Authentication (Who are you?):** Handled by Firebase. Every request to the server must include a digital "ID Card" (Token) proving the user is logged in.
|
||||||
* **Authorization (What can you do?):** Handled by the Web Platform. Even if a Staff member tries to "hack" the app to ask for an Invoice, the Server will check their role, see they are "Staff" (not "Client"), and block the request immediately.
|
* **Authorization (What can you do?):** Handled by the Web Platform. Even if a Staff member tries to "hack" the app to ask for an Invoice, the Server will check their role, see they are "Staff" (not "Client"), and block the request immediately.
|
||||||
* **Verification Gates:** The system includes human-in-the-loop security. A staff member cannot just sign up and work; they enter a "Pending" state until a Krow Admin manually reviews their documents on the Web Platform.
|
* **Verification Gates:** The system includes human-in-the-loop security. A staff member cannot just sign up and work; they enter a "Pending" state until a KROW Admin manually reviews their documents on the Web Platform.
|
||||||
|
|
||||||
## 7. Critical Dependencies
|
## 7. Critical Dependencies
|
||||||
The ecosystem relies on the **Web Platform** being online.
|
The ecosystem relies on the **Web Platform** being online.
|
||||||
* **If the Staff App fails:** Only workers are affected.
|
* **If the Staff App fails:** Only workers are affected.
|
||||||
* **If the Client App fails:** Only businesses are affected.
|
* **If the Client App fails:** Only businesses are affected.
|
||||||
* **If the Web Platform fails:** The entire system stops. No new shifts can be posted, no one can apply, and real-time clock-ins might fail (though the apps have some offline safeguards).
|
* **If the Web Platform fails:** The entire system stops. No new shifts can be posted, no one can apply, and real-time clock-ins might fail (though the apps have some offline safeguards).
|
||||||
* **3rd Party Reliance:** The system depends on **Firebase** (for login) and **Payment Gateways** (to move money). If these global services go down, Krow's ability to log users in or pay them will pause.
|
* **3rd Party Reliance:** The system depends on **Firebase** (for login) and **Payment Gateways** (to move money). If these global services go down, KROW's ability to log users in or pay them will pause.
|
||||||
|
|
||||||
## 8. Overview Diagram
|
## 8. Overview Diagram
|
||||||
```mermaid
|
```mermaid
|
||||||
@@ -75,7 +75,7 @@ flowchart LR
|
|||||||
StaffApp["Staff Mobile App<br/>(Workers)"]:::staffApp
|
StaffApp["Staff Mobile App<br/>(Workers)"]:::staffApp
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Core_Platform["Krow Web Platform"]
|
subgraph Core_Platform["KROW Web Platform"]
|
||||||
direction TB
|
direction TB
|
||||||
AdminPanel["Admin Dashboard<br/>(Operations Team)"]:::backend
|
AdminPanel["Admin Dashboard<br/>(Operations Team)"]:::backend
|
||||||
APIServer["Central API Server<br/>(GraphQL Logic)"]:::backend
|
APIServer["Central API Server<br/>(GraphQL Logic)"]:::backend
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Application Overview: Krow Client Mobile App
|
# Application Overview: KROW Client Mobile App
|
||||||
|
|
||||||
## 1. Executive Summary
|
## 1. Executive Summary
|
||||||
The **Krow Client App** is a mobile workforce management tool designed for business owners and managers. Think of it as a "control center" that allows businesses to easily hire, manage, and pay temporary staff for events or shifts.
|
The **KROW Client App** is a mobile workforce management tool designed for business owners and managers. Think of it as a "control center" that allows businesses to easily hire, manage, and pay temporary staff for events or shifts.
|
||||||
|
|
||||||
Instead of calling agencies or managing spreadsheets, a business user opens this app to:
|
Instead of calling agencies or managing spreadsheets, a business user opens this app to:
|
||||||
* Define a job (Event/Shift).
|
* Define a job (Event/Shift).
|
||||||
@@ -11,10 +11,10 @@ Instead of calling agencies or managing spreadsheets, a business user opens this
|
|||||||
* Handle payments and invoices.
|
* Handle payments and invoices.
|
||||||
|
|
||||||
## 2. High-Level Architecture
|
## 2. High-Level Architecture
|
||||||
The application follows a **Modern Mobile Architecture** designed for reliability and speed. It acts as a "smart interface" that connects the user to the central Krow platform.
|
The application follows a **Modern Mobile Architecture** designed for reliability and speed. It acts as a "smart interface" that connects the user to the central KROW platform.
|
||||||
|
|
||||||
* **The Frontend (This App):** Handles everything the user sees and touches. It validates inputs (like ensuring a shift end time is after the start time) and displays data beautifully.
|
* **The Frontend (This App):** Handles everything the user sees and touches. It validates inputs (like ensuring a shift end time is after the start time) and displays data beautifully.
|
||||||
* **The Bridge (API Layer):** The app talks to the Krow Cloud Server using **GraphQL**. This allows the app to ask for exactly the data it needs—no more, no less—making it fast even on slower connections.
|
* **The Bridge (API Layer):** The app talks to the KROW Cloud Server using **GraphQL**. This allows the app to ask for exactly the data it needs—no more, no less—making it fast even on slower connections.
|
||||||
* **The Backend (The Brain):** All heavy processing (matching staff to jobs, processing payments) happens on the secure server, not on the phone.
|
* **The Backend (The Brain):** All heavy processing (matching staff to jobs, processing payments) happens on the secure server, not on the phone.
|
||||||
|
|
||||||
## 3. Major Components & Modules
|
## 3. Major Components & Modules
|
||||||
@@ -45,9 +45,9 @@ These modules contain the specific business logic and UI for each distinct part
|
|||||||
## 5. External System Communication
|
## 5. External System Communication
|
||||||
The app does not live in isolation; it talks to several outside services:
|
The app does not live in isolation; it talks to several outside services:
|
||||||
|
|
||||||
* **The Krow Backend (GraphQL):** The primary source of truth for all data (Events, Staff profiles, Shifts).
|
* **The KROW Backend (GraphQL):** The primary source of truth for all data (Events, Staff profiles, Shifts).
|
||||||
* **Firebase Auth:** Handles secure login (passwords, email verification) so the Krow team doesn't have to build security from scratch.
|
* **Firebase Auth:** Handles secure login (passwords, email verification) so the KROW team doesn't have to build security from scratch.
|
||||||
* **Firebase Remote Config:** Allows the Krow team to change app settings (like feature flags or text) without forcing users to update the app.
|
* **Firebase Remote Config:** Allows the KROW team to change app settings (like feature flags or text) without forcing users to update the app.
|
||||||
* **NFC Services:** Interacts with physical NFC tags, likely for scanning staff badges or verifying presence at a location.
|
* **NFC Services:** Interacts with physical NFC tags, likely for scanning staff badges or verifying presence at a location.
|
||||||
* **Geolocation Services:** Uses the phone's GPS to verify that actions (like creating a hub) are happening at the correct physical address.
|
* **Geolocation Services:** Uses the phone's GPS to verify that actions (like creating a hub) are happening at the correct physical address.
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ flowchart TD
|
|||||||
|
|
||||||
subgraph External_Services [External Cloud Ecosystem]
|
subgraph External_Services [External Cloud Ecosystem]
|
||||||
direction TB
|
direction TB
|
||||||
Krow_Backend[Krow Backend Server]:::external
|
Krow_Backend[KROW Backend Server]:::external
|
||||||
Firebase[Firebase Auth & Config]:::external
|
Firebase[Firebase Auth & Config]:::external
|
||||||
Maps_NFC[Geolocation & NFC Services]:::external
|
Maps_NFC[Geolocation & NFC Services]:::external
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Use Case Documentation: Krow Client Mobile App
|
# Use Case Documentation: KROW Client Mobile App
|
||||||
|
|
||||||
## 1. App Initialization
|
## 1. App Initialization
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Application Overview: Krow Staff Mobile App
|
# Application Overview: KROW Staff Mobile App
|
||||||
|
|
||||||
## 1. Executive Summary
|
## 1. Executive Summary
|
||||||
The **Krow Staff App** is the mobile companion for the workforce—the people picking up shifts and performing the work. It operates as a "gig economy" platform where individuals can find flexible work, manage their schedule, and track their income.
|
The **KROW Staff App** is the mobile companion for the workforce—the people picking up shifts and performing the work. It operates as a "gig economy" platform where individuals can find flexible work, manage their schedule, and track their income.
|
||||||
|
|
||||||
For a staff member (e.g., a waiter, chef, or event security), this app is their digital office. They use it to:
|
For a staff member (e.g., a waiter, chef, or event security), this app is their digital office. They use it to:
|
||||||
* Browse and apply for available shifts in their area.
|
* Browse and apply for available shifts in their area.
|
||||||
@@ -14,7 +14,7 @@ For a staff member (e.g., a waiter, chef, or event security), this app is their
|
|||||||
Like the Client App, this application is built on a **Modern Mobile Architecture** that prioritizes offline resilience and real-time accuracy.
|
Like the Client App, this application is built on a **Modern Mobile Architecture** that prioritizes offline resilience and real-time accuracy.
|
||||||
|
|
||||||
* **The Frontend (The App):** A polished, user-friendly interface that guides staff through complex workflows (like onboarding or shift execution) simply.
|
* **The Frontend (The App):** A polished, user-friendly interface that guides staff through complex workflows (like onboarding or shift execution) simply.
|
||||||
* **The Bridge (API Layer):** Communicates with the Krow Cloud via **GraphQL**, allowing the app to fetch personalized job feeds and update work status efficiently.
|
* **The Bridge (API Layer):** Communicates with the KROW Cloud via **GraphQL**, allowing the app to fetch personalized job feeds and update work status efficiently.
|
||||||
* **The Backend (The Engine):** Manages the "marketplace" logic—matching staff skills to event requirements, calculating pay rates, and processing compliance rules.
|
* **The Backend (The Engine):** Manages the "marketplace" logic—matching staff skills to event requirements, calculating pay rates, and processing compliance rules.
|
||||||
|
|
||||||
## 3. Major Components & Modules
|
## 3. Major Components & Modules
|
||||||
@@ -43,7 +43,7 @@ These modules handle the specific tasks a staff member needs to perform:
|
|||||||
* **Data Repository:** intelligent data handling. It knows when to fetch new jobs from the internet and when to show the saved schedule from the local database (crucial for venues with poor reception).
|
* **Data Repository:** intelligent data handling. It knows when to fetch new jobs from the internet and when to show the saved schedule from the local database (crucial for venues with poor reception).
|
||||||
|
|
||||||
## 5. External System Communication
|
## 5. External System Communication
|
||||||
* **The Krow Backend (GraphQL):** The central authority for job listings, user profiles, and time/attendance records.
|
* **The KROW Backend (GraphQL):** The central authority for job listings, user profiles, and time/attendance records.
|
||||||
* **Firebase Auth:** Secure identity management for login and signup.
|
* **Firebase Auth:** Secure identity management for login and signup.
|
||||||
* **Firebase Remote Config:** Allows dynamic updates to app behavior (e.g., maintenance mode messages).
|
* **Firebase Remote Config:** Allows dynamic updates to app behavior (e.g., maintenance mode messages).
|
||||||
* **Geolocation & Maps:** Verifies that the staff member is physically at the venue when clocking in and provides directions to the job site.
|
* **Geolocation & Maps:** Verifies that the staff member is physically at the venue when clocking in and provides directions to the job site.
|
||||||
@@ -107,7 +107,7 @@ flowchart TD
|
|||||||
|
|
||||||
subgraph External_Services [External Cloud Ecosystem]
|
subgraph External_Services [External Cloud Ecosystem]
|
||||||
direction TB
|
direction TB
|
||||||
Krow_Backend[Krow Backend Server]:::external
|
Krow_Backend[KROW Backend Server]:::external
|
||||||
Firebase[Firebase Auth & Config]:::external
|
Firebase[Firebase Auth & Config]:::external
|
||||||
Geo_Maps[Geolocation & Maps]:::external
|
Geo_Maps[Geolocation & Maps]:::external
|
||||||
Hardware[Camera & NFC Hardware]:::external
|
Hardware[Camera & NFC Hardware]:::external
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Use Case Documentation: Krow Staff Mobile App
|
# Use Case Documentation: KROW Staff Mobile App
|
||||||
|
|
||||||
## 1. App Initialization
|
## 1. App Initialization
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
## 5. Admin Verification
|
## 5. Admin Verification
|
||||||
|
|
||||||
* **Primary Actor:** Staff Member (Passive) / Admin
|
* **Primary Actor:** Staff Member (Passive) / Admin
|
||||||
* **Description:** The waiting state while Krow admins review the submitted profile.
|
* **Description:** The waiting state while KROW admins review the submitted profile.
|
||||||
* **Preconditions:** Profile submitted.
|
* **Preconditions:** Profile submitted.
|
||||||
* **Main Flow:**
|
* **Main Flow:**
|
||||||
1. User sees "Waiting for Admin Review" screen.
|
1. User sees "Waiting for Admin Review" screen.
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
# Krow Legacy Ecosystem: System Bible
|
# KROW Legacy Ecosystem: System Bible
|
||||||
|
|
||||||
**Status:** Official System Constitution
|
**Status:** Official System Constitution
|
||||||
**Version:** 1.0.0
|
**Version:** 1.0.0
|
||||||
**Date:** 2026-02-06
|
**Date:** 2026-02-06
|
||||||
**Scope:** Krow Legacy Ecosystem (Client App, Staff App, Web Platform)
|
**Scope:** KROW Legacy Ecosystem (Client App, Staff App, Web Platform)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Executive Summary
|
## 1. Executive Summary
|
||||||
|
|
||||||
The **Krow Legacy Ecosystem** is a comprehensive digital marketplace designed to revolutionize the temporary staffing industry. It eliminates the friction of traditional agencies by directly connecting **businesses** (restaurants, event organizers) with **temporary workers** (wait staff, security, chefs) through a unified, automated platform.
|
The **KROW Legacy Ecosystem** is a comprehensive digital marketplace designed to revolutionize the temporary staffing industry. It eliminates the friction of traditional agencies by directly connecting **businesses** (restaurants, event organizers) with **temporary workers** (wait staff, security, chefs) through a unified, automated platform.
|
||||||
|
|
||||||
**Why It Exists:**
|
**Why It Exists:**
|
||||||
The traditional staffing model is slow, opaque, and manual (spreadsheets, phone calls). This ecosystem digitizes the entire lifecycle—from finding staff and verifying their skills to tracking attendance and processing payments—creating a transparent, efficient market for flexible work.
|
The traditional staffing model is slow, opaque, and manual (spreadsheets, phone calls). This ecosystem digitizes the entire lifecycle—from finding staff and verifying their skills to tracking attendance and processing payments—creating a transparent, efficient market for flexible work.
|
||||||
@@ -88,7 +88,7 @@ flowchart TD
|
|||||||
StaffApp["Staff App<br/>(Workers)"]:::staffApp
|
StaffApp["Staff App<br/>(Workers)"]:::staffApp
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Core_Platform["Krow Web Platform"]
|
subgraph Core_Platform["KROW Web Platform"]
|
||||||
direction TB
|
direction TB
|
||||||
AdminPanel["Admin Dashboard<br/>(Laravel Nova)"]:::backend
|
AdminPanel["Admin Dashboard<br/>(Laravel Nova)"]:::backend
|
||||||
APIServer["GraphQL API<br/>(Laravel Lighthouse)"]:::backend
|
APIServer["GraphQL API<br/>(Laravel Lighthouse)"]:::backend
|
||||||
@@ -229,7 +229,7 @@ These are the officially recognized system workflows.
|
|||||||
* **Shift:** A specific time block requiring a worker (e.g., "Friday 18:00-22:00").
|
* **Shift:** A specific time block requiring a worker (e.g., "Friday 18:00-22:00").
|
||||||
* **Position:** A specific role required for a shift (e.g., "Head Waiter").
|
* **Position:** A specific role required for a shift (e.g., "Head Waiter").
|
||||||
* **Clock-In:** The digital act of starting work, verified by location and time.
|
* **Clock-In:** The digital act of starting work, verified by location and time.
|
||||||
* **Admin Panel:** The web interface for Krow operations staff.
|
* **Admin Panel:** The web interface for KROW operations staff.
|
||||||
* **Marketplace:** The collection of all open, unassigned shifts visible to staff.
|
* **Marketplace:** The collection of all open, unassigned shifts visible to staff.
|
||||||
* **BLoC:** (Business Logic Component) The code pattern used in the mobile apps to manage state.
|
* **BLoC:** (Business Logic Component) The code pattern used in the mobile apps to manage state.
|
||||||
* **GraphQL:** The data query language used for API communication.
|
* **GraphQL:** The data query language used for API communication.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Application Overview: Krow Web Platform (Backend)
|
# Application Overview: KROW Web Platform (Backend)
|
||||||
|
|
||||||
## 1. Executive Summary
|
## 1. Executive Summary
|
||||||
The **Krow Web Platform** is the central "brain" and "command center" of the entire Krow ecosystem. While the mobile apps are the tools used in the field, this web application is the engine that powers them.
|
The **KROW Web Platform** is the central "brain" and "command center" of the entire KROW ecosystem. While the mobile apps are the tools used in the field, this web application is the engine that powers them.
|
||||||
|
|
||||||
It serves two primary functions:
|
It serves two primary functions:
|
||||||
1. **The Engine:** It processes all data from the mobile apps—saving new events, finding available staff, handling payments, and verifying check-ins.
|
1. **The Engine:** It processes all data from the mobile apps—saving new events, finding available staff, handling payments, and verifying check-ins.
|
||||||
2. **The Control Tower (Admin Panel):** It provides a powerful website where Krow administrators can oversee the entire business, manage users, resolve disputes, and configure system-wide settings.
|
2. **The Control Tower (Admin Panel):** It provides a powerful website where KROW administrators can oversee the entire business, manage users, resolve disputes, and configure system-wide settings.
|
||||||
|
|
||||||
## 2. High-Level Architecture
|
## 2. High-Level Architecture
|
||||||
The application follows a **Monolithic Web Architecture** using the **Laravel** framework. This is a battle-tested, robust structure ideal for building complex, data-heavy business platforms.
|
The application follows a **Monolithic Web Architecture** using the **Laravel** framework. This is a battle-tested, robust structure ideal for building complex, data-heavy business platforms.
|
||||||
@@ -84,7 +84,7 @@ flowchart TD
|
|||||||
classDef data fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1b5e20
|
classDef data fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#1b5e20
|
||||||
classDef external fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#4a148c
|
classDef external fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#4a148c
|
||||||
|
|
||||||
subgraph Server_Application [Krow Web Platform]
|
subgraph Server_Application [KROW Web Platform]
|
||||||
direction TB
|
direction TB
|
||||||
|
|
||||||
subgraph Interfaces [Access Layers]
|
subgraph Interfaces [Access Layers]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Use Case Documentation: Krow Web Platform (Admin Panel)
|
# Use Case Documentation: KROW Web Platform (Admin Panel)
|
||||||
|
|
||||||
## 1. Admin Login
|
## 1. Admin Login
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# Krow Platform: System Architecture Overview
|
# KROW Platform: System Architecture Overview
|
||||||
|
|
||||||
## 1. Executive Summary: The Business Purpose
|
## 1. Executive Summary: The Business Purpose
|
||||||
The **Krow Platform** is an end-to-end workforce management ecosystem designed to bridge the gap between businesses that need staff ("Clients") and the temporary workers who fill those roles ("Staff").
|
The **KROW Platform** is an end-to-end workforce management ecosystem designed to bridge the gap between businesses that need staff ("Clients") and the temporary workers who fill those roles ("Staff").
|
||||||
|
|
||||||
Traditionally, this process involves phone calls, paper timesheets, and manual payroll. Krow digitizes the entire lifecycle:
|
Traditionally, this process involves phone calls, paper timesheets, and manual payroll. KROW digitizes the entire lifecycle:
|
||||||
1. **Finding Work:** Clients post shifts instantly; workers claim them via mobile.
|
1. **Finding Work:** Clients post shifts instantly; workers claim them via mobile.
|
||||||
2. **Doing Work:** GPS-verified clock-ins and digital timesheets ensure accuracy.
|
2. **Doing Work:** GPS-verified clock-ins and digital timesheets ensure accuracy.
|
||||||
3. **Managing Business:** A web dashboard provides analytics, billing, and compliance oversight.
|
3. **Managing Business:** A web dashboard provides analytics, billing, and compliance oversight.
|
||||||
@@ -23,7 +23,7 @@ The platform consists of three distinct applications, each tailored to a specifi
|
|||||||
* **Role:** The supply pool. It acts as their personal agency, handling job discovery, schedule management, and instant payouts.
|
* **Role:** The supply pool. It acts as their personal agency, handling job discovery, schedule management, and instant payouts.
|
||||||
* **Key Value:** Flexibility and financial security. Workers choose when they work and get paid faster.
|
* **Key Value:** Flexibility and financial security. Workers choose when they work and get paid faster.
|
||||||
|
|
||||||
### C. Krow Web Application (The "HQ")
|
### C. KROW Web Application (The "HQ")
|
||||||
* **User:** Administrators, HR, Finance, and Client Executives.
|
* **User:** Administrators, HR, Finance, and Client Executives.
|
||||||
* **Role:** The command center. It handles the heavy lifting—complex invoicing, vendor management, compliance audits, and strategic data analysis.
|
* **Role:** The command center. It handles the heavy lifting—complex invoicing, vendor management, compliance audits, and strategic data analysis.
|
||||||
* **Key Value:** Control and insight. It turns operational data into cost-saving strategies.
|
* **Key Value:** Control and insight. It turns operational data into cost-saving strategies.
|
||||||
@@ -51,7 +51,7 @@ To maintain privacy and organization, data is strictly compartmentalized:
|
|||||||
|
|
||||||
* **Worker Data:** Owned by the worker but accessible to the platform. Clients can only see limited details (Name, Rating, Skills) of workers assigned to *their* specific shifts. They cannot see a worker's full financial history or assignments with other clients.
|
* **Worker Data:** Owned by the worker but accessible to the platform. Clients can only see limited details (Name, Rating, Skills) of workers assigned to *their* specific shifts. They cannot see a worker's full financial history or assignments with other clients.
|
||||||
* **Client Data:** Owned by the business. Workers see only what is necessary to do the job (Location, Dress Code, Supervisor Name). They cannot see the client's internal billing or strategic reports.
|
* **Client Data:** Owned by the business. Workers see only what is necessary to do the job (Location, Dress Code, Supervisor Name). They cannot see the client's internal billing or strategic reports.
|
||||||
* **Platform Data:** owned by Krow (Admins). This includes the aggregate data used for "Smart Strategies" and market analysis—e.g., "Average hourly rate for a Bartender in downtown."
|
* **Platform Data:** owned by KROW (Admins). This includes the aggregate data used for "Smart Strategies" and market analysis—e.g., "Average hourly rate for a Bartender in downtown."
|
||||||
|
|
||||||
## 6. Security & Access Control
|
## 6. Security & Access Control
|
||||||
The system operates on a **Role-Based Access Control (RBAC)** model:
|
The system operates on a **Role-Based Access Control (RBAC)** model:
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ This document details the primary business actions and user flows within the **C
|
|||||||
|
|
||||||
### 1.2 Register Business Account (Sign Up)
|
### 1.2 Register Business Account (Sign Up)
|
||||||
* **Actor:** New Business Manager
|
* **Actor:** New Business Manager
|
||||||
* **Description:** Creating a new identity for the business on the Krow platform.
|
* **Description:** Creating a new identity for the business on the KROW platform.
|
||||||
* **Main Flow:**
|
* **Main Flow:**
|
||||||
1. User taps "Sign Up".
|
1. User taps "Sign Up".
|
||||||
2. User enters company details (Name, Industry).
|
2. User enters company details (Name, Industry).
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ The system is structured into three main logical layers:
|
|||||||
* **Responsibility:** managing the professional identity and legal requirements.
|
* **Responsibility:** managing the professional identity and legal requirements.
|
||||||
* **Key Features:**
|
* **Key Features:**
|
||||||
* **Compliance:** Uploading and verifying `Certificates`, `Documents`, and Tax Forms (`I-9`, `W-4`).
|
* **Compliance:** Uploading and verifying `Certificates`, `Documents`, and Tax Forms (`I-9`, `W-4`).
|
||||||
* **Level Up:** `Krow University` for training and a `Leaderboard` for gamification.
|
* **Level Up:** `KROW University` for training and a `Leaderboard` for gamification.
|
||||||
* **Support:** `FAQs`, `Privacy`, and `Messages`.
|
* **Support:** `FAQs`, `Privacy`, and `Messages`.
|
||||||
|
|
||||||
## 4. Component Responsibilities
|
## 4. Component Responsibilities
|
||||||
@@ -87,7 +87,7 @@ The architecture is designed to interface with several critical external systems
|
|||||||
|
|
||||||
* **Flutter for Mobile:** Ensures the app is responsive and performs well on the wide variety of devices low-wage workers might own.
|
* **Flutter for Mobile:** Ensures the app is responsive and performs well on the wide variety of devices low-wage workers might own.
|
||||||
* **Strict Compliance Separation:** Dedicating a specific sub-module to `Compliance` (Tax forms, I-9) highlights the importance of legal adherence in the staffing industry.
|
* **Strict Compliance Separation:** Dedicating a specific sub-module to `Compliance` (Tax forms, I-9) highlights the importance of legal adherence in the staffing industry.
|
||||||
* **Gamification (Level Up):** The inclusion of `Krow University` and `Leaderboards` is a deliberate design choice to drive worker engagement and retention.
|
* **Gamification (Level Up):** The inclusion of `KROW University` and `Leaderboards` is a deliberate design choice to drive worker engagement and retention.
|
||||||
* **Offline-First Capabilities (Planned):** The architecture supports local data caching, which is critical for workers who may have spotty data connections while on job sites.
|
* **Offline-First Capabilities (Planned):** The architecture supports local data caching, which is critical for workers who may have spotty data connections while on job sites.
|
||||||
|
|
||||||
## 9. Overview Diagram
|
## 9. Overview Diagram
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ This document details the primary business actions available within the **Staff
|
|||||||
|
|
||||||
### 1.2 Onboarding & Registration
|
### 1.2 Onboarding & Registration
|
||||||
* **Actor:** New Worker
|
* **Actor:** New Worker
|
||||||
* **Description:** Creating a new profile to join the Krow network.
|
* **Description:** Creating a new profile to join the KROW network.
|
||||||
* **Main Flow:**
|
* **Main Flow:**
|
||||||
1. Worker enters phone number.
|
1. Worker enters phone number.
|
||||||
2. System sends SMS OTP.
|
2. System sends SMS OTP.
|
||||||
@@ -111,10 +111,10 @@ This document details the primary business actions available within the **Staff
|
|||||||
* **Description:** Submitting legal employment forms.
|
* **Description:** Submitting legal employment forms.
|
||||||
* **Main Flow:** Navigate to "Tax Forms" -> Complete W-4 or I-9 digitally -> Sign and Submit.
|
* **Main Flow:** Navigate to "Tax Forms" -> Complete W-4 or I-9 digitally -> Sign and Submit.
|
||||||
|
|
||||||
### 5.3 Krow University Training
|
### 5.3 KROW University Training
|
||||||
* **Actor:** Temporary Worker
|
* **Actor:** Temporary Worker
|
||||||
* **Description:** Improving skills to unlock better jobs.
|
* **Description:** Improving skills to unlock better jobs.
|
||||||
* **Main Flow:** Navigate to "Krow University" -> Select Module -> Watch Video/Take Quiz -> Earn Badge.
|
* **Main Flow:** Navigate to "KROW University" -> Select Module -> Watch Video/Take Quiz -> Earn Badge.
|
||||||
|
|
||||||
### 5.4 Account Settings
|
### 5.4 Account Settings
|
||||||
* **Actor:** Temporary Worker
|
* **Actor:** Temporary Worker
|
||||||
@@ -193,7 +193,7 @@ flowchart TD
|
|||||||
ComplianceMenu --> UploadDocs[Upload Certificates]
|
ComplianceMenu --> UploadDocs[Upload Certificates]
|
||||||
ComplianceMenu --> TaxForms["Manage Tax Forms (W-4/I-9)"]
|
ComplianceMenu --> TaxForms["Manage Tax Forms (W-4/I-9)"]
|
||||||
|
|
||||||
Profile --> KrowUniversity[Krow University]
|
Profile --> KrowUniversity[KROW University]
|
||||||
KrowUniversity --> StartTraining[Start Training Module]
|
KrowUniversity --> StartTraining[Start Training Module]
|
||||||
|
|
||||||
Profile --> BankAccount[Manage Bank Details]
|
Profile --> BankAccount[Manage Bank Details]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# The Krow Platform System Bible
|
# The KROW Platform System Bible
|
||||||
|
|
||||||
**Status:** Official / Living Document
|
**Status:** Official / Living Document
|
||||||
**Version:** 1.0.0
|
**Version:** 1.0.0
|
||||||
@@ -8,10 +8,10 @@
|
|||||||
## 1. Executive Summary
|
## 1. Executive Summary
|
||||||
|
|
||||||
### What the System Is
|
### What the System Is
|
||||||
The **Krow Platform** is a multi-sided workforce management ecosystem that digitizes the entire lifecycle of temporary staffing. It replaces fragmented, manual processes (phone calls, spreadsheets, paper timesheets) with a unified digital infrastructure connecting businesses ("Clients") directly with temporary workers ("Staff").
|
The **KROW Platform** is a multi-sided workforce management ecosystem that digitizes the entire lifecycle of temporary staffing. It replaces fragmented, manual processes (phone calls, spreadsheets, paper timesheets) with a unified digital infrastructure connecting businesses ("Clients") directly with temporary workers ("Staff").
|
||||||
|
|
||||||
### Why It Exists
|
### Why It Exists
|
||||||
The temporary staffing industry suffers from friction, lack of transparency, and delayed payments. Businesses struggle to find reliable staff quickly, while workers face uncertain schedules and slow wage access. Krow exists to remove this friction, ensuring shifts are filled instantly, work is verified accurately, and payments are processed swiftly.
|
The temporary staffing industry suffers from friction, lack of transparency, and delayed payments. Businesses struggle to find reliable staff quickly, while workers face uncertain schedules and slow wage access. KROW exists to remove this friction, ensuring shifts are filled instantly, work is verified accurately, and payments are processed swiftly.
|
||||||
|
|
||||||
### Who It Serves
|
### Who It Serves
|
||||||
1. **Clients (Businesses):** Venue managers and owners who need on-demand or scheduled staff.
|
1. **Clients (Businesses):** Venue managers and owners who need on-demand or scheduled staff.
|
||||||
@@ -19,7 +19,7 @@ The temporary staffing industry suffers from friction, lack of transparency, and
|
|||||||
3. **Admins (Operations):** Internal teams managing the marketplace, compliance, and financial flows.
|
3. **Admins (Operations):** Internal teams managing the marketplace, compliance, and financial flows.
|
||||||
|
|
||||||
### High-Level Value Proposition
|
### High-Level Value Proposition
|
||||||
Krow transforms labor from a manual logistical headache into a streamlined digital asset. For clients, it provides "staff on tap" with verified compliance. For workers, it offers "freedom and instant pay." For the platform operators, it delivers data-driven oversight of a complex marketplace.
|
KROW transforms labor from a manual logistical headache into a streamlined digital asset. For clients, it provides "staff on tap" with verified compliance. For workers, it offers "freedom and instant pay." For the platform operators, it delivers data-driven oversight of a complex marketplace.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ The ecosystem comprises three distinct applications, each serving a specific use
|
|||||||
* **Responsibility:** Supply fulfillment. Empowering workers to find jobs, manage their schedule, verify their presence (Clock In), and access earnings.
|
* **Responsibility:** Supply fulfillment. Empowering workers to find jobs, manage their schedule, verify their presence (Clock In), and access earnings.
|
||||||
* **Concept:** The worker's "Digital Agency" in their pocket.
|
* **Concept:** The worker's "Digital Agency" in their pocket.
|
||||||
|
|
||||||
### 3. Krow Web Application (The "HQ")
|
### 3. KROW Web Application (The "HQ")
|
||||||
* **Platform:** React (Web)
|
* **Platform:** React (Web)
|
||||||
* **Responsibility:** Ecosystem governance. The command center for high-level analytics, complex financial operations (invoicing/payouts), vendor management, and system administration.
|
* **Responsibility:** Ecosystem governance. The command center for high-level analytics, complex financial operations (invoicing/payouts), vendor management, and system administration.
|
||||||
* **Concept:** The "Mission Control" for the business backend.
|
* **Concept:** The "Mission Control" for the business backend.
|
||||||
@@ -69,7 +69,7 @@ The ecosystem comprises three distinct applications, each serving a specific use
|
|||||||
|
|
||||||
## 4. System Architecture Overview
|
## 4. System Architecture Overview
|
||||||
|
|
||||||
The Krow Platform follows a **Service-Oriented Architecture (SOA)** where multiple front-end clients interface with a shared, monolithic logical backend (exposed via API Gateway).
|
The KROW Platform follows a **Service-Oriented Architecture (SOA)** where multiple front-end clients interface with a shared, monolithic logical backend (exposed via API Gateway).
|
||||||
|
|
||||||
### Architectural Style
|
### Architectural Style
|
||||||
* **Centralized State:** A single backend database serves as the source of truth for all apps.
|
* **Centralized State:** A single backend database serves as the source of truth for all apps.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Web Application: Architecture Overview
|
# Web Application: Architecture Overview
|
||||||
|
|
||||||
## 1. Executive Summary
|
## 1. Executive Summary
|
||||||
The **Krow Web Application** is the "Mission Control" for the entire Krow platform. It acts as a powerful administrative dashboard where clients and internal operations teams can see the "Big Picture" that mobile apps can't provide.
|
The **KROW Web Application** is the "Mission Control" for the entire KROW platform. It acts as a powerful administrative dashboard where clients and internal operations teams can see the "Big Picture" that mobile apps can't provide.
|
||||||
|
|
||||||
While the mobile apps are for *doing* the work (checking in, posting a shift), the web app is for *managing* the business of that work. It allows users to analyze workforce performance, manage complex vendor relationships, handle high-volume invoicing, and use AI to optimize staffing strategies.
|
While the mobile apps are for *doing* the work (checking in, posting a shift), the web app is for *managing* the business of that work. It allows users to analyze workforce performance, manage complex vendor relationships, handle high-volume invoicing, and use AI to optimize staffing strategies.
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ The Web App serves as the central hub connecting various services:
|
|||||||
## 9. Overview Diagram
|
## 9. Overview Diagram
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
subgraph WebApplication["Krow Web Application"]
|
subgraph WebApplication["KROW Web Application"]
|
||||||
direction TB
|
direction TB
|
||||||
|
|
||||||
subgraph PresentationLayer["Presentation Layer (UI)"]
|
subgraph PresentationLayer["Presentation Layer (UI)"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Web Application: Use Case Overview
|
# Web Application: Use Case Overview
|
||||||
|
|
||||||
This document details the primary business actions and user flows within the **Krow Web Application**. It is organized according to the logical workflows for each primary user role as defined in the system's architecture.
|
This document details the primary business actions and user flows within the **KROW Web Application**. It is organized according to the logical workflows for each primary user role as defined in the system's architecture.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ This document details the primary business actions and user flows within the **K
|
|||||||
### 3.2 Finance & Billing Management
|
### 3.2 Finance & Billing Management
|
||||||
* **Actor:** Client Executive / Finance Admin
|
* **Actor:** Client Executive / Finance Admin
|
||||||
* **Description:** Managing corporate financial obligations.
|
* **Description:** Managing corporate financial obligations.
|
||||||
* **Main Flow:** User views all pending invoices -> Downloads detailed line-item reports -> Processes payments to Krow.
|
* **Main Flow:** User views all pending invoices -> Downloads detailed line-item reports -> Processes payments to KROW.
|
||||||
|
|
||||||
### 3.3 Operations Overview
|
### 3.3 Operations Overview
|
||||||
* **Actor:** Client Executive
|
* **Actor:** Client Executive
|
||||||
@@ -81,7 +81,7 @@ This document details the primary business actions and user flows within the **K
|
|||||||
### 4.3 Vendor Finance
|
### 4.3 Vendor Finance
|
||||||
* **Actor:** Vendor Manager
|
* **Actor:** Vendor Manager
|
||||||
* **Description:** Managing agency revenue and worker payouts.
|
* **Description:** Managing agency revenue and worker payouts.
|
||||||
* **Main Flow:** User views payout history -> Submits invoices for completed shifts -> Tracks pending payments from Krow.
|
* **Main Flow:** User views payout history -> Submits invoices for completed shifts -> Tracks pending payments from KROW.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Krow DevOps Launchpad</title>
|
<title>KROW DevOps Launchpad</title>
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.svg">
|
<link rel="icon" type="image/x-icon" href="favicon.svg">
|
||||||
|
|
||||||
<!-- Tailwind CSS -->
|
<!-- Tailwind CSS -->
|
||||||
@@ -256,7 +256,7 @@
|
|||||||
<div class="p-6 border-b border-gray-200">
|
<div class="p-6 border-b border-gray-200">
|
||||||
<div class="flex items-center justify-center space-x-3">
|
<div class="flex items-center justify-center space-x-3">
|
||||||
<div class="w-12 h-12 rounded-xl flex items-center justify-center">
|
<div class="w-12 h-12 rounded-xl flex items-center justify-center">
|
||||||
<img src="logo.svg" alt="Krow Logo" style="height: 60px;" onerror="this.style.display='none'">
|
<img src="logo.svg" alt="KROW Logo" style="height: 60px;" onerror="this.style.display='none'">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-xl font-bold text-gray-900">KROW DevOps</h1>
|
<h1 class="text-xl font-bold text-gray-900">KROW DevOps</h1>
|
||||||
@@ -306,7 +306,7 @@
|
|||||||
<div id="home-view" class="p-8">
|
<div id="home-view" class="p-8">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<h2 class="text-3xl font-bold text-gray-900 mb-2">Krow DevOps Launchpad</h2>
|
<h2 class="text-3xl font-bold text-gray-900 mb-2">KROW DevOps Launchpad</h2>
|
||||||
<p class="text-gray-600">Central hub for KROW development and operations infrastructure</p>
|
<p class="text-gray-600">Central hub for KROW development and operations infrastructure</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -427,7 +427,7 @@
|
|||||||
if (allowed) {
|
if (allowed) {
|
||||||
showApp(user);
|
showApp(user);
|
||||||
} else {
|
} else {
|
||||||
pendingError = `Access Denied: <b>${user.email}</b> is not authorized.<br>Please contact the Krow Internal Developers or an Administrator to request access.`;
|
pendingError = `Access Denied: <b>${user.email}</b> is not authorized.<br>Please contact the KROW Internal Developers or an Administrator to request access.`;
|
||||||
signOut(auth);
|
signOut(auth);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user