From c4d0d865d733866393a7a7f187e9a59a6eb88cb5 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Wed, 18 Feb 2026 15:42:10 -0500 Subject: [PATCH 1/3] feat: Add comments to clarify the need for APPLICATIONSTATUS and SHIFTSTATUS enums in ShiftsRepositoryImpl --- .../lib/src/data/repositories_impl/shifts_repository_impl.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/data/repositories_impl/shifts_repository_impl.dart b/apps/mobile/packages/features/staff/shifts/lib/src/data/repositories_impl/shifts_repository_impl.dart index 8be4f612..4428a780 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/data/repositories_impl/shifts_repository_impl.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/data/repositories_impl/shifts_repository_impl.dart @@ -15,6 +15,8 @@ class ShiftsRepositoryImpl // Cache: ApplicationID -> RoleID (For Accept/Decline w/ Update mutation) final Map _appToRoleIdMap = {}; + // This need to be an APPLICATION + // THERE SHOULD BE APPLICATIONSTATUS and SHIFTSTATUS enums in the domain layer to avoid this string mapping and potential bugs. @override Future> getMyShifts({ required DateTime start, From 6b43a570d6d32c209e90c019fb7c5ef31838f07b Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Wed, 18 Feb 2026 16:16:49 -0500 Subject: [PATCH 2/3] Replace location sharing with profile visibility Replace the previous location-sharing privacy model with a profile-visibility feature. Renamed localization keys (en/es) and updated UI widget text. Added repository methods to get/update profile visibility using Data Connect, wired new GraphQL query (getStaffProfileVisibility) and mutation (UpdateStaffProfileVisibility), and added corresponding use cases (GetProfileVisibilityUseCase, UpdateProfileVisibilityUseCase). Updated BLoC, events, and state to use boolean isProfileVisible instead of PrivacySettingsEntity and removed old location-sharing usecases/entities. Also updated module DI and public exports accordingly; asset loading for legal docs kept with minor error logging. --- .../lib/src/l10n/en.i18n.json | 6 +- .../lib/src/l10n/es.i18n.json | 6 +- .../privacy_settings_repository_impl.dart | 121 ++++++++++-------- ...privacy_settings_repository_interface.dart | 12 +- .../get_privacy_settings_usecase.dart | 22 ---- .../get_profile_visibility_usecase.dart | 19 +++ .../update_location_sharing_usecase.dart | 35 ----- .../update_profile_visibility_usecase.dart | 32 +++++ .../blocs/privacy_security_bloc.dart | 48 ++++--- .../blocs/privacy_security_event.dart | 21 +-- .../blocs/privacy_security_state.dart | 19 +-- .../pages/privacy_security_page.dart | 2 +- .../privacy/privacy_section_widget.dart | 10 +- .../src/staff_privacy_security_module.dart | 12 +- .../lib/staff_privacy_security.dart | 2 - .../dataconnect/connector/staff/mutations.gql | 9 ++ .../dataconnect/connector/staff/queries.gql | 7 + 17 files changed, 201 insertions(+), 182 deletions(-) delete mode 100644 apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/get_privacy_settings_usecase.dart create mode 100644 apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/get_profile_visibility_usecase.dart delete mode 100644 apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/update_location_sharing_usecase.dart create mode 100644 apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/update_profile_visibility_usecase.dart diff --git a/apps/mobile/packages/core_localization/lib/src/l10n/en.i18n.json b/apps/mobile/packages/core_localization/lib/src/l10n/en.i18n.json index ab54d771..81a04a66 100644 --- a/apps/mobile/packages/core_localization/lib/src/l10n/en.i18n.json +++ b/apps/mobile/packages/core_localization/lib/src/l10n/en.i18n.json @@ -1129,9 +1129,9 @@ "title": "Privacy & Security", "privacy_section": "Privacy", "legal_section": "Legal", - "location_sharing": { - "title": "Location Sharing", - "subtitle": "Share location during shifts" + "profile_visibility": { + "title": "Profile Visibility", + "subtitle": "Show your profile to other users" }, "terms_of_service": { "title": "Terms of Service" diff --git a/apps/mobile/packages/core_localization/lib/src/l10n/es.i18n.json b/apps/mobile/packages/core_localization/lib/src/l10n/es.i18n.json index e537d3da..23257957 100644 --- a/apps/mobile/packages/core_localization/lib/src/l10n/es.i18n.json +++ b/apps/mobile/packages/core_localization/lib/src/l10n/es.i18n.json @@ -1129,9 +1129,9 @@ "title": "Privacidad y Seguridad", "privacy_section": "Privacidad", "legal_section": "Legal", - "location_sharing": { - "title": "Compartir Ubicación", - "subtitle": "Compartir ubicación durante turnos" + "profile_visibility": { + "title": "Visibilidad del Perfil", + "subtitle": "Mostrar tu perfil a otros usuarios" }, "terms_of_service": { "title": "Términos de Servicio" diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/data/repositories_impl/privacy_settings_repository_impl.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/data/repositories_impl/privacy_settings_repository_impl.dart index b317e470..66225fc4 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/data/repositories_impl/privacy_settings_repository_impl.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/data/repositories_impl/privacy_settings_repository_impl.dart @@ -1,79 +1,92 @@ +import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc; import 'package:flutter/services.dart'; import 'package:krow_data_connect/krow_data_connect.dart'; -import '../../domain/entities/privacy_settings_entity.dart'; import '../../domain/repositories/privacy_settings_repository_interface.dart'; /// Data layer implementation of privacy settings repository -/// -/// Handles all backend communication for privacy settings, -/// using DataConnectService for automatic auth and token refresh, +/// +/// Handles all backend communication for privacy settings via Data Connect, /// and loads legal documents from app assets class PrivacySettingsRepositoryImpl implements PrivacySettingsRepositoryInterface { - final DataConnectService _service; - PrivacySettingsRepositoryImpl(this._service); + final DataConnectService _service; + @override - Future getPrivacySettings() async { - return _service.run( - () async { - // TODO: Call Data Connect query to fetch privacy settings - // For now, return default settings - return PrivacySettingsEntity( - locationSharing: true, - updatedAt: DateTime.now(), - ); - }, - ); + Future getProfileVisibility() async { + return _service.run(() async { + // Get current user ID + final String staffId = await _service.getStaffId(); + + // Call Data Connect query: getStaffProfileVisibility + final fdc.QueryResult< + GetStaffProfileVisibilityData, + GetStaffProfileVisibilityVariables + > + response = await _service.connector + .getStaffProfileVisibility(staffId: staffId) + .execute(); + + // Return the profile visibility status from the first result + if (response.data.staff != null) { + return response.data.staff?.isProfileVisible ?? true; + } + + // Default to visible if no staff record found + return true; + }); } @override - Future updateLocationSharing(bool enabled) async { - return _service.run( - () async { - // TODO: Call Data Connect mutation to update location sharing preference - // For now, return updated settings - return PrivacySettingsEntity( - locationSharing: enabled, - updatedAt: DateTime.now(), - ); - }, - ); + Future updateProfileVisibility(bool isVisible) async { + return _service.run(() async { + // Get staff ID for the current user + final String staffId = await _service.getStaffId(); + + // Call Data Connect mutation: UpdateStaffProfileVisibility + await _service.connector + .updateStaffProfileVisibility( + id: staffId, + isProfileVisible: isVisible, + ) + .execute(); + + // Return the requested visibility state + return isVisible; + }); } @override Future getTermsOfService() async { - return _service.run( - () async { - try { - // Load from package asset path - return await rootBundle.loadString( - 'packages/staff_privacy_security/lib/src/assets/legal/terms_of_service.txt', - ); - } catch (e) { - // Final fallback if asset not found - return 'Terms of Service - Content unavailable. Please contact support@krow.com'; - } - }, - ); + return _service.run(() async { + try { + // Load from package asset path + return await rootBundle.loadString( + 'packages/staff_privacy_security/lib/src/assets/legal/terms_of_service.txt', + ); + } catch (e) { + // Final fallback if asset not found + print('Error loading terms of service: $e'); + return 'Terms of Service - Content unavailable. Please contact support@krow.com'; + } + }); } @override Future getPrivacyPolicy() async { - return _service.run( - () async { - try { - // Load from package asset path - return await rootBundle.loadString( - 'packages/staff_privacy_security/lib/src/assets/legal/privacy_policy.txt', - ); - } catch (e) { - // Final fallback if asset not found - return 'Privacy Policy - Content unavailable. Please contact privacy@krow.com'; - } - }, - ); + return _service.run(() async { + try { + // Load from package asset path + return await rootBundle.loadString( + 'packages/staff_privacy_security/lib/src/assets/legal/privacy_policy.txt', + ); + } catch (e) { + // Final fallback if asset not found + print('Error loading privacy policy: $e'); + return 'Privacy Policy - Content unavailable. Please contact privacy@krow.com'; + } + }); } } diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/repositories/privacy_settings_repository_interface.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/repositories/privacy_settings_repository_interface.dart index 666cc0b9..8057a76e 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/repositories/privacy_settings_repository_interface.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/repositories/privacy_settings_repository_interface.dart @@ -1,14 +1,12 @@ -import '../entities/privacy_settings_entity.dart'; - /// Interface for privacy settings repository operations abstract class PrivacySettingsRepositoryInterface { - /// Fetch the current user's privacy settings - Future getPrivacySettings(); + /// Fetch the current staff member's profile visibility setting + Future getProfileVisibility(); - /// Update location sharing preference + /// Update profile visibility preference /// - /// Returns the updated privacy settings - Future updateLocationSharing(bool enabled); + /// Returns the updated profile visibility status + Future updateProfileVisibility(bool isVisible); /// Fetch terms of service content Future getTermsOfService(); diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/get_privacy_settings_usecase.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/get_privacy_settings_usecase.dart deleted file mode 100644 index f3066bcb..00000000 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/get_privacy_settings_usecase.dart +++ /dev/null @@ -1,22 +0,0 @@ -import '../entities/privacy_settings_entity.dart'; -import '../repositories/privacy_settings_repository_interface.dart'; - -/// Use case to retrieve the current user's privacy settings -class GetPrivacySettingsUseCase { - final PrivacySettingsRepositoryInterface _repository; - - GetPrivacySettingsUseCase(this._repository); - - /// Execute the use case to get privacy settings - Future call() async { - try { - return await _repository.getPrivacySettings(); - } catch (e) { - // Return default settings on error - return PrivacySettingsEntity( - locationSharing: true, - updatedAt: DateTime.now(), - ); - } - } -} diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/get_profile_visibility_usecase.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/get_profile_visibility_usecase.dart new file mode 100644 index 00000000..3b21da61 --- /dev/null +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/get_profile_visibility_usecase.dart @@ -0,0 +1,19 @@ +import '../repositories/privacy_settings_repository_interface.dart'; + +/// Use case to retrieve the current staff member's profile visibility setting +class GetProfileVisibilityUseCase { + final PrivacySettingsRepositoryInterface _repository; + + GetProfileVisibilityUseCase(this._repository); + + /// Execute the use case to get profile visibility status + /// Returns true if profile is visible, false if hidden + Future call() async { + try { + return await _repository.getProfileVisibility(); + } catch (e) { + // Return default (visible) on error + return true; + } + } +} diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/update_location_sharing_usecase.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/update_location_sharing_usecase.dart deleted file mode 100644 index 2ee00d33..00000000 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/update_location_sharing_usecase.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:equatable/equatable.dart'; - -import '../entities/privacy_settings_entity.dart'; -import '../repositories/privacy_settings_repository_interface.dart'; - -/// Parameters for updating location sharing -class UpdateLocationSharingParams extends Equatable { - /// Whether to enable or disable location sharing - final bool enabled; - - const UpdateLocationSharingParams({required this.enabled}); - - @override - List get props => [enabled]; -} - -/// Use case to update location sharing preference -class UpdateLocationSharingUseCase { - final PrivacySettingsRepositoryInterface _repository; - - UpdateLocationSharingUseCase(this._repository); - - /// Execute the use case to update location sharing - Future call(UpdateLocationSharingParams params) async { - try { - return await _repository.updateLocationSharing(params.enabled); - } catch (e) { - // Return current settings on error - return PrivacySettingsEntity( - locationSharing: params.enabled, - updatedAt: DateTime.now(), - ); - } - } -} diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/update_profile_visibility_usecase.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/update_profile_visibility_usecase.dart new file mode 100644 index 00000000..9048ae59 --- /dev/null +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/domain/usecases/update_profile_visibility_usecase.dart @@ -0,0 +1,32 @@ +import 'package:equatable/equatable.dart'; + +import '../repositories/privacy_settings_repository_interface.dart'; + +/// Parameters for updating profile visibility +class UpdateProfileVisibilityParams extends Equatable { + /// Whether to show (true) or hide (false) the profile + final bool isVisible; + + const UpdateProfileVisibilityParams({required this.isVisible}); + + @override + List get props => [isVisible]; +} + +/// Use case to update profile visibility setting +class UpdateProfileVisibilityUseCase { + final PrivacySettingsRepositoryInterface _repository; + + UpdateProfileVisibilityUseCase(this._repository); + + /// Execute the use case to update profile visibility + /// Returns the updated visibility status + Future call(UpdateProfileVisibilityParams params) async { + try { + return await _repository.updateProfileVisibility(params.isVisible); + } catch (e) { + // Return the requested state on error (optimistic) + return params.isVisible; + } + } +} diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_bloc.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_bloc.dart index 70b51944..c7e350d7 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_bloc.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_bloc.dart @@ -1,10 +1,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:equatable/equatable.dart'; -import '../../domain/entities/privacy_settings_entity.dart'; -import '../../domain/repositories/privacy_settings_repository_interface.dart'; -import '../../domain/usecases/get_privacy_settings_usecase.dart'; -import '../../domain/usecases/update_location_sharing_usecase.dart'; +import '../../domain/usecases/get_profile_visibility_usecase.dart'; +import '../../domain/usecases/update_profile_visibility_usecase.dart'; import '../../domain/usecases/get_terms_usecase.dart'; import '../../domain/usecases/get_privacy_policy_usecase.dart'; @@ -14,72 +12,72 @@ part 'privacy_security_state.dart'; /// BLoC managing privacy and security settings state class PrivacySecurityBloc extends Bloc { - final GetPrivacySettingsUseCase _getPrivacySettingsUseCase; - final UpdateLocationSharingUseCase _updateLocationSharingUseCase; + final GetProfileVisibilityUseCase _getProfileVisibilityUseCase; + final UpdateProfileVisibilityUseCase _updateProfileVisibilityUseCase; final GetTermsUseCase _getTermsUseCase; final GetPrivacyPolicyUseCase _getPrivacyPolicyUseCase; PrivacySecurityBloc({ - required GetPrivacySettingsUseCase getPrivacySettingsUseCase, - required UpdateLocationSharingUseCase updateLocationSharingUseCase, + required GetProfileVisibilityUseCase getProfileVisibilityUseCase, + required UpdateProfileVisibilityUseCase updateProfileVisibilityUseCase, required GetTermsUseCase getTermsUseCase, required GetPrivacyPolicyUseCase getPrivacyPolicyUseCase, - }) : _getPrivacySettingsUseCase = getPrivacySettingsUseCase, - _updateLocationSharingUseCase = updateLocationSharingUseCase, + }) : _getProfileVisibilityUseCase = getProfileVisibilityUseCase, + _updateProfileVisibilityUseCase = updateProfileVisibilityUseCase, _getTermsUseCase = getTermsUseCase, _getPrivacyPolicyUseCase = getPrivacyPolicyUseCase, super(const PrivacySecurityState()) { - on(_onFetchPrivacySettings); - on(_onUpdateLocationSharing); + on(_onFetchProfileVisibility); + on(_onUpdateProfileVisibility); on(_onFetchTerms); on(_onFetchPrivacyPolicy); } - Future _onFetchPrivacySettings( - FetchPrivacySettingsEvent event, + Future _onFetchProfileVisibility( + FetchProfileVisibilityEvent event, Emitter emit, ) async { emit(state.copyWith(isLoading: true, error: null)); try { - final settings = await _getPrivacySettingsUseCase.call(); + final bool isVisible = await _getProfileVisibilityUseCase.call(); emit( state.copyWith( isLoading: false, - privacySettings: settings, + isProfileVisible: isVisible, ), ); } catch (e) { emit( state.copyWith( isLoading: false, - error: 'Failed to fetch privacy settings', + error: 'Failed to fetch profile visibility', ), ); } } - Future _onUpdateLocationSharing( - UpdateLocationSharingEvent event, + Future _onUpdateProfileVisibility( + UpdateProfileVisibilityEvent event, Emitter emit, ) async { emit(state.copyWith(isUpdating: true, error: null)); try { - final settings = await _updateLocationSharingUseCase.call( - UpdateLocationSharingParams(enabled: event.enabled), + final bool isVisible = await _updateProfileVisibilityUseCase.call( + UpdateProfileVisibilityParams(isVisible: event.isVisible), ); emit( state.copyWith( isUpdating: false, - privacySettings: settings, + isProfileVisible: isVisible, ), ); } catch (e) { emit( state.copyWith( isUpdating: false, - error: 'Failed to update location sharing', + error: 'Failed to update profile visibility', ), ); } @@ -92,7 +90,7 @@ class PrivacySecurityBloc emit(state.copyWith(isLoadingTerms: true, error: null)); try { - final content = await _getTermsUseCase.call(); + final String content = await _getTermsUseCase.call(); emit( state.copyWith( isLoadingTerms: false, @@ -116,7 +114,7 @@ class PrivacySecurityBloc emit(state.copyWith(isLoadingPrivacyPolicy: true, error: null)); try { - final content = await _getPrivacyPolicyUseCase.call(); + final String content = await _getPrivacyPolicyUseCase.call(); emit( state.copyWith( isLoadingPrivacyPolicy: false, diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_event.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_event.dart index d1a9caac..8960ac53 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_event.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_event.dart @@ -5,23 +5,23 @@ abstract class PrivacySecurityEvent extends Equatable { const PrivacySecurityEvent(); @override - List get props => []; + List get props => []; } -/// Event to fetch current privacy settings -class FetchPrivacySettingsEvent extends PrivacySecurityEvent { - const FetchPrivacySettingsEvent(); +/// Event to fetch current profile visibility setting +class FetchProfileVisibilityEvent extends PrivacySecurityEvent { + const FetchProfileVisibilityEvent(); } -/// Event to update location sharing preference -class UpdateLocationSharingEvent extends PrivacySecurityEvent { - /// Whether to enable or disable location sharing - final bool enabled; +/// Event to update profile visibility +class UpdateProfileVisibilityEvent extends PrivacySecurityEvent { + /// Whether to show (true) or hide (false) the profile + final bool isVisible; - const UpdateLocationSharingEvent({required this.enabled}); + const UpdateProfileVisibilityEvent({required this.isVisible}); @override - List get props => [enabled]; + List get props => [isVisible]; } /// Event to fetch terms of service @@ -33,3 +33,4 @@ class FetchTermsEvent extends PrivacySecurityEvent { class FetchPrivacyPolicyEvent extends PrivacySecurityEvent { const FetchPrivacyPolicyEvent(); } + diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_state.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_state.dart index 14a6c39d..a52d1c38 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_state.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_state.dart @@ -2,13 +2,13 @@ part of 'privacy_security_bloc.dart'; /// State for privacy security BLoC class PrivacySecurityState extends Equatable { - /// Current privacy settings - final PrivacySettingsEntity? privacySettings; + /// Current profile visibility setting (true = visible, false = hidden) + final bool isProfileVisible; - /// Whether settings are currently loading + /// Whether profile visibility is currently loading final bool isLoading; - /// Whether settings are currently being updated + /// Whether profile visibility is currently being updated final bool isUpdating; /// Terms of service content @@ -27,7 +27,7 @@ class PrivacySecurityState extends Equatable { final String? error; const PrivacySecurityState({ - this.privacySettings, + this.isProfileVisible = true, this.isLoading = false, this.isUpdating = false, this.termsContent, @@ -39,7 +39,7 @@ class PrivacySecurityState extends Equatable { /// Create a copy with optional field overrides PrivacySecurityState copyWith({ - PrivacySettingsEntity? privacySettings, + bool? isProfileVisible, bool? isLoading, bool? isUpdating, String? termsContent, @@ -49,7 +49,7 @@ class PrivacySecurityState extends Equatable { String? error, }) { return PrivacySecurityState( - privacySettings: privacySettings ?? this.privacySettings, + isProfileVisible: isProfileVisible ?? this.isProfileVisible, isLoading: isLoading ?? this.isLoading, isUpdating: isUpdating ?? this.isUpdating, termsContent: termsContent ?? this.termsContent, @@ -62,8 +62,8 @@ class PrivacySecurityState extends Equatable { } @override - List get props => [ - privacySettings, + List get props => [ + isProfileVisible, isLoading, isUpdating, termsContent, @@ -73,3 +73,4 @@ class PrivacySecurityState extends Equatable { error, ]; } + diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/pages/privacy_security_page.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/pages/privacy_security_page.dart index 5897fa8e..28749dbe 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/pages/privacy_security_page.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/pages/privacy_security_page.dart @@ -25,7 +25,7 @@ class PrivacySecurityPage extends StatelessWidget { ), body: BlocProvider.value( value: Modular.get() - ..add(const FetchPrivacySettingsEvent()), + ..add(const FetchProfileVisibilityEvent()), child: BlocBuilder( builder: (BuildContext context, PrivacySecurityState state) { if (state.isLoading) { diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/widgets/privacy/privacy_section_widget.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/widgets/privacy/privacy_section_widget.dart index 8209ead0..3ad0ec45 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/widgets/privacy/privacy_section_widget.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/widgets/privacy/privacy_section_widget.dart @@ -7,7 +7,7 @@ import '../../blocs/privacy_security_bloc.dart'; import '../settings_section_header_widget.dart'; import '../settings_switch_tile_widget.dart'; -/// Widget displaying privacy settings including location sharing preference +/// Widget displaying privacy settings including profile visibility preference class PrivacySectionWidget extends StatelessWidget { const PrivacySectionWidget({super.key}); @@ -34,12 +34,12 @@ class PrivacySectionWidget extends StatelessWidget { child: Column( children: [ SettingsSwitchTile( - title: t.staff_privacy_security.location_sharing.title, - subtitle: t.staff_privacy_security.location_sharing.subtitle, - value: state.privacySettings?.locationSharing ?? false, + title: t.staff_privacy_security.profile_visibility.title, + subtitle: t.staff_privacy_security.profile_visibility.subtitle, + value: state.isProfileVisible, onChanged: (bool value) { BlocProvider.of(context).add( - UpdateLocationSharingEvent(enabled: value), + UpdateProfileVisibilityEvent(isVisible: value), ); }, ), diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/staff_privacy_security_module.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/staff_privacy_security_module.dart index 86667131..22b0d405 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/staff_privacy_security_module.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/staff_privacy_security_module.dart @@ -6,9 +6,9 @@ import 'package:krow_data_connect/krow_data_connect.dart'; import 'data/repositories_impl/privacy_settings_repository_impl.dart'; import 'domain/repositories/privacy_settings_repository_interface.dart'; import 'domain/usecases/get_privacy_policy_usecase.dart'; -import 'domain/usecases/get_privacy_settings_usecase.dart'; +import 'domain/usecases/get_profile_visibility_usecase.dart'; import 'domain/usecases/get_terms_usecase.dart'; -import 'domain/usecases/update_location_sharing_usecase.dart'; +import 'domain/usecases/update_profile_visibility_usecase.dart'; import 'presentation/blocs/legal/privacy_policy_cubit.dart'; import 'presentation/blocs/legal/terms_cubit.dart'; import 'presentation/blocs/privacy_security_bloc.dart'; @@ -33,12 +33,12 @@ class PrivacySecurityModule extends Module { // Use Cases i.addSingleton( - () => GetPrivacySettingsUseCase( + () => GetProfileVisibilityUseCase( i(), ), ); i.addSingleton( - () => UpdateLocationSharingUseCase( + () => UpdateProfileVisibilityUseCase( i(), ), ); @@ -56,8 +56,8 @@ class PrivacySecurityModule extends Module { // BLoC i.add( () => PrivacySecurityBloc( - getPrivacySettingsUseCase: i(), - updateLocationSharingUseCase: i(), + getProfileVisibilityUseCase: i(), + updateProfileVisibilityUseCase: i(), getTermsUseCase: i(), getPrivacyPolicyUseCase: i(), ), diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/staff_privacy_security.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/staff_privacy_security.dart index d6630e8c..a638651d 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/staff_privacy_security.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/staff_privacy_security.dart @@ -1,7 +1,5 @@ export 'src/domain/entities/privacy_settings_entity.dart'; export 'src/domain/repositories/privacy_settings_repository_interface.dart'; -export 'src/domain/usecases/get_privacy_settings_usecase.dart'; -export 'src/domain/usecases/update_location_sharing_usecase.dart'; export 'src/domain/usecases/get_terms_usecase.dart'; export 'src/domain/usecases/get_privacy_policy_usecase.dart'; export 'src/data/repositories_impl/privacy_settings_repository_impl.dart'; diff --git a/backend/dataconnect/connector/staff/mutations.gql b/backend/dataconnect/connector/staff/mutations.gql index 797ca1bd..23f9b0c7 100644 --- a/backend/dataconnect/connector/staff/mutations.gql +++ b/backend/dataconnect/connector/staff/mutations.gql @@ -214,3 +214,12 @@ mutation UpdateStaff( mutation DeleteStaff($id: UUID!) @auth(level: USER) { staff_delete(id: $id) } + +mutation UpdateStaffProfileVisibility($id: UUID!, $isProfileVisible: Boolean!) @auth(level: USER) { + staff_update( + id: $id + data: { + isProfileVisible: $isProfileVisible + } + ) +} diff --git a/backend/dataconnect/connector/staff/queries.gql b/backend/dataconnect/connector/staff/queries.gql index aecf8891..61bb7113 100644 --- a/backend/dataconnect/connector/staff/queries.gql +++ b/backend/dataconnect/connector/staff/queries.gql @@ -204,3 +204,10 @@ query filterStaff( zipCode } } + +query getStaffProfileVisibility($staffId: UUID!) @auth(level: USER) { + staff(id: $staffId) { + id + isProfileVisible + } +} From 3f3579067cca25d8299631b4fe43ecd682404062 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Wed, 18 Feb 2026 16:30:22 -0500 Subject: [PATCH 3/3] feat: Implement profile visibility update feedback and localization updates --- .../lib/src/l10n/en.i18n.json | 5 +- .../lib/src/l10n/es.i18n.json | 5 +- .../blocs/privacy_security_bloc.dart | 12 ++- .../blocs/privacy_security_event.dart | 4 + .../blocs/privacy_security_state.dart | 7 ++ .../widgets/legal/legal_section_widget.dart | 2 +- .../privacy/privacy_section_widget.dart | 84 +++++++++++-------- 7 files changed, 81 insertions(+), 38 deletions(-) diff --git a/apps/mobile/packages/core_localization/lib/src/l10n/en.i18n.json b/apps/mobile/packages/core_localization/lib/src/l10n/en.i18n.json index 81a04a66..9fb4251f 100644 --- a/apps/mobile/packages/core_localization/lib/src/l10n/en.i18n.json +++ b/apps/mobile/packages/core_localization/lib/src/l10n/en.i18n.json @@ -1131,13 +1131,16 @@ "legal_section": "Legal", "profile_visibility": { "title": "Profile Visibility", - "subtitle": "Show your profile to other users" + "subtitle": "Let clients see your profile" }, "terms_of_service": { "title": "Terms of Service" }, "privacy_policy": { "title": "Privacy Policy" + }, + "success": { + "profile_visibility_updated": "Profile visibility updated successfully!" } }, "success": { diff --git a/apps/mobile/packages/core_localization/lib/src/l10n/es.i18n.json b/apps/mobile/packages/core_localization/lib/src/l10n/es.i18n.json index 23257957..77370c8e 100644 --- a/apps/mobile/packages/core_localization/lib/src/l10n/es.i18n.json +++ b/apps/mobile/packages/core_localization/lib/src/l10n/es.i18n.json @@ -1131,13 +1131,16 @@ "legal_section": "Legal", "profile_visibility": { "title": "Visibilidad del Perfil", - "subtitle": "Mostrar tu perfil a otros usuarios" + "subtitle": "Deja que los clientes vean tu perfil" }, "terms_of_service": { "title": "Términos de Servicio" }, "privacy_policy": { "title": "Política de Privacidad" + }, + "success": { + "profile_visibility_updated": "¡Visibilidad del perfil actualizada exitosamente!" } }, "success": { diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_bloc.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_bloc.dart index c7e350d7..d333824d 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_bloc.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_bloc.dart @@ -31,6 +31,7 @@ class PrivacySecurityBloc on(_onUpdateProfileVisibility); on(_onFetchTerms); on(_onFetchPrivacyPolicy); + on(_onClearProfileVisibilityUpdated); } Future _onFetchProfileVisibility( @@ -61,7 +62,7 @@ class PrivacySecurityBloc UpdateProfileVisibilityEvent event, Emitter emit, ) async { - emit(state.copyWith(isUpdating: true, error: null)); + emit(state.copyWith(isUpdating: true, error: null, profileVisibilityUpdated: false)); try { final bool isVisible = await _updateProfileVisibilityUseCase.call( @@ -71,6 +72,7 @@ class PrivacySecurityBloc state.copyWith( isUpdating: false, isProfileVisible: isVisible, + profileVisibilityUpdated: true, ), ); } catch (e) { @@ -78,6 +80,7 @@ class PrivacySecurityBloc state.copyWith( isUpdating: false, error: 'Failed to update profile visibility', + profileVisibilityUpdated: false, ), ); } @@ -130,4 +133,11 @@ class PrivacySecurityBloc ); } } + + void _onClearProfileVisibilityUpdated( + ClearProfileVisibilityUpdatedEvent event, + Emitter emit, + ) { + emit(state.copyWith(profileVisibilityUpdated: false)); + } } diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_event.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_event.dart index 8960ac53..6dbfcfdd 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_event.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_event.dart @@ -34,3 +34,7 @@ class FetchPrivacyPolicyEvent extends PrivacySecurityEvent { const FetchPrivacyPolicyEvent(); } +/// Event to clear the profile visibility updated flag after showing snackbar +class ClearProfileVisibilityUpdatedEvent extends PrivacySecurityEvent { + const ClearProfileVisibilityUpdatedEvent(); +} diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_state.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_state.dart index a52d1c38..a84666ad 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_state.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/blocs/privacy_security_state.dart @@ -11,6 +11,9 @@ class PrivacySecurityState extends Equatable { /// Whether profile visibility is currently being updated final bool isUpdating; + /// Whether the profile visibility was just successfully updated + final bool profileVisibilityUpdated; + /// Terms of service content final String? termsContent; @@ -30,6 +33,7 @@ class PrivacySecurityState extends Equatable { this.isProfileVisible = true, this.isLoading = false, this.isUpdating = false, + this.profileVisibilityUpdated = false, this.termsContent, this.isLoadingTerms = false, this.privacyPolicyContent, @@ -42,6 +46,7 @@ class PrivacySecurityState extends Equatable { bool? isProfileVisible, bool? isLoading, bool? isUpdating, + bool? profileVisibilityUpdated, String? termsContent, bool? isLoadingTerms, String? privacyPolicyContent, @@ -52,6 +57,7 @@ class PrivacySecurityState extends Equatable { isProfileVisible: isProfileVisible ?? this.isProfileVisible, isLoading: isLoading ?? this.isLoading, isUpdating: isUpdating ?? this.isUpdating, + profileVisibilityUpdated: profileVisibilityUpdated ?? this.profileVisibilityUpdated, termsContent: termsContent ?? this.termsContent, isLoadingTerms: isLoadingTerms ?? this.isLoadingTerms, privacyPolicyContent: privacyPolicyContent ?? this.privacyPolicyContent, @@ -66,6 +72,7 @@ class PrivacySecurityState extends Equatable { isProfileVisible, isLoading, isUpdating, + profileVisibilityUpdated, termsContent, isLoadingTerms, privacyPolicyContent, diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/widgets/legal/legal_section_widget.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/widgets/legal/legal_section_widget.dart index e1dfc013..d50540a3 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/widgets/legal/legal_section_widget.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/widgets/legal/legal_section_widget.dart @@ -23,7 +23,7 @@ class LegalSectionWidget extends StatelessWidget { // Legal Section Header SettingsSectionHeader( title: t.staff_privacy_security.legal_section, - icon: Icons.shield, + icon: UiIcons.shield, ), Container( diff --git a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/widgets/privacy/privacy_section_widget.dart b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/widgets/privacy/privacy_section_widget.dart index 3ad0ec45..c8a54a63 100644 --- a/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/widgets/privacy/privacy_section_widget.dart +++ b/apps/mobile/packages/features/staff/profile_sections/settings/privacy_security/lib/src/presentation/widgets/privacy/privacy_section_widget.dart @@ -13,42 +13,58 @@ class PrivacySectionWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (BuildContext context, PrivacySecurityState state) { - return Column( - children: [ - // Privacy Section Header - SettingsSectionHeader( - title: t.staff_privacy_security.privacy_section, - icon: UiIcons.eye, - ), - const SizedBox(height: 12.0), - Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(UiConstants.radiusBase), - border: Border.all( - color: UiColors.border, + return BlocListener( + listener: (BuildContext context, PrivacySecurityState state) { + // Show success message when profile visibility update just completed + if (state.profileVisibilityUpdated && state.error == null) { + UiSnackbar.show( + context, + message: t.staff_privacy_security.success.profile_visibility_updated, + type: UiSnackbarType.success, + ); + // Clear the flag after showing the snackbar + context.read().add( + const ClearProfileVisibilityUpdatedEvent(), + ); + } + }, + child: BlocBuilder( + builder: (BuildContext context, PrivacySecurityState state) { + return Column( + children: [ + // Privacy Section Header + SettingsSectionHeader( + title: t.staff_privacy_security.privacy_section, + icon: UiIcons.eye, + ), + const SizedBox(height: 12.0), + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(UiConstants.radiusBase), + border: Border.all( + color: UiColors.border, + ), + ), + child: Column( + children: [ + SettingsSwitchTile( + title: t.staff_privacy_security.profile_visibility.title, + subtitle: t.staff_privacy_security.profile_visibility.subtitle, + value: state.isProfileVisible, + onChanged: (bool value) { + BlocProvider.of(context).add( + UpdateProfileVisibilityEvent(isVisible: value), + ); + }, + ), + ], ), ), - child: Column( - children: [ - SettingsSwitchTile( - title: t.staff_privacy_security.profile_visibility.title, - subtitle: t.staff_privacy_security.profile_visibility.subtitle, - value: state.isProfileVisible, - onChanged: (bool value) { - BlocProvider.of(context).add( - UpdateProfileVisibilityEvent(isVisible: value), - ); - }, - ), - ], - ), - ), - ], - ); - }, + ], + ); + }, + ), ); } }