diff options
Diffstat (limited to 'packages/demobank-ui/src/pages')
-rw-r--r-- | packages/demobank-ui/src/pages/AccountPage.tsx | 81 | ||||
-rw-r--r-- | packages/demobank-ui/src/pages/AdminPage.tsx | 117 | ||||
-rw-r--r-- | packages/demobank-ui/src/pages/BankFrame.tsx | 81 | ||||
-rw-r--r-- | packages/demobank-ui/src/pages/BusinessAccount.tsx | 90 | ||||
-rw-r--r-- | packages/demobank-ui/src/pages/HomePage.tsx | 4 | ||||
-rw-r--r-- | packages/demobank-ui/src/pages/Routing.tsx | 23 |
6 files changed, 281 insertions, 115 deletions
diff --git a/packages/demobank-ui/src/pages/AccountPage.tsx b/packages/demobank-ui/src/pages/AccountPage.tsx index 769e85804..370605871 100644 --- a/packages/demobank-ui/src/pages/AccountPage.tsx +++ b/packages/demobank-ui/src/pages/AccountPage.tsx @@ -104,49 +104,48 @@ export function AccountPage({ account, onLoadNotOk }: Props): VNode { )} <section style={{ marginTop: "2em" }}> - <Moves account={account} /> + <div class="active"> + <h3>{i18n.str`Latest transactions`}</h3> + <Transactions account={account} /> + </div> </section> </Fragment> ); } -function Moves({ account }: { account: string }): VNode { - const [tab, setTab] = useState<"transactions" | "cashouts">("transactions"); - const { i18n } = useTranslationContext(); - return ( - <article> - <div class="payments"> - <div class="tab"> - <button - class={tab === "transactions" ? "tablinks active" : "tablinks"} - onClick={(): void => { - setTab("transactions"); - }} - > - {i18n.str`Transactions`} - </button> - <button - class={tab === "cashouts" ? "tablinks active" : "tablinks"} - onClick={(): void => { - setTab("cashouts"); - }} - > - {i18n.str`Cashouts`} - </button> - </div> - {tab === "transactions" && ( - <div class="active"> - <h3>{i18n.str`Latest transactions`}</h3> - <Transactions account={account} /> - </div> - )} - {tab === "cashouts" && ( - <div class="active"> - <h3>{i18n.str`Latest cashouts`}</h3> - <Cashouts account={account} /> - </div> - )} - </div> - </article> - ); -} +// function Moves({ account }: { account: string }): VNode { +// const [tab, setTab] = useState<"transactions" | "cashouts">("transactions"); +// const { i18n } = useTranslationContext(); +// return ( +// <article> +// <div class="payments"> +// <div class="tab"> +// <button +// class={tab === "transactions" ? "tablinks active" : "tablinks"} +// onClick={(): void => { +// setTab("transactions"); +// }} +// > +// {i18n.str`Transactions`} +// </button> +// <button +// class={tab === "cashouts" ? "tablinks active" : "tablinks"} +// onClick={(): void => { +// setTab("cashouts"); +// }} +// > +// {i18n.str`Cashouts`} +// </button> +// </div> +// {tab === "transactions" && ( +// )} +// {tab === "cashouts" && ( +// <div class="active"> +// <h3>{i18n.str`Latest cashouts`}</h3> +// <Cashouts account={account} /> +// </div> +// )} +// </div> +// </article> +// ); +// } diff --git a/packages/demobank-ui/src/pages/AdminPage.tsx b/packages/demobank-ui/src/pages/AdminPage.tsx index 9efd37f12..f8efddd80 100644 --- a/packages/demobank-ui/src/pages/AdminPage.tsx +++ b/packages/demobank-ui/src/pages/AdminPage.tsx @@ -24,8 +24,8 @@ import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { ErrorMessage, usePageContext } from "../context/pageState.js"; import { - useAccountDetails, - useAccounts, + useBusinessAccountDetails, + useBusinessAccounts, useAdminAccountAPI, } from "../hooks/circuit.js"; import { @@ -71,7 +71,7 @@ export function AdminPage({ onLoadNotOk }: Props): VNode { })); } - const result = useAccounts({ account }); + const result = useBusinessAccounts({ account }); const { i18n } = useTranslationContext(); if (result.loading) return <div />; @@ -86,6 +86,10 @@ export function AdminPage({ onLoadNotOk }: Props): VNode { <ShowAccountDetails account={showDetails} onLoadNotOk={onLoadNotOk} + onChangePassword={() => { + setUpdatePassword(showDetails); + setShowDetails(undefined); + }} onUpdateSuccess={() => { showInfoMessage(i18n.str`Account updated`); setShowDetails(undefined); @@ -230,7 +234,7 @@ function initializeFromTemplate( return initial as any; } -function UpdateAccountPassword({ +export function UpdateAccountPassword({ account, onClear, onUpdateSuccess, @@ -242,7 +246,7 @@ function UpdateAccountPassword({ account: string; }): VNode { const { i18n } = useTranslationContext(); - const result = useAccountDetails(account); + const result = useBusinessAccountDetails(account); const { changePassword } = useAdminAccountAPI(); const [password, setPassword] = useState<string | undefined>(); const [repeat, setRepeat] = useState<string | undefined>(); @@ -268,7 +272,7 @@ function UpdateAccountPassword({ <div> <div> <h1 class="nav welcome-text"> - <i18n.Translate>Admin panel</i18n.Translate> + <i18n.Translate>Update password for {account}</i18n.Translate> </h1> </div> {error && ( @@ -277,10 +281,6 @@ function UpdateAccountPassword({ <form class="pure-form"> <fieldset> - <label for="username">{i18n.str`Username`}</label> - <input name="username" type="text" readOnly value={account} /> - </fieldset> - <fieldset> <label>{i18n.str`Password`}</label> <input type="password" @@ -366,7 +366,7 @@ function CreateNewAccount({ <div> <div> <h1 class="nav welcome-text"> - <i18n.Translate>Admin panel</i18n.Translate> + <i18n.Translate>New account</i18n.Translate> </h1> </div> {error && ( @@ -428,19 +428,21 @@ function CreateNewAccount({ ); } -function ShowAccountDetails({ +export function ShowAccountDetails({ account, onClear, onUpdateSuccess, onLoadNotOk, + onChangePassword, }: { onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode; - onClear: () => void; + onClear?: () => void; + onChangePassword: () => void; onUpdateSuccess: () => void; account: string; }): VNode { const { i18n } = useTranslationContext(); - const result = useAccountDetails(account); + const result = useBusinessAccountDetails(account); const { updateAccount } = useAdminAccountAPI(); const [update, setUpdate] = useState(false); const [submitAccount, setSubmitAccount] = useState< @@ -459,7 +461,7 @@ function ShowAccountDetails({ <div> <div> <h1 class="nav welcome-text"> - <i18n.Translate>Admin panel</i18n.Translate> + <i18n.Translate>Business account details</i18n.Translate> </h1> </div> {error && ( @@ -474,42 +476,59 @@ function ShowAccountDetails({ <p> <div style={{ display: "flex", justifyContent: "space-between" }}> <div> - <input - class="pure-button" - type="submit" - value={i18n.str`Close`} - onClick={async (e) => { - e.preventDefault(); - onClear(); - }} - /> + {onClear ? ( + <input + class="pure-button" + type="submit" + value={i18n.str`Close`} + onClick={async (e) => { + e.preventDefault(); + onClear(); + }} + /> + ) : undefined} </div> - <div> - <input - id="select-exchange" - class="pure-button pure-button-primary content" - disabled={update && !submitAccount} - type="submit" - value={update ? i18n.str`Confirm` : i18n.str`Update`} - onClick={async (e) => { - e.preventDefault(); - - if (!update) { - setUpdate(true); - } else { - if (!submitAccount) return; - try { - await updateAccount(account, { - cashout_address: submitAccount.cashout_address, - contact_data: submitAccount.contact_data, - }); - onUpdateSuccess(); - } catch (error) { - handleError(error, saveError, i18n); + <div style={{ display: "flex" }}> + <div> + <input + id="select-exchange" + class="pure-button pure-button-primary content" + disabled={update && !submitAccount} + type="submit" + value={i18n.str`Change password`} + onClick={async (e) => { + e.preventDefault(); + onChangePassword(); + }} + /> + </div> + <div> + <input + id="select-exchange" + class="pure-button pure-button-primary content" + disabled={update && !submitAccount} + type="submit" + value={update ? i18n.str`Confirm` : i18n.str`Update`} + onClick={async (e) => { + e.preventDefault(); + + if (!update) { + setUpdate(true); + } else { + if (!submitAccount) return; + try { + await updateAccount(account, { + cashout_address: submitAccount.cashout_address, + contact_data: submitAccount.contact_data, + }); + onUpdateSuccess(); + } catch (error) { + handleError(error, saveError, i18n); + } } - } - }} - /> + }} + /> + </div> </div> </div> </p> diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx b/packages/demobank-ui/src/pages/BankFrame.tsx index ed36daa21..0fb75b87b 100644 --- a/packages/demobank-ui/src/pages/BankFrame.tsx +++ b/packages/demobank-ui/src/pages/BankFrame.tsx @@ -15,6 +15,7 @@ */ import { Logger } from "@gnu-taler/taler-util"; +import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; import { ComponentChildren, Fragment, h, VNode } from "preact"; import talerLogo from "../assets/logo-white.svg"; import { LangSelectorLikePy as LangSelector } from "../components/LangSelector.js"; @@ -24,41 +25,46 @@ import { PageStateType, usePageContext, } from "../context/pageState.js"; -import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; +import { useBusinessAccountDetails } from "../hooks/circuit.js"; import { bankUiSettings } from "../settings.js"; const logger = new Logger("BankFrame"); +function MaybeBusinessButton({ + account, + onClick, +}: { + account: string; + onClick: () => void; +}): VNode { + const { i18n } = useTranslationContext(); + const result = useBusinessAccountDetails(account); + if (!result.ok) return <Fragment />; + return ( + <div class="some-space"> + <a + href="#" + class="pure-button pure-button-primary" + onClick={(e) => { + e.preventDefault(); + onClick(); + }} + >{i18n.str`Business Profile`}</a> + </div> + ); +} + export function BankFrame({ children, + goToBusinessAccount, }: { children: ComponentChildren; + goToBusinessAccount?: () => void; }): VNode { const { i18n } = useTranslationContext(); const backend = useBackendContext(); const { pageState, pageStateSetter } = usePageContext(); logger.trace("state", pageState); - const logOut = ( - <div class="logout"> - <a - href="#" - class="pure-button logout-button" - onClick={() => { - pageStateSetter((prevState: PageStateType) => { - const { talerWithdrawUri, withdrawalId, ...rest } = prevState; - backend.logOut(); - return { - ...rest, - withdrawalInProgress: false, - error: undefined, - info: undefined, - isRawPayto: false, - }; - }); - }} - >{i18n.str`Logout`}</a> - </div> - ); const demo_sites = []; for (const i in bankUiSettings.demoSites) @@ -120,7 +126,36 @@ export function BankFrame({ /> )} <StatusBanner /> - {backend.state.status === "loggedIn" ? logOut : null} + {backend.state.status === "loggedIn" ? ( + <div class="top-right"> + {goToBusinessAccount ? ( + <MaybeBusinessButton + account={backend.state.username} + onClick={goToBusinessAccount} + /> + ) : undefined} + <div class="some-space"> + <a + href="#" + class="pure-button logout-button" + onClick={() => { + pageStateSetter((prevState: PageStateType) => { + const { talerWithdrawUri, withdrawalId, ...rest } = + prevState; + backend.logOut(); + return { + ...rest, + withdrawalInProgress: false, + error: undefined, + info: undefined, + isRawPayto: false, + }; + }); + }} + >{i18n.str`Logout`}</a> + </div> + </div> + ) : null} {children} </section> <section id="footer" class="footer"> diff --git a/packages/demobank-ui/src/pages/BusinessAccount.tsx b/packages/demobank-ui/src/pages/BusinessAccount.tsx new file mode 100644 index 000000000..d845c2fa0 --- /dev/null +++ b/packages/demobank-ui/src/pages/BusinessAccount.tsx @@ -0,0 +1,90 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +import { TranslatedString } from "@gnu-taler/taler-util"; +import { + HttpResponsePaginated, + useTranslationContext, +} from "@gnu-taler/web-util/lib/index.browser"; +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { Cashouts } from "../components/Cashouts/index.js"; +import { useBackendContext } from "../context/backend.js"; +import { usePageContext } from "../context/pageState.js"; +import { ShowAccountDetails, UpdateAccountPassword } from "./AdminPage.js"; +import { LoginForm } from "./LoginForm.js"; + +interface Props { + onClose: () => void; + onRegister: () => void; + onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode; +} +export function BusinessAccount({ + onClose, + onLoadNotOk, + onRegister, +}: Props): VNode { + const { i18n } = useTranslationContext(); + const { pageStateSetter } = usePageContext(); + const backend = useBackendContext(); + const [updatePassword, setUpdatePassword] = useState(false); + function showInfoMessage(info: TranslatedString): void { + pageStateSetter((prev) => ({ + ...prev, + info, + })); + } + + if (backend.state.status === "loggedOut") { + return <LoginForm onRegister={onRegister} />; + } + + if (updatePassword) { + return ( + <UpdateAccountPassword + account={backend.state.username} + onLoadNotOk={onLoadNotOk} + onUpdateSuccess={() => { + showInfoMessage(i18n.str`Password changed`); + setUpdatePassword(false); + }} + onClear={() => { + setUpdatePassword(false); + }} + /> + ); + } + return ( + <div> + <ShowAccountDetails + account={backend.state.username} + onLoadNotOk={onLoadNotOk} + onUpdateSuccess={() => { + showInfoMessage(i18n.str`Account updated`); + }} + onChangePassword={() => { + setUpdatePassword(true); + }} + onClear={onClose} + /> + <section style={{ marginTop: "2em" }}> + <div class="active"> + <h3>{i18n.str`Latest cashouts`}</h3> + <Cashouts /> + </div> + </section> + </div> + ); +} diff --git a/packages/demobank-ui/src/pages/HomePage.tsx b/packages/demobank-ui/src/pages/HomePage.tsx index e60732d42..76eb8d515 100644 --- a/packages/demobank-ui/src/pages/HomePage.tsx +++ b/packages/demobank-ui/src/pages/HomePage.tsx @@ -50,6 +50,7 @@ export function HomePage({ onRegister }: { onRegister: () => void }): VNode { } function saveErrorAndLogout(error: PageStateType["error"]): void { + console.log("rrot", error); saveError(error); backend.logOut(); } @@ -123,6 +124,7 @@ function handleNotOkResult( return function handleNotOkResult2<T, E>( result: HttpResponsePaginated<T, E>, ): VNode { + console.log("qweqwe", JSON.stringify(result, undefined, 2)); if (result.clientError && result.isUnauthorized) { onErrorHandler({ title: i18n.str`Wrong credentials for "${account}"`, @@ -139,7 +141,7 @@ function handleNotOkResult( if (!result.ok) { onErrorHandler({ title: i18n.str`The backend reported a problem: HTTP status #${result.status}`, - description: `Diagnostic from ${result.info?.url.href} is "${result.message}"`, + description: `Diagnostic from ${result.info?.url} is "${result.message}"`, debug: JSON.stringify(result.error), }); return <LoginForm onRegister={onRegister} />; diff --git a/packages/demobank-ui/src/pages/Routing.tsx b/packages/demobank-ui/src/pages/Routing.tsx index a88af9b0b..cff561aac 100644 --- a/packages/demobank-ui/src/pages/Routing.tsx +++ b/packages/demobank-ui/src/pages/Routing.tsx @@ -28,6 +28,7 @@ import { HomePage } from "./HomePage.js"; import { BankFrame } from "./BankFrame.js"; import { PublicHistoriesPage } from "./PublicHistoriesPage.js"; import { RegistrationPage } from "./RegistrationPage.js"; +import { BusinessAccount } from "./BusinessAccount.js"; function handleNotOkResult( safe: string, @@ -96,7 +97,11 @@ export function Routing(): VNode { <Route path="/account" component={() => ( - <BankFrame> + <BankFrame + goToBusinessAccount={() => { + route("/business"); + }} + > <HomePage onRegister={() => { route("/register"); @@ -105,6 +110,22 @@ export function Routing(): VNode { </BankFrame> )} /> + <Route + path="/business" + component={() => ( + <BankFrame> + <BusinessAccount + onClose={() => { + route("/account"); + }} + onRegister={() => { + route("/register"); + }} + onLoadNotOk={handleNotOkResult("/account", saveError, i18n)} + /> + </BankFrame> + )} + /> <Route default component={Redirect} to="/account" /> </Router> ); |