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")} + - + -