Skip to content

Commit

Permalink
Merge pull request #19 from UofA-Blueprint/ASC-81
Browse files Browse the repository at this point in the history
Login Modal Component
  • Loading branch information
PaulHo0501 authored May 7, 2024
2 parents f29fbf5 + 8dc7e50 commit e7d4641
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 62 deletions.
31 changes: 22 additions & 9 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ import InputFieldTestRoute from "./routes/InputFieldTestRoute";
import MemberHeaderTestRoute from "@/routes/MemberHeaderTestRoute";
import { MemberInformation } from "./components/MemberInformation";
import ModalTestRoute from "@/routes/ModalTestRoute";

import { LoginModal } from "./components/LoginModal";

const router = createBrowserRouter([

{
path: "/test",
element: <Test />,
Expand Down Expand Up @@ -62,15 +61,14 @@ const router = createBrowserRouter([
element: <ToastTestRoute />,
},
{

path:"/icon-select",
element:<IconOptionTest/>
path: "/icon-select",
element: <IconOptionTest />,
},
{
path: "/ProfilePic",
element: <ProfilePictureTest />,
},
{
path:"/ProfilePic",
element:<ProfilePictureTest/>
},{

path: "/color-picker-test",
element: <ColorPickerTestRoute />,
},
Expand Down Expand Up @@ -98,6 +96,21 @@ const router = createBrowserRouter([
path: "/modal-test",
element: <ModalTestRoute />,
},
{
path: "/login-modal-test",
element: (
<div className="flex flex-col gap-y-8 justify-center items-center py-20 bg-slate-100">
<LoginModal
title="Admin Panel"
type="admin"
/>
<LoginModal
title="Caregiver Panel"
type="member"
/>
</div>
),
},
]);

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 @@ -27,7 +27,7 @@ const Button: React.FC<ButtonProps> = ({
status === "disabled"
? "bg-neutrals-light-200 text-gray-400 cursor-default"
: fill
? "relative overflow-hidden bg-primary-main text-white z-10 active:bg-primary-main active:text-white transition-colors hover:bg-neutrals-light-100 hover:text-primary-main hover:border-2 hover:border-primary-main rounded-full w-full h-full before:absolute before:right-0 before:top-0 before:w-full before:h-full before:z-0 before:bg-primary-main before:content[''] before:transition-transform before:duration-300 hover:before:transform hover:before:translate-x-full"
? "relative overflow-hidden bg-primary-main text-white z-10 active:bg-primary-main active:text-white transition-colors hover:bg-neutrals-light-100 hover:text-primary-main border-2 border-primary-main hover:border-primary-main rounded-full w-full h-full before:absolute before:right-0 before:top-0 before:w-full before:h-full before:z-0 before:bg-primary-main before:content[''] before:transition-transform before:duration-300 hover:before:transform hover:before:translate-x-full"
: "relative overflow-hidden text-primary-dark border-primary-dark border-2 active:bg-white active:text-primary-dark transition-colors before:absolute before:left-0 before:top-0 before:-z-10 before:h-full before:w-full before:origin-top-left before:scale-x-0 before:bg-primary-dark before:transition-transform before:duration-300 before:content-[''] hover:text-white before:hover:scale-x-100"
} ${rounded ? "rounded-full" : "rounded-lg"} w-full h-full`,
className
Expand Down
56 changes: 34 additions & 22 deletions src/components/InputCode.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
//#region Imports
import { twMerge } from "tailwind-merge";
import { useRef, useState } from "react";
import { useRef, useState, useEffect } from "react";
import { WarningCircle } from "@phosphor-icons/react";
//#endregion

//#region Interfaces
interface InputCodeProps {
/**
* Indicates whether the input field has an error.
* If true, a red border and small information will be displayed.
*/
error: boolean;

/**
* The label of the input field.
* Defaults to an empty string if not provided.
Expand All @@ -25,6 +19,10 @@ interface InputCodeProps {
*/
required: boolean;
className?: string;

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

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

const [input, setInput] = useState<Record<number, string>>({
0: "",
1: "",
2: "",
3: "",
4: "",
5: "",
});
const [value, setValue] = useState<string>("");
const [error, setError] = useState<boolean>(false);

//#region useEffect
useEffect(() => {
checkError();
}, [inputRefs]);
//#endregion

//#region Functions
/**
Expand All @@ -73,7 +71,7 @@ export function InputCode({
inputRefs[index]?.current?.value === ""
? inputRefs[index - 1]?.current?.focus()
: setInput((prev) => ({ ...prev, [index]: "" }));
} else if (e.key === "Tab" && index > 0) {
} else if (e.key === "Tab" && index > 0 && index < 5 && value !== "") {
setInput((prev) => ({ ...prev, [index]: value }));
setValue("");
} else if (index < 5 && value !== "") {
Expand All @@ -82,30 +80,44 @@ export function InputCode({
setValue("");
}
};

/**
* 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

return (
<div
className={twMerge(
"flex flex-col gap-y-2 md:gap-y-3 text-neutrals-dark-500 text-body-reg",
"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">
<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">
<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={`w-10 h-14 md:w-12 md:h-16 text-center text-2xl md:text-3xl rounded-md bg-neutrals-light-300 border-2 border-status-red-main ${
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)}
Expand All @@ -116,7 +128,7 @@ export function InputCode({

{/* Error Message */}
{error && (
<div className="flex text-body-sm items-center gap-x-1 text-status-red-main">
<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>
Expand Down
84 changes: 55 additions & 29 deletions src/components/InputField.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import { WarningCircle } from "@phosphor-icons/react";
import { useRef, useState } from "react";
import { Eye, EyeSlash } from "@phosphor-icons/react";

export interface InputFieldProps {
type: string;
error: boolean;
label?: string; // Optional; Default to none.
required: boolean;
placeholder?: string;

// set Field value
setInput: (input: string) => void;
}

function InputField({ type, error, label, required }: InputFieldProps) {
// todo: functionality
function InputField({
type,
error,
label,
required,
placeholder,
setInput,
}: InputFieldProps) {
const inputRef = useRef<HTMLInputElement>(null);
const [showPassword, setShowPassword] = useState<boolean>(false);

function checkLen(event: { preventDefault: () => void }) {
event.preventDefault();
Expand All @@ -22,39 +34,53 @@ function InputField({ type, error, label, required }: InputFieldProps) {
}

return (
<div className="mt-5 w-[30em]">
{required === false ? (
<label className="font-extrabold block mb-1 ml-1 text-sm text-light-500 dark:text-white">
{label}
</label>
) : (
<label className="font-extrabold block mb-1 ml-1 text-sm text-light-500 dark:text-white">
{label} <span className="text-red-500"> * </span>
</label>
)}
<div className="w-full flex flex-col gap-y-2">
{/* Label */}
<div className="flex gap-x-2 font-display font-semibold">
<label className="block text-light-500">{label}</label>
{required && <span className="text-status-red-main">*</span>}
</div>

{error !== true ? (
{/* Input */}
<div className="relative">
<input
placeholder={"Enter " + type + "..."}
type={type}
placeholder={placeholder}
type={type === "password" && showPassword ? "text" : type}
ref={inputRef}
onSelect={checkLen}
className="bg-gray-50 border border-gray-300 text-sm rounded-lg focus:outline-none focus:drop-shadow-[0_0px_5px_rgba(0,0,0,0.25)] hover:outline-none hover:drop-shadow-[0_0px_5px_rgba(0,0,0,0.25)] block w-full p-2.5 dark:text-black"
className={`bg-gray-100 rounded-lg w-full p-4`}
onChange={(e) => setInput(e.target.value)}
></input>
) : (
<div>
<input
placeholder={"Enter " + type + "..."}
type={type}
ref={inputRef}
onSelect={checkLen}
className="bg-gray-50 border border-red-500 text-light-500 text-sm rounded-lg focus:outline-none focus:drop-shadow-[0_0px_5px_rgba(0,0,0,0.25)] hover:outline-none hover:drop-shadow-[0_0px_5px_rgba(0,0,0,0.25)] block w-full p-2.5 dark:text-black"
></input>

<div className="flex mt-1 align-middle">
<WarningCircle color="red" className="mr-1" size={"1.1em"} />
<span className="text-red-500 text-xs "> Error {type} </span>
</div>
{type.toLowerCase() === "password" && !showPassword && (
<Eye
size={20}
className="absolute top-1/2 right-4 transform -translate-y-1/2 cursor-pointer hover:text-primary-main transition duration-300 ease-in-out"
onClick={() => setShowPassword(!showPassword)}
/>
)}

{/* Show password button */}
{type.toLowerCase() === "password" && showPassword && (
<EyeSlash
size={20}
className="absolute top-1/2 right-4 transform -translate-y-1/2 cursor-pointer hover:text-primary-main transition duration-300 ease-in-out"
onClick={() => setShowPassword(!showPassword)}
/>
)}
</div>

{/* Error */}
{error && (
<div className="flex items-center">
<WarningCircle
color="red"
className="mr-1"
size={"1.1em"}
/>
<span className="text-status-red-main text-body-smfont-display">
Error {type}{" "}
</span>
</div>
)}
</div>
Expand Down
Loading

0 comments on commit e7d4641

Please sign in to comment.