towards new core bank api
This commit is contained in:
parent
5640f0a67d
commit
15af6c619d
@ -27,6 +27,7 @@ import { Test } from "../pages/Test.js";
|
|||||||
import { useBackendContext } from "../context/backend.js";
|
import { useBackendContext } from "../context/backend.js";
|
||||||
import { LoginForm } from "../pages/LoginForm.js";
|
import { LoginForm } from "../pages/LoginForm.js";
|
||||||
import { AdminHome } from "../pages/admin/Home.js";
|
import { AdminHome } from "../pages/admin/Home.js";
|
||||||
|
import { bankUiSettings } from "../settings.js";
|
||||||
|
|
||||||
export function Routing(): VNode {
|
export function Routing(): VNode {
|
||||||
const history = createHashHistory();
|
const history = createHashHistory();
|
||||||
@ -45,6 +46,10 @@ export function Routing(): VNode {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/public-accounts"
|
||||||
|
component={() => <PublicHistoriesPage />}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/operation/:wopid"
|
path="/operation/:wopid"
|
||||||
component={({ wopid }: { wopid: string }) => (
|
component={({ wopid }: { wopid: string }) => (
|
||||||
@ -53,22 +58,21 @@ export function Routing(): VNode {
|
|||||||
onContinue={() => {
|
onContinue={() => {
|
||||||
route("/account");
|
route("/account");
|
||||||
}}
|
}}
|
||||||
// onLoadNotOk={() => {
|
|
||||||
// route("/account");
|
|
||||||
// }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Route
|
|
||||||
path="/register"
|
|
||||||
component={() => (
|
|
||||||
<RegistrationPage
|
|
||||||
onComplete={() => {
|
|
||||||
route("/account");
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{bankUiSettings.allowRegistrations &&
|
||||||
|
<Route
|
||||||
|
path="/register"
|
||||||
|
component={() => (
|
||||||
|
<RegistrationPage
|
||||||
|
onComplete={() => {
|
||||||
|
route("/account");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
<Route default component={Redirect} to="/login" />
|
<Route default component={Redirect} to="/login" />
|
||||||
</Router>
|
</Router>
|
||||||
</BankFrame>
|
</BankFrame>
|
||||||
@ -93,16 +97,6 @@ export function Routing(): VNode {
|
|||||||
path="/public-accounts"
|
path="/public-accounts"
|
||||||
component={() => <PublicHistoriesPage />}
|
component={() => <PublicHistoriesPage />}
|
||||||
/>
|
/>
|
||||||
<Route
|
|
||||||
path="/register"
|
|
||||||
component={() => (
|
|
||||||
<RegistrationPage
|
|
||||||
onComplete={() => {
|
|
||||||
route("/account");
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Route
|
<Route
|
||||||
path="/account"
|
path="/account"
|
||||||
component={() => {
|
component={() => {
|
||||||
|
166
packages/demobank-ui/src/declaration.d.ts
vendored
166
packages/demobank-ui/src/declaration.d.ts
vendored
@ -99,11 +99,6 @@ 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".
|
||||||
@ -126,7 +121,7 @@ namespace SandboxBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface SandboxError {
|
export interface SandboxError {
|
||||||
error: SandboxErrorDetail;
|
error?: SandboxErrorDetail;
|
||||||
}
|
}
|
||||||
interface SandboxErrorDetail {
|
interface SandboxErrorDetail {
|
||||||
// String enum classifying the error.
|
// String enum classifying the error.
|
||||||
@ -152,26 +147,12 @@ namespace SandboxBackend {
|
|||||||
UtilError = "util-error",
|
UtilError = "util-error",
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Access {
|
|
||||||
interface PublicAccountsResponse {
|
|
||||||
publicAccounts: PublicAccount[];
|
|
||||||
}
|
|
||||||
interface PublicAccount {
|
|
||||||
iban: string;
|
|
||||||
balance: string;
|
|
||||||
// The account name _and_ the username of the
|
|
||||||
// Sandbox customer that owns such a bank account.
|
|
||||||
accountLabel: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BankAccountBalanceResponse {
|
type EmailAddress = string;
|
||||||
// Available balance on the account.
|
type PhoneNumber = string;
|
||||||
balance: Balance;
|
|
||||||
// payto://-URI of the account. (New)
|
namespace CoreBank {
|
||||||
paytoUri: string;
|
|
||||||
// Number indicating the max debit allowed for the requesting user.
|
|
||||||
debitThreshold: Amount;
|
|
||||||
}
|
|
||||||
interface BankAccountCreateWithdrawalRequest {
|
interface BankAccountCreateWithdrawalRequest {
|
||||||
// Amount to withdraw.
|
// Amount to withdraw.
|
||||||
amount: Amount;
|
amount: Amount;
|
||||||
@ -243,11 +224,144 @@ namespace SandboxBackend {
|
|||||||
amount?: string;
|
amount?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BankRegistrationRequest {
|
interface RegisterAccountRequest {
|
||||||
|
// Username
|
||||||
username: string;
|
username: string;
|
||||||
|
|
||||||
|
// Password.
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
|
// Legal name of the account owner
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
// Defaults to false.
|
||||||
|
is_public?: boolean;
|
||||||
|
|
||||||
|
// Is this a taler exchange account?
|
||||||
|
// If true:
|
||||||
|
// - incoming transactions to the account that do not
|
||||||
|
// have a valid reserve public key are automatically
|
||||||
|
// - the account provides the taler-wire-gateway-api endpoints
|
||||||
|
// Defaults to false.
|
||||||
|
is_taler_exchange?: boolean;
|
||||||
|
|
||||||
|
// Addresses where to send the TAN for transactions.
|
||||||
|
// Currently only used for cashouts.
|
||||||
|
// If missing, cashouts will fail.
|
||||||
|
// In the future, might be used for other transactions
|
||||||
|
// as well.
|
||||||
|
challenge_contact_data?: ChallengeContactData;
|
||||||
|
|
||||||
|
// 'payto' address pointing a bank account
|
||||||
|
// external to the libeufin-bank.
|
||||||
|
// Payments will be sent to this bank account
|
||||||
|
// when the user wants to convert the local currency
|
||||||
|
// back to fiat currency outside libeufin-bank.
|
||||||
|
cashout_payto_uri?: string;
|
||||||
|
|
||||||
|
// Internal payto URI of this bank account.
|
||||||
|
// Used mostly for testing.
|
||||||
|
internal_payto_uri?: string;
|
||||||
}
|
}
|
||||||
|
interface ChallengeContactData {
|
||||||
|
|
||||||
|
// E-Mail address
|
||||||
|
email?: EmailAddress;
|
||||||
|
|
||||||
|
// Phone number.
|
||||||
|
phone?: PhoneNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AccountReconfiguration {
|
||||||
|
|
||||||
|
// Addresses where to send the TAN for transactions.
|
||||||
|
// Currently only used for cashouts.
|
||||||
|
// If missing, cashouts will fail.
|
||||||
|
// In the future, might be used for other transactions
|
||||||
|
// as well.
|
||||||
|
challenge_contact_data?: ChallengeContactData;
|
||||||
|
|
||||||
|
// 'payto' address pointing a bank account
|
||||||
|
// external to the libeufin-bank.
|
||||||
|
// Payments will be sent to this bank account
|
||||||
|
// when the user wants to convert the local currency
|
||||||
|
// back to fiat currency outside libeufin-bank.
|
||||||
|
cashout_address?: string;
|
||||||
|
|
||||||
|
// Legal name associated with $username.
|
||||||
|
// When missing, the old name is kept.
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
// If present, change the is_exchange configuration.
|
||||||
|
// See RegisterAccountRequest
|
||||||
|
is_exchange?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface AccountPasswordChange {
|
||||||
|
|
||||||
|
// New password.
|
||||||
|
new_password: string;
|
||||||
|
}
|
||||||
|
interface PublicAccountsResponse {
|
||||||
|
public_accounts: PublicAccount[];
|
||||||
|
}
|
||||||
|
interface PublicAccount {
|
||||||
|
payto_uri: string;
|
||||||
|
|
||||||
|
balance: Balance;
|
||||||
|
|
||||||
|
// The account name (=username) of the
|
||||||
|
// libeufin-bank account.
|
||||||
|
account_name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ListBankAccountsResponse {
|
||||||
|
accounts: AccountMinimalData[];
|
||||||
|
}
|
||||||
|
// interface Balance {
|
||||||
|
// amount: Amount;
|
||||||
|
// credit_debit_indicator: "credit" | "debit";
|
||||||
|
// }
|
||||||
|
type Balance = Amount
|
||||||
|
interface AccountMinimalData {
|
||||||
|
// Username
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
// Legal name of the account owner.
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
// current balance of the account
|
||||||
|
balance: Balance;
|
||||||
|
|
||||||
|
// Number indicating the max debit allowed for the requesting user.
|
||||||
|
debit_threshold: Amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AccountData {
|
||||||
|
// Legal name of the account owner.
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
// Available balance on the account.
|
||||||
|
balance: Balance;
|
||||||
|
|
||||||
|
// payto://-URI of the account.
|
||||||
|
payto_uri: string;
|
||||||
|
|
||||||
|
// Number indicating the max debit allowed for the requesting user.
|
||||||
|
debit_threshold: Amount;
|
||||||
|
|
||||||
|
contact_data?: ChallengeContactData;
|
||||||
|
|
||||||
|
// 'payto' address pointing the bank account
|
||||||
|
// where to send cashouts. This field is optional
|
||||||
|
// because not all the accounts are required to participate
|
||||||
|
// in the merchants' circuit. One example is the exchange:
|
||||||
|
// that never cashouts. Registering these accounts can
|
||||||
|
// be done via the access API.
|
||||||
|
cashout_payto_uri?: string;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Circuit {
|
namespace Circuit {
|
||||||
|
@ -44,13 +44,13 @@ export function useAccessAPI(): AccessAPI {
|
|||||||
const account = state.username;
|
const account = state.username;
|
||||||
|
|
||||||
const createWithdrawal = async (
|
const createWithdrawal = async (
|
||||||
data: SandboxBackend.Access.BankAccountCreateWithdrawalRequest,
|
data: SandboxBackend.CoreBank.BankAccountCreateWithdrawalRequest,
|
||||||
): Promise<
|
): Promise<
|
||||||
HttpResponseOk<SandboxBackend.Access.BankAccountCreateWithdrawalResponse>
|
HttpResponseOk<SandboxBackend.CoreBank.BankAccountCreateWithdrawalResponse>
|
||||||
> => {
|
> => {
|
||||||
const res =
|
const res =
|
||||||
await request<SandboxBackend.Access.BankAccountCreateWithdrawalResponse>(
|
await request<SandboxBackend.CoreBank.BankAccountCreateWithdrawalResponse>(
|
||||||
`access-api/accounts/${account}/withdrawals`,
|
`accounts/${account}/withdrawals`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data,
|
data,
|
||||||
@ -60,10 +60,10 @@ export function useAccessAPI(): AccessAPI {
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
const createTransaction = async (
|
const createTransaction = async (
|
||||||
data: SandboxBackend.Access.CreateBankAccountTransactionCreate,
|
data: SandboxBackend.CoreBank.CreateBankAccountTransactionCreate,
|
||||||
): Promise<HttpResponseOk<void>> => {
|
): Promise<HttpResponseOk<void>> => {
|
||||||
const res = await request<void>(
|
const res = await request<void>(
|
||||||
`access-api/accounts/${account}/transactions`,
|
`accounts/${account}/transactions`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data,
|
data,
|
||||||
@ -74,7 +74,7 @@ export function useAccessAPI(): AccessAPI {
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
const deleteAccount = async (): Promise<HttpResponseOk<void>> => {
|
const deleteAccount = async (): Promise<HttpResponseOk<void>> => {
|
||||||
const res = await request<void>(`access-api/accounts/${account}`, {
|
const res = await request<void>(`accounts/${account}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
contentType: "json",
|
contentType: "json",
|
||||||
});
|
});
|
||||||
@ -94,7 +94,7 @@ export function useAccessAnonAPI(): AccessAnonAPI {
|
|||||||
const { request } = useAuthenticatedBackend();
|
const { request } = useAuthenticatedBackend();
|
||||||
|
|
||||||
const abortWithdrawal = async (id: string): Promise<HttpResponseOk<void>> => {
|
const abortWithdrawal = async (id: string): Promise<HttpResponseOk<void>> => {
|
||||||
const res = await request<void>(`access-api/withdrawals/${id}/abort`, {
|
const res = await request<void>(`accounts/withdrawals/${id}/abort`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
contentType: "json",
|
contentType: "json",
|
||||||
});
|
});
|
||||||
@ -104,7 +104,7 @@ export function useAccessAnonAPI(): AccessAnonAPI {
|
|||||||
const confirmWithdrawal = async (
|
const confirmWithdrawal = async (
|
||||||
id: string,
|
id: string,
|
||||||
): Promise<HttpResponseOk<void>> => {
|
): Promise<HttpResponseOk<void>> => {
|
||||||
const res = await request<void>(`access-api/withdrawals/${id}/confirm`, {
|
const res = await request<void>(`withdrawals/${id}/confirm`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
contentType: "json",
|
contentType: "json",
|
||||||
});
|
});
|
||||||
@ -122,10 +122,10 @@ export function useTestingAPI(): TestingAPI {
|
|||||||
const mutateAll = useMatchMutate();
|
const mutateAll = useMatchMutate();
|
||||||
const { request: noAuthRequest } = usePublicBackend();
|
const { request: noAuthRequest } = usePublicBackend();
|
||||||
const register = async (
|
const register = async (
|
||||||
data: SandboxBackend.Access.BankRegistrationRequest,
|
data: SandboxBackend.CoreBank.RegisterAccountRequest,
|
||||||
): Promise<HttpResponseOk<void>> => {
|
): Promise<HttpResponseOk<void>> => {
|
||||||
// FIXME: This API is deprecated. The normal account registration API should be used instead.
|
// FIXME: This API is deprecated. The normal account registration API should be used instead.
|
||||||
const res = await noAuthRequest<void>(`access-api/testing/register`, {
|
const res = await noAuthRequest<void>(`accounts`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
data,
|
data,
|
||||||
contentType: "json",
|
contentType: "json",
|
||||||
@ -139,18 +139,18 @@ export function useTestingAPI(): TestingAPI {
|
|||||||
|
|
||||||
export interface TestingAPI {
|
export interface TestingAPI {
|
||||||
register: (
|
register: (
|
||||||
data: SandboxBackend.Access.BankRegistrationRequest,
|
data: SandboxBackend.CoreBank.RegisterAccountRequest,
|
||||||
) => Promise<HttpResponseOk<void>>;
|
) => Promise<HttpResponseOk<void>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccessAPI {
|
export interface AccessAPI {
|
||||||
createWithdrawal: (
|
createWithdrawal: (
|
||||||
data: SandboxBackend.Access.BankAccountCreateWithdrawalRequest,
|
data: SandboxBackend.CoreBank.BankAccountCreateWithdrawalRequest,
|
||||||
) => Promise<
|
) => Promise<
|
||||||
HttpResponseOk<SandboxBackend.Access.BankAccountCreateWithdrawalResponse>
|
HttpResponseOk<SandboxBackend.CoreBank.BankAccountCreateWithdrawalResponse>
|
||||||
>;
|
>;
|
||||||
createTransaction: (
|
createTransaction: (
|
||||||
data: SandboxBackend.Access.CreateBankAccountTransactionCreate,
|
data: SandboxBackend.CoreBank.CreateBankAccountTransactionCreate,
|
||||||
) => Promise<HttpResponseOk<void>>;
|
) => Promise<HttpResponseOk<void>>;
|
||||||
deleteAccount: () => Promise<HttpResponseOk<void>>;
|
deleteAccount: () => Promise<HttpResponseOk<void>>;
|
||||||
}
|
}
|
||||||
@ -167,15 +167,15 @@ export interface InstanceTemplateFilter {
|
|||||||
export function useAccountDetails(
|
export function useAccountDetails(
|
||||||
account: string,
|
account: string,
|
||||||
): HttpResponse<
|
): HttpResponse<
|
||||||
SandboxBackend.Access.BankAccountBalanceResponse,
|
SandboxBackend.CoreBank.AccountData,
|
||||||
SandboxBackend.SandboxError
|
SandboxBackend.SandboxError
|
||||||
> {
|
> {
|
||||||
const { fetcher } = useAuthenticatedBackend();
|
const { fetcher } = useAuthenticatedBackend();
|
||||||
|
|
||||||
const { data, error } = useSWR<
|
const { data, error } = useSWR<
|
||||||
HttpResponseOk<SandboxBackend.Access.BankAccountBalanceResponse>,
|
HttpResponseOk<SandboxBackend.CoreBank.AccountData>,
|
||||||
RequestError<SandboxBackend.SandboxError>
|
RequestError<SandboxBackend.SandboxError>
|
||||||
>([`access-api/accounts/${account}`], fetcher, {
|
>([`accounts/${account}`], fetcher, {
|
||||||
refreshInterval: 0,
|
refreshInterval: 0,
|
||||||
refreshWhenHidden: false,
|
refreshWhenHidden: false,
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
@ -187,28 +187,8 @@ export function useAccountDetails(
|
|||||||
keepPreviousData: true,
|
keepPreviousData: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
//FIXME: remove optional when libeufin sandbox has implemented the feature
|
|
||||||
if (data && typeof data.data.debitThreshold === "undefined") {
|
|
||||||
data.data.debitThreshold = "0";
|
|
||||||
}
|
|
||||||
//FIXME: sandbox server should return amount string
|
|
||||||
if (data) {
|
if (data) {
|
||||||
const isAmount = Amounts.parse(data.data.debitThreshold);
|
return data;
|
||||||
if (isAmount) {
|
|
||||||
//server response with correct format
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
const { currency } = Amounts.parseOrThrow(data.data.balance.amount);
|
|
||||||
const clone = structuredClone(data);
|
|
||||||
|
|
||||||
const theNumber = Number.parseInt(data.data.debitThreshold, 10);
|
|
||||||
const value = Number.isNaN(theNumber) ? 0 : theNumber;
|
|
||||||
clone.data.debitThreshold = Amounts.stringify({
|
|
||||||
currency,
|
|
||||||
value: value,
|
|
||||||
fraction: 0,
|
|
||||||
});
|
|
||||||
return clone;
|
|
||||||
}
|
}
|
||||||
if (error) return error.cause;
|
if (error) return error.cause;
|
||||||
return { loading: true };
|
return { loading: true };
|
||||||
@ -218,15 +198,15 @@ export function useAccountDetails(
|
|||||||
export function useWithdrawalDetails(
|
export function useWithdrawalDetails(
|
||||||
wid: string,
|
wid: string,
|
||||||
): HttpResponse<
|
): HttpResponse<
|
||||||
SandboxBackend.Access.BankAccountGetWithdrawalResponse,
|
SandboxBackend.CoreBank.BankAccountGetWithdrawalResponse,
|
||||||
SandboxBackend.SandboxError
|
SandboxBackend.SandboxError
|
||||||
> {
|
> {
|
||||||
const { fetcher } = useAuthenticatedBackend();
|
const { fetcher } = useAuthenticatedBackend();
|
||||||
|
|
||||||
const { data, error } = useSWR<
|
const { data, error } = useSWR<
|
||||||
HttpResponseOk<SandboxBackend.Access.BankAccountGetWithdrawalResponse>,
|
HttpResponseOk<SandboxBackend.CoreBank.BankAccountGetWithdrawalResponse>,
|
||||||
RequestError<SandboxBackend.SandboxError>
|
RequestError<SandboxBackend.SandboxError>
|
||||||
>([`access-api/withdrawals/${wid}`], fetcher, {
|
>([`withdrawals/${wid}`], fetcher, {
|
||||||
refreshInterval: 1000,
|
refreshInterval: 1000,
|
||||||
refreshWhenHidden: false,
|
refreshWhenHidden: false,
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
@ -248,15 +228,15 @@ export function useTransactionDetails(
|
|||||||
account: string,
|
account: string,
|
||||||
tid: string,
|
tid: string,
|
||||||
): HttpResponse<
|
): HttpResponse<
|
||||||
SandboxBackend.Access.BankAccountTransactionInfo,
|
SandboxBackend.CoreBank.BankAccountTransactionInfo,
|
||||||
SandboxBackend.SandboxError
|
SandboxBackend.SandboxError
|
||||||
> {
|
> {
|
||||||
const { fetcher } = useAuthenticatedBackend();
|
const { paginatedFetcher } = useAuthenticatedBackend();
|
||||||
|
|
||||||
const { data, error } = useSWR<
|
const { data, error } = useSWR<
|
||||||
HttpResponseOk<SandboxBackend.Access.BankAccountTransactionInfo>,
|
HttpResponseOk<SandboxBackend.CoreBank.BankAccountTransactionInfo>,
|
||||||
RequestError<SandboxBackend.SandboxError>
|
RequestError<SandboxBackend.SandboxError>
|
||||||
>([`access-api/accounts/${account}/transactions/${tid}`], fetcher, {
|
>([`accounts/${account}/transactions/${tid}`], paginatedFetcher, {
|
||||||
refreshInterval: 0,
|
refreshInterval: 0,
|
||||||
refreshWhenHidden: false,
|
refreshWhenHidden: false,
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
@ -281,7 +261,7 @@ interface PaginationFilter {
|
|||||||
export function usePublicAccounts(
|
export function usePublicAccounts(
|
||||||
args?: PaginationFilter,
|
args?: PaginationFilter,
|
||||||
): HttpResponsePaginated<
|
): HttpResponsePaginated<
|
||||||
SandboxBackend.Access.PublicAccountsResponse,
|
SandboxBackend.CoreBank.PublicAccountsResponse,
|
||||||
SandboxBackend.SandboxError
|
SandboxBackend.SandboxError
|
||||||
> {
|
> {
|
||||||
const { paginatedFetcher } = usePublicBackend();
|
const { paginatedFetcher } = usePublicBackend();
|
||||||
@ -293,13 +273,13 @@ export function usePublicAccounts(
|
|||||||
error: afterError,
|
error: afterError,
|
||||||
isValidating: loadingAfter,
|
isValidating: loadingAfter,
|
||||||
} = useSWR<
|
} = useSWR<
|
||||||
HttpResponseOk<SandboxBackend.Access.PublicAccountsResponse>,
|
HttpResponseOk<SandboxBackend.CoreBank.PublicAccountsResponse>,
|
||||||
RequestError<SandboxBackend.SandboxError>
|
RequestError<SandboxBackend.SandboxError>
|
||||||
>([`access-api/public-accounts`, args?.page, PAGE_SIZE], paginatedFetcher);
|
>([`public-accounts`, args?.page, PAGE_SIZE], paginatedFetcher);
|
||||||
|
|
||||||
const [lastAfter, setLastAfter] = useState<
|
const [lastAfter, setLastAfter] = useState<
|
||||||
HttpResponse<
|
HttpResponse<
|
||||||
SandboxBackend.Access.PublicAccountsResponse,
|
SandboxBackend.CoreBank.PublicAccountsResponse,
|
||||||
SandboxBackend.SandboxError
|
SandboxBackend.SandboxError
|
||||||
>
|
>
|
||||||
>({ loading: true });
|
>({ loading: true });
|
||||||
@ -312,7 +292,7 @@ export function usePublicAccounts(
|
|||||||
|
|
||||||
// if the query returns less that we ask, then we have reach the end or beginning
|
// if the query returns less that we ask, then we have reach the end or beginning
|
||||||
const isReachingEnd =
|
const isReachingEnd =
|
||||||
afterData && afterData.data.publicAccounts.length < PAGE_SIZE;
|
afterData && afterData.data.public_accounts.length < PAGE_SIZE;
|
||||||
const isReachingStart = false;
|
const isReachingStart = false;
|
||||||
|
|
||||||
const pagination = {
|
const pagination = {
|
||||||
@ -320,7 +300,7 @@ export function usePublicAccounts(
|
|||||||
isReachingStart,
|
isReachingStart,
|
||||||
loadMore: () => {
|
loadMore: () => {
|
||||||
if (!afterData || isReachingEnd) return;
|
if (!afterData || isReachingEnd) return;
|
||||||
if (afterData.data.publicAccounts.length < MAX_RESULT_SIZE) {
|
if (afterData.data.public_accounts.length < MAX_RESULT_SIZE) {
|
||||||
setPage(page + 1);
|
setPage(page + 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -329,12 +309,12 @@ export function usePublicAccounts(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const publicAccounts = !afterData
|
const public_accounts = !afterData
|
||||||
? []
|
? []
|
||||||
: (afterData || lastAfter).data.publicAccounts;
|
: (afterData || lastAfter).data.public_accounts;
|
||||||
if (loadingAfter) return { loading: true, data: { publicAccounts } };
|
if (loadingAfter) return { loading: true, data: { public_accounts } };
|
||||||
if (afterData) {
|
if (afterData) {
|
||||||
return { ok: true, data: { publicAccounts }, ...pagination };
|
return { ok: true, data: { public_accounts }, ...pagination };
|
||||||
}
|
}
|
||||||
return { loading: true };
|
return { loading: true };
|
||||||
}
|
}
|
||||||
@ -349,7 +329,7 @@ export function useTransactions(
|
|||||||
account: string,
|
account: string,
|
||||||
args?: PaginationFilter,
|
args?: PaginationFilter,
|
||||||
): HttpResponsePaginated<
|
): HttpResponsePaginated<
|
||||||
SandboxBackend.Access.BankAccountTransactionsResponse,
|
SandboxBackend.CoreBank.BankAccountTransactionsResponse,
|
||||||
SandboxBackend.SandboxError
|
SandboxBackend.SandboxError
|
||||||
> {
|
> {
|
||||||
const { paginatedFetcher } = useAuthenticatedBackend();
|
const { paginatedFetcher } = useAuthenticatedBackend();
|
||||||
@ -361,10 +341,10 @@ export function useTransactions(
|
|||||||
error: afterError,
|
error: afterError,
|
||||||
isValidating: loadingAfter,
|
isValidating: loadingAfter,
|
||||||
} = useSWR<
|
} = useSWR<
|
||||||
HttpResponseOk<SandboxBackend.Access.BankAccountTransactionsResponse>,
|
HttpResponseOk<SandboxBackend.CoreBank.BankAccountTransactionsResponse>,
|
||||||
RequestError<SandboxBackend.SandboxError>
|
RequestError<SandboxBackend.SandboxError>
|
||||||
>(
|
>(
|
||||||
[`access-api/accounts/${account}/transactions`, args?.page, PAGE_SIZE],
|
[`accounts/${account}/transactions`, args?.page, PAGE_SIZE],
|
||||||
paginatedFetcher, {
|
paginatedFetcher, {
|
||||||
refreshInterval: 0,
|
refreshInterval: 0,
|
||||||
refreshWhenHidden: false,
|
refreshWhenHidden: false,
|
||||||
@ -378,7 +358,7 @@ export function useTransactions(
|
|||||||
|
|
||||||
const [lastAfter, setLastAfter] = useState<
|
const [lastAfter, setLastAfter] = useState<
|
||||||
HttpResponse<
|
HttpResponse<
|
||||||
SandboxBackend.Access.BankAccountTransactionsResponse,
|
SandboxBackend.CoreBank.BankAccountTransactionsResponse,
|
||||||
SandboxBackend.SandboxError
|
SandboxBackend.SandboxError
|
||||||
>
|
>
|
||||||
>({ loading: true });
|
>({ loading: true });
|
||||||
|
@ -310,7 +310,7 @@ export function useAuthenticatedBackend(): useBackendType {
|
|||||||
]): Promise<HttpResponseOk<T>> {
|
]): Promise<HttpResponseOk<T>> {
|
||||||
return requestHandler<T>(baseUrl, endpoint, {
|
return requestHandler<T>(baseUrl, endpoint, {
|
||||||
basicAuth: creds,
|
basicAuth: creds,
|
||||||
params: { page, size },
|
params: { delta: size, start: size * page },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[baseUrl, creds],
|
[baseUrl, creds],
|
||||||
|
@ -9,20 +9,25 @@ export function useCredentialsChecker() {
|
|||||||
//while merchant backend doesn't have a login endpoint
|
//while merchant backend doesn't have a login endpoint
|
||||||
async function requestNewLoginToken(
|
async function requestNewLoginToken(
|
||||||
username: string,
|
username: string,
|
||||||
password: AccessToken,
|
password: string,
|
||||||
): Promise<LoginResult> {
|
): Promise<LoginResult> {
|
||||||
const data: LoginTokenRequest = {
|
const data: LoginTokenRequest = {
|
||||||
scope: "write",
|
scope: "readwrite" as "write", //FIX: different than merchant
|
||||||
duration: {
|
duration: {
|
||||||
d_us: "forever"
|
// d_us: "forever" //FIX: should return shortest
|
||||||
|
d_us: 1000 * 60 * 60 * 23
|
||||||
},
|
},
|
||||||
refreshable: true,
|
refreshable: true,
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await request<LoginTokenSuccessResponse>(baseUrl, `accounts/${username}/token`, {
|
const response = await request<LoginTokenSuccessResponse>(baseUrl, `accounts/${username}/token`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
token: password,
|
basicAuth: {
|
||||||
data
|
username: username,
|
||||||
|
password,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
contentType: "json"
|
||||||
});
|
});
|
||||||
return { valid: true, token: response.data.token, expiration: response.data.expiration };
|
return { valid: true, token: response.data.token, expiration: response.data.expiration };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -60,7 +60,7 @@ export namespace State {
|
|||||||
|
|
||||||
export interface InvalidIban {
|
export interface InvalidIban {
|
||||||
status: "invalid-iban",
|
status: "invalid-iban",
|
||||||
error: HttpResponseOk<SandboxBackend.Access.BankAccountBalanceResponse>;
|
error: HttpResponseOk<SandboxBackend.CoreBank.AccountData>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserNotFound {
|
export interface UserNotFound {
|
||||||
|
@ -40,7 +40,7 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
//logout if there is any error, not if loading
|
//logout if there is any error, not if loading
|
||||||
backend.logOut();
|
// backend.logOut();
|
||||||
if (result.status === HttpStatusCode.NotFound) {
|
if (result.status === HttpStatusCode.NotFound) {
|
||||||
notifyError(i18n.str`Username or account label "${account}" not found`, undefined);
|
notifyError(i18n.str`Username or account label "${account}" not found`, undefined);
|
||||||
return {
|
return {
|
||||||
@ -55,9 +55,13 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { data } = result;
|
const { data } = result;
|
||||||
const balance = Amounts.parseOrThrow(data.balance.amount);
|
|
||||||
const debitThreshold = Amounts.parseOrThrow(data.debitThreshold);
|
// FIXME: balance
|
||||||
const payto = parsePaytoUri(data.paytoUri);
|
// const balance = Amounts.parseOrThrow(data.balance.amount);
|
||||||
|
const balance = Amounts.parseOrThrow(data.balance);
|
||||||
|
|
||||||
|
const debitThreshold = Amounts.parseOrThrow(data.debit_threshold);
|
||||||
|
const payto = parsePaytoUri(data.payto_uri);
|
||||||
|
|
||||||
if (!payto || !payto.isKnown || (payto.targetType !== "iban" && payto.targetType !== "x-taler-bank")) {
|
if (!payto || !payto.isKnown || (payto.targetType !== "iban" && payto.targetType !== "x-taler-bank")) {
|
||||||
return {
|
return {
|
||||||
@ -66,7 +70,9 @@ export function useComponentState({ account, goToBusinessAccount, goToConfirmOpe
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const balanceIsDebit = data.balance.credit_debit_indicator == "debit";
|
// FIXME: balance
|
||||||
|
const balanceIsDebit = true;
|
||||||
|
// data.balance.credit_debit_indicator == "debit";
|
||||||
const limit = balanceIsDebit
|
const limit = balanceIsDebit
|
||||||
? Amounts.sub(debitThreshold, balance).amount
|
? Amounts.sub(debitThreshold, balance).amount
|
||||||
: Amounts.add(balance, debitThreshold).amount;
|
: Amounts.add(balance, debitThreshold).amount;
|
||||||
|
@ -27,7 +27,7 @@ import { useSettings } from "../../hooks/settings.js";
|
|||||||
|
|
||||||
export function InvalidIbanView({ error }: State.InvalidIban) {
|
export function InvalidIbanView({ error }: State.InvalidIban) {
|
||||||
return (
|
return (
|
||||||
<div>Payto from server is not valid "{error.data.paytoUri}"</div>
|
<div>Payto from server is not valid "{error.data.payto_uri}"</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,15 +458,14 @@ function WelcomeAccount({ account }: { account: string }): VNode {
|
|||||||
const result = useAccountDetails(account);
|
const result = useAccountDetails(account);
|
||||||
if (!result.ok) return <div />
|
if (!result.ok) return <div />
|
||||||
|
|
||||||
// const account = "Sebastian"
|
const payto = parsePaytoUri(result.data.payto_uri)
|
||||||
const payto = parsePaytoUri(result.data.paytoUri)
|
|
||||||
if (!payto) return <div />
|
if (!payto) return <div />
|
||||||
|
|
||||||
const accountNumber = !payto.isKnown ? undefined : payto.targetType === "iban" ? payto.iban : payto.targetType === "x-taler-bank" ? payto.account : undefined;
|
const accountNumber = !payto.isKnown ? undefined : payto.targetType === "iban" ? payto.iban : payto.targetType === "x-taler-bank" ? payto.account : undefined;
|
||||||
return <i18n.Translate>
|
return <i18n.Translate>
|
||||||
Welcome, {account} {accountNumber !== undefined ?
|
Welcome, {account} {accountNumber !== undefined ?
|
||||||
<span>
|
<span>
|
||||||
(<a href={result.data.paytoUri}>{accountNumber}</a> <CopyButton getContent={() => result.data.paytoUri} />)
|
(<a href={result.data.payto_uri}>{accountNumber}</a> <CopyButton getContent={() => result.data.payto_uri} />)
|
||||||
</span>
|
</span>
|
||||||
: <Fragment />}!
|
: <Fragment />}!
|
||||||
</i18n.Translate>
|
</i18n.Translate>
|
||||||
@ -477,9 +476,12 @@ function AccountBalance({ account }: { account: string }): VNode {
|
|||||||
const result = useAccountDetails(account);
|
const result = useAccountDetails(account);
|
||||||
if (!result.ok) return <div />
|
if (!result.ok) return <div />
|
||||||
|
|
||||||
|
// FIXME: balance
|
||||||
return <div>
|
return <div>
|
||||||
{Amounts.currencyOf(result.data.balance.amount)}
|
{Amounts.currencyOf(result.data.balance)}
|
||||||
|
{Amounts.stringifyValue(result.data.balance)}
|
||||||
|
{/* {Amounts.currencyOf(result.data.balance.amount)}
|
||||||
{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""}
|
{result.data.balance.credit_debit_indicator === "debit" ? "-" : ""}
|
||||||
{Amounts.stringifyValue(result.data.balance.amount)}
|
{Amounts.stringifyValue(result.data.balance.amount)} */}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -120,8 +120,8 @@ export function handleNotOkResult(
|
|||||||
) => VNode {
|
) => VNode {
|
||||||
return function handleNotOkResult2<T>(
|
return function handleNotOkResult2<T>(
|
||||||
result:
|
result:
|
||||||
| HttpResponsePaginated<T, SandboxBackend.SandboxError>
|
| HttpResponsePaginated<T, SandboxBackend.SandboxError | undefined>
|
||||||
| HttpResponse<T, SandboxBackend.SandboxError>,
|
| HttpResponse<T, SandboxBackend.SandboxError| undefined>,
|
||||||
): VNode {
|
): VNode {
|
||||||
if (result.loading) return <Loading />;
|
if (result.loading) return <Loading />;
|
||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
@ -139,7 +139,7 @@ export function handleNotOkResult(
|
|||||||
notify({
|
notify({
|
||||||
type: "error",
|
type: "error",
|
||||||
title: i18n.str`Could not load due to a client error`,
|
title: i18n.str`Could not load due to a client error`,
|
||||||
description: errorData.error.description as TranslatedString,
|
description: errorData?.error?.description as TranslatedString,
|
||||||
debug: JSON.stringify(result),
|
debug: JSON.stringify(result),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@ -148,7 +148,7 @@ export function handleNotOkResult(
|
|||||||
notify({
|
notify({
|
||||||
type: "error",
|
type: "error",
|
||||||
title: i18n.str`Server returned with error`,
|
title: i18n.str`Server returned with error`,
|
||||||
description: result.payload.error.description as TranslatedString,
|
description: result.payload?.error?.description as TranslatedString,
|
||||||
debug: JSON.stringify(result.payload),
|
debug: JSON.stringify(result.payload),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -31,12 +31,13 @@ import { useCredentialsCheckerOld } from "../hooks/backend.js";
|
|||||||
*/
|
*/
|
||||||
export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
|
export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
|
||||||
const backend = useBackendContext();
|
const backend = useBackendContext();
|
||||||
const [username, setUsername] = useState<string | undefined>();
|
const currentUser = backend.state.status === "loggedIn" ? backend.state.username : undefined
|
||||||
|
const [username, setUsername] = useState<string | undefined>(currentUser);
|
||||||
const [password, setPassword] = useState<string | undefined>();
|
const [password, setPassword] = useState<string | undefined>();
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
// const { requestNewLoginToken, refreshLoginToken } = useCredentialsChecker();
|
const { requestNewLoginToken, refreshLoginToken } = useCredentialsChecker();
|
||||||
|
|
||||||
const testLogin = useCredentialsCheckerOld();
|
// const testLogin = useCredentialsCheckerOld();
|
||||||
const ref = useRef<HTMLInputElement>(null);
|
const ref = useRef<HTMLInputElement>(null);
|
||||||
useEffect(function focusInput() {
|
useEffect(function focusInput() {
|
||||||
ref.current?.focus();
|
ref.current?.focus();
|
||||||
@ -46,8 +47,8 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
|
|||||||
const errors = undefinedIfEmpty({
|
const errors = undefinedIfEmpty({
|
||||||
username: !username
|
username: !username
|
||||||
? i18n.str`Missing username`
|
? i18n.str`Missing username`
|
||||||
: !USERNAME_REGEX.test(username)
|
// : !USERNAME_REGEX.test(username)
|
||||||
? i18n.str`Use letters and numbers only, and start with a lowercase letter`
|
// ? i18n.str`Use letters and numbers only, and start with a lowercase letter`
|
||||||
: undefined,
|
: undefined,
|
||||||
password: !password ? i18n.str`Missing password` : undefined,
|
password: !password ? i18n.str`Missing password` : undefined,
|
||||||
}) ?? busy;
|
}) ?? busy;
|
||||||
@ -59,19 +60,18 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
|
|||||||
async function doLogin() {
|
async function doLogin() {
|
||||||
if (!username || !password) return;
|
if (!username || !password) return;
|
||||||
setBusy({})
|
setBusy({})
|
||||||
const testResult = await testLogin(username, password);
|
const result = await requestNewLoginToken(username, password);
|
||||||
if (testResult.valid) {
|
if (result.valid) {
|
||||||
backend.logIn({ username, password });
|
backend.logIn({ username, password });
|
||||||
} else {
|
} else {
|
||||||
if (testResult.requestError) {
|
const { cause } = result;
|
||||||
const { cause } = testResult;
|
switch (cause.type) {
|
||||||
switch (cause.type) {
|
case ErrorType.CLIENT: {
|
||||||
case ErrorType.CLIENT: {
|
if (cause.status === HttpStatusCode.Unauthorized) {
|
||||||
if (cause.status === HttpStatusCode.Unauthorized) {
|
saveError({
|
||||||
saveError({
|
title: i18n.str`Wrong credentials for "${username}"`,
|
||||||
title: i18n.str`Wrong credentials for "${username}"`,
|
});
|
||||||
});
|
} else
|
||||||
} else
|
|
||||||
if (cause.status === HttpStatusCode.NotFound) {
|
if (cause.status === HttpStatusCode.NotFound) {
|
||||||
saveError({
|
saveError({
|
||||||
title: i18n.str`Account not found`,
|
title: i18n.str`Account not found`,
|
||||||
@ -79,50 +79,44 @@ export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
|
|||||||
} else {
|
} else {
|
||||||
saveError({
|
saveError({
|
||||||
title: i18n.str`Could not load due to a client error`,
|
title: i18n.str`Could not load due to a client error`,
|
||||||
description: cause.payload.error.description,
|
// description: cause.payload.error.description,
|
||||||
debug: JSON.stringify(cause.payload),
|
debug: JSON.stringify(cause.payload),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ErrorType.SERVER: {
|
case ErrorType.SERVER: {
|
||||||
saveError({
|
saveError({
|
||||||
title: i18n.str`Server had a problem, try again later or report.`,
|
title: i18n.str`Server had a problem, try again later or report.`,
|
||||||
description: cause.payload.error.description,
|
// description: cause.payload.error.description,
|
||||||
debug: JSON.stringify(cause.payload),
|
debug: JSON.stringify(cause.payload),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ErrorType.TIMEOUT: {
|
case ErrorType.TIMEOUT: {
|
||||||
saveError({
|
saveError({
|
||||||
title: i18n.str`Request timeout, try again later.`,
|
title: i18n.str`Request timeout, try again later.`,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ErrorType.UNREADABLE: {
|
case ErrorType.UNREADABLE: {
|
||||||
saveError({
|
saveError({
|
||||||
title: i18n.str`Unexpected error.`,
|
title: i18n.str`Unexpected error.`,
|
||||||
description: `Response from ${cause.info?.url} is unreadable, http status: ${cause.status}` as TranslatedString,
|
description: `Response from ${cause.info?.url} is unreadable, http status: ${cause.status}` as TranslatedString,
|
||||||
debug: JSON.stringify(cause),
|
debug: JSON.stringify(cause),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
saveError({
|
saveError({
|
||||||
title: i18n.str`Unexpected error, please report.`,
|
title: i18n.str`Unexpected error, please report.`,
|
||||||
description: `Diagnostic from ${cause.info?.url} is "${cause.message}"` as TranslatedString,
|
description: `Diagnostic from ${cause.info?.url} is "${cause.message}"` as TranslatedString,
|
||||||
debug: JSON.stringify(cause),
|
debug: JSON.stringify(cause),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
saveError({
|
|
||||||
title: i18n.str`Unexpected error, please report.`,
|
|
||||||
debug: JSON.stringify(testResult.error),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
backend.logOut();
|
// backend.logOut();
|
||||||
}
|
}
|
||||||
setPassword(undefined);
|
setPassword(undefined);
|
||||||
setBusy(undefined)
|
setBusy(undefined)
|
||||||
|
@ -36,8 +36,8 @@ export function PublicHistoriesPage({}: Props): VNode {
|
|||||||
const result = usePublicAccounts();
|
const result = usePublicAccounts();
|
||||||
|
|
||||||
const [showAccount, setShowAccount] = useState(
|
const [showAccount, setShowAccount] = useState(
|
||||||
result.ok && result.data.publicAccounts.length > 0
|
result.ok && result.data.public_accounts.length > 0
|
||||||
? result.data.publicAccounts[0].accountLabel
|
? result.data.public_accounts[0].account_name
|
||||||
: undefined,
|
: undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -51,9 +51,9 @@ export function PublicHistoriesPage({}: Props): VNode {
|
|||||||
const accountsBar = [];
|
const accountsBar = [];
|
||||||
|
|
||||||
// Ask story of all the public accounts.
|
// Ask story of all the public accounts.
|
||||||
for (const account of data.publicAccounts) {
|
for (const account of data.public_accounts) {
|
||||||
logger.trace("Asking transactions for", account.accountLabel);
|
logger.trace("Asking transactions for", account.account_name);
|
||||||
const isSelected = account.accountLabel == showAccount;
|
const isSelected = account.account_name == showAccount;
|
||||||
accountsBar.push(
|
accountsBar.push(
|
||||||
<li
|
<li
|
||||||
class={
|
class={
|
||||||
@ -65,13 +65,13 @@ export function PublicHistoriesPage({}: Props): VNode {
|
|||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
class="pure-menu-link"
|
class="pure-menu-link"
|
||||||
onClick={() => setShowAccount(account.accountLabel)}
|
onClick={() => setShowAccount(account.account_name)}
|
||||||
>
|
>
|
||||||
{account.accountLabel}
|
{account.account_name}
|
||||||
</a>
|
</a>
|
||||||
</li>,
|
</li>,
|
||||||
);
|
);
|
||||||
txs[account.accountLabel] = <Transactions account={account.accountLabel} />;
|
txs[account.account_name] = <Transactions account={account.account_name} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -53,6 +53,7 @@ export const USERNAME_REGEX = /^[a-z][a-zA-Z0-9-]*$/;
|
|||||||
function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
|
function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
|
||||||
const backend = useBackendContext();
|
const backend = useBackendContext();
|
||||||
const [username, setUsername] = useState<string | undefined>();
|
const [username, setUsername] = useState<string | undefined>();
|
||||||
|
const [name, setName] = useState<string | undefined>();
|
||||||
const [password, setPassword] = useState<string | undefined>();
|
const [password, setPassword] = useState<string | undefined>();
|
||||||
const [repeatPassword, setRepeatPassword] = useState<string | undefined>();
|
const [repeatPassword, setRepeatPassword] = useState<string | undefined>();
|
||||||
|
|
||||||
@ -60,6 +61,9 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
|
|||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
|
|
||||||
const errors = undefinedIfEmpty({
|
const errors = undefinedIfEmpty({
|
||||||
|
name: !name
|
||||||
|
? i18n.str`Missing name`
|
||||||
|
: undefined,
|
||||||
username: !username
|
username: !username
|
||||||
? i18n.str`Missing username`
|
? i18n.str`Missing username`
|
||||||
: !USERNAME_REGEX.test(username)
|
: !USERNAME_REGEX.test(username)
|
||||||
@ -74,9 +78,9 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
|
|||||||
});
|
});
|
||||||
|
|
||||||
async function doRegistrationStep() {
|
async function doRegistrationStep() {
|
||||||
if (!username || !password) return;
|
if (!username || !password || !name) return;
|
||||||
try {
|
try {
|
||||||
await register({ username, password });
|
await register({ name, username, password });
|
||||||
setUsername(undefined);
|
setUsername(undefined);
|
||||||
backend.logIn({ username, password });
|
backend.logIn({ username, password });
|
||||||
onComplete();
|
onComplete();
|
||||||
@ -97,13 +101,13 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
|
|||||||
? error.message
|
? error.message
|
||||||
: JSON.stringify(error)) as TranslatedString
|
: JSON.stringify(error)) as TranslatedString
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setPassword(undefined);
|
setPassword(undefined);
|
||||||
setRepeatPassword(undefined);
|
setRepeatPassword(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function delay(ms: number):Promise<void> {
|
async function delay(ms: number): Promise<void> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resolve(undefined);
|
resolve(undefined);
|
||||||
@ -117,14 +121,15 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
|
|||||||
setUsername(undefined);
|
setUsername(undefined);
|
||||||
setPassword(undefined);
|
setPassword(undefined);
|
||||||
setRepeatPassword(undefined);
|
setRepeatPassword(undefined);
|
||||||
await register({ username: user, password: pass });
|
const username = `_${user.first}-${user.second}_`
|
||||||
backend.logIn({ username: user, password: pass });
|
await register({ username, name: `${user.first} ${user.second}`, password: pass });
|
||||||
|
backend.logIn({ username, password: pass });
|
||||||
onComplete();
|
onComplete();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof RequestError) {
|
if (error instanceof RequestError) {
|
||||||
if (tries > 0) {
|
if (tries > 0) {
|
||||||
await delay(200)
|
await delay(200)
|
||||||
await doRandomRegistration(tries-1)
|
await doRandomRegistration(tries - 1)
|
||||||
} else {
|
} else {
|
||||||
notify(
|
notify(
|
||||||
buildRequestErrorMessage(i18n, error.cause, {
|
buildRequestErrorMessage(i18n, error.cause, {
|
||||||
@ -142,7 +147,7 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
|
|||||||
? error.message
|
? error.message
|
||||||
: JSON.stringify(error)) as TranslatedString
|
: JSON.stringify(error)) as TranslatedString
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,17 +262,19 @@ function RegistrationForm({ onComplete }: { onComplete: () => void }): VNode {
|
|||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<p class="mt-10 text-center text-sm text-gray-500 border-t">
|
{bankUiSettings.allowRandomAccountCreation &&
|
||||||
<button type="submit"
|
<p class="mt-10 text-center text-sm text-gray-500 border-t">
|
||||||
class="flex mt-4 w-full justify-center rounded-md bg-green-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
|
<button type="submit"
|
||||||
onClick={(e) => {
|
class="flex mt-4 w-full justify-center rounded-md bg-green-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600"
|
||||||
e.preventDefault()
|
onClick={(e) => {
|
||||||
doRandomRegistration()
|
e.preventDefault()
|
||||||
}}
|
doRandomRegistration()
|
||||||
>
|
}}
|
||||||
<i18n.Translate>Create a random user</i18n.Translate>
|
>
|
||||||
</button>
|
<i18n.Translate>Create a random user</i18n.Translate>
|
||||||
</p>
|
</button>
|
||||||
|
</p>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -59,27 +59,41 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, onCancel, focus }: {
|
|||||||
}, [focus]);
|
}, [focus]);
|
||||||
|
|
||||||
if (!!settings.currentWithdrawalOperationId) {
|
if (!!settings.currentWithdrawalOperationId) {
|
||||||
return <div class="rounded-md bg-yellow-50 ring-yellow-2 p-4">
|
return <div>
|
||||||
<div class="flex">
|
|
||||||
<div class="flex-shrink-0">
|
<div class="rounded-md bg-yellow-50 ring-yellow-2 p-4">
|
||||||
<svg class="h-5 w-5 text-yellow-300" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
<div class="flex">
|
||||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
|
<div class="flex-shrink-0">
|
||||||
</svg>
|
<svg class="h-5 w-5 text-yellow-300" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||||
</div>
|
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd" />
|
||||||
<div class="ml-3">
|
</svg>
|
||||||
<h3 class="text-sm font-bold text-yellow-800">
|
</div>
|
||||||
<i18n.Translate>There is an operation already</i18n.Translate>
|
<div class="ml-3">
|
||||||
</h3>
|
<h3 class="text-sm font-bold text-yellow-800">
|
||||||
<div class="mt-2 text-sm text-yellow-700">
|
<i18n.Translate>There is an operation already</i18n.Translate>
|
||||||
<p>
|
</h3>
|
||||||
<i18n.Translate>
|
<div class="mt-2 text-sm text-yellow-700">
|
||||||
To complete or cancel the operation click <a class="font-semibold text-yellow-700 hover:text-yellow-600" href={`#/operation/${settings.currentWithdrawalOperationId}`}>here</a>
|
<p>
|
||||||
</i18n.Translate>
|
<i18n.Translate>
|
||||||
</p>
|
To complete or cancel the operation click <a class="font-semibold text-yellow-700 hover:text-yellow-600" href={`#/operation/${settings.currentWithdrawalOperationId}`}>here</a>
|
||||||
|
</i18n.Translate>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div >
|
||||||
|
<div class="flex justify-end gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8 " >
|
||||||
|
<button type="button" class="text-sm font-semibold leading-6 text-gray-900 bg-white p-2 rounded-sm"
|
||||||
|
onClick={() => {
|
||||||
|
updateSettings("currentWithdrawalOperationId", undefined)
|
||||||
|
onCancel()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i18n.Translate>Cancel</i18n.Translate>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div >
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const trimmedAmountStr = amountStr?.trim();
|
const trimmedAmountStr = amountStr?.trim();
|
||||||
|
@ -16,9 +16,14 @@ export function AdminAccount({ onRegister }: { onRegister: () => void }): VNode
|
|||||||
return handleNotOkResult(i18n, onRegister)(result);
|
return handleNotOkResult(i18n, onRegister)(result);
|
||||||
}
|
}
|
||||||
const { data } = result;
|
const { data } = result;
|
||||||
const balance = Amounts.parseOrThrow(data.balance.amount);
|
|
||||||
const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
|
//FIXME: libeufin does not follow the spec
|
||||||
const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
|
const balance = Amounts.parseOrThrow(data.balance);
|
||||||
|
const balanceIsDebit = true;
|
||||||
|
// const balance = Amounts.parseOrThrow(data.balance.amount);
|
||||||
|
// const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
|
||||||
|
|
||||||
|
const debitThreshold = Amounts.parseOrThrow(result.data.debit_threshold);
|
||||||
const limit = balanceIsDebit
|
const limit = balanceIsDebit
|
||||||
? Amounts.sub(debitThreshold, balance).amount
|
? Amounts.sub(debitThreshold, balance).amount
|
||||||
: Amounts.add(balance, debitThreshold).amount;
|
: Amounts.add(balance, debitThreshold).amount;
|
||||||
|
@ -41,7 +41,9 @@ export function RemoveAccount({
|
|||||||
if (focus) ref.current?.focus();
|
if (focus) ref.current?.focus();
|
||||||
}, [focus]);
|
}, [focus]);
|
||||||
|
|
||||||
const balance = Amounts.parse(result.data.balance.amount);
|
//FIXME: libeufin does not follow the spec
|
||||||
|
const balance = Amounts.parse(result.data.balance);
|
||||||
|
// const balance = Amounts.parse(result.data.balance.amount);
|
||||||
if (!balance) {
|
if (!balance) {
|
||||||
return <div>there was an error reading the balance</div>;
|
return <div>there was an error reading the balance</div>;
|
||||||
}
|
}
|
||||||
|
@ -201,13 +201,13 @@ function useRatiosAndFeeConfigWithChangeDetection(): HttpResponse<
|
|||||||
(result.data.name !== oldResult.name ||
|
(result.data.name !== oldResult.name ||
|
||||||
result.data.version !== oldResult.version ||
|
result.data.version !== oldResult.version ||
|
||||||
result.data.ratios_and_fees.buy_at_ratio !==
|
result.data.ratios_and_fees.buy_at_ratio !==
|
||||||
oldResult.ratios_and_fees.buy_at_ratio ||
|
oldResult.ratios_and_fees.buy_at_ratio ||
|
||||||
result.data.ratios_and_fees.buy_in_fee !==
|
result.data.ratios_and_fees.buy_in_fee !==
|
||||||
oldResult.ratios_and_fees.buy_in_fee ||
|
oldResult.ratios_and_fees.buy_in_fee ||
|
||||||
result.data.ratios_and_fees.sell_at_ratio !==
|
result.data.ratios_and_fees.sell_at_ratio !==
|
||||||
oldResult.ratios_and_fees.sell_at_ratio ||
|
oldResult.ratios_and_fees.sell_at_ratio ||
|
||||||
result.data.ratios_and_fees.sell_out_fee !==
|
result.data.ratios_and_fees.sell_out_fee !==
|
||||||
oldResult.ratios_and_fees.sell_out_fee ||
|
oldResult.ratios_and_fees.sell_out_fee ||
|
||||||
result.data.fiat_currency !== oldResult.fiat_currency);
|
result.data.fiat_currency !== oldResult.fiat_currency);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -236,10 +236,14 @@ function CreateCashout({
|
|||||||
if (!ratiosResult.ok) return onLoadNotOk(ratiosResult);
|
if (!ratiosResult.ok) return onLoadNotOk(ratiosResult);
|
||||||
const config = ratiosResult.data;
|
const config = ratiosResult.data;
|
||||||
|
|
||||||
const balance = Amounts.parseOrThrow(result.data.balance.amount);
|
//FIXME: libeufin does not follow the spec
|
||||||
const debitThreshold = Amounts.parseOrThrow(result.data.debitThreshold);
|
const balance = Amounts.parseOrThrow(result.data.balance);
|
||||||
|
const balanceIsDebit = true;
|
||||||
|
// const balance = Amounts.parseOrThrow(result.data.balance.amount);
|
||||||
|
// const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
|
||||||
|
|
||||||
|
const debitThreshold = Amounts.parseOrThrow(result.data.debit_threshold);
|
||||||
const zero = Amounts.zeroOfCurrency(balance.currency);
|
const zero = Amounts.zeroOfCurrency(balance.currency);
|
||||||
const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
|
|
||||||
const limit = balanceIsDebit
|
const limit = balanceIsDebit
|
||||||
? Amounts.sub(debitThreshold, balance).amount
|
? Amounts.sub(debitThreshold, balance).amount
|
||||||
: Amounts.add(balance, debitThreshold).amount;
|
: Amounts.add(balance, debitThreshold).amount;
|
||||||
@ -250,15 +254,14 @@ function CreateCashout({
|
|||||||
const sellFee = !config.ratios_and_fees.sell_out_fee
|
const sellFee = !config.ratios_and_fees.sell_out_fee
|
||||||
? zero
|
? zero
|
||||||
: Amounts.parseOrThrow(
|
: Amounts.parseOrThrow(
|
||||||
`${balance.currency}:${config.ratios_and_fees.sell_out_fee}`,
|
`${balance.currency}:${config.ratios_and_fees.sell_out_fee}`,
|
||||||
);
|
);
|
||||||
const fiatCurrency = config.fiat_currency;
|
const fiatCurrency = config.fiat_currency;
|
||||||
|
|
||||||
if (!sellRate || sellRate < 0) return <div>error rate</div>;
|
if (!sellRate || sellRate < 0) return <div>error rate</div>;
|
||||||
|
|
||||||
const amount = Amounts.parseOrThrow(
|
const amount = Amounts.parseOrThrow(
|
||||||
`${!form.isDebit ? fiatCurrency : balance.currency}:${
|
`${!form.isDebit ? fiatCurrency : balance.currency}:${!form.amount ? "0" : form.amount
|
||||||
!form.amount ? "0" : form.amount
|
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -273,10 +276,10 @@ function CreateCashout({
|
|||||||
error instanceof RequestError
|
error instanceof RequestError
|
||||||
? buildRequestErrorMessage(i18n, error.cause)
|
? buildRequestErrorMessage(i18n, error.cause)
|
||||||
: {
|
: {
|
||||||
type: "error",
|
type: "error",
|
||||||
title: i18n.str`Could not estimate the cashout`,
|
title: i18n.str`Could not estimate the cashout`,
|
||||||
description: error.message as TranslatedString
|
description: error.message as TranslatedString
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -289,10 +292,10 @@ function CreateCashout({
|
|||||||
error instanceof RequestError
|
error instanceof RequestError
|
||||||
? buildRequestErrorMessage(i18n, error.cause)
|
? buildRequestErrorMessage(i18n, error.cause)
|
||||||
: {
|
: {
|
||||||
type: "error",
|
type: "error",
|
||||||
title: i18n.str`Could not estimate the cashout`,
|
title: i18n.str`Could not estimate the cashout`,
|
||||||
description: error.message,
|
description: error.message,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -307,14 +310,14 @@ function CreateCashout({
|
|||||||
amount: !form.amount
|
amount: !form.amount
|
||||||
? i18n.str`required`
|
? i18n.str`required`
|
||||||
: !amount
|
: !amount
|
||||||
? i18n.str`could not be parsed`
|
? i18n.str`could not be parsed`
|
||||||
: Amounts.cmp(limit, calc.debit) === -1
|
: Amounts.cmp(limit, calc.debit) === -1
|
||||||
? i18n.str`balance is not enough`
|
? i18n.str`balance is not enough`
|
||||||
: Amounts.cmp(calc.beforeFee, sellFee) === -1
|
: Amounts.cmp(calc.beforeFee, sellFee) === -1
|
||||||
? i18n.str`the total amount to transfer does not cover the fees`
|
? i18n.str`the total amount to transfer does not cover the fees`
|
||||||
: Amounts.isZero(calc.credit)
|
: Amounts.isZero(calc.credit)
|
||||||
? i18n.str`the total transfer at destination will be zero`
|
? i18n.str`the total transfer at destination will be zero`
|
||||||
: undefined,
|
: undefined,
|
||||||
channel: !form.channel ? i18n.str`required` : undefined,
|
channel: !form.channel ? i18n.str`required` : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -341,7 +344,7 @@ function CreateCashout({
|
|||||||
{form.isDebit
|
{form.isDebit
|
||||||
? i18n.str`Amount to send`
|
? i18n.str`Amount to send`
|
||||||
: i18n.str`Amount to receive`}
|
: i18n.str`Amount to receive`}
|
||||||
|
|
||||||
</label>
|
</label>
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }}>
|
||||||
<Amount
|
<Amount
|
||||||
@ -520,12 +523,12 @@ function CreateCashout({
|
|||||||
status === HttpStatusCode.BadRequest
|
status === HttpStatusCode.BadRequest
|
||||||
? i18n.str`The exchange rate was incorrectly applied`
|
? i18n.str`The exchange rate was incorrectly applied`
|
||||||
: status === HttpStatusCode.Forbidden
|
: status === HttpStatusCode.Forbidden
|
||||||
? i18n.str`A institutional user tried the operation`
|
? i18n.str`A institutional user tried the operation`
|
||||||
: status === HttpStatusCode.Conflict
|
: status === HttpStatusCode.Conflict
|
||||||
? i18n.str`Need a contact data where to send the TAN`
|
? i18n.str`Need a contact data where to send the TAN`
|
||||||
: status === HttpStatusCode.PreconditionFailed
|
: status === HttpStatusCode.PreconditionFailed
|
||||||
? i18n.str`The account does not have sufficient funds`
|
? i18n.str`The account does not have sufficient funds`
|
||||||
: undefined,
|
: undefined,
|
||||||
onServerError: (status) =>
|
onServerError: (status) =>
|
||||||
status === HttpStatusCode.ServiceUnavailable
|
status === HttpStatusCode.ServiceUnavailable
|
||||||
? i18n.str`The bank does not support the TAN channel for this operation`
|
? i18n.str`The bank does not support the TAN channel for this operation`
|
||||||
@ -539,7 +542,7 @@ function CreateCashout({
|
|||||||
? error.message
|
? error.message
|
||||||
: JSON.stringify(error)) as TranslatedString
|
: JSON.stringify(error)) as TranslatedString
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -665,8 +668,8 @@ export function ShowCashoutDetails({
|
|||||||
status === HttpStatusCode.NotFound
|
status === HttpStatusCode.NotFound
|
||||||
? i18n.str`Cashout not found. It may be also mean that it was already aborted.`
|
? i18n.str`Cashout not found. It may be also mean that it was already aborted.`
|
||||||
: status === HttpStatusCode.PreconditionFailed
|
: status === HttpStatusCode.PreconditionFailed
|
||||||
? i18n.str`Cashout was already confimed`
|
? i18n.str`Cashout was already confimed`
|
||||||
: undefined,
|
: undefined,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -676,7 +679,7 @@ export function ShowCashoutDetails({
|
|||||||
? error.message
|
? error.message
|
||||||
: JSON.stringify(error)) as TranslatedString
|
: JSON.stringify(error)) as TranslatedString
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -702,12 +705,12 @@ export function ShowCashoutDetails({
|
|||||||
status === HttpStatusCode.NotFound
|
status === HttpStatusCode.NotFound
|
||||||
? i18n.str`Cashout not found. It may be also mean that it was already aborted.`
|
? i18n.str`Cashout not found. It may be also mean that it was already aborted.`
|
||||||
: status === HttpStatusCode.PreconditionFailed
|
: status === HttpStatusCode.PreconditionFailed
|
||||||
? i18n.str`Cashout was already confimed`
|
? i18n.str`Cashout was already confimed`
|
||||||
: status === HttpStatusCode.Conflict
|
: status === HttpStatusCode.Conflict
|
||||||
? i18n.str`Confirmation failed. Maybe the user changed their cash-out address between the creation and the confirmation`
|
? i18n.str`Confirmation failed. Maybe the user changed their cash-out address between the creation and the confirmation`
|
||||||
: status === HttpStatusCode.Forbidden
|
: status === HttpStatusCode.Forbidden
|
||||||
? i18n.str`Invalid code`
|
? i18n.str`Invalid code`
|
||||||
: undefined,
|
: undefined,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -717,7 +720,7 @@ export function ShowCashoutDetails({
|
|||||||
? error.message
|
? error.message
|
||||||
: JSON.stringify(error)) as TranslatedString
|
: JSON.stringify(error)) as TranslatedString
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,8 @@ export interface BankUiSettings {
|
|||||||
backendBaseURL: string;
|
backendBaseURL: string;
|
||||||
allowRegistrations: boolean;
|
allowRegistrations: boolean;
|
||||||
showDemoNav: boolean;
|
showDemoNav: boolean;
|
||||||
|
simplePasswordForRandomAccounts: boolean;
|
||||||
|
allowRandomAccountCreation: boolean;
|
||||||
bankName: string;
|
bankName: string;
|
||||||
demoSites: [string, string][];
|
demoSites: [string, string][];
|
||||||
}
|
}
|
||||||
@ -30,6 +32,8 @@ const defaultSettings: BankUiSettings = {
|
|||||||
allowRegistrations: true,
|
allowRegistrations: true,
|
||||||
bankName: "Taler Bank",
|
bankName: "Taler Bank",
|
||||||
showDemoNav: true,
|
showDemoNav: true,
|
||||||
|
simplePasswordForRandomAccounts: true,
|
||||||
|
allowRandomAccountCreation: true,
|
||||||
demoSites: [
|
demoSites: [
|
||||||
["Landing", "https://demo.taler.net/"],
|
["Landing", "https://demo.taler.net/"],
|
||||||
["Bank", "https://bank.demo.taler.net/"],
|
["Bank", "https://bank.demo.taler.net/"],
|
||||||
|
Loading…
Reference in New Issue
Block a user