From 3859e7b9762cc311102b94ed9f0019c56bf1cbf0 Mon Sep 17 00:00:00 2001 From: estifanos Date: Tue, 1 Sep 2026 05:47:05 +0000 Subject: [PATCH] feat: add non-expiring license option to behavior configuration with conditional UI updates --- .../components/BehaviorTab.tsx | 184 +++++++++++------- apps/backoffice/src/app/i18n/locales/am.ts | 3 + apps/backoffice/src/app/i18n/locales/en.ts | 3 + 3 files changed, 123 insertions(+), 67 deletions(-) diff --git a/apps/backoffice/src/app/features/certificate-requirements/components/BehaviorTab.tsx b/apps/backoffice/src/app/features/certificate-requirements/components/BehaviorTab.tsx index cab0bf4c4..891acfc15 100644 --- a/apps/backoffice/src/app/features/certificate-requirements/components/BehaviorTab.tsx +++ b/apps/backoffice/src/app/features/certificate-requirements/components/BehaviorTab.tsx @@ -114,11 +114,20 @@ export function BehaviorTab({ licenseType }: { licenseType: LicenseType }) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [licenseType.id]); - function set(key: K, value: Draft[K]) { - setDraft((current) => ({ ...current, [key]: value })); + function patch(values: Partial) { + setDraft((current) => ({ ...current, ...values })); setDirty(true); } + function set(key: K, value: Draft[K]) { + patch({ [key]: value } as Partial); + } + + // Nothing this type issues carries an expiry date — a transfer, or any + // one-off record. Renewal, its window and its reminders are all measured + // against an expiry that never arrives, so none of them are asked for. + const expires = draft.validityDays !== null || draft.validityMonths > 0; + async function onSave() { const ok = await run( () => save({ id: licenseType.id, ...draft }).unwrap(), @@ -289,94 +298,135 @@ export function BehaviorTab({ licenseType }: { licenseType: LicenseType }) { the seed says `validityMonths: 12` and this now says "12 Months", so the two read the same. Months advance the calendar (issued on the 31st, expires on the 31st); days are for terms shorter than a - month can express. */} + month can express. The third unit is no term at all, which the + server stores as `validityMonths: 0`. */} - { - const next = typeof v === 'number' ? v : 0; - if (!next) return; - if (draft.validityDays !== null) set('validityDays', next); - else set('validityMonths', next); - }} - // Matches the server's ranges, so the box cannot offer a value the - // save would reject: 1–3650 days, or 6–240 months. - min={draft.validityDays !== null ? 1 : 6} - max={draft.validityDays !== null ? 3650 : 240} - allowNegative={false} - disabled={!canEdit} - flex={1} - /> + {expires && ( + { + const next = typeof v === 'number' ? v : 0; + if (!next) return; + if (draft.validityDays !== null) set('validityDays', next); + else set('validityMonths', next); + }} + // Matches the server's ranges, so the box cannot offer a value the + // save would reject: 1–3650 days, or 6–240 months. + min={draft.validityDays !== null ? 1 : 6} + max={draft.validityDays !== null ? 3650 : 240} + allowNegative={false} + disabled={!canEdit} + flex={1} + /> + )}