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

Feat hgn email template #679

Merged
merged 3 commits into from
Jul 25, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const UserNavbar = () => {
<Menu className="h-[18px] w-[18px] text-neutral-dark-2 transition-all duration-300 hover:text-neutral-dark-2/50" />
<Logo />
</div>
<div className="flex w-full max-w-[220px] items-center justify-between gap-1">
<div className="flex w-full max-w-[290px] items-center justify-between gap-1">
{navlinks.map((item, index) => (
<Link
key={index}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use client";

import { useState } from "react";

import CustomButton from "~/components/common/common-button/common-button";
import CustomInput from "~/components/common/input/input";
import PasswordSuccessfulModal from "~/components/common/modals/password-successful";

const Password = () => {
const [open, setOpen] = useState<boolean>(false);
return (
<div className="w-full max-w-[674px] px-8 pt-[50px]">
<div className="mb-8">
<h2 className="mb-2 text-2xl font-semibold text-neutral-dark-1">
Password Settings
</h2>
<p className="text-base text-neutral-dark-1">
Update password for enhanced account security
</p>
</div>
<div>
<div className="mb-6 grid gap-4">
<CustomInput
placeholder="Enter current password"
label="Current Password"
className="border-border"
type="password"
/>
<CustomInput
placeholder="Enter new password"
label="New Password"
className="border-border"
type="password"
/>
<CustomInput
placeholder="Confirm new password"
label="Confrim new Password"
className="border-border"
type="password"
/>
</div>
<div className="flex items-center justify-start gap-6">
<CustomButton variant="outline" onClick={() => setOpen(false)}>
Cancel
</CustomButton>
<CustomButton className="bg-primary" onClick={() => setOpen(true)}>
Update Password
</CustomButton>
</div>
</div>
<PasswordSuccessfulModal onClose={() => setOpen(!open)} show={open} />
</div>
);
};

export default Password;
8 changes: 7 additions & 1 deletion src/app/dashboard/(user-dashboard)/settings/account/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import PasswordSettings from "./_component.tsx/password";

const page = () => {
return <div>page</div>;
return (
<div>
<PasswordSettings />
</div>
);
};

export default page;
50 changes: 50 additions & 0 deletions src/components/common/modals/notification-settings-saved/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use client";

import React from "react";

import CustomButton from "~/components/common/common-button/common-button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogOverlay,
DialogTitle,
} from "~/components/ui/dialog";

interface ModalProperties {
show: boolean;
onClose: () => void;
}

const NotificationSettingSavedModal: React.FC<ModalProperties> = ({
show,
onClose,
}) => {
return (
<Dialog open={show} onOpenChange={onClose}>
<DialogOverlay
data-testid="overlay"
className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50"
/>
<DialogContent
className="mx-10 rounded-md bg-white p-6 shadow-md"
onClick={(event) => event.stopPropagation()}
>
<DialogTitle className="text-lg font-semibold">
Notification Updated!
</DialogTitle>
<DialogDescription className="text-sm font-normal text-muted-foreground">
Notification preferences updated successfully. Remember, you can
always adjust these settings later.
</DialogDescription>
<div className="flex justify-end">
<div onClick={onClose}>
<CustomButton variant="primary">Done</CustomButton>
</div>
</div>
</DialogContent>
</Dialog>
);
};

export default NotificationSettingSavedModal;
50 changes: 50 additions & 0 deletions src/components/common/modals/password-successful/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use client";

import React from "react";

import CustomButton from "~/components/common/common-button/common-button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogOverlay,
DialogTitle,
} from "~/components/ui/dialog";

interface ModalProperties {
show: boolean;
onClose: () => void;
}

const PasswordSuccessfulModal: React.FC<ModalProperties> = ({
show,
onClose,
}) => {
return (
<Dialog open={show} onOpenChange={onClose}>
<DialogOverlay
data-testid="overlay"
className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50"
/>
<DialogContent
className="mx-10 rounded-md bg-white p-6 shadow-md"
onClick={(event) => event.stopPropagation()}
>
<DialogTitle className="text-lg font-semibold">
Password Successfully Updated!
</DialogTitle>
<DialogDescription className="text-sm font-normal text-muted-foreground">
Your password has been successfully updated! You can now log in with
your new password.
</DialogDescription>
<div className="flex justify-end">
<div onClick={onClose}>
<CustomButton variant="primary">Continue</CustomButton>
</div>
</div>
</DialogContent>
</Dialog>
);
};

export default PasswordSuccessfulModal;
104 changes: 104 additions & 0 deletions src/components/common/password-check/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"use client";

import { CircleCheck } from "lucide-react";
import React, { useEffect, useState } from "react";

interface PasswordCheckProperties {
minLength: number;
password: string;
onStrengthChange: (strength: PasswordCheck) => void;
}

interface PasswordCheck {
minLengthCheck: boolean;
containsUppercase: boolean;
containNumber: boolean;
}

const handlerContainsNumber = (value: string) => {
const regex = /\d/;
return regex.test(value);
};

const handleContainsUpperCase = (value: string) => {
const regex = /[A-Z]/;
return regex.test(value);
};

const PasswordCheck: React.FC<PasswordCheckProperties> = ({
password,
minLength,
onStrengthChange,
}) => {
const [checkPassword, setCheckPassword] = useState<PasswordCheck>({
minLengthCheck: false,
containsUppercase: false,
containNumber: false,
});

useEffect(() => {
const handleCheckPassword = (password: string) => {
const containsUpperCase = handleContainsUpperCase(password);
const containsNumber = handlerContainsNumber(password);
const minLengthCheck = password.length >= minLength;

const newStrength: PasswordCheck = {
minLengthCheck,
containsUppercase: containsUpperCase,
containNumber: containsNumber,
};

setCheckPassword(newStrength);
onStrengthChange(newStrength);
};
handleCheckPassword(password);
}, [password, minLength, onStrengthChange]);

return (
<div className="l-4 mt-[8px]">
<div className="flex items-center gap-[16px]">
<div
className={`w-[100px] border-[3px] border-solid ${checkPassword.minLengthCheck || checkPassword.containNumber || checkPassword.containsUppercase ? "border-[#6DC347]" : "border-[#B6B6B6]"}`}
></div>
<div
className={`w-[100px] border-[3px] border-solid ${(checkPassword.minLengthCheck && checkPassword.containNumber) || (checkPassword.containNumber && checkPassword.containsUppercase) || (checkPassword.minLengthCheck && checkPassword.containsUppercase) ? "border-[#6DC347]" : "border-[#B6B6B6]"}`}
></div>
<div
className={`w-[100px] border-[3px] border-solid ${checkPassword.minLengthCheck && checkPassword.containNumber && checkPassword.containsUppercase ? "border-[#6DC347]" : "border-[#B6B6B6]"}`}
></div>
</div>
<div>
<p className="mt-[24px]">Weak password. Must contain:</p>
<div className="mt-[12px] flex items-center gap-[8px]">
<CircleCheck
data-testid="circle-check-icon"
style={{
color: checkPassword.containsUppercase ? "#6DC347" : "#B6B6B6",
}}
/>
<p className="">At least 1 uppercase</p>
</div>
<div className="mt-[8px] flex items-center gap-[8px]">
<CircleCheck
data-testid="circle-check-icon"
style={{
color: checkPassword.containNumber ? "#6DC347" : "#B6B6B6",
}}
/>
<p>At least 1 number</p>
</div>
<div className="mt-[8px] flex items-center gap-[8px]">
<CircleCheck
data-testid="circle-check-icon"
style={{
color: checkPassword.minLengthCheck ? "#6DC347" : "#B6B6B6",
}}
/>
<p>At least {minLength} characters</p>
</div>
</div>
</div>
);
};

export default PasswordCheck;
57 changes: 57 additions & 0 deletions src/components/common/password-input/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"use client";

import { EyeIcon, EyeOff } from "lucide-react";
import { useState } from "react";

import { Input } from "~/components/ui/input";

interface PasswordToggleProperties {
password: string;
onPasswordChange: (password: string) => void;
name: string;
placeholder: string;
id: string;
}

const PasswordInput: React.FC<PasswordToggleProperties> = ({
password,
onPasswordChange,
name,
id,
placeholder,
}) => {
const [isPasswordHidden, setPasswordHidden] = useState(true);

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
onPasswordChange(event.target.value);
};

return (
<div className="flex flex-col gap-10">
<div className="relative w-full">
<button
type="button"
className="absolute inset-y-0 right-3 my-auto text-[#939393] active:text-[#434343]"
onClick={() => setPasswordHidden(!isPasswordHidden)}
aria-label={isPasswordHidden ? "Show password" : "Hide password"}
title={isPasswordHidden ? "Show password" : "Hide password"}
>
{isPasswordHidden ? <EyeIcon /> : <EyeOff />}
</button>

<Input
aria-label="Password"
type={isPasswordHidden ? "password" : "text"}
id={id}
name={name}
placeholder={placeholder}
value={password}
onChange={handleChange}
className="h-[48px] w-full rounded-[8px] border border-[#B2B0B0] bg-transparent px-[16px] py-[12px] pr-[48px] text-[#939393] shadow-none outline-none placeholder:text-[14px] placeholder:text-[#939393] focus:border-primary focus-visible:ring-0 focus-visible:ring-transparent"
/>
</div>
</div>
);
};

export { PasswordInput };