diff options
Diffstat (limited to 'packages/merchant-backoffice-ui/src/Routing.tsx')
-rw-r--r-- | packages/merchant-backoffice-ui/src/Routing.tsx | 398 |
1 files changed, 206 insertions, 192 deletions
diff --git a/packages/merchant-backoffice-ui/src/Routing.tsx b/packages/merchant-backoffice-ui/src/Routing.tsx index 9e801a233..874c2b0f2 100644 --- a/packages/merchant-backoffice-ui/src/Routing.tsx +++ b/packages/merchant-backoffice-ui/src/Routing.tsx @@ -19,14 +19,17 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { AbsoluteTime, TalerError, TalerErrorDetail, TranslatedString } from "@gnu-taler/taler-util"; import { - ErrorType, - HttpError, + AbsoluteTime, + TalerError, + TranslatedString +} from "@gnu-taler/taler-util"; +import { urlPattern, - useTranslationContext, + useTranslationContext } from "@gnu-taler/web-util/browser"; -import { Fragment, FunctionComponent, VNode, h } from "preact"; +import { createHashHistory } from "history"; +import { Fragment, VNode, h } from "preact"; import { Route, Router, route } from "preact-router"; import { useEffect, useErrorBoundary, useState } from "preact/hooks"; import { Loading } from "./components/exception/loading.js"; @@ -35,13 +38,10 @@ import { NotConnectedAppMenu, NotificationCard, } from "./components/menu/index.js"; +import { useSessionContext } from "./context/session.js"; import { useInstanceBankAccounts } from "./hooks/bank.js"; import { useInstanceKYCDetails } from "./hooks/instance.js"; import { usePreference } from "./hooks/preference.js"; -import { - DEFAULT_ADMIN_USERNAME, - useSessionContext, -} from "./context/session.js"; import InstanceCreatePage from "./paths/admin/create/index.js"; import InstanceListPage from "./paths/admin/list/index.js"; import BankAccountCreatePage from "./paths/instance/accounts/create/index.js"; @@ -73,10 +73,9 @@ import WebhookCreatePage from "./paths/instance/webhooks/create/index.js"; import WebhookListPage from "./paths/instance/webhooks/list/index.js"; import WebhookUpdatePage from "./paths/instance/webhooks/update/index.js"; import { LoginPage } from "./paths/login/index.js"; -import NotFoundPage from "./paths/notfound/index.js"; +import { NotFoundPage } from "./paths/notfound/index.js"; import { Settings } from "./paths/settings/index.js"; import { Notification } from "./utils/types.js"; -import { createHashHistory } from "history"; export enum InstancePaths { error = "/error", @@ -122,7 +121,7 @@ export enum InstancePaths { } // eslint-disable-next-line @typescript-eslint/no-empty-function -const noop = () => { }; +// const noop = () => { }; export enum AdminPaths { list_instances = "/instances", @@ -130,7 +129,7 @@ export enum AdminPaths { update_instance = "/instance/:id/update", } -export interface Props { } +export interface Props {} export const privatePages = { home: urlPattern(/\/home/, () => "#/home"), @@ -143,7 +142,7 @@ export const publicPages = { const history = createHashHistory(); export function Routing(_p: Props): VNode { - const { i18n } = useTranslationContext(); + // const { i18n } = useTranslationContext(); const { state } = useSessionContext(); type GlobalNotifState = @@ -158,85 +157,90 @@ export function Routing(_p: Props): VNode { const now = AbsoluteTime.now(); const instance = useInstanceBankAccounts(); - const accounts = !instance || instance instanceof TalerError || instance.data.type === "fail" ? undefined : instance.result; + const accounts = + !instance || instance instanceof TalerError || instance.type === "fail" + ? undefined + : instance.body; const shouldWarnAboutMissingBankAccounts = - !state.isAdmin && accounts !== undefined && accounts.length < 1 && + !state.isAdmin && + accounts !== undefined && + accounts.length < 1 && (AbsoluteTime.isNever(preference.hideMissingAccountUntil) || AbsoluteTime.cmp(now, preference.hideMissingAccountUntil) > 1); - ; + const shouldLogin = state.status === "loggedOut" || state.status === "expired"; - function ServerErrorRedirectTo(to: InstancePaths | AdminPaths) { - return function ServerErrorRedirectToImpl( - error: HttpError<TalerErrorDetail>, - ) { - if (error.type === ErrorType.TIMEOUT) { - setGlobalNotification({ - message: i18n.str`The request to the backend take too long and was cancelled`, - description: i18n.str`Diagnostic from ${error.info.url} is "${error.message}"`, - type: "ERROR", - to, - }); - } else { - setGlobalNotification({ - message: i18n.str`The backend reported a problem: HTTP status #${error.status}`, - description: i18n.str`Diagnostic from ${error.info.url} is '${error.message}'`, - details: - error.type === ErrorType.CLIENT || error.type === ErrorType.SERVER - ? error.payload.hint - : undefined, - type: "ERROR", - to, - }); - } - return <Redirect to={to} />; - }; - } + // function ServerErrorRedirectTo(to: InstancePaths | AdminPaths) { + // return function ServerErrorRedirectToImpl( + // error: HttpError<TalerErrorDetail>, + // ) { + // if (error.type === ErrorType.TIMEOUT) { + // setGlobalNotification({ + // message: i18n.str`The request to the backend take too long and was cancelled`, + // description: i18n.str`Diagnostic from ${error.info.url} is "${error.message}"`, + // type: "ERROR", + // to, + // }); + // } else { + // setGlobalNotification({ + // message: i18n.str`The backend reported a problem: HTTP status #${error.status}`, + // description: i18n.str`Diagnostic from ${error.info.url} is '${error.message}'`, + // details: + // error.type === ErrorType.CLIENT || error.type === ErrorType.SERVER + // ? error.payload.hint + // : undefined, + // type: "ERROR", + // to, + // }); + // } + // return <Redirect to={to} />; + // }; + // } // const LoginPageAccessDeniend = onUnauthorized - const LoginPageAccessDenied = () => { - return ( - <Fragment> - <NotificationCard - notification={{ - message: i18n.str`Access denied`, - description: i18n.str`Session expired or password changed.`, - type: "ERROR", - }} - /> - <LoginPage /> - </Fragment> - ); - }; - - function IfAdminCreateDefaultOr<T>(Next: FunctionComponent<unknown>) { - return function IfAdminCreateDefaultOrImpl(props?: T) { - if (state.isAdmin && state.instance === DEFAULT_ADMIN_USERNAME) { - return ( - <Fragment> - <NotificationCard - notification={{ - message: i18n.str`No 'default' instance configured yet.`, - description: i18n.str`Create a 'default' instance to begin using the merchant backoffice.`, - type: "INFO", - }} - /> - <InstanceCreatePage - forceId={DEFAULT_ADMIN_USERNAME} - onConfirm={() => { - route(InstancePaths.bank_list); - }} - /> - </Fragment> - ); - } - if (props) { - return <Next {...props} />; - } - return <Next />; - }; - } + // const LoginPageAccessDenied = () => { + // return ( + // <Fragment> + // <NotificationCard + // notification={{ + // message: i18n.str`Access denied`, + // description: i18n.str`Session expired or password changed.`, + // type: "ERROR", + // }} + // /> + // <LoginPage /> + // </Fragment> + // ); + // }; + + // function IfAdminCreateDefaultOr<T>(Next: FunctionComponent<unknown>) { + // return function IfAdminCreateDefaultOrImpl(props?: T) { + // if (state.isAdmin && state.instance === DEFAULT_ADMIN_USERNAME) { + // return ( + // <Fragment> + // <NotificationCard + // notification={{ + // message: i18n.str`No 'default' instance configured yet.`, + // description: i18n.str`Create a 'default' instance to begin using the merchant backoffice.`, + // type: "INFO", + // }} + // /> + // <InstanceCreatePage + // forceId={DEFAULT_ADMIN_USERNAME} + // onConfirm={() => { + // route(InstancePaths.bank_list); + // }} + // /> + // </Fragment> + // ); + // } + // if (props) { + // return <Next {...props} />; + // } + // return <Next />; + // }; + // } if (shouldLogin) { return ( @@ -252,9 +256,11 @@ export function Routing(_p: Props): VNode { <Fragment> <Menu /> <BankAccountBanner /> - <BankAccountCreatePage onConfirm={() => { - route(InstancePaths.bank_list); - }} /> + <BankAccountCreatePage + onConfirm={() => { + route(InstancePaths.bank_list); + }} + /> </Fragment> ); } @@ -306,8 +312,8 @@ export function Routing(_p: Props): VNode { onUpdate={(id: string): void => { route(`/instance/${id}/update`); }} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.error)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.error)} /> )} {state.isAdmin && ( @@ -328,9 +334,9 @@ export function Routing(_p: Props): VNode { onConfirm={() => { route(AdminPaths.list_instances); }} - onUpdateError={ServerErrorRedirectTo(AdminPaths.list_instances)} - onLoadError={ServerErrorRedirectTo(AdminPaths.list_instances)} - onNotFound={NotFoundPage} + // onUpdateError={ServerErrorRedirectTo(AdminPaths.list_instances)} + // onLoadError={ServerErrorRedirectTo(AdminPaths.list_instances)} + // onNotFound={NotFoundPage} /> )} {/** @@ -345,10 +351,10 @@ export function Routing(_p: Props): VNode { onConfirm={() => { route(`/`); }} - onUpdateError={noop} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.error)} + // onUpdateError={noop} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.error)} /> {/** * Update instance page @@ -362,9 +368,9 @@ export function Routing(_p: Props): VNode { onCancel={() => { route(InstancePaths.order_list); }} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.error)} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.error)} /> {/** * Inventory pages @@ -372,28 +378,28 @@ export function Routing(_p: Props): VNode { <Route path={InstancePaths.inventory_list} component={ProductListPage} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} onCreate={() => { route(InstancePaths.inventory_new); }} onSelect={(id: string) => { route(InstancePaths.inventory_update.replace(":pid", id)); }} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} /> <Route path={InstancePaths.inventory_update} component={ProductUpdatePage} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.inventory_list)} onConfirm={() => { route(InstancePaths.inventory_list); }} onBack={() => { route(InstancePaths.inventory_list); }} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.inventory_list)} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} /> <Route path={InstancePaths.inventory_new} @@ -411,28 +417,28 @@ export function Routing(_p: Props): VNode { <Route path={InstancePaths.bank_list} component={BankAccountListPage} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} onCreate={() => { route(InstancePaths.bank_new); }} onSelect={(id: string) => { route(InstancePaths.bank_update.replace(":bid", id)); }} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} /> <Route path={InstancePaths.bank_update} component={BankAccountUpdatePage} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.inventory_list)} onConfirm={() => { route(InstancePaths.bank_list); }} onBack={() => { route(InstancePaths.bank_list); }} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.inventory_list)} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} /> <Route path={InstancePaths.bank_new} @@ -456,16 +462,16 @@ export function Routing(_p: Props): VNode { onSelect={(id: string) => { route(InstancePaths.order_details.replace(":oid", id)); }} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} /> <Route path={InstancePaths.order_details} component={OrderDetailsPage} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.order_list)} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.order_list)} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} onBack={() => { route(InstancePaths.order_list); }} @@ -486,9 +492,9 @@ export function Routing(_p: Props): VNode { <Route path={InstancePaths.transfers_list} component={TransferListPage} - onUnauthorized={LoginPageAccessDenied} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} - onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} + // onUnauthorized={LoginPageAccessDenied} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} onCreate={() => { route(InstancePaths.transfers_new); }} @@ -509,9 +515,9 @@ export function Routing(_p: Props): VNode { <Route path={InstancePaths.webhooks_list} component={WebhookListPage} - onUnauthorized={LoginPageAccessDenied} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} - onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} + // onUnauthorized={LoginPageAccessDenied} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} onCreate={() => { route(InstancePaths.webhooks_new); }} @@ -525,9 +531,9 @@ export function Routing(_p: Props): VNode { onConfirm={() => { route(InstancePaths.webhooks_list); }} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.webhooks_list)} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.webhooks_list)} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} onBack={() => { route(InstancePaths.webhooks_list); }} @@ -548,9 +554,9 @@ export function Routing(_p: Props): VNode { <Route path={InstancePaths.otp_devices_list} component={ValidatorListPage} - onUnauthorized={LoginPageAccessDenied} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} - onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} + // onUnauthorized={LoginPageAccessDenied} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} onCreate={() => { route(InstancePaths.otp_devices_new); }} @@ -564,9 +570,9 @@ export function Routing(_p: Props): VNode { onConfirm={() => { route(InstancePaths.otp_devices_list); }} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.otp_devices_list)} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.otp_devices_list)} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} onBack={() => { route(InstancePaths.otp_devices_list); }} @@ -587,9 +593,9 @@ export function Routing(_p: Props): VNode { <Route path={InstancePaths.templates_list} component={TemplateListPage} - onUnauthorized={LoginPageAccessDenied} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} - onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} + // onUnauthorized={LoginPageAccessDenied} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)} onCreate={() => { route(InstancePaths.templates_new); }} @@ -609,9 +615,9 @@ export function Routing(_p: Props): VNode { onConfirm={() => { route(InstancePaths.templates_list); }} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} onBack={() => { route(InstancePaths.templates_list); }} @@ -632,9 +638,9 @@ export function Routing(_p: Props): VNode { onOrderCreated={(id: string) => { route(InstancePaths.order_details.replace(":oid", id)); }} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} onBack={() => { route(InstancePaths.templates_list); }} @@ -642,9 +648,9 @@ export function Routing(_p: Props): VNode { <Route path={InstancePaths.templates_qr} component={TemplateQrPage} - onUnauthorized={LoginPageAccessDenied} - onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)} - onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + // onUnauthorized={LoginPageAccessDenied} + // onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)} + // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} onBack={() => { route(InstancePaths.templates_list); }} @@ -673,52 +679,52 @@ function AdminInstanceUpdatePage({ id, ...rest }: { id: string } & InstanceUpdatePageProps): VNode { - const { i18n } = useTranslationContext(); + // const { i18n } = useTranslationContext(); return ( <Fragment> <InstanceAdminUpdatePage {...rest} instanceId={id} - onLoadError={(error: HttpError<TalerErrorDetail>) => { - const notif = - error.type === ErrorType.TIMEOUT - ? { - message: i18n.str`The request to the backend take too long and was cancelled`, - description: i18n.str`Diagnostic from ${error.info.url} is '${error.message}'`, - type: "ERROR" as const, - } - : { - message: i18n.str`The backend reported a problem: HTTP status #${error.status}`, - description: i18n.str`Diagnostic from ${error.info.url} is '${error.message}'`, - details: - error.type === ErrorType.CLIENT || - error.type === ErrorType.SERVER - ? error.payload.hint - : undefined, - type: "ERROR" as const, - }; - return ( - <Fragment> - <NotificationCard notification={notif} /> - <LoginPage /> - </Fragment> - ); - }} - onUnauthorized={() => { - return ( - <Fragment> - <NotificationCard - notification={{ - message: i18n.str`Access denied`, - description: i18n.str`The access token provided is invalid`, - type: "ERROR", - }} - /> - <LoginPage /> - </Fragment> - ); - }} + // onLoadError={(error: HttpError<TalerErrorDetail>) => { + // const notif = + // error.type === ErrorType.TIMEOUT + // ? { + // message: i18n.str`The request to the backend take too long and was cancelled`, + // description: i18n.str`Diagnostic from ${error.info.url} is '${error.message}'`, + // type: "ERROR" as const, + // } + // : { + // message: i18n.str`The backend reported a problem: HTTP status #${error.status}`, + // description: i18n.str`Diagnostic from ${error.info.url} is '${error.message}'`, + // details: + // error.type === ErrorType.CLIENT || + // error.type === ErrorType.SERVER + // ? error.payload.hint + // : undefined, + // type: "ERROR" as const, + // }; + // return ( + // <Fragment> + // <NotificationCard notification={notif} /> + // <LoginPage /> + // </Fragment> + // ); + // }} + // onUnauthorized={() => { + // return ( + // <Fragment> + // <NotificationCard + // notification={{ + // message: i18n.str`Access denied`, + // description: i18n.str`The access token provided is invalid`, + // type: "ERROR", + // }} + // /> + // <LoginPage /> + // </Fragment> + // ); + // }} /> </Fragment> ); @@ -727,7 +733,7 @@ function AdminInstanceUpdatePage({ function BankAccountBanner(): VNode { const { i18n } = useTranslationContext(); - const [_, updatePref] = usePreference(); + const [, updatePref] = usePreference(); const now = AbsoluteTime.now(); const oneDay = { d_ms: 1000 * 60 * 60 * 24 }; const tomorrow = AbsoluteTime.addDuration(now, oneDay); @@ -740,7 +746,10 @@ function BankAccountBanner(): VNode { description: ( <div> <p> - <i18n.Translate>Without this the merchant backend will refuse to create new orders.</i18n.Translate> + <i18n.Translate> + Without this the merchant backend will refuse to create new + orders. + </i18n.Translate> </p> <div class="buttons is-right"> <button @@ -754,10 +763,9 @@ function BankAccountBanner(): VNode { ), }} /> - ) + ); } - function KycBanner(): VNode { const kycStatus = useInstanceKYCDetails(); const { i18n } = useTranslationContext(); @@ -766,7 +774,11 @@ function KycBanner(): VNode { const now = AbsoluteTime.now(); - const needsToBeShown = kycStatus !== undefined && !(kycStatus instanceof TalerError) && kycStatus.type === "ok" && !!kycStatus.body; + const needsToBeShown = + kycStatus !== undefined && + !(kycStatus instanceof TalerError) && + kycStatus.type === "ok" && + !!kycStatus.body; const hidden = AbsoluteTime.cmp(now, prefs.hideKycUntil) < 1; if (hidden || !needsToBeShown) return <Fragment />; @@ -782,8 +794,10 @@ function KycBanner(): VNode { description: ( <div> <p> - Some transfer are on hold until a KYC process is completed. Go to - the KYC section in the left panel for more information + <i18n.Translate> + Some transfer are on hold until a KYC process is completed. Go + to the KYC section in the left panel for more information + </i18n.Translate> </p> <div class="buttons is-right"> <button |