Merge pull request #579 from Oloodi/493-implement-rapid-order-creation-voice-text-in-client-mobile-app
Prepare & Handover M4 Mobile Applications for Client Testing
This commit is contained in:
@@ -43,7 +43,6 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
|
||||||
applicationId = "com.krowwithus.client"
|
applicationId = "com.krowwithus.client"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
@@ -51,18 +50,17 @@ android {
|
|||||||
targetSdk = flutter.targetSdkVersion
|
targetSdk = flutter.targetSdkVersion
|
||||||
versionCode = flutter.versionCode
|
versionCode = flutter.versionCode
|
||||||
versionName = flutter.versionName
|
versionName = flutter.versionName
|
||||||
|
manifestPlaceholders["GOOGLE_MAPS_API_KEY"] = dartEnvironmentVariables["GOOGLE_MAPS_API_KEY"] ?: ""
|
||||||
manifestPlaceholders["GOOGLE_MAPS_API_KEY"] = dartEnvironmentVariables["GOOGLE_MAPS_API_KEY"] ?: ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
create("release") {
|
create("release") {
|
||||||
if (System.getenv()["CI"] == "true") {
|
if (System.getenv()["CI"] == "true") {
|
||||||
// CodeMagic CI environment
|
// CodeMagic CI environment
|
||||||
storeFile = file(System.getenv()["CM_KEYSTORE_PATH_CLIENT"] ?: "")
|
storeFile = file(System.getenv()["CM_KEYSTORE_PATH"] ?: "")
|
||||||
storePassword = System.getenv()["CM_KEYSTORE_PASSWORD_CLIENT"]
|
storePassword = System.getenv()["CM_KEYSTORE_PASSWORD"]
|
||||||
keyAlias = System.getenv()["CM_KEY_ALIAS_CLIENT"]
|
keyAlias = System.getenv()["CM_KEY_ALIAS"]
|
||||||
keyPassword = System.getenv()["CM_KEY_PASSWORD_CLIENT"]
|
keyPassword = System.getenv()["CM_KEY_PASSWORD"]
|
||||||
} else {
|
} else {
|
||||||
// Local development environment
|
// Local development environment
|
||||||
keyAlias = keystoreProperties["keyAlias"] as String?
|
keyAlias = keystoreProperties["keyAlias"] as String?
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class _SessionListenerState extends State<SessionListener> {
|
|||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Modular.to.popSafe();
|
||||||
_proceedToLogin();
|
_proceedToLogin();
|
||||||
},
|
},
|
||||||
child: const Text('Log In'),
|
child: const Text('Log In'),
|
||||||
@@ -134,7 +134,7 @@ class _SessionListenerState extends State<SessionListener> {
|
|||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Modular.to.popSafe();;
|
||||||
_proceedToLogin();
|
_proceedToLogin();
|
||||||
},
|
},
|
||||||
child: const Text('Log Out'),
|
child: const Text('Log Out'),
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
|
||||||
applicationId = "com.krowwithus.staff"
|
applicationId = "com.krowwithus.staff"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
@@ -59,10 +58,10 @@ android {
|
|||||||
create("release") {
|
create("release") {
|
||||||
if (System.getenv()["CI"] == "true") {
|
if (System.getenv()["CI"] == "true") {
|
||||||
// CodeMagic CI environment
|
// CodeMagic CI environment
|
||||||
storeFile = file(System.getenv()["CM_KEYSTORE_PATH_STAFF"] ?: "")
|
storeFile = file(System.getenv()["CM_KEYSTORE_PATH"] ?: "")
|
||||||
storePassword = System.getenv()["CM_KEYSTORE_PASSWORD_STAFF"]
|
storePassword = System.getenv()["CM_KEYSTORE_PASSWORD"]
|
||||||
keyAlias = System.getenv()["CM_KEY_ALIAS_STAFF"]
|
keyAlias = System.getenv()["CM_KEY_ALIAS"]
|
||||||
keyPassword = System.getenv()["CM_KEY_PASSWORD_STAFF"]
|
keyPassword = System.getenv()["CM_KEY_PASSWORD"]
|
||||||
} else {
|
} else {
|
||||||
// Local development environment
|
// Local development environment
|
||||||
keyAlias = keystoreProperties["keyAlias"] as String?
|
keyAlias = keystoreProperties["keyAlias"] as String?
|
||||||
|
|||||||
@@ -1,162 +0,0 @@
|
|||||||
{
|
|
||||||
"project_info": {
|
|
||||||
"project_number": "933560802882",
|
|
||||||
"project_id": "krow-workforce-dev",
|
|
||||||
"storage_bucket": "krow-workforce-dev.firebasestorage.app"
|
|
||||||
},
|
|
||||||
"client": [
|
|
||||||
{
|
|
||||||
"client_info": {
|
|
||||||
"mobilesdk_app_id": "1:933560802882:android:edcddb83ea4bbb517757db",
|
|
||||||
"android_client_info": {
|
|
||||||
"package_name": "com.krow.app.business.dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"api_key": [
|
|
||||||
{
|
|
||||||
"current_key": "AIzaSyDBYhflhK6DThKnS7RM-9raKdvyKzLUjY4"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"services": {
|
|
||||||
"appinvite_service": {
|
|
||||||
"other_platform_oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-dppsapp5i3lsfrlm1mhob2s21peofg1t.apps.googleusercontent.com",
|
|
||||||
"client_type": 2,
|
|
||||||
"ios_info": {
|
|
||||||
"bundle_id": "com.krow.app.staff.dev"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"client_info": {
|
|
||||||
"mobilesdk_app_id": "1:933560802882:android:d49b8c0f4d19e95e7757db",
|
|
||||||
"android_client_info": {
|
|
||||||
"package_name": "com.krow.app.staff.dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"api_key": [
|
|
||||||
{
|
|
||||||
"current_key": "AIzaSyDBYhflhK6DThKnS7RM-9raKdvyKzLUjY4"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"services": {
|
|
||||||
"appinvite_service": {
|
|
||||||
"other_platform_oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-dppsapp5i3lsfrlm1mhob2s21peofg1t.apps.googleusercontent.com",
|
|
||||||
"client_type": 2,
|
|
||||||
"ios_info": {
|
|
||||||
"bundle_id": "com.krow.app.staff.dev"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"client_info": {
|
|
||||||
"mobilesdk_app_id": "1:933560802882:android:da13569105659ead7757db",
|
|
||||||
"android_client_info": {
|
|
||||||
"package_name": "com.krowwithus.client"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"api_key": [
|
|
||||||
{
|
|
||||||
"current_key": "AIzaSyDBYhflhK6DThKnS7RM-9raKdvyKzLUjY4"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"services": {
|
|
||||||
"appinvite_service": {
|
|
||||||
"other_platform_oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-dppsapp5i3lsfrlm1mhob2s21peofg1t.apps.googleusercontent.com",
|
|
||||||
"client_type": 2,
|
|
||||||
"ios_info": {
|
|
||||||
"bundle_id": "com.krow.app.staff.dev"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"client_info": {
|
|
||||||
"mobilesdk_app_id": "1:933560802882:android:1ae05d85c865f77c7757db",
|
|
||||||
"android_client_info": {
|
|
||||||
"package_name": "com.krowwithus.staff"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-ikdfv3o5f47g36qqgvfq55o4m19n7gk4.apps.googleusercontent.com",
|
|
||||||
"client_type": 1,
|
|
||||||
"android_info": {
|
|
||||||
"package_name": "com.krowwithus.staff",
|
|
||||||
"certificate_hash": "ac917ae8470ab29f1107c773c6017ff5ea5d102d"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"api_key": [
|
|
||||||
{
|
|
||||||
"current_key": "AIzaSyDBYhflhK6DThKnS7RM-9raKdvyKzLUjY4"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"services": {
|
|
||||||
"appinvite_service": {
|
|
||||||
"other_platform_oauth_client": [
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-grp98a1v7amflnnup68vh01tj06eaem1.apps.googleusercontent.com",
|
|
||||||
"client_type": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"client_id": "933560802882-dppsapp5i3lsfrlm1mhob2s21peofg1t.apps.googleusercontent.com",
|
|
||||||
"client_type": 2,
|
|
||||||
"ios_info": {
|
|
||||||
"bundle_id": "com.krow.app.staff.dev"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"configuration_version": "1"
|
|
||||||
}
|
|
||||||
@@ -104,7 +104,7 @@ class _SessionListenerState extends State<SessionListener> {
|
|||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Modular.to.popSafe();;
|
||||||
_proceedToLogin();
|
_proceedToLogin();
|
||||||
},
|
},
|
||||||
child: const Text('Log In'),
|
child: const Text('Log In'),
|
||||||
@@ -134,7 +134,7 @@ class _SessionListenerState extends State<SessionListener> {
|
|||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Modular.to.popSafe();;
|
||||||
_proceedToLogin();
|
_proceedToLogin();
|
||||||
},
|
},
|
||||||
child: const Text('Log Out'),
|
child: const Text('Log Out'),
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:core_localization/src/l10n/strings.g.dart';
|
import 'package:core_localization/src/l10n/strings.g.dart';
|
||||||
|
|
||||||
import '../../domain/repositories/locale_repository_interface.dart';
|
import '../../domain/repositories/locale_repository_interface.dart';
|
||||||
@@ -31,12 +32,10 @@ class LocaleRepositoryImpl implements LocaleRepositoryInterface {
|
|||||||
return getDefaultLocale();
|
return getDefaultLocale();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We can hardcode this to english based on customer requirements,
|
||||||
|
/// but in a more dynamic app this should be the device locale or a fallback to english.
|
||||||
@override
|
@override
|
||||||
Locale getDefaultLocale() {
|
Locale getDefaultLocale() {
|
||||||
final Locale deviceLocale = AppLocaleUtils.findDeviceLocale().flutterLocale;
|
|
||||||
if (getSupportedLocales().contains(deviceLocale)) {
|
|
||||||
return deviceLocale;
|
|
||||||
}
|
|
||||||
return const Locale('en');
|
return const Locale('en');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ class StaffConnectorRepositoryImpl implements StaffConnectorRepository {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> getProfileCompletion() async {
|
Future<bool> getProfileCompletion() async {
|
||||||
return true;
|
|
||||||
return _service.run(() async {
|
return _service.run(() async {
|
||||||
final String staffId = await _service.getStaffId();
|
final String staffId = await _service.getStaffId();
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ class StaffPayment extends Equatable {
|
|||||||
required this.amount,
|
required this.amount,
|
||||||
required this.status,
|
required this.status,
|
||||||
this.paidAt,
|
this.paidAt,
|
||||||
|
this.shiftTitle,
|
||||||
|
this.shiftLocation,
|
||||||
|
this.locationAddress,
|
||||||
|
this.hoursWorked,
|
||||||
|
this.hourlyRate,
|
||||||
|
this.workedTime,
|
||||||
});
|
});
|
||||||
/// Unique identifier.
|
/// Unique identifier.
|
||||||
final String id;
|
final String id;
|
||||||
@@ -47,6 +53,24 @@ class StaffPayment extends Equatable {
|
|||||||
/// When the payment was successfully processed.
|
/// When the payment was successfully processed.
|
||||||
final DateTime? paidAt;
|
final DateTime? paidAt;
|
||||||
|
|
||||||
|
/// Title of the shift worked.
|
||||||
|
final String? shiftTitle;
|
||||||
|
|
||||||
|
/// Location/hub name of the shift.
|
||||||
|
final String? shiftLocation;
|
||||||
|
|
||||||
|
/// Address of the shift location.
|
||||||
|
final String? locationAddress;
|
||||||
|
|
||||||
|
/// Number of hours worked.
|
||||||
|
final double? hoursWorked;
|
||||||
|
|
||||||
|
/// Hourly rate for the shift.
|
||||||
|
final double? hourlyRate;
|
||||||
|
|
||||||
|
/// Work session duration or status.
|
||||||
|
final String? workedTime;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => <Object?>[id, staffId, assignmentId, amount, status, paidAt];
|
List<Object?> get props => <Object?>[id, staffId, assignmentId, amount, status, paidAt, shiftTitle, shiftLocation, locationAddress, hoursWorked, hourlyRate, workedTime];
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
|
import 'package:core_localization/core_localization.dart';
|
||||||
import 'package:design_system/design_system.dart';
|
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:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:krow_core/core.dart';
|
|
||||||
import 'package:core_localization/core_localization.dart';
|
|
||||||
import '../blocs/coverage_bloc.dart';
|
import '../blocs/coverage_bloc.dart';
|
||||||
import '../blocs/coverage_event.dart';
|
import '../blocs/coverage_event.dart';
|
||||||
import '../blocs/coverage_state.dart';
|
import '../blocs/coverage_state.dart';
|
||||||
|
|
||||||
import '../widgets/coverage_calendar_selector.dart';
|
import '../widgets/coverage_calendar_selector.dart';
|
||||||
import '../widgets/coverage_quick_stats.dart';
|
import '../widgets/coverage_quick_stats.dart';
|
||||||
import '../widgets/coverage_shift_list.dart';
|
import '../widgets/coverage_shift_list.dart';
|
||||||
@@ -78,21 +77,6 @@ class _CoveragePageState extends State<CoveragePage> {
|
|||||||
pinned: true,
|
pinned: true,
|
||||||
expandedHeight: 300.0,
|
expandedHeight: 300.0,
|
||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
leading: IconButton(
|
|
||||||
onPressed: () => Modular.to.toClientHome(),
|
|
||||||
icon: Container(
|
|
||||||
padding: const EdgeInsets.all(UiConstants.space2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: UiColors.primaryForeground.withOpacity(0.2),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
UiIcons.arrowLeft,
|
|
||||||
color: UiColors.primaryForeground,
|
|
||||||
size: UiConstants.space4,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
title: AnimatedSwitcher(
|
title: AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|||||||
@@ -506,21 +506,21 @@ class _WorkerRow extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (worker.status == CoverageWorkerStatus.checkedIn)
|
// if (worker.status == CoverageWorkerStatus.checkedIn)
|
||||||
UiButton.primary(
|
// UiButton.primary(
|
||||||
text: context.t.client_coverage.worker_row.verify,
|
// text: context.t.client_coverage.worker_row.verify,
|
||||||
size: UiButtonSize.small,
|
// size: UiButtonSize.small,
|
||||||
onPressed: () {
|
// onPressed: () {
|
||||||
UiSnackbar.show(
|
// UiSnackbar.show(
|
||||||
context,
|
// context,
|
||||||
message:
|
// message:
|
||||||
context.t.client_coverage.worker_row.verified_message(
|
// context.t.client_coverage.worker_row.verified_message(
|
||||||
name: worker.name,
|
// name: worker.name,
|
||||||
),
|
// ),
|
||||||
type: UiSnackbarType.success,
|
// type: UiSnackbarType.success,
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -161,14 +161,7 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
|||||||
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(
|
|
||||||
color: UiColors.black.withValues(alpha: 0.04),
|
|
||||||
blurRadius: 8,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -249,9 +242,12 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
|||||||
size: 14,
|
size: 14,
|
||||||
color: UiColors.iconSecondary,
|
color: UiColors.iconSecondary,
|
||||||
),
|
),
|
||||||
Text(
|
Expanded(
|
||||||
order.eventName,
|
child: Text(
|
||||||
style: UiTypography.headline5m.textSecondary,
|
order.eventName,
|
||||||
|
style: UiTypography.headline5m.textSecondary,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -313,7 +309,8 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
order.hubManagerName!,
|
order.hubManagerName!,
|
||||||
style: UiTypography.footnote2r.textSecondary,
|
style:
|
||||||
|
UiTypography.footnote2r.textSecondary,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -335,7 +332,8 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
|||||||
bgColor: UiColors.primary.withValues(alpha: 0.08),
|
bgColor: UiColors.primary.withValues(alpha: 0.08),
|
||||||
onTap: () => _openEditSheet(order: order),
|
onTap: () => _openEditSheet(order: order),
|
||||||
),
|
),
|
||||||
if (_canEditOrder(order)) const SizedBox(width: UiConstants.space2),
|
if (_canEditOrder(order))
|
||||||
|
const SizedBox(width: UiConstants.space2),
|
||||||
if (order.confirmedApps.isNotEmpty)
|
if (order.confirmedApps.isNotEmpty)
|
||||||
_buildHeaderIconButton(
|
_buildHeaderIconButton(
|
||||||
icon: _expanded
|
icon: _expanded
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:krow_domain/src/entities/reports/daily_ops_report.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
import '../../../domain/repositories/reports_repository.dart';
|
import '../../../domain/repositories/reports_repository.dart';
|
||||||
import 'daily_ops_event.dart';
|
import 'daily_ops_event.dart';
|
||||||
import 'daily_ops_state.dart';
|
import 'daily_ops_state.dart';
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs
|
import 'package:client_reports/src/presentation/blocs/coverage/coverage_bloc.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/coverage/coverage_bloc.dart';
|
|
||||||
import 'package:client_reports/src/presentation/blocs/coverage/coverage_event.dart';
|
import 'package:client_reports/src/presentation/blocs/coverage/coverage_event.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/coverage/coverage_state.dart';
|
import 'package:client_reports/src/presentation/blocs/coverage/coverage_state.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
import 'package:design_system/design_system.dart';
|
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:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:krow_core/core.dart';
|
||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
class CoverageReportPage extends StatefulWidget {
|
class CoverageReportPage extends StatefulWidget {
|
||||||
const CoverageReportPage({super.key});
|
const CoverageReportPage({super.key});
|
||||||
@@ -23,11 +23,10 @@ class _CoverageReportPageState extends State<CoverageReportPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider<CoverageBloc>(
|
||||||
create: (BuildContext context) => Modular.get<CoverageBloc>()
|
create: (BuildContext context) => Modular.get<CoverageBloc>()
|
||||||
..add(LoadCoverageReport(startDate: _startDate, endDate: _endDate)),
|
..add(LoadCoverageReport(startDate: _startDate, endDate: _endDate)),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: UiColors.bgMenu,
|
|
||||||
body: BlocBuilder<CoverageBloc, CoverageState>(
|
body: BlocBuilder<CoverageBloc, CoverageState>(
|
||||||
builder: (BuildContext context, CoverageState state) {
|
builder: (BuildContext context, CoverageState state) {
|
||||||
if (state is CoverageLoading) {
|
if (state is CoverageLoading) {
|
||||||
@@ -64,7 +63,7 @@ class _CoverageReportPageState extends State<CoverageReportPage> {
|
|||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => Navigator.of(context).pop(),
|
onTap: () => Modular.to.popSafe(),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs, implementation_imports
|
import 'package:client_reports/src/presentation/blocs/daily_ops/daily_ops_bloc.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/daily_ops/daily_ops_bloc.dart';
|
|
||||||
import 'package:client_reports/src/presentation/blocs/daily_ops/daily_ops_event.dart';
|
import 'package:client_reports/src/presentation/blocs/daily_ops/daily_ops_event.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/daily_ops/daily_ops_state.dart';
|
import 'package:client_reports/src/presentation/blocs/daily_ops/daily_ops_state.dart';
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
@@ -8,7 +7,8 @@ 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:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:krow_domain/src/entities/reports/daily_ops_report.dart';
|
import 'package:krow_core/core.dart';
|
||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
class DailyOpsReportPage extends StatefulWidget {
|
class DailyOpsReportPage extends StatefulWidget {
|
||||||
const DailyOpsReportPage({super.key});
|
const DailyOpsReportPage({super.key});
|
||||||
@@ -50,11 +50,10 @@ class _DailyOpsReportPageState extends State<DailyOpsReportPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider<DailyOpsBloc>(
|
||||||
create: (BuildContext context) => Modular.get<DailyOpsBloc>()
|
create: (BuildContext context) => Modular.get<DailyOpsBloc>()
|
||||||
..add(LoadDailyOpsReport(date: _selectedDate)),
|
..add(LoadDailyOpsReport(date: _selectedDate)),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: UiColors.bgMenu,
|
|
||||||
body: BlocBuilder<DailyOpsBloc, DailyOpsState>(
|
body: BlocBuilder<DailyOpsBloc, DailyOpsState>(
|
||||||
builder: (BuildContext context, DailyOpsState state) {
|
builder: (BuildContext context, DailyOpsState state) {
|
||||||
if (state is DailyOpsLoading) {
|
if (state is DailyOpsLoading) {
|
||||||
@@ -94,7 +93,7 @@ class _DailyOpsReportPageState extends State<DailyOpsReportPage> {
|
|||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => Navigator.of(context).pop(),
|
onTap: () => Modular.to.popSafe(),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
@@ -245,6 +244,7 @@ class _DailyOpsReportPageState extends State<DailyOpsReportPage> {
|
|||||||
crossAxisCount: 2,
|
crossAxisCount: 2,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
mainAxisSpacing: 12,
|
mainAxisSpacing: 12,
|
||||||
crossAxisSpacing: 12,
|
crossAxisSpacing: 12,
|
||||||
childAspectRatio: 1.2,
|
childAspectRatio: 1.2,
|
||||||
@@ -318,7 +318,7 @@ class _DailyOpsReportPageState extends State<DailyOpsReportPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space8),
|
||||||
Text(
|
Text(
|
||||||
context.t.client_reports.daily_ops_report
|
context.t.client_reports.daily_ops_report
|
||||||
.all_shifts_title
|
.all_shifts_title
|
||||||
@@ -398,14 +398,8 @@ class _OpsStatCard extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
boxShadow: <BoxShadow>[
|
border: Border.all(color: UiColors.border, width: 0.5),
|
||||||
BoxShadow(
|
|
||||||
color: UiColors.black.withOpacity(0.06),
|
|
||||||
blurRadius: 4,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -440,7 +434,8 @@ class _OpsStatCard extends StatelessWidget {
|
|||||||
color: UiColors.textPrimary,
|
color: UiColors.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
|
||||||
|
//UiChip(label: subValue),
|
||||||
// Colored pill badge (matches prototype)
|
// Colored pill badge (matches prototype)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
@@ -449,12 +444,12 @@ class _OpsStatCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: color.withOpacity(0.12),
|
color: color.withOpacity(0.12),
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
subValue,
|
subValue,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 10,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: color,
|
color: color,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import 'package:client_reports/src/presentation/blocs/forecast/forecast_bloc.dart';
|
import 'package:client_reports/src/presentation/blocs/forecast/forecast_bloc.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/forecast/forecast_event.dart';
|
import 'package:client_reports/src/presentation/blocs/forecast/forecast_event.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/forecast/forecast_state.dart';
|
import 'package:client_reports/src/presentation/blocs/forecast/forecast_state.dart';
|
||||||
|
import 'package:krow_core/core.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
@@ -28,7 +29,6 @@ class _ForecastReportPageState extends State<ForecastReportPage> {
|
|||||||
create: (BuildContext context) => Modular.get<ForecastBloc>()
|
create: (BuildContext context) => Modular.get<ForecastBloc>()
|
||||||
..add(LoadForecastReport(startDate: _startDate, endDate: _endDate)),
|
..add(LoadForecastReport(startDate: _startDate, endDate: _endDate)),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: UiColors.bgMenu,
|
|
||||||
body: BlocBuilder<ForecastBloc, ForecastState>(
|
body: BlocBuilder<ForecastBloc, ForecastState>(
|
||||||
builder: (BuildContext context, ForecastState state) {
|
builder: (BuildContext context, ForecastState state) {
|
||||||
if (state is ForecastLoading) {
|
if (state is ForecastLoading) {
|
||||||
@@ -86,7 +86,7 @@ class _ForecastReportPageState extends State<ForecastReportPage> {
|
|||||||
(ForecastWeek week) => _WeeklyBreakdownItem(week: week),
|
(ForecastWeek week) => _WeeklyBreakdownItem(week: week),
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 40),
|
const SizedBox(height: UiConstants.space24),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -124,7 +124,7 @@ class _ForecastReportPageState extends State<ForecastReportPage> {
|
|||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => Navigator.of(context).pop(),
|
onTap: () => Modular.to.popSafe(),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
@@ -184,6 +184,7 @@ class _ForecastReportPageState extends State<ForecastReportPage> {
|
|||||||
final TranslationsClientReportsForecastReportEn t = context.t.client_reports.forecast_report;
|
final TranslationsClientReportsForecastReportEn t = context.t.client_reports.forecast_report;
|
||||||
return GridView.count(
|
return GridView.count(
|
||||||
crossAxisCount: 2,
|
crossAxisCount: 2,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
mainAxisSpacing: 12,
|
mainAxisSpacing: 12,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs
|
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs
|
||||||
|
import 'package:krow_core/core.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/no_show/no_show_bloc.dart';
|
import 'package:client_reports/src/presentation/blocs/no_show/no_show_bloc.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/no_show/no_show_event.dart';
|
import 'package:client_reports/src/presentation/blocs/no_show/no_show_event.dart';
|
||||||
@@ -27,7 +28,6 @@ class _NoShowReportPageState extends State<NoShowReportPage> {
|
|||||||
create: (BuildContext context) => Modular.get<NoShowBloc>()
|
create: (BuildContext context) => Modular.get<NoShowBloc>()
|
||||||
..add(LoadNoShowReport(startDate: _startDate, endDate: _endDate)),
|
..add(LoadNoShowReport(startDate: _startDate, endDate: _endDate)),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: UiColors.bgMenu,
|
|
||||||
body: BlocBuilder<NoShowBloc, NoShowState>(
|
body: BlocBuilder<NoShowBloc, NoShowState>(
|
||||||
builder: (BuildContext context, NoShowState state) {
|
builder: (BuildContext context, NoShowState state) {
|
||||||
if (state is NoShowLoading) {
|
if (state is NoShowLoading) {
|
||||||
@@ -67,7 +67,7 @@ class _NoShowReportPageState extends State<NoShowReportPage> {
|
|||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => Navigator.of(context).pop(),
|
onTap: () => Modular.to.popSafe(),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs, implementation_imports
|
import 'package:client_reports/src/presentation/blocs/performance/performance_bloc.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/performance/performance_bloc.dart';
|
|
||||||
import 'package:client_reports/src/presentation/blocs/performance/performance_event.dart';
|
import 'package:client_reports/src/presentation/blocs/performance/performance_event.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/performance/performance_state.dart';
|
import 'package:client_reports/src/presentation/blocs/performance/performance_state.dart';
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
@@ -7,7 +6,8 @@ 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_domain/src/entities/reports/performance_report.dart';
|
import 'package:krow_core/core.dart';
|
||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
class PerformanceReportPage extends StatefulWidget {
|
class PerformanceReportPage extends StatefulWidget {
|
||||||
const PerformanceReportPage({super.key});
|
const PerformanceReportPage({super.key});
|
||||||
@@ -26,7 +26,6 @@ class _PerformanceReportPageState extends State<PerformanceReportPage> {
|
|||||||
create: (BuildContext context) => Modular.get<PerformanceBloc>()
|
create: (BuildContext context) => Modular.get<PerformanceBloc>()
|
||||||
..add(LoadPerformanceReport(startDate: _startDate, endDate: _endDate)),
|
..add(LoadPerformanceReport(startDate: _startDate, endDate: _endDate)),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: UiColors.bgMenu,
|
|
||||||
body: BlocBuilder<PerformanceBloc, PerformanceState>(
|
body: BlocBuilder<PerformanceBloc, PerformanceState>(
|
||||||
builder: (BuildContext context, PerformanceState state) {
|
builder: (BuildContext context, PerformanceState state) {
|
||||||
if (state is PerformanceLoading) {
|
if (state is PerformanceLoading) {
|
||||||
@@ -143,7 +142,7 @@ class _PerformanceReportPageState extends State<PerformanceReportPage> {
|
|||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => Navigator.of(context).pop(),
|
onTap: () => Modular.to.popSafe(),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs, implementation_imports
|
import 'package:client_reports/src/presentation/blocs/summary/reports_summary_bloc.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/summary/reports_summary_bloc.dart';
|
|
||||||
import 'package:client_reports/src/presentation/blocs/summary/reports_summary_event.dart';
|
import 'package:client_reports/src/presentation/blocs/summary/reports_summary_event.dart';
|
||||||
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';
|
||||||
@@ -80,10 +78,9 @@ class _ReportsPageState extends State<ReportsPage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider.value(
|
return BlocProvider<ReportsSummaryBloc>.value(
|
||||||
value: _summaryBloc,
|
value: _summaryBloc,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: UiColors.bgMenu,
|
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// ignore_for_file: always_specify_types, depend_on_referenced_packages, dead_code, dead_null_aware_expression, unused_local_variable, unused_import, sort_constructors_first, prefer_final_fields, prefer_const_constructors, deprecated_member_use, implicit_call_tearoffs
|
import 'package:client_reports/src/presentation/blocs/spend/spend_bloc.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/spend/spend_bloc.dart';
|
|
||||||
import 'package:client_reports/src/presentation/blocs/spend/spend_event.dart';
|
import 'package:client_reports/src/presentation/blocs/spend/spend_event.dart';
|
||||||
import 'package:client_reports/src/presentation/blocs/spend/spend_state.dart';
|
import 'package:client_reports/src/presentation/blocs/spend/spend_state.dart';
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
@@ -9,6 +8,7 @@ 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:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:krow_core/core.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
class SpendReportPage extends StatefulWidget {
|
class SpendReportPage extends StatefulWidget {
|
||||||
@@ -35,11 +35,10 @@ class _SpendReportPageState extends State<SpendReportPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider<SpendBloc>(
|
||||||
create: (BuildContext context) => Modular.get<SpendBloc>()
|
create: (BuildContext context) => Modular.get<SpendBloc>()
|
||||||
..add(LoadSpendReport(startDate: _startDate, endDate: _endDate)),
|
..add(LoadSpendReport(startDate: _startDate, endDate: _endDate)),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: UiColors.bgMenu,
|
|
||||||
body: BlocBuilder<SpendBloc, SpendState>(
|
body: BlocBuilder<SpendBloc, SpendState>(
|
||||||
builder: (BuildContext context, SpendState state) {
|
builder: (BuildContext context, SpendState state) {
|
||||||
if (state is SpendLoading) {
|
if (state is SpendLoading) {
|
||||||
@@ -72,7 +71,7 @@ class _SpendReportPageState extends State<SpendReportPage> {
|
|||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => Navigator.of(context).pop(),
|
onTap: () => Modular.to.popSafe(),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ class _PhoneVerificationPageState extends State<PhoneVerificationPage> {
|
|||||||
BlocProvider.of<AuthBloc>(
|
BlocProvider.of<AuthBloc>(
|
||||||
context,
|
context,
|
||||||
).add(AuthResetRequested(mode: widget.mode));
|
).add(AuthResetRequested(mode: widget.mode));
|
||||||
Navigator.of(context).pop();
|
Modular.to.popSafe();;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ class FullWidthDivider extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
//final screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: UiConstants.space10),
|
const SizedBox(height: UiConstants.space10),
|
||||||
Transform.translate(
|
// Transform.translate(
|
||||||
offset: const Offset(-UiConstants.space4, 0),
|
// offset: const Offset(-UiConstants.space4, 0),
|
||||||
child: SizedBox(width: screenWidth, child: const Divider()),
|
// child: SizedBox(width: screenWidth, child: const Divider()),
|
||||||
),
|
// ),
|
||||||
const SizedBox(height: UiConstants.space10),
|
// const SizedBox(height: UiConstants.space10),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,16 @@ class PaymentsRepositoryImpl
|
|||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
return response.data.recentPayments.map((dc.ListRecentPaymentsByStaffIdRecentPayments payment) {
|
return response.data.recentPayments.map((dc.ListRecentPaymentsByStaffIdRecentPayments payment) {
|
||||||
|
// Extract shift details from nested application structure
|
||||||
|
final String? shiftTitle = payment.application.shiftRole.shift.title;
|
||||||
|
final String? locationAddress = payment.application.shiftRole.shift.locationAddress;
|
||||||
|
final double? hoursWorked = payment.application.shiftRole.hours;
|
||||||
|
final double? hourlyRate = payment.application.shiftRole.role.costPerHour;
|
||||||
|
// Extract hub details from order
|
||||||
|
final String? locationHub = payment.invoice.order.teamHub.hubName;
|
||||||
|
final String? hubAddress = payment.invoice.order.teamHub.address;
|
||||||
|
final String? shiftLocation = locationAddress ?? hubAddress;
|
||||||
|
|
||||||
return StaffPayment(
|
return StaffPayment(
|
||||||
id: payment.id,
|
id: payment.id,
|
||||||
staffId: payment.staffId,
|
staffId: payment.staffId,
|
||||||
@@ -74,6 +84,12 @@ class PaymentsRepositoryImpl
|
|||||||
amount: payment.invoice.amount,
|
amount: payment.invoice.amount,
|
||||||
status: PaymentAdapter.toPaymentStatus(payment.status?.stringValue ?? 'UNKNOWN'),
|
status: PaymentAdapter.toPaymentStatus(payment.status?.stringValue ?? 'UNKNOWN'),
|
||||||
paidAt: _service.toDateTime(payment.invoice.issueDate),
|
paidAt: _service.toDateTime(payment.invoice.issueDate),
|
||||||
|
shiftTitle: shiftTitle,
|
||||||
|
shiftLocation: locationHub,
|
||||||
|
locationAddress: shiftLocation,
|
||||||
|
hoursWorked: hoursWorked,
|
||||||
|
hourlyRate: hourlyRate,
|
||||||
|
workedTime: payment.workedTime,
|
||||||
);
|
);
|
||||||
}).toList();
|
}).toList();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:krow_core/core.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';
|
||||||
@@ -10,7 +9,6 @@ import '../blocs/payments/payments_bloc.dart';
|
|||||||
import '../blocs/payments/payments_event.dart';
|
import '../blocs/payments/payments_event.dart';
|
||||||
import '../blocs/payments/payments_state.dart';
|
import '../blocs/payments/payments_state.dart';
|
||||||
import '../widgets/payment_stats_card.dart';
|
import '../widgets/payment_stats_card.dart';
|
||||||
import '../widgets/pending_pay_card.dart';
|
|
||||||
import '../widgets/payment_history_item.dart';
|
import '../widgets/payment_history_item.dart';
|
||||||
import '../widgets/earnings_graph.dart';
|
import '../widgets/earnings_graph.dart';
|
||||||
|
|
||||||
@@ -172,17 +170,7 @@ class _PaymentsPageState extends State<PaymentsPage> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space4),
|
const SizedBox(height: UiConstants.space8),
|
||||||
|
|
||||||
// Pending Pay
|
|
||||||
if (state.summary.pendingEarnings > 0)
|
|
||||||
PendingPayCard(
|
|
||||||
amount: state.summary.pendingEarnings,
|
|
||||||
onCashOut: () {
|
|
||||||
Modular.to.pushNamed('${StaffPaths.payments}early-pay');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space6),
|
|
||||||
|
|
||||||
// Recent Payments
|
// Recent Payments
|
||||||
if (state.history.isNotEmpty)
|
if (state.history.isNotEmpty)
|
||||||
@@ -191,7 +179,7 @@ class _PaymentsPageState extends State<PaymentsPage> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
"Recent Payments",
|
"Recent Payments",
|
||||||
style: UiTypography.body2m.textPrimary,
|
style: UiTypography.body1b,
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space3),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Column(
|
Column(
|
||||||
@@ -201,16 +189,16 @@ class _PaymentsPageState extends State<PaymentsPage> {
|
|||||||
bottom: UiConstants.space2),
|
bottom: UiConstants.space2),
|
||||||
child: PaymentHistoryItem(
|
child: PaymentHistoryItem(
|
||||||
amount: payment.amount,
|
amount: payment.amount,
|
||||||
title: "Shift Payment",
|
title: payment.shiftTitle ?? "Shift Payment",
|
||||||
location: "Varies",
|
location: payment.shiftLocation ?? "Varies",
|
||||||
address: "Payment ID: ${payment.id}",
|
address: payment.locationAddress ?? payment.id,
|
||||||
date: payment.paidAt != null
|
date: payment.paidAt != null
|
||||||
? DateFormat('E, MMM d')
|
? DateFormat('E, MMM d')
|
||||||
.format(payment.paidAt!)
|
.format(payment.paidAt!)
|
||||||
: 'Pending',
|
: 'Pending',
|
||||||
workedTime: "Completed",
|
workedTime: payment.workedTime ?? "Completed",
|
||||||
hours: 0,
|
hours: (payment.hoursWorked ?? 0).toInt(),
|
||||||
rate: 0.0,
|
rate: payment.hourlyRate ?? 0.0,
|
||||||
status: payment.status.name.toUpperCase(),
|
status: payment.status.name.toUpperCase(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,13 +32,7 @@ class PaymentHistoryItem extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
boxShadow: <BoxShadow>[
|
border: Border.all(color: UiColors.border, width: 0.5),
|
||||||
BoxShadow(
|
|
||||||
color: UiColors.black.withValues(alpha: 0.05),
|
|
||||||
blurRadius: 2,
|
|
||||||
offset: const Offset(0, 1),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -77,7 +71,7 @@ class PaymentHistoryItem extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
UiIcons.chart,
|
UiIcons.dollar,
|
||||||
color: UiColors.mutedForeground,
|
color: UiColors.mutedForeground,
|
||||||
size: 24,
|
size: 24,
|
||||||
),
|
),
|
||||||
@@ -98,7 +92,7 @@ class PaymentHistoryItem extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: UiTypography.body2b.textPrimary,
|
style: UiTypography.body2m,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
location,
|
location,
|
||||||
@@ -112,7 +106,7 @@ class PaymentHistoryItem extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
"\$${amount.toStringAsFixed(0)}",
|
"\$${amount.toStringAsFixed(0)}",
|
||||||
style: UiTypography.headline4m.textPrimary,
|
style: UiTypography.headline4b,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"\$${rate.toStringAsFixed(0)}/hr · ${hours}h",
|
"\$${rate.toStringAsFixed(0)}/hr · ${hours}h",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:krow_core/core.dart';
|
import 'package:krow_core/core.dart';
|
||||||
|
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||||
import '../../../domain/usecases/apply_for_shift_usecase.dart';
|
import '../../../domain/usecases/apply_for_shift_usecase.dart';
|
||||||
import '../../../domain/usecases/decline_shift_usecase.dart';
|
import '../../../domain/usecases/decline_shift_usecase.dart';
|
||||||
import '../../../domain/usecases/get_shift_details_usecase.dart';
|
import '../../../domain/usecases/get_shift_details_usecase.dart';
|
||||||
@@ -12,11 +13,13 @@ class ShiftDetailsBloc extends Bloc<ShiftDetailsEvent, ShiftDetailsState>
|
|||||||
final GetShiftDetailsUseCase getShiftDetails;
|
final GetShiftDetailsUseCase getShiftDetails;
|
||||||
final ApplyForShiftUseCase applyForShift;
|
final ApplyForShiftUseCase applyForShift;
|
||||||
final DeclineShiftUseCase declineShift;
|
final DeclineShiftUseCase declineShift;
|
||||||
|
final GetProfileCompletionUseCase getProfileCompletion;
|
||||||
|
|
||||||
ShiftDetailsBloc({
|
ShiftDetailsBloc({
|
||||||
required this.getShiftDetails,
|
required this.getShiftDetails,
|
||||||
required this.applyForShift,
|
required this.applyForShift,
|
||||||
required this.declineShift,
|
required this.declineShift,
|
||||||
|
required this.getProfileCompletion,
|
||||||
}) : super(ShiftDetailsInitial()) {
|
}) : super(ShiftDetailsInitial()) {
|
||||||
on<LoadShiftDetailsEvent>(_onLoadDetails);
|
on<LoadShiftDetailsEvent>(_onLoadDetails);
|
||||||
on<BookShiftDetailsEvent>(_onBookShift);
|
on<BookShiftDetailsEvent>(_onBookShift);
|
||||||
@@ -34,8 +37,9 @@ class ShiftDetailsBloc extends Bloc<ShiftDetailsEvent, ShiftDetailsState>
|
|||||||
final shift = await getShiftDetails(
|
final shift = await getShiftDetails(
|
||||||
GetShiftDetailsArguments(shiftId: event.shiftId, roleId: event.roleId),
|
GetShiftDetailsArguments(shiftId: event.shiftId, roleId: event.roleId),
|
||||||
);
|
);
|
||||||
|
final isProfileComplete = await getProfileCompletion();
|
||||||
if (shift != null) {
|
if (shift != null) {
|
||||||
emit(ShiftDetailsLoaded(shift));
|
emit(ShiftDetailsLoaded(shift, isProfileComplete: isProfileComplete));
|
||||||
} else {
|
} else {
|
||||||
emit(const ShiftDetailsError("Shift not found"));
|
emit(const ShiftDetailsError("Shift not found"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ class ShiftDetailsLoading extends ShiftDetailsState {}
|
|||||||
|
|
||||||
class ShiftDetailsLoaded extends ShiftDetailsState {
|
class ShiftDetailsLoaded extends ShiftDetailsState {
|
||||||
final Shift shift;
|
final Shift shift;
|
||||||
const ShiftDetailsLoaded(this.shift);
|
final bool isProfileComplete;
|
||||||
|
const ShiftDetailsLoaded(this.shift, {this.isProfileComplete = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [shift];
|
List<Object?> get props => [shift, isProfileComplete];
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShiftDetailsError extends ShiftDetailsState {
|
class ShiftDetailsError extends ShiftDetailsState {
|
||||||
|
|||||||
@@ -125,6 +125,9 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
|
|
||||||
final Shift displayShift = widget.shift;
|
final Shift displayShift = widget.shift;
|
||||||
final i18n = Translations.of(context).staff_shifts.shift_details;
|
final i18n = Translations.of(context).staff_shifts.shift_details;
|
||||||
|
final isProfileComplete = state is ShiftDetailsLoaded
|
||||||
|
? state.isProfileComplete
|
||||||
|
: false;
|
||||||
|
|
||||||
final duration = _calculateDuration(displayShift);
|
final duration = _calculateDuration(displayShift);
|
||||||
final estimatedTotal =
|
final estimatedTotal =
|
||||||
@@ -142,6 +145,16 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
if (!isProfileComplete)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
|
child: UiNoticeBanner(
|
||||||
|
title: 'Complete Your Account',
|
||||||
|
description:
|
||||||
|
'Complete your account to book this shift and start earning',
|
||||||
|
icon: UiIcons.sparkles,
|
||||||
|
),
|
||||||
|
),
|
||||||
ShiftDetailsHeader(shift: displayShift),
|
ShiftDetailsHeader(shift: displayShift),
|
||||||
const Divider(height: 1, thickness: 0.5),
|
const Divider(height: 1, thickness: 0.5),
|
||||||
ShiftStatsRow(
|
ShiftStatsRow(
|
||||||
@@ -194,20 +207,21 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ShiftDetailsBottomBar(
|
if (isProfileComplete)
|
||||||
shift: displayShift,
|
ShiftDetailsBottomBar(
|
||||||
onApply: () => _bookShift(context, displayShift),
|
shift: displayShift,
|
||||||
onDecline: () => BlocProvider.of<ShiftDetailsBloc>(
|
onApply: () => _bookShift(context, displayShift),
|
||||||
context,
|
onDecline: () => BlocProvider.of<ShiftDetailsBloc>(
|
||||||
).add(DeclineShiftDetailsEvent(displayShift.id)),
|
context,
|
||||||
onAccept: () =>
|
).add(DeclineShiftDetailsEvent(displayShift.id)),
|
||||||
BlocProvider.of<ShiftDetailsBloc>(context).add(
|
onAccept: () =>
|
||||||
BookShiftDetailsEvent(
|
BlocProvider.of<ShiftDetailsBloc>(context).add(
|
||||||
displayShift.id,
|
BookShiftDetailsEvent(
|
||||||
roleId: displayShift.roleId,
|
displayShift.id,
|
||||||
|
roleId: displayShift.roleId,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
|
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||||
import 'domain/repositories/shifts_repository_interface.dart';
|
import 'domain/repositories/shifts_repository_interface.dart';
|
||||||
import 'data/repositories_impl/shifts_repository_impl.dart';
|
import 'data/repositories_impl/shifts_repository_impl.dart';
|
||||||
import 'domain/usecases/get_shift_details_usecase.dart';
|
import 'domain/usecases/get_shift_details_usecase.dart';
|
||||||
@@ -14,11 +15,21 @@ class ShiftDetailsModule extends Module {
|
|||||||
// Repository
|
// Repository
|
||||||
i.add<ShiftsRepositoryInterface>(ShiftsRepositoryImpl.new);
|
i.add<ShiftsRepositoryInterface>(ShiftsRepositoryImpl.new);
|
||||||
|
|
||||||
|
// StaffConnectorRepository for profile completion
|
||||||
|
i.addLazySingleton<StaffConnectorRepository>(
|
||||||
|
() => StaffConnectorRepositoryImpl(),
|
||||||
|
);
|
||||||
|
|
||||||
// UseCases
|
// UseCases
|
||||||
i.add(GetShiftDetailsUseCase.new);
|
i.add(GetShiftDetailsUseCase.new);
|
||||||
i.add(AcceptShiftUseCase.new);
|
i.add(AcceptShiftUseCase.new);
|
||||||
i.add(DeclineShiftUseCase.new);
|
i.add(DeclineShiftUseCase.new);
|
||||||
i.add(ApplyForShiftUseCase.new);
|
i.add(ApplyForShiftUseCase.new);
|
||||||
|
i.addLazySingleton<GetProfileCompletionUseCase>(
|
||||||
|
() => GetProfileCompletionUseCase(
|
||||||
|
repository: i.get<StaffConnectorRepository>(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Bloc
|
// Bloc
|
||||||
i.add(ShiftDetailsBloc.new);
|
i.add(ShiftDetailsBloc.new);
|
||||||
|
|||||||
@@ -32,18 +32,18 @@ class StaffShiftsModule extends Module {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Repository
|
// Repository
|
||||||
i.add<ShiftsRepositoryInterface>(ShiftsRepositoryImpl.new);
|
i.addLazySingleton<ShiftsRepositoryInterface>(ShiftsRepositoryImpl.new);
|
||||||
|
|
||||||
// UseCases
|
// UseCases
|
||||||
i.add(GetMyShiftsUseCase.new);
|
i.addLazySingleton(GetMyShiftsUseCase.new);
|
||||||
i.add(GetAvailableShiftsUseCase.new);
|
i.addLazySingleton(GetAvailableShiftsUseCase.new);
|
||||||
i.add(GetPendingAssignmentsUseCase.new);
|
i.addLazySingleton(GetPendingAssignmentsUseCase.new);
|
||||||
i.add(GetCancelledShiftsUseCase.new);
|
i.addLazySingleton(GetCancelledShiftsUseCase.new);
|
||||||
i.add(GetHistoryShiftsUseCase.new);
|
i.addLazySingleton(GetHistoryShiftsUseCase.new);
|
||||||
i.add(AcceptShiftUseCase.new);
|
i.addLazySingleton(AcceptShiftUseCase.new);
|
||||||
i.add(DeclineShiftUseCase.new);
|
i.addLazySingleton(DeclineShiftUseCase.new);
|
||||||
i.add(ApplyForShiftUseCase.new);
|
i.addLazySingleton(ApplyForShiftUseCase.new);
|
||||||
i.add(GetShiftDetailsUseCase.new);
|
i.addLazySingleton(GetShiftDetailsUseCase.new);
|
||||||
|
|
||||||
// Bloc
|
// Bloc
|
||||||
i.add(
|
i.add(
|
||||||
|
|||||||
3
apps/web/pnpm-lock.yaml
generated
3
apps/web/pnpm-lock.yaml
generated
@@ -4,9 +4,6 @@ settings:
|
|||||||
autoInstallPeers: true
|
autoInstallPeers: true
|
||||||
excludeLinksFromLockfile: false
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
overrides:
|
|
||||||
'@dataconnect/generated': link:src/dataconnect-generated
|
|
||||||
|
|
||||||
importers:
|
importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
|
|||||||
@@ -1,2 +1,5 @@
|
|||||||
|
packages:
|
||||||
|
- '.'
|
||||||
|
|
||||||
overrides:
|
overrides:
|
||||||
'@dataconnect/generated': link:src/dataconnect-generated
|
'@dataconnect/generated': link:src/dataconnect-generated
|
||||||
|
|||||||
@@ -62,12 +62,6 @@ export const NAV_CONFIG: NavGroup[] = [
|
|||||||
icon: LayoutDashboard,
|
icon: LayoutDashboard,
|
||||||
allowedRoles: ['Vendor'],
|
allowedRoles: ['Vendor'],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Savings Engine',
|
|
||||||
path: '/savings',
|
|
||||||
icon: PiggyBank,
|
|
||||||
allowedRoles: ALL_ROLES,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: 'Vendor Performance',
|
label: 'Vendor Performance',
|
||||||
path: '/performance',
|
path: '/performance',
|
||||||
@@ -117,23 +111,6 @@ export const NAV_CONFIG: NavGroup[] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'Marketplace',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Discovery',
|
|
||||||
path: '/marketplace',
|
|
||||||
icon: ShoppingBag,
|
|
||||||
allowedRoles: ['Client', 'Admin'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Compare Rates',
|
|
||||||
path: '/marketplace/compare',
|
|
||||||
icon: Scale,
|
|
||||||
allowedRoles: ['Client', 'Admin'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'Workforce',
|
title: 'Workforce',
|
||||||
items: [
|
items: [
|
||||||
@@ -143,18 +120,6 @@ export const NAV_CONFIG: NavGroup[] = [
|
|||||||
icon: Users,
|
icon: Users,
|
||||||
allowedRoles: ALL_ROLES,
|
allowedRoles: ALL_ROLES,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Onboarding',
|
|
||||||
path: '/onboarding',
|
|
||||||
icon: UserPlus,
|
|
||||||
allowedRoles: ALL_ROLES,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Teams',
|
|
||||||
path: '/teams',
|
|
||||||
icon: Users2,
|
|
||||||
allowedRoles: ALL_ROLES,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: 'Compliance',
|
label: 'Compliance',
|
||||||
path: '/compliance',
|
path: '/compliance',
|
||||||
@@ -197,44 +162,4 @@ export const NAV_CONFIG: NavGroup[] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'Analytics & Comm',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Reports',
|
|
||||||
path: '/reports',
|
|
||||||
icon: PieChart,
|
|
||||||
allowedRoles: ALL_ROLES,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Activity Log',
|
|
||||||
path: '/activity',
|
|
||||||
icon: History,
|
|
||||||
allowedRoles: ['Vendor', 'Admin'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Messages',
|
|
||||||
path: '/messages',
|
|
||||||
icon: MessageSquare,
|
|
||||||
allowedRoles: ALL_ROLES,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Tutorials',
|
|
||||||
path: '/tutorials',
|
|
||||||
icon: BookOpen,
|
|
||||||
allowedRoles: ['Client', 'Admin'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Support',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
label: 'Help Center',
|
|
||||||
path: '/support',
|
|
||||||
icon: HelpCircle,
|
|
||||||
allowedRoles: ['Client', 'Admin'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/common/components/ui/dialog";
|
} from "@/common/components/ui/dialog";
|
||||||
import EventFormWizard from "./EventFormWizard";
|
import EventFormWizard from "./EventFormWizard";
|
||||||
import { useCreateOrder, useListBusinesses, useListHubs } from "@/dataconnect-generated/react";
|
import { useCreateOrder, useListBusinesses, useListTeamHubs } from "@/dataconnect-generated/react";
|
||||||
import { OrderType, OrderStatus } from "@/dataconnect-generated";
|
import { OrderType, OrderStatus } from "@/dataconnect-generated";
|
||||||
import { dataConnect } from "@/features/auth/firebase";
|
import { dataConnect } from "@/features/auth/firebase";
|
||||||
import { useToast } from "@/common/components/ui/use-toast";
|
import { useToast } from "@/common/components/ui/use-toast";
|
||||||
@@ -26,7 +26,7 @@ export default function CreateOrderDialog({ open, onOpenChange }: CreateOrderDia
|
|||||||
const [selectedHubId, setSelectedHubId] = React.useState<string>("");
|
const [selectedHubId, setSelectedHubId] = React.useState<string>("");
|
||||||
|
|
||||||
const { data: businessesData } = useListBusinesses(dataConnect);
|
const { data: businessesData } = useListBusinesses(dataConnect);
|
||||||
const { data: hubsData } = useListHubs(dataConnect);
|
const { data: hubsData } = useListTeamHubs(dataConnect);
|
||||||
|
|
||||||
const createOrderMutation = useCreateOrder(dataConnect, {
|
const createOrderMutation = useCreateOrder(dataConnect, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@@ -109,9 +109,9 @@ export default function CreateOrderDialog({ open, onOpenChange }: CreateOrderDia
|
|||||||
<SelectValue placeholder="Select Hub" />
|
<SelectValue placeholder="Select Hub" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{hubsData?.hubs.map((h) => (
|
{hubsData?.teamHubs.map((h) => (
|
||||||
<SelectItem key={h.id} value={h.id}>
|
<SelectItem key={h.id} value={h.id}>
|
||||||
{h.name}
|
{h.hubName}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
# Reusable script for building the Flutter app
|
# Reusable script for building the Flutter app
|
||||||
client-app-android-apk-build-script: &client-app-android-apk-build-script
|
client-app-android-apk-build-script: &client-app-android-apk-build-script
|
||||||
name: 👷🤖 Build Client App APK (Android)
|
name: 👷 🤖 Build Client App APK (Android)
|
||||||
script: |
|
script: |
|
||||||
dart pub global activate melos
|
dart pub global activate melos
|
||||||
export PATH="$PATH":"$HOME/.pub-cache/bin"
|
export PATH="$PATH":"$HOME/.pub-cache/bin"
|
||||||
@@ -12,7 +12,7 @@ client-app-android-apk-build-script: &client-app-android-apk-build-script
|
|||||||
make mobile-client-build PLATFORM=apk MODE=release
|
make mobile-client-build PLATFORM=apk MODE=release
|
||||||
|
|
||||||
client-app-ios-build-script: &client-app-ios-build-script
|
client-app-ios-build-script: &client-app-ios-build-script
|
||||||
name: 👷🍎 Build Client App (iOS)
|
name: 👷 🍎 Build Client App (iOS)
|
||||||
script: |
|
script: |
|
||||||
dart pub global activate melos
|
dart pub global activate melos
|
||||||
export PATH="$PATH":"$HOME/.pub-cache/bin"
|
export PATH="$PATH":"$HOME/.pub-cache/bin"
|
||||||
@@ -20,7 +20,7 @@ client-app-ios-build-script: &client-app-ios-build-script
|
|||||||
make mobile-client-build PLATFORM=ios MODE=release
|
make mobile-client-build PLATFORM=ios MODE=release
|
||||||
|
|
||||||
staff-app-android-apk-build-script: &staff-app-android-apk-build-script
|
staff-app-android-apk-build-script: &staff-app-android-apk-build-script
|
||||||
name: 👷🤖 Build Staff App APK (Android)
|
name: 👷 🤖 Build Staff App APK (Android)
|
||||||
script: |
|
script: |
|
||||||
dart pub global activate melos
|
dart pub global activate melos
|
||||||
export PATH="$PATH":"$HOME/.pub-cache/bin"
|
export PATH="$PATH":"$HOME/.pub-cache/bin"
|
||||||
@@ -28,7 +28,7 @@ staff-app-android-apk-build-script: &staff-app-android-apk-build-script
|
|||||||
make mobile-staff-build PLATFORM=apk MODE=release
|
make mobile-staff-build PLATFORM=apk MODE=release
|
||||||
|
|
||||||
staff-app-ios-build-script: &staff-app-ios-build-script
|
staff-app-ios-build-script: &staff-app-ios-build-script
|
||||||
name: 👷🍎 Build Staff App (iOS)
|
name: 👷 🍎 Build Staff App (iOS)
|
||||||
script: |
|
script: |
|
||||||
dart pub global activate melos
|
dart pub global activate melos
|
||||||
export PATH="$PATH":"$HOME/.pub-cache/bin"
|
export PATH="$PATH":"$HOME/.pub-cache/bin"
|
||||||
@@ -37,7 +37,7 @@ staff-app-ios-build-script: &staff-app-ios-build-script
|
|||||||
|
|
||||||
# Reusable script for distributing Android to Firebase
|
# Reusable script for distributing Android to Firebase
|
||||||
distribute-android-script: &distribute-android-script
|
distribute-android-script: &distribute-android-script
|
||||||
name: 🚛🤖 Distribute Android to Firebase App Distribution
|
name: 🚛 🤖 Distribute Android to Firebase App Distribution
|
||||||
script: |
|
script: |
|
||||||
# Distribute Android APK
|
# Distribute Android APK
|
||||||
# Note: Using wildcards to catch app-release.apk
|
# Note: Using wildcards to catch app-release.apk
|
||||||
@@ -167,7 +167,7 @@ workflows:
|
|||||||
# =================================================================================
|
# =================================================================================
|
||||||
client-app-dev-android:
|
client-app-dev-android:
|
||||||
<<: *client-app-base
|
<<: *client-app-base
|
||||||
name: 🚛🤖 Client App Dev (Android App Distribution)
|
name: 🚛 🤖 Client App Dev (Android App Distribution)
|
||||||
environment:
|
environment:
|
||||||
flutter: stable
|
flutter: stable
|
||||||
xcode: latest
|
xcode: latest
|
||||||
@@ -175,11 +175,7 @@ workflows:
|
|||||||
groups:
|
groups:
|
||||||
- client_app_dev_credentials
|
- client_app_dev_credentials
|
||||||
android_signing:
|
android_signing:
|
||||||
- keystore: krow_client_dev
|
- keystore: KROW_CLIENT_DEV
|
||||||
keystore_environment_variable: CM_KEYSTORE_PATH_CLIENT
|
|
||||||
keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_CLIENT
|
|
||||||
key_alias_environment_variable: CM_KEY_ALIAS_CLIENT
|
|
||||||
key_password_environment_variable: CM_KEY_PASSWORD_CLIENT
|
|
||||||
vars:
|
vars:
|
||||||
ENV: dev
|
ENV: dev
|
||||||
scripts:
|
scripts:
|
||||||
@@ -196,11 +192,7 @@ workflows:
|
|||||||
groups:
|
groups:
|
||||||
- client_app_staging_credentials
|
- client_app_staging_credentials
|
||||||
android_signing:
|
android_signing:
|
||||||
- keystore: krow_client_staging
|
- keystore: KROW_CLIENT_STAGING
|
||||||
keystore_environment_variable: CM_KEYSTORE_PATH_CLIENT
|
|
||||||
keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_CLIENT
|
|
||||||
key_alias_environment_variable: CM_KEY_ALIAS_CLIENT
|
|
||||||
key_password_environment_variable: CM_KEY_PASSWORD_CLIENT
|
|
||||||
vars:
|
vars:
|
||||||
ENV: staging
|
ENV: staging
|
||||||
scripts:
|
scripts:
|
||||||
@@ -209,12 +201,12 @@ workflows:
|
|||||||
|
|
||||||
client-app-prod-android:
|
client-app-prod-android:
|
||||||
<<: *client-app-base
|
<<: *client-app-base
|
||||||
name: 🚛🤖 Client App Prod (Android App Distribution)
|
name: 🚛 🤖 Client App Prod (Android App Distribution)
|
||||||
environment:
|
environment:
|
||||||
groups:
|
groups:
|
||||||
- client_app_prod_credentials
|
- client_app_prod_credentials
|
||||||
android_signing:
|
android_signing:
|
||||||
- keystore: krow_client_prod
|
- keystore: KROW_CLIENT_PROD
|
||||||
keystore_environment_variable: CM_KEYSTORE_PATH_CLIENT
|
keystore_environment_variable: CM_KEYSTORE_PATH_CLIENT
|
||||||
keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_CLIENT
|
keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_CLIENT
|
||||||
key_alias_environment_variable: CM_KEY_ALIAS_CLIENT
|
key_alias_environment_variable: CM_KEY_ALIAS_CLIENT
|
||||||
@@ -230,7 +222,7 @@ workflows:
|
|||||||
# =================================================================================
|
# =================================================================================
|
||||||
client-app-dev-ios:
|
client-app-dev-ios:
|
||||||
<<: *client-app-base
|
<<: *client-app-base
|
||||||
name: 🚛🍎 Client App Dev (iOS App Distribution)
|
name: 🚛 🍎 Client App Dev (iOS App Distribution)
|
||||||
environment:
|
environment:
|
||||||
groups:
|
groups:
|
||||||
- client_app_dev_credentials
|
- client_app_dev_credentials
|
||||||
@@ -242,7 +234,7 @@ workflows:
|
|||||||
|
|
||||||
client-app-staging-ios:
|
client-app-staging-ios:
|
||||||
<<: *client-app-base
|
<<: *client-app-base
|
||||||
name: 🚛🍎 Client App Staging (iOS App Distribution)
|
name: 🚛 🍎 Client App Staging (iOS App Distribution)
|
||||||
environment:
|
environment:
|
||||||
groups:
|
groups:
|
||||||
- client_app_staging_credentials
|
- client_app_staging_credentials
|
||||||
@@ -254,7 +246,7 @@ workflows:
|
|||||||
|
|
||||||
client-app-prod-ios:
|
client-app-prod-ios:
|
||||||
<<: *client-app-base
|
<<: *client-app-base
|
||||||
name: 🚛🍎 Client App Prod (iOS App Distribution)
|
name: 🚛 🍎 Client App Prod (iOS App Distribution)
|
||||||
environment:
|
environment:
|
||||||
groups:
|
groups:
|
||||||
- client_app_prod_credentials
|
- client_app_prod_credentials
|
||||||
@@ -269,7 +261,7 @@ workflows:
|
|||||||
# =================================================================================
|
# =================================================================================
|
||||||
staff-app-dev-android:
|
staff-app-dev-android:
|
||||||
<<: *staff-app-base
|
<<: *staff-app-base
|
||||||
name: 🚛🤖👨🍳 Staff App Dev (Android App Distribution)
|
name: 🚛 🤖 👨🍳 Staff App Dev (Android App Distribution)
|
||||||
environment:
|
environment:
|
||||||
flutter: stable
|
flutter: stable
|
||||||
xcode: latest
|
xcode: latest
|
||||||
@@ -277,11 +269,7 @@ workflows:
|
|||||||
groups:
|
groups:
|
||||||
- staff_app_dev_credentials
|
- staff_app_dev_credentials
|
||||||
android_signing:
|
android_signing:
|
||||||
- keystore: krow_staff_dev
|
- keystore: KROW_STAFF_DEV
|
||||||
keystore_environment_variable: CM_KEYSTORE_PATH_STAFF
|
|
||||||
keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_STAFF
|
|
||||||
key_alias_environment_variable: CM_KEY_ALIAS_STAFF
|
|
||||||
key_password_environment_variable: CM_KEY_PASSWORD_STAFF
|
|
||||||
vars:
|
vars:
|
||||||
ENV: dev
|
ENV: dev
|
||||||
scripts:
|
scripts:
|
||||||
@@ -290,7 +278,7 @@ workflows:
|
|||||||
|
|
||||||
staff-app-staging-android:
|
staff-app-staging-android:
|
||||||
<<: *staff-app-base
|
<<: *staff-app-base
|
||||||
name: 🚛🤖👨🍳 Staff App Staging (Android App Distribution)
|
name: 🚛 🤖 👨🍳 Staff App Staging (Android App Distribution)
|
||||||
environment:
|
environment:
|
||||||
flutter: stable
|
flutter: stable
|
||||||
xcode: latest
|
xcode: latest
|
||||||
@@ -298,11 +286,7 @@ workflows:
|
|||||||
groups:
|
groups:
|
||||||
- staff_app_staging_credentials
|
- staff_app_staging_credentials
|
||||||
android_signing:
|
android_signing:
|
||||||
- keystore: krow_staff_staging
|
- keystore: KROW_STAFF_STAGING
|
||||||
keystore_environment_variable: CM_KEYSTORE_PATH_STAFF
|
|
||||||
keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_STAFF
|
|
||||||
key_alias_environment_variable: CM_KEY_ALIAS_STAFF
|
|
||||||
key_password_environment_variable: CM_KEY_PASSWORD_STAFF
|
|
||||||
vars:
|
vars:
|
||||||
ENV: staging
|
ENV: staging
|
||||||
scripts:
|
scripts:
|
||||||
@@ -311,7 +295,7 @@ workflows:
|
|||||||
|
|
||||||
staff-app-prod-android:
|
staff-app-prod-android:
|
||||||
<<: *staff-app-base
|
<<: *staff-app-base
|
||||||
name: 🚛🤖👨🍳 Staff App Prod (Android App Distribution)
|
name: 🚛 🤖 👨🍳 Staff App Prod (Android App Distribution)
|
||||||
environment:
|
environment:
|
||||||
flutter: stable
|
flutter: stable
|
||||||
xcode: latest
|
xcode: latest
|
||||||
@@ -319,11 +303,7 @@ workflows:
|
|||||||
groups:
|
groups:
|
||||||
- staff_app_prod_credentials
|
- staff_app_prod_credentials
|
||||||
android_signing:
|
android_signing:
|
||||||
- keystore: krow_staff_prod
|
- keystore: KROW_STAFF_PROD
|
||||||
keystore_environment_variable: CM_KEYSTORE_PATH_STAFF
|
|
||||||
keystore_password_environment_variable: CM_KEYSTORE_PASSWORD_STAFF
|
|
||||||
key_alias_environment_variable: CM_KEY_ALIAS_STAFF
|
|
||||||
key_password_environment_variable: CM_KEY_PASSWORD_STAFF
|
|
||||||
vars:
|
vars:
|
||||||
ENV: prod
|
ENV: prod
|
||||||
scripts:
|
scripts:
|
||||||
@@ -335,7 +315,7 @@ workflows:
|
|||||||
# =================================================================================
|
# =================================================================================
|
||||||
staff-app-dev-ios:
|
staff-app-dev-ios:
|
||||||
<<: *staff-app-base
|
<<: *staff-app-base
|
||||||
name: 🚛🍎👨🍳 Staff App Dev (iOS App Distribution)
|
name: 🚛 🍎 👨🍳 Staff App Dev (iOS App Distribution)
|
||||||
environment:
|
environment:
|
||||||
groups:
|
groups:
|
||||||
- staff_app_dev_credentials
|
- staff_app_dev_credentials
|
||||||
@@ -347,7 +327,7 @@ workflows:
|
|||||||
|
|
||||||
staff-app-staging-ios:
|
staff-app-staging-ios:
|
||||||
<<: *staff-app-base
|
<<: *staff-app-base
|
||||||
name: 🚛🍎👨🍳 Staff App Staging (iOS App Distribution)
|
name: 🚛 🍎 👨🍳 Staff App Staging (iOS App Distribution)
|
||||||
environment:
|
environment:
|
||||||
groups:
|
groups:
|
||||||
- staff_app_staging_credentials
|
- staff_app_staging_credentials
|
||||||
@@ -359,7 +339,7 @@ workflows:
|
|||||||
|
|
||||||
staff-app-prod-ios:
|
staff-app-prod-ios:
|
||||||
<<: *staff-app-base
|
<<: *staff-app-base
|
||||||
name: 🚛🍎👨🍳 Staff App Prod (iOS App Distribution)
|
name: 🚛 🍎 👨🍳 Staff App Prod (iOS App Distribution)
|
||||||
environment:
|
environment:
|
||||||
groups:
|
groups:
|
||||||
- staff_app_prod_credentials
|
- staff_app_prod_credentials
|
||||||
|
|||||||
82
docs/MILESTONES/M4/demos/m4-client-note.md
Normal file
82
docs/MILESTONES/M4/demos/m4-client-note.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# KROW Workforce Platform — M4 Guide
|
||||||
|
|
||||||
|
**Version:** Milestone 4 (0.0.1-IlianaStaffM4 and 0.0.1-IlianaClientM4)
|
||||||
|
**Estimated Duration:** 25-30 minutes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Deliverables
|
||||||
|
|
||||||
|
- **Client Mobile Application** (v0.0.1-IlianaClientM4)
|
||||||
|
- **Staff Mobile Application** (v0.0.1-IlianaStaffM4)
|
||||||
|
- **Full Demo Video** - Comprehensive walkthrough of all (M1 - M4) completed features of the mobile applications.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Overview
|
||||||
|
|
||||||
|
### Core Improvements
|
||||||
|
M4 delivers three key areas of improvement:
|
||||||
|
|
||||||
|
1. **Overall Application Improvements**
|
||||||
|
- Auth session persistence: Users stay signed in after reopening the app
|
||||||
|
- Stability fixes from M3 client feedback and dev team discoveries
|
||||||
|
- UI/UX improvements across key screens for clarity and speed
|
||||||
|
|
||||||
|
2. **Client App Updates**
|
||||||
|
- Complete order creation flow (Rapid, Permanent, Recurring orders)
|
||||||
|
- Shift manager assignment support
|
||||||
|
- Paid/unpaid break handling in orders
|
||||||
|
- Complete Reports section (Daily Ops, Spend, Coverage, No-show, Performance)
|
||||||
|
- Cost centres in hubs for location/business unit tracking
|
||||||
|
- Billing approval workflow for pending bills
|
||||||
|
|
||||||
|
3. **Staff App Updates**
|
||||||
|
- Profile completion requirements gating payments and clockings
|
||||||
|
- Worker benefits integration
|
||||||
|
- Enhanced shift discovery with filtering by location
|
||||||
|
- Spanish localization support
|
||||||
|
- AI-verified document uploads (Attire, Documents, Certificates)
|
||||||
|
- FAQ and Privacy Policy
|
||||||
|
- Worker profile visibility controls
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Required Test Accounts
|
||||||
|
|
||||||
|
**Client Account (Business User):**
|
||||||
|
- Email: `legendary@krowd.com`
|
||||||
|
- Password: `Demo2026!`
|
||||||
|
- Client Name: "KROW"
|
||||||
|
|
||||||
|
**Staff Account (Worker):**
|
||||||
|
- Phone: `+15557654321`
|
||||||
|
- OTP Code: `123456` (testing mode)
|
||||||
|
- Name: "Mariana Torres"
|
||||||
|
|
||||||
|
***Note on Profile Completion***
|
||||||
|
When a staff user hasn't completed their profile, they see an empty/incomplete state on their home screen. Currently tracked sections to mark as complete:
|
||||||
|
- Profile Information (full name, email, phone, preferred locations)
|
||||||
|
- Emergency Contact
|
||||||
|
|
||||||
|
Future sections can be added as mandatory, such as Tax Forms, Bank Account, Documents, Certificates, and Attires.
|
||||||
|
|
||||||
|
***Profile Blocking Rules***
|
||||||
|
When the profile is incomplete, the following features are blocked to encourage completion:
|
||||||
|
- Clock-in page is hidden
|
||||||
|
- Payments are blocked
|
||||||
|
- "My Shifts" and History sections are hidden
|
||||||
|
- Users can view available shifts but cannot book them
|
||||||
|
|
||||||
|
This ensures we have all necessary information for compliance and payroll before workers are allowed to work.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. M4 Key Deliverables
|
||||||
|
|
||||||
|
✅ Stronger reliability and stability
|
||||||
|
✅ Completed client ordering and reporting workflows
|
||||||
|
✅ Better profile and shift tooling for staff
|
||||||
|
✅ AI-assisted document verification
|
||||||
|
✅ Localization support (Spanish)
|
||||||
|
✅ Improved billing and cost tracking controls
|
||||||
Reference in New Issue
Block a user