Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Constraints on Passwords in Sign Up page #280

Merged
merged 2 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions landing_page/src/app/lib/utils/RegEx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
enum PasswordRequirement {
UPPERCASE = "At least one uppercase letter",
LOWERCASE = "At least one lowercase letter",
DIGIT = "At least one digit",
SPECIAL_CHAR = "At least one special character (@#$%^&+=!)",
LENGTH = "Minimum length of 8 characters",
}

export type TPasswordValidationResult = {
isValid: boolean;
unmetRequirements: PasswordRequirement[];
};

/**
* Validates a password against a set of requirements.
*
* The password must meet the following criteria:
* - Contain at least one uppercase letter
* - Contain at least one lowercase letter
* - Contain at least one digit
* - Contain at least one special character from the set [@#$%^&+=!]
* - Minimum length of 8 characters
*/
export function validatePassword(password: string): TPasswordValidationResult {
const requirements: Record<PasswordRequirement, boolean> = {
[PasswordRequirement.UPPERCASE]: /[A-Z]/.test(password),
[PasswordRequirement.LOWERCASE]: /[a-z]/.test(password),
[PasswordRequirement.DIGIT]: /\d/.test(password),
[PasswordRequirement.SPECIAL_CHAR]: /[@#$%^&+=!]/.test(password),
[PasswordRequirement.LENGTH]: password.length >= 8,
};

const unmetRequirements = Object.keys(requirements)
.filter((requirement) => !requirements[requirement as PasswordRequirement])
.map((requirement) => requirement as PasswordRequirement);

return {
isValid: unmetRequirements.length === 0,
unmetRequirements,
};
}
31 changes: 26 additions & 5 deletions landing_page/src/app/signup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
SelectTrigger,
SelectValue,
} from "../components/Select/Select";
import { validatePassword, TPasswordValidationResult } from "../lib/utils/RegEx";
import { useRouter } from "next/navigation";
import { api } from "../lib/axios";
import { setCookie } from 'cookies-next/client';
Expand Down Expand Up @@ -38,11 +39,21 @@ export default function SignupPage() {
walletAddress: "",
role: "",
});
const [passwordValidation, setPasswordValidation] = useState<TPasswordValidationResult>({
isValid: false,
unmetRequirements: [],
});

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);

if (!passwordValidation.isValid) {
toast.error("Password does not meet requirements");
setLoading(false);
return;
}

if (formData.password !== formData.confirmPassword) {
toast.error("Passwords do not match");
setLoading(false);
Expand All @@ -57,15 +68,15 @@ export default function SignupPage() {
userType: formData.role,
});



// localStorage.setItem("token", response.data.token);

setCookie('landver_token', response.data.data.token);

toast.success("Registration successful!");
setTimeout(() => {
router.push("https://demo.landver.net")
router.push("https://demo.landver.net")
}, 1000);
} catch (err: unknown) {
const error = err as SignupError;
Expand All @@ -80,6 +91,10 @@ export default function SignupPage() {
...formData,
[e.target.name]: e.target.value,
});

if (e.target.name === "password") {
setPasswordValidation(validatePassword(e.target.value));
}
};

const handleRoleChange = (value: string) => {
Expand Down Expand Up @@ -130,6 +145,12 @@ export default function SignupPage() {
}
/>

<ul className="text-sm text-red-500">
{passwordValidation.unmetRequirements.map((requirement, index) => (
<li key={index}>{requirement}</li>
))}
</ul>

<Input
name="confirmPassword"
type={showConfirmPassword ? "text" : "password"}
Expand Down Expand Up @@ -177,8 +198,8 @@ export default function SignupPage() {
</a>
</div>

<Button
classname="w-full text-sm font-semibold"
<Button
classname="w-full text-sm font-semibold"
disabled={loading}
>
{loading ? "Signing up..." : "Sign up"}
Expand Down
Loading