Edit Age, Gender, Locations, Languages.
parent
f4ed4bd110
commit
d246829784
|
@ -136,6 +136,7 @@ class MyDrawer extends StatelessWidget {
|
|||
MaterialPageRoute(
|
||||
builder: (context) => const UserDataPage(
|
||||
isRegProcess: false,
|
||||
isEditMode: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -2,7 +2,20 @@ enum Gender {
|
|||
none,
|
||||
male,
|
||||
female,
|
||||
divers,
|
||||
divers;
|
||||
|
||||
String get displayName {
|
||||
switch (this) {
|
||||
case Gender.none:
|
||||
return 'unknown';
|
||||
case Gender.male:
|
||||
return 'male';
|
||||
case Gender.female:
|
||||
return 'female';
|
||||
case Gender.divers:
|
||||
return 'diverse';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum SkillOption {
|
||||
|
|
|
@ -64,7 +64,7 @@ class SkillsForm extends StatelessWidget {
|
|||
MaterialPageRoute(
|
||||
// set following registration page HERE
|
||||
builder: (context) =>
|
||||
MatchingForm(isRegProcess: isRegProcess),
|
||||
UserVisionPage(isRegProcess: isRegProcess),
|
||||
),
|
||||
);
|
||||
} else if (isRegProcess) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
|
||||
import '../constants.dart';
|
||||
import '../enumerations.dart';
|
||||
import 'language.dart';
|
||||
import 'location.dart';
|
||||
|
||||
|
@ -13,11 +14,13 @@ class UserProfile {
|
|||
final String lastName;
|
||||
String? profilePictureUrl;
|
||||
String? bio;
|
||||
Gender? gender;
|
||||
int? born;
|
||||
final String risk;
|
||||
final List<String> skills;
|
||||
final List<String> skillsSought;
|
||||
final List<Language> languages;
|
||||
final Map<String, MyLocation?> locations;
|
||||
List<Language> languages;
|
||||
Map<String, MyLocation?> locations;
|
||||
|
||||
UserProfile({
|
||||
required this.id,
|
||||
|
@ -28,6 +31,8 @@ class UserProfile {
|
|||
required this.lastName,
|
||||
this.profilePictureUrl,
|
||||
this.bio,
|
||||
this.gender,
|
||||
this.born,
|
||||
required this.risk,
|
||||
required this.skills,
|
||||
required this.skillsSought,
|
||||
|
@ -51,6 +56,8 @@ class UserProfile {
|
|||
risk: data[Constants.dbFieldUsersRiskTolerance] ?? '',
|
||||
profilePictureUrl: data[Constants.dbFieldUsersProfilePic],
|
||||
bio: data[Constants.dbFieldUsersBio],
|
||||
gender: Gender.values[data[Constants.dbFieldUsersGender] ?? 0],
|
||||
born: data[Constants.dbFieldUsersYearBorn],
|
||||
languages: [],
|
||||
locations: {},
|
||||
);
|
||||
|
|
|
@ -3,9 +3,9 @@ import 'package:cloud_firestore/cloud_firestore.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
import '../components/location_dialog.dart';
|
||||
import '../components/my_button.dart';
|
||||
import '../components/my_drawer.dart';
|
||||
import '../constants.dart';
|
||||
import '../enumerations.dart';
|
||||
import '../forms/skills_form.dart';
|
||||
|
@ -13,11 +13,14 @@ import '../models/language.dart';
|
|||
import '../models/language_setting.dart';
|
||||
import '../models/location.dart';
|
||||
import '../services/auth/auth_service.dart';
|
||||
import '../utils/math.dart';
|
||||
|
||||
class UserDataPage extends StatefulWidget {
|
||||
final bool isRegProcess;
|
||||
final bool isEditMode;
|
||||
|
||||
const UserDataPage({super.key, required this.isRegProcess});
|
||||
const UserDataPage(
|
||||
{super.key, required this.isRegProcess, required this.isEditMode});
|
||||
|
||||
@override
|
||||
State<UserDataPage> createState() => _UserDataPageState();
|
||||
|
@ -87,14 +90,14 @@ class _UserDataPageState extends State<UserDataPage> {
|
|||
MyLocation userLoc;
|
||||
for (var doc in locationSnapshot.docs) {
|
||||
userLoc = MyLocation(
|
||||
street: doc['street'],
|
||||
country: doc['country'],
|
||||
administrativeArea: doc['administrativeArea'],
|
||||
locality: doc['locality'],
|
||||
subLocality: doc['subLocality'],
|
||||
postalCode: doc['postalCode'],
|
||||
latitude: doc['latitude'],
|
||||
longitude: doc['longitude'],
|
||||
street: doc[Constants.dbFieldLocationStreet],
|
||||
country: doc[Constants.dbFieldLocationCountry],
|
||||
administrativeArea: doc[Constants.dbFieldLocationArea],
|
||||
locality: doc[Constants.dbFieldLocationLocality],
|
||||
subLocality: doc[Constants.dbFieldLocationSubLocality],
|
||||
postalCode: doc[Constants.dbFieldLocationPostalCode],
|
||||
latitude: doc[Constants.dbFieldLocationLatitude],
|
||||
longitude: doc[Constants.dbFieldLocationLongitude],
|
||||
);
|
||||
if (doc.id == Constants.dbDocMainLocation) {
|
||||
_mainLocationFromDb = userLoc;
|
||||
|
@ -132,7 +135,7 @@ class _UserDataPageState extends State<UserDataPage> {
|
|||
// Load data from JSON file when the widget initializes
|
||||
loadLanguagesFromJson();
|
||||
} catch (error) {
|
||||
_showSnackBar("Error fetching settings: $error");
|
||||
_showSnackBar('Error fetching settings: $error');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,14 +228,14 @@ class _UserDataPageState extends State<UserDataPage> {
|
|||
_secondaryLocationFromDb = null,
|
||||
_secondLocationExists = false
|
||||
},
|
||||
onError: (e) => _showSnackBar("Error updating document: $e"),
|
||||
onError: (e) => _showSnackBar('Error updating document: $e'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Save Languages - only if selected values changed
|
||||
if (_selectedLanguages.isEmpty) {
|
||||
_showSnackBar("No language selected");
|
||||
_showSnackBar('No language selected');
|
||||
return false;
|
||||
} else if (_languagesFromDb.length != _selectedLanguages.length ||
|
||||
_selectedLanguages
|
||||
|
@ -315,6 +318,50 @@ class _UserDataPageState extends State<UserDataPage> {
|
|||
});
|
||||
}
|
||||
|
||||
void _saveButtonClicked(BuildContext context) async {
|
||||
bool success = await saveUserData(context);
|
||||
if (context.mounted) {
|
||||
if (success) {
|
||||
if (widget.isRegProcess) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SkillsForm(
|
||||
isRegProcess: widget.isRegProcess,
|
||||
skillsSought: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
if (widget.isEditMode == true) {
|
||||
// pass data back to caller
|
||||
Map<String, MyLocation?> locations = {
|
||||
if (_mainLocation != null)
|
||||
Constants.dbDocMainLocation: _mainLocation,
|
||||
if (_secondaryLocation != null)
|
||||
Constants.dbDocSecondLocation: _secondaryLocation,
|
||||
};
|
||||
|
||||
Navigator.pop(context, {
|
||||
Constants.dbFieldUsersYearBorn: _selectedYear,
|
||||
Constants.dbFieldUsersGender: genderView,
|
||||
Constants.dbCollectionLanguages: _languagesFromDb,
|
||||
Constants.dbCollectionLocations: locations,
|
||||
});
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Failed to save user data.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (languagesList.isEmpty) {
|
||||
|
@ -322,10 +369,18 @@ class _UserDataPageState extends State<UserDataPage> {
|
|||
} else {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text("User Data"),
|
||||
title:
|
||||
Text('${widget.isRegProcess ? 'User Data' : 'Edit your data'} '),
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
if (widget.isRegProcess)
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
AuthService().signOut();
|
||||
},
|
||||
icon: const Icon(Icons.logout))
|
||||
],
|
||||
),
|
||||
drawer: const MyDrawer(),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: ListView(
|
||||
|
@ -409,7 +464,7 @@ class _UserDataPageState extends State<UserDataPage> {
|
|||
children: [
|
||||
const Padding(padding: EdgeInsets.symmetric(horizontal: 8)),
|
||||
Text(_selectedYear != null
|
||||
? '${DateTime.now().year - (_selectedYear ?? 0)} years old'
|
||||
? '${calcAge(_selectedYear)} years old'
|
||||
: 'undefined age'),
|
||||
const SizedBox(width: 20),
|
||||
DropdownMenu(
|
||||
|
@ -457,7 +512,7 @@ class _UserDataPageState extends State<UserDataPage> {
|
|||
),
|
||||
ButtonSegment<Gender>(
|
||||
value: Gender.divers,
|
||||
label: Text('divers'),
|
||||
label: Text('diverse'),
|
||||
),
|
||||
],
|
||||
selected: <Gender>{genderView},
|
||||
|
@ -477,32 +532,9 @@ class _UserDataPageState extends State<UserDataPage> {
|
|||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: MyButton(
|
||||
text: widget.isRegProcess ? 'Save and continue' : "Save",
|
||||
onTap: () async {
|
||||
bool success = await saveUserData(context);
|
||||
if (context.mounted) {
|
||||
if (success) {
|
||||
if (widget.isRegProcess) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SkillsForm(
|
||||
isRegProcess: widget.isRegProcess,
|
||||
skillsSought: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Failed to save user data.'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
text: widget.isRegProcess ? 'Save and continue' : 'Save',
|
||||
onTap: () {
|
||||
_saveButtonClicked(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
|
@ -4,7 +4,10 @@ import 'package:firebase_auth/firebase_auth.dart';
|
|||
import '../constants.dart';
|
||||
import '../models/user_profile.dart';
|
||||
import '../services/user_service.dart';
|
||||
import '../utils/helper.dart';
|
||||
import '../utils/math.dart';
|
||||
import 'edit_profile_page.dart';
|
||||
import 'user_data_page.dart';
|
||||
|
||||
class UserProfilePage extends StatefulWidget {
|
||||
const UserProfilePage({super.key});
|
||||
|
@ -57,6 +60,26 @@ class _UserProfilePageState extends State<UserProfilePage> {
|
|||
}
|
||||
}
|
||||
|
||||
void editUserDataInfo() async {
|
||||
final updatedUserData = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
const UserDataPage(isRegProcess: false, isEditMode: true),
|
||||
),
|
||||
);
|
||||
|
||||
if (updatedUserData != null) {
|
||||
setState(() {
|
||||
// above Type of updatedUserData is dynamic, so check UserDataPage
|
||||
myData.born = updatedUserData[Constants.dbFieldUsersYearBorn];
|
||||
myData.gender = updatedUserData[Constants.dbFieldUsersGender];
|
||||
myData.languages = updatedUserData[Constants.dbCollectionLanguages];
|
||||
myData.locations = updatedUserData[Constants.dbCollectionLocations];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -70,47 +93,13 @@ class _UserProfilePageState extends State<UserProfilePage> {
|
|||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: OutlinedButton.icon(
|
||||
label: const Text('Edit'),
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: editNameInfo,
|
||||
),
|
||||
),
|
||||
CircleAvatar(
|
||||
radius: 50,
|
||||
backgroundImage: profileImageUrl != null
|
||||
? NetworkImage(profileImageUrl!)
|
||||
: null,
|
||||
child: profileImageUrl == null
|
||||
? const Icon(Icons.person, size: 50)
|
||||
: null,
|
||||
),
|
||||
_buildAvatar(context),
|
||||
const SizedBox(height: 16),
|
||||
Text(myData.name, style: const TextStyle(fontSize: 24)),
|
||||
Text(myData.email, style: const TextStyle(fontSize: 16)),
|
||||
const SizedBox(height: 32),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Short description of yourself',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
Text(myData.bio ?? 'N/A',
|
||||
style: const TextStyle(fontSize: 16)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(color: Theme.of(context).colorScheme.primary),
|
||||
const SizedBox(height: 16),
|
||||
Divider(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
_buildLocation(context),
|
||||
const SizedBox(height: 16),
|
||||
Divider(color: Theme.of(context).colorScheme.primary),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
|
@ -118,4 +107,130 @@ class _UserProfilePageState extends State<UserProfilePage> {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLocation(BuildContext context) {
|
||||
int age = calcAge(myData.born);
|
||||
return Column(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Age',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
(age > 0
|
||||
? '$age years old, born ${myData.born}'
|
||||
: 'n/a'),
|
||||
style: const TextStyle(fontSize: 16)),
|
||||
],
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: OutlinedButton.icon(
|
||||
label: const Text('Edit'),
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: editUserDataInfo,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Gender',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
Text(getDisplayText(myData.gender),
|
||||
style: const TextStyle(fontSize: 16)),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Spoken languages',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
myData.languages.map((lang) => lang.name).join(', '),
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Locations',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
if (myData.locations.isEmpty)
|
||||
const Text('n/a', style: TextStyle(fontSize: 16)),
|
||||
if (myData.locations.containsKey(Constants.dbDocMainLocation))
|
||||
Text(
|
||||
myData.locations[Constants.dbDocMainLocation].toString(),
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
if (myData.locations.containsKey(Constants.dbDocSecondLocation))
|
||||
Text(
|
||||
myData.locations[Constants.dbDocSecondLocation].toString(),
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAvatar(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
child: OutlinedButton.icon(
|
||||
label: const Text('Edit'),
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: editNameInfo,
|
||||
),
|
||||
),
|
||||
CircleAvatar(
|
||||
radius: 50,
|
||||
backgroundImage:
|
||||
profileImageUrl != null ? NetworkImage(profileImageUrl!) : null,
|
||||
child: profileImageUrl == null
|
||||
? const Icon(Icons.person, size: 50)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(myData.name, style: const TextStyle(fontSize: 24)),
|
||||
Text(myData.email, style: const TextStyle(fontSize: 16)),
|
||||
const SizedBox(height: 32),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Short description of yourself',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
Text(myData.bio ?? 'n/a', style: const TextStyle(fontSize: 16)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,16 @@ import '../enumerations.dart';
|
|||
import '../forms/corporate_culture_form.dart';
|
||||
import '../services/auth/auth_service.dart';
|
||||
|
||||
class MatchingForm extends StatefulWidget {
|
||||
const MatchingForm({super.key, required this.isRegProcess});
|
||||
class UserVisionPage extends StatefulWidget {
|
||||
const UserVisionPage({super.key, required this.isRegProcess});
|
||||
|
||||
final bool isRegProcess;
|
||||
|
||||
@override
|
||||
MatchingFormState createState() => MatchingFormState();
|
||||
UserVisionPageState createState() => UserVisionPageState();
|
||||
}
|
||||
|
||||
class MatchingFormState extends State<MatchingForm> {
|
||||
class UserVisionPageState extends State<UserVisionPage> {
|
||||
Map<VisionOption, bool> selectedVisionOptions = {
|
||||
VisionOption.marketLeader: false,
|
||||
VisionOption.sustainableBusiness: false,
|
||||
|
|
|
@ -37,7 +37,10 @@ class AuthGate extends StatelessWidget {
|
|||
return HomePage();
|
||||
} else {
|
||||
// also in case of (snapshot.hasError)
|
||||
return const UserDataPage(isRegProcess: true);
|
||||
return const UserDataPage(
|
||||
isRegProcess: true,
|
||||
isEditMode: false,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -86,8 +89,8 @@ class AuthGate extends StatelessWidget {
|
|||
.collection(Constants.dbCollectionUsers)
|
||||
.doc(currentUserId)
|
||||
.set({
|
||||
'uid': currentUserId,
|
||||
'email': email,
|
||||
Constants.dbFieldUsersID: currentUserId,
|
||||
Constants.dbFieldUsersEmail: email,
|
||||
});
|
||||
} catch (e) {
|
||||
print("Error creating user document: $e");
|
||||
|
|
|
@ -2,6 +2,14 @@ import 'dart:math';
|
|||
|
||||
import '../models/user_profile.dart';
|
||||
|
||||
/// Approximate determination of age
|
||||
int calcAge(int? birthYear) {
|
||||
if (birthYear != null) {
|
||||
return (DateTime.now().year - birthYear);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// Convert decimal coordinate to degrees minutes seconds (DMS).
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue