@asteby/metacore-runtime-react 23.9.2 → 23.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +43 -0
  3. package/dist/action-modal-dispatcher.d.ts.map +1 -1
  4. package/dist/action-modal-dispatcher.js +122 -0
  5. package/dist/dynamic-kanban.d.ts.map +1 -1
  6. package/dist/dynamic-kanban.js +6 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -0
  10. package/dist/license/index.d.ts +5 -0
  11. package/dist/license/index.d.ts.map +1 -0
  12. package/dist/license/index.js +4 -0
  13. package/dist/license/license-expiry-banner.d.ts +16 -0
  14. package/dist/license/license-expiry-banner.d.ts.map +1 -0
  15. package/dist/license/license-expiry-banner.js +85 -0
  16. package/dist/license/license-gate.d.ts +40 -0
  17. package/dist/license/license-gate.d.ts.map +1 -0
  18. package/dist/license/license-gate.js +108 -0
  19. package/dist/license/license-status-badge.d.ts +7 -0
  20. package/dist/license/license-status-badge.d.ts.map +1 -0
  21. package/dist/license/license-status-badge.js +57 -0
  22. package/dist/license/types.d.ts +63 -0
  23. package/dist/license/types.d.ts.map +1 -0
  24. package/dist/license/types.js +45 -0
  25. package/dist/types.d.ts +8 -0
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/use-i18n-resource-version.d.ts +2 -0
  28. package/dist/use-i18n-resource-version.d.ts.map +1 -0
  29. package/dist/use-i18n-resource-version.js +42 -0
  30. package/package.json +3 -3
  31. package/src/__tests__/license-gate.test.tsx +242 -0
  32. package/src/__tests__/wizard-action-modal.test.tsx +121 -0
  33. package/src/action-modal-dispatcher.tsx +233 -0
  34. package/src/dynamic-kanban.tsx +6 -0
  35. package/src/index.ts +15 -0
  36. package/src/license/index.ts +21 -0
  37. package/src/license/license-expiry-banner.tsx +151 -0
  38. package/src/license/license-gate.tsx +277 -0
  39. package/src/license/license-status-badge.tsx +93 -0
  40. package/src/license/types.ts +90 -0
  41. package/src/types.ts +9 -0
  42. package/src/use-i18n-resource-version.ts +42 -0
  43. package/LICENSE +0 -201
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @asteby/metacore-runtime-react
2
2
 
3
+ ## 23.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c94ec63: feat(license-gate): gate de licencia reutilizable — el negocio blindado en el SDK.
8
+
9
+ Nuevo primitivo `<LicenseGate>` (más `<LicenseExpiryBanner>`, `<LicenseStatusBadge>` y los helpers `isLicenseOperable`/`isLicenseBlocking`/`isPresetEntitled`/`isTrialExpired`) que ops y todos los verticales montan en una línea para blindar la app tras la licencia de instancia:
10
+ - Sin enforcement o estado operable (`valid`/`stale`/`grace`) → renderiza children; `stale`/`grace` montan un banner degradado (`expired` no descartable, el resto descartable con TTL en localStorage).
11
+ - Enforcement && `missing`/`invalid`/`expired` → modal bloqueante full-screen no descartable con formulario de activación (clave `lic_…` o token pegado); `plan === 'trial'` + `expired` muestra "Tu prueba gratuita terminó". La activación exitosa desbloquea sin recargar.
12
+ - Branding-aware (logo/nombre) con fallback neutro; i18n es-first con `t(key, { defaultValue })`.
13
+
14
+ Independiente del kernel: el host resuelve el `LicenseState` con su propio transporte y pasa `onActivate`. No requiere release del kernel.
15
+
16
+ ## 23.9.3
17
+
18
+ ### Patch Changes
19
+
20
+ - e67e403: fix(kanban): las etiquetas de lane dejan de mostrar la clave i18n cruda cuando el bundle del addon llega tarde. Un hook (useI18nResourceVersion) re-resuelve las etiquetas al mergearse el bundle async (addResourceBundle), sin depender del bindI18nStore del host.
21
+
3
22
  ## 23.9.2
4
23
 
5
24
  ### Patch Changes
package/README.md CHANGED
@@ -30,6 +30,49 @@ Peers: `react`, `react-dom`, `react-i18next`, `i18next`, `@tanstack/react-router
30
30
  | `useMetadataCache()` | Zustand store for table/modal metadata, persisted to LocalStorage. `prefetchAll(api)` warms it from `/metadata/all`. |
31
31
  | `defaultGetDynamicColumns` / `makeDefaultGetDynamicColumns(helpers)` | The factory `<DynamicTable>` uses to convert metadata into TanStack column defs. The default reads from `col.key` (matching the kernel contract). |
32
32
  | `DynamicIcon` | Lucide icon resolver by name. |
33
+ | `<LicenseGate state={…} onActivate={…}>` | El blindaje del negocio: envuelve la app; bloquea con un modal full-screen no descartable cuando la licencia de instancia falta/venció, degrada con banner en `grace`/`stale`. Branding-aware. |
34
+ | `<LicenseExpiryBanner state={…} />` / `<LicenseStatusBadge status={…} />` | Piezas sueltas del gate para Ajustes u otros lugares. |
35
+ | `isLicenseOperable` / `isLicenseBlocking` / `isPresetEntitled` / `isTrialExpired` | Helpers puros sobre `LicenseState`, espejo de `State.Operable()/Blocking()` del backend. |
36
+
37
+ ## Blindaje de licencia (`LicenseGate`)
38
+
39
+ El gate de licencia es un **primitivo del SDK**: ops y todos los verticales lo montan
40
+ en una línea. Reparto de responsabilidades:
41
+
42
+ - **kernel** → *enforcement* (qué está permitido; firma/verifica el estado).
43
+ - **SDK** (este módulo) → *UX* (gate, banner, badge).
44
+ - **hub** → *emisión* (mintea el token Ed25519).
45
+ - **hosts** → *wiring* (resuelven el `LicenseState` con su propio transporte y montan el gate).
46
+
47
+ Es **independiente del kernel**: no importa su cliente ni fija su versión. El host
48
+ resuelve el estado (p. ej. `GET /admin/license`) y pasa una promesa `onActivate`.
49
+
50
+ ```tsx
51
+ import { LicenseGate } from '@asteby/metacore-runtime-react'
52
+ import { useLicenseState, useActivateLicense } from './features/license'
53
+
54
+ export function Shell({ children }) {
55
+ const { data: state } = useLicenseState() // GET /admin/license
56
+ const activate = useActivateLicense() // POST /admin/license/activate
57
+
58
+ return (
59
+ <LicenseGate
60
+ state={state}
61
+ onActivate={(code) => activate.mutateAsync(code)}
62
+ branding={{ name: brand.name, logo: brand.logo }}
63
+ onManage={() => navigate('/settings/license')}
64
+ >
65
+ {children}
66
+ </LicenseGate>
67
+ )
68
+ }
69
+ ```
70
+
71
+ - Sin enforcement o estado operable (`valid`/`stale`/`grace`) → renderiza children.
72
+ `stale`/`grace` montan además el banner degradado (`expired` no es descartable, el resto sí con TTL en localStorage).
73
+ - Enforcement && `missing`/`invalid`/`expired` → modal **bloqueante** con el formulario de activación.
74
+ `plan === 'trial'` + `expired` → copy *"Tu prueba gratuita terminó"*.
75
+ - Activación exitosa desbloquea **sin recargar**: el host refresca el `state` y el gate se desmonta.
33
76
 
34
77
  ## Minimal usage
35
78
 
@@ -1 +1 @@
1
- {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AAgDA,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAExB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AA0FhD,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,sCAiDlB"}
1
+ {"version":3,"file":"action-modal-dispatcher.d.ts","sourceRoot":"","sources":["../src/action-modal-dispatcher.tsx"],"names":[],"mappings":"AAgDA,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAExB,MAAM,sBAAsB,CAAA;AAE7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAA;AA0FhD,wBAAgB,qBAAqB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,MAAM,EACN,KAAK,EACL,MAAM,EACN,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,sCA+DlB"}
@@ -87,6 +87,9 @@ export function ActionModalDispatcher({ open, onOpenChange, action, model, recor
87
87
  if (CustomComponent) {
88
88
  return (_jsx(CustomComponent, { open: open, onOpenChange: onOpenChange, action: action, model: model, record: record, endpoint: endpoint, onSuccess: onSuccess }));
89
89
  }
90
+ if (action.steps && action.steps.length > 0) {
91
+ return (_jsx(WizardActionModal, { open: open, onOpenChange: onOpenChange, action: action, model: model, record: record, endpoint: endpoint, onSuccess: onSuccess }));
92
+ }
90
93
  if (action.fields && action.fields.length > 0) {
91
94
  return (_jsx(GenericActionModal, { open: open, onOpenChange: onOpenChange, action: action, model: model, record: record, endpoint: endpoint, onSuccess: onSuccess }));
92
95
  }
@@ -254,6 +257,125 @@ function GenericActionModal({ open, onOpenChange, action, model, record, endpoin
254
257
  return (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData)] }, field.key));
255
258
  }), relations.length > 0 && (_jsx(FieldCell, { fullWidth: true, children: _jsx(DynamicRelations, { record: record, relations: relations }) }))] }) }), _jsxs(DialogFooter, { className: "shrink-0", children: [_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') }), _jsxs(Button, { onClick: execute, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), tl(action.label)] })] })] }) }));
256
259
  }
260
+ // buildFieldDefaults seeds formData for a set of action fields, honoring the
261
+ // same line-items prefill spec + boolean/empty rules GenericActionModal uses, so
262
+ // wizard steps and single-page forms initialize identically.
263
+ function buildFieldDefaults(fields, record) {
264
+ const defaults = {};
265
+ for (const field of fields) {
266
+ if (isLineItemsField(field)) {
267
+ const dv = lineItemsDefault(field);
268
+ defaults[field.key] = isPrefillSpec(dv)
269
+ ? buildPrefillRows(dv, record)
270
+ : Array.isArray(dv)
271
+ ? dv
272
+ : [];
273
+ continue;
274
+ }
275
+ defaults[field.key] = field.defaultValue ?? (field.type === 'boolean' ? false : '');
276
+ }
277
+ return defaults;
278
+ }
279
+ // validateFields returns the first validation error for a set of required
280
+ // fields, or null when they all pass. Shared by the wizard (per-step gate) and
281
+ // mirrors GenericActionModal's inline checks. `tl` localizes the field label.
282
+ function validateFields(fields, formData, tl) {
283
+ for (const field of fields) {
284
+ if (!field.required)
285
+ continue;
286
+ if (isLineItemsField(field)) {
287
+ const rows = formData[field.key];
288
+ if (!Array.isArray(rows) || rows.length === 0) {
289
+ return `${tl(field.label)} requiere al menos un renglón`;
290
+ }
291
+ continue;
292
+ }
293
+ if (!formData[field.key] && formData[field.key] !== false) {
294
+ return `${tl(field.label)} es requerido`;
295
+ }
296
+ }
297
+ return null;
298
+ }
299
+ // WizardActionModal — the third render-path: a multi-step form. It accumulates
300
+ // every step's fields into ONE formData object and, on the final step, POSTs all
301
+ // of them to the same endpoint GenericActionModal uses (buildActionUrl). Each
302
+ // step is validated before "Siguiente" advances; a progress/step bar shows where
303
+ // the user is. Widgets are rendered by the same renderField/resolveWidget path,
304
+ // so line-items, dynamic_select, uploads and dates behave identically to a
305
+ // single-page action form — no widget is duplicated.
306
+ function WizardActionModal({ open, onOpenChange, action, model, record, endpoint, onSuccess }) {
307
+ const { t } = useTranslation();
308
+ const tl = (s) => t(s, { defaultValue: s });
309
+ const api = useApi();
310
+ const steps = action.steps ?? [];
311
+ const [stepIndex, setStepIndex] = useState(0);
312
+ const [formData, setFormData] = useState({});
313
+ const [executing, setExecuting] = useState(false);
314
+ // Reset to the first step and seed defaults for EVERY step's fields whenever
315
+ // the modal (re)opens, so accumulated values from a prior run don't leak.
316
+ useEffect(() => {
317
+ if (!open)
318
+ return;
319
+ const allFields = steps.flatMap((s) => s.fields ?? []);
320
+ setFormData(buildFieldDefaults(allFields, record));
321
+ setStepIndex(0);
322
+ }, [open, action.steps, record]);
323
+ const updateField = (key, value) => setFormData((prev) => ({ ...prev, [key]: value }));
324
+ const step = steps[stepIndex];
325
+ const isLast = stepIndex === steps.length - 1;
326
+ const stepFields = step?.fields ?? [];
327
+ const hasLineItems = useMemo(() => stepFields.some(isLineItemsField), [stepFields]);
328
+ const widthPx = hasLineItems ? '820px' : undefined;
329
+ const goNext = () => {
330
+ const err = validateFields(stepFields, formData, tl);
331
+ if (err) {
332
+ toast.error(err);
333
+ return;
334
+ }
335
+ setStepIndex((i) => Math.min(i + 1, steps.length - 1));
336
+ };
337
+ const goBack = () => setStepIndex((i) => Math.max(i - 1, 0));
338
+ const submit = async () => {
339
+ // Guard every step's required fields on final submit (a user could reach
340
+ // the last step with an untouched earlier line-items grid otherwise).
341
+ for (const s of steps) {
342
+ const err = validateFields(s.fields ?? [], formData, tl);
343
+ if (err) {
344
+ toast.error(err);
345
+ return;
346
+ }
347
+ }
348
+ setExecuting(true);
349
+ try {
350
+ const url = buildActionUrl(endpoint, model, record?.id, action.key);
351
+ const res = await api.post(url, formData);
352
+ if (res.data.success) {
353
+ toast.success(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.success'));
354
+ onOpenChange(false);
355
+ onSuccess();
356
+ }
357
+ else {
358
+ toast.error(res.data.message ? t(res.data.message, { defaultValue: res.data.message }) : t('common.error'));
359
+ }
360
+ }
361
+ catch (err) {
362
+ toast.error(err?.response?.data?.message || t('common.error'));
363
+ }
364
+ finally {
365
+ setExecuting(false);
366
+ }
367
+ };
368
+ if (steps.length === 0)
369
+ return null;
370
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: 'flex max-h-[90vh] flex-col overflow-hidden ' + (widthPx ? '' : 'sm:max-w-xl'), style: { maxHeight: '90vh', ...(widthPx ? { maxWidth: widthPx, width: '95vw' } : {}) }, children: [_jsxs(DialogHeader, { className: "shrink-0", children: [_jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(DynamicIcon, { name: action.icon, className: "h-5 w-5" }), tl(action.label)] }), _jsxs("div", { className: "pt-2", children: [_jsx("div", { className: "flex items-center gap-1.5", role: "list", "aria-label": "progress", children: steps.map((s, i) => (_jsx("div", { role: "listitem", "aria-current": i === stepIndex ? 'step' : undefined, className: "h-1.5 flex-1 rounded-full", style: {
371
+ backgroundColor: i <= stepIndex ? (action.color || 'hsl(var(--primary))') : 'hsl(var(--muted))',
372
+ } }, i))) }), _jsxs(DialogDescription, { className: "pt-2", children: [t('common.step', { defaultValue: 'Paso' }), " ", stepIndex + 1, "/", steps.length, step?.title ? ` · ${tl(step.title)}` : ''] }), step?.description && (_jsx("p", { className: "pt-1 text-sm text-muted-foreground", children: tl(step.description) }))] })] }), _jsx("div", { className: "-mx-1 min-h-0 flex-1 overflow-y-auto px-1 py-4", children: _jsx(FieldGrid, { children: stepFields.map((field) => {
373
+ const fullWidth = isLineItemsField(field) ||
374
+ resolveWidget(field) === 'textarea' ||
375
+ resolveWidget(field) === 'richtext';
376
+ return (_jsxs(FieldCell, { fullWidth: fullWidth, children: [_jsx(FieldLabel, { htmlFor: field.key, required: field.required, children: tl(field.label) }), renderField(field, formData[field.key], (v) => updateField(field.key, v), formData)] }, field.key));
377
+ }) }) }), _jsxs(DialogFooter, { className: "shrink-0", children: [stepIndex > 0 ? (_jsx(Button, { variant: "outline", onClick: goBack, disabled: executing, children: t('common.back', { defaultValue: 'Atrás' }) })) : (_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: executing, children: t('common.cancel') })), isLast ? (_jsxs(Button, { onClick: submit, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: [executing ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(DynamicIcon, { name: action.icon, className: "mr-2 h-4 w-4" }), tl(action.label)] })) : (_jsx(Button, { onClick: goNext, disabled: executing, style: action.color ? { backgroundColor: action.color, color: 'white' } : undefined, children: t('common.next', { defaultValue: 'Siguiente' }) }))] })] }) }));
378
+ }
257
379
  function renderField(field, value, onChange,
258
380
  // Full current form values — lets a line-items grid (and any cascading
259
381
  // header picker) resolve a `dependsOn` reference against sibling header
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-kanban.d.ts","sourceRoot":"","sources":["../src/dynamic-kanban.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAyF9B,OAAO,EAEH,qBAAqB,EACrB,qBAAqB,EACxB,MAAM,gBAAgB,CAAA;AAUvB,OAAO,KAAK,EACR,aAAa,EACb,gBAAgB,EAGhB,SAAS,EACT,eAAe,EAClB,MAAM,SAAS,CAAA;AAIhB,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,CAAA;AAMvD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,aAAa,GAAG,SAAS,EAAE,CAejE;AAQD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAA;AAE/C,wBAAgB,YAAY,CACxB,OAAO,EAAE,GAAG,EAAE,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,SAAS,EAAE,GACpB,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAepB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAC/B,WAAW,EAAE,eAAe,EAAE,GAAG,SAAS,EAC1C,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACX,OAAO,CAOT;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAC/B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAC3B,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACnB,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAYpB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACpE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAC7B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GAChB,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAWnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC3B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,UAAU,EAAE,OAAO,GACpB,MAAM,CAMR;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC7B,QAAQ,EAAE,aAAa,EACvB,SAAS,SAAI,GACd;IAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAAE,CAkBhE;AAKD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,GAAG,OAAO,CASvE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACjC,IAAI,EAAE,GAAG,EACT,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GACzE,OAAO,CAUT;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC3B,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GACxD,MAAM,CAIR;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAChC,IAAI,EAAE,GAAG,EACT,IAAI,EAAE,gBAAgB,EAAE,EACxB,KAAK,EAAE,MAAM,GACd,OAAO,CAQT;AAwDD,MAAM,WAAW,kBAAkB;IAC/B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kFAAkF;IAClF,cAAc,CAAC,EAAE,GAAG,CAAA;IACpB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAChC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACvC;AAED,wBAAgB,aAAa,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,cAAc,EACd,WAAW,EACX,QAAQ,EACR,QAAa,EACb,YAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,cAAc,GACjB,EAAE,kBAAkB,qBAsmCpB"}
1
+ {"version":3,"file":"dynamic-kanban.d.ts","sourceRoot":"","sources":["../src/dynamic-kanban.tsx"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
+ import { useI18nResourceVersion } from './use-i18n-resource-version';
4
5
  import { DndContext, DragOverlay, PointerSensor, useSensor, useSensors, useDraggable, useDroppable, } from '@dnd-kit/core';
5
6
  import { SortableContext, horizontalListSortingStrategy, useSortable, } from '@dnd-kit/sortable';
6
7
  import { CSS } from '@dnd-kit/utilities';
@@ -1082,6 +1083,11 @@ function SortableSmartLane({ droppableDisabled, smartProps, }) {
1082
1083
  }
1083
1084
  function KanbanLane({ stage, count, totalCount, serverTotal, hasMore, loadingMore, onLoadMore, filterFields, laneFilter, onFunnelChange, onQueryChange, isDark, dimmed, dnd, model, columns, automationsAvailable, automationRules, onAutomationCreate, onAutomationUpdate, onAutomationRemove, extraFilters, configurable, onConfigure, children, }) {
1084
1085
  const { t } = useTranslation();
1086
+ // Re-resolve the lane label when an addon i18n bundle lands after the board
1087
+ // painted (async addResourceBundle) — otherwise a manifest-key label like
1088
+ // "integration_github.stage.in_progress" stays raw until an unrelated
1089
+ // re-render. Independent of the host's react-i18next bindI18nStore config.
1090
+ useI18nResourceVersion();
1085
1091
  // Infinite scroll: the sentinel lives at the bottom of the lane's own scroll
1086
1092
  // container; a load in flight or an exhausted stage disables it.
1087
1093
  const { rootRef, sentinelRef } = useInfiniteScrollSentinel({
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './types';
2
+ export { LicenseGate, LicenseExpiryBanner, LicenseStatusBadge, isLicenseOperable, isLicenseBlocking, isPresetEntitled, isTrialExpired, type LicenseGateProps, type LicenseExpiryBannerProps, type LicenseStatusBadgeProps, type LicenseState, type LicenseStatus, type LicenseBranding, } from './license';
2
3
  export * from './options-context';
3
4
  export * from './dynamic-table';
4
5
  export { DynamicKanban, type DynamicKanbanProps, deriveStages, groupByStage, isTransitionAllowed, applyOptimisticMove, selectCardColumns, UNASSIGNED_LANE, } from './dynamic-kanban';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
  // duplicate-symbol conflict; consumers who want the canonical SDK type
5
5
  // should import from `@asteby/metacore-sdk` directly.
6
6
  export * from './types';
7
+ export { LicenseGate, LicenseExpiryBanner, LicenseStatusBadge, isLicenseOperable, isLicenseBlocking, isPresetEntitled, isTrialExpired, } from './license';
7
8
  export * from './options-context';
8
9
  export * from './dynamic-table';
9
10
  export { DynamicKanban, deriveStages, groupByStage, isTransitionAllowed, applyOptimisticMove, selectCardColumns, UNASSIGNED_LANE, } from './dynamic-kanban';
@@ -0,0 +1,5 @@
1
+ export { LicenseGate, type LicenseGateProps, } from './license-gate';
2
+ export { LicenseExpiryBanner, type LicenseExpiryBannerProps, } from './license-expiry-banner';
3
+ export { LicenseStatusBadge, type LicenseStatusBadgeProps, } from './license-status-badge';
4
+ export { isLicenseOperable, isLicenseBlocking, isPresetEntitled, isTrialExpired, type LicenseState, type LicenseStatus, type LicenseBranding, } from './types';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/license/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,WAAW,EACX,KAAK,gBAAgB,GACxB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACH,mBAAmB,EACnB,KAAK,wBAAwB,GAChC,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACH,kBAAkB,EAClB,KAAK,uBAAuB,GAC/B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACH,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,eAAe,GACvB,MAAM,SAAS,CAAA"}
@@ -0,0 +1,4 @@
1
+ export { LicenseGate, } from './license-gate';
2
+ export { LicenseExpiryBanner, } from './license-expiry-banner';
3
+ export { LicenseStatusBadge, } from './license-status-badge';
4
+ export { isLicenseOperable, isLicenseBlocking, isPresetEntitled, isTrialExpired, } from './types';
@@ -0,0 +1,16 @@
1
+ import type { LicenseState } from './types';
2
+ export interface LicenseExpiryBannerProps {
3
+ state: LicenseState | undefined;
4
+ /** Días de anticipación para el aviso de renovación. Default 15. */
5
+ upcomingThresholdDays?: number;
6
+ /** Clave localStorage para recordar el descarte. */
7
+ dismissStorageKey?: string;
8
+ /** Acción "Gestionar licencia" — el host enruta a sus Ajustes. Si se omite,
9
+ * no se muestra el botón (el gate no asume rutas del host). */
10
+ onManage?: () => void;
11
+ /** Etiqueta del botón de gestión (default localizado). */
12
+ manageLabel?: string;
13
+ className?: string;
14
+ }
15
+ export declare function LicenseExpiryBanner({ state, upcomingThresholdDays, dismissStorageKey, onManage, manageLabel, className, }: LicenseExpiryBannerProps): import("react").JSX.Element | null;
16
+ //# sourceMappingURL=license-expiry-banner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"license-expiry-banner.d.ts","sourceRoot":"","sources":["../../src/license/license-expiry-banner.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAU3C,MAAM,WAAW,wBAAwB;IACrC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;IAC/B,oEAAoE;IACpE,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;mEAC+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,mBAAmB,CAAC,EAChC,KAAK,EACL,qBAA+C,EAC/C,iBAA+B,EAC/B,QAAQ,EACR,WAAW,EACX,SAAS,GACZ,EAAE,wBAAwB,sCAqG1B"}
@@ -0,0 +1,85 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Banner de vencimiento de licencia — la señal degradada que acompaña a las
4
+ * posturas que NO bloquean la app pero exigen atención:
5
+ * - valid && days_remaining <= threshold → aviso de renovación próxima
6
+ * - stale → check-in con el hub pendiente
7
+ * - grace → vencida, dentro de la gracia
8
+ * - expired → vencida en duro (persistente)
9
+ *
10
+ * Silencioso sin enforcement o si el estado aún no cargó — nunca rompe el shell.
11
+ *
12
+ * Descartabilidad: todas las posturas son descartables con TTL en localStorage
13
+ * (re-aparecen pasado el TTL) MENOS `expired`, que se queda fijo como recordatorio
14
+ * porque el backend ya bloquea escrituras nuevas.
15
+ */
16
+ import { useState } from 'react';
17
+ import { useTranslation } from 'react-i18next';
18
+ import { AlertTriangle, Clock, CloudOff, X } from 'lucide-react';
19
+ import { Button } from '@asteby/metacore-ui';
20
+ const DISMISS_KEY = 'license_banner_dismissed_at';
21
+ const DISMISS_TTL_MS = 12 * 60 * 60 * 1000; // re-mostrar cada 12h aunque se descarte
22
+ const UPCOMING_THRESHOLD_DAYS = 15;
23
+ function cx(...parts) {
24
+ return parts.filter(Boolean).join(' ');
25
+ }
26
+ export function LicenseExpiryBanner({ state, upcomingThresholdDays = UPCOMING_THRESHOLD_DAYS, dismissStorageKey = DISMISS_KEY, onManage, manageLabel, className, }) {
27
+ const { t } = useTranslation();
28
+ const [dismissed, setDismissed] = useState(() => {
29
+ if (typeof window === 'undefined')
30
+ return false;
31
+ const raw = window.localStorage.getItem(dismissStorageKey);
32
+ if (!raw)
33
+ return false;
34
+ return Date.now() - Number(raw) < DISMISS_TTL_MS;
35
+ });
36
+ if (!state || !state.enforced)
37
+ return null;
38
+ const isUpcoming = state.status === 'valid' && state.days_remaining <= upcomingThresholdDays;
39
+ const isStale = state.status === 'stale';
40
+ const isGrace = state.status === 'grace';
41
+ const isExpired = state.status === 'expired';
42
+ if (!isUpcoming && !isStale && !isGrace && !isExpired)
43
+ return null;
44
+ // Expired no es descartable — se queda como recordatorio constante.
45
+ if (dismissed && !isExpired)
46
+ return null;
47
+ const dismiss = () => {
48
+ setDismissed(true);
49
+ try {
50
+ window.localStorage.setItem(dismissStorageKey, String(Date.now()));
51
+ }
52
+ catch {
53
+ // cuota/entorno sin storage — el descarte solo dura la sesión.
54
+ }
55
+ };
56
+ const message = isExpired
57
+ ? t('license.banner.expired', {
58
+ defaultValue: 'Tu licencia venció. Algunas acciones pueden estar bloqueadas.',
59
+ })
60
+ : isStale
61
+ ? t('license.banner.stale', {
62
+ defaultValue: 'Esta instancia no ha podido verificar su licencia con el hub. Reconéctala para completar el check-in.',
63
+ })
64
+ : isGrace
65
+ ? t('license.banner.grace', {
66
+ defaultValue: 'Tu licencia venció y está en periodo de gracia hasta {{date}} ({{days}} días restantes).',
67
+ date: state.grace_until
68
+ ? new Date(state.grace_until).toLocaleDateString()
69
+ : '—',
70
+ days: state.days_remaining,
71
+ })
72
+ : t('license.banner.upcoming', {
73
+ defaultValue: 'Tu licencia vence en {{days}} días.',
74
+ days: state.days_remaining,
75
+ });
76
+ const Icon = isExpired ? AlertTriangle : isStale ? CloudOff : Clock;
77
+ return (_jsxs("div", { role: "alert", "data-license-status": state.status, className: cx('flex items-center justify-between gap-3 border-b px-4 py-2.5 text-sm', isExpired
78
+ ? 'border-destructive/30 bg-destructive text-white'
79
+ : 'border-amber-500/30 bg-amber-500/10 text-foreground', className), children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx(Icon, { className: "h-4 w-4 shrink-0", "aria-hidden": true }), _jsx("span", { className: "truncate", children: message })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [onManage && (_jsx(Button, { size: "sm", variant: isExpired ? 'secondary' : 'outline', onClick: onManage, children: manageLabel ??
80
+ t('license.banner.manage', {
81
+ defaultValue: 'Gestionar licencia',
82
+ }) })), !isExpired && (_jsx(Button, { size: "icon", variant: "ghost", className: "h-7 w-7", onClick: dismiss, title: t('license.banner.dismiss', {
83
+ defaultValue: 'Descartar',
84
+ }), children: _jsx(X, { className: "h-4 w-4" }) }))] })] }));
85
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * <LicenseGate> — el primitivo del blindaje. Un host lo monta en UNA línea
3
+ * envolviendo su app autenticada:
4
+ *
5
+ * <LicenseGate state={state} onActivate={activate}>
6
+ * <AppShell />
7
+ * </LicenseGate>
8
+ *
9
+ * Comportamiento según el estado (resuelto por el backend del host):
10
+ * - Sin enforcement, o estado operable (valid/stale/grace) → renderiza children.
11
+ * stale/grace además montan el <LicenseExpiryBanner> degradado por encima.
12
+ * - enforcement && missing/invalid/expired → modal BLOQUEANTE full-screen,
13
+ * no descartable, con el formulario de activación. Al activar con éxito el
14
+ * host refresca el estado y el gate se abre sin recargar.
15
+ *
16
+ * Es branding-aware si el host pasa `branding` (logo/nombre desde el
17
+ * PlatformConfig del SDK); si no, cae a un encabezado neutro.
18
+ *
19
+ * INDEPENDIENTE del kernel: no importa su cliente ni fija su versión. Solo
20
+ * consume `LicenseState` y una promesa `onActivate`.
21
+ */
22
+ import { type ReactNode } from 'react';
23
+ import { type LicenseState, type LicenseBranding } from './types';
24
+ export interface LicenseGateProps {
25
+ /** Estado actual, resuelto por el backend del host. `undefined` mientras
26
+ * carga → fail-open (renderiza children, nunca destella el candado). */
27
+ state: LicenseState | undefined;
28
+ /** Activa una licencia con el código/token pegado. Resuelve → el host
29
+ * refresca `state` y el gate se abre. Rechaza con un Error cuyo `message`
30
+ * se muestra al usuario. */
31
+ onActivate: (code: string) => Promise<void>;
32
+ children: ReactNode;
33
+ /** Branding opcional (logo/nombre) para el encabezado del modal. */
34
+ branding?: LicenseBranding;
35
+ /** Acción "Gestionar licencia" del banner degradado (opcional). */
36
+ onManage?: () => void;
37
+ className?: string;
38
+ }
39
+ export declare function LicenseGate({ state, onActivate, children, branding, onManage, className, }: LicenseGateProps): import("react").JSX.Element;
40
+ //# sourceMappingURL=license-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"license-gate.d.ts","sourceRoot":"","sources":["../../src/license/license-gate.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAIhD,OAAO,EAGH,KAAK,YAAY,EACjB,KAAK,eAAe,EACvB,MAAM,SAAS,CAAA;AAQhB,MAAM,WAAW,gBAAgB;IAC7B;4EACwE;IACxE,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;IAC/B;;gCAE4B;IAC5B,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3C,QAAQ,EAAE,SAAS,CAAA;IACnB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,WAAW,CAAC,EACxB,KAAK,EACL,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,SAAS,GACZ,EAAE,gBAAgB,+BAqBlB"}
@@ -0,0 +1,108 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * <LicenseGate> — el primitivo del blindaje. Un host lo monta en UNA línea
4
+ * envolviendo su app autenticada:
5
+ *
6
+ * <LicenseGate state={state} onActivate={activate}>
7
+ * <AppShell />
8
+ * </LicenseGate>
9
+ *
10
+ * Comportamiento según el estado (resuelto por el backend del host):
11
+ * - Sin enforcement, o estado operable (valid/stale/grace) → renderiza children.
12
+ * stale/grace además montan el <LicenseExpiryBanner> degradado por encima.
13
+ * - enforcement && missing/invalid/expired → modal BLOQUEANTE full-screen,
14
+ * no descartable, con el formulario de activación. Al activar con éxito el
15
+ * host refresca el estado y el gate se abre sin recargar.
16
+ *
17
+ * Es branding-aware si el host pasa `branding` (logo/nombre desde el
18
+ * PlatformConfig del SDK); si no, cae a un encabezado neutro.
19
+ *
20
+ * INDEPENDIENTE del kernel: no importa su cliente ni fija su versión. Solo
21
+ * consume `LicenseState` y una promesa `onActivate`.
22
+ */
23
+ import { useState } from 'react';
24
+ import { useTranslation } from 'react-i18next';
25
+ import { ShieldAlert, Clock, Loader2 } from 'lucide-react';
26
+ import { Button, Input } from '@asteby/metacore-ui';
27
+ import { isLicenseBlocking, isTrialExpired, } from './types';
28
+ import { LicenseStatusBadge } from './license-status-badge';
29
+ import { LicenseExpiryBanner } from './license-expiry-banner';
30
+ function cx(...parts) {
31
+ return parts.filter(Boolean).join(' ');
32
+ }
33
+ export function LicenseGate({ state, onActivate, children, branding, onManage, className, }) {
34
+ const blocking = isLicenseBlocking(state);
35
+ return (_jsxs(_Fragment, { children: [!blocking && (_jsx(LicenseExpiryBanner, { state: state, onManage: onManage })), children, blocking && state && (_jsx(LicenseGateModal, { state: state, onActivate: onActivate, branding: branding, className: className }))] }));
36
+ }
37
+ function LicenseGateModal({ state, onActivate, branding, className, }) {
38
+ const { t } = useTranslation();
39
+ const [code, setCode] = useState('');
40
+ const [submitting, setSubmitting] = useState(false);
41
+ const [error, setError] = useState(null);
42
+ const trialExpired = isTrialExpired(state);
43
+ const title = trialExpired
44
+ ? t('license.gate.trial_title', {
45
+ defaultValue: 'Tu prueba gratuita terminó',
46
+ })
47
+ : t('license.gate.title', { defaultValue: 'Activa tu licencia' });
48
+ const description = trialExpired
49
+ ? t('license.gate.trial_description', {
50
+ defaultValue: 'Tu prueba gratuita terminó. Activa una licencia para continuar. Pega tu clave (lic_…) o el token firmado que te entregaron.',
51
+ })
52
+ : state.status === 'expired'
53
+ ? t('license.gate.expired_description', {
54
+ defaultValue: 'Tu licencia venció y el periodo de gracia terminó. Activa una licencia vigente para seguir operando. Pega tu clave (lic_…) o el token firmado.',
55
+ })
56
+ : state.status === 'invalid'
57
+ ? t('license.gate.invalid_description', {
58
+ defaultValue: 'La licencia de esta instancia no es válida. Pega una clave (lic_…) o el token firmado que te entregó tu proveedor para reactivarla.',
59
+ })
60
+ : t('license.gate.missing_description', {
61
+ defaultValue: 'Esta instancia necesita una licencia activa para operar. Pega tu clave (lic_…) o el token firmado para activarla.',
62
+ });
63
+ const canSubmit = code.trim().length > 0 && !submitting;
64
+ const submit = async () => {
65
+ if (!canSubmit)
66
+ return;
67
+ setSubmitting(true);
68
+ setError(null);
69
+ try {
70
+ await onActivate(code.trim());
71
+ // Éxito → el host refresca `state`; el gate se re-renderiza y este
72
+ // modal se desmonta solo. No recargamos.
73
+ }
74
+ catch (e) {
75
+ const message = e instanceof Error && e.message
76
+ ? e.message
77
+ : t('license.gate.activate_error', {
78
+ defaultValue: 'No se pudo activar la licencia.',
79
+ });
80
+ setError(message);
81
+ setSubmitting(false);
82
+ }
83
+ };
84
+ return (_jsx("div", { role: "dialog", "aria-modal": "true", "aria-labelledby": "license-gate-title", "data-license-status": state.status, className: cx('fixed inset-0 z-[100] flex items-center justify-center bg-background/90 p-4 backdrop-blur-sm', className), children: _jsx("div", { className: "w-full max-w-lg overflow-hidden rounded-2xl border bg-card text-card-foreground shadow-2xl", children: _jsxs("div", { className: "flex flex-col gap-4 p-6", children: [_jsxs("div", { className: "flex items-start gap-3", children: [branding?.logo ? (_jsx("img", { src: branding.logo, alt: branding.name ?? '', className: "h-10 w-10 shrink-0 rounded-xl object-contain" })) : (_jsx("div", { className: cx('flex h-10 w-10 shrink-0 items-center justify-center rounded-xl', trialExpired
85
+ ? 'bg-primary/10 text-primary'
86
+ : 'bg-destructive/10 text-destructive'), children: trialExpired ? (_jsx(Clock, { className: "h-5 w-5", "aria-hidden": true })) : (_jsx(ShieldAlert, { className: "h-5 w-5", "aria-hidden": true })) })), _jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [_jsx("h2", { id: "license-gate-title", className: "text-lg leading-tight font-semibold", children: title }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(LicenseStatusBadge, { status: state.status }), _jsx("span", { className: "text-muted-foreground text-xs", children: branding?.name
87
+ ? branding.name
88
+ : t('license.gate.instance', {
89
+ defaultValue: 'Esta instancia',
90
+ }) })] })] })] }), _jsx("p", { className: "text-muted-foreground text-sm", children: description }), state.reason && (_jsx("p", { className: "bg-muted/50 text-muted-foreground rounded-md px-3 py-2 text-xs", children: state.reason })), _jsxs("form", { className: "flex flex-col gap-2", onSubmit: (e) => {
91
+ e.preventDefault();
92
+ void submit();
93
+ }, children: [_jsx("label", { htmlFor: "license-gate-code", className: "text-sm font-medium", children: t('license.gate.code_label', {
94
+ defaultValue: 'Clave o token de licencia',
95
+ }) }), _jsx(Input, { id: "license-gate-code", autoFocus: true, autoComplete: "off", spellCheck: false, placeholder: t('license.gate.code_placeholder', {
96
+ defaultValue: 'lic_… o pega el token firmado',
97
+ }), value: code, disabled: submitting, "aria-invalid": error ? true : undefined, onChange: (e) => {
98
+ setCode(e.target.value);
99
+ if (error)
100
+ setError(null);
101
+ } }), error && (_jsx("p", { role: "alert", className: "text-destructive text-sm", children: error })), _jsxs(Button, { type: "submit", className: "mt-1 w-full", disabled: !canSubmit, children: [submitting && (_jsx(Loader2, { className: "h-4 w-4 animate-spin", "aria-hidden": true })), submitting
102
+ ? t('license.gate.activating', {
103
+ defaultValue: 'Activando…',
104
+ })
105
+ : t('license.gate.activate', {
106
+ defaultValue: 'Activar licencia',
107
+ })] })] })] }) }) }));
108
+ }
@@ -0,0 +1,7 @@
1
+ import type { LicenseStatus } from './types';
2
+ export interface LicenseStatusBadgeProps {
3
+ status: LicenseStatus;
4
+ className?: string;
5
+ }
6
+ export declare function LicenseStatusBadge({ status, className }: LicenseStatusBadgeProps): import("react").JSX.Element;
7
+ //# sourceMappingURL=license-status-badge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"license-status-badge.d.ts","sourceRoot":"","sources":["../../src/license/license-status-badge.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAuD5C,MAAM,WAAW,uBAAuB;IACpC,MAAM,EAAE,aAAa,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,uBAAuB,+BAkBhF"}