diff --git a/api/client.jsx b/api/client.jsx index 22c061c..cc1b734 100644 --- a/api/client.jsx +++ b/api/client.jsx @@ -1,13 +1,13 @@ -import axios from 'axios'; +import axios from "axios"; const apiClient = axios.create({ - baseURL: 'https://codefirst.iut.uca.fr/containers/Optifit-optifit-ef-api/api/v1/', - timeout: 10000, - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - // ajoute ici les headers supplémentaires, ex. Auth - }, + baseURL: + "https://codefirst.iut.uca.fr/containers/Optifit-optifit-ef-api/api/v1/", + timeout: 10000, + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, }); export default apiClient; diff --git a/api/services/abstract.service.tsx b/api/services/abstract.service.tsx index 967be3f..3552918 100644 --- a/api/services/abstract.service.tsx +++ b/api/services/abstract.service.tsx @@ -1,30 +1,41 @@ import AsyncStorage from "@react-native-async-storage/async-storage"; +import { getItemAsync } from "expo-secure-store"; import apiClient from "../client"; import { AUTH } from "../endpoints"; -const ACCESS_TOKEN_PATH = "access-token"; -const REFRESH_TOKEN_PATH = "refresh-token"; - export abstract class AbstractService { - protected async request(callback: () => Promise): Promise { - try { - return await callback(); - } catch (error: any) { - if (error.response?.status === 401) { - const success = await this.tryRefreshToken(); - if (success) { - return await callback(); - } else { - throw new Error("Session expired"); - } - } - throw error; - } + protected IDP_URL = + "https://codefirst.iut.uca.fr/containers/Optifit-optifit-ef-api/api/v1"; + protected URL = + "https://codefirst.iut.uca.fr/containers/Optifit-optifit-ef-api/api/v1"; + + protected CLIENT_ID = "mobile-app"; + protected CLIENT_SECRET = "super-secret"; + + protected SCOPES = + "openid profile training.generate training.read offline_access"; + + protected ACCESS_TOKEN_PATH = "access-token"; + protected REFRESH_TOKEN_PATH = "refresh-token"; + + protected async request(path: string, options: RequestInit = {}) { + const token = await getItemAsync(this.ACCESS_TOKEN_PATH); + const res = await fetch(`${this.URL}${path}`, { + ...options, + headers: { + ...(options.headers || {}), + Authorization: `Bearer ${token}`, + }, + }); + + return token && res.status === 401 + ? await this.tryRefreshToken() + : await res.json(); } private async tryRefreshToken(): Promise { try { - const refreshToken = await AsyncStorage.getItem(REFRESH_TOKEN_PATH); + const refreshToken = await AsyncStorage.getItem(this.REFRESH_TOKEN_PATH); if (!refreshToken) return false; @@ -34,18 +45,15 @@ export abstract class AbstractService { const { accessToken, refreshToken: newRefreshToken } = response.data; - // Save new tokens - await AsyncStorage.setItem(ACCESS_TOKEN_PATH, accessToken); - await AsyncStorage.setItem("refreshToken", newRefreshToken); + await AsyncStorage.setItem(this.ACCESS_TOKEN_PATH, accessToken); + await AsyncStorage.setItem(this.REFRESH_TOKEN_PATH, newRefreshToken); - // Update apiClient headers apiClient.defaults.headers.common[ "Authorization" ] = `Bearer ${accessToken}`; return true; } catch (e) { - console.error("Refresh token failed", e); return false; } } diff --git a/api/services/exercice/ExercicesServices.jsx b/api/services/exercice/ExercicesServices.jsx deleted file mode 100644 index 619e4b4..0000000 --- a/api/services/exercice/ExercicesServices.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import apiClient from "@/api/client"; -import { EXERCICES } from "@/api/endpoints"; -import { AbstractService } from "../abstract.service"; - -export class ExerciceService extends AbstractService { - async getExercices() { - return this.request(async () => { - const response = await apiClient.get(EXERCICES.GETALL); - return response.data.data; - }); - } -} diff --git a/api/services/exercice/exercice.service.api.tsx b/api/services/exercice/exercice.service.api.tsx new file mode 100644 index 0000000..2c2e58a --- /dev/null +++ b/api/services/exercice/exercice.service.api.tsx @@ -0,0 +1,14 @@ +import { EXERCICES } from "@/api/endpoints"; +import { Workout } from "@/model/Workout"; +import { AbstractService } from "../abstract.service"; +import { IExerciceInterface } from "./exercice.service.interface"; + +export class ExerciceAPIService + extends AbstractService + implements IExerciceInterface +{ + async getExercices(): Promise { + const data = await this.request(EXERCICES.GETALL); + return data.data.map((item: any) => Workout.fromJson(item)); + } +} diff --git a/api/services/exercice/exercice.service.interface.tsx b/api/services/exercice/exercice.service.interface.tsx new file mode 100644 index 0000000..b99b782 --- /dev/null +++ b/api/services/exercice/exercice.service.interface.tsx @@ -0,0 +1,3 @@ +export interface IExerciceInterface { + getExercices(): Promise; +} diff --git a/api/services/exercice/exercice.service.stub.tsx b/api/services/exercice/exercice.service.stub.tsx new file mode 100644 index 0000000..d45b8de --- /dev/null +++ b/api/services/exercice/exercice.service.stub.tsx @@ -0,0 +1,11 @@ +import { AbstractService } from "../abstract.service"; +import { IExerciceInterface } from "./exercice.service.interface"; + +export class ExerciceStubService + extends AbstractService + implements IExerciceInterface +{ + async getExercices() { + return []; + } +} diff --git a/api/services/user/user.service.api.tsx b/api/services/user/user.service.api.tsx index d905d64..b9b55d5 100644 --- a/api/services/user/user.service.api.tsx +++ b/api/services/user/user.service.api.tsx @@ -1,14 +1,31 @@ -import apiClient from "@/api/client"; -import { AUTH } from "@/api/endpoints"; import { User } from "@/model/User"; +import { setItemAsync } from "expo-secure-store"; import { AbstractService as AbstractAPIService } from "../abstract.service"; import { IUserService } from "./user.service.interface"; export class UserAPIService extends AbstractAPIService implements IUserService { async login(email: string, password: string): Promise { - return this.request(async () => { - const response = await apiClient.get(AUTH.LOGIN); - return response.data.data; + const body = new URLSearchParams({ + grant_type: "password", + client_id: email, + client_secret: this.CLIENT_SECRET, + email, + password, + scope: this.SCOPES, }); + + const res = await fetch(`${this.IDP_URL}/connect/token`, { + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: body.toString(), + }); + + if (!res.ok) throw new Error(`Auth failed: ${res.status}`); + const json = await res.json(); + + await setItemAsync(this.ACCESS_TOKEN_PATH, json.access_token); + await setItemAsync(this.REFRESH_TOKEN_PATH, json.refresh_token); + + return json; } } diff --git a/api/services/user/user.service.stub.tsx b/api/services/user/user.service.stub.tsx index 16d9388..f3de839 100644 --- a/api/services/user/user.service.stub.tsx +++ b/api/services/user/user.service.stub.tsx @@ -3,166 +3,154 @@ import { IUserService } from "./user.service.interface"; export class UserStubService implements IUserService { private readonly users: User[] = [ - new User( - "Alice", - 28, - 165, - 58, - false, - "alice.png", - 3, - "Perdre du poids", - [], - "YOGA", - "GOOD", - "BEGINNER", - "test@1.com", - "password1" - ), - new User( - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - "test@2.com", - "password2" - ), - new User( - "Charlie", - 22, - 172, - 70, - true, - "charlie.png", - 2, - "Se remettre en forme", - [], - "BIKING", - "GOOD", - "BEGINNER", - "test@3.com", - "password3" - ), - new User( - "Diana", - 31, - 160, - 55, - false, - "diana.png", - 5, - "Préparer un marathon", - [], - "RUNNING", - "GOOD", - "VERY_SPORTY", - "test@4.com", - "password4" - ), - new User( - "Ethan", - 40, - 180, - 88, - true, - "ethan.png", - 1, - "Maintenir sa forme", - ["MIGRAINE"], - "WALKING", - "BAD", - "SPORTY", - "test@5.com", - "password5" - ), - new User( - "Fiona", - 26, - 167, - 62, - false, - "fiona.png", - 3, - "Renforcer le dos", - ["MIGRAINE"], - "CARDIO", - "BAD", - "BEGINNER", - "test@6.com", - "password6" - ), - new User( - "George", - 30, - 185, - 90, - true, - "george.png", - 4, - "Perdre du gras", - [], - "BIKING", - "TERRIBLE", - "SPORTY", - "test@7.com", - "password7" - ), - new User( - "Hanna", - 24, - 158, - 54, - false, - "hanna.png", - 2, - "Se tonifier", - [], - "RANDO", - "GOOD", - "BEGINNER", - "test@8.com", - "password8" - ), - new User( - "Ivan", - 50, - 175, - 95, - true, - "ivan.png", - 1, - "Rééducation", - ["ARTHROSE"], - "WALKING", - "BAD", - "BEGINNER", - "test@9.com", - "password9" - ), - new User( - "Julia", - 29, - 170, - 60, - false, - "julia.png", - 3, - "Rester active", - [], - "ELSE", - "GOOD", - "SPORTY", - "test@10.com", - "password10" - ), + new User({ + name: "Alice", + age: 28, + height: 165, + weight: 58, + sexe: false, + logo: "alice.png", + nbSessionPerWeek: 3, + goal: "Perdre du poids", + healthProblems: [], + sport: "YOGA", + sleepLevel: "GOOD", + sportLevel: "BEGINNER", + email: "test@1.com", + password: "password1", + }), + new User({ + email: "test@2.com", + password: "password2", + }), + new User({ + name: "Charlie", + age: 22, + height: 172, + weight: 70, + sexe: true, + logo: "charlie.png", + nbSessionPerWeek: 2, + goal: "Se remettre en forme", + healthProblems: [], + sport: "BIKING", + sleepLevel: "GOOD", + sportLevel: "BEGINNER", + email: "test@3.com", + password: "password3", + }), + new User({ + name: "Diana", + age: 31, + height: 160, + weight: 55, + sexe: false, + logo: "diana.png", + nbSessionPerWeek: 5, + goal: "Préparer un marathon", + healthProblems: [], + sport: "RUNNING", + sleepLevel: "GOOD", + sportLevel: "VERY_SPORTY", + email: "test@4.com", + password: "password4", + }), + new User({ + name: "Ethan", + age: 40, + height: 180, + weight: 88, + sexe: true, + logo: "ethan.png", + nbSessionPerWeek: 1, + goal: "Maintenir sa forme", + healthProblems: ["MIGRAINE"], + sport: "WALKING", + sleepLevel: "BAD", + sportLevel: "SPORTY", + email: "test@5.com", + password: "password5", + }), + new User({ + name: "Fiona", + age: 26, + height: 167, + weight: 62, + sexe: false, + logo: "fiona.png", + nbSessionPerWeek: 3, + goal: "Renforcer le dos", + healthProblems: ["MIGRAINE"], + sport: "CARDIO", + sleepLevel: "BAD", + sportLevel: "BEGINNER", + email: "test@6.com", + password: "password6", + }), + new User({ + name: "George", + age: 30, + height: 185, + weight: 90, + sexe: true, + logo: "george.png", + nbSessionPerWeek: 4, + goal: "Perdre du gras", + healthProblems: [], + sport: "BIKING", + sleepLevel: "TERRIBLE", + sportLevel: "SPORTY", + email: "test@7.com", + password: "password7", + }), + new User({ + name: "Hanna", + age: 24, + height: 158, + weight: 54, + sexe: false, + logo: "hanna.png", + nbSessionPerWeek: 2, + goal: "Se tonifier", + healthProblems: [], + sport: "RANDO", + sleepLevel: "GOOD", + sportLevel: "BEGINNER", + email: "test@8.com", + password: "password8", + }), + new User({ + name: "Ivan", + age: 50, + height: 175, + weight: 95, + sexe: true, + logo: "ivan.png", + nbSessionPerWeek: 1, + goal: "Rééducation", + healthProblems: ["ARTHROSE"], + sport: "WALKING", + sleepLevel: "BAD", + sportLevel: "BEGINNER", + email: "test@9.com", + password: "password9", + }), + new User({ + name: "Julia", + age: 29, + height: 170, + weight: 60, + sexe: false, + logo: "julia.png", + nbSessionPerWeek: 3, + goal: "Rester active", + healthProblems: [], + sport: "ELSE", + sleepLevel: "GOOD", + sportLevel: "SPORTY", + email: "test@10.com", + password: "password10", + }), ]; async login(email: string, password: string): Promise { @@ -171,7 +159,7 @@ export class UserStubService implements IUserService { ); if (!user) { - throw new Error("No user."); + throw new Error("User not found."); } return user; diff --git a/app/(auth)/reset-password-with-email.tsx b/app/(auth)/reset-password-with-email.tsx index 1041196..fbdbd70 100644 --- a/app/(auth)/reset-password-with-email.tsx +++ b/app/(auth)/reset-password-with-email.tsx @@ -1,13 +1,13 @@ +import BackButton from "@/components/BackButton"; +import { EMPTY_FIELD, INVALID_EMAIL } from "@/components/Errors"; +import FormError from "@/components/form/FormError"; +import TextInput from "@/components/form/FormInput"; +import CodeSent from "@/components/modals/CodeSent"; import Button from "@/components/ui/Button"; import Screen from "@/components/ui/Screen"; import Text from "@/components/ui/Text"; -import TextInput from "@/components/form/FormInput"; -import BackButton from "@/components/BackButton"; -import { View } from "react-native"; import React from "react"; -import CodeSent from "@/components/modals/CodeSent"; -import FormError from "@/components/form/FormError"; -import { EMPTY_FIELD, INVALID_EMAIL } from "@/components/Errors"; +import { View } from "react-native"; import { isEmail } from "validator"; export default function ResetPasswordWithEmail() { diff --git a/app/(tabs)/(add)/AddScreen.tsx b/app/(tabs)/(add)/AddScreen.tsx index 04f5310..905c59f 100644 --- a/app/(tabs)/(add)/AddScreen.tsx +++ b/app/(tabs)/(add)/AddScreen.tsx @@ -1,16 +1,16 @@ -import {SafeAreaView, View, Text} from "react-native"; -import React from "react"; import InternalError from "@/components/error/InternalErrorProblem"; +import React from "react"; +import { SafeAreaView, View } from "react-native"; export default function AddScreen() { - return ( - - - - - - ); + return ( + + + + + + ); } //Welcome to Add Screen -// We will do it soon \ No newline at end of file +// We will do it soon diff --git a/app/(tabs)/(add)/_layout.tsx b/app/(tabs)/(add)/_layout.tsx index 1209652..4164c04 100644 --- a/app/(tabs)/(add)/_layout.tsx +++ b/app/(tabs)/(add)/_layout.tsx @@ -1,11 +1,13 @@ -import React from "react"; import { Stack } from "expo-router"; +import React from "react"; export default function RootoLayout() { return ( - + ); diff --git a/app/(tabs)/(exercice)/ExercicesScreen.tsx b/app/(tabs)/(exercice)/ExercicesScreen.tsx index 0e86150..545b2ee 100644 --- a/app/(tabs)/(exercice)/ExercicesScreen.tsx +++ b/app/(tabs)/(exercice)/ExercicesScreen.tsx @@ -1,11 +1,11 @@ -import { ExerciceService } from "@/api/services/exercice/ExercicesServices"; +import { ExerciceAPIService } from "@/api/services/exercice/exercice.service.api"; import WorkoutCardComponent from "@/components/WorkoutCardComponent"; import { Workout } from "@/model/Workout"; import { useRouter } from "expo-router"; import React, { useEffect, useState } from "react"; import { FlatList, Text, TouchableOpacity, View } from "react-native"; -const service = new ExerciceService(); +const service = new ExerciceAPIService(); export default function ExercicesScreen() { const [exercices, setExercices] = useState([]); diff --git a/app/(tabs)/(exercice)/WorkoutScreen.tsx b/app/(tabs)/(exercice)/WorkoutScreen.tsx index 01bd46a..9d38197 100644 --- a/app/(tabs)/(exercice)/WorkoutScreen.tsx +++ b/app/(tabs)/(exercice)/WorkoutScreen.tsx @@ -1,10 +1,10 @@ -import { ExerciceService } from "@/api/services/exercice/ExercicesServices"; +import { ExerciceAPIService } from "@/api/services/exercice/exercice.service.api"; import WorkoutPresentationComponent from "@/components/WorkoutPresentationComponent"; import { useRouter } from "expo-router"; import React, { useEffect, useState } from "react"; import { ActivityIndicator, Text, View } from "react-native"; -const service = new ExerciceService(); +const service = new ExerciceAPIService(); export default function WorkoutScreen() { const router = useRouter(); diff --git a/app/(tabs)/(exercice)/_layout.tsx b/app/(tabs)/(exercice)/_layout.tsx index 278487e..578e177 100644 --- a/app/(tabs)/(exercice)/_layout.tsx +++ b/app/(tabs)/(exercice)/_layout.tsx @@ -1,19 +1,17 @@ +import { Stack } from "expo-router"; import React from "react"; -import {Stack} from "expo-router"; export default function RootoLayout() { - return ( - - + return ( + + - - - - - ); - -} \ No newline at end of file + + + ); +} diff --git a/app/(tabs)/(help)/HelpsScreen.tsx b/app/(tabs)/(help)/HelpsScreen.tsx index c478b22..f9506f9 100644 --- a/app/(tabs)/(help)/HelpsScreen.tsx +++ b/app/(tabs)/(help)/HelpsScreen.tsx @@ -1,14 +1,13 @@ -import {SafeAreaView, Text, View} from "react-native"; -import React from "react"; import Blocked from "@/components/error/BlockedProblem"; +import React from "react"; +import { SafeAreaView, View } from "react-native"; export default function HelpsScreen() { - return ( - - - - - - - ); -} \ No newline at end of file + return ( + + + + + + ); +} diff --git a/app/(tabs)/(help)/_layout.tsx b/app/(tabs)/(help)/_layout.tsx index 3d0f8ca..ffefda4 100644 --- a/app/(tabs)/(help)/_layout.tsx +++ b/app/(tabs)/(help)/_layout.tsx @@ -1,15 +1,14 @@ +import { Stack } from "expo-router"; import React from "react"; -import {Stack} from "expo-router"; -import HelpsScreen from "@/app/(tabs)/(help)/HelpsScreen"; export default function RootoLayout() { - return ( - - - - - ); - -} \ No newline at end of file + return ( + + + + ); +} diff --git a/app/(tabs)/(home)/HomeScreen.tsx b/app/(tabs)/(home)/HomeScreen.tsx index a979719..16ee8c5 100644 --- a/app/(tabs)/(home)/HomeScreen.tsx +++ b/app/(tabs)/(home)/HomeScreen.tsx @@ -1,4 +1,4 @@ -import { ExerciceService } from "@/api/services/exercice/ExercicesServices"; +import { ExerciceAPIService } from "@/api/services/exercice/exercice.service.api"; import ActivitiesComponent from "@/components/ActivitiesComponent"; import CalendarComponent from "@/components/CalendarComponent"; import WelcomeComponent from "@/components/WelcomeComponent"; @@ -8,7 +8,7 @@ import { Workout } from "@/model/Workout"; import React, { useEffect, useState } from "react"; import { ScrollView, Text, View } from "react-native"; -const service = new ExerciceService(); +const service = new ExerciceAPIService(); export default function HomeScreen() { const [exercices, setExercices] = useState([]); diff --git a/app/(tabs)/(home)/_layout.tsx b/app/(tabs)/(home)/_layout.tsx index 07cd7de..8dbbcc5 100644 --- a/app/(tabs)/(home)/_layout.tsx +++ b/app/(tabs)/(home)/_layout.tsx @@ -3,9 +3,11 @@ import React from "react"; export default function RootoLayout() { return ( - + ); diff --git a/app/(tabs)/(home)/index.tsx b/app/(tabs)/(home)/index.tsx index 26a2e2a..3467af5 100644 --- a/app/(tabs)/(home)/index.tsx +++ b/app/(tabs)/(home)/index.tsx @@ -1,10 +1,6 @@ -import {SafeAreaView, Text, View} from "react-native"; -import React from "react"; import HomeScreen from "@/app/(tabs)/(home)/HomeScreen"; +import React from "react"; export default function App() { - - return ( - - ); -} \ No newline at end of file + return ; +} diff --git a/app/(tabs)/(profil)/ProfileScreen.tsx b/app/(tabs)/(profil)/ProfileScreen.tsx index 67d5bab..17a0036 100644 --- a/app/(tabs)/(profil)/ProfileScreen.tsx +++ b/app/(tabs)/(profil)/ProfileScreen.tsx @@ -1,8 +1,8 @@ -import { SafeAreaView, Text, View } from "react-native"; -import React from "react"; import Button from "@/components/ui/Button"; import { useSession } from "@/ctx"; import { router } from "expo-router"; +import React from "react"; +import { SafeAreaView, Text, View } from "react-native"; export default function ProfileScreen() { const { signOut } = useSession(); diff --git a/app/(tabs)/(profil)/_layout.tsx b/app/(tabs)/(profil)/_layout.tsx index 104e7e8..c3e1f32 100644 --- a/app/(tabs)/(profil)/_layout.tsx +++ b/app/(tabs)/(profil)/_layout.tsx @@ -3,10 +3,12 @@ import React from "react"; export default function RootoLayout() { return ( - - - + + + ); } diff --git a/app/(utility)/Error.tsx b/app/(utility)/Error.tsx index 259cedb..7950895 100644 --- a/app/(utility)/Error.tsx +++ b/app/(utility)/Error.tsx @@ -1,37 +1,50 @@ -import Text from "@/components/ui/Text"; -import React, {forwardRef} from "react"; -import {Image, View, ViewProps} from "react-native"; import BackButton from "@/components/BackButton"; +import Text from "@/components/ui/Text"; +import React, { forwardRef } from "react"; +import { Image, View, ViewProps } from "react-native"; -import {Entypo} from "@expo/vector-icons"; +import { Entypo } from "@expo/vector-icons"; -export interface ProblemProps extends ViewProps{ - picture: string; - problem: string; - description: string; - information: string; - isVisible?: boolean; +export interface ProblemProps extends ViewProps { + picture: string; + problem: string; + description: string; + information: string; + isVisible?: boolean; } -export default forwardRef (({className, ...Props}, ref) => { - return ( - - - - +export default forwardRef(({ className, ...Props }, ref) => { + return ( + + + + - - - + + + - {Props.problem} - {Props.description} - - - - {Props.information} - - + + {" "} + {Props.problem}{" "} + + + {" "} + {Props.description}{" "} + + + + + + {" "} + {Props.information}{" "} + - ); -}); \ No newline at end of file + + + ); +}); diff --git a/components/ActivitiesComponent.tsx b/components/ActivitiesComponent.tsx index 61ce961..0a5725d 100644 --- a/components/ActivitiesComponent.tsx +++ b/components/ActivitiesComponent.tsx @@ -1,113 +1,114 @@ -import {Text, TouchableOpacity, View} from "react-native"; -import {CurveType, LineChart} from "react-native-gifted-charts"; -import React, {useRef, useState} from "react"; +import React, { useRef, useState } from "react"; +import { Text, TouchableOpacity, View } from "react-native"; +import { CurveType, LineChart } from "react-native-gifted-charts"; export default function ActivitiesComponent() { - const ref = useRef(null) - const [dateSelected, setDateSelected] = useState('1d'); - const [lineData, setLineData] = useState([ - {value: 4}, - {value: 14}, - {value: 8}, - {value: 38}, - {value: 36}, - {value: 28}, - ]); + const ref = useRef(null); + const [dateSelected, setDateSelected] = useState("1d"); + const [lineData, setLineData] = useState([ + { value: 4 }, + { value: 14 }, + { value: 8 }, + { value: 38 }, + { value: 36 }, + { value: 28 }, + ]); - const months = ['1d','1w','1m','1y','All'] - const changeMonthSelected = (ind: number) => { - const selectedMonth = months[ind]; - setDateSelected(selectedMonth); + const months = ["1d", "1w", "1m", "1y", "All"]; + const changeMonthSelected = (ind: number) => { + const selectedMonth = months[ind]; + setDateSelected(selectedMonth); - // Update lineData based on the selected month - let newData: React.SetStateAction<{ value: number; }[]>; - switch (selectedMonth) { - case '1d': - newData = [ - {value: 4}, - {value: 14}, - {value: 8}, - {value: 38}, - {value: 36}, - {value: 28}, - ]; - break; - case '1w': - newData = [ - {value: 8}, - {value: 14}, - {value: 8}, - {value: 38}, - {value: 14}, - {value: 28}, - {value: 4}, - ]; - break; - case '1m': - newData = [ - {value: 10}, - {value: 20}, - {value: 30}, - {value: 40}, - {value: 50}, - {value: 60}, - ]; - break; - case '1y': - newData = [ - {value: 15}, - {value: 25}, - {value: 35}, - {value: 45}, - {value: 55}, - {value: 65}, - ]; - break; - case 'All': - newData = [ - {value: 5}, - {value: 15}, - {value: 25}, - {value: 35}, - {value: 45}, - {value: 55}, - ]; - break; - default: - newData = []; - } - setLineData(newData); - }; + // Update lineData based on the selected month + let newData: React.SetStateAction<{ value: number }[]>; + switch (selectedMonth) { + case "1d": + newData = [ + { value: 4 }, + { value: 14 }, + { value: 8 }, + { value: 38 }, + { value: 36 }, + { value: 28 }, + ]; + break; + case "1w": + newData = [ + { value: 8 }, + { value: 14 }, + { value: 8 }, + { value: 38 }, + { value: 14 }, + { value: 28 }, + { value: 4 }, + ]; + break; + case "1m": + newData = [ + { value: 10 }, + { value: 20 }, + { value: 30 }, + { value: 40 }, + { value: 50 }, + { value: 60 }, + ]; + break; + case "1y": + newData = [ + { value: 15 }, + { value: 25 }, + { value: 35 }, + { value: 45 }, + { value: 55 }, + { value: 65 }, + ]; + break; + case "All": + newData = [ + { value: 5 }, + { value: 15 }, + { value: 25 }, + { value: 35 }, + { value: 45 }, + { value: 55 }, + ]; + break; + default: + newData = []; + } + setLineData(newData); + }; - return ( - - - {months.map((item, index) => { - return ( - changeMonthSelected(index)}> - {months[index]} - - ); - })} - - - - ); -} \ No newline at end of file + return ( + + + {months.map((item, index) => { + return ( + changeMonthSelected(index)} + > + {months[index]} + + ); + })} + + + + ); +} diff --git a/components/CalendarComponent.tsx b/components/CalendarComponent.tsx index b784d3d..21f8c4a 100644 --- a/components/CalendarComponent.tsx +++ b/components/CalendarComponent.tsx @@ -1,51 +1,51 @@ -import {FlatList, TouchableOpacity,Text, View} from "react-native"; -import {useState} from "react"; import dayjs from "dayjs"; +import { useState } from "react"; +import { FlatList, Text, TouchableOpacity, View } from "react-native"; export default function CalendarComponent() { - const [selectedDay] = useState(dayjs().date()); + const [selectedDay] = useState(dayjs().date()); - const days = Array.from({ length: 7 }, (_, index) => { - const day = dayjs().add(index, "day"); - return { - id: day.date(), - label: day.format("ddd"), - }; - }); + const days = Array.from({ length: 7 }, (_, index) => { + const day = dayjs().add(index, "day"); + return { + id: day.date(), + label: day.format("ddd"), + }; + }); - return ( - - item.id.toString()} - showsHorizontalScrollIndicator={false} - contentContainerStyle={{ gap: 12 }} // Espacement entre les items - renderItem={({ item }) => ( - - - {item.label} - - - {item.id} - - - )} - /> - - ); -} \ No newline at end of file + return ( + + item.id.toString()} + showsHorizontalScrollIndicator={false} + contentContainerStyle={{ gap: 12 }} // Espacement entre les items + renderItem={({ item }) => ( + + + {item.label} + + + {item.id} + + + )} + /> + + ); +} diff --git a/components/CheckBox.tsx b/components/CheckBox.tsx index 6827d96..4ed814b 100644 --- a/components/CheckBox.tsx +++ b/components/CheckBox.tsx @@ -1,6 +1,3 @@ -import React, { forwardRef } from "react"; -import { View, TouchableOpacity, ViewProps } from "react-native"; -import Text from "./ui/Text"; import { AntDesign, Entypo, @@ -8,6 +5,8 @@ import { Ionicons, MaterialCommunityIcons, } from "@expo/vector-icons"; +import React, { forwardRef } from "react"; +import { TouchableOpacity, View, ViewProps } from "react-native"; import { AntDesignIconNames, CommunityIconNames, @@ -16,6 +15,7 @@ import { FontAwesomeIconNames, IonIconNames, } from "./Icons"; +import Text from "./ui/Text"; export type CheckBoxDirection = "row" | "col"; diff --git a/components/Errors.tsx b/components/Errors.tsx index b85981e..8726830 100644 --- a/components/Errors.tsx +++ b/components/Errors.tsx @@ -5,6 +5,8 @@ export const NOT_MATCHING_PASSWORD = "Les mots de passe sont différents"; export const NOT_FOUND = "Ressource introuvable :<"; export const NO_INTERNET = "Pas de connexion à internet"; export const INTERNAL_ERROR = "Erreur interne, veuillez nous pardonner"; -export const MAINTENANCE = "Le serveur est en maintenance, veuillez réessayer plus tard"; -export const NOT_AUTHORIZED = "Vous n'êtes pas autorisé à accéder à cette ressource"; -export const FEATURE_LOCKED = "Cette fonctionnalité est verrouillée"; \ No newline at end of file +export const MAINTENANCE = + "Le serveur est en maintenance, veuillez réessayer plus tard"; +export const NOT_AUTHORIZED = + "Vous n'êtes pas autorisé à accéder à cette ressource"; +export const FEATURE_LOCKED = "Cette fonctionnalité est verrouillée"; diff --git a/components/LinearProgressBar.tsx b/components/LinearProgressBar.tsx index 88f1650..f0574ce 100644 --- a/components/LinearProgressBar.tsx +++ b/components/LinearProgressBar.tsx @@ -49,9 +49,7 @@ export default function LinearProgressBar({ duration = 10 }) { {isRunning ? "En cours..." : "Play"} diff --git a/components/WorkoutCardComponent.tsx b/components/WorkoutCardComponent.tsx index 4d45455..0014c72 100644 --- a/components/WorkoutCardComponent.tsx +++ b/components/WorkoutCardComponent.tsx @@ -7,23 +7,15 @@ import { ImageBackground, TouchableOpacity, View } from "react-native"; interface WorkoutCardComponentProps { exercise?: Workout; - background?: String; + background?: string; height?: number; } export default function WorkoutCardComponent({ exercise, - height, - background, -}: WorkoutCardComponentProps) { - const style = () => { - return `h-full rounded-2xl overflow-hidden ${background ?? "bg-black"}`; - }; - - const styleImage = () => { - return `w-full h-full `; - }; +}: Readonly) { const router = useRouter(); + return ( ) { const router = useRouter(); return ( - {/* Permet de pousser le reste du contenu vers le bas */} - {/* Texte en bas */} {workout.nbSeries} x {workout.nbRepetitions} @@ -52,7 +50,6 @@ export default function WorkoutPresentationComponent({ - {/* Barre de progression */} diff --git a/components/error/BlockedProblem.tsx b/components/error/BlockedProblem.tsx index be92d2a..1c59680 100644 --- a/components/error/BlockedProblem.tsx +++ b/components/error/BlockedProblem.tsx @@ -1,15 +1,16 @@ -import React from "react"; -import {FEATURE_LOCKED} from "@/components/Errors"; import Error from "@/app/(utility)/Error"; +//@ts-ignore import blockedPict from "@/assets/images/Blocked.png"; +import { FEATURE_LOCKED } from "@/components/Errors"; +import React from "react"; export default function Blocked() { - return ( - - ); -} \ No newline at end of file + return ( + + ); +} diff --git a/components/error/InternalErrorProblem.tsx b/components/error/InternalErrorProblem.tsx index 371a33f..dc952b6 100644 --- a/components/error/InternalErrorProblem.tsx +++ b/components/error/InternalErrorProblem.tsx @@ -1,15 +1,16 @@ -import React from "react"; -import {INTERNAL_ERROR} from "@/components/Errors"; import Error from "@/app/(utility)/Error"; +//@ts-ignore import internalErrorPict from "@/assets/images/InternalError.png"; +import { INTERNAL_ERROR } from "@/components/Errors"; +import React from "react"; export default function InternalError() { - return ( - - ); -} \ No newline at end of file + return ( + + ); +} diff --git a/components/error/MaintenanceProblem.tsx b/components/error/MaintenanceProblem.tsx index f1de7f6..21924d9 100644 --- a/components/error/MaintenanceProblem.tsx +++ b/components/error/MaintenanceProblem.tsx @@ -1,15 +1,16 @@ -import React from "react"; -import {MAINTENANCE} from "@/components/Errors"; import Error from "@/app/(utility)/Error"; +//@ts-ignore import maintenancePict from "@/assets/images/Maintenance.png"; +import { MAINTENANCE } from "@/components/Errors"; +import React from "react"; export default function Maintenance() { - return ( - - ); -} \ No newline at end of file + return ( + + ); +} diff --git a/components/error/NoInternetProblem.tsx b/components/error/NoInternetProblem.tsx index 70544a7..ccd9328 100644 --- a/components/error/NoInternetProblem.tsx +++ b/components/error/NoInternetProblem.tsx @@ -1,15 +1,16 @@ -import React from "react"; -import {NO_INTERNET} from "@/components/Errors"; import Error from "@/app/(utility)/Error"; +//@ts-ignore import noInternetPict from "@/assets/images/NoInternet.png"; +import { NO_INTERNET } from "@/components/Errors"; +import React from "react"; export default function NoInternet() { - return ( - - ); -} \ No newline at end of file + return ( + + ); +} diff --git a/components/error/NotAllowedProblem.tsx b/components/error/NotAllowedProblem.tsx index e9351db..f267fff 100644 --- a/components/error/NotAllowedProblem.tsx +++ b/components/error/NotAllowedProblem.tsx @@ -1,15 +1,16 @@ -import React from "react"; -import {NOT_AUTHORIZED} from "@/components/Errors"; import Error from "@/app/(utility)/Error"; +//@ts-ignore import notAllowedPict from "@/assets/images/NotAllowed.png"; +import { NOT_AUTHORIZED } from "@/components/Errors"; +import React from "react"; export default function NotAllowedProblem() { - return ( - - ); -} \ No newline at end of file + return ( + + ); +} diff --git a/components/error/NotFoundProblem.tsx b/components/error/NotFoundProblem.tsx index 21cab7b..0180a71 100644 --- a/components/error/NotFoundProblem.tsx +++ b/components/error/NotFoundProblem.tsx @@ -1,15 +1,16 @@ -import React from "react"; -import {NOT_FOUND} from "@/components/Errors"; import Error from "@/app/(utility)/Error"; +//@ts-ignore import notFoundPict from "@/assets/images/NotFound.png"; +import { NOT_FOUND } from "@/components/Errors"; +import React from "react"; export default function NotFound() { - return ( - - ); -} \ No newline at end of file + return ( + + ); +} diff --git a/components/form/LoginForm.tsx b/components/form/LoginForm.tsx index 06c7fb1..4925777 100644 --- a/components/form/LoginForm.tsx +++ b/components/form/LoginForm.tsx @@ -35,13 +35,13 @@ export default React.forwardRef( if (isEmail(email)) { if (password != "") { validateForm(); - const user = userService.login(email, password); + const user = userService.login(email, password).catch((e) => { + invalidateForm("Email ou mot de passe incorrect"); + }); user.then((u) => { if (u) { signIn(u); router.replace("/HomeScreen"); - } else { - invalidateForm("Email ou mot de passe incorrect"); } }); } else { diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx index 6d53996..6ecd9d7 100644 --- a/components/ui/Button.tsx +++ b/components/ui/Button.tsx @@ -1,9 +1,9 @@ +import { AntDesign } from "@expo/vector-icons"; import React from "react"; import { TouchableOpacity, TouchableOpacityProps, View } from "react-native"; -import CustomText from "./Text"; -import { AntDesign } from "@expo/vector-icons"; -import { AntDesignIconNames } from "../Icons"; import { Size } from "../Constants"; +import { AntDesignIconNames } from "../Icons"; +import CustomText from "./Text"; export type ButtonStyle = "default" | "outline" | "secondary"; diff --git a/components/ui/Screen.tsx b/components/ui/Screen.tsx index 4a49ba9..4e08bc5 100644 --- a/components/ui/Screen.tsx +++ b/components/ui/Screen.tsx @@ -1,6 +1,6 @@ +import React from "react"; import { View, ViewProps } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; -import React from "react"; export default React.forwardRef( ({ children, ...props }, ref): React.ReactElement => { diff --git a/components/ui/SegmentedControl.tsx b/components/ui/SegmentedControl.tsx index 4cca1f6..6a82d5d 100644 --- a/components/ui/SegmentedControl.tsx +++ b/components/ui/SegmentedControl.tsx @@ -1,7 +1,7 @@ -import React from "react"; import SegmentedControl, { SegmentedControlProps, } from "@react-native-segmented-control/segmented-control"; +import React from "react"; export default React.forwardRef( (props, ref): React.ReactElement => { diff --git a/components/ui/Slider.tsx b/components/ui/Slider.tsx index 2af3987..4e60069 100644 --- a/components/ui/Slider.tsx +++ b/components/ui/Slider.tsx @@ -1,5 +1,5 @@ -import React from "react"; import Slider, { SliderProps } from "@react-native-community/slider"; +import React from "react"; export default React.forwardRef( ({ ...props }, ref): React.ReactElement => { diff --git a/ctx.tsx b/ctx.tsx index 8185d37..3125957 100644 --- a/ctx.tsx +++ b/ctx.tsx @@ -27,7 +27,7 @@ export function SessionProvider(props: Readonly) { { - setSessionStr(JSON.stringify(user)); + setSessionStr(user.toJSON()); }, signOut: () => { setSessionStr(null); diff --git a/model/User.ts b/model/User.ts index 4e3d5e0..25be84a 100644 --- a/model/User.ts +++ b/model/User.ts @@ -6,37 +6,52 @@ import { } from "./enums/Enums"; export class User { - private _name: string | undefined; - private _age: number | undefined; - private _height: number | undefined; - private _weight: number | undefined; - private _sexe: boolean | undefined; // true = Male, false = Female - private _logo: string | undefined; - private _nbSessionPerWeek: number | undefined; - private _goal: string | undefined; - private _healthProblems: EHealthProblem[] | undefined; - private _sport: ESport | undefined; - private _sleepLevel: ESleepLevel | undefined; - private _sportLevel: ESportLevel | undefined; - private _email: string; - private _password: string; - - constructor( - name: string | undefined, - age: number | undefined, - height: number | undefined, - weight: number | undefined, - sexe: boolean | undefined, - logo: string | undefined, - nbSessionPerWeek: number | undefined, - goal: string | undefined, - healthProblems: EHealthProblem[] | undefined, - sport: ESport | undefined, - sleepLevel: ESleepLevel | undefined, - sportLevel: ESportLevel | undefined, - email: string, - password: string - ) { + private _name?: string; + private _age?: number; + private _height?: number; + private _weight?: number; + private _sexe?: boolean; // true = Male, false = Female + private _logo?: string; + private _nbSessionPerWeek?: number; + private _goal?: string; + private _healthProblems?: EHealthProblem[]; + private _sport?: ESport; + private _sleepLevel?: ESleepLevel; + private _sportLevel?: ESportLevel; + private _email?: string; + private _password?: string; + + constructor({ + name, + age, + height, + weight, + sexe, + logo, + nbSessionPerWeek, + goal, + healthProblems, + sport, + sleepLevel, + sportLevel, + email, + password, + }: { + name?: string; + age?: number; + height?: number; + weight?: number; + sexe?: boolean; + logo?: string; + nbSessionPerWeek?: number; + goal?: string; + healthProblems?: EHealthProblem[]; + sport?: ESport; + sleepLevel?: ESleepLevel; + sportLevel?: ESportLevel; + email?: string; + password?: string; + } = {}) { this._name = name; this._age = age; this._height = height; @@ -82,7 +97,7 @@ export class User { return this._nbSessionPerWeek; } - get goals(): string | undefined { + get goal(): string | undefined { return this._goal; } @@ -90,7 +105,7 @@ export class User { return this._healthProblems; } - get sports(): ESport | undefined { + get sport(): ESport | undefined { return this._sport; } @@ -102,11 +117,11 @@ export class User { return this._sportLevel; } - get email(): string { + get email(): string | undefined { return this._email; } - get password(): string { + get password(): string | undefined { return this._password; } @@ -139,7 +154,7 @@ export class User { this._nbSessionPerWeek = value; } - set goals(value: string | undefined) { + set goal(value: string | undefined) { this._goal = value; } @@ -147,7 +162,7 @@ export class User { this._healthProblems = value; } - set sports(value: ESport | undefined) { + set sport(value: ESport | undefined) { this._sport = value; } @@ -184,21 +199,40 @@ export class User { } static fromJSON(json: any): User { - return new User( - json._name, - json._age, - json._height, - json._weight, - json._sexe, - json._logo, - json._nbSessionPerWeek, - json._goal, - json._healthProblems, - json._sport, - json._sleepLevel, - json._sportLevel, - json._email, - json._password - ); + return new User({ + name: json.name, + age: json.age, + height: json.height, + weight: json.weight, + sexe: json.sexe, + logo: json.logo, + nbSessionPerWeek: json.nbSessionPerWeek, + goal: json.goal, + healthProblems: json.healthProblems, + sport: json.sport, + sleepLevel: json.sleepLevel, + sportLevel: json.sportLevel, + email: json.email, + password: json.password, + }); + } + + toJSON(): any { + return JSON.stringify({ + name: this.name, + age: this.age, + height: this.height, + weight: this.weight, + sexe: this.sexe, + logo: this.logo, + nbSessionPerWeek: this.nbSessionPerWeek, + goal: this.goal, + healthProblems: this.healthProblems, + sport: this.sport, + sleepLevel: this.sleepLevel, + sportLevel: this.sportLevel, + email: this.email, + password: this.password, + }); } } diff --git a/model/Workout.ts b/model/Workout.ts index b1e28c4..605119c 100644 --- a/model/Workout.ts +++ b/model/Workout.ts @@ -1,10 +1,118 @@ -export interface Workout { - id: string; - name: string; - description: string; - duration: number; - image: string; - video: string; - nbSeries: number; - nbRepetitions: number; +export class Workout { + private _id?: string; + private _name?: string; + private _description?: string; + private _duration?: number; + private _image?: string; + private _video?: string; + private _nbSeries?: number; + private _nbRepetitions?: number; + + constructor({ + id, + name, + description, + duration, + image, + video, + nbSeries, + nbRepetitions, + }: { + id?: string; + name?: string; + description?: string; + duration?: number; + image?: string; + video?: string; + nbSeries?: number; + nbRepetitions?: number; + } = {}) { + this._id = id; + this._name = name; + this._description = description; + this._duration = duration; + this._image = image; + this._video = video; + this._nbSeries = nbSeries; + this._nbRepetitions = nbRepetitions; + } + + // Getters + get id(): string | undefined { + return this._id; + } + + get name(): string | undefined { + return this._name; + } + + get description(): string | undefined { + return this._description; + } + + get duration(): number | undefined { + return this._duration; + } + + get image(): string | undefined { + return this._image; + } + + get video(): string | undefined { + return this._video; + } + + get nbSeries(): number | undefined { + return this._nbSeries; + } + + get nbRepetitions(): number | undefined { + return this._nbRepetitions; + } + + // Setters + set id(value: string | undefined) { + this._id = value; + } + + set name(value: string | undefined) { + this._name = value; + } + + set description(value: string | undefined) { + this._description = value; + } + + set duration(value: number | undefined) { + this._duration = value; + } + + set image(value: string | undefined) { + this._image = value; + } + + set video(value: string | undefined) { + this._video = value; + } + + set nbSeries(value: number | undefined) { + this._nbSeries = value; + } + + set nbRepetitions(value: number | undefined) { + this._nbRepetitions = value; + } + + static fromJson(json: any): Workout { + return new Workout({ + id: json.id, + name: json.name, + description: json.description, + duration: json.duration, + image: json.image, + video: json.video, + nbSeries: json.nbSeries, + nbRepetitions: json.nbRepetitions, + }); + } } diff --git a/package-lock.json b/package-lock.json index 0cf18da..c001595 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "expo-linear-gradient": "^14.0.2", "expo-linking": "^7.0.5", "expo-router": "~5.0.7", - "expo-secure-store": "^14.0.1", + "expo-secure-store": "^14.2.3", "expo-splash-screen": "~0.30.8", "expo-status-bar": "^2.0.1", "expo-symbols": "~0.4.4", diff --git a/package.json b/package.json index f27b386..644bc68 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "expo-linear-gradient": "^14.0.2", "expo-linking": "^7.0.5", "expo-router": "~5.0.7", - "expo-secure-store": "^14.0.1", + "expo-secure-store": "^14.2.3", "expo-splash-screen": "~0.30.8", "expo-status-bar": "^2.0.1", "expo-symbols": "~0.4.4",