diff options
author | Özgür Kesim <oec-taler@kesim.org> | 2023-10-06 16:33:05 +0200 |
---|---|---|
committer | Özgür Kesim <oec-taler@kesim.org> | 2023-10-06 16:33:05 +0200 |
commit | fe7b51ef2736edbf04f5bbd9d19f2a2d04baccc2 (patch) | |
tree | 66c68c8d6a666f6e74dc663c9ee4f07879f6626c /packages/merchant-backoffice-ui/src/paths/instance | |
parent | 35611f0bf9cf67638b171c2a300fab1797d3d8f0 (diff) | |
parent | 97d7be7503168f4f3bbd05905d32aa76ca1636b2 (diff) |
Merge branch 'master' into age-withdraw
Diffstat (limited to 'packages/merchant-backoffice-ui/src/paths/instance')
11 files changed, 153 insertions, 86 deletions
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx index cbfe1d573..db73217ed 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx @@ -66,7 +66,7 @@ export function CardTable({ <span class="icon"> <i class="mdi mdi-shopping" /> </span> - <i18n.Translate>Products</i18n.Translate> + <i18n.Translate>Inventory</i18n.Translate> </p> <div class="card-header-icon" aria-label="more options"> <span @@ -142,7 +142,7 @@ function Table({ <i18n.Translate>Taxes</i18n.Translate> </th> <th> - <i18n.Translate>Profit</i18n.Translate> + <i18n.Translate>Sales</i18n.Translate> </th> <th> <i18n.Translate>Stock</i18n.Translate> @@ -190,18 +190,21 @@ function Table({ src={i.image ? i.image : emptyImage} style={{ border: "solid black 1px", - width: 100, - height: 100, + maxHeight: "2em", + width: "auto", + height: "auto", }} /> </td> <td + class="has-tooltip-right" + data-tooltip={i.description} onClick={() => rowSelection !== i.id && rowSelectionHandler(i.id) } style={{ cursor: "pointer" }} > - {i.description} + {i.description.length > 30 ? i.description.substring(0, 30) + "..." : i.description} </td> <td onClick={() => diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx index 85c50e5ed..274a7c2ea 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx @@ -53,7 +53,7 @@ export default function ProductList({ onNotFound, }: Props): VNode { const result = useInstanceProducts(); - const { deleteProduct, updateProduct } = useProductAPI(); + const { deleteProduct, updateProduct, getProduct } = useProductAPI(); const [deleting, setDeleting] = useState<MerchantBackend.Products.ProductDetail & WithId | null>(null); const [notif, setNotif] = useState<Notification | undefined>(undefined); @@ -74,11 +74,61 @@ export default function ProductList({ return onNotFound(); return onLoadError(result); } + const [errorId, setErrorId] = useState<string | undefined>( + undefined, + ); + + const [productId, setProductId] = useState<string>() + async function testIfProductExistAndSelect(orderId: string | undefined): Promise<void> { + if (!orderId) { + setErrorId(i18n.str`Enter a product id`); + return; + } + try { + await getProduct(orderId); + onSelect(orderId); + setErrorId(undefined); + } catch { + setErrorId(i18n.str`product not found`); + } + } return ( <section class="section is-main-section"> <NotificationCard notification={notif} /> + <div class="level"> + <div class="level-left"> + <div class="level-item"> + <div class="field has-addons"> + <div class="control"> + <input + class={errorId ? "input is-danger" : "input"} + type="text" + value={productId ?? ""} + onChange={(e) => setProductId(e.currentTarget.value)} + placeholder={i18n.str`product id`} + /> + {errorId && <p class="help is-danger">{errorId}</p>} + </div> + <span + class="has-tooltip-bottom" + data-tooltip={i18n.str`jump to product with the given product ID`} + > + <button + class="button" + onClick={(e) => testIfProductExistAndSelect(productId)} + > + <span class="icon"> + <i class="mdi mdi-arrow-right" /> + </span> + </button> + </span> + </div> + </div> + </div> + </div> + <CardTable instances={result.data} onCreate={onCreate} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx index 2201e75a5..0d2bb2c30 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { useTranslationContext } from "@gnu-taler/web-util/browser"; +import { HttpError, RequestError, useApiContext, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, h, VNode } from "preact"; import { StateUpdater, useEffect, useState } from "preact/hooks"; import { AsyncButton } from "../../../../components/exception/AsyncButton.js"; @@ -35,6 +35,7 @@ import { PAYTO_WIRE_METHOD_LOOKUP, URL_REGEX, } from "../../../../utils/constants.js"; +import { useBackendBaseRequest } from "../../../../hooks/backend.js"; type Entity = MerchantBackend.Rewards.ReserveCreateRequest; @@ -65,6 +66,7 @@ function ViewStep({ setReserve, }: ViewProps): VNode { const { i18n } = useTranslationContext(); + const {request} = useApiContext() const [wireMethods, setWireMethods] = useState<Array<string>>([]); const [exchangeQueryError, setExchangeQueryError] = useState< string | undefined @@ -123,19 +125,26 @@ function ViewStep({ <AsyncButton class="has-tooltip-left" onClick={() => { - return fetch(`${reserve.exchange_url}wire`) - .then((r) => r.json()) + if (!reserve.exchange_url) { + return Promise.resolve(); + } + + return request<any>(reserve.exchange_url, "keys") //</div>fetch(`${reserve.exchange_url}wire`) .then((r) => { - const wireMethods = r.accounts.map((a: any) => { - const match = PAYTO_WIRE_METHOD_LOOKUP.exec(a.payto_uri); - return (match && match[1]) || ""; - }); + if (r.loading) return; + if (r.ok) { + const wireMethods = r.data.accounts.map((a: any) => { + const match = PAYTO_WIRE_METHOD_LOOKUP.exec(a.payto_uri); + return (match && match[1]) || ""; + }); + } setWireMethods(wireMethods); setCurrentStep(Steps.WIRE_METHOD); return; }) - .catch((r: any) => { - setExchangeQueryError(r.message); + .catch((r: RequestError<{}>) => { + console.log(r.cause) + setExchangeQueryError(r.cause.message); }); }} data-tooltip={ diff --git a/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx index 4b0db200a..89dba63b2 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx @@ -30,13 +30,13 @@ import { AccessToken } from "../../../declaration.js"; interface Props { instanceId: string; - currentToken: string | undefined; - onClearToken: () => void; - onNewToken: (s: AccessToken) => void; + hasToken: boolean | undefined; + onClearToken: (c: AccessToken | undefined) => void; + onNewToken: (c: AccessToken | undefined, s: AccessToken) => void; onBack?: () => void; } -export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewToken, onClearToken }: Props): VNode { +export function DetailPage({ instanceId, hasToken, onBack, onNewToken, onClearToken }: Props): VNode { type State = { old_token: string; new_token: string; repeat_token: string }; const [form, setValue] = useState<Partial<State>>({ old_token: "", @@ -45,11 +45,9 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo }); const { i18n } = useTranslationContext(); - const hasOldtoken = !!oldToken - const hasInputTheCorrectOldToken = hasOldtoken && oldToken !== form.old_token; const errors = { - old_token: hasInputTheCorrectOldToken - ? i18n.str`is not the same as the current access token` + old_token: hasToken && !form.old_token + ? i18n.str`you need your access token to perform the operation` : undefined, new_token: !form.new_token ? i18n.str`cannot be empty` @@ -72,8 +70,9 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo async function submitForm() { if (hasErrors) return; + const ot = hasToken ? `secret-token:${form.old_token}` as AccessToken : undefined; const nt = `secret-token:${form.new_token}` as AccessToken; - onNewToken(nt) + onNewToken(ot, nt) } return ( @@ -98,32 +97,38 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo <div class="column" /> <div class="column is-four-fifths"> <FormProvider errors={errors} object={form} valueHandler={setValue}> - {hasOldtoken && ( - <Input<State> - name="old_token" - label={i18n.str`Current access token`} - tooltip={i18n.str`access token currently in use`} - inputType="password" - /> - )} - {!hasInputTheCorrectOldToken && <Fragment> - {hasOldtoken && <Fragment> - <p> - <i18n.Translate> - Clearing the access token will mean public access to the instance. - </i18n.Translate> - </p> - <div class="buttons is-right mt-5"> - <button - disabled={!!hasInputTheCorrectOldToken} - class="button" - onClick={onClearToken} - > - <i18n.Translate>Clear token</i18n.Translate> - </button> - </div> - </Fragment> - } + <Fragment> + {hasToken && ( + <Fragment> + <Input<State> + name="old_token" + label={i18n.str`Current access token`} + tooltip={i18n.str`access token currently in use`} + inputType="password" + /> + <p> + <i18n.Translate> + Clearing the access token will mean public access to the instance. + </i18n.Translate> + </p> + <div class="buttons is-right mt-5"> + <button + class="button" + onClick={() => { + if (hasToken) { + const ot = `secret-token:${form.old_token}` as AccessToken; + onClearToken(ot) + } else { + onClearToken(undefined) + } + }} + > + <i18n.Translate>Clear token</i18n.Translate> + </button> + </div> + </Fragment> + )} + <Input<State> name="new_token" @@ -137,7 +142,7 @@ export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewTo tooltip={i18n.str`confirm the same access token`} inputType="password" /> - </Fragment>} + </Fragment> </FormProvider> <div class="buttons is-right mt-5"> {onBack && ( diff --git a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx index 0a49448f8..bc2bd9fa3 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx @@ -33,8 +33,6 @@ interface Props { onNotFound: () => VNode; } -const PREFIX = "secret-token:" - export default function Token({ onLoadError, onChange, @@ -44,21 +42,36 @@ export default function Token({ const { i18n } = useTranslationContext(); const [notif, setNotif] = useState<Notification | undefined>(undefined); - const { clearToken, setNewToken } = useInstanceAPI(); - const { token: rootToken } = useBackendContext(); - const { token: instanceToken, id, admin } = useInstanceContext(); + const { clearAccessToken, setNewAccessToken } = useInstanceAPI(); + const { id } = useInstanceContext(); + const result = useInstanceDetails() + + if (result.loading) return <Loading />; + if (!result.ok) { + if ( + result.type === ErrorType.CLIENT && + result.status === HttpStatusCode.Unauthorized + ) + return onUnauthorized(); + if ( + result.type === ErrorType.CLIENT && + result.status === HttpStatusCode.NotFound + ) + return onNotFound(); + return onLoadError(result); + } + + const hasToken = result.data.auth.method === "token" - const currentToken = !admin ? rootToken : instanceToken - const hasPrefix = currentToken !== undefined && currentToken.token.startsWith(PREFIX) return ( <Fragment> <NotificationCard notification={notif} /> <DetailPage instanceId={id} - currentToken={hasPrefix ? currentToken.token.substring(PREFIX.length) : currentToken?.token} - onClearToken={async (): Promise<void> => { + hasToken={hasToken} + onClearToken={async (currentToken): Promise<void> => { try { - await clearToken(); + await clearAccessToken(currentToken); onChange(); } catch (error) { if (error instanceof Error) { @@ -70,9 +83,9 @@ export default function Token({ } } }} - onNewToken={async (newToken): Promise<void> => { + onNewToken={async (currentToken, newToken): Promise<void> => { try { - await setNewToken(newToken); + await setNewAccessToken(currentToken, newToken); onChange(); } catch (error) { if (error instanceof Error) { diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx index a1c608f15..01a3d0252 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx @@ -39,9 +39,6 @@ type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & { //MerchantBackend.Instances.InstanceAuthConfigurationMessage interface Props { onUpdate: (d: Entity) => void; - onChangeAuth: ( - d: MerchantBackend.Instances.InstanceAuthConfigurationMessage, - ) => Promise<void>; selected: MerchantBackend.Instances.QueryInstancesResponse; isLoading: boolean; onBack: () => void; @@ -78,7 +75,6 @@ function getTokenValuePart(t?: string): string | undefined { export function UpdatePage({ onUpdate, - onChangeAuth, selected, onBack, }: Props): VNode { diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx index 6c5e7a514..e44cf5c0f 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx @@ -46,17 +46,17 @@ export interface Props { } export default function Update(props: Props): VNode { - const { updateInstance, clearToken, setNewToken } = useInstanceAPI(); + const { updateInstance } = useInstanceAPI(); const result = useInstanceDetails(); - return CommonUpdate(props, result, updateInstance, clearToken, setNewToken); + return CommonUpdate(props, result, updateInstance, ); } export function AdminUpdate(props: Props & { instanceId: string }): VNode { - const { updateInstance, clearToken, setNewToken } = useManagementAPI( + const { updateInstance } = useManagementAPI( props.instanceId, ); const result = useManagedInstanceDetails(props.instanceId); - return CommonUpdate(props, result, updateInstance, clearToken, setNewToken); + return CommonUpdate(props, result, updateInstance, ); } function CommonUpdate( @@ -73,8 +73,6 @@ function CommonUpdate( MerchantBackend.ErrorDetail >, updateInstance: any, - clearToken: () => Promise<void>, - setNewToken: (t: AccessToken) => Promise<void>, ): VNode { const [notif, setNotif] = useState<Notification | undefined>(undefined); const { i18n } = useTranslationContext(); @@ -114,13 +112,6 @@ function CommonUpdate( }), ); }} - onChangeAuth={( - d: MerchantBackend.Instances.InstanceAuthConfigurationMessage, - ): Promise<void> => { - const apiCall = - d.method === "external" ? clearToken() : setNewToken(d.token! as AccessToken); - return apiCall.then(onConfirm).catch(onUpdateError); - }} /> </Fragment> ); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx index bdc86d226..cebc1ade6 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx @@ -70,8 +70,8 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { : state.otp_key.length !== 32 ? i18n.str`size of the key should be 32` : undefined, - otp_description: !state.otp_description ? i18n.str`required` - : !/[a-zA-Z0-9]*/.test(state.otp_description) + otp_device_description: !state.otp_device_description ? i18n.str`required` + : !/[a-zA-Z0-9]*/.test(state.otp_device_description) ? i18n.str`no valid. only characters and numbers` : undefined, @@ -103,7 +103,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { tooltip={i18n.str`Internal id on the system`} /> <Input<Entity> - name="otp_description" + name="otp_device_description" label={i18n.str`Descripiton`} tooltip={i18n.str`Useful to identify the device physically`} /> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatedSuccessfully.tsx index 22ae55677..db3842711 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatedSuccessfully.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatedSuccessfully.tsx @@ -77,7 +77,7 @@ export function CreatedSuccessfully({ <input class="input" readonly - value={entity.otp_description} + value={entity.otp_device_description} /> </p> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/UpdatePage.tsx index 585c12e11..79be9802f 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/validators/update/UpdatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/UpdatePage.tsx @@ -87,7 +87,7 @@ export function UpdatePage({ device, onUpdate, onBack }: Props): VNode { errors={errors} > <Input<Entity> - name="otp_description" + name="otp_device_description" label={i18n.str`Description`} tooltip={i18n.str`dddd`} /> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx index 9a27ccfee..52f6c6c29 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx @@ -80,7 +80,7 @@ export default function UpdateValidator({ device={{ id: vid, otp_algorithm: result.data.otp_algorithm, - otp_description: result.data.device_description, + otp_device_description: result.data.device_description, otp_key: undefined, otp_ctr: result.data.otp_ctr }} |