diff --git a/lib/constants.dart b/lib/constants.dart index c196117..48e5ff9 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -18,6 +18,8 @@ class Constants { static const String dbFieldUsersYearBorn = 'born'; static const String dbFieldUsersSkills = 'skills'; static const String dbFieldUsersSkillsSought = 'skills_sought'; + static const String dbFieldUsersAvailability = 'availability'; + static const String dbFieldUsersVisions = 'visions'; static const String pathLanguagesJson = 'lib/assets/languages.json'; } diff --git a/lib/enumerations.dart b/lib/enumerations.dart index 7345fd3..ea1f6be 100644 --- a/lib/enumerations.dart +++ b/lib/enumerations.dart @@ -36,3 +36,43 @@ enum SkillOption { } } } + +enum VisionOption { + marketLeader, + sustainableBusiness, + innovativeProduct, + exitStrategy; + + String get displayName { + switch (this) { + case VisionOption.marketLeader: + return 'Become market leader'; + case VisionOption.sustainableBusiness: + return 'Build a sustainable and ethical business'; + case VisionOption.innovativeProduct: + return "Develop an innovative product that improves people's lives"; + case VisionOption.exitStrategy: + return 'Pursue an exit strategy through sale or IPO'; + } + } +} + +enum AvailabilityOption { + lessThan10Hours, + tenTo20Hours, + twentyTo40Hours, + fullTime; + + String get displayName { + switch (this) { + case AvailabilityOption.lessThan10Hours: + return 'Less than 10 hours'; + case AvailabilityOption.tenTo20Hours: + return 'Part time (10 - 20 hours)'; + case AvailabilityOption.twentyTo40Hours: + return 'Part time (20 - 40 hours)'; + case AvailabilityOption.fullTime: + return 'Full time (40 hours or more)'; + } + } +} diff --git a/lib/forms/profile_category_form.dart b/lib/forms/profile_category_form.dart index c0cfc8a..3b88d5a 100644 --- a/lib/forms/profile_category_form.dart +++ b/lib/forms/profile_category_form.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../helper.dart'; class ProfileCategoryForm extends StatefulWidget { final String title; @@ -66,7 +67,7 @@ class ProfileCategoryFormState extends State> { children: List.generate(widget.options.length, (index) { final option = widget.options[index]; return ChoiceChip( - label: Text(_getDisplayText(option)), + label: Text(getDisplayText(option)), selected: _selectedOptions.contains(option), onSelected: (selected) { setState(() { @@ -110,18 +111,6 @@ class ProfileCategoryFormState extends State> { ); } - String _getDisplayText(dynamic option) { - // Check if the option is an enum and has a displayName property - if (option is Enum) { - final dynamicEnum = option as dynamic; - if (dynamicEnum.displayName != null) { - return dynamicEnum.displayName; - } - } - // Fallback to default toString if not an enum - return option.toString().split('.').last; - } - void _showSnackBar(String message) { if (!_isSnackBarVisible) { _isSnackBarVisible = true; diff --git a/lib/forms/skills_form.dart b/lib/forms/skills_form.dart index f1637e3..d27b898 100644 --- a/lib/forms/skills_form.dart +++ b/lib/forms/skills_form.dart @@ -1,8 +1,8 @@ +import 'package:cofounderella/pages/user_vision_page.dart'; import 'package:flutter/material.dart'; import '../../enumerations.dart'; import '../../services/auth/auth_service.dart'; import '../../services/user_service.dart'; -import '../pages/registration_complete_page.dart'; import 'profile_category_form.dart'; class SkillsForm extends StatelessWidget { @@ -62,8 +62,8 @@ class SkillsForm extends StatelessWidget { Navigator.push( context, MaterialPageRoute( - // TODO set following registration page HERE - builder: (context) => const RegistrationCompletePage(), + // set following registration page HERE + builder: (context) => MatchingForm(isRegProcess: isRegProcess), ), ); } else if (isRegProcess) { diff --git a/lib/helper.dart b/lib/helper.dart index 0c56871..89df3a4 100644 --- a/lib/helper.dart +++ b/lib/helper.dart @@ -29,6 +29,21 @@ String convertDecimalToDMS(double decimalValue) { return '${degrees.abs()}° ${minutes.abs()}\' ${seconds.abs()}" $direction'; } +/// +/// Get the displayName of our own Enumerations. +/// +String getDisplayText(dynamic option) { + // Check if the option is an enum and has a displayName property + if (option is Enum) { + final dynamicEnum = option as dynamic; + if (dynamicEnum.displayName != null) { + return dynamicEnum.displayName; + } + } + // Fallback to default toString if not an enum + return option.toString().split('.').last; +} + /// /// Show a simple message dialog /// diff --git a/lib/pages/registration_complete_page.dart b/lib/pages/registration_complete_page.dart index 009e056..803a563 100644 --- a/lib/pages/registration_complete_page.dart +++ b/lib/pages/registration_complete_page.dart @@ -27,7 +27,7 @@ class RegistrationCompletePage extends StatelessWidget { ), const SizedBox(height: 80), const Text( - "You can now use the app.", + "You can enjoy the app now.", style: TextStyle(fontSize: 18), ), const SizedBox(height: 20), diff --git a/lib/pages/user_vision_page.dart b/lib/pages/user_vision_page.dart new file mode 100644 index 0000000..a854425 --- /dev/null +++ b/lib/pages/user_vision_page.dart @@ -0,0 +1,171 @@ +import "package:cloud_firestore/cloud_firestore.dart"; +import "package:flutter/material.dart"; +import "../constants.dart"; +import "../enumerations.dart"; +import "registration_complete_page.dart"; +import "../services/auth/auth_service.dart"; + +class MatchingForm extends StatefulWidget { + const MatchingForm({super.key, required this.isRegProcess}); + + final bool isRegProcess; + + @override + MatchingFormState createState() => MatchingFormState(); +} + +class MatchingFormState extends State { + Map selectedVisionOptions = { + VisionOption.marketLeader: false, + VisionOption.sustainableBusiness: false, + VisionOption.innovativeProduct: false, + VisionOption.exitStrategy: false, + }; + AvailabilityOption? availability; + + // get instance of auth + final AuthService _authService = AuthService(); + + @override + void initState() { + super.initState(); + _loadDataFromFirebase(); + } + + Future _loadDataFromFirebase() async { + final userDoc = FirebaseFirestore.instance + .collection(Constants.dbCollectionUsers) + .doc(_authService.getCurrentUser()!.uid); + final snapshot = await userDoc.get(); + + if (snapshot.exists) { + final data = snapshot.data(); + setState(() { + // Load Vision options + if (data?[Constants.dbFieldUsersVisions] != null) { + for (var option in VisionOption.values) { + selectedVisionOptions[option] = data?[Constants.dbFieldUsersVisions] + .contains(option.toString()); + } + } + // Load Availability option + if (data?[Constants.dbFieldUsersAvailability] != null) { + availability = AvailabilityOption.values.firstWhere( + (e) => e.toString() == data?[Constants.dbFieldUsersAvailability], + ); + } + }); + } + } + + Future _saveDataToFirebase() async { + final userDoc = FirebaseFirestore.instance + .collection(Constants.dbCollectionUsers) + .doc(_authService.getCurrentUser()!.uid); + + await userDoc.set( + { + Constants.dbFieldUsersVisions: selectedVisionOptions.entries + .where((entry) => entry.value) + .map((entry) => entry.key.toString()) + .toList(), + Constants.dbFieldUsersAvailability: availability?.toString(), + }, + SetOptions(merge: true), // avoid overwriting existing data + ); + } + + bool isVisionSelected() { + return selectedVisionOptions.containsValue(true); + } + + void handleSubmit() { + if (!isVisionSelected()) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Please choose at least one long-term vision.'), + )); + return; + } + if (availability == null) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Please select an availability option.'), + )); + return; + } + _saveDataToFirebase(); + // Handle the form submission logic here + Navigator.push( + context, + MaterialPageRoute( + // TODO set following registration page HERE + builder: (context) => const RegistrationCompletePage(), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Personal preferences'), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Vision and goals', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const Text('What is your long-term vision for a startup?'), + ...VisionOption.values.map((option) { + return CheckboxListTile( + title: Text(option.displayName), + value: selectedVisionOptions[option], + controlAffinity: ListTileControlAffinity.platform, + onChanged: (bool? value) { + if (value != null) { + setState(() { + selectedVisionOptions[option] = value; + }); + } + }, + ); + }), + const SizedBox(height: 40), + const Align( + alignment: Alignment.centerLeft, + child: Text( + 'Availability and commitment', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + ), + const Text( + 'How much time can you devote to the startup each week?', + ), + ...AvailabilityOption.values.map((option) { + return RadioListTile( + title: Text(option.displayName), + value: option, + groupValue: availability, + onChanged: (AvailabilityOption? value) { + setState(() { + availability = value; + }); + }, + ); + }), + const SizedBox(height: 20), + Center( + child: ElevatedButton( + onPressed: handleSubmit, + child: Text(widget.isRegProcess ? 'Save and continue' : 'Save'), + ), + ), + ], + ), + ), + ); + } +}