diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..3480f00a Binary files /dev/null and b/.DS_Store differ diff --git a/Recruit-Training/.DS_Store b/Recruit-Training/.DS_Store new file mode 100644 index 00000000..245e1ba0 Binary files /dev/null and b/Recruit-Training/.DS_Store differ diff --git a/Recruit-Training/Advanced-Recruit-Training/.DS_Store b/Recruit-Training/Advanced-Recruit-Training/.DS_Store new file mode 100644 index 00000000..b0c5719c Binary files /dev/null and b/Recruit-Training/Advanced-Recruit-Training/.DS_Store differ diff --git a/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/App.tsx b/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/App.tsx index 488bc934..2ce4cc2b 100644 --- a/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/App.tsx +++ b/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/App.tsx @@ -1,12 +1,240 @@ +import React, { + useCallback, + useMemo, + useReducer, + useRef, + useState, +} from "react"; + import BatteryInput from "~/components/batteryInput"; import Header from "~/components/header"; import SpeedInput from "~/components/speedInput"; import WeatherInput from "~/components/weatherInput"; -const App = () => { - const calculateRange = () => { - return; +// 1. Define the State interface +interface State { + speed: number | string; + battery: number | string; + weather: number | string; + changed: { + speed: boolean; + battery: boolean; + weather: boolean; + }; +} + +type ActionType = + | { + type: "UPDATE_SPEED_AND_CHANGED"; // New combined action type + payload: { + speed: number | string; // Speed value + altered: boolean; // Indicates if the input was changed + }; + } + | { + type: "UPDATE_BATTERY_AND_CHANGED"; // New combined action type + payload: { + battery: number | string; // battery value + altered: boolean; // Indicates if the input was changed + }; + } + | { + type: "UPDATE_WEATHER_AND_CHANGED"; // New combined action type + payload: { + weather: number | string; // battery value + altered: boolean; // Indicates if the input was changed + }; + }; + +const initialState: State = { + speed: "", + battery: "", + weather: 0, + changed: { + speed: false, + battery: false, + weather: false, + }, +}; + +const reducer = (state: State, action: ActionType): State => { + switch (action.type) { + case "UPDATE_SPEED_AND_CHANGED": + return { + ...state, + speed: action.payload.speed, + changed: { + ...state.changed, + speed: action.payload.altered, + }, + }; + case "UPDATE_BATTERY_AND_CHANGED": + return { + ...state, + battery: action.payload.battery, + changed: { + ...state.changed, + battery: action.payload.altered, + }, + }; + case "UPDATE_WEATHER_AND_CHANGED": + return { + ...state, + weather: action.payload.weather, + changed: { + ...state.changed, + weather: action.payload.altered, + }, + }; + default: + return state; } +}; +const App = () => { + const [displayRange, setSD] = useState(false); + const [state, dispatch] = useReducer(reducer, initialState); + const { speed, battery, weather } = state; //initialize the state + + //to avoid the type errors: + const handleSpeedChange = useCallback( + (e: React.ChangeEvent) => { + const value = e.target.value; + dispatch({ + type: "UPDATE_SPEED_AND_CHANGED", + payload: { + speed: value === "" ? "" : Number(value), //empty input is allowed or the input is converted to a number + altered: true, //the input changed so we chnage it to true + }, + }); + setSD(false); + console.log("Speed:", value); // Add this line + }, + [], + ); + + const handleBatteryChange = useCallback( + (e: React.ChangeEvent) => { + const value = e.target.value; + dispatch({ + type: "UPDATE_BATTERY_AND_CHANGED", + payload: { + battery: value === "" ? "" : Number(value), //empty input is allowed or the input is converted to a number + altered: true, //the input changed so we chnage it to true + }, + }); + setSD(false); + }, + [], + ); + + const handleWeatherChange = useCallback( + (e: React.ChangeEvent) => { + const value = e.target.value; + // Allow empty input, or convert to a number + dispatch({ + type: "UPDATE_WEATHER_AND_CHANGED", + payload: { + weather: value === "" ? "" : Number(value), //empty input is allowed or the input is converted to a number + altered: true, //the input changed so we chnage it to true + }, + }); + setSD(false); + }, + [], + ); + + const validInputs = useMemo(() => { + //reset battery and speed + let speedError = ""; + let batteryError = ""; + let isValid = true; + + //check the changed fields + if (state.changed.speed) { + if (state.speed === "") { + isValid = false; + speedError = "Speed is required"; + } else if (Number(state.speed) < 0 || Number(state.speed) > 90) { + isValid = false; + speedError = "The speed should be within the range of 0 to 90"; + } + } else if (state.speed === "") { + //when nothing has been entered yet + isValid = false; + } + + if (state.changed.battery) { + if (state.battery === "") { + isValid = false; + batteryError = "Battery is required"; + } else if ( + typeof state.battery === "number" && + (state.battery < 0 || state.battery > 100) + ) { + isValid = false; + batteryError = + "The battery percentage should be within the range of 0 to 100"; + } + } else if (state.battery === "") { + //when nothing has been entered yet + isValid = false; + } + + return { speedError, batteryError, isValid }; + }, [state.changed, state.speed, state.battery]); //recompute the answer if speed or battery change + + /*const calculateRange = useMemo(() => { + const { speedError, batteryError, isValid } = validInputs; + + if (!isValid) { + console.error(speedError, batteryError); + return null; + } + return ( + -( + (Number(state.speed) * Number(state.speed) * Number(state.battery)) / + 2500 + ) + + 4 * Number(state.battery) + + Number(state.weather) + ); + }, [validInputs.isValid, state.battery, state.speed, state.weather]); + + const handleClick = () => { + const range = calculateRange; // Get the calculated range + rangeRef.current = range; // Set the calculated range to the ref + console.log(rangeRef.current); + console.log(typeof rangeRef.current); + console.log(rangeRef.current !== null); + };*/ + + const calculatedRange = useMemo(() => { + const { isValid } = validInputs; + if (isValid) + return ( + -( + (Number(state.speed) * Number(state.speed) * Number(state.battery)) / + 2500 + ) + + 4 * Number(state.battery) + + Number(state.weather) + ); + return null; + }, [ + validInputs.isValid, + state.speed, + state.battery, + state.weather, + displayRange, + ]); + + const handleClick = () => { + if (validInputs.isValid) { + setSD(true); // Show the range if inputs are valid + } else { + setSD(false); // Hide or show an error message if inputs are invalid + } + }; return (
@@ -14,11 +242,42 @@ const App = () => {
- - + + {validInputs.speedError && ( +
{validInputs.speedError}
+ )} + {/* speed error message */} + + {validInputs.batteryError && ( +
{validInputs.batteryError}
+ )} + {/* Battery error message */}
- + +
+ + {/*make the button*/} +
+ {" "} + {/* Add text-center here */} + +
+ {displayRange && calculatedRange !== null ? ( +

+ The predicted range of the Eylsia is{" "} + {calculatedRange.toFixed(2)} km. +

+ ) : ( +

+ )} +
diff --git a/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/InputProps.ts b/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/InputProps.ts new file mode 100644 index 00000000..f96d23da --- /dev/null +++ b/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/InputProps.ts @@ -0,0 +1,5 @@ +// Define the props type +export interface InputProps { + value: number | string; // Expecting a string for the input value + onChange: (e: React.ChangeEvent) => void; // Function to handle changes +} diff --git a/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/components/batteryInput/index.tsx b/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/components/batteryInput/index.tsx index ca4586c0..d08ae47c 100644 --- a/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/components/batteryInput/index.tsx +++ b/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/components/batteryInput/index.tsx @@ -1,4 +1,6 @@ -const BatteryInput = () => { +import type { InputProps } from "~/InputProps"; + +const BatteryInput: React.FC = ({ value, onChange }) => { return (
@@ -8,6 +10,9 @@ const BatteryInput = () => { name="battery" type="number" placeholder="Battery" + value={value} + onChange={onChange} + required />
); diff --git a/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/components/speedInput/index.tsx b/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/components/speedInput/index.tsx index aa381c14..3e7f825c 100644 --- a/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/components/speedInput/index.tsx +++ b/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/components/speedInput/index.tsx @@ -1,4 +1,6 @@ -const SpeedInput = () => { +import type { InputProps } from "~/InputProps"; + +const SpeedInput: React.FC = ({ value, onChange }) => { return ( <>
@@ -9,6 +11,9 @@ const SpeedInput = () => { name="speed" type="number" placeholder="Speed" + value={value} + onChange={onChange} + required />
diff --git a/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/components/weatherInput/index.tsx b/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/components/weatherInput/index.tsx index fd5ad35b..e913006c 100644 --- a/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/components/weatherInput/index.tsx +++ b/Recruit-Training/Advanced-Recruit-Training/Telemetry-Teaser/src/components/weatherInput/index.tsx @@ -1,4 +1,8 @@ -const WeatherInput = () => { +import React from "react"; + +import type { InputProps } from "~/InputProps"; + +const WeatherInput: React.FC = ({ value, onChange }) => { return ( <> Cloud @@ -8,7 +12,8 @@ const WeatherInput = () => { type="range" min="0" max="100" - value="50" + value={value} + onChange={onChange} /> Sun