Merge remote-tracking branch 'origin/408-feature-implement-paidunpaid-breaks---client-app-frontend-development' into staff_recurring_permanent_order

This commit is contained in:
José Salazar
2026-02-19 14:47:35 -05:00
19 changed files with 278 additions and 214 deletions

View File

@@ -1129,15 +1129,18 @@
"title": "Privacy & Security", "title": "Privacy & Security",
"privacy_section": "Privacy", "privacy_section": "Privacy",
"legal_section": "Legal", "legal_section": "Legal",
"location_sharing": { "profile_visibility": {
"title": "Location Sharing", "title": "Profile Visibility",
"subtitle": "Share location during shifts" "subtitle": "Let clients see your profile"
}, },
"terms_of_service": { "terms_of_service": {
"title": "Terms of Service" "title": "Terms of Service"
}, },
"privacy_policy": { "privacy_policy": {
"title": "Privacy Policy" "title": "Privacy Policy"
},
"success": {
"profile_visibility_updated": "Profile visibility updated successfully!"
} }
}, },
"success": { "success": {

View File

@@ -1129,15 +1129,18 @@
"title": "Privacidad y Seguridad", "title": "Privacidad y Seguridad",
"privacy_section": "Privacidad", "privacy_section": "Privacidad",
"legal_section": "Legal", "legal_section": "Legal",
"location_sharing": { "profile_visibility": {
"title": "Compartir Ubicación", "title": "Visibilidad del Perfil",
"subtitle": "Compartir ubicación durante turnos" "subtitle": "Deja que los clientes vean tu perfil"
}, },
"terms_of_service": { "terms_of_service": {
"title": "Términos de Servicio" "title": "Términos de Servicio"
}, },
"privacy_policy": { "privacy_policy": {
"title": "Política de Privacidad" "title": "Política de Privacidad"
},
"success": {
"profile_visibility_updated": "¡Visibilidad del perfil actualizada exitosamente!"
} }
}, },
"success": { "success": {

View File

@@ -1,79 +1,92 @@
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:krow_data_connect/krow_data_connect.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'; import '../../domain/repositories/privacy_settings_repository_interface.dart';
/// Data layer implementation of privacy settings repository /// Data layer implementation of privacy settings repository
/// ///
/// Handles all backend communication for privacy settings, /// Handles all backend communication for privacy settings via Data Connect,
/// using DataConnectService for automatic auth and token refresh,
/// and loads legal documents from app assets /// and loads legal documents from app assets
class PrivacySettingsRepositoryImpl class PrivacySettingsRepositoryImpl
implements PrivacySettingsRepositoryInterface { implements PrivacySettingsRepositoryInterface {
final DataConnectService _service;
PrivacySettingsRepositoryImpl(this._service); PrivacySettingsRepositoryImpl(this._service);
final DataConnectService _service;
@override @override
Future<PrivacySettingsEntity> getPrivacySettings() async { Future<bool> getProfileVisibility() async {
return _service.run<PrivacySettingsEntity>( return _service.run<bool>(() async {
() async { // Get current user ID
// TODO: Call Data Connect query to fetch privacy settings final String staffId = await _service.getStaffId();
// For now, return default settings
return PrivacySettingsEntity( // Call Data Connect query: getStaffProfileVisibility
locationSharing: true, final fdc.QueryResult<
updatedAt: DateTime.now(), 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 @override
Future<PrivacySettingsEntity> updateLocationSharing(bool enabled) async { Future<bool> updateProfileVisibility(bool isVisible) async {
return _service.run<PrivacySettingsEntity>( return _service.run<bool>(() async {
() async { // Get staff ID for the current user
// TODO: Call Data Connect mutation to update location sharing preference final String staffId = await _service.getStaffId();
// For now, return updated settings
return PrivacySettingsEntity( // Call Data Connect mutation: UpdateStaffProfileVisibility
locationSharing: enabled, await _service.connector
updatedAt: DateTime.now(), .updateStaffProfileVisibility(
); id: staffId,
}, isProfileVisible: isVisible,
); )
.execute();
// Return the requested visibility state
return isVisible;
});
} }
@override @override
Future<String> getTermsOfService() async { Future<String> getTermsOfService() async {
return _service.run<String>( return _service.run<String>(() async {
() async { try {
try { // Load from package asset path
// Load from package asset path return await rootBundle.loadString(
return await rootBundle.loadString( 'packages/staff_privacy_security/lib/src/assets/legal/terms_of_service.txt',
'packages/staff_privacy_security/lib/src/assets/legal/terms_of_service.txt', );
); } catch (e) {
} catch (e) { // Final fallback if asset not found
// Final fallback if asset not found print('Error loading terms of service: $e');
return 'Terms of Service - Content unavailable. Please contact support@krow.com'; return 'Terms of Service - Content unavailable. Please contact support@krow.com';
} }
}, });
);
} }
@override @override
Future<String> getPrivacyPolicy() async { Future<String> getPrivacyPolicy() async {
return _service.run<String>( return _service.run<String>(() async {
() async { try {
try { // Load from package asset path
// Load from package asset path return await rootBundle.loadString(
return await rootBundle.loadString( 'packages/staff_privacy_security/lib/src/assets/legal/privacy_policy.txt',
'packages/staff_privacy_security/lib/src/assets/legal/privacy_policy.txt', );
); } catch (e) {
} catch (e) { // Final fallback if asset not found
// Final fallback if asset not found print('Error loading privacy policy: $e');
return 'Privacy Policy - Content unavailable. Please contact privacy@krow.com'; return 'Privacy Policy - Content unavailable. Please contact privacy@krow.com';
} }
}, });
);
} }
} }

View File

@@ -1,14 +1,12 @@
import '../entities/privacy_settings_entity.dart';
/// Interface for privacy settings repository operations /// Interface for privacy settings repository operations
abstract class PrivacySettingsRepositoryInterface { abstract class PrivacySettingsRepositoryInterface {
/// Fetch the current user's privacy settings /// Fetch the current staff member's profile visibility setting
Future<PrivacySettingsEntity> getPrivacySettings(); Future<bool> getProfileVisibility();
/// Update location sharing preference /// Update profile visibility preference
/// ///
/// Returns the updated privacy settings /// Returns the updated profile visibility status
Future<PrivacySettingsEntity> updateLocationSharing(bool enabled); Future<bool> updateProfileVisibility(bool isVisible);
/// Fetch terms of service content /// Fetch terms of service content
Future<String> getTermsOfService(); Future<String> getTermsOfService();

View File

@@ -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<PrivacySettingsEntity> call() async {
try {
return await _repository.getPrivacySettings();
} catch (e) {
// Return default settings on error
return PrivacySettingsEntity(
locationSharing: true,
updatedAt: DateTime.now(),
);
}
}
}

View File

@@ -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<bool> call() async {
try {
return await _repository.getProfileVisibility();
} catch (e) {
// Return default (visible) on error
return true;
}
}
}

View File

@@ -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<Object?> 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<PrivacySettingsEntity> 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(),
);
}
}
}

View File

@@ -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<Object?> get props => <Object?>[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<bool> call(UpdateProfileVisibilityParams params) async {
try {
return await _repository.updateProfileVisibility(params.isVisible);
} catch (e) {
// Return the requested state on error (optimistic)
return params.isVisible;
}
}
}

View File

@@ -1,10 +1,8 @@
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import '../../domain/entities/privacy_settings_entity.dart'; import '../../domain/usecases/get_profile_visibility_usecase.dart';
import '../../domain/repositories/privacy_settings_repository_interface.dart'; import '../../domain/usecases/update_profile_visibility_usecase.dart';
import '../../domain/usecases/get_privacy_settings_usecase.dart';
import '../../domain/usecases/update_location_sharing_usecase.dart';
import '../../domain/usecases/get_terms_usecase.dart'; import '../../domain/usecases/get_terms_usecase.dart';
import '../../domain/usecases/get_privacy_policy_usecase.dart'; import '../../domain/usecases/get_privacy_policy_usecase.dart';
@@ -14,72 +12,75 @@ part 'privacy_security_state.dart';
/// BLoC managing privacy and security settings state /// BLoC managing privacy and security settings state
class PrivacySecurityBloc class PrivacySecurityBloc
extends Bloc<PrivacySecurityEvent, PrivacySecurityState> { extends Bloc<PrivacySecurityEvent, PrivacySecurityState> {
final GetPrivacySettingsUseCase _getPrivacySettingsUseCase; final GetProfileVisibilityUseCase _getProfileVisibilityUseCase;
final UpdateLocationSharingUseCase _updateLocationSharingUseCase; final UpdateProfileVisibilityUseCase _updateProfileVisibilityUseCase;
final GetTermsUseCase _getTermsUseCase; final GetTermsUseCase _getTermsUseCase;
final GetPrivacyPolicyUseCase _getPrivacyPolicyUseCase; final GetPrivacyPolicyUseCase _getPrivacyPolicyUseCase;
PrivacySecurityBloc({ PrivacySecurityBloc({
required GetPrivacySettingsUseCase getPrivacySettingsUseCase, required GetProfileVisibilityUseCase getProfileVisibilityUseCase,
required UpdateLocationSharingUseCase updateLocationSharingUseCase, required UpdateProfileVisibilityUseCase updateProfileVisibilityUseCase,
required GetTermsUseCase getTermsUseCase, required GetTermsUseCase getTermsUseCase,
required GetPrivacyPolicyUseCase getPrivacyPolicyUseCase, required GetPrivacyPolicyUseCase getPrivacyPolicyUseCase,
}) : _getPrivacySettingsUseCase = getPrivacySettingsUseCase, }) : _getProfileVisibilityUseCase = getProfileVisibilityUseCase,
_updateLocationSharingUseCase = updateLocationSharingUseCase, _updateProfileVisibilityUseCase = updateProfileVisibilityUseCase,
_getTermsUseCase = getTermsUseCase, _getTermsUseCase = getTermsUseCase,
_getPrivacyPolicyUseCase = getPrivacyPolicyUseCase, _getPrivacyPolicyUseCase = getPrivacyPolicyUseCase,
super(const PrivacySecurityState()) { super(const PrivacySecurityState()) {
on<FetchPrivacySettingsEvent>(_onFetchPrivacySettings); on<FetchProfileVisibilityEvent>(_onFetchProfileVisibility);
on<UpdateLocationSharingEvent>(_onUpdateLocationSharing); on<UpdateProfileVisibilityEvent>(_onUpdateProfileVisibility);
on<FetchTermsEvent>(_onFetchTerms); on<FetchTermsEvent>(_onFetchTerms);
on<FetchPrivacyPolicyEvent>(_onFetchPrivacyPolicy); on<FetchPrivacyPolicyEvent>(_onFetchPrivacyPolicy);
on<ClearProfileVisibilityUpdatedEvent>(_onClearProfileVisibilityUpdated);
} }
Future<void> _onFetchPrivacySettings( Future<void> _onFetchProfileVisibility(
FetchPrivacySettingsEvent event, FetchProfileVisibilityEvent event,
Emitter<PrivacySecurityState> emit, Emitter<PrivacySecurityState> emit,
) async { ) async {
emit(state.copyWith(isLoading: true, error: null)); emit(state.copyWith(isLoading: true, error: null));
try { try {
final settings = await _getPrivacySettingsUseCase.call(); final bool isVisible = await _getProfileVisibilityUseCase.call();
emit( emit(
state.copyWith( state.copyWith(
isLoading: false, isLoading: false,
privacySettings: settings, isProfileVisible: isVisible,
), ),
); );
} catch (e) { } catch (e) {
emit( emit(
state.copyWith( state.copyWith(
isLoading: false, isLoading: false,
error: 'Failed to fetch privacy settings', error: 'Failed to fetch profile visibility',
), ),
); );
} }
} }
Future<void> _onUpdateLocationSharing( Future<void> _onUpdateProfileVisibility(
UpdateLocationSharingEvent event, UpdateProfileVisibilityEvent event,
Emitter<PrivacySecurityState> emit, Emitter<PrivacySecurityState> emit,
) async { ) async {
emit(state.copyWith(isUpdating: true, error: null)); emit(state.copyWith(isUpdating: true, error: null, profileVisibilityUpdated: false));
try { try {
final settings = await _updateLocationSharingUseCase.call( final bool isVisible = await _updateProfileVisibilityUseCase.call(
UpdateLocationSharingParams(enabled: event.enabled), UpdateProfileVisibilityParams(isVisible: event.isVisible),
); );
emit( emit(
state.copyWith( state.copyWith(
isUpdating: false, isUpdating: false,
privacySettings: settings, isProfileVisible: isVisible,
profileVisibilityUpdated: true,
), ),
); );
} catch (e) { } catch (e) {
emit( emit(
state.copyWith( state.copyWith(
isUpdating: false, isUpdating: false,
error: 'Failed to update location sharing', error: 'Failed to update profile visibility',
profileVisibilityUpdated: false,
), ),
); );
} }
@@ -92,7 +93,7 @@ class PrivacySecurityBloc
emit(state.copyWith(isLoadingTerms: true, error: null)); emit(state.copyWith(isLoadingTerms: true, error: null));
try { try {
final content = await _getTermsUseCase.call(); final String content = await _getTermsUseCase.call();
emit( emit(
state.copyWith( state.copyWith(
isLoadingTerms: false, isLoadingTerms: false,
@@ -116,7 +117,7 @@ class PrivacySecurityBloc
emit(state.copyWith(isLoadingPrivacyPolicy: true, error: null)); emit(state.copyWith(isLoadingPrivacyPolicy: true, error: null));
try { try {
final content = await _getPrivacyPolicyUseCase.call(); final String content = await _getPrivacyPolicyUseCase.call();
emit( emit(
state.copyWith( state.copyWith(
isLoadingPrivacyPolicy: false, isLoadingPrivacyPolicy: false,
@@ -132,4 +133,11 @@ class PrivacySecurityBloc
); );
} }
} }
void _onClearProfileVisibilityUpdated(
ClearProfileVisibilityUpdatedEvent event,
Emitter<PrivacySecurityState> emit,
) {
emit(state.copyWith(profileVisibilityUpdated: false));
}
} }

View File

@@ -5,23 +5,23 @@ abstract class PrivacySecurityEvent extends Equatable {
const PrivacySecurityEvent(); const PrivacySecurityEvent();
@override @override
List<Object?> get props => []; List<Object?> get props => <Object?>[];
} }
/// Event to fetch current privacy settings /// Event to fetch current profile visibility setting
class FetchPrivacySettingsEvent extends PrivacySecurityEvent { class FetchProfileVisibilityEvent extends PrivacySecurityEvent {
const FetchPrivacySettingsEvent(); const FetchProfileVisibilityEvent();
} }
/// Event to update location sharing preference /// Event to update profile visibility
class UpdateLocationSharingEvent extends PrivacySecurityEvent { class UpdateProfileVisibilityEvent extends PrivacySecurityEvent {
/// Whether to enable or disable location sharing /// Whether to show (true) or hide (false) the profile
final bool enabled; final bool isVisible;
const UpdateLocationSharingEvent({required this.enabled}); const UpdateProfileVisibilityEvent({required this.isVisible});
@override @override
List<Object?> get props => [enabled]; List<Object?> get props => <Object?>[isVisible];
} }
/// Event to fetch terms of service /// Event to fetch terms of service
@@ -33,3 +33,8 @@ class FetchTermsEvent extends PrivacySecurityEvent {
class FetchPrivacyPolicyEvent extends PrivacySecurityEvent { class FetchPrivacyPolicyEvent extends PrivacySecurityEvent {
const FetchPrivacyPolicyEvent(); const FetchPrivacyPolicyEvent();
} }
/// Event to clear the profile visibility updated flag after showing snackbar
class ClearProfileVisibilityUpdatedEvent extends PrivacySecurityEvent {
const ClearProfileVisibilityUpdatedEvent();
}

View File

@@ -2,15 +2,18 @@ part of 'privacy_security_bloc.dart';
/// State for privacy security BLoC /// State for privacy security BLoC
class PrivacySecurityState extends Equatable { class PrivacySecurityState extends Equatable {
/// Current privacy settings /// Current profile visibility setting (true = visible, false = hidden)
final PrivacySettingsEntity? privacySettings; final bool isProfileVisible;
/// Whether settings are currently loading /// Whether profile visibility is currently loading
final bool isLoading; final bool isLoading;
/// Whether settings are currently being updated /// Whether profile visibility is currently being updated
final bool isUpdating; final bool isUpdating;
/// Whether the profile visibility was just successfully updated
final bool profileVisibilityUpdated;
/// Terms of service content /// Terms of service content
final String? termsContent; final String? termsContent;
@@ -27,9 +30,10 @@ class PrivacySecurityState extends Equatable {
final String? error; final String? error;
const PrivacySecurityState({ const PrivacySecurityState({
this.privacySettings, this.isProfileVisible = true,
this.isLoading = false, this.isLoading = false,
this.isUpdating = false, this.isUpdating = false,
this.profileVisibilityUpdated = false,
this.termsContent, this.termsContent,
this.isLoadingTerms = false, this.isLoadingTerms = false,
this.privacyPolicyContent, this.privacyPolicyContent,
@@ -39,9 +43,10 @@ class PrivacySecurityState extends Equatable {
/// Create a copy with optional field overrides /// Create a copy with optional field overrides
PrivacySecurityState copyWith({ PrivacySecurityState copyWith({
PrivacySettingsEntity? privacySettings, bool? isProfileVisible,
bool? isLoading, bool? isLoading,
bool? isUpdating, bool? isUpdating,
bool? profileVisibilityUpdated,
String? termsContent, String? termsContent,
bool? isLoadingTerms, bool? isLoadingTerms,
String? privacyPolicyContent, String? privacyPolicyContent,
@@ -49,9 +54,10 @@ class PrivacySecurityState extends Equatable {
String? error, String? error,
}) { }) {
return PrivacySecurityState( return PrivacySecurityState(
privacySettings: privacySettings ?? this.privacySettings, isProfileVisible: isProfileVisible ?? this.isProfileVisible,
isLoading: isLoading ?? this.isLoading, isLoading: isLoading ?? this.isLoading,
isUpdating: isUpdating ?? this.isUpdating, isUpdating: isUpdating ?? this.isUpdating,
profileVisibilityUpdated: profileVisibilityUpdated ?? this.profileVisibilityUpdated,
termsContent: termsContent ?? this.termsContent, termsContent: termsContent ?? this.termsContent,
isLoadingTerms: isLoadingTerms ?? this.isLoadingTerms, isLoadingTerms: isLoadingTerms ?? this.isLoadingTerms,
privacyPolicyContent: privacyPolicyContent ?? this.privacyPolicyContent, privacyPolicyContent: privacyPolicyContent ?? this.privacyPolicyContent,
@@ -62,10 +68,11 @@ class PrivacySecurityState extends Equatable {
} }
@override @override
List<Object?> get props => [ List<Object?> get props => <Object?>[
privacySettings, isProfileVisible,
isLoading, isLoading,
isUpdating, isUpdating,
profileVisibilityUpdated,
termsContent, termsContent,
isLoadingTerms, isLoadingTerms,
privacyPolicyContent, privacyPolicyContent,
@@ -73,3 +80,4 @@ class PrivacySecurityState extends Equatable {
error, error,
]; ];
} }

View File

@@ -25,7 +25,7 @@ class PrivacySecurityPage extends StatelessWidget {
), ),
body: BlocProvider<PrivacySecurityBloc>.value( body: BlocProvider<PrivacySecurityBloc>.value(
value: Modular.get<PrivacySecurityBloc>() value: Modular.get<PrivacySecurityBloc>()
..add(const FetchPrivacySettingsEvent()), ..add(const FetchProfileVisibilityEvent()),
child: BlocBuilder<PrivacySecurityBloc, PrivacySecurityState>( child: BlocBuilder<PrivacySecurityBloc, PrivacySecurityState>(
builder: (BuildContext context, PrivacySecurityState state) { builder: (BuildContext context, PrivacySecurityState state) {
if (state.isLoading) { if (state.isLoading) {

View File

@@ -23,7 +23,7 @@ class LegalSectionWidget extends StatelessWidget {
// Legal Section Header // Legal Section Header
SettingsSectionHeader( SettingsSectionHeader(
title: t.staff_privacy_security.legal_section, title: t.staff_privacy_security.legal_section,
icon: Icons.shield, icon: UiIcons.shield,
), ),
Container( Container(

View File

@@ -7,48 +7,64 @@ import '../../blocs/privacy_security_bloc.dart';
import '../settings_section_header_widget.dart'; import '../settings_section_header_widget.dart';
import '../settings_switch_tile_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 { class PrivacySectionWidget extends StatelessWidget {
const PrivacySectionWidget({super.key}); const PrivacySectionWidget({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<PrivacySecurityBloc, PrivacySecurityState>( return BlocListener<PrivacySecurityBloc, PrivacySecurityState>(
builder: (BuildContext context, PrivacySecurityState state) { listener: (BuildContext context, PrivacySecurityState state) {
return Column( // Show success message when profile visibility update just completed
children: <Widget>[ if (state.profileVisibilityUpdated && state.error == null) {
// Privacy Section Header UiSnackbar.show(
SettingsSectionHeader( context,
title: t.staff_privacy_security.privacy_section, message: t.staff_privacy_security.success.profile_visibility_updated,
icon: UiIcons.eye, type: UiSnackbarType.success,
), );
const SizedBox(height: 12.0), // Clear the flag after showing the snackbar
Container( context.read<PrivacySecurityBloc>().add(
decoration: BoxDecoration( const ClearProfileVisibilityUpdatedEvent(),
color: Colors.white, );
borderRadius: BorderRadius.circular(UiConstants.radiusBase), }
border: Border.all( },
color: UiColors.border, child: BlocBuilder<PrivacySecurityBloc, PrivacySecurityState>(
builder: (BuildContext context, PrivacySecurityState state) {
return Column(
children: <Widget>[
// 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: <Widget>[
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<PrivacySecurityBloc>(context).add(
UpdateProfileVisibilityEvent(isVisible: value),
);
},
),
],
), ),
), ),
child: Column( ],
children: <Widget>[ );
SettingsSwitchTile( },
title: t.staff_privacy_security.location_sharing.title, ),
subtitle: t.staff_privacy_security.location_sharing.subtitle,
value: state.privacySettings?.locationSharing ?? false,
onChanged: (bool value) {
BlocProvider.of<PrivacySecurityBloc>(context).add(
UpdateLocationSharingEvent(enabled: value),
);
},
),
],
),
),
],
);
},
); );
} }
} }

View File

@@ -6,9 +6,9 @@ import 'package:krow_data_connect/krow_data_connect.dart';
import 'data/repositories_impl/privacy_settings_repository_impl.dart'; import 'data/repositories_impl/privacy_settings_repository_impl.dart';
import 'domain/repositories/privacy_settings_repository_interface.dart'; import 'domain/repositories/privacy_settings_repository_interface.dart';
import 'domain/usecases/get_privacy_policy_usecase.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/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/privacy_policy_cubit.dart';
import 'presentation/blocs/legal/terms_cubit.dart'; import 'presentation/blocs/legal/terms_cubit.dart';
import 'presentation/blocs/privacy_security_bloc.dart'; import 'presentation/blocs/privacy_security_bloc.dart';
@@ -33,12 +33,12 @@ class PrivacySecurityModule extends Module {
// Use Cases // Use Cases
i.addSingleton( i.addSingleton(
() => GetPrivacySettingsUseCase( () => GetProfileVisibilityUseCase(
i<PrivacySettingsRepositoryInterface>(), i<PrivacySettingsRepositoryInterface>(),
), ),
); );
i.addSingleton( i.addSingleton(
() => UpdateLocationSharingUseCase( () => UpdateProfileVisibilityUseCase(
i<PrivacySettingsRepositoryInterface>(), i<PrivacySettingsRepositoryInterface>(),
), ),
); );
@@ -56,8 +56,8 @@ class PrivacySecurityModule extends Module {
// BLoC // BLoC
i.add( i.add(
() => PrivacySecurityBloc( () => PrivacySecurityBloc(
getPrivacySettingsUseCase: i(), getProfileVisibilityUseCase: i(),
updateLocationSharingUseCase: i(), updateProfileVisibilityUseCase: i(),
getTermsUseCase: i(), getTermsUseCase: i(),
getPrivacyPolicyUseCase: i(), getPrivacyPolicyUseCase: i(),
), ),

View File

@@ -1,7 +1,5 @@
export 'src/domain/entities/privacy_settings_entity.dart'; export 'src/domain/entities/privacy_settings_entity.dart';
export 'src/domain/repositories/privacy_settings_repository_interface.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_terms_usecase.dart';
export 'src/domain/usecases/get_privacy_policy_usecase.dart'; export 'src/domain/usecases/get_privacy_policy_usecase.dart';
export 'src/data/repositories_impl/privacy_settings_repository_impl.dart'; export 'src/data/repositories_impl/privacy_settings_repository_impl.dart';

View File

@@ -15,6 +15,8 @@ class ShiftsRepositoryImpl
// Cache: ApplicationID -> RoleID (For Accept/Decline w/ Update mutation) // Cache: ApplicationID -> RoleID (For Accept/Decline w/ Update mutation)
final Map<String, String> _appToRoleIdMap = {}; final Map<String, String> _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 @override
Future<List<Shift>> getMyShifts({ Future<List<Shift>> getMyShifts({
required DateTime start, required DateTime start,

View File

@@ -214,3 +214,12 @@ mutation UpdateStaff(
mutation DeleteStaff($id: UUID!) @auth(level: USER) { mutation DeleteStaff($id: UUID!) @auth(level: USER) {
staff_delete(id: $id) staff_delete(id: $id)
} }
mutation UpdateStaffProfileVisibility($id: UUID!, $isProfileVisible: Boolean!) @auth(level: USER) {
staff_update(
id: $id
data: {
isProfileVisible: $isProfileVisible
}
)
}

View File

@@ -204,3 +204,10 @@ query filterStaff(
zipCode zipCode
} }
} }
query getStaffProfileVisibility($staffId: UUID!) @auth(level: USER) {
staff(id: $staffId) {
id
isProfileVisible
}
}