Change password

This commit is contained in:
2025-02-10 14:27:38 +08:00
parent e7d92377ce
commit 65f3d8af1e
3 changed files with 127 additions and 16 deletions

View File

@@ -0,0 +1,77 @@
import { useState } from "react";
import { Button, Divider, Input } from "@heroui/react";
import { toast } from "react-toastify";
import http from "../http";
import { validatePassword } from "./SignupView";
export default function ChangePasswordView({
onClose,
}: {
onClose: () => void;
}) {
const [oldPassword, setOldPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [confirmNewPassword, setConfirmNewPassword] = useState("");
const handleChangePassword = async () => {
if (newPassword !== confirmNewPassword) {
toast.error("New password and confirm new password do not match.");
return;
}
if (!validatePassword(newPassword)) {
toast.error(
"Password must be at least 12 characters long and include uppercase, lowercase, number, and special character."
);
return;
}
const changePasswordRequest = {
currentPassword: oldPassword,
newPassword: newPassword,
};
try {
const response = await http.put(
"/User/change-password",
changePasswordRequest
);
toast.success(response.data);
onClose();
} catch (error) {
toast.error(
(error as any).response?.data ||
"Something went wrong! Please try again."
);
}
};
return (
<div className="flex flex-col gap-8">
<p className="text-xl">Change password</p>
<Divider />
<div className="flex flex-col gap-2">
<Input
label="Old password"
type="password"
value={oldPassword}
onChange={(e) => setOldPassword(e.target.value)}
/>
<div className="w-full h-4"></div>
<Input
label="New password"
type="password"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
<Input
label="Confirm new password"
type="password"
value={confirmNewPassword}
onChange={(e) => setConfirmNewPassword(e.target.value)}
/>
</div>
<Button onClick={handleChangePassword}>Save</Button>
</div>
);
}

View File

@@ -4,6 +4,12 @@ import { useState } from "react";
import { toast } from "react-toastify";
import http, { login } from "../http";
export const validatePassword = (password: string): boolean => {
const passwordComplexityRegex =
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z\d])[A-Za-z\d@$!%*?&\\]{12,}$/;
return passwordComplexityRegex.test(password);
};
export default function SignupView({
onLogin,
email = "",
@@ -21,12 +27,6 @@ export default function SignupView({
const [confirmPassword, setConfirmPassword] = useState("");
const [signupEnabled, setSignupEnabled] = useState(true);
const validatePassword = (password: string): boolean => {
const passwordComplexityRegex =
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z\d])[A-Za-z\d@$!%*?&\\]{12,}$/;
return passwordComplexityRegex.test(password);
};
const validateFields = () => {
if (
!firstName ||

View File

@@ -2,15 +2,28 @@ import { useEffect, useState } from "react";
import http, { getAccessToken, logout } from "../http";
import { useNavigate } from "react-router-dom";
import { UserProfile } from "../models/user-profile";
import { Button, Card, Divider, Input } from "@heroui/react";
import {
Button,
Card,
Divider,
Input,
Modal,
ModalBody,
ModalContent,
ModalFooter,
ModalHeader,
useDisclosure,
} from "@heroui/react";
import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { IconDownload, IconUpload } from "@tabler/icons-react";
import { IconDownload, IconEdit, IconUpload } from "@tabler/icons-react";
import { toast } from "react-toastify";
import ChangePasswordView from "../components/ChangePasswordView";
export default function MemberPage() {
const accessToken = getAccessToken();
const navigate = useNavigate();
const { isOpen, onOpen, onOpenChange } = useDisclosure();
const [userProfile, setUserProfile] = useState<UserProfile | null>(null);
@@ -137,7 +150,20 @@ export default function MemberPage() {
</div>
</div>
<div className="flex flex-col gap-2">
<p>Who am I</p>
<div className="flex flex-row justify-between *:my-auto">
<p>Who am I</p>
<Button
variant="flat"
size="sm"
className="text-md"
startContent={<IconEdit size={18} />}
onPress={() => {
navigate("edit");
}}
>
Edit bio
</Button>
</div>
<Card className="bg-white dark:bg-black h-full max-h-[352px] min-w-96">
{userProfile.whoAmI.length > 0 ? (
<Markdown
@@ -159,17 +185,25 @@ export default function MemberPage() {
<Button variant="light" color="danger" onPress={logout}>
Log out
</Button>
<Button
color="primary"
onPress={() => {
navigate("edit");
}}
>
Edit profile
<Button variant="light" color="primary" onPress={onOpen}>
Change password
</Button>
</div>
</Card>
)}
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent>
{(onClose) => (
<>
<ModalHeader />
<ModalBody>
<ChangePasswordView onClose={onClose} />
</ModalBody>
<ModalFooter />
</>
)}
</ModalContent>
</Modal>
</div>
);
}