diff --git a/Client/src/Components/Inputs/Field/index.jsx b/Client/src/Components/Inputs/Field/index.jsx
index 6fb8ea612..0b2fe56fe 100644
--- a/Client/src/Components/Inputs/Field/index.jsx
+++ b/Client/src/Components/Inputs/Field/index.jsx
@@ -12,7 +12,7 @@ import Visibility from "@mui/icons-material/Visibility";
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
import Button from "../../Button";
import "./index.css";
-import { useState } from "react";
+import { forwardRef, useState } from "react";
/**
* @param {Object} props
@@ -30,140 +30,151 @@ import { useState } from "react";
* @param {function} props.onChange - Function called on input change.
* @param {string} [props.error] - Error message to display for the input field.
* @param {boolean} [props.disabled] - Indicates if the input field is disabled.
+ * @param {React.Ref} [ref] - Ref forwarded to the underlying `TextField` component. Allows for direct interactions such as focusing.
*/
-const Field = ({
- type = "text",
- id,
- label,
- https,
- isRequired,
- isOptional,
- optionalLabel,
- hasCopy,
- autoComplete,
- placeholder,
- value,
- onChange,
- error,
- disabled,
-}) => {
- const theme = useTheme();
+const Field = forwardRef(
+ (
+ {
+ type = "text",
+ id,
+ label,
+ https,
+ isRequired,
+ isOptional,
+ optionalLabel,
+ hasCopy,
+ autoComplete,
+ placeholder,
+ value,
+ onChange,
+ error,
+ disabled,
+ },
+ ref
+ ) => {
+ const theme = useTheme();
- // TODO - are we using this feature anywhere ?
- const [copy, setCopy] = useState(false);
- const handleCopy = () => {
- setCopy(true);
- setTimeout(() => setCopy(false), 1000);
- };
+ // TODO - are we using this feature anywhere ?
+ const [copy, setCopy] = useState(false);
+ const handleCopy = () => {
+ setCopy(true);
+ setTimeout(() => setCopy(false), 1000);
+ };
- const [isVisible, setVisible] = useState(false);
+ const [isVisible, setVisible] = useState(false);
- return (
-
- {label && (
-
- {label}
- {isRequired ? * : ""}
- {isOptional ? (
-
- {optionalLabel || "(optional)"}
-
- ) : (
- ""
- )}
-
- )}
-
-
- {https ? "https" : "http"}://
-
-
- ),
- endAdornment:
- type === "password" ? (
-
- setVisible((show) => !show)}
- tabIndex={-1}
- sx={{
- color: theme.palette.section.borderColor,
- padding: `calc(${theme.gap.xs} / 2)`,
- "&:focus": {
- outline: "none",
- },
- "& .MuiTouchRipple-root": {
- pointerEvents: "none",
- display: "none",
- },
- }}
- >
- {!isVisible ? : }
-
-
+ return (
+
+ {label && (
+
+ {label}
+ {isRequired ? * : ""}
+ {isOptional ? (
+
+ {optionalLabel || "(optional)"}
+
) : (
- hasCopy && (
-
- }
- onClick={handleCopy}
+ ""
+ )}
+
+ )}
+
+
+ {https ? "https" : "http"}://
+
+
+ ),
+ endAdornment:
+ type === "password" ? (
+
+ setVisible((show) => !show)}
+ tabIndex={-1}
sx={{
- borderLeft: `solid 1px ${theme.palette.section.borderColor}`,
- lineHeight: 0,
+ color: theme.palette.section.borderColor,
+ padding: `calc(${theme.gap.xs} / 2)`,
+ "&:focus": {
+ outline: "none",
+ },
"& .MuiTouchRipple-root": {
pointerEvents: "none",
display: "none",
},
}}
- />
+ >
+ {!isVisible ? : }
+
- )
- ),
- }}
- />
- {error && (
-
- {error}
-
- )}
-
- );
-};
+ ) : (
+ hasCopy && (
+
+ }
+ onClick={handleCopy}
+ sx={{
+ borderLeft: `solid 1px ${theme.palette.section.borderColor}`,
+ lineHeight: 0,
+ "& .MuiTouchRipple-root": {
+ pointerEvents: "none",
+ display: "none",
+ },
+ }}
+ />
+
+ )
+ ),
+ }}
+ />
+ {error && (
+
+ {error}
+
+ )}
+
+ );
+ }
+);
Field.propTypes = {
type: PropTypes.oneOf(["text", "password", "url", "email", "description"]),
diff --git a/Client/src/Pages/Auth/Login.jsx b/Client/src/Pages/Auth/Login.jsx
index 1f61d8740..f69a95cfd 100644
--- a/Client/src/Pages/Auth/Login.jsx
+++ b/Client/src/Pages/Auth/Login.jsx
@@ -1,4 +1,4 @@
-import { useState, useEffect } from "react";
+import { useState, useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { Box, Stack, Typography } from "@mui/material";
import { useTheme } from "@emotion/react";
@@ -21,7 +21,7 @@ import "./index.css";
*
* @param {Object} props
* @param {Function} props.onContinue - Callback function to handle "Continue with Email" button click.
- * @returns {JSX.Element}
+ * @returns {JSX.Element}
*/
const LandingPage = ({ onContinue }) => {
const theme = useTheme();
@@ -72,6 +72,13 @@ const LandingPage = ({ onContinue }) => {
*/
const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => {
const theme = useTheme();
+ const inputRef = useRef(null);
+
+ useEffect(() => {
+ if (inputRef.current) {
+ inputRef.current.focus();
+ }
+ }, []);
return (
<>
@@ -92,6 +99,7 @@ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => {
value={form.email}
onChange={onChange}
error={errors.email}
+ ref={inputRef}
/>
@@ -136,6 +144,13 @@ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => {
*/
const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => {
const theme = useTheme();
+ const inputRef = useRef(null);
+
+ useEffect(() => {
+ if (inputRef.current) {
+ inputRef.current.focus();
+ }
+ }, []);
return (
<>
@@ -156,6 +171,7 @@ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => {
value={form.password}
onChange={onChange}
error={errors.password}
+ ref={inputRef}
/>