password complexity indicator
This commit is contained in:
@@ -67,6 +67,7 @@ export default function LoginView({ onSignup }: { onSignup: () => void }) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex py-2 px-1 justify-between">
|
<div className="flex py-2 px-1 justify-between">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
isDisabled
|
||||||
classNames={{
|
classNames={{
|
||||||
label: "text-small",
|
label: "text-small",
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { Card } from "@heroui/react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface PasswordComplexityIndicatorProps {
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkCriteria = (password: string) => ({
|
||||||
|
isLongEnough: password.length >= 12,
|
||||||
|
hasLowercase: /[a-z]/.test(password),
|
||||||
|
hasUppercase: /[A-Z]/.test(password),
|
||||||
|
hasNumber: /\d/.test(password),
|
||||||
|
hasSpecialChar: /[^A-Za-z\d]/.test(password),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dot indicator component
|
||||||
|
const DotIndicator = ({ met }: { met: boolean }) => (
|
||||||
|
<span
|
||||||
|
className={`w-3 h-3 rounded-full ${met ? "bg-green-500" : "bg-red-500"}`}
|
||||||
|
></span>
|
||||||
|
);
|
||||||
|
|
||||||
|
// List item component
|
||||||
|
const CriteriaItem = ({ label, met }: { label: string; met: boolean }) => (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<DotIndicator met={met} />
|
||||||
|
<span className="text-sm opacity-50">{label}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Main password complexity component
|
||||||
|
const PasswordComplexityIndicator: React.FC<
|
||||||
|
PasswordComplexityIndicatorProps
|
||||||
|
> = ({ password }) => {
|
||||||
|
const criteria = checkCriteria(password);
|
||||||
|
|
||||||
|
const criteriaList = [
|
||||||
|
{ label: "12 characters", met: criteria.isLongEnough },
|
||||||
|
{ label: "Lowercase", met: criteria.hasLowercase },
|
||||||
|
{ label: "Uppercase", met: criteria.hasUppercase },
|
||||||
|
{ label: "Number", met: criteria.hasNumber },
|
||||||
|
{ label: "Special character", met: criteria.hasSpecialChar },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card radius="sm" className="p-4 bg-neutral-500/10">
|
||||||
|
<div className="grid grid-cols-2">
|
||||||
|
{criteriaList.map((criterion, index) => (
|
||||||
|
<CriteriaItem
|
||||||
|
key={index}
|
||||||
|
label={criterion.label}
|
||||||
|
met={criterion.met}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PasswordComplexityIndicator;
|
||||||
@@ -3,6 +3,7 @@ import { IconMail, IconLock } from "@tabler/icons-react";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import http, { login } from "../http";
|
import http, { login } from "../http";
|
||||||
|
import PasswordComplexityIndicator from "./PasswordComplexityIndicator";
|
||||||
|
|
||||||
export const validatePassword = (password: string): boolean => {
|
export const validatePassword = (password: string): boolean => {
|
||||||
const passwordComplexityRegex =
|
const passwordComplexityRegex =
|
||||||
@@ -108,26 +109,30 @@ export default function SignupView({
|
|||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
<Input
|
<Input
|
||||||
|
size="sm"
|
||||||
label="First name"
|
label="First name"
|
||||||
value={firstName}
|
value={firstName}
|
||||||
onValueChange={setFirstName}
|
onValueChange={setFirstName}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
|
size="sm"
|
||||||
label="Last name"
|
label="Last name"
|
||||||
value={lastName}
|
value={lastName}
|
||||||
onValueChange={setLastName}
|
onValueChange={setLastName}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
|
size="sm"
|
||||||
endContent={<IconMail />}
|
endContent={<IconMail />}
|
||||||
label="Email"
|
label="Email"
|
||||||
type="email"
|
type="email"
|
||||||
value={emailValue}
|
value={emailValue}
|
||||||
onValueChange={setEmailValue}
|
onValueChange={setEmailValue}
|
||||||
/>
|
/>
|
||||||
<Input label="NRIC" value={nric} onValueChange={setNric} />
|
<Input size="sm" label="NRIC" value={nric} onValueChange={setNric} />
|
||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
<Select
|
<Select
|
||||||
|
size="sm"
|
||||||
label="Gender"
|
label="Gender"
|
||||||
selectedKeys={[gender]}
|
selectedKeys={[gender]}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -139,12 +144,13 @@ export default function SignupView({
|
|||||||
</Select>
|
</Select>
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
className="rounded-xl px-4 transition-colors dark:bg-neutral-800 dark:hover:bg-neutral-700"
|
className="rounded-lg px-4 transition-colors dark:bg-neutral-800 dark:hover:bg-neutral-700"
|
||||||
value={dateOfBirth}
|
value={dateOfBirth}
|
||||||
onChange={(e) => setDateOfBirth(e.target.value)}
|
onChange={(e) => setDateOfBirth(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<Input
|
||||||
|
size="sm"
|
||||||
endContent={<IconLock />}
|
endContent={<IconLock />}
|
||||||
label="Password"
|
label="Password"
|
||||||
type="password"
|
type="password"
|
||||||
@@ -152,12 +158,14 @@ export default function SignupView({
|
|||||||
onValueChange={setPassword}
|
onValueChange={setPassword}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
|
size="sm"
|
||||||
endContent={<IconLock />}
|
endContent={<IconLock />}
|
||||||
label="Confirm password"
|
label="Confirm password"
|
||||||
type="password"
|
type="password"
|
||||||
value={confirmPassword}
|
value={confirmPassword}
|
||||||
onValueChange={setConfirmPassword}
|
onValueChange={setConfirmPassword}
|
||||||
/>
|
/>
|
||||||
|
<PasswordComplexityIndicator password={password} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4 w-full">
|
<div className="flex flex-col gap-4 w-full">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
Reference in New Issue
Block a user