[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-merchant-backoffice] branch master updated: better error handling
From: |
gnunet |
Subject: |
[taler-merchant-backoffice] branch master updated: better error handling |
Date: |
Fri, 02 Apr 2021 22:23:57 +0200 |
This is an automated email from the git hooks/post-receive script.
sebasjm pushed a commit to branch master
in repository merchant-backoffice.
The following commit(s) were added to refs/heads/master by this push:
new bf8b800 better error handling
bf8b800 is described below
commit bf8b800bf1ee2fdb206a1ecfaba59a1f0031b55e
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Fri Apr 2 17:23:10 2021 -0300
better error handling
---
CHANGELOG.md | 2 +
packages/frontend/src/ApplicationReadyRoutes.tsx | 80 +++-----
packages/frontend/src/InstanceRoutes.tsx | 47 ++---
packages/frontend/src/declaration.d.ts | 39 ++++
packages/frontend/src/hooks/backend.ts | 224 +++++++++++++++------
packages/frontend/src/hooks/instance.ts | 19 +-
packages/frontend/src/hooks/order.ts | 137 +++++++------
packages/frontend/src/hooks/product.ts | 39 ++--
packages/frontend/src/hooks/tips.ts | 49 +++--
packages/frontend/src/hooks/transfer.ts | 25 +--
packages/frontend/src/index.tsx | 61 +++---
packages/frontend/src/messages/en.po | 9 +-
.../frontend/src/paths/admin/create/CreatePage.tsx | 6 +-
packages/frontend/src/paths/admin/create/index.tsx | 4 +-
packages/frontend/src/paths/admin/list/index.tsx | 17 +-
.../frontend/src/paths/instance/details/index.tsx | 15 +-
.../orders/create/OrderCreatedSuccessfully.tsx | 4 +-
.../src/paths/instance/orders/create/index.tsx | 2 +-
.../src/paths/instance/orders/details/index.tsx | 24 ++-
.../src/paths/instance/orders/list/index.tsx | 34 +---
.../src/paths/instance/products/list/index.tsx | 22 +-
.../src/paths/instance/tips/list/index.tsx | 15 +-
.../src/paths/instance/transfers/list/index.tsx | 16 +-
.../frontend/src/paths/instance/update/index.tsx | 19 +-
packages/frontend/src/utils/constants.ts | 2 +-
25 files changed, 521 insertions(+), 390 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 082c849..ee6c160 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,8 @@ and this project adheres to [Semantic
Versioning](https://semver.org/spec/v2.0.0
- order id field to go
frontend, too many redirects
+BUGS TEST CASES:
+https://git.taler.net/anastasis.git/tree/src/cli/test_anastasis_reducer_enter_secret.sh
- navigation to another instance should not do full refresh
- cleanup instance and token management, because code is a mess and can be
refactored
diff --git a/packages/frontend/src/ApplicationReadyRoutes.tsx
b/packages/frontend/src/ApplicationReadyRoutes.tsx
index a4f3f4f..6644118 100644
--- a/packages/frontend/src/ApplicationReadyRoutes.tsx
+++ b/packages/frontend/src/ApplicationReadyRoutes.tsx
@@ -36,67 +36,52 @@ export function ApplicationReadyRoutes(): VNode {
changeBackend(url);
if (token) updateToken(token);
};
- const list = useBackendInstancesTestForAdmin()
+ const result = useBackendInstancesTestForAdmin()
const clearTokenAndGoToRoot = () => {
clearAllTokens();
route('/')
}
- if (!list.data) {
- if (list.unauthorized) {
- return <Fragment>
- <NotYetReadyAppMenu title="Login"
- onLogout={clearTokenAndGoToRoot}
- />
- <NotificationCard notification={{
- message: i18n`Access denied`,
- description: i18n`Check your token is valid`,
- type: 'ERROR'
- }}
- />
- <LoginPage onConfirm={updateLoginStatus} />
- </Fragment>
- }
- if (list.notfound) {
- const path = new URL(backendURL).pathname
- const match = INSTANCE_ID_LOOKUP.exec(path)
- if (!match || !match[1]) {
- // this should be rare because
- // query to /config is ok but the URL
- // doest not match with our pattern
- return <Fragment>
- <NotYetReadyAppMenu title="Error" onLogout={clearTokenAndGoToRoot} />
- <NotificationCard notification={{
- message: i18n`Couldn't access the server`,
- description: i18n`Could not infer instance id from url
${backendURL}`,
- type: 'ERROR',
- }}
- />
- <LoginPage onConfirm={updateLoginStatus} />
- </Fragment>
- }
+ if (result.clientError && result.isUnauthorized) {
+ return <Fragment>
+ <NotYetReadyAppMenu title="Login" onLogout={clearTokenAndGoToRoot} />
+ <NotificationCard notification={{
+ message: i18n`Access denied`,
+ description: i18n`Check your token is valid`,
+ type: 'ERROR'
+ }}
+ />
+ <LoginPage onConfirm={updateLoginStatus} />
+ </Fragment>
+ }
- return <Fragment>
- <Menu instance={match[1]} onLogout={clearTokenAndGoToRoot} />
- <InstanceRoutes id={match[1]} />
- </Fragment>
- }
- if (list.error) {
+ if (result.loading) return <NotYetReadyAppMenu title="Loading..." />
+
+ if (!result.ok) {
+ const path = new URL(backendURL).pathname
+ const match = INSTANCE_ID_LOOKUP.exec(path)
+ if (!match || !match[1]) {
+ // this should be rare because
+ // query to /config is ok but the URL
+ // doest not match with our pattern
return <Fragment>
- <NotYetReadyAppMenu title="Error" />
+ <NotYetReadyAppMenu title="Error" onLogout={clearTokenAndGoToRoot} />
<NotificationCard notification={{
- message: i18n`Couldn't access the server`,
- description: list.error.message,
- type: 'ERROR'
- }} />
+ message: i18n`Couldn't access the server.`,
+ description: i18n`Could not infer instance id from url
${backendURL}`,
+ type: 'ERROR',
+ }}
+ />
<LoginPage onConfirm={updateLoginStatus} />
</Fragment>
}
- // is loading
- return <NotYetReadyAppMenu title="Loading..." />
+ return <Fragment>
+ <Menu instance={match[1]} onLogout={clearTokenAndGoToRoot} />
+ <InstanceRoutes id={match[1]} />
+ </Fragment>
}
let instance
@@ -106,6 +91,7 @@ export function ApplicationReadyRoutes(): VNode {
} finally {
if (!instance) instance = 'default'
}
+
return <Fragment>
<Menu instance={instance} admin onLogout={clearTokenAndGoToRoot} />
<InstanceRoutes admin id={instance} />
diff --git a/packages/frontend/src/InstanceRoutes.tsx
b/packages/frontend/src/InstanceRoutes.tsx
index fb8e0ac..afffd51 100644
--- a/packages/frontend/src/InstanceRoutes.tsx
+++ b/packages/frontend/src/InstanceRoutes.tsx
@@ -26,7 +26,7 @@ import { useMessageTemplate } from 'preact-messages';
import { createHashHistory } from 'history';
import { useBackendDefaultToken, useBackendInstanceToken } from './hooks';
import { InstanceContextProvider, useBackendContext } from './context/backend';
-import { SwrError } from "./hooks/backend";
+import { HttpError, HttpResponseServerError, RequestInfo, SwrError } from
"./hooks/backend";
// import { Notification } from './utils/types';
import LoginPage from './paths/login';
@@ -53,6 +53,7 @@ import InstanceListPage from './paths/admin/list';
import InstanceCreatePage from "./paths/admin/create";
import { NotificationCard } from './components/menu';
import { Loading } from './components/exception/loading';
+import { MerchantBackend } from './declaration';
export enum InstancePaths {
// details = '/',
@@ -109,8 +110,8 @@ export function InstanceRoutes({ id, admin }: Props): VNode
{
const value = useMemo(() => ({ id, token, admin }), [id, token])
- const LoginPageServerError = (error: SwrError) => <Fragment>
- <NotificationCard notification={{ message: i18n`Problem reaching the
server`, description: i18n`Got message: ${error.message} from: ${error.backend}
(hasToken: ${error.hasToken})`, type: 'ERROR' }} />
+ const LoginPageServerError = (error: HttpError) => <Fragment>
+ <NotificationCard notification={{ message: `Server reported a problem:
HTTP status #${error.status}`, description: `Got message: ${error.message}
from: ${error.info?.url}`, type: 'ERROR' }} />
<LoginPage onConfirm={updateLoginStatus} />
</Fragment>
@@ -120,15 +121,15 @@ export function InstanceRoutes({ id, admin }: Props):
VNode {
</Fragment>
function IfAdminCreateDefaultOr<T>(Next: FunctionComponent<any>) {
- return (props?:T)=> {
- if (admin) {
+ return (props?: T) => {
+ if (admin && id === 'default') {
return <Fragment>
<NotificationCard notification={{
message: 'No default instance',
description: 'in order to use merchant backoffice, you should
create the default instance',
type: 'INFO'
}} />
- <InstanceCreatePage onError={() => null} forceId="default"
onConfirm={() => {
+ <InstanceCreatePage forceId="default" onConfirm={() => {
route(AdminPaths.list_instances)
}} />
</Fragment>
@@ -161,7 +162,6 @@ export function InstanceRoutes({ id, admin }: Props): VNode
{
<Route path={AdminPaths.new_instance} component={InstanceCreatePage}
onBack={() => route(AdminPaths.list_instances)}
onConfirm={() => { route(AdminPaths.list_instances); }}
- onError={LoginPageServerError}
/>
}
@@ -169,7 +169,8 @@ export function InstanceRoutes({ id, admin }: Props): VNode
{
<Route path={AdminPaths.update_instance}
component={AdminInstanceUpdatePage}
onBack={() => route(AdminPaths.list_instances)}
onConfirm={() => { route(AdminPaths.list_instances); }}
- onLoadError={LoginPageServerError}
+ onUpdateError={(e: Error) => { }}
+ onNotFound={NotFoundPage}
/>
}
@@ -177,12 +178,12 @@ export function InstanceRoutes({ id, admin }: Props):
VNode {
* Update instance page
*/}
<Route path={InstancePaths.update} component={InstanceUpdatePage}
- onUnauthorized={LoginPageAccessDenied}
- onLoadError={IfAdminCreateDefaultOr(LoginPageServerError)}
+ onBack={() => { route(`/`); }}
+ onConfirm={() => { route(`/`); }}
+ onUpdateError={(e: Error) => { }}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
- onBack={() => {route(`/`);}}
- onConfirm={() => {route(`/`);}}
- onUpdateError={(e: Error) => {}}
+ onUnauthorized={LoginPageAccessDenied}
+ onLoadError={LoginPageServerError}
/>
{/**
@@ -190,7 +191,7 @@ export function InstanceRoutes({ id, admin }: Props): VNode
{
*/}
<Route path={InstancePaths.product_list} component={ProductListPage}
onUnauthorized={LoginPageAccessDenied}
- onLoadError={IfAdminCreateDefaultOr(LoginPageServerError)}
+ onLoadError={LoginPageServerError}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
/>
<Route path={InstancePaths.product_update} component={ProductUpdatePage}
@@ -202,21 +203,21 @@ export function InstanceRoutes({ id, admin }: Props):
VNode {
* Order pages
*/}
<Route path={InstancePaths.order_list} component={OrderListPage}
- onCreate={() => {route(InstancePaths.order_new)}}
+ onCreate={() => { route(InstancePaths.order_new) }}
onSelect={(id: string) => {
route(InstancePaths.order_details.replace(':oid', id)) }}
onUnauthorized={LoginPageAccessDenied}
- onLoadError={IfAdminCreateDefaultOr(LoginPageServerError)}
+ onLoadError={LoginPageServerError}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
/>
<Route path={InstancePaths.order_details} component={OrderDetailsPage}
onUnauthorized={LoginPageAccessDenied}
- onLoadError={IfAdminCreateDefaultOr(LoginPageServerError)}
+ onLoadError={LoginPageServerError}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
- onBack={() => {route(InstancePaths.order_list)}}
+ onBack={() => { route(InstancePaths.order_list) }}
/>
<Route path={InstancePaths.order_new} component={OrderCreatePage}
- onConfirm={() => {route(InstancePaths.order_list)}}
- onBack={() => {route(InstancePaths.order_list)}}
+ onConfirm={() => { route(InstancePaths.order_list) }}
+ onBack={() => { route(InstancePaths.order_list) }}
/>
{/**
@@ -224,7 +225,7 @@ export function InstanceRoutes({ id, admin }: Props): VNode
{
*/}
<Route path={InstancePaths.transfers_list} component={TransferListPage}
onUnauthorized={LoginPageAccessDenied}
- onLoadError={IfAdminCreateDefaultOr(LoginPageServerError)}
+ onLoadError={LoginPageServerError}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
/>
@@ -257,9 +258,9 @@ function AdminInstanceUpdatePage({ id, ...rest }: { id:
string } & InstanceUpdat
const i18n = useMessageTemplate('');
return <InstanceContextProvider value={value}>
<InstanceUpdatePage {...rest}
- onLoadError={(error: SwrError) => {
+ onLoadError={(error: HttpError) => {
return <Fragment>
- <NotificationCard notification={{ message: i18n`Problem reaching the
server`, description: i18n`Got message: ${error.message} from: ${error.backend}
(hasToken: ${error.hasToken})`, type: 'ERROR' }} />
+ <NotificationCard notification={{ message: `Server reported a
problem: HTTP status #${error.status}`, description: `Got message:
${error.message} from: ${error.info?.url}`, type: 'ERROR' }} />
<LoginPage onConfirm={updateLoginStatus} />
</Fragment>
}}
diff --git a/packages/frontend/src/declaration.d.ts
b/packages/frontend/src/declaration.d.ts
index cf639b1..6f258c4 100644
--- a/packages/frontend/src/declaration.d.ts
+++ b/packages/frontend/src/declaration.d.ts
@@ -48,6 +48,45 @@ type UUID = string;
type Integer = number;
export namespace MerchantBackend {
+ interface ErrorDetail {
+
+ // Numeric error code unique to the condition.
+ // The other arguments are specific to the error value reported here.
+ code: number;
+
+ // Human-readable description of the error, i.e. "missing parameter",
"commitment violation", ...
+ // Should give a human-readable hint about the error's nature.
Optional, may change without notice!
+ hint?: string;
+
+ // Optional detail about the specific input value that failed. May
change without notice!
+ detail?: string;
+
+ // Name of the parameter that was bogus (if applicable).
+ parameter?: string;
+
+ // Path to the argument that was bogus (if applicable).
+ path?: string;
+
+ // Offset of the argument that was bogus (if applicable).
+ offset?: string;
+
+ // Index of the argument that was bogus (if applicable).
+ index?: string;
+
+ // Name of the object that was bogus (if applicable).
+ object?: string;
+
+ // Name of the currency than was problematic (if applicable).
+ currency?: string;
+
+ // Expected type (if applicable).
+ type_expected?: string;
+
+ // Type that was provided instead (if applicable).
+ type_actual?: string;
+ }
+
+
// Delivery location, loosely modeled as a subset of
// ISO20022's PostalAddress25.
interface Tax {
diff --git a/packages/frontend/src/hooks/backend.ts
b/packages/frontend/src/hooks/backend.ts
index 943d826..f9466a0 100644
--- a/packages/frontend/src/hooks/backend.ts
+++ b/packages/frontend/src/hooks/backend.ts
@@ -19,14 +19,11 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import useSWR, { mutate, cache, useSWRInfinite } from 'swr';
-import axios from 'axios'
+import { mutate, cache } from 'swr';
+import axios, { AxiosError } from 'axios'
import { MerchantBackend } from '../declaration';
-import { useBackendContext, useInstanceContext } from '../context/backend';
-import { useEffect, useMemo, useState } from 'preact/hooks';
-import { MAX_RESULT_SIZE, PAGE_SIZE } from '../utils/constants';
-import { add, addHours, addSeconds, format, max } from 'date-fns';
-import { OrderAPI } from './order';
+import { useBackendContext } from '../context/backend';
+import { useEffect, useState } from 'preact/hooks';
export function mutateAll(re: RegExp) {
cache.keys().filter(key => {
@@ -35,38 +32,86 @@ export function mutateAll(re: RegExp) {
}).forEach(key => mutate(key, null))
}
-export type HttpResponse<T> = HttpResponseOk<T> | HttpResponseError;
+export type HttpResponse<T> = HttpResponseOk<T> | HttpResponseLoading<T> |
HttpError;
+export type HttpResponsePaginated<T> = HttpResponseOkPaginated<T> |
HttpResponseLoading<T> | HttpError;
+
+export interface RequestInfo {
+ url: string;
+ hasToken: boolean;
+ params: any;
+ data: any;
+}
+
+interface HttpResponseLoading<T> {
+ ok?: false;
+ loading: true;
+ clientError?: false;
+ serverError?: false;
+
+ data?: T;
+}
+export interface HttpResponseOk<T> {
+ ok: true;
+ loading?: false;
+ clientError?: false;
+ serverError?: false;
-interface HttpResponseOk<T> {
data: T;
- unauthorized: boolean;
- notfound: boolean;
- isLoadingMore?: boolean;
- loadMore?: () => void;
- loadMorePrev?: () => void;
+ info?: RequestInfo;
+}
+
+export type HttpResponseOkPaginated<T> = HttpResponseOk<T> & WithPagination
+
+export interface WithPagination {
+ loadMore: () => void;
+ loadMorePrev: () => void;
isReachingEnd?: boolean;
isReachingStart?: boolean;
}
+export type HttpError = HttpResponseClientError | HttpResponseServerError |
HttpResponseUnexpectedError;
export interface SwrError {
info: any,
status: number,
message: string,
- backend: string,
- hasToken: boolean,
-}
-interface HttpResponseError {
- data: undefined;
- unauthorized: boolean;
- notfound: boolean;
- error?: SwrError;
- isLoadingMore?: boolean;
- loadMore?: () => void;
- loadMorePrev?: () => void;
- isReachingEnd?: boolean;
- isReachingStart?: boolean;
+}
+export interface HttpResponseServerError {
+ ok?: false;
+ loading?: false;
+ clientError?: false;
+ serverError: true;
+
+ error?: MerchantBackend.ErrorDetail;
+ status: number;
+ message: string;
+ info?: RequestInfo;
+}
+interface HttpResponseClientError {
+ ok?: false;
+ loading?: false;
+ clientError: true;
+ serverError?: false;
+
+ info?: RequestInfo;
+ isUnauthorized: boolean;
+ isNotfound: boolean;
+ status: number;
+ error?: MerchantBackend.ErrorDetail;
+ message: string;
+
}
+interface HttpResponseUnexpectedError {
+ ok?: false;
+ loading?: false;
+ clientError?: false;
+ serverError?: false;
+
+ info?: RequestInfo;
+ status?: number;
+ error: any;
+ message: string;
+}
type Methods = 'get' | 'post' | 'patch' | 'delete' | 'put';
@@ -77,17 +122,76 @@ interface RequestOptions {
params?: any;
}
+function buildRequestOk<T>(res: any, url: string, hasToken: boolean):
HttpResponseOk<T> {
+ return {
+ ok: true, data: res.data, info: {
+ params: res.config.params,
+ data: res.config.data,
+ url,
+ hasToken,
+ }
+ }
+}
+
+// function buildResponse<T>(data?: T, error?: MerchantBackend.ErrorDetail,
isValidating?: boolean): HttpResponse<T> {
+// if (isValidating) return {loading: true}
+// if (error) return buildRequestFailed()
+// }
-export async function request(url: string, options: RequestOptions = {}):
Promise<any> {
+function buildRequestFailed(ex: AxiosError<MerchantBackend.ErrorDetail>, url:
string, hasToken: boolean): HttpResponseClientError | HttpResponseServerError |
HttpResponseUnexpectedError {
+ const status = ex.response?.status
+
+ const info = {
+ data: ex.request?.data,
+ params: ex.request?.params,
+ url,
+ hasToken,
+ };
+
+ if (status && status >= 400 && status < 500) {
+ const error: HttpResponseClientError = {
+ clientError: true,
+ isNotfound: status === 404,
+ isUnauthorized: status === 401,
+ status,
+ info,
+ message: ex.response?.data?.hint || ex.message,
+ error: ex.response?.data
+ }
+ return error
+ }
+ if (status && status >= 500 && status < 600) {
+ const error: HttpResponseServerError = {
+ serverError: true,
+ status,
+ info,
+ message: ex.response?.data?.hint || ex.message,
+ error: ex.response?.data
+ }
+ return error;
+ }
+
+ const error: HttpResponseUnexpectedError = {
+ info,
+ status,
+ error: ex,
+ message: ex.message
+ }
+
+ return error
+}
+
+export async function request<T>(url: string, options: RequestOptions = {}):
Promise<HttpResponseOk<T>> {
const headers = options.token ? { Authorization: `Bearer ${options.token}` }
: undefined
+
try {
- // http://localhost:9966/instances/blog/private/instances
- // Hack, endpoint should respond 404
- if (/^\/instances\/[^/]*\/private\/instances$/.test(new
URL(url).pathname)) {
- console.warn(`HACK: Not going to query ${url}, instead return 404`)
- throw ({ response: { status: 404 }, message: 'not found' })
- }
+ // // http://localhost:9966/instances/blog/private/instances
+ // // Hack, endpoint should respond 404
+ // if (/^\/instances\/[^/]*\/private\/instances$/.test(new
URL(url).pathname)) {
+ // console.warn(`HACK: Not going to query ${url}, instead return 404`)
+ // throw ({ response: { status: 404 }, message: 'not found' })
+ // }
const res = await axios({
@@ -98,59 +202,49 @@ export async function request(url: string, options:
RequestOptions = {}): Promis
data: options.data,
params: options.params
})
- return res.data
+
+ return buildRequestOk<T>(res, url, !!options.token)
} catch (e) {
- console.error(e)
- const info = e.response?.data
- const status = e.response?.status
- const hint = info?.hint
- throw { info, status, message: hint || e.message, backend: url, hasToken:
!!options.token }
+ const error = buildRequestFailed(e, url, !!options.token)
+ throw error
}
}
-export function fetcher(url: string, token: string, backend: string) {
- return request(`${backend}${url}`, { token })
+export function fetcher<T>(url: string, token: string, backend: string):
Promise<HttpResponseOk<T>> {
+ return request<T>(`${backend}${url}`, { token })
}
export function useBackendInstancesTestForAdmin():
HttpResponse<MerchantBackend.Instances.InstancesResponse> {
const { url, token } = useBackendContext()
- interface Result {
- data?: MerchantBackend.Instances.InstancesResponse;
- error?: SwrError;
- }
- const [result, setResult] = useState<Result | undefined>(undefined)
+
+ type Type = MerchantBackend.Instances.InstancesResponse;
+
+ const [result, setResult] = useState<HttpResponse<Type>>({ loading: true })
useEffect(() => {
- request(`${url}/private/instances`, { token })
- .then(data => setResult({ data }))
- .catch(error => setResult({ error }))
+ request<Type>(`${url}/private/instances`, { token })
+ .then(data => setResult(data))
+ .catch(error => setResult(error))
}, [url, token])
- const data = result?.data
- const error = result?.error
- return { data, unauthorized: error?.status === 401, notfound: error?.status
=== 404, error }
+ return result
}
export function useBackendConfig():
HttpResponse<MerchantBackend.VersionResponse> {
const { url, token } = useBackendContext()
- interface Result {
- data?: MerchantBackend.VersionResponse;
- error?: SwrError;
- }
- const [result, setResult] = useState<Result | undefined>(undefined)
+ type Type = MerchantBackend.VersionResponse;
+
+ const [result, setResult] = useState<HttpResponse<Type>>({ loading: true })
useEffect(() => {
- request(`${url}/config`, { token })
- .then(data => setResult({ data }))
- .catch(error => setResult({ error }))
+ request<Type>(`${url}/config`, { token })
+ .then(data => setResult(data))
+ .catch(error => setResult(error))
}, [url, token])
- const data = result?.data
- const error = result?.error
-
- return { data, unauthorized: error?.status === 401, notfound: error?.status
=== 404, error }
+ return result
}
diff --git a/packages/frontend/src/hooks/instance.ts
b/packages/frontend/src/hooks/instance.ts
index 31709bc..c96c1f6 100644
--- a/packages/frontend/src/hooks/instance.ts
+++ b/packages/frontend/src/hooks/instance.ts
@@ -1,6 +1,6 @@
import { MerchantBackend } from '../declaration';
import { useBackendContext, useInstanceContext } from '../context/backend';
-import { fetcher, HttpResponse, request, SwrError } from './backend';
+import { fetcher, HttpError, HttpResponse, HttpResponseOk, request, SwrError }
from './backend';
import useSWR, { mutate } from 'swr';
@@ -69,9 +69,6 @@ export function useInstanceAPI(): InstanceAPI {
export function useInstanceDetails():
HttpResponse<MerchantBackend.Instances.QueryInstancesResponse> {
- // const { url: baseUrl } = useBackendContext();
- // const { token, id, admin } = useInstanceContext();
- // const url = !admin ? baseUrl : `${baseUrl}/instances/${id}`
const { url: baseUrl, token: baseToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
@@ -81,17 +78,23 @@ export function useInstanceDetails():
HttpResponse<MerchantBackend.Instances.Que
url: `${baseUrl}/instances/${id}`, token: instanceToken
}
- const { data, error } =
useSWR<MerchantBackend.Instances.QueryInstancesResponse,
SwrError>([`/private/`, token, url], fetcher)
+ const { data, error, isValidating } =
useSWR<HttpResponseOk<MerchantBackend.Instances.QueryInstancesResponse>,
HttpError>([`/private/`, token, url], fetcher)
- return { data, unauthorized: error?.status === 401, notfound: error?.status
=== 404, error }
+ if (isValidating) return {loading:true, data: data?.data}
+ if (data) return data
+ if (error) return error
+ return {loading: true}
}
export function useBackendInstances():
HttpResponse<MerchantBackend.Instances.InstancesResponse> {
const { url } = useBackendContext()
const { token } = useInstanceContext();
- const { data, error } = useSWR<MerchantBackend.Instances.InstancesResponse,
SwrError>(['/private/instances', token, url], fetcher)
+ const { data, error, isValidating } =
useSWR<HttpResponseOk<MerchantBackend.Instances.InstancesResponse>,
HttpError>(['/private/instances', token, url], fetcher)
- return { data, unauthorized: error?.status === 401, notfound: error?.status
=== 404, error }
+ if (isValidating) return {loading:true, data: data?.data}
+ if (data) return data
+ if (error) return error
+ return {loading: true}
}
diff --git a/packages/frontend/src/hooks/order.ts
b/packages/frontend/src/hooks/order.ts
index 2b51366..da056dd 100644
--- a/packages/frontend/src/hooks/order.ts
+++ b/packages/frontend/src/hooks/order.ts
@@ -4,26 +4,23 @@ import useSWR from 'swr';
import { useBackendContext, useInstanceContext } from '../context/backend';
import { MerchantBackend } from '../declaration';
import { MAX_RESULT_SIZE, PAGE_SIZE } from '../utils/constants';
-import { fetcher, HttpResponse, mutateAll, request, SwrError } from
'./backend';
+import { fetcher, HttpError, HttpResponse, HttpResponseOk,
HttpResponsePaginated, mutateAll, request, SwrError, WithPagination } from
'./backend';
export interface OrderAPI {
//FIXME: add OutOfStockResponse on 410
- createOrder: (data: MerchantBackend.Orders.PostOrderRequest) =>
Promise<MerchantBackend.Orders.PostOrderResponse>;
- forgetOrder: (id: string, data: MerchantBackend.Orders.ForgetRequest) =>
Promise<void>;
- refundOrder: (id: string, data: MerchantBackend.Orders.RefundRequest) =>
Promise<MerchantBackend.Orders.MerchantRefundResponse>;
- deleteOrder: (id: string) => Promise<void>;
- getPaymentURL: (id: string) => Promise<string>;
+ createOrder: (data: MerchantBackend.Orders.PostOrderRequest) =>
Promise<HttpResponseOk<MerchantBackend.Orders.PostOrderResponse>>;
+ forgetOrder: (id: string, data: MerchantBackend.Orders.ForgetRequest) =>
Promise<HttpResponseOk<void>>;
+ refundOrder: (id: string, data: MerchantBackend.Orders.RefundRequest) =>
Promise<HttpResponseOk<MerchantBackend.Orders.MerchantRefundResponse>>;
+ deleteOrder: (id: string) => Promise<HttpResponseOk<void>>;
+ getPaymentURL: (id: string) => Promise<HttpResponseOk<string>>;
}
type YesOrNo = 'yes' | 'no';
-export function orderFetcher(url: string, token: string, backend: string,
paid?: YesOrNo, refunded?: YesOrNo, wired?: YesOrNo, searchDate?: Date, delta?:
number) {
- const newDate = searchDate && addHours(searchDate, 3) // remove this, locale
- // if we are
- const newDatePlus1SecIfNeeded = delta && delta < 0 && newDate ?
addSeconds(newDate, 1) : newDate
- const date = newDatePlus1SecIfNeeded ? format(newDatePlus1SecIfNeeded,
'yyyy-MM-dd HH:mm:ss') : undefined
- return request(`${backend}${url}`, { token, params: { paid, refunded, wired,
delta, date } })
+export function orderFetcher<T>(url: string, token: string, backend: string,
paid?: YesOrNo, refunded?: YesOrNo, wired?: YesOrNo, searchDate?: Date, delta?:
number): Promise<HttpResponseOk<T>> {
+ const date_ms = delta && delta < 0 && searchDate ? searchDate.getTime() + 1
: searchDate?.getTime()
+ return request<T>(`${backend}${url}`, { token, params: { paid, refunded,
wired, delta, date_ms } })
}
@@ -37,65 +34,70 @@ export function useOrderAPI(): OrderAPI {
url: `${baseUrl}/instances/${id}`, token: instanceToken
}
- const createOrder = async (data: MerchantBackend.Orders.PostOrderRequest):
Promise<MerchantBackend.Orders.PostOrderResponse> => {
- const res = await request(`${url}/private/orders`, {
+ const createOrder = async (data: MerchantBackend.Orders.PostOrderRequest):
Promise<HttpResponseOk<MerchantBackend.Orders.PostOrderResponse>> => {
+ mutateAll(/@"\/private\/orders"@/)
+ return
request<MerchantBackend.Orders.PostOrderResponse>(`${url}/private/orders`, {
method: 'post',
token,
data
})
-
- mutateAll(/@"\/private\/orders"@/)
- return res
+ // return res
}
- const refundOrder = async (orderId: string, data:
MerchantBackend.Orders.RefundRequest):
Promise<MerchantBackend.Orders.MerchantRefundResponse> => {
- const res = await request(`${url}/private/orders/${orderId}/refund`, {
+ const refundOrder = async (orderId: string, data:
MerchantBackend.Orders.RefundRequest):
Promise<HttpResponseOk<MerchantBackend.Orders.MerchantRefundResponse>> => {
+ mutateAll(/@"\/private\/orders"@/)
+ return
request<MerchantBackend.Orders.MerchantRefundResponse>(`${url}/private/orders/${orderId}/refund`,
{
method: 'post',
token,
data
})
- mutateAll(/@"\/private\/orders"@/)
- return res
+ // return res
}
- const forgetOrder = async (orderId: string, data:
MerchantBackend.Orders.ForgetRequest): Promise<void> => {
- await request(`${url}/private/orders/${orderId}/forget`, {
+ const forgetOrder = async (orderId: string, data:
MerchantBackend.Orders.ForgetRequest): Promise<HttpResponseOk<void>> => {
+ mutateAll(/@"\/private\/orders"@/)
+ return request(`${url}/private/orders/${orderId}/forget`, {
method: 'patch',
token,
data
})
- mutateAll(/@"\/private\/orders"@/)
}
- const deleteOrder = async (orderId: string): Promise<void> => {
- await request(`${url}/private/orders/${orderId}`, {
+ const deleteOrder = async (orderId: string): Promise<HttpResponseOk<void>>
=> {
+ mutateAll(/@"\/private\/orders"@/)
+ return request(`${url}/private/orders/${orderId}`, {
method: 'delete',
token
})
-
- mutateAll(/@"\/private\/orders"@/)
}
- const getPaymentURL = async (orderId: string): Promise<string> => {
- const data = await request(`${url}/private/orders/${orderId}`, {
+ const getPaymentURL = async (orderId: string):
Promise<HttpResponseOk<string>> => {
+ return
request<MerchantBackend.Orders.MerchantOrderStatusResponse>(`${url}/private/orders/${orderId}`,
{
method: 'get',
token
+ }).then((res) => {
+ const url = res.data.order_status === "unpaid" ? res.data.taler_pay_uri
: res.data.contract_terms.fulfillment_url
+ const response: HttpResponseOk<string> = res as any
+ response.data = url || ''
+ return response
})
- return data.taler_pay_uri || data.contract_terms?.fulfillment_url
}
return { createOrder, forgetOrder, deleteOrder, refundOrder, getPaymentURL }
}
-export function useOrderDetails(oderId:string):
HttpResponse<MerchantBackend.Orders.MerchantOrderStatusResponse> {
+export function useOrderDetails(oderId: string):
HttpResponse<MerchantBackend.Orders.MerchantOrderStatusResponse> {
const { url: baseUrl } = useBackendContext();
const { token, id: instanceId, admin } = useInstanceContext();
const url = !admin ? baseUrl : `${baseUrl}/instances/${instanceId}`
- const { data, error } =
useSWR<MerchantBackend.Orders.MerchantOrderStatusResponse,
SwrError>([`/private/orders/${oderId}`, token, url], fetcher)
+ const { data, error, isValidating } =
useSWR<HttpResponseOk<MerchantBackend.Orders.MerchantOrderStatusResponse>,
HttpError>([`/private/orders/${oderId}`, token, url], fetcher)
- return { data, unauthorized: error?.status === 401, notfound: error?.status
=== 404, error }
+ if (isValidating) return { loading: true, data: data?.data }
+ if (data) return data
+ if (error) return error
+ return { loading: true }
}
export interface InstanceOrderFilter {
@@ -105,7 +107,7 @@ export interface InstanceOrderFilter {
date?: Date;
}
-export function useInstanceOrders(args: InstanceOrderFilter, updateFilter:
(d:Date)=>void): HttpResponse<MerchantBackend.Orders.OrderHistory> {
+export function useInstanceOrders(args: InstanceOrderFilter, updateFilter: (d:
Date) => void): HttpResponsePaginated<MerchantBackend.Orders.OrderHistory> {
const { url: baseUrl, token: baseToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
@@ -127,51 +129,58 @@ export function useInstanceOrders(args:
InstanceOrderFilter, updateFilter: (d:Da
* the logic of double query should be inside the orderFetch so from the
hook perspective and cache
* is just one query and one error status
*/
- const { data:beforeData, error:beforeError } =
useSWR<MerchantBackend.Orders.OrderHistory, SwrError>(
+ const { data: beforeData, error: beforeError, isValidating: loadingBefore }
= useSWR<HttpResponseOk<MerchantBackend.Orders.OrderHistory>, HttpError>(
[`/private/orders`, token, url, args?.paid, args?.refunded, args?.wired,
args?.date, totalBefore],
orderFetcher,
)
- const { data:afterData, error:afterError } =
useSWR<MerchantBackend.Orders.OrderHistory, SwrError>(
+ const { data: afterData, error: afterError, isValidating: loadingAfter } =
useSWR<HttpResponseOk<MerchantBackend.Orders.OrderHistory>, HttpError>(
[`/private/orders`, token, url, args?.paid, args?.refunded, args?.wired,
args?.date, -totalAfter],
orderFetcher,
)
//this will save last result
- const [lastBefore, setLastBefore] =
useState<MerchantBackend.Orders.OrderHistory | undefined>(undefined)
- const [lastAfter, setLastAfter] =
useState<MerchantBackend.Orders.OrderHistory | undefined>(undefined)
+ const [lastBefore, setLastBefore] =
useState<HttpResponse<MerchantBackend.Orders.OrderHistory>>({ loading: true })
+ const [lastAfter, setLastAfter] =
useState<HttpResponse<MerchantBackend.Orders.OrderHistory>>({ loading: true })
useEffect(() => {
if (afterData) setLastAfter(afterData)
if (beforeData) setLastBefore(beforeData)
}, [afterData, beforeData])
// this has problems when there are some ids missing
- const isReachingEnd = afterData && afterData.orders.length < totalAfter;
- const isReachingStart = (!args?.date) || (beforeData &&
beforeData.orders.length < totalBefore);
-
- const orders = !beforeData || !afterData ? undefined : (beforeData ||
lastBefore).orders.slice().reverse().concat((afterData || lastAfter).orders)
- const unauthorized = beforeError?.status === 401 || afterError?.status ===
401
- const notfound = beforeError?.status === 404 || afterError?.status === 404
-
- const loadMore = () => {
- if (!orders) return
- if (orders.length < MAX_RESULT_SIZE) {
- setPageAfter(pageAfter + 1)
- } else {
- const from =
afterData?.orders?.[afterData?.orders?.length-1]?.timestamp?.t_ms
- if (from) updateFilter(new Date(from))
- }
- }
- const loadMorePrev = () => {
- if (!orders) return
- if (orders.length < MAX_RESULT_SIZE) {
- setPageBefore(pageBefore + 1)
- } else {
- const from =
beforeData?.orders?.[beforeData?.orders?.length-1]?.timestamp?.t_ms
- if (from) updateFilter(new Date(from))
- }
+ if (beforeError) return beforeError
+ if (afterError) return afterError
+
+
+ const pagination = {
+ isReachingEnd: afterData && afterData.data.orders.length < totalAfter,
+ isReachingStart: (!args?.date) || (beforeData &&
beforeData.data.orders.length < totalBefore),
+ loadMore: () => {
+ if (!afterData) return
+ if (afterData.data.orders.length < MAX_RESULT_SIZE) {
+ setPageAfter(pageAfter + 1)
+ } else {
+ const from = afterData.data.orders[afterData.data.orders.length -
1].timestamp.t_ms
+ if (from) updateFilter(new Date(from))
+ }
+ },
+ loadMorePrev: () => {
+ if (!beforeData) return
+ if (beforeData.data.orders.length < MAX_RESULT_SIZE) {
+ setPageBefore(pageBefore + 1)
+ } else if (beforeData) {
+ const from = beforeData.data.orders[beforeData.data.orders.length -
1].timestamp.t_ms
+ if (from) updateFilter(new Date(from))
+ }
+ },
+ }
+
+ const orders = !beforeData || !afterData ? [] : (beforeData ||
lastBefore).data.orders.slice().reverse().concat((afterData ||
lastAfter).data.orders)
+ if (loadingAfter || loadingBefore) return { loading: true, data: { orders:
orders } }
+ if (beforeData && afterData) {
+ return { ok: true, data: { orders: orders }, ...pagination }
}
+ return { loading: true }
- return { data: orders ? {orders} : undefined, loadMorePrev, loadMore,
isReachingEnd, isReachingStart, unauthorized, notfound, error: beforeError ?
beforeError : afterError }
}
diff --git a/packages/frontend/src/hooks/product.ts
b/packages/frontend/src/hooks/product.ts
index 1909a18..6568a05 100644
--- a/packages/frontend/src/hooks/product.ts
+++ b/packages/frontend/src/hooks/product.ts
@@ -2,7 +2,7 @@ import { useEffect } from 'preact/hooks';
import useSWR, { useSWRInfinite } from 'swr';
import { useBackendContext, useInstanceContext } from '../context/backend';
import { MerchantBackend } from '../declaration';
-import { fetcher, HttpResponse, mutateAll, request, SwrError } from
'./backend';
+import { fetcher, HttpError, HttpResponse, HttpResponseOk, mutateAll, request,
SwrError } from './backend';
export interface ProductAPI {
@@ -77,29 +77,34 @@ export function useInstanceProducts():
HttpResponse<(MerchantBackend.Products.Pr
url: `${baseUrl}/instances/${id}`, token: instanceToken
};
- const list = useSWR<MerchantBackend.Products.InventorySummaryResponse,
SwrError>([`/private/products`, token, url], fetcher);
+ const { data: list, error: listError, isValidating: listLoading } =
useSWR<HttpResponseOk<MerchantBackend.Products.InventorySummaryResponse>,
HttpError>([`/private/products`, token, url], fetcher);
- const getKey = (pageIndex: number) => {
- if (!list.data || !list.data.products.length) return null
+ const { data: products, error: productError, setSize } =
useSWRInfinite<HttpResponseOk<MerchantBackend.Products.ProductDetail>,
HttpError>((pageIndex: number) => {
+ if (!list?.data || !list.data.products.length || listError) return null
return [`/private/products/${list.data.products[pageIndex].product_id}`,
token, url]
- }
-
- const res = useSWRInfinite<MerchantBackend.Products.ProductDetail,
SwrError>(getKey, fetcher)
- const { data, error, setSize, isValidating } = res
+ }, fetcher)
useEffect(() => {
- if (list.data && list.data?.products?.length > 0) {
- setSize(list.data?.products?.length)
+ if (list?.data && list.data.products.length > 0) {
+ setSize(list.data.products.length)
}
- }, [list.data])
+ }, [list?.data])
- if (list.data && list.data.products.length === 0) {
- return { data: [], unauthorized: false, notfound: false }
- }
+ if (listLoading) return { loading: true, data: [] }
+ if (listError) return listError
+ if (productError) return productError
+ if (list?.data && list.data.products.length === 0) {
+ return { ok: true, data: [] }
+ }
+ if (products) {
+ const dataWithId = products.map((d, i) => {
+ //take the id from the queried url
+ return ({ ...d.data, id: d.info?.url.replace(/.*\/private\/products\//,
'') || '' })
+ })
+ return { ok: true, data: dataWithId }
+ }
- const dataWithId = !error ? data?.map((d, i) => ({ ...d, id:
list.data?.products?.[i]?.product_id || '' })) : undefined
-
- return { data: dataWithId, unauthorized: error?.status === 401, notfound:
error?.status === 404, error };
+ return { loading: true }
}
diff --git a/packages/frontend/src/hooks/tips.ts
b/packages/frontend/src/hooks/tips.ts
index 3500c00..3809276 100644
--- a/packages/frontend/src/hooks/tips.ts
+++ b/packages/frontend/src/hooks/tips.ts
@@ -1,6 +1,6 @@
import { MerchantBackend } from '../declaration';
import { useBackendContext, useInstanceContext } from '../context/backend';
-import { request, mutateAll, HttpResponse, SwrError, fetcher } from
'./backend';
+import { request, mutateAll, HttpResponse, SwrError, fetcher, HttpError,
HttpResponseOk } from './backend';
import useSWR from 'swr';
@@ -14,47 +14,42 @@ export function useTipsMutateAPI(): TipsMutateAPI {
url: `${baseUrl}/instances/${id}`, token: instanceToken
};
- //reserves
- const createReserve = async (data:
MerchantBackend.Tips.ReserveCreateRequest):
Promise<MerchantBackend.Tips.ReserveCreateConfirmation> => {
- const res = await request(`${url}/private/reserves`, {
+ const createReserve = async (data:
MerchantBackend.Tips.ReserveCreateRequest):
Promise<HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>> => {
+ mutateAll(/@"\/private\/reserves"@/);
+
+ return
request<MerchantBackend.Tips.ReserveCreateConfirmation>(`${url}/private/reserves`,
{
method: 'post',
token,
data
});
+ };
+ const authorizeTipReserve = (pub: string, data:
MerchantBackend.Tips.TipCreateRequest):
Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => {
mutateAll(/@"\/private\/reserves"@/);
- return res;
- };
- const authorizeTipReserve = async (pub: string, data:
MerchantBackend.Tips.TipCreateRequest):
Promise<MerchantBackend.Tips.TipCreateConfirmation> => {
- const res = await request(`${url}/private/reserves/${pub}/authorize-tip`, {
+ return
request<MerchantBackend.Tips.TipCreateConfirmation>(`${url}/private/reserves/${pub}/authorize-tip`,
{
method: 'post',
token,
data
});
+ };
+ const authorizeTip = (data: MerchantBackend.Tips.TipCreateRequest):
Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => {
mutateAll(/@"\/private\/reserves"@/);
- return res;
- };
- const authorizeTip = async (data: MerchantBackend.Tips.TipCreateRequest):
Promise<MerchantBackend.Tips.TipCreateConfirmation> => {
- const res = await request(`${url}/private/tips`, {
+ return
request<MerchantBackend.Tips.TipCreateConfirmation>(`${url}/private/tips`, {
method: 'post',
token,
data
});
-
- mutateAll(/@"\/private\/reserves"@/);
- return res;
};
- const deleteReserve = async (pub: string): Promise<void> => {
- await request(`${url}/private/reserves/${pub}`, {
+ const deleteReserve = (pub: string): Promise<HttpResponse<void>> => {
+ mutateAll(/@"\/private\/reserves"@/);
+ return request(`${url}/private/reserves/${pub}`, {
method: 'delete',
token,
});
-
- mutateAll(/@"\/private\/reserves"@/);
};
@@ -62,13 +57,12 @@ export function useTipsMutateAPI(): TipsMutateAPI {
}
export interface TipsMutateAPI {
- createReserve: (data: MerchantBackend.Tips.ReserveCreateRequest) =>
Promise<MerchantBackend.Tips.ReserveCreateConfirmation>;
- authorizeTipReserve: (id: string, data:
MerchantBackend.Tips.TipCreateRequest) =>
Promise<MerchantBackend.Tips.TipCreateConfirmation>;
- authorizeTip: (data: MerchantBackend.Tips.TipCreateRequest) =>
Promise<MerchantBackend.Tips.TipCreateConfirmation>;
- deleteReserve: (id: string) => Promise<void>;
+ createReserve: (data: MerchantBackend.Tips.ReserveCreateRequest) =>
Promise<HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>>;
+ authorizeTipReserve: (id: string, data:
MerchantBackend.Tips.TipCreateRequest) =>
Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>;
+ authorizeTip: (data: MerchantBackend.Tips.TipCreateRequest) =>
Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>;
+ deleteReserve: (id: string) => Promise<HttpResponse<void>>;
}
-
export function useInstanceTips():
HttpResponse<MerchantBackend.Tips.TippingReserveStatus> {
const { url: baseUrl, token: baseToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
@@ -79,8 +73,11 @@ export function useInstanceTips():
HttpResponse<MerchantBackend.Tips.TippingRese
url: `${baseUrl}/instances/${id}`, token: instanceToken
}
- const { data, error } = useSWR<MerchantBackend.Tips.TippingReserveStatus,
SwrError>([`/private/reserves`, token, url], fetcher)
+ const { data, error, isValidating } =
useSWR<HttpResponseOk<MerchantBackend.Tips.TippingReserveStatus>,
HttpError>([`/private/reserves`, token, url], fetcher)
- return { data, unauthorized: error?.status === 401, notfound: error?.status
=== 404, error }
+ if (isValidating) return {loading:true, data: data?.data}
+ if (data) return data
+ if (error) return error
+ return {loading: true}
}
diff --git a/packages/frontend/src/hooks/transfer.ts
b/packages/frontend/src/hooks/transfer.ts
index 4984cc6..0c9e7b8 100644
--- a/packages/frontend/src/hooks/transfer.ts
+++ b/packages/frontend/src/hooks/transfer.ts
@@ -1,13 +1,12 @@
import { MerchantBackend } from '../declaration';
import { useBackendContext, useInstanceContext } from '../context/backend';
-import { request, mutateAll, HttpResponse, SwrError } from './backend';
+import { request, mutateAll, HttpResponse, HttpError, HttpResponseOk } from
'./backend';
import useSWR from 'swr';
-function transferFetcher(url: string, token: string, backend: string) {
- return request(`${backend}${url}`, { token, params: { payto_uri: '' } })
+async function transferFetcher<T>(url: string, token: string, backend:
string): Promise<HttpResponseOk<T>> {
+ return request<T>(`${backend}${url}`, { token, params: { payto_uri: '' } })
}
-
export function useTransferMutateAPI(): TransferMutateAPI {
const { url: baseUrl, token: adminToken } = useBackendContext();
const { token: instanceToken, id, admin } = useInstanceContext();
@@ -18,22 +17,21 @@ export function useTransferMutateAPI(): TransferMutateAPI {
url: `${baseUrl}/instances/${id}`, token: instanceToken
};
- const informTransfer = async (data:
MerchantBackend.Transfers.TransferInformation):
Promise<MerchantBackend.Transfers.MerchantTrackTransferResponse> => {
- const res = await request(`${url}/private/transfers`, {
+ const informTransfer = async (data:
MerchantBackend.Transfers.TransferInformation):
Promise<HttpResponseOk<MerchantBackend.Transfers.MerchantTrackTransferResponse>>
=> {
+ mutateAll(/@"\/private\/transfers"@/);
+
+ return
request<MerchantBackend.Transfers.MerchantTrackTransferResponse>(`${url}/private/transfers`,
{
method: 'post',
token,
data
});
-
- mutateAll(/@"\/private\/transfers"@/);
- return res;
};
return { informTransfer };
}
export interface TransferMutateAPI {
- informTransfer: (data: MerchantBackend.Transfers.TransferInformation) =>
Promise<MerchantBackend.Transfers.MerchantTrackTransferResponse>;
+ informTransfer: (data: MerchantBackend.Transfers.TransferInformation) =>
Promise<HttpResponseOk<MerchantBackend.Transfers.MerchantTrackTransferResponse>>;
}
export function useInstanceTransfers():
HttpResponse<MerchantBackend.Transfers.TransferList> {
@@ -46,9 +44,12 @@ export function useInstanceTransfers():
HttpResponse<MerchantBackend.Transfers.T
url: `${baseUrl}/instances/${id}`, token: instanceToken
}
- const { data, error } = useSWR<MerchantBackend.Transfers.TransferList,
SwrError>([`/private/transfers`, token, url], transferFetcher)
+ const { data, error, isValidating } =
useSWR<HttpResponseOk<MerchantBackend.Transfers.TransferList>,
HttpError>([`/private/transfers`, token, url], transferFetcher)
- return { data, unauthorized: error?.status === 401, notfound: error?.status
=== 404, error }
+ if (isValidating) return {loading:true, data: data?.data}
+ if (data) return data
+ if (error) return error
+ return {loading: true}
}
diff --git a/packages/frontend/src/index.tsx b/packages/frontend/src/index.tsx
index a2145c8..7f7884f 100644
--- a/packages/frontend/src/index.tsx
+++ b/packages/frontend/src/index.tsx
@@ -22,7 +22,7 @@
import "./scss/main.scss"
import { h, VNode } from 'preact';
-import { useEffect, useMemo } from "preact/hooks";
+import { useMemo } from "preact/hooks";
import { route } from 'preact-router';
import { MessageProvider, useMessageTemplate } from 'preact-messages';
@@ -35,6 +35,7 @@ import { hasKey, onTranslationError } from
"./utils/functions";
import LoginPage from './paths/login';
import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes";
import { NotificationCard, NotYetReadyAppMenu } from "./components/menu";
+import { Loading } from "./components/exception/loading";
export default function Application(): VNode {
const state = useBackendContextState()
@@ -50,12 +51,9 @@ export default function Application(): VNode {
function ApplicationStatusRoutes(): VNode {
const { changeBackend, triedToLog, updateToken, resetBackend } =
useBackendContext()
- const backendConfig = useBackendConfig();
+ const result = useBackendConfig();
const i18n = useMessageTemplate()
- const v = `${backendConfig.data?.currency} ${backendConfig.data?.version}`
- const ctx = useMemo(() => ({ currency: backendConfig.data?.currency || '',
version: backendConfig.data?.version || '' }), [v])
-
const updateLoginInfoAndGoToRoot = (url: string, token?: string) => {
changeBackend(url)
if (token) updateToken(token)
@@ -69,27 +67,44 @@ function ApplicationStatusRoutes(): VNode {
</div>
}
- if (!backendConfig.data) {
+ if (result.clientError && result.isUnauthorized) return <div id="app">
+ <NotYetReadyAppMenu title="Login" />
+ <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
+ </div>
- if (!backendConfig.error) return <div class="is-loading" />
+ if (result.clientError && result.isNotfound) return <div id="app">
+ <NotYetReadyAppMenu title="Error" />
+ <NotificationCard notification={{
+ message: i18n`Server not found`,
+ type: 'ERROR',
+ description: `Check your url`,
+ }} />
+ <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
+ </div>
- if (backendConfig.unauthorized) {
- return <div id="app">
- <NotYetReadyAppMenu title="Login" />
- <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
- </div>
- }
+ if (result.serverError) return <div id="app">
+ <NotYetReadyAppMenu title="Error" />
+ <NotificationCard notification={{
+ message: i18n`Couldn't access the server`,
+ type: 'ERROR',
+ description: i18n`Got message ${result.message} from
${result.info?.url}`,
+ }} />
+ <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
+ </div>
- return <div id="app">
- <NotYetReadyAppMenu title="Error" />
- <NotificationCard notification={{
- message: i18n`Couldnt access the server`,
- type: 'ERROR',
- description: i18n`Got message: ${backendConfig.error.message} from:
${backendConfig.error.backend} (hasToken: ${backendConfig.error.hasToken})`,
- }} />
- <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
- </div>
- }
+ if (result.loading) return <Loading />
+
+ if (!result.ok) return <div id="app">
+ <NotYetReadyAppMenu title="Error" />
+ <NotificationCard notification={{
+ message: i18n`Unexpected Error`,
+ type: 'ERROR',
+ description: i18n`Got message ${result.error.message} from
${result.info?.url}`,
+ }} />
+ <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
+ </div>
+
+ const ctx = useMemo(() => ({ currency: result.data.currency, version:
result.data.version }), [result.data.currency, result.data.version])
return <div id="app" class="has-navbar-fixed-top">
<ConfigContextProvider value={ctx}>
diff --git a/packages/frontend/src/messages/en.po
b/packages/frontend/src/messages/en.po
index 245df6c..a21f4cd 100644
--- a/packages/frontend/src/messages/en.po
+++ b/packages/frontend/src/messages/en.po
@@ -145,10 +145,13 @@ msgstr "Pay delay"
msgid "fields.instance.default_wire_transfer_delay.label"
msgstr "Wire transfer delay"
-msgid "Couldnt access the server"
-msgstr "Couldnt access the server"
+msgid "Couldn't access the server"
+msgstr "Couldn't access the server"
-msgid "Got message: %s from: %s (hasToken: %s)"
+msgid "Unexpected Error"
+msgstr "Unexpected Error"
+
+msgid "Got message %s from %s"
msgstr "Got message \"%s\" from %s"
msgid "Merchant"
diff --git a/packages/frontend/src/paths/admin/create/CreatePage.tsx
b/packages/frontend/src/paths/admin/create/CreatePage.tsx
index 84a6241..21cc219 100644
--- a/packages/frontend/src/paths/admin/create/CreatePage.tsx
+++ b/packages/frontend/src/paths/admin/create/CreatePage.tsx
@@ -51,9 +51,9 @@ interface KeyValue {
function with_defaults(id?: string): Partial<Entity> {
return {
id,
- default_pay_delay: { d_ms: 1000 },
- default_wire_fee_amortization: 10,
- default_wire_transfer_delay: { d_ms: 2000 },
+ default_pay_delay: { d_ms: 1000*60*5 },
+ default_wire_fee_amortization: 1,
+ default_wire_transfer_delay: { d_ms: 2000*60*5 },
};
}
diff --git a/packages/frontend/src/paths/admin/create/index.tsx
b/packages/frontend/src/paths/admin/create/index.tsx
index c8aa992..e277d15 100644
--- a/packages/frontend/src/paths/admin/create/index.tsx
+++ b/packages/frontend/src/paths/admin/create/index.tsx
@@ -22,6 +22,7 @@ import { useState } from "preact/hooks";
import { NotificationCard } from "../../../components/menu";
import { MerchantBackend } from "../../../declaration";
import { useAdminAPI } from "../../../hooks/admin";
+import { RequestInfo } from "../../../hooks/backend";
import { Notification } from "../../../utils/types";
import { CreatePage } from "./CreatePage";
import { InstanceCreatedSuccessfully } from "./InstanceCreatedSuccessfully";
@@ -29,12 +30,11 @@ import { InstanceCreatedSuccessfully } from
"./InstanceCreatedSuccessfully";
interface Props {
onBack?: () => void;
onConfirm: () => void;
- onError: (error: any) => void;
forceId?: string;
}
export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage;
-export default function Create({ onBack, onConfirm, onError, forceId }:
Props): VNode {
+export default function Create({ onBack, onConfirm, forceId }: Props): VNode {
const { createInstance } = useAdminAPI();
const [notif, setNotif] = useState<Notification | undefined>(undefined)
const [createdOk, setCreatedOk] = useState<Entity | undefined>(undefined);
diff --git a/packages/frontend/src/paths/admin/list/index.tsx
b/packages/frontend/src/paths/admin/list/index.tsx
index bbc094f..3c6a44c 100644
--- a/packages/frontend/src/paths/admin/list/index.tsx
+++ b/packages/frontend/src/paths/admin/list/index.tsx
@@ -21,7 +21,7 @@
import { Fragment, h, VNode } from 'preact';
import { View } from './View';
-import { SwrError } from '../../../hooks/backend';
+import { HttpError, HttpResponseServerError, RequestInfo, SwrError } from
'../../../hooks/backend';
import { useAdminAPI } from "../../../hooks/admin";
import { useState } from 'preact/hooks';
import { MerchantBackend } from '../../../declaration';
@@ -31,24 +31,23 @@ import { Loading } from
'../../../components/exception/loading';
import { useBackendInstances } from '../../../hooks/instance';
interface Props {
- // pushNotification: (n: Notification) => void;
onCreate: () => void;
onUpdate: (id: string) => void;
instances: MerchantBackend.Instances.Instance[];
onUnauthorized: () => VNode;
- onLoadError: (e: SwrError) => VNode;
+ onNotFound: () => VNode;
+ onLoadError: (error: HttpError) => VNode;
}
-export default function Instances({ onUnauthorized, onLoadError, onCreate,
onUpdate }: Props): VNode {
+export default function Instances({ onUnauthorized, onLoadError, onNotFound,
onCreate, onUpdate }: Props): VNode {
const result = useBackendInstances()
const [deleting, setDeleting] = useState<MerchantBackend.Instances.Instance
| null>(null)
const { deleteInstance } = useAdminAPI()
- if (result.unauthorized) return onUnauthorized()
- if (!result.data) {
- if (result.error) return onLoadError(result.error)
- return <Loading />
- }
+ if (result.clientError && result.isUnauthorized) return onUnauthorized()
+ if (result.clientError && result.isNotfound) return onNotFound()
+ if (result.loading) return <Loading />
+ if (!result.ok) return onLoadError(result)
return <Fragment>
<View instances={result.data.instances}
diff --git a/packages/frontend/src/paths/instance/details/index.tsx
b/packages/frontend/src/paths/instance/details/index.tsx
index b212018..b8b63a0 100644
--- a/packages/frontend/src/paths/instance/details/index.tsx
+++ b/packages/frontend/src/paths/instance/details/index.tsx
@@ -16,15 +16,16 @@
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { useInstanceContext } from "../../../context/backend";
-import { SwrError } from "../../../hooks/backend";
+import { HttpError, HttpResponseServerError, RequestInfo, SwrError } from
"../../../hooks/backend";
import { DetailPage } from "./DetailPage";
import { DeleteModal } from "../../../components/modal";
import { Loading } from "../../../components/exception/loading";
import { useInstanceAPI, useInstanceDetails } from "../../../hooks/instance";
+import { MerchantBackend } from "../../../declaration";
interface Props {
onUnauthorized: () => VNode;
- onLoadError: (e: SwrError) => VNode;
+ onLoadError: (error: HttpError) => VNode;
onUpdate: () => void;
onNotFound: () => VNode;
onDelete: () => void;
@@ -37,12 +38,10 @@ export default function Detail({ onUpdate, onLoadError,
onUnauthorized, onDelete
const { deleteInstance } = useInstanceAPI()
- if (result.unauthorized) return onUnauthorized()
- if (result.notfound) return onNotFound();
- if (!result.data) {
- if (result.error) return onLoadError(result.error)
- return <Loading />
- }
+ if (result.clientError && result.isUnauthorized) return onUnauthorized()
+ if (result.clientError && result.isNotfound) return onNotFound()
+ if (result.loading) return <Loading />
+ if (!result.ok) return onLoadError(result)
return <Fragment>
<DetailPage
diff --git
a/packages/frontend/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
b/packages/frontend/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
index 2dbf916..091c4d0 100644
---
a/packages/frontend/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
+++
b/packages/frontend/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
@@ -30,8 +30,8 @@ export function OrderCreatedSuccessfully({ entity, onConfirm,
onCreateAnother }:
const [url, setURL] = useState<string | undefined>(undefined)
useEffect(() => {
- getPaymentURL(entity.response.order_id).then(url => {
- setURL(url)
+ getPaymentURL(entity.response.order_id).then(response => {
+ setURL(response.data)
})
},[entity.response.order_id])
diff --git a/packages/frontend/src/paths/instance/orders/create/index.tsx
b/packages/frontend/src/paths/instance/orders/create/index.tsx
index 9f323df..9d978c2 100644
--- a/packages/frontend/src/paths/instance/orders/create/index.tsx
+++ b/packages/frontend/src/paths/instance/orders/create/index.tsx
@@ -58,7 +58,7 @@ export default function ({ onConfirm, onBack }: Props): VNode
{
onBack={onBack}
onCreate={(request: MerchantBackend.Orders.PostOrderRequest) => {
createOrder(request).then((response) => {
- setCreatedOk({ request, response })
+ setCreatedOk({ request, response: response.data })
}).catch((error) => {
setNotif({
message: 'could not create order',
diff --git a/packages/frontend/src/paths/instance/orders/details/index.tsx
b/packages/frontend/src/paths/instance/orders/details/index.tsx
index bad41d2..dbd23d6 100644
--- a/packages/frontend/src/paths/instance/orders/details/index.tsx
+++ b/packages/frontend/src/paths/instance/orders/details/index.tsx
@@ -16,7 +16,9 @@
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Loading } from "../../../../components/exception/loading";
-import { SwrError } from "../../../../hooks/backend";
+import { NotificationCard } from "../../../../components/menu";
+import { MerchantBackend } from "../../../../declaration";
+import { HttpError, HttpResponseServerError, RequestInfo } from
"../../../../hooks/backend";
import { useOrderDetails, useOrderAPI } from "../../../../hooks/order";
import { Notification } from "../../../../utils/types";
import { DetailPage } from "./DetailPage";
@@ -27,23 +29,23 @@ export interface Props {
onBack: () => void;
onUnauthorized: () => VNode;
onNotFound: () => VNode;
- onLoadError: (e: SwrError) => VNode;
+ onLoadError: (error: HttpError) => VNode;
}
export default function Update({ oid, onBack, onLoadError, onNotFound,
onUnauthorized }: Props): VNode {
const { refundOrder } = useOrderAPI();
- const details = useOrderDetails(oid)
+ const result = useOrderDetails(oid)
const [notif, setNotif] = useState<Notification | undefined>(undefined)
- if (details.unauthorized) return onUnauthorized()
- if (details.notfound) return onNotFound();
-
- if (!details.data) {
- if (details.error) return onLoadError(details.error)
- return <Loading />
- }
+ if (result.clientError && result.isUnauthorized) return onUnauthorized()
+ if (result.clientError && result.isNotfound) return onNotFound()
+ if (result.loading) return <Loading />
+ if (!result.ok) return onLoadError(result)
return <Fragment>
+
+ <NotificationCard notification={notif} />
+
<DetailPage
onBack={onBack}
id={oid}
@@ -57,7 +59,7 @@ export default function Update({ oid, onBack, onLoadError,
onNotFound, onUnautho
description: error.message
}))
}
- selected={details.data}
+ selected={result.data}
/>
</Fragment>
}
\ No newline at end of file
diff --git a/packages/frontend/src/paths/instance/orders/list/index.tsx
b/packages/frontend/src/paths/instance/orders/list/index.tsx
index c4c8b25..d846c27 100644
--- a/packages/frontend/src/paths/instance/orders/list/index.tsx
+++ b/packages/frontend/src/paths/instance/orders/list/index.tsx
@@ -22,7 +22,7 @@
import { h, VNode } from 'preact';
import { useState } from 'preact/hooks';
import { MerchantBackend } from '../../../../declaration';
-import { SwrError } from '../../../../hooks/backend';
+import { HttpError, HttpResponseServerError, RequestInfo, SwrError } from
'../../../../hooks/backend';
import { CardTable } from './Table';
import { format } from 'date-fns';
import { DatePicker } from '../../../../components/form/DatePicker';
@@ -30,10 +30,11 @@ import { NotificationCard } from
'../../../../components/menu';
import { Notification } from '../../../../utils/types';
import { copyToClipboard } from '../../../../utils/functions';
import { InstanceOrderFilter, useInstanceOrders, useOrderAPI } from
'../../../../hooks/order';
+import { Loading } from '../../../../components/exception/loading';
interface Props {
onUnauthorized: () => VNode;
- onLoadError: (e: SwrError) => VNode;
+ onLoadError: (error: HttpError) => VNode;
onNotFound: () => VNode;
onSelect: (id: string) => void;
onCreate: () => void;
@@ -47,24 +48,14 @@ export default function ({ onUnauthorized, onLoadError,
onCreate, onSelect, onNo
const setNewDate = (date: Date) => setFilter(prev => ({ ...prev, date }))
const result = useInstanceOrders(filter, setNewDate)
- const { createOrder, refundOrder, getPaymentURL } = useOrderAPI()
- // const { currency } = useConfigContext()
-
- let instances: (MerchantBackend.Orders.OrderHistoryEntry & { id: string })[];
+ const { refundOrder, getPaymentURL } = useOrderAPI()
const [notif, setNotif] = useState<Notification | undefined>(undefined)
- if (result.unauthorized) return onUnauthorized()
- if (result.notfound) return onNotFound()
- if (!result.data) {
- if (result.error) return onLoadError(result.error)
- instances = []
- } else {
- instances = result.data.orders.map(o => ({ ...o, id: o.order_id }))
- }
- interface Values {
- filter: []
- }
+ if (result.clientError && result.isUnauthorized) return onUnauthorized()
+ if (result.clientError && result.isNotfound) return onNotFound()
+ if (result.loading) return <Loading />
+ if (!result.ok) return onLoadError(result)
const isPaidActive = filter.paid === 'yes' ? "is-active" : ''
const isRefundedActive = filter.refunded === 'yes' ? "is-active" : ''
@@ -72,11 +63,6 @@ export default function ({ onUnauthorized, onLoadError,
onCreate, onSelect, onNo
const isAllActive = filter.paid === undefined && filter.refunded ===
undefined && filter.wired === undefined ? 'is-active' : ''
return <section class="section is-main-section">
- <NotificationCard notification={{
- message: 'DEMO WARNING',
- type: 'WARN',
- description: 'refund button is being forced in the first row, other
depends on the refundable property'
- }} />
<NotificationCard notification={notif} />
<div class="columns">
@@ -117,10 +103,10 @@ export default function ({ onUnauthorized, onLoadError,
onCreate, onSelect, onNo
dateReceiver={setNewDate}
/>
- <CardTable instances={instances}
+ <CardTable instances={result.data.orders.map(o => ({ ...o, id: o.order_id
}))}
onCreate={onCreate}
onSelect={(order) => onSelect(order.id)}
- onCopyURL={(id) => getPaymentURL(id).then(copyToClipboard)}
+ onCopyURL={(id) => getPaymentURL(id).then((resp) =>
copyToClipboard(resp.data))}
onRefund={(id, value) => refundOrder(id, value)
.then(() => setNotif({
message: 'refund created successfully',
diff --git a/packages/frontend/src/paths/instance/products/list/index.tsx
b/packages/frontend/src/paths/instance/products/list/index.tsx
index 5578670..9eff8f4 100644
--- a/packages/frontend/src/paths/instance/products/list/index.tsx
+++ b/packages/frontend/src/paths/instance/products/list/index.tsx
@@ -21,7 +21,7 @@
import { h, VNode } from 'preact';
import { create } from 'yup/lib/Reference';
-import { SwrError } from '../../../../hooks/backend';
+import { HttpError, HttpResponseServerError, RequestInfo } from
'../../../../hooks/backend';
import { useProductAPI } from "../../../../hooks/product";
import { CardTable } from './Table';
import logo from '../../../../assets/logo.jpeg';
@@ -34,25 +34,23 @@ import { NotificationCard } from
'../../../../components/menu';
interface Props {
onUnauthorized: () => VNode;
onNotFound: () => VNode;
- onLoadError: (e: SwrError) => VNode;
+ onLoadError: (e: HttpError) => VNode;
}
export default function ({ onUnauthorized, onLoadError, onNotFound }: Props):
VNode {
const result = useInstanceProducts()
const { createProduct, deleteProduct } = useProductAPI()
const { currency } = useConfigContext()
- if (result.unauthorized) return onUnauthorized()
- if (result.notfound) return onNotFound()
-
- if (!result.data) {
- if (result.error) return onLoadError(result.error)
- return <Loading />
- }
+ if (result.clientError && result.isUnauthorized) return onUnauthorized()
+ if (result.clientError && result.isNotfound) return onNotFound()
+ if (result.loading) return <Loading />
+ if (!result.ok) return onLoadError(result)
+
return <section class="section is-main-section">
<NotificationCard notification={{
message: 'DEMO',
- type:'WARN',
- description:<ul>
+ type: 'WARN',
+ description: <ul>
<li>image return object when api says string</li>
</ul>
}} />
@@ -72,7 +70,7 @@ export default function ({ onUnauthorized, onLoadError,
onNotFound }: Props): VN
unit: 'units',
next_restock: { t_ms: 'never' }, //WTF? should not be required
})}
- onDelete={(prod: (MerchantBackend.Products.ProductDetail & {id:string}))
=> deleteProduct(prod.id)}
+ onDelete={(prod: (MerchantBackend.Products.ProductDetail & { id: string
})) => deleteProduct(prod.id)}
onUpdate={() => null}
/>
</section>
diff --git a/packages/frontend/src/paths/instance/tips/list/index.tsx
b/packages/frontend/src/paths/instance/tips/list/index.tsx
index 50d02e4..5d4cbd1 100644
--- a/packages/frontend/src/paths/instance/tips/list/index.tsx
+++ b/packages/frontend/src/paths/instance/tips/list/index.tsx
@@ -23,13 +23,13 @@ import { h, VNode } from 'preact';
import { Loading } from '../../../../components/exception/loading';
import { useConfigContext } from '../../../../context/backend';
import { MerchantBackend } from '../../../../declaration';
-import { SwrError } from '../../../../hooks/backend';
+import { HttpError, HttpResponseServerError, RequestInfo } from
'../../../../hooks/backend';
import { useInstanceTips, useTipsMutateAPI } from "../../../../hooks/tips";
import { CardTable } from './Table';
interface Props {
onUnauthorized: () => VNode;
- onLoadError: (e: SwrError) => VNode;
+ onLoadError: (e: HttpError) => VNode;
onNotFound: () => VNode;
}
export default function ({ onUnauthorized, onLoadError, onNotFound }: Props):
VNode {
@@ -37,14 +37,11 @@ export default function ({ onUnauthorized, onLoadError,
onNotFound }: Props): VN
const { createReserve, deleteReserve } = useTipsMutateAPI()
const { currency } = useConfigContext()
- if (result.unauthorized) return onUnauthorized()
- if (result.notfound) return onNotFound()
+ if (result.clientError && result.isUnauthorized) return onUnauthorized()
+ if (result.clientError && result.isNotfound) return onNotFound()
+ if (result.loading) return <Loading />
+ if (!result.ok) return onLoadError(result)
- if (!result.data) {
- if (result.error) return onLoadError(result.error)
- return <Loading />
- }
-
return <section class="section is-main-section">
<CardTable instances={result.data.reserves.filter(r => r.active).map(o =>
({ ...o, id: o.reserve_pub }))}
onCreate={() => createReserve({
diff --git a/packages/frontend/src/paths/instance/transfers/list/index.tsx
b/packages/frontend/src/paths/instance/transfers/list/index.tsx
index e8e20f4..eaa3c06 100644
--- a/packages/frontend/src/paths/instance/transfers/list/index.tsx
+++ b/packages/frontend/src/paths/instance/transfers/list/index.tsx
@@ -22,27 +22,25 @@
import { h, VNode } from 'preact';
import { Loading } from '../../../../components/exception/loading';
import { useConfigContext } from '../../../../context/backend';
-import { SwrError } from '../../../../hooks/backend';
+import { MerchantBackend } from '../../../../declaration';
+import { HttpError, HttpResponseServerError, RequestInfo, SwrError } from
'../../../../hooks/backend';
import { useInstanceTransfers, useTransferMutateAPI } from
"../../../../hooks/transfer";
import { CardTable } from './Table';
interface Props {
onUnauthorized: () => VNode;
- onLoadError: (e: SwrError) => VNode;
+ onLoadError: (error: HttpError) => VNode;
onNotFound: () => VNode;
}
export default function ({ onUnauthorized, onLoadError, onNotFound }: Props):
VNode {
const result = useInstanceTransfers()
const { informTransfer } = useTransferMutateAPI()
- const { currency } = useConfigContext()
- if (result.unauthorized) return onUnauthorized()
- if (result.notfound) return onNotFound();
+ if (result.clientError && result.isUnauthorized) return onUnauthorized()
+ if (result.clientError && result.isNotfound) return onNotFound()
+ if (result.loading) return <Loading />
+ if (!result.ok) return onLoadError(result)
- if (!result.data) {
- if (result.error) return onLoadError(result.error)
- return <Loading />
- }
return <section class="section is-main-section">
<CardTable instances={result.data.transfers.map(o => ({ ...o, id:
String(o.transfer_serial_id) }))}
onCreate={() => informTransfer({
diff --git a/packages/frontend/src/paths/instance/update/index.tsx
b/packages/frontend/src/paths/instance/update/index.tsx
index 41c450e..f26a5d2 100644
--- a/packages/frontend/src/paths/instance/update/index.tsx
+++ b/packages/frontend/src/paths/instance/update/index.tsx
@@ -19,7 +19,7 @@ import { Loading } from
"../../../components/exception/loading";
import { UpdateTokenModal } from "../../../components/modal";
import { useInstanceContext } from "../../../context/backend";
import { MerchantBackend } from "../../../declaration";
-import { SwrError } from "../../../hooks/backend";
+import { HttpError, HttpResponseServerError, RequestInfo, SwrError } from
"../../../hooks/backend";
import { useInstanceAPI, useInstanceDetails } from "../../../hooks/instance";
import { UpdatePage } from "./UpdatePage";
@@ -29,28 +29,25 @@ export interface Props {
onUnauthorized: () => VNode;
onNotFound: () => VNode;
- onLoadError: (e: SwrError) => VNode;
+ onLoadError: (e: HttpError) => VNode;
onUpdateError: (e: Error) => void;
}
export default function Update({ onBack, onConfirm, onLoadError, onNotFound,
onUpdateError, onUnauthorized }: Props): VNode {
const { updateInstance } = useInstanceAPI();
- const details = useInstanceDetails()
+ const result = useInstanceDetails()
- if (details.unauthorized) return onUnauthorized()
- if (details.notfound) return onNotFound();
-
- if (!details.data) {
- if (details.error) return onLoadError(details.error)
- return <Loading />
- }
+ if (result.clientError && result.isUnauthorized) return onUnauthorized()
+ if (result.clientError && result.isNotfound) return onNotFound()
+ if (result.loading) return <Loading />
+ if (!result.ok) return onLoadError(result)
return <Fragment>
<UpdatePage
onBack={onBack}
isLoading={false}
- selected={details.data}
+ selected={result.data}
onUpdate={(d: MerchantBackend.Instances.InstanceReconfigurationMessage,
t?: MerchantBackend.Instances.InstanceAuthConfigurationMessage): Promise<void>
=> {
return updateInstance(d, t).then(onConfirm).catch(onUpdateError)
}} />
diff --git a/packages/frontend/src/utils/constants.ts
b/packages/frontend/src/utils/constants.ts
index a65f58b..8d642d7 100644
--- a/packages/frontend/src/utils/constants.ts
+++ b/packages/frontend/src/utils/constants.ts
@@ -30,4 +30,4 @@ export const INSTANCE_ID_LOOKUP = /^\/instances\/([^/]*)\/?$/
export const PAGE_SIZE = 20
// how bigger can be the result set
// after this threshold, load more with move the cursor
-export const MAX_RESULT_SIZE = 39;
\ No newline at end of file
+export const MAX_RESULT_SIZE = PAGE_SIZE*2-1;
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-merchant-backoffice] branch master updated: better error handling,
gnunet <=