centered forms, show balance in accont info
This commit is contained in:
parent
8743db9e40
commit
8f32ad272d
13
packages/demobank-ui/src/declaration.d.ts
vendored
13
packages/demobank-ui/src/declaration.d.ts
vendored
@ -99,6 +99,11 @@ type Amount = string;
|
|||||||
type UUID = string;
|
type UUID = string;
|
||||||
type Integer = number;
|
type Integer = number;
|
||||||
|
|
||||||
|
interface Balance {
|
||||||
|
amount: Amount;
|
||||||
|
credit_debit_indicator: "credit" | "debit";
|
||||||
|
}
|
||||||
|
|
||||||
namespace SandboxBackend {
|
namespace SandboxBackend {
|
||||||
export interface Config {
|
export interface Config {
|
||||||
// Name of this API, always "circuit".
|
// Name of this API, always "circuit".
|
||||||
@ -161,10 +166,7 @@ namespace SandboxBackend {
|
|||||||
|
|
||||||
interface BankAccountBalanceResponse {
|
interface BankAccountBalanceResponse {
|
||||||
// Available balance on the account.
|
// Available balance on the account.
|
||||||
balance: {
|
balance: Balance;
|
||||||
amount: Amount;
|
|
||||||
credit_debit_indicator: "credit" | "debit";
|
|
||||||
};
|
|
||||||
// payto://-URI of the account. (New)
|
// payto://-URI of the account. (New)
|
||||||
paytoUri: string;
|
paytoUri: string;
|
||||||
}
|
}
|
||||||
@ -304,6 +306,9 @@ namespace SandboxBackend {
|
|||||||
|
|
||||||
// Legal subject owning the account.
|
// Legal subject owning the account.
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
// current balance of the account
|
||||||
|
balance: Balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CircuitAccountData {
|
interface CircuitAccountData {
|
||||||
|
@ -230,7 +230,10 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
|
|||||||
</div>
|
</div>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<section id="main">
|
<section
|
||||||
|
id="main"
|
||||||
|
style={{ width: 600, marginLeft: "auto", marginRight: "auto" }}
|
||||||
|
>
|
||||||
{!customers.length ? (
|
{!customers.length ? (
|
||||||
<div></div>
|
<div></div>
|
||||||
) : (
|
) : (
|
||||||
@ -242,12 +245,18 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
|
|||||||
<tr>
|
<tr>
|
||||||
<th>{i18n.str`Username`}</th>
|
<th>{i18n.str`Username`}</th>
|
||||||
<th>{i18n.str`Name`}</th>
|
<th>{i18n.str`Name`}</th>
|
||||||
<th></th>
|
<th>{i18n.str`Balance`}</th>
|
||||||
<th></th>
|
<th>{i18n.str`Actions`}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{customers.map((item, idx) => {
|
{customers.map((item, idx) => {
|
||||||
|
const balance = !item.balance
|
||||||
|
? undefined
|
||||||
|
: Amounts.parse(item.balance.amount);
|
||||||
|
const balanceIsDebit =
|
||||||
|
item.balance &&
|
||||||
|
item.balance.credit_debit_indicator == "debit";
|
||||||
return (
|
return (
|
||||||
<tr key={idx}>
|
<tr key={idx}>
|
||||||
<td>
|
<td>
|
||||||
@ -262,6 +271,20 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{item.name}</td>
|
<td>{item.name}</td>
|
||||||
|
<td>
|
||||||
|
{!balance ? (
|
||||||
|
i18n.str`unknown`
|
||||||
|
) : (
|
||||||
|
<span class="amount">
|
||||||
|
{balanceIsDebit ? <b>-</b> : null}
|
||||||
|
<span class="value">{`${Amounts.stringifyValue(
|
||||||
|
balance,
|
||||||
|
)}`}</span>
|
||||||
|
|
||||||
|
<span class="currency">{`${balance.currency}`}</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
@ -272,8 +295,7 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
|
|||||||
>
|
>
|
||||||
change password
|
change password
|
||||||
</a>
|
</a>
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
@ -283,8 +305,7 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
|
|||||||
>
|
>
|
||||||
cashouts
|
cashouts
|
||||||
</a>
|
</a>
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
@ -384,82 +405,84 @@ export function UpdateAccountPassword({
|
|||||||
<ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
|
<ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<form class="pure-form">
|
<div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
|
||||||
<fieldset>
|
<form class="pure-form">
|
||||||
<label>{i18n.str`Password`}</label>
|
<fieldset>
|
||||||
<input
|
<label>{i18n.str`Password`}</label>
|
||||||
type="password"
|
|
||||||
value={password ?? ""}
|
|
||||||
onChange={(e) => {
|
|
||||||
setPassword(e.currentTarget.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ShowInputErrorLabel
|
|
||||||
message={errors?.password}
|
|
||||||
isDirty={password !== undefined}
|
|
||||||
/>
|
|
||||||
</fieldset>
|
|
||||||
<fieldset>
|
|
||||||
<label>{i18n.str`Repeast password`}</label>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
value={repeat ?? ""}
|
|
||||||
onChange={(e) => {
|
|
||||||
setRepeat(e.currentTarget.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ShowInputErrorLabel
|
|
||||||
message={errors?.repeat}
|
|
||||||
isDirty={repeat !== undefined}
|
|
||||||
/>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
<p>
|
|
||||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
|
||||||
<div>
|
|
||||||
<input
|
<input
|
||||||
class="pure-button"
|
type="password"
|
||||||
type="submit"
|
value={password ?? ""}
|
||||||
value={i18n.str`Close`}
|
onChange={(e) => {
|
||||||
onClick={async (e) => {
|
setPassword(e.currentTarget.value);
|
||||||
e.preventDefault();
|
|
||||||
onClear();
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
<ShowInputErrorLabel
|
||||||
<div>
|
message={errors?.password}
|
||||||
|
isDirty={password !== undefined}
|
||||||
|
/>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<label>{i18n.str`Repeast password`}</label>
|
||||||
<input
|
<input
|
||||||
id="select-exchange"
|
type="password"
|
||||||
class="pure-button pure-button-primary content"
|
value={repeat ?? ""}
|
||||||
disabled={!!errors}
|
onChange={(e) => {
|
||||||
type="submit"
|
setRepeat(e.currentTarget.value);
|
||||||
value={i18n.str`Confirm`}
|
}}
|
||||||
onClick={async (e) => {
|
/>
|
||||||
e.preventDefault();
|
<ShowInputErrorLabel
|
||||||
if (!!errors || !password) return;
|
message={errors?.repeat}
|
||||||
try {
|
isDirty={repeat !== undefined}
|
||||||
const r = await changePassword(account, {
|
/>
|
||||||
new_password: password,
|
</fieldset>
|
||||||
});
|
</form>
|
||||||
onUpdateSuccess();
|
<p>
|
||||||
} catch (error) {
|
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||||
if (error instanceof RequestError) {
|
<div>
|
||||||
saveError(buildRequestErrorMessage(i18n, error.cause));
|
<input
|
||||||
} else {
|
class="pure-button"
|
||||||
saveError({
|
type="submit"
|
||||||
title: i18n.str`Operation failed, please report`,
|
value={i18n.str`Close`}
|
||||||
description:
|
onClick={async (e) => {
|
||||||
error instanceof Error
|
e.preventDefault();
|
||||||
? error.message
|
onClear();
|
||||||
: JSON.stringify(error),
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
id="select-exchange"
|
||||||
|
class="pure-button pure-button-primary content"
|
||||||
|
disabled={!!errors}
|
||||||
|
type="submit"
|
||||||
|
value={i18n.str`Confirm`}
|
||||||
|
onClick={async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!!errors || !password) return;
|
||||||
|
try {
|
||||||
|
const r = await changePassword(account, {
|
||||||
|
new_password: password,
|
||||||
});
|
});
|
||||||
|
onUpdateSuccess();
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof RequestError) {
|
||||||
|
saveError(buildRequestErrorMessage(i18n, error.cause));
|
||||||
|
} else {
|
||||||
|
saveError({
|
||||||
|
title: i18n.str`Operation failed, please report`,
|
||||||
|
description:
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: JSON.stringify(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -488,81 +511,83 @@ function CreateNewAccount({
|
|||||||
<ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
|
<ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<AccountForm
|
<div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
|
||||||
template={undefined}
|
<AccountForm
|
||||||
purpose="create"
|
template={undefined}
|
||||||
onChange={(a) => {
|
purpose="create"
|
||||||
console.log(a);
|
onChange={(a) => {
|
||||||
setSubmitAccount(a);
|
console.log(a);
|
||||||
}}
|
setSubmitAccount(a);
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
class="pure-button"
|
class="pure-button"
|
||||||
type="submit"
|
type="submit"
|
||||||
value={i18n.str`Close`}
|
value={i18n.str`Close`}
|
||||||
onClick={async (e) => {
|
onClick={async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onClose();
|
onClose();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
id="select-exchange"
|
id="select-exchange"
|
||||||
class="pure-button pure-button-primary content"
|
class="pure-button pure-button-primary content"
|
||||||
disabled={!submitAccount}
|
disabled={!submitAccount}
|
||||||
type="submit"
|
type="submit"
|
||||||
value={i18n.str`Confirm`}
|
value={i18n.str`Confirm`}
|
||||||
onClick={async (e) => {
|
onClick={async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (!submitAccount) return;
|
if (!submitAccount) return;
|
||||||
try {
|
try {
|
||||||
const account: SandboxBackend.Circuit.CircuitAccountRequest =
|
const account: SandboxBackend.Circuit.CircuitAccountRequest =
|
||||||
{
|
{
|
||||||
cashout_address: submitAccount.cashout_address,
|
cashout_address: submitAccount.cashout_address,
|
||||||
contact_data: submitAccount.contact_data,
|
contact_data: submitAccount.contact_data,
|
||||||
internal_iban: submitAccount.iban,
|
internal_iban: submitAccount.iban,
|
||||||
name: submitAccount.name,
|
name: submitAccount.name,
|
||||||
username: submitAccount.username,
|
username: submitAccount.username,
|
||||||
password: randomPassword(),
|
password: randomPassword(),
|
||||||
};
|
};
|
||||||
|
|
||||||
await createAccount(account);
|
await createAccount(account);
|
||||||
onCreateSuccess(account.password);
|
onCreateSuccess(account.password);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof RequestError) {
|
if (error instanceof RequestError) {
|
||||||
saveError(
|
saveError(
|
||||||
buildRequestErrorMessage(i18n, error.cause, {
|
buildRequestErrorMessage(i18n, error.cause, {
|
||||||
onClientError: (status) =>
|
onClientError: (status) =>
|
||||||
status === HttpStatusCode.Forbidden
|
status === HttpStatusCode.Forbidden
|
||||||
? i18n.str`The rights to perform the operation are not sufficient`
|
? i18n.str`The rights to perform the operation are not sufficient`
|
||||||
: status === HttpStatusCode.BadRequest
|
: status === HttpStatusCode.BadRequest
|
||||||
? i18n.str`Input data was invalid`
|
? i18n.str`Input data was invalid`
|
||||||
: status === HttpStatusCode.Conflict
|
: status === HttpStatusCode.Conflict
|
||||||
? i18n.str`At least one registration detail was not available`
|
? i18n.str`At least one registration detail was not available`
|
||||||
: undefined,
|
: undefined,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
saveError({
|
saveError({
|
||||||
title: i18n.str`Operation failed, please report`,
|
title: i18n.str`Operation failed, please report`,
|
||||||
description:
|
description:
|
||||||
error instanceof Error
|
error instanceof Error
|
||||||
? error.message
|
? error.message
|
||||||
: JSON.stringify(error),
|
: JSON.stringify(error),
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -608,90 +633,92 @@ export function ShowAccountDetails({
|
|||||||
{error && (
|
{error && (
|
||||||
<ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
|
<ErrorBannerFloat error={error} onClear={() => saveError(undefined)} />
|
||||||
)}
|
)}
|
||||||
<AccountForm
|
<div style={{ maxWidth: 600, overflowX: "hidden", margin: "auto" }}>
|
||||||
template={result.data}
|
<AccountForm
|
||||||
purpose={update ? "update" : "show"}
|
template={result.data}
|
||||||
onChange={(a) => setSubmitAccount(a)}
|
purpose={update ? "update" : "show"}
|
||||||
/>
|
onChange={(a) => setSubmitAccount(a)}
|
||||||
|
/>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||||
<div>
|
|
||||||
{onClear ? (
|
|
||||||
<input
|
|
||||||
class="pure-button"
|
|
||||||
type="submit"
|
|
||||||
value={i18n.str`Close`}
|
|
||||||
onClick={async (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
onClear();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : undefined}
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex" }}>
|
|
||||||
<div>
|
<div>
|
||||||
<input
|
{onClear ? (
|
||||||
id="select-exchange"
|
<input
|
||||||
class="pure-button pure-button-primary content"
|
class="pure-button"
|
||||||
disabled={update && !submitAccount}
|
type="submit"
|
||||||
type="submit"
|
value={i18n.str`Close`}
|
||||||
value={i18n.str`Change password`}
|
onClick={async (e) => {
|
||||||
onClick={async (e) => {
|
e.preventDefault();
|
||||||
e.preventDefault();
|
onClear();
|
||||||
onChangePassword();
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
) : undefined}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div style={{ display: "flex" }}>
|
||||||
<input
|
<div>
|
||||||
id="select-exchange"
|
<input
|
||||||
class="pure-button pure-button-primary content"
|
id="select-exchange"
|
||||||
disabled={update && !submitAccount}
|
class="pure-button pure-button-primary content"
|
||||||
type="submit"
|
disabled={update && !submitAccount}
|
||||||
value={update ? i18n.str`Confirm` : i18n.str`Update`}
|
type="submit"
|
||||||
onClick={async (e) => {
|
value={i18n.str`Change password`}
|
||||||
e.preventDefault();
|
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) {
|
if (!update) {
|
||||||
setUpdate(true);
|
setUpdate(true);
|
||||||
} else {
|
} else {
|
||||||
if (!submitAccount) return;
|
if (!submitAccount) return;
|
||||||
try {
|
try {
|
||||||
await updateAccount(account, {
|
await updateAccount(account, {
|
||||||
cashout_address: submitAccount.cashout_address,
|
cashout_address: submitAccount.cashout_address,
|
||||||
contact_data: submitAccount.contact_data,
|
contact_data: submitAccount.contact_data,
|
||||||
});
|
|
||||||
onUpdateSuccess();
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof RequestError) {
|
|
||||||
saveError(
|
|
||||||
buildRequestErrorMessage(i18n, error.cause, {
|
|
||||||
onClientError: (status) =>
|
|
||||||
status === HttpStatusCode.Forbidden
|
|
||||||
? i18n.str`The rights to change the account are not sufficient`
|
|
||||||
: status === HttpStatusCode.NotFound
|
|
||||||
? i18n.str`The username was not found`
|
|
||||||
: undefined,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
saveError({
|
|
||||||
title: i18n.str`Operation failed, please report`,
|
|
||||||
description:
|
|
||||||
error instanceof Error
|
|
||||||
? error.message
|
|
||||||
: JSON.stringify(error),
|
|
||||||
});
|
});
|
||||||
|
onUpdateSuccess();
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof RequestError) {
|
||||||
|
saveError(
|
||||||
|
buildRequestErrorMessage(i18n, error.cause, {
|
||||||
|
onClientError: (status) =>
|
||||||
|
status === HttpStatusCode.Forbidden
|
||||||
|
? i18n.str`The rights to change the account are not sufficient`
|
||||||
|
: status === HttpStatusCode.NotFound
|
||||||
|
? i18n.str`The username was not found`
|
||||||
|
: undefined,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
saveError({
|
||||||
|
title: i18n.str`Operation failed, please report`,
|
||||||
|
description:
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: JSON.stringify(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user