Merge branch 'dev' into feature/centralized-data-error-handling and resolve conflicts

This commit is contained in:
2026-02-11 12:34:29 +05:30
158 changed files with 10945 additions and 5478 deletions

View File

@@ -1,3 +1,4 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
@@ -36,7 +37,7 @@ class GetStartedPage extends StatelessWidget {
// Content Overlay
Padding(
padding: const EdgeInsets.all(24.0),
padding: const EdgeInsets.all(UiConstants.space6),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.stretch,
@@ -44,7 +45,7 @@ class GetStartedPage extends StatelessWidget {
// Main text and actions
const GetStartedHeader(),
const SizedBox(height: 48),
const SizedBox(height: UiConstants.space10),
// Actions
GetStartedActions(
@@ -52,7 +53,7 @@ class GetStartedPage extends StatelessWidget {
onLoginPressed: onLoginPressed,
),
const SizedBox(height: 32),
const SizedBox(height: UiConstants.space8),
],
),
),

View File

@@ -1,15 +1,16 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart'
hide ModularWatchExtension;
import 'package:krow_core/core.dart';
import '../blocs/profile_setup/profile_setup_bloc.dart';
import '../widgets/profile_setup_page/profile_setup_basic_info.dart';
import '../widgets/profile_setup_page/profile_setup_location.dart';
import '../widgets/profile_setup_page/profile_setup_experience.dart';
import '../widgets/profile_setup_page/profile_setup_header.dart';
import 'package:staff_authentication/staff_authentication.dart';
import 'package:krow_core/core.dart';
import '../widgets/profile_setup_page/profile_setup_location.dart';
/// Page for setting up the user profile after authentication.
class ProfileSetupPage extends StatefulWidget {
@@ -106,7 +107,8 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
}
},
builder: (BuildContext context, ProfileSetupState state) {
final bool isCreatingProfile = state.status == ProfileSetupStatus.loading;
final bool isCreatingProfile =
state.status == ProfileSetupStatus.loading;
return Scaffold(
body: SafeArea(
@@ -125,7 +127,10 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
// Step Indicators
UiStepIndicator(
stepIcons: steps
.map((Map<String, dynamic> step) => step['icon'] as IconData)
.map(
(Map<String, dynamic> step) =>
step['icon'] as IconData,
)
.toList(),
currentStep: _currentStep,
),
@@ -211,9 +216,10 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
return ProfileSetupLocation(
preferredLocations: state.preferredLocations,
maxDistanceMiles: state.maxDistanceMiles,
onLocationsChanged: (List<String> val) => BlocProvider.of<ProfileSetupBloc>(
context,
).add(ProfileSetupLocationsChanged(val)),
onLocationsChanged: (List<String> val) =>
BlocProvider.of<ProfileSetupBloc>(
context,
).add(ProfileSetupLocationsChanged(val)),
onDistanceChanged: (double val) => BlocProvider.of<ProfileSetupBloc>(
context,
).add(ProfileSetupDistanceChanged(val)),
@@ -222,12 +228,14 @@ class _ProfileSetupPageState extends State<ProfileSetupPage> {
return ProfileSetupExperience(
skills: state.skills,
industries: state.industries,
onSkillsChanged: (List<String> val) => BlocProvider.of<ProfileSetupBloc>(
context,
).add(ProfileSetupSkillsChanged(val)),
onIndustriesChanged: (List<String> val) => BlocProvider.of<ProfileSetupBloc>(
context,
).add(ProfileSetupIndustriesChanged(val)),
onSkillsChanged: (List<String> val) =>
BlocProvider.of<ProfileSetupBloc>(
context,
).add(ProfileSetupSkillsChanged(val)),
onIndustriesChanged: (List<String> val) =>
BlocProvider.of<ProfileSetupBloc>(
context,
).add(ProfileSetupIndustriesChanged(val)),
);
default:
return const SizedBox.shrink();

View File

@@ -1,6 +1,6 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:staff_authentication/staff_authentication.dart';
/// A common widget that displays a "Having trouble? Contact Support" link.
class AuthTroubleLink extends StatelessWidget {

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:staff_authentication/staff_authentication.dart';
import 'package:flutter/material.dart';
/// A widget that displays the welcome text and description on the Get Started page.
class GetStartedHeader extends StatelessWidget {
@@ -20,9 +20,7 @@ class GetStartedHeader extends StatelessWidget {
text: TextSpan(
style: UiTypography.displayM,
children: <InlineSpan>[
TextSpan(
text: i18n.title_part1,
),
TextSpan(text: i18n.title_part1),
TextSpan(
text: i18n.title_part2,
style: UiTypography.displayMb.textLink,
@@ -39,4 +37,4 @@ class GetStartedHeader extends StatelessWidget {
],
);
}
}
}

View File

@@ -1,6 +1,6 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:staff_authentication/staff_authentication.dart';
/// A widget that handles the OTP resend logic and countdown timer.
class OtpResendSection extends StatefulWidget {

View File

@@ -1,6 +1,7 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:staff_authentication/staff_authentication.dart';
import '../../common/auth_trouble_link.dart';
/// A widget that displays the primary action button and trouble link for OTP verification.

View File

@@ -1,6 +1,6 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:staff_authentication/staff_authentication.dart';
/// A widget that displays the title and subtitle for the OTP Verification page.
class OtpVerificationHeader extends StatelessWidget {

View File

@@ -1,7 +1,7 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:staff_authentication/src/presentation/widgets/common/auth_trouble_link.dart';
import 'package:staff_authentication/staff_authentication.dart';
/// A widget that displays the primary action button and trouble link for Phone Input.
class PhoneInputActions extends StatelessWidget {

View File

@@ -1,6 +1,6 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:staff_authentication/staff_authentication.dart';
/// A widget that displays the title and subtitle for the Phone Input page.
class PhoneInputHeader extends StatelessWidget {

View File

@@ -1,7 +1,7 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:staff_authentication/src/presentation/widgets/common/section_title_subtitle.dart';
import 'package:staff_authentication/staff_authentication.dart';
/// A widget for setting up basic profile information (photo, name, bio).
class ProfileSetupBasicInfo extends StatelessWidget {

View File

@@ -1,8 +1,8 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:krow_domain/krow_domain.dart';
import 'package:staff_authentication/src/presentation/widgets/common/section_title_subtitle.dart';
import 'package:staff_authentication/staff_authentication.dart';
/// A widget for setting up skills and preferred industries.
class ProfileSetupExperience extends StatelessWidget {

View File

@@ -1,6 +1,6 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:staff_authentication/staff_authentication.dart';
/// A header widget for the profile setup page showing back button and step count.
class ProfileSetupHeader extends StatelessWidget {

View File

@@ -1,10 +1,11 @@
import 'dart:async';
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:staff_authentication/src/presentation/blocs/profile_setup/profile_setup_bloc.dart';
import 'package:staff_authentication/src/presentation/widgets/common/section_title_subtitle.dart';
import 'package:staff_authentication/staff_authentication.dart';
/// A widget for setting up preferred work locations and distance.
class ProfileSetupLocation extends StatefulWidget {
@@ -47,22 +48,23 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
void _onSearchChanged(String query) {
if (_debounce?.isActive ?? false) _debounce!.cancel();
_debounce = Timer(const Duration(milliseconds: 300), () {
context
.read<ProfileSetupBloc>()
.add(ProfileSetupLocationQueryChanged(query));
context.read<ProfileSetupBloc>().add(
ProfileSetupLocationQueryChanged(query),
);
});
}
/// Adds the selected location.
void _addLocation(String location) {
if (location.isNotEmpty && !widget.preferredLocations.contains(location)) {
final List<String> updatedList =
List<String>.from(widget.preferredLocations)..add(location);
final List<String> updatedList = List<String>.from(
widget.preferredLocations,
)..add(location);
widget.onLocationsChanged(updatedList);
_locationController.clear();
context
.read<ProfileSetupBloc>()
.add(const ProfileSetupClearLocationSuggestions());
context.read<ProfileSetupBloc>().add(
const ProfileSetupClearLocationSuggestions(),
);
}
}
@@ -79,10 +81,16 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
// Search Input
UiTextField(
label: t.staff_authentication.profile_setup_page.location
label: t
.staff_authentication
.profile_setup_page
.location
.add_location_label,
controller: _locationController,
hintText: t.staff_authentication.profile_setup_page.location
hintText: t
.staff_authentication
.profile_setup_page
.location
.add_location_hint,
onChanged: _onSearchChanged,
),
@@ -99,15 +107,8 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
constraints: const BoxConstraints(maxHeight: 200),
margin: const EdgeInsets.only(top: UiConstants.space2),
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
color: UiColors.cardViewBackground,
borderRadius: UiConstants.radiusMd,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: ListView.separated(
shrinkWrap: true,
@@ -167,12 +168,18 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
t.staff_authentication.profile_setup_page.location
t
.staff_authentication
.profile_setup_page
.location
.min_dist_label,
style: UiTypography.footnote1r.textSecondary,
),
Text(
t.staff_authentication.profile_setup_page.location
t
.staff_authentication
.profile_setup_page
.location
.max_dist_label,
style: UiTypography.footnote1r.textSecondary,
),
@@ -185,8 +192,9 @@ class _ProfileSetupLocationState extends State<ProfileSetupLocation> {
/// Removes the specified [location] from the list.
void _removeLocation({required String location}) {
final List<String> updatedList =
List<String>.from(widget.preferredLocations)..remove(location);
final List<String> updatedList = List<String>.from(
widget.preferredLocations,
)..remove(location);
widget.onLocationsChanged(updatedList);
}
}

View File

@@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
import 'package:krow_data_connect/krow_data_connect.dart';
import 'package:firebase_auth/firebase_auth.dart' as firebase;
import 'package:staff_authentication/src/data/repositories_impl/auth_repository_impl.dart';
import 'package:staff_authentication/src/domain/repositories/auth_repository_interface.dart';
import 'package:staff_authentication/src/domain/usecases/sign_in_with_phone_usecase.dart';
import 'package:staff_authentication/src/domain/usecases/verify_otp_usecase.dart';
import 'package:staff_authentication/src/domain/repositories/profile_setup_repository.dart';
import 'package:staff_authentication/src/data/repositories_impl/profile_setup_repository_impl.dart';
import 'package:staff_authentication/src/domain/usecases/submit_profile_setup_usecase.dart';
import 'package:staff_authentication/src/domain/repositories/place_repository.dart';
import 'package:staff_authentication/src/data/repositories_impl/place_repository_impl.dart';
import 'package:staff_authentication/src/domain/usecases/search_cities_usecase.dart';
import 'package:staff_authentication/src/presentation/blocs/auth_bloc.dart';
import 'package:staff_authentication/src/presentation/blocs/profile_setup/profile_setup_bloc.dart';
import 'package:staff_authentication/src/presentation/pages/get_started_page.dart';
import 'package:staff_authentication/src/presentation/pages/phone_verification_page.dart';
import 'package:staff_authentication/src/presentation/pages/profile_setup_page.dart';
import 'package:staff_authentication/src/domain/ui_entities/auth_mode.dart';
/// A [Module] for the staff authentication feature.
class StaffAuthenticationModule extends Module {
@override
List<Module> get imports => <Module>[DataConnectModule()];
@override
void binds(Injector i) {
// Repositories
i.addLazySingleton<AuthRepositoryInterface>(
() => AuthRepositoryImpl(
firebaseAuth: firebase.FirebaseAuth.instance,
dataConnect: ExampleConnector.instance,
),
);
i.addLazySingleton<ProfileSetupRepository>(
() => ProfileSetupRepositoryImpl(
firebaseAuth: firebase.FirebaseAuth.instance,
dataConnect: ExampleConnector.instance,
),
);
i.addLazySingleton<PlaceRepository>(PlaceRepositoryImpl.new);
// UseCases
i.addLazySingleton(SignInWithPhoneUseCase.new);
i.addLazySingleton(VerifyOtpUseCase.new);
i.addLazySingleton(SubmitProfileSetup.new);
i.addLazySingleton(SearchCitiesUseCase.new);
// BLoCs
i.addLazySingleton<AuthBloc>(
() => AuthBloc(
signInUseCase: i.get<SignInWithPhoneUseCase>(),
verifyOtpUseCase: i.get<VerifyOtpUseCase>(),
),
);
i.add<ProfileSetupBloc>(
() => ProfileSetupBloc(
submitProfileSetup: i.get<SubmitProfileSetup>(),
searchCities: i.get<SearchCitiesUseCase>(),
),
);
}
@override
void routes(RouteManager r) {
r.child(StaffPaths.root, child: (_) => const GetStartedPage());
r.child(
StaffPaths.phoneVerification,
child: (BuildContext context) {
final Map<String, dynamic>? data = r.args.data;
final String? modeName = data?['mode'];
final AuthMode mode = AuthMode.values.firstWhere(
(AuthMode e) => e.name == modeName,
orElse: () => AuthMode.login,
);
return PhoneVerificationPage(mode: mode);
},
);
r.child(StaffPaths.profileSetup, child: (_) => const ProfileSetupPage());
}
}