aboutsummaryrefslogtreecommitdiff
path: root/packages/demobank-ui/src/pages/AdminPage.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/demobank-ui/src/pages/AdminPage.tsx')
-rw-r--r--packages/demobank-ui/src/pages/AdminPage.tsx150
1 files changed, 149 insertions, 1 deletions
diff --git a/packages/demobank-ui/src/pages/AdminPage.tsx b/packages/demobank-ui/src/pages/AdminPage.tsx
index f8efddd80..d15ac02c4 100644
--- a/packages/demobank-ui/src/pages/AdminPage.tsx
+++ b/packages/demobank-ui/src/pages/AdminPage.tsx
@@ -14,7 +14,11 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { parsePaytoUri, TranslatedString } from "@gnu-taler/taler-util";
+import {
+ Amounts,
+ parsePaytoUri,
+ TranslatedString,
+} from "@gnu-taler/taler-util";
import {
HttpResponsePaginated,
RequestError,
@@ -22,7 +26,9 @@ import {
} from "@gnu-taler/web-util/lib/index.browser";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
+import { Cashouts } from "../components/Cashouts/index.js";
import { ErrorMessage, usePageContext } from "../context/pageState.js";
+import { useAccountDetails } from "../hooks/access.js";
import {
useBusinessAccountDetails,
useBusinessAccounts,
@@ -60,7 +66,10 @@ interface Props {
export function AdminPage({ onLoadNotOk }: Props): VNode {
const [account, setAccount] = useState<string | undefined>();
const [showDetails, setShowDetails] = useState<string | undefined>();
+ const [showCashouts, setShowCashouts] = useState<string | undefined>();
const [updatePassword, setUpdatePassword] = useState<string | undefined>();
+ const [removeAccount, setRemoveAccount] = useState<string | undefined>();
+
const [createAccount, setCreateAccount] = useState(false);
const { pageStateSetter } = usePageContext();
@@ -81,6 +90,23 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
const { customers } = result.data;
+ if (showCashouts) {
+ return (
+ <div>
+ <Cashouts account={showCashouts} />
+ <input
+ class="pure-button"
+ type="submit"
+ value={i18n.str`Close`}
+ onClick={async (e) => {
+ e.preventDefault();
+ setShowCashouts(undefined);
+ }}
+ />
+ </div>
+ );
+ }
+
if (showDetails) {
return (
<ShowAccountDetails
@@ -100,6 +126,21 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
/>
);
}
+ if (removeAccount) {
+ return (
+ <RemoveAccount
+ account={removeAccount}
+ onLoadNotOk={onLoadNotOk}
+ onUpdateSuccess={() => {
+ showInfoMessage(i18n.str`Account removed`);
+ setRemoveAccount(undefined);
+ }}
+ onClear={() => {
+ setRemoveAccount(undefined);
+ }}
+ />
+ );
+ }
if (updatePassword) {
return (
<UpdateAccountPassword
@@ -164,6 +205,7 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
<th>{i18n.str`Username`}</th>
<th>{i18n.str`Name`}</th>
<th></th>
+ <th></th>
</tr>
</thead>
<tbody>
@@ -193,6 +235,28 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
change password
</a>
</td>
+ <td>
+ <a
+ href="#"
+ onClick={(e) => {
+ e.preventDefault();
+ setShowCashouts(item.username);
+ }}
+ >
+ cashouts
+ </a>
+ </td>
+ <td>
+ <a
+ href="#"
+ onClick={(e) => {
+ e.preventDefault();
+ setRemoveAccount(item.username);
+ }}
+ >
+ remove
+ </a>
+ </td>
</tr>
);
})}
@@ -536,6 +600,90 @@ export function ShowAccountDetails({
);
}
+function RemoveAccount({
+ account,
+ onClear,
+ onUpdateSuccess,
+ onLoadNotOk,
+}: {
+ onLoadNotOk: <T, E>(error: HttpResponsePaginated<T, E>) => VNode;
+ onClear: () => void;
+ onUpdateSuccess: () => void;
+ account: string;
+}): VNode {
+ const { i18n } = useTranslationContext();
+ const result = useAccountDetails(account);
+ const { deleteAccount } = useAdminAccountAPI();
+ const [error, saveError] = useState<ErrorMessage | undefined>();
+
+ if (result.clientError) {
+ if (result.isNotfound) return <div>account not found</div>;
+ }
+ if (!result.ok) {
+ return onLoadNotOk(result);
+ }
+
+ const balance = Amounts.parse(result.data.balance.amount);
+ if (!balance) {
+ return <div>there was an error reading the balance</div>;
+ }
+ const isBalanceEmpty = Amounts.isZero(balance);
+ return (
+ <div>
+ <div>
+ <h1 class="nav welcome-text">
+ <i18n.Translate>Remove account: {account}</i18n.Translate>
+ </h1>
+ </div>
+ {!isBalanceEmpty && (
+ <ErrorBanner
+ error={{
+ title: i18n.str`Can't delete the account`,
+ description: i18n.str`Balance is not empty`,
+ }}
+ />
+ )}
+ {error && (
+ <ErrorBanner error={error} onClear={() => saveError(undefined)} />
+ )}
+
+ <p>
+ <div style={{ display: "flex", justifyContent: "space-between" }}>
+ <div>
+ <input
+ class="pure-button"
+ type="submit"
+ value={i18n.str`Cancel`}
+ onClick={async (e) => {
+ e.preventDefault();
+ onClear();
+ }}
+ />
+ </div>
+ <div>
+ <input
+ id="select-exchange"
+ class="pure-button pure-button-primary content"
+ disabled={!isBalanceEmpty}
+ type="submit"
+ value={i18n.str`Confirm`}
+ onClick={async (e) => {
+ e.preventDefault();
+ try {
+ const r = await deleteAccount(account);
+ onUpdateSuccess();
+ } catch (error) {
+ handleError(error, saveError, i18n);
+ }
+ }}
+ />
+ </div>
+ </div>
+ </p>
+ </div>
+ );
+}
+
/**
* Create valid account object to update or create
* Take template as initial values for the form