6177214e-ce7c-49e3-99de-ff9721b26f63 — Commit fea30e2c
Changed files
comwell_key_app/assets/icons/c_logo.svg | 3 + comwell_key_app/assets/translations/da-DK.json | 15 +- comwell_key_app/assets/translations/en-US.json | 15 +- .../ios/Runner/RunnerRelease.entitlements | 5 + .../lib/.generated/services/models/user_dto.g.dart | 4 +- comwell_key_app/lib/check_in/check_in_page.dart | 61 +-- .../common/components/comwell_card_component.dart | 61 +++ .../comwell_club_signup_bottom_sheet.dart | 295 ++++++++++++++ .../profile/components/profile_settings_item.dart | 2 + .../lib/profile/cubit/profile_cubit.dart | 29 +- .../lib/profile/cubit/profile_state.dart | 16 +- comwell_key_app/lib/profile/profile_page.dart | 447 +++++++++++++-------- .../lib/profile/profile_repository.dart | 9 + .../cubit/profile_settings_state.dart | 2 + .../lib/profile_settings/model/user.dart | 21 +- .../profile_settings/profile_settings_page.dart | 6 +- comwell_key_app/lib/services/api.dart | 14 + .../lib/services/mappers/user_mapper.dart | 13 +- comwell_key_app/lib/services/models/user_dto.dart | 10 +- .../profile_settings_cubit_test.dart | 1 + 20 files changed, 771 insertions(+), 258 deletions(-)
Diff
diff --git a/comwell_key_app/assets/icons/c_logo.svg b/comwell_key_app/assets/icons/c_logo.svg
new file mode 100644
index 00000000..045956c1
--- /dev/null
+++ b/comwell_key_app/assets/icons/c_logo.svg
@@ -0,0 +1,3 @@
+<svg width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M13.5768 2.84039C12.1761 2.12891 10.5468 1.68341 8.86373 1.68341C5.34933 1.68341 2.90342 3.73198 2.90342 6.6751C2.90342 9.6581 5.45133 12.3158 9.37391 12.3158C11.284 12.3158 13.1945 11.7674 14.7992 11.0372H14.8751L13.5768 13.3913C12.3801 13.8376 11.0561 14 9.73099 14C3.71938 14 0 11.3215 0 6.97957C0 2.77949 3.71938 0 8.94087 0C10.4943 0 12.0745 0.222359 13.5768 0.506791V2.84039Z" fill="black"/>
+</svg>
diff --git a/comwell_key_app/assets/translations/da-DK.json b/comwell_key_app/assets/translations/da-DK.json
index bcd45acc..37b451ed 100644
--- a/comwell_key_app/assets/translations/da-DK.json
+++ b/comwell_key_app/assets/translations/da-DK.json
@@ -214,5 +214,18 @@
"sms": "SMS",
"sms_subtitle": "Ja, jeg vil gerne modtage SMS med nyheder, inspiration og invitationer til events. Jeg kan til enhver tid, afslutte min tilmelding.",
"update_profile": "Opdater profil",
- "profil_settings_invalid_date": "Ugyldigt dato"
+ "profile_settings_invalid_date": "Ugyldigt dato",
+ "gender": "Køn",
+ "comwell_club_dialog_title": "Bliv medlem",
+ "comwell_club_dialog_description": "Indtast følgende oplysninger for at blive medlem af Comwell Club",
+ "postal_code": "Postnummer",
+ "male": "Mand",
+ "female": "Kvinde",
+ "not_specified": "Ikke angivet",
+ "tos_accept": "Accepter ",
+ "tos_accept_link": "regler og betingelser for Comwell Club",
+ "newsletter_accept": "Ja tak, jeg vil gerne opdateres på aktuelle medlemstilbud, Comwell Club overraskelser og andre anbefalinger tilpasset mig. Jeg kan til enhver tid afmelde mig igen.",
+ "read_more": "Læs mere",
+ "comwell_club_inactive": "Inaktiv",
+ "points": "Point"
}
\ No newline at end of file
diff --git a/comwell_key_app/assets/translations/en-US.json b/comwell_key_app/assets/translations/en-US.json
index d4bfcf1a..a6504e80 100644
--- a/comwell_key_app/assets/translations/en-US.json
+++ b/comwell_key_app/assets/translations/en-US.json
@@ -214,5 +214,18 @@
"sms": "SMS",
"sms_subtitle": "Yes, I would like to recieve SMS with news, inspiration and invitations to events. I can at any time, unsubscribe.",
"update_profile": "Update profile",
- "profil_settings_invalid_date": "Invalid date"
+ "profile_settings_invalid_date": "Invalid date",
+ "gender": "Gender",
+ "comwell_club_dialog_title": "Become a member",
+ "comwell_club_dialog_description": "Please fill in the following information to join Comwell Club",
+ "postal_code": "Postal code",
+ "male": "Male",
+ "female": "Female",
+ "not_specified": "Not Specified",
+ "tos_accept": "Accept ",
+ "tos_accept_link": "terms and conditions for Comwell Club",
+ "newsletter_accept": "Yes, I would like to receive updates on current member offers, Comwell Club surprises and other recommendations tailored to me. I can unsubscribe at any time.",
+ "read_more": "Read more",
+ "comwell_club_inactive": "Inactive",
+ "points": "Points"
}
diff --git a/comwell_key_app/ios/Runner/RunnerRelease.entitlements b/comwell_key_app/ios/Runner/RunnerRelease.entitlements
new file mode 100644
index 00000000..0c67376e
--- /dev/null
+++ b/comwell_key_app/ios/Runner/RunnerRelease.entitlements
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>
diff --git a/comwell_key_app/lib/.generated/services/models/user_dto.g.dart b/comwell_key_app/lib/.generated/services/models/user_dto.g.dart
index 1a05bd57..bd8e6e18 100644
--- a/comwell_key_app/lib/.generated/services/models/user_dto.g.dart
+++ b/comwell_key_app/lib/.generated/services/models/user_dto.g.dart
@@ -13,7 +13,7 @@ UserDto _$UserDtoFromJson(Map json) => UserDto(
email: json['email'] as String,
clubId: json['clubId'] as String,
clubLevel: json['clubLevel'] as String,
- clubLevelName: json['clubLevelName'] as String,
+ clubLevelName: json['clubLevelName'] as String?,
birthDate: DateTime.parse(json['birthDate'] as String),
phoneNumber: json['phoneNumber'] as String,
gender: json['gender'] as String,
@@ -21,7 +21,7 @@ UserDto _$UserDtoFromJson(Map json) => UserDto(
addressZip: json['addressZip'] as String,
addressCity: json['addressCity'] as String,
addressCountry: json['addressCountry'] as String,
- points: (json['points'] as num).toInt(),
+ points: (json['points'] as num?)?.toInt(),
locale: json['locale'] as String,
);
diff --git a/comwell_key_app/lib/check_in/check_in_page.dart b/comwell_key_app/lib/check_in/check_in_page.dart
index bc845ed5..472bf50f 100644
--- a/comwell_key_app/lib/check_in/check_in_page.dart
+++ b/comwell_key_app/lib/check_in/check_in_page.dart
@@ -1,5 +1,6 @@
import 'package:comwell_key_app/check_in/bloc/check_in_cubit.dart';
import 'package:comwell_key_app/check_in/bloc/check_in_state.dart';
+import 'package:comwell_key_app/common/components/comwell_card_component.dart';
import 'package:comwell_key_app/utils/lottie_utils.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@@ -134,65 +135,7 @@ class _CheckInPageState extends State<CheckInPage>
right: 16,
curve: Easing.linear,
onEnd: onAnimationEnd,
- child: Column(
- children: [
- AspectRatio(
- aspectRatio: 359 / 212,
- child: Container(
- alignment: Alignment.center,
- clipBehavior: Clip.antiAlias,
- decoration: const BoxDecoration(
- color: Color(0xffAA8D65),
- borderRadius:
- BorderRadius.all(Radius.circular(15))),
- child: Stack(
- children: [
- Align(
- alignment: Alignment.bottomLeft,
- child: Opacity(
- opacity: 0.2,
- child: SvgPicture.asset(
- "assets/images/co.svg"))),
- Align(
- alignment: Alignment.topRight,
- child: Opacity(
- opacity: 0.2,
- child: SvgPicture.asset(
- "assets/images/v.svg"))),
- Align(
- alignment: Alignment.center,
- child: getCardContent(context)),
- ],
- ),
- ),
- ),
- const SizedBox(height: 80),
- AnimatedOpacity(
- opacity: state.checkInStatus is! CheckInStatusInitial
- ? 1.0
- : 0.0,
- duration: const Duration(milliseconds: 1000),
- child: Container(
- alignment: Alignment.center,
- width: mq.size.width,
- child: Column(
- children: [
- Text(state.titleStringId.tr()),
- const SizedBox(height: 12),
- Text(
- state.subtitleStringId.tr(),
- textAlign: TextAlign.center,
- style: Theme.of(context)
- .textTheme
- .bodySmall
- ?.copyWith(color: Colors.black45),
- ),
- ],
- ),
- ),
- ),
- ],
- ),
+ child: ComwellCard(content: getCardContent(context)),
),
if (state.checkInStatus is CheckInStatusError)
Align(
diff --git a/comwell_key_app/lib/common/components/comwell_card_component.dart b/comwell_key_app/lib/common/components/comwell_card_component.dart
new file mode 100644
index 00000000..53307ec8
--- /dev/null
+++ b/comwell_key_app/lib/common/components/comwell_card_component.dart
@@ -0,0 +1,61 @@
+import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+
+class ComwellCard extends StatelessWidget {
+ final Widget content;
+ final double aspectRatio;
+ final Color backgroundColor;
+ final double borderRadius;
+ final String? bottomLeftImage = 'assets/images/co.svg';
+ final String? topRightImage = 'assets/images/v.svg';
+ final double imageOpacity;
+
+ const ComwellCard({
+ Key? key,
+ required this.content,
+ this.aspectRatio = 359 / 212,
+ this.backgroundColor = sandColor,
+ this.borderRadius = 15,
+ this.imageOpacity = 0.2,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return AspectRatio(
+ aspectRatio: aspectRatio,
+ child: Container(
+ alignment: Alignment.center,
+ clipBehavior: Clip.antiAlias,
+ decoration: BoxDecoration(
+ color: backgroundColor,
+ borderRadius: BorderRadius.all(Radius.circular(borderRadius)),
+ ),
+ child: Stack(
+ children: [
+ if (bottomLeftImage != null)
+ Align(
+ alignment: Alignment.bottomLeft,
+ child: Opacity(
+ opacity: imageOpacity,
+ child: SvgPicture.asset(bottomLeftImage!),
+ ),
+ ),
+ if (topRightImage != null)
+ Align(
+ alignment: Alignment.topRight,
+ child: Opacity(
+ opacity: imageOpacity,
+ child: SvgPicture.asset(topRightImage!),
+ ),
+ ),
+ Align(
+ alignment: Alignment.center,
+ child: content,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/comwell_key_app/lib/profile/components/comwell_club_signup_bottom_sheet.dart b/comwell_key_app/lib/profile/components/comwell_club_signup_bottom_sheet.dart
new file mode 100644
index 00000000..633c5cef
--- /dev/null
+++ b/comwell_key_app/lib/profile/components/comwell_club_signup_bottom_sheet.dart
@@ -0,0 +1,295 @@
+import 'package:comwell_key_app/common/components/round_icon_button.dart';
+import 'package:comwell_key_app/profile/cubit/profile_cubit.dart';
+import 'package:comwell_key_app/profile_settings/components/comwell_text_field.dart';
+import 'package:comwell_key_app/profile_settings/components/date_time_picker.dart';
+import 'package:comwell_key_app/profile_settings/model/address.dart';
+import 'package:comwell_key_app/profile_settings/model/user.dart';
+import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:url_launcher/url_launcher.dart';
+
+class ComwellClubSignupBottomSheet extends StatefulWidget {
+ final User user;
+ const ComwellClubSignupBottomSheet({super.key, required this.user});
+
+ @override
+ State<ComwellClubSignupBottomSheet> createState() =>
+ _ComwellClubSignupBottomSheetState();
+}
+
+class _ComwellClubSignupBottomSheetState
+ extends State<ComwellClubSignupBottomSheet> {
+ late final TextEditingController _postNumberController;
+ String _selectedGender = '';
+
+ @override
+ void initState() {
+ super.initState();
+ _postNumberController =
+ TextEditingController(text: widget.user.address.zipCode);
+ }
+
+ @override
+ void dispose() {
+ _postNumberController.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ final cubit = context.read<ProfileCubit>();
+ return Wrap(
+ children: [
+ Padding(
+ padding: MediaQuery.of(context).viewInsets,
+ child: Container(
+ decoration: const BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(24),
+ topRight: Radius.circular(24),
+ ),
+ ),
+ height: MediaQuery.of(context).copyWith().size.height * 0.6,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 40, left: 16),
+ child: Container(
+ height: 47,
+ color: Colors.white,
+ child: Stack(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text('comwell_club_dialog_title'.tr(),
+ style: theme.textTheme.headlineLarge),
+ RoundIconButton(
+ icon: 'assets/icons/close-icon.svg',
+ color: sandColor[20]!,
+ onPressed: () {
+ Navigator.pop(context);
+ }),
+ ],
+ )
+ ],
+ ),
+ ),
+ ),
+ const SizedBox(height: 24),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: ComwellTextField(
+ fieldName: "postal_code".tr(),
+ initialValue: widget.user.address.zipCode,
+ readOnly: false,
+ controller: _postNumberController,
+ ),
+ ),
+ const SizedBox(height: 16),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Container(
+ height: 62,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: colorDivider),
+ ),
+ child: DropdownButtonHideUnderline(
+ child: DropdownButton<String>(
+ value: _selectedGender.isEmpty ? null : _selectedGender,
+ isExpanded: true,
+ dropdownColor: Colors.white,
+ alignment: Alignment.centerLeft,
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ hint: Text(
+ "gender".tr(),
+ style: theme.textTheme.headlineSmall,
+ ),
+ items: [
+ DropdownMenuItem(
+ value: "Male",
+ child: Text(
+ "male".tr(),
+ style: theme.textTheme.headlineSmall,
+ ),
+ ),
+ DropdownMenuItem(
+ value: "Female",
+ child: Text(
+ "female".tr(),
+ style: theme.textTheme.headlineSmall,
+ ),
+ ),
+ DropdownMenuItem(
+ value: "Other",
+ child: Text(
+ "not_specified".tr(),
+ style: theme.textTheme.headlineSmall,
+ ),
+ ),
+ ],
+ onChanged: (String? value) {
+ if (value != null) {
+ setState(() {
+ _selectedGender = value;
+ });
+ }
+ },
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(height: 10),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Row(
+ children: [
+ Checkbox(
+ value: cubit.state.isToSAccepted,
+ onChanged: (bool? value) {
+ setState(() {
+ cubit.onToSClick(value);
+ });
+ },
+ ),
+ Expanded(
+ child: Row(
+ children: [
+ Expanded(
+ child: RichText(
+ text: TextSpan(
+ children: <TextSpan>[
+ TextSpan(
+ text: "tos_accept".tr(),
+ style: TextStyle(
+ fontSize:
+ theme.textTheme.bodySmall!.fontSize,
+ color: theme.textTheme.bodySmall!.color,
+ fontWeight: FontWeight.w300,
+ ),
+ ),
+ TextSpan(
+ text: "tos_accept_link".tr(),
+ recognizer: TapGestureRecognizer()
+ ..onTap = () {
+ launchUrl(Uri.parse('https://comwell.com/club/club-regler'));
+ },
+ style: TextStyle(
+ fontSize:
+ theme.textTheme.bodySmall!.fontSize,
+ color: sandColor[80],
+ decoration: TextDecoration.underline,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 8),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Row(
+ children: [
+ Checkbox(
+ value: cubit.state.isNewsletterAccepted,
+ onChanged: (bool? value) {
+ setState(() {
+ cubit.onNewsletterClick(value);
+ });
+ },
+ ),
+ Expanded(
+ child: RichText(
+ text: TextSpan(
+ children: <TextSpan>[
+ TextSpan(
+ text: "newsletter_accept".tr(),
+ style: TextStyle(
+ fontSize: theme.textTheme.bodySmall!.fontSize,
+ color: theme.textTheme.bodySmall!.color,
+ fontWeight: FontWeight.w300,
+ ),
+ ),
+ TextSpan(
+ text: "read_more".tr(),
+ recognizer: TapGestureRecognizer()
+ ..onTap = () {
+ launchUrl(Uri.parse('https://comwell.com/club/club-permission'));
+
+ },
+ style: TextStyle(
+ fontSize: theme.textTheme.bodySmall!.fontSize,
+ color: sandColor[80],
+ decoration: TextDecoration.underline,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 16),
+ const Divider(color: colorDivider),
+ const SizedBox(height: 16),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: ElevatedButton(
+ style: ElevatedButton.styleFrom(
+ elevation: 0,
+ backgroundColor: cubit.state.isToSAccepted &&
+ _selectedGender.isNotEmpty
+ ? sandColor
+ : Colors.grey[200],
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(25),
+ ),
+ minimumSize: const Size(double.infinity, 52),
+ ),
+ onPressed: () async {
+ final response = cubit.state.isToSAccepted && _selectedGender.isNotEmpty
+ ? await cubit.onComwellClubSignupClick(widget.user.copyWith(
+ gender: _selectedGender,
+ address: Address(
+ zipCode: _postNumberController.text,
+ street: '',
+ city: '',
+ country: '')))
+ : null;
+
+ if (response != null) {
+ Navigator.pop(context);
+ }
+ },
+ child: Text(
+ 'save'.tr(),
+ style: theme.textTheme.headlineSmall?.copyWith(
+ color: cubit.state.isToSAccepted
+ ? Colors.white
+ : Colors.grey[400],
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/comwell_key_app/lib/profile/components/profile_settings_item.dart b/comwell_key_app/lib/profile/components/profile_settings_item.dart
index d170e20d..fb4e537f 100644
--- a/comwell_key_app/lib/profile/components/profile_settings_item.dart
+++ b/comwell_key_app/lib/profile/components/profile_settings_item.dart
@@ -10,6 +10,8 @@ Widget profileSettingsItem(
}) {
return ListTile(
leading: Container(
+ width: 36,
+ height: 36,
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: sandColor[20],
diff --git a/comwell_key_app/lib/profile/cubit/profile_cubit.dart b/comwell_key_app/lib/profile/cubit/profile_cubit.dart
index 15671059..fe23ad8d 100644
--- a/comwell_key_app/lib/profile/cubit/profile_cubit.dart
+++ b/comwell_key_app/lib/profile/cubit/profile_cubit.dart
@@ -9,8 +9,8 @@ part 'profile_state.dart';
class ProfileCubit extends Cubit<ProfileState> {
final ProfileRepository profileRepository;
ProfileCubit({required this.profileRepository})
- : super(const ProfileState(isLoading: false, user: null, error: null));
-
+ : super(const ProfileState(isLoading: false, user: null, error: null, isToSAccepted: false, isNewsletterAccepted: false));
+
void sendPageViewEvent() async {
await FirebaseAnalytics.instance.logScreenView(
screenName: 'Profile',
@@ -29,5 +29,30 @@ class ProfileCubit extends Cubit<ProfileState> {
}
}
+ void onToSClick(bool? value) async {
+ print("onToSClick: ${value}");
+ emit(state.setToSAccepted(isToSAccepted: value!));
+ print("state: ${state.isToSAccepted}");
+ }
+
+ void onNewsletterClick(bool? value) async {
+ emit(state.setNewsletterAccepted(isNewsletterAccepted: value!));
+ }
+
+ String getGender(String gender) {
+ switch (gender) {
+ case 'Male':
+ return 'M';
+ case 'Female':
+ return 'F';
+ default:
+ return 'O';
+ }
+ }
+
+ Future<dynamic> onComwellClubSignupClick(User user) async {
+ return await profileRepository.signupForComwellClub(user);
+ }
+
void deleteAllInfoOnLogOut() async {}
}
diff --git a/comwell_key_app/lib/profile/cubit/profile_state.dart b/comwell_key_app/lib/profile/cubit/profile_state.dart
index 36fe49b3..d8b93cf1 100644
--- a/comwell_key_app/lib/profile/cubit/profile_state.dart
+++ b/comwell_key_app/lib/profile/cubit/profile_state.dart
@@ -4,26 +4,34 @@ class ProfileState extends Equatable {
final Error? error;
final bool isLoading;
final User? user;
+ final bool isToSAccepted;
+ final bool isNewsletterAccepted;
- const ProfileState({this.error, required this.isLoading, this.user});
+ const ProfileState({this.error, required this.isLoading, this.user, this.isToSAccepted = false, this.isNewsletterAccepted = false});
@override
- List<Object> get props => [isLoading];
+ List<Object> get props => [isLoading, isToSAccepted, isNewsletterAccepted];
const ProfileState.initial()
: isLoading = false,
user = null,
- error = null;
+ error = null,
+ isToSAccepted = false,
+ isNewsletterAccepted = false;
ProfileState userFetched({required User user}) => _copyWith(user: user);
ProfileState loading() => _copyWith(isLoading: true);
ProfileState setError(Error error) => _copyWith(error: error);
+ ProfileState setNewsletterAccepted({required bool isNewsletterAccepted}) => _copyWith(isNewsletterAccepted: isNewsletterAccepted);
+ ProfileState setToSAccepted({required bool isToSAccepted}) => _copyWith(isToSAccepted: isToSAccepted);
- ProfileState _copyWith({User? user, Error? error, bool? isLoading}) {
+ ProfileState _copyWith({User? user, Error? error, bool? isLoading, bool? isToSAccepted, bool? isNewsletterAccepted}) {
return ProfileState(
user: user ?? this.user,
error: error ?? this.error,
isLoading: isLoading ?? this.isLoading,
+ isToSAccepted: isToSAccepted ?? this.isToSAccepted,
+ isNewsletterAccepted: isNewsletterAccepted ?? this.isNewsletterAccepted,
);
}
}
diff --git a/comwell_key_app/lib/profile/profile_page.dart b/comwell_key_app/lib/profile/profile_page.dart
index bc417ae6..a37993b6 100644
--- a/comwell_key_app/lib/profile/profile_page.dart
+++ b/comwell_key_app/lib/profile/profile_page.dart
@@ -1,15 +1,18 @@
import 'package:comwell_key_app/authentication/bloc/authentication_bloc.dart';
+import 'package:comwell_key_app/common/components/comwell_card_component.dart';
import 'package:comwell_key_app/common/components/round_icon_button.dart';
+import 'package:comwell_key_app/profile/components/comwell_club_signup_bottom_sheet.dart';
import 'package:comwell_key_app/profile/components/profile_settings_item.dart';
import 'package:comwell_key_app/profile/cubit/profile_cubit.dart';
-import 'package:comwell_key_app/profile_settings/model/user.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:comwell_key_app/utils/secure_storage.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
+import 'package:url_launcher/url_launcher.dart';
class ProfilePage extends StatelessWidget {
final SecureStorage secureStorage = SecureStorage();
@@ -43,6 +46,7 @@ class ProfilePage extends StatelessWidget {
Widget _buildProfilePage(ProfileCubit cubit, BuildContext context) {
final user = cubit.state.user;
+ final isActive = cubit.state.user!.clubLevel != '';
return SafeArea(
child: Column(
@@ -59,148 +63,156 @@ class ProfilePage extends StatelessWidget {
}),
),
),
- Expanded(
- flex: 2,
+ Padding(
+ padding: const EdgeInsets.all(16.0),
child: Column(
children: [
- const Spacer(),
- Stack(children: [
- Image.asset('assets/images/cc_inactive.png'),
- Positioned(
- bottom: 20,
- left: 20,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- 'name_cc_sign'.tr(),
- style: const TextStyle(
- color: Colors.black,
- fontSize: 14,
- fontWeight: FontWeight.w600,
- ),
- ),
- Text(
- '${user?.firstName} ${user?.lastName}',
- style: const TextStyle(
- color: Colors.black,
- fontSize: 16,
- fontWeight: FontWeight.w600,
- ),
- ),
- ]),
- ),
- ]),
- const SizedBox(
- height: 20,
- )
+ ComwellCard(
+ content: getCardContent(context, cubit, isActive),
+ backgroundColor: isActive ? sandColor : earthColor[10]!),
],
),
),
- Container(
- color: Colors.white,
- child: Column(
- children: [
- const SizedBox(height: 10),
- Container(
- margin: const EdgeInsets.symmetric(horizontal: 16),
- padding: const EdgeInsets.symmetric(vertical: 16),
- decoration: BoxDecoration(
- color: sandColor[10],
- borderRadius: const BorderRadius.all(Radius.circular(10)),
- ),
- height: 115,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text("become_cc_member_title".tr(),
- textAlign: TextAlign.start,
- style: const TextStyle(
- color: Colors.black,
- fontSize: 16,
- fontWeight: FontWeight.w600)),
- SizedBox(
- width: 250,
- child: Text(
- "become_cc_member_subtitle".tr(),
- textAlign: TextAlign.start,
- maxLines: 4,
- style: const TextStyle(
- color: Colors.black,
- fontSize: 14,
- fontWeight: FontWeight.w500),
+ const SizedBox(height: 10),
+ Expanded(
+ child: Container(
+ color: Colors.white,
+ child: Column(
+ children: [
+ const SizedBox(height: 10),
+ !isActive
+ ? Container(
+ margin: const EdgeInsets.symmetric(horizontal: 16),
+ padding: const EdgeInsets.symmetric(vertical: 16),
+ decoration: BoxDecoration(
+ color: sandColor[10],
+ borderRadius:
+ const BorderRadius.all(Radius.circular(10)),
+ ),
+ height: 115,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ GestureDetector(
+ onTap: () async {
+ await
+ showModalBottomSheet<void>(
+ context: context,
+ isScrollControlled: true,
+ backgroundColor: Colors.white,
+ builder: (context) =>
+ ComwellClubSignupBottomSheet(user: user!),
+ );
+ cubit.init();
+ },
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text("become_cc_member_title".tr(),
+ textAlign: TextAlign.start,
+ style: const TextStyle(
+ color: Colors.black,
+ fontSize: 16,
+ fontWeight: FontWeight.w600)),
+ SizedBox(
+ width: 250,
+ child: Text(
+ "become_cc_member_subtitle".tr(),
+ textAlign: TextAlign.start,
+ maxLines: 4,
+ style: const TextStyle(
+ color: Colors.black,
+ fontSize: 14,
+ fontWeight: FontWeight.w500),
+ ),
+ ),
+ ],
+ ),
),
- ),
- ],
- ),
- const Icon(
- Icons.chevron_right,
- color: Colors.black,
- size: 24,
- ),
- ],
- )),
- const SizedBox(height: 10),
- profileSettingsItem(
- context,
- icon: Image.asset('assets/icons/user-circle.png'),
- text: 'profile_settings_profile_menu'.tr(),
- trailingIcon: Icons.chevron_right,
- onTap: () async {
- await context.pushNamed(AppRoutes.profileSettings.name);
- cubit.init();
- },
- ),
- const Padding(
- padding: EdgeInsets.symmetric(horizontal: 16.0),
- child: Divider(color: colorDivider),
- ),
- profileSettingsItem(
- context,
- icon: Image.asset('assets/icons/card.png'),
- text: 'payment_card_profile_menu'.tr(),
- trailingIcon: Icons.chevron_right,
- onTap: () {
- context.pushNamed(AppRoutes.paymentCards.name);
- },
- ),
- const Padding(
- padding: EdgeInsets.symmetric(horizontal: 16.0),
- child: Divider(color: colorDivider),
- ),
- profileSettingsItem(
- context,
- icon: Image.asset('assets/icons/bell.png'),
- trailingIcon: Icons.chevron_right,
- text: 'notifications_profile_menu'.tr(),
- onTap: () {
- context.pushNamed(AppRoutes.notifications.name);
- },
- ),
- const Padding(
- padding: EdgeInsets.symmetric(horizontal: 16.0),
- child: Divider(color: colorDivider),
- ),
- const SizedBox(height: 10),
- Center(
- child: OutlinedButton(
- style: OutlinedButton.styleFrom(
- side: const BorderSide(color: colorDivider),
- ),
- onPressed: () {
- showLogoutDialog(context);
+ const Icon(
+ Icons.chevron_right,
+ color: Colors.black,
+ size: 24,
+ ),
+ ],
+ ),
+ )
+ : const SizedBox(),
+ const SizedBox(height: 10),
+ profileSettingsItem(
+ context,
+ icon: Image.asset('assets/icons/user-circle.png'),
+ text: 'profile_settings_profile_menu'.tr(),
+ trailingIcon: Icons.chevron_right,
+ onTap: () async {
+ await context.pushNamed(AppRoutes.profileSettings.name);
+ cubit.init();
},
- child: Text(
- 'logout_profile_menu'.tr(),
- style: TextStyle(color: Colors.grey[600]),
+ ),
+ const Padding(
+ padding: EdgeInsets.symmetric(horizontal: 16.0),
+ child: Divider(color: colorDivider),
+ ),
+ profileSettingsItem(
+ context,
+ icon: Image.asset('assets/icons/card.png'),
+ text: 'payment_card_profile_menu'.tr(),
+ trailingIcon: Icons.chevron_right,
+ onTap: () {
+ context.pushNamed(AppRoutes.paymentCards.name);
+ },
+ ),
+ const Padding(
+ padding: EdgeInsets.symmetric(horizontal: 16.0),
+ child: Divider(color: colorDivider),
+ ),
+ profileSettingsItem(
+ context,
+ icon: Image.asset('assets/icons/bell.png'),
+ trailingIcon: Icons.chevron_right,
+ text: 'notifications_profile_menu'.tr(),
+ onTap: () {
+ context.pushNamed(AppRoutes.notifications.name);
+ },
+ ),
+ const Padding(
+ padding: EdgeInsets.symmetric(horizontal: 16.0),
+ child: Divider(color: colorDivider),
+ ),
+ const SizedBox(height: 10),
+ if (isActive) ...[
+ profileSettingsItem(
+ context,
+ icon: SvgPicture.asset('assets/icons/c_logo.svg'),
+ text: "Comwell Club",
+ trailingIcon: Icons.open_in_new,
+ onTap: () {
+ launchUrl(Uri.parse('https://comwell.com/profile'));
+ },
+ ),
+ const Padding(
+ padding: EdgeInsets.symmetric(horizontal: 16.0),
+ child: Divider(color: colorDivider),
+ ),
+ ],
+ const SizedBox(height: 10),
+ Center(
+ child: OutlinedButton(
+ style: OutlinedButton.styleFrom(
+ side: const BorderSide(color: colorDivider),
+ ),
+ onPressed: () {
+ showLogoutDialog(context);
+ },
+ child: Text(
+ 'logout_profile_menu'.tr(),
+ style: TextStyle(color: Colors.grey[600]),
+ ),
),
),
- ),
- const SizedBox(height: 20),
- ],
+ const Spacer(),
+ ],
+ ),
),
),
],
@@ -216,65 +228,152 @@ class ProfilePage extends StatelessWidget {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
- child: SizedBox(
- height: 220,
- child: Padding(
- padding: const EdgeInsets.only(left: 20, right: 20, top: 20),
- child: Column(
- children: [
- const SizedBox(
- height: 10,
+ child: Padding(
+ padding: const EdgeInsets.all(24.0),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ "logout_dialog_title".tr(),
+ textAlign: TextAlign.center,
+ style: const TextStyle(
+ color: Colors.black,
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
),
- Text("logout_dialog_title".tr(),
- textAlign: TextAlign.center,
- style: const TextStyle(
- color: Colors.black,
- fontSize: 18,
- fontWeight: FontWeight.w600)),
- const SizedBox(height: 20),
- ElevatedButton(
+ ),
+ const SizedBox(height: 24),
+ SizedBox(
+ width: double.infinity,
+ child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: sandColor,
- minimumSize: const Size(280, 52),
- maximumSize: const Size(280, 52),
+ padding: const EdgeInsets.symmetric(vertical: 16),
),
onPressed: () {
context
.read<AuthenticationBloc>()
.add(AuthenticationLogoutPressed());
},
- child: Text("logout_profile_menu".tr(),
- style: const TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w600,
- color: Colors.white)),
- ),
- const SizedBox(
- height: 5,
+ child: Text(
+ "logout_profile_menu".tr(),
+ style: const TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ color: Colors.white,
+ ),
+ ),
),
- OutlinedButton(
+ ),
+ const SizedBox(height: 12),
+ SizedBox(
+ width: double.infinity,
+ child: OutlinedButton(
style: OutlinedButton.styleFrom(
- side: const BorderSide(color: colorDivider),
- minimumSize: const Size(280, 52),
- maximumSize: const Size(280, 52),
+ side: BorderSide(color: Colors.grey[400]!),
+ padding: const EdgeInsets.symmetric(vertical: 16),
),
onPressed: () {
context.pop();
},
- child: Text("cancel".tr(),
- style: const TextStyle(
- fontSize: 16,
- fontWeight: FontWeight.w600,
- color: Colors.black)),
- ),
- const SizedBox(
- height: 10,
+ child: Text(
+ "cancel".tr(),
+ style: const TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ color: Colors.black,
+ ),
+ ),
),
- ],
- ),
+ ),
+ ],
),
),
);
});
}
+
+ Widget getCardContent(
+ BuildContext context, ProfileCubit cubit, bool isActive) {
+ final theme = Theme.of(context);
+ final user = cubit.state.user!;
+
+ return SizedBox(
+ width: double.infinity,
+ height: double.infinity,
+ child: Stack(
+ children: [
+ Positioned(
+ top: 20,
+ left: 20,
+ child: Container(
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(32),
+ ),
+ child: Text(
+ isActive ? 'Comwell Club' : 'comwell_club_inactive'.tr(),
+ style: theme.textTheme.bodySmall!.copyWith(
+ color: Colors.black,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ ),
+ ),
+ Positioned(
+ bottom: 20,
+ left: 20,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'Navn',
+ style: TextStyle(
+ color: isActive ? Colors.white : Colors.black,
+ fontSize: 14,
+ fontWeight: FontWeight.w400,
+ ),
+ ),
+ Text(
+ '${user.firstName} ${user.lastName}',
+ style: TextStyle(
+ color: isActive ? Colors.white : Colors.black,
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ ],
+ ),
+ ),
+ if (isActive)
+ Positioned(
+ bottom: 20,
+ right: 20,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ Text(
+ 'points'.tr(),
+ style: const TextStyle(
+ color: Colors.white,
+ fontSize: 14,
+ fontWeight: FontWeight.w400,
+ ),
+ ),
+ Text(
+ '${user.points} ${'points'.tr()}',
+ style: const TextStyle(
+ color: Colors.white,
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
}
diff --git a/comwell_key_app/lib/profile/profile_repository.dart b/comwell_key_app/lib/profile/profile_repository.dart
index bfea5796..03cc435f 100644
--- a/comwell_key_app/lib/profile/profile_repository.dart
+++ b/comwell_key_app/lib/profile/profile_repository.dart
@@ -40,4 +40,13 @@ class ProfileRepository {
return user;
}
}
+
+ Future<User> signupForComwellClub(User user) async {
+ final response = await api.signupForComwellClub(user);
+ final data = response.data as Json;
+ final userDto = UserDto.fromJson(data);
+ final updatedUser = userDto.toUser();
+ await db.userDAO.saveUser(userDto);
+ return updatedUser;
+ }
}
diff --git a/comwell_key_app/lib/profile_settings/cubit/profile_settings_state.dart b/comwell_key_app/lib/profile_settings/cubit/profile_settings_state.dart
index 5628af93..446a2e3c 100644
--- a/comwell_key_app/lib/profile_settings/cubit/profile_settings_state.dart
+++ b/comwell_key_app/lib/profile_settings/cubit/profile_settings_state.dart
@@ -25,6 +25,8 @@ class ProfileSettingsState extends Equatable {
ProfileSettingsState userLoaded({required User user}) =>
_copyWith(user: user);
+
+
ProfileSettingsState _copyWith({
User? user,
Error? error,
diff --git a/comwell_key_app/lib/profile_settings/model/user.dart b/comwell_key_app/lib/profile_settings/model/user.dart
index dd039fac..80f808bf 100644
--- a/comwell_key_app/lib/profile_settings/model/user.dart
+++ b/comwell_key_app/lib/profile_settings/model/user.dart
@@ -12,7 +12,10 @@ class User {
final DateTime birthDate;
final String shopperReference;
final int points;
-
+ final String? gender;
+ final String? clubId;
+ final String? clubLevel;
+ final String? clubLevelName;
User({
required this.id,
required this.firstName,
@@ -24,11 +27,15 @@ class User {
required this.birthDate,
required this.shopperReference,
required this.points,
+ this.gender,
+ this.clubId,
+ this.clubLevel,
+ this.clubLevelName,
});
@override
String toString() {
- return 'User(id: $id, firstName: $firstName, lastName: $lastName, addressCountry: $addressCountry, phone: $phoneNumber, email: $email, address: $address, birthDate: $birthDate, shopperReference: $shopperReference, points: $points)';
+ return 'User(id: $id, firstName: $firstName, lastName: $lastName, addressCountry: $addressCountry, phone: $phoneNumber, email: $email, address: $address, birthDate: $birthDate, shopperReference: $shopperReference, points: $points, clubLevel: $clubLevel, clubLevelName: $clubLevelName)';
}
User copyWith({
@@ -42,6 +49,10 @@ class User {
String? shopperReference,
DateTime? birthDate,
int? points,
+ String? gender,
+ String? clubId,
+ String? clubLevel,
+ String? clubLevelName,
}) {
return User(
id: id ?? this.id,
@@ -54,8 +65,10 @@ class User {
birthDate: birthDate ?? this.birthDate,
shopperReference: shopperReference ?? this.shopperReference,
points: points ?? this.points,
+ gender: gender ?? this.gender,
+ clubId: clubId ?? this.clubId,
+ clubLevel: clubLevel ?? this.clubLevel,
+ clubLevelName: clubLevelName ?? this.clubLevelName,
);
}
-
-
}
diff --git a/comwell_key_app/lib/profile_settings/profile_settings_page.dart b/comwell_key_app/lib/profile_settings/profile_settings_page.dart
index 4b1fdc0d..67b95f16 100644
--- a/comwell_key_app/lib/profile_settings/profile_settings_page.dart
+++ b/comwell_key_app/lib/profile_settings/profile_settings_page.dart
@@ -100,7 +100,7 @@ class ProfileSettingsPage extends StatelessWidget {
}),
const SizedBox(height: 8),
ComwellTextField(
- fieldName: "profil_settings_email".tr(),
+ fieldName: "profile_settings_email".tr(),
initialValue: state.user!.email,
readOnly: true,
controller: TextEditingController(text: state.user!.email),
@@ -166,6 +166,10 @@ class ProfileSettingsPage extends StatelessWidget {
shopperReference: state.user!.shopperReference,
points: state.user!.points,
addressCountry: state.user!.addressCountry,
+ gender: state.user!.gender,
+ clubId: state.user!.clubId,
+ clubLevel: state.user!.clubLevel,
+ clubLevelName: state.user!.clubLevelName,
),
);
},
diff --git a/comwell_key_app/lib/services/api.dart b/comwell_key_app/lib/services/api.dart
index 483df776..79db4f26 100644
--- a/comwell_key_app/lib/services/api.dart
+++ b/comwell_key_app/lib/services/api.dart
@@ -76,6 +76,7 @@ class Api {
}
Future<Response<dynamic>> fetchProfileSettings() async {
+ print("fetching profile settings");
final response = await dio.get('/Members/v1/GetGuestProfile');
print("response in api: $response");
return response;
@@ -85,6 +86,19 @@ class Api {
return dio.delete<void>('/Members/v1/DeleteGuest');
}
+ Future<Response<dynamic>> signupForComwellClub(User user) async {
+ final zipCode = user.address.zipCode;
+ final body = {
+ "gender": user.gender,
+ "addressZip": zipCode
+
+ };
+ final json = jsonEncode(body);
+ print("json in api: $json");
+ final response = await dio.post<dynamic>('/Members/v1/ClubSignup', data: json);
+ return response;
+ }
+
// TODO: Remove guestId when backend is updated
Future<Response<dynamic>> getNotificationPermissions(int guestId) async {
final body = {
diff --git a/comwell_key_app/lib/services/mappers/user_mapper.dart b/comwell_key_app/lib/services/mappers/user_mapper.dart
index 044ac312..4f3e3adc 100644
--- a/comwell_key_app/lib/services/mappers/user_mapper.dart
+++ b/comwell_key_app/lib/services/mappers/user_mapper.dart
@@ -19,10 +19,13 @@ extension UserMapper on UserDto {
),
birthDate: birthDate,
shopperReference: clubId,
- points: points,
+ points: points ?? 0,
+ gender: gender,
+ clubId: clubId,
+ clubLevel: clubLevel,
+ clubLevelName: clubLevelName,
);
}
-
}
extension UserDtoMapper on User {
@@ -33,11 +36,11 @@ extension UserDtoMapper on User {
lastName: lastName,
email: email,
clubId: shopperReference,
- clubLevel: '',
- clubLevelName: '',
+ clubLevel: clubLevel ?? '',
+ clubLevelName: clubLevelName,
birthDate: birthDate,
phoneNumber: phoneNumber,
- gender: '',
+ gender: gender ?? '',
addressStreet: address.street,
addressZip: address.zipCode,
addressCity: address.city,
diff --git a/comwell_key_app/lib/services/models/user_dto.dart b/comwell_key_app/lib/services/models/user_dto.dart
index 94b0576b..9c3d512a 100644
--- a/comwell_key_app/lib/services/models/user_dto.dart
+++ b/comwell_key_app/lib/services/models/user_dto.dart
@@ -11,7 +11,7 @@ class UserDto {
final String email;
final String clubId;
final String clubLevel;
- final String clubLevelName;
+ final String? clubLevelName;
final DateTime birthDate;
final String phoneNumber;
final String gender;
@@ -19,7 +19,7 @@ class UserDto {
final String addressZip;
final String addressCity;
final String addressCountry;
- final int points;
+ final int? points;
final String locale;
UserDto({
@@ -53,11 +53,11 @@ class UserDto {
lastName: user.lastName,
email: user.email,
clubId: user.shopperReference,
- clubLevel: '',
- clubLevelName: '',
+ clubLevel: user.clubLevel ?? '',
+ clubLevelName: user.clubLevelName ?? '',
birthDate: user.birthDate,
phoneNumber: user.phoneNumber,
- gender: '',
+ gender: user.gender ?? '',
addressStreet: user.address.street,
addressZip: user.address.zipCode,
addressCity: user.address.city,
diff --git a/comwell_key_app/test/profile_settings_test/profile_settings_cubit_test.dart b/comwell_key_app/test/profile_settings_test/profile_settings_cubit_test.dart
index ecaae12a..41b68823 100644
--- a/comwell_key_app/test/profile_settings_test/profile_settings_cubit_test.dart
+++ b/comwell_key_app/test/profile_settings_test/profile_settings_cubit_test.dart
@@ -42,6 +42,7 @@ void main() {
lastName: 'Doe',
email: 'john.doe@example.com',
phoneNumber: '1234567890',
+ gender: 'Male',
address: Address(
street: '123 Main St',
city: 'Anytown',