diff --git a/.idea/modules.xml b/.idea/modules.xml
index cfa0d987..de9529fa 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -3,6 +3,7 @@
+
@@ -12,7 +13,6 @@
-
diff --git a/apps/mobile_app/lib/navigation/app_router.dart b/apps/mobile_app/lib/navigation/app_router.dart
index 485f4469..d6a540a0 100644
--- a/apps/mobile_app/lib/navigation/app_router.dart
+++ b/apps/mobile_app/lib/navigation/app_router.dart
@@ -1,4 +1,5 @@
import 'package:auth/auth.dart';
+import 'package:customer_service/customer_service.dart';
import 'package:legacy_dashboard_shell/legacy_dashboard_builder.dart';
import 'package:dashboard_shell/dashboard_builder.dart';
import 'package:flutter/material.dart';
@@ -43,6 +44,11 @@ void configureAppRouter() {
],
),
+ GoRoute(
+ path: AppRoutes.customerService,
+ name: 'customer_service',
+ pageBuilder: CustomerServiceBuilder().buildPage,
+ ),
GoRoute(
path: AppRoutes.login,
name: 'login',
diff --git a/apps/mobile_app/linux/flutter/generated_plugin_registrant.cc b/apps/mobile_app/linux/flutter/generated_plugin_registrant.cc
index e71a16d2..f6f23bfe 100644
--- a/apps/mobile_app/linux/flutter/generated_plugin_registrant.cc
+++ b/apps/mobile_app/linux/flutter/generated_plugin_registrant.cc
@@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h"
+#include
void fl_register_plugins(FlPluginRegistry* registry) {
+ g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
+ fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
+ url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}
diff --git a/apps/mobile_app/linux/flutter/generated_plugins.cmake b/apps/mobile_app/linux/flutter/generated_plugins.cmake
index 2e1de87a..f16b4c34 100644
--- a/apps/mobile_app/linux/flutter/generated_plugins.cmake
+++ b/apps/mobile_app/linux/flutter/generated_plugins.cmake
@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
+ url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
diff --git a/apps/mobile_app/pubspec.yaml b/apps/mobile_app/pubspec.yaml
index 9fdc39d3..71f59dc4 100644
--- a/apps/mobile_app/pubspec.yaml
+++ b/apps/mobile_app/pubspec.yaml
@@ -41,8 +41,6 @@ dependencies:
path: ../../modules/auth
home:
path: ../../modules/home
- hub:
- path: ../../modules/legacy/modules/hub
profile:
path: ../../modules/profile
notifications:
@@ -51,6 +49,10 @@ dependencies:
path: ../../modules/dashboard_shell
legacy_dashboard_shell:
path: ../../modules/legacy/modules/legacy_dashboard_shell
+ hub:
+ path: ../../modules/legacy/modules/hub
+ customer_service:
+ path: ../../modules/legacy/modules/customer_service
splash:
path: ../../modules/splash
#packages dependencies go here
diff --git a/modules/legacy/melos_legacy.iml b/modules/legacy/melos_legacy.iml
index 26a45e79..bb9b68c7 100644
--- a/modules/legacy/melos_legacy.iml
+++ b/modules/legacy/melos_legacy.iml
@@ -17,10 +17,13 @@
+
+
+
-
+
\ No newline at end of file
diff --git a/modules/legacy/modules/customer_service/lib/src/customer_service_builder.dart b/modules/legacy/modules/customer_service/lib/src/customer_service_builder.dart
new file mode 100644
index 00000000..a5d4d885
--- /dev/null
+++ b/modules/legacy/modules/customer_service/lib/src/customer_service_builder.dart
@@ -0,0 +1,18 @@
+import 'package:customer_service/src/presentation/customer_service_screen.dart';
+import 'package:flutter/material.dart';
+import 'package:go_router/go_router.dart';
+import 'package:get_it/get_it.dart';
+import 'package:navigation/navigation.dart';
+
+class CustomerServiceBuilder {
+ const CustomerServiceBuilder();
+
+ Page buildPage(BuildContext context, GoRouterState state) {
+ final NavigationContract navigationContract = GetIt.I();
+
+ return MaterialPage(
+ key: state.pageKey,
+ child: CustomerServiceScreen(navigationContract: navigationContract),
+ );
+ }
+}
diff --git a/modules/legacy/modules/customer_service/lib/src/domain/entities/send_email_request_entity.dart b/modules/legacy/modules/customer_service/lib/src/domain/entities/send_email_request_entity.dart
new file mode 100644
index 00000000..55204429
--- /dev/null
+++ b/modules/legacy/modules/customer_service/lib/src/domain/entities/send_email_request_entity.dart
@@ -0,0 +1,15 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'send_email_request_entity.freezed.dart';
+
+@freezed
+abstract class SendEmailRequestEntity with _$SendEmailRequestEntity{
+ const factory SendEmailRequestEntity({
+ required String country,
+ required String channel,
+ required String name,
+ required String email,
+ required String subject,
+ required String body,
+ }) = _SendEmailRequestEntity;
+}
\ No newline at end of file
diff --git a/modules/legacy/modules/customer_service/lib/src/domain/entities/send_email_request_entity.freezed.dart b/modules/legacy/modules/customer_service/lib/src/domain/entities/send_email_request_entity.freezed.dart
new file mode 100644
index 00000000..a92da8c7
--- /dev/null
+++ b/modules/legacy/modules/customer_service/lib/src/domain/entities/send_email_request_entity.freezed.dart
@@ -0,0 +1,286 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// coverage:ignore-file
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'send_email_request_entity.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+// dart format off
+T _$identity(T value) => value;
+/// @nodoc
+mixin _$SendEmailRequestEntity {
+
+ String get country; String get channel; String get name; String get email; String get subject; String get body;
+/// Create a copy of SendEmailRequestEntity
+/// with the given fields replaced by the non-null parameter values.
+@JsonKey(includeFromJson: false, includeToJson: false)
+@pragma('vm:prefer-inline')
+$SendEmailRequestEntityCopyWith get copyWith => _$SendEmailRequestEntityCopyWithImpl(this as SendEmailRequestEntity, _$identity);
+
+
+
+@override
+bool operator ==(Object other) {
+ return identical(this, other) || (other.runtimeType == runtimeType&&other is SendEmailRequestEntity&&(identical(other.country, country) || other.country == country)&&(identical(other.channel, channel) || other.channel == channel)&&(identical(other.name, name) || other.name == name)&&(identical(other.email, email) || other.email == email)&&(identical(other.subject, subject) || other.subject == subject)&&(identical(other.body, body) || other.body == body));
+}
+
+
+@override
+int get hashCode => Object.hash(runtimeType,country,channel,name,email,subject,body);
+
+@override
+String toString() {
+ return 'SendEmailRequestEntity(country: $country, channel: $channel, name: $name, email: $email, subject: $subject, body: $body)';
+}
+
+
+}
+
+/// @nodoc
+abstract mixin class $SendEmailRequestEntityCopyWith<$Res> {
+ factory $SendEmailRequestEntityCopyWith(SendEmailRequestEntity value, $Res Function(SendEmailRequestEntity) _then) = _$SendEmailRequestEntityCopyWithImpl;
+@useResult
+$Res call({
+ String country, String channel, String name, String email, String subject, String body
+});
+
+
+
+
+}
+/// @nodoc
+class _$SendEmailRequestEntityCopyWithImpl<$Res>
+ implements $SendEmailRequestEntityCopyWith<$Res> {
+ _$SendEmailRequestEntityCopyWithImpl(this._self, this._then);
+
+ final SendEmailRequestEntity _self;
+ final $Res Function(SendEmailRequestEntity) _then;
+
+/// Create a copy of SendEmailRequestEntity
+/// with the given fields replaced by the non-null parameter values.
+@pragma('vm:prefer-inline') @override $Res call({Object? country = null,Object? channel = null,Object? name = null,Object? email = null,Object? subject = null,Object? body = null,}) {
+ return _then(_self.copyWith(
+country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
+as String,channel: null == channel ? _self.channel : channel // ignore: cast_nullable_to_non_nullable
+as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
+as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable
+as String,subject: null == subject ? _self.subject : subject // ignore: cast_nullable_to_non_nullable
+as String,body: null == body ? _self.body : body // ignore: cast_nullable_to_non_nullable
+as String,
+ ));
+}
+
+}
+
+
+/// Adds pattern-matching-related methods to [SendEmailRequestEntity].
+extension SendEmailRequestEntityPatterns on SendEmailRequestEntity {
+/// A variant of `map` that fallback to returning `orElse`.
+///
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case final Subclass value:
+/// return ...;
+/// case _:
+/// return orElse();
+/// }
+/// ```
+
+@optionalTypeArgs TResult maybeMap(TResult Function( _SendEmailRequestEntity value)? $default,{required TResult orElse(),}){
+final _that = this;
+switch (_that) {
+case _SendEmailRequestEntity() when $default != null:
+return $default(_that);case _:
+ return orElse();
+
+}
+}
+/// A `switch`-like method, using callbacks.
+///
+/// Callbacks receives the raw object, upcasted.
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case final Subclass value:
+/// return ...;
+/// case final Subclass2 value:
+/// return ...;
+/// }
+/// ```
+
+@optionalTypeArgs TResult map(TResult Function( _SendEmailRequestEntity value) $default,){
+final _that = this;
+switch (_that) {
+case _SendEmailRequestEntity():
+return $default(_that);case _:
+ throw StateError('Unexpected subclass');
+
+}
+}
+/// A variant of `map` that fallback to returning `null`.
+///
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case final Subclass value:
+/// return ...;
+/// case _:
+/// return null;
+/// }
+/// ```
+
+@optionalTypeArgs TResult? mapOrNull(TResult? Function( _SendEmailRequestEntity value)? $default,){
+final _that = this;
+switch (_that) {
+case _SendEmailRequestEntity() when $default != null:
+return $default(_that);case _:
+ return null;
+
+}
+}
+/// A variant of `when` that fallback to an `orElse` callback.
+///
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case Subclass(:final field):
+/// return ...;
+/// case _:
+/// return orElse();
+/// }
+/// ```
+
+@optionalTypeArgs TResult maybeWhen(TResult Function( String country, String channel, String name, String email, String subject, String body)? $default,{required TResult orElse(),}) {final _that = this;
+switch (_that) {
+case _SendEmailRequestEntity() when $default != null:
+return $default(_that.country,_that.channel,_that.name,_that.email,_that.subject,_that.body);case _:
+ return orElse();
+
+}
+}
+/// A `switch`-like method, using callbacks.
+///
+/// As opposed to `map`, this offers destructuring.
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case Subclass(:final field):
+/// return ...;
+/// case Subclass2(:final field2):
+/// return ...;
+/// }
+/// ```
+
+@optionalTypeArgs TResult when(TResult Function( String country, String channel, String name, String email, String subject, String body) $default,) {final _that = this;
+switch (_that) {
+case _SendEmailRequestEntity():
+return $default(_that.country,_that.channel,_that.name,_that.email,_that.subject,_that.body);case _:
+ throw StateError('Unexpected subclass');
+
+}
+}
+/// A variant of `when` that fallback to returning `null`
+///
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case Subclass(:final field):
+/// return ...;
+/// case _:
+/// return null;
+/// }
+/// ```
+
+@optionalTypeArgs TResult? whenOrNull(TResult? Function( String country, String channel, String name, String email, String subject, String body)? $default,) {final _that = this;
+switch (_that) {
+case _SendEmailRequestEntity() when $default != null:
+return $default(_that.country,_that.channel,_that.name,_that.email,_that.subject,_that.body);case _:
+ return null;
+
+}
+}
+
+}
+
+/// @nodoc
+
+
+class _SendEmailRequestEntity implements SendEmailRequestEntity {
+ const _SendEmailRequestEntity({required this.country, required this.channel, required this.name, required this.email, required this.subject, required this.body});
+
+
+@override final String country;
+@override final String channel;
+@override final String name;
+@override final String email;
+@override final String subject;
+@override final String body;
+
+/// Create a copy of SendEmailRequestEntity
+/// with the given fields replaced by the non-null parameter values.
+@override @JsonKey(includeFromJson: false, includeToJson: false)
+@pragma('vm:prefer-inline')
+_$SendEmailRequestEntityCopyWith<_SendEmailRequestEntity> get copyWith => __$SendEmailRequestEntityCopyWithImpl<_SendEmailRequestEntity>(this, _$identity);
+
+
+
+@override
+bool operator ==(Object other) {
+ return identical(this, other) || (other.runtimeType == runtimeType&&other is _SendEmailRequestEntity&&(identical(other.country, country) || other.country == country)&&(identical(other.channel, channel) || other.channel == channel)&&(identical(other.name, name) || other.name == name)&&(identical(other.email, email) || other.email == email)&&(identical(other.subject, subject) || other.subject == subject)&&(identical(other.body, body) || other.body == body));
+}
+
+
+@override
+int get hashCode => Object.hash(runtimeType,country,channel,name,email,subject,body);
+
+@override
+String toString() {
+ return 'SendEmailRequestEntity(country: $country, channel: $channel, name: $name, email: $email, subject: $subject, body: $body)';
+}
+
+
+}
+
+/// @nodoc
+abstract mixin class _$SendEmailRequestEntityCopyWith<$Res> implements $SendEmailRequestEntityCopyWith<$Res> {
+ factory _$SendEmailRequestEntityCopyWith(_SendEmailRequestEntity value, $Res Function(_SendEmailRequestEntity) _then) = __$SendEmailRequestEntityCopyWithImpl;
+@override @useResult
+$Res call({
+ String country, String channel, String name, String email, String subject, String body
+});
+
+
+
+
+}
+/// @nodoc
+class __$SendEmailRequestEntityCopyWithImpl<$Res>
+ implements _$SendEmailRequestEntityCopyWith<$Res> {
+ __$SendEmailRequestEntityCopyWithImpl(this._self, this._then);
+
+ final _SendEmailRequestEntity _self;
+ final $Res Function(_SendEmailRequestEntity) _then;
+
+/// Create a copy of SendEmailRequestEntity
+/// with the given fields replaced by the non-null parameter values.
+@override @pragma('vm:prefer-inline') $Res call({Object? country = null,Object? channel = null,Object? name = null,Object? email = null,Object? subject = null,Object? body = null,}) {
+ return _then(_SendEmailRequestEntity(
+country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
+as String,channel: null == channel ? _self.channel : channel // ignore: cast_nullable_to_non_nullable
+as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
+as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable
+as String,subject: null == subject ? _self.subject : subject // ignore: cast_nullable_to_non_nullable
+as String,body: null == body ? _self.body : body // ignore: cast_nullable_to_non_nullable
+as String,
+ ));
+}
+
+
+}
+
+// dart format on
diff --git a/modules/legacy/modules/customer_service/lib/src/domain/repositories/customer_service_repository.dart b/modules/legacy/modules/customer_service/lib/src/domain/repositories/customer_service_repository.dart
new file mode 100644
index 00000000..e69de29b
diff --git a/modules/legacy/modules/customer_service/lib/src/domain/send_email_use_case.dart b/modules/legacy/modules/customer_service/lib/src/domain/send_email_use_case.dart
new file mode 100644
index 00000000..e07870df
--- /dev/null
+++ b/modules/legacy/modules/customer_service/lib/src/domain/send_email_use_case.dart
@@ -0,0 +1,5 @@
+import 'package:customer_service/src/domain/entities/send_email_request_entity.dart';
+
+abstract class SendEmailUseCase {
+ Future sendEmail({required SendEmailRequestEntity request});
+}
diff --git a/modules/legacy/modules/customer_service/lib/src/domain/send_email_use_case_impl.dart b/modules/legacy/modules/customer_service/lib/src/domain/send_email_use_case_impl.dart
new file mode 100644
index 00000000..86002122
--- /dev/null
+++ b/modules/legacy/modules/customer_service/lib/src/domain/send_email_use_case_impl.dart
@@ -0,0 +1,13 @@
+import 'package:customer_service/src/domain/entities/send_email_request_entity.dart';
+import 'package:customer_service/src/domain/send_email_use_case.dart';
+
+class SendEmailUseCaseImpl implements SendEmailUseCase {
+ //SignUpUseCaseImpl(this._repository);
+
+ //final AuthRepository _repository;
+
+ @override
+ Future sendEmail({required SendEmailRequestEntity request}) async {
+ //return _repository.signUp(request: request);
+ }
+}
diff --git a/modules/legacy/modules/customer_service/lib/src/presentation/contact_screen.dart b/modules/legacy/modules/customer_service/lib/src/presentation/contact_screen.dart
new file mode 100644
index 00000000..d5507856
--- /dev/null
+++ b/modules/legacy/modules/customer_service/lib/src/presentation/contact_screen.dart
@@ -0,0 +1,122 @@
+import 'package:customer_service/src/presentation/state/contact_view_model.dart';
+import 'package:design_system/design_system.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:navigation/navigation.dart';
+import 'package:sf_localizations/sf_localizations.dart';
+import 'package:utils/utils.dart';
+
+const Map country = {
+ 'SPAIN': 'España',
+ 'PORTUGAL': 'Portugal',
+ 'FRANCE': 'France',
+ 'ITALIA': 'Italia',
+ 'GERMANY': 'Deutschland',
+ 'OTHER': I18n.other,
+};
+
+const Map channel = {
+ 'ONLINE_SHOP': I18n.channelOnline,
+ 'AMAZON': I18n.channelAmazon,
+ 'STORE': I18n.channelStore,
+ 'OTHER': I18n.other,
+};
+
+class ContactScreen extends ConsumerWidget {
+ final NavigationContract navigationContract;
+
+ const ContactScreen({super.key, required this.navigationContract});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final theme = ref.watch(themePortProvider);
+
+ final vm = ref.read(contactViewModelProvider.notifier);
+ final viewState = ref.watch(contactViewModelProvider);
+
+ return Scaffold(
+ backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
+ body: SafeArea(
+ child: Container(
+ padding: SizeUtils.getByScreen(
+ small: EdgeInsets.symmetric(horizontal: 38, vertical: 14),
+ big: EdgeInsets.symmetric(horizontal: 36, vertical: 12)
+ ),
+ child: SingleChildScrollView(
+ child: Column(
+ children: [
+ Stack(
+ children: [
+ SizedBox(
+ height: SizeUtils.getByScreen(small: 36, big: 36),
+ child: Align(
+ alignment: Alignment.centerLeft,
+ child: Icon(Icons.arrow_back, size: SizeUtils.getByScreen(small: 36, big: 34))
+ )
+ ),
+ Center(child: Text(context.translate(I18n.contactTitle),
+ style: TextStyle(fontSize: SizeUtils.getByScreen(small: 28, big: 27))))
+ ],
+ ),
+ SizedBox(height: SizeUtils.getByScreen(small: 40, big: 38)),
+ CustomDropdown(
+ items: country.values.map(Text.new).toList(growable: false),
+ values: country.keys.toList(),
+ onChanged: (x){vm.setCountry(x);},
+ hint: 'Choose your country'
+ ),
+ SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
+ CustomDropdown(
+ items: channel.values.map(Text.new).toList(growable: false),
+ values: channel.keys.toList(),
+ onChanged: (x){vm.setChannel(x);},
+ hint: 'Purchase channel'
+ ),
+ SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
+ CustomTextField(
+ controller: vm.nameController,
+ hint: 'Enter your name',
+ ),
+ SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
+ CustomTextField(
+ controller: vm.emailController,
+ keyboardType: TextInputType.emailAddress,
+ hint: 'Enter your email',
+ ),
+ SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
+ CustomTextField(
+ controller: vm.subjectController,
+ hint: 'Your message subject',
+ ),
+ SizedBox(height: SizeUtils.getByScreen(small: 18, big: 17)),
+ CustomTextField(
+ controller: vm.bodyController,
+ keyboardType: TextInputType.multiline,
+ hint: 'Your message',
+ lines: 8,
+ ),
+ if (viewState.errorMessage.isNotEmpty) ...[
+ const SizedBox(height: 4),
+ Text(
+ viewState.errorMessage,
+ textAlign: TextAlign.center,
+ style: const TextStyle(
+ color: Color.fromRGBO(239, 17, 17, 1),
+ fontSize: 12,
+ ),
+ ),
+ ],
+ SizedBox(height: SizeUtils.getByScreen(small: 28, big: 27)),
+ PrimaryButton(
+ onPressed: vm.sendEmail,
+ text: 'Send!',
+ color: theme.getColorFor(ThemeCode.buttonPrimary)
+ )
+ ],
+ )
+ )
+ )
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/modules/legacy/modules/customer_service/lib/src/presentation/customer_service_screen.dart b/modules/legacy/modules/customer_service/lib/src/presentation/customer_service_screen.dart
new file mode 100644
index 00000000..28f839ff
--- /dev/null
+++ b/modules/legacy/modules/customer_service/lib/src/presentation/customer_service_screen.dart
@@ -0,0 +1,139 @@
+import 'package:customer_service/src/presentation/contact_screen.dart';
+import 'package:design_system/design_system.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:navigation/navigation.dart';
+import 'package:sf_localizations/sf_localizations.dart';
+import 'package:utils/utils.dart';
+import 'package:url_launcher/url_launcher.dart';
+
+class CustomerServiceScreen extends ConsumerWidget {
+ final NavigationContract navigationContract;
+
+ const CustomerServiceScreen({super.key, required this.navigationContract});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final theme = ref.watch(themePortProvider);
+
+ return Scaffold(
+ backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
+ body: SafeArea(
+ child: Container(
+ padding: SizeUtils.getByScreen(
+ small: EdgeInsets.symmetric(horizontal: 18, vertical: 14),
+ big: EdgeInsets.symmetric(horizontal: 16, vertical: 12)
+ ),
+ child: Column(
+ children: [
+ Stack(
+ children: [
+ SizedBox(
+ height: SizeUtils.getByScreen(small: 36, big: 36),
+ child: Align(
+ alignment: Alignment.centerLeft,
+ child: Icon(Icons.arrow_back, size: SizeUtils.getByScreen(small: 36, big: 34))
+ )
+ ),
+ Center(child: Text(context.translate(I18n.customerService),
+ style: TextStyle(fontSize: SizeUtils.getByScreen(small: 28, big: 27))))
+ ],
+ ),
+ SizedBox(height: SizeUtils.getByScreen(small: 40, big: 38)),
+ AppSectionButton(
+ onPressed: () async {
+ final Uri url = Uri.parse('https://www.savefamilygps.com/');
+ if (!await launchUrl(url)) {
+ throw Exception('Could not launch $url');
+ }
+ },
+ icon: Icons.sunny,
+ text: "Visit our Website"
+ ),
+ SizedBox(height: SizeUtils.getByScreen(small: 10, big: 9)),
+ AppSectionButton(
+ onPressed: () async {
+ final Uri url = Uri.parse('https://savefamilygpshelp.zendesk.com/hc/es');
+ if (!await launchUrl(url)) {
+ throw Exception('Could not launch $url');
+ }
+ },
+ icon: Icons.handshake_outlined,
+ text: "Can we help you?"
+ ),
+ SizedBox(height: SizeUtils.getByScreen(small: 10, big: 9)),
+ AppSectionButton(
+ onPressed: (){Navigator.push(context,
+ MaterialPageRoute(
+ builder: (_) => ContactScreen(navigationContract: navigationContract),
+ ));},
+ icon: Icons.email_outlined,
+ text: context.translate(I18n.contactTitle)
+ ),
+ ],
+ )
+ )
+ ),
+ );
+ }
+}
+
+
+class AppSectionButton extends ConsumerWidget {
+
+ final GestureTapCallback onPressed;
+ final IconData icon;
+ final String text;
+
+ const AppSectionButton({
+ required this.onPressed,
+ required this.icon,
+ required this.text,
+ });
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final theme = ref.read(themePortProvider);
+
+ return GestureDetector(
+ onTap: onPressed,
+ child: Container(
+ padding: SizeUtils.getByScreen(
+ small: EdgeInsets.symmetric(horizontal: 22, vertical: 10),
+ big: EdgeInsets.symmetric(horizontal: 21, vertical: 8)
+ ),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.all(
+ Radius.circular(SizeUtils.getByScreen(small: 12, big: 18))),
+ color: theme.getColorFor(ThemeCode.backgroundSecondary),
+ ),
+ child: Row(
+ children: [
+ Container(
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ color: theme.getColorFor(ThemeCode.backgroundPrimary),
+ ),
+ padding: EdgeInsets.all(
+ SizeUtils.getByScreen(small: 4, big: 12)),
+ child: Icon(icon,
+ size: SizeUtils.getByScreen(small: 40, big: 44),
+ color: Color(0xFF588EA5),
+ weight: 30,
+ ),
+ ),
+ SizedBox(width: SizeUtils.getByScreen(small: 16, big: 15)),
+ Expanded(
+ child: Text(context.translate(text),
+ style: TextStyle(
+ fontSize: SizeUtils.getByScreen(small: 18, big: 19),
+ fontWeight: FontWeight.w500
+ )
+ )
+ ),
+ ],
+ ),
+ )
+ );
+ }
+}
\ No newline at end of file
diff --git a/modules/legacy/modules/customer_service/lib/src/presentation/providers/send_email_use_case_provider.dart b/modules/legacy/modules/customer_service/lib/src/presentation/providers/send_email_use_case_provider.dart
new file mode 100644
index 00000000..b4fe853a
--- /dev/null
+++ b/modules/legacy/modules/customer_service/lib/src/presentation/providers/send_email_use_case_provider.dart
@@ -0,0 +1,9 @@
+import 'package:customer_service/src/domain/send_email_use_case.dart';
+import 'package:customer_service/src/domain/send_email_use_case_impl.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+final sendEmailUseCaseProvider =
+Provider.autoDispose((ref) {
+ //final authRepository = ref.read(customerServiceRepositoryProvider);
+ return SendEmailUseCaseImpl();
+});
diff --git a/modules/legacy/modules/customer_service/lib/src/presentation/state/contact_view_model.dart b/modules/legacy/modules/customer_service/lib/src/presentation/state/contact_view_model.dart
new file mode 100644
index 00000000..6f804672
--- /dev/null
+++ b/modules/legacy/modules/customer_service/lib/src/presentation/state/contact_view_model.dart
@@ -0,0 +1,129 @@
+// import 'package:customer_service/src/domain/send_email_use_case.dart';
+// import 'package:customer_service/src/presentation/providers/send_email_use_case_provider.dart';
+import 'package:customer_service/src/presentation/state/contact_view_state.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:sf_localizations/sf_localizations.dart';
+import 'package:url_launcher/url_launcher.dart';
+
+final contactViewModelProvider =
+NotifierProvider.autoDispose(
+ ContactViewModel.new,
+);
+
+class ContactViewModel extends Notifier {
+ //late final SendEmailUseCase _sendEmailUseCase;
+
+ late final TextEditingController nameController;
+ late final TextEditingController emailController;
+ late final TextEditingController subjectController;
+ late final TextEditingController bodyController;
+
+ static final RegExp _emailRegex = RegExp(
+ r'^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$',
+ caseSensitive: false,
+ );
+
+ @override
+ ContactViewState build() {
+ //_sendEmailUseCase = ref.read(sendEmailUseCaseProvider);
+
+ nameController = TextEditingController();
+ emailController = TextEditingController();
+ subjectController = TextEditingController();
+ bodyController = TextEditingController();
+
+ nameController.addListener(_onNameChanged);
+ emailController.addListener(_onEmailChanged);
+ subjectController.addListener(_onSubjectChanged);
+ bodyController.addListener(_onBodyChanged);
+
+ ref.onDispose(disposeControllers);
+
+ return const ContactViewState();
+ }
+
+ void setCountry(String value) {
+ if (value == state.country) return;
+
+ state = state.copyWith(country: value, errorMessage: '');
+ }
+
+ void setChannel(String value) {
+ if (value == state.channel) return;
+
+ state = state.copyWith(channel: value, errorMessage: '');
+ }
+
+ void _onNameChanged() {
+ final text = nameController.text;
+ if (text == state.name) return;
+
+ state = state.copyWith(name: text, errorMessage: '');
+ }
+
+ void _onEmailChanged() {
+ final text = emailController.text;
+ if (text == state.email) return;
+
+ state = state.copyWith(email: text, errorMessage: '');
+ state = state.copyWith(emailError: _emailErrorFor(text));
+ }
+
+ bool _isValidEmail(String email) => _emailRegex.hasMatch(email);
+
+ String _emailErrorFor(String value) {
+ final email = value.trim();
+ if (email.isEmpty) return I18n.errorEmailRequired;
+ if (!_isValidEmail(email)) return I18n.errorEmailInvalid;
+ return '';
+ }
+
+ void _onSubjectChanged() {
+ final text = subjectController.text;
+ if (text == state.subject) return;
+
+ state = state.copyWith(subject: text, errorMessage: '');
+ }
+
+ void _onBodyChanged() {
+ final text = bodyController.text;
+ if (text == state.body) return;
+
+ state = state.copyWith(body: text, errorMessage: '');
+ }
+
+ void sendEmail() async {
+ final receiver = 'aitorarana@savefamilygps.com';
+
+ //final name = state.name;
+ final sender = state.email;
+ final subject = state.subject;
+ final body = state.body;
+
+ if (sender.isEmpty) {
+ state = state.copyWith(errorMessage: I18n.errorEmailRequired);
+ return;
+ }
+ if (_isValidEmail(sender)) {
+ state = state.copyWith(errorMessage: I18n.errorEmailInvalid);
+ }
+
+ final Uri url = Uri.parse('mailto:$receiver?from=$sender&subject=$subject&body=$body');
+ if (!await launchUrl(url)) {
+ throw Exception('Could not launch $url');
+ }
+ }
+
+ void disposeControllers() {
+ nameController.removeListener(_onNameChanged);
+ emailController.removeListener(_onEmailChanged);
+ subjectController.removeListener(_onSubjectChanged);
+ bodyController.removeListener(_onBodyChanged);
+
+ nameController.dispose();
+ emailController.dispose();
+ subjectController.dispose();
+ bodyController.dispose();
+ }
+}
\ No newline at end of file
diff --git a/modules/legacy/modules/customer_service/lib/src/presentation/state/contact_view_state.dart b/modules/legacy/modules/customer_service/lib/src/presentation/state/contact_view_state.dart
new file mode 100644
index 00000000..08133560
--- /dev/null
+++ b/modules/legacy/modules/customer_service/lib/src/presentation/state/contact_view_state.dart
@@ -0,0 +1,17 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'contact_view_state.freezed.dart';
+
+@freezed
+abstract class ContactViewState with _$ContactViewState{
+ const factory ContactViewState({
+ @Default('') String country,
+ @Default('') String channel,
+ @Default('') String name,
+ @Default('') String email,
+ @Default('') String subject,
+ @Default('') String body,
+ @Default('') String errorMessage,
+ @Default('') String emailError,
+ }) = _ContactViewState;
+}
\ No newline at end of file
diff --git a/modules/legacy/modules/customer_service/lib/src/presentation/state/contact_view_state.freezed.dart b/modules/legacy/modules/customer_service/lib/src/presentation/state/contact_view_state.freezed.dart
new file mode 100644
index 00000000..31f58ae1
--- /dev/null
+++ b/modules/legacy/modules/customer_service/lib/src/presentation/state/contact_view_state.freezed.dart
@@ -0,0 +1,292 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// coverage:ignore-file
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'contact_view_state.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+// dart format off
+T _$identity(T value) => value;
+/// @nodoc
+mixin _$ContactViewState {
+
+ String get country; String get channel; String get name; String get email; String get subject; String get body; String get errorMessage; String get emailError;
+/// Create a copy of ContactViewState
+/// with the given fields replaced by the non-null parameter values.
+@JsonKey(includeFromJson: false, includeToJson: false)
+@pragma('vm:prefer-inline')
+$ContactViewStateCopyWith get copyWith => _$ContactViewStateCopyWithImpl(this as ContactViewState, _$identity);
+
+
+
+@override
+bool operator ==(Object other) {
+ return identical(this, other) || (other.runtimeType == runtimeType&&other is ContactViewState&&(identical(other.country, country) || other.country == country)&&(identical(other.channel, channel) || other.channel == channel)&&(identical(other.name, name) || other.name == name)&&(identical(other.email, email) || other.email == email)&&(identical(other.subject, subject) || other.subject == subject)&&(identical(other.body, body) || other.body == body)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.emailError, emailError) || other.emailError == emailError));
+}
+
+
+@override
+int get hashCode => Object.hash(runtimeType,country,channel,name,email,subject,body,errorMessage,emailError);
+
+@override
+String toString() {
+ return 'ContactViewState(country: $country, channel: $channel, name: $name, email: $email, subject: $subject, body: $body, errorMessage: $errorMessage, emailError: $emailError)';
+}
+
+
+}
+
+/// @nodoc
+abstract mixin class $ContactViewStateCopyWith<$Res> {
+ factory $ContactViewStateCopyWith(ContactViewState value, $Res Function(ContactViewState) _then) = _$ContactViewStateCopyWithImpl;
+@useResult
+$Res call({
+ String country, String channel, String name, String email, String subject, String body, String errorMessage, String emailError
+});
+
+
+
+
+}
+/// @nodoc
+class _$ContactViewStateCopyWithImpl<$Res>
+ implements $ContactViewStateCopyWith<$Res> {
+ _$ContactViewStateCopyWithImpl(this._self, this._then);
+
+ final ContactViewState _self;
+ final $Res Function(ContactViewState) _then;
+
+/// Create a copy of ContactViewState
+/// with the given fields replaced by the non-null parameter values.
+@pragma('vm:prefer-inline') @override $Res call({Object? country = null,Object? channel = null,Object? name = null,Object? email = null,Object? subject = null,Object? body = null,Object? errorMessage = null,Object? emailError = null,}) {
+ return _then(_self.copyWith(
+country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
+as String,channel: null == channel ? _self.channel : channel // ignore: cast_nullable_to_non_nullable
+as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
+as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable
+as String,subject: null == subject ? _self.subject : subject // ignore: cast_nullable_to_non_nullable
+as String,body: null == body ? _self.body : body // ignore: cast_nullable_to_non_nullable
+as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
+as String,emailError: null == emailError ? _self.emailError : emailError // ignore: cast_nullable_to_non_nullable
+as String,
+ ));
+}
+
+}
+
+
+/// Adds pattern-matching-related methods to [ContactViewState].
+extension ContactViewStatePatterns on ContactViewState {
+/// A variant of `map` that fallback to returning `orElse`.
+///
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case final Subclass value:
+/// return ...;
+/// case _:
+/// return orElse();
+/// }
+/// ```
+
+@optionalTypeArgs TResult maybeMap(TResult Function( _ContactViewState value)? $default,{required TResult orElse(),}){
+final _that = this;
+switch (_that) {
+case _ContactViewState() when $default != null:
+return $default(_that);case _:
+ return orElse();
+
+}
+}
+/// A `switch`-like method, using callbacks.
+///
+/// Callbacks receives the raw object, upcasted.
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case final Subclass value:
+/// return ...;
+/// case final Subclass2 value:
+/// return ...;
+/// }
+/// ```
+
+@optionalTypeArgs TResult map(TResult Function( _ContactViewState value) $default,){
+final _that = this;
+switch (_that) {
+case _ContactViewState():
+return $default(_that);case _:
+ throw StateError('Unexpected subclass');
+
+}
+}
+/// A variant of `map` that fallback to returning `null`.
+///
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case final Subclass value:
+/// return ...;
+/// case _:
+/// return null;
+/// }
+/// ```
+
+@optionalTypeArgs TResult? mapOrNull(TResult? Function( _ContactViewState value)? $default,){
+final _that = this;
+switch (_that) {
+case _ContactViewState() when $default != null:
+return $default(_that);case _:
+ return null;
+
+}
+}
+/// A variant of `when` that fallback to an `orElse` callback.
+///
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case Subclass(:final field):
+/// return ...;
+/// case _:
+/// return orElse();
+/// }
+/// ```
+
+@optionalTypeArgs TResult maybeWhen(TResult Function( String country, String channel, String name, String email, String subject, String body, String errorMessage, String emailError)? $default,{required TResult orElse(),}) {final _that = this;
+switch (_that) {
+case _ContactViewState() when $default != null:
+return $default(_that.country,_that.channel,_that.name,_that.email,_that.subject,_that.body,_that.errorMessage,_that.emailError);case _:
+ return orElse();
+
+}
+}
+/// A `switch`-like method, using callbacks.
+///
+/// As opposed to `map`, this offers destructuring.
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case Subclass(:final field):
+/// return ...;
+/// case Subclass2(:final field2):
+/// return ...;
+/// }
+/// ```
+
+@optionalTypeArgs TResult when(TResult Function( String country, String channel, String name, String email, String subject, String body, String errorMessage, String emailError) $default,) {final _that = this;
+switch (_that) {
+case _ContactViewState():
+return $default(_that.country,_that.channel,_that.name,_that.email,_that.subject,_that.body,_that.errorMessage,_that.emailError);case _:
+ throw StateError('Unexpected subclass');
+
+}
+}
+/// A variant of `when` that fallback to returning `null`
+///
+/// It is equivalent to doing:
+/// ```dart
+/// switch (sealedClass) {
+/// case Subclass(:final field):
+/// return ...;
+/// case _:
+/// return null;
+/// }
+/// ```
+
+@optionalTypeArgs TResult? whenOrNull(TResult? Function( String country, String channel, String name, String email, String subject, String body, String errorMessage, String emailError)? $default,) {final _that = this;
+switch (_that) {
+case _ContactViewState() when $default != null:
+return $default(_that.country,_that.channel,_that.name,_that.email,_that.subject,_that.body,_that.errorMessage,_that.emailError);case _:
+ return null;
+
+}
+}
+
+}
+
+/// @nodoc
+
+
+class _ContactViewState implements ContactViewState {
+ const _ContactViewState({this.country = '', this.channel = '', this.name = '', this.email = '', this.subject = '', this.body = '', this.errorMessage = '', this.emailError = ''});
+
+
+@override@JsonKey() final String country;
+@override@JsonKey() final String channel;
+@override@JsonKey() final String name;
+@override@JsonKey() final String email;
+@override@JsonKey() final String subject;
+@override@JsonKey() final String body;
+@override@JsonKey() final String errorMessage;
+@override@JsonKey() final String emailError;
+
+/// Create a copy of ContactViewState
+/// with the given fields replaced by the non-null parameter values.
+@override @JsonKey(includeFromJson: false, includeToJson: false)
+@pragma('vm:prefer-inline')
+_$ContactViewStateCopyWith<_ContactViewState> get copyWith => __$ContactViewStateCopyWithImpl<_ContactViewState>(this, _$identity);
+
+
+
+@override
+bool operator ==(Object other) {
+ return identical(this, other) || (other.runtimeType == runtimeType&&other is _ContactViewState&&(identical(other.country, country) || other.country == country)&&(identical(other.channel, channel) || other.channel == channel)&&(identical(other.name, name) || other.name == name)&&(identical(other.email, email) || other.email == email)&&(identical(other.subject, subject) || other.subject == subject)&&(identical(other.body, body) || other.body == body)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.emailError, emailError) || other.emailError == emailError));
+}
+
+
+@override
+int get hashCode => Object.hash(runtimeType,country,channel,name,email,subject,body,errorMessage,emailError);
+
+@override
+String toString() {
+ return 'ContactViewState(country: $country, channel: $channel, name: $name, email: $email, subject: $subject, body: $body, errorMessage: $errorMessage, emailError: $emailError)';
+}
+
+
+}
+
+/// @nodoc
+abstract mixin class _$ContactViewStateCopyWith<$Res> implements $ContactViewStateCopyWith<$Res> {
+ factory _$ContactViewStateCopyWith(_ContactViewState value, $Res Function(_ContactViewState) _then) = __$ContactViewStateCopyWithImpl;
+@override @useResult
+$Res call({
+ String country, String channel, String name, String email, String subject, String body, String errorMessage, String emailError
+});
+
+
+
+
+}
+/// @nodoc
+class __$ContactViewStateCopyWithImpl<$Res>
+ implements _$ContactViewStateCopyWith<$Res> {
+ __$ContactViewStateCopyWithImpl(this._self, this._then);
+
+ final _ContactViewState _self;
+ final $Res Function(_ContactViewState) _then;
+
+/// Create a copy of ContactViewState
+/// with the given fields replaced by the non-null parameter values.
+@override @pragma('vm:prefer-inline') $Res call({Object? country = null,Object? channel = null,Object? name = null,Object? email = null,Object? subject = null,Object? body = null,Object? errorMessage = null,Object? emailError = null,}) {
+ return _then(_ContactViewState(
+country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
+as String,channel: null == channel ? _self.channel : channel // ignore: cast_nullable_to_non_nullable
+as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
+as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable
+as String,subject: null == subject ? _self.subject : subject // ignore: cast_nullable_to_non_nullable
+as String,body: null == body ? _self.body : body // ignore: cast_nullable_to_non_nullable
+as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
+as String,emailError: null == emailError ? _self.emailError : emailError // ignore: cast_nullable_to_non_nullable
+as String,
+ ));
+}
+
+
+}
+
+// dart format on
diff --git a/modules/legacy/modules/customer_service/lib/src/providers/customer_service_repository_provider.dart b/modules/legacy/modules/customer_service/lib/src/providers/customer_service_repository_provider.dart
new file mode 100644
index 00000000..a8d0bd56
--- /dev/null
+++ b/modules/legacy/modules/customer_service/lib/src/providers/customer_service_repository_provider.dart
@@ -0,0 +1,8 @@
+/*
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+final customerServiceRepositoryProvider = Provider((ref) {
+ final remote = ref.read(customerServiceRemoteDatasourceProvider);
+ return CustomerServiceRepositoryImpl(remote);
+});
+*/
diff --git a/modules/legacy/modules/dashboard_shell/lib/legacy_dashboard_builder.dart b/modules/legacy/modules/dashboard_shell/lib/legacy_dashboard_builder.dart
deleted file mode 100644
index b9f78bb0..00000000
--- a/modules/legacy/modules/dashboard_shell/lib/legacy_dashboard_builder.dart
+++ /dev/null
@@ -1 +0,0 @@
-// TODO Implement this library.
\ No newline at end of file
diff --git a/modules/legacy/modules/hub/lib/src/features/hub/presentation/hub_screen.dart b/modules/legacy/modules/hub/lib/src/features/hub/presentation/hub_screen.dart
index 47eb7af8..408f99ab 100644
--- a/modules/legacy/modules/hub/lib/src/features/hub/presentation/hub_screen.dart
+++ b/modules/legacy/modules/hub/lib/src/features/hub/presentation/hub_screen.dart
@@ -51,7 +51,7 @@ class HubScreen extends ConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AppSectionButton(
- onPressed: (){},
+ onPressed: (){navigationContract.pushTo(AppRoutes.customerService);},
icon: SFIcons.customerService,
text: I18n.customerService),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 7)),
diff --git a/modules/legacy/modules/hub/lib/src/features/hub/presentation/state/hub_view_state.dart b/modules/legacy/modules/hub/lib/src/features/hub/presentation/state/hub_view_state.dart
index f7968fa4..a1c6dbde 100644
--- a/modules/legacy/modules/hub/lib/src/features/hub/presentation/state/hub_view_state.dart
+++ b/modules/legacy/modules/hub/lib/src/features/hub/presentation/state/hub_view_state.dart
@@ -1,7 +1,6 @@
import 'package:hub/src/features/hub/domain/entities/device_entity.dart';
import 'package:hub/src/features/hub/domain/entities/position_entity.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
-import 'package:latlong2/latlong.dart';
part 'hub_view_state.freezed.dart';
diff --git a/modules/legacy/modules/hub/pubspec.yaml b/modules/legacy/modules/hub/pubspec.yaml
index 44a59922..aa7ceb59 100644
--- a/modules/legacy/modules/hub/pubspec.yaml
+++ b/modules/legacy/modules/hub/pubspec.yaml
@@ -33,7 +33,7 @@ dependencies:
sdk: flutter
#modules dependencies go here
dashboard_shell:
- path: ../../modules/dashboard_shell
+ path: ../../../../modules/dashboard_shell
#packages dependencies go here
design_system:
path: ../../../../packages/design_system
diff --git a/packages/navigation/lib/app_routes.dart b/packages/navigation/lib/app_routes.dart
index 45ba35c8..c4ddab10 100644
--- a/packages/navigation/lib/app_routes.dart
+++ b/packages/navigation/lib/app_routes.dart
@@ -17,6 +17,8 @@ class AppRoutes {
static const legacy = '/legacy';
+ static const customerService = '$legacy/customer_service';
+
static const legacyDashboard = '$legacy/dashboard';
static const dashboardHub = '$legacyDashboard/hub';
diff --git a/packages/sf_localizations/assets/l10n/en.json b/packages/sf_localizations/assets/l10n/en.json
index b58dd5dc..4ea242cc 100755
--- a/packages/sf_localizations/assets/l10n/en.json
+++ b/packages/sf_localizations/assets/l10n/en.json
@@ -139,5 +139,17 @@
"watchesOnMap": "Smartwatch on the map",
"home": "Home",
"location": "Location",
- "chat": "Chat"
+ "chat": "Chat",
+ "channelOnline": "SF online shop",
+ "channelAmazon": "Amazon",
+ "channelStore": "Physical store",
+ "other": "Other",
+ "contactTitle": "Contact us",
+ "selectCountry": "Choose your country",
+ "selectChannel": "Purchase channel",
+ "enterName": "Enter your name",
+ "enterEmail": "Enter your email",
+ "enterSubject": "Your message subject",
+ "enterMessage": "Your message",
+ "sendEmail": "Send!"
}
\ No newline at end of file
diff --git a/packages/sf_localizations/assets/l10n/es.json b/packages/sf_localizations/assets/l10n/es.json
index c2e75c56..48151842 100644
--- a/packages/sf_localizations/assets/l10n/es.json
+++ b/packages/sf_localizations/assets/l10n/es.json
@@ -139,5 +139,17 @@
"watchesOnMap": "Reloj inteligente en el mapa",
"home": "Inicio",
"location": "Mapa",
- "chat": "Chat"
+ "chat": "Chat",
+ "channelOnline": "Tienda online SF",
+ "channelAmazon": "Amazon",
+ "channelStore": "Tienda física",
+ "other": "Otro",
+ "contactTitle": "Contacta con nosotros",
+ "selectCountry": "Selecciona tu país",
+ "selectChannel": "Caal de compra",
+ "enterName": "Introduce tu nombre",
+ "enterEmail": "Introduce tu correo electrónico",
+ "enterSubject": "Asunto del mensaje",
+ "enterMessage": "Tu mensaje",
+ "sendEmail": "!Enviar!"
}
\ No newline at end of file
diff --git a/packages/sf_localizations/lib/src/generated/i18n.dart b/packages/sf_localizations/lib/src/generated/i18n.dart
index bd01ab75..764d1d8a 100755
--- a/packages/sf_localizations/lib/src/generated/i18n.dart
+++ b/packages/sf_localizations/lib/src/generated/i18n.dart
@@ -170,4 +170,16 @@ class I18n {
static const String home = 'home';
static const String location = 'location';
static const String chat = 'chat';
+ static const String channelOnline = 'channelOnline';
+ static const String channelAmazon = 'channelAmazon';
+ static const String channelStore = 'channelStore';
+ static const String other = 'other';
+ static const String contactTitle = 'contactTitle';
+ static const String selectCountry = 'selectCountry';
+ static const String selectChannel = 'selectChannel';
+ static const String enterName = 'enterName';
+ static const String enterEmail = 'enterEmail';
+ static const String enterSubject = 'enterSubject';
+ static const String enterMessage = 'enterMessage';
+ static const String sendEmail = 'sendEmail';
}