Skip to content

Commit

Permalink
Merge pull request #29 from UofA-Blueprint/ASC-91
Browse files Browse the repository at this point in the history
ASC-91: Member Login Page
  • Loading branch information
PaulHo0501 authored Jul 4, 2024
2 parents ec45044 + 6bc686f commit 0a61fdc
Show file tree
Hide file tree
Showing 7 changed files with 289 additions and 160 deletions.
20 changes: 17 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import MediaUploadStatusTestRoute from "./routes/test/MediaUploadStatusTestRoute
import ModalTestRoute from "@/routes/test/ModalTestRoute";
import GalleryTestRoute from "./routes/test/GalleryTestRoute";
import { MemberInformation } from "./components/MemberInformation";
import { LoginModal } from "./components/LoginModal";
import MemberProfilePictureTest from "@/routes/test/MemberProfilePictureTest";

// routes
import AdminLogin from "./routes/admin/AdminLogin";
import CaregiverLogin from "./routes/caregiver/CaregiverLogin";

const router = createBrowserRouter([
{
Expand Down Expand Up @@ -122,19 +122,33 @@ const router = createBrowserRouter([
element: <MemberProfilePictureTest />,
},

// Admin routes
// Admin Routes
{
path: "/admin",
element: (
<div>
<p>Home Page</p>
<p>Admin Home Page</p>
</div>
),
},
{
path: "/admin/login",
element: <AdminLogin />,
},

// Caregiver Routes
{
path: "/",
element: (
<div>
<p>Caregiver Home Page</p>
</div>
),
},
{
path: "/login",
element: <CaregiverLogin />,
},
]);

function App() {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const Button: React.FC<ButtonProps> = ({
onClick,
rounded = false,
fill = true,
disabled = false,
disabled,
fontSize = "1em",
className = null,
}) => {
Expand Down
232 changes: 128 additions & 104 deletions src/components/InputCode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,25 @@ import { WarningCircle } from "@phosphor-icons/react";

//#region Interfaces
interface InputCodeProps {
/**
* The label of the input field.
* Defaults to an empty string if not provided.
*/
label?: string;
/**
* The label of the input field.
* Defaults to an empty string if not provided.
*/
label?: string;

/**
* Indicates whether the input field is required.
* If true, an asterisk will be displayed after the label,
* and the text length will be checked to ensure it is not 0.
*/
required: boolean;
className?: string;
/**
* Indicates whether the input field is required.
* If true, an asterisk will be displayed after the label,
* and the text length will be checked to ensure it is not 0.
*/
required: boolean;

// set Field value
input: Record<number, string>;
setInput: React.Dispatch<React.SetStateAction<Record<number, string>>>;
error: boolean;
className?: string;

// set Field value
setError: React.Dispatch<React.SetStateAction<boolean>>;
setInput: React.Dispatch<React.SetStateAction<Record<number, string>>>;
}
//#endregion

Expand All @@ -38,101 +40,123 @@ interface InputCodeProps {
* @returns {JSX.Element} The rendered input code component.
*/
export function InputCode({
label = "",
required,
className,
input,
setInput,
label = "",
required,
error,
className,
setError,
setInput,
}: InputCodeProps): JSX.Element {
const inputRefs = Array.from({ length: 6 }, () =>
useRef<HTMLInputElement | null>(null)
);
const inputRefs = Array.from({ length: 6 }, () =>
useRef<HTMLInputElement>(null),
);

const [value, setValue] = useState<string>("");
const [showError, setShowError] = useState<boolean>(false);

//#region Functions
/**
* This function handles inputs based on the selected key.
* @param {React.KeyboardEvent<HTMLInputElement>} e - The keyboard event.
* @param {number} index - The index of the input field.
*/
const handleKeyDown = (
e: React.KeyboardEvent<HTMLInputElement>,
index: number,
) => {
setValue(e.key.toString());

// Erase the value of the current input field when the backspace key is pressed
if (e.key === "Backspace") {
if (inputRefs[index]?.current?.value === "" && index > 0)
inputRefs[index - 1]?.current?.focus();
setValue("");
}

const [value, setValue] = useState<string>("");
const [error, setError] = useState<boolean>(false);
// Enter the value of the current input
else {
if (index <= 5 && value != "") {
// inputRefs[index + 1]?.current?.focus();
inputRefs[index + 1]?.current?.select();
}
}
};

//#region useEffect
useEffect(() => {
checkError();
}, [inputRefs]);
//#endregion
/**
* This function checks if there is an error in the input field.
* @returns None
*/
const checkError = (index: number) => {
setShowError(true);
inputRefs[index]?.current?.focus();

//#region Functions
/**
* This function handles inputs based on the selected key.
* @param {React.KeyboardEvent<HTMLInputElement>} e - The keyboard event.
* @param {number} index - The index of the input field.
*/
const handleKeyDown = (
e: React.KeyboardEvent<HTMLInputElement>,
index: number
) => {
if (e.key === "Backspace" && index > 0) {
inputRefs[index]?.current?.value === ""
? inputRefs[index - 1]?.current?.focus()
: setInput((prev) => ({ ...prev, [index]: "" }));
} else if (e.key === "Tab" && index > 0 && index < 5 && value !== "") {
setInput((prev) => ({ ...prev, [index]: value }));
setValue("");
} else if (index < 5 && value !== "") {
setInput((prev) => ({ ...prev, [index]: value }));
inputRefs[index + 1]?.current?.focus();
setValue("");
}
};
inputRefs.forEach((input) => {
if (input.current?.value == "") {
setError(true);
} else {
setError(false);
}
});
};

/**
* This function checks if there is an error in the input field.
* @returns None
*/
const checkError = () => {
inputRefs.forEach((input) => {
if (input.current?.value == "") {
setError(true);
} else {
setError(false);
}
});
};
//#endregion
/**
* This function handles the change event of the input field.
* @param {React.ChangeEvent<HTMLInputElement>} e - The change event.
* @returns None
*/
const handleInputChange = (index: number) => {
checkError(index);
setInput({
0: inputRefs[0]?.current?.value || "",
1: inputRefs[1]?.current?.value || "",
2: inputRefs[2]?.current?.value || "",
3: inputRefs[3]?.current?.value || "",
4: inputRefs[4]?.current?.value || "",
5: inputRefs[5]?.current?.value || "",
});
};
//#endregion

return (
<div
className={twMerge(
"flex flex-col gap-y-2 md:gap-y-3 text-neutrals-dark-500 text-body-reg w-full",
className
)}
>
{/* Title section */}
<div className="flex gap-x-1 items-center font-display font-semibold">
<p className="text-base capitalize">{label}</p>
{required && <p className="text-status-red-main">*</p>}
</div>
return (
<div
className={twMerge(
"flex flex-col gap-y-2 md:gap-y-3 text-neutrals-dark-500 text-body-reg w-full",
className,
)}
>
{/* Title section */}
<div className="flex gap-x-1 items-center font-display font-semibold">
<p className="text-base capitalize">{label}</p>
{required && <p className="text-status-red-main">*</p>}
</div>

{/* Input Code Section */}
<div className="flex gap-x-2 w-full">
{[0, 1, 2, 3, 4, 5].map((_, index) => (
<input
key={index}
ref={inputRefs[index]}
type="text"
maxLength={1}
className={`h-14 w-1/6 md:h-16 text-center text-2xl md:text-3xl rounded-md bg-neutrals-light-300 border-2 border-status-red-main font-display ${
error ? "border-status-red-main" : "border-none"
}`}
onChange={(e) => setValue(e.target.value)}
onKeyDown={(e) => handleKeyDown(e, index)}
/>
))}
</div>
{/* Input Code Section */}
<div className="flex gap-x-2 w-full">
{[0, 1, 2, 3, 4, 5].map((_, index) => (
<input
key={index}
ref={inputRefs[index]}
type="text"
maxLength={1}
className={`h-14 w-1/6 md:h-16 text-center text-2xl md:text-3xl rounded-md bg-neutrals-light-300 border-2 border-status-red-main font-display ${
error && showError
? "border-status-red-main"
: "border-none"
}`}
onClick={() => checkError(index)}
onChange={(e) => handleInputChange(index)}
onKeyDown={(e) => handleKeyDown(e, index)}
/>
))}
</div>

{/* Error Message */}
{error && (
<div className="flex text-body-sm items-center gap-x-1 text-status-red-main font-display">
<WarningCircle size={20} />
<p>Error passcode</p>
</div>
)}
</div>
);
{/* Error Message */}
{showError && error && (
<div className="flex text-body-sm items-center gap-x-1 text-status-red-main font-display">
<WarningCircle size={20} />
<p>Error passcode</p>
</div>
)}
</div>
);
}
12 changes: 9 additions & 3 deletions src/components/InputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ export interface InputFieldProps {
placeholder?: string;

// set Field value
setError: React.Dispatch<React.SetStateAction<boolean>>;
setInput: React.Dispatch<React.SetStateAction<string>>;
}

function InputField({
type,
error,
setError,
label,
required,
placeholder,
Expand All @@ -32,16 +34,20 @@ function InputField({
setInput(inputRef.current.value);
if (inputRef.current.value.trim() === "" && required === true) {
setShowError(true);
setError(true);
}
if (type === "email") {
const email = inputRef.current.value.trim();
const emailRegex =
/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
const valid = emailRegex.test(email);
setShowError(!valid);
setError(!valid);
} else if (type === "password") {
const password = inputRef.current.value.trim();
password.length < 6 ? setShowError(true) : setShowError(false);
password.length < 6 ? setError(true) : setError(false);
} else if (type === "text") {
const text = inputRef.current.value.trim();
text.length === 0 ? setError(true) : setError(false);
}
}
}
Expand Down Expand Up @@ -84,7 +90,7 @@ function InputField({
</div>

{/* Error */}
{showError && (
{showError && error && (
<div className="flex items-center">
<WarningCircle
color="red"
Expand Down
Loading

0 comments on commit 0a61fdc

Please sign in to comment.