pixelon 0.4.2 → 0.5.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/dist/index.js CHANGED
@@ -12838,29 +12838,22 @@ const FormDrawer = ({
12838
12838
  onClose();
12839
12839
  methods.reset();
12840
12840
  }, [onClose, onBeforeClose, methods]);
12841
- const [invalidMsg, setInvalidMsg] = React.useState(null);
12842
- React.useEffect(() => {
12843
- if (!isOpen) setInvalidMsg(null);
12844
- }, [isOpen]);
12845
- React.useEffect(() => {
12846
- const sub = methods.watch(() => setInvalidMsg(null));
12847
- return () => sub.unsubscribe();
12848
- }, [methods.watch]);
12849
12841
  const handleFormSubmit = React.useCallback(
12850
12842
  async (data) => {
12851
- setInvalidMsg(null);
12852
12843
  await onSubmit(data);
12853
12844
  },
12854
12845
  [onSubmit]
12855
12846
  );
12856
12847
  const handleInvalid = React.useCallback(
12857
12848
  (formErrors) => {
12858
- const faltan = Object.keys(formErrors).map((name) => fields.find((f) => f.name === name)?.label ?? name);
12859
- setInvalidMsg(
12860
- faltan.length > 0 ? `Falta completar: ${faltan.join(", ")}.` : "Revisa los campos marcados."
12861
- );
12849
+ const primero = Object.keys(formErrors)[0];
12850
+ if (!primero) return;
12851
+ const nodo = document.querySelector(`[name="${CSS.escape(primero)}"]`);
12852
+ if (!nodo) return;
12853
+ nodo.scrollIntoView({ block: "center", behavior: "smooth" });
12854
+ window.setTimeout(() => nodo.focus({ preventScroll: true }), 300);
12862
12855
  },
12863
- [fields]
12856
+ []
12864
12857
  );
12865
12858
  const handleSubmitClick = methods.handleSubmit(handleFormSubmit, handleInvalid);
12866
12859
  const drawerTitle = title || DEFAULT_TITLES[mode];
@@ -12918,7 +12911,6 @@ const FormDrawer = ({
12918
12911
  ) }) }),
12919
12912
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${baseClass}__footer`, children: [
12920
12913
  footerExtra && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${baseClass}__footer-extra`, children: footerExtra }),
12921
- invalidMsg && mode !== "view" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: `${baseClass}__invalid`, role: "alert", children: invalidMsg }),
12922
12914
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${baseClass}__actions`, children: [
12923
12915
  /* @__PURE__ */ jsxRuntime.jsx(
12924
12916
  Button,
@@ -14133,7 +14125,7 @@ function getFormatter(key) {
14133
14125
  }
14134
14126
 
14135
14127
  const OPTIONS_LOAD_ERROR = "No se pudieron cargar las opciones. Int\xE9ntalo de nuevo.";
14136
- function unwrap$3(raw) {
14128
+ function unwrap$4(raw) {
14137
14129
  if (raw && typeof raw === "object" && "response" in raw) {
14138
14130
  return raw.response;
14139
14131
  }
@@ -14154,7 +14146,7 @@ function resolveBody(body, ctx) {
14154
14146
  }
14155
14147
  async function loadOptions(eventBus, ep, ctx) {
14156
14148
  const raw = await fetchViaEventBus(eventBus, ep.service, ep.url, ep.method, resolveBody(ep.body, ctx));
14157
- let data = unwrap$3(raw);
14149
+ let data = unwrap$4(raw);
14158
14150
  if (ep.response_path && data && typeof data === "object" && !Array.isArray(data)) {
14159
14151
  data = data[ep.response_path];
14160
14152
  }
@@ -14192,7 +14184,7 @@ const mensajeDeError = (e, respaldo) => {
14192
14184
  };
14193
14185
 
14194
14186
  const REFRESH_INTERVAL_MS = 2e4;
14195
- function unwrap$2(raw) {
14187
+ function unwrap$3(raw) {
14196
14188
  if (raw && typeof raw === "object" && "response" in raw) {
14197
14189
  return raw.response;
14198
14190
  }
@@ -14224,7 +14216,7 @@ function resolvePayload(map, ctx) {
14224
14216
  function useReactiveCrud(config, eventBus) {
14225
14217
  const pageSize = config.pageSize ?? 10;
14226
14218
  const [skip, setSkip] = React.useState(0);
14227
- const [filters, setFilters] = React.useState([]);
14219
+ const [filters, setFilters] = React.useState(config.initialFilters ?? []);
14228
14220
  const [state, setState] = React.useState({ rows: [], total: 0, loading: false, error: null });
14229
14221
  const [actionError, setActionError] = React.useState(null);
14230
14222
  const reqIdRef = React.useRef(0);
@@ -14241,7 +14233,7 @@ function useReactiveCrud(config, eventBus) {
14241
14233
  filters
14242
14234
  });
14243
14235
  assertOk(raw);
14244
- const data = unwrap$2(raw);
14236
+ const data = unwrap$3(raw);
14245
14237
  let rows = [];
14246
14238
  let total = 0;
14247
14239
  if (Array.isArray(data)) {
@@ -14445,6 +14437,20 @@ const FileField = ({
14445
14437
  ] });
14446
14438
  };
14447
14439
 
14440
+ const RESPALDO = { miles: ".", decimal: "," };
14441
+ function separadoresDelLocale(locale) {
14442
+ if (!locale) return RESPALDO;
14443
+ try {
14444
+ const partes = new Intl.NumberFormat(locale).formatToParts(123456789e-2);
14445
+ const miles = partes.find((p) => p.type === "group")?.value;
14446
+ const decimal = partes.find((p) => p.type === "decimal")?.value;
14447
+ if (!miles || !decimal) return RESPALDO;
14448
+ return { miles, decimal };
14449
+ } catch {
14450
+ return RESPALDO;
14451
+ }
14452
+ }
14453
+
14448
14454
  const CRUD_TO_FORM_MOLECULE = {
14449
14455
  INPUT: "INPUT",
14450
14456
  INPUT_NUMBER: "INPUT_NUMBER",
@@ -14501,6 +14507,8 @@ function ActionFormDrawer({ action, row, idField, eventBus, onExecute, onClose }
14501
14507
  alive = false;
14502
14508
  };
14503
14509
  }, [eventBus, action.key, normalFields]);
14510
+ const { locale } = useChatConfig();
14511
+ const separadores = React.useMemo(() => separadoresDelLocale(locale), [locale]);
14504
14512
  const fields = React.useMemo(
14505
14513
  () => normalFields.map((f) => ({
14506
14514
  name: f.name,
@@ -14511,9 +14519,19 @@ function ActionFormDrawer({ action, row, idField, eventBus, onExecute, onClose }
14511
14519
  options: f.options ?? optionsByField[f.name],
14512
14520
  // SPEC-106 T8 — `uiProps` llega tal cual a la molécula, así que el desplegable dice lo
14513
14521
  // que pasó de verdad en vez del «no hay» de siempre. Sin error, se queda el de siempre.
14514
- ...optionsErrorByField[f.name] ? { uiProps: { notFoundContent: optionsErrorByField[f.name] } } : {}
14522
+ ...optionsErrorByField[f.name] ? { uiProps: { notFoundContent: optionsErrorByField[f.name] } } : {},
14523
+ // SPEC-128 — el dinero se escribe como se escribe AQUÍ.
14524
+ //
14525
+ // Los campos de moneda caían a `thousandSeparator ?? ','`, un valor por defecto en formato
14526
+ // inglés: al teclear 180000 el campo mostraba «180,000» mientras la tarjeta que recibe el
14527
+ // cliente decía «$ 180.000,00 COP». El mismo importe escrito de una manera y leído de otra.
14528
+ //
14529
+ // Se deriva del locale que el host ya declara en la config del chat, no de un valor fijo ni
14530
+ // del navegador: `navigator.language` puede ser `es-419` —español, pero con coma para los
14531
+ // miles— y entonces el resultado dependería del portátil de cada quien.
14532
+ ...f.moleculeType === "INPUT_CURRENCY" ? { uiProps: { thousandSeparator: separadores.miles, decimalSeparator: separadores.decimal } } : {}
14515
14533
  })),
14516
- [normalFields, optionsByField, optionsErrorByField]
14534
+ [normalFields, optionsByField, optionsErrorByField, separadores]
14517
14535
  );
14518
14536
  const validationSchema = React.useMemo(() => buildCrudYupSchema(normalFields), [normalFields]);
14519
14537
  const uploadOne = async (f, file, idx) => {
@@ -15172,6 +15190,95 @@ const FileViewer = ({
15172
15190
  ] });
15173
15191
  };
15174
15192
 
15193
+ function unwrap$2(raw) {
15194
+ if (raw && typeof raw === "object" && "response" in raw) {
15195
+ return raw.response;
15196
+ }
15197
+ return raw;
15198
+ }
15199
+ function hora(iso) {
15200
+ if (!iso) return "";
15201
+ const d = new Date(iso);
15202
+ if (Number.isNaN(d.getTime())) return "";
15203
+ return d.toLocaleString(void 0, { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" });
15204
+ }
15205
+ function TranscriptSection({ transcript, values, eventBus }) {
15206
+ const chatId = values[transcript.chatIdField];
15207
+ const id = chatId == null || chatId === "" ? "" : String(chatId);
15208
+ const [datos, setDatos] = React.useState(null);
15209
+ const [cargando, setCargando] = React.useState(true);
15210
+ const [fallo, setFallo] = React.useState(false);
15211
+ React.useEffect(() => {
15212
+ if (!eventBus || !id) {
15213
+ setCargando(false);
15214
+ return;
15215
+ }
15216
+ let vivo = true;
15217
+ setCargando(true);
15218
+ setFallo(false);
15219
+ void (async () => {
15220
+ try {
15221
+ const raw = await fetchViaEventBus(
15222
+ eventBus,
15223
+ transcript.get.service,
15224
+ transcript.get.url,
15225
+ transcript.get.method ?? "POST",
15226
+ { [transcript.param ?? "chat_id"]: id }
15227
+ );
15228
+ if (vivo) {
15229
+ setDatos(unwrap$2(raw) ?? {});
15230
+ setCargando(false);
15231
+ }
15232
+ } catch {
15233
+ if (vivo) {
15234
+ setFallo(true);
15235
+ setCargando(false);
15236
+ }
15237
+ }
15238
+ })();
15239
+ return () => {
15240
+ vivo = false;
15241
+ };
15242
+ }, [eventBus, id, transcript]);
15243
+ if (!id) return null;
15244
+ const lineas = datos?.lines ?? [];
15245
+ const dijoAlgo = (datos?.from_user ?? 0) > 0;
15246
+ const nombres = {
15247
+ user: transcript.labels?.user ?? "Cliente",
15248
+ assistant: transcript.labels?.assistant ?? "Asistente"
15249
+ };
15250
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-crud-transcript", children: [
15251
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "px-chat-crud-transcript__title", children: transcript.title ?? "Conversaci\xF3n" }),
15252
+ cargando && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-transcript__note", children: "Cargando\u2026" }),
15253
+ !cargando && fallo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-transcript__note", children: "No se pudo cargar la conversaci\xF3n." }),
15254
+ !cargando && !fallo && !dijoAlgo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-transcript__note", children: transcript.silentText ?? "Todav\xEDa no ha escrito nada." }),
15255
+ !cargando && !fallo && lineas.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "px-chat-crud-transcript__lines", children: lineas.map((l, i) => /* @__PURE__ */ jsxRuntime.jsxs(
15256
+ "li",
15257
+ {
15258
+ className: [
15259
+ "px-chat-crud-transcript__line",
15260
+ l.rol === "user" ? "is-user" : "is-assistant"
15261
+ ].join(" "),
15262
+ children: [
15263
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-chat-crud-transcript__who", children: [
15264
+ l.rol === "user" ? nombres.user : nombres.assistant,
15265
+ hora(l.created_date) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-crud-transcript__when", children: hora(l.created_date) })
15266
+ ] }),
15267
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-crud-transcript__text", children: l.text })
15268
+ ]
15269
+ },
15270
+ `${i}-${l.created_date ?? ""}`
15271
+ )) }),
15272
+ !cargando && !fallo && (datos?.total ?? 0) > lineas.length && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-crud-transcript__note", children: [
15273
+ "Se muestran los \xFAltimos ",
15274
+ lineas.length,
15275
+ " de ",
15276
+ datos?.total,
15277
+ " mensajes."
15278
+ ] })
15279
+ ] });
15280
+ }
15281
+
15175
15282
  function unwrap$1(raw) {
15176
15283
  if (raw && typeof raw === "object" && "response" in raw) {
15177
15284
  return raw.response;
@@ -15300,6 +15407,7 @@ function DetailDrawer({ row, config, eventBus, onClose, loadedOptions }) {
15300
15407
  ] })
15301
15408
  ] }, f.name);
15302
15409
  }) }),
15410
+ config.transcript && /* @__PURE__ */ jsxRuntime.jsx(TranscriptSection, { transcript: config.transcript, values, eventBus }),
15303
15411
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-reactive-crud-detail__photos", children: [
15304
15412
  /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "px-chat-reactive-crud-detail__photos-title", children: "Fotos y documentos" }),
15305
15413
  loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-reactive-crud-detail__empty", children: "Cargando\u2026" }) : files.length ? /* @__PURE__ */ jsxRuntime.jsx(FileViewer, { files }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-reactive-crud-detail__empty", children: "Este servicio no tiene archivos." })
@@ -16006,6 +16114,10 @@ function matchesVisibleWhen(a, row) {
16006
16114
  if (!a.visibleWhen) return true;
16007
16115
  return cumplenTodas(a.visibleWhen, row);
16008
16116
  }
16117
+ function laQueTocaAhora(actions, row) {
16118
+ const candidatas = actions.filter((a) => a.ahoraToca && cumplenTodas(a.ahoraToca, row));
16119
+ return candidatas.length === 1 ? candidatas[0] : void 0;
16120
+ }
16009
16121
  function condicionesDeshabilitado(a) {
16010
16122
  if (!a.disabledWhen) return [];
16011
16123
  return Array.isArray(a.disabledWhen) ? a.disabledWhen : [a.disabledWhen];
@@ -16136,6 +16248,7 @@ function ReactiveCrud({ config, eventBus, autoOpenCreate, prefill, onCreated, on
16136
16248
  total: crud.total,
16137
16249
  loading: crud.loading,
16138
16250
  filters: { skip: crud.skip, limit: crud.pageSize, all_data: false, filters: crud.filters },
16251
+ defaultFilters: config.initialFilters,
16139
16252
  tableScroll: config.tableScroll,
16140
16253
  layout: config.layout,
16141
16254
  cardsBelow: config.cardsBelow,
@@ -16325,7 +16438,7 @@ function DeadlineCountdown({ deadline, label, expiredLabel }) {
16325
16438
  }
16326
16439
 
16327
16440
  function ReactiveCrudItemChat({ component, eventBus }) {
16328
- const { resolveModuleConfig } = useChatConfig();
16441
+ const { resolveModuleConfig, resolveMomento } = useChatConfig();
16329
16442
  const props = component.props ?? {};
16330
16443
  const top = component;
16331
16444
  const pick = (key) => props[key] !== void 0 ? props[key] : top[key];
@@ -16333,7 +16446,11 @@ function ReactiveCrudItemChat({ component, eventBus }) {
16333
16446
  const recordId = pick("record_id") ?? pick("recordId") ?? pick("id") ?? pick("service_id");
16334
16447
  const recordLabel = pick("record_label") ?? pick("label");
16335
16448
  const allowedRaw = pick("actions");
16336
- const allowed = Array.isArray(allowedRaw) && allowedRaw.length > 0 ? allowedRaw : void 0;
16449
+ const allowedLegacy = Array.isArray(allowedRaw) && allowedRaw.length > 0 ? allowedRaw : void 0;
16450
+ const momento = pick("momento");
16451
+ const delMomento = momento && resolveMomento ? resolveMomento(momento) : void 0;
16452
+ const allowed = delMomento ?? allowedLegacy;
16453
+ const sinAcciones = delMomento?.length === 0;
16337
16454
  const config = typeof moduleKey === "string" && resolveModuleConfig ? resolveModuleConfig(moduleKey) : void 0;
16338
16455
  if (!config || recordId == null || recordId === "") return null;
16339
16456
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -16343,11 +16460,12 @@ function ReactiveCrudItemChat({ component, eventBus }) {
16343
16460
  recordId: String(recordId),
16344
16461
  recordLabel,
16345
16462
  allowed: Array.isArray(allowed) ? allowed.map(String) : void 0,
16463
+ sinAcciones,
16346
16464
  eventBus
16347
16465
  }
16348
16466
  );
16349
16467
  }
16350
- function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus }) {
16468
+ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, sinAcciones, eventBus }) {
16351
16469
  const { chatIsForeign } = useChatConfig();
16352
16470
  const crud = useReactiveCrud(config, eventBus);
16353
16471
  const [activeAction, setActiveAction] = React.useState(null);
@@ -16396,9 +16514,26 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
16396
16514
  }, [recordId]);
16397
16515
  const gastada = !esUltima;
16398
16516
  const actions = React.useMemo(() => {
16399
- if (!row || gastada) return [];
16400
- return (config.rowActions ?? []).filter((a) => !allowed || allowed.includes(a.key)).filter((a) => matchesVisibleWhen(a, row));
16517
+ if (!row) return [];
16518
+ if (gastada) {
16519
+ const hechas = (config.rowActions ?? []).filter((a) => !allowed || allowed.includes(a.key)).filter((a) => matchesDoneWhen(a, row));
16520
+ const vistas = /* @__PURE__ */ new Set();
16521
+ return hechas.filter((a) => {
16522
+ const linea = a.doneLabel ?? a.label;
16523
+ if (vistas.has(linea)) return false;
16524
+ vistas.add(linea);
16525
+ return true;
16526
+ });
16527
+ }
16528
+ const visibles = (config.rowActions ?? []).filter((a) => !allowed || allowed.includes(a.key)).filter((a) => matchesVisibleWhen(a, row));
16529
+ const toca = laQueTocaAhora(visibles, row);
16530
+ if (!toca) return visibles;
16531
+ return [toca, ...visibles.filter((a) => a.key !== toca.key)];
16401
16532
  }, [config.rowActions, row, allowed, gastada]);
16533
+ const claveQueToca = React.useMemo(
16534
+ () => row && !gastada ? laQueTocaAhora(actions, row)?.key : void 0,
16535
+ [actions, row, gastada]
16536
+ );
16402
16537
  const title = recordLabel ?? config.title ?? "Servicio";
16403
16538
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-crud-item", ref: raiz, "data-panel-record": recordId, children: [
16404
16539
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-item__header", children: title }),
@@ -16409,7 +16544,7 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
16409
16544
  crud.loading && !row && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-item__hint", children: "Cargando\u2026" }),
16410
16545
  !crud.loading && !row && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-item__hint", children: "No se encontr\xF3 el servicio." }),
16411
16546
  gastada && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-item__hint", children: "Este paso ya se gestion\xF3. Contin\xFAa en el mensaje m\xE1s reciente." }),
16412
- row && config.deadline && typeof row[config.deadline.field] === "string" && /* @__PURE__ */ jsxRuntime.jsx(
16547
+ row && !gastada && config.deadline && typeof row[config.deadline.field] === "string" && /* @__PURE__ */ jsxRuntime.jsx(
16413
16548
  DeadlineCountdown,
16414
16549
  {
16415
16550
  deadline: row[config.deadline.field],
@@ -16420,7 +16555,7 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
16420
16555
  row && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-crud-item__actions", children: [
16421
16556
  config.detail && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "px-chat-crud-item__action", onClick: () => setDetailRow(row), children: "Ver" }),
16422
16557
  actions.map((a) => {
16423
- const disabled = Boolean(chatIsForeign) || matchesDisabledWhen(a, row);
16558
+ const disabled = gastada || Boolean(chatIsForeign) || matchesDisabledWhen(a, row);
16424
16559
  const motivo = chatIsForeign ? "Esta conversaci\xF3n es de otra persona" : disabledReasonFor(a, row);
16425
16560
  const done = matchesDoneWhen(a, row);
16426
16561
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -16430,13 +16565,18 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
16430
16565
  className: [
16431
16566
  "px-chat-crud-item__action",
16432
16567
  a.intent === "danger" ? "is-danger" : "",
16433
- disabled && motivo ? "is-con-motivo" : ""
16568
+ disabled && motivo ? "is-con-motivo" : "",
16569
+ // SPEC-129 T6 — lo hecho se pinta como un acta, no como un botón apagado.
16570
+ gastada ? "is-hecho" : "",
16571
+ // SPEC-127 T4 — señalada, pero solo si de verdad se puede pulsar: destacar una
16572
+ // acción apagada sería dirigir a alguien a una puerta cerrada.
16573
+ a.key === claveQueToca && !disabled ? "is-ahora" : ""
16434
16574
  ].filter(Boolean).join(" "),
16435
16575
  disabled,
16436
16576
  onClick: () => a.form ? setActiveAction({ action: a, row }) : crud.runEndpoints(a.endpoints ?? [a.endpoint], { ...row, id: row[config.idField] }),
16437
16577
  children: [
16438
16578
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-chat-crud-item__action-texts", children: [
16439
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: a.label }),
16579
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: gastada && a.doneLabel ? a.doneLabel : a.label }),
16440
16580
  disabled && motivo && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-crud-item__action-reason", children: motivo })
16441
16581
  ] }),
16442
16582
  done && /* @__PURE__ */ jsxRuntime.jsx(Icon$1, { type: "check-circle", width: 18, height: 18, className: "px-chat-crud-item__done", "aria-label": "Hecho" })
@@ -16445,7 +16585,7 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
16445
16585
  a.key
16446
16586
  );
16447
16587
  }),
16448
- actions.length === 0 && !config.detail && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-item__hint", children: "Sin acciones disponibles." })
16588
+ actions.length === 0 && !config.detail && !sinAcciones && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-item__hint", children: "Sin acciones disponibles." })
16449
16589
  ] }),
16450
16590
  activeAction && /* @__PURE__ */ jsxRuntime.jsx(
16451
16591
  ActionFormDrawer,
@@ -16665,7 +16805,7 @@ const ServicesMap = ({ config, eventBus, apiKey, filter, fill }) => {
16665
16805
  ),
16666
16806
  simulated > 0 && ` \xB7 ${config.live?.labels?.simulatedText ?? "incluye posiciones simuladas"}`
16667
16807
  ] }),
16668
- missing > 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: `${CLASS$6}__note`, children: (labels.missingText ?? "{n} sin ubicaci\xF3n registrada").replace("{n}", String(missing)) }),
16808
+ missing > 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: `${CLASS$6}__note`, children: ((missing === 1 ? labels.missingTextOne : void 0) ?? labels.missingText ?? "{n} sin ubicaci\xF3n registrada").replace("{n}", String(missing)) }),
16669
16809
  outside > 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: `${CLASS$6}__note`, children: (labels.outsideText ?? "{n} fuera de la zona (aleja el mapa para verlos)").replace(
16670
16810
  "{n}",
16671
16811
  String(outside)