commit 601614be93f061bb37c57c5d5fcbcdd65f7c4232
parent 22c2c530845aaebd88a56913397923cc05ea818a
Author: Iván Ávalos <avalos@disroot.org>
Date: Wed, 30 Jul 2025 16:35:23 +0200
backoffice-ui: fix token family management
Diffstat:
5 files changed, 32 insertions(+), 26 deletions(-)
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx b/packages/merchant-backoffice-ui/src/components/form/InputDuration.tsx
@@ -33,6 +33,7 @@ export interface Props<T> extends InputProps<T> {
withForever?: boolean;
side?: ComponentChildren;
withoutClear?: boolean;
+ useProtocolDuration?: boolean;
}
export function InputDuration<T>({
@@ -46,6 +47,7 @@ export function InputDuration<T>({
withForever,
withoutClear,
side,
+ useProtocolDuration,
}: Props<keyof T>): VNode {
const [opened, setOpened] = useState(false);
const { i18n } = useTranslationContext();
@@ -188,7 +190,12 @@ export function InputDuration<T>({
minutes
value={!value || value.d_ms === "forever" ? 0 : value.d_ms}
onChange={(v) => {
- onChange({ d_ms: v } as any);
+ let duration: any = { d_ms: v};
+ if (useProtocolDuration === true) {
+ onChange(Duration.toTalerProtocolDuration(duration) as any);
+ } else {
+ onChange(duration);
+ }
}}
/>
</SimpleModal>
diff --git a/packages/merchant-backoffice-ui/src/components/tokenfamily/TokenFamilyForm.tsx b/packages/merchant-backoffice-ui/src/components/tokenfamily/TokenFamilyForm.tsx
@@ -24,7 +24,7 @@ import {
Duration,
TalerMerchantApi
} from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { InputSelectOne, useTranslationContext } from "@gnu-taler/web-util/browser";
import { h } from "preact";
import { useCallback, useEffect, useState } from "preact/hooks";
import { useSessionContext } from "../../context/session.js";
@@ -53,6 +53,7 @@ export function TokenFamilyForm({ onSubscribe }: Props) {
duration: Duration.toTalerProtocolDuration(Duration.getForever()),
valid_after: AbsoluteTime.toProtocolTimestamp(AbsoluteTime.never()),
valid_before: AbsoluteTime.toProtocolTimestamp(AbsoluteTime.never()),
+ validity_granularity: Duration.toTalerProtocolDuration(Duration.getForever()),
});
const { i18n } = useTranslationContext();
@@ -63,6 +64,7 @@ export function TokenFamilyForm({ onSubscribe }: Props) {
description: !value.description ? i18n.str`Required` : undefined,
valid_after: !value.valid_after ? undefined : undefined,
valid_before: !value.valid_before ? i18n.str`Required` : undefined,
+ validity_granularity: !value.validity_granularity ? i18n.str`Required` : undefined,
duration: !value.duration ? i18n.str`Required` : undefined,
kind: !value.kind ? i18n.str`Required` : undefined,
};
@@ -127,10 +129,17 @@ export function TokenFamilyForm({ onSubscribe }: Props) {
withTimestampSupport
/>
<InputDuration<Entity>
+ name="validity_granularity"
+ label={i18n.str`Validity Granularity`}
+ tooltip={i18n.str`Rounding granularity for the start validity of keys, must be 1 minute, 1 hour, 1 day, 1 week, 30 days or 90 days`}
+ useProtocolDuration
+ />
+ <InputDuration<Entity>
name="duration"
label={i18n.str`Duration`}
tooltip={i18n.str`Validity duration of a issued token`}
withForever
+ useProtocolDuration
/>
</FormProvider>
</div>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/tokenfamilies/update/UpdatePage.tsx
@@ -30,9 +30,7 @@ import { InputDate } from "../../../../components/form/InputDate.js";
import { InputDuration } from "../../../../components/form/InputDuration.js";
import { undefinedIfEmpty } from "../../../../utils/table.js";
-type Entity = Omit<TalerMerchantApi.TokenFamilyUpdateRequest, "duration"> & {
- duration: Duration,
-};
+type Entity = TalerMerchantApi.TokenFamilyUpdateRequest;
interface Props {
onUpdate: (d: TalerMerchantApi.TokenFamilyUpdateRequest) => Promise<void>;
@@ -40,17 +38,8 @@ interface Props {
tokenFamily: TalerMerchantApi.TokenFamilyUpdateRequest;
}
-function convert(from: TalerMerchantApi.TokenFamilyUpdateRequest) {
- const { duration, ...rest } = from;
-
- const converted = {
- duration: Duration.fromTalerProtocolDuration(duration),
- };
- return { ...converted, ...rest };
-}
-
export function UpdatePage({ onUpdate, onBack, tokenFamily }: Props) {
- const [value, valueHandler] = useState<Partial<Entity>>(convert(tokenFamily));
+ const [value, valueHandler] = useState<Partial<Entity>>(tokenFamily);
const { i18n } = useTranslationContext();
const errors = undefinedIfEmpty<FormErrors<Entity>>({
name: !value.name ? i18n.str`Required` : undefined,
@@ -65,13 +54,7 @@ export function UpdatePage({ onUpdate, onBack, tokenFamily }: Props) {
const submitForm = () => {
if (hasErrors) return Promise.reject();
- const { duration, ...rest } = value as Required<Entity>;
- const result: TalerMerchantApi.TokenFamilyUpdateRequest = {
- ...rest,
- duration: Duration.toTalerProtocolDuration(duration),
- };
-
- return onUpdate(result);
+ return onUpdate(value as Entity);
}
@@ -131,6 +114,7 @@ export function UpdatePage({ onUpdate, onBack, tokenFamily }: Props) {
label={i18n.str`Duration`}
tooltip={i18n.str`Validity duration of a issued token`}
withForever
+ useProtocolDuration
/>
</FormProvider>
diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts
@@ -2341,11 +2341,17 @@ export class TalerMerchantInstanceHttpClient {
headers.Authorization = makeBearerTokenAuthHeader(token);
}
const resp = await this.httpLib.fetch(url.href, {
- method: "POST",
+ method: "PATCH",
body,
headers,
});
switch (resp.status) {
+ case HttpStatusCode.NoContent: {
+ this.cacheEvictor.notifySuccess(
+ TalerMerchantInstanceCacheEviction.UPDATE_TOKENFAMILY,
+ );
+ return opEmptySuccess();
+ }
case HttpStatusCode.Ok: {
this.cacheEvictor.notifySuccess(
TalerMerchantInstanceCacheEviction.UPDATE_TOKENFAMILY,
diff --git a/packages/taler-util/src/types-taler-merchant.ts b/packages/taler-util/src/types-taler-merchant.ts
@@ -3136,8 +3136,8 @@ export interface TokenFamilyDetails {
// How many tokens have been issued for this family.
issued: Integer;
- // How many tokens have been redeemed for this family.
- redeemed: Integer;
+ // How many tokens have been used for this family.
+ used: Integer;
}
export interface OrderChoice {
@@ -4260,7 +4260,7 @@ export const codecForTokenFamilyDetails = (): Codec<TokenFamilyDetails> =>
.property("duration", codecForDuration)
.property("kind", codecForTokenFamilyKind)
.property("issued", codecForNumber())
- .property("redeemed", codecForNumber())
+ .property("used", codecForNumber())
.build("TalerMerchantApi.TokenFamilyDetails");
export const codecForTokenFamiliesList = (): Codec<TokenFamiliesList> =>