From eebb85bef4bb6bba41533fa0ff343cf2f1995761 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 27 Jan 2023 15:08:03 -0300 Subject: webhook api --- .../merchant-backoffice-ui/src/InstanceRoutes.tsx | 46 +++++ .../src/components/menu/SideBar.tsx | 10 + .../merchant-backoffice-ui/src/declaration.d.ts | 76 +++++++ .../merchant-backoffice-ui/src/hooks/backend.ts | 23 +++ .../merchant-backoffice-ui/src/hooks/webhooks.ts | 165 +++++++++++++++ .../src/paths/instance/templates/list/Table.tsx | 2 +- .../instance/webhooks/create/Create.stories.tsx | 28 +++ .../paths/instance/webhooks/create/CreatePage.tsx | 140 +++++++++++++ .../src/paths/instance/webhooks/create/index.tsx | 61 ++++++ .../paths/instance/webhooks/list/List.stories.tsx | 28 +++ .../src/paths/instance/webhooks/list/ListPage.tsx | 64 ++++++ .../src/paths/instance/webhooks/list/Table.tsx | 225 +++++++++++++++++++++ .../src/paths/instance/webhooks/list/index.tsx | 95 +++++++++ .../instance/webhooks/update/Update.stories.tsx | 32 +++ .../paths/instance/webhooks/update/UpdatePage.tsx | 146 +++++++++++++ .../src/paths/instance/webhooks/update/index.tsx | 85 ++++++++ 16 files changed, 1225 insertions(+), 1 deletion(-) create mode 100644 packages/merchant-backoffice-ui/src/hooks/webhooks.ts create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/Create.stories.tsx create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/CreatePage.tsx create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/index.tsx create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/List.stories.tsx create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/Update.stories.tsx create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx create mode 100644 packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx (limited to 'packages/merchant-backoffice-ui') diff --git a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx index 3be793ada..56f223620 100644 --- a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx +++ b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx @@ -51,6 +51,9 @@ import TemplateCreatePage from "./paths/instance/templates/create/index.js"; import TemplateUsePage from "./paths/instance/templates/use/index.js"; import TemplateListPage from "./paths/instance/templates/list/index.js"; import TemplateUpdatePage from "./paths/instance/templates/update/index.js"; +import WebhookCreatePage from "./paths/instance/webhooks/create/index.js"; +import WebhookListPage from "./paths/instance/webhooks/list/index.js"; +import WebhookUpdatePage from "./paths/instance/webhooks/update/index.js"; import TransferCreatePage from "./paths/instance/transfers/create/index.js"; import TransferListPage from "./paths/instance/transfers/list/index.js"; import InstanceUpdatePage, { @@ -87,6 +90,10 @@ export enum InstancePaths { templates_update = "/templates/:tid/update", templates_new = "/templates/new", templates_use = "/templates/:tid/use", + + webhooks_list = "/webhooks", + webhooks_update = "/webhooks/:tid/update", + webhooks_new = "/webhooks/new", } // eslint-disable-next-line @typescript-eslint/no-empty-function @@ -389,6 +396,45 @@ export function InstanceRoutes({ route(InstancePaths.transfers_list); }} /> + {/** + * Webhooks pages + */} + { + route(InstancePaths.webhooks_new); + }} + onSelect={(id: string) => { + route(InstancePaths.webhooks_update.replace(":tid", id)); + }} + /> + { + route(InstancePaths.webhooks_list); + }} + onUnauthorized={LoginPageAccessDenied} + onLoadError={ServerErrorRedirectTo(InstancePaths.webhooks_list)} + onNotFound={IfAdminCreateDefaultOr(NotFoundPage)} + onBack={() => { + route(InstancePaths.webhooks_list); + }} + /> + { + route(InstancePaths.webhooks_list); + }} + onBack={() => { + route(InstancePaths.webhooks_list); + }} + /> {/** * Templates pages */} diff --git a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx index 92d144b1a..c7ece9ca2 100644 --- a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx +++ b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx @@ -140,6 +140,16 @@ export function Sidebar({ Reserves +
  • + + + + + + Webhooks + + +
  • {needKYC && (
  • diff --git a/packages/merchant-backoffice-ui/src/declaration.d.ts b/packages/merchant-backoffice-ui/src/declaration.d.ts index b0621c13c..32e6b44ea 100644 --- a/packages/merchant-backoffice-ui/src/declaration.d.ts +++ b/packages/merchant-backoffice-ui/src/declaration.d.ts @@ -1360,6 +1360,82 @@ export namespace MerchantBackend { } } + namespace Webhooks { + interface WebhookAddDetails { + + // Webhook ID to use. + webhook_id: string; + + // The event of the webhook: why the webhook is used. + event_type: string; + + // URL of the webhook where the customer will be redirected. + url: string; + + // Method used by the webhook + http_method: string; + + // Header template of the webhook + header_template?: string; + + // Body template by the webhook + body_template?: string; + + } + interface WebhookPatchDetails { + + // The event of the webhook: why the webhook is used. + event_type: string; + + // URL of the webhook where the customer will be redirected. + url: string; + + // Method used by the webhook + http_method: string; + + // Header template of the webhook + header_template?: string; + + // Body template by the webhook + body_template?: string; + + } + interface WebhookSummaryResponse { + + // List of webhooks that are present in our backend. + webhooks: WebhookEntry[]; + + } + interface WebhookEntry { + + // Webhook identifier, as found in the webhook. + webhook_id: string; + + // The event of the webhook: why the webhook is used. + event_type: string; + + } + interface WebhookDetails { + + // The event of the webhook: why the webhook is used. + event_type: string; + + // URL of the webhook where the customer will be redirected. + url: string; + + // Method used by the webhook + http_method: string; + + // Header template of the webhook + header_template?: string; + + // Body template by the webhook + body_template?: string; + + } + + } + interface ContractTerms { // Human-readable description of the whole purchase summary: string; diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts b/packages/merchant-backoffice-ui/src/hooks/backend.ts index a0639a4a0..3f3db2fa1 100644 --- a/packages/merchant-backoffice-ui/src/hooks/backend.ts +++ b/packages/merchant-backoffice-ui/src/hooks/backend.ts @@ -115,6 +115,11 @@ interface useBackendInstanceRequestType { position?: string, delta?: number, ) => Promise>; + webhookFetcher: ( + path: string, + position?: string, + delta?: number, + ) => Promise>; } interface useBackendBaseRequestType { request: ( @@ -274,6 +279,23 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType { [backend, token], ); + const webhookFetcher = useCallback( + function webhookFetcherImpl( + path: string, + position?: string, + delta?: number, + ): Promise> { + const params: any = {}; + if (delta !== undefined) { + params.limit = delta; + } + if (position !== undefined) params.offset = position; + + return requestHandler(backend, path, { params, token }); + }, + [backend, token], + ); + return { request, fetcher, @@ -283,5 +305,6 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType { tipsDetailFetcher, transferFetcher, templateFetcher, + webhookFetcher, }; } diff --git a/packages/merchant-backoffice-ui/src/hooks/webhooks.ts b/packages/merchant-backoffice-ui/src/hooks/webhooks.ts new file mode 100644 index 000000000..9f196cefa --- /dev/null +++ b/packages/merchant-backoffice-ui/src/hooks/webhooks.ts @@ -0,0 +1,165 @@ +/* + This file is part of GNU Taler + (C) 2021-2023 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ +import { MerchantBackend } from "../declaration.js"; +import { useMatchMutate, useBackendInstanceRequest } from "./backend.js"; +import useSWR from "swr"; +import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js"; +import { useEffect, useState } from "preact/hooks"; +import { + HttpError, + HttpResponse, + HttpResponseOk, + HttpResponsePaginated, +} from "../utils/request.js"; + +export function useWebhookAPI(): WebhookAPI { + const mutateAll = useMatchMutate(); + const { request } = useBackendInstanceRequest(); + + const createWebhook = async ( + data: MerchantBackend.Webhooks.WebhookAddDetails, + ): Promise> => { + const res = await request(`/private/webhooks`, { + method: "POST", + data, + }); + await mutateAll(/.*private\/webhooks.*/); + return res; + }; + + const updateWebhook = async ( + webhookId: string, + data: MerchantBackend.Webhooks.WebhookPatchDetails, + ): Promise> => { + const res = await request(`/private/webhooks/${webhookId}`, { + method: "PATCH", + data, + }); + await mutateAll(/.*private\/webhooks.*/); + return res; + }; + + const deleteWebhook = async ( + webhookId: string, + ): Promise> => { + const res = await request(`/private/webhooks/${webhookId}`, { + method: "DELETE", + }); + await mutateAll(/.*private\/webhooks.*/); + return res; + }; + + return { createWebhook, updateWebhook, deleteWebhook }; +} + +export interface WebhookAPI { + createWebhook: ( + data: MerchantBackend.Webhooks.WebhookAddDetails, + ) => Promise>; + updateWebhook: ( + id: string, + data: MerchantBackend.Webhooks.WebhookPatchDetails, + ) => Promise>; + deleteWebhook: (id: string) => Promise>; +} + +export interface InstanceWebhookFilter { + //FIXME: add filter to the webhook list + position?: string; +} + +export function useInstanceWebhooks( + args?: InstanceWebhookFilter, + updatePosition?: (id: string) => void, +): HttpResponsePaginated { + const { webhookFetcher } = useBackendInstanceRequest(); + + const [pageAfter, setPageAfter] = useState(1); + + const totalAfter = pageAfter * PAGE_SIZE; + + const { + data: afterData, + error: afterError, + isValidating: loadingAfter, + } = useSWR< + HttpResponseOk, + HttpError + >([`/private/webhooks`, args?.position, -totalAfter], webhookFetcher); + + const [lastAfter, setLastAfter] = useState< + HttpResponse + >({ loading: true }); + useEffect(() => { + if (afterData) setLastAfter(afterData); + }, [afterData]); + + if (afterError) return afterError; + + const isReachingEnd = + afterData && afterData.data.webhooks.length < totalAfter; + const isReachingStart = false; + + const pagination = { + isReachingEnd, + isReachingStart, + loadMore: () => { + if (!afterData || isReachingEnd) return; + if (afterData.data.webhooks.length < MAX_RESULT_SIZE) { + setPageAfter(pageAfter + 1); + } else { + const from = `${afterData.data.webhooks[afterData.data.webhooks.length - 1] + .webhook_id + }`; + if (from && updatePosition) updatePosition(from); + } + }, + loadMorePrev: () => { + return + }, + }; + + const webhooks = !afterData ? [] : (afterData || lastAfter).data.webhooks; + + if (loadingAfter) + return { loading: true, data: { webhooks } }; + if (afterData) { + return { ok: true, data: { webhooks }, ...pagination }; + } + return { loading: true }; +} + +export function useWebhookDetails( + webhookId: string, +): HttpResponse { + const { webhookFetcher } = useBackendInstanceRequest(); + + const { data, error, isValidating } = useSWR< + HttpResponseOk, + HttpError + >([`/private/webhooks/${webhookId}`], webhookFetcher, { + refreshInterval: 0, + refreshWhenHidden: false, + revalidateOnFocus: false, + revalidateOnReconnect: false, + refreshWhenOffline: false, + }); + + if (isValidating) return { loading: true, data: data?.data }; + if (data) return data; + if (error) return error; + return { loading: true }; +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx index 6635d6c55..57d328d39 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/Table.tsx @@ -180,7 +180,7 @@ function Table({ + )} + + Confirm + + + +
    +
    + + + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/index.tsx new file mode 100644 index 000000000..9f1c5e905 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/create/index.tsx @@ -0,0 +1,61 @@ +/* + This file is part of GNU Taler + (C) 2021-2023 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { NotificationCard } from "../../../../components/menu/index.js"; +import { MerchantBackend } from "../../../../declaration.js"; +import { useWebhookAPI } from "../../../../hooks/webhooks.js"; +import { Notification } from "../../../../utils/types.js"; +import { CreatePage } from "./CreatePage.js"; + +export type Entity = MerchantBackend.Webhooks.WebhookAddDetails; +interface Props { + onBack?: () => void; + onConfirm: () => void; +} + +export default function CreateWebhook({ onConfirm, onBack }: Props): VNode { + const { createWebhook } = useWebhookAPI(); + const [notif, setNotif] = useState(undefined); + const { i18n } = useTranslationContext(); + + return ( + <> + + { + return createWebhook(request) + .then(() => onConfirm()) + .catch((error) => { + setNotif({ + message: i18n.str`could not inform template`, + type: "ERROR", + description: error.message, + }); + }); + }} + /> + + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/List.stories.tsx new file mode 100644 index 000000000..702e9ba4a --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/List.stories.tsx @@ -0,0 +1,28 @@ +/* + This file is part of GNU Taler + (C) 2021-2023 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { FunctionalComponent, h } from "preact"; +import { ListPage as TestedComponent } from "./ListPage.js"; + +export default { + title: "Pages/Templates/List", + component: TestedComponent, +}; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx new file mode 100644 index 000000000..942a8a63e --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/ListPage.tsx @@ -0,0 +1,64 @@ +/* + This file is part of GNU Taler + (C) 2021-2023 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { h, VNode } from "preact"; +import { MerchantBackend } from "../../../../declaration.js"; +import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; +import { CardTable } from "./Table.js"; + +export interface Props { + webhooks: MerchantBackend.Webhooks.WebhookEntry[]; + onLoadMoreBefore?: () => void; + onLoadMoreAfter?: () => void; + onCreate: () => void; + onDelete: (e: MerchantBackend.Webhooks.WebhookEntry) => void; + onSelect: (e: MerchantBackend.Webhooks.WebhookEntry) => void; +} + +export function ListPage({ + webhooks, + onCreate, + onDelete, + onSelect, + onLoadMoreBefore, + onLoadMoreAfter, +}: Props): VNode { + const form = { payto_uri: "" }; + + const { i18n } = useTranslationContext(); + return ( +
    + ({ + ...o, + id: String(o.webhook_id), + }))} + onCreate={onCreate} + onDelete={onDelete} + onSelect={onSelect} + onLoadMoreBefore={onLoadMoreBefore} + hasMoreBefore={!onLoadMoreBefore} + onLoadMoreAfter={onLoadMoreAfter} + hasMoreAfter={!onLoadMoreAfter} + /> +
    + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx new file mode 100644 index 000000000..1981cabdd --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx @@ -0,0 +1,225 @@ +/* + This file is part of GNU Taler + (C) 2021-2023 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; +import { h, VNode } from "preact"; +import { StateUpdater, useState } from "preact/hooks"; +import { MerchantBackend } from "../../../../declaration.js"; + +type Entity = MerchantBackend.Webhooks.WebhookEntry; + +interface Props { + webhooks: Entity[]; + onDelete: (e: Entity) => void; + onSelect: (e: Entity) => void; + onCreate: () => void; + onLoadMoreBefore?: () => void; + hasMoreBefore?: boolean; + hasMoreAfter?: boolean; + onLoadMoreAfter?: () => void; +} + +export function CardTable({ + webhooks, + onCreate, + onDelete, + onSelect, + onLoadMoreAfter, + onLoadMoreBefore, + hasMoreAfter, + hasMoreBefore, +}: Props): VNode { + const [rowSelection, rowSelectionHandler] = useState([]); + + const { i18n } = useTranslationContext(); + + return ( +
    +
    +

    + + + + Webhooks +

    +
    + + + +
    +
    +
    +
    +
    + {webhooks.length > 0 ? ( + { + console.log("test", d); + }} + rowSelection={rowSelection} + rowSelectionHandler={rowSelectionHandler} + onLoadMoreAfter={onLoadMoreAfter} + onLoadMoreBefore={onLoadMoreBefore} + hasMoreAfter={hasMoreAfter} + hasMoreBefore={hasMoreBefore} + /> + ) : ( + + )} + + + + + ); +} +interface TableProps { + rowSelection: string[]; + instances: Entity[]; + onDelete: (e: Entity) => void; + onNewOrder: (e: Entity) => void; + onSelect: (e: Entity) => void; + rowSelectionHandler: StateUpdater; + onLoadMoreBefore?: () => void; + hasMoreBefore?: boolean; + hasMoreAfter?: boolean; + onLoadMoreAfter?: () => void; +} + +function toggleSelected(id: T): (prev: T[]) => T[] { + return (prev: T[]): T[] => + prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id); +} + +function Table({ + instances, + onLoadMoreAfter, + onDelete, + onNewOrder, + onSelect, + onLoadMoreBefore, + hasMoreAfter, + hasMoreBefore, +}: TableProps): VNode { + const { i18n } = useTranslationContext(); + return ( +
    + {onLoadMoreBefore && ( + + )} +
    + + + + + + + + {instances.map((i) => { + return ( + + + + + + ); + })} + +
    + ID + + Event type + +
    onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.webhook_id} + onSelect(i)} + style={{ cursor: "pointer" }} + > + {i.event_type} + +
    + + {/* */} +
    +
    + {onLoadMoreAfter && ( + + )} +
    + ); +} + +function EmptyTable(): VNode { + const { i18n } = useTranslationContext(); + return ( +
    +

    + + + +

    +

    + + There is no webhooks yet, add more pressing the + sign + +

    +
    + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx new file mode 100644 index 000000000..c5846e4db --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx @@ -0,0 +1,95 @@ +/* + This file is part of GNU Taler + (C) 2021-2023 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { Loading } from "../../../../components/exception/loading.js"; +import { NotificationCard } from "../../../../components/menu/index.js"; +import { MerchantBackend } from "../../../../declaration.js"; +import { + useInstanceWebhooks, + useWebhookAPI, +} from "../../../../hooks/webhooks.js"; +import { HttpError } from "../../../../utils/request.js"; +import { Notification } from "../../../../utils/types.js"; +import { ListPage } from "./ListPage.js"; + +interface Props { + onUnauthorized: () => VNode; + onLoadError: (error: HttpError) => VNode; + onNotFound: () => VNode; + onCreate: () => void; + onSelect: (id: string) => void; +} + +export default function ListWebhooks({ + onUnauthorized, + onLoadError, + onCreate, + onSelect, + onNotFound, +}: Props): VNode { + const [position, setPosition] = useState(undefined); + const { i18n } = useTranslationContext(); + const [notif, setNotif] = useState(undefined); + const { deleteWebhook } = useWebhookAPI(); + const result = useInstanceWebhooks({ position }, (id) => setPosition(id)); + + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return ; + if (!result.ok) return onLoadError(result); + + return ( + + + + { + onSelect(e.webhook_id); + }} + onDelete={(e: MerchantBackend.Webhooks.WebhookEntry) => + deleteWebhook(e.webhook_id) + .then(() => + setNotif({ + message: i18n.str`webhook delete successfully`, + type: "SUCCESS", + }), + ) + .catch((error) => + setNotif({ + message: i18n.str`could not delete the webhook`, + type: "ERROR", + description: error.message, + }), + ) + } + /> + + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/Update.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/Update.stories.tsx new file mode 100644 index 000000000..8d07cb31f --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/Update.stories.tsx @@ -0,0 +1,32 @@ +/* + This file is part of GNU Taler + (C) 2021-2023 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { h, VNode, FunctionalComponent } from "preact"; +import { UpdatePage as TestedComponent } from "./UpdatePage.js"; + +export default { + title: "Pages/Templates/Update", + component: TestedComponent, + argTypes: { + onUpdate: { action: "onUpdate" }, + onBack: { action: "onBack" }, + }, +}; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx new file mode 100644 index 000000000..4e3674dca --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/UpdatePage.tsx @@ -0,0 +1,146 @@ +/* + This file is part of GNU Taler + (C) 2021-2023 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { AsyncButton } from "../../../../components/exception/AsyncButton.js"; +import { + FormErrors, + FormProvider, +} from "../../../../components/form/FormProvider.js"; +import { Input } from "../../../../components/form/Input.js"; +import { useBackendContext } from "../../../../context/backend.js"; +import { MerchantBackend, WithId } from "../../../../declaration.js"; + +type Entity = MerchantBackend.Webhooks.WebhookPatchDetails & WithId; + +interface Props { + onUpdate: (d: Entity) => Promise; + onBack?: () => void; + webhook: Entity; +} +const validMethod = ["GET", "POST", "PUT", "PATCH", "HEAD"]; + +export function UpdatePage({ webhook, onUpdate, onBack }: Props): VNode { + const { i18n } = useTranslationContext(); + + const [state, setState] = useState>(webhook); + + const errors: FormErrors = { + event_type: !state.event_type ? i18n.str`required` : undefined, + http_method: !state.http_method + ? i18n.str`required` + : !validMethod.includes(state.http_method) + ? i18n.str`should be one of "${validMethod.join(", ")}"` + : undefined, + url: !state.url ? i18n.str`required` : undefined, + }; + + const hasErrors = Object.keys(errors).some( + (k) => (errors as any)[k] !== undefined, + ); + + const submitForm = () => { + if (hasErrors) return Promise.reject(); + return onUpdate(state as any); + }; + + return ( +
    +
    +
    +
    +
    +
    +
    + + Webhook: {webhook.id} + +
    +
    +
    +
    +
    +
    + +
    +
    +
    + + + name="event_type" + label={i18n.str`Event`} + tooltip={i18n.str`The event of the webhook: why the webhook is used`} + /> + + name="http_method" + label={i18n.str`Method`} + tooltip={i18n.str`Method used by the webhook`} + /> + + name="url" + label={i18n.str`URL`} + tooltip={i18n.str`URL of the webhook where the customer will be redirected`} + /> + + name="header_template" + label={i18n.str`Header`} + inputType="multiline" + tooltip={i18n.str`Header template of the webhook`} + /> + + name="body_template" + inputType="multiline" + label={i18n.str`Body`} + tooltip={i18n.str`Body template by the webhook`} + /> + + +
    + {onBack && ( + + )} + + Confirm + +
    +
    +
    +
    +
    +
    + ); +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx new file mode 100644 index 000000000..3597fb849 --- /dev/null +++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx @@ -0,0 +1,85 @@ +/* + This file is part of GNU Taler + (C) 2021-2023 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser"; +import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { Loading } from "../../../../components/exception/loading.js"; +import { NotificationCard } from "../../../../components/menu/index.js"; +import { MerchantBackend, WithId } from "../../../../declaration.js"; +import { + useWebhookAPI, + useWebhookDetails, +} from "../../../../hooks/webhooks.js"; +import { HttpError } from "../../../../utils/request.js"; +import { Notification } from "../../../../utils/types.js"; +import { UpdatePage } from "./UpdatePage.js"; + +export type Entity = MerchantBackend.Webhooks.WebhookPatchDetails & WithId; + +interface Props { + onBack?: () => void; + onConfirm: () => void; + onUnauthorized: () => VNode; + onNotFound: () => VNode; + onLoadError: (e: HttpError) => VNode; + tid: string; +} +export default function UpdateWebhook({ + tid, + onConfirm, + onBack, + onUnauthorized, + onNotFound, + onLoadError, +}: Props): VNode { + const { updateWebhook } = useWebhookAPI(); + const result = useWebhookDetails(tid); + const [notif, setNotif] = useState(undefined); + + const { i18n } = useTranslationContext(); + + if (result.clientError && result.isUnauthorized) return onUnauthorized(); + if (result.clientError && result.isNotfound) return onNotFound(); + if (result.loading) return ; + if (!result.ok) return onLoadError(result); + + return ( + + + { + return updateWebhook(tid, data) + .then(onConfirm) + .catch((error) => { + setNotif({ + message: i18n.str`could not update template`, + type: "ERROR", + description: error.message, + }); + }); + }} + /> + + ); +} -- cgit v1.2.3