@asteby/metacore-runtime-react 45.0.0 → 45.2.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.
- package/CHANGELOG.md +17 -0
- package/dist/approval-events.d.ts +16 -0
- package/dist/approval-events.d.ts.map +1 -0
- package/dist/approval-events.js +27 -0
- package/dist/approval-gate.d.ts +39 -0
- package/dist/approval-gate.d.ts.map +1 -0
- package/dist/approval-gate.js +110 -0
- package/dist/approval-pin-dialog.d.ts +25 -0
- package/dist/approval-pin-dialog.d.ts.map +1 -0
- package/dist/approval-pin-dialog.js +66 -0
- package/dist/business/catalog.d.ts +16 -0
- package/dist/business/catalog.d.ts.map +1 -0
- package/dist/business/catalog.js +42 -0
- package/dist/business/customer-picker.d.ts +39 -0
- package/dist/business/customer-picker.d.ts.map +1 -0
- package/dist/business/customer-picker.js +51 -0
- package/dist/business/feedback.d.ts +72 -0
- package/dist/business/feedback.d.ts.map +1 -0
- package/dist/business/feedback.js +52 -0
- package/dist/business/field-errors.d.ts +26 -0
- package/dist/business/field-errors.d.ts.map +1 -0
- package/dist/business/field-errors.js +59 -0
- package/dist/business/format.d.ts +49 -0
- package/dist/business/format.d.ts.map +1 -0
- package/dist/business/format.js +128 -0
- package/dist/business/index.d.ts +14 -0
- package/dist/business/index.d.ts.map +1 -0
- package/dist/business/index.js +13 -0
- package/dist/business/line-items-editor.d.ts +25 -0
- package/dist/business/line-items-editor.d.ts.map +1 -0
- package/dist/business/line-items-editor.js +36 -0
- package/dist/business/line-items.d.ts +69 -0
- package/dist/business/line-items.d.ts.map +1 -0
- package/dist/business/line-items.js +133 -0
- package/dist/business/payment-capture.d.ts +21 -0
- package/dist/business/payment-capture.d.ts.map +1 -0
- package/dist/business/payment-capture.js +24 -0
- package/dist/business/payment.d.ts +61 -0
- package/dist/business/payment.d.ts.map +1 -0
- package/dist/business/payment.js +74 -0
- package/dist/business/product-picker.d.ts +24 -0
- package/dist/business/product-picker.d.ts.map +1 -0
- package/dist/business/product-picker.js +48 -0
- package/dist/business/product-search.d.ts +70 -0
- package/dist/business/product-search.d.ts.map +1 -0
- package/dist/business/product-search.js +51 -0
- package/dist/business/refund-destination.d.ts +19 -0
- package/dist/business/refund-destination.d.ts.map +1 -0
- package/dist/business/refund-destination.js +31 -0
- package/dist/business/refund.d.ts +29 -0
- package/dist/business/refund.d.ts.map +1 -0
- package/dist/business/refund.js +43 -0
- package/dist/business/use-async-search.d.ts +10 -0
- package/dist/business/use-async-search.d.ts.map +1 -0
- package/dist/business/use-async-search.js +38 -0
- package/dist/document/action-bar.d.ts +12 -0
- package/dist/document/action-bar.d.ts.map +1 -0
- package/dist/document/action-bar.js +39 -0
- package/dist/document/cancel-with-reason.d.ts +47 -0
- package/dist/document/cancel-with-reason.d.ts.map +1 -0
- package/dist/document/cancel-with-reason.js +52 -0
- package/dist/document/document-header.d.ts +41 -0
- package/dist/document/document-header.d.ts.map +1 -0
- package/dist/document/document-header.js +30 -0
- package/dist/document/document-page.d.ts +37 -0
- package/dist/document/document-page.d.ts.map +1 -0
- package/dist/document/document-page.js +137 -0
- package/dist/document/document-timeline.d.ts +20 -0
- package/dist/document/document-timeline.d.ts.map +1 -0
- package/dist/document/document-timeline.js +26 -0
- package/dist/document/evaluate.d.ts +31 -0
- package/dist/document/evaluate.d.ts.map +1 -0
- package/dist/document/evaluate.js +170 -0
- package/dist/document/format.d.ts +9 -0
- package/dist/document/format.d.ts.map +1 -0
- package/dist/document/format.js +52 -0
- package/dist/document/index.d.ts +13 -0
- package/dist/document/index.d.ts.map +1 -0
- package/dist/document/index.js +12 -0
- package/dist/document/registry.d.ts +20 -0
- package/dist/document/registry.d.ts.map +1 -0
- package/dist/document/registry.js +20 -0
- package/dist/document/smart-buttons.d.ts +16 -0
- package/dist/document/smart-buttons.d.ts.map +1 -0
- package/dist/document/smart-buttons.js +16 -0
- package/dist/document/status-badge.d.ts +16 -0
- package/dist/document/status-badge.d.ts.map +1 -0
- package/dist/document/status-badge.js +23 -0
- package/dist/document/types.d.ts +196 -0
- package/dist/document/types.d.ts.map +1 -0
- package/dist/document/types.js +1 -0
- package/dist/document/use-document-sources.d.ts +14 -0
- package/dist/document/use-document-sources.d.ts.map +1 -0
- package/dist/document/use-document-sources.js +72 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/server-error.d.ts.map +1 -1
- package/dist/server-error.js +19 -0
- package/package.json +1 -1
- package/src/__tests__/approval-gate.test.tsx +134 -0
- package/src/approval-events.ts +39 -0
- package/src/approval-gate.tsx +180 -0
- package/src/approval-pin-dialog.tsx +162 -0
- package/src/business/business.test.ts +164 -0
- package/src/business/catalog.ts +60 -0
- package/src/business/customer-picker.tsx +201 -0
- package/src/business/feedback.tsx +141 -0
- package/src/business/field-errors.ts +92 -0
- package/src/business/format.ts +151 -0
- package/src/business/index.ts +13 -0
- package/src/business/line-items-editor.tsx +259 -0
- package/src/business/line-items.ts +190 -0
- package/src/business/payment-capture.tsx +158 -0
- package/src/business/payment.ts +125 -0
- package/src/business/product-picker.tsx +171 -0
- package/src/business/product-search.ts +108 -0
- package/src/business/refund-destination.tsx +121 -0
- package/src/business/refund.ts +71 -0
- package/src/business/use-async-search.ts +43 -0
- package/src/document/__tests__/document-page.test.tsx +107 -0
- package/src/document/__tests__/evaluate.test.ts +140 -0
- package/src/document/action-bar.tsx +140 -0
- package/src/document/cancel-with-reason.tsx +214 -0
- package/src/document/document-header.tsx +140 -0
- package/src/document/document-page.tsx +268 -0
- package/src/document/document-timeline.tsx +66 -0
- package/src/document/evaluate.ts +180 -0
- package/src/document/format.ts +59 -0
- package/src/document/index.ts +28 -0
- package/src/document/registry.ts +39 -0
- package/src/document/smart-buttons.tsx +54 -0
- package/src/document/status-badge.tsx +44 -0
- package/src/document/types.ts +203 -0
- package/src/document/use-document-sources.ts +91 -0
- package/src/index.ts +23 -0
- package/src/server-error.ts +20 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/** Máximo de secundarias visibles; el resto pasa a «Más…» (§5.2 / §6.2). */
|
|
2
|
+
export const MAX_VISIBLE_SECONDARY = 3;
|
|
3
|
+
/**
|
|
4
|
+
* Lee una ruta del contexto. `$.x` son los derivados; `sources.k.length` es el
|
|
5
|
+
* total de una fuente; el resto cuelga del registro (`record.x` o `x`).
|
|
6
|
+
*/
|
|
7
|
+
export function readPath(ctx, path) {
|
|
8
|
+
if (!path)
|
|
9
|
+
return undefined;
|
|
10
|
+
let parts = path.split('.');
|
|
11
|
+
let cur;
|
|
12
|
+
if (parts[0] === '$') {
|
|
13
|
+
cur = ctx.derived;
|
|
14
|
+
parts = parts.slice(1);
|
|
15
|
+
}
|
|
16
|
+
else if (parts[0] === 'sources') {
|
|
17
|
+
cur = ctx.sources;
|
|
18
|
+
parts = parts.slice(1);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
cur = ctx.record;
|
|
22
|
+
if (parts[0] === 'record')
|
|
23
|
+
parts = parts.slice(1);
|
|
24
|
+
}
|
|
25
|
+
for (const p of parts) {
|
|
26
|
+
if (cur == null)
|
|
27
|
+
return undefined;
|
|
28
|
+
if (p === 'length' && Array.isArray(cur)) {
|
|
29
|
+
cur = cur.length;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
cur = cur[p];
|
|
33
|
+
}
|
|
34
|
+
return cur;
|
|
35
|
+
}
|
|
36
|
+
function sameValue(a, b) {
|
|
37
|
+
if (a == null || b == null)
|
|
38
|
+
return a == b;
|
|
39
|
+
return String(a) === String(b);
|
|
40
|
+
}
|
|
41
|
+
function isTruthy(v) {
|
|
42
|
+
if (v == null || v === false || v === '' || v === 0)
|
|
43
|
+
return false;
|
|
44
|
+
if (Array.isArray(v))
|
|
45
|
+
return v.length > 0;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
function toNumber(v) {
|
|
49
|
+
const n = typeof v === 'number' ? v : parseFloat(String(v));
|
|
50
|
+
return Number.isFinite(n) ? n : NaN;
|
|
51
|
+
}
|
|
52
|
+
export function evaluatePredicate(p, ctx) {
|
|
53
|
+
const v = readPath(ctx, p.field);
|
|
54
|
+
switch (p.op) {
|
|
55
|
+
case 'eq':
|
|
56
|
+
return sameValue(v, p.value);
|
|
57
|
+
case 'neq':
|
|
58
|
+
return !sameValue(v, p.value);
|
|
59
|
+
case 'in':
|
|
60
|
+
return Array.isArray(p.value) && p.value.some((x) => sameValue(v, x));
|
|
61
|
+
case 'not_in':
|
|
62
|
+
return !(Array.isArray(p.value) && p.value.some((x) => sameValue(v, x)));
|
|
63
|
+
case 'truthy':
|
|
64
|
+
return isTruthy(v);
|
|
65
|
+
case 'falsy':
|
|
66
|
+
return !isTruthy(v);
|
|
67
|
+
case 'gt':
|
|
68
|
+
return toNumber(v) > toNumber(p.value);
|
|
69
|
+
case 'gte':
|
|
70
|
+
return toNumber(v) >= toNumber(p.value);
|
|
71
|
+
case 'lt':
|
|
72
|
+
return toNumber(v) < toNumber(p.value);
|
|
73
|
+
case 'lte':
|
|
74
|
+
return toNumber(v) <= toNumber(p.value);
|
|
75
|
+
default:
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Un arreglo es un AND; sin condición siempre se cumple. */
|
|
80
|
+
export function evaluateWhen(when, ctx) {
|
|
81
|
+
if (!when)
|
|
82
|
+
return true;
|
|
83
|
+
const list = Array.isArray(when) ? when : [when];
|
|
84
|
+
return list.every((p) => evaluatePredicate(p, ctx));
|
|
85
|
+
}
|
|
86
|
+
/** Estado actual de una máquina para el registro (o su fallback / un estado desconocido explícito). */
|
|
87
|
+
export function resolveStatus(machine, ctx) {
|
|
88
|
+
const raw = readPath(ctx, machine.field);
|
|
89
|
+
const value = raw == null ? '' : String(raw);
|
|
90
|
+
const found = machine.states.find((s) => s.value === value);
|
|
91
|
+
if (found)
|
|
92
|
+
return found;
|
|
93
|
+
if (machine.fallback)
|
|
94
|
+
return machine.fallback;
|
|
95
|
+
if (!value)
|
|
96
|
+
return undefined;
|
|
97
|
+
return { value, label: value, tone: 'neutral' };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Distribución de acciones para el estado actual. Una acción `hiddenWhen` se
|
|
101
|
+
* omite; una `blockedWhen` queda deshabilitada con motivo. Más de
|
|
102
|
+
* `MAX_VISIBLE_SECONDARY` secundarias se derraman a «Más…»; una acción marcada
|
|
103
|
+
* `destructive` siempre va a la zona destructiva aunque el layout la ponga en otro lado.
|
|
104
|
+
*/
|
|
105
|
+
export function resolveActions(spec, ctx) {
|
|
106
|
+
const byKey = new Map(spec.actions.map((a) => [a.key, a]));
|
|
107
|
+
const layout = spec.layouts.find((r) => evaluateWhen(r.when, ctx))?.layout ?? spec.defaultLayout ?? {};
|
|
108
|
+
const resolve = (key) => {
|
|
109
|
+
if (!key)
|
|
110
|
+
return undefined;
|
|
111
|
+
const def = byKey.get(key);
|
|
112
|
+
if (!def || (def.hiddenWhen && evaluateWhen(def.hiddenWhen, ctx)))
|
|
113
|
+
return undefined;
|
|
114
|
+
const rules = def.blockedWhen ? (Array.isArray(def.blockedWhen) ? def.blockedWhen : [def.blockedWhen]) : [];
|
|
115
|
+
const hit = rules.find((r) => evaluateWhen(r.when, ctx));
|
|
116
|
+
return { def, blockedReason: hit?.reason };
|
|
117
|
+
};
|
|
118
|
+
const many = (keys) => (keys ?? []).map(resolve).filter((a) => !!a);
|
|
119
|
+
const destructive = many(layout.destructive);
|
|
120
|
+
const secondary = [];
|
|
121
|
+
const more = [];
|
|
122
|
+
for (const a of [...many(layout.secondary)]) {
|
|
123
|
+
if (a.def.destructive)
|
|
124
|
+
destructive.push(a);
|
|
125
|
+
else
|
|
126
|
+
secondary.push(a);
|
|
127
|
+
}
|
|
128
|
+
for (const a of many(layout.more)) {
|
|
129
|
+
if (a.def.destructive)
|
|
130
|
+
destructive.push(a);
|
|
131
|
+
else
|
|
132
|
+
more.push(a);
|
|
133
|
+
}
|
|
134
|
+
const overflow = secondary.splice(MAX_VISIBLE_SECONDARY);
|
|
135
|
+
more.unshift(...overflow);
|
|
136
|
+
let primary = resolve(layout.primary);
|
|
137
|
+
if (primary?.def.destructive) {
|
|
138
|
+
destructive.unshift(primary);
|
|
139
|
+
primary = undefined;
|
|
140
|
+
}
|
|
141
|
+
return { primary, secondary, more, destructive };
|
|
142
|
+
}
|
|
143
|
+
/** Interpola `{{campo}}` con valores del registro (sin evaluar código). */
|
|
144
|
+
export function interpolate(template, ctx) {
|
|
145
|
+
return template.replace(/\{\{\s*([\w.$]+)\s*\}\}/g, (_, path) => {
|
|
146
|
+
const v = readPath(ctx, path);
|
|
147
|
+
return v == null ? '' : String(v);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/** Título: la interpolación puede dejar vacío el resultado; se prueban alternativas separadas por `||`. */
|
|
151
|
+
export function resolveTitle(template, ctx) {
|
|
152
|
+
for (const alt of template.split('||')) {
|
|
153
|
+
const out = interpolate(alt.trim(), ctx).trim();
|
|
154
|
+
if (out)
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
return '';
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Quita del spec las acciones que dependen de una acción del modelo que el
|
|
161
|
+
* backend (o el addon que la aporta) no declara: mejor un botón menos que uno
|
|
162
|
+
* que abre un modal vacío. Las que resuelve la propia página (`href`/`openUrl`/`tab`) siempre se conservan.
|
|
163
|
+
*/
|
|
164
|
+
export function pruneUnavailableActions(spec, availableModelActions) {
|
|
165
|
+
const have = new Set(availableModelActions);
|
|
166
|
+
return {
|
|
167
|
+
...spec,
|
|
168
|
+
actions: spec.actions.filter((a) => a.href || a.openUrl || a.tab || have.has(a.modelAction ?? a.key)),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DocumentFieldDef } from './types';
|
|
2
|
+
export interface FieldFormatOptions {
|
|
3
|
+
locale?: string;
|
|
4
|
+
currency?: string;
|
|
5
|
+
timeZone?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Formatea un valor de campo; vacío → «—». Moneda y zona horaria son config de la org, no se hardcodean. */
|
|
8
|
+
export declare function formatFieldValue(value: unknown, format?: DocumentFieldDef['format'], opts?: FieldFormatOptions): string;
|
|
9
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/document/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,MAAM,WAAW,kBAAkB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,6GAA6G;AAC7G,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,OAAO,EACd,MAAM,GAAE,gBAAgB,CAAC,QAAQ,CAAU,EAC3C,IAAI,GAAE,kBAAuB,GAC9B,MAAM,CA6CR"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** Formatea un valor de campo; vacío → «—». Moneda y zona horaria son config de la org, no se hardcodean. */
|
|
2
|
+
export function formatFieldValue(value, format = 'text', opts = {}) {
|
|
3
|
+
if (value === null || value === undefined || value === '')
|
|
4
|
+
return '—';
|
|
5
|
+
const locale = opts.locale || 'es-MX';
|
|
6
|
+
// Relación resuelta por el kernel: `{value,label}` o un objeto con `name`.
|
|
7
|
+
if (typeof value === 'object' && !(value instanceof Date)) {
|
|
8
|
+
const o = value;
|
|
9
|
+
const text = o.label ?? o.name ?? o.title;
|
|
10
|
+
return text == null || text === '' ? '—' : String(text);
|
|
11
|
+
}
|
|
12
|
+
switch (format) {
|
|
13
|
+
case 'money': {
|
|
14
|
+
const n = typeof value === 'number' ? value : parseFloat(String(value));
|
|
15
|
+
if (!Number.isFinite(n))
|
|
16
|
+
return '—';
|
|
17
|
+
try {
|
|
18
|
+
return new Intl.NumberFormat(locale, {
|
|
19
|
+
style: 'currency',
|
|
20
|
+
currency: opts.currency || 'USD',
|
|
21
|
+
}).format(n);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return n.toFixed(2);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
case 'percent': {
|
|
28
|
+
const n = typeof value === 'number' ? value : parseFloat(String(value));
|
|
29
|
+
return Number.isFinite(n) ? `${n}%` : '—';
|
|
30
|
+
}
|
|
31
|
+
case 'date':
|
|
32
|
+
case 'datetime': {
|
|
33
|
+
const d = new Date(String(value));
|
|
34
|
+
if (Number.isNaN(d.getTime()))
|
|
35
|
+
return String(value);
|
|
36
|
+
try {
|
|
37
|
+
return new Intl.DateTimeFormat(locale, {
|
|
38
|
+
dateStyle: 'medium',
|
|
39
|
+
...(format === 'datetime' ? { timeStyle: 'short' } : {}),
|
|
40
|
+
timeZone: opts.timeZone || undefined,
|
|
41
|
+
}).format(d);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return d.toISOString();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
case 'boolean':
|
|
48
|
+
return value ? 'Sí' : 'No';
|
|
49
|
+
default:
|
|
50
|
+
return String(value);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './types';
|
|
2
|
+
export { MAX_VISIBLE_SECONDARY, evaluatePredicate, evaluateWhen, interpolate, pruneUnavailableActions, readPath, resolveActions, resolveStatus, resolveTitle, } from './evaluate';
|
|
3
|
+
export { formatFieldValue, type FieldFormatOptions } from './format';
|
|
4
|
+
export { StatusBadge, STATUS_TONE_STYLE, type StatusBadgeProps } from './status-badge';
|
|
5
|
+
export { ActionBar, type ActionBarProps } from './action-bar';
|
|
6
|
+
export { DocumentHeader, type DocumentHeaderProps, type HeaderBadge, type HeaderMetric } from './document-header';
|
|
7
|
+
export { SmartButtons, type SmartButtonItem, type SmartButtonsProps } from './smart-buttons';
|
|
8
|
+
export { DocumentTimeline, type DocumentTimelineProps, type TimelineEntry } from './document-timeline';
|
|
9
|
+
export { CancelWithReason, type CancelPayload, type CancelReason, type CancelWithReasonProps } from './cancel-with-reason';
|
|
10
|
+
export { DocumentPage, type DocumentPageProps } from './document-page';
|
|
11
|
+
export { useDocumentSources, type DocumentSourcesState } from './use-document-sources';
|
|
12
|
+
export { clearDocumentPages, findStatusMachine, getDocumentPage, registerDocumentPage, type DocumentPageRegistration, } from './registry';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/document/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAA;AACvB,OAAO,EACH,qBAAqB,EACrB,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,uBAAuB,EACvB,QAAQ,EACR,cAAc,EACd,aAAa,EACb,YAAY,GACf,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,gBAAgB,EAAE,KAAK,kBAAkB,EAAE,MAAM,UAAU,CAAA;AACpE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtF,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACjH,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAC5F,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACtG,OAAO,EAAE,gBAAgB,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC1H,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,EAAE,kBAAkB,EAAE,KAAK,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AACtF,OAAO,EACH,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,oBAAoB,EACpB,KAAK,wBAAwB,GAChC,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './types';
|
|
2
|
+
export { MAX_VISIBLE_SECONDARY, evaluatePredicate, evaluateWhen, interpolate, pruneUnavailableActions, readPath, resolveActions, resolveStatus, resolveTitle, } from './evaluate';
|
|
3
|
+
export { formatFieldValue } from './format';
|
|
4
|
+
export { StatusBadge, STATUS_TONE_STYLE } from './status-badge';
|
|
5
|
+
export { ActionBar } from './action-bar';
|
|
6
|
+
export { DocumentHeader } from './document-header';
|
|
7
|
+
export { SmartButtons } from './smart-buttons';
|
|
8
|
+
export { DocumentTimeline } from './document-timeline';
|
|
9
|
+
export { CancelWithReason } from './cancel-with-reason';
|
|
10
|
+
export { DocumentPage } from './document-page';
|
|
11
|
+
export { useDocumentSources } from './use-document-sources';
|
|
12
|
+
export { clearDocumentPages, findStatusMachine, getDocumentPage, registerDocumentPage, } from './registry';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DocumentContext, DocumentSpec, StatusMachine } from './types';
|
|
2
|
+
export interface DocumentPageRegistration {
|
|
3
|
+
spec: DocumentSpec;
|
|
4
|
+
/**
|
|
5
|
+
* Calcula derivados (`$.x`) a partir del registro y las fuentes cargadas —
|
|
6
|
+
* p. ej. el estado fiscal a partir de fiscal_documents y el de cobro a
|
|
7
|
+
* partir de saldo y vencimiento. Función pura, sin efectos.
|
|
8
|
+
*/
|
|
9
|
+
derive?: (ctx: Omit<DocumentContext, 'derived'>) => Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Registra la página bajo `spec.model` y, opcionalmente, bajo alias (la ruta
|
|
13
|
+
* `/m/<model>` puede llegar como `invoices` o `Invoice` según el enlace).
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerDocumentPage(reg: DocumentPageRegistration, aliases?: string[]): void;
|
|
16
|
+
export declare function getDocumentPage(model: string): DocumentPageRegistration | undefined;
|
|
17
|
+
export declare function clearDocumentPages(): void;
|
|
18
|
+
/** Catálogo compartido de estados por id de máquina (lo usan las columnas `status` de las pestañas). */
|
|
19
|
+
export declare function findStatusMachine(spec: DocumentSpec, id: string): StatusMachine | undefined;
|
|
20
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/document/registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE3E,MAAM,WAAW,wBAAwB;IACrC,IAAI,EAAE,YAAY,CAAA;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,SAAS,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC9E;AAID;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,wBAAwB,EAAE,OAAO,GAAE,MAAM,EAAO,GAAG,IAAI,CAGhG;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CAEnF;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED,wGAAwG;AACxG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAE3F"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const registry = new Map();
|
|
2
|
+
/**
|
|
3
|
+
* Registra la página bajo `spec.model` y, opcionalmente, bajo alias (la ruta
|
|
4
|
+
* `/m/<model>` puede llegar como `invoices` o `Invoice` según el enlace).
|
|
5
|
+
*/
|
|
6
|
+
export function registerDocumentPage(reg, aliases = []) {
|
|
7
|
+
registry.set(reg.spec.model, reg);
|
|
8
|
+
for (const a of aliases)
|
|
9
|
+
registry.set(a, reg);
|
|
10
|
+
}
|
|
11
|
+
export function getDocumentPage(model) {
|
|
12
|
+
return registry.get(model);
|
|
13
|
+
}
|
|
14
|
+
export function clearDocumentPages() {
|
|
15
|
+
registry.clear();
|
|
16
|
+
}
|
|
17
|
+
/** Catálogo compartido de estados por id de máquina (lo usan las columnas `status` de las pestañas). */
|
|
18
|
+
export function findStatusMachine(spec, id) {
|
|
19
|
+
return spec.statuses.find((s) => s.id === id) ?? spec.catalogs?.find((s) => s.id === id);
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface SmartButtonItem {
|
|
2
|
+
key: string;
|
|
3
|
+
label: string;
|
|
4
|
+
icon?: string;
|
|
5
|
+
/** undefined = botón sin contador (p. ej. «Pedido origen»). */
|
|
6
|
+
count?: number;
|
|
7
|
+
/** Deshabilitado (sin destino / vacío). */
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
onClick?: () => void;
|
|
10
|
+
}
|
|
11
|
+
export interface SmartButtonsProps {
|
|
12
|
+
items: SmartButtonItem[];
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function SmartButtons({ items, className }: SmartButtonsProps): import("react").JSX.Element | null;
|
|
16
|
+
//# sourceMappingURL=smart-buttons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smart-buttons.d.ts","sourceRoot":"","sources":["../../src/document/smart-buttons.tsx"],"names":[],"mappings":"AAOA,MAAM,WAAW,eAAe;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,eAAe,EAAE,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,iBAAiB,sCA8BnE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// SmartButtons — accesos con contador al estilo Odoo (Pagos (2) · REP (1) ·
|
|
3
|
+
// Notas de crédito (0)…). Un botón sin registros se ve atenuado, no oculto,
|
|
4
|
+
// salvo que la definición pida `hideWhenEmpty`.
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { cn } from '@asteby/metacore-ui/lib';
|
|
7
|
+
import { DynamicIcon } from '../dynamic-icon';
|
|
8
|
+
export function SmartButtons({ items, className }) {
|
|
9
|
+
const { t } = useTranslation();
|
|
10
|
+
if (items.length === 0)
|
|
11
|
+
return null;
|
|
12
|
+
return (_jsx("nav", { "aria-label": t('document.related', { defaultValue: 'Relacionados' }), className: cn('flex flex-wrap gap-2', className), "data-smart-buttons": '', children: items.map((it) => {
|
|
13
|
+
const empty = it.count === 0;
|
|
14
|
+
return (_jsxs("button", { type: 'button', disabled: it.disabled, onClick: it.onClick, "data-smart-button": it.key, className: cn('inline-flex items-center gap-2 rounded-md border bg-card px-3 py-1.5 text-sm transition-colors', 'hover:bg-accent disabled:cursor-default disabled:opacity-50 disabled:hover:bg-card', empty && 'text-muted-foreground'), children: [it.icon ? _jsx(DynamicIcon, { name: it.icon, className: 'h-4 w-4' }) : null, _jsx("span", { children: t(it.label, { defaultValue: it.label }) }), it.count !== undefined ? (_jsx("span", { className: 'rounded-full bg-muted px-1.5 text-xs font-semibold tabular-nums', children: it.count })) : null] }, it.key));
|
|
15
|
+
}) }));
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type LucideIcon } from 'lucide-react';
|
|
2
|
+
import type { StatusDef, StatusTone } from './types';
|
|
3
|
+
type BadgeVariant = 'muted' | 'info' | 'success' | 'warning' | 'danger';
|
|
4
|
+
export declare const STATUS_TONE_STYLE: Record<StatusTone, {
|
|
5
|
+
variant: BadgeVariant;
|
|
6
|
+
icon: LucideIcon;
|
|
7
|
+
}>;
|
|
8
|
+
export interface StatusBadgeProps {
|
|
9
|
+
status: Pick<StatusDef, 'label' | 'tone' | 'strike'>;
|
|
10
|
+
/** Descripción para lectores de pantalla («Estado fiscal»). */
|
|
11
|
+
srLabel?: string;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function StatusBadge({ status, srLabel, className }: StatusBadgeProps): import("react").JSX.Element;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=status-badge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-badge.d.ts","sourceRoot":"","sources":["../../src/document/status-badge.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAkE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAG9G,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAEpD,KAAK,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEvE,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,UAAU,EAAE;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAO7F,CAAA;AAED,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAA;IACpD,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,gBAAgB,+BAgB3E"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// StatusBadge — badge semántico con texto explícito e ícono por tono: el color
|
|
3
|
+
// nunca es la única señal (benchmark §6.3). El catálogo de estados de cada
|
|
4
|
+
// documento vive en su `StatusMachine`; aquí solo se pinta.
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { AlertOctagon, AlertTriangle, CheckCircle2, Circle, Clock, Info } from 'lucide-react';
|
|
7
|
+
import { Badge } from '@asteby/metacore-ui/primitives';
|
|
8
|
+
import { cn } from '@asteby/metacore-ui/lib';
|
|
9
|
+
export const STATUS_TONE_STYLE = {
|
|
10
|
+
neutral: { variant: 'muted', icon: Circle },
|
|
11
|
+
info: { variant: 'info', icon: Info },
|
|
12
|
+
success: { variant: 'success', icon: CheckCircle2 },
|
|
13
|
+
caution: { variant: 'warning', icon: Clock },
|
|
14
|
+
warning: { variant: 'warning', icon: AlertTriangle },
|
|
15
|
+
critical: { variant: 'danger', icon: AlertOctagon },
|
|
16
|
+
};
|
|
17
|
+
export function StatusBadge({ status, srLabel, className }) {
|
|
18
|
+
const { t } = useTranslation();
|
|
19
|
+
const style = STATUS_TONE_STYLE[status.tone] ?? STATUS_TONE_STYLE.neutral;
|
|
20
|
+
const Icon = style.icon;
|
|
21
|
+
const text = t(status.label, { defaultValue: status.label });
|
|
22
|
+
return (_jsxs(Badge, { variant: style.variant, "data-tone": status.tone, className: cn(status.strike && 'line-through', className), children: [_jsx(Icon, { "aria-hidden": true }), srLabel ? _jsxs("span", { className: 'sr-only', children: [srLabel, ": "] }) : null, text] }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/** Tono semántico de un estado. El color nunca es la única señal: cada tono trae ícono y texto. */
|
|
3
|
+
export type StatusTone = 'neutral' | 'info' | 'success' | 'caution' | 'warning' | 'critical';
|
|
4
|
+
/** Un estado del catálogo de un documento. */
|
|
5
|
+
export interface StatusDef {
|
|
6
|
+
value: string;
|
|
7
|
+
/** Texto explícito (o clave i18n; se traduce con `t`). */
|
|
8
|
+
label: string;
|
|
9
|
+
tone: StatusTone;
|
|
10
|
+
/** Texto tachado (Cancelada, Anulada): conserva el rastro sin alarmar. */
|
|
11
|
+
strike?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Una máquina de estados con su propio badge (p. ej. fiscal y cobro, independientes). */
|
|
14
|
+
export interface StatusMachine {
|
|
15
|
+
id: string;
|
|
16
|
+
/** Etiqueta accesible del badge («Estado fiscal»). */
|
|
17
|
+
label?: string;
|
|
18
|
+
/** Ruta del valor en el contexto de evaluación (`record.x`, `$.x`). */
|
|
19
|
+
field: string;
|
|
20
|
+
states: StatusDef[];
|
|
21
|
+
/** Estado a mostrar cuando el valor no está en el catálogo. */
|
|
22
|
+
fallback?: StatusDef;
|
|
23
|
+
}
|
|
24
|
+
export type PredicateOp = 'eq' | 'neq' | 'in' | 'not_in' | 'truthy' | 'falsy' | 'gt' | 'gte' | 'lt' | 'lte';
|
|
25
|
+
/** Condición sobre una ruta del contexto. Un arreglo de predicados es un AND. */
|
|
26
|
+
export interface Predicate {
|
|
27
|
+
field: string;
|
|
28
|
+
op: PredicateOp;
|
|
29
|
+
value?: unknown;
|
|
30
|
+
}
|
|
31
|
+
export type When = Predicate | Predicate[];
|
|
32
|
+
/** Regla de bloqueo: si `when` se cumple, la acción se ve deshabilitada y `reason` explica qué hacer antes. */
|
|
33
|
+
export interface BlockRule {
|
|
34
|
+
when: When;
|
|
35
|
+
reason: string;
|
|
36
|
+
}
|
|
37
|
+
/** Un botón del documento (acción). `key` enlaza con `ActionDefinition.key` del modelo si existe. */
|
|
38
|
+
export interface DocumentActionDef {
|
|
39
|
+
key: string;
|
|
40
|
+
label: string;
|
|
41
|
+
/** Nombre de ícono lucide. */
|
|
42
|
+
icon?: string;
|
|
43
|
+
/** Acción del modelo (metadata) que se dispara; por defecto `key`. */
|
|
44
|
+
modelAction?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Acciones que resuelve la propia página sin acción del modelo (las plantillas
|
|
47
|
+
* interpolan `{{campo}}` / `{{$.derivado}}`):
|
|
48
|
+
* - `href`: navega dentro de la app (`onNavigate` del host).
|
|
49
|
+
* - `openUrl`: abre una URL en otra pestaña (descargar PDF/XML).
|
|
50
|
+
* - `tab`: selecciona una pestaña (ver detalle del error, ver pagos).
|
|
51
|
+
*/
|
|
52
|
+
href?: string;
|
|
53
|
+
openUrl?: string;
|
|
54
|
+
tab?: string;
|
|
55
|
+
/** Estilo crítico: siempre va en la zona destructiva. */
|
|
56
|
+
destructive?: boolean;
|
|
57
|
+
/** Se muestra deshabilitada con el motivo en lugar de desaparecer. */
|
|
58
|
+
blockedWhen?: BlockRule | BlockRule[];
|
|
59
|
+
/** Se oculta por completo cuando se cumple (p. ej. sin permiso o sin conector). */
|
|
60
|
+
hiddenWhen?: When;
|
|
61
|
+
}
|
|
62
|
+
/** Distribución de acciones para un estado (§5.4): 1 primaria, 2–3 secundarias, resto en «Más…». */
|
|
63
|
+
export interface ActionLayout {
|
|
64
|
+
primary?: string;
|
|
65
|
+
secondary?: string[];
|
|
66
|
+
more?: string[];
|
|
67
|
+
destructive?: string[];
|
|
68
|
+
}
|
|
69
|
+
/** Regla: si `when` se cumple, se usa `layout`. Gana la primera que coincide. */
|
|
70
|
+
export interface ActionLayoutRule {
|
|
71
|
+
when: When;
|
|
72
|
+
layout: ActionLayout;
|
|
73
|
+
}
|
|
74
|
+
/** Botón inteligente con contador (estilo Odoo): navega a lo relacionado. */
|
|
75
|
+
export interface SmartButtonDef {
|
|
76
|
+
key: string;
|
|
77
|
+
label: string;
|
|
78
|
+
icon?: string;
|
|
79
|
+
/** Fuente (`DocumentSourceDef.key`) cuyo total alimenta el contador; sin ella no hay contador. */
|
|
80
|
+
source?: string;
|
|
81
|
+
/** Ruta de un valor del registro cuyo truthy activa el botón sin fuente (p. ej. «Pedido origen»). */
|
|
82
|
+
field?: string;
|
|
83
|
+
/** Modelo destino para navegar a la lista filtrada. */
|
|
84
|
+
href?: string;
|
|
85
|
+
/** No se muestra cuando el contador es 0 (por defecto se muestra deshabilitado). */
|
|
86
|
+
hideWhenEmpty?: boolean;
|
|
87
|
+
/** Selecciona esta pestaña al hacer clic. */
|
|
88
|
+
tab?: string;
|
|
89
|
+
}
|
|
90
|
+
/** Consulta a un modelo relacionado con el registro (`{{id}}` se interpola). */
|
|
91
|
+
export interface DocumentSourceDef {
|
|
92
|
+
key: string;
|
|
93
|
+
model: string;
|
|
94
|
+
/** Columna del modelo relacionado que apunta al registro. */
|
|
95
|
+
foreignKey: string;
|
|
96
|
+
/** Filtros estáticos extra (`f_<col>=eq:<val>`). */
|
|
97
|
+
where?: Record<string, string>;
|
|
98
|
+
limit?: number;
|
|
99
|
+
/**
|
|
100
|
+
* Relación de dos saltos: en vez de filtrar por el id del registro, filtra
|
|
101
|
+
* `foreignKey` con los valores de `field` de las filas de otra fuente ya
|
|
102
|
+
* cargada (`via.source`). Ej.: los REP de una factura cuelgan del pago
|
|
103
|
+
* (`source_id = payment_id`), y los pagos salen de las aplicaciones.
|
|
104
|
+
*/
|
|
105
|
+
via?: {
|
|
106
|
+
source: string;
|
|
107
|
+
field: string;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export interface DocumentFieldDef {
|
|
111
|
+
label: string;
|
|
112
|
+
/** Ruta en el contexto. */
|
|
113
|
+
path: string;
|
|
114
|
+
format?: 'text' | 'money' | 'date' | 'datetime' | 'mono' | 'percent' | 'boolean';
|
|
115
|
+
/** Ancho completo de la rejilla. */
|
|
116
|
+
wide?: boolean;
|
|
117
|
+
/** Oculta el campo cuando el valor está vacío. */
|
|
118
|
+
hideEmpty?: boolean;
|
|
119
|
+
}
|
|
120
|
+
export interface DocumentFieldGroup {
|
|
121
|
+
title?: string;
|
|
122
|
+
fields: DocumentFieldDef[];
|
|
123
|
+
}
|
|
124
|
+
export interface DocumentColumnDef {
|
|
125
|
+
label: string;
|
|
126
|
+
path: string;
|
|
127
|
+
format?: DocumentFieldDef['format'];
|
|
128
|
+
/** Catálogo de estados (id de máquina registrada) para pintar un badge. */
|
|
129
|
+
status?: string;
|
|
130
|
+
}
|
|
131
|
+
export type DocumentTabDef = {
|
|
132
|
+
key: string;
|
|
133
|
+
label: string;
|
|
134
|
+
kind: 'fields';
|
|
135
|
+
groups: DocumentFieldGroup[];
|
|
136
|
+
} | {
|
|
137
|
+
key: string;
|
|
138
|
+
label: string;
|
|
139
|
+
kind: 'records';
|
|
140
|
+
source: string;
|
|
141
|
+
columns: DocumentColumnDef[];
|
|
142
|
+
empty?: string;
|
|
143
|
+
} | {
|
|
144
|
+
key: string;
|
|
145
|
+
label: string;
|
|
146
|
+
kind: 'slot';
|
|
147
|
+
slot: string;
|
|
148
|
+
count?: string;
|
|
149
|
+
};
|
|
150
|
+
/** Métrica del encabezado (total, saldo, fechas…). */
|
|
151
|
+
export interface DocumentMetricDef extends DocumentFieldDef {
|
|
152
|
+
tone?: 'default' | 'critical';
|
|
153
|
+
}
|
|
154
|
+
export interface DocumentSpec {
|
|
155
|
+
/** Clave del modelo (`invoices`). */
|
|
156
|
+
model: string;
|
|
157
|
+
/** Rutas candidatas para el título, en orden; `{{a}}{{b}}` se interpola. */
|
|
158
|
+
title: string;
|
|
159
|
+
/** Texto bajo el título (cliente…). */
|
|
160
|
+
subtitle?: DocumentFieldDef;
|
|
161
|
+
/** Campo que se copia con botón (UUID). */
|
|
162
|
+
copyable?: DocumentFieldDef;
|
|
163
|
+
/** Badges independientes (fiscal, cobro…). */
|
|
164
|
+
statuses: StatusMachine[];
|
|
165
|
+
/** Catálogos extra que solo pintan celdas de pestañas (`column.status`), sin badge en la cabecera. */
|
|
166
|
+
catalogs?: StatusMachine[];
|
|
167
|
+
metrics?: DocumentMetricDef[];
|
|
168
|
+
actions: DocumentActionDef[];
|
|
169
|
+
layouts: ActionLayoutRule[];
|
|
170
|
+
/** Layout si ninguna regla coincide. */
|
|
171
|
+
defaultLayout?: ActionLayout;
|
|
172
|
+
smartButtons?: SmartButtonDef[];
|
|
173
|
+
sources?: DocumentSourceDef[];
|
|
174
|
+
tabs: DocumentTabDef[];
|
|
175
|
+
}
|
|
176
|
+
/** Contexto de evaluación: registro, fuentes cargadas y derivados. */
|
|
177
|
+
export interface DocumentContext {
|
|
178
|
+
record: Record<string, unknown>;
|
|
179
|
+
/** Filas por fuente (`sources[key]`). */
|
|
180
|
+
sources: Record<string, Record<string, unknown>[]>;
|
|
181
|
+
/** Derivados (`$`). */
|
|
182
|
+
derived: Record<string, unknown>;
|
|
183
|
+
}
|
|
184
|
+
export interface ResolvedAction {
|
|
185
|
+
def: DocumentActionDef;
|
|
186
|
+
/** Motivo cuando la acción está bloqueada. */
|
|
187
|
+
blockedReason?: string;
|
|
188
|
+
}
|
|
189
|
+
export interface ResolvedActionLayout {
|
|
190
|
+
primary?: ResolvedAction;
|
|
191
|
+
secondary: ResolvedAction[];
|
|
192
|
+
more: ResolvedAction[];
|
|
193
|
+
destructive: ResolvedAction[];
|
|
194
|
+
}
|
|
195
|
+
export type DocumentSlotRenderer = (ctx: DocumentContext) => ReactNode;
|
|
196
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/document/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DocumentSourceDef } from './types';
|
|
2
|
+
export interface DocumentSourcesState {
|
|
3
|
+
sources: Record<string, Record<string, unknown>[]>;
|
|
4
|
+
loading: boolean;
|
|
5
|
+
/** Vuelve a pedir todas las fuentes (tras ejecutar una acción). */
|
|
6
|
+
reload: () => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Carga las filas relacionadas con el documento (pagos, REP, notas de crédito…)
|
|
10
|
+
* que alimentan contadores, pestañas y derivados. Un fallo en una fuente deja
|
|
11
|
+
* esa fuente vacía: la página sigue siendo usable.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useDocumentSources(defs: DocumentSourceDef[] | undefined, recordId: string | number | undefined): DocumentSourcesState;
|
|
14
|
+
//# sourceMappingURL=use-document-sources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-document-sources.d.ts","sourceRoot":"","sources":["../../src/document/use-document-sources.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAQhD,MAAM,WAAW,oBAAoB;IACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAA;IAClD,OAAO,EAAE,OAAO,CAAA;IAChB,mEAAmE;IACnE,MAAM,EAAE,MAAM,IAAI,CAAA;CACrB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAC9B,IAAI,EAAE,iBAAiB,EAAE,GAAG,SAAS,EACrC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GACtC,oBAAoB,CAgEtB"}
|