Add staff privacy & security feature and routes

This commit is contained in:
Achintha Isuru
2026-02-18 13:40:49 -05:00
parent f5a23c3aaa
commit 96849baf46
24 changed files with 1010 additions and 5 deletions

View File

@@ -284,13 +284,24 @@ extension StaffNavigator on IModularNavigator {
pushNamed(StaffPaths.faqs);
}
/// Pushes the privacy and security settings page.
// ==========================================================================
// PRIVACY & SECURITY
// ==========================================================================
/// Navigates to the privacy and security settings page.
///
/// Manage privacy preferences and security settings.
void toPrivacy() {
pushNamed(StaffPaths.privacy);
/// Manage privacy preferences including:
/// * Location sharing settings
/// * View terms of service
/// * View privacy policy
void toPrivacySecurity() {
pushNamed(StaffPaths.privacySecurity);
}
// ==========================================================================
// MESSAGING & COMMUNICATION
// ==========================================================================
/// Pushes the messages page (placeholder).
///
/// Access internal messaging system.

View File

@@ -205,8 +205,19 @@ class StaffPaths {
/// FAQs - frequently asked questions.
static const String faqs = '/faqs';
// ==========================================================================
// PRIVACY & SECURITY
// ==========================================================================
/// Privacy and security settings.
static const String privacy = '/privacy';
///
/// Manage privacy preferences, location sharing, terms of service,
/// and privacy policy.
static const String privacySecurity = '/worker-main/privacy-security/';
// ==========================================================================
// MESSAGING & COMMUNICATION (Placeholders)
// ==========================================================================
/// Messages - internal messaging system (placeholder).
static const String messages = '/messages';

View File

@@ -1125,6 +1125,21 @@
"service_unavailable": "Service is currently unavailable."
}
},
"staff_privacy_security": {
"title": "Privacy & Security",
"privacy_section": "Privacy",
"legal_section": "Legal",
"location_sharing": {
"title": "Location Sharing",
"subtitle": "Share location during shifts"
},
"terms_of_service": {
"title": "Terms of Service"
},
"privacy_policy": {
"title": "Privacy Policy"
}
},
"success": {
"hub": {
"created": "Hub created successfully!",

View File

@@ -1125,6 +1125,21 @@
"service_unavailable": "El servicio no está disponible actualmente."
}
},
"staff_privacy_security": {
"title": "Privacidad y Seguridad",
"privacy_section": "Privacidad",
"legal_section": "Legal",
"location_sharing": {
"title": "Compartir Ubicación",
"subtitle": "Compartir ubicación durante turnos"
},
"terms_of_service": {
"title": "Términos de Servicio"
},
"privacy_policy": {
"title": "Política de Privacidad"
}
},
"success": {
"hub": {
"created": "¡Hub creado exitosamente!",

View File

@@ -0,0 +1,65 @@
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
class PrivacySettingsRepositoryImpl
implements PrivacySettingsRepositoryInterface {
final DataConnectService _service;
PrivacySettingsRepositoryImpl(this._service);
@override
Future<PrivacySettingsEntity> getPrivacySettings() async {
return _service.run<PrivacySettingsEntity>(
() async {
// TODO: Call Data Connect query to fetch privacy settings
// For now, return default settings
return PrivacySettingsEntity(
locationSharing: true,
updatedAt: DateTime.now(),
);
},
);
}
@override
Future<PrivacySettingsEntity> updateLocationSharing(bool enabled) async {
return _service.run<PrivacySettingsEntity>(
() async {
// TODO: Call Data Connect mutation to update location sharing preference
// For now, return updated settings
return PrivacySettingsEntity(
locationSharing: enabled,
updatedAt: DateTime.now(),
);
},
);
}
@override
Future<String> getTermsOfService() async {
return _service.run<String>(
() async {
// TODO: Call Data Connect query to fetch terms of service content
// For now, return placeholder
return 'Terms of Service Content';
},
);
}
@override
Future<String> getPrivacyPolicy() async {
return _service.run<String>(
() async {
// TODO: Call Data Connect query to fetch privacy policy content
// For now, return placeholder
return 'Privacy Policy Content';
},
);
}
}

View File

@@ -0,0 +1,29 @@
import 'package:equatable/equatable.dart';
/// Privacy settings entity representing user privacy preferences
class PrivacySettingsEntity extends Equatable {
/// Whether location sharing during shifts is enabled
final bool locationSharing;
/// The timestamp when these settings were last updated
final DateTime? updatedAt;
const PrivacySettingsEntity({
required this.locationSharing,
this.updatedAt,
});
/// Create a copy with optional field overrides
PrivacySettingsEntity copyWith({
bool? locationSharing,
DateTime? updatedAt,
}) {
return PrivacySettingsEntity(
locationSharing: locationSharing ?? this.locationSharing,
updatedAt: updatedAt ?? this.updatedAt,
);
}
@override
List<Object?> get props => [locationSharing, updatedAt];
}

View File

@@ -0,0 +1,18 @@
import '../entities/privacy_settings_entity.dart';
/// Interface for privacy settings repository operations
abstract class PrivacySettingsRepositoryInterface {
/// Fetch the current user's privacy settings
Future<PrivacySettingsEntity> getPrivacySettings();
/// Update location sharing preference
///
/// Returns the updated privacy settings
Future<PrivacySettingsEntity> updateLocationSharing(bool enabled);
/// Fetch terms of service content
Future<String> getTermsOfService();
/// Fetch privacy policy content
Future<String> getPrivacyPolicy();
}

View File

@@ -0,0 +1,17 @@
import '../repositories/privacy_settings_repository_interface.dart';
/// Use case to retrieve privacy policy
class GetPrivacyPolicyUseCase {
final PrivacySettingsRepositoryInterface _repository;
GetPrivacyPolicyUseCase(this._repository);
/// Execute the use case to get privacy policy
Future<String> call() async {
try {
return await _repository.getPrivacyPolicy();
} catch (e) {
return 'Privacy Policy is currently unavailable.';
}
}
}

View File

@@ -0,0 +1,22 @@
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,17 @@
import '../repositories/privacy_settings_repository_interface.dart';
/// Use case to retrieve terms of service
class GetTermsUseCase {
final PrivacySettingsRepositoryInterface _repository;
GetTermsUseCase(this._repository);
/// Execute the use case to get terms of service
Future<String> call() async {
try {
return await _repository.getTermsOfService();
} catch (e) {
return 'Terms of Service is currently unavailable.';
}
}
}

View File

@@ -0,0 +1,35 @@
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,135 @@
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_terms_usecase.dart';
import '../../domain/usecases/get_privacy_policy_usecase.dart';
part 'privacy_security_event.dart';
part 'privacy_security_state.dart';
/// BLoC managing privacy and security settings state
class PrivacySecurityBloc
extends Bloc<PrivacySecurityEvent, PrivacySecurityState> {
final GetPrivacySettingsUseCase _getPrivacySettingsUseCase;
final UpdateLocationSharingUseCase _updateLocationSharingUseCase;
final GetTermsUseCase _getTermsUseCase;
final GetPrivacyPolicyUseCase _getPrivacyPolicyUseCase;
PrivacySecurityBloc({
required GetPrivacySettingsUseCase getPrivacySettingsUseCase,
required UpdateLocationSharingUseCase updateLocationSharingUseCase,
required GetTermsUseCase getTermsUseCase,
required GetPrivacyPolicyUseCase getPrivacyPolicyUseCase,
}) : _getPrivacySettingsUseCase = getPrivacySettingsUseCase,
_updateLocationSharingUseCase = updateLocationSharingUseCase,
_getTermsUseCase = getTermsUseCase,
_getPrivacyPolicyUseCase = getPrivacyPolicyUseCase,
super(const PrivacySecurityState()) {
on<FetchPrivacySettingsEvent>(_onFetchPrivacySettings);
on<UpdateLocationSharingEvent>(_onUpdateLocationSharing);
on<FetchTermsEvent>(_onFetchTerms);
on<FetchPrivacyPolicyEvent>(_onFetchPrivacyPolicy);
}
Future<void> _onFetchPrivacySettings(
FetchPrivacySettingsEvent event,
Emitter<PrivacySecurityState> emit,
) async {
emit(state.copyWith(isLoading: true, error: null));
try {
final settings = await _getPrivacySettingsUseCase.call();
emit(
state.copyWith(
isLoading: false,
privacySettings: settings,
),
);
} catch (e) {
emit(
state.copyWith(
isLoading: false,
error: 'Failed to fetch privacy settings',
),
);
}
}
Future<void> _onUpdateLocationSharing(
UpdateLocationSharingEvent event,
Emitter<PrivacySecurityState> emit,
) async {
emit(state.copyWith(isUpdating: true, error: null));
try {
final settings = await _updateLocationSharingUseCase.call(
UpdateLocationSharingParams(enabled: event.enabled),
);
emit(
state.copyWith(
isUpdating: false,
privacySettings: settings,
),
);
} catch (e) {
emit(
state.copyWith(
isUpdating: false,
error: 'Failed to update location sharing',
),
);
}
}
Future<void> _onFetchTerms(
FetchTermsEvent event,
Emitter<PrivacySecurityState> emit,
) async {
emit(state.copyWith(isLoadingTerms: true, error: null));
try {
final content = await _getTermsUseCase.call();
emit(
state.copyWith(
isLoadingTerms: false,
termsContent: content,
),
);
} catch (e) {
emit(
state.copyWith(
isLoadingTerms: false,
error: 'Failed to fetch terms of service',
),
);
}
}
Future<void> _onFetchPrivacyPolicy(
FetchPrivacyPolicyEvent event,
Emitter<PrivacySecurityState> emit,
) async {
emit(state.copyWith(isLoadingPrivacyPolicy: true, error: null));
try {
final content = await _getPrivacyPolicyUseCase.call();
emit(
state.copyWith(
isLoadingPrivacyPolicy: false,
privacyPolicyContent: content,
),
);
} catch (e) {
emit(
state.copyWith(
isLoadingPrivacyPolicy: false,
error: 'Failed to fetch privacy policy',
),
);
}
}
}

View File

@@ -0,0 +1,35 @@
part of 'privacy_security_bloc.dart';
/// Base class for privacy security BLoC events
abstract class PrivacySecurityEvent extends Equatable {
const PrivacySecurityEvent();
@override
List<Object?> get props => [];
}
/// Event to fetch current privacy settings
class FetchPrivacySettingsEvent extends PrivacySecurityEvent {
const FetchPrivacySettingsEvent();
}
/// Event to update location sharing preference
class UpdateLocationSharingEvent extends PrivacySecurityEvent {
/// Whether to enable or disable location sharing
final bool enabled;
const UpdateLocationSharingEvent({required this.enabled});
@override
List<Object?> get props => [enabled];
}
/// Event to fetch terms of service
class FetchTermsEvent extends PrivacySecurityEvent {
const FetchTermsEvent();
}
/// Event to fetch privacy policy
class FetchPrivacyPolicyEvent extends PrivacySecurityEvent {
const FetchPrivacyPolicyEvent();
}

View File

@@ -0,0 +1,75 @@
part of 'privacy_security_bloc.dart';
/// State for privacy security BLoC
class PrivacySecurityState extends Equatable {
/// Current privacy settings
final PrivacySettingsEntity? privacySettings;
/// Whether settings are currently loading
final bool isLoading;
/// Whether settings are currently being updated
final bool isUpdating;
/// Terms of service content
final String? termsContent;
/// Whether terms are currently loading
final bool isLoadingTerms;
/// Privacy policy content
final String? privacyPolicyContent;
/// Whether privacy policy is currently loading
final bool isLoadingPrivacyPolicy;
/// Error message, if any
final String? error;
const PrivacySecurityState({
this.privacySettings,
this.isLoading = false,
this.isUpdating = false,
this.termsContent,
this.isLoadingTerms = false,
this.privacyPolicyContent,
this.isLoadingPrivacyPolicy = false,
this.error,
});
/// Create a copy with optional field overrides
PrivacySecurityState copyWith({
PrivacySettingsEntity? privacySettings,
bool? isLoading,
bool? isUpdating,
String? termsContent,
bool? isLoadingTerms,
String? privacyPolicyContent,
bool? isLoadingPrivacyPolicy,
String? error,
}) {
return PrivacySecurityState(
privacySettings: privacySettings ?? this.privacySettings,
isLoading: isLoading ?? this.isLoading,
isUpdating: isUpdating ?? this.isUpdating,
termsContent: termsContent ?? this.termsContent,
isLoadingTerms: isLoadingTerms ?? this.isLoadingTerms,
privacyPolicyContent: privacyPolicyContent ?? this.privacyPolicyContent,
isLoadingPrivacyPolicy:
isLoadingPrivacyPolicy ?? this.isLoadingPrivacyPolicy,
error: error,
);
}
@override
List<Object?> get props => [
privacySettings,
isLoading,
isUpdating,
termsContent,
isLoadingTerms,
privacyPolicyContent,
isLoadingPrivacyPolicy,
error,
];
}

View File

@@ -0,0 +1,9 @@
import 'package:flutter_modular/flutter_modular.dart';
/// Extension on IModularNavigator for privacy security navigation
extension PrivacySecurityNavigator on IModularNavigator {
/// Navigate to privacy security page
Future<dynamic> toPrivacySecurityPage() {
return pushNamed('/privacy-security');
}
}

View File

@@ -0,0 +1,5 @@
/// Navigation route paths for privacy security feature
class PrivacySecurityPaths {
/// Route to privacy security main page
static const String privacySecurity = '/privacy-security';
}

View File

@@ -0,0 +1,190 @@
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';
import '../blocs/privacy_security_bloc.dart';
import '../widgets/settings_action_tile_widget.dart';
import '../widgets/settings_divider_widget.dart';
import '../widgets/settings_section_header_widget.dart';
import '../widgets/settings_switch_tile_widget.dart';
/// Page displaying privacy & security settings for staff
class PrivacySecurityPage extends StatelessWidget {
const PrivacySecurityPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: UiAppBar(
title: t.staff_privacy_security.title,
showBackButton: true,
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1),
child: Container(color: UiColors.border, height: 1),
),
),
body: BlocProvider<PrivacySecurityBloc>.value(
value: Modular.get<PrivacySecurityBloc>()
..add(const FetchPrivacySettingsEvent()),
child: BlocBuilder<PrivacySecurityBloc, PrivacySecurityState>(
builder: (BuildContext context, PrivacySecurityState state) {
if (state.isLoading) {
return const UiLoadingPage();
}
return SingleChildScrollView(
padding: const EdgeInsets.all(UiConstants.space6),
child: Column(
children: <Widget>[
// Privacy Section
SettingsSectionHeader(
title: t.staff_privacy_security.privacy_section,
icon: Icons.visibility,
),
const SizedBox(height: 12.0),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.0),
border: Border.all(
color: UiColors.border,
),
),
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),
);
},
),
],
),
),
const SizedBox(height: 24.0),
// Legal Section
SettingsSectionHeader(
title: t.staff_privacy_security.legal_section,
icon: Icons.shield,
),
const SizedBox(height: 12.0),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.0),
border: Border.all(
color: UiColors.border,
),
),
child: Column(
children: <Widget>[
SettingsActionTile(
title:
t.staff_privacy_security.terms_of_service.title,
onTap: () => _showTermsDialog(context),
),
const SettingsDivider(),
SettingsActionTile(
title: t.staff_privacy_security.privacy_policy.title,
onTap: () => _showPrivacyPolicyDialog(context),
),
],
),
),
const SizedBox(height: 24.0),
],
),
);
},
),
),
);
}
/// Show terms of service in a modal dialog
void _showTermsDialog(BuildContext context) {
BlocProvider.of<PrivacySecurityBloc>(context)
.add(const FetchTermsEvent());
showDialog(
context: context,
builder: (BuildContext dialogContext) =>
BlocBuilder<PrivacySecurityBloc, PrivacySecurityState>(
builder: (BuildContext context, PrivacySecurityState state) {
return AlertDialog(
title: Text(
t.staff_privacy_security.terms_of_service.title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
content: SingleChildScrollView(
child: Text(
state.termsContent ?? 'Loading...',
style: const TextStyle(fontSize: 14),
),
),
actions: <Widget>[
TextButton(
onPressed: () => Modular.to.pop(),
child: Text(t.common.ok),
),
],
);
},
),
);
}
/// Show privacy policy in a modal dialog
void _showPrivacyPolicyDialog(BuildContext context) {
BlocProvider.of<PrivacySecurityBloc>(context)
.add(const FetchPrivacyPolicyEvent());
showDialog(
context: context,
builder: (BuildContext dialogContext) =>
BlocBuilder<PrivacySecurityBloc, PrivacySecurityState>(
builder: (BuildContext context, PrivacySecurityState state) {
return AlertDialog(
title: Text(
t.staff_privacy_security.privacy_policy.title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
content: SingleChildScrollView(
child: Text(
state.privacyPolicyContent ?? 'Loading...',
style: const TextStyle(fontSize: 14),
),
),
actions: <Widget>[
TextButton(
onPressed: () => Modular.to.pop(),
child: Text(t.common.ok),
),
],
);
},
),
);
}
}

View File

@@ -0,0 +1,63 @@
import 'package:flutter/material.dart';
import 'package:design_system/design_system.dart';
/// Reusable widget for action tile (tap to navigate)
class SettingsActionTile extends StatelessWidget {
/// The title of the action
final String title;
/// Optional subtitle describing the action
final String? subtitle;
/// Callback when tile is tapped
final VoidCallback onTap;
const SettingsActionTile({
Key? key,
required this.title,
this.subtitle,
required this.onTap,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Padding(
padding: EdgeInsets.all(UiConstants.space4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
title,
style: UiTypography.body2r.copyWith(
fontWeight: FontWeight.w500,
),
),
if (subtitle != null) ...<Widget>[
SizedBox(height: UiConstants.space1),
Text(
subtitle!,
style: UiTypography.footnote1r.copyWith(
color: UiColors.muted,
),
),
],
],
),
),
Icon(
Icons.chevron_right,
size: 20,
color: UiColors.muted,
),
],
),
),
);
}
}

View File

@@ -0,0 +1,15 @@
import 'package:flutter/material.dart';
import 'package:design_system/design_system.dart';
/// Divider widget for separating items within settings sections
class SettingsDivider extends StatelessWidget {
const SettingsDivider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Divider(
height: 1,
color: UiColors.border,
);
}
}

View File

@@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:design_system/design_system.dart';
/// Reusable widget for settings section header with icon
class SettingsSectionHeader extends StatelessWidget {
/// The title of the section
final String title;
/// The icon to display next to the title
final IconData icon;
const SettingsSectionHeader({
Key? key,
required this.title,
required this.icon,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Icon(
icon,
size: 20,
color: UiColors.primary,
),
SizedBox(width: UiConstants.space2),
Text(
title,
style: UiTypography.body1r.copyWith(
fontWeight: FontWeight.w600,
),
),
],
);
}
}

View File

@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import 'package:design_system/design_system.dart';
/// Reusable widget for toggle tile in privacy settings
class SettingsSwitchTile extends StatelessWidget {
/// The title of the setting
final String title;
/// The subtitle describing the setting
final String subtitle;
/// Current toggle value
final bool value;
/// Callback when toggle is changed
final ValueChanged<bool> onChanged;
const SettingsSwitchTile({
Key? key,
required this.title,
required this.subtitle,
required this.value,
required this.onChanged,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(UiConstants.space4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
title,
style: UiTypography.body2r.copyWith(
fontWeight: FontWeight.w500,
),
),
SizedBox(height: UiConstants.space1),
Text(
subtitle,
style: UiTypography.footnote1r.copyWith(
color: UiColors.muted,
),
),
],
),
),
Switch(
value: value,
onChanged: onChanged,
activeColor: UiColors.primary,
),
],
),
);
}
}

View File

@@ -0,0 +1,14 @@
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';
export 'src/presentation/blocs/privacy_security_bloc.dart';
export 'src/presentation/pages/privacy_security_page.dart';
export 'src/presentation/widgets/settings_switch_tile_widget.dart';
export 'src/presentation/widgets/settings_action_tile_widget.dart';
export 'src/presentation/widgets/settings_section_header_widget.dart';
export 'src/presentation/widgets/settings_divider_widget.dart';
export 'staff_privacy_security_module.dart';

View File

@@ -0,0 +1,70 @@
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_data_connect/krow_data_connect.dart';
import 'src/data/repositories_impl/privacy_settings_repository_impl.dart';
import 'src/domain/repositories/privacy_settings_repository_interface.dart';
import 'src/domain/usecases/get_privacy_policy_usecase.dart';
import 'src/domain/usecases/get_privacy_settings_usecase.dart';
import 'src/domain/usecases/get_terms_usecase.dart';
import 'src/domain/usecases/update_location_sharing_usecase.dart';
import 'src/presentation/blocs/privacy_security_bloc.dart';
import 'src/presentation/pages/privacy_security_page.dart';
/// Module for privacy security feature
///
/// Provides:
/// - Dependency injection for repositories, use cases, and BLoCs
/// - Route definitions delegated to core routing
class PrivacySecurityModule extends Module {
@override
void binds(i) {
// Repository
i.addSingleton<PrivacySettingsRepositoryInterface>(
() => PrivacySettingsRepositoryImpl(
Modular.get<DataConnectService>(),
),
);
// Use Cases
i.addSingleton(
() => GetPrivacySettingsUseCase(
i<PrivacySettingsRepositoryInterface>(),
),
);
i.addSingleton(
() => UpdateLocationSharingUseCase(
i<PrivacySettingsRepositoryInterface>(),
),
);
i.addSingleton(
() => GetTermsUseCase(
i<PrivacySettingsRepositoryInterface>(),
),
);
i.addSingleton(
() => GetPrivacyPolicyUseCase(
i<PrivacySettingsRepositoryInterface>(),
),
);
// BLoC
i.addSingleton(
() => PrivacySecurityBloc(
getPrivacySettingsUseCase: i(),
updateLocationSharingUseCase: i(),
getTermsUseCase: i(),
getPrivacyPolicyUseCase: i(),
),
);
}
@override
@override
void routes(r) {
// Route is handled by core routing (StaffPaths.privacySecurity)
r.child(
'/',
child: (context) => const PrivacySecurityPage(),
);
}
}

View File

@@ -0,0 +1,40 @@
name: staff_privacy_security
description: Privacy & Security settings feature for staff application.
version: 0.0.1
publish_to: none
resolution: workspace
environment:
sdk: '>=3.10.0 <4.0.0'
flutter: ">=3.0.0"
dependencies:
flutter:
sdk: flutter
flutter_bloc: ^8.1.0
flutter_modular: ^6.3.0
equatable: ^2.0.5
firebase_data_connect: ^0.2.2+1
url_launcher: ^6.2.0
# Architecture Packages
krow_domain:
path: ../../../../../domain
krow_data_connect:
path: ../../../../../data_connect
krow_core:
path: ../../../../../core
design_system:
path: ../../../../../design_system
core_localization:
path: ../../../../../core_localization
dev_dependencies:
flutter_test:
sdk: flutter
bloc_test: ^9.1.0
mocktail: ^1.0.0
flutter:
uses-material-design: true