parent
96176e7c39
commit
9b7f77dd74
@ -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>
|
||||||
|
);
|
||||||
|
}
|
@ -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" />;
|
||||||
|
}
|
Binary file not shown.
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,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 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,40 +1,40 @@
|
|||||||
import {Image, ImageBackground, Text, TouchableOpacity, View} from "react-native";
|
import { useSession } from "@/ctx";
|
||||||
import {Ionicons} from "@expo/vector-icons";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {useSession} from "@/ctx";
|
import { Image, ImageBackground, Text, View } from "react-native";
|
||||||
|
|
||||||
export default function WelcomeComponent() {
|
export default function WelcomeComponent() {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const formattedDate = date.toLocaleDateString('fr-FR', {
|
const formattedDate = date.toLocaleDateString("fr-FR", {
|
||||||
year: 'numeric',
|
year: "numeric",
|
||||||
month: 'long',
|
month: "long",
|
||||||
day: 'numeric',
|
day: "numeric",
|
||||||
});
|
});
|
||||||
const {session} = useSession();
|
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")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<View>
|
||||||
return (
|
<Text className="text-gray-500 font-semibold ml-4">
|
||||||
<View className="rounded-2xl overflow-hidden shadow-lg h-full bg-black justify-center">
|
{formattedDate}
|
||||||
<ImageBackground
|
</Text>
|
||||||
source={require("assets/images/black-back.png")}
|
<Text className="text-white text-4xl ml-4 mt-0.5">
|
||||||
className="w-full h-full justify-center"
|
Hello {session?.name} !
|
||||||
>
|
</Text>
|
||||||
|
</View>
|
||||||
<View className="flex-row items-center justify-between">
|
</View>
|
||||||
<View className="flex-row items-center w-full">
|
</View>
|
||||||
<Image className="h-16 w-16 rounded-2xl"
|
</ImageBackground>
|
||||||
source={require("assets/images/sigma-profile.jpeg")}
|
</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} !</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
</ImageBackground>
|
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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";
|
File diff suppressed because it is too large
Load Diff
@ -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,
|
|
||||||
};
|
|
Loading…
Reference in new issue