276 lines
11 KiB
TypeScript
276 lines
11 KiB
TypeScript
import React, { useState } from "react";
|
|
import { Form, Field } from "react-final-form";
|
|
import Slider from "@mui/material/Slider";
|
|
import { useLazySendTestVersionQuery } from "../store/api/chatApi";
|
|
import { LuLoader2 } from "react-icons/lu";
|
|
import { Link } from "react-router-dom";
|
|
interface FormValues {
|
|
age?: number;
|
|
height?: number;
|
|
weight?: number;
|
|
healthGoal?: string;
|
|
dietType?: string;
|
|
exerciseLevel?: string;
|
|
hydrationGoal?: string;
|
|
userInput?: string;
|
|
}
|
|
|
|
|
|
const MultiStepForm: React.FC = () => {
|
|
const [formValues, setFormValues] = useState<FormValues>({});
|
|
const [stage, setStage] = useState<number>(1);
|
|
const [data, setData] = useState<string | null>(null)
|
|
|
|
const [sendTestMessage, { isLoading, isFetching }] = useLazySendTestVersionQuery()
|
|
|
|
const nextStage = () => setStage((prev) => prev + 1);
|
|
const previousStage = () => setStage((prev) => prev - 1);
|
|
|
|
const saveFormData = (values: FormValues) => {
|
|
setFormValues((prev) => ({
|
|
...prev,
|
|
...values,
|
|
}));
|
|
};
|
|
|
|
const onSubmit = (values: FormValues) => {
|
|
saveFormData(values);
|
|
nextStage();
|
|
};
|
|
console.log(isLoading)
|
|
|
|
const finalSubmit = async () => {
|
|
const res = await sendTestMessage(formValues).unwrap()
|
|
setData(res)
|
|
};
|
|
|
|
const selectEmoji = (
|
|
value: number | undefined,
|
|
thresholds: number[],
|
|
emojis: string[]
|
|
) => {
|
|
if (value === undefined) return null;
|
|
if (value <= thresholds[0]) return emojis[0];
|
|
if (value <= thresholds[1]) return emojis[1];
|
|
return emojis[2];
|
|
};
|
|
|
|
return !data ? (
|
|
<div className="w-full max-w-md bg-white rounded-lg shadow-lg p-8 text-center">
|
|
|
|
<h1 className="text-2xl font-bold text-gray-700 mb-6">
|
|
Fill in your profile and get some advices
|
|
</h1>
|
|
|
|
<Form<FormValues>
|
|
onSubmit={onSubmit}
|
|
initialValues={formValues}
|
|
render={({ handleSubmit, values }) => (
|
|
<form onSubmit={handleSubmit}>
|
|
{stage === 1 && (<> <div>
|
|
<h2 className="text-xl font-semibold text-gray-600 mb-4">
|
|
Stage 1: Base information
|
|
</h2>
|
|
<div className="text-3xl mb-4">
|
|
{selectEmoji(values.age, [17, 50], ["👶", "🧑", "👴"])}
|
|
</div>
|
|
<Field
|
|
name="age"
|
|
parse={(value) => (value === "" ? 0 : Number(value))}
|
|
>
|
|
{({ input }) => (
|
|
<input
|
|
{...input}
|
|
type="number"
|
|
placeholder="Enter age"
|
|
min={0}
|
|
max={100}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-indigo-500"
|
|
/>
|
|
)}
|
|
</Field>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div>
|
|
|
|
<div className="text-3xl mb-4">
|
|
{selectEmoji(values.height, [150, 175], ["🌱", "🌳", "🌲"])}
|
|
</div>
|
|
<Field
|
|
name="height"
|
|
parse={(value) => (value === "" ? 0 : Number(value))}
|
|
>
|
|
{({ input }) => (
|
|
<input
|
|
{...input}
|
|
type="number"
|
|
placeholder="Enter height"
|
|
min={0}
|
|
max={250}
|
|
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-indigo-500"
|
|
/>
|
|
)}
|
|
</Field>
|
|
|
|
</div>
|
|
<div >
|
|
<h2 className="text-xl font-semibold text-gray-600 mb-4">
|
|
</h2>
|
|
<Field<string> name="dietType" component="select" className="w-full p-3 border border-gray-300 rounded-lg text-gray-800 focus:outline-none focus:border-blue-500">
|
|
<option value="">Select your goal</option>
|
|
<option value="weight_loss">Weight Loss</option>
|
|
<option value="muscle_gain">Muscle Gain</option>
|
|
<option value="improve_energy">Improve Energy</option>
|
|
<option value="enhance_focus">Enhance Focus</option>
|
|
<option value="general_health">General Health</option>
|
|
</Field>
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
<div className="text-3xl mb-4">
|
|
{selectEmoji(values.weight, [70, 99], ["🐭", "🐱", "🐘"])}
|
|
</div>
|
|
<Field
|
|
name="weight"
|
|
parse={(value) => (value === "" ? 0 : Number(value))}
|
|
>
|
|
{({ input }) => (
|
|
<div>
|
|
<Slider
|
|
value={input.value || 0}
|
|
onChange={(_, value) =>
|
|
input.onChange(
|
|
Array.isArray(value) ? value[0] : value
|
|
)
|
|
}
|
|
min={0}
|
|
max={200}
|
|
className="text-indigo-500"
|
|
/>
|
|
<div className="text-gray-600 mt-2">
|
|
Current Weight: {input.value || 0} kg
|
|
</div>
|
|
</div>
|
|
)}
|
|
</Field>
|
|
<div className="flex justify-end">
|
|
|
|
<button
|
|
type="button"
|
|
onClick={nextStage}
|
|
className="px-4 py-2 bg-bright-blue text-white rounded-md hover:bg-indigo-500"
|
|
>
|
|
Next
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</>)}
|
|
|
|
|
|
{stage === 2 && (
|
|
<>
|
|
<div>
|
|
<h2 className="text-xl font-semibold text-gray-600 mb-4">
|
|
Stage 2: Details
|
|
</h2>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="text-start">
|
|
<div className="mb-4">
|
|
<label className="text-gray-700 mb-2 block">Diet Type</label>
|
|
<Field<string> name="dietType" component="select" className="w-full p-3 border border-gray-300 rounded-lg text-gray-800 focus:outline-none focus:border-blue-500">
|
|
<option value="">Select diet type</option>
|
|
<option value="keto">Keto</option>
|
|
<option value="low_carb">Low Carb</option>
|
|
<option value="intermittent_fasting">Intermittent Fasting</option>
|
|
<option value="mediterranean">Mediterranean</option>
|
|
</Field>
|
|
</div>
|
|
<div className="mb-4">
|
|
<label className="text-gray-700 mb-2 block">Exercise Level</label>
|
|
<Field<string> name="exerciseLevel" component="select" className="w-full p-3 border border-gray-300 rounded-lg text-gray-800 focus:outline-none focus:border-blue-500">
|
|
<option value="">Select exercise level</option>
|
|
<option value="beginner">Beginner</option>
|
|
<option value="intermediate">Intermediate</option>
|
|
<option value="advanced">Advanced</option>
|
|
</Field>
|
|
</div>
|
|
<div className="mb-4">
|
|
<label className="text-gray-700 mb-2 block">Hydration Goal</label>
|
|
<Field<string> name="hydrationGoal" component="select" className="w-full p-3 border border-gray-300 rounded-lg text-gray-800 focus:outline-none focus:border-blue-500">
|
|
<option value="">Select hydration goal</option>
|
|
<option value="2_liters">2 Liters</option>
|
|
<option value="3_liters">3 Liters</option>
|
|
<option value="4_liters">4 Liters</option>
|
|
</Field>
|
|
</div>
|
|
<div className="mb-4">
|
|
<label className="text-gray-700 mb-2 block">Your Preferences</label>
|
|
<Field<string> name="userInput" component="input" type="text" placeholder="Enter your preferences or comments" className="w-full p-3 border border-gray-300 rounded-lg text-gray-800 focus:outline-none focus:border-blue-500" />
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<button type="button" onClick={previousStage} className="px-4 py-2 bg-gray-400 text-white rounded-md hover:bg-gray-500">
|
|
Previous
|
|
</button>
|
|
<button type="submit" className="px-4 py-2 bg-bright-blue text-white rounded-md hover:bg-indigo-500">
|
|
Next
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)}
|
|
{stage === 3 && (
|
|
<div className="text-start">
|
|
<h2 className="text-xl font-semibold text-gray-600 mb-4 text-center">Summary</h2>
|
|
<p><strong>Age:</strong> {formValues.age}</p>
|
|
<p><strong>Height:</strong> {formValues.height} cm</p>
|
|
<p><strong>Weight:</strong> {formValues.weight} kg</p>
|
|
<p><strong>Health Goal:</strong> {formValues.healthGoal}</p>
|
|
<p><strong>Diet Type:</strong> {formValues.dietType || "Not specified"}</p>
|
|
<p><strong>Exercise Level:</strong> {formValues.exerciseLevel || "Not specified"}</p>
|
|
<p><strong>Hydration Goal:</strong> {formValues.hydrationGoal || "Not specified"}</p>
|
|
<p><strong>User Input:</strong> {formValues.userInput || "Not specified"}</p>
|
|
<div className="flex justify-between mt-4">
|
|
<button type="button" disabled={isLoading} onClick={previousStage} className="px-4 py-2 bg-gray-400 text-white rounded-md hover:bg-gray-500">
|
|
Previous
|
|
</button>
|
|
<button type="button" disabled={isLoading} onClick={finalSubmit} className="px-4 py-2 bg-green-500 text-white rounded-md hover:bg-green-600">
|
|
{isLoading || isFetching ? <LuLoader2 className="animate-spin" /> : 'Confirm'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</form>
|
|
)}
|
|
/>
|
|
</div>
|
|
) : (<div className="w-full flex flex-col items-center gap-6">
|
|
<h1 className="text-4xl flex items-center sm:text-5xl md:text-6xl font-semibold mb-4 text-center text-dark-blue">
|
|
Advices for your health
|
|
</h1>
|
|
<p className="w-1/2">{data}</p>
|
|
<div className="flex gap-2 items-center">
|
|
<Link to='/dashboard'>
|
|
<button className="bg-bright-blue text-white font-medium py-2 px-5 rounded hover:bg-deep-blue transition duration-300 shadow-md">
|
|
Get started with full version
|
|
</button>
|
|
</Link>
|
|
<button onClick={() => { setData(null), setStage(1) }} className="px-4 py-2 bg-gray-400 text-white rounded-md hover:bg-gray-500">
|
|
Try again
|
|
</button>
|
|
|
|
</div>
|
|
</div>
|
|
)
|
|
};
|
|
|
|
export default MultiStepForm;
|