@ -0,0 +1,9 @@
|
||||
import { Stack } from "expo-router";
|
||||
|
||||
export default function QuizLayout() {
|
||||
return (
|
||||
<Stack initialRouteName="quiz">
|
||||
<Stack.Screen name="quiz" options={{ headerShown: false }} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
import BackButton from "@/components/BackButton";
|
||||
import { toBgColor, toTextColor } from "@/components/Constants";
|
||||
import ActivityQuestion, {
|
||||
ActivityQuestionRef,
|
||||
} from "@/components/quiz/ActivityQuestion";
|
||||
import AgeQuestion, { AgeQuestionRef } from "@/components/quiz/AgeQuestion";
|
||||
import BeginnerQuestion, {
|
||||
BeginnerQuestionRef,
|
||||
} from "@/components/quiz/BeginnerQuestion";
|
||||
import FrequencyQuestion, {
|
||||
FrequencyQuestionRef,
|
||||
} from "@/components/quiz/FrequencyQuestion";
|
||||
import GenderQuestion, {
|
||||
GenderQuestionRef,
|
||||
} from "@/components/quiz/GenderQuestion";
|
||||
import GoalQuestion, { GoalQuestionRef } from "@/components/quiz/GoalQuestion";
|
||||
import HeightQuestion, {
|
||||
HeightQuestionRef,
|
||||
} from "@/components/quiz/HeightQuestion";
|
||||
import SleepQuestion, {
|
||||
SleepQuestionRef,
|
||||
} from "@/components/quiz/SleepQuestion";
|
||||
import SportQuestion, {
|
||||
SportQuestionRef,
|
||||
} from "@/components/quiz/SportQuestion";
|
||||
import WeightQuestion, {
|
||||
WeightQuestionRef,
|
||||
} from "@/components/quiz/WeightQuestion";
|
||||
import Button from "@/components/ui/Button";
|
||||
import Screen from "@/components/ui/Screen";
|
||||
import Text from "@/components/ui/Text";
|
||||
import { useSession } from "@/ctx";
|
||||
import { useRouter } from "expo-router";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Quiz() {
|
||||
const { signIn, session } = useSession();
|
||||
const router = useRouter();
|
||||
|
||||
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
||||
const goalRef = useRef<GoalQuestionRef>(null);
|
||||
const genderRef = useRef<GenderQuestionRef>(null);
|
||||
const weightRef = useRef<WeightQuestionRef>(null);
|
||||
const heightRef = useRef<HeightQuestionRef>(null);
|
||||
const ageRef = useRef<AgeQuestionRef>(null);
|
||||
const beginnerRef = useRef<BeginnerQuestionRef>(null);
|
||||
const activityRef = useRef<ActivityQuestionRef>(null);
|
||||
const frequencyRef = useRef<FrequencyQuestionRef>(null);
|
||||
const sportQuestionRef = useRef<SportQuestionRef>(null);
|
||||
const sleepQuestionRef = useRef<SleepQuestionRef>(null);
|
||||
|
||||
interface Question<T = any> {
|
||||
component: React.ForwardRefExoticComponent<T & React.RefAttributes<any>>;
|
||||
props: T;
|
||||
}
|
||||
|
||||
const questions: Question[] = [
|
||||
{ component: GoalQuestion, props: { ref: goalRef } },
|
||||
{ component: GenderQuestion, props: { ref: genderRef } },
|
||||
{
|
||||
component: WeightQuestion,
|
||||
props: { ref: weightRef },
|
||||
},
|
||||
{ component: HeightQuestion, props: { ref: heightRef } },
|
||||
{ component: AgeQuestion, props: { ref: ageRef } },
|
||||
{ component: BeginnerQuestion, props: { ref: beginnerRef } },
|
||||
{ component: ActivityQuestion, props: { ref: activityRef } },
|
||||
//{ component: IllnessQuestion, props: {} },
|
||||
{
|
||||
component: FrequencyQuestion,
|
||||
props: { ref: frequencyRef, isMale: genderRef.current?.getAnswer() },
|
||||
},
|
||||
{ component: SportQuestion, props: { ref: sportQuestionRef } },
|
||||
{ component: SleepQuestion, props: { ref: sleepQuestionRef } },
|
||||
];
|
||||
|
||||
const goNext = () => {
|
||||
if (currentQuestionIndex < questions.length - 1) {
|
||||
setCurrentQuestionIndex(currentQuestionIndex + 1);
|
||||
} else {
|
||||
Collect();
|
||||
}
|
||||
};
|
||||
|
||||
function Collect() {
|
||||
if (session) {
|
||||
session.healthProblems = [];
|
||||
|
||||
session.goals = goalRef.current?.getAnswer();
|
||||
session.sexe = genderRef.current?.getAnswer();
|
||||
session.weight = weightRef.current?.getAnswer();
|
||||
session.height = heightRef.current?.getAnswer();
|
||||
session.age = ageRef.current?.getAnswer();
|
||||
session.sportLevel = activityRef.current?.getAnswer();
|
||||
session.nbSessionPerWeek = frequencyRef.current?.getAnswer();
|
||||
session.sports = sportQuestionRef.current?.getAnswer();
|
||||
session.sleepLevel = sleepQuestionRef.current?.getAnswer();
|
||||
signIn(session);
|
||||
}
|
||||
|
||||
router.replace("/(tabs)/(home)/HomeScreen");
|
||||
}
|
||||
|
||||
return (
|
||||
<Screen>
|
||||
<View className="gap-4 justify-between h-full">
|
||||
<View className="flex-row justify-between items-center gap-4">
|
||||
<BackButton
|
||||
className="mt-2"
|
||||
onPress={() => {
|
||||
if (currentQuestionIndex === 0) {
|
||||
router.replace("/(auth)/log-in");
|
||||
} else {
|
||||
setCurrentQuestionIndex((i) => Math.max(i - 1, 0));
|
||||
}
|
||||
}}
|
||||
icon={currentQuestionIndex === 0 ? "close" : "arrowleft"}
|
||||
/>
|
||||
<Text size="2xl" weight="bold">
|
||||
Questionnaire
|
||||
</Text>
|
||||
<View className={"px-4 py-2 rounded-2xl" + " " + toBgColor("blue")}>
|
||||
<Text className={toTextColor("blue")} weight="bold">
|
||||
{currentQuestionIndex + 1} sur {questions.length}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
{questions.map((question, index) =>
|
||||
React.createElement(question.component, {
|
||||
isVisible: index === currentQuestionIndex,
|
||||
key: index,
|
||||
...question.props,
|
||||
})
|
||||
)}
|
||||
<Button afterIcon="arrowright" onPress={goNext}>
|
||||
{currentQuestionIndex == questions.length - 1
|
||||
? "Terminer"
|
||||
: "Suivant"}
|
||||
</Button>
|
||||
</View>
|
||||
</Screen>
|
||||
);
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
import {SafeAreaView, View, Text} from "react-native";
|
||||
import React from "react";
|
||||
import InternalError from "@/components/error/InternalErrorProblem";
|
||||
|
||||
export default function AddScreen() {
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<View>
|
||||
<Text className="m-7 font-extrabold">Welcome to Add Screen </Text>
|
||||
<Text>We will do it soon</Text>
|
||||
<InternalError/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//<Text className="m-7 font-extrabold">Welcome to Add Screen </Text>
|
||||
// <Text>We will do it soon</Text>
|
@ -1,54 +1,58 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { FlatList, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { getExercices } from "@/api/services/ExercicesServices";
|
||||
import { useRouter } from "expo-router";
|
||||
import { Workout } from "@/model/Workout";
|
||||
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";
|
||||
|
||||
export default function ExercicesScreen() {
|
||||
const [exercices, setExercices] = useState<Workout[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const router = useRouter();
|
||||
const [exercices, setExercices] = useState<Workout[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const data = await getExercices();
|
||||
setExercices(data);
|
||||
} catch (err: any) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const data = await getExercices();
|
||||
setExercices(data);
|
||||
} catch (err: any) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <Text className="text-white">Chargement...</Text>;
|
||||
}
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
if (error) {
|
||||
return <Text className="text-red-500">Erreur : {error}</Text>;
|
||||
}
|
||||
if (loading) {
|
||||
return <Text className="text-white">Chargement...</Text>;
|
||||
}
|
||||
|
||||
return (
|
||||
if (error) {
|
||||
return <Text className="text-red-500">Erreur : {error}</Text>;
|
||||
}
|
||||
|
||||
<View className="p-4">
|
||||
<FlatList
|
||||
data={exercices}
|
||||
keyExtractor={(item) => item.id}
|
||||
renderItem={({ item }) => (
|
||||
<TouchableOpacity
|
||||
className="p-4 mb-2 bg-gray-800 rounded-lg"
|
||||
onPress={() => router.push({ pathname: "/WorkoutScreen", params: { workout: JSON.stringify(item) } })}
|
||||
>
|
||||
<WorkoutCardComponent exercise={item} background={"black"} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View className="p-4">
|
||||
<FlatList
|
||||
data={exercices}
|
||||
keyExtractor={(item) => item.id}
|
||||
renderItem={({ item }) => (
|
||||
<TouchableOpacity
|
||||
className="p-4 mb-2 bg-gray-800 rounded-lg"
|
||||
onPress={() =>
|
||||
router.push({
|
||||
pathname: "/WorkoutScreen",
|
||||
params: { workout: JSON.stringify(item) },
|
||||
})
|
||||
}
|
||||
>
|
||||
<WorkoutCardComponent exercise={item} background={"black"} />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -1,78 +1,65 @@
|
||||
import {ScrollView, Text, View} from "react-native";
|
||||
import React, {useEffect, useState} from "react";
|
||||
import WorkoutCardComponent from "@/components/WorkoutCardComponent";
|
||||
import Screen from "@/components/ui/Screen";
|
||||
import { getExercices } from "@/api/services/ExercicesServices";
|
||||
import ActivitiesComponent from "@/components/ActivitiesComponent";
|
||||
import CalendarComponent from "@/components/CalendarComponent";
|
||||
import WelcomeComponent from "@/components/WelcomeComponent";
|
||||
import ActivitiesComponent from "@/components/ActivitiesComponent";
|
||||
import {Workout} from "@/model/Workout";
|
||||
import {getExercices} from "@/api/services/ExercicesServices";
|
||||
|
||||
import WorkoutCardComponent from "@/components/WorkoutCardComponent";
|
||||
import Screen from "@/components/ui/Screen";
|
||||
import { Workout } from "@/model/Workout";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { ScrollView, Text, View } from "react-native";
|
||||
export default function HomeScreen() {
|
||||
const [exercices, setExercices] = useState<Workout[]>([]);
|
||||
|
||||
const [exercices, setExercices] = useState<Workout[]>([]);
|
||||
|
||||
const getRandomNumber = (min: number, max: number) => {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
const getRandomNumber = (min: number, max: number) => {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
};
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const data = await getExercices();
|
||||
setExercices(data);
|
||||
} catch (err: any) {}
|
||||
};
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const data = await getExercices();
|
||||
setExercices(data);
|
||||
} catch (err: any) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ScrollView className="h-full ">
|
||||
<Screen>
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
<View className="h-1/6 justify-center">
|
||||
<WelcomeComponent />
|
||||
</View>
|
||||
return (
|
||||
<ScrollView className="h-full ">
|
||||
<Screen>
|
||||
<View className="h-1/6 justify-center">
|
||||
<WelcomeComponent />
|
||||
</View>
|
||||
|
||||
<View className="h-1/6 mt-2">
|
||||
<View className="flex-row justify-between items-center mb-4">
|
||||
<Text className="text-lg font-bold text-black">
|
||||
Fitness Metrics
|
||||
</Text>
|
||||
<Text className="text-orange-500 font-semibold">
|
||||
See All
|
||||
</Text>
|
||||
</View>
|
||||
<CalendarComponent />
|
||||
</View>
|
||||
<View className="h-1/6 mt-2">
|
||||
<View className="flex-row justify-between items-center mb-4">
|
||||
<Text className="text-lg font-bold text-black">
|
||||
Fitness Metrics
|
||||
</Text>
|
||||
<Text className="text-orange-500 font-semibold">See All</Text>
|
||||
</View>
|
||||
<CalendarComponent />
|
||||
</View>
|
||||
|
||||
<View className="h-1/5 mt-8">
|
||||
<View className="flex-row justify-between items-center mb-4">
|
||||
<Text className="text-lg font-bold text-black">
|
||||
Workout
|
||||
</Text>
|
||||
<Text className="text-orange-500 font-semibold">
|
||||
See All
|
||||
</Text>
|
||||
</View>
|
||||
<WorkoutCardComponent exercise={exercices[getRandomNumber(0,3)]} background="bg-black" />
|
||||
</View>
|
||||
<View className="h-1/5 mt-8">
|
||||
<View className="flex-row justify-between items-center mb-4">
|
||||
<Text className="text-lg font-bold text-black">Workout</Text>
|
||||
<Text className="text-orange-500 font-semibold">See All</Text>
|
||||
</View>
|
||||
<WorkoutCardComponent
|
||||
exercise={exercices[getRandomNumber(0, 3)]}
|
||||
background="bg-black"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View className="h-1/5 mt-8">
|
||||
<View className="flex-row justify-between items-center mb-4 mt-6 ">
|
||||
<Text className="text-lg font-bold text-black">
|
||||
Activities
|
||||
</Text>
|
||||
<Text className="text-orange-500 font-semibold">
|
||||
See All
|
||||
</Text>
|
||||
</View>
|
||||
<ActivitiesComponent />
|
||||
</View>
|
||||
</Screen>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
<View className="h-1/5 mt-8">
|
||||
<View className="flex-row justify-between items-center mb-4 mt-6 ">
|
||||
<Text className="text-lg font-bold text-black">Activities</Text>
|
||||
<Text className="text-orange-500 font-semibold">See All</Text>
|
||||
</View>
|
||||
<ActivitiesComponent />
|
||||
</View>
|
||||
</Screen>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
import Text from "@/components/ui/Text";
|
||||
import React, {forwardRef} from "react";
|
||||
import {Image, View, ViewProps} from "react-native";
|
||||
import BackButton from "@/components/BackButton";
|
||||
|
||||
import {Entypo} from "@expo/vector-icons";
|
||||
|
||||
export interface ProblemProps extends ViewProps{
|
||||
picture: string;
|
||||
problem: string;
|
||||
description: string;
|
||||
information: string;
|
||||
isVisible?: boolean;
|
||||
}
|
||||
|
||||
export default forwardRef<any, ProblemProps> (({className, ...Props}, ref) => {
|
||||
return (
|
||||
<View className={"gap-4 justify-between h-3/4" + " " + className} {...Props} ref={ref}>
|
||||
<View className="flex-row justify-between items-center p-4">
|
||||
<BackButton/>
|
||||
</View>
|
||||
|
||||
<View className="flex-row justify-center">
|
||||
<Image className="aspect-square w-3/5 h-3/5" source={Props.picture}/>
|
||||
</View>
|
||||
|
||||
<Text position="center" weight="bold" size="3xl"> {Props.problem} </Text>
|
||||
<Text size="lg" position="center" className="text-gray-400"> {Props.description} </Text>
|
||||
<View className="flex-row justify-center">
|
||||
<View className="flex-row items-center border-2 rounded-2xl bg-red-300 border-red-600 p-4">
|
||||
<Entypo name="warning" size={30} color="red"/>
|
||||
<Text size="lg" position="center"> {Props.information} </Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
});
|
@ -0,0 +1,15 @@
|
||||
import { useSession } from "@/ctx";
|
||||
import { Redirect } from "expo-router";
|
||||
import Loading from "./loading";
|
||||
|
||||
export default function Index() {
|
||||
const { session, isLoading } = useSession();
|
||||
|
||||
if (isLoading) return <Loading />;
|
||||
|
||||
if (session) {
|
||||
return <Redirect href="/(tabs)/(home)/HomeScreen" />;
|
||||
}
|
||||
|
||||
return <Redirect href="/log-in" />;
|
||||
}
|
After Width: | Height: | Size: 160 KiB |
After Width: | Height: | Size: 532 KiB |
After Width: | Height: | Size: 563 KiB |
After Width: | Height: | Size: 332 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 39 KiB |
@ -0,0 +1,149 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { View, TouchableOpacity, ViewProps } from "react-native";
|
||||
import Text from "./ui/Text";
|
||||
import {
|
||||
AntDesign,
|
||||
Entypo,
|
||||
FontAwesome6,
|
||||
Ionicons,
|
||||
MaterialCommunityIcons,
|
||||
} from "@expo/vector-icons";
|
||||
import {
|
||||
AntDesignIconNames,
|
||||
CommunityIconNames,
|
||||
EntypoIconNames,
|
||||
FontAwesome6IconNames,
|
||||
FontAwesomeIconNames,
|
||||
IonIconNames,
|
||||
} from "./Icons";
|
||||
|
||||
export type CheckBoxDirection = "row" | "col";
|
||||
|
||||
interface CheckBoxProps extends ViewProps {
|
||||
label?: string;
|
||||
onChange: () => void;
|
||||
antIcon?: AntDesignIconNames;
|
||||
entypoIcon?: EntypoIconNames;
|
||||
fontAwesomeIcon?: FontAwesomeIconNames;
|
||||
fontAwesome6Icon?: FontAwesome6IconNames;
|
||||
communityIcon?: CommunityIconNames;
|
||||
IonIcon?: IonIconNames;
|
||||
value: boolean;
|
||||
isCheckIconVisible?: boolean;
|
||||
endText?: string;
|
||||
direction?: CheckBoxDirection;
|
||||
}
|
||||
|
||||
export default forwardRef<any, CheckBoxProps>(
|
||||
(
|
||||
{
|
||||
label,
|
||||
onChange,
|
||||
antIcon,
|
||||
entypoIcon,
|
||||
fontAwesomeIcon,
|
||||
fontAwesome6Icon,
|
||||
communityIcon,
|
||||
IonIcon,
|
||||
value,
|
||||
isCheckIconVisible,
|
||||
endText,
|
||||
direction,
|
||||
className,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress={onChange}
|
||||
className={
|
||||
"items-center p-4 rounded-3xl" +
|
||||
" " +
|
||||
((direction ?? "row") == "row"
|
||||
? "flex-row justify-between gap-4"
|
||||
: "justify-center gap-1") +
|
||||
" " +
|
||||
(value
|
||||
? "bg-orange-600 border-4 border-orange-300"
|
||||
: "bg-gray-300 border-4 border-gray-300") +
|
||||
" " +
|
||||
(className ?? "")
|
||||
}
|
||||
{...props}
|
||||
{...ref}
|
||||
>
|
||||
<View>
|
||||
{antIcon ? (
|
||||
<AntDesign
|
||||
name={antIcon}
|
||||
size={30}
|
||||
color={value ? "white" : "black"}
|
||||
/>
|
||||
) : null}
|
||||
{entypoIcon ? (
|
||||
<Entypo
|
||||
name={entypoIcon}
|
||||
size={30}
|
||||
color={value ? "white" : "black"}
|
||||
/>
|
||||
) : null}
|
||||
{communityIcon ? (
|
||||
<MaterialCommunityIcons
|
||||
name={communityIcon}
|
||||
size={30}
|
||||
color={value ? "white" : "black"}
|
||||
/>
|
||||
) : null}
|
||||
{fontAwesomeIcon ? (
|
||||
<FontAwesome6
|
||||
name={fontAwesomeIcon}
|
||||
size={30}
|
||||
color={value ? "white" : "black"}
|
||||
/>
|
||||
) : null}
|
||||
{fontAwesome6Icon ? (
|
||||
<FontAwesome6
|
||||
name={fontAwesome6Icon}
|
||||
size={30}
|
||||
color={value ? "white" : "black"}
|
||||
/>
|
||||
) : null}
|
||||
{IonIcon ? (
|
||||
<Ionicons
|
||||
name={IonIcon}
|
||||
size={30}
|
||||
color={value ? "white" : "black"}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
{label != null ? (
|
||||
<Text
|
||||
weight="bold"
|
||||
color={value ? "white" : "black"}
|
||||
className={(direction ?? "row") == "row" ? "flex-1" : ""}
|
||||
>
|
||||
{label}
|
||||
</Text>
|
||||
) : null}
|
||||
|
||||
{isCheckIconVisible ? (
|
||||
<View
|
||||
className={
|
||||
"h-5 w-5 rounded border justify-center items-center" +
|
||||
" " +
|
||||
(value ? "border-white" : "border-black")
|
||||
}
|
||||
>
|
||||
{value && <View className="h-2 w-2 bg-white" />}
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
{endText != null ? (
|
||||
<Text color={value ? "white" : "black"}>{endText}</Text>
|
||||
) : null}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
);
|
@ -1,3 +1,10 @@
|
||||
export const EMPTY_FIELD = "Un des champs est vide !";
|
||||
export const INVALID_EMAIL = "Adresse mail invalide !";
|
||||
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";
|
@ -1,3 +1,26 @@
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import {
|
||||
AntDesign,
|
||||
Entypo,
|
||||
FontAwesome,
|
||||
Ionicons,
|
||||
MaterialCommunityIcons,
|
||||
} from "@expo/vector-icons";
|
||||
|
||||
export type AntDesignIconNames = keyof typeof AntDesign.glyphMap;
|
||||
export type EntypoIconNames = keyof typeof Entypo.glyphMap;
|
||||
export type FontAwesomeIconNames = keyof typeof FontAwesome.glyphMap;
|
||||
export type FontAwesome6IconNames =
|
||||
| "person-running"
|
||||
| "person-walking"
|
||||
| "person-hiking"
|
||||
| "skateboarding"
|
||||
| "bike"
|
||||
| "basketball"
|
||||
| "heart"
|
||||
| "yoga"
|
||||
| "setting"
|
||||
| "beer"
|
||||
| "shield-heart"
|
||||
| "weight-scale";
|
||||
export type CommunityIconNames = keyof typeof MaterialCommunityIcons.glyphMap;
|
||||
export type IonIconNames = keyof typeof Ionicons.glyphMap;
|
||||
|
@ -1,59 +1,62 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { View, Text, Animated, TouchableOpacity } from 'react-native';
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Animated, Text, TouchableOpacity, View } from "react-native";
|
||||
|
||||
export default function LinearProgressBar({ duration = 10 }) {
|
||||
const [timeLeft, setTimeLeft] = useState(duration);
|
||||
const [isRunning, setIsRunning] = useState(false);
|
||||
const progress = useRef(new Animated.Value(0)).current;
|
||||
const intervalRef = useRef(null);
|
||||
|
||||
const startAnimation = () => {
|
||||
setIsRunning(true);
|
||||
progress.setValue(0);
|
||||
setTimeLeft(duration);
|
||||
|
||||
Animated.timing(progress, {
|
||||
toValue: 1,
|
||||
duration: duration * 1000,
|
||||
useNativeDriver: false,
|
||||
}).start(() => setIsRunning(false));
|
||||
|
||||
//@ts-ignore
|
||||
intervalRef.current = setInterval(() => {
|
||||
setTimeLeft((prev) => (prev > 0 ? prev - 1 : 0));
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
//@ts-ignore
|
||||
return () => clearInterval(intervalRef.current);
|
||||
}, []);
|
||||
|
||||
const progressWidth = progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ["0%", "100%"],
|
||||
});
|
||||
|
||||
return (
|
||||
<View className="w-full p-4 items-center">
|
||||
<Text className="text-center mb-2 color-white font-extrabold">Temps restant : {timeLeft}s</Text>
|
||||
<View className="w-full h-4 bg-gray-200 rounded-full overflow-hidden mb-4">
|
||||
<Animated.View
|
||||
style={{ width: progressWidth }}
|
||||
className="h-full bg-orange-400"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
onPress={startAnimation}
|
||||
disabled={isRunning}
|
||||
className={`px-4 py-2 rounded-full ${isRunning ? 'bg-orange-400' : 'bg-orange-400'}`}
|
||||
>
|
||||
<Text className="text-white font-bold">
|
||||
{isRunning ? 'En cours...' : 'Play'}
|
||||
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
const [timeLeft, setTimeLeft] = useState(duration);
|
||||
const [isRunning, setIsRunning] = useState(false);
|
||||
const progress = useRef(new Animated.Value(0)).current;
|
||||
const intervalRef = useRef(null);
|
||||
|
||||
const startAnimation = () => {
|
||||
setIsRunning(true);
|
||||
progress.setValue(0);
|
||||
setTimeLeft(duration);
|
||||
|
||||
Animated.timing(progress, {
|
||||
toValue: 1,
|
||||
duration: duration * 1000,
|
||||
useNativeDriver: false,
|
||||
}).start(() => setIsRunning(false));
|
||||
|
||||
//@ts-ignore
|
||||
intervalRef.current = setInterval(() => {
|
||||
setTimeLeft((prev) => (prev > 0 ? prev - 1 : 0));
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
//@ts-ignore
|
||||
return () => clearInterval(intervalRef.current);
|
||||
}, []);
|
||||
|
||||
const progressWidth = progress.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ["0%", "100%"],
|
||||
});
|
||||
|
||||
return (
|
||||
<View className="w-full p-4 items-center">
|
||||
<Text className="text-center mb-2 color-white font-extrabold">
|
||||
Temps restant : {timeLeft}s
|
||||
</Text>
|
||||
<View className="w-full h-4 bg-gray-200 rounded-full overflow-hidden mb-4">
|
||||
<Animated.View
|
||||
style={{ width: progressWidth }}
|
||||
className="h-full bg-orange-400"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
onPress={startAnimation}
|
||||
disabled={isRunning}
|
||||
className={`px-4 py-2 rounded-full ${
|
||||
isRunning ? "bg-orange-400" : "bg-orange-400"
|
||||
}`}
|
||||
>
|
||||
<Text className="text-white font-bold">
|
||||
{isRunning ? "En cours..." : "Play"}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -1,40 +1,40 @@
|
||||
import {Image, ImageBackground, Text, TouchableOpacity, View} from "react-native";
|
||||
import {Ionicons} from "@expo/vector-icons";
|
||||
import { useSession } from "@/ctx";
|
||||
import React from "react";
|
||||
import {useSession} from "@/ctx";
|
||||
import { Image, ImageBackground, Text, View } from "react-native";
|
||||
|
||||
export default function WelcomeComponent() {
|
||||
const date = new Date();
|
||||
const formattedDate = date.toLocaleDateString('fr-FR', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
const {session} = useSession();
|
||||
const date = new Date();
|
||||
const formattedDate = date.toLocaleDateString("fr-FR", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
const { session } = useSession();
|
||||
|
||||
return (
|
||||
<View className="rounded-2xl overflow-hidden shadow-lg h-full bg-black justify-center">
|
||||
<ImageBackground
|
||||
source={require("assets/images/black-back.png")}
|
||||
className="w-full h-full justify-center"
|
||||
>
|
||||
<View className="flex-row items-center justify-between">
|
||||
<View className="flex-row items-center w-full">
|
||||
<Image
|
||||
className="h-16 w-16 rounded-2xl"
|
||||
source={require("assets/images/sigma-profile.jpeg")}
|
||||
/>
|
||||
|
||||
|
||||
return (
|
||||
<View className="rounded-2xl overflow-hidden shadow-lg h-full bg-black justify-center">
|
||||
<ImageBackground
|
||||
source={require("assets/images/black-back.png")}
|
||||
className="w-full h-full justify-center"
|
||||
>
|
||||
|
||||
<View className="flex-row items-center justify-between">
|
||||
<View className="flex-row items-center w-full">
|
||||
<Image className="h-16 w-16 rounded-2xl"
|
||||
source={require("assets/images/sigma-profile.jpeg")}
|
||||
/>
|
||||
|
||||
<View>
|
||||
<Text className="text-gray-500 font-semibold ml-4">{formattedDate}</Text>
|
||||
<Text className="text-white text-4xl ml-4 mt-0.5">Hello {session} !</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
</ImageBackground>
|
||||
</View>
|
||||
<View>
|
||||
<Text className="text-gray-500 font-semibold ml-4">
|
||||
{formattedDate}
|
||||
</Text>
|
||||
<Text className="text-white text-4xl ml-4 mt-0.5">
|
||||
Hello {session?.name} !
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</ImageBackground>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,67 +1,75 @@
|
||||
import {ImageBackground, Text, TouchableOpacity, View} from "react-native";
|
||||
import { Workout } from "@/model/Workout";
|
||||
import { AntDesign, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { useRouter } from "expo-router";
|
||||
import React from "react";
|
||||
import {AntDesign, MaterialCommunityIcons} from "@expo/vector-icons";
|
||||
import {Workout} from "@/model/Workout";
|
||||
import {useRouter} from "expo-router";
|
||||
|
||||
interface WorkoutCardComponentProps {
|
||||
exercise?: Workout,
|
||||
background?: String,
|
||||
height?: number,
|
||||
|
||||
exercise?: Workout;
|
||||
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 `
|
||||
}
|
||||
const router = useRouter();
|
||||
return (
|
||||
<View className="h-full rounded-2xl overflow-hidden bg-black">
|
||||
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 `;
|
||||
};
|
||||
const router = useRouter();
|
||||
return (
|
||||
<View className="h-full rounded-2xl overflow-hidden bg-black">
|
||||
<ImageBackground
|
||||
source={require("assets/images/Sigma-2.png")}
|
||||
className="h-full w-full"
|
||||
>
|
||||
<View className="flex-row justify-between p-4">
|
||||
<View className="flex-row space-x-4 h-44 items-top justify-center ">
|
||||
<View className="flex-row items-top">
|
||||
<Text className="text-white text-sm ml-1">
|
||||
{exercise?.duration} min
|
||||
</Text>
|
||||
</View>
|
||||
<View className="flex-row justify-center">
|
||||
<MaterialCommunityIcons
|
||||
name="square-rounded"
|
||||
size={8}
|
||||
color="white"
|
||||
/>
|
||||
</View>
|
||||
<View className="flex-row">
|
||||
<Text className="text-white text-sm ml-1">
|
||||
{exercise?.name} kcal
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<ImageBackground
|
||||
source={require("assets/images/Sigma-2.png")}
|
||||
className='h-full w-full'
|
||||
>
|
||||
<View className="flex-row justify-between p-4">
|
||||
<View className="flex-row space-x-4 h-44 items-top justify-center ">
|
||||
<View className="flex-row items-top">
|
||||
<Text className="text-white text-sm ml-1">{exercise?.duration} min</Text>
|
||||
</View>
|
||||
<View className="flex-row justify-center">
|
||||
<MaterialCommunityIcons name="square-rounded" size={8} color="white"/>
|
||||
</View>
|
||||
<View className="flex-row">
|
||||
<Text className="text-white text-sm ml-1">{exercise?.name} kcal</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="absolute bottom-0 left-0 right-0 p-4 bg-opacity-50">
|
||||
<Text className="text-white text-lg font-bold">{exercise?.name}</Text>
|
||||
<Text className="text-gray-300 text-sm">{exercise?.nbRepetitions}</Text>
|
||||
<View className="flex-row items-center mt-2">
|
||||
<Text className="text-white text-xs bg-gray-800 py-1 px-3 rounded-full">
|
||||
{exercise?.name}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<TouchableOpacity className="absolute bottom-2 right-4 p-4 rounded-full"
|
||||
onPress={() => {
|
||||
router.push("/WorkoutScreen");
|
||||
}}>
|
||||
<AntDesign name="play" size={50} color="orange"/>
|
||||
</TouchableOpacity>
|
||||
</ImageBackground>
|
||||
|
||||
<View className="absolute bottom-0 left-0 right-0 p-4 bg-opacity-50">
|
||||
<Text className="text-white text-lg font-bold">{exercise?.name}</Text>
|
||||
<Text className="text-gray-300 text-sm">
|
||||
{exercise?.nbRepetitions}
|
||||
</Text>
|
||||
<View className="flex-row items-center mt-2">
|
||||
<Text className="text-white text-xs bg-gray-800 py-1 px-3 rounded-full">
|
||||
{exercise?.name}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
<TouchableOpacity
|
||||
className="absolute bottom-2 right-4 p-4 rounded-full"
|
||||
onPress={() => {
|
||||
router.push("/WorkoutScreen");
|
||||
}}
|
||||
>
|
||||
<AntDesign name="play" size={50} color="orange" />
|
||||
</TouchableOpacity>
|
||||
</ImageBackground>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@ -1,53 +1,63 @@
|
||||
import {ImageBackground, TouchableOpacity, View, Text} from "react-native";
|
||||
import LinearProgressBar from "@/components/LinearProgressBar";
|
||||
import Screen from "@/components/ui/Screen";
|
||||
import { Workout } from "@/model/Workout";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Router, useRouter } from "expo-router";
|
||||
import * as React from "react";
|
||||
import {Ionicons} from "@expo/vector-icons";
|
||||
import {Router, useRouter} from "expo-router";
|
||||
import LinearProgressBar from "@/components/LinearProgressBar";
|
||||
import {Workout} from "@/model/Workout";
|
||||
|
||||
import { ImageBackground, Text, TouchableOpacity, View } from "react-native";
|
||||
|
||||
type WorkoutPresentationComponentProps = {
|
||||
workout: Workout;
|
||||
dataExercise: Workout[];
|
||||
router: Router; // Typage précis recommandé selon ta navigation
|
||||
workout: Workout;
|
||||
dataExercise: Workout[];
|
||||
router: Router; // Typage précis recommandé selon ta navigation
|
||||
};
|
||||
|
||||
export default function WorkoutPresentationComponent({ workout}: WorkoutPresentationComponentProps) {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<ImageBackground className="h-full w-full"
|
||||
source={require("assets/images/backgroundWourkout.jpg")}>
|
||||
<Screen>
|
||||
<View className="flex-col h-full justify-between">
|
||||
|
||||
{/* Bouton Retour */}
|
||||
<View className="mt-5 ml-5">
|
||||
<TouchableOpacity onPress={() => { router.replace("/ExercicesScreen"); }}>
|
||||
<Ionicons name="chevron-back-circle-outline" size={50} color="white"/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Permet de pousser le reste du contenu vers le bas */}
|
||||
<View className="flex-grow" />
|
||||
|
||||
{/* Texte en bas */}
|
||||
<View className="items-center mb-10">
|
||||
<Text className="text-white bg-transparent border-2 border-white px-3 py-1 rounded-full text-2xl font-bold">
|
||||
{workout.nbSeries} x {workout.nbRepetitions}
|
||||
</Text>
|
||||
|
||||
<Text className="text-white text-4xl font-bold mt-2">{workout.name}</Text>
|
||||
</View>
|
||||
|
||||
{/* Barre de progression */}
|
||||
<View className="mb-5">
|
||||
<LinearProgressBar duration={workout.duration} />
|
||||
</View>
|
||||
|
||||
</View>
|
||||
|
||||
</Screen>
|
||||
</ImageBackground>
|
||||
);
|
||||
}
|
||||
export default function WorkoutPresentationComponent({
|
||||
workout,
|
||||
}: WorkoutPresentationComponentProps) {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<ImageBackground
|
||||
className="h-full w-full"
|
||||
source={require("assets/images/backgroundWourkout.jpg")}
|
||||
>
|
||||
<Screen>
|
||||
<View className="flex-col h-full justify-between">
|
||||
{/* Bouton Retour */}
|
||||
<View className="mt-5 ml-5">
|
||||
<TouchableOpacity
|
||||
onPress={() => {
|
||||
router.replace("/ExercicesScreen");
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
name="chevron-back-circle-outline"
|
||||
size={50}
|
||||
color="white"
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Permet de pousser le reste du contenu vers le bas */}
|
||||
<View className="flex-grow" />
|
||||
|
||||
{/* Texte en bas */}
|
||||
<View className="items-center mb-10">
|
||||
<Text className="text-white bg-transparent border-2 border-white px-3 py-1 rounded-full text-2xl font-bold">
|
||||
{workout.nbSeries} x {workout.nbRepetitions}
|
||||
</Text>
|
||||
|
||||
<Text className="text-white text-4xl font-bold mt-2">
|
||||
{workout.name}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Barre de progression */}
|
||||
<View className="mb-5">
|
||||
<LinearProgressBar duration={workout.duration} />
|
||||
</View>
|
||||
</View>
|
||||
</Screen>
|
||||
</ImageBackground>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import {FEATURE_LOCKED} from "@/components/Errors";
|
||||
import Error from "@/app/(utility)/Error";
|
||||
import blockedPict from "@/assets/images/Blocked.png";
|
||||
|
||||
export default function Blocked() {
|
||||
return (
|
||||
<Error
|
||||
picture={blockedPict}
|
||||
problem="Fonctionnalité bloquée"
|
||||
description={FEATURE_LOCKED}
|
||||
information="Devenez PREMIUM pour débloquer"
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import {INTERNAL_ERROR} from "@/components/Errors";
|
||||
import Error from "@/app/(utility)/Error";
|
||||
import internalErrorPict from "@/assets/images/InternalError.png";
|
||||
|
||||
export default function InternalError() {
|
||||
return (
|
||||
<Error
|
||||
picture={internalErrorPict}
|
||||
problem="Problème interne"
|
||||
description={INTERNAL_ERROR}
|
||||
information="Contactez le support"
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import {MAINTENANCE} from "@/components/Errors";
|
||||
import Error from "@/app/(utility)/Error";
|
||||
import maintenancePict from "@/assets/images/Maintenance.png";
|
||||
|
||||
export default function Maintenance() {
|
||||
return (
|
||||
<Error
|
||||
picture={maintenancePict}
|
||||
problem="Maintenance"
|
||||
description={MAINTENANCE}
|
||||
information="Revenez plus tard"
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import {NO_INTERNET} from "@/components/Errors";
|
||||
import Error from "@/app/(utility)/Error";
|
||||
import noInternetPict from "@/assets/images/NoInternet.png";
|
||||
|
||||
export default function NoInternet() {
|
||||
return (
|
||||
<Error
|
||||
picture={noInternetPict}
|
||||
problem="Pas d'internet"
|
||||
description={NO_INTERNET}
|
||||
information="Réessayez plus tard"
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import {NOT_AUTHORIZED} from "@/components/Errors";
|
||||
import Error from "@/app/(utility)/Error";
|
||||
import notAllowedPict from "@/assets/images/NotAllowed.png";
|
||||
|
||||
export default function NotAllowedProblem() {
|
||||
return (
|
||||
<Error
|
||||
picture={notAllowedPict}
|
||||
problem="Pas autorisé"
|
||||
description={NOT_AUTHORIZED}
|
||||
information="Connectez vous avec plus de privilèges"
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import {NOT_FOUND} from "@/components/Errors";
|
||||
import Error from "@/app/(utility)/Error";
|
||||
import notFoundPict from "@/assets/images/NotFound.png";
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<Error
|
||||
picture={notFoundPict}
|
||||
problem="Introuvable"
|
||||
description={NOT_FOUND}
|
||||
information="Status Code : 404"
|
||||
/>
|
||||
);
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
import { ESportLevel, SportLevels } from "@/model/enums/Enums";
|
||||
import React, { useImperativeHandle, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import Checkbox from "../CheckBox";
|
||||
import {
|
||||
AntDesignIconNames,
|
||||
CommunityIconNames,
|
||||
EntypoIconNames,
|
||||
FontAwesome6IconNames,
|
||||
} from "../Icons";
|
||||
import Question, { QuestionChildProps } from "./Question";
|
||||
|
||||
export interface ActivityQuestionRef {
|
||||
getAnswer: () => ESportLevel;
|
||||
}
|
||||
|
||||
export default React.forwardRef<ActivityQuestionRef, QuestionChildProps>(
|
||||
({ ...props }, ref): React.ReactElement => {
|
||||
const [checkedItems, setCheckedItems] = useState([
|
||||
true,
|
||||
...Array(4).fill(false),
|
||||
]);
|
||||
|
||||
const handleChange = (index: number) => {
|
||||
const newCheckedState = checkedItems.map((_, i) => i === index);
|
||||
setCheckedItems(newCheckedState);
|
||||
};
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getAnswer: () => {
|
||||
let selected = 0;
|
||||
checkedItems.forEach((item, index) => {
|
||||
if (item) {
|
||||
selected = index;
|
||||
}
|
||||
});
|
||||
return SportLevels[SportLevels.length - 1 - selected];
|
||||
},
|
||||
}));
|
||||
|
||||
interface IData {
|
||||
label: string;
|
||||
commIcon?: CommunityIconNames;
|
||||
antIcon?: AntDesignIconNames;
|
||||
entypoIcon?: EntypoIconNames;
|
||||
fontAwesomeIcon?: FontAwesome6IconNames;
|
||||
}
|
||||
|
||||
const data: IData[] = [
|
||||
{ label: "Athlète", antIcon: "smile-circle" },
|
||||
{ label: "Très sportif", antIcon: "smileo" },
|
||||
{ label: "Un peu sportif", antIcon: "meh" },
|
||||
{ label: "Peu sportif", antIcon: "frowno" },
|
||||
{ label: "Pas du tout sportif", antIcon: "frown" },
|
||||
];
|
||||
|
||||
return (
|
||||
<Question
|
||||
question="Comment estimez-vous votre niveau d'activité ?"
|
||||
{...ref}
|
||||
{...props}
|
||||
>
|
||||
<View className="gap-2">
|
||||
{data.map((item, index) => (
|
||||
<Checkbox
|
||||
key={index}
|
||||
label={item.label}
|
||||
value={checkedItems[index]}
|
||||
onChange={() => handleChange(index)}
|
||||
antIcon={item.antIcon}
|
||||
endText={(data.length - index).toString()}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</Question>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,55 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
ReactElement,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
} from "react";
|
||||
import { View } from "react-native";
|
||||
import Slider from "../ui/Slider";
|
||||
import Text from "../ui/Text";
|
||||
import Question, { QuestionChildProps } from "./Question";
|
||||
|
||||
const MIN_AGE = 18;
|
||||
const MAX_AGE = 100;
|
||||
|
||||
export interface AgeQuestionRef {
|
||||
getAnswer: () => number;
|
||||
}
|
||||
|
||||
export default forwardRef<AgeQuestionRef, QuestionChildProps>(
|
||||
(props, ref): ReactElement => {
|
||||
const [answer, setAnswer] = useState<number>(MIN_AGE);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getAnswer: () => answer,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Question question="Quel âge avez-vous ?" {...ref} {...props}>
|
||||
<View className="flex-row justify-center">
|
||||
{answer <= MIN_AGE ? (
|
||||
<Text className="mt-8" size="4xl">
|
||||
- de
|
||||
</Text>
|
||||
) : null}
|
||||
{answer >= MAX_AGE ? (
|
||||
<Text className="mt-8" size="4xl">
|
||||
+ de
|
||||
</Text>
|
||||
) : null}
|
||||
<Text size="8xl" weight="bold">
|
||||
{answer}
|
||||
</Text>
|
||||
<Text className="mt-8 ml-1" size="4xl">
|
||||
ans
|
||||
</Text>
|
||||
</View>
|
||||
<Slider
|
||||
minimumValue={MIN_AGE}
|
||||
maximumValue={MAX_AGE}
|
||||
onValueChange={setAnswer}
|
||||
/>
|
||||
</Question>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,59 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
ReactElement,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Image, View } from "react-native";
|
||||
import Question, { QuestionChildProps } from "./Question";
|
||||
|
||||
//@ts-ignore
|
||||
import BenchImage from "@/assets/images/bench.png";
|
||||
import CheckBox from "../CheckBox";
|
||||
|
||||
export interface BeginnerQuestionRef {
|
||||
getAnswer: () => boolean;
|
||||
}
|
||||
|
||||
export default forwardRef<BeginnerQuestionRef, QuestionChildProps>(
|
||||
(props, ref): ReactElement => {
|
||||
const [answer, setAnswer] = useState<boolean>(false);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getAnswer: () => answer,
|
||||
}));
|
||||
|
||||
function handleChangeOne() {
|
||||
setAnswer(true);
|
||||
}
|
||||
|
||||
function handleChangeTwo() {
|
||||
setAnswer(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<Question
|
||||
question="Êtes-vous un novice de la musculation ?"
|
||||
{...ref}
|
||||
{...props}
|
||||
>
|
||||
<Image className="self-center" source={BenchImage} alt="" />
|
||||
<View>
|
||||
<CheckBox
|
||||
className=""
|
||||
label="Oui"
|
||||
value={answer}
|
||||
onChange={handleChangeOne}
|
||||
fontAwesomeIcon={"check"}
|
||||
/>
|
||||
<CheckBox
|
||||
label="Non"
|
||||
value={!answer}
|
||||
onChange={handleChangeTwo}
|
||||
entypoIcon={"cross"}
|
||||
/>
|
||||
</View>
|
||||
</Question>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,48 @@
|
||||
import React, { useImperativeHandle } from "react";
|
||||
import { View } from "react-native";
|
||||
import SegmentedControl from "../ui/SegmentedControl";
|
||||
import Text from "../ui/Text";
|
||||
import Question, { QuestionChildProps } from "./Question";
|
||||
|
||||
const ANSWERS = ["1", "2", "3", "4", "5"];
|
||||
|
||||
export interface FrequencyQuestionRef {
|
||||
getAnswer: () => number;
|
||||
}
|
||||
|
||||
export interface FrequencyQuestionProps extends QuestionChildProps {
|
||||
isMale: boolean;
|
||||
}
|
||||
|
||||
export default React.forwardRef<FrequencyQuestionRef, FrequencyQuestionProps>(
|
||||
({ isMale, ...props }, ref): React.ReactElement => {
|
||||
const [answer, setAnswer] = React.useState("1");
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getAnswer: () => parseInt(answer),
|
||||
}));
|
||||
|
||||
return (
|
||||
<Question
|
||||
question="Nombre de séance(s) par semaine ?"
|
||||
{...ref}
|
||||
{...props}
|
||||
>
|
||||
<View className="items-center">
|
||||
<Text size="2xl">
|
||||
Je suis {isMale ? "prêt" : "prête"} à m'entraîner
|
||||
</Text>
|
||||
<Text size="9xl" weight="bold">
|
||||
{answer}
|
||||
</Text>
|
||||
<Text size="2xl">fois par semaine !</Text>
|
||||
</View>
|
||||
<SegmentedControl
|
||||
values={ANSWERS}
|
||||
selectedIndex={ANSWERS.indexOf(answer)}
|
||||
onValueChange={setAnswer}
|
||||
/>
|
||||
</Question>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,55 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
ReactElement,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
} from "react";
|
||||
import { View } from "react-native";
|
||||
import Checkbox from "../CheckBox";
|
||||
import Question, { QuestionChildProps } from "./Question";
|
||||
|
||||
export interface GenderQuestionRef {
|
||||
getAnswer: () => boolean;
|
||||
}
|
||||
|
||||
export default forwardRef<GenderQuestionRef, QuestionChildProps>(
|
||||
({ ...props }, ref): ReactElement => {
|
||||
const [answer, setAnswer] = useState(true);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getAnswer: () => answer,
|
||||
}));
|
||||
|
||||
const handleChangeOne = () => {
|
||||
setAnswer(true);
|
||||
};
|
||||
const handleChangeTwo = () => {
|
||||
setAnswer(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Question
|
||||
question="Quel est votre genre physiologique ?"
|
||||
{...ref}
|
||||
{...props}
|
||||
>
|
||||
<View className="gap-2">
|
||||
<Checkbox
|
||||
label="Homme"
|
||||
value={answer}
|
||||
onChange={handleChangeOne}
|
||||
antIcon={"man"}
|
||||
isCheckIconVisible={true}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Femme"
|
||||
value={!answer}
|
||||
onChange={handleChangeTwo}
|
||||
antIcon={"woman"}
|
||||
isCheckIconVisible={true}
|
||||
/>
|
||||
</View>
|
||||
</Question>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,88 @@
|
||||
import { EGoal, Goals } from "@/model/enums/Enums";
|
||||
import React, {
|
||||
forwardRef,
|
||||
ReactElement,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
} from "react";
|
||||
import { View } from "react-native";
|
||||
import CheckBox from "../CheckBox";
|
||||
import {
|
||||
AntDesignIconNames,
|
||||
CommunityIconNames,
|
||||
EntypoIconNames,
|
||||
FontAwesome6IconNames,
|
||||
IonIconNames,
|
||||
} from "../Icons";
|
||||
import Question, { QuestionChildProps } from "./Question";
|
||||
|
||||
export interface GoalQuestionRef {
|
||||
getAnswer: () => EGoal;
|
||||
}
|
||||
|
||||
export default forwardRef<GoalQuestionRef, QuestionChildProps>(
|
||||
({ ...props }, ref): ReactElement => {
|
||||
const [checkedItems, setCheckedItems] = useState([
|
||||
true,
|
||||
...Array(4).fill(false),
|
||||
]);
|
||||
|
||||
interface IData {
|
||||
label: string;
|
||||
commIcon?: CommunityIconNames;
|
||||
antIcon?: AntDesignIconNames;
|
||||
entypoIcon?: EntypoIconNames;
|
||||
fontAwesomeIcon?: FontAwesome6IconNames;
|
||||
ionIcon?: IonIconNames;
|
||||
}
|
||||
|
||||
const data: IData[] = [
|
||||
{ label: "Perte de poids", commIcon: "weight" },
|
||||
{ label: "Renforcement musculaire", commIcon: "arm-flex-outline" },
|
||||
{ label: "Prise de masse", ionIcon: "beer-outline" },
|
||||
{ label: "Amélioration endurance", fontAwesomeIcon: "shield-heart" },
|
||||
{ label: "Maintenir en forme", antIcon: "linechart" },
|
||||
];
|
||||
|
||||
const handleChange = (index: number) => {
|
||||
const newCheckedState = checkedItems.map((_, i) => i === index);
|
||||
setCheckedItems(newCheckedState);
|
||||
};
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getAnswer: () => {
|
||||
let selected = 0;
|
||||
checkedItems.forEach((item, index) => {
|
||||
if (item) {
|
||||
selected = index;
|
||||
}
|
||||
});
|
||||
return Goals[selected];
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
<Question
|
||||
question="Quel est votre objectif dans l'application ?"
|
||||
{...ref}
|
||||
{...props}
|
||||
>
|
||||
<View className="gap-2">
|
||||
{data.map((item, index) => (
|
||||
<CheckBox
|
||||
key={index}
|
||||
label={item.label}
|
||||
value={checkedItems[index]}
|
||||
onChange={() => handleChange(index)}
|
||||
fontAwesome6Icon={item.fontAwesomeIcon}
|
||||
communityIcon={item.commIcon}
|
||||
antIcon={item.antIcon}
|
||||
entypoIcon={item.entypoIcon}
|
||||
IonIcon={item.ionIcon}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</Question>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,63 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
ReactElement,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
} from "react";
|
||||
import { View } from "react-native";
|
||||
import SegmentedControl from "../ui/SegmentedControl";
|
||||
import Slider from "../ui/Slider";
|
||||
import Text from "../ui/Text";
|
||||
import Question, { QuestionChildProps } from "./Question";
|
||||
|
||||
const MIN_HEIGHT = 120;
|
||||
const MAX_HEIGHT = 250;
|
||||
|
||||
export interface HeightQuestionRef {
|
||||
getAnswer: () => number;
|
||||
}
|
||||
|
||||
export default forwardRef<HeightQuestionRef, QuestionChildProps>(
|
||||
({ ...props }, ref): ReactElement => {
|
||||
const [answer, setAnswer] = useState<number>(MIN_HEIGHT);
|
||||
const UNITS = ["cm", "inch"];
|
||||
const [unit, setUnit] = useState<string>("cm");
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getAnswer: () => answer,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Question question="Quel est votre taille ?" {...ref} {...props}>
|
||||
<SegmentedControl
|
||||
values={UNITS}
|
||||
selectedIndex={UNITS.indexOf(unit)}
|
||||
onValueChange={setUnit}
|
||||
/>
|
||||
<View className="flex-row justify-center">
|
||||
{answer <= MIN_HEIGHT ? (
|
||||
<Text className="mt-8" size="4xl">
|
||||
- de
|
||||
</Text>
|
||||
) : null}
|
||||
{answer >= MAX_HEIGHT ? (
|
||||
<Text className="mt-8" size="4xl">
|
||||
+ de
|
||||
</Text>
|
||||
) : null}
|
||||
<Text size="8xl" weight="bold">
|
||||
{answer}
|
||||
</Text>
|
||||
<Text className="mt-8" size="4xl">
|
||||
{unit}
|
||||
</Text>
|
||||
</View>
|
||||
<Slider
|
||||
minimumValue={MIN_HEIGHT}
|
||||
maximumValue={MAX_HEIGHT}
|
||||
onValueChange={setAnswer}
|
||||
/>
|
||||
</Question>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,54 @@
|
||||
import React, { useState } from "react";
|
||||
import { Image, Text, View } from "react-native";
|
||||
import { MultiSelect } from "react-native-element-dropdown";
|
||||
import Question, { QuestionChildProps } from "./Question";
|
||||
|
||||
//@ts-ignore
|
||||
import WheelChair from "@/assets/images/wheelchair.png";
|
||||
import { EHealthProblem } from "@/model/enums/Enums";
|
||||
|
||||
export default React.forwardRef<any, QuestionChildProps>(
|
||||
({ ...props }, ref) => {
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
|
||||
type DataItem = {
|
||||
label: string;
|
||||
value: EHealthProblem;
|
||||
};
|
||||
|
||||
const data: DataItem[] = [
|
||||
{ label: "Arthrose", value: "ARTHROSE" },
|
||||
{ label: "Migraine", value: "MIGRAINE" },
|
||||
];
|
||||
|
||||
const renderItem = (item: { label: string }) => {
|
||||
return (
|
||||
<View className="p-4">
|
||||
<Text>{item.label}</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Question
|
||||
question="Avez-vous des problèmes physiques ?"
|
||||
{...props}
|
||||
{...ref}
|
||||
>
|
||||
<Image className="self-center" source={WheelChair} alt="" />
|
||||
<View className="border-2 border-orange-500 rounded-3xl p-4">
|
||||
<MultiSelect
|
||||
data={data}
|
||||
labelField="label"
|
||||
valueField="value"
|
||||
placeholder="Selectionnez un problème physique "
|
||||
searchPlaceholder="Search..."
|
||||
value={selected}
|
||||
onChange={setSelected}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
</View>
|
||||
</Question>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,28 @@
|
||||
import Text from "@/components/ui/Text";
|
||||
import React from "react";
|
||||
import { View, ViewProps } from "react-native";
|
||||
|
||||
export interface QuestionChildProps extends ViewProps {
|
||||
isVisible?: boolean;
|
||||
}
|
||||
|
||||
interface QuestionProps extends QuestionChildProps {
|
||||
question: string;
|
||||
}
|
||||
|
||||
export default React.forwardRef<any, QuestionProps>(
|
||||
({ question, isVisible, children, ...props }, ref): React.ReactElement => {
|
||||
const getClassName = () => {
|
||||
return "gap-6" + " " + (isVisible ? "block" : "hidden");
|
||||
};
|
||||
|
||||
return (
|
||||
<View className={getClassName()} {...ref} {...props}>
|
||||
<Text size="4xl" position="center" weight="bold">
|
||||
{question}
|
||||
</Text>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,91 @@
|
||||
import { ESleepLevel, SleepLevels } from "@/model/enums/Enums";
|
||||
import React, { useImperativeHandle, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import Checkbox from "../CheckBox";
|
||||
import { AntDesignIconNames } from "../Icons";
|
||||
import Question, { QuestionChildProps } from "./Question";
|
||||
|
||||
export interface SleepQuestionRef {
|
||||
getAnswer: () => ESleepLevel;
|
||||
}
|
||||
|
||||
export default React.forwardRef<SleepQuestionRef, QuestionChildProps>(
|
||||
({ ...props }, ref): React.ReactElement => {
|
||||
const [checkedItems, setCheckedItems] = useState([
|
||||
true,
|
||||
...Array(4).fill(false),
|
||||
]);
|
||||
|
||||
const handleChange = (index: number) => {
|
||||
const newCheckedState = checkedItems.map((_, i) => i === index);
|
||||
setCheckedItems(newCheckedState);
|
||||
};
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getAnswer: () => {
|
||||
let selected = 0;
|
||||
checkedItems.forEach((item, index) => {
|
||||
if (item) {
|
||||
selected = index;
|
||||
}
|
||||
});
|
||||
return SleepLevels[SleepLevels.length - 1 - selected];
|
||||
},
|
||||
}));
|
||||
|
||||
interface IData {
|
||||
label: string;
|
||||
icon: AntDesignIconNames;
|
||||
endText: string;
|
||||
}
|
||||
|
||||
const data: IData[] = [
|
||||
{
|
||||
label: "Excellent",
|
||||
icon: "smile-circle",
|
||||
endText: ">8 heures",
|
||||
},
|
||||
{
|
||||
label: "Bien",
|
||||
icon: "smileo",
|
||||
endText: "7-8 heures",
|
||||
},
|
||||
{
|
||||
label: "Mauvaise",
|
||||
icon: "meh",
|
||||
endText: "6-7 heures",
|
||||
},
|
||||
{
|
||||
label: "Très mauvaise",
|
||||
icon: "frowno",
|
||||
endText: "3-4 heures",
|
||||
},
|
||||
{
|
||||
label: "Insomniaque",
|
||||
icon: "frown",
|
||||
endText: "<2 heures",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Question
|
||||
question="Quelle est la qualité de votre sommeil ?"
|
||||
{...ref}
|
||||
{...props}
|
||||
>
|
||||
<View className="gap-2">
|
||||
{data.map((item, index) => (
|
||||
<Checkbox
|
||||
key={index}
|
||||
label={item.label}
|
||||
value={checkedItems[index]}
|
||||
onChange={() => handleChange(index)}
|
||||
antIcon={item.icon}
|
||||
endText={item.endText}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</Question>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,86 @@
|
||||
import { ESport, Sports } from "@/model/enums/Enums";
|
||||
import React, {
|
||||
forwardRef,
|
||||
ReactElement,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
} from "react";
|
||||
import { View } from "react-native";
|
||||
import CheckBox from "../CheckBox";
|
||||
import {
|
||||
AntDesignIconNames,
|
||||
CommunityIconNames,
|
||||
EntypoIconNames,
|
||||
FontAwesome6IconNames,
|
||||
} from "../Icons";
|
||||
import Question, { QuestionChildProps } from "./Question";
|
||||
|
||||
export interface SportQuestionRef {
|
||||
getAnswer: () => ESport;
|
||||
}
|
||||
|
||||
export default forwardRef<SportQuestionRef, QuestionChildProps>(
|
||||
(props, ref): ReactElement => {
|
||||
const [checkedItems, setCheckedItems] = useState([
|
||||
true,
|
||||
...Array(8).fill(false),
|
||||
]);
|
||||
|
||||
const handleChange = (index: number) => {
|
||||
const newCheckedState = checkedItems.map((_, i) => i === index);
|
||||
setCheckedItems(newCheckedState);
|
||||
};
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getAnswer: () => {
|
||||
let selected = 0;
|
||||
checkedItems.forEach((item, index) => {
|
||||
if (item) {
|
||||
selected = index;
|
||||
}
|
||||
});
|
||||
return Sports[selected];
|
||||
},
|
||||
}));
|
||||
|
||||
interface IData {
|
||||
label: string;
|
||||
commIcon?: CommunityIconNames;
|
||||
antIcon?: AntDesignIconNames;
|
||||
entypoIcon?: EntypoIconNames;
|
||||
fontAwesomeIcon?: FontAwesome6IconNames;
|
||||
}
|
||||
|
||||
const data: IData[] = [
|
||||
{ label: "Course", fontAwesomeIcon: "person-running" },
|
||||
{ label: "Marche", fontAwesomeIcon: "person-walking" },
|
||||
{ label: "Rando", fontAwesomeIcon: "person-hiking" },
|
||||
{ label: "Skate", commIcon: "skateboarding" },
|
||||
{ label: "Cyclisme", commIcon: "bike" },
|
||||
{ label: "Basket", fontAwesomeIcon: "basketball" },
|
||||
{ label: "Cardio", antIcon: "heart" },
|
||||
{ label: "Yoga", commIcon: "yoga" },
|
||||
{ label: "Autre", entypoIcon: "dots-three-horizontal" },
|
||||
];
|
||||
|
||||
return (
|
||||
<Question question="Quel sport pratiquez-vous ?" {...ref} {...props}>
|
||||
<View className="flex-row justify-center flex-wrap gap-2">
|
||||
{data.map((item, index) => (
|
||||
<CheckBox
|
||||
key={index}
|
||||
label={item.label}
|
||||
value={checkedItems[index]}
|
||||
onChange={() => handleChange(index)}
|
||||
fontAwesome6Icon={item.fontAwesomeIcon}
|
||||
communityIcon={item.commIcon}
|
||||
antIcon={item.antIcon}
|
||||
entypoIcon={item.entypoIcon}
|
||||
direction="col"
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</Question>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,65 @@
|
||||
import React, {
|
||||
forwardRef,
|
||||
ReactElement,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
} from "react";
|
||||
import { View } from "react-native";
|
||||
import SegmentedControl from "../ui/SegmentedControl";
|
||||
import Slider from "../ui/Slider";
|
||||
import Text from "../ui/Text";
|
||||
import Question, { QuestionChildProps } from "./Question";
|
||||
|
||||
const MIN_WEIGHT = 40;
|
||||
const MAX_WEIGHT = 200;
|
||||
|
||||
export interface WeightQuestionRef {
|
||||
getAnswer: () => number;
|
||||
}
|
||||
|
||||
const WeightQuestion = forwardRef<WeightQuestionRef, QuestionChildProps>(
|
||||
(props, ref): ReactElement => {
|
||||
const UNITS = ["kg", "lb"];
|
||||
const [answer, setAnswer] = useState<number>(MIN_WEIGHT);
|
||||
const [unit, setUnit] = useState<string>("kg");
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getAnswer: () => answer,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Question question="Quel est votre poids ?" {...props}>
|
||||
<SegmentedControl
|
||||
values={UNITS}
|
||||
selectedIndex={UNITS.indexOf(unit)}
|
||||
onValueChange={setUnit}
|
||||
/>
|
||||
<View className="flex-row justify-center gap-2">
|
||||
{answer <= MIN_WEIGHT && (
|
||||
<Text className="mt-8" size="4xl">
|
||||
- de
|
||||
</Text>
|
||||
)}
|
||||
{answer >= MAX_WEIGHT && (
|
||||
<Text className="mt-8" size="4xl">
|
||||
+ de
|
||||
</Text>
|
||||
)}
|
||||
<Text size="8xl" weight="bold">
|
||||
{answer}
|
||||
</Text>
|
||||
<Text className="mt-8" size="4xl">
|
||||
{unit}
|
||||
</Text>
|
||||
</View>
|
||||
<Slider
|
||||
minimumValue={MIN_WEIGHT}
|
||||
maximumValue={MAX_WEIGHT}
|
||||
onValueChange={setAnswer}
|
||||
/>
|
||||
</Question>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default WeightQuestion;
|
@ -0,0 +1,22 @@
|
||||
import React from "react";
|
||||
import SegmentedControl, {
|
||||
SegmentedControlProps,
|
||||
} from "@react-native-segmented-control/segmented-control";
|
||||
|
||||
export default React.forwardRef<any, SegmentedControlProps>(
|
||||
(props, ref): React.ReactElement => {
|
||||
return (
|
||||
<SegmentedControl
|
||||
fontStyle={{
|
||||
fontSize: 16,
|
||||
fontWeight: "bold",
|
||||
color: "black",
|
||||
}}
|
||||
tintColor="#60A5FA"
|
||||
backgroundColor="#D1D5DB"
|
||||
{...ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,17 @@
|
||||
import React from "react";
|
||||
import Slider, { SliderProps } from "@react-native-community/slider";
|
||||
|
||||
export default React.forwardRef<any, SliderProps>(
|
||||
({ ...props }, ref): React.ReactElement => {
|
||||
return (
|
||||
<Slider
|
||||
step={1}
|
||||
thumbTintColor="#F97316"
|
||||
minimumTrackTintColor="#F97316"
|
||||
maximumTrackTintColor="#F97316"
|
||||
{...ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,204 @@
|
||||
import {
|
||||
EHealthProblem,
|
||||
ESleepLevel,
|
||||
ESport,
|
||||
ESportLevel,
|
||||
} from "./enums/Enums";
|
||||
|
||||
export class User {
|
||||
private _name: string;
|
||||
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,
|
||||
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
|
||||
) {
|
||||
this._name = name;
|
||||
this._age = age;
|
||||
this._height = height;
|
||||
this._weight = weight;
|
||||
this._sexe = sexe;
|
||||
this._logo = logo;
|
||||
this._nbSessionPerWeek = nbSessionPerWeek;
|
||||
this._goal = goal;
|
||||
this._healthProblems = healthProblems;
|
||||
this._sport = sport;
|
||||
this._sleepLevel = sleepLevel;
|
||||
this._sportLevel = sportLevel;
|
||||
this._email = email;
|
||||
this._password = password;
|
||||
}
|
||||
|
||||
// Getters
|
||||
get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get age(): number | undefined {
|
||||
return this._age;
|
||||
}
|
||||
|
||||
get height(): number | undefined {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
get weight(): number | undefined {
|
||||
return this._weight;
|
||||
}
|
||||
|
||||
get sexe(): boolean | undefined {
|
||||
return this._sexe;
|
||||
}
|
||||
|
||||
get logo(): string | undefined {
|
||||
return this._logo;
|
||||
}
|
||||
|
||||
get nbSessionPerWeek(): number | undefined {
|
||||
return this._nbSessionPerWeek;
|
||||
}
|
||||
|
||||
get goals(): string | undefined {
|
||||
return this._goal;
|
||||
}
|
||||
|
||||
get healthProblems(): EHealthProblem[] | undefined {
|
||||
return this._healthProblems;
|
||||
}
|
||||
|
||||
get sports(): ESport | undefined {
|
||||
return this._sport;
|
||||
}
|
||||
|
||||
get sleepLevel(): ESleepLevel | undefined {
|
||||
return this._sleepLevel;
|
||||
}
|
||||
|
||||
get sportLevel(): ESportLevel | undefined {
|
||||
return this._sportLevel;
|
||||
}
|
||||
|
||||
get email(): string {
|
||||
return this._email;
|
||||
}
|
||||
|
||||
get password(): string {
|
||||
return this._password;
|
||||
}
|
||||
|
||||
// Setters
|
||||
set name(value: string) {
|
||||
this._name = value;
|
||||
}
|
||||
|
||||
set age(value: number | undefined) {
|
||||
this._age = value;
|
||||
}
|
||||
|
||||
set height(value: number | undefined) {
|
||||
this._height = value;
|
||||
}
|
||||
|
||||
set weight(value: number | undefined) {
|
||||
this._weight = value;
|
||||
}
|
||||
|
||||
set sexe(value: boolean | undefined) {
|
||||
this._sexe = value;
|
||||
}
|
||||
|
||||
set logo(value: string | undefined) {
|
||||
this._logo = value;
|
||||
}
|
||||
|
||||
set nbSessionPerWeek(value: number | undefined) {
|
||||
this._nbSessionPerWeek = value;
|
||||
}
|
||||
|
||||
set goals(value: string | undefined) {
|
||||
this._goal = value;
|
||||
}
|
||||
|
||||
set healthProblems(value: EHealthProblem[] | undefined) {
|
||||
this._healthProblems = value;
|
||||
}
|
||||
|
||||
set sports(value: ESport | undefined) {
|
||||
this._sport = value;
|
||||
}
|
||||
|
||||
set sleepLevel(value: ESleepLevel | undefined) {
|
||||
this._sleepLevel = value;
|
||||
}
|
||||
|
||||
set sportLevel(value: ESportLevel | undefined) {
|
||||
this._sportLevel = value;
|
||||
}
|
||||
|
||||
set email(value: string) {
|
||||
this._email = value;
|
||||
}
|
||||
|
||||
set password(value: string) {
|
||||
this._password = value;
|
||||
}
|
||||
|
||||
public isQuizDone(): boolean {
|
||||
return (
|
||||
this._name !== undefined &&
|
||||
this._age !== undefined &&
|
||||
this._height !== undefined &&
|
||||
this._weight !== undefined &&
|
||||
this._sexe !== undefined &&
|
||||
this._nbSessionPerWeek !== undefined &&
|
||||
this._goal !== undefined &&
|
||||
this._healthProblems !== undefined &&
|
||||
this._sport !== undefined &&
|
||||
this._sleepLevel !== undefined &&
|
||||
this._sportLevel !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
export interface Workout {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
duration: number;
|
||||
image: string;
|
||||
video: string;
|
||||
nbSeries: number;
|
||||
nbRepetitions: number;
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
duration: number;
|
||||
image: string;
|
||||
video: string;
|
||||
nbSeries: number;
|
||||
nbRepetitions: number;
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
export const Goals = [
|
||||
"WEIGHT_LOSE",
|
||||
"IMPROVE_MUSCLE",
|
||||
"WEIGHT_GAIN",
|
||||
"IMPROVE_STAMINA",
|
||||
"KEEP_FIT",
|
||||
] as const;
|
||||
|
||||
export const SportLevels = [
|
||||
"NOT_SPORTY",
|
||||
"BEGINNER",
|
||||
"SPORTY",
|
||||
"VERY_SPORTY",
|
||||
"PERFORMER",
|
||||
] as const;
|
||||
|
||||
export const Sports = [
|
||||
"RUNNING",
|
||||
"WALKING",
|
||||
"RANDO",
|
||||
"SKATEBOARD",
|
||||
"BIKING",
|
||||
"BASKETBALL",
|
||||
"CARDIO",
|
||||
"YOGA",
|
||||
"ELSE",
|
||||
] as const;
|
||||
|
||||
export const SleepLevels = [
|
||||
"TERRIBLE",
|
||||
"VERY_BAD",
|
||||
"BAD",
|
||||
"GOOD",
|
||||
"EXCELLENT",
|
||||
] as const;
|
||||
|
||||
export type EGoal = (typeof Goals)[number];
|
||||
export type ESportLevel = (typeof SportLevels)[number];
|
||||
export type ESport = (typeof Sports)[number];
|
||||
export type ESleepLevel = (typeof SleepLevels)[number];
|
||||
|
||||
export type EHealthProblem = "ARTHROSE" | "MIGRAINE";
|
@ -0,0 +1,5 @@
|
||||
import { User } from "@/model/User";
|
||||
|
||||
export interface IUserService {
|
||||
login(email: string, password: string): User | undefined;
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
import { User } from "@/model/User";
|
||||
import { IUserService } from "./user.service.interface";
|
||||
|
||||
export class UserServiceStub 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(
|
||||
"Bob",
|
||||
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"
|
||||
),
|
||||
];
|
||||
|
||||
login(email: string, password: string): User | undefined {
|
||||
return this.users.find((x) => x.email === email && x.password === password);
|
||||
}
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { createAvatar } from "@gluestack-ui/avatar";
|
||||
|
||||
import { View, Text, Image, Platform } from "react-native";
|
||||
|
||||
import { tva } from "@gluestack-ui/nativewind-utils/tva";
|
||||
import {
|
||||
withStyleContext,
|
||||
useStyleContext,
|
||||
} from "@gluestack-ui/nativewind-utils/withStyleContext";
|
||||
import type { VariantProps } from "@gluestack-ui/nativewind-utils";
|
||||
const SCOPE = "AVATAR";
|
||||
|
||||
const UIAvatar = createAvatar({
|
||||
Root: withStyleContext(View, SCOPE),
|
||||
Badge: View,
|
||||
Group: View,
|
||||
Image: Image,
|
||||
FallbackText: Text,
|
||||
});
|
||||
|
||||
const avatarStyle = tva({
|
||||
base: "rounded-full justify-center items-center relative bg-primary-600 group-[.avatar-group]/avatar-group:-ml-2.5",
|
||||
variants: {
|
||||
size: {
|
||||
xs: "w-6 h-6",
|
||||
sm: "w-8 h-8",
|
||||
md: "w-12 h-12",
|
||||
lg: "w-16 h-16",
|
||||
xl: "w-24 h-24",
|
||||
"2xl": "w-32 h-32",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const avatarFallbackTextStyle = tva({
|
||||
base: "text-typography-0 font-semibold overflow-hidden text-transform:uppercase web:cursor-default",
|
||||
|
||||
parentVariants: {
|
||||
size: {
|
||||
xs: "text-2xs",
|
||||
sm: "text-xs",
|
||||
md: "text-base",
|
||||
lg: "text-xl",
|
||||
xl: "text-3xl",
|
||||
"2xl": "text-5xl",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const avatarGroupStyle = tva({
|
||||
base: "group/avatar-group flex-row-reverse relative avatar-group",
|
||||
});
|
||||
|
||||
const avatarBadgeStyle = tva({
|
||||
base: "w-5 h-5 bg-success-500 rounded-full absolute right-0 bottom-0 border-background-0 border-2",
|
||||
parentVariants: {
|
||||
size: {
|
||||
xs: "w-2 h-2",
|
||||
sm: "w-2 h-2",
|
||||
md: "w-3 h-3",
|
||||
lg: "w-4 h-4",
|
||||
xl: "w-6 h-6",
|
||||
"2xl": "w-8 h-8",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const avatarImageStyle = tva({
|
||||
base: "h-full w-full rounded-full absolute",
|
||||
});
|
||||
|
||||
type IAvatarProps = Omit<
|
||||
React.ComponentPropsWithoutRef<typeof UIAvatar>,
|
||||
"context"
|
||||
> &
|
||||
VariantProps<typeof avatarStyle>;
|
||||
|
||||
const Avatar = React.forwardRef<
|
||||
React.ElementRef<typeof UIAvatar>,
|
||||
IAvatarProps
|
||||
>(({ className, size = "md", ...props }, ref) => {
|
||||
return (
|
||||
<UIAvatar
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={avatarStyle({ size, class: className })}
|
||||
context={{ size }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IAvatarBadgeProps = React.ComponentPropsWithoutRef<typeof UIAvatar.Badge> &
|
||||
VariantProps<typeof avatarBadgeStyle>;
|
||||
|
||||
const AvatarBadge = React.forwardRef<
|
||||
React.ElementRef<typeof UIAvatar.Badge>,
|
||||
IAvatarBadgeProps
|
||||
>(({ className, size, ...props }, ref) => {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
return (
|
||||
<UIAvatar.Badge
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={avatarBadgeStyle({
|
||||
parentVariants: {
|
||||
size: parentSize,
|
||||
},
|
||||
size,
|
||||
class: className,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IAvatarFallbackTextProps = React.ComponentPropsWithoutRef<
|
||||
typeof UIAvatar.FallbackText
|
||||
> &
|
||||
VariantProps<typeof avatarFallbackTextStyle>;
|
||||
const AvatarFallbackText = React.forwardRef<
|
||||
React.ElementRef<typeof UIAvatar.FallbackText>,
|
||||
IAvatarFallbackTextProps
|
||||
>(({ className, size, ...props }, ref) => {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
return (
|
||||
<UIAvatar.FallbackText
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={avatarFallbackTextStyle({
|
||||
parentVariants: {
|
||||
size: parentSize,
|
||||
},
|
||||
size,
|
||||
class: className,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IAvatarImageProps = React.ComponentPropsWithoutRef<typeof UIAvatar.Image> &
|
||||
VariantProps<typeof avatarImageStyle>;
|
||||
|
||||
const AvatarImage = React.forwardRef<
|
||||
React.ElementRef<typeof UIAvatar.Image>,
|
||||
IAvatarImageProps
|
||||
>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<UIAvatar.Image
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={avatarImageStyle({
|
||||
class: className,
|
||||
})}
|
||||
// @ts-expect-error
|
||||
style={
|
||||
Platform.OS === "web"
|
||||
? // eslint-disable-next-line react-native/no-inline-styles
|
||||
{ height: "revert-layer", width: "revert-layer" }
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type IAvatarGroupProps = React.ComponentPropsWithoutRef<typeof UIAvatar.Group> &
|
||||
VariantProps<typeof avatarGroupStyle>;
|
||||
|
||||
const AvatarGroup = React.forwardRef<
|
||||
React.ElementRef<typeof UIAvatar.Group>,
|
||||
IAvatarGroupProps
|
||||
>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<UIAvatar.Group
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={avatarGroupStyle({
|
||||
class: className,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export { Avatar, AvatarBadge, AvatarFallbackText, AvatarImage, AvatarGroup };
|
@ -1,237 +0,0 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { createCheckbox } from '@gluestack-ui/checkbox';
|
||||
import { View, Pressable, Text, Platform } from 'react-native';
|
||||
import type { TextProps, ViewProps } from 'react-native';
|
||||
import { tva } from '@gluestack-ui/nativewind-utils/tva';
|
||||
import { PrimitiveIcon, IPrimitiveIcon, UIIcon } from '@gluestack-ui/icon';
|
||||
import {
|
||||
withStyleContext,
|
||||
useStyleContext,
|
||||
} from '@gluestack-ui/nativewind-utils/withStyleContext';
|
||||
import { cssInterop } from 'nativewind';
|
||||
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||
|
||||
const IndicatorWrapper = React.forwardRef<
|
||||
React.ElementRef<typeof View>,
|
||||
ViewProps
|
||||
>(({ ...props }, ref) => {
|
||||
return <View {...props} ref={ref} />;
|
||||
});
|
||||
|
||||
const LabelWrapper = React.forwardRef<React.ElementRef<typeof Text>, TextProps>(
|
||||
({ ...props }, ref) => {
|
||||
return <Text {...props} ref={ref} />;
|
||||
}
|
||||
);
|
||||
|
||||
const IconWrapper = React.forwardRef<
|
||||
React.ElementRef<typeof PrimitiveIcon>,
|
||||
IPrimitiveIcon
|
||||
>(({ ...props }, ref) => {
|
||||
return <UIIcon {...props} ref={ref} />;
|
||||
});
|
||||
|
||||
const SCOPE = 'CHECKBOX';
|
||||
const UICheckbox = createCheckbox({
|
||||
// @ts-expect-error
|
||||
Root:
|
||||
Platform.OS === 'web'
|
||||
? withStyleContext(View, SCOPE)
|
||||
: withStyleContext(Pressable, SCOPE),
|
||||
Group: View,
|
||||
Icon: IconWrapper,
|
||||
Label: LabelWrapper,
|
||||
Indicator: IndicatorWrapper,
|
||||
});
|
||||
|
||||
cssInterop(PrimitiveIcon, {
|
||||
className: {
|
||||
target: 'style',
|
||||
nativeStyleToProp: {
|
||||
height: true,
|
||||
width: true,
|
||||
fill: true,
|
||||
color: 'classNameColor',
|
||||
stroke: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const checkboxStyle = tva({
|
||||
base: 'group/checkbox flex-row items-center justify-start web:cursor-pointer data-[disabled=true]:cursor-not-allowed',
|
||||
variants: {
|
||||
size: {
|
||||
lg: 'gap-2',
|
||||
md: 'gap-2',
|
||||
sm: 'gap-1.5',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const checkboxIndicatorStyle = tva({
|
||||
base: 'justify-center items-center border-outline-400 bg-transparent rounded web:data-[focus-visible=true]:outline-none web:data-[focus-visible=true]:ring-2 web:data-[focus-visible=true]:ring-indicator-primary data-[checked=true]:bg-primary-600 data-[checked=true]:border-primary-600 data-[hover=true]:data-[checked=false]:border-outline-500 data-[hover=true]:bg-transparent data-[hover=true]:data-[invalid=true]:border-error-700 data-[hover=true]:data-[checked=true]:bg-primary-700 data-[hover=true]:data-[checked=true]:border-primary-700 data-[hover=true]:data-[checked=true]:data-[disabled=true]:border-primary-600 data-[hover=true]:data-[checked=true]:data-[disabled=true]:bg-primary-600 data-[hover=true]:data-[checked=true]:data-[disabled=true]:opacity-40 data-[hover=true]:data-[checked=true]:data-[disabled=true]:data-[invalid=true]:border-error-700 data-[hover=true]:data-[disabled=true]:border-outline-400 data-[hover=true]:data-[disabled=true]:data-[invalid=true]:border-error-700 data-[active=true]:data-[checked=true]:bg-primary-800 data-[active=true]:data-[checked=true]:border-primary-800 data-[invalid=true]:border-error-700 data-[disabled=true]:opacity-40',
|
||||
parentVariants: {
|
||||
size: {
|
||||
lg: 'w-6 h-6 border-[3px]',
|
||||
md: 'w-5 h-5 border-2',
|
||||
sm: 'w-4 h-4 border-2',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const checkboxLabelStyle = tva({
|
||||
base: 'text-typography-600 data-[checked=true]:text-typography-900 data-[hover=true]:text-typography-900 data-[hover=true]:data-[checked=true]:text-typography-900 data-[hover=true]:data-[checked=true]:data-[disabled=true]:text-typography-900 data-[hover=true]:data-[disabled=true]:text-typography-400 data-[active=true]:text-typography-900 data-[active=true]:data-[checked=true]:text-typography-900 data-[disabled=true]:opacity-40 web:select-none',
|
||||
parentVariants: {
|
||||
size: {
|
||||
lg: 'text-lg',
|
||||
md: 'text-base',
|
||||
sm: 'text-sm',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const checkboxIconStyle = tva({
|
||||
base: 'text-typography-50 fill-none',
|
||||
|
||||
parentVariants: {
|
||||
size: {
|
||||
sm: 'h-3 w-3',
|
||||
md: 'h-4 w-4',
|
||||
lg: 'h-5 w-5',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const CheckboxGroup = UICheckbox.Group;
|
||||
|
||||
type ICheckboxProps = React.ComponentPropsWithoutRef<typeof UICheckbox> &
|
||||
VariantProps<typeof checkboxStyle>;
|
||||
|
||||
const Checkbox = React.forwardRef<
|
||||
React.ElementRef<typeof UICheckbox>,
|
||||
ICheckboxProps
|
||||
>(({ className, size = 'md', ...props }, ref) => {
|
||||
return (
|
||||
<UICheckbox
|
||||
className={checkboxStyle({
|
||||
class: className,
|
||||
size,
|
||||
})}
|
||||
{...props}
|
||||
context={{
|
||||
size,
|
||||
}}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type ICheckboxIndicatorProps = React.ComponentPropsWithoutRef<
|
||||
typeof UICheckbox.Indicator
|
||||
> &
|
||||
VariantProps<typeof checkboxIndicatorStyle>;
|
||||
|
||||
const CheckboxIndicator = React.forwardRef<
|
||||
React.ElementRef<typeof UICheckbox.Indicator>,
|
||||
ICheckboxIndicatorProps
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
return (
|
||||
<UICheckbox.Indicator
|
||||
className={checkboxIndicatorStyle({
|
||||
parentVariants: {
|
||||
size: parentSize,
|
||||
},
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type ICheckboxLabelProps = React.ComponentPropsWithoutRef<
|
||||
typeof UICheckbox.Label
|
||||
> &
|
||||
VariantProps<typeof checkboxLabelStyle>;
|
||||
const CheckboxLabel = React.forwardRef<
|
||||
React.ElementRef<typeof UICheckbox.Label>,
|
||||
ICheckboxLabelProps
|
||||
>(({ className, ...props }, ref) => {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
return (
|
||||
<UICheckbox.Label
|
||||
className={checkboxLabelStyle({
|
||||
parentVariants: {
|
||||
size: parentSize,
|
||||
},
|
||||
class: className,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
type ICheckboxIconProps = React.ComponentPropsWithoutRef<
|
||||
typeof UICheckbox.Icon
|
||||
> &
|
||||
VariantProps<typeof checkboxIconStyle>;
|
||||
|
||||
const CheckboxIcon = React.forwardRef<
|
||||
React.ElementRef<typeof UICheckbox.Icon>,
|
||||
ICheckboxIconProps
|
||||
>(({ className, size, ...props }, ref) => {
|
||||
const { size: parentSize } = useStyleContext(SCOPE);
|
||||
|
||||
if (typeof size === 'number') {
|
||||
return (
|
||||
<UICheckbox.Icon
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={checkboxIconStyle({ class: className })}
|
||||
size={size}
|
||||
/>
|
||||
);
|
||||
} else if (
|
||||
(props.height !== undefined || props.width !== undefined) &&
|
||||
size === undefined
|
||||
) {
|
||||
return (
|
||||
<UICheckbox.Icon
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={checkboxIconStyle({ class: className })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<UICheckbox.Icon
|
||||
className={checkboxIconStyle({
|
||||
parentVariants: {
|
||||
size: parentSize,
|
||||
},
|
||||
class: className,
|
||||
size,
|
||||
})}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
Checkbox.displayName = 'Checkbox';
|
||||
CheckboxIndicator.displayName = 'CheckboxIndicator';
|
||||
CheckboxLabel.displayName = 'CheckboxLabel';
|
||||
CheckboxIcon.displayName = 'CheckboxIcon';
|
||||
|
||||
export {
|
||||
Checkbox,
|
||||
CheckboxIndicator,
|
||||
CheckboxLabel,
|
||||
CheckboxIcon,
|
||||
CheckboxGroup,
|
||||
};
|