diff --git a/lib/components/feedback_dialog.dart b/lib/components/feedback_dialog.dart index d58fe0e..5c33590 100644 --- a/lib/components/feedback_dialog.dart +++ b/lib/components/feedback_dialog.dart @@ -75,10 +75,11 @@ class _FeedbackDialogState extends State { _firestore.collection(Constants.dbCollectionFeedbacks); // Write the server's timestamp and the user's feedback - await collection.doc().set({ + await collection.add({ 'timestamp': FieldValue.serverTimestamp(), 'feedback': _feedbackController.text, 'user': _authService.getCurrentUser()!.uid, + 'email': _authService.getCurrentUser()!.email, }); message = 'Feedback sent successfully. Thank you!'; diff --git a/lib/enumerations.dart b/lib/enumerations.dart index 2e5b5c6..a64b1e0 100644 --- a/lib/enumerations.dart +++ b/lib/enumerations.dart @@ -88,6 +88,18 @@ enum AvailabilityOption { return 'Full time (40 hours or more)'; } } + + /// @returns [AvailabilityOption.lessThan10Hours] in case [str] is null or empty + static AvailabilityOption fromString(String? str) { + if (str == null || str.isEmpty) { + return AvailabilityOption.lessThan10Hours; // return a default value + } + return AvailabilityOption.values.firstWhere( + (x) => x.toString() == str, + orElse: () => + throw ArgumentError('Invalid AvailabilityOption enum value: $str'), + ); + } } enum WorkValueOption { @@ -128,6 +140,18 @@ enum CultureOption { return 'Traditional and structured'; } } + + /// @returns [CultureOption.traditional] in case [str] is null or empty + static CultureOption fromString(String? str) { + if (str == null || str.isEmpty) { + return CultureOption.traditional; // return a default value + } + return CultureOption.values.firstWhere( + (x) => x.toString() == str, + orElse: () => + throw ArgumentError('Invalid CultureOption enum value: $str'), + ); + } } enum CommunicationPreference { @@ -148,6 +172,18 @@ enum CommunicationPreference { return 'Formal reports and documentation'; } } + + /// @returns [CommunicationPreference.daily] in case [str] is null or empty + static CommunicationPreference fromString(String? str) { + if (str == null || str.isEmpty) { + return CommunicationPreference.daily; // return a default value + } + return CommunicationPreference.values.firstWhere( + (x) => x.toString() == str, + orElse: () => throw ArgumentError( + 'Invalid CommunicationPreference enum value: $str'), + ); + } } enum RiskTolerance { @@ -171,4 +207,16 @@ enum RiskTolerance { return 'Proactively taking risks to maximize rewards'; } } + + /// @returns [RiskTolerance.balanced] in case [str] is null or empty + static RiskTolerance fromString(String? str) { + if (str == null || str.isEmpty) { + return RiskTolerance.balanced; // return a default value != null + } + return RiskTolerance.values.firstWhere( + (x) => x.toString() == str, + orElse: () => + throw ArgumentError('Invalid RiskTolerance enum value: $str'), + ); + } } diff --git a/lib/forms/corporate_culture_form.dart b/lib/forms/corporate_culture_form.dart index 99b9aac..8828440 100644 --- a/lib/forms/corporate_culture_form.dart +++ b/lib/forms/corporate_culture_form.dart @@ -129,8 +129,10 @@ class CultureValuesFormPageState extends State { // // set following registration page HERE // - builder: (context) => - RisksFormPage(isRegProcess: widget.isRegProcess), + builder: (context) => RisksFormPage( + isRegProcess: widget.isRegProcess, + isEditMode: false, + ), ), ); } else { diff --git a/lib/forms/risks_form.dart b/lib/forms/risks_form.dart index abd8b21..df1234b 100644 --- a/lib/forms/risks_form.dart +++ b/lib/forms/risks_form.dart @@ -7,9 +7,11 @@ import '../pages/registration_complete_page.dart'; import '../services/auth/auth_service.dart'; class RisksFormPage extends StatefulWidget { - const RisksFormPage({super.key, required this.isRegProcess}); + const RisksFormPage( + {super.key, required this.isRegProcess, required this.isEditMode}); final bool isRegProcess; + final bool isEditMode; @override RisksFormPageState createState() => RisksFormPageState(); @@ -17,7 +19,7 @@ class RisksFormPage extends StatefulWidget { class RisksFormPageState extends State { CommunicationPreference? communicationPreference; - RiskTolerance? riskTolerance; + RiskTolerance? riskPreference; final AuthService _authService = AuthService(); @@ -45,7 +47,7 @@ class RisksFormPageState extends State { } // Load Risk Tolerance if (data[Constants.dbFieldUsersRiskTolerance] != null) { - riskTolerance = RiskTolerance.values.firstWhereOrNull((x) => + riskPreference = RiskTolerance.values.firstWhereOrNull((x) => x.toString() == data[Constants.dbFieldUsersRiskTolerance]); } } @@ -53,45 +55,77 @@ class RisksFormPageState extends State { } } - Future _saveDataToFirebase() async { - final userDoc = FirebaseFirestore.instance - .collection(Constants.dbCollectionUsers) - .doc(_authService.getCurrentUser()!.uid); + Future _saveDataToFirebase() async { + try { + final userDoc = FirebaseFirestore.instance + .collection(Constants.dbCollectionUsers) + .doc(_authService.getCurrentUser()!.uid); - await userDoc.set( - { - Constants.dbFieldUsersCommunication: - communicationPreference?.toString(), - Constants.dbFieldUsersRiskTolerance: riskTolerance?.toString(), - }, - SetOptions(merge: true), // avoid overwriting existing data - ); + await userDoc.set( + { + Constants.dbFieldUsersCommunication: + communicationPreference?.toString(), + Constants.dbFieldUsersRiskTolerance: riskPreference?.toString(), + }, + SetOptions(merge: true), // avoid overwriting existing data + ); + return true; + } catch (e) { + _showSnackBar(e.toString()); + return false; + } } - void handleSubmit() { + void _showSnackBar(String message) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text(message), + )); + } + + Future handleSubmit() async { if (communicationPreference == null) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Please select a communication preference.'), )); return; } - if (riskTolerance == null) { + if (riskPreference == null) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Please select the willingness to take risks.'), )); return; } - _saveDataToFirebase(); // Handle the form submission logic here - Navigator.push( - context, - MaterialPageRoute( - // - // TODO set following registration page HERE - // - builder: (context) => const RegistrationCompletePage(), - ), - ); + bool success = await _saveDataToFirebase(); + if (success) { + if (widget.isRegProcess) { + Navigator.push( + context, + MaterialPageRoute( + // + // TODO set following registration page HERE + // + builder: (context) => const RegistrationCompletePage(), + ), + ); + } else { + if (widget.isEditMode == true) { + // pass selectedOptions data back to caller + Navigator.pop(context, { + Constants.dbFieldUsersCommunication: communicationPreference, + Constants.dbFieldUsersRiskTolerance: riskPreference, + }); + } else { + Navigator.pop(context); + } + } + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Failed to save data.'), + ), + ); + } } @override @@ -133,10 +167,10 @@ class RisksFormPageState extends State { return RadioListTile( title: Text(option.displayName), value: option, - groupValue: riskTolerance, + groupValue: riskPreference, onChanged: (RiskTolerance? value) { setState(() { - riskTolerance = value; + riskPreference = value; }); }, ); diff --git a/lib/models/user_profile.dart b/lib/models/user_profile.dart index cfc2959..d283ee5 100644 --- a/lib/models/user_profile.dart +++ b/lib/models/user_profile.dart @@ -20,6 +20,7 @@ class UserProfile { RiskTolerance risk; AvailabilityOption availability; CultureOption culture; + CommunicationPreference communication; List skills; List skillsSought; List visions; @@ -41,6 +42,7 @@ class UserProfile { required this.risk, required this.availability, required this.culture, + required this.communication, required this.skills, required this.skillsSought, required this.visions, @@ -66,6 +68,8 @@ class UserProfile { AvailabilityOption.fromString(data[Constants.dbFieldUsersAvailability]); CultureOption culture = CultureOption.fromString(data[Constants.dbFieldUsersCorpCulture]); + CommunicationPreference communication = CommunicationPreference.fromString( + data[Constants.dbFieldUsersCommunication]); return UserProfile( id: doc.id, @@ -80,6 +84,7 @@ class UserProfile { risk: risk, availability: availability, culture: culture, + communication: communication, workValues: works, profilePictureUrl: data[Constants.dbFieldUsersProfilePic], bio: data[Constants.dbFieldUsersBio], diff --git a/lib/pages/user_profile_page.dart b/lib/pages/user_profile_page.dart index dd87e0e..f5b0b5a 100644 --- a/lib/pages/user_profile_page.dart +++ b/lib/pages/user_profile_page.dart @@ -4,6 +4,7 @@ import 'package:firebase_auth/firebase_auth.dart'; import '../constants.dart'; import '../enumerations.dart'; import '../forms/corporate_culture_form.dart'; +import '../forms/risks_form.dart'; import '../forms/skills_form.dart'; import '../models/user_profile.dart'; import '../services/user_service.dart'; @@ -167,6 +168,27 @@ class _UserProfilePageState extends State { } } + void editUserCommunicationInfo() async { + final updatedUserData = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const RisksFormPage( + isRegProcess: false, + isEditMode: true, + ), + ), + ); + + if (updatedUserData != null) { + setState(() { + // above Type of updatedUserData is dynamic, so convert + myData.risk = updatedUserData[Constants.dbFieldUsersRiskTolerance]; + myData.communication = + updatedUserData[Constants.dbFieldUsersCommunication]; + }); + } + } + @override Widget build(BuildContext context) { return Scaffold( @@ -200,6 +222,10 @@ class _UserProfilePageState extends State { const SizedBox(height: 16), Divider(color: Theme.of(context).colorScheme.primary), const SizedBox(height: 16), + _buildRisks(context), + const SizedBox(height: 16), + Divider(color: Theme.of(context).colorScheme.primary), + const SizedBox(height: 16), ], ), ), @@ -207,6 +233,73 @@ class _UserProfilePageState extends State { ); } + Widget _buildRisks(BuildContext context) { + return Column( + children: [ + Align( + alignment: Alignment.centerLeft, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Communication profile', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + Text(myData.communication.displayName, + style: const TextStyle(fontSize: 16)), + ], + ), + ), + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: Align( + alignment: Alignment.topRight, + child: OutlinedButton.icon( + label: const Text('Edit'), + icon: const Icon(Icons.edit), + onPressed: editUserCommunicationInfo, + ), + ), + ), + ], + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Risk profile', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + Text(myData.risk.displayName, + style: const TextStyle(fontSize: 16)), + ], + ), + ), + ], + ), + ], + ), + ), + ], + ); + } + Widget _buildWorkCulture(BuildContext context) { return Column( children: [