taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 53e9842f4590fe4b991dedd058a9014428b5d4f8
parent 692e8fb22dd099b6e6131d0541c8311b3c700428
Author: Sebastian <sebasjm@gmail.com>
Date:   Fri, 31 Oct 2025 10:48:26 -0300

otp category:

Diffstat:
Mpackages/merchant-backoffice-ui/src/paths/instance/categories/create/CreatePage.tsx | 30++++++++++++++++++++----------
Mpackages/merchant-backoffice-ui/src/paths/instance/categories/create/index.tsx | 32+-------------------------------
Mpackages/merchant-backoffice-ui/src/paths/instance/categories/list/Table.tsx | 134+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Mpackages/merchant-backoffice-ui/src/paths/instance/categories/list/index.tsx | 26--------------------------
Mpackages/merchant-backoffice-ui/src/paths/instance/categories/update/UpdatePage.tsx | 54++++++++++++++++++++++++++++--------------------------
Mpackages/merchant-backoffice-ui/src/paths/instance/categories/update/index.tsx | 37+------------------------------------
Mpackages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx | 108+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mpackages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx | 26--------------------------
Mpackages/web-util/src/hooks/useNotifications.ts | 6+++---
9 files changed, 201 insertions(+), 252 deletions(-)

diff --git a/packages/merchant-backoffice-ui/src/paths/instance/categories/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/categories/create/CreatePage.tsx @@ -19,8 +19,8 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { TalerMerchantApi } from "@gnu-taler/taler-util"; -import { useTranslationContext } from "@gnu-taler/web-util/browser"; +import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { ButtonBetterBulma, LocalNotificationBannerBulma, useLocalNotificationBetter, useTranslationContext } from "@gnu-taler/web-util/browser"; import { VNode, h } from "preact"; import { useState } from "preact/hooks"; import { @@ -28,16 +28,18 @@ import { FormProvider, } from "../../../../components/form/FormProvider.js"; import { Input } from "../../../../components/form/Input.js"; +import { useSessionContext } from "../../../../context/session.js"; +import { useTokenFamilyDetails } from "../../../../hooks/tokenfamily.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; type Entity = TalerMerchantApi.CategoryCreateRequest; interface Props { - onCreate: (d: Entity) => Promise<void>; + onCreated: () => void; onBack?: () => void; } -export function CreatePage({ onCreate, onBack }: Props): VNode { +export function CreatePage({ onCreated, onBack }: Props): VNode { const { i18n } = useTranslationContext(); const [state, setState] = useState<Partial<Entity>>({ name_i18n: {} }); @@ -51,14 +53,23 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { }); const hasErrors = errors !== undefined; + const { state: session, lib } = useSessionContext(); + const [notification, safeFunctionHandler] = useLocalNotificationBetter(); - const submitForm = () => { - if (hasErrors) return Promise.reject(); - return onCreate(state as Entity); - }; + const data = !!errors ? undefined : state as TalerMerchantApi.CategoryCreateRequest + const create = safeFunctionHandler(lib.instance + .addCategory, !session.token || !data ? undefined : [session.token, data]) + create.onSuccess = onCreated + create.onFail = (fail) => { + switch (fail.case) { + case HttpStatusCode.Unauthorized: return i18n.str`Unauthorized` + case HttpStatusCode.NotFound: return i18n.str`Not found` + } + } return ( <div> + <LocalNotificationBannerBulma notification={notification} /> <section class="section is-main-section"> <div class="columns"> <div class="column" /> @@ -82,13 +93,12 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { </button> )} <ButtonBetterBulma - disabled={hasErrors} data-tooltip={ hasErrors ? i18n.str`Please complete the marked fields` : i18n.str`Confirm operation` } - onClick={submitForm} + onClick={create} > <i18n.Translate>Confirm</i18n.Translate> </ButtonBetterBulma> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/categories/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/categories/create/index.tsx @@ -35,42 +35,12 @@ interface Props { } export default function CreateCategory({ onConfirm, onBack }: Props): VNode { - const { state, lib } = useSessionContext(); - - const { i18n } = useTranslationContext(); return ( <> - <LocalNotificationBannerBulma notification={notification} /> <CreatePage onBack={onBack} - onCreate={async (request: Entity) => { - return lib.instance - .addCategory(state.token, request) - .then((resp) => { - if (resp.type === "ok") { - setNotif({ - message: i18n.str`Category added successfully`, - type: "SUCCESS", - }); - onConfirm() - } else { - setNotif({ - message: i18n.str`Could not add category`, - type: "ERROR", - description: resp.detail?.hint, - }); - } - }) - .catch((error) => { - setNotif({ - message: i18n.str`Could not add category`, - type: "ERROR", - description: - error instanceof Error ? error.message : String(error), - }); - }); - }} + onCreated={onConfirm} /> </> ); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/categories/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/categories/list/Table.tsx @@ -19,16 +19,16 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { TalerMerchantApi } from "@gnu-taler/taler-util"; -import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { h, VNode } from "preact"; +import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { ButtonBetterBulma, LocalNotificationBannerBulma, SafeHandlerTemplate, useLocalNotificationBetter, useTranslationContext } from "@gnu-taler/web-util/browser"; +import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; +import { useSessionContext } from "../../../../context/session.js"; type Entity = TalerMerchantApi.CategoryListEntry; interface Props { devices: Entity[]; - onDelete: (e: Entity) => void; onSelect: (e: Entity) => void; onCreate: () => void; onLoadMoreBefore?: () => void; @@ -38,7 +38,6 @@ interface Props { export function CardTable({ devices, onCreate, - onDelete, onSelect, onLoadMoreAfter, onLoadMoreBefore, @@ -46,55 +45,96 @@ export function CardTable({ const [rowSelection, rowSelectionHandler] = useState<string[]>([]); const { i18n } = useTranslationContext(); + const { state: session, lib } = useSessionContext(); + const [notification, safeFunctionHandler] = useLocalNotificationBetter(); + + const remove = safeFunctionHandler(lib.instance.deleteCategory).lambda((id: string) => !session.token ? undefined! : [session.token, id]) + remove.onSuccess = () => i18n.str`Category deleted` + remove.onFail = (fail) => { + switch (fail.case) { + case HttpStatusCode.Unauthorized: return i18n.str`Unauthorized` + case HttpStatusCode.NotFound: return i18n.str`Not found` + } + } + // async (e: TalerMerchantApi.CategoryListEntry) => { + // return lib.instance + // .deleteCategory(state.token, String(e.category_id)) + // .then((resp) => { + // if (resp.type === "ok") { + // setNotif({ + // message: i18n.str`Category delete successfully`, + // type: "SUCCESS", + // }); + // } else { + // setNotif({ + // message: i18n.str`Could not delete the category`, + // type: "ERROR", + // description: resp.detail?.hint, + // }); + // } + // }) + // .catch((error) => + // setNotif({ + // message: i18n.str`Could not delete the category`, + // type: "ERROR", + // description: error instanceof Error ? error.message : String(error), + // }), + // ); + // } return ( - <div class="card has-table"> - <header class="card-header"> - <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-label" /> - </span> - <i18n.Translate>Categories</i18n.Translate> - </p> - <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - data-tooltip={i18n.str`Add new devices`} - > - <button class="button is-info" type="button" onClick={onCreate}> - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> - </button> - </span> - </div> - </header> - <div class="card-content"> - <div class="b-table has-pagination"> - <div class="table-wrapper has-mobile-cards"> - {devices.length > 0 ? ( - <Table - instances={devices} - onDelete={onDelete} - onSelect={onSelect} - rowSelection={rowSelection} - rowSelectionHandler={rowSelectionHandler} - onLoadMoreAfter={onLoadMoreAfter} - onLoadMoreBefore={onLoadMoreBefore} - /> - ) : ( - <EmptyTable /> - )} + <Fragment> + <LocalNotificationBannerBulma notification={notification} /> + + <div class="card has-table"> + <header class="card-header"> + <p class="card-header-title"> + <span class="icon"> + <i class="mdi mdi-label" /> + </span> + <i18n.Translate>Categories</i18n.Translate> + </p> + <div class="card-header-icon" aria-label="more options"> + <span + class="has-tooltip-left" + data-tooltip={i18n.str`Add new devices`} + > + <button class="button is-info" type="button" onClick={onCreate}> + <span class="icon is-small"> + <i class="mdi mdi-plus mdi-36px" /> + </span> + </button> + </span> + </div> + </header> + <div class="card-content"> + <div class="b-table has-pagination"> + <div class="table-wrapper has-mobile-cards"> + {devices.length > 0 ? ( + <Table + instances={devices} + onDelete={remove} + onSelect={onSelect} + rowSelection={rowSelection} + rowSelectionHandler={rowSelectionHandler} + onLoadMoreAfter={onLoadMoreAfter} + onLoadMoreBefore={onLoadMoreBefore} + /> + ) : ( + <EmptyTable /> + )} + </div> </div> </div> </div> - </div> + </Fragment> + ); } interface TableProps { rowSelection: string[]; instances: Entity[]; - onDelete: (e: Entity) => void; + onDelete: SafeHandlerTemplate<[id: string],unknown>; onSelect: (e: Entity) => void; rowSelectionHandler: StateUpdater<string[]>; onLoadMoreBefore?: () => void; @@ -159,13 +199,13 @@ function Table({ </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <button + <ButtonBetterBulma class="button is-danger is-small has-tooltip-left" data-tooltip={i18n.str`Delete selected category from the database`} - onClick={() => onDelete(i)} + onClick={onDelete.withArgs(String(i.category_id))} > <i18n.Translate>Delete</i18n.Translate> - </button> + </ButtonBetterBulma> </div> </td> </tr> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/categories/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/categories/list/index.tsx @@ -70,7 +70,6 @@ export default function ListCategories({ onCreate, onSelect }: Props): VNode { return ( <Fragment> - <LocalNotificationBannerBulma notification={notification} /> <section class="section is-main-section"> <CardTable @@ -81,31 +80,6 @@ export default function ListCategories({ onCreate, onSelect }: Props): VNode { onSelect={(e) => { onSelect(String(e.category_id)); }} - onDelete={async (e: TalerMerchantApi.CategoryListEntry) => { - return lib.instance - .deleteCategory(state.token, String(e.category_id)) - .then((resp) => { - if (resp.type === "ok") { - setNotif({ - message: i18n.str`Category delete successfully`, - type: "SUCCESS", - }); - } else { - setNotif({ - message: i18n.str`Could not delete the category`, - type: "ERROR", - description: resp.detail?.hint, - }); - } - }) - .catch((error) => - setNotif({ - message: i18n.str`Could not delete the category`, - type: "ERROR", - description: error instanceof Error ? error.message : String(error), - }), - ); - }} /> </section> </Fragment> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/categories/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/categories/update/UpdatePage.tsx @@ -25,7 +25,7 @@ import { TalerError, TalerMerchantApi, } from "@gnu-taler/taler-util"; -import { Loading, useTranslationContext } from "@gnu-taler/web-util/browser"; +import { ButtonBetterBulma, Loading, LocalNotificationBannerBulma, useLocalNotificationBetter, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, h, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; import emptyImage from "../../../../assets/empty.png"; @@ -43,25 +43,25 @@ import { type Entity = TalerMerchantApi.CategoryProductList & WithId; interface Props { - onUpdate: (d: Entity) => Promise<void>; + onUpdated: () => void; onBack?: () => void; category: Entity; } -export function UpdatePage({ category, onUpdate, onBack }: Props): VNode { +export function UpdatePage({ category, onUpdated, onBack }: Props): VNode { const { i18n } = useTranslationContext(); const { state: { token }, lib, } = useSessionContext(); // FIXME: if the product list is big the will bring a lot of info - const inventoryResult = useInstanceProducts(); + // const inventoryResult = useInstanceProducts(); - const inventory = - !inventoryResult || - inventoryResult instanceof TalerError || - inventoryResult.type === "fail" - ? [] - : inventoryResult.body; + // const inventory = + // !inventoryResult || + // inventoryResult instanceof TalerError || + // inventoryResult.type === "fail" + // ? [] + // : inventoryResult.body; const [state, setState] = useState< Partial<Entity & { product_map: { id: string; description: string }[] }> @@ -72,7 +72,6 @@ export function UpdatePage({ category, onUpdate, onBack }: Props): VNode { useEffect(() => { if (!category || !category?.products) return; - console.log(category.products); const ps = category.products.map((prod) => { return lib.instance .getProductDetails(token, String(prod.product_id)) @@ -80,9 +79,9 @@ export function UpdatePage({ category, onUpdate, onBack }: Props): VNode { return res.type === "fail" ? undefined : { - id: String(prod.product_id), - description: res.body.description, - }; + id: String(prod.product_id), + description: res.body.description, + }; }); }); Promise.all(ps).then((all) => { @@ -90,18 +89,22 @@ export function UpdatePage({ category, onUpdate, onBack }: Props): VNode { setState({ ...state, product_map }); }); }, []); - - const submitForm = () => { - const pids = state.product_map?.map((p) => { - return { product_id: p.id }; - }); - state.products = pids; - delete state.product_map; - return onUpdate(state as Entity); - }; + const [notification, safeFunctionHandler] = useLocalNotificationBetter(); + const data = state as TalerMerchantApi.CategoryCreateRequest; + const update = safeFunctionHandler(lib.instance + .updateCategory, !token ? undefined : [token, category.id, data]) + update.onSuccess = onUpdated + update.onFail = (fail) => { + switch(fail.case) { + case HttpStatusCode.Unauthorized: return i18n.str`Unauthorized` + case HttpStatusCode.NotFound: return i18n.str`Not found` + } + } return ( <div> + <LocalNotificationBannerBulma notification={notification} /> + <section class="section"> <section class="hero is-hero-bar"> <div class="hero-body"> @@ -139,14 +142,13 @@ export function UpdatePage({ category, onUpdate, onBack }: Props): VNode { </button> )} <ButtonBetterBulma - disabled={false} data-tooltip={i18n.str`Confirm operation`} - onClick={submitForm} + onClick={update} > <i18n.Translate>Confirm</i18n.Translate> </ButtonBetterBulma> </div> - <ProductListSmall onSelect={() => {}} list={category.products} /> + <ProductListSmall onSelect={() => { }} list={category.products} /> </div> </div> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/categories/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/categories/update/index.tsx @@ -26,13 +26,10 @@ import { } from "@gnu-taler/taler-util"; import { LocalNotificationBannerBulma, useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; -import { useState } from "preact/hooks"; import { ErrorLoadingMerchant } from "../../../../components/ErrorLoadingMerchant.js"; import { Loading } from "../../../../components/exception/loading.js"; -import { NotificationCard } from "../../../../components/menu/index.js"; import { useSessionContext } from "../../../../context/session.js"; import { useCategoryDetails } from "../../../../hooks/category.js"; -import { Notification } from "../../../../utils/types.js"; import { LoginPage } from "../../../login/index.js"; import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js"; import { UpdatePage } from "./UpdatePage.js"; @@ -49,10 +46,6 @@ export default function UpdateCategory({ }: Props): VNode { const result = useCategoryDetails(cid); - const { state, lib } = useSessionContext(); - - const { i18n } = useTranslationContext(); - if (!result) return <Loading />; if (result instanceof TalerError) { return <ErrorLoadingMerchant error={result} />; @@ -73,41 +66,13 @@ export default function UpdateCategory({ return ( <Fragment> - <LocalNotificationBannerBulma notification={notification} /> <UpdatePage category={{ ...result.body, id: cid, }} onBack={onBack} - onUpdate={async (newInfo) => { - return lib.instance - .updateCategory(state.token, cid, newInfo) - .then((d) => { - if (d.type === "ok") { - onConfirm(); - } else { - switch (d.case) { - case HttpStatusCode.NotFound: { - setNotif({ - message: i18n.str`Could not update category`, - type: "ERROR", - description: i18n.str`Category ID is unknown`, - }); - break; - } - } - } - }) - .catch((error) => { - setNotif({ - message: i18n.str`Could not update category`, - type: "ERROR", - description: - error instanceof Error ? error.message : String(error), - }); - }); - }} + onUpdated={onConfirm} /> </Fragment> ); diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/Table.tsx @@ -19,16 +19,16 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { TalerMerchantApi } from "@gnu-taler/taler-util"; -import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { h, VNode } from "preact"; +import { HttpStatusCode, TalerMerchantApi } from "@gnu-taler/taler-util"; +import { ButtonBetterBulma, LocalNotificationBannerBulma, SafeHandlerTemplate, useLocalNotificationBetter, useTranslationContext } from "@gnu-taler/web-util/browser"; +import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; +import { useSessionContext } from "../../../../context/session.js"; type Entity = TalerMerchantApi.OtpDeviceEntry; interface Props { devices: Entity[]; - onDelete: (e: Entity) => void; onSelect: (e: Entity) => void; onCreate: () => void; onLoadMoreBefore?: () => void; @@ -38,63 +38,77 @@ interface Props { export function CardTable({ devices, onCreate, - onDelete, onSelect, onLoadMoreAfter, onLoadMoreBefore, }: Props): VNode { const [rowSelection, rowSelectionHandler] = useState<string[]>([]); + const [notification, safeFunctionHandler] = useLocalNotificationBetter(); + const { state: session, lib } = useSessionContext(); const { i18n } = useTranslationContext(); + const remove = safeFunctionHandler(lib.instance.deleteOtpDevice) + .lambda((id: string) => !session.token ? undefined! : [session.token, id]) + + remove.onFail = (fail) => { + switch (fail.case) { + case HttpStatusCode.Unauthorized: return i18n.str`Unauthorized` + case HttpStatusCode.NotFound: return i18n.str`Not found` + } + } return ( - <div class="card has-table"> - <header class="card-header"> - <p class="card-header-title"> - <span class="icon"> - <i class="mdi mdi-lock" /> - </span> - <i18n.Translate>OTP devices</i18n.Translate> - </p> - <div class="card-header-icon" aria-label="more options"> - <span - class="has-tooltip-left" - data-tooltip={i18n.str`Add new devices`} - > - <button class="button is-info" type="button" onClick={onCreate}> - <span class="icon is-small"> - <i class="mdi mdi-plus mdi-36px" /> - </span> - </button> - </span> - </div> - </header> - <div class="card-content"> - <div class="b-table has-pagination"> - <div class="table-wrapper has-mobile-cards"> - {devices.length > 0 ? ( - <Table - instances={devices} - onDelete={onDelete} - onSelect={onSelect} - rowSelection={rowSelection} - rowSelectionHandler={rowSelectionHandler} - onLoadMoreAfter={onLoadMoreAfter} - onLoadMoreBefore={onLoadMoreBefore} - /> - ) : ( - <EmptyTable /> - )} + <Fragment> + <LocalNotificationBannerBulma notification={notification} /> + + <div class="card has-table"> + <header class="card-header"> + <p class="card-header-title"> + <span class="icon"> + <i class="mdi mdi-lock" /> + </span> + <i18n.Translate>OTP devices</i18n.Translate> + </p> + <div class="card-header-icon" aria-label="more options"> + <span + class="has-tooltip-left" + data-tooltip={i18n.str`Add new devices`} + > + <button class="button is-info" type="button" onClick={onCreate}> + <span class="icon is-small"> + <i class="mdi mdi-plus mdi-36px" /> + </span> + </button> + </span> + </div> + </header> + <div class="card-content"> + <div class="b-table has-pagination"> + <div class="table-wrapper has-mobile-cards"> + {devices.length > 0 ? ( + <Table + instances={devices} + onDelete={remove} + onSelect={onSelect} + rowSelection={rowSelection} + rowSelectionHandler={rowSelectionHandler} + onLoadMoreAfter={onLoadMoreAfter} + onLoadMoreBefore={onLoadMoreBefore} + /> + ) : ( + <EmptyTable /> + )} + </div> </div> </div> </div> - </div> + </Fragment> ); } interface TableProps { rowSelection: string[]; instances: Entity[]; - onDelete: (e: Entity) => void; + onDelete: SafeHandlerTemplate<[id: string], unknown>; onSelect: (e: Entity) => void; rowSelectionHandler: StateUpdater<string[]>; onLoadMoreBefore?: () => void; @@ -150,13 +164,13 @@ function Table({ </td> <td class="is-actions-cell right-sticky"> <div class="buttons is-right"> - <button + <ButtonBetterBulma class="button is-danger is-small has-tooltip-left" data-tooltip={i18n.str`Delete selected devices from the database`} - onClick={() => onDelete(i)} + onClick={onDelete.withArgs(i.otp_device_id)} > <i18n.Translate>Delete</i18n.Translate> - </button> + </ButtonBetterBulma> </div> </td> </tr> diff --git a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx @@ -67,7 +67,6 @@ export default function ListOtpDevices({ onCreate, onSelect }: Props): VNode { return ( <Fragment> - <LocalNotificationBannerBulma notification={notification} /> <section class="section is-main-section"> <CardTable @@ -78,31 +77,6 @@ export default function ListOtpDevices({ onCreate, onSelect }: Props): VNode { onSelect={(e) => { onSelect(e.otp_device_id); }} - onDelete={async (e: TalerMerchantApi.OtpDeviceEntry) => { - return lib.instance - .deleteOtpDevice(state.token, e.otp_device_id) - .then((resp) => { - if (resp.type === "ok") { - setNotif({ - message: i18n.str`The device was successfully deleted`, - type: "SUCCESS", - }); - } else { - setNotif({ - message: i18n.str`Could not delete the device`, - type: "ERROR", - description: resp.detail?.hint, - }); - } - }) - .catch((error) => - setNotif({ - message: i18n.str`Could not delete the device`, - type: "ERROR", - description: error instanceof Error ? error.message : String(error), - }), - ); - }} /> </section> </Fragment> diff --git a/packages/web-util/src/hooks/useNotifications.ts b/packages/web-util/src/hooks/useNotifications.ts @@ -242,7 +242,7 @@ export function useLocalNotificationBetter(): [ }, lambda(converter) { type D = Parameters<typeof converter>; - type R = SafeHandlerTemplate<D, Error>; + type SH = SafeHandlerTemplate<D, R>; const r = { ...handler, args: undefined, @@ -252,7 +252,7 @@ export function useLocalNotificationBetter(): [ return e; }, }; - return r as any as R; + return r as any as SH; }, call: async (): Promise<void> => { if (!handler.args) return; @@ -343,7 +343,7 @@ export interface SafeHandlerTemplate<Args extends any[], Errors> { lambda<OtherArgs extends any[]>( e: (...d: OtherArgs) => Args, init?: OtherArgs - ): SafeHandlerTemplate<OtherArgs, Error>; + ): SafeHandlerTemplate<OtherArgs, Errors>; /** * creates another handler with new arguements * @param args