Skip to content

Commit

Permalink
use modals for pw change
Browse files Browse the repository at this point in the history
  • Loading branch information
kayra1 committed Aug 1, 2024
1 parent 186e034 commit f7ad75a
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 120 deletions.
101 changes: 0 additions & 101 deletions ui/src/app/change_password/page.tsx

This file was deleted.

9 changes: 6 additions & 3 deletions ui/src/app/login.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { useState } from "react";
import { useContext, useState } from "react";
import { useAuth } from "./auth/authContext";
import { useCookies } from "react-cookie";
import { useRouter } from "next/navigation";
import { ChangePasswordModal, ChangePasswordModalContext, ChangePasswordModalData } from "./users/components";



export function AccountTab() {
const router = useRouter()
const [cookies, setCookie, removeCookie] = useCookies(['user_token']);
const [menuOpen, setMenuOpen] = useState<boolean>(false)
const changePasswordModalContext = useContext(ChangePasswordModalContext)
const authDetails = useAuth()
return (
<>
Expand All @@ -21,7 +24,7 @@ export function AccountTab() {
<i className="p-icon--menu"></i>
<span className="p-contextual-menu__dropdown" id="menu-3" aria-hidden={!menuOpen} style={{ bottom: "40px" }}>
<span className="p-contextual-menu__group">
<button className="p-contextual-menu__link" onMouseDown={() => router.push("/change_password")}>Change Password</button>
<button className="p-contextual-menu__link" onMouseDown={() => changePasswordModalContext.setModalData({ "id": authDetails.user ? authDetails.user.id.toString() : "", "username": authDetails.user ? authDetails.user.username : "" })}>Change Password</button>
<button className="p-contextual-menu__link" onMouseDown={() => removeCookie("user_token")}>Log Out</button>
</span>
</span>
Expand Down
28 changes: 16 additions & 12 deletions ui/src/app/nav.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client"

import { SetStateAction, Dispatch, useState } from "react"
import { SetStateAction, Dispatch, useState, useContext } from "react"
import { QueryClient, QueryClientProvider } from "react-query";
import Image from "next/image";
import { Aside, AsideContext } from "./aside";
Expand All @@ -9,6 +9,7 @@ import { usePathname } from "next/navigation";
import { useAuth } from "./auth/authContext";
import UploadCSRAsidePanel from "./certificate_requests/asideForm";
import UploadUserAsidePanel from "./users/asideForm";
import { ChangePasswordModalData, ChangePasswordModal, ChangePasswordModalContext } from "./users/components";

export function SideBar({ activePath, sidebarVisible, setSidebarVisible }: { activePath: string, sidebarVisible: boolean, setSidebarVisible: Dispatch<SetStateAction<boolean>> }) {
const auth = useAuth()
Expand Down Expand Up @@ -56,7 +57,6 @@ export function SideBar({ activePath, sidebarVisible, setSidebarVisible }: { act
</div>
</div>
</div>

</header >
)
}
Expand Down Expand Up @@ -106,6 +106,7 @@ export default function Navigation({
const [sidebarVisible, setSidebarVisible] = useState<boolean>(true)
const [asideOpen, setAsideOpen] = useState<boolean>(false)
const [asideData, setAsideData] = useState<any>(null)
const [changePasswordModalData, setChangePasswordModalData] = useState<ChangePasswordModalData>(null)
let asideForm = UploadCSRAsidePanel
if (activePath == "/users") {
asideForm = UploadUserAsidePanel
Expand All @@ -114,18 +115,21 @@ export default function Navigation({
<QueryClientProvider client={queryClient}>
<div className="l-application" role="presentation">
<AsideContext.Provider value={{ isOpen: asideOpen, setIsOpen: setAsideOpen, extraData: asideData, setExtraData: setAsideData }}>
{
shouldRenderNavigation ? (
<>
<TopBar setSidebarVisible={setSidebarVisible} />
<SideBar activePath={activePath} sidebarVisible={sidebarVisible} setSidebarVisible={setSidebarVisible} />
</>
) : (
<></>
)
}
<ChangePasswordModalContext.Provider value={{ modalData: changePasswordModalData, setModalData: setChangePasswordModalData }}>
{
shouldRenderNavigation ? (
<>
<TopBar setSidebarVisible={setSidebarVisible} />
<SideBar activePath={activePath} sidebarVisible={sidebarVisible} setSidebarVisible={setSidebarVisible} />
</>
) : (
<></>
)
}
</ChangePasswordModalContext.Provider>
<main className="l-main">
{children}
{changePasswordModalData != null && <ChangePasswordModal modalData={changePasswordModalData} setModalData={setChangePasswordModalData} />}
</main>
<Aside FormComponent={asideForm} />
</AsideContext.Provider>
Expand Down
103 changes: 102 additions & 1 deletion ui/src/app/users/components.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { Dispatch, SetStateAction } from "react"
import { Dispatch, SetStateAction, useState, ChangeEvent, createContext } from "react"
import { useAuth } from "../auth/authContext"
import { useMutation, useQueryClient } from "react-query"
import { changePassword } from "../queries"
import { passwordIsValid } from "../utils"

export type ConfirmationModalData = {
onMouseDownFunc: () => void
Expand All @@ -10,6 +14,21 @@ interface ConfirmationModalProps {
setModalData: Dispatch<SetStateAction<ConfirmationModalData>>
}

export type ChangePasswordModalData = {
id: string
username: string
} | null

interface ChangePasswordModalProps {
modalData: ChangePasswordModalData
setModalData: Dispatch<SetStateAction<ChangePasswordModalData>>
}

export const ChangePasswordModalContext = createContext<ChangePasswordModalProps>({
modalData: null,
setModalData: () => { }
})

export function ConfirmationModal({ modalData, setModalData }: ConfirmationModalProps) {
const confirmQuery = () => {
modalData?.onMouseDownFunc()
Expand All @@ -29,4 +48,86 @@ export function ConfirmationModal({ modalData, setModalData }: ConfirmationModal
</section>
</div>
)
}

export function ChangePasswordModal({ modalData, setModalData }: ChangePasswordModalProps) {
const auth = useAuth()
const queryClient = useQueryClient()
const mutation = useMutation(changePassword, {
onSuccess: () => {
queryClient.invalidateQueries('users')
setErrorText("")
setModalData(null)
},
onError: (e: Error) => {
setErrorText(e.message)
}
})
const [showPassword1, setShowPassword1] = useState<boolean>(false)
const [password1, setPassword1] = useState<string>("")
const [password2, setPassword2] = useState<string>("")
const passwordsMatch = password1 === password2
const [errorText, setErrorText] = useState<string>("")
const handlePassword1Change = (event: ChangeEvent<HTMLInputElement>) => { setPassword1(event.target.value) }
const handlePassword2Change = (event: ChangeEvent<HTMLInputElement>) => { setPassword2(event.target.value) }
return (
<div className="p-modal" id="modal">
<section className="p-modal__dialog" role="dialog" aria-modal="true" aria-labelledby="modal-title" aria-describedby="modal-description">
<header className="p-modal__header">
<h2 className="p-modal__title" id="modal-title">Change Password</h2>
</header>
<form className={"p-form-validation " + ((!passwordIsValid(password1) && password1 != "") || (!passwordsMatch && password2 != "") ? "is-error" : "")}>
<div className="p-form__group row">
<label className="p-form__label">Username</label>
<input type="text" id="InputUsername" name="InputUsername" value={modalData?.username} disabled={true} />
<div>
<label className="p-form__label">New Password</label>
<button className="p-button--base u-no-margin--bottom has-icon" style={{ float: "right" }} aria-live="polite" aria-controls="password" onClick={(e) => { e.preventDefault(); setShowPassword1(!showPassword1) }}>
{showPassword1 ? (
<>
<span className="p-form-password-toggle__label">
Hide
</span>
<i className="p-icon--hide"></i>
</>
) : (
<>
<span className="p-form-password-toggle__label">
Show
</span>
<i className="p-icon--show"></i>
</>
)}
</button>
</div>
<input className="p-form-validation__input" type={showPassword1 ? "text" : "password"} id="password1" name="password" placeholder="******" autoComplete="current-password" required={true} onChange={handlePassword1Change} />
<p className="p-form-help-text">
Password must have 8 or more characters, must include at least one capital letter, one lowercase letter, and either a number or a symbol.
</p>
<label htmlFor="p-form__label">Confirm New Password</label>
<input className="p-form-validation__input" type="password" id="InputPassword" name="password2" placeholder="******" autoComplete="current-password" onChange={handlePassword2Change} />
{!passwordIsValid(password1) && password1 != "" && <p className="p-form-validation__message">Password is not valid</p>}
{passwordIsValid(password1) && !passwordsMatch && password2 != "" && <p className="p-form-validation__message">Passwords do not match</p>}
{errorText &&
<div className="p-notification--negative">
<div className="p-notification__content">
<h5 className="p-notification__title">Error</h5>
<p className="p-notification__message">{errorText.split("error: ")}</p>
</div>
</div>
}

</div>
</form>
<footer className="p-modal__footer">
<button className="u-no-margin--bottom" aria-controls="modal" onMouseDown={() => setModalData(null)}>Cancel</button>
{!passwordsMatch || !passwordIsValid(password1) ? (
<button className="p-button--positive u-no-margin--bottom" type="submit" name="submit" disabled={true}>Submit</button>
) : (
<button className="p-button--positive u-no-margin--bottom" type="submit" name="submit" onClick={(event) => { event.preventDefault(); mutation.mutate({ authToken: (auth.user ? auth.user.authToken : ""), id: modalData ? modalData.id : "", password: password1 }) }}>Submit</button>
)}
</footer>
</section>
</div>
)
}
9 changes: 6 additions & 3 deletions ui/src/app/users/row.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState, Dispatch, SetStateAction, useEffect, useRef, useContext } from "react"
import { UseMutationResult, useMutation, useQueryClient } from "react-query"
import { RequiredCSRParams, deleteUser } from "../queries"
import { ConfirmationModalData, ConfirmationModal } from "./components"
import { ConfirmationModalData, ConfirmationModal, ChangePasswordModalData, ChangePasswordModal } from "./components"
import "./../globals.scss"
import { useAuth } from "../auth/authContext"
import { AsideContext } from "../aside"
Expand All @@ -18,6 +18,7 @@ export default function Row({ id, username, ActionMenuExpanded, setActionMenuExp
const auth = useAuth()
const asideContext = useContext(AsideContext)
const [confirmationModalData, setConfirmationModalData] = useState<ConfirmationModalData>(null)
const [changePasswordModalData, setChangePasswordModalData] = useState<ChangePasswordModalData>(null)
const queryClient = useQueryClient()
const deleteMutation = useMutation(deleteUser, {
onSuccess: () => queryClient.invalidateQueries('users')
Expand All @@ -32,8 +33,9 @@ export default function Row({ id, username, ActionMenuExpanded, setActionMenuExp
})
}
const handleChangePassword = () => {
asideContext.setExtraData({ "user": { "id": id, "username": username } })
asideContext.setIsOpen(true)
// asideContext.setExtraData({ "user": { "id": id, "username": username } })
// asideContext.setIsOpen(true)
setChangePasswordModalData({ "id": id.toString(), "username": username })
}

return (
Expand Down Expand Up @@ -66,6 +68,7 @@ export default function Row({ id, username, ActionMenuExpanded, setActionMenuExp
</span>
</td>
{confirmationModalData != null && <ConfirmationModal modalData={confirmationModalData} setModalData={setConfirmationModalData} />}
{changePasswordModalData != null && <ChangePasswordModal modalData={changePasswordModalData} setModalData={setChangePasswordModalData} />}
</tr>
</>
)
Expand Down

0 comments on commit f7ad75a

Please sign in to comment.