centered forms, show balance in accont info

This commit is contained in:
Sebastian 2023-03-06 15:12:43 -03:00
parent 8743db9e40
commit 8f32ad272d
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
2 changed files with 257 additions and 225 deletions

View File

@ -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 {

View File

@ -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>
&nbsp;
<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> &nbsp;
<td>
<a <a
href="#" href="#"
onClick={(e) => { onClick={(e) => {
@ -283,8 +305,7 @@ export function AdminPage({ onLoadNotOk }: Props): VNode {
> >
cashouts cashouts
</a> </a>
</td> &nbsp;
<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>
); );
} }