Add explicit types and improve type safety across codebase
This commit adds explicit type annotations to variables, function parameters, and return types throughout the codebase, particularly in widget trees, Bloc logic, and repository implementations. The changes improve code readability, maintainability, and type safety, and align with Dart best practices. No business logic was changed.
This commit is contained in:
@@ -103,7 +103,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
// action in the IDE, or press "p" in the console), to see the
|
// action in the IDE, or press "p" in the console), to see the
|
||||||
// wireframe for each widget.
|
// wireframe for each widget.
|
||||||
mainAxisAlignment: .center,
|
mainAxisAlignment: .center,
|
||||||
children: [
|
children: <Widget>[
|
||||||
const Text('You have pushed the button this many times:'),
|
const Text('You have pushed the button this many times:'),
|
||||||
Text(
|
Text(
|
||||||
'$_counter',
|
'$_counter',
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ void main() async {
|
|||||||
/// The main application module.
|
/// The main application module.
|
||||||
class AppModule extends Module {
|
class AppModule extends Module {
|
||||||
@override
|
@override
|
||||||
List<Module> get imports => [core_localization.LocalizationModule()];
|
List<Module> get imports => <Module>[core_localization.LocalizationModule()];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void routes(r) {
|
void routes(RouteManager r) {
|
||||||
// Set the initial route to the authentication module
|
// Set the initial route to the authentication module
|
||||||
r.module("/", module: staff_authentication.StaffAuthenticationModule());
|
r.module("/", module: staff_authentication.StaffAuthenticationModule());
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ class AppWidget extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider<core_localization.LocaleBloc>(
|
return BlocProvider<core_localization.LocaleBloc>(
|
||||||
create: (context) =>
|
create: (BuildContext context) =>
|
||||||
Modular.get<core_localization.LocaleBloc>()
|
Modular.get<core_localization.LocaleBloc>()
|
||||||
..add(const core_localization.LoadLocale()),
|
..add(const core_localization.LoadLocale()),
|
||||||
child:
|
child:
|
||||||
@@ -48,14 +48,14 @@ class AppWidget extends StatelessWidget {
|
|||||||
core_localization.LocaleBloc,
|
core_localization.LocaleBloc,
|
||||||
core_localization.LocaleState
|
core_localization.LocaleState
|
||||||
>(
|
>(
|
||||||
builder: (context, state) {
|
builder: (BuildContext context, core_localization.LocaleState state) {
|
||||||
return MaterialApp.router(
|
return MaterialApp.router(
|
||||||
title: "KROW Staff",
|
title: "KROW Staff",
|
||||||
theme: UiTheme.light,
|
theme: UiTheme.light,
|
||||||
routerConfig: Modular.routerConfig,
|
routerConfig: Modular.routerConfig,
|
||||||
locale: state.locale,
|
locale: state.locale,
|
||||||
supportedLocales: state.supportedLocales,
|
supportedLocales: state.supportedLocales,
|
||||||
localizationsDelegates: const [
|
localizationsDelegates: const <LocalizationsDelegate<dynamic>>[
|
||||||
GlobalMaterialLocalizations.delegate,
|
GlobalMaterialLocalizations.delegate,
|
||||||
GlobalWidgetsLocalizations.delegate,
|
GlobalWidgetsLocalizations.delegate,
|
||||||
GlobalCupertinoLocalizations.delegate,
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ abstract class UseCaseArgument extends Equatable {
|
|||||||
const UseCaseArgument();
|
const UseCaseArgument();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
List<Object?> get props => <Object?>[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ class LocaleBloc extends Bloc<LocaleEvent, LocaleState> {
|
|||||||
LoadLocale event,
|
LoadLocale event,
|
||||||
Emitter<LocaleState> emit,
|
Emitter<LocaleState> emit,
|
||||||
) async {
|
) async {
|
||||||
final savedLocale = await getLocaleUseCase();
|
final Locale? savedLocale = await getLocaleUseCase();
|
||||||
final locale = const Locale('es');
|
final Locale locale = const Locale('es');
|
||||||
|
|
||||||
LocaleSettings.setLocaleRaw(locale.languageCode);
|
LocaleSettings.setLocaleRaw(locale.languageCode);
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class LocaleRepositoryImpl implements LocaleRepositoryInterface {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Locale?> getSavedLocale() async {
|
Future<Locale?> getSavedLocale() async {
|
||||||
final languageCode = await _localDataSource.getLanguageCode();
|
final String? languageCode = await _localDataSource.getLanguageCode();
|
||||||
if (languageCode != null) {
|
if (languageCode != null) {
|
||||||
return Locale(languageCode);
|
return Locale(languageCode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ class HomeRepositoryMock {
|
|||||||
///
|
///
|
||||||
/// Returns a tuple of (businessName, photoUrl).
|
/// Returns a tuple of (businessName, photoUrl).
|
||||||
(String, String?) getUserSession() {
|
(String, String?) getUserSession() {
|
||||||
final session = ClientSessionStore.instance.session;
|
final ClientSession? session = ClientSessionStore.instance.session;
|
||||||
final businessName = session?.business?.businessName ?? 'Your Company';
|
final String businessName = session?.business?.businessName ?? 'Your Company';
|
||||||
final photoUrl = session?.userPhotoUrl;
|
final String? photoUrl = session?.userPhotoUrl;
|
||||||
return (businessName, photoUrl);
|
return (businessName, photoUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class UiTheme {
|
|||||||
),
|
),
|
||||||
maximumSize: const Size(double.infinity, 54),
|
maximumSize: const Size(double.infinity, 54),
|
||||||
).copyWith(
|
).copyWith(
|
||||||
side: WidgetStateProperty.resolveWith<BorderSide?>((states) {
|
side: WidgetStateProperty.resolveWith<BorderSide?>((Set<WidgetState> states) {
|
||||||
if (states.contains(WidgetState.disabled)) {
|
if (states.contains(WidgetState.disabled)) {
|
||||||
return const BorderSide(
|
return const BorderSide(
|
||||||
color: UiColors.borderPrimary,
|
color: UiColors.borderPrimary,
|
||||||
@@ -79,7 +79,7 @@ class UiTheme {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
overlayColor: WidgetStateProperty.resolveWith((states) {
|
overlayColor: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
|
||||||
if (states.contains(WidgetState.hovered))
|
if (states.contains(WidgetState.hovered))
|
||||||
return UiColors.buttonPrimaryHover;
|
return UiColors.buttonPrimaryHover;
|
||||||
return null;
|
return null;
|
||||||
@@ -238,7 +238,7 @@ class UiTheme {
|
|||||||
navigationBarTheme: NavigationBarThemeData(
|
navigationBarTheme: NavigationBarThemeData(
|
||||||
backgroundColor: UiColors.white,
|
backgroundColor: UiColors.white,
|
||||||
indicatorColor: UiColors.primaryInverse.withAlpha(51), // 20% of 255
|
indicatorColor: UiColors.primaryInverse.withAlpha(51), // 20% of 255
|
||||||
labelTextStyle: WidgetStateProperty.resolveWith((states) {
|
labelTextStyle: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
|
||||||
if (states.contains(WidgetState.selected)) {
|
if (states.contains(WidgetState.selected)) {
|
||||||
return UiTypography.footnote2m.textPrimary;
|
return UiTypography.footnote2m.textPrimary;
|
||||||
}
|
}
|
||||||
@@ -248,7 +248,7 @@ class UiTheme {
|
|||||||
|
|
||||||
// Switch Theme
|
// Switch Theme
|
||||||
switchTheme: SwitchThemeData(
|
switchTheme: SwitchThemeData(
|
||||||
trackColor: WidgetStateProperty.resolveWith<Color>((states) {
|
trackColor: WidgetStateProperty.resolveWith<Color>((Set<WidgetState> states) {
|
||||||
if (states.contains(WidgetState.selected)) {
|
if (states.contains(WidgetState.selected)) {
|
||||||
return UiColors.switchActive;
|
return UiColors.switchActive;
|
||||||
}
|
}
|
||||||
@@ -259,7 +259,7 @@ class UiTheme {
|
|||||||
|
|
||||||
// Checkbox Theme
|
// Checkbox Theme
|
||||||
checkboxTheme: CheckboxThemeData(
|
checkboxTheme: CheckboxThemeData(
|
||||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
fillColor: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
|
||||||
if (states.contains(WidgetState.selected)) return UiColors.primary;
|
if (states.contains(WidgetState.selected)) return UiColors.primary;
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
@@ -268,7 +268,7 @@ class UiTheme {
|
|||||||
|
|
||||||
// Radio Theme
|
// Radio Theme
|
||||||
radioTheme: RadioThemeData(
|
radioTheme: RadioThemeData(
|
||||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
fillColor: WidgetStateProperty.resolveWith((Set<WidgetState> states) {
|
||||||
if (states.contains(WidgetState.selected)) return UiColors.primary;
|
if (states.contains(WidgetState.selected)) return UiColors.primary;
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ class UiButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Multiple elements case: Use a Row with MainAxisSize.min
|
// Multiple elements case: Use a Row with MainAxisSize.min
|
||||||
final List<Widget> children = [];
|
final List<Widget> children = <Widget>[];
|
||||||
|
|
||||||
if (leadingIcon != null) {
|
if (leadingIcon != null) {
|
||||||
children.add(Icon(leadingIcon, size: iconSize));
|
children.add(Icon(leadingIcon, size: iconSize));
|
||||||
|
|||||||
@@ -68,21 +68,21 @@ class UiChip extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final backgroundColor = _getBackgroundColor();
|
final Color backgroundColor = _getBackgroundColor();
|
||||||
final contentColor = _getContentColor();
|
final Color contentColor = _getContentColor();
|
||||||
final textStyle = _getTextStyle().copyWith(color: contentColor);
|
final TextStyle textStyle = _getTextStyle().copyWith(color: contentColor);
|
||||||
final padding = _getPadding();
|
final EdgeInsets padding = _getPadding();
|
||||||
final iconSize = _getIconSize();
|
final double iconSize = _getIconSize();
|
||||||
|
|
||||||
final content = Row(
|
final Row content = Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: <Widget>[
|
||||||
if (leadingIcon != null) ...[
|
if (leadingIcon != null) ...<Widget>[
|
||||||
Icon(leadingIcon, size: iconSize, color: contentColor),
|
Icon(leadingIcon, size: iconSize, color: contentColor),
|
||||||
SizedBox(width: _getGap()),
|
SizedBox(width: _getGap()),
|
||||||
],
|
],
|
||||||
Text(label, style: textStyle),
|
Text(label, style: textStyle),
|
||||||
if (trailingIcon != null) ...[
|
if (trailingIcon != null) ...<Widget>[
|
||||||
SizedBox(width: _getGap()),
|
SizedBox(width: _getGap()),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: onTrailingIconTap,
|
onTap: onTrailingIconTap,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class UiStepIndicator extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.symmetric(vertical: UiConstants.space2),
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space2),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: List.generate(stepIcons.length, (index) {
|
children: List.generate(stepIcons.length, (int index) {
|
||||||
final bool isActive = index == currentStep;
|
final bool isActive = index == currentStep;
|
||||||
final bool isCompleted = index < currentStep;
|
final bool isCompleted = index < currentStep;
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ class UiStepIndicator extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ class UiTextField extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: <Widget>[
|
||||||
if (label != null) ...[
|
if (label != null) ...<Widget>[
|
||||||
Text(label!, style: UiTypography.body4m.textSecondary),
|
Text(label!, style: UiTypography.body4m.textSecondary),
|
||||||
const SizedBox(height: UiConstants.space1),
|
const SizedBox(height: UiConstants.space1),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export 'package:core_localization/core_localization.dart';
|
|||||||
/// A [Module] for the client authentication feature.
|
/// A [Module] for the client authentication feature.
|
||||||
class ClientAuthenticationModule extends Module {
|
class ClientAuthenticationModule extends Module {
|
||||||
@override
|
@override
|
||||||
List<Module> get imports => [DataConnectModule()];
|
List<Module> get imports => <Module>[DataConnectModule()];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void binds(Injector i) {
|
void binds(Injector i) {
|
||||||
@@ -59,7 +59,7 @@ class ClientAuthenticationModule extends Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void routes(r) {
|
void routes(RouteManager r) {
|
||||||
r.child('/', child: (_) => const ClientGetStartedPage());
|
r.child('/', child: (_) => const ClientGetStartedPage());
|
||||||
r.child('/client-sign-in', child: (_) => const ClientSignInPage());
|
r.child('/client-sign-in', child: (_) => const ClientSignInPage());
|
||||||
r.child('/client-sign-up', child: (_) => const ClientSignUpPage());
|
r.child('/client-sign-up', child: (_) => const ClientSignUpPage());
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||||
|
import 'package:firebase_data_connect/src/core/ref.dart';
|
||||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||||
import 'package:krow_domain/krow_domain.dart' as domain;
|
import 'package:krow_domain/krow_domain.dart' as domain;
|
||||||
import '../../domain/repositories/auth_repository_interface.dart';
|
import '../../domain/repositories/auth_repository_interface.dart';
|
||||||
@@ -24,12 +25,12 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
|||||||
required String password,
|
required String password,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final credential = await _firebaseAuth.signInWithEmailAndPassword(
|
final firebase.UserCredential credential = await _firebaseAuth.signInWithEmailAndPassword(
|
||||||
email: email,
|
email: email,
|
||||||
password: password,
|
password: password,
|
||||||
);
|
);
|
||||||
|
|
||||||
final firebaseUser = credential.user;
|
final firebase.User? firebaseUser = credential.user;
|
||||||
if (firebaseUser == null) {
|
if (firebaseUser == null) {
|
||||||
throw Exception('Sign-in failed, no Firebase user received.');
|
throw Exception('Sign-in failed, no Firebase user received.');
|
||||||
}
|
}
|
||||||
@@ -59,12 +60,12 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
|||||||
required String password,
|
required String password,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final credential = await _firebaseAuth.createUserWithEmailAndPassword(
|
final firebase.UserCredential credential = await _firebaseAuth.createUserWithEmailAndPassword(
|
||||||
email: email,
|
email: email,
|
||||||
password: password,
|
password: password,
|
||||||
);
|
);
|
||||||
|
|
||||||
final firebaseUser = credential.user;
|
final firebase.User? firebaseUser = credential.user;
|
||||||
if (firebaseUser == null) {
|
if (firebaseUser == null) {
|
||||||
throw Exception('Sign-up failed, Firebase user could not be created.');
|
throw Exception('Sign-up failed, Firebase user could not be created.');
|
||||||
}
|
}
|
||||||
@@ -72,20 +73,20 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
|||||||
// Client-specific business logic:
|
// Client-specific business logic:
|
||||||
// 1. Create a `Business` entity.
|
// 1. Create a `Business` entity.
|
||||||
// 2. Create a `User` entity associated with the business.
|
// 2. Create a `User` entity associated with the business.
|
||||||
final createBusinessResponse = await _dataConnect.createBusiness(
|
final OperationResult<dc.CreateBusinessData, dc.CreateBusinessVariables> createBusinessResponse = await _dataConnect.createBusiness(
|
||||||
businessName: companyName,
|
businessName: companyName,
|
||||||
userId: firebaseUser.uid,
|
userId: firebaseUser.uid,
|
||||||
rateGroup: dc.BusinessRateGroup.STANDARD,
|
rateGroup: dc.BusinessRateGroup.STANDARD,
|
||||||
status: dc.BusinessStatus.PENDING,
|
status: dc.BusinessStatus.PENDING,
|
||||||
).execute();
|
).execute();
|
||||||
|
|
||||||
final businessData = createBusinessResponse.data?.business_insert;
|
final dc.CreateBusinessBusinessInsert? businessData = createBusinessResponse.data?.business_insert;
|
||||||
if (businessData == null) {
|
if (businessData == null) {
|
||||||
await firebaseUser.delete(); // Rollback if business creation fails
|
await firebaseUser.delete(); // Rollback if business creation fails
|
||||||
throw Exception('Business creation failed after Firebase user registration.');
|
throw Exception('Business creation failed after Firebase user registration.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final createUserResponse = await _dataConnect.createUser(
|
final OperationResult<dc.CreateUserData, dc.CreateUserVariables> createUserResponse = await _dataConnect.createUser(
|
||||||
id: firebaseUser.uid,
|
id: firebaseUser.uid,
|
||||||
role: dc.UserBaseRole.USER,
|
role: dc.UserBaseRole.USER,
|
||||||
)
|
)
|
||||||
@@ -93,7 +94,7 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
|||||||
.userRole('BUSINESS')
|
.userRole('BUSINESS')
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
final newUserData = createUserResponse.data?.user_insert;
|
final dc.CreateUserUserInsert? newUserData = createUserResponse.data?.user_insert;
|
||||||
if (newUserData == null) {
|
if (newUserData == null) {
|
||||||
await firebaseUser.delete(); // Rollback if user profile creation fails
|
await firebaseUser.delete(); // Rollback if user profile creation fails
|
||||||
// TO-DO: Also delete the created Business if this fails
|
// TO-DO: Also delete the created Business if this fails
|
||||||
@@ -137,27 +138,27 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
|||||||
required String firebaseUserId,
|
required String firebaseUserId,
|
||||||
required String? fallbackEmail,
|
required String? fallbackEmail,
|
||||||
}) async {
|
}) async {
|
||||||
final response = await _dataConnect.getUserById(id: firebaseUserId).execute();
|
final QueryResult<dc.GetUserByIdData, dc.GetUserByIdVariables> response = await _dataConnect.getUserById(id: firebaseUserId).execute();
|
||||||
final user = response.data?.user;
|
final dc.GetUserByIdUser? user = response.data?.user;
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw Exception('Authenticated user profile not found in database.');
|
throw Exception('Authenticated user profile not found in database.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final email = user.email ?? fallbackEmail;
|
final String? email = user.email ?? fallbackEmail;
|
||||||
if (email == null || email.isEmpty) {
|
if (email == null || email.isEmpty) {
|
||||||
throw Exception('User email is missing in profile data.');
|
throw Exception('User email is missing in profile data.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final domainUser = domain.User(
|
final domain.User domainUser = domain.User(
|
||||||
id: user.id,
|
id: user.id,
|
||||||
email: email,
|
email: email,
|
||||||
role: user.role.stringValue,
|
role: user.role.stringValue,
|
||||||
);
|
);
|
||||||
|
|
||||||
final businessResponse = await _dataConnect.getBusinessesByUserId(
|
final QueryResult<dc.GetBusinessesByUserIdData, dc.GetBusinessesByUserIdVariables> businessResponse = await _dataConnect.getBusinessesByUserId(
|
||||||
userId: firebaseUserId,
|
userId: firebaseUserId,
|
||||||
).execute();
|
).execute();
|
||||||
final business = businessResponse.data.businesses.isNotEmpty
|
final dc.GetBusinessesByUserIdBusinesses? business = businessResponse.data.businesses.isNotEmpty
|
||||||
? businessResponse.data.businesses.first
|
? businessResponse.data.businesses.first
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ class SignInWithEmailArguments extends UseCaseArgument {
|
|||||||
const SignInWithEmailArguments({required this.email, required this.password});
|
const SignInWithEmailArguments({required this.email, required this.password});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [email, password];
|
List<Object?> get props => <Object?>[email, password];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ class SignInWithSocialArguments extends UseCaseArgument {
|
|||||||
const SignInWithSocialArguments({required this.provider});
|
const SignInWithSocialArguments({required this.provider});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [provider];
|
List<Object?> get props => <Object?>[provider];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,5 +18,5 @@ class SignUpWithEmailArguments extends UseCaseArgument {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [companyName, email, password];
|
List<Object?> get props => <Object?>[companyName, email, password];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:krow_domain/src/entities/users/user.dart';
|
||||||
import '../../domain/arguments/sign_in_with_email_arguments.dart';
|
import '../../domain/arguments/sign_in_with_email_arguments.dart';
|
||||||
import '../../domain/arguments/sign_in_with_social_arguments.dart';
|
import '../../domain/arguments/sign_in_with_social_arguments.dart';
|
||||||
import '../../domain/arguments/sign_up_with_email_arguments.dart';
|
import '../../domain/arguments/sign_up_with_email_arguments.dart';
|
||||||
@@ -50,7 +51,7 @@ class ClientAuthBloc extends Bloc<ClientAuthEvent, ClientAuthState> {
|
|||||||
) async {
|
) async {
|
||||||
emit(state.copyWith(status: ClientAuthStatus.loading));
|
emit(state.copyWith(status: ClientAuthStatus.loading));
|
||||||
try {
|
try {
|
||||||
final user = await _signInWithEmail(
|
final User user = await _signInWithEmail(
|
||||||
SignInWithEmailArguments(email: event.email, password: event.password),
|
SignInWithEmailArguments(email: event.email, password: event.password),
|
||||||
);
|
);
|
||||||
emit(state.copyWith(status: ClientAuthStatus.authenticated, user: user));
|
emit(state.copyWith(status: ClientAuthStatus.authenticated, user: user));
|
||||||
@@ -71,7 +72,7 @@ class ClientAuthBloc extends Bloc<ClientAuthEvent, ClientAuthState> {
|
|||||||
) async {
|
) async {
|
||||||
emit(state.copyWith(status: ClientAuthStatus.loading));
|
emit(state.copyWith(status: ClientAuthStatus.loading));
|
||||||
try {
|
try {
|
||||||
final user = await _signUpWithEmail(
|
final User user = await _signUpWithEmail(
|
||||||
SignUpWithEmailArguments(
|
SignUpWithEmailArguments(
|
||||||
companyName: event.companyName,
|
companyName: event.companyName,
|
||||||
email: event.email,
|
email: event.email,
|
||||||
@@ -96,7 +97,7 @@ class ClientAuthBloc extends Bloc<ClientAuthEvent, ClientAuthState> {
|
|||||||
) async {
|
) async {
|
||||||
emit(state.copyWith(status: ClientAuthStatus.loading));
|
emit(state.copyWith(status: ClientAuthStatus.loading));
|
||||||
try {
|
try {
|
||||||
final user = await _signInWithSocial(
|
final User user = await _signInWithSocial(
|
||||||
SignInWithSocialArguments(provider: event.provider),
|
SignInWithSocialArguments(provider: event.provider),
|
||||||
);
|
);
|
||||||
emit(state.copyWith(status: ClientAuthStatus.authenticated, user: user));
|
emit(state.copyWith(status: ClientAuthStatus.authenticated, user: user));
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ abstract class ClientAuthEvent extends Equatable {
|
|||||||
const ClientAuthEvent();
|
const ClientAuthEvent();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
List<Object?> get props => <Object?>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event dispatched when a user attempts to sign in with email and password.
|
/// Event dispatched when a user attempts to sign in with email and password.
|
||||||
@@ -16,7 +16,7 @@ class ClientSignInRequested extends ClientAuthEvent {
|
|||||||
const ClientSignInRequested({required this.email, required this.password});
|
const ClientSignInRequested({required this.email, required this.password});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [email, password];
|
List<Object?> get props => <Object?>[email, password];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event dispatched when a user attempts to create a new business account.
|
/// Event dispatched when a user attempts to create a new business account.
|
||||||
@@ -32,7 +32,7 @@ class ClientSignUpRequested extends ClientAuthEvent {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [companyName, email, password];
|
List<Object?> get props => <Object?>[companyName, email, password];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event dispatched for third-party authentication (Google/Apple).
|
/// Event dispatched for third-party authentication (Google/Apple).
|
||||||
@@ -42,7 +42,7 @@ class ClientSocialSignInRequested extends ClientAuthEvent {
|
|||||||
const ClientSocialSignInRequested({required this.provider});
|
const ClientSocialSignInRequested({required this.provider});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [provider];
|
List<Object?> get props => <Object?>[provider];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event dispatched when the user requests to terminate their session.
|
/// Event dispatched when the user requests to terminate their session.
|
||||||
|
|||||||
@@ -50,5 +50,5 @@ class ClientAuthState extends Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [status, user, errorMessage];
|
List<Object?> get props => <Object?>[status, user, errorMessage];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class ClientGetStartedPage extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: <Widget>[
|
||||||
// Background Illustration/Visuals from prototype
|
// Background Illustration/Visuals from prototype
|
||||||
Positioned(
|
Positioned(
|
||||||
top: -100,
|
top: -100,
|
||||||
@@ -28,7 +28,7 @@ class ClientGetStartedPage extends StatelessWidget {
|
|||||||
|
|
||||||
SafeArea(
|
SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
const SizedBox(height: UiConstants.space10),
|
const SizedBox(height: UiConstants.space10),
|
||||||
// Logo
|
// Logo
|
||||||
Center(
|
Center(
|
||||||
@@ -48,7 +48,7 @@ class ClientGetStartedPage extends StatelessWidget {
|
|||||||
horizontal: UiConstants.space6,
|
horizontal: UiConstants.space6,
|
||||||
),
|
),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: <Widget>[
|
||||||
// Representative cards from prototype
|
// Representative cards from prototype
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 20,
|
top: 20,
|
||||||
@@ -76,7 +76,7 @@ class ClientGetStartedPage extends StatelessWidget {
|
|||||||
vertical: UiConstants.space10,
|
vertical: UiConstants.space10,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.client_authentication.get_started_page.title,
|
t.client_authentication.get_started_page.title,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@@ -132,7 +132,7 @@ class _ShiftOrderCard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
boxShadow: [
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withOpacity(0.1),
|
color: UiColors.black.withOpacity(0.1),
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
@@ -143,9 +143,9 @@ class _ShiftOrderCard extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(UiConstants.space1),
|
padding: const EdgeInsets.all(UiConstants.space1),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@@ -195,7 +195,7 @@ class _WorkerProfileCard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
boxShadow: [
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withOpacity(0.1),
|
color: UiColors.black.withOpacity(0.1),
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
@@ -204,7 +204,7 @@ class _WorkerProfileCard extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 16,
|
radius: 16,
|
||||||
backgroundColor: UiColors.primary.withOpacity(0.1),
|
backgroundColor: UiColors.primary.withOpacity(0.1),
|
||||||
@@ -214,7 +214,7 @@ class _WorkerProfileCard extends StatelessWidget {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text('Alex Thompson', style: UiTypography.footnote1b),
|
Text('Alex Thompson', style: UiTypography.footnote1b),
|
||||||
Text(
|
Text(
|
||||||
'Professional Waiter • 4.9★',
|
'Professional Waiter • 4.9★',
|
||||||
@@ -236,7 +236,7 @@ class _CalendarCard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.accent,
|
color: UiColors.accent,
|
||||||
borderRadius: UiConstants.radiusMd,
|
borderRadius: UiConstants.radiusMd,
|
||||||
boxShadow: [
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withOpacity(0.1),
|
color: UiColors.black.withOpacity(0.1),
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
|
|||||||
@@ -35,13 +35,13 @@ class ClientSignInPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.client_authentication.sign_in_page;
|
final TranslationsClientAuthenticationSignInPageEn i18n = t.client_authentication.sign_in_page;
|
||||||
final authBloc = Modular.get<ClientAuthBloc>();
|
final ClientAuthBloc authBloc = Modular.get<ClientAuthBloc>();
|
||||||
|
|
||||||
return BlocProvider.value(
|
return BlocProvider.value(
|
||||||
value: authBloc,
|
value: authBloc,
|
||||||
child: BlocConsumer<ClientAuthBloc, ClientAuthState>(
|
child: BlocConsumer<ClientAuthBloc, ClientAuthState>(
|
||||||
listener: (context, state) {
|
listener: (BuildContext context, ClientAuthState state) {
|
||||||
if (state.status == ClientAuthStatus.authenticated) {
|
if (state.status == ClientAuthStatus.authenticated) {
|
||||||
Modular.to.navigateClientHome();
|
Modular.to.navigateClientHome();
|
||||||
} else if (state.status == ClientAuthStatus.error) {
|
} else if (state.status == ClientAuthStatus.error) {
|
||||||
@@ -52,8 +52,8 @@ class ClientSignInPage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: (context, state) {
|
builder: (BuildContext context, ClientAuthState state) {
|
||||||
final isLoading = state.status == ClientAuthStatus.loading;
|
final bool isLoading = state.status == ClientAuthStatus.loading;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: const UiAppBar(showBackButton: true),
|
appBar: const UiAppBar(showBackButton: true),
|
||||||
@@ -69,14 +69,14 @@ class ClientSignInPage extends StatelessWidget {
|
|||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: <Widget>[
|
||||||
SectionTitle(title: i18n.title, subtitle: i18n.subtitle),
|
SectionTitle(title: i18n.title, subtitle: i18n.subtitle),
|
||||||
const SizedBox(height: UiConstants.space8),
|
const SizedBox(height: UiConstants.space8),
|
||||||
|
|
||||||
// Sign In Form
|
// Sign In Form
|
||||||
ClientSignInForm(
|
ClientSignInForm(
|
||||||
isLoading: isLoading,
|
isLoading: isLoading,
|
||||||
onSignIn: ({required email, required password}) =>
|
onSignIn: ({required String email, required String password}) =>
|
||||||
_handleSignIn(
|
_handleSignIn(
|
||||||
context,
|
context,
|
||||||
email: email,
|
email: email,
|
||||||
@@ -99,7 +99,7 @@ class ClientSignInPage extends StatelessWidget {
|
|||||||
// Sign Up Link
|
// Sign Up Link
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
i18n.no_account,
|
i18n.no_account,
|
||||||
style: UiTypography.body2r.textSecondary,
|
style: UiTypography.body2r.textSecondary,
|
||||||
|
|||||||
@@ -39,13 +39,13 @@ class ClientSignUpPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.client_authentication.sign_up_page;
|
final TranslationsClientAuthenticationSignUpPageEn i18n = t.client_authentication.sign_up_page;
|
||||||
final authBloc = Modular.get<ClientAuthBloc>();
|
final ClientAuthBloc authBloc = Modular.get<ClientAuthBloc>();
|
||||||
|
|
||||||
return BlocProvider.value(
|
return BlocProvider.value(
|
||||||
value: authBloc,
|
value: authBloc,
|
||||||
child: BlocConsumer<ClientAuthBloc, ClientAuthState>(
|
child: BlocConsumer<ClientAuthBloc, ClientAuthState>(
|
||||||
listener: (context, state) {
|
listener: (BuildContext context, ClientAuthState state) {
|
||||||
if (state.status == ClientAuthStatus.authenticated) {
|
if (state.status == ClientAuthStatus.authenticated) {
|
||||||
Modular.to.navigateClientHome();
|
Modular.to.navigateClientHome();
|
||||||
} else if (state.status == ClientAuthStatus.error) {
|
} else if (state.status == ClientAuthStatus.error) {
|
||||||
@@ -56,8 +56,8 @@ class ClientSignUpPage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: (context, state) {
|
builder: (BuildContext context, ClientAuthState state) {
|
||||||
final isLoading = state.status == ClientAuthStatus.loading;
|
final bool isLoading = state.status == ClientAuthStatus.loading;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: const UiAppBar(showBackButton: true),
|
appBar: const UiAppBar(showBackButton: true),
|
||||||
@@ -73,7 +73,7 @@ class ClientSignUpPage extends StatelessWidget {
|
|||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: <Widget>[
|
||||||
SectionTitle(title: i18n.title, subtitle: i18n.subtitle),
|
SectionTitle(title: i18n.title, subtitle: i18n.subtitle),
|
||||||
const SizedBox(height: UiConstants.space8),
|
const SizedBox(height: UiConstants.space8),
|
||||||
|
|
||||||
@@ -82,9 +82,9 @@ class ClientSignUpPage extends StatelessWidget {
|
|||||||
isLoading: isLoading,
|
isLoading: isLoading,
|
||||||
onSignUp:
|
onSignUp:
|
||||||
({
|
({
|
||||||
required companyName,
|
required String companyName,
|
||||||
required email,
|
required String email,
|
||||||
required password,
|
required String password,
|
||||||
}) => _handleSignUp(
|
}) => _handleSignUp(
|
||||||
context,
|
context,
|
||||||
companyName: companyName,
|
companyName: companyName,
|
||||||
@@ -108,7 +108,7 @@ class ClientSignUpPage extends StatelessWidget {
|
|||||||
// Sign In Link
|
// Sign In Link
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
i18n.has_account,
|
i18n.has_account,
|
||||||
style: UiTypography.body2r.textSecondary,
|
style: UiTypography.body2r.textSecondary,
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ class ClientSignInForm extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ClientSignInFormState extends State<ClientSignInForm> {
|
class _ClientSignInFormState extends State<ClientSignInForm> {
|
||||||
final _emailController = TextEditingController();
|
final TextEditingController _emailController = TextEditingController();
|
||||||
final _passwordController = TextEditingController();
|
final TextEditingController _passwordController = TextEditingController();
|
||||||
bool _obscurePassword = true;
|
bool _obscurePassword = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -46,10 +46,10 @@ class _ClientSignInFormState extends State<ClientSignInForm> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.client_authentication.sign_in_page;
|
final TranslationsClientAuthenticationSignInPageEn i18n = t.client_authentication.sign_in_page;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
// Email Field
|
// Email Field
|
||||||
UiTextField(
|
UiTextField(
|
||||||
label: i18n.email_label,
|
label: i18n.email_label,
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ class ClientSignUpForm extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ClientSignUpFormState extends State<ClientSignUpForm> {
|
class _ClientSignUpFormState extends State<ClientSignUpForm> {
|
||||||
final _companyController = TextEditingController();
|
final TextEditingController _companyController = TextEditingController();
|
||||||
final _emailController = TextEditingController();
|
final TextEditingController _emailController = TextEditingController();
|
||||||
final _passwordController = TextEditingController();
|
final TextEditingController _passwordController = TextEditingController();
|
||||||
final _confirmPasswordController = TextEditingController();
|
final TextEditingController _confirmPasswordController = TextEditingController();
|
||||||
bool _obscurePassword = true;
|
bool _obscurePassword = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -62,10 +62,10 @@ class _ClientSignUpFormState extends State<ClientSignUpForm> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.client_authentication.sign_up_page;
|
final TranslationsClientAuthenticationSignUpPageEn i18n = t.client_authentication.sign_up_page;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
// Company Name Field
|
// Company Name Field
|
||||||
UiTextField(
|
UiTextField(
|
||||||
label: i18n.company_label,
|
label: i18n.company_label,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class AuthDivider extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
const Expanded(child: Divider()),
|
const Expanded(child: Divider()),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space4),
|
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space4),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class SectionTitle extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(title, style: UiTypography.headline1m),
|
Text(title, style: UiTypography.headline1m),
|
||||||
Text(subtitle, style: UiTypography.body2r.textSecondary),
|
Text(subtitle, style: UiTypography.body2r.textSecondary),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -52,18 +52,18 @@ class ClientCreateOrderRepositoryImpl
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> createOneTimeOrder(domain.OneTimeOrder order) async {
|
Future<void> createOneTimeOrder(domain.OneTimeOrder order) async {
|
||||||
final businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||||
if (businessId == null || businessId.isEmpty) {
|
if (businessId == null || businessId.isEmpty) {
|
||||||
await _firebaseAuth.signOut();
|
await _firebaseAuth.signOut();
|
||||||
throw Exception('Business is missing. Please sign in again.');
|
throw Exception('Business is missing. Please sign in again.');
|
||||||
}
|
}
|
||||||
final vendorId = order.vendorId;
|
final String? vendorId = order.vendorId;
|
||||||
if (vendorId == null || vendorId.isEmpty) {
|
if (vendorId == null || vendorId.isEmpty) {
|
||||||
throw Exception('Vendor is missing.');
|
throw Exception('Vendor is missing.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final orderTimestamp = _toTimestamp(order.date);
|
final fdc.Timestamp orderTimestamp = _toTimestamp(order.date);
|
||||||
final orderResult = await _dataConnect
|
final fdc.OperationResult<dc.CreateOrderData, dc.CreateOrderVariables> orderResult = await _dataConnect
|
||||||
.createOrder(businessId: businessId, orderType: dc.OrderType.ONE_TIME)
|
.createOrder(businessId: businessId, orderType: dc.OrderType.ONE_TIME)
|
||||||
.vendorId(vendorId)
|
.vendorId(vendorId)
|
||||||
.location(order.location)
|
.location(order.location)
|
||||||
@@ -71,19 +71,19 @@ class ClientCreateOrderRepositoryImpl
|
|||||||
.date(orderTimestamp)
|
.date(orderTimestamp)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
final orderId = orderResult.data?.order_insert.id;
|
final String? orderId = orderResult.data?.order_insert.id;
|
||||||
if (orderId == null) {
|
if (orderId == null) {
|
||||||
throw Exception('Order creation failed.');
|
throw Exception('Order creation failed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final workersNeeded = order.positions.fold<int>(
|
final int workersNeeded = order.positions.fold<int>(
|
||||||
0,
|
0,
|
||||||
(sum, position) => sum + position.count,
|
(int sum, domain.OneTimeOrderPosition position) => sum + position.count,
|
||||||
);
|
);
|
||||||
final shiftTitle = 'Shift 1 ${_formatDate(order.date)}';
|
final String shiftTitle = 'Shift 1 ${_formatDate(order.date)}';
|
||||||
final shiftCost = _calculateShiftCost(order);
|
final double shiftCost = _calculateShiftCost(order);
|
||||||
|
|
||||||
final shiftResult = await _dataConnect
|
final fdc.OperationResult<dc.CreateShiftData, dc.CreateShiftVariables> shiftResult = await _dataConnect
|
||||||
.createShift(title: shiftTitle, orderId: orderId)
|
.createShift(title: shiftTitle, orderId: orderId)
|
||||||
.date(orderTimestamp)
|
.date(orderTimestamp)
|
||||||
.location(order.location)
|
.location(order.location)
|
||||||
@@ -95,19 +95,19 @@ class ClientCreateOrderRepositoryImpl
|
|||||||
.cost(shiftCost)
|
.cost(shiftCost)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
final shiftId = shiftResult.data?.shift_insert.id;
|
final String? shiftId = shiftResult.data?.shift_insert.id;
|
||||||
if (shiftId == null) {
|
if (shiftId == null) {
|
||||||
throw Exception('Shift creation failed.');
|
throw Exception('Shift creation failed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final position in order.positions) {
|
for (final domain.OneTimeOrderPosition position in order.positions) {
|
||||||
final start = _parseTime(order.date, position.startTime);
|
final DateTime start = _parseTime(order.date, position.startTime);
|
||||||
final end = _parseTime(order.date, position.endTime);
|
final DateTime end = _parseTime(order.date, position.endTime);
|
||||||
final normalizedEnd =
|
final DateTime normalizedEnd =
|
||||||
end.isBefore(start) ? end.add(const Duration(days: 1)) : end;
|
end.isBefore(start) ? end.add(const Duration(days: 1)) : end;
|
||||||
final hours = normalizedEnd.difference(start).inMinutes / 60.0;
|
final double hours = normalizedEnd.difference(start).inMinutes / 60.0;
|
||||||
final rate = order.roleRates[position.role] ?? 0;
|
final double rate = order.roleRates[position.role] ?? 0;
|
||||||
final totalValue = rate * hours * position.count;
|
final double totalValue = rate * hours * position.count;
|
||||||
|
|
||||||
await _dataConnect
|
await _dataConnect
|
||||||
.createShiftRole(
|
.createShiftRole(
|
||||||
@@ -136,13 +136,13 @@ class ClientCreateOrderRepositoryImpl
|
|||||||
|
|
||||||
double _calculateShiftCost(domain.OneTimeOrder order) {
|
double _calculateShiftCost(domain.OneTimeOrder order) {
|
||||||
double total = 0;
|
double total = 0;
|
||||||
for (final position in order.positions) {
|
for (final domain.OneTimeOrderPosition position in order.positions) {
|
||||||
final start = _parseTime(order.date, position.startTime);
|
final DateTime start = _parseTime(order.date, position.startTime);
|
||||||
final end = _parseTime(order.date, position.endTime);
|
final DateTime end = _parseTime(order.date, position.endTime);
|
||||||
final normalizedEnd =
|
final DateTime normalizedEnd =
|
||||||
end.isBefore(start) ? end.add(const Duration(days: 1)) : end;
|
end.isBefore(start) ? end.add(const Duration(days: 1)) : end;
|
||||||
final hours = normalizedEnd.difference(start).inMinutes / 60.0;
|
final double hours = normalizedEnd.difference(start).inMinutes / 60.0;
|
||||||
final rate = order.roleRates[position.role] ?? 0;
|
final double rate = order.roleRates[position.role] ?? 0;
|
||||||
total += rate * hours * position.count;
|
total += rate * hours * position.count;
|
||||||
}
|
}
|
||||||
return total;
|
return total;
|
||||||
@@ -170,16 +170,16 @@ class ClientCreateOrderRepositoryImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
||||||
final utc = dateTime.toUtc();
|
final DateTime utc = dateTime.toUtc();
|
||||||
final seconds = utc.millisecondsSinceEpoch ~/ 1000;
|
final int seconds = utc.millisecondsSinceEpoch ~/ 1000;
|
||||||
final nanoseconds = (utc.microsecondsSinceEpoch % 1000000) * 1000;
|
final int nanoseconds = (utc.microsecondsSinceEpoch % 1000000) * 1000;
|
||||||
return fdc.Timestamp(nanoseconds, seconds);
|
return fdc.Timestamp(nanoseconds, seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _formatDate(DateTime dateTime) {
|
String _formatDate(DateTime dateTime) {
|
||||||
final year = dateTime.year.toString().padLeft(4, '0');
|
final String year = dateTime.year.toString().padLeft(4, '0');
|
||||||
final month = dateTime.month.toString().padLeft(2, '0');
|
final String month = dateTime.month.toString().padLeft(2, '0');
|
||||||
final day = dateTime.day.toString().padLeft(2, '0');
|
final String day = dateTime.day.toString().padLeft(2, '0');
|
||||||
return '$year-$month-$day';
|
return '$year-$month-$day';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:firebase_data_connect/src/core/ref.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
@@ -26,10 +27,10 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState> {
|
|||||||
|
|
||||||
Future<void> _loadVendors() async {
|
Future<void> _loadVendors() async {
|
||||||
try {
|
try {
|
||||||
final result = await _dataConnect.listVendors().execute();
|
final QueryResult<dc.ListVendorsData, void> result = await _dataConnect.listVendors().execute();
|
||||||
final vendors = result.data.vendors
|
final List<Vendor> vendors = result.data.vendors
|
||||||
.map(
|
.map(
|
||||||
(vendor) => Vendor(
|
(dc.ListVendorsVendors vendor) => Vendor(
|
||||||
id: vendor.id,
|
id: vendor.id,
|
||||||
name: vendor.companyName,
|
name: vendor.companyName,
|
||||||
rates: const <String, double>{},
|
rates: const <String, double>{},
|
||||||
@@ -44,12 +45,12 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState> {
|
|||||||
|
|
||||||
Future<void> _loadRolesForVendor(String vendorId) async {
|
Future<void> _loadRolesForVendor(String vendorId) async {
|
||||||
try {
|
try {
|
||||||
final result = await _dataConnect.listRolesByVendorId(
|
final QueryResult<dc.ListRolesByVendorIdData, dc.ListRolesByVendorIdVariables> result = await _dataConnect.listRolesByVendorId(
|
||||||
vendorId: vendorId,
|
vendorId: vendorId,
|
||||||
).execute();
|
).execute();
|
||||||
final roles = result.data.roles
|
final List<OneTimeOrderRoleOption> roles = result.data.roles
|
||||||
.map(
|
.map(
|
||||||
(role) => OneTimeOrderRoleOption(
|
(dc.ListRolesByVendorIdRoles role) => OneTimeOrderRoleOption(
|
||||||
id: role.id,
|
id: role.id,
|
||||||
name: role.name,
|
name: role.name,
|
||||||
costPerHour: role.costPerHour,
|
costPerHour: role.costPerHour,
|
||||||
@@ -146,7 +147,7 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState> {
|
|||||||
emit(state.copyWith(status: OneTimeOrderStatus.loading));
|
emit(state.copyWith(status: OneTimeOrderStatus.loading));
|
||||||
try {
|
try {
|
||||||
final Map<String, double> roleRates = <String, double>{
|
final Map<String, double> roleRates = <String, double>{
|
||||||
for (final role in state.roles) role.id: role.costPerHour,
|
for (final OneTimeOrderRoleOption role in state.roles) role.id: role.costPerHour,
|
||||||
};
|
};
|
||||||
final OneTimeOrder order = OneTimeOrder(
|
final OneTimeOrder order = OneTimeOrder(
|
||||||
date: state.date,
|
date: state.date,
|
||||||
|
|||||||
@@ -301,9 +301,9 @@ class OneTimeOrderPositionCard extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<DropdownMenuItem<String>> _buildRoleItems() {
|
List<DropdownMenuItem<String>> _buildRoleItems() {
|
||||||
final items = roles
|
final List<DropdownMenuItem<String>> items = roles
|
||||||
.map(
|
.map(
|
||||||
(role) => DropdownMenuItem<String>(
|
(OneTimeOrderRoleOption role) => DropdownMenuItem<String>(
|
||||||
value: role.id,
|
value: role.id,
|
||||||
child: Text(
|
child: Text(
|
||||||
'${role.name} - \$${role.costPerHour.toStringAsFixed(0)}',
|
'${role.name} - \$${role.costPerHour.toStringAsFixed(0)}',
|
||||||
@@ -313,7 +313,7 @@ class OneTimeOrderPositionCard extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
final hasSelected = roles.any((role) => role.id == position.role);
|
final bool hasSelected = roles.any((OneTimeOrderRoleOption role) => role.id == position.role);
|
||||||
if (position.role.isNotEmpty && !hasSelected) {
|
if (position.role.isNotEmpty && !hasSelected) {
|
||||||
items.add(
|
items.add(
|
||||||
DropdownMenuItem<String>(
|
DropdownMenuItem<String>(
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export 'src/presentation/navigation/client_home_navigator.dart';
|
|||||||
/// including repositories, use cases, and BLoCs.
|
/// including repositories, use cases, and BLoCs.
|
||||||
class ClientHomeModule extends Module {
|
class ClientHomeModule extends Module {
|
||||||
@override
|
@override
|
||||||
List<Module> get imports => [DataConnectModule()];
|
List<Module> get imports => <Module>[DataConnectModule()];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void binds(Injector i) {
|
void binds(Injector i) {
|
||||||
@@ -41,7 +41,7 @@ class ClientHomeModule extends Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void routes(r) {
|
void routes(RouteManager r) {
|
||||||
r.child('/', child: (_) => const ClientHomePage());
|
r.child('/', child: (_) => const ClientHomePage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
UserSessionData getUserSessionData() {
|
UserSessionData getUserSessionData() {
|
||||||
final (businessName, photoUrl) = _mock.getUserSession();
|
final (String businessName, String? photoUrl) = _mock.getUserSession();
|
||||||
return UserSessionData(
|
return UserSessionData(
|
||||||
businessName: businessName,
|
businessName: businessName,
|
||||||
photoUrl: photoUrl,
|
photoUrl: photoUrl,
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import 'package:client_home/src/domain/repositories/home_repository_interface.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:krow_domain/src/entities/home/home_dashboard_data.dart';
|
||||||
import '../../domain/usecases/get_dashboard_data_usecase.dart';
|
import '../../domain/usecases/get_dashboard_data_usecase.dart';
|
||||||
import '../../domain/usecases/get_user_session_data_usecase.dart';
|
import '../../domain/usecases/get_user_session_data_usecase.dart';
|
||||||
import 'client_home_event.dart';
|
import 'client_home_event.dart';
|
||||||
@@ -29,10 +31,10 @@ class ClientHomeBloc extends Bloc<ClientHomeEvent, ClientHomeState> {
|
|||||||
emit(state.copyWith(status: ClientHomeStatus.loading));
|
emit(state.copyWith(status: ClientHomeStatus.loading));
|
||||||
try {
|
try {
|
||||||
// Get session data
|
// Get session data
|
||||||
final sessionData = _getUserSessionDataUseCase();
|
final UserSessionData sessionData = _getUserSessionDataUseCase();
|
||||||
|
|
||||||
// Get dashboard data
|
// Get dashboard data
|
||||||
final data = await _getDashboardDataUseCase();
|
final HomeDashboardData data = await _getDashboardDataUseCase();
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
@@ -63,7 +65,7 @@ class ClientHomeBloc extends Bloc<ClientHomeEvent, ClientHomeState> {
|
|||||||
ClientHomeWidgetVisibilityToggled event,
|
ClientHomeWidgetVisibilityToggled event,
|
||||||
Emitter<ClientHomeState> emit,
|
Emitter<ClientHomeState> emit,
|
||||||
) {
|
) {
|
||||||
final newVisibility = Map<String, bool>.from(state.widgetVisibility);
|
final Map<String, bool> newVisibility = Map<String, bool>.from(state.widgetVisibility);
|
||||||
newVisibility[event.widgetId] = !(newVisibility[event.widgetId] ?? true);
|
newVisibility[event.widgetId] = !(newVisibility[event.widgetId] ?? true);
|
||||||
emit(state.copyWith(widgetVisibility: newVisibility));
|
emit(state.copyWith(widgetVisibility: newVisibility));
|
||||||
}
|
}
|
||||||
@@ -72,14 +74,14 @@ class ClientHomeBloc extends Bloc<ClientHomeEvent, ClientHomeState> {
|
|||||||
ClientHomeWidgetReordered event,
|
ClientHomeWidgetReordered event,
|
||||||
Emitter<ClientHomeState> emit,
|
Emitter<ClientHomeState> emit,
|
||||||
) {
|
) {
|
||||||
final newList = List<String>.from(state.widgetOrder);
|
final List<String> newList = List<String>.from(state.widgetOrder);
|
||||||
int oldIndex = event.oldIndex;
|
int oldIndex = event.oldIndex;
|
||||||
int newIndex = event.newIndex;
|
int newIndex = event.newIndex;
|
||||||
|
|
||||||
if (oldIndex < newIndex) {
|
if (oldIndex < newIndex) {
|
||||||
newIndex -= 1;
|
newIndex -= 1;
|
||||||
}
|
}
|
||||||
final item = newList.removeAt(oldIndex);
|
final String item = newList.removeAt(oldIndex);
|
||||||
newList.insert(newIndex, item);
|
newList.insert(newIndex, item);
|
||||||
|
|
||||||
emit(state.copyWith(widgetOrder: newList));
|
emit(state.copyWith(widgetOrder: newList));
|
||||||
@@ -91,14 +93,14 @@ class ClientHomeBloc extends Bloc<ClientHomeEvent, ClientHomeState> {
|
|||||||
) {
|
) {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
widgetOrder: const [
|
widgetOrder: const <String>[
|
||||||
'actions',
|
'actions',
|
||||||
'reorder',
|
'reorder',
|
||||||
'coverage',
|
'coverage',
|
||||||
'spending',
|
'spending',
|
||||||
'liveActivity',
|
'liveActivity',
|
||||||
],
|
],
|
||||||
widgetVisibility: const {
|
widgetVisibility: const <String, bool>{
|
||||||
'actions': true,
|
'actions': true,
|
||||||
'reorder': true,
|
'reorder': true,
|
||||||
'coverage': true,
|
'coverage': true,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ abstract class ClientHomeEvent extends Equatable {
|
|||||||
const ClientHomeEvent();
|
const ClientHomeEvent();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
List<Object?> get props => <Object?>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClientHomeStarted extends ClientHomeEvent {}
|
class ClientHomeStarted extends ClientHomeEvent {}
|
||||||
@@ -16,7 +16,7 @@ class ClientHomeWidgetVisibilityToggled extends ClientHomeEvent {
|
|||||||
const ClientHomeWidgetVisibilityToggled(this.widgetId);
|
const ClientHomeWidgetVisibilityToggled(this.widgetId);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [widgetId];
|
List<Object?> get props => <Object?>[widgetId];
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClientHomeWidgetReordered extends ClientHomeEvent {
|
class ClientHomeWidgetReordered extends ClientHomeEvent {
|
||||||
@@ -25,7 +25,7 @@ class ClientHomeWidgetReordered extends ClientHomeEvent {
|
|||||||
const ClientHomeWidgetReordered(this.oldIndex, this.newIndex);
|
const ClientHomeWidgetReordered(this.oldIndex, this.newIndex);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [oldIndex, newIndex];
|
List<Object?> get props => <Object?>[oldIndex, newIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClientHomeLayoutReset extends ClientHomeEvent {}
|
class ClientHomeLayoutReset extends ClientHomeEvent {}
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ class ClientHomeState extends Equatable {
|
|||||||
|
|
||||||
const ClientHomeState({
|
const ClientHomeState({
|
||||||
this.status = ClientHomeStatus.initial,
|
this.status = ClientHomeStatus.initial,
|
||||||
this.widgetOrder = const [
|
this.widgetOrder = const <String>[
|
||||||
'actions',
|
'actions',
|
||||||
],
|
],
|
||||||
this.widgetVisibility = const {
|
this.widgetVisibility = const <String, bool>{
|
||||||
'actions': true,
|
'actions': true,
|
||||||
},
|
},
|
||||||
this.isEditMode = false,
|
this.isEditMode = false,
|
||||||
@@ -60,7 +60,7 @@ class ClientHomeState extends Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => <Object?>[
|
||||||
status,
|
status,
|
||||||
widgetOrder,
|
widgetOrder,
|
||||||
widgetVisibility,
|
widgetVisibility,
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ class ClientHomeSheets {
|
|||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
builder: (context) {
|
builder: (BuildContext context) {
|
||||||
return ShiftOrderFormSheet(
|
return ShiftOrderFormSheet(
|
||||||
initialData: initialData,
|
initialData: initialData,
|
||||||
onSubmit: (data) {
|
onSubmit: (Map<String, dynamic> data) {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
onSubmit(data);
|
onSubmit(data);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,20 +21,20 @@ class ClientHomePage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.client_home;
|
final TranslationsClientHomeEn i18n = t.client_home;
|
||||||
|
|
||||||
return BlocProvider<ClientHomeBloc>(
|
return BlocProvider<ClientHomeBloc>(
|
||||||
create: (context) =>
|
create: (BuildContext context) =>
|
||||||
Modular.get<ClientHomeBloc>()..add(ClientHomeStarted()),
|
Modular.get<ClientHomeBloc>()..add(ClientHomeStarted()),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
ClientHomeHeader(i18n: i18n),
|
ClientHomeHeader(i18n: i18n),
|
||||||
ClientHomeEditBanner(i18n: i18n),
|
ClientHomeEditBanner(i18n: i18n),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: BlocBuilder<ClientHomeBloc, ClientHomeState>(
|
child: BlocBuilder<ClientHomeBloc, ClientHomeState>(
|
||||||
builder: (context, state) {
|
builder: (BuildContext context, ClientHomeState state) {
|
||||||
if (state.isEditMode) {
|
if (state.isEditMode) {
|
||||||
return _buildEditModeList(context, state);
|
return _buildEditModeList(context, state);
|
||||||
}
|
}
|
||||||
@@ -58,12 +58,12 @@ class ClientHomePage extends StatelessWidget {
|
|||||||
UiConstants.space4,
|
UiConstants.space4,
|
||||||
100,
|
100,
|
||||||
),
|
),
|
||||||
onReorder: (oldIndex, newIndex) {
|
onReorder: (int oldIndex, int newIndex) {
|
||||||
BlocProvider.of<ClientHomeBloc>(context).add(
|
BlocProvider.of<ClientHomeBloc>(context).add(
|
||||||
ClientHomeWidgetReordered(oldIndex, newIndex),
|
ClientHomeWidgetReordered(oldIndex, newIndex),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
children: state.widgetOrder.map((id) {
|
children: state.widgetOrder.map((String id) {
|
||||||
return Container(
|
return Container(
|
||||||
key: ValueKey(id),
|
key: ValueKey(id),
|
||||||
margin: const EdgeInsets.only(bottom: UiConstants.space4),
|
margin: const EdgeInsets.only(bottom: UiConstants.space4),
|
||||||
@@ -86,7 +86,7 @@ class ClientHomePage extends StatelessWidget {
|
|||||||
UiConstants.space4,
|
UiConstants.space4,
|
||||||
100,
|
100,
|
||||||
),
|
),
|
||||||
children: state.widgetOrder.map((id) {
|
children: state.widgetOrder.map((String id) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: UiConstants.space4),
|
padding: const EdgeInsets.only(bottom: UiConstants.space4),
|
||||||
child: DashboardWidgetBuilder(
|
child: DashboardWidgetBuilder(
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ class ActionsWidget extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Check if client_home exists in t
|
// Check if client_home exists in t
|
||||||
final i18n = t.client_home.actions;
|
final TranslationsClientHomeActionsEn i18n = t.client_home.actions;
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _ActionCard(
|
child: _ActionCard(
|
||||||
title: i18n.rapid,
|
title: i18n.rapid,
|
||||||
@@ -96,7 +96,7 @@ class _ActionCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: 36,
|
width: 36,
|
||||||
height: 36,
|
height: 36,
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ class ClientHomeEditBanner extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<ClientHomeBloc, ClientHomeState>(
|
return BlocBuilder<ClientHomeBloc, ClientHomeState>(
|
||||||
buildWhen: (prev, curr) => prev.isEditMode != curr.isEditMode,
|
buildWhen: (ClientHomeState prev, ClientHomeState curr) => prev.isEditMode != curr.isEditMode,
|
||||||
builder: (context, state) {
|
builder: (BuildContext context, ClientHomeState state) {
|
||||||
return AnimatedContainer(
|
return AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
height: state.isEditMode ? 76 : 0,
|
height: state.isEditMode ? 76 : 0,
|
||||||
@@ -40,13 +40,13 @@ class ClientHomeEditBanner extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: <Widget>[
|
||||||
const Icon(UiIcons.edit, size: 16, color: UiColors.primary),
|
const Icon(UiIcons.edit, size: 16, color: UiColors.primary),
|
||||||
const SizedBox(width: UiConstants.space2),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
i18n.dashboard.edit_mode_active,
|
i18n.dashboard.edit_mode_active,
|
||||||
style: UiTypography.footnote1b.copyWith(
|
style: UiTypography.footnote1b.copyWith(
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ class ClientHomeHeader extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<ClientHomeBloc, ClientHomeState>(
|
return BlocBuilder<ClientHomeBloc, ClientHomeState>(
|
||||||
builder: (context, state) {
|
builder: (BuildContext context, ClientHomeState state) {
|
||||||
final businessName = state.businessName;
|
final String businessName = state.businessName;
|
||||||
final photoUrl = state.photoUrl;
|
final String? photoUrl = state.photoUrl;
|
||||||
final avatarLetter = businessName.trim().isNotEmpty
|
final String avatarLetter = businessName.trim().isNotEmpty
|
||||||
? businessName.trim()[0].toUpperCase()
|
? businessName.trim()[0].toUpperCase()
|
||||||
: 'C';
|
: 'C';
|
||||||
|
|
||||||
@@ -41,9 +41,9 @@ class ClientHomeHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
@@ -73,7 +73,7 @@ class ClientHomeHeader extends StatelessWidget {
|
|||||||
const SizedBox(width: UiConstants.space3),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
i18n.dashboard.welcome_back,
|
i18n.dashboard.welcome_back,
|
||||||
style: UiTypography.footnote2r.textSecondary,
|
style: UiTypography.footnote2r.textSecondary,
|
||||||
|
|||||||
@@ -23,32 +23,32 @@ class CoverageDashboard extends StatelessWidget {
|
|||||||
double todayCost = 0;
|
double todayCost = 0;
|
||||||
|
|
||||||
for (final s in shifts) {
|
for (final s in shifts) {
|
||||||
final needed = s['workersNeeded'] as int? ?? 0;
|
final int needed = s['workersNeeded'] as int? ?? 0;
|
||||||
final confirmed = s['filled'] as int? ?? 0;
|
final int confirmed = s['filled'] as int? ?? 0;
|
||||||
final rate = s['hourlyRate'] as double? ?? 20.0;
|
final double rate = s['hourlyRate'] as double? ?? 20.0;
|
||||||
|
|
||||||
totalNeeded += needed;
|
totalNeeded += needed;
|
||||||
totalConfirmed += confirmed;
|
totalConfirmed += confirmed;
|
||||||
todayCost += rate * 8 * confirmed;
|
todayCost += rate * 8 * confirmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
final coveragePercent = totalNeeded > 0
|
final int coveragePercent = totalNeeded > 0
|
||||||
? ((totalConfirmed / totalNeeded) * 100).round()
|
? ((totalConfirmed / totalNeeded) * 100).round()
|
||||||
: 100;
|
: 100;
|
||||||
final unfilledPositions = totalNeeded - totalConfirmed;
|
final int unfilledPositions = totalNeeded - totalConfirmed;
|
||||||
|
|
||||||
final checkedInCount = applications
|
final int checkedInCount = applications
|
||||||
.where((a) => (a as Map)['checkInTime'] != null)
|
.where((a) => (a as Map)['checkInTime'] != null)
|
||||||
.length;
|
.length;
|
||||||
final lateWorkersCount = applications
|
final int lateWorkersCount = applications
|
||||||
.where((a) => (a as Map)['status'] == 'LATE')
|
.where((a) => (a as Map)['status'] == 'LATE')
|
||||||
.length;
|
.length;
|
||||||
|
|
||||||
final isCoverageGood = coveragePercent >= 90;
|
final bool isCoverageGood = coveragePercent >= 90;
|
||||||
final coverageBadgeColor = isCoverageGood
|
final Color coverageBadgeColor = isCoverageGood
|
||||||
? const Color(0xFFD1FAE5) // TODO: Use design system color if available
|
? const Color(0xFFD1FAE5) // TODO: Use design system color if available
|
||||||
: const Color(0xFFFEF3C7);
|
: const Color(0xFFFEF3C7);
|
||||||
final coverageTextColor = isCoverageGood
|
final Color coverageTextColor = isCoverageGood
|
||||||
? const Color(0xFF047857)
|
? const Color(0xFF047857)
|
||||||
: const Color(0xFFB45309);
|
: const Color(0xFFB45309);
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ class CoverageDashboard extends StatelessWidget {
|
|||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: [
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withValues(alpha: 0.02),
|
color: UiColors.black.withValues(alpha: 0.02),
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
@@ -67,10 +67,10 @@ class CoverageDashboard extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text("Today's Status", style: UiTypography.body1m.textSecondary),
|
Text("Today's Status", style: UiTypography.body1m.textSecondary),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
@@ -94,17 +94,17 @@ class CoverageDashboard extends StatelessWidget {
|
|||||||
|
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
_StatusCard(
|
_StatusCard(
|
||||||
label: 'Unfilled Today',
|
label: 'Unfilled Today',
|
||||||
value: '$unfilledPositions',
|
value: '$unfilledPositions',
|
||||||
icon: UiIcons.warning,
|
icon: UiIcons.warning,
|
||||||
isWarning: unfilledPositions > 0,
|
isWarning: unfilledPositions > 0,
|
||||||
),
|
),
|
||||||
if (lateWorkersCount > 0) ...[
|
if (lateWorkersCount > 0) ...<Widget>[
|
||||||
const SizedBox(height: UiConstants.space2),
|
const SizedBox(height: UiConstants.space2),
|
||||||
_StatusCard(
|
_StatusCard(
|
||||||
label: 'Running Late',
|
label: 'Running Late',
|
||||||
@@ -119,7 +119,7 @@ class CoverageDashboard extends StatelessWidget {
|
|||||||
const SizedBox(width: UiConstants.space2),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
_StatusCard(
|
_StatusCard(
|
||||||
label: 'Checked In',
|
label: 'Checked In',
|
||||||
value: '$checkedInCount/$totalConfirmed',
|
value: '$checkedInCount/$totalConfirmed',
|
||||||
@@ -194,9 +194,9 @@ class _StatusCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Icon(icon, size: 16, color: iconColor),
|
Icon(icon, size: 16, color: iconColor),
|
||||||
const SizedBox(width: UiConstants.space2),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ class CoverageWidget extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
"TODAY'S COVERAGE",
|
"TODAY'S COVERAGE",
|
||||||
style: UiTypography.footnote1b.copyWith(
|
style: UiTypography.footnote1b.copyWith(
|
||||||
@@ -55,7 +55,7 @@ class CoverageWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space2),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _MetricCard(
|
child: _MetricCard(
|
||||||
icon: UiIcons.target,
|
icon: UiIcons.target,
|
||||||
@@ -114,7 +114,7 @@ class _MetricCard extends StatelessWidget {
|
|||||||
color: UiColors.cardViewBackground,
|
color: UiColors.cardViewBackground,
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: [
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withValues(alpha: 0.02),
|
color: UiColors.black.withValues(alpha: 0.02),
|
||||||
blurRadius: 2,
|
blurRadius: 2,
|
||||||
@@ -123,9 +123,9 @@ class _MetricCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Icon(icon, size: 14, color: iconColor),
|
Icon(icon, size: 14, color: iconColor),
|
||||||
const SizedBox(width: 6), // 6px
|
const SizedBox(width: 6), // 6px
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ class DashboardWidgetBuilder extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.client_home.widgets;
|
final TranslationsClientHomeWidgetsEn i18n = t.client_home.widgets;
|
||||||
final widgetContent = _buildWidgetContent(context);
|
final Widget widgetContent = _buildWidgetContent(context);
|
||||||
|
|
||||||
if (isEditMode) {
|
if (isEditMode) {
|
||||||
return DraggableWidgetWrapper(
|
return DraggableWidgetWrapper(
|
||||||
@@ -64,11 +64,11 @@ class DashboardWidgetBuilder extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
case 'reorder':
|
case 'reorder':
|
||||||
return ReorderWidget(
|
return ReorderWidget(
|
||||||
onReorderPressed: (data) {
|
onReorderPressed: (Map<String, dynamic> data) {
|
||||||
ClientHomeSheets.showOrderFormSheet(
|
ClientHomeSheets.showOrderFormSheet(
|
||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
onSubmit: (submittedData) {
|
onSubmit: (Map<String, dynamic> submittedData) {
|
||||||
// Handle form submission if needed
|
// Handle form submission if needed
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ class DraggableWidgetWrapper extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
spacing: UiConstants.space2,
|
spacing: UiConstants.space2,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: UiConstants.space2,
|
horizontal: UiConstants.space2,
|
||||||
@@ -48,7 +48,7 @@ class DraggableWidgetWrapper extends StatelessWidget {
|
|||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
const Icon(
|
const Icon(
|
||||||
UiIcons.gripVertical,
|
UiIcons.gripVertical,
|
||||||
size: 14,
|
size: 14,
|
||||||
|
|||||||
@@ -33,14 +33,14 @@ class HeaderIconButton extends StatelessWidget {
|
|||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: 32,
|
width: 32,
|
||||||
height: 32,
|
height: 32,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isActive ? UiColors.primary : UiColors.white,
|
color: isActive ? UiColors.primary : UiColors.white,
|
||||||
borderRadius: UiConstants.radiusMd,
|
borderRadius: UiConstants.radiusMd,
|
||||||
boxShadow: [
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withValues(alpha: 0.05),
|
color: Colors.black.withValues(alpha: 0.05),
|
||||||
blurRadius: 2,
|
blurRadius: 2,
|
||||||
|
|||||||
@@ -13,18 +13,18 @@ class LiveActivityWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.client_home;
|
final TranslationsClientHomeEn i18n = t.client_home;
|
||||||
|
|
||||||
// Mock data
|
// Mock data
|
||||||
final shifts = [
|
final List<Map<String, Object>> shifts = <Map<String, Object>>[
|
||||||
{
|
<String, Object>{
|
||||||
'workersNeeded': 5,
|
'workersNeeded': 5,
|
||||||
'filled': 4,
|
'filled': 4,
|
||||||
'hourlyRate': 20.0,
|
'hourlyRate': 20.0,
|
||||||
'status': 'OPEN',
|
'status': 'OPEN',
|
||||||
'date': DateTime.now().toIso8601String().split('T')[0],
|
'date': DateTime.now().toIso8601String().split('T')[0],
|
||||||
},
|
},
|
||||||
{
|
<String, Object>{
|
||||||
'workersNeeded': 5,
|
'workersNeeded': 5,
|
||||||
'filled': 5,
|
'filled': 5,
|
||||||
'hourlyRate': 22.0,
|
'hourlyRate': 22.0,
|
||||||
@@ -32,18 +32,18 @@ class LiveActivityWidget extends StatelessWidget {
|
|||||||
'date': DateTime.now().toIso8601String().split('T')[0],
|
'date': DateTime.now().toIso8601String().split('T')[0],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
final applications = [
|
final List<Map<String, String>> applications = <Map<String, String>>[
|
||||||
{'status': 'CONFIRMED', 'checkInTime': '09:00'},
|
<String, String>{'status': 'CONFIRMED', 'checkInTime': '09:00'},
|
||||||
{'status': 'CONFIRMED', 'checkInTime': '09:05'},
|
<String, String>{'status': 'CONFIRMED', 'checkInTime': '09:05'},
|
||||||
{'status': 'CONFIRMED'},
|
<String, String>{'status': 'CONFIRMED'},
|
||||||
{'status': 'LATE'},
|
<String, String>{'status': 'LATE'},
|
||||||
];
|
];
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
i18n.widgets.live_activity.toUpperCase(),
|
i18n.widgets.live_activity.toUpperCase(),
|
||||||
style: UiTypography.footnote1b.textSecondary.copyWith(
|
style: UiTypography.footnote1b.textSecondary.copyWith(
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ class ReorderWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.client_home.reorder;
|
final TranslationsClientHomeReorderEn i18n = t.client_home.reorder;
|
||||||
|
|
||||||
// Mock recent orders
|
// Mock recent orders
|
||||||
final recentOrders = [
|
final List<Map<String, Object>> recentOrders = <Map<String, Object>>[
|
||||||
{
|
<String, Object>{
|
||||||
'title': 'Server',
|
'title': 'Server',
|
||||||
'location': 'Downtown Restaurant',
|
'location': 'Downtown Restaurant',
|
||||||
'hourlyRate': 18.0,
|
'hourlyRate': 18.0,
|
||||||
@@ -24,7 +24,7 @@ class ReorderWidget extends StatelessWidget {
|
|||||||
'workers': 3,
|
'workers': 3,
|
||||||
'type': 'One Day',
|
'type': 'One Day',
|
||||||
},
|
},
|
||||||
{
|
<String, Object>{
|
||||||
'title': 'Bartender',
|
'title': 'Bartender',
|
||||||
'location': 'Rooftop Bar',
|
'location': 'Rooftop Bar',
|
||||||
'hourlyRate': 22.0,
|
'hourlyRate': 22.0,
|
||||||
@@ -32,7 +32,7 @@ class ReorderWidget extends StatelessWidget {
|
|||||||
'workers': 2,
|
'workers': 2,
|
||||||
'type': 'One Day',
|
'type': 'One Day',
|
||||||
},
|
},
|
||||||
{
|
<String, Object>{
|
||||||
'title': 'Event Staff',
|
'title': 'Event Staff',
|
||||||
'location': 'Convention Center',
|
'location': 'Convention Center',
|
||||||
'hourlyRate': 20.0,
|
'hourlyRate': 20.0,
|
||||||
@@ -44,7 +44,7 @@ class ReorderWidget extends StatelessWidget {
|
|||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
i18n.title,
|
i18n.title,
|
||||||
style: UiTypography.footnote1b.textSecondary.copyWith(
|
style: UiTypography.footnote1b.textSecondary.copyWith(
|
||||||
@@ -57,11 +57,11 @@ class ReorderWidget extends StatelessWidget {
|
|||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: recentOrders.length,
|
itemCount: recentOrders.length,
|
||||||
separatorBuilder: (context, index) =>
|
separatorBuilder: (BuildContext context, int index) =>
|
||||||
const SizedBox(width: UiConstants.space3),
|
const SizedBox(width: UiConstants.space3),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
final order = recentOrders[index];
|
final Map<String, Object> order = recentOrders[index];
|
||||||
final totalCost =
|
final double totalCost =
|
||||||
(order['hourlyRate'] as double) *
|
(order['hourlyRate'] as double) *
|
||||||
(order['hours'] as int) *
|
(order['hours'] as int) *
|
||||||
(order['workers'] as int);
|
(order['workers'] as int);
|
||||||
@@ -73,7 +73,7 @@ class ReorderWidget extends StatelessWidget {
|
|||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: [
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withValues(alpha: 0.02),
|
color: UiColors.black.withValues(alpha: 0.02),
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
@@ -82,13 +82,13 @@ class ReorderWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: 36,
|
width: 36,
|
||||||
height: 36,
|
height: 36,
|
||||||
@@ -108,7 +108,7 @@ class ReorderWidget extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
order['title'] as String,
|
order['title'] as String,
|
||||||
style: UiTypography.body2b,
|
style: UiTypography.body2b,
|
||||||
@@ -128,7 +128,7 @@ class ReorderWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'\$${totalCost.toStringAsFixed(0)}',
|
'\$${totalCost.toStringAsFixed(0)}',
|
||||||
style: UiTypography.body1b,
|
style: UiTypography.body1b,
|
||||||
@@ -146,7 +146,7 @@ class ReorderWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space3),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
_Badge(
|
_Badge(
|
||||||
icon: UiIcons.success,
|
icon: UiIcons.success,
|
||||||
text: order['type'] as String,
|
text: order['type'] as String,
|
||||||
@@ -222,7 +222,7 @@ class _Badge extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(color: bg, borderRadius: UiConstants.radiusSm),
|
decoration: BoxDecoration(color: bg, borderRadius: UiConstants.radiusSm),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Icon(icon, size: 10, color: bg == textColor ? UiColors.white : color),
|
Icon(icon, size: 10, color: bg == textColor ? UiColors.white : color),
|
||||||
const SizedBox(width: UiConstants.space1),
|
const SizedBox(width: UiConstants.space1),
|
||||||
Text(text, style: UiTypography.footnote2b.copyWith(color: textColor)),
|
Text(text, style: UiTypography.footnote2b.copyWith(color: textColor)),
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
|||||||
|
|
||||||
late List<Map<String, dynamic>> _positions;
|
late List<Map<String, dynamic>> _positions;
|
||||||
|
|
||||||
final List<String> _roles = [
|
final List<String> _roles = <String>[
|
||||||
'Server',
|
'Server',
|
||||||
'Bartender',
|
'Bartender',
|
||||||
'Cook',
|
'Cook',
|
||||||
@@ -48,11 +48,11 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Vendor options
|
// Vendor options
|
||||||
final List<Map<String, dynamic>> _vendors = [
|
final List<Map<String, dynamic>> _vendors = <Map<String, dynamic>>[
|
||||||
{
|
<String, dynamic>{
|
||||||
'id': 'v1',
|
'id': 'v1',
|
||||||
'name': 'Elite Staffing',
|
'name': 'Elite Staffing',
|
||||||
'rates': {
|
'rates': <String, double>{
|
||||||
'Server': 25.0,
|
'Server': 25.0,
|
||||||
'Bartender': 30.0,
|
'Bartender': 30.0,
|
||||||
'Cook': 28.0,
|
'Cook': 28.0,
|
||||||
@@ -63,10 +63,10 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
|||||||
'Event Staff': 19.0,
|
'Event Staff': 19.0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
<String, dynamic>{
|
||||||
'id': 'v2',
|
'id': 'v2',
|
||||||
'name': 'Premier Workforce',
|
'name': 'Premier Workforce',
|
||||||
'rates': {
|
'rates': <String, double>{
|
||||||
'Server': 22.0,
|
'Server': 22.0,
|
||||||
'Bartender': 28.0,
|
'Bartender': 28.0,
|
||||||
'Cook': 25.0,
|
'Cook': 25.0,
|
||||||
@@ -81,7 +81,7 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
|||||||
|
|
||||||
String? _selectedVendorId;
|
String? _selectedVendorId;
|
||||||
|
|
||||||
final List<int> _lunchBreakOptions = [0, 30, 45, 60];
|
final List<int> _lunchBreakOptions = <int>[0, 30, 45, 60];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -427,7 +427,7 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
|||||||
style: UiTypography.body2r.textPrimary,
|
style: UiTypography.body2r.textPrimary,
|
||||||
items: _vendors
|
items: _vendors
|
||||||
.map(
|
.map(
|
||||||
(vendor) => DropdownMenuItem<String>(
|
(Map<String, dynamic> vendor) => DropdownMenuItem<String>(
|
||||||
value: vendor['id'],
|
value: vendor['id'],
|
||||||
child: Text(vendor['name']),
|
child: Text(vendor['name']),
|
||||||
),
|
),
|
||||||
@@ -565,7 +565,7 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
|||||||
items: _roles,
|
items: _roles,
|
||||||
itemBuilder: (dynamic role) {
|
itemBuilder: (dynamic role) {
|
||||||
final Map<String, dynamic>? vendor = _vendors.firstWhere(
|
final Map<String, dynamic>? vendor = _vendors.firstWhere(
|
||||||
(v) => v['id'] == _selectedVendorId,
|
(Map<String, dynamic> v) => v['id'] == _selectedVendorId,
|
||||||
orElse: () => _vendors.first,
|
orElse: () => _vendors.first,
|
||||||
);
|
);
|
||||||
final Map<String, dynamic>? rates = vendor?['rates'] as Map<String, dynamic>?;
|
final Map<String, dynamic>? rates = vendor?['rates'] as Map<String, dynamic>?;
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ class SpendingWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.client_home;
|
final TranslationsClientHomeEn i18n = t.client_home;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
i18n.widgets.spending.toUpperCase(),
|
i18n.widgets.spending.toUpperCase(),
|
||||||
style: UiTypography.footnote1b.textSecondary.copyWith(
|
style: UiTypography.footnote1b.textSecondary.copyWith(
|
||||||
@@ -43,12 +43,12 @@ class SpendingWidget extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(UiConstants.space3),
|
padding: const EdgeInsets.all(UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: const LinearGradient(
|
gradient: const LinearGradient(
|
||||||
colors: [UiColors.primary, Color(0xFF0830B8)],
|
colors: <Color>[UiColors.primary, Color(0xFF0830B8)],
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
),
|
),
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
boxShadow: [
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.primary.withValues(alpha: 0.3),
|
color: UiColors.primary.withValues(alpha: 0.3),
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
@@ -57,13 +57,13 @@ class SpendingWidget extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
const Text(
|
const Text(
|
||||||
'This Week',
|
'This Week',
|
||||||
style: TextStyle(color: Colors.white70, fontSize: 9),
|
style: TextStyle(color: Colors.white70, fontSize: 9),
|
||||||
@@ -89,7 +89,7 @@ class SpendingWidget extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: <Widget>[
|
||||||
const Text(
|
const Text(
|
||||||
'Next 7 Days',
|
'Next 7 Days',
|
||||||
style: TextStyle(color: Colors.white70, fontSize: 9),
|
style: TextStyle(color: Colors.white70, fontSize: 9),
|
||||||
@@ -122,7 +122,7 @@ class SpendingWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
@@ -142,7 +142,7 @@ class SpendingWidget extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'💡 ' +
|
'💡 ' +
|
||||||
i18n.dashboard.insight_lightbulb(amount: '180'),
|
i18n.dashboard.insight_lightbulb(amount: '180'),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export 'src/presentation/pages/client_hubs_page.dart';
|
|||||||
/// A [Module] for the client hubs feature.
|
/// A [Module] for the client hubs feature.
|
||||||
class ClientHubsModule extends Module {
|
class ClientHubsModule extends Module {
|
||||||
@override
|
@override
|
||||||
List<Module> get imports => [DataConnectModule()];
|
List<Module> get imports => <Module>[DataConnectModule()];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void binds(Injector i) {
|
void binds(Injector i) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||||
|
import 'package:firebase_data_connect/src/core/ref.dart';
|
||||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||||
import 'package:krow_domain/krow_domain.dart' as domain;
|
import 'package:krow_domain/krow_domain.dart' as domain;
|
||||||
import '../../domain/repositories/hub_repository_interface.dart';
|
import '../../domain/repositories/hub_repository_interface.dart';
|
||||||
@@ -16,8 +17,8 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<domain.Hub>> getHubs() async {
|
Future<List<domain.Hub>> getHubs() async {
|
||||||
final business = await _getBusinessForCurrentUser();
|
final dc.GetBusinessesByUserIdBusinesses business = await _getBusinessForCurrentUser();
|
||||||
final teamId = await _getOrCreateTeamId(business);
|
final String teamId = await _getOrCreateTeamId(business);
|
||||||
return _fetchHubsForTeam(teamId: teamId, businessId: business.id);
|
return _fetchHubsForTeam(teamId: teamId, businessId: business.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,11 +27,11 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
|||||||
required String name,
|
required String name,
|
||||||
required String address,
|
required String address,
|
||||||
}) async {
|
}) async {
|
||||||
final business = await _getBusinessForCurrentUser();
|
final dc.GetBusinessesByUserIdBusinesses business = await _getBusinessForCurrentUser();
|
||||||
final teamId = await _getOrCreateTeamId(business);
|
final String teamId = await _getOrCreateTeamId(business);
|
||||||
final city = business.city;
|
final String? city = business.city;
|
||||||
|
|
||||||
final result = await _dataConnect
|
final OperationResult<dc.CreateTeamHubData, dc.CreateTeamHubVariables> result = await _dataConnect
|
||||||
.createTeamHub(
|
.createTeamHub(
|
||||||
teamId: teamId,
|
teamId: teamId,
|
||||||
hubName: name,
|
hubName: name,
|
||||||
@@ -38,17 +39,17 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
|||||||
)
|
)
|
||||||
.city(city?.isNotEmpty == true ? city : '')
|
.city(city?.isNotEmpty == true ? city : '')
|
||||||
.execute();
|
.execute();
|
||||||
final createdId = result.data?.teamHub_insert.id;
|
final String? createdId = result.data?.teamHub_insert.id;
|
||||||
if (createdId == null) {
|
if (createdId == null) {
|
||||||
throw Exception('Hub creation failed.');
|
throw Exception('Hub creation failed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final hubs = await _fetchHubsForTeam(
|
final List<domain.Hub> hubs = await _fetchHubsForTeam(
|
||||||
teamId: teamId,
|
teamId: teamId,
|
||||||
businessId: business.id,
|
businessId: business.id,
|
||||||
);
|
);
|
||||||
domain.Hub? createdHub;
|
domain.Hub? createdHub;
|
||||||
for (final hub in hubs) {
|
for (final domain.Hub hub in hubs) {
|
||||||
if (hub.id == createdId) {
|
if (hub.id == createdId) {
|
||||||
createdHub = hub;
|
createdHub = hub;
|
||||||
break;
|
break;
|
||||||
@@ -79,8 +80,8 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<dc.GetBusinessesByUserIdBusinesses> _getBusinessForCurrentUser() async {
|
Future<dc.GetBusinessesByUserIdBusinesses> _getBusinessForCurrentUser() async {
|
||||||
final session = dc.ClientSessionStore.instance.session;
|
final dc.ClientSession? session = dc.ClientSessionStore.instance.session;
|
||||||
final cachedBusiness = session?.business;
|
final dc.ClientBusinessSession? cachedBusiness = session?.business;
|
||||||
if (cachedBusiness != null) {
|
if (cachedBusiness != null) {
|
||||||
return dc.GetBusinessesByUserIdBusinesses(
|
return dc.GetBusinessesByUserIdBusinesses(
|
||||||
id: cachedBusiness.id,
|
id: cachedBusiness.id,
|
||||||
@@ -103,12 +104,12 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final user = _firebaseAuth.currentUser;
|
final firebase.User? user = _firebaseAuth.currentUser;
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw Exception('User is not authenticated.');
|
throw Exception('User is not authenticated.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final result = await _dataConnect.getBusinessesByUserId(
|
final QueryResult<dc.GetBusinessesByUserIdData, dc.GetBusinessesByUserIdVariables> result = await _dataConnect.getBusinessesByUserId(
|
||||||
userId: user.uid,
|
userId: user.uid,
|
||||||
).execute();
|
).execute();
|
||||||
if (result.data.businesses.isEmpty) {
|
if (result.data.businesses.isEmpty) {
|
||||||
@@ -116,7 +117,7 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
|||||||
throw Exception('No business found for this user. Please sign in again.');
|
throw Exception('No business found for this user. Please sign in again.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final business = result.data.businesses.first;
|
final dc.GetBusinessesByUserIdBusinesses business = result.data.businesses.first;
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
dc.ClientSessionStore.instance.setSession(
|
dc.ClientSessionStore.instance.setSession(
|
||||||
dc.ClientSession(
|
dc.ClientSession(
|
||||||
@@ -140,14 +141,14 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
|||||||
Future<String> _getOrCreateTeamId(
|
Future<String> _getOrCreateTeamId(
|
||||||
dc.GetBusinessesByUserIdBusinesses business,
|
dc.GetBusinessesByUserIdBusinesses business,
|
||||||
) async {
|
) async {
|
||||||
final teamsResult = await _dataConnect.getTeamsByOwnerId(
|
final QueryResult<dc.GetTeamsByOwnerIdData, dc.GetTeamsByOwnerIdVariables> teamsResult = await _dataConnect.getTeamsByOwnerId(
|
||||||
ownerId: business.id,
|
ownerId: business.id,
|
||||||
).execute();
|
).execute();
|
||||||
if (teamsResult.data.teams.isNotEmpty) {
|
if (teamsResult.data.teams.isNotEmpty) {
|
||||||
return teamsResult.data.teams.first.id;
|
return teamsResult.data.teams.first.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
final createTeamBuilder = _dataConnect.createTeam(
|
final dc.CreateTeamVariablesBuilder createTeamBuilder = _dataConnect.createTeam(
|
||||||
teamName: '${business.businessName} Team',
|
teamName: '${business.businessName} Team',
|
||||||
ownerId: business.id,
|
ownerId: business.id,
|
||||||
ownerName: business.contactName ?? '',
|
ownerName: business.contactName ?? '',
|
||||||
@@ -157,8 +158,8 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
|||||||
createTeamBuilder.email(business.email);
|
createTeamBuilder.email(business.email);
|
||||||
}
|
}
|
||||||
|
|
||||||
final createTeamResult = await createTeamBuilder.execute();
|
final OperationResult<dc.CreateTeamData, dc.CreateTeamVariables> createTeamResult = await createTeamBuilder.execute();
|
||||||
final teamId = createTeamResult.data?.team_insert.id;
|
final String? teamId = createTeamResult.data?.team_insert.id;
|
||||||
if (teamId == null) {
|
if (teamId == null) {
|
||||||
throw Exception('Team creation failed.');
|
throw Exception('Team creation failed.');
|
||||||
}
|
}
|
||||||
@@ -170,13 +171,13 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
|||||||
required String teamId,
|
required String teamId,
|
||||||
required String businessId,
|
required String businessId,
|
||||||
}) async {
|
}) async {
|
||||||
final hubsResult = await _dataConnect.getTeamHubsByTeamId(
|
final QueryResult<dc.GetTeamHubsByTeamIdData, dc.GetTeamHubsByTeamIdVariables> hubsResult = await _dataConnect.getTeamHubsByTeamId(
|
||||||
teamId: teamId,
|
teamId: teamId,
|
||||||
).execute();
|
).execute();
|
||||||
|
|
||||||
return hubsResult.data.teamHubs
|
return hubsResult.data.teamHubs
|
||||||
.map(
|
.map(
|
||||||
(hub) => domain.Hub(
|
(dc.GetTeamHubsByTeamIdTeamHubs hub) => domain.Hub(
|
||||||
id: hub.id,
|
id: hub.id,
|
||||||
businessId: businessId,
|
businessId: businessId,
|
||||||
name: hub.hubName,
|
name: hub.hubName,
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ class AssignNfcTagArguments extends UseCaseArgument {
|
|||||||
const AssignNfcTagArguments({required this.hubId, required this.nfcTagId});
|
const AssignNfcTagArguments({required this.hubId, required this.nfcTagId});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [hubId, nfcTagId];
|
List<Object?> get props => <Object?>[hubId, nfcTagId];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ class CreateHubArguments extends UseCaseArgument {
|
|||||||
const CreateHubArguments({required this.name, required this.address});
|
const CreateHubArguments({required this.name, required this.address});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [name, address];
|
List<Object?> get props => <Object?>[name, address];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,5 +13,5 @@ class DeleteHubArguments extends UseCaseArgument {
|
|||||||
const DeleteHubArguments({required this.hubId});
|
const DeleteHubArguments({required this.hubId});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [hubId];
|
List<Object?> get props => <Object?>[hubId];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
|
import 'package:krow_domain/src/entities/business/hub.dart';
|
||||||
import '../../domain/arguments/assign_nfc_tag_arguments.dart';
|
import '../../domain/arguments/assign_nfc_tag_arguments.dart';
|
||||||
import '../../domain/arguments/create_hub_arguments.dart';
|
import '../../domain/arguments/create_hub_arguments.dart';
|
||||||
import '../../domain/arguments/delete_hub_arguments.dart';
|
import '../../domain/arguments/delete_hub_arguments.dart';
|
||||||
@@ -64,7 +65,7 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
|||||||
) async {
|
) async {
|
||||||
emit(state.copyWith(status: ClientHubsStatus.loading));
|
emit(state.copyWith(status: ClientHubsStatus.loading));
|
||||||
try {
|
try {
|
||||||
final hubs = await _getHubsUseCase();
|
final List<Hub> hubs = await _getHubsUseCase();
|
||||||
emit(state.copyWith(status: ClientHubsStatus.success, hubs: hubs));
|
emit(state.copyWith(status: ClientHubsStatus.success, hubs: hubs));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(
|
emit(
|
||||||
@@ -85,7 +86,7 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
|||||||
await _createHubUseCase(
|
await _createHubUseCase(
|
||||||
CreateHubArguments(name: event.name, address: event.address),
|
CreateHubArguments(name: event.name, address: event.address),
|
||||||
);
|
);
|
||||||
final hubs = await _getHubsUseCase();
|
final List<Hub> hubs = await _getHubsUseCase();
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
status: ClientHubsStatus.actionSuccess,
|
status: ClientHubsStatus.actionSuccess,
|
||||||
@@ -111,7 +112,7 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
|||||||
emit(state.copyWith(status: ClientHubsStatus.actionInProgress));
|
emit(state.copyWith(status: ClientHubsStatus.actionInProgress));
|
||||||
try {
|
try {
|
||||||
await _deleteHubUseCase(DeleteHubArguments(hubId: event.hubId));
|
await _deleteHubUseCase(DeleteHubArguments(hubId: event.hubId));
|
||||||
final hubs = await _getHubsUseCase();
|
final List<Hub> hubs = await _getHubsUseCase();
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
status: ClientHubsStatus.actionSuccess,
|
status: ClientHubsStatus.actionSuccess,
|
||||||
@@ -138,7 +139,7 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
|||||||
await _assignNfcTagUseCase(
|
await _assignNfcTagUseCase(
|
||||||
AssignNfcTagArguments(hubId: event.hubId, nfcTagId: event.nfcTagId),
|
AssignNfcTagArguments(hubId: event.hubId, nfcTagId: event.nfcTagId),
|
||||||
);
|
);
|
||||||
final hubs = await _getHubsUseCase();
|
final List<Hub> hubs = await _getHubsUseCase();
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
status: ClientHubsStatus.actionSuccess,
|
status: ClientHubsStatus.actionSuccess,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ abstract class ClientHubsEvent extends Equatable {
|
|||||||
const ClientHubsEvent();
|
const ClientHubsEvent();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
List<Object?> get props => <Object?>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event triggered to fetch the list of hubs.
|
/// Event triggered to fetch the list of hubs.
|
||||||
@@ -22,7 +22,7 @@ class ClientHubsAddRequested extends ClientHubsEvent {
|
|||||||
const ClientHubsAddRequested({required this.name, required this.address});
|
const ClientHubsAddRequested({required this.name, required this.address});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [name, address];
|
List<Object?> get props => <Object?>[name, address];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event triggered to delete a hub.
|
/// Event triggered to delete a hub.
|
||||||
@@ -32,7 +32,7 @@ class ClientHubsDeleteRequested extends ClientHubsEvent {
|
|||||||
const ClientHubsDeleteRequested(this.hubId);
|
const ClientHubsDeleteRequested(this.hubId);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [hubId];
|
List<Object?> get props => <Object?>[hubId];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event triggered to assign an NFC tag to a hub.
|
/// Event triggered to assign an NFC tag to a hub.
|
||||||
@@ -46,7 +46,7 @@ class ClientHubsNfcTagAssignRequested extends ClientHubsEvent {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [hubId, nfcTagId];
|
List<Object?> get props => <Object?>[hubId, nfcTagId];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event triggered to clear any error or success messages.
|
/// Event triggered to clear any error or success messages.
|
||||||
@@ -61,7 +61,7 @@ class ClientHubsAddDialogToggled extends ClientHubsEvent {
|
|||||||
const ClientHubsAddDialogToggled({required this.visible});
|
const ClientHubsAddDialogToggled({required this.visible});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [visible];
|
List<Object?> get props => <Object?>[visible];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event triggered to toggle the visibility of the "Identify NFC" dialog.
|
/// Event triggered to toggle the visibility of the "Identify NFC" dialog.
|
||||||
@@ -71,5 +71,5 @@ class ClientHubsIdentifyDialogToggled extends ClientHubsEvent {
|
|||||||
const ClientHubsIdentifyDialogToggled({this.hub});
|
const ClientHubsIdentifyDialogToggled({this.hub});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [hub];
|
List<Object?> get props => <Object?>[hub];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class ClientHubsState extends Equatable {
|
|||||||
|
|
||||||
const ClientHubsState({
|
const ClientHubsState({
|
||||||
this.status = ClientHubsStatus.initial,
|
this.status = ClientHubsStatus.initial,
|
||||||
this.hubs = const [],
|
this.hubs = const <Hub>[],
|
||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
this.successMessage,
|
this.successMessage,
|
||||||
this.showAddHubDialog = false,
|
this.showAddHubDialog = false,
|
||||||
@@ -57,7 +57,7 @@ class ClientHubsState extends Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => <Object?>[
|
||||||
status,
|
status,
|
||||||
hubs,
|
hubs,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
|
|||||||
@@ -3,6 +3,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:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
|
import 'package:krow_domain/src/entities/business/hub.dart';
|
||||||
import '../blocs/client_hubs_bloc.dart';
|
import '../blocs/client_hubs_bloc.dart';
|
||||||
import '../blocs/client_hubs_event.dart';
|
import '../blocs/client_hubs_event.dart';
|
||||||
import '../blocs/client_hubs_state.dart';
|
import '../blocs/client_hubs_state.dart';
|
||||||
@@ -23,10 +24,10 @@ class ClientHubsPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider<ClientHubsBloc>(
|
return BlocProvider<ClientHubsBloc>(
|
||||||
create: (context) =>
|
create: (BuildContext context) =>
|
||||||
Modular.get<ClientHubsBloc>()..add(const ClientHubsFetched()),
|
Modular.get<ClientHubsBloc>()..add(const ClientHubsFetched()),
|
||||||
child: BlocConsumer<ClientHubsBloc, ClientHubsState>(
|
child: BlocConsumer<ClientHubsBloc, ClientHubsState>(
|
||||||
listener: (context, state) {
|
listener: (BuildContext context, ClientHubsState state) {
|
||||||
if (state.errorMessage != null) {
|
if (state.errorMessage != null) {
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(
|
||||||
context,
|
context,
|
||||||
@@ -44,7 +45,7 @@ class ClientHubsPage extends StatelessWidget {
|
|||||||
).add(const ClientHubsMessageCleared());
|
).add(const ClientHubsMessageCleared());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: (context, state) {
|
builder: (BuildContext context, ClientHubsState state) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: UiColors.bgMenu,
|
backgroundColor: UiColors.bgMenu,
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
@@ -67,7 +68,7 @@ class ClientHubsPage extends StatelessWidget {
|
|||||||
vertical: UiConstants.space5,
|
vertical: UiConstants.space5,
|
||||||
).copyWith(bottom: 100),
|
).copyWith(bottom: 100),
|
||||||
sliver: SliverList(
|
sliver: SliverList(
|
||||||
delegate: SliverChildListDelegate([
|
delegate: SliverChildListDelegate(<Widget>[
|
||||||
if (state.status == ClientHubsStatus.loading)
|
if (state.status == ClientHubsStatus.loading)
|
||||||
const Center(child: CircularProgressIndicator())
|
const Center(child: CircularProgressIndicator())
|
||||||
else if (state.hubs.isEmpty)
|
else if (state.hubs.isEmpty)
|
||||||
@@ -79,9 +80,9 @@ class ClientHubsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else ...[
|
else ...<Widget>[
|
||||||
...state.hubs.map(
|
...state.hubs.map(
|
||||||
(hub) => HubCard(
|
(Hub hub) => HubCard(
|
||||||
hub: hub,
|
hub: hub,
|
||||||
onNfcPressed: () =>
|
onNfcPressed: () =>
|
||||||
BlocProvider.of<ClientHubsBloc>(
|
BlocProvider.of<ClientHubsBloc>(
|
||||||
@@ -105,7 +106,7 @@ class ClientHubsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
if (state.showAddHubDialog)
|
if (state.showAddHubDialog)
|
||||||
AddHubDialog(
|
AddHubDialog(
|
||||||
onCreate: (name, address) {
|
onCreate: (String name, String address) {
|
||||||
BlocProvider.of<ClientHubsBloc>(context).add(
|
BlocProvider.of<ClientHubsBloc>(context).add(
|
||||||
ClientHubsAddRequested(name: name, address: address),
|
ClientHubsAddRequested(name: name, address: address),
|
||||||
);
|
);
|
||||||
@@ -117,7 +118,7 @@ class ClientHubsPage extends StatelessWidget {
|
|||||||
if (state.hubToIdentify != null)
|
if (state.hubToIdentify != null)
|
||||||
IdentifyNfcDialog(
|
IdentifyNfcDialog(
|
||||||
hub: state.hubToIdentify!,
|
hub: state.hubToIdentify!,
|
||||||
onAssign: (tagId) {
|
onAssign: (String tagId) {
|
||||||
BlocProvider.of<ClientHubsBloc>(context).add(
|
BlocProvider.of<ClientHubsBloc>(context).add(
|
||||||
ClientHubsNfcTagAssignRequested(
|
ClientHubsNfcTagAssignRequested(
|
||||||
hubId: state.hubToIdentify!.id,
|
hubId: state.hubToIdentify!.id,
|
||||||
@@ -159,7 +160,7 @@ class ClientHubsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => Modular.to.pop(),
|
onTap: () => Modular.to.pop(),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|||||||
@@ -51,14 +51,14 @@ class _AddHubDialogState extends State<AddHubDialog> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
boxShadow: const [
|
boxShadow: const <BoxShadow>[
|
||||||
BoxShadow(color: UiColors.popupShadow, blurRadius: 20),
|
BoxShadow(color: UiColors.popupShadow, blurRadius: 20),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.client_hubs.add_hub_dialog.title,
|
t.client_hubs.add_hub_dialog.title,
|
||||||
style: UiTypography.headline3m.textPrimary,
|
style: UiTypography.headline3m.textPrimary,
|
||||||
@@ -83,7 +83,7 @@ class _AddHubDialogState extends State<AddHubDialog> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space8),
|
const SizedBox(height: UiConstants.space8),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: UiButton.secondary(
|
child: UiButton.secondary(
|
||||||
onPressed: widget.onCancel,
|
onPressed: widget.onCancel,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class HubCard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
boxShadow: const [
|
boxShadow: const <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.popupShadow,
|
color: UiColors.popupShadow,
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
@@ -42,7 +42,7 @@ class HubCard extends StatelessWidget {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: 52,
|
width: 52,
|
||||||
height: 52,
|
height: 52,
|
||||||
@@ -60,13 +60,13 @@ class HubCard extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(hub.name, style: UiTypography.body1b.textPrimary),
|
Text(hub.name, style: UiTypography.body1b.textPrimary),
|
||||||
if (hub.address.isNotEmpty)
|
if (hub.address.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: UiConstants.space1),
|
padding: const EdgeInsets.only(top: UiConstants.space1),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
const Icon(
|
const Icon(
|
||||||
UiIcons.mapPin,
|
UiIcons.mapPin,
|
||||||
size: 12,
|
size: 12,
|
||||||
@@ -99,7 +99,7 @@ class HubCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: onDeletePressed,
|
onPressed: onDeletePressed,
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class HubEmptyState extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgBanner,
|
color: UiColors.bgBanner,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
boxShadow: const [
|
boxShadow: const <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.popupShadow,
|
color: UiColors.popupShadow,
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
@@ -26,7 +26,7 @@ class HubEmptyState extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ class HubInfoCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
const Icon(UiIcons.nfc, size: 20, color: UiColors.primary),
|
const Icon(UiIcons.nfc, size: 20, color: UiColors.primary),
|
||||||
const SizedBox(width: UiConstants.space3),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.client_hubs.about_hubs.title,
|
t.client_hubs.about_hubs.title,
|
||||||
style: UiTypography.body2b.textPrimary,
|
style: UiTypography.body2b.textPrimary,
|
||||||
|
|||||||
@@ -48,13 +48,13 @@ class _IdentifyNfcDialogState extends State<IdentifyNfcDialog> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
boxShadow: const [
|
boxShadow: const <BoxShadow>[
|
||||||
BoxShadow(color: UiColors.popupShadow, blurRadius: 20),
|
BoxShadow(color: UiColors.popupShadow, blurRadius: 20),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.client_hubs.nfc_dialog.title,
|
t.client_hubs.nfc_dialog.title,
|
||||||
style: UiTypography.headline3m.textPrimary,
|
style: UiTypography.headline3m.textPrimary,
|
||||||
@@ -87,7 +87,7 @@ class _IdentifyNfcDialogState extends State<IdentifyNfcDialog> {
|
|||||||
text: t.client_hubs.nfc_dialog.scan_button,
|
text: t.client_hubs.nfc_dialog.scan_button,
|
||||||
leadingIcon: UiIcons.nfc,
|
leadingIcon: UiIcons.nfc,
|
||||||
),
|
),
|
||||||
if (_nfcTagId != null) ...[
|
if (_nfcTagId != null) ...<Widget>[
|
||||||
const SizedBox(height: UiConstants.space6),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
@@ -98,10 +98,10 @@ class _IdentifyNfcDialogState extends State<IdentifyNfcDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: <Widget>[
|
||||||
const Icon(
|
const Icon(
|
||||||
UiIcons.success,
|
UiIcons.success,
|
||||||
size: 20,
|
size: 20,
|
||||||
@@ -141,7 +141,7 @@ class _IdentifyNfcDialogState extends State<IdentifyNfcDialog> {
|
|||||||
],
|
],
|
||||||
const SizedBox(height: UiConstants.space8),
|
const SizedBox(height: UiConstants.space8),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: UiButton.secondary(
|
child: UiButton.secondary(
|
||||||
onPressed: widget.onCancel,
|
onPressed: widget.onCancel,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class ClientSettingsModule extends Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void routes(r) {
|
void routes(RouteManager r) {
|
||||||
r.child('/', child: (_) => const ClientSettingsPage());
|
r.child('/', child: (_) => const ClientSettingsPage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ abstract class ClientSettingsEvent extends Equatable {
|
|||||||
const ClientSettingsEvent();
|
const ClientSettingsEvent();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
List<Object?> get props => <Object?>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClientSettingsSignOutRequested extends ClientSettingsEvent {
|
class ClientSettingsSignOutRequested extends ClientSettingsEvent {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ abstract class ClientSettingsState extends Equatable {
|
|||||||
const ClientSettingsState();
|
const ClientSettingsState();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
List<Object?> get props => <Object?>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClientSettingsInitial extends ClientSettingsState {
|
class ClientSettingsInitial extends ClientSettingsState {
|
||||||
@@ -25,5 +25,5 @@ class ClientSettingsError extends ClientSettingsState {
|
|||||||
const ClientSettingsError(this.message);
|
const ClientSettingsError(this.message);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [message];
|
List<Object?> get props => <Object?>[message];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ class SettingsProfileHeader extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
/// Builds the profile header UI.
|
/// Builds the profile header UI.
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final labels = t.client_settings.profile;
|
final TranslationsClientSettingsProfileEn labels = t.client_settings.profile;
|
||||||
final session = dc.ClientSessionStore.instance.session;
|
final dc.ClientSession? session = dc.ClientSessionStore.instance.session;
|
||||||
final businessName =
|
final String businessName =
|
||||||
session?.business?.businessName ?? 'Your Company';
|
session?.business?.businessName ?? 'Your Company';
|
||||||
final email = session?.user.email ?? 'client@example.com';
|
final String email = session?.user.email ?? 'client@example.com';
|
||||||
final photoUrl = session?.userPhotoUrl;
|
final String? photoUrl = session?.userPhotoUrl;
|
||||||
final avatarLetter = businessName.trim().isNotEmpty
|
final String avatarLetter = businessName.trim().isNotEmpty
|
||||||
? businessName.trim()[0].toUpperCase()
|
? businessName.trim()[0].toUpperCase()
|
||||||
: 'C';
|
: 'C';
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ class SettingsProfileHeader extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
spacing: UiConstants.space4,
|
spacing: UiConstants.space4,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
@@ -69,13 +69,13 @@ class SettingsProfileHeader extends StatelessWidget {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(businessName, style: UiTypography.body1b.textPrimary),
|
Text(businessName, style: UiTypography.body1b.textPrimary),
|
||||||
const SizedBox(height: UiConstants.space1),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
spacing: UiConstants.space1,
|
spacing: UiConstants.space1,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Icon(
|
Icon(
|
||||||
UiIcons.mail,
|
UiIcons.mail,
|
||||||
size: 14,
|
size: 14,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class SettingsQuickLinks extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
/// Builds the quick links UI.
|
/// Builds the quick links UI.
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final labels = t.client_settings.profile;
|
final TranslationsClientSettingsProfileEn labels = t.client_settings.profile;
|
||||||
|
|
||||||
return SliverPadding(
|
return SliverPadding(
|
||||||
padding: const EdgeInsets.all(UiConstants.space5),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
@@ -28,7 +28,7 @@ class SettingsQuickLinks extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
labels.quick_links,
|
labels.quick_links,
|
||||||
style: UiTypography.footnote1b.textPrimary,
|
style: UiTypography.footnote1b.textPrimary,
|
||||||
@@ -85,9 +85,9 @@ class _QuickLinkItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Icon(icon, size: 20, color: UiColors.iconSecondary),
|
Icon(icon, size: 20, color: UiColors.iconSecondary),
|
||||||
const SizedBox(width: UiConstants.space3),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Text(title, style: UiTypography.footnote1m.textPrimary),
|
Text(title, style: UiTypography.footnote1m.textPrimary),
|
||||||
|
|||||||
@@ -21,15 +21,15 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
|
|||||||
required DateTime start,
|
required DateTime start,
|
||||||
required DateTime end,
|
required DateTime end,
|
||||||
}) async {
|
}) async {
|
||||||
final businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||||
if (businessId == null || businessId.isEmpty) {
|
if (businessId == null || businessId.isEmpty) {
|
||||||
await _firebaseAuth.signOut();
|
await _firebaseAuth.signOut();
|
||||||
throw Exception('Business is missing. Please sign in again.');
|
throw Exception('Business is missing. Please sign in again.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final startTimestamp = _toTimestamp(_startOfDay(start));
|
final fdc.Timestamp startTimestamp = _toTimestamp(_startOfDay(start));
|
||||||
final endTimestamp = _toTimestamp(_endOfDay(end));
|
final fdc.Timestamp endTimestamp = _toTimestamp(_endOfDay(end));
|
||||||
final result = await _dataConnect
|
final fdc.QueryResult<dc.ListShiftRolesByBusinessAndDateRangeData, dc.ListShiftRolesByBusinessAndDateRangeVariables> result = await _dataConnect
|
||||||
.listShiftRolesByBusinessAndDateRange(
|
.listShiftRolesByBusinessAndDateRange(
|
||||||
businessId: businessId,
|
businessId: businessId,
|
||||||
start: startTimestamp,
|
start: startTimestamp,
|
||||||
@@ -37,28 +37,28 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
|
|||||||
)
|
)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
final businessName =
|
final String businessName =
|
||||||
dc.ClientSessionStore.instance.session?.business?.businessName ??
|
dc.ClientSessionStore.instance.session?.business?.businessName ??
|
||||||
'Your Company';
|
'Your Company';
|
||||||
|
|
||||||
return result.data.shiftRoles.map((shiftRole) {
|
return result.data.shiftRoles.map((dc.ListShiftRolesByBusinessAndDateRangeShiftRoles shiftRole) {
|
||||||
print(
|
print(
|
||||||
'ViewOrders shiftRole: shiftId=${shiftRole.shiftId} roleId=${shiftRole.roleId} '
|
'ViewOrders shiftRole: shiftId=${shiftRole.shiftId} roleId=${shiftRole.roleId} '
|
||||||
'startTime=${shiftRole.startTime?.toJson()} endTime=${shiftRole.endTime?.toJson()} '
|
'startTime=${shiftRole.startTime?.toJson()} endTime=${shiftRole.endTime?.toJson()} '
|
||||||
'hours=${shiftRole.hours} totalValue=${shiftRole.totalValue}',
|
'hours=${shiftRole.hours} totalValue=${shiftRole.totalValue}',
|
||||||
);
|
);
|
||||||
final shiftDate = shiftRole.shift.date?.toDateTime();
|
final DateTime? shiftDate = shiftRole.shift.date?.toDateTime();
|
||||||
final dateStr = shiftDate == null
|
final String dateStr = shiftDate == null
|
||||||
? ''
|
? ''
|
||||||
: DateFormat('yyyy-MM-dd').format(shiftDate);
|
: DateFormat('yyyy-MM-dd').format(shiftDate);
|
||||||
final startTime = _formatTime(shiftRole.startTime);
|
final String startTime = _formatTime(shiftRole.startTime);
|
||||||
final endTime = _formatTime(shiftRole.endTime);
|
final String endTime = _formatTime(shiftRole.endTime);
|
||||||
final filled = shiftRole.assigned ?? 0;
|
final int filled = shiftRole.assigned ?? 0;
|
||||||
final workersNeeded = shiftRole.count;
|
final int workersNeeded = shiftRole.count;
|
||||||
final hours = shiftRole.hours ?? 0;
|
final double hours = shiftRole.hours ?? 0;
|
||||||
final totalValue = shiftRole.totalValue ?? 0;
|
final double totalValue = shiftRole.totalValue ?? 0;
|
||||||
final hourlyRate = _hourlyRate(shiftRole.totalValue, shiftRole.hours);
|
final double hourlyRate = _hourlyRate(shiftRole.totalValue, shiftRole.hours);
|
||||||
final status = filled >= workersNeeded ? 'filled' : 'open';
|
final String status = filled >= workersNeeded ? 'filled' : 'open';
|
||||||
|
|
||||||
return domain.OrderItem(
|
return domain.OrderItem(
|
||||||
id: _shiftRoleKey(shiftRole.shiftId, shiftRole.roleId),
|
id: _shiftRoleKey(shiftRole.shiftId, shiftRole.roleId),
|
||||||
@@ -84,15 +84,15 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
|
|||||||
Future<Map<String, List<Map<String, dynamic>>>> getAcceptedApplicationsForDay(
|
Future<Map<String, List<Map<String, dynamic>>>> getAcceptedApplicationsForDay(
|
||||||
DateTime day,
|
DateTime day,
|
||||||
) async {
|
) async {
|
||||||
final businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||||
if (businessId == null || businessId.isEmpty) {
|
if (businessId == null || businessId.isEmpty) {
|
||||||
await _firebaseAuth.signOut();
|
await _firebaseAuth.signOut();
|
||||||
throw Exception('Business is missing. Please sign in again.');
|
throw Exception('Business is missing. Please sign in again.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final dayStart = _toTimestamp(_startOfDay(day));
|
final fdc.Timestamp dayStart = _toTimestamp(_startOfDay(day));
|
||||||
final dayEnd = _toTimestamp(_endOfDay(day));
|
final fdc.Timestamp dayEnd = _toTimestamp(_endOfDay(day));
|
||||||
final result = await _dataConnect
|
final fdc.QueryResult<dc.ListAcceptedApplicationsByBusinessForDayData, dc.ListAcceptedApplicationsByBusinessForDayVariables> result = await _dataConnect
|
||||||
.listAcceptedApplicationsByBusinessForDay(
|
.listAcceptedApplicationsByBusinessForDay(
|
||||||
businessId: businessId,
|
businessId: businessId,
|
||||||
dayStart: dayStart,
|
dayStart: dayStart,
|
||||||
@@ -100,13 +100,13 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
|
|||||||
)
|
)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
final Map<String, List<Map<String, dynamic>>> grouped = {};
|
final Map<String, List<Map<String, dynamic>>> grouped = <String, List<Map<String, dynamic>>>{};
|
||||||
for (final application in result.data.applications) {
|
for (final dc.ListAcceptedApplicationsByBusinessForDayApplications application in result.data.applications) {
|
||||||
print(
|
print(
|
||||||
'ViewOrders app: shiftId=${application.shiftId} roleId=${application.roleId} '
|
'ViewOrders app: shiftId=${application.shiftId} roleId=${application.roleId} '
|
||||||
'checkIn=${application.checkInTime?.toJson()} checkOut=${application.checkOutTime?.toJson()}',
|
'checkIn=${application.checkInTime?.toJson()} checkOut=${application.checkOutTime?.toJson()}',
|
||||||
);
|
);
|
||||||
final key = _shiftRoleKey(application.shiftId, application.roleId);
|
final String key = _shiftRoleKey(application.shiftId, application.roleId);
|
||||||
grouped.putIfAbsent(key, () => <Map<String, dynamic>>[]);
|
grouped.putIfAbsent(key, () => <Map<String, dynamic>>[]);
|
||||||
grouped[key]!.add(<String, dynamic>{
|
grouped[key]!.add(<String, dynamic>{
|
||||||
'id': application.id,
|
'id': application.id,
|
||||||
@@ -124,9 +124,9 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
||||||
final utc = dateTime.toUtc();
|
final DateTime utc = dateTime.toUtc();
|
||||||
final seconds = utc.millisecondsSinceEpoch ~/ 1000;
|
final int seconds = utc.millisecondsSinceEpoch ~/ 1000;
|
||||||
final nanoseconds = (utc.microsecondsSinceEpoch % 1000000) * 1000;
|
final int nanoseconds = (utc.microsecondsSinceEpoch % 1000000) * 1000;
|
||||||
return fdc.Timestamp(nanoseconds, seconds);
|
return fdc.Timestamp(nanoseconds, seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
|
|||||||
if (timestamp == null) {
|
if (timestamp == null) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
final dateTime = timestamp.toDateTime();
|
final DateTime dateTime = timestamp.toDateTime();
|
||||||
return DateFormat('HH:mm').format(dateTime);
|
return DateFormat('HH:mm').format(dateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState> {
|
|||||||
final List<OrderItem> orders = await _getOrdersUseCase(
|
final List<OrderItem> orders = await _getOrdersUseCase(
|
||||||
OrdersRangeArguments(start: rangeStart, end: rangeEnd),
|
OrdersRangeArguments(start: rangeStart, end: rangeEnd),
|
||||||
);
|
);
|
||||||
final apps = await _getAcceptedAppsUseCase(
|
final Map<String, List<Map<String, dynamic>>> apps = await _getAcceptedAppsUseCase(
|
||||||
OrdersDayArguments(day: dayForApps),
|
OrdersDayArguments(day: dayForApps),
|
||||||
);
|
);
|
||||||
final List<OrderItem> updatedOrders = _applyApplications(orders, apps);
|
final List<OrderItem> updatedOrders = _applyApplications(orders, apps);
|
||||||
@@ -70,7 +70,7 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState> {
|
|||||||
final DateTime? selectedDate = state.selectedDate;
|
final DateTime? selectedDate = state.selectedDate;
|
||||||
final DateTime updatedSelectedDate =
|
final DateTime updatedSelectedDate =
|
||||||
selectedDate != null &&
|
selectedDate != null &&
|
||||||
calendarDays.any((day) => _isSameDay(day, selectedDate))
|
calendarDays.any((DateTime day) => _isSameDay(day, selectedDate))
|
||||||
? selectedDate
|
? selectedDate
|
||||||
: calendarDays.first;
|
: calendarDays.first;
|
||||||
emit(
|
emit(
|
||||||
@@ -106,7 +106,7 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState> {
|
|||||||
|
|
||||||
Future<void> _refreshAcceptedApplications(DateTime day) async {
|
Future<void> _refreshAcceptedApplications(DateTime day) async {
|
||||||
try {
|
try {
|
||||||
final apps = await _getAcceptedAppsUseCase(
|
final Map<String, List<Map<String, dynamic>>> apps = await _getAcceptedAppsUseCase(
|
||||||
OrdersDayArguments(day: day),
|
OrdersDayArguments(day: day),
|
||||||
);
|
);
|
||||||
final List<OrderItem> updatedOrders =
|
final List<OrderItem> updatedOrders =
|
||||||
@@ -122,14 +122,14 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState> {
|
|||||||
List<OrderItem> orders,
|
List<OrderItem> orders,
|
||||||
Map<String, List<Map<String, dynamic>>> apps,
|
Map<String, List<Map<String, dynamic>>> apps,
|
||||||
) {
|
) {
|
||||||
return orders.map((order) {
|
return orders.map((OrderItem order) {
|
||||||
final confirmed = apps[order.id] ?? const <Map<String, dynamic>>[];
|
final List<Map<String, dynamic>> confirmed = apps[order.id] ?? const <Map<String, dynamic>>[];
|
||||||
if (confirmed.isEmpty) {
|
if (confirmed.isEmpty) {
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
final filled = confirmed.length;
|
final int filled = confirmed.length;
|
||||||
final status =
|
final String status =
|
||||||
filled >= order.workersNeeded ? 'filled' : order.status;
|
filled >= order.workersNeeded ? 'filled' : order.status;
|
||||||
return OrderItem(
|
return OrderItem(
|
||||||
id: order.id,
|
id: order.id,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||||
|
import 'package:firebase_data_connect/src/core/ref.dart';
|
||||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||||
import 'package:krow_domain/krow_domain.dart' as domain;
|
import 'package:krow_domain/krow_domain.dart' as domain;
|
||||||
import '../../domain/repositories/auth_repository_interface.dart';
|
import '../../domain/repositories/auth_repository_interface.dart';
|
||||||
@@ -19,7 +20,7 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
|||||||
@override
|
@override
|
||||||
Stream<domain.User?> get currentUser => _firebaseAuth
|
Stream<domain.User?> get currentUser => _firebaseAuth
|
||||||
.authStateChanges()
|
.authStateChanges()
|
||||||
.map((firebaseUser) {
|
.map((firebase.User? firebaseUser) {
|
||||||
if (firebaseUser == null) {
|
if (firebaseUser == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -35,24 +36,24 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
|||||||
/// Signs in with a phone number and returns a verification ID.
|
/// Signs in with a phone number and returns a verification ID.
|
||||||
@override
|
@override
|
||||||
Future<String?> signInWithPhone({required String phoneNumber}) async {
|
Future<String?> signInWithPhone({required String phoneNumber}) async {
|
||||||
final completer = Completer<String?>();
|
final Completer<String?> completer = Completer<String?>();
|
||||||
|
|
||||||
await _firebaseAuth.verifyPhoneNumber(
|
await _firebaseAuth.verifyPhoneNumber(
|
||||||
phoneNumber: phoneNumber,
|
phoneNumber: phoneNumber,
|
||||||
verificationCompleted: (_) {},
|
verificationCompleted: (_) {},
|
||||||
verificationFailed: (e) {
|
verificationFailed: (firebase.FirebaseAuthException e) {
|
||||||
if (!completer.isCompleted) {
|
if (!completer.isCompleted) {
|
||||||
completer.completeError(
|
completer.completeError(
|
||||||
Exception(e.message ?? 'Phone verification failed.'),
|
Exception(e.message ?? 'Phone verification failed.'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
codeSent: (verificationId, _) {
|
codeSent: (String verificationId, _) {
|
||||||
if (!completer.isCompleted) {
|
if (!completer.isCompleted) {
|
||||||
completer.complete(verificationId);
|
completer.complete(verificationId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
codeAutoRetrievalTimeout: (verificationId) {
|
codeAutoRetrievalTimeout: (String verificationId) {
|
||||||
if (!completer.isCompleted) {
|
if (!completer.isCompleted) {
|
||||||
completer.complete(verificationId);
|
completer.complete(verificationId);
|
||||||
}
|
}
|
||||||
@@ -74,20 +75,20 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
|||||||
required String verificationId,
|
required String verificationId,
|
||||||
required String smsCode,
|
required String smsCode,
|
||||||
}) async {
|
}) async {
|
||||||
final credential = firebase.PhoneAuthProvider.credential(
|
final firebase.PhoneAuthCredential credential = firebase.PhoneAuthProvider.credential(
|
||||||
verificationId: verificationId,
|
verificationId: verificationId,
|
||||||
smsCode: smsCode,
|
smsCode: smsCode,
|
||||||
);
|
);
|
||||||
final userCredential = await _firebaseAuth.signInWithCredential(credential);
|
final firebase.UserCredential userCredential = await _firebaseAuth.signInWithCredential(credential);
|
||||||
final firebaseUser = userCredential.user;
|
final firebase.User? firebaseUser = userCredential.user;
|
||||||
if (firebaseUser == null) {
|
if (firebaseUser == null) {
|
||||||
throw Exception('Phone verification failed, no Firebase user received.');
|
throw Exception('Phone verification failed, no Firebase user received.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final response = await _dataConnect.getUserById(
|
final QueryResult<dc.GetUserByIdData, dc.GetUserByIdVariables> response = await _dataConnect.getUserById(
|
||||||
id: firebaseUser.uid,
|
id: firebaseUser.uid,
|
||||||
).execute();
|
).execute();
|
||||||
final user = response.data?.user;
|
final dc.GetUserByIdUser? user = response.data?.user;
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
await _firebaseAuth.signOut();
|
await _firebaseAuth.signOut();
|
||||||
throw Exception('Authenticated user profile not found in database.');
|
throw Exception('Authenticated user profile not found in database.');
|
||||||
@@ -97,7 +98,7 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
|||||||
throw Exception('User is not authorized for this app.');
|
throw Exception('User is not authorized for this app.');
|
||||||
}
|
}
|
||||||
|
|
||||||
final email = user.email ?? '';
|
final String email = user.email ?? '';
|
||||||
if (email.isEmpty) {
|
if (email.isEmpty) {
|
||||||
await _firebaseAuth.signOut();
|
await _firebaseAuth.signOut();
|
||||||
throw Exception('User email is missing in profile data.');
|
throw Exception('User email is missing in profile data.');
|
||||||
|
|||||||
@@ -13,5 +13,5 @@ class SignInWithPhoneArguments extends UseCaseArgument {
|
|||||||
const SignInWithPhoneArguments({required this.phoneNumber});
|
const SignInWithPhoneArguments({required this.phoneNumber});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [phoneNumber];
|
List<Object> get props => <Object>[phoneNumber];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,5 +20,5 @@ class VerifyOtpArguments extends UseCaseArgument {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [verificationId, smsCode];
|
List<Object> get props => <Object>[verificationId, smsCode];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import 'package:staff_authentication/src/domain/ui_entities/auth_mode.dart';
|
|||||||
abstract class AuthEvent extends Equatable {
|
abstract class AuthEvent extends Equatable {
|
||||||
const AuthEvent();
|
const AuthEvent();
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [];
|
List<Object> get props => <Object>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event for requesting a sign-in with a phone number.
|
/// Event for requesting a sign-in with a phone number.
|
||||||
@@ -19,7 +19,7 @@ class AuthSignInRequested extends AuthEvent {
|
|||||||
const AuthSignInRequested({this.phoneNumber, required this.mode});
|
const AuthSignInRequested({this.phoneNumber, required this.mode});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [mode];
|
List<Object> get props => <Object>[mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event for submitting an OTP (One-Time Password) for verification.
|
/// Event for submitting an OTP (One-Time Password) for verification.
|
||||||
@@ -36,7 +36,7 @@ class AuthOtpSubmitted extends AuthEvent {
|
|||||||
const AuthOtpSubmitted({required this.verificationId, required this.smsCode});
|
const AuthOtpSubmitted({required this.verificationId, required this.smsCode});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [verificationId, smsCode];
|
List<Object> get props => <Object>[verificationId, smsCode];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event for clearing any authentication error in the state.
|
/// Event for clearing any authentication error in the state.
|
||||||
@@ -50,7 +50,7 @@ class AuthOtpUpdated extends AuthEvent {
|
|||||||
const AuthOtpUpdated(this.otp);
|
const AuthOtpUpdated(this.otp);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [otp];
|
List<Object> get props => <Object>[otp];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event for updating the current draft phone number in the state.
|
/// Event for updating the current draft phone number in the state.
|
||||||
@@ -61,5 +61,5 @@ class AuthPhoneUpdated extends AuthEvent {
|
|||||||
const AuthPhoneUpdated(this.phoneNumber);
|
const AuthPhoneUpdated(this.phoneNumber);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [phoneNumber];
|
List<Object> get props => <Object>[phoneNumber];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class AuthState extends Equatable {
|
|||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => <Object?>[
|
||||||
status,
|
status,
|
||||||
verificationId,
|
verificationId,
|
||||||
mode,
|
mode,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ abstract class ProfileSetupEvent extends Equatable {
|
|||||||
const ProfileSetupEvent();
|
const ProfileSetupEvent();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
List<Object?> get props => <Object?>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event triggered when the full name changes.
|
/// Event triggered when the full name changes.
|
||||||
@@ -17,7 +17,7 @@ class ProfileSetupFullNameChanged extends ProfileSetupEvent {
|
|||||||
const ProfileSetupFullNameChanged(this.fullName);
|
const ProfileSetupFullNameChanged(this.fullName);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [fullName];
|
List<Object?> get props => <Object?>[fullName];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event triggered when the bio changes.
|
/// Event triggered when the bio changes.
|
||||||
@@ -29,7 +29,7 @@ class ProfileSetupBioChanged extends ProfileSetupEvent {
|
|||||||
const ProfileSetupBioChanged(this.bio);
|
const ProfileSetupBioChanged(this.bio);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [bio];
|
List<Object?> get props => <Object?>[bio];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event triggered when the preferred locations change.
|
/// Event triggered when the preferred locations change.
|
||||||
@@ -41,7 +41,7 @@ class ProfileSetupLocationsChanged extends ProfileSetupEvent {
|
|||||||
const ProfileSetupLocationsChanged(this.locations);
|
const ProfileSetupLocationsChanged(this.locations);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [locations];
|
List<Object?> get props => <Object?>[locations];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event triggered when the max distance changes.
|
/// Event triggered when the max distance changes.
|
||||||
@@ -53,7 +53,7 @@ class ProfileSetupDistanceChanged extends ProfileSetupEvent {
|
|||||||
const ProfileSetupDistanceChanged(this.distance);
|
const ProfileSetupDistanceChanged(this.distance);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [distance];
|
List<Object?> get props => <Object?>[distance];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event triggered when the skills change.
|
/// Event triggered when the skills change.
|
||||||
@@ -65,7 +65,7 @@ class ProfileSetupSkillsChanged extends ProfileSetupEvent {
|
|||||||
const ProfileSetupSkillsChanged(this.skills);
|
const ProfileSetupSkillsChanged(this.skills);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [skills];
|
List<Object?> get props => <Object?>[skills];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event triggered when the industries change.
|
/// Event triggered when the industries change.
|
||||||
@@ -77,7 +77,7 @@ class ProfileSetupIndustriesChanged extends ProfileSetupEvent {
|
|||||||
const ProfileSetupIndustriesChanged(this.industries);
|
const ProfileSetupIndustriesChanged(this.industries);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [industries];
|
List<Object?> get props => <Object?>[industries];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event triggered when the profile submission is requested.
|
/// Event triggered when the profile submission is requested.
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ class ProfileSetupState extends Equatable {
|
|||||||
const ProfileSetupState({
|
const ProfileSetupState({
|
||||||
this.fullName = '',
|
this.fullName = '',
|
||||||
this.bio = '',
|
this.bio = '',
|
||||||
this.preferredLocations = const [],
|
this.preferredLocations = const <String>[],
|
||||||
this.maxDistanceMiles = 25,
|
this.maxDistanceMiles = 25,
|
||||||
this.skills = const [],
|
this.skills = const <String>[],
|
||||||
this.industries = const [],
|
this.industries = const <String>[],
|
||||||
this.status = ProfileSetupStatus.initial,
|
this.status = ProfileSetupStatus.initial,
|
||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
});
|
});
|
||||||
@@ -65,7 +65,7 @@ class ProfileSetupState extends Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => <Object?>[
|
||||||
fullName,
|
fullName,
|
||||||
bio,
|
bio,
|
||||||
preferredLocations,
|
preferredLocations,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import '../../domain/ui_entities/auth_mode.dart';
|
|||||||
extension AuthNavigator on IModularNavigator {
|
extension AuthNavigator on IModularNavigator {
|
||||||
/// Navigates to the phone verification page.
|
/// Navigates to the phone verification page.
|
||||||
void pushPhoneVerification(AuthMode mode) {
|
void pushPhoneVerification(AuthMode mode) {
|
||||||
pushNamed('./phone-verification', arguments: {'mode': mode.name});
|
pushNamed('./phone-verification', arguments: <String, String>{'mode': mode.name});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Navigates to the profile setup page, replacing the current route.
|
/// Navigates to the profile setup page, replacing the current route.
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class GetStartedPage extends StatelessWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
// Background
|
// Background
|
||||||
const Expanded(child: GetStartedBackground()),
|
const Expanded(child: GetStartedBackground()),
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ class GetStartedPage extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: <Widget>[
|
||||||
// Main text and actions
|
// Main text and actions
|
||||||
const GetStartedHeader(),
|
const GetStartedHeader(),
|
||||||
|
|
||||||
|
|||||||
@@ -61,11 +61,11 @@ class PhoneVerificationPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider<AuthBloc>(
|
return BlocProvider<AuthBloc>(
|
||||||
create: (context) => Modular.get<AuthBloc>(),
|
create: (BuildContext context) => Modular.get<AuthBloc>(),
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) {
|
builder: (BuildContext context) {
|
||||||
return BlocListener<AuthBloc, AuthState>(
|
return BlocListener<AuthBloc, AuthState>(
|
||||||
listener: (context, state) {
|
listener: (BuildContext context, AuthState state) {
|
||||||
if (state.status == AuthStatus.authenticated) {
|
if (state.status == AuthStatus.authenticated) {
|
||||||
if (state.mode == AuthMode.signup) {
|
if (state.mode == AuthMode.signup) {
|
||||||
Modular.to.pushReplacementProfileSetup();
|
Modular.to.pushReplacementProfileSetup();
|
||||||
@@ -75,7 +75,7 @@ class PhoneVerificationPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: BlocBuilder<AuthBloc, AuthState>(
|
child: BlocBuilder<AuthBloc, AuthState>(
|
||||||
builder: (context, state) {
|
builder: (BuildContext context, AuthState state) {
|
||||||
// Check if we are in the OTP step
|
// Check if we are in the OTP step
|
||||||
final bool isOtpStep =
|
final bool isOtpStep =
|
||||||
state.status == AuthStatus.codeSent ||
|
state.status == AuthStatus.codeSent ||
|
||||||
@@ -93,7 +93,7 @@ class PhoneVerificationPage extends StatelessWidget {
|
|||||||
child: isOtpStep
|
child: isOtpStep
|
||||||
? OtpVerification(
|
? OtpVerification(
|
||||||
state: state,
|
state: state,
|
||||||
onOtpSubmitted: (otp) => _onOtpSubmitted(
|
onOtpSubmitted: (String otp) => _onOtpSubmitted(
|
||||||
context: context,
|
context: context,
|
||||||
otp: otp,
|
otp: otp,
|
||||||
verificationId: state.verificationId ?? '',
|
verificationId: state.verificationId ?? '',
|
||||||
|
|||||||
@@ -24,18 +24,18 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
|
|||||||
int _currentStep = 0;
|
int _currentStep = 0;
|
||||||
|
|
||||||
/// List of steps in the profile setup process.
|
/// List of steps in the profile setup process.
|
||||||
List<Map<String, dynamic>> get _steps => [
|
List<Map<String, dynamic>> get _steps => <Map<String, dynamic>>[
|
||||||
{
|
<String, dynamic>{
|
||||||
'id': 'basic',
|
'id': 'basic',
|
||||||
'title': t.staff_authentication.profile_setup_page.steps.basic,
|
'title': t.staff_authentication.profile_setup_page.steps.basic,
|
||||||
'icon': UiIcons.user,
|
'icon': UiIcons.user,
|
||||||
},
|
},
|
||||||
{
|
<String, dynamic>{
|
||||||
'id': 'location',
|
'id': 'location',
|
||||||
'title': t.staff_authentication.profile_setup_page.steps.location,
|
'title': t.staff_authentication.profile_setup_page.steps.location,
|
||||||
'icon': UiIcons.mapPin,
|
'icon': UiIcons.mapPin,
|
||||||
},
|
},
|
||||||
{
|
<String, dynamic>{
|
||||||
'id': 'experience',
|
'id': 'experience',
|
||||||
'title': t.staff_authentication.profile_setup_page.steps.experience,
|
'title': t.staff_authentication.profile_setup_page.steps.experience,
|
||||||
'icon': UiIcons.briefcase,
|
'icon': UiIcons.briefcase,
|
||||||
@@ -83,15 +83,15 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
|
|||||||
@override
|
@override
|
||||||
/// Builds the profile setup page UI.
|
/// Builds the profile setup page UI.
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final steps = _steps;
|
final List<Map<String, dynamic>> steps = _steps;
|
||||||
|
|
||||||
// Calculate progress
|
// Calculate progress
|
||||||
final double progress = (_currentStep + 1) / steps.length;
|
final double progress = (_currentStep + 1) / steps.length;
|
||||||
|
|
||||||
return BlocProvider<ProfileSetupBloc>(
|
return BlocProvider<ProfileSetupBloc>(
|
||||||
create: (context) => Modular.get<ProfileSetupBloc>(),
|
create: (BuildContext context) => Modular.get<ProfileSetupBloc>(),
|
||||||
child: BlocConsumer<ProfileSetupBloc, ProfileSetupState>(
|
child: BlocConsumer<ProfileSetupBloc, ProfileSetupState>(
|
||||||
listener: (context, state) {
|
listener: (BuildContext context, ProfileSetupState state) {
|
||||||
if (state.status == ProfileSetupStatus.success) {
|
if (state.status == ProfileSetupStatus.success) {
|
||||||
Modular.to.pushWorkerHome();
|
Modular.to.pushWorkerHome();
|
||||||
} else if (state.status == ProfileSetupStatus.failure) {
|
} else if (state.status == ProfileSetupStatus.failure) {
|
||||||
@@ -105,13 +105,13 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: (context, state) {
|
builder: (BuildContext context, ProfileSetupState state) {
|
||||||
final isCreatingProfile = state.status == ProfileSetupStatus.loading;
|
final bool isCreatingProfile = state.status == ProfileSetupStatus.loading;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
// Progress Bar
|
// Progress Bar
|
||||||
LinearProgressIndicator(value: progress),
|
LinearProgressIndicator(value: progress),
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
|
|||||||
// Step Indicators
|
// Step Indicators
|
||||||
UiStepIndicator(
|
UiStepIndicator(
|
||||||
stepIcons: steps
|
stepIcons: steps
|
||||||
.map((step) => step['icon'] as IconData)
|
.map((Map<String, dynamic> step) => step['icon'] as IconData)
|
||||||
.toList(),
|
.toList(),
|
||||||
currentStep: _currentStep,
|
currentStep: _currentStep,
|
||||||
),
|
),
|
||||||
@@ -200,10 +200,10 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
|
|||||||
return ProfileSetupBasicInfo(
|
return ProfileSetupBasicInfo(
|
||||||
fullName: state.fullName,
|
fullName: state.fullName,
|
||||||
bio: state.bio,
|
bio: state.bio,
|
||||||
onFullNameChanged: (val) => BlocProvider.of<ProfileSetupBloc>(
|
onFullNameChanged: (String val) => BlocProvider.of<ProfileSetupBloc>(
|
||||||
context,
|
context,
|
||||||
).add(ProfileSetupFullNameChanged(val)),
|
).add(ProfileSetupFullNameChanged(val)),
|
||||||
onBioChanged: (val) => BlocProvider.of<ProfileSetupBloc>(
|
onBioChanged: (String val) => BlocProvider.of<ProfileSetupBloc>(
|
||||||
context,
|
context,
|
||||||
).add(ProfileSetupBioChanged(val)),
|
).add(ProfileSetupBioChanged(val)),
|
||||||
);
|
);
|
||||||
@@ -211,10 +211,10 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
|
|||||||
return ProfileSetupLocation(
|
return ProfileSetupLocation(
|
||||||
preferredLocations: state.preferredLocations,
|
preferredLocations: state.preferredLocations,
|
||||||
maxDistanceMiles: state.maxDistanceMiles,
|
maxDistanceMiles: state.maxDistanceMiles,
|
||||||
onLocationsChanged: (val) => BlocProvider.of<ProfileSetupBloc>(
|
onLocationsChanged: (List<String> val) => BlocProvider.of<ProfileSetupBloc>(
|
||||||
context,
|
context,
|
||||||
).add(ProfileSetupLocationsChanged(val)),
|
).add(ProfileSetupLocationsChanged(val)),
|
||||||
onDistanceChanged: (val) => BlocProvider.of<ProfileSetupBloc>(
|
onDistanceChanged: (double val) => BlocProvider.of<ProfileSetupBloc>(
|
||||||
context,
|
context,
|
||||||
).add(ProfileSetupDistanceChanged(val)),
|
).add(ProfileSetupDistanceChanged(val)),
|
||||||
);
|
);
|
||||||
@@ -222,10 +222,10 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
|
|||||||
return ProfileSetupExperience(
|
return ProfileSetupExperience(
|
||||||
skills: state.skills,
|
skills: state.skills,
|
||||||
industries: state.industries,
|
industries: state.industries,
|
||||||
onSkillsChanged: (val) => BlocProvider.of<ProfileSetupBloc>(
|
onSkillsChanged: (List<String> val) => BlocProvider.of<ProfileSetupBloc>(
|
||||||
context,
|
context,
|
||||||
).add(ProfileSetupSkillsChanged(val)),
|
).add(ProfileSetupSkillsChanged(val)),
|
||||||
onIndustriesChanged: (val) => BlocProvider.of<ProfileSetupBloc>(
|
onIndustriesChanged: (List<String> val) => BlocProvider.of<ProfileSetupBloc>(
|
||||||
context,
|
context,
|
||||||
).add(ProfileSetupIndustriesChanged(val)),
|
).add(ProfileSetupIndustriesChanged(val)),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class AuthTroubleLink extends StatelessWidget {
|
|||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
spacing: UiConstants.space1,
|
spacing: UiConstants.space1,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.staff_authentication.common.trouble_question,
|
t.staff_authentication.common.trouble_question,
|
||||||
style: UiTypography.body2r.textSecondary,
|
style: UiTypography.body2r.textSecondary,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class SectionTitleSubtitle extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: UiConstants.space1,
|
spacing: UiConstants.space1,
|
||||||
children: [
|
children: <Widget>[
|
||||||
// Title
|
// Title
|
||||||
Text(title, style: UiTypography.headline1m),
|
Text(title, style: UiTypography.headline1m),
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class GetStartedActions extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: <Widget>[
|
||||||
// Sign Up Button
|
// Sign Up Button
|
||||||
UiButton.primary(
|
UiButton.primary(
|
||||||
text: t.staff_authentication.get_started_page.sign_up_button,
|
text: t.staff_authentication.get_started_page.sign_up_button,
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ class GetStartedBackground extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(top: 24.0),
|
padding: const EdgeInsets.only(top: 24.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
// Logo
|
// Logo
|
||||||
Image.asset(UiImageAssets.logoBlue, height: 40),
|
Image.asset(UiImageAssets.logoBlue, height: 40),
|
||||||
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: <Widget>[
|
||||||
// Hero Image
|
// Hero Image
|
||||||
Container(
|
Container(
|
||||||
width: 288,
|
width: 288,
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ class GetStartedHeader extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: <Widget>[
|
||||||
RichText(
|
RichText(
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
style: UiTypography.displayM,
|
style: UiTypography.displayM,
|
||||||
children: [
|
children: <InlineSpan>[
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: t.staff_authentication.get_started_page.title_part1,
|
text: t.staff_authentication.get_started_page.title_part1,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class OtpVerification extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
@@ -41,7 +41,7 @@ class OtpVerification extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
OtpVerificationHeader(phoneNumber: state.phoneNumber),
|
OtpVerificationHeader(phoneNumber: state.phoneNumber),
|
||||||
const SizedBox(height: UiConstants.space8),
|
const SizedBox(height: UiConstants.space8),
|
||||||
OtpInputField(
|
OtpInputField(
|
||||||
|
|||||||
@@ -36,17 +36,17 @@ class _OtpInputFieldState extends State<OtpInputField> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
for (final controller in _controllers) {
|
for (final TextEditingController controller in _controllers) {
|
||||||
controller.dispose();
|
controller.dispose();
|
||||||
}
|
}
|
||||||
for (final node in _focusNodes) {
|
for (final FocusNode node in _focusNodes) {
|
||||||
node.dispose();
|
node.dispose();
|
||||||
}
|
}
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper getter to compute the current OTP code from all controllers.
|
/// Helper getter to compute the current OTP code from all controllers.
|
||||||
String get _otpCode => _controllers.map((c) => c.text).join();
|
String get _otpCode => _controllers.map((TextEditingController c) => c.text).join();
|
||||||
|
|
||||||
/// Handles changes to the OTP input fields.
|
/// Handles changes to the OTP input fields.
|
||||||
void _onChanged({
|
void _onChanged({
|
||||||
@@ -69,10 +69,10 @@ class _OtpInputFieldState extends State<OtpInputField> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: List.generate(6, (index) {
|
children: List.generate(6, (int index) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 56,
|
width: 56,
|
||||||
height: 56,
|
height: 56,
|
||||||
@@ -80,7 +80,7 @@ class _OtpInputFieldState extends State<OtpInputField> {
|
|||||||
controller: _controllers[index],
|
controller: _controllers[index],
|
||||||
focusNode: _focusNodes[index],
|
focusNode: _focusNodes[index],
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
maxLength: 1,
|
maxLength: 1,
|
||||||
style: UiTypography.headline3m,
|
style: UiTypography.headline3m,
|
||||||
@@ -108,7 +108,7 @@ class _OtpInputFieldState extends State<OtpInputField> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onChanged: (value) =>
|
onChanged: (String value) =>
|
||||||
_onChanged(context: context, index: index, value: value),
|
_onChanged(context: context, index: index, value: value),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class _OtpResendSectionState extends State<OtpResendSection> {
|
|||||||
return Center(
|
return Center(
|
||||||
child: Text.rich(
|
child: Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: <InlineSpan>[
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: widget.hasError
|
text: widget.hasError
|
||||||
? ''
|
? ''
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class OtpVerificationActions extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: <Widget>[
|
||||||
isLoading
|
isLoading
|
||||||
? ElevatedButton(
|
? ElevatedButton(
|
||||||
onPressed: null,
|
onPressed: null,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class OtpVerificationHeader extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.staff_authentication.phone_verification_page.enter_code_title,
|
t.staff_authentication.phone_verification_page.enter_code_title,
|
||||||
style: UiTypography.headline1m,
|
style: UiTypography.headline1m,
|
||||||
@@ -27,7 +27,7 @@ class OtpVerificationHeader extends StatelessWidget {
|
|||||||
.phone_verification_page
|
.phone_verification_page
|
||||||
.code_sent_message,
|
.code_sent_message,
|
||||||
style: UiTypography.body2r.textSecondary,
|
style: UiTypography.body2r.textSecondary,
|
||||||
children: [
|
children: <InlineSpan>[
|
||||||
TextSpan(text: '+1 $phoneNumber', style: UiTypography.body2b),
|
TextSpan(text: '+1 $phoneNumber', style: UiTypography.body2b),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: t
|
text: t
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class PhoneInput extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
@@ -31,13 +31,13 @@ class PhoneInput extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
const PhoneInputHeader(),
|
const PhoneInputHeader(),
|
||||||
const SizedBox(height: UiConstants.space8),
|
const SizedBox(height: UiConstants.space8),
|
||||||
PhoneInputFormField(
|
PhoneInputFormField(
|
||||||
initialValue: state.phoneNumber,
|
initialValue: state.phoneNumber,
|
||||||
error: state.errorMessage ?? '',
|
error: state.errorMessage ?? '',
|
||||||
onChanged: (value) {
|
onChanged: (String value) {
|
||||||
BlocProvider.of<AuthBloc>(
|
BlocProvider.of<AuthBloc>(
|
||||||
context,
|
context,
|
||||||
).add(AuthPhoneUpdated(value));
|
).add(AuthPhoneUpdated(value));
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class PhoneInputActions extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: <Widget>[
|
||||||
isLoading
|
isLoading
|
||||||
? UiButton.secondary(
|
? UiButton.secondary(
|
||||||
onPressed: null,
|
onPressed: null,
|
||||||
|
|||||||
@@ -47,21 +47,21 @@ class _PhoneInputFormFieldState extends State<PhoneInputFormField> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.staff_authentication.phone_input.label,
|
t.staff_authentication.phone_input.label,
|
||||||
style: UiTypography.footnote1m.textSecondary,
|
style: UiTypography.footnote1m.textSecondary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space2),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 48,
|
height: 48,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text('🇺🇸', style: UiTypography.headline2m),
|
Text('🇺🇸', style: UiTypography.headline2m),
|
||||||
const SizedBox(width: UiConstants.space1),
|
const SizedBox(width: UiConstants.space1),
|
||||||
Text('+1', style: UiTypography.body1m),
|
Text('+1', style: UiTypography.body1m),
|
||||||
@@ -73,7 +73,7 @@ class _PhoneInputFormFieldState extends State<PhoneInputFormField> {
|
|||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
keyboardType: TextInputType.phone,
|
keyboardType: TextInputType.phone,
|
||||||
inputFormatters: [
|
inputFormatters: <TextInputFormatter>[
|
||||||
FilteringTextInputFormatter.digitsOnly,
|
FilteringTextInputFormatter.digitsOnly,
|
||||||
LengthLimitingTextInputFormatter(10),
|
LengthLimitingTextInputFormatter(10),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class PhoneInputHeader extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.staff_authentication.phone_input.title,
|
t.staff_authentication.phone_input.title,
|
||||||
style: UiTypography.headline1m,
|
style: UiTypography.headline1m,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class ProfileSetupBasicInfo extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
SectionTitleSubtitle(
|
SectionTitleSubtitle(
|
||||||
title: t.staff_authentication.profile_setup_page.basic_info.title,
|
title: t.staff_authentication.profile_setup_page.basic_info.title,
|
||||||
subtitle:
|
subtitle:
|
||||||
@@ -42,7 +42,7 @@ class ProfileSetupBasicInfo extends StatelessWidget {
|
|||||||
// Photo Upload
|
// Photo Upload
|
||||||
Center(
|
Center(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: 120,
|
width: 120,
|
||||||
height: 120,
|
height: 120,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class ProfileSetupExperience extends StatelessWidget {
|
|||||||
/// Callback for when industries change.
|
/// Callback for when industries change.
|
||||||
final ValueChanged<List<String>> onIndustriesChanged;
|
final ValueChanged<List<String>> onIndustriesChanged;
|
||||||
|
|
||||||
static const List<String> _allSkillKeys = [
|
static const List<String> _allSkillKeys = <String>[
|
||||||
'food_service',
|
'food_service',
|
||||||
'bartending',
|
'bartending',
|
||||||
'warehouse',
|
'warehouse',
|
||||||
@@ -30,7 +30,7 @@ class ProfileSetupExperience extends StatelessWidget {
|
|||||||
'cooking',
|
'cooking',
|
||||||
];
|
];
|
||||||
|
|
||||||
static const List<String> _allIndustryKeys = [
|
static const List<String> _allIndustryKeys = <String>[
|
||||||
'hospitality',
|
'hospitality',
|
||||||
'food_service',
|
'food_service',
|
||||||
'warehouse',
|
'warehouse',
|
||||||
@@ -50,7 +50,7 @@ class ProfileSetupExperience extends StatelessWidget {
|
|||||||
|
|
||||||
/// Toggles a skill.
|
/// Toggles a skill.
|
||||||
void _toggleSkill({required String skill}) {
|
void _toggleSkill({required String skill}) {
|
||||||
final updatedList = List<String>.from(skills);
|
final List<String> updatedList = List<String>.from(skills);
|
||||||
if (updatedList.contains(skill)) {
|
if (updatedList.contains(skill)) {
|
||||||
updatedList.remove(skill);
|
updatedList.remove(skill);
|
||||||
} else {
|
} else {
|
||||||
@@ -61,7 +61,7 @@ class ProfileSetupExperience extends StatelessWidget {
|
|||||||
|
|
||||||
/// Toggles an industry.
|
/// Toggles an industry.
|
||||||
void _toggleIndustry({required String industry}) {
|
void _toggleIndustry({required String industry}) {
|
||||||
final updatedList = List<String>.from(industries);
|
final List<String> updatedList = List<String>.from(industries);
|
||||||
if (updatedList.contains(industry)) {
|
if (updatedList.contains(industry)) {
|
||||||
updatedList.remove(industry);
|
updatedList.remove(industry);
|
||||||
} else {
|
} else {
|
||||||
@@ -75,7 +75,7 @@ class ProfileSetupExperience extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
SectionTitleSubtitle(
|
SectionTitleSubtitle(
|
||||||
title: t.staff_authentication.profile_setup_page.experience.title,
|
title: t.staff_authentication.profile_setup_page.experience.title,
|
||||||
subtitle:
|
subtitle:
|
||||||
@@ -92,10 +92,10 @@ class ProfileSetupExperience extends StatelessWidget {
|
|||||||
Wrap(
|
Wrap(
|
||||||
spacing: UiConstants.space2,
|
spacing: UiConstants.space2,
|
||||||
runSpacing: UiConstants.space2,
|
runSpacing: UiConstants.space2,
|
||||||
children: _allSkillKeys.map((key) {
|
children: _allSkillKeys.map((String key) {
|
||||||
final isSelected = skills.contains(key);
|
final bool isSelected = skills.contains(key);
|
||||||
// Dynamic translation access
|
// Dynamic translation access
|
||||||
final label = _getSkillLabel(key);
|
final String label = _getSkillLabel(key);
|
||||||
|
|
||||||
return UiChip(
|
return UiChip(
|
||||||
label: label,
|
label: label,
|
||||||
@@ -118,9 +118,9 @@ class ProfileSetupExperience extends StatelessWidget {
|
|||||||
Wrap(
|
Wrap(
|
||||||
spacing: UiConstants.space2,
|
spacing: UiConstants.space2,
|
||||||
runSpacing: UiConstants.space2,
|
runSpacing: UiConstants.space2,
|
||||||
children: _allIndustryKeys.map((key) {
|
children: _allIndustryKeys.map((String key) {
|
||||||
final isSelected = industries.contains(key);
|
final bool isSelected = industries.contains(key);
|
||||||
final label = _getIndustryLabel(key);
|
final String label = _getIndustryLabel(key);
|
||||||
|
|
||||||
return UiChip(
|
return UiChip(
|
||||||
label: label,
|
label: label,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class ProfileSetupHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: <Widget>[
|
||||||
if (currentStep > 0 && onBackTap != null)
|
if (currentStep > 0 && onBackTap != null)
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: onBackTap,
|
onTap: onBackTap,
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
|
|||||||
|
|
||||||
/// Adds the current text from the controller as a location.
|
/// Adds the current text from the controller as a location.
|
||||||
void _addLocation() {
|
void _addLocation() {
|
||||||
final loc = _locationController.text.trim();
|
final String loc = _locationController.text.trim();
|
||||||
if (loc.isNotEmpty && !widget.preferredLocations.contains(loc)) {
|
if (loc.isNotEmpty && !widget.preferredLocations.contains(loc)) {
|
||||||
final updatedList = List<String>.from(widget.preferredLocations)
|
final List<String> updatedList = List<String>.from(widget.preferredLocations)
|
||||||
..add(loc);
|
..add(loc);
|
||||||
widget.onLocationsChanged(updatedList);
|
widget.onLocationsChanged(updatedList);
|
||||||
_locationController.clear();
|
_locationController.clear();
|
||||||
@@ -55,7 +55,7 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: <Widget>[
|
||||||
SectionTitleSubtitle(
|
SectionTitleSubtitle(
|
||||||
title: t.staff_authentication.profile_setup_page.location.title,
|
title: t.staff_authentication.profile_setup_page.location.title,
|
||||||
subtitle: t.staff_authentication.profile_setup_page.location.subtitle,
|
subtitle: t.staff_authentication.profile_setup_page.location.subtitle,
|
||||||
@@ -66,7 +66,7 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
|
|||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
spacing: UiConstants.space2,
|
spacing: UiConstants.space2,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: UiTextField(
|
child: UiTextField(
|
||||||
label: t
|
label: t
|
||||||
@@ -102,7 +102,7 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
|
|||||||
Wrap(
|
Wrap(
|
||||||
spacing: UiConstants.space2,
|
spacing: UiConstants.space2,
|
||||||
runSpacing: UiConstants.space2,
|
runSpacing: UiConstants.space2,
|
||||||
children: widget.preferredLocations.map((loc) {
|
children: widget.preferredLocations.map((String loc) {
|
||||||
return UiChip(
|
return UiChip(
|
||||||
label: loc,
|
label: loc,
|
||||||
leadingIcon: UiIcons.mapPin,
|
leadingIcon: UiIcons.mapPin,
|
||||||
@@ -132,7 +132,7 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space2),
|
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space2),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t
|
t
|
||||||
.staff_authentication
|
.staff_authentication
|
||||||
@@ -158,7 +158,7 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
|
|||||||
|
|
||||||
/// Removes the specified [location] from the list.
|
/// Removes the specified [location] from the list.
|
||||||
void _removeLocation({required String location}) {
|
void _removeLocation({required String location}) {
|
||||||
final updatedList = List<String>.from(widget.preferredLocations)
|
final List<String> updatedList = List<String>.from(widget.preferredLocations)
|
||||||
..remove(location);
|
..remove(location);
|
||||||
widget.onLocationsChanged(updatedList);
|
widget.onLocationsChanged(updatedList);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
library staff_authentication;
|
library staff_authentication;
|
||||||
|
|
||||||
|
import 'package:flutter/src/widgets/framework.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:krow_data_connect/krow_data_connect.dart';
|
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||||
@@ -23,7 +24,7 @@ export 'package:core_localization/core_localization.dart';
|
|||||||
/// A [Module] for the staff authentication feature.
|
/// A [Module] for the staff authentication feature.
|
||||||
class StaffAuthenticationModule extends Module {
|
class StaffAuthenticationModule extends Module {
|
||||||
@override
|
@override
|
||||||
List<Module> get imports => [DataConnectModule()];
|
List<Module> get imports => <Module>[DataConnectModule()];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void binds(Injector i) {
|
void binds(Injector i) {
|
||||||
@@ -50,15 +51,15 @@ class StaffAuthenticationModule extends Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void routes(r) {
|
void routes(RouteManager r) {
|
||||||
r.child('/', child: (_) => const GetStartedPage());
|
r.child('/', child: (_) => const GetStartedPage());
|
||||||
r.child(
|
r.child(
|
||||||
'/phone-verification',
|
'/phone-verification',
|
||||||
child: (context) {
|
child: (BuildContext context) {
|
||||||
final Map<String, dynamic>? data = r.args.data;
|
final Map<String, dynamic>? data = r.args.data;
|
||||||
final String? modeName = data?['mode'];
|
final String? modeName = data?['mode'];
|
||||||
final AuthMode mode = AuthMode.values.firstWhere(
|
final AuthMode mode = AuthMode.values.firstWhere(
|
||||||
(e) => e.name == modeName,
|
(AuthMode e) => e.name == modeName,
|
||||||
orElse: () => AuthMode.login,
|
orElse: () => AuthMode.login,
|
||||||
);
|
);
|
||||||
return PhoneVerificationPage(mode: mode);
|
return PhoneVerificationPage(mode: mode);
|
||||||
|
|||||||
Reference in New Issue
Block a user