diff options
Diffstat (limited to 'packages/demobank-ui/src/pages/UpdateAccountPassword.tsx')
-rw-r--r-- | packages/demobank-ui/src/pages/UpdateAccountPassword.tsx | 259 |
1 files changed, 153 insertions, 106 deletions
diff --git a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx index d82dac4b1..e3f0de8cc 100644 --- a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx +++ b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx @@ -1,16 +1,16 @@ -import { TalerError, TranslatedString } from "@gnu-taler/taler-util"; -import { HttpResponsePaginated, RequestError, notify, notifyError, useTranslationContext } from "@gnu-taler/web-util/browser"; +import { notify, notifyInfo, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js"; -import { buildRequestErrorMessage, undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js"; -import { doAutoFocus } from "./PaytoWireTransferForm.js"; import { useBankCoreApiContext } from "../context/config.js"; -import { assertUnreachable } from "./HomePage.js"; import { useBackendState } from "../hooks/backend.js"; +import { undefinedIfEmpty, withRuntimeErrorHandling } from "../utils.js"; +import { assertUnreachable } from "./HomePage.js"; +import { doAutoFocus } from "./PaytoWireTransferForm.js"; +import { ProfileNavigation } from "./ProfileNavigation.js"; export function UpdateAccountPassword({ - account, + account: accountName, onCancel, onUpdateSuccess, focus, @@ -22,13 +22,18 @@ export function UpdateAccountPassword({ }): VNode { const { i18n } = useTranslationContext(); const { state: credentials } = useBackendState(); - const creds = credentials.status !== "loggedIn" ? undefined : credentials + const token = credentials.status !== "loggedIn" ? undefined : credentials.token const { api } = useBankCoreApiContext(); + const [current, setCurrent] = useState<string | undefined>(); const [password, setPassword] = useState<string | undefined>(); const [repeat, setRepeat] = useState<string | undefined>(); + const accountIsTheCurrentUser = credentials.status === "loggedIn" ? + credentials.username === accountName : false + const errors = undefinedIfEmpty({ + current: !accountIsTheCurrentUser ? undefined : !current ? i18n.str`required` : undefined, password: !password ? i18n.str`required` : undefined, repeat: !repeat ? i18n.str`required` @@ -37,13 +42,16 @@ export function UpdateAccountPassword({ : undefined, }); + async function doChangePassword() { - if (!!errors || !password || !creds) return; + if (!!errors || !password || !token) return; await withRuntimeErrorHandling(i18n, async () => { - const resp = await api.updatePassword(creds, { + const resp = await api.updatePassword({ username: accountName, token }, { + // old_password: current, new_password: password, }); if (resp.type === "ok") { + notifyInfo(i18n.str`Password changed`); onUpdateSuccess(); } else { switch (resp.case) { @@ -51,6 +59,10 @@ export function UpdateAccountPassword({ type: "error", title: i18n.str`Not authorized to change the password, maybe the session is invalid.` }) + case "no-rights": return notify({ + type: "error", + title: i18n.str`This user have no right on to change the password.` + }) case "not-found": return notify({ type: "error", title: i18n.str`Account not found` @@ -62,112 +74,147 @@ export function UpdateAccountPassword({ } return ( - <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg"> - <div class="px-4 sm:px-0"> - <h2 class="text-base font-semibold leading-7 text-gray-900"> - <i18n.Translate>Update password for account "{account}"</i18n.Translate> - </h2> - </div> - <form - class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2" - autoCapitalize="none" - autoCorrect="off" - onSubmit={e => { - e.preventDefault() - }} - > - <div class="px-4 py-6 sm:p-8"> - <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> + <Fragment> + {accountIsTheCurrentUser ? + <ProfileNavigation current="credentials" /> : + <h1 class="text-base font-semibold leading-6 text-gray-900"> + <i18n.Translate>Account "{accountName}"</i18n.Translate> + </h1> - <div class="sm:col-span-5"> - <label - class="block text-sm font-medium leading-6 text-gray-900" - for="password" - > - {i18n.str`New password`} - </label> - <div class="mt-2"> - <input - ref={focus ? doAutoFocus : undefined} - type="password" - class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - name="password" - id="password" - data-error={!!errors?.password && password !== undefined} - value={password ?? ""} - onChange={(e) => { - setPassword(e.currentTarget.value) - }} - // placeholder="" - autocomplete="off" - /> - <ShowInputErrorLabel - message={errors?.password} - isDirty={password !== undefined} - /> - </div> - {/* <p class="mt-2 text-sm text-gray-500" > - <i18n.Translate>user </i18n.Translate> - </p> */} - </div> + } - <div class="sm:col-span-5"> - <label - class="block text-sm font-medium leading-6 text-gray-900" - for="repeat" - > - {i18n.str`Type it again`} - </label> - <div class="mt-2"> - <input - type="password" - class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - name="repeat" - id="repeat" - data-error={!!errors?.repeat && repeat !== undefined} - value={repeat ?? ""} - onChange={(e) => { - setRepeat(e.currentTarget.value) - }} - // placeholder="" - autocomplete="off" - /> - <ShowInputErrorLabel - message={errors?.repeat} - isDirty={repeat !== undefined} - /> + <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg"> + <div class="px-4 sm:px-0"> + <h2 class="text-base font-semibold leading-7 text-gray-900"> + <i18n.Translate>Update password</i18n.Translate> + </h2> + </div> + <form + class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2" + autoCapitalize="none" + autoCorrect="off" + onSubmit={e => { + e.preventDefault() + }} + > + <div class="px-4 py-6 sm:p-8"> + <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> + <div class="sm:col-span-5"> + <label + class="block text-sm font-medium leading-6 text-gray-900" + for="password" + > + {i18n.str`New password`} + </label> + <div class="mt-2"> + <input + ref={focus ? doAutoFocus : undefined} + type="password" + class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + name="password" + id="password" + data-error={!!errors?.password && password !== undefined} + value={password ?? ""} + onChange={(e) => { + setPassword(e.currentTarget.value) + }} + autocomplete="off" + /> + <ShowInputErrorLabel + message={errors?.password} + isDirty={password !== undefined} + /> + </div> </div> - <p class="mt-2 text-sm text-gray-500" > - <i18n.Translate>repeat the same password</i18n.Translate> - </p> - </div> + <div class="sm:col-span-5"> + <label + class="block text-sm font-medium leading-6 text-gray-900" + for="repeat" + > + {i18n.str`Type it again`} + </label> + <div class="mt-2"> + <input + type="password" + class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + name="repeat" + id="repeat" + data-error={!!errors?.repeat && repeat !== undefined} + value={repeat ?? ""} + onChange={(e) => { + setRepeat(e.currentTarget.value) + }} + // placeholder="" + autocomplete="off" + /> + <ShowInputErrorLabel + message={errors?.repeat} + isDirty={repeat !== undefined} + /> + </div> + <p class="mt-2 text-sm text-gray-500" > + <i18n.Translate>repeat the same password</i18n.Translate> + </p> + </div> + {accountIsTheCurrentUser ? + <div class="sm:col-span-5"> + <label + class="block text-sm font-medium leading-6 text-gray-900" + for="password" + > + {i18n.str`Current password`} + </label> + <div class="mt-2"> + <input + type="password" + class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + name="current" + id="current-password" + data-error={!!errors?.current && current !== undefined} + value={current ?? ""} + onChange={(e) => { + setCurrent(e.currentTarget.value) + }} + autocomplete="off" + /> + <ShowInputErrorLabel + message={errors?.current} + isDirty={current !== undefined} + /> + </div> + <p class="mt-2 text-sm text-gray-500" > + <i18n.Translate>your current password, for security</i18n.Translate> + </p> + </div> + : undefined} + </div> </div> - </div> - <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8"> - {onCancel ? - <button type="button" class="text-sm font-semibold leading-6 text-gray-900" - onClick={onCancel} + <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8"> + {onCancel ? + <button type="button" class="text-sm font-semibold leading-6 text-gray-900" + onClick={onCancel} + > + <i18n.Translate>Cancel</i18n.Translate> + </button> + : <div /> + } + <button type="submit" + class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" + disabled={!!errors} + onClick={(e) => { + e.preventDefault() + doChangePassword() + }} > - <i18n.Translate>Cancel</i18n.Translate> + <i18n.Translate>Change</i18n.Translate> </button> - : <div /> - } - <button type="submit" - class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" - disabled={!!errors} - onClick={(e) => { - e.preventDefault() - doChangePassword() - }} - > - <i18n.Translate>Change</i18n.Translate> - </button> - </div> - </form> - </div> + </div> + </form> + </div> + </Fragment> ); }
\ No newline at end of file |