diff --git a/src/components/ChangePasswordForm.tsx b/src/components/ChangePasswordForm.tsx new file mode 100644 index 0000000..c610809 --- /dev/null +++ b/src/components/ChangePasswordForm.tsx @@ -0,0 +1,120 @@ +import { BadRequest, type FeathersError } from "@feathersjs/errors" +import React, { useEffect, useRef, useState } from "react" +import { Form } from "react-bootstrap" +import ErrorManager, { type BadRequestData } from "./ErrorManager" +import { FloppyDiskArrowIn } from "iconoir-react" + +export type ChangePasswordFormPayload = { + currentPassword: string + password: string + verifyPassword: string +} + +export interface ChangePasswordFormProps { + className?: string + onSubmit: (payload: ChangePasswordFormPayload) => void + error?: FeathersError | null +} + +const ChangePasswordForm: React.FC = ({ + className, + onSubmit, + error, +}) => { + const delayTimerRef = useRef(null) + const [formError, setFormError] = useState(null) + + const formPayload = useRef({ + currentPassword: "", + password: "", + verifyPassword: "", + }) + + const handleOnSubmit = (e: React.FormEvent) => { + e.preventDefault() + console.info("[ChangePasswordForm] @handleOnSubmit") + const errorsAsData: { [key: string]: BadRequestData } = {} + if (formPayload.current.password !== formPayload.current.verifyPassword) { + errorsAsData.verifyPassword = { + label: "Verify password", + message: "The passwords you entered don't match. Please try again.", + } + } + if (Object.keys(errorsAsData).length > 0) { + setFormError(new BadRequest("Please check your entries.", errorsAsData)) + return false + } + onSubmit(formPayload.current) + } + + const updateValue = (key: keyof ChangePasswordFormPayload, value: string) => { + formPayload.current[key] = value + console.info("[RegisterForm] @updatePreview", key, value) + // handpick the fields to preview + delayTimerRef.current = setTimeout(() => { + setFormError(null) + }, 400) + } + + console.info("[ChangePasswordForm] @render", { error }) + + useEffect(() => { + return () => { + if (delayTimerRef.current) { + clearTimeout(delayTimerRef.current) + } + } + }, []) + + return ( + <> +
+ + + + Current Password + updateValue("currentPassword", e.target.value)} + type="password" + /> + + + New Password + updateValue("password", e.target.value)} + type="password" + /> + + + + Verify New Password + + updateValue("verifyPassword", e.target.value)} + type="password" + /> + + + + +

+ Any Questions?
+ Contact us at{" "} + info@impresso-project.ch +

+ + ) +} + +export default ChangePasswordForm diff --git a/src/components/ChangePasswordModal.tsx b/src/components/ChangePasswordModal.tsx new file mode 100644 index 0000000..8de8808 --- /dev/null +++ b/src/components/ChangePasswordModal.tsx @@ -0,0 +1,53 @@ +import { Col, Container, Form, Modal, Row } from "react-bootstrap" +import { BrowserViewChangePassword } from "../constants" +import { useBrowserStore } from "../store" +import Alert from "./Alert" +import { useRef, useState } from "react" +import ChangePasswordForm, { + type ChangePasswordFormPayload, +} from "./ChangePasswordForm" +import type { FeathersError } from "@feathersjs/errors" +import { changePasswordService } from "../services" + +const ChangePassword = () => { + const view = useBrowserStore((state) => state.view) + const setView = useBrowserStore((state) => state.setView) + const [error, setError] = useState(null) + + const handleOnSubmit = (payload: ChangePasswordFormPayload) => { + console.info("[ChangePasswordModal] @handleOnSubmit", payload) + changePasswordService + .create({ + password: payload.password, + currentPassword: payload.currentPassword, + }) + .then((data) => { + console.info( + "[ChangePasswordModal] Password changed successfully. data:", + data + ) + setView(null) + }) + .catch((err: FeathersError) => { + setError(err) + console.error("[ChangePasswordModal] create", err.message, err.data) + }) + } + + return ( + setView(null)} + > + + Change Password + + + + + + ) +} + +export default ChangePassword diff --git a/src/components/Modals.tsx b/src/components/Modals.tsx index 8496b72..b663c70 100644 --- a/src/components/Modals.tsx +++ b/src/components/Modals.tsx @@ -3,15 +3,17 @@ import LoginModal from "./LoginModal" import RegisterModal from "./RegisterModal" import ProfileModal from "./ProfileModal" import TermsOfUseModal from "./TermsOfUseModal" +import ChangePasswordModal from "./ChangePasswordModal" const Modals: React.FC<{ termsOfuseContent?: string }> = ({ - termsOfuseContent='', + termsOfuseContent = "", }) => { return (
+
diff --git a/src/components/UserArea.tsx b/src/components/UserArea.tsx index 01b1ef8..fca8705 100644 --- a/src/components/UserArea.tsx +++ b/src/components/UserArea.tsx @@ -5,6 +5,7 @@ import { userService } from "../services" import { forwardRef, useEffect } from "react" import { PageDown } from "iconoir-react" import { + BrowserViewChangePassword, BrowserViewLogin, BrowserViewProfile, BrowserViewRegister, @@ -91,13 +92,21 @@ const UserArea = () => { setView(BrowserViewProfile)}> Profile - Datasets - setView(BrowserViewTermsOfUse)}> - Terms Of Use + setView(BrowserViewChangePassword)}> + Change Password Log out {/* add separator */} + {/* add separator */} + + + Corpus Overview + + setView(BrowserViewTermsOfUse)}> + Terms Of Use + + { export const versionService = app.service("version") export const userService = app.service("me") export const usersService = app.service("users") +export const changePasswordService = app.service("change-password") export const termsOfUseService = app.service("terms-of-use") export const loginService = app.service("authentication") diff --git a/src/styles/global.css b/src/styles/global.css index a66ed34..9777eba 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -524,7 +524,7 @@ hr { } a.dropdown-item { position: relative; - display: block; + display: inline-block; width: auto; } a.dropdown-item::after {