diff --git a/backend/src/place/place.controller.ts b/backend/src/place/place.controller.ts index b98b3ae8447043bd4718649dabc4f0298abc29ad..4af704793dae0195e75f9617ab00e0d0660995de 100644 --- a/backend/src/place/place.controller.ts +++ b/backend/src/place/place.controller.ts @@ -24,6 +24,8 @@ import { CustomAdminRequest } from 'src/auth/admin/interface/customAdminReq'; import { UpdatePlaceReqDto } from './dto/update-place.req.dto'; import { AuthAdminGuard } from 'src/auth/admin/authAdmin.guard'; import { GetPlaceDto } from './dto/get-place.dto'; +import { AuthUserGuard } from 'src/auth/user/authUser.guard'; +import { CustomUserRequest } from 'src/auth/user/interface/customUserReq'; @Controller('place') @ApiTags('Place') @@ -96,4 +98,13 @@ export class PlaceController { throw e; } } + + @Get('open/:idTown/:lang') + @ApiBearerAuth('jwt') + @ApiConsumes('json') + @UseGuards(AuthUserGuard) + @ApiParam({ name: 'idTown', type: Number }) + async findOpenPlaces(@Param('idTown') idTown: number, @Param('lang') lang: string, @Req() req: CustomUserRequest) { + return await this.placeService.findPlacesNotVisitedByUserAndOpenResponse(req.user.email, lang as LANGUAGES, idTown); + } } diff --git a/backend/src/place/place.module.ts b/backend/src/place/place.module.ts index 31f722214a648554fff4ed8f703ab3f75afb2449..2b6720f6a91ae596d310c6d7144bf3c8c67f1c30 100644 --- a/backend/src/place/place.module.ts +++ b/backend/src/place/place.module.ts @@ -16,6 +16,14 @@ import { JwtService } from '@nestjs/jwt'; import { EncryptionService } from 'src/auth/encryption/encryption.service'; import { Admin } from 'src/admin/entities/admin.entity'; import { AdminResetCode } from 'src/auth/admin/entitites/admin-reset-code.entity'; +import { AuthUserService } from 'src/auth/user/authUserservice'; +import { UserResetCode } from 'src/auth/user/entities/user-reset-code.entity'; +import { UserConfirmCode } from 'src/auth/user/entities/user-confirm-code.entity'; +import { UserService } from 'src/user/user.service'; +import { EmailService } from 'src/email/email.service'; +import { User } from 'src/user/entities/user.entity'; +import { CategoryService } from 'src/category/category.service'; +import { Category } from 'src/category/entities/category.entity'; @Module({ controllers: [PlaceController], @@ -28,6 +36,10 @@ import { AdminResetCode } from 'src/auth/admin/entitites/admin-reset-code.entity JwtService, EncryptionService, TownService, + AuthUserService, + UserService, + EmailService, + CategoryService, ], imports: [ TypeOrmModule.forFeature([ @@ -39,6 +51,10 @@ import { AdminResetCode } from 'src/auth/admin/entitites/admin-reset-code.entity State, Admin, AdminResetCode, + UserResetCode, + UserConfirmCode, + User, + Category, ]), TypeOrmModule, ], diff --git a/backend/src/place/place.service.ts b/backend/src/place/place.service.ts index 115604f7175fcb68604ecb2808e2ce9b1829285e..a480ddd3d4c0818c026184f0bac074bd5c599504 100644 --- a/backend/src/place/place.service.ts +++ b/backend/src/place/place.service.ts @@ -249,4 +249,25 @@ export class PlaceService { return placesWithAvailability.filter((place) => place !== null); } + + async findPlacesNotVisitedByUserAndOpenResponse(email: string, lang: LANGUAGES, idTown: number) { + const places: GetPlaceDto[] = await this.findPlacesNotVisitedByUserAndOpen(email, lang, idTown); + + const placesResponse = []; + for (const place of places) { + const visited: any = await this.dataSource + .getRepository(Visited) + .createQueryBuilder('visited') + .leftJoinAndSelect('visited.place', 'place') + .where('visited.user = :email', { email }) + .andWhere('place.idPlace = :idPlace', { idPlace: place.idPlace }) + .getOne(); + + placesResponse.push({ + ...place, + visited: visited ? true : false, + }); + } + return placesResponse; + } } diff --git a/backend/src/pointOfInterest/PointOfInterest.service.ts b/backend/src/pointOfInterest/PointOfInterest.service.ts index 8ec12a181d57561bee56aa60583c40f4491315bf..607e1100170a2f0ba14da19966e69ac912933205 100644 --- a/backend/src/pointOfInterest/PointOfInterest.service.ts +++ b/backend/src/pointOfInterest/PointOfInterest.service.ts @@ -48,11 +48,11 @@ export class PointOfInterestService { const insertedId = (await this.pointRepository.insert(createPointDto)).raw.insertId; const spanishAudio = await this.convertTextToSpeech( - `${createPointAndTradDto.contentES}\n${createPointAndTradDto.directionsES}`, + `${createPointAndTradDto.contentES}\n${createPointAndTradDto.directionsES || ''}`, LANGUAGES.ES, ); const englishAudio = await this.convertTextToSpeech( - `${createPointAndTradDto.contentEN}\n${createPointAndTradDto.directionsEN}`, + `${createPointAndTradDto.contentEN}\n${createPointAndTradDto.directionsEN || ''}`, LANGUAGES.EN, ); diff --git a/backend/src/route/dto/create-route-req.ts b/backend/src/route/dto/create-route-req.ts index 8e515934aa7c98c298e75fe55ff06de4e9a0a3b0..a8a2c4056b0b5b470e9f7164d26bd70376f5af55 100644 --- a/backend/src/route/dto/create-route-req.ts +++ b/backend/src/route/dto/create-route-req.ts @@ -7,3 +7,13 @@ export class CreateRouteReq { @ApiProperty({ name: 'end', type: Number }) end: number; } + +export class CreateCustomRouteReq { + @ApiProperty({ name: 'placesIds', type: [Number] }) + placesIds: number[]; + @ApiProperty({ name: 'start', type: Number }) + start: number; + + @ApiProperty({ name: 'end', type: Number }) + end: number; +} diff --git a/backend/src/route/route.controller.ts b/backend/src/route/route.controller.ts index aedd78d98c1fbeae8f404ba9af12c5ae121b1237..6a602c3cdd1db377ea450aeb5e38c84e0a557bc0 100644 --- a/backend/src/route/route.controller.ts +++ b/backend/src/route/route.controller.ts @@ -1,10 +1,10 @@ import { Controller, Param, UseGuards, Req, Query, Body, Post, Get, Patch } from '@nestjs/common'; import { RouteService } from './route.service'; -import { ApiBearerAuth, ApiBody, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger'; import { LANGUAGES } from 'src/shared/enum/languages.enum'; import { AuthUserGuard } from 'src/auth/user/authUser.guard'; import { CustomUserRequest } from 'src/auth/user/interface/customUserReq'; -import { CreateRouteReq } from './dto/create-route-req'; +import { CreateCustomRouteReq, CreateRouteReq } from './dto/create-route-req'; import { RouteStatus } from './entities/route.entity'; import { UpdateRouteStatusDto } from './dto/updateRouteStatus.dto'; @@ -82,4 +82,22 @@ export class RouteController { async updateRoute(@Body() updateRouteStatusDto: UpdateRouteStatusDto, @Param('idRoute') idRoute: number) { return await this.routeService.updateRoute(idRoute, updateRouteStatusDto.status); } + + @Post('custom/:idTown/:lang') + @ApiOperation({ summary: 'Create a custom route' }) + @ApiBearerAuth('jwt') + @UseGuards(AuthUserGuard) + async createCustomRoute( + @Req() req: CustomUserRequest, + @Body() createRouteReq: CreateCustomRouteReq, + @Param('lang') lang: LANGUAGES, + ) { + try { + const { email } = req.user; + return await this.routeService.createCustomRoute(email, lang as LANGUAGES, createRouteReq); + } catch (error) { + console.log(error); + return error; + } + } } diff --git a/backend/src/route/route.service.ts b/backend/src/route/route.service.ts index 3876497685a22b41bcdbc3f01bb878abeb775640..b1264a4c95739a05088dbb3341144e12d59b47f3 100644 --- a/backend/src/route/route.service.ts +++ b/backend/src/route/route.service.ts @@ -17,6 +17,7 @@ import { CreateTravelPlaceDto } from 'src/travel-place/dto/create-travel-place.d import { UserService } from 'src/user/user.service'; import { TravelPlace } from 'src/travel-place/entities/travel-place.entity'; import { ServerConstants } from 'src/constants/server.contants'; +import { CreateCustomRouteReq } from './dto/create-route-req'; @Injectable() export class RouteService { @@ -179,4 +180,59 @@ export class RouteService { return res; } + + async createCustomRoute(email: string, lang: LANGUAGES, createRouteReq: CreateCustomRouteReq) { + const { start, end, placesIds } = createRouteReq; + if (placesIds.length === 0) return []; + const place = await this.placeService.findOneAndTradAndAvailable(placesIds[0], lang); + const user = await this.userService.findOne(email); + + const town: Town = await this.townRepository.findOneBy({ townId: place.idTown }); + + const places = []; + for (const idPlace of placesIds) { + const place = await this.placeService.findOneAndTradAndAvailable(idPlace, lang); + places.push(place); + } + const placesNotVisited: RecommendPlace[] = places.map((place: GetPlaceDto) => { + return { + idPlace: place.idPlace, + openAt: place.openAt, + closeAt: place.closeAt, + categories: place.categories.map((category) => category.idCategory), + rating: 0, + }; + }); + + const system = new RecommendationsSystem([], placesNotVisited, start, end); + const chosen: RecommendPlace[] = system.recommend(); + const startDate: Date = new Date(); + startDate.setHours(start); + const endDate = new Date(); + endDate.setHours(end); + const createRouteDto: CreateRouteDto = { startDate, endDate, town, user }; + const idRoute = (await this.routeRepository.save(createRouteDto)).idRoute; + + for (const curRecommended of chosen) { + const place = await this.placeService.findOneAndTradAndAvailable(curRecommended.idPlace, lang); + const endDate = new Date(); + endDate.setHours(curRecommended.closeAt); + endDate.setMinutes(0); + const startDate = new Date(); + startDate.setHours(curRecommended.openAt); + startDate.setMinutes(0); + + const createTravelPlace: CreateTravelPlaceDto = { + ...place, + done: false, + idRoute, + endDate, + startDate, + }; + + await this.travelPlaceService.create(createTravelPlace); + } + + return await this.getRouteById(idRoute, email, lang); + } } diff --git a/backend/src/visited/templates/visit_template.ts b/backend/src/visited/templates/visit_template.ts index c16cab2f5c96612626254f39b89eef3239540dd4..6511106f519234fcfda25667e4c9a29f4ad34433 100644 --- a/backend/src/visited/templates/visit_template.ts +++ b/backend/src/visited/templates/visit_template.ts @@ -19,6 +19,8 @@ export const visit_template = (places: string[]) => ` position: relative; display: flex; flex-direction: row; + justify-content: center; + align-items: center; height: 300px; width: 450px; gap: 10px; @@ -29,7 +31,7 @@ export const visit_template = (places: string[]) => ` } .container .image { flex-grow: 1; - max-width: 100%; + max-width: 50%; height: 90%; border-radius: 10px; overflow: hidden; diff --git a/mobile/app/_layout.tsx b/mobile/app/_layout.tsx index 34580862ce1cf15d73c30d59cc26997b9016a299..0a2b0091972113e1420b55349b5d8da4968b600d 100644 --- a/mobile/app/_layout.tsx +++ b/mobile/app/_layout.tsx @@ -15,6 +15,7 @@ import { useSetUp, } from "../src/common/contexts/set_up_context"; import { useEffect } from "react"; +import { LANG_CONSTANTS } from "../src/lang/lang"; export default function Root() { return ( @@ -128,6 +129,21 @@ const MainLayout = () => { statusBarColor: LIGHT_THEME.color.primary, }} /> + (); + if (!townId) { + return null; + } + return ; +} diff --git a/mobile/app/state/[stateId]/town/_layout.tsx b/mobile/app/state/[stateId]/town/_layout.tsx index 891a48c487513dded04edf3ba0875ec419c4dd16..ce24299b26e3c4953c2ef3b65c189b1f9650bf58 100644 --- a/mobile/app/state/[stateId]/town/_layout.tsx +++ b/mobile/app/state/[stateId]/town/_layout.tsx @@ -1,6 +1,8 @@ -import { Stack } from "expo-router"; +import { router, Stack } from "expo-router"; import { LIGHT_THEME } from "../../../../src/common/const/theme"; import { useTranslation } from "react-i18next"; +import React from "react"; +import { MaterialCommunityIcons } from "@expo/vector-icons"; export default function Layout() { const { t } = useTranslation(); diff --git a/mobile/assets/GPLv3_Logo-removebg-preview.png b/mobile/assets/GPLv3_Logo-removebg-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..fef7cbc761a256c9077a3ef879d05dbc7ea3deb2 Binary files /dev/null and b/mobile/assets/GPLv3_Logo-removebg-preview.png differ diff --git a/mobile/assets/labsol-removebg-preview.png b/mobile/assets/labsol-removebg-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..2b0cf4d2c8ca55be4c4699c8535f38e31fbb1f28 Binary files /dev/null and b/mobile/assets/labsol-removebg-preview.png differ diff --git a/mobile/package-lock.json b/mobile/package-lock.json index 7382d52821c21fbc45764483e981e012a938645e..0ec993e864fe3ffbce381f0754d92d31a44090da 100644 --- a/mobile/package-lock.json +++ b/mobile/package-lock.json @@ -34,6 +34,7 @@ "i18n-js": "^4.4.3", "i18next": "^23.11.5", "metro-config": "^0.80.12", + "mime": "^4.0.4", "nativewind": "^2.0.11", "react": "18.2.0", "react-hook-form": "^7.51.2", @@ -5372,6 +5373,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@react-native-community/cli-tools/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/@react-native-community/cli-tools/node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -12296,14 +12309,18 @@ } }, "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", "bin": { - "mime": "cli.js" + "mime": "bin/cli.js" }, "engines": { - "node": ">=4.0.0" + "node": ">=16" } }, "node_modules/mime-db": { diff --git a/mobile/package.json b/mobile/package.json index b2ed53aafcbccd7aae0d599bd468a3dc50c94183..601123fab2201da9338927b1ae137cb882a5a204 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -21,10 +21,13 @@ "expo-camera": "~14.1.1", "expo-checkbox": "~2.7.0", "expo-constants": "~15.4.5", + "expo-crypto": "~12.8.1", + "expo-file-system": "~16.0.9", "expo-image-picker": "~14.7.1", "expo-linear-gradient": "~12.7.2", "expo-linking": "~6.2.2", "expo-localization": "~14.8.4", + "expo-media-library": "~15.9.2", "expo-router": "~3.4.8", "expo-screen-orientation": "~6.4.1", "expo-secure-store": "~12.8.1", @@ -32,6 +35,7 @@ "i18n-js": "^4.4.3", "i18next": "^23.11.5", "metro-config": "^0.80.12", + "mime": "^4.0.4", "nativewind": "^2.0.11", "react": "18.2.0", "react-hook-form": "^7.51.2", @@ -45,10 +49,7 @@ "react-native-reanimated": "~3.6.2", "react-native-safe-area-context": "4.8.2", "react-native-screens": "~3.29.0", - "react-native-svg": "14.1.0", - "expo-file-system": "~16.0.9", - "expo-media-library": "~15.9.2", - "expo-crypto": "~12.8.1" + "react-native-svg": "14.1.0" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/mobile/src/activity/components/activity_bottom_sheet.tsx b/mobile/src/activity/components/activity_bottom_sheet.tsx index 17054a27843cbc5911c0cb279c829cbaf6437810..45c585987b597888313d19e39c0dcdee04c53bdf 100644 --- a/mobile/src/activity/components/activity_bottom_sheet.tsx +++ b/mobile/src/activity/components/activity_bottom_sheet.tsx @@ -67,17 +67,9 @@ export const ActivityBottomSheet = ({ keyExtractor={(item) => item} ItemSeparatorComponent={() => } renderItem={({ item }) => ( - - {item} - + + {item} + )} /> )} @@ -146,4 +138,13 @@ const styles = StyleSheet.create({ show_more_text: { fontWeight: "600", }, + tag: { + backgroundColor: LIGHT_THEME.color.primary, + color: LIGHT_THEME.color.white, + height: 30, + paddingVertical: 5, + paddingHorizontal: 10, + borderRadius: 15, + marginHorizontal: 5, + }, }); diff --git a/mobile/src/activity/domain/datasources/activity_datasource.ts b/mobile/src/activity/domain/datasources/activity_datasource.ts index ea2fe13974d9bb60d588479f0d3b25247f0282f0..6e7e701f71f872bcf5d5a09f767d03159a32249d 100644 --- a/mobile/src/activity/domain/datasources/activity_datasource.ts +++ b/mobile/src/activity/domain/datasources/activity_datasource.ts @@ -1,6 +1,11 @@ import { ActivityPlaceEntity } from "../entities/activity_place_entity"; export interface ActivityDataSource { - getPlaceActivity(activityId: number, townId: number, stateId: number, placeNumber: number): Promise; - rankActivity(activityId: number, rank: number): Promise; -} \ No newline at end of file + getPlaceActivity( + activityId: number, + townId: number, + stateId: number, + placeNumber: number + ): Promise; + rankActivity(activityId: number, rank: number): Promise; +} diff --git a/mobile/src/activity/hooks/useGetOpenActivities.ts b/mobile/src/activity/hooks/useGetOpenActivities.ts new file mode 100644 index 0000000000000000000000000000000000000000..3050394f844da804ada163d1360c5bf86943452b --- /dev/null +++ b/mobile/src/activity/hooks/useGetOpenActivities.ts @@ -0,0 +1,12 @@ +import { useDataContext } from "../../common/contexts/data_context"; +import { useGet } from "../../common/hooks/useGet"; +import { ActivityInfoEntity } from "../domain/entities/activity_info_entity"; + +export const useGetOpenActivities = (townId: number) => { + const { statesRepository } = useDataContext(); + const callback = async () => { + return (await statesRepository!.getOpenActivities(townId)) || []; + }; + const { data, requestStatus } = useGet(callback); + return { activities: data, requestStatus }; +}; diff --git a/mobile/src/activity/infrastructure/datasources/prod/activity_datasource.ts b/mobile/src/activity/infrastructure/datasources/prod/activity_datasource.ts index 2d2fb3d871d553a7045da9609557960195bc2c35..a2478ba93b3a9e258282b8fb1bb9c4556b30cf9d 100644 --- a/mobile/src/activity/infrastructure/datasources/prod/activity_datasource.ts +++ b/mobile/src/activity/infrastructure/datasources/prod/activity_datasource.ts @@ -13,7 +13,6 @@ export class ActivityDatasourceProd implements ActivityDataSource { idPlace: activityId, rating: rank, }); - console.info("rankActivity", status); if (status !== 201) { throw new Error("Error al calificar la actividad"); } @@ -30,7 +29,6 @@ export class ActivityDatasourceProd implements ActivityDataSource { if (status !== 200) { throw new Error("Error al obtener la información del lugar"); } - console.log(data); return activityPlaceModelToEntity(data); } } diff --git a/mobile/src/activity/screens/activity_point.tsx b/mobile/src/activity/screens/activity_point.tsx index ff99b59a4677b1c49011700ff788771dea9f169d..010b3c79cd8d2e3d8c4e285d9bfb3664600d726d 100644 --- a/mobile/src/activity/screens/activity_point.tsx +++ b/mobile/src/activity/screens/activity_point.tsx @@ -14,6 +14,7 @@ import { useRankActivity } from "../hooks/useRankActivity"; import { useGetActivityPoint } from "../hooks/useGetActivityPoint"; import { FloatingBackButton } from "../../common/components/floating_back_button"; import { Ionicons } from "@expo/vector-icons"; +import { useTranslation } from "react-i18next"; const touristGuide = require("../../../assets/guide.gif"); @@ -32,6 +33,7 @@ export const ActivityPointScreen = memo( stateId, placeNumber: id, }); + const { t } = useTranslation(); const { onUnmount } = useAudio(); const { openRatingModal, closeRatingModal, ratingModal, rankActivity } = @@ -89,14 +91,14 @@ export const ActivityPointScreen = memo( - Activity Point + {t("activityPointScreen.activityPointLavel")} - End Activity + {t("activityPointScreen.endActivityButton")} @@ -124,7 +126,9 @@ export const ActivityPointScreen = memo( {data.directions && ( <> - Directions + + {t("activityPointScreen.directionsLabel")} + {data.directions.content} @@ -134,7 +138,7 @@ export const ActivityPointScreen = memo( style={styles.endActivityButtonText} onPress={doNextActivity} > - Next Activity + {t("activityPointScreen.nextActivityButton")} @@ -145,14 +149,14 @@ export const ActivityPointScreen = memo( style={styles.endActivityButtonText} onPress={openRatingModal} > - End Activity + {t("activityPointScreen.endActivityButton")} )} - {!data.directions && ratingModal && ( + {ratingModal && ( ; @@ -9,16 +10,17 @@ interface CodeFormProps { } export const CodeForm = ({ setValue, getNewResetCode }: CodeFormProps) => { + const { t } = useTranslation(); const onTextChange = (value: string) => { setValue("code", value); }; return ( - Introduce el código de verificación + {t("code_form.insertVerificationCode")} {getNewResetCode && ( - Obtener nuevo código + {t("code_form.resendCode")} )} ); diff --git a/mobile/src/auth/components/login_form.tsx b/mobile/src/auth/components/login_form.tsx index c96d403d12bda2272c300bfda6880726dd4f99f6..dc85758605238d260b46c2f3520bc85d3f8c3bc4 100644 --- a/mobile/src/auth/components/login_form.tsx +++ b/mobile/src/auth/components/login_form.tsx @@ -82,7 +82,7 @@ export const LoginForm = ({ control, onSubmit }: LoginFormProps) => { style={{ width: "100%", textAlign: "right" }} > - Recuperar contraseña + {LANG.t("loginScreen.forgotPasswordButton")} diff --git a/mobile/src/auth/components/reset_password_form.tsx b/mobile/src/auth/components/reset_password_form.tsx index f585d51a82fda631b24dd3279735352b2c6b057d..e26070c904b59293896aaf0e26d335304ecd2d28 100644 --- a/mobile/src/auth/components/reset_password_form.tsx +++ b/mobile/src/auth/components/reset_password_form.tsx @@ -1,26 +1,31 @@ import { View, Text, StyleSheet } from "react-native"; import { CustomTextInput } from "../../common/components/form/text_input"; import { Control, Controller } from "react-hook-form"; -import { ResetPasswordFormValues } from "../pages/reset_password_page"; +import { ResetPasswordFormValues } from "../hooks/useResetPassword"; +import { useTranslation } from "react-i18next"; interface ResetPasswordFormProps { control: Control; } export const ResetPasswordForm = ({ control }: ResetPasswordFormProps) => { + const { t } = useTranslation(); return ( - Para reestablecer tu contraseña te enviaremos un email + {t("code_form.toResetPassword")} ( + render={({ + field: { onChange, onBlur, value }, + formState: { errors }, + }) => ( ; @@ -21,9 +22,10 @@ interface SignUpFormProps { } export const SignUpForm = ({ control, onSubmit }: SignUpFormProps) => { + const { t } = useTranslation(); return ( - Sign Up + {t("registerScreen.registerButton")} { formState: { errors }, }) => ( { /> )} rules={{ - required: "Name is required", + required: t("formsErrors.nameIsRequired"), }} /> { formState: { errors }, }) => ( { /> )} rules={{ - required: "Last name is required", + required: t("formsErrors.lastNameIsRequired"), }} /> { formState: { errors }, }) => ( { /> )} rules={{ - required: "Email is required", + required: t("formsErrors.emailIsRequired"), pattern: { value: /\S+@\S+\.\S+/, message: "Invalid email" }, }} - /> { formState: { errors }, }) => ( )} - rules={{ required: "Password is required" }} + rules={{ required: t("formsErrors.passwordIsRequired") }} /> { formState: { errors }, }) => ( { }} /> )} - rules={{ required: "Confirm password is required" }} + rules={{ required: t("formsErrors.requiredField") }} /> ( )} defaultValue=" " - /> - Sign Up + + {t("registerScreen.registerButton")} + - + -