@asteby/metacore-runtime-react 38.0.0 → 38.1.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 +6 -0
- package/dist/assist-interview.d.ts +67 -0
- package/dist/assist-interview.d.ts.map +1 -0
- package/dist/assist-interview.js +214 -0
- package/dist/dialogs/dynamic-record.d.ts.map +1 -1
- package/dist/dialogs/dynamic-record.js +5 -1
- package/dist/dynamic-form.d.ts.map +1 -1
- package/dist/dynamic-form.js +2 -1
- package/dist/form-layout-ui.d.ts.map +1 -1
- package/dist/form-layout-ui.js +1 -1
- package/dist/form-layout.d.ts +18 -0
- package/dist/form-layout.d.ts.map +1 -1
- package/dist/form-layout.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +1 -1
- package/src/assist-interview.tsx +411 -0
- package/src/dialogs/dynamic-record.tsx +30 -7
- package/src/dynamic-form.tsx +12 -1
- package/src/form-layout-ui.tsx +0 -3
- package/src/form-layout.ts +20 -0
- package/src/index.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @asteby/metacore-runtime-react
|
|
2
2
|
|
|
3
|
+
## 38.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 086e2b3: Paso asistido por IA en los wizards declarativos (`form_layout.sections[].assist`, kernel ≥ 0.141.0): `AssistInterview` renderiza dentro del paso una entrevista conversacional guiada por el proveedor del host (`/assist/:provider/sessions`): una pregunta a la vez con efecto de escritura, progreso en vivo (leyendo el sitio, buscando logo, detectando colores, IA…), tarjetas de vista previa (logo en claro/oscuro, paleta) y respuestas rápidas; al terminar rellena los campos `output` del formulario. Disponible en `DynamicRecordDialog` y `DynamicForm`.
|
|
8
|
+
|
|
3
9
|
## 38.0.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { FormAssist } from './form-layout';
|
|
2
|
+
export interface AssistProgressStep {
|
|
3
|
+
label: string;
|
|
4
|
+
state: 'pending' | 'running' | 'done' | 'error';
|
|
5
|
+
detail?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface AssistQuestion {
|
|
8
|
+
key: string;
|
|
9
|
+
prompt: string;
|
|
10
|
+
kind?: 'text' | 'choice' | 'confirm';
|
|
11
|
+
options?: {
|
|
12
|
+
value: string;
|
|
13
|
+
label: string;
|
|
14
|
+
}[];
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
/** Prefilled suggestion the user can accept with Enter. */
|
|
17
|
+
suggestion?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface AssistCard {
|
|
20
|
+
kind: string;
|
|
21
|
+
title?: string;
|
|
22
|
+
logo?: string;
|
|
23
|
+
logo_light?: string;
|
|
24
|
+
logo_dark?: string;
|
|
25
|
+
colors?: Record<string, string>;
|
|
26
|
+
lines?: {
|
|
27
|
+
label: string;
|
|
28
|
+
value: string;
|
|
29
|
+
}[];
|
|
30
|
+
}
|
|
31
|
+
export interface AssistTurn {
|
|
32
|
+
id: string;
|
|
33
|
+
type: 'message' | 'progress' | 'card' | 'question' | 'answer' | 'result';
|
|
34
|
+
text?: string;
|
|
35
|
+
steps?: AssistProgressStep[];
|
|
36
|
+
card?: AssistCard;
|
|
37
|
+
question?: AssistQuestion;
|
|
38
|
+
fields?: Record<string, any>;
|
|
39
|
+
}
|
|
40
|
+
export interface AssistSession {
|
|
41
|
+
id: string;
|
|
42
|
+
working: boolean;
|
|
43
|
+
done: boolean;
|
|
44
|
+
turns: AssistTurn[];
|
|
45
|
+
result?: Record<string, any>;
|
|
46
|
+
error?: string;
|
|
47
|
+
/** Optional: how many beats the provider expects (progress dots). */
|
|
48
|
+
beats?: number;
|
|
49
|
+
}
|
|
50
|
+
export interface AssistInterviewProps {
|
|
51
|
+
assist: FormAssist;
|
|
52
|
+
/** Live form values; the declared `input` fields are sent on start. */
|
|
53
|
+
values: Record<string, any>;
|
|
54
|
+
/** Called with the provider's result fields (limited to `output`). */
|
|
55
|
+
onApply: (fields: Record<string, any>) => void;
|
|
56
|
+
/** Start immediately (the step is the chat itself). Default true. */
|
|
57
|
+
autoStart?: boolean;
|
|
58
|
+
/** Small uppercase label above the headline (e.g. the step title). */
|
|
59
|
+
eyebrow?: string;
|
|
60
|
+
className?: string;
|
|
61
|
+
}
|
|
62
|
+
export declare function AssistInterview({ assist, values, onApply, autoStart, eyebrow, className }: AssistInterviewProps): import("react").JSX.Element;
|
|
63
|
+
/** Preview card: logo on dark and light, colour swatches, key/value lines. */
|
|
64
|
+
export declare function AssistCardView({ card }: {
|
|
65
|
+
card: AssistCard;
|
|
66
|
+
}): import("react").JSX.Element;
|
|
67
|
+
//# sourceMappingURL=assist-interview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assist-interview.d.ts","sourceRoot":"","sources":["../src/assist-interview.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C,MAAM,WAAW,kBAAkB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAA;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,cAAc;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAA;IACpC,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC7C;AAED,MAAM,WAAW,UAAU;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAA;IACxE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAA;IAC5B,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAC/B;AAED,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,oBAAoB;IACjC,MAAM,EAAE,UAAU,CAAA;IAClB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC3B,sEAAsE;IACtE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAA;IAC9C,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;CACrB;AAwBD,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,oBAAoB,+BAyQtH;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,+BAwC5D"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// AssistInterview — the conversational AI step of a declarative wizard
|
|
3
|
+
// (`form_layout.sections[].assist`, kernel v0.141.0).
|
|
4
|
+
//
|
|
5
|
+
// Link-onboarding style: ONE question at a time as a big typed headline,
|
|
6
|
+
// progress beats on top, a single answer input (or quick-reply chips) at the
|
|
7
|
+
// bottom, and the assistant's work (fetching a website, thinking…) shown as
|
|
8
|
+
// live status. No form inputs: the provider asks for whatever it needs, does
|
|
9
|
+
// the work, previews what it found and returns the fields the form takes.
|
|
10
|
+
// The panel is generic — it renders the turns the provider sends and knows
|
|
11
|
+
// nothing about brands. Protocol (host `/assist/:provider/…`):
|
|
12
|
+
//
|
|
13
|
+
// POST /assist/:provider/sessions { input } → Session
|
|
14
|
+
// GET /assist/:provider/sessions/:id → Session (poll while working)
|
|
15
|
+
// POST /assist/:provider/sessions/:id/reply { key, value } → Session
|
|
16
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
17
|
+
import { Button, Input } from '@asteby/metacore-ui/primitives';
|
|
18
|
+
import { ArrowRight, Check, CheckCircle2, Loader2, RotateCcw, Sparkles, XCircle } from 'lucide-react';
|
|
19
|
+
import { useApi } from './api-context';
|
|
20
|
+
const POLL_MS = 800;
|
|
21
|
+
const THINKING_HINTS = ['Un momento…', 'Sigo trabajando…', 'Casi listo…'];
|
|
22
|
+
function unwrap(res) {
|
|
23
|
+
const d = res?.data?.data ?? res?.data ?? res;
|
|
24
|
+
return d;
|
|
25
|
+
}
|
|
26
|
+
function useTypewriter(text) {
|
|
27
|
+
const [n, setN] = useState(0);
|
|
28
|
+
const reduce = typeof window !== 'undefined' && !!window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
setN(reduce ? text.length : 0);
|
|
31
|
+
}, [text, reduce]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (n >= text.length)
|
|
34
|
+
return;
|
|
35
|
+
const t = setTimeout(() => setN(v => Math.min(text.length, v + 3)), 22);
|
|
36
|
+
return () => clearTimeout(t);
|
|
37
|
+
}, [n, text]);
|
|
38
|
+
return { shown: text.slice(0, n), typing: n < text.length };
|
|
39
|
+
}
|
|
40
|
+
export function AssistInterview({ assist, values, onApply, autoStart = true, eyebrow, className }) {
|
|
41
|
+
const api = useApi();
|
|
42
|
+
const [session, setSession] = useState(null);
|
|
43
|
+
const [starting, setStarting] = useState(false);
|
|
44
|
+
const [answer, setAnswer] = useState('');
|
|
45
|
+
const [applied, setApplied] = useState(false);
|
|
46
|
+
const [error, setError] = useState(null);
|
|
47
|
+
const [hintIdx, setHintIdx] = useState(0);
|
|
48
|
+
const inputRef = useRef(null);
|
|
49
|
+
// Answers given so far: if the host loses the session (restart, TTL) we
|
|
50
|
+
// start a new one and replay them, so the user never types twice.
|
|
51
|
+
const answersRef = useRef([]);
|
|
52
|
+
const base = `/assist/${encodeURIComponent(assist.provider)}/sessions`;
|
|
53
|
+
const inputPayload = useMemo(() => {
|
|
54
|
+
const out = {};
|
|
55
|
+
for (const k of assist.input ?? [])
|
|
56
|
+
out[k] = values?.[k];
|
|
57
|
+
return out;
|
|
58
|
+
}, [assist.input, values]);
|
|
59
|
+
const start = useCallback(async () => {
|
|
60
|
+
if (starting)
|
|
61
|
+
return;
|
|
62
|
+
setStarting(true);
|
|
63
|
+
setError(null);
|
|
64
|
+
setApplied(false);
|
|
65
|
+
answersRef.current = [];
|
|
66
|
+
try {
|
|
67
|
+
const res = await api.post(base, { input: inputPayload });
|
|
68
|
+
setSession(unwrap(res));
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
setError(e?.response?.data?.message || e?.message || 'No se pudo iniciar el asistente');
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
setStarting(false);
|
|
75
|
+
}
|
|
76
|
+
}, [api, base, inputPayload, starting]);
|
|
77
|
+
const autoFired = useRef(false);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!autoStart || autoFired.current || session)
|
|
80
|
+
return;
|
|
81
|
+
autoFired.current = true;
|
|
82
|
+
void start();
|
|
83
|
+
}, [autoStart, session, start]);
|
|
84
|
+
// Poll while the provider works.
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (!session || !session.working || session.done)
|
|
87
|
+
return;
|
|
88
|
+
const t = setInterval(async () => {
|
|
89
|
+
try {
|
|
90
|
+
const res = await api.get(`${base}/${session.id}`);
|
|
91
|
+
setSession(unwrap(res));
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
if (isGone(e)) {
|
|
95
|
+
try {
|
|
96
|
+
setSession(await recover());
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
/* next tick retries */
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}, POLL_MS);
|
|
104
|
+
return () => clearInterval(t);
|
|
105
|
+
}, [api, base, session]);
|
|
106
|
+
// Rotate the thinking hint while working.
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (!session?.working)
|
|
109
|
+
return;
|
|
110
|
+
setHintIdx(0);
|
|
111
|
+
const t = setInterval(() => setHintIdx(i => Math.min(i + 1, THINKING_HINTS.length - 1)), 2600);
|
|
112
|
+
return () => clearInterval(t);
|
|
113
|
+
}, [session?.working]);
|
|
114
|
+
// Apply the result once.
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
if (!session?.done || !session.result || applied)
|
|
117
|
+
return;
|
|
118
|
+
const allowed = assist.output?.length ? new Set(assist.output) : null;
|
|
119
|
+
const out = {};
|
|
120
|
+
for (const [k, v] of Object.entries(session.result)) {
|
|
121
|
+
if (allowed && !allowed.has(k))
|
|
122
|
+
continue;
|
|
123
|
+
if (v === undefined || v === null || v === '')
|
|
124
|
+
continue;
|
|
125
|
+
out[k] = v;
|
|
126
|
+
}
|
|
127
|
+
onApply(out);
|
|
128
|
+
setApplied(true);
|
|
129
|
+
}, [session, applied, assist.output, onApply]);
|
|
130
|
+
const isGone = (e) => e?.response?.status === 404 || /sesi[oó]n no encontrada/i.test(String(e?.response?.data?.message || e?.message || ''));
|
|
131
|
+
// Recreate the session and replay every answer (host lost it).
|
|
132
|
+
const recover = useCallback(async () => {
|
|
133
|
+
const res = await api.post(base, { input: inputPayload });
|
|
134
|
+
let s = unwrap(res);
|
|
135
|
+
for (const a of answersRef.current) {
|
|
136
|
+
// wait until the provider is ready for the next answer
|
|
137
|
+
for (let i = 0; i < 120 && s.working && !s.done; i++) {
|
|
138
|
+
await new Promise(r => setTimeout(r, POLL_MS));
|
|
139
|
+
s = unwrap(await api.get(`${base}/${s.id}`));
|
|
140
|
+
}
|
|
141
|
+
if (s.done)
|
|
142
|
+
break;
|
|
143
|
+
s = unwrap(await api.post(`${base}/${s.id}/reply`, { key: a.key, value: a.value }));
|
|
144
|
+
}
|
|
145
|
+
return s;
|
|
146
|
+
}, [api, base, inputPayload]);
|
|
147
|
+
const reply = useCallback(async (key, value) => {
|
|
148
|
+
if (!session)
|
|
149
|
+
return;
|
|
150
|
+
setAnswer('');
|
|
151
|
+
setError(null);
|
|
152
|
+
setSession(s => (s ? { ...s, working: true } : s));
|
|
153
|
+
try {
|
|
154
|
+
const res = await api.post(`${base}/${session.id}/reply`, { key, value });
|
|
155
|
+
answersRef.current = [...answersRef.current, { key, value }];
|
|
156
|
+
setSession(unwrap(res));
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
if (isGone(e)) {
|
|
160
|
+
try {
|
|
161
|
+
answersRef.current = [...answersRef.current, { key, value }];
|
|
162
|
+
setSession(await recover());
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
catch (e2) {
|
|
166
|
+
setError(e2?.response?.data?.message || e2?.message || 'No se pudo retomar la conversación');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
setError(e?.response?.data?.message || e?.message || 'No se pudo enviar la respuesta');
|
|
171
|
+
}
|
|
172
|
+
setSession(s => (s ? { ...s, working: false } : s));
|
|
173
|
+
}
|
|
174
|
+
}, [api, base, session, recover]);
|
|
175
|
+
const turns = session?.turns ?? [];
|
|
176
|
+
const last = turns[turns.length - 1];
|
|
177
|
+
const pendingQuestion = !session?.working && !session?.done && last?.type === 'question' ? last.question : null;
|
|
178
|
+
const lastSpoken = [...turns].reverse().find(t => t.type === 'message' || t.type === 'question');
|
|
179
|
+
const headline = lastSpoken?.type === 'question' ? lastSpoken.question?.prompt ?? '' : lastSpoken?.text ?? '';
|
|
180
|
+
const { shown, typing } = useTypewriter(headline || (starting ? 'Un segundo…' : ''));
|
|
181
|
+
// The latest progress block stays visible as compact chips (done / error
|
|
182
|
+
// included) until the next block replaces it; the card that followed it
|
|
183
|
+
// shows whenever the assistant is waiting on the user or finished.
|
|
184
|
+
const activeProgress = [...turns].reverse().find(t => t.type === 'progress');
|
|
185
|
+
const lastCard = [...turns].reverse().find(t => t.type === 'card')?.card;
|
|
186
|
+
const answered = turns.filter(t => t.type === 'answer').length;
|
|
187
|
+
const beats = Math.max(session?.beats ?? 5, 2);
|
|
188
|
+
const progress = session?.done ? beats : Math.min(answered + 1, beats);
|
|
189
|
+
const running = activeProgress?.steps?.find(s => s.state === 'running');
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
if (pendingQuestion && !typing)
|
|
192
|
+
inputRef.current?.focus();
|
|
193
|
+
}, [pendingQuestion, typing]);
|
|
194
|
+
const submitText = () => {
|
|
195
|
+
if (!pendingQuestion)
|
|
196
|
+
return;
|
|
197
|
+
const v = answer.trim() || pendingQuestion.suggestion || '';
|
|
198
|
+
if (v)
|
|
199
|
+
void reply(pendingQuestion.key, v);
|
|
200
|
+
};
|
|
201
|
+
return (_jsxs("div", { className: 'relative overflow-hidden rounded-2xl border bg-gradient-to-b from-primary/[0.06] via-background to-background px-5 py-6 sm:px-8 sm:py-8 ' + (className ?? ''), children: [_jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsx("p", { className: "text-xs font-semibold uppercase tracking-wider text-primary", children: eyebrow || assist.label || 'Asistente' }), _jsx("div", { className: "flex items-center gap-1.5", "aria-label": "progreso", children: Array.from({ length: beats }).map((_, i) => (_jsx("span", { className: 'h-1.5 rounded-full transition-all duration-500 ' + (i < progress ? 'w-6 bg-primary' : 'w-3 bg-muted-foreground/25') }, i))) })] }), _jsxs("div", { className: "mt-5 flex items-start gap-3 sm:gap-4", children: [_jsx("span", { className: "mt-1 flex size-9 shrink-0 items-center justify-center rounded-xl bg-primary text-primary-foreground shadow-lg shadow-primary/25 sm:size-10", children: _jsx(Sparkles, { className: 'size-4 sm:size-5 ' + (session?.working || starting ? 'animate-pulse' : '') }) }), _jsxs("h3", { className: "min-h-[3.5rem] text-lg font-semibold leading-snug tracking-tight sm:min-h-[4rem] sm:text-2xl", children: [shown, typing && _jsx("span", { className: "ml-0.5 inline-block h-5 w-[2px] animate-pulse bg-primary align-middle" })] })] }), _jsxs("div", { className: "mt-3 min-h-6 pl-12 sm:pl-14", children: [(session?.working || starting) && !running && (_jsxs("span", { className: "inline-flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "size-3.5 animate-spin" }), THINKING_HINTS[hintIdx]] })), activeProgress && (_jsx("ul", { className: "mt-2 flex flex-wrap gap-x-4 gap-y-1", "aria-label": "pasos", children: (activeProgress.steps ?? []).map((s, i) => (_jsxs("li", { className: 'inline-flex items-center gap-1.5 text-xs ' + (s.state === 'pending' ? 'text-muted-foreground/70' : s.state === 'error' ? 'text-destructive' : 'text-foreground/80'), children: [s.state === 'done' ? _jsx(CheckCircle2, { className: "size-3.5 text-emerald-500" }) : s.state === 'running' ? _jsx(Loader2, { className: "size-3.5 animate-spin text-primary" }) : s.state === 'error' ? _jsx(XCircle, { className: "size-3.5" }) : _jsx("span", { className: "size-1.5 rounded-full bg-muted-foreground/40" }), s.label, s.state === 'running' && s.detail && _jsxs("span", { className: "text-muted-foreground", children: ["\u00B7 ", s.detail] })] }, i))) })), lastCard && !session?.working && _jsx(AssistCardView, { card: lastCard }), session?.done && (_jsxs("span", { className: "inline-flex items-center gap-2 text-sm font-medium text-emerald-600 dark:text-emerald-400", children: [_jsx(Check, { className: "size-4" }), applied ? 'Listo. Pulsa Siguiente para revisar lo que armé.' : 'Listo.'] })), (error || session?.error) && _jsx("p", { className: "text-sm text-destructive", children: error || session?.error })] }), pendingQuestion && (_jsxs("div", { className: "mt-5 pl-0 sm:pl-14", children: [pendingQuestion.kind === 'choice' || pendingQuestion.kind === 'confirm' ? (_jsx("div", { className: "flex flex-wrap gap-2", children: (pendingQuestion.options ?? [{ value: 'yes', label: 'Sí' }, { value: 'no', label: 'No' }]).map(o => (_jsx(Button, { type: "button", variant: "outline", className: "h-10 rounded-full px-4", onClick: () => void reply(pendingQuestion.key, o.value), children: o.label }, o.value))) })) : (_jsxs("div", { className: "relative", children: [_jsx(Input, { ref: inputRef, value: answer, onChange: e => setAnswer(e.target.value), onKeyDown: e => {
|
|
202
|
+
if (e.key === 'Enter') {
|
|
203
|
+
e.preventDefault();
|
|
204
|
+
submitText();
|
|
205
|
+
}
|
|
206
|
+
}, placeholder: pendingQuestion.suggestion || pendingQuestion.placeholder || 'Escribe tu respuesta…', className: "h-12 pr-12 text-base", autoFocus: true }), _jsx(Button, { type: "button", size: "icon", className: "absolute right-1.5 top-1.5 size-9", onClick: submitText, disabled: !answer.trim() && !pendingQuestion.suggestion, children: _jsx(ArrowRight, { className: "size-4" }) })] })), pendingQuestion.suggestion && pendingQuestion.kind !== 'choice' && (_jsx("p", { className: "pt-1.5 text-xs text-muted-foreground", children: "Enter acepta la sugerencia." }))] })), !session && !starting && (_jsx("div", { className: "mt-5 pl-0 sm:pl-14", children: _jsxs(Button, { type: "button", onClick: () => void start(), children: [_jsx(Sparkles, { className: "mr-2 size-4" }), assist.label || 'Empezar'] }) })), session?.done && (_jsx("div", { className: "mt-4 pl-0 sm:pl-14", children: _jsxs("button", { type: "button", onClick: () => { setSession(null); setApplied(false); autoFired.current = false; }, className: "inline-flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground", children: [_jsx(RotateCcw, { className: "size-3.5" }), " Empezar de nuevo"] }) }))] }));
|
|
207
|
+
}
|
|
208
|
+
/** Preview card: logo on dark and light, colour swatches, key/value lines. */
|
|
209
|
+
export function AssistCardView({ card }) {
|
|
210
|
+
const colors = Object.entries(card.colors ?? {});
|
|
211
|
+
const logoDark = card.logo_light || card.logo;
|
|
212
|
+
const logoLight = card.logo_dark || card.logo;
|
|
213
|
+
return (_jsxs("div", { className: "mt-3 rounded-xl border bg-background p-3", children: [card.title && _jsx("p", { className: "pb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: card.title }), (card.logo || card.logo_light || card.logo_dark) && (_jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsx("div", { className: "flex h-20 items-center justify-center rounded-lg bg-neutral-950 p-3", children: logoDark && _jsx("img", { src: logoDark, alt: "", className: "max-h-full max-w-full object-contain" }) }), _jsx("div", { className: "flex h-20 items-center justify-center rounded-lg border bg-white p-3", children: logoLight && _jsx("img", { src: logoLight, alt: "", className: "max-h-full max-w-full object-contain" }) })] })), colors.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2 pt-3", children: colors.map(([k, v]) => (_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-full border px-2 py-1 text-xs", children: [_jsx("span", { className: "size-4 rounded-full border", style: { backgroundColor: v } }), _jsx("span", { className: "text-muted-foreground", children: k }), _jsx("span", { className: "font-mono", children: v })] }, k))) })), (card.lines ?? []).length > 0 && (_jsx("dl", { className: "grid gap-1 pt-3 text-sm", children: card.lines.map(l => (_jsxs("div", { className: "grid grid-cols-[6rem_1fr] gap-2", children: [_jsx("dt", { className: "text-muted-foreground", children: l.label }), _jsx("dd", { className: "min-w-0 break-words", children: l.value })] }, l.label))) }))] }));
|
|
214
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AA+C1C,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAEjF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"dynamic-record.d.ts","sourceRoot":"","sources":["../../src/dialogs/dynamic-record.tsx"],"names":[],"mappings":"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"}
|
|
@@ -32,6 +32,7 @@ import { useOptionsResolver } from '../use-options-resolver';
|
|
|
32
32
|
import { getFieldRef, getVisibleWhen, evaluateVisibleWhen } from '../dynamic-form-schema';
|
|
33
33
|
import { groupFieldsBySection } from '../form-layout';
|
|
34
34
|
import { FieldSection, WizardProgress } from '../form-layout-ui';
|
|
35
|
+
import { AssistInterview } from '../assist-interview';
|
|
35
36
|
import { FieldCell } from '../field-grid';
|
|
36
37
|
import { isNilUuid, normalizeNilUuid } from '../nil-uuid';
|
|
37
38
|
import { normalizeRefFieldsForSubmit } from './normalize-submit';
|
|
@@ -718,7 +719,10 @@ export function DynamicRecordDialog({ open, onOpenChange, nestedInlineCreateSelf
|
|
|
718
719
|
setStepIndex(Math.min(clampedStep + 1, groups.length - 1));
|
|
719
720
|
};
|
|
720
721
|
const goBackStep = () => setStepIndex(Math.max(clampedStep - 1, 0));
|
|
721
|
-
return (_jsx(RecordDialogModelContext.Provider, { value: model, children: _jsx(Dialog, { open: open, onOpenChange: onOpenChange, nestedInlineCreateSelf: nestedInlineCreateSelf, children: _jsxs(DialogContent, { className: "sm:max-w-2xl max-h-[90dvh] flex flex-col p-0 gap-0 overflow-hidden", style: { maxHeight: '90dvh' }, children: [_jsxs(DialogHeader, { className: "p-6 pb-4 border-b shrink-0", children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: config.description })] }), _jsx("div", { className: "flex-1 overflow-y-auto p-6", children: loading ? (_jsx(LoadingSkeleton, {})) : modalMeta ? (_jsx(ModelContext.Provider, { value: model, children: _jsx(ImageUrlContext.Provider, { value: getImageUrl, children: _jsx(TimeZoneContext.Provider, { value: timeZone, children: _jsxs(CurrencyContext.Provider, { value: currency, children: [_jsxs("form", { id: formId, onSubmit: handleSubmit, className: "grid gap-y-4", children: [isSteps && (_jsx(WizardProgress, { groups: groups, stepIndex: clampedStep, onStepClick: i => setStepIndex(i) })), (isSteps ? [groups[clampedStep]] : groups).map(group =>
|
|
722
|
+
return (_jsx(RecordDialogModelContext.Provider, { value: model, children: _jsx(Dialog, { open: open, onOpenChange: onOpenChange, nestedInlineCreateSelf: nestedInlineCreateSelf, children: _jsxs(DialogContent, { className: "sm:max-w-2xl max-h-[90dvh] flex flex-col p-0 gap-0 overflow-hidden", style: { maxHeight: '90dvh' }, children: [_jsxs(DialogHeader, { className: "p-6 pb-4 border-b shrink-0", children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: config.description })] }), _jsx("div", { className: "flex-1 overflow-y-auto p-6", children: loading ? (_jsx(LoadingSkeleton, {})) : modalMeta ? (_jsx(ModelContext.Provider, { value: model, children: _jsx(ImageUrlContext.Provider, { value: getImageUrl, children: _jsx(TimeZoneContext.Provider, { value: timeZone, children: _jsxs(CurrencyContext.Provider, { value: currency, children: [_jsxs("form", { id: formId, onSubmit: handleSubmit, className: "grid gap-y-4", children: [isSteps && (_jsx(WizardProgress, { groups: groups, stepIndex: clampedStep, onStepClick: i => setStepIndex(i) })), (isSteps ? [groups[clampedStep]] : groups).map(group =>
|
|
723
|
+
// An assisted step IS the interview: no form inputs, the
|
|
724
|
+
// provider asks what it needs and fills the fields.
|
|
725
|
+
isSteps && isEditable && group.assist ? (_jsx(AssistInterview, { assist: group.assist, values: formValues, eyebrow: group.title, autoStart: group.assist.trigger !== 'button', onApply: fields => setFormValues(prev => ({ ...prev, ...fields })) }, group.key)) : (_jsxs(FieldSection, { group: group, children: [_jsx("div", { className: "grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2", children: renderFields(group.fields) }), isEditable && group.assist && (_jsx(AssistInterview, { assist: group.assist, values: formValues, eyebrow: group.title, autoStart: group.assist.trigger === 'auto', onApply: fields => setFormValues(prev => ({ ...prev, ...fields })) }))] }, group.key))), record?.external_url && (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2", children: _jsx("div", { className: "sm:col-span-2 min-w-0", children: _jsxs("a", { href: record.external_url, target: "_blank", rel: "noreferrer", className: "inline-flex items-center gap-1.5 text-sm text-primary hover:underline mt-1", children: [_jsx(ExternalLink, { className: "h-3.5 w-3.5" }), "Ver en ", record.external_provider ?? 'proveedor externo'] }) }) }))] }), !isCreate && record && relations.length > 0 && (_jsx("div", { className: "mt-6", children: _jsx(DynamicRelations, { record: record, relations: relations, lineSubtable: true, embedOnly: true, canCreate: mode === 'edit', canEdit: mode === 'edit', canDelete: mode === 'edit', onChange: handleChildChange }) }))] }) }) }) })) : null }), _jsxs(DialogFooter, { className: "p-4 border-t shrink-0 sm:justify-between", children: [isView && onOpenFullPage ? (_jsxs(Button, { variant: "ghost", size: "sm", className: "text-muted-foreground", onClick: () => { onOpenChange(false); onOpenFullPage(); }, children: [_jsx(ExternalLink, { className: "mr-1.5 h-3.5 w-3.5" }), "Ver p\u00E1gina completa"] })) : _jsx("span", {}), _jsxs("div", { className: "flex items-center gap-2", children: [isSteps && clampedStep > 0 ? (_jsx(Button, { variant: "outline", onClick: goBackStep, disabled: saving || deleting, children: "Anterior" })) : (_jsx(Button, { variant: "outline", onClick: () => onOpenChange(false), disabled: saving || deleting, children: config.cancelLabel })), isView && onDelete && (_jsxs(Button, { variant: "destructive", onClick: handleDelete, disabled: deleting || loading, children: [deleting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), deleting ? 'Eliminando...' : 'Eliminar'] })), isView && onEdit && (_jsx(Button, { onClick: onEdit, disabled: deleting || loading, children: "Editar" })), isEditable && isSteps && !isLastStep && (_jsx(Button, { type: "button", onClick: goNextStep, disabled: saving || loading, children: "Siguiente" })), isEditable && (!isSteps || isLastStep) && (_jsxs(Button, { type: "submit", form: formId, disabled: saving || loading, children: [saving && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), saving ? config.submittingLabel : config.submitLabel] }))] })] })] }) }) }));
|
|
722
726
|
}
|
|
723
727
|
function LoadingSkeleton() {
|
|
724
728
|
return (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-4", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Skeleton, { className: "h-3.5 w-24" }), _jsx(Skeleton, { className: "h-9 w-full" })] }, i))) }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamic-form.d.ts","sourceRoot":"","sources":["../src/dynamic-form.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"dynamic-form.d.ts","sourceRoot":"","sources":["../src/dynamic-form.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAwB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAA;AAerE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EACH,cAAc,EACd,aAAa,EAOhB,MAAM,uBAAuB,CAAA;AAY9B,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEzF,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,UAAU,CAAA;CAC1B;AAED,wBAAgB,WAAW,CAAC,EACxB,MAAM,EACN,aAAa,EACb,QAAQ,EACR,QAAQ,EACR,WAAuB,EACvB,WAAwB,EACxB,QAAgB,EAChB,UAAU,GACb,EAAE,gBAAgB,+BAwMlB"}
|
package/dist/dynamic-form.js
CHANGED
|
@@ -5,6 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
import { useEffect, useMemo, useState } from 'react';
|
|
6
6
|
import { groupFieldsBySection } from './form-layout';
|
|
7
7
|
import { FieldSection, WizardProgress } from './form-layout-ui';
|
|
8
|
+
import { AssistInterview } from './assist-interview';
|
|
8
9
|
import { Input, Textarea, Label, Switch, Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@asteby/metacore-ui/primitives';
|
|
9
10
|
import { buildZodSchema, resolveWidget, isLineItemsField, evaluateBalance, applyOptionWhen, getDependsOn, getVisibleWhen, evaluateVisibleWhen, } from './dynamic-form-schema';
|
|
10
11
|
import { ScanLine } from 'lucide-react';
|
|
@@ -132,7 +133,7 @@ export function DynamicForm({ fields, initialValues, onSubmit, onCancel, submitL
|
|
|
132
133
|
setStepIndex((i) => Math.min(i + 1, groups.length - 1));
|
|
133
134
|
};
|
|
134
135
|
const goBack = () => setStepIndex((i) => Math.max(i - 1, 0));
|
|
135
|
-
return (_jsxs("form", { onSubmit: handleSubmit, className: "grid gap-4", children: [_jsx(WizardProgress, { groups: groups, stepIndex: stepIndex, onStepClick: i => setStepIndex(i) }), renderGrid(step.fields), _jsxs("div", { className: "flex justify-between gap-2 pt-2", children: [stepIndex > 0 ? (_jsx(Button, { type: "button", variant: "outline", onClick: goBack, disabled: submitting || disabled, children: "Anterior" })) : onCancel ? (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: submitting || disabled, children: cancelLabel })) : (_jsx("span", {})), isLast ? (_jsx(Button, { type: "submit", disabled: submitting || disabled || balanceBlocked, children: submitLabel })) : (_jsx(Button, { type: "button", onClick: goNext, disabled: submitting || disabled, children: "Siguiente" }))] })] }));
|
|
136
|
+
return (_jsxs("form", { onSubmit: handleSubmit, className: "grid gap-4", children: [_jsx(WizardProgress, { groups: groups, stepIndex: stepIndex, onStepClick: i => setStepIndex(i) }), step.assist ? (_jsx(AssistInterview, { assist: step.assist, values: values, eyebrow: step.title, autoStart: step.assist.trigger !== 'button', onApply: fields => setValues(prev => ({ ...prev, ...fields })) })) : (renderGrid(step.fields)), _jsxs("div", { className: "flex justify-between gap-2 pt-2", children: [stepIndex > 0 ? (_jsx(Button, { type: "button", variant: "outline", onClick: goBack, disabled: submitting || disabled, children: "Anterior" })) : onCancel ? (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: submitting || disabled, children: cancelLabel })) : (_jsx("span", {})), isLast ? (_jsx(Button, { type: "submit", disabled: submitting || disabled || balanceBlocked, children: submitLabel })) : (_jsx(Button, { type: "button", onClick: goNext, disabled: submitting || disabled, children: "Siguiente" }))] })] }));
|
|
136
137
|
}
|
|
137
138
|
// ── Sections / flat mode ───────────────────────────────────────────────
|
|
138
139
|
// Layout: scalar header fields flow through a responsive 2-column grid;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-layout-ui.d.ts","sourceRoot":"","sources":["../src/form-layout-ui.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,GACX,EAAE;IACC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC5B,+BA6CA;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC3B,MAAM,EACN,SAAS,EACT,SAAkB,EAClB,WAAW,GACd,EAAE;IACC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gFAAgF;IAChF,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC,+
|
|
1
|
+
{"version":3,"file":"form-layout-ui.d.ts","sourceRoot":"","sources":["../src/form-layout-ui.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,EACzB,KAAK,EACL,QAAQ,GACX,EAAE;IACC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,CAAA;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC5B,+BA6CA;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC3B,MAAM,EACN,SAAS,EACT,SAAkB,EAClB,WAAW,GACd,EAAE;IACC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gFAAgF;IAChF,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC,+BAYA"}
|
package/dist/form-layout-ui.js
CHANGED
|
@@ -37,5 +37,5 @@ export function FieldSection({ group, children, }) {
|
|
|
37
37
|
export function WizardProgress({ groups, stepIndex, stepLabel = 'Paso', onStepClick, }) {
|
|
38
38
|
const current = groups[stepIndex];
|
|
39
39
|
const steps = groups.map((g, i) => ({ key: g.key, label: g.title ?? `${stepLabel} ${i + 1}` }));
|
|
40
|
-
return (_jsxs("div", { className: "pt-1", children: [_jsx(ProcessStepper, { steps: steps, activeIndex: stepIndex, onStepClick: onStepClick }), _jsxs("p", { className: "pt-3 text-sm text-muted-foreground", children: [stepLabel, " ", stepIndex + 1, "/", groups.length, current?.title ? ` · ${current.title}` : ''] })
|
|
40
|
+
return (_jsxs("div", { className: "pt-1", children: [_jsx(ProcessStepper, { steps: steps, activeIndex: stepIndex, onStepClick: onStepClick }), _jsxs("p", { className: "pt-3 text-sm text-muted-foreground", children: [stepLabel, " ", stepIndex + 1, "/", groups.length, current?.title ? ` · ${current.title}` : ''] })] }));
|
|
41
41
|
}
|
package/dist/form-layout.d.ts
CHANGED
|
@@ -13,6 +13,22 @@ export interface FormSection {
|
|
|
13
13
|
* Tolerates the camelCase alias, same as fields. */
|
|
14
14
|
visible_when?: VisibleWhen;
|
|
15
15
|
visibleWhen?: VisibleWhen;
|
|
16
|
+
/** AI-assisted step (kernel v0.141.0): the SDK renders an interview panel
|
|
17
|
+
* driven by the host's assist provider inside this section. */
|
|
18
|
+
assist?: FormAssist;
|
|
19
|
+
}
|
|
20
|
+
/** Declaration of an AI-assisted step (`form_layout.sections[].assist`). */
|
|
21
|
+
export interface FormAssist {
|
|
22
|
+
/** Host-registered provider key, e.g. `brand.website_dna`. */
|
|
23
|
+
provider: string;
|
|
24
|
+
label?: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
/** Form fields sent to the provider when the session starts. */
|
|
27
|
+
input?: string[];
|
|
28
|
+
/** Form fields the provider may fill when it finishes. */
|
|
29
|
+
output?: string[];
|
|
30
|
+
/** `button` (default) or `auto` (start as soon as every input has a value). */
|
|
31
|
+
trigger?: 'button' | 'auto';
|
|
16
32
|
}
|
|
17
33
|
/** Model-level layout directive. `mode` defaults to `"sections"`. */
|
|
18
34
|
export interface FormLayout {
|
|
@@ -30,6 +46,8 @@ export interface FieldGroup<F> {
|
|
|
30
46
|
/** True for the synthetic group holding section-less / unknown-section fields. */
|
|
31
47
|
isDefault: boolean;
|
|
32
48
|
fields: F[];
|
|
49
|
+
/** AI-assisted step declaration, when the section has one. */
|
|
50
|
+
assist?: FormAssist;
|
|
33
51
|
}
|
|
34
52
|
/** The synthetic key of the orphan group (fields with no / unknown `section`). */
|
|
35
53
|
export declare const DEFAULT_SECTION_KEY = "__default__";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-layout.d.ts","sourceRoot":"","sources":["../src/form-layout.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAG1C;0DAC0D;AAC1D,MAAM,WAAW,WAAW;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;wDAGoD;IACpD,YAAY,CAAC,EAAE,WAAW,CAAA;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"form-layout.d.ts","sourceRoot":"","sources":["../src/form-layout.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAG1C;0DAC0D;AAC1D,MAAM,WAAW,WAAW;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;wDAGoD;IACpD,YAAY,CAAC,EAAE,WAAW,CAAA;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB;mEAC+D;IAC/D,MAAM,CAAC,EAAE,UAAU,CAAA;CACtB;AAED,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IACvB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;CAC9B;AAED,qEAAqE;AACrE,MAAM,WAAW,UAAU;IACvB,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAA;IAC3B,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAA;CAC3B;AAED;oDACoD;AACpD,MAAM,WAAW,UAAU,CAAC,CAAC;IACzB,0DAA0D;IAC1D,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,kFAAkF;IAClF,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,CAAC,EAAE,CAAA;IACX,8DAA8D;IAC9D,MAAM,CAAC,EAAE,UAAU,CAAA;CACtB;AAED,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,gBAAgB,CAAA;AAEhD,yEAAyE;AACzE,wBAAgB,eAAe,CAC3B,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GACjE,MAAM,GAAG,SAAS,CAIpB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAC/D,MAAM,EAAE,CAAC,EAAE,EACX,MAAM,EAAE,UAAU,GAAG,SAAS,EAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GACpC,UAAU,CAAC,CAAC,CAAC,EAAE,CAqDjB"}
|
package/dist/form-layout.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -86,4 +86,6 @@ export { StatWidget, BarWidget, LineWidget, AreaWidget, PieWidget, DonutWidget,
|
|
|
86
86
|
export { WidgetRenderer, WidgetSkeleton, SIZE_SPAN, SIZE_CLASS, type WidgetRendererProps, } from './widgets/widget-renderer';
|
|
87
87
|
export { WidgetCard, DeltaChip, WidgetEmpty, WidgetError, type WidgetCardProps, } from './widgets/widget-card';
|
|
88
88
|
export { formatWidgetValue, formatAxisTick, formatDelta, accentClasses, paletteColor, CHART_PALETTE, type AccentClasses, type WidgetFormatCtx, } from './widgets/widget-format';
|
|
89
|
+
export { AssistInterview, AssistCardView } from './assist-interview';
|
|
90
|
+
export type { AssistSession, AssistTurn, AssistQuestion, AssistCard, AssistProgressStep } from './assist-interview';
|
|
89
91
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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
|
@@ -85,3 +85,4 @@ export { StatWidget, BarWidget, LineWidget, AreaWidget, PieWidget, DonutWidget,
|
|
|
85
85
|
export { WidgetRenderer, WidgetSkeleton, SIZE_SPAN, SIZE_CLASS, } from './widgets/widget-renderer';
|
|
86
86
|
export { WidgetCard, DeltaChip, WidgetEmpty, WidgetError, } from './widgets/widget-card';
|
|
87
87
|
export { formatWidgetValue, formatAxisTick, formatDelta, accentClasses, paletteColor, CHART_PALETTE, } from './widgets/widget-format';
|
|
88
|
+
export { AssistInterview, AssistCardView } from './assist-interview';
|
package/package.json
CHANGED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
// AssistInterview — the conversational AI step of a declarative wizard
|
|
2
|
+
// (`form_layout.sections[].assist`, kernel v0.141.0).
|
|
3
|
+
//
|
|
4
|
+
// Link-onboarding style: ONE question at a time as a big typed headline,
|
|
5
|
+
// progress beats on top, a single answer input (or quick-reply chips) at the
|
|
6
|
+
// bottom, and the assistant's work (fetching a website, thinking…) shown as
|
|
7
|
+
// live status. No form inputs: the provider asks for whatever it needs, does
|
|
8
|
+
// the work, previews what it found and returns the fields the form takes.
|
|
9
|
+
// The panel is generic — it renders the turns the provider sends and knows
|
|
10
|
+
// nothing about brands. Protocol (host `/assist/:provider/…`):
|
|
11
|
+
//
|
|
12
|
+
// POST /assist/:provider/sessions { input } → Session
|
|
13
|
+
// GET /assist/:provider/sessions/:id → Session (poll while working)
|
|
14
|
+
// POST /assist/:provider/sessions/:id/reply { key, value } → Session
|
|
15
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
16
|
+
import { Button, Input } from '@asteby/metacore-ui/primitives'
|
|
17
|
+
import { ArrowRight, Check, CheckCircle2, Loader2, RotateCcw, Sparkles, XCircle } from 'lucide-react'
|
|
18
|
+
import { useApi } from './api-context'
|
|
19
|
+
import type { FormAssist } from './form-layout'
|
|
20
|
+
|
|
21
|
+
export interface AssistProgressStep {
|
|
22
|
+
label: string
|
|
23
|
+
state: 'pending' | 'running' | 'done' | 'error'
|
|
24
|
+
detail?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AssistQuestion {
|
|
28
|
+
key: string
|
|
29
|
+
prompt: string
|
|
30
|
+
kind?: 'text' | 'choice' | 'confirm'
|
|
31
|
+
options?: { value: string; label: string }[]
|
|
32
|
+
placeholder?: string
|
|
33
|
+
/** Prefilled suggestion the user can accept with Enter. */
|
|
34
|
+
suggestion?: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface AssistCard {
|
|
38
|
+
kind: string
|
|
39
|
+
title?: string
|
|
40
|
+
logo?: string
|
|
41
|
+
logo_light?: string
|
|
42
|
+
logo_dark?: string
|
|
43
|
+
colors?: Record<string, string>
|
|
44
|
+
lines?: { label: string; value: string }[]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface AssistTurn {
|
|
48
|
+
id: string
|
|
49
|
+
type: 'message' | 'progress' | 'card' | 'question' | 'answer' | 'result'
|
|
50
|
+
text?: string
|
|
51
|
+
steps?: AssistProgressStep[]
|
|
52
|
+
card?: AssistCard
|
|
53
|
+
question?: AssistQuestion
|
|
54
|
+
fields?: Record<string, any>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface AssistSession {
|
|
58
|
+
id: string
|
|
59
|
+
working: boolean
|
|
60
|
+
done: boolean
|
|
61
|
+
turns: AssistTurn[]
|
|
62
|
+
result?: Record<string, any>
|
|
63
|
+
error?: string
|
|
64
|
+
/** Optional: how many beats the provider expects (progress dots). */
|
|
65
|
+
beats?: number
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface AssistInterviewProps {
|
|
69
|
+
assist: FormAssist
|
|
70
|
+
/** Live form values; the declared `input` fields are sent on start. */
|
|
71
|
+
values: Record<string, any>
|
|
72
|
+
/** Called with the provider's result fields (limited to `output`). */
|
|
73
|
+
onApply: (fields: Record<string, any>) => void
|
|
74
|
+
/** Start immediately (the step is the chat itself). Default true. */
|
|
75
|
+
autoStart?: boolean
|
|
76
|
+
/** Small uppercase label above the headline (e.g. the step title). */
|
|
77
|
+
eyebrow?: string
|
|
78
|
+
className?: string
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const POLL_MS = 800
|
|
82
|
+
const THINKING_HINTS = ['Un momento…', 'Sigo trabajando…', 'Casi listo…']
|
|
83
|
+
|
|
84
|
+
function unwrap(res: any): AssistSession {
|
|
85
|
+
const d = res?.data?.data ?? res?.data ?? res
|
|
86
|
+
return d as AssistSession
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function useTypewriter(text: string) {
|
|
90
|
+
const [n, setN] = useState(0)
|
|
91
|
+
const reduce = typeof window !== 'undefined' && !!window.matchMedia?.('(prefers-reduced-motion: reduce)').matches
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
setN(reduce ? text.length : 0)
|
|
94
|
+
}, [text, reduce])
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (n >= text.length) return
|
|
97
|
+
const t = setTimeout(() => setN(v => Math.min(text.length, v + 3)), 22)
|
|
98
|
+
return () => clearTimeout(t)
|
|
99
|
+
}, [n, text])
|
|
100
|
+
return { shown: text.slice(0, n), typing: n < text.length }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function AssistInterview({ assist, values, onApply, autoStart = true, eyebrow, className }: AssistInterviewProps) {
|
|
104
|
+
const api = useApi()
|
|
105
|
+
const [session, setSession] = useState<AssistSession | null>(null)
|
|
106
|
+
const [starting, setStarting] = useState(false)
|
|
107
|
+
const [answer, setAnswer] = useState('')
|
|
108
|
+
const [applied, setApplied] = useState(false)
|
|
109
|
+
const [error, setError] = useState<string | null>(null)
|
|
110
|
+
const [hintIdx, setHintIdx] = useState(0)
|
|
111
|
+
const inputRef = useRef<HTMLInputElement | null>(null)
|
|
112
|
+
// Answers given so far: if the host loses the session (restart, TTL) we
|
|
113
|
+
// start a new one and replay them, so the user never types twice.
|
|
114
|
+
const answersRef = useRef<{ key: string; value: any }[]>([])
|
|
115
|
+
const base = `/assist/${encodeURIComponent(assist.provider)}/sessions`
|
|
116
|
+
|
|
117
|
+
const inputPayload = useMemo(() => {
|
|
118
|
+
const out: Record<string, any> = {}
|
|
119
|
+
for (const k of assist.input ?? []) out[k] = values?.[k]
|
|
120
|
+
return out
|
|
121
|
+
}, [assist.input, values])
|
|
122
|
+
|
|
123
|
+
const start = useCallback(async () => {
|
|
124
|
+
if (starting) return
|
|
125
|
+
setStarting(true)
|
|
126
|
+
setError(null)
|
|
127
|
+
setApplied(false)
|
|
128
|
+
answersRef.current = []
|
|
129
|
+
try {
|
|
130
|
+
const res = await api.post(base, { input: inputPayload })
|
|
131
|
+
setSession(unwrap(res))
|
|
132
|
+
} catch (e: any) {
|
|
133
|
+
setError(e?.response?.data?.message || e?.message || 'No se pudo iniciar el asistente')
|
|
134
|
+
} finally {
|
|
135
|
+
setStarting(false)
|
|
136
|
+
}
|
|
137
|
+
}, [api, base, inputPayload, starting])
|
|
138
|
+
|
|
139
|
+
const autoFired = useRef(false)
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
if (!autoStart || autoFired.current || session) return
|
|
142
|
+
autoFired.current = true
|
|
143
|
+
void start()
|
|
144
|
+
}, [autoStart, session, start])
|
|
145
|
+
|
|
146
|
+
// Poll while the provider works.
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
if (!session || !session.working || session.done) return
|
|
149
|
+
const t = setInterval(async () => {
|
|
150
|
+
try {
|
|
151
|
+
const res = await api.get(`${base}/${session.id}`)
|
|
152
|
+
setSession(unwrap(res))
|
|
153
|
+
} catch (e: any) {
|
|
154
|
+
if (isGone(e)) {
|
|
155
|
+
try {
|
|
156
|
+
setSession(await recover())
|
|
157
|
+
} catch {
|
|
158
|
+
/* next tick retries */
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}, POLL_MS)
|
|
163
|
+
return () => clearInterval(t)
|
|
164
|
+
}, [api, base, session])
|
|
165
|
+
|
|
166
|
+
// Rotate the thinking hint while working.
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
if (!session?.working) return
|
|
169
|
+
setHintIdx(0)
|
|
170
|
+
const t = setInterval(() => setHintIdx(i => Math.min(i + 1, THINKING_HINTS.length - 1)), 2600)
|
|
171
|
+
return () => clearInterval(t)
|
|
172
|
+
}, [session?.working])
|
|
173
|
+
|
|
174
|
+
// Apply the result once.
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
if (!session?.done || !session.result || applied) return
|
|
177
|
+
const allowed = assist.output?.length ? new Set(assist.output) : null
|
|
178
|
+
const out: Record<string, any> = {}
|
|
179
|
+
for (const [k, v] of Object.entries(session.result)) {
|
|
180
|
+
if (allowed && !allowed.has(k)) continue
|
|
181
|
+
if (v === undefined || v === null || v === '') continue
|
|
182
|
+
out[k] = v
|
|
183
|
+
}
|
|
184
|
+
onApply(out)
|
|
185
|
+
setApplied(true)
|
|
186
|
+
}, [session, applied, assist.output, onApply])
|
|
187
|
+
|
|
188
|
+
const isGone = (e: any) => e?.response?.status === 404 || /sesi[oó]n no encontrada/i.test(String(e?.response?.data?.message || e?.message || ''))
|
|
189
|
+
|
|
190
|
+
// Recreate the session and replay every answer (host lost it).
|
|
191
|
+
const recover = useCallback(async () => {
|
|
192
|
+
const res = await api.post(base, { input: inputPayload })
|
|
193
|
+
let s = unwrap(res)
|
|
194
|
+
for (const a of answersRef.current) {
|
|
195
|
+
// wait until the provider is ready for the next answer
|
|
196
|
+
for (let i = 0; i < 120 && s.working && !s.done; i++) {
|
|
197
|
+
await new Promise(r => setTimeout(r, POLL_MS))
|
|
198
|
+
s = unwrap(await api.get(`${base}/${s.id}`))
|
|
199
|
+
}
|
|
200
|
+
if (s.done) break
|
|
201
|
+
s = unwrap(await api.post(`${base}/${s.id}/reply`, { key: a.key, value: a.value }))
|
|
202
|
+
}
|
|
203
|
+
return s
|
|
204
|
+
}, [api, base, inputPayload])
|
|
205
|
+
|
|
206
|
+
const reply = useCallback(
|
|
207
|
+
async (key: string, value: any) => {
|
|
208
|
+
if (!session) return
|
|
209
|
+
setAnswer('')
|
|
210
|
+
setError(null)
|
|
211
|
+
setSession(s => (s ? { ...s, working: true } : s))
|
|
212
|
+
try {
|
|
213
|
+
const res = await api.post(`${base}/${session.id}/reply`, { key, value })
|
|
214
|
+
answersRef.current = [...answersRef.current, { key, value }]
|
|
215
|
+
setSession(unwrap(res))
|
|
216
|
+
} catch (e: any) {
|
|
217
|
+
if (isGone(e)) {
|
|
218
|
+
try {
|
|
219
|
+
answersRef.current = [...answersRef.current, { key, value }]
|
|
220
|
+
setSession(await recover())
|
|
221
|
+
return
|
|
222
|
+
} catch (e2: any) {
|
|
223
|
+
setError(e2?.response?.data?.message || e2?.message || 'No se pudo retomar la conversación')
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
setError(e?.response?.data?.message || e?.message || 'No se pudo enviar la respuesta')
|
|
227
|
+
}
|
|
228
|
+
setSession(s => (s ? { ...s, working: false } : s))
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
[api, base, session, recover],
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
const turns = session?.turns ?? []
|
|
235
|
+
const last = turns[turns.length - 1]
|
|
236
|
+
const pendingQuestion = !session?.working && !session?.done && last?.type === 'question' ? last.question : null
|
|
237
|
+
const lastSpoken = [...turns].reverse().find(t => t.type === 'message' || t.type === 'question')
|
|
238
|
+
const headline = lastSpoken?.type === 'question' ? lastSpoken.question?.prompt ?? '' : lastSpoken?.text ?? ''
|
|
239
|
+
const { shown, typing } = useTypewriter(headline || (starting ? 'Un segundo…' : ''))
|
|
240
|
+
// The latest progress block stays visible as compact chips (done / error
|
|
241
|
+
// included) until the next block replaces it; the card that followed it
|
|
242
|
+
// shows whenever the assistant is waiting on the user or finished.
|
|
243
|
+
const activeProgress = [...turns].reverse().find(t => t.type === 'progress')
|
|
244
|
+
const lastCard = [...turns].reverse().find(t => t.type === 'card')?.card
|
|
245
|
+
const answered = turns.filter(t => t.type === 'answer').length
|
|
246
|
+
const beats = Math.max(session?.beats ?? 5, 2)
|
|
247
|
+
const progress = session?.done ? beats : Math.min(answered + 1, beats)
|
|
248
|
+
const running = activeProgress?.steps?.find(s => s.state === 'running')
|
|
249
|
+
|
|
250
|
+
useEffect(() => {
|
|
251
|
+
if (pendingQuestion && !typing) inputRef.current?.focus()
|
|
252
|
+
}, [pendingQuestion, typing])
|
|
253
|
+
|
|
254
|
+
const submitText = () => {
|
|
255
|
+
if (!pendingQuestion) return
|
|
256
|
+
const v = answer.trim() || pendingQuestion.suggestion || ''
|
|
257
|
+
if (v) void reply(pendingQuestion.key, v)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<div className={'relative overflow-hidden rounded-2xl border bg-gradient-to-b from-primary/[0.06] via-background to-background px-5 py-6 sm:px-8 sm:py-8 ' + (className ?? '')}>
|
|
262
|
+
{/* top: eyebrow + beats */}
|
|
263
|
+
<div className="flex items-center justify-between gap-4">
|
|
264
|
+
<p className="text-xs font-semibold uppercase tracking-wider text-primary">{eyebrow || assist.label || 'Asistente'}</p>
|
|
265
|
+
<div className="flex items-center gap-1.5" aria-label="progreso">
|
|
266
|
+
{Array.from({ length: beats }).map((_, i) => (
|
|
267
|
+
<span key={i} className={'h-1.5 rounded-full transition-all duration-500 ' + (i < progress ? 'w-6 bg-primary' : 'w-3 bg-muted-foreground/25')} />
|
|
268
|
+
))}
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
{/* the question — one at a time, typed out */}
|
|
273
|
+
<div className="mt-5 flex items-start gap-3 sm:gap-4">
|
|
274
|
+
<span className="mt-1 flex size-9 shrink-0 items-center justify-center rounded-xl bg-primary text-primary-foreground shadow-lg shadow-primary/25 sm:size-10">
|
|
275
|
+
<Sparkles className={'size-4 sm:size-5 ' + (session?.working || starting ? 'animate-pulse' : '')} />
|
|
276
|
+
</span>
|
|
277
|
+
<h3 className="min-h-[3.5rem] text-lg font-semibold leading-snug tracking-tight sm:min-h-[4rem] sm:text-2xl">
|
|
278
|
+
{shown}
|
|
279
|
+
{typing && <span className="ml-0.5 inline-block h-5 w-[2px] animate-pulse bg-primary align-middle" />}
|
|
280
|
+
</h3>
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
{/* status line */}
|
|
284
|
+
<div className="mt-3 min-h-6 pl-12 sm:pl-14">
|
|
285
|
+
{(session?.working || starting) && !running && (
|
|
286
|
+
<span className="inline-flex items-center gap-2 text-sm text-muted-foreground">
|
|
287
|
+
<Loader2 className="size-3.5 animate-spin" />
|
|
288
|
+
{THINKING_HINTS[hintIdx]}
|
|
289
|
+
</span>
|
|
290
|
+
)}
|
|
291
|
+
{activeProgress && (
|
|
292
|
+
<ul className="mt-2 flex flex-wrap gap-x-4 gap-y-1" aria-label="pasos">
|
|
293
|
+
{(activeProgress.steps ?? []).map((s, i) => (
|
|
294
|
+
<li key={i} className={'inline-flex items-center gap-1.5 text-xs ' + (s.state === 'pending' ? 'text-muted-foreground/70' : s.state === 'error' ? 'text-destructive' : 'text-foreground/80')}>
|
|
295
|
+
{s.state === 'done' ? <CheckCircle2 className="size-3.5 text-emerald-500" /> : s.state === 'running' ? <Loader2 className="size-3.5 animate-spin text-primary" /> : s.state === 'error' ? <XCircle className="size-3.5" /> : <span className="size-1.5 rounded-full bg-muted-foreground/40" />}
|
|
296
|
+
{s.label}
|
|
297
|
+
{s.state === 'running' && s.detail && <span className="text-muted-foreground">· {s.detail}</span>}
|
|
298
|
+
</li>
|
|
299
|
+
))}
|
|
300
|
+
</ul>
|
|
301
|
+
)}
|
|
302
|
+
{lastCard && !session?.working && <AssistCardView card={lastCard} />}
|
|
303
|
+
{session?.done && (
|
|
304
|
+
<span className="inline-flex items-center gap-2 text-sm font-medium text-emerald-600 dark:text-emerald-400">
|
|
305
|
+
<Check className="size-4" />
|
|
306
|
+
{applied ? 'Listo. Pulsa Siguiente para revisar lo que armé.' : 'Listo.'}
|
|
307
|
+
</span>
|
|
308
|
+
)}
|
|
309
|
+
{(error || session?.error) && <p className="text-sm text-destructive">{error || session?.error}</p>}
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
{/* the answer */}
|
|
313
|
+
{pendingQuestion && (
|
|
314
|
+
<div className="mt-5 pl-0 sm:pl-14">
|
|
315
|
+
{pendingQuestion.kind === 'choice' || pendingQuestion.kind === 'confirm' ? (
|
|
316
|
+
<div className="flex flex-wrap gap-2">
|
|
317
|
+
{(pendingQuestion.options ?? [{ value: 'yes', label: 'Sí' }, { value: 'no', label: 'No' }]).map(o => (
|
|
318
|
+
<Button key={o.value} type="button" variant="outline" className="h-10 rounded-full px-4" onClick={() => void reply(pendingQuestion.key, o.value)}>
|
|
319
|
+
{o.label}
|
|
320
|
+
</Button>
|
|
321
|
+
))}
|
|
322
|
+
</div>
|
|
323
|
+
) : (
|
|
324
|
+
<div className="relative">
|
|
325
|
+
<Input
|
|
326
|
+
ref={inputRef}
|
|
327
|
+
value={answer}
|
|
328
|
+
onChange={e => setAnswer(e.target.value)}
|
|
329
|
+
onKeyDown={e => {
|
|
330
|
+
if (e.key === 'Enter') {
|
|
331
|
+
e.preventDefault()
|
|
332
|
+
submitText()
|
|
333
|
+
}
|
|
334
|
+
}}
|
|
335
|
+
placeholder={pendingQuestion.suggestion || pendingQuestion.placeholder || 'Escribe tu respuesta…'}
|
|
336
|
+
className="h-12 pr-12 text-base"
|
|
337
|
+
autoFocus
|
|
338
|
+
/>
|
|
339
|
+
<Button type="button" size="icon" className="absolute right-1.5 top-1.5 size-9" onClick={submitText} disabled={!answer.trim() && !pendingQuestion.suggestion}>
|
|
340
|
+
<ArrowRight className="size-4" />
|
|
341
|
+
</Button>
|
|
342
|
+
</div>
|
|
343
|
+
)}
|
|
344
|
+
{pendingQuestion.suggestion && pendingQuestion.kind !== 'choice' && (
|
|
345
|
+
<p className="pt-1.5 text-xs text-muted-foreground">Enter acepta la sugerencia.</p>
|
|
346
|
+
)}
|
|
347
|
+
</div>
|
|
348
|
+
)}
|
|
349
|
+
|
|
350
|
+
{/* idle / retry */}
|
|
351
|
+
{!session && !starting && (
|
|
352
|
+
<div className="mt-5 pl-0 sm:pl-14">
|
|
353
|
+
<Button type="button" onClick={() => void start()}>
|
|
354
|
+
<Sparkles className="mr-2 size-4" />
|
|
355
|
+
{assist.label || 'Empezar'}
|
|
356
|
+
</Button>
|
|
357
|
+
</div>
|
|
358
|
+
)}
|
|
359
|
+
{session?.done && (
|
|
360
|
+
<div className="mt-4 pl-0 sm:pl-14">
|
|
361
|
+
<button type="button" onClick={() => { setSession(null); setApplied(false); autoFired.current = false }} className="inline-flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground">
|
|
362
|
+
<RotateCcw className="size-3.5" /> Empezar de nuevo
|
|
363
|
+
</button>
|
|
364
|
+
</div>
|
|
365
|
+
)}
|
|
366
|
+
</div>
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Preview card: logo on dark and light, colour swatches, key/value lines. */
|
|
371
|
+
export function AssistCardView({ card }: { card: AssistCard }) {
|
|
372
|
+
const colors = Object.entries(card.colors ?? {})
|
|
373
|
+
const logoDark = card.logo_light || card.logo
|
|
374
|
+
const logoLight = card.logo_dark || card.logo
|
|
375
|
+
return (
|
|
376
|
+
<div className="mt-3 rounded-xl border bg-background p-3">
|
|
377
|
+
{card.title && <p className="pb-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground">{card.title}</p>}
|
|
378
|
+
{(card.logo || card.logo_light || card.logo_dark) && (
|
|
379
|
+
<div className="grid grid-cols-2 gap-2">
|
|
380
|
+
<div className="flex h-20 items-center justify-center rounded-lg bg-neutral-950 p-3">
|
|
381
|
+
{logoDark && <img src={logoDark} alt="" className="max-h-full max-w-full object-contain" />}
|
|
382
|
+
</div>
|
|
383
|
+
<div className="flex h-20 items-center justify-center rounded-lg border bg-white p-3">
|
|
384
|
+
{logoLight && <img src={logoLight} alt="" className="max-h-full max-w-full object-contain" />}
|
|
385
|
+
</div>
|
|
386
|
+
</div>
|
|
387
|
+
)}
|
|
388
|
+
{colors.length > 0 && (
|
|
389
|
+
<div className="flex flex-wrap gap-2 pt-3">
|
|
390
|
+
{colors.map(([k, v]) => (
|
|
391
|
+
<span key={k} className="inline-flex items-center gap-1.5 rounded-full border px-2 py-1 text-xs">
|
|
392
|
+
<span className="size-4 rounded-full border" style={{ backgroundColor: v }} />
|
|
393
|
+
<span className="text-muted-foreground">{k}</span>
|
|
394
|
+
<span className="font-mono">{v}</span>
|
|
395
|
+
</span>
|
|
396
|
+
))}
|
|
397
|
+
</div>
|
|
398
|
+
)}
|
|
399
|
+
{(card.lines ?? []).length > 0 && (
|
|
400
|
+
<dl className="grid gap-1 pt-3 text-sm">
|
|
401
|
+
{card.lines!.map(l => (
|
|
402
|
+
<div key={l.label} className="grid grid-cols-[6rem_1fr] gap-2">
|
|
403
|
+
<dt className="text-muted-foreground">{l.label}</dt>
|
|
404
|
+
<dd className="min-w-0 break-words">{l.value}</dd>
|
|
405
|
+
</div>
|
|
406
|
+
))}
|
|
407
|
+
</dl>
|
|
408
|
+
)}
|
|
409
|
+
</div>
|
|
410
|
+
)
|
|
411
|
+
}
|
|
@@ -63,6 +63,7 @@ import { getFieldRef, getVisibleWhen, evaluateVisibleWhen } from '../dynamic-for
|
|
|
63
63
|
import type { VisibleWhen } from '../types'
|
|
64
64
|
import { groupFieldsBySection, type FormLayout } from '../form-layout'
|
|
65
65
|
import { FieldSection, WizardProgress } from '../form-layout-ui'
|
|
66
|
+
import { AssistInterview } from '../assist-interview'
|
|
66
67
|
import { FieldCell } from '../field-grid'
|
|
67
68
|
import { isNilUuid, normalizeNilUuid } from '../nil-uuid'
|
|
68
69
|
import { normalizeRefFieldsForSubmit } from './normalize-submit'
|
|
@@ -1082,13 +1083,35 @@ export function DynamicRecordDialog({
|
|
|
1082
1083
|
{isSteps && (
|
|
1083
1084
|
<WizardProgress groups={groups} stepIndex={clampedStep} onStepClick={i => setStepIndex(i)} />
|
|
1084
1085
|
)}
|
|
1085
|
-
{(isSteps ? [groups[clampedStep]] : groups).map(group =>
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1086
|
+
{(isSteps ? [groups[clampedStep]] : groups).map(group =>
|
|
1087
|
+
// An assisted step IS the interview: no form inputs, the
|
|
1088
|
+
// provider asks what it needs and fills the fields.
|
|
1089
|
+
isSteps && isEditable && group.assist ? (
|
|
1090
|
+
<AssistInterview
|
|
1091
|
+
key={group.key}
|
|
1092
|
+
assist={group.assist}
|
|
1093
|
+
values={formValues}
|
|
1094
|
+
eyebrow={group.title}
|
|
1095
|
+
autoStart={group.assist.trigger !== 'button'}
|
|
1096
|
+
onApply={fields => setFormValues(prev => ({ ...prev, ...fields }))}
|
|
1097
|
+
/>
|
|
1098
|
+
) : (
|
|
1099
|
+
<FieldSection key={group.key} group={group}>
|
|
1100
|
+
<div className="grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2">
|
|
1101
|
+
{renderFields(group.fields)}
|
|
1102
|
+
</div>
|
|
1103
|
+
{isEditable && group.assist && (
|
|
1104
|
+
<AssistInterview
|
|
1105
|
+
assist={group.assist}
|
|
1106
|
+
values={formValues}
|
|
1107
|
+
eyebrow={group.title}
|
|
1108
|
+
autoStart={group.assist.trigger === 'auto'}
|
|
1109
|
+
onApply={fields => setFormValues(prev => ({ ...prev, ...fields }))}
|
|
1110
|
+
/>
|
|
1111
|
+
)}
|
|
1112
|
+
</FieldSection>
|
|
1113
|
+
),
|
|
1114
|
+
)}
|
|
1092
1115
|
|
|
1093
1116
|
{record?.external_url && (
|
|
1094
1117
|
<div className="grid grid-cols-1 sm:grid-cols-2">
|
package/src/dynamic-form.tsx
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { useEffect, useMemo, useState } from 'react'
|
|
5
5
|
import { groupFieldsBySection, type FormLayout } from './form-layout'
|
|
6
6
|
import { FieldSection, WizardProgress } from './form-layout-ui'
|
|
7
|
+
import { AssistInterview } from './assist-interview'
|
|
7
8
|
import {
|
|
8
9
|
Input,
|
|
9
10
|
Textarea,
|
|
@@ -212,7 +213,17 @@ export function DynamicForm({
|
|
|
212
213
|
return (
|
|
213
214
|
<form onSubmit={handleSubmit} className="grid gap-4">
|
|
214
215
|
<WizardProgress groups={groups} stepIndex={stepIndex} onStepClick={i => setStepIndex(i)} />
|
|
215
|
-
{
|
|
216
|
+
{step.assist ? (
|
|
217
|
+
<AssistInterview
|
|
218
|
+
assist={step.assist}
|
|
219
|
+
values={values}
|
|
220
|
+
eyebrow={step.title}
|
|
221
|
+
autoStart={step.assist.trigger !== 'button'}
|
|
222
|
+
onApply={fields => setValues(prev => ({ ...prev, ...fields }))}
|
|
223
|
+
/>
|
|
224
|
+
) : (
|
|
225
|
+
renderGrid(step.fields)
|
|
226
|
+
)}
|
|
216
227
|
<div className="flex justify-between gap-2 pt-2">
|
|
217
228
|
{stepIndex > 0 ? (
|
|
218
229
|
<Button type="button" variant="outline" onClick={goBack} disabled={submitting || disabled}>
|
package/src/form-layout-ui.tsx
CHANGED
|
@@ -103,9 +103,6 @@ export function WizardProgress({
|
|
|
103
103
|
{stepLabel} {stepIndex + 1}/{groups.length}
|
|
104
104
|
{current?.title ? ` · ${current.title}` : ''}
|
|
105
105
|
</p>
|
|
106
|
-
{current?.description && (
|
|
107
|
-
<p className="pt-1 text-sm text-muted-foreground">{current.description}</p>
|
|
108
|
-
)}
|
|
109
106
|
</div>
|
|
110
107
|
)
|
|
111
108
|
}
|
package/src/form-layout.ts
CHANGED
|
@@ -31,6 +31,23 @@ export interface FormSection {
|
|
|
31
31
|
* Tolerates the camelCase alias, same as fields. */
|
|
32
32
|
visible_when?: VisibleWhen
|
|
33
33
|
visibleWhen?: VisibleWhen
|
|
34
|
+
/** AI-assisted step (kernel v0.141.0): the SDK renders an interview panel
|
|
35
|
+
* driven by the host's assist provider inside this section. */
|
|
36
|
+
assist?: FormAssist
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Declaration of an AI-assisted step (`form_layout.sections[].assist`). */
|
|
40
|
+
export interface FormAssist {
|
|
41
|
+
/** Host-registered provider key, e.g. `brand.website_dna`. */
|
|
42
|
+
provider: string
|
|
43
|
+
label?: string
|
|
44
|
+
description?: string
|
|
45
|
+
/** Form fields sent to the provider when the session starts. */
|
|
46
|
+
input?: string[]
|
|
47
|
+
/** Form fields the provider may fill when it finishes. */
|
|
48
|
+
output?: string[]
|
|
49
|
+
/** `button` (default) or `auto` (start as soon as every input has a value). */
|
|
50
|
+
trigger?: 'button' | 'auto'
|
|
34
51
|
}
|
|
35
52
|
|
|
36
53
|
/** Model-level layout directive. `mode` defaults to `"sections"`. */
|
|
@@ -50,6 +67,8 @@ export interface FieldGroup<F> {
|
|
|
50
67
|
/** True for the synthetic group holding section-less / unknown-section fields. */
|
|
51
68
|
isDefault: boolean
|
|
52
69
|
fields: F[]
|
|
70
|
+
/** AI-assisted step declaration, when the section has one. */
|
|
71
|
+
assist?: FormAssist
|
|
53
72
|
}
|
|
54
73
|
|
|
55
74
|
/** The synthetic key of the orphan group (fields with no / unknown `section`). */
|
|
@@ -141,6 +160,7 @@ export function groupFieldsBySection<F extends { section?: string }>(
|
|
|
141
160
|
collapsed: s.collapsed,
|
|
142
161
|
isDefault: false,
|
|
143
162
|
fields: secFields,
|
|
163
|
+
assist: s.assist,
|
|
144
164
|
})
|
|
145
165
|
}
|
|
146
166
|
return groups
|
package/src/index.ts
CHANGED
|
@@ -475,3 +475,5 @@ export {
|
|
|
475
475
|
type AccentClasses,
|
|
476
476
|
type WidgetFormatCtx,
|
|
477
477
|
} from './widgets/widget-format'
|
|
478
|
+
export { AssistInterview, AssistCardView } from './assist-interview'
|
|
479
|
+
export type { AssistSession, AssistTurn, AssistQuestion, AssistCard, AssistProgressStep } from './assist-interview'
|