pixelon 0.4.3 → 0.5.1
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/dist/components/molecules/chat/StructuredContent/components/LocationField/usePlaces.d.ts +11 -0
- package/dist/components/molecules/chat/StructuredContent/components/ReactiveCrud/ReactiveCrud.d.ts +1 -0
- package/dist/components/molecules/chat/StructuredContent/components/ReactiveCrud/TranscriptSection.d.ts +10 -0
- package/dist/components/molecules/chat/StructuredContent/components/ReactiveCrud/types.d.ts +40 -1
- package/dist/components/molecules/chat/StructuredContent/components/ServicesMap/types.d.ts +9 -0
- package/dist/components/molecules/chat/StructuredContent/components/index.d.ts +0 -1
- package/dist/components/organisms/chat/Chat/hooks/useAutoScroll.d.ts +8 -1
- package/dist/components/templates/lumi/Crud/helpers/confirmSummary.d.ts +13 -0
- package/dist/components/templates/lumi/Crud/types.d.ts +6 -0
- package/dist/core/chat/ChatConfigContext.d.ts +25 -0
- package/dist/core/i18n/separadores.d.ts +12 -0
- package/dist/index.esm.js +293 -44
- package/dist/index.js +293 -44
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
- package/src/styles/index.scss +1 -0
package/dist/index.esm.js
CHANGED
|
@@ -10924,6 +10924,7 @@ const Drawer = ({
|
|
|
10924
10924
|
"data-testid": dataTestId,
|
|
10925
10925
|
scrollIndicatorOffset = 1
|
|
10926
10926
|
}) => {
|
|
10927
|
+
const idTitulo = useId();
|
|
10927
10928
|
const [isMobile, setIsMobile] = useState(false);
|
|
10928
10929
|
const contentRef = useRef(null);
|
|
10929
10930
|
const [showScrollIndicator, setShowScrollIndicator] = useState(false);
|
|
@@ -11047,14 +11048,14 @@ const Drawer = ({
|
|
|
11047
11048
|
style: getDrawerStyles(),
|
|
11048
11049
|
role: "dialog",
|
|
11049
11050
|
"aria-modal": "true",
|
|
11050
|
-
"aria-labelledby": title ?
|
|
11051
|
+
"aria-labelledby": title ? idTitulo : void 0,
|
|
11051
11052
|
"data-testid": dataTestId,
|
|
11052
11053
|
children: /* @__PURE__ */ jsxs("div", { className: `${COMPONENT_PREFIX}drawer__viewport`, children: [
|
|
11053
11054
|
(title || showCloseButton || headerExtra) && /* @__PURE__ */ jsxs("div", { className: `${COMPONENT_PREFIX}drawer__header`, children: [
|
|
11054
11055
|
title && /* @__PURE__ */ jsx(
|
|
11055
11056
|
"h2",
|
|
11056
11057
|
{
|
|
11057
|
-
id:
|
|
11058
|
+
id: idTitulo,
|
|
11058
11059
|
className: `${COMPONENT_PREFIX}drawer__title`,
|
|
11059
11060
|
children: title
|
|
11060
11061
|
}
|
|
@@ -12817,29 +12818,22 @@ const FormDrawer = ({
|
|
|
12817
12818
|
onClose();
|
|
12818
12819
|
methods.reset();
|
|
12819
12820
|
}, [onClose, onBeforeClose, methods]);
|
|
12820
|
-
const [invalidMsg, setInvalidMsg] = useState(null);
|
|
12821
|
-
useEffect(() => {
|
|
12822
|
-
if (!isOpen) setInvalidMsg(null);
|
|
12823
|
-
}, [isOpen]);
|
|
12824
|
-
useEffect(() => {
|
|
12825
|
-
const sub = methods.watch(() => setInvalidMsg(null));
|
|
12826
|
-
return () => sub.unsubscribe();
|
|
12827
|
-
}, [methods.watch]);
|
|
12828
12821
|
const handleFormSubmit = useCallback(
|
|
12829
12822
|
async (data) => {
|
|
12830
|
-
setInvalidMsg(null);
|
|
12831
12823
|
await onSubmit(data);
|
|
12832
12824
|
},
|
|
12833
12825
|
[onSubmit]
|
|
12834
12826
|
);
|
|
12835
12827
|
const handleInvalid = useCallback(
|
|
12836
12828
|
(formErrors) => {
|
|
12837
|
-
const
|
|
12838
|
-
|
|
12839
|
-
|
|
12840
|
-
);
|
|
12829
|
+
const primero = Object.keys(formErrors)[0];
|
|
12830
|
+
if (!primero) return;
|
|
12831
|
+
const nodo = document.querySelector(`[name="${CSS.escape(primero)}"]`);
|
|
12832
|
+
if (!nodo) return;
|
|
12833
|
+
nodo.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
12834
|
+
window.setTimeout(() => nodo.focus({ preventScroll: true }), 300);
|
|
12841
12835
|
},
|
|
12842
|
-
[
|
|
12836
|
+
[]
|
|
12843
12837
|
);
|
|
12844
12838
|
const handleSubmitClick = methods.handleSubmit(handleFormSubmit, handleInvalid);
|
|
12845
12839
|
const drawerTitle = title || DEFAULT_TITLES[mode];
|
|
@@ -12897,7 +12891,6 @@ const FormDrawer = ({
|
|
|
12897
12891
|
) }) }),
|
|
12898
12892
|
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__footer`, children: [
|
|
12899
12893
|
footerExtra && /* @__PURE__ */ jsx("div", { className: `${baseClass}__footer-extra`, children: footerExtra }),
|
|
12900
|
-
invalidMsg && mode !== "view" && /* @__PURE__ */ jsx("p", { className: `${baseClass}__invalid`, role: "alert", children: invalidMsg }),
|
|
12901
12894
|
/* @__PURE__ */ jsxs("div", { className: `${baseClass}__actions`, children: [
|
|
12902
12895
|
/* @__PURE__ */ jsx(
|
|
12903
12896
|
Button,
|
|
@@ -13175,7 +13168,9 @@ function mapFieldsToFormFields(fields, mode) {
|
|
|
13175
13168
|
readOnly: field.readOnly,
|
|
13176
13169
|
placeholder: field.placeholder,
|
|
13177
13170
|
uiProps: field.uiProps,
|
|
13178
|
-
transformOnSubmit: field.parseValue
|
|
13171
|
+
transformOnSubmit: field.parseValue,
|
|
13172
|
+
gridSpan: field.gridSpan
|
|
13173
|
+
// SPEC-131 T7
|
|
13179
13174
|
}));
|
|
13180
13175
|
}
|
|
13181
13176
|
|
|
@@ -13355,6 +13350,47 @@ function calculateTotalPages(totalRecords, limit) {
|
|
|
13355
13350
|
return Math.ceil(totalRecords / limit);
|
|
13356
13351
|
}
|
|
13357
13352
|
|
|
13353
|
+
function etiquetaDeOpcion(valor, uiProps) {
|
|
13354
|
+
const opciones = uiProps?.options;
|
|
13355
|
+
if (!Array.isArray(opciones)) return null;
|
|
13356
|
+
const encontrada = opciones.find((o) => o && String(o.value) === String(valor));
|
|
13357
|
+
return encontrada?.label != null ? String(encontrada.label) : null;
|
|
13358
|
+
}
|
|
13359
|
+
function esNumerico(moleculeType) {
|
|
13360
|
+
return moleculeType === "INPUT_CURRENCY" || moleculeType === "INPUT_NUMBER";
|
|
13361
|
+
}
|
|
13362
|
+
function aTexto(valor, uiProps, moleculeType) {
|
|
13363
|
+
if (valor == null || valor === "") return "";
|
|
13364
|
+
if (typeof valor === "boolean") return valor ? "S\xED" : "No";
|
|
13365
|
+
if (esNumerico(moleculeType)) {
|
|
13366
|
+
const n = Number(String(valor).replace(/[^\d.-]/g, ""));
|
|
13367
|
+
if (Number.isFinite(n)) return n.toLocaleString();
|
|
13368
|
+
}
|
|
13369
|
+
if (typeof File !== "undefined" && valor instanceof File) return valor.name;
|
|
13370
|
+
if (Array.isArray(valor)) {
|
|
13371
|
+
const partes = valor.map((v) => aTexto(v, uiProps, moleculeType)).filter(Boolean);
|
|
13372
|
+
return partes.join(", ");
|
|
13373
|
+
}
|
|
13374
|
+
if (typeof valor === "object") {
|
|
13375
|
+
const o = valor;
|
|
13376
|
+
for (const clave of ["label", "address", "name", "text", "value"]) {
|
|
13377
|
+
if (typeof o[clave] === "string" && o[clave]) return o[clave];
|
|
13378
|
+
}
|
|
13379
|
+
return "";
|
|
13380
|
+
}
|
|
13381
|
+
return etiquetaDeOpcion(valor, uiProps) ?? String(valor);
|
|
13382
|
+
}
|
|
13383
|
+
function resumenParaConfirmar(campos, datos) {
|
|
13384
|
+
if (!datos) return [];
|
|
13385
|
+
const lineas = [];
|
|
13386
|
+
for (const campo of campos) {
|
|
13387
|
+
const valor = aTexto(datos[campo.name], campo.uiProps, campo.moleculeType);
|
|
13388
|
+
if (!valor) continue;
|
|
13389
|
+
lineas.push({ label: campo.label ?? campo.name, valor });
|
|
13390
|
+
}
|
|
13391
|
+
return lineas;
|
|
13392
|
+
}
|
|
13393
|
+
|
|
13358
13394
|
const useAnchoPropio = () => {
|
|
13359
13395
|
const ref = useRef(null);
|
|
13360
13396
|
const [ancho, setAncho] = useState(null);
|
|
@@ -13799,6 +13835,10 @@ function Crud({
|
|
|
13799
13835
|
return "Formulario";
|
|
13800
13836
|
}
|
|
13801
13837
|
};
|
|
13838
|
+
const resumenConfirmacion = useMemo(
|
|
13839
|
+
() => confirmAction === "create" || confirmAction === "edit" ? resumenParaConfirmar(formFields, pendingFormData) : [],
|
|
13840
|
+
[confirmAction, formFields, pendingFormData]
|
|
13841
|
+
);
|
|
13802
13842
|
const getConfirmMessage = () => {
|
|
13803
13843
|
switch (confirmAction) {
|
|
13804
13844
|
case "create":
|
|
@@ -13938,6 +13978,10 @@ function Crud({
|
|
|
13938
13978
|
className: `${COMPONENT_PREFIX}crud__confirm-drawer`,
|
|
13939
13979
|
children: /* @__PURE__ */ jsxs("div", { className: `${COMPONENT_PREFIX}crud__confirm-content`, children: [
|
|
13940
13980
|
/* @__PURE__ */ jsx("p", { className: `${COMPONENT_PREFIX}crud__confirm-message`, children: getConfirmMessage() }),
|
|
13981
|
+
resumenConfirmacion.length > 0 && /* @__PURE__ */ jsx("dl", { className: `${COMPONENT_PREFIX}crud__confirm-summary`, children: resumenConfirmacion.map((linea) => /* @__PURE__ */ jsxs("div", { className: `${COMPONENT_PREFIX}crud__confirm-summary-row`, children: [
|
|
13982
|
+
/* @__PURE__ */ jsx("dt", { children: linea.label }),
|
|
13983
|
+
/* @__PURE__ */ jsx("dd", { children: linea.valor })
|
|
13984
|
+
] }, linea.label)) }),
|
|
13941
13985
|
/* @__PURE__ */ jsxs("div", { className: `${COMPONENT_PREFIX}crud__confirm-actions`, children: [
|
|
13942
13986
|
/* @__PURE__ */ jsx(
|
|
13943
13987
|
Button,
|
|
@@ -14112,7 +14156,7 @@ function getFormatter(key) {
|
|
|
14112
14156
|
}
|
|
14113
14157
|
|
|
14114
14158
|
const OPTIONS_LOAD_ERROR = "No se pudieron cargar las opciones. Int\xE9ntalo de nuevo.";
|
|
14115
|
-
function unwrap$
|
|
14159
|
+
function unwrap$4(raw) {
|
|
14116
14160
|
if (raw && typeof raw === "object" && "response" in raw) {
|
|
14117
14161
|
return raw.response;
|
|
14118
14162
|
}
|
|
@@ -14133,7 +14177,7 @@ function resolveBody(body, ctx) {
|
|
|
14133
14177
|
}
|
|
14134
14178
|
async function loadOptions(eventBus, ep, ctx) {
|
|
14135
14179
|
const raw = await fetchViaEventBus(eventBus, ep.service, ep.url, ep.method, resolveBody(ep.body, ctx));
|
|
14136
|
-
let data = unwrap$
|
|
14180
|
+
let data = unwrap$4(raw);
|
|
14137
14181
|
if (ep.response_path && data && typeof data === "object" && !Array.isArray(data)) {
|
|
14138
14182
|
data = data[ep.response_path];
|
|
14139
14183
|
}
|
|
@@ -14171,7 +14215,7 @@ const mensajeDeError = (e, respaldo) => {
|
|
|
14171
14215
|
};
|
|
14172
14216
|
|
|
14173
14217
|
const REFRESH_INTERVAL_MS = 2e4;
|
|
14174
|
-
function unwrap$
|
|
14218
|
+
function unwrap$3(raw) {
|
|
14175
14219
|
if (raw && typeof raw === "object" && "response" in raw) {
|
|
14176
14220
|
return raw.response;
|
|
14177
14221
|
}
|
|
@@ -14220,7 +14264,7 @@ function useReactiveCrud(config, eventBus) {
|
|
|
14220
14264
|
filters
|
|
14221
14265
|
});
|
|
14222
14266
|
assertOk(raw);
|
|
14223
|
-
const data = unwrap$
|
|
14267
|
+
const data = unwrap$3(raw);
|
|
14224
14268
|
let rows = [];
|
|
14225
14269
|
let total = 0;
|
|
14226
14270
|
if (Array.isArray(data)) {
|
|
@@ -14424,6 +14468,20 @@ const FileField = ({
|
|
|
14424
14468
|
] });
|
|
14425
14469
|
};
|
|
14426
14470
|
|
|
14471
|
+
const RESPALDO = { miles: ".", decimal: "," };
|
|
14472
|
+
function separadoresDelLocale(locale) {
|
|
14473
|
+
if (!locale) return RESPALDO;
|
|
14474
|
+
try {
|
|
14475
|
+
const partes = new Intl.NumberFormat(locale).formatToParts(123456789e-2);
|
|
14476
|
+
const miles = partes.find((p) => p.type === "group")?.value;
|
|
14477
|
+
const decimal = partes.find((p) => p.type === "decimal")?.value;
|
|
14478
|
+
if (!miles || !decimal) return RESPALDO;
|
|
14479
|
+
return { miles, decimal };
|
|
14480
|
+
} catch {
|
|
14481
|
+
return RESPALDO;
|
|
14482
|
+
}
|
|
14483
|
+
}
|
|
14484
|
+
|
|
14427
14485
|
const CRUD_TO_FORM_MOLECULE = {
|
|
14428
14486
|
INPUT: "INPUT",
|
|
14429
14487
|
INPUT_NUMBER: "INPUT_NUMBER",
|
|
@@ -14480,6 +14538,8 @@ function ActionFormDrawer({ action, row, idField, eventBus, onExecute, onClose }
|
|
|
14480
14538
|
alive = false;
|
|
14481
14539
|
};
|
|
14482
14540
|
}, [eventBus, action.key, normalFields]);
|
|
14541
|
+
const { locale } = useChatConfig();
|
|
14542
|
+
const separadores = useMemo(() => separadoresDelLocale(locale), [locale]);
|
|
14483
14543
|
const fields = useMemo(
|
|
14484
14544
|
() => normalFields.map((f) => ({
|
|
14485
14545
|
name: f.name,
|
|
@@ -14490,9 +14550,19 @@ function ActionFormDrawer({ action, row, idField, eventBus, onExecute, onClose }
|
|
|
14490
14550
|
options: f.options ?? optionsByField[f.name],
|
|
14491
14551
|
// SPEC-106 T8 — `uiProps` llega tal cual a la molécula, así que el desplegable dice lo
|
|
14492
14552
|
// que pasó de verdad en vez del «no hay» de siempre. Sin error, se queda el de siempre.
|
|
14493
|
-
...optionsErrorByField[f.name] ? { uiProps: { notFoundContent: optionsErrorByField[f.name] } } : {}
|
|
14553
|
+
...optionsErrorByField[f.name] ? { uiProps: { notFoundContent: optionsErrorByField[f.name] } } : {},
|
|
14554
|
+
// SPEC-128 — el dinero se escribe como se escribe AQUÍ.
|
|
14555
|
+
//
|
|
14556
|
+
// Los campos de moneda caían a `thousandSeparator ?? ','`, un valor por defecto en formato
|
|
14557
|
+
// inglés: al teclear 180000 el campo mostraba «180,000» mientras la tarjeta que recibe el
|
|
14558
|
+
// cliente decía «$ 180.000,00 COP». El mismo importe escrito de una manera y leído de otra.
|
|
14559
|
+
//
|
|
14560
|
+
// Se deriva del locale que el host ya declara en la config del chat, no de un valor fijo ni
|
|
14561
|
+
// del navegador: `navigator.language` puede ser `es-419` —español, pero con coma para los
|
|
14562
|
+
// miles— y entonces el resultado dependería del portátil de cada quien.
|
|
14563
|
+
...f.moleculeType === "INPUT_CURRENCY" ? { uiProps: { thousandSeparator: separadores.miles, decimalSeparator: separadores.decimal } } : {}
|
|
14494
14564
|
})),
|
|
14495
|
-
[normalFields, optionsByField, optionsErrorByField]
|
|
14565
|
+
[normalFields, optionsByField, optionsErrorByField, separadores]
|
|
14496
14566
|
);
|
|
14497
14567
|
const validationSchema = useMemo(() => buildCrudYupSchema(normalFields), [normalFields]);
|
|
14498
14568
|
const uploadOne = async (f, file, idx) => {
|
|
@@ -15151,6 +15221,95 @@ const FileViewer = ({
|
|
|
15151
15221
|
] });
|
|
15152
15222
|
};
|
|
15153
15223
|
|
|
15224
|
+
function unwrap$2(raw) {
|
|
15225
|
+
if (raw && typeof raw === "object" && "response" in raw) {
|
|
15226
|
+
return raw.response;
|
|
15227
|
+
}
|
|
15228
|
+
return raw;
|
|
15229
|
+
}
|
|
15230
|
+
function hora(iso) {
|
|
15231
|
+
if (!iso) return "";
|
|
15232
|
+
const d = new Date(iso);
|
|
15233
|
+
if (Number.isNaN(d.getTime())) return "";
|
|
15234
|
+
return d.toLocaleString(void 0, { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" });
|
|
15235
|
+
}
|
|
15236
|
+
function TranscriptSection({ transcript, values, eventBus }) {
|
|
15237
|
+
const chatId = values[transcript.chatIdField];
|
|
15238
|
+
const id = chatId == null || chatId === "" ? "" : String(chatId);
|
|
15239
|
+
const [datos, setDatos] = useState(null);
|
|
15240
|
+
const [cargando, setCargando] = useState(true);
|
|
15241
|
+
const [fallo, setFallo] = useState(false);
|
|
15242
|
+
useEffect(() => {
|
|
15243
|
+
if (!eventBus || !id) {
|
|
15244
|
+
setCargando(false);
|
|
15245
|
+
return;
|
|
15246
|
+
}
|
|
15247
|
+
let vivo = true;
|
|
15248
|
+
setCargando(true);
|
|
15249
|
+
setFallo(false);
|
|
15250
|
+
void (async () => {
|
|
15251
|
+
try {
|
|
15252
|
+
const raw = await fetchViaEventBus(
|
|
15253
|
+
eventBus,
|
|
15254
|
+
transcript.get.service,
|
|
15255
|
+
transcript.get.url,
|
|
15256
|
+
transcript.get.method ?? "POST",
|
|
15257
|
+
{ [transcript.param ?? "chat_id"]: id }
|
|
15258
|
+
);
|
|
15259
|
+
if (vivo) {
|
|
15260
|
+
setDatos(unwrap$2(raw) ?? {});
|
|
15261
|
+
setCargando(false);
|
|
15262
|
+
}
|
|
15263
|
+
} catch {
|
|
15264
|
+
if (vivo) {
|
|
15265
|
+
setFallo(true);
|
|
15266
|
+
setCargando(false);
|
|
15267
|
+
}
|
|
15268
|
+
}
|
|
15269
|
+
})();
|
|
15270
|
+
return () => {
|
|
15271
|
+
vivo = false;
|
|
15272
|
+
};
|
|
15273
|
+
}, [eventBus, id, transcript]);
|
|
15274
|
+
if (!id) return null;
|
|
15275
|
+
const lineas = datos?.lines ?? [];
|
|
15276
|
+
const dijoAlgo = (datos?.from_user ?? 0) > 0;
|
|
15277
|
+
const nombres = {
|
|
15278
|
+
user: transcript.labels?.user ?? "Cliente",
|
|
15279
|
+
assistant: transcript.labels?.assistant ?? "Asistente"
|
|
15280
|
+
};
|
|
15281
|
+
return /* @__PURE__ */ jsxs("div", { className: "px-chat-crud-transcript", children: [
|
|
15282
|
+
/* @__PURE__ */ jsx("h4", { className: "px-chat-crud-transcript__title", children: transcript.title ?? "Conversaci\xF3n" }),
|
|
15283
|
+
cargando && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-transcript__note", children: "Cargando\u2026" }),
|
|
15284
|
+
!cargando && fallo && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-transcript__note", children: "No se pudo cargar la conversaci\xF3n." }),
|
|
15285
|
+
!cargando && !fallo && !dijoAlgo && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-transcript__note", children: transcript.silentText ?? "Todav\xEDa no ha escrito nada." }),
|
|
15286
|
+
!cargando && !fallo && lineas.length > 0 && /* @__PURE__ */ jsx("ol", { className: "px-chat-crud-transcript__lines", children: lineas.map((l, i) => /* @__PURE__ */ jsxs(
|
|
15287
|
+
"li",
|
|
15288
|
+
{
|
|
15289
|
+
className: [
|
|
15290
|
+
"px-chat-crud-transcript__line",
|
|
15291
|
+
l.rol === "user" ? "is-user" : "is-assistant"
|
|
15292
|
+
].join(" "),
|
|
15293
|
+
children: [
|
|
15294
|
+
/* @__PURE__ */ jsxs("span", { className: "px-chat-crud-transcript__who", children: [
|
|
15295
|
+
l.rol === "user" ? nombres.user : nombres.assistant,
|
|
15296
|
+
hora(l.created_date) && /* @__PURE__ */ jsx("span", { className: "px-chat-crud-transcript__when", children: hora(l.created_date) })
|
|
15297
|
+
] }),
|
|
15298
|
+
/* @__PURE__ */ jsx("span", { className: "px-chat-crud-transcript__text", children: l.text })
|
|
15299
|
+
]
|
|
15300
|
+
},
|
|
15301
|
+
`${i}-${l.created_date ?? ""}`
|
|
15302
|
+
)) }),
|
|
15303
|
+
!cargando && !fallo && (datos?.total ?? 0) > lineas.length && /* @__PURE__ */ jsxs("div", { className: "px-chat-crud-transcript__note", children: [
|
|
15304
|
+
"Se muestran los \xFAltimos ",
|
|
15305
|
+
lineas.length,
|
|
15306
|
+
" de ",
|
|
15307
|
+
datos?.total,
|
|
15308
|
+
" mensajes."
|
|
15309
|
+
] })
|
|
15310
|
+
] });
|
|
15311
|
+
}
|
|
15312
|
+
|
|
15154
15313
|
function unwrap$1(raw) {
|
|
15155
15314
|
if (raw && typeof raw === "object" && "response" in raw) {
|
|
15156
15315
|
return raw.response;
|
|
@@ -15279,6 +15438,7 @@ function DetailDrawer({ row, config, eventBus, onClose, loadedOptions }) {
|
|
|
15279
15438
|
] })
|
|
15280
15439
|
] }, f.name);
|
|
15281
15440
|
}) }),
|
|
15441
|
+
config.transcript && /* @__PURE__ */ jsx(TranscriptSection, { transcript: config.transcript, values, eventBus }),
|
|
15282
15442
|
/* @__PURE__ */ jsxs("div", { className: "px-chat-reactive-crud-detail__photos", children: [
|
|
15283
15443
|
/* @__PURE__ */ jsx("h4", { className: "px-chat-reactive-crud-detail__photos-title", children: "Fotos y documentos" }),
|
|
15284
15444
|
loading ? /* @__PURE__ */ jsx("div", { className: "px-chat-reactive-crud-detail__empty", children: "Cargando\u2026" }) : files.length ? /* @__PURE__ */ jsx(FileViewer, { files }) : /* @__PURE__ */ jsx("div", { className: "px-chat-reactive-crud-detail__empty", children: "Este servicio no tiene archivos." })
|
|
@@ -15767,7 +15927,8 @@ function usePlaceSuggestions(query, enabled) {
|
|
|
15767
15927
|
return {
|
|
15768
15928
|
lat: typeof loc.lat === "function" ? loc.lat() : loc.lat,
|
|
15769
15929
|
lng: typeof loc.lng === "function" ? loc.lng() : loc.lng,
|
|
15770
|
-
address: String(place.formattedAddress ?? "")
|
|
15930
|
+
address: limpiarDireccion(String(place.formattedAddress ?? ""))
|
|
15931
|
+
// SPEC-131 T6
|
|
15771
15932
|
};
|
|
15772
15933
|
} catch {
|
|
15773
15934
|
return null;
|
|
@@ -15777,13 +15938,27 @@ function usePlaceSuggestions(query, enabled) {
|
|
|
15777
15938
|
);
|
|
15778
15939
|
return { suggestions, resolve, clear: () => setSuggestions([]) };
|
|
15779
15940
|
}
|
|
15941
|
+
function limpiarDireccion(address) {
|
|
15942
|
+
const normal = (s) => s.trim().toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
15943
|
+
const vistos = /* @__PURE__ */ new Set();
|
|
15944
|
+
const tramos = [];
|
|
15945
|
+
for (const tramo of address.split(",")) {
|
|
15946
|
+
const limpio = tramo.trim();
|
|
15947
|
+
if (!limpio) continue;
|
|
15948
|
+
const clave = normal(limpio);
|
|
15949
|
+
if (vistos.has(clave)) continue;
|
|
15950
|
+
vistos.add(clave);
|
|
15951
|
+
tramos.push(limpio);
|
|
15952
|
+
}
|
|
15953
|
+
return tramos.join(", ");
|
|
15954
|
+
}
|
|
15780
15955
|
function reverseGeocode(lat, lng) {
|
|
15781
15956
|
return new Promise((resolve) => {
|
|
15782
15957
|
const maps = window.google?.maps;
|
|
15783
15958
|
if (!maps?.Geocoder) return resolve(null);
|
|
15784
15959
|
new maps.Geocoder().geocode({ location: { lat, lng } }, (results, status) => {
|
|
15785
15960
|
if (status !== "OK" || !results?.length) return resolve(null);
|
|
15786
|
-
resolve(String(results[0].formatted_address ?? "") || null);
|
|
15961
|
+
resolve(limpiarDireccion(String(results[0].formatted_address ?? "")) || null);
|
|
15787
15962
|
});
|
|
15788
15963
|
});
|
|
15789
15964
|
}
|
|
@@ -15940,6 +16115,10 @@ function toRenderField(f, loaded, eventBus, mapsApiKey, loadError) {
|
|
|
15940
16115
|
showInModes: f.showInModes,
|
|
15941
16116
|
required: f.required,
|
|
15942
16117
|
placeholder: f.placeholder,
|
|
16118
|
+
// SPEC-131 T7 — la fila entera: al elegir la dirección se despliega el mapa de verificación
|
|
16119
|
+
// y, a media fila, su campo hermano se quedaba con ~400px de blanco al lado. Medido entre
|
|
16120
|
+
// «¿En qué situación está?» y «¿A dónde lo llevamos?» del formulario de solicitud.
|
|
16121
|
+
gridSpan: 2,
|
|
15943
16122
|
uiProps: {
|
|
15944
16123
|
// SPEC-074 — `error` viene del render de CUSTOM (DynamicForm ya lo pasaba) y aquí se
|
|
15945
16124
|
// desechaba: el campo obligatorio más importante era el único mudo.
|
|
@@ -15985,6 +16164,10 @@ function matchesVisibleWhen(a, row) {
|
|
|
15985
16164
|
if (!a.visibleWhen) return true;
|
|
15986
16165
|
return cumplenTodas(a.visibleWhen, row);
|
|
15987
16166
|
}
|
|
16167
|
+
function laQueTocaAhora(actions, row) {
|
|
16168
|
+
const candidatas = actions.filter((a) => a.ahoraToca && cumplenTodas(a.ahoraToca, row));
|
|
16169
|
+
return candidatas.length === 1 ? candidatas[0] : void 0;
|
|
16170
|
+
}
|
|
15988
16171
|
function condicionesDeshabilitado(a) {
|
|
15989
16172
|
if (!a.disabledWhen) return [];
|
|
15990
16173
|
return Array.isArray(a.disabledWhen) ? a.disabledWhen : [a.disabledWhen];
|
|
@@ -16305,7 +16488,7 @@ function DeadlineCountdown({ deadline, label, expiredLabel }) {
|
|
|
16305
16488
|
}
|
|
16306
16489
|
|
|
16307
16490
|
function ReactiveCrudItemChat({ component, eventBus }) {
|
|
16308
|
-
const { resolveModuleConfig } = useChatConfig();
|
|
16491
|
+
const { resolveModuleConfig, resolveMomento } = useChatConfig();
|
|
16309
16492
|
const props = component.props ?? {};
|
|
16310
16493
|
const top = component;
|
|
16311
16494
|
const pick = (key) => props[key] !== void 0 ? props[key] : top[key];
|
|
@@ -16313,7 +16496,11 @@ function ReactiveCrudItemChat({ component, eventBus }) {
|
|
|
16313
16496
|
const recordId = pick("record_id") ?? pick("recordId") ?? pick("id") ?? pick("service_id");
|
|
16314
16497
|
const recordLabel = pick("record_label") ?? pick("label");
|
|
16315
16498
|
const allowedRaw = pick("actions");
|
|
16316
|
-
const
|
|
16499
|
+
const allowedLegacy = Array.isArray(allowedRaw) && allowedRaw.length > 0 ? allowedRaw : void 0;
|
|
16500
|
+
const momento = pick("momento");
|
|
16501
|
+
const delMomento = momento && resolveMomento ? resolveMomento(momento) : void 0;
|
|
16502
|
+
const allowed = delMomento ?? allowedLegacy;
|
|
16503
|
+
const sinAcciones = delMomento?.length === 0;
|
|
16317
16504
|
const config = typeof moduleKey === "string" && resolveModuleConfig ? resolveModuleConfig(moduleKey) : void 0;
|
|
16318
16505
|
if (!config || recordId == null || recordId === "") return null;
|
|
16319
16506
|
return /* @__PURE__ */ jsx(
|
|
@@ -16323,11 +16510,12 @@ function ReactiveCrudItemChat({ component, eventBus }) {
|
|
|
16323
16510
|
recordId: String(recordId),
|
|
16324
16511
|
recordLabel,
|
|
16325
16512
|
allowed: Array.isArray(allowed) ? allowed.map(String) : void 0,
|
|
16513
|
+
sinAcciones,
|
|
16326
16514
|
eventBus
|
|
16327
16515
|
}
|
|
16328
16516
|
);
|
|
16329
16517
|
}
|
|
16330
|
-
function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus }) {
|
|
16518
|
+
function ServiceActionsPanel({ config, recordId, recordLabel, allowed, sinAcciones, eventBus }) {
|
|
16331
16519
|
const { chatIsForeign } = useChatConfig();
|
|
16332
16520
|
const crud = useReactiveCrud(config, eventBus);
|
|
16333
16521
|
const [activeAction, setActiveAction] = useState(null);
|
|
@@ -16376,9 +16564,26 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
|
|
|
16376
16564
|
}, [recordId]);
|
|
16377
16565
|
const gastada = !esUltima;
|
|
16378
16566
|
const actions = useMemo(() => {
|
|
16379
|
-
if (!row
|
|
16380
|
-
|
|
16567
|
+
if (!row) return [];
|
|
16568
|
+
if (gastada) {
|
|
16569
|
+
const hechas = (config.rowActions ?? []).filter((a) => !allowed || allowed.includes(a.key)).filter((a) => matchesDoneWhen(a, row));
|
|
16570
|
+
const vistas = /* @__PURE__ */ new Set();
|
|
16571
|
+
return hechas.filter((a) => {
|
|
16572
|
+
const linea = a.doneLabel ?? a.label;
|
|
16573
|
+
if (vistas.has(linea)) return false;
|
|
16574
|
+
vistas.add(linea);
|
|
16575
|
+
return true;
|
|
16576
|
+
});
|
|
16577
|
+
}
|
|
16578
|
+
const visibles = (config.rowActions ?? []).filter((a) => !allowed || allowed.includes(a.key)).filter((a) => matchesVisibleWhen(a, row));
|
|
16579
|
+
const toca = laQueTocaAhora(visibles, row);
|
|
16580
|
+
if (!toca) return visibles;
|
|
16581
|
+
return [toca, ...visibles.filter((a) => a.key !== toca.key)];
|
|
16381
16582
|
}, [config.rowActions, row, allowed, gastada]);
|
|
16583
|
+
const claveQueToca = useMemo(
|
|
16584
|
+
() => row && !gastada ? laQueTocaAhora(actions, row)?.key : void 0,
|
|
16585
|
+
[actions, row, gastada]
|
|
16586
|
+
);
|
|
16382
16587
|
const title = recordLabel ?? config.title ?? "Servicio";
|
|
16383
16588
|
return /* @__PURE__ */ jsxs("div", { className: "px-chat-crud-item", ref: raiz, "data-panel-record": recordId, children: [
|
|
16384
16589
|
/* @__PURE__ */ jsx("div", { className: "px-chat-crud-item__header", children: title }),
|
|
@@ -16389,7 +16594,7 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
|
|
|
16389
16594
|
crud.loading && !row && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-item__hint", children: "Cargando\u2026" }),
|
|
16390
16595
|
!crud.loading && !row && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-item__hint", children: "No se encontr\xF3 el servicio." }),
|
|
16391
16596
|
gastada && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-item__hint", children: "Este paso ya se gestion\xF3. Contin\xFAa en el mensaje m\xE1s reciente." }),
|
|
16392
|
-
row && config.deadline && typeof row[config.deadline.field] === "string" && /* @__PURE__ */ jsx(
|
|
16597
|
+
row && !gastada && config.deadline && typeof row[config.deadline.field] === "string" && /* @__PURE__ */ jsx(
|
|
16393
16598
|
DeadlineCountdown,
|
|
16394
16599
|
{
|
|
16395
16600
|
deadline: row[config.deadline.field],
|
|
@@ -16400,7 +16605,7 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
|
|
|
16400
16605
|
row && /* @__PURE__ */ jsxs("div", { className: "px-chat-crud-item__actions", children: [
|
|
16401
16606
|
config.detail && /* @__PURE__ */ jsx("button", { type: "button", className: "px-chat-crud-item__action", onClick: () => setDetailRow(row), children: "Ver" }),
|
|
16402
16607
|
actions.map((a) => {
|
|
16403
|
-
const disabled = Boolean(chatIsForeign) || matchesDisabledWhen(a, row);
|
|
16608
|
+
const disabled = gastada || Boolean(chatIsForeign) || matchesDisabledWhen(a, row);
|
|
16404
16609
|
const motivo = chatIsForeign ? "Esta conversaci\xF3n es de otra persona" : disabledReasonFor(a, row);
|
|
16405
16610
|
const done = matchesDoneWhen(a, row);
|
|
16406
16611
|
return /* @__PURE__ */ jsxs(
|
|
@@ -16410,13 +16615,18 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
|
|
|
16410
16615
|
className: [
|
|
16411
16616
|
"px-chat-crud-item__action",
|
|
16412
16617
|
a.intent === "danger" ? "is-danger" : "",
|
|
16413
|
-
disabled && motivo ? "is-con-motivo" : ""
|
|
16618
|
+
disabled && motivo ? "is-con-motivo" : "",
|
|
16619
|
+
// SPEC-129 T6 — lo hecho se pinta como un acta, no como un botón apagado.
|
|
16620
|
+
gastada ? "is-hecho" : "",
|
|
16621
|
+
// SPEC-127 T4 — señalada, pero solo si de verdad se puede pulsar: destacar una
|
|
16622
|
+
// acción apagada sería dirigir a alguien a una puerta cerrada.
|
|
16623
|
+
a.key === claveQueToca && !disabled ? "is-ahora" : ""
|
|
16414
16624
|
].filter(Boolean).join(" "),
|
|
16415
16625
|
disabled,
|
|
16416
16626
|
onClick: () => a.form ? setActiveAction({ action: a, row }) : crud.runEndpoints(a.endpoints ?? [a.endpoint], { ...row, id: row[config.idField] }),
|
|
16417
16627
|
children: [
|
|
16418
16628
|
/* @__PURE__ */ jsxs("span", { className: "px-chat-crud-item__action-texts", children: [
|
|
16419
|
-
/* @__PURE__ */ jsx("span", { children: a.label }),
|
|
16629
|
+
/* @__PURE__ */ jsx("span", { children: gastada && a.doneLabel ? a.doneLabel : a.label }),
|
|
16420
16630
|
disabled && motivo && /* @__PURE__ */ jsx("span", { className: "px-chat-crud-item__action-reason", children: motivo })
|
|
16421
16631
|
] }),
|
|
16422
16632
|
done && /* @__PURE__ */ jsx(Icon$1, { type: "check-circle", width: 18, height: 18, className: "px-chat-crud-item__done", "aria-label": "Hecho" })
|
|
@@ -16425,7 +16635,7 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
|
|
|
16425
16635
|
a.key
|
|
16426
16636
|
);
|
|
16427
16637
|
}),
|
|
16428
|
-
actions.length === 0 && !config.detail && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-item__hint", children: "Sin acciones disponibles." })
|
|
16638
|
+
actions.length === 0 && !config.detail && !sinAcciones && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-item__hint", children: "Sin acciones disponibles." })
|
|
16429
16639
|
] }),
|
|
16430
16640
|
activeAction && /* @__PURE__ */ jsx(
|
|
16431
16641
|
ActionFormDrawer,
|
|
@@ -16533,6 +16743,8 @@ const ServicesMap = ({ config, eventBus, apiKey, filter, fill }) => {
|
|
|
16533
16743
|
const { service, url, method, payloadMap } = config.data.list;
|
|
16534
16744
|
void fetchViaEventBus(eventBus, service, url, method ?? "POST", {
|
|
16535
16745
|
...payloadMap,
|
|
16746
|
+
// SPEC-131 T6 — el del config primero, el de la instancia después: quien acota manda.
|
|
16747
|
+
...config.defaultFilter ?? {},
|
|
16536
16748
|
...filter ?? {}
|
|
16537
16749
|
}).then((res) => {
|
|
16538
16750
|
if (!alive) return;
|
|
@@ -16645,7 +16857,7 @@ const ServicesMap = ({ config, eventBus, apiKey, filter, fill }) => {
|
|
|
16645
16857
|
),
|
|
16646
16858
|
simulated > 0 && ` \xB7 ${config.live?.labels?.simulatedText ?? "incluye posiciones simuladas"}`
|
|
16647
16859
|
] }),
|
|
16648
|
-
missing > 0 && /* @__PURE__ */ jsx("p", { className: `${CLASS$6}__note`, children: (labels.missingText ?? "{n} sin ubicaci\xF3n registrada").replace("{n}", String(missing)) }),
|
|
16860
|
+
missing > 0 && /* @__PURE__ */ jsx("p", { className: `${CLASS$6}__note`, children: ((missing === 1 ? labels.missingTextOne : void 0) ?? labels.missingText ?? "{n} sin ubicaci\xF3n registrada").replace("{n}", String(missing)) }),
|
|
16649
16861
|
outside > 0 && /* @__PURE__ */ jsx("p", { className: `${CLASS$6}__note`, children: (labels.outsideText ?? "{n} fuera de la zona (aleja el mapa para verlos)").replace(
|
|
16650
16862
|
"{n}",
|
|
16651
16863
|
String(outside)
|
|
@@ -18686,6 +18898,10 @@ const useInputFocus = ({
|
|
|
18686
18898
|
};
|
|
18687
18899
|
};
|
|
18688
18900
|
|
|
18901
|
+
function reservaNecesaria(clientHeight, scrollHeight, targetTop) {
|
|
18902
|
+
const debajoDelObjetivo = scrollHeight - targetTop;
|
|
18903
|
+
return Math.max(0, clientHeight - debajoDelObjetivo);
|
|
18904
|
+
}
|
|
18689
18905
|
const useAutoScroll = ({
|
|
18690
18906
|
messagesEndRef,
|
|
18691
18907
|
messagesContainerRef
|
|
@@ -18704,11 +18920,12 @@ const useAutoScroll = ({
|
|
|
18704
18920
|
const container = messagesContainerRef.current;
|
|
18705
18921
|
const spacer = messagesEndRef.current;
|
|
18706
18922
|
if (!container || !spacer || !target) return;
|
|
18707
|
-
spacer.style.minHeight =
|
|
18923
|
+
spacer.style.minHeight = "0px";
|
|
18708
18924
|
const containerRect = container.getBoundingClientRect();
|
|
18709
18925
|
const msgRect = target.getBoundingClientRect();
|
|
18710
18926
|
const padTop = parseFloat(getComputedStyle(container).paddingTop) || 0;
|
|
18711
18927
|
const targetTop = msgRect.top - containerRect.top + container.scrollTop - padTop;
|
|
18928
|
+
spacer.style.minHeight = `${reservaNecesaria(container.clientHeight, container.scrollHeight, targetTop)}px`;
|
|
18712
18929
|
container.scrollTo({ top: targetTop, behavior: "smooth" });
|
|
18713
18930
|
}, [messagesContainerRef, messagesEndRef]);
|
|
18714
18931
|
const last = useCallback((selector) => {
|
|
@@ -18750,15 +18967,33 @@ const useAutoScroll = ({
|
|
|
18750
18967
|
});
|
|
18751
18968
|
}, 150);
|
|
18752
18969
|
}, [doScroll]);
|
|
18753
|
-
const
|
|
18970
|
+
const aterrizarAlFinal = useCallback(() => {
|
|
18971
|
+
const container = messagesContainerRef.current;
|
|
18972
|
+
if (!container) return;
|
|
18973
|
+
resetSpacer();
|
|
18974
|
+
const hasta = Date.now() + 1e3;
|
|
18975
|
+
let ultimoAlto = -1;
|
|
18976
|
+
const pegar = () => {
|
|
18977
|
+
const c = messagesContainerRef.current;
|
|
18978
|
+
if (!c) return;
|
|
18979
|
+
if (c.scrollHeight !== ultimoAlto) {
|
|
18980
|
+
ultimoAlto = c.scrollHeight;
|
|
18981
|
+
c.scrollTop = c.scrollHeight;
|
|
18982
|
+
}
|
|
18983
|
+
if (Date.now() < hasta) requestAnimationFrame(pegar);
|
|
18984
|
+
};
|
|
18985
|
+
requestAnimationFrame(pegar);
|
|
18986
|
+
}, [messagesContainerRef, resetSpacer]);
|
|
18987
|
+
const scrollToBottom = useCallback((suave = true) => {
|
|
18754
18988
|
cancelPending();
|
|
18755
18989
|
resetSpacer();
|
|
18756
18990
|
requestAnimationFrame(() => {
|
|
18991
|
+
const comportamiento = suave ? "smooth" : "auto";
|
|
18757
18992
|
const container = messagesContainerRef.current;
|
|
18758
18993
|
if (container) {
|
|
18759
|
-
container.scrollTo({ top: container.scrollHeight, behavior:
|
|
18994
|
+
container.scrollTo({ top: container.scrollHeight, behavior: comportamiento });
|
|
18760
18995
|
} else {
|
|
18761
|
-
messagesEndRef.current?.scrollIntoView({ behavior:
|
|
18996
|
+
messagesEndRef.current?.scrollIntoView({ behavior: comportamiento, block: "end" });
|
|
18762
18997
|
}
|
|
18763
18998
|
});
|
|
18764
18999
|
}, [cancelPending, resetSpacer, messagesContainerRef, messagesEndRef]);
|
|
@@ -18766,7 +19001,9 @@ const useAutoScroll = ({
|
|
|
18766
19001
|
scrollToLastUserMessage,
|
|
18767
19002
|
pinLastUserMessage,
|
|
18768
19003
|
scrollToProactive,
|
|
18769
|
-
scrollToBottom
|
|
19004
|
+
scrollToBottom,
|
|
19005
|
+
aterrizarAlFinal
|
|
19006
|
+
// SPEC-131 T3
|
|
18770
19007
|
};
|
|
18771
19008
|
};
|
|
18772
19009
|
|
|
@@ -19319,7 +19556,7 @@ const Chat = ({
|
|
|
19319
19556
|
usePreventOverscroll();
|
|
19320
19557
|
const { showThinking, scrollButtonState, scrollToTopPending } = useChatStore();
|
|
19321
19558
|
const { attachments, handleAttachmentsChange, clearAttachments } = useAttachments();
|
|
19322
|
-
const { scrollToBottom, scrollToLastUserMessage, pinLastUserMessage, scrollToProactive } = useAutoScroll({
|
|
19559
|
+
const { scrollToBottom, scrollToLastUserMessage, pinLastUserMessage, scrollToProactive, aterrizarAlFinal } = useAutoScroll({
|
|
19323
19560
|
messagesEndRef,
|
|
19324
19561
|
messagesContainerRef
|
|
19325
19562
|
});
|
|
@@ -19347,11 +19584,23 @@ const Chat = ({
|
|
|
19347
19584
|
}
|
|
19348
19585
|
}, [scrollToTopPending, scrollToLastUserMessage]);
|
|
19349
19586
|
useEffect(() => {
|
|
19587
|
+
if (messages[messages.length - 1]?.proactive) return;
|
|
19350
19588
|
const spacer = messagesEndRef.current;
|
|
19351
19589
|
if (spacer && spacer.style.minHeight && spacer.style.minHeight !== "0px") {
|
|
19352
19590
|
pinLastUserMessage();
|
|
19353
19591
|
}
|
|
19354
19592
|
}, [messages]);
|
|
19593
|
+
const yaAterrizo = useRef(false);
|
|
19594
|
+
useEffect(() => {
|
|
19595
|
+
if (messages.length === 0) {
|
|
19596
|
+
yaAterrizo.current = false;
|
|
19597
|
+
return;
|
|
19598
|
+
}
|
|
19599
|
+
if (yaAterrizo.current) return;
|
|
19600
|
+
yaAterrizo.current = true;
|
|
19601
|
+
if (messages[messages.length - 1]?.proactive) return;
|
|
19602
|
+
aterrizarAlFinal();
|
|
19603
|
+
}, [messages, aterrizarAlFinal]);
|
|
19355
19604
|
const lastProactiveIdRef = useRef(void 0);
|
|
19356
19605
|
useEffect(() => {
|
|
19357
19606
|
const last = messages[messages.length - 1];
|
|
@@ -19469,7 +19718,7 @@ const Chat = ({
|
|
|
19469
19718
|
{
|
|
19470
19719
|
className: `${COMPONENT_PREFIX$1}chat__scroll-to-bottom`,
|
|
19471
19720
|
variant: "circle",
|
|
19472
|
-
onClick: scrollToBottom,
|
|
19721
|
+
onClick: () => scrollToBottom(),
|
|
19473
19722
|
"aria-label": "Ir al final",
|
|
19474
19723
|
icon: /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
19475
19724
|
/* @__PURE__ */ jsx("path", { d: "M12 8v8", strokeWidth: "2", strokeLinecap: "round" }),
|