pixelon 0.3.77 → 0.3.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/components/atoms/lumi/FieldValue/FieldValue.d.ts +15 -0
  2. package/dist/components/atoms/lumi/FieldValue/index.d.ts +2 -0
  3. package/dist/components/atoms/lumi/FieldValue/types.d.ts +10 -0
  4. package/dist/components/atoms/lumi/Icon/Icon.d.ts +1 -0
  5. package/dist/components/molecules/chat/StructuredContent/components/ReactiveCrud/ChatLinkCell.d.ts +20 -0
  6. package/dist/components/molecules/chat/StructuredContent/components/ReactiveCrud/optionsLoader.d.ts +2 -1
  7. package/dist/components/molecules/chat/StructuredContent/components/ReactiveCrud/types.d.ts +6 -0
  8. package/dist/components/molecules/lumi/Drawer/Drawer.d.ts +8 -0
  9. package/dist/components/molecules/lumi/RecordCard/RecordCard.d.ts +14 -0
  10. package/dist/components/molecules/lumi/RecordCard/index.d.ts +2 -0
  11. package/dist/components/molecules/lumi/RecordCard/types.d.ts +16 -0
  12. package/dist/components/molecules/lumi/ScrollToEnd/ScrollToEnd.d.ts +21 -0
  13. package/dist/components/molecules/lumi/ScrollToEnd/index.d.ts +2 -0
  14. package/dist/components/templates/lumi/Crud/CardList.d.ts +34 -0
  15. package/dist/components/templates/lumi/Crud/Crud.d.ts +1 -1
  16. package/dist/components/templates/lumi/Crud/ScrollToEnd.d.ts +18 -0
  17. package/dist/components/templates/lumi/Crud/helpers/fieldValue.d.ts +12 -0
  18. package/dist/components/templates/lumi/Crud/types.d.ts +25 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.esm.js +566 -276
  21. package/dist/index.js +567 -275
  22. package/dist/styles.css +1 -1
  23. package/dist/styles.css.map +1 -1
  24. package/package.json +1 -1
  25. package/src/styles/index.scss +4 -0
package/dist/index.js CHANGED
@@ -4008,13 +4008,13 @@ function resolveVariable(template, formValues, fieldStates) {
4008
4008
  }
4009
4009
  return "";
4010
4010
  }
4011
- function resolveBody(obj, formValues, fieldStates) {
4011
+ function resolveBody$1(obj, formValues, fieldStates) {
4012
4012
  if (typeof obj === "string") return resolveVariable(obj, formValues, fieldStates);
4013
- if (Array.isArray(obj)) return obj.map((item) => resolveBody(item, formValues, fieldStates));
4013
+ if (Array.isArray(obj)) return obj.map((item) => resolveBody$1(item, formValues, fieldStates));
4014
4014
  if (obj !== null && typeof obj === "object") {
4015
4015
  const resolved = {};
4016
4016
  for (const [key, value] of Object.entries(obj)) {
4017
- resolved[key] = resolveBody(value, formValues, fieldStates);
4017
+ resolved[key] = resolveBody$1(value, formValues, fieldStates);
4018
4018
  }
4019
4019
  return resolved;
4020
4020
  }
@@ -4064,7 +4064,7 @@ const CostSummaryField = ({
4064
4064
  return;
4065
4065
  }
4066
4066
  fetchedRef.current = true;
4067
- const body = resolveBody(field.endpoint.body || {}, values, fieldStates);
4067
+ const body = resolveBody$1(field.endpoint.body || {}, values, fieldStates);
4068
4068
  setLoading(true);
4069
4069
  setError("");
4070
4070
  fetchViaEventBus(eventBus, field.endpoint.service, field.endpoint.url, field.endpoint.method, body).then((data) => {
@@ -4518,7 +4518,7 @@ function useReactiveEndpoints({ fields, fieldStates, setFieldState, eventBus, ti
4518
4518
  setFieldState(field.name, { loading: true, error: null });
4519
4519
  const startTime = Date.now();
4520
4520
  try {
4521
- const resolvedBody = field.endpoint.body ? resolveBody(field.endpoint.body, formValues, fieldStates) : void 0;
4521
+ const resolvedBody = field.endpoint.body ? resolveBody$1(field.endpoint.body, formValues, fieldStates) : void 0;
4522
4522
  const params = field.endpoint.params ? resolveParams(field.endpoint.params, formValues, fieldStates) : {};
4523
4523
  const requestBody = resolvedBody ?? (Object.keys(params).length > 0 ? params : void 0);
4524
4524
  if (eventBus) {
@@ -4793,7 +4793,7 @@ function useReactiveForm(props) {
4793
4793
  const handleSubmit = async () => {
4794
4794
  if (isSubmitting) return;
4795
4795
  setIsSubmitting(true);
4796
- const resolvedPayload = resolveBody(submit.payload_map, values, fieldStates);
4796
+ const resolvedPayload = resolveBody$1(submit.payload_map, values, fieldStates);
4797
4797
  if (eventBus) {
4798
4798
  try {
4799
4799
  const data = await fetchViaEventBus(
@@ -5753,6 +5753,7 @@ const LUMI_ICON_TYPES = {
5753
5753
  MENU: "menu",
5754
5754
  ARROW_UP: "arrow-up",
5755
5755
  ARROW_RIGHT: "arrow-right",
5756
+ ARROW_LEFT: "arrow-left",
5756
5757
  // Iconos para Servicios
5757
5758
  BUILDING: "building",
5758
5759
  MAP_PIN: "map-pin",
@@ -6424,6 +6425,24 @@ const ArrowRightIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
6424
6425
  ]
6425
6426
  }
6426
6427
  );
6428
+ const ArrowLeftIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
6429
+ "svg",
6430
+ {
6431
+ width: "16",
6432
+ height: "16",
6433
+ viewBox: "0 0 24 24",
6434
+ fill: "none",
6435
+ stroke: "currentColor",
6436
+ strokeWidth: "2",
6437
+ strokeLinecap: "round",
6438
+ strokeLinejoin: "round",
6439
+ ...props,
6440
+ children: [
6441
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "19", y1: "12", x2: "5", y2: "12" }),
6442
+ /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "12 19 5 12 12 5" })
6443
+ ]
6444
+ }
6445
+ );
6427
6446
  const BuildingIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
6428
6447
  "svg",
6429
6448
  {
@@ -6565,6 +6584,7 @@ const lumiIconMap = {
6565
6584
  [LUMI_ICON_TYPES.MENU]: MenuIcon,
6566
6585
  [LUMI_ICON_TYPES.ARROW_UP]: ArrowUpIcon,
6567
6586
  [LUMI_ICON_TYPES.ARROW_RIGHT]: ArrowRightIcon,
6587
+ [LUMI_ICON_TYPES.ARROW_LEFT]: ArrowLeftIcon,
6568
6588
  // Iconos para Servicios
6569
6589
  [LUMI_ICON_TYPES.BUILDING]: BuildingIcon,
6570
6590
  [LUMI_ICON_TYPES.MAP_PIN]: MapPinIcon,
@@ -10886,6 +10906,7 @@ const Drawer = ({
10886
10906
  size = DRAWER_SIZES.RESPONSIVE,
10887
10907
  fixedSize = 400,
10888
10908
  responsivePercentage = 80,
10909
+ mobileSheet = false,
10889
10910
  title,
10890
10911
  headerExtra,
10891
10912
  showCloseButton = true,
@@ -10968,11 +10989,13 @@ const Drawer = ({
10968
10989
  `${COMPONENT_PREFIX}drawer--${size}`,
10969
10990
  isOpen && `${COMPONENT_PREFIX}drawer--open`,
10970
10991
  isMobile && `${COMPONENT_PREFIX}drawer--mobile`,
10992
+ mobileSheet && `${COMPONENT_PREFIX}drawer--sheet`,
10971
10993
  className
10972
10994
  ].filter(Boolean).join(" ");
10973
10995
  const overlayClasses = [
10974
10996
  `${COMPONENT_PREFIX}drawer-overlay`,
10975
- isOpen && showOverlay && `${COMPONENT_PREFIX}drawer-overlay--visible`
10997
+ isOpen && showOverlay && `${COMPONENT_PREFIX}drawer-overlay--visible`,
10998
+ mobileSheet && `${COMPONENT_PREFIX}drawer-overlay--sheet`
10976
10999
  ].filter(Boolean).join(" ");
10977
11000
  const getDrawerStyles = () => {
10978
11001
  if (isMobile) {
@@ -11036,6 +11059,7 @@ const Drawer = ({
11036
11059
  variant: "ghost",
11037
11060
  onClick: onClose,
11038
11061
  "aria-label": "Cerrar drawer",
11062
+ className: `${COMPONENT_PREFIX}drawer__close`,
11039
11063
  icon: /* @__PURE__ */ jsxRuntime.jsx(Icon, { type: LUMI_ICON_TYPES.CLOSE, size: "medium" })
11040
11064
  }
11041
11065
  )
@@ -13079,36 +13103,45 @@ const Badge = ({
13079
13103
  };
13080
13104
  Badge.displayName = "LumiBadge";
13081
13105
 
13106
+ function fieldOptions(field) {
13107
+ return field.uiProps?.options;
13108
+ }
13109
+ function renderFieldValue(field, row) {
13110
+ const value = row[field.key];
13111
+ const options = fieldOptions(field);
13112
+ const opt = options?.find((o) => String(o.value) === String(value));
13113
+ if (field.moleculeType === "BADGE") {
13114
+ if (value == null || value === "") return null;
13115
+ return React.createElement(Badge, { color: opt?.color, children: opt?.label ?? String(value) });
13116
+ }
13117
+ if (field.moleculeType === "INPUT_CURRENCY") {
13118
+ if (value == null || value === "") return null;
13119
+ const cp = field.uiProps ?? {};
13120
+ return React.createElement(CurrencyDisplay, {
13121
+ value,
13122
+ showCode: cp.showCode ?? true,
13123
+ showSymbol: cp.showSymbol ?? true,
13124
+ currency: cp.currency ?? { decimalPlaces: 0 },
13125
+ align: field.align ?? "right",
13126
+ size: cp.size ?? "md"
13127
+ });
13128
+ }
13129
+ if (field.formatValue) return field.formatValue(value, row);
13130
+ if (opt) return opt.label;
13131
+ return value;
13132
+ }
13133
+ function hasFieldValue(field, row) {
13134
+ const value = row[field.key];
13135
+ return value != null && value !== "";
13136
+ }
13137
+
13082
13138
  function mapFieldsToTableColumns(fields) {
13083
13139
  return fields.filter((field) => field.showInTable !== false).map((field) => {
13084
13140
  let width = field.columnWidth;
13085
13141
  if (field.fixed && typeof field.columnWidth === "number") {
13086
13142
  width = `${field.columnWidth}px`;
13087
13143
  }
13088
- const options = field.uiProps?.options;
13089
- const build = (row) => {
13090
- const value = row[field.key];
13091
- const opt = options?.find((o) => String(o.value) === String(value));
13092
- if (field.moleculeType === "BADGE") {
13093
- if (value == null || value === "") return null;
13094
- return React.createElement(Badge, { color: opt?.color, children: opt?.label ?? String(value) });
13095
- }
13096
- if (field.moleculeType === "INPUT_CURRENCY") {
13097
- if (value == null || value === "") return null;
13098
- const cp = field.uiProps ?? {};
13099
- return React.createElement(CurrencyDisplay, {
13100
- value,
13101
- showCode: cp.showCode ?? true,
13102
- showSymbol: cp.showSymbol ?? true,
13103
- currency: cp.currency ?? { decimalPlaces: 0 },
13104
- align: field.align ?? "right",
13105
- size: cp.size ?? "md"
13106
- });
13107
- }
13108
- if (field.formatValue) return field.formatValue(value, row);
13109
- if (opt) return opt.label;
13110
- return value;
13111
- };
13144
+ const build = (row) => renderFieldValue(field, row);
13112
13145
  return {
13113
13146
  key: field.key,
13114
13147
  title: field.label,
@@ -13139,10 +13172,201 @@ function mapFieldsToFormFields(fields, mode) {
13139
13172
  }));
13140
13173
  }
13141
13174
 
13175
+ const CLASS$e = `${COMPONENT_PREFIX}field-value`;
13176
+ const FieldValue = ({
13177
+ label,
13178
+ children,
13179
+ layout = "inline",
13180
+ className
13181
+ }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: [CLASS$e, `${CLASS$e}--${layout}`, className].filter(Boolean).join(" "), children: [
13182
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${CLASS$e}__label`, children: label }),
13183
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${CLASS$e}__value`, children })
13184
+ ] });
13185
+ FieldValue.displayName = "FieldValue";
13186
+
13187
+ const CLASS$d = `${COMPONENT_PREFIX}record-card`;
13188
+ const RecordCard = ({
13189
+ title,
13190
+ summary,
13191
+ children,
13192
+ actions,
13193
+ open = false,
13194
+ onToggle,
13195
+ ariaLabel,
13196
+ className
13197
+ }) => {
13198
+ const cuerpoId = React.useId();
13199
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: [CLASS$d, open ? `${CLASS$d}--open` : "", className].filter(Boolean).join(" "), children: [
13200
+ /* @__PURE__ */ jsxRuntime.jsxs(
13201
+ "button",
13202
+ {
13203
+ type: "button",
13204
+ className: `${CLASS$d}__header`,
13205
+ "aria-expanded": open,
13206
+ "aria-controls": cuerpoId,
13207
+ "aria-label": ariaLabel,
13208
+ onClick: onToggle,
13209
+ children: [
13210
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `${CLASS$d}__heading`, children: [
13211
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${CLASS$d}__title`, children: title }),
13212
+ summary && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${CLASS$d}__summary`, children: summary })
13213
+ ] }),
13214
+ /* @__PURE__ */ jsxRuntime.jsx(
13215
+ Icon,
13216
+ {
13217
+ type: open ? "chevron-up" : "chevron-down",
13218
+ width: 18,
13219
+ height: 18,
13220
+ className: `${CLASS$d}__chevron`,
13221
+ "aria-hidden": true
13222
+ }
13223
+ )
13224
+ ]
13225
+ }
13226
+ ),
13227
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${CLASS$d}__body`, id: cuerpoId, children: [
13228
+ children,
13229
+ actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${CLASS$d}__actions`, children: actions })
13230
+ ] })
13231
+ ] });
13232
+ };
13233
+ RecordCard.displayName = "RecordCard";
13234
+
13235
+ const CLASS$c = `${COMPONENT_PREFIX}crud-cards`;
13236
+ function summaryFields(fields) {
13237
+ const visibles = fields.filter((f) => f.showInTable !== false);
13238
+ const marcados = visibles.filter((f) => f.summary);
13239
+ return marcados.length > 0 ? marcados : visibles.slice(0, 3);
13240
+ }
13241
+ function CardList({
13242
+ fields,
13243
+ rows,
13244
+ rowKey,
13245
+ buildOptions,
13246
+ loading = false,
13247
+ emptyText = "No hay registros"
13248
+ }) {
13249
+ const [abierta, setAbierta] = React.useState(null);
13250
+ const resumen = summaryFields(fields);
13251
+ const claveResumen = new Set(resumen.map((f) => f.key));
13252
+ const cuerpo = fields.filter((f) => f.showInTable !== false && !claveResumen.has(f.key));
13253
+ if (loading && rows.length === 0) {
13254
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${CLASS$c}__estado`, children: "Cargando..." });
13255
+ }
13256
+ if (rows.length === 0) {
13257
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${CLASS$c}__estado`, children: emptyText });
13258
+ }
13259
+ const [principal, ...secundarios] = resumen;
13260
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: CLASS$c, children: rows.map((row) => {
13261
+ const key = rowKey(row);
13262
+ const opciones = buildOptions?.(row) ?? [];
13263
+ const titulo = principal ? renderFieldValue(principal, row) : key;
13264
+ return /* @__PURE__ */ jsxRuntime.jsx(
13265
+ RecordCard,
13266
+ {
13267
+ open: abierta === key,
13268
+ onToggle: () => setAbierta((actual) => actual === key ? null : key),
13269
+ ariaLabel: principal ? `${principal.label}: ${String(row[principal.key] ?? "")}` : void 0,
13270
+ title: titulo,
13271
+ summary: secundarios.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: secundarios.map((f) => /* @__PURE__ */ jsxRuntime.jsx(React.Fragment, { children: renderFieldValue(f, row) }, f.key)) }) : void 0,
13272
+ actions: opciones.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: opciones.map((o) => /* @__PURE__ */ jsxRuntime.jsxs(
13273
+ "button",
13274
+ {
13275
+ type: "button",
13276
+ className: [
13277
+ `${CLASS$c}__accion`,
13278
+ o.disabled && o.disabledReason ? "is-con-motivo" : ""
13279
+ ].filter(Boolean).join(" "),
13280
+ disabled: o.disabled,
13281
+ onClick: () => void o.onClick(),
13282
+ children: [
13283
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: o.label }),
13284
+ o.disabled && o.disabledReason && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${CLASS$c}__motivo`, children: o.disabledReason })
13285
+ ]
13286
+ },
13287
+ o.label
13288
+ )) }) : void 0,
13289
+ children: cuerpo.map(
13290
+ (f) => hasFieldValue(f, row) ? /* @__PURE__ */ jsxRuntime.jsx(FieldValue, { label: f.label, children: renderFieldValue(f, row) }, f.key) : null
13291
+ )
13292
+ },
13293
+ key
13294
+ );
13295
+ }) });
13296
+ }
13297
+ CardList.displayName = "CardList";
13298
+
13299
+ const CLASS$b = `${COMPONENT_PREFIX}crud-scroll-end`;
13300
+ const MARGEN = 24;
13301
+ const SELECTOR_CRUD = `.${COMPONENT_PREFIX}table-container, .${COMPONENT_PREFIX}crud-cards`;
13302
+ const ScrollToEnd = ({
13303
+ hostRef,
13304
+ selector = SELECTOR_CRUD,
13305
+ dep,
13306
+ label = "Ir al final de la lista"
13307
+ }) => {
13308
+ const [visible, setVisible] = React.useState(false);
13309
+ const scrollerRef = React.useRef(null);
13310
+ React.useEffect(() => {
13311
+ const host = hostRef.current;
13312
+ if (!host) return;
13313
+ const scroller = host.querySelector(selector) ?? host;
13314
+ scrollerRef.current = scroller;
13315
+ if (!scroller) {
13316
+ setVisible(false);
13317
+ return;
13318
+ }
13319
+ const revisar = () => {
13320
+ const s = scrollerRef.current;
13321
+ if (!s) return;
13322
+ setVisible(s.scrollHeight - s.clientHeight - s.scrollTop > MARGEN);
13323
+ };
13324
+ revisar();
13325
+ scroller.addEventListener("scroll", revisar, { passive: true });
13326
+ let observador;
13327
+ if (typeof ResizeObserver !== "undefined") {
13328
+ observador = new ResizeObserver(revisar);
13329
+ observador.observe(scroller);
13330
+ if (scroller.firstElementChild) observador.observe(scroller.firstElementChild);
13331
+ }
13332
+ return () => {
13333
+ scroller.removeEventListener("scroll", revisar);
13334
+ observador?.disconnect();
13335
+ };
13336
+ }, [hostRef, selector, dep]);
13337
+ const alFinal = React.useCallback(() => {
13338
+ const s = scrollerRef.current;
13339
+ if (!s) return;
13340
+ s.scrollTo({ top: s.scrollHeight, behavior: "smooth" });
13341
+ }, []);
13342
+ if (!visible) return null;
13343
+ return /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: CLASS$b, onClick: alFinal, "aria-label": label, title: label, children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { type: "chevron-down", width: 18, height: 18, "aria-hidden": true }) });
13344
+ };
13345
+ ScrollToEnd.displayName = "ScrollToEnd";
13346
+
13142
13347
  function calculateTotalPages(totalRecords, limit) {
13143
13348
  return Math.ceil(totalRecords / limit);
13144
13349
  }
13145
13350
 
13351
+ const useAnchoPropio = () => {
13352
+ const ref = React.useRef(null);
13353
+ const [ancho, setAncho] = React.useState(null);
13354
+ React.useEffect(() => {
13355
+ const nodo = ref.current;
13356
+ if (!nodo) return;
13357
+ if (typeof ResizeObserver === "undefined") {
13358
+ setAncho(nodo.getBoundingClientRect().width);
13359
+ return;
13360
+ }
13361
+ const observador = new ResizeObserver((entradas) => {
13362
+ const w = entradas[0]?.contentRect.width;
13363
+ if (typeof w === "number") setAncho(w);
13364
+ });
13365
+ observador.observe(nodo);
13366
+ return () => observador.disconnect();
13367
+ }, []);
13368
+ return [ref, ancho];
13369
+ };
13146
13370
  const useIsMobile = () => {
13147
13371
  const [isMobile, setIsMobile] = React.useState(false);
13148
13372
  React.useEffect(() => {
@@ -13182,6 +13406,8 @@ function Crud({
13182
13406
  defaultFilters,
13183
13407
  tableHeight,
13184
13408
  tableScroll,
13409
+ layout = "auto",
13410
+ cardsBelow = 768,
13185
13411
  hidePagination = false,
13186
13412
  formDrawerWidth = "80%",
13187
13413
  formColumns = 2,
@@ -13286,6 +13512,69 @@ function Crud({
13286
13512
  }
13287
13513
  return columns;
13288
13514
  }, [renderByFields, isMobile, tableScroll?.x]);
13515
+ const buildRowOptions = React.useCallback((row) => {
13516
+ const menuOptions = [];
13517
+ if (rowActionTypes.includes("edit") && !disabledActions?.edit && onEdit) {
13518
+ menuOptions.push({
13519
+ label: labels?.editButton || "Editar",
13520
+ icon: "edit",
13521
+ onClick: () => {
13522
+ openFormDrawer("edit", row);
13523
+ }
13524
+ });
13525
+ }
13526
+ if (rowActionTypes.includes("delete") && !disabledActions?.delete && onDelete) {
13527
+ menuOptions.push({
13528
+ label: labels?.deleteButton || "Eliminar",
13529
+ icon: "trash",
13530
+ onClick: () => {
13531
+ openConfirmDrawer("delete", void 0, [row]);
13532
+ }
13533
+ });
13534
+ }
13535
+ if (rowActionTypes.includes("view") && !disabledActions?.view) {
13536
+ menuOptions.push({
13537
+ label: labels?.viewButton || "Ver",
13538
+ icon: "eye",
13539
+ onClick: async () => {
13540
+ if (onView) {
13541
+ await onView([row]);
13542
+ }
13543
+ openFormDrawer("view", row);
13544
+ }
13545
+ });
13546
+ }
13547
+ rowActions?.forEach((action) => {
13548
+ if (action.show && !action.show(row)) return;
13549
+ menuOptions.push({
13550
+ label: action.label,
13551
+ icon: action.icon,
13552
+ onClick: () => {
13553
+ if (action.drawerId) {
13554
+ openCustomDrawer(action.drawerId, row);
13555
+ }
13556
+ if (action.onClick) {
13557
+ action.onClick(row);
13558
+ }
13559
+ },
13560
+ disabled: action.disabled ? action.disabled(row) : false,
13561
+ // SPEC-090 T3 — una acción apagada sin motivo es indistinguible de una rota.
13562
+ disabledReason: action.disabledReason?.(row)
13563
+ });
13564
+ });
13565
+ return menuOptions;
13566
+ }, [
13567
+ rowActionTypes,
13568
+ disabledActions,
13569
+ onEdit,
13570
+ onDelete,
13571
+ onView,
13572
+ labels,
13573
+ openFormDrawer,
13574
+ openConfirmDrawer,
13575
+ openCustomDrawer,
13576
+ rowActions
13577
+ ]);
13289
13578
  const tableColumns = React.useMemo(() => {
13290
13579
  if (!showRowActions) return baseTableColumns;
13291
13580
  const actionsColumn = {
@@ -13297,55 +13586,7 @@ function Crud({
13297
13586
  cellType: "CHILDREN",
13298
13587
  dataSource: {
13299
13588
  build: (row) => {
13300
- const menuOptions = [];
13301
- if (rowActionTypes.includes("edit") && !disabledActions?.edit && onEdit) {
13302
- menuOptions.push({
13303
- label: labels?.editButton || "Editar",
13304
- icon: "edit",
13305
- onClick: () => {
13306
- openFormDrawer("edit", row);
13307
- }
13308
- });
13309
- }
13310
- if (rowActionTypes.includes("delete") && !disabledActions?.delete && onDelete) {
13311
- menuOptions.push({
13312
- label: labels?.deleteButton || "Eliminar",
13313
- icon: "trash",
13314
- onClick: () => {
13315
- openConfirmDrawer("delete", void 0, [row]);
13316
- }
13317
- });
13318
- }
13319
- if (rowActionTypes.includes("view") && !disabledActions?.view) {
13320
- menuOptions.push({
13321
- label: labels?.viewButton || "Ver",
13322
- icon: "eye",
13323
- onClick: async () => {
13324
- if (onView) {
13325
- await onView([row]);
13326
- }
13327
- openFormDrawer("view", row);
13328
- }
13329
- });
13330
- }
13331
- rowActions?.forEach((action) => {
13332
- if (action.show && !action.show(row)) return;
13333
- menuOptions.push({
13334
- label: action.label,
13335
- icon: action.icon,
13336
- onClick: () => {
13337
- if (action.drawerId) {
13338
- openCustomDrawer(action.drawerId, row);
13339
- }
13340
- if (action.onClick) {
13341
- action.onClick(row);
13342
- }
13343
- },
13344
- disabled: action.disabled ? action.disabled(row) : false,
13345
- // SPEC-090 T3 — una acción apagada sin motivo es indistinguible de una rota.
13346
- disabledReason: action.disabledReason?.(row)
13347
- });
13348
- });
13589
+ const menuOptions = buildRowOptions(row);
13349
13590
  if (menuOptions.length === 0) return null;
13350
13591
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", justifyContent: "center", alignItems: "center", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
13351
13592
  DropdownMenu,
@@ -13368,20 +13609,7 @@ function Crud({
13368
13609
  }
13369
13610
  };
13370
13611
  return [...baseTableColumns, actionsColumn];
13371
- }, [
13372
- baseTableColumns,
13373
- showRowActions,
13374
- rowActionTypes,
13375
- disabledActions,
13376
- labels,
13377
- onEdit,
13378
- onDelete,
13379
- onView,
13380
- rowActions,
13381
- openFormDrawer,
13382
- openConfirmDrawer,
13383
- openCustomDrawer
13384
- ]);
13612
+ }, [baseTableColumns, showRowActions, buildRowOptions]);
13385
13613
  const formFields = React.useMemo(() => {
13386
13614
  return mapFieldsToFormFields(renderByFields, formMode);
13387
13615
  }, [renderByFields, formMode]);
@@ -13578,6 +13806,10 @@ function Crud({
13578
13806
  return "\xBFEst\xE1 seguro que desea realizar esta acci\xF3n?";
13579
13807
  }
13580
13808
  };
13809
+ const [refContenedor, anchoPropio] = useAnchoPropio();
13810
+ const enTarjetas = layout === "cards" || // Mientras no se ha medido (`null`) se mantiene la tabla: pintar tarjetas y saltar a tabla un
13811
+ // instante después es el parpadeo que se acaba de quitar en otro sitio.
13812
+ layout === "auto" && anchoPropio !== null && anchoPropio < cardsBelow;
13581
13813
  const crudClasses = [
13582
13814
  `${COMPONENT_PREFIX}crud`,
13583
13815
  loading && `${COMPONENT_PREFIX}crud--loading`,
@@ -13594,24 +13826,43 @@ function Crud({
13594
13826
  showLabels: false
13595
13827
  }
13596
13828
  ) }),
13597
- !formOnly && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${COMPONENT_PREFIX}crud__table`, children: /* @__PURE__ */ jsxRuntime.jsx(
13598
- Table$1,
13829
+ !formOnly && /* @__PURE__ */ jsxRuntime.jsxs(
13830
+ "div",
13599
13831
  {
13600
- dataSource,
13601
- columns: tableColumns,
13602
- selectable: showActionBar && actionBarMode === "full",
13603
- selectedRowKeys: selectedKeys,
13604
- onSelectionChange: handleSelectionChange,
13605
- rowKey: getRowKey,
13606
- loading,
13607
- height: tableHeight,
13608
- scroll: tableScroll,
13609
- compact: size === "small",
13610
- bordered: true,
13611
- hoverable: true,
13612
- emptyText: labels?.emptyText
13832
+ className: `${COMPONENT_PREFIX}crud__data${enTarjetas ? ` ${COMPONENT_PREFIX}crud__data--cards` : ""}`,
13833
+ children: [
13834
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${COMPONENT_PREFIX}crud__table`, ref: refContenedor, children: enTarjetas ? /* @__PURE__ */ jsxRuntime.jsx(
13835
+ CardList,
13836
+ {
13837
+ fields: renderByFields,
13838
+ rows: dataSource,
13839
+ rowKey: getRowKey,
13840
+ buildOptions: showRowActions ? buildRowOptions : void 0,
13841
+ loading,
13842
+ emptyText: labels?.emptyText
13843
+ }
13844
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
13845
+ Table$1,
13846
+ {
13847
+ dataSource,
13848
+ columns: tableColumns,
13849
+ selectable: showActionBar && actionBarMode === "full",
13850
+ selectedRowKeys: selectedKeys,
13851
+ onSelectionChange: handleSelectionChange,
13852
+ rowKey: getRowKey,
13853
+ loading,
13854
+ height: tableHeight,
13855
+ scroll: tableScroll,
13856
+ compact: size === "small",
13857
+ bordered: true,
13858
+ hoverable: true,
13859
+ emptyText: labels?.emptyText
13860
+ }
13861
+ ) }),
13862
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollToEnd, { hostRef: refContenedor, dep: `${enTarjetas}:${dataSource.length}:${loading}` })
13863
+ ]
13613
13864
  }
13614
- ) }),
13865
+ ),
13615
13866
  !formOnly && !filters.all_data && !hidePagination && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${COMPONENT_PREFIX}crud__pagination`, children: /* @__PURE__ */ jsxRuntime.jsx(
13616
13867
  Pagination,
13617
13868
  {
@@ -13676,6 +13927,7 @@ function Crud({
13676
13927
  position: "right",
13677
13928
  size: "responsive",
13678
13929
  responsivePercentage: 30,
13930
+ mobileSheet: true,
13679
13931
  className: `${COMPONENT_PREFIX}crud__confirm-drawer`,
13680
13932
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${COMPONENT_PREFIX}crud__confirm-content`, children: [
13681
13933
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: `${COMPONENT_PREFIX}crud__confirm-message`, children: getConfirmMessage() }),
@@ -13724,6 +13976,60 @@ function Crud({
13724
13976
  }
13725
13977
  Crud.displayName = "Crud";
13726
13978
 
13979
+ const CLASS$a = "px-chat-reactive-crud__chat-link";
13980
+ function Burbuja() {
13981
+ return /* @__PURE__ */ jsxRuntime.jsx(
13982
+ "svg",
13983
+ {
13984
+ width: 18,
13985
+ height: 18,
13986
+ viewBox: "0 0 24 24",
13987
+ fill: "none",
13988
+ stroke: "currentColor",
13989
+ strokeWidth: 2,
13990
+ strokeLinecap: "round",
13991
+ strokeLinejoin: "round",
13992
+ "aria-hidden": true,
13993
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
13994
+ }
13995
+ );
13996
+ }
13997
+ const ChatLinkCell = ({
13998
+ targets,
13999
+ eventBus
14000
+ }) => {
14001
+ if (targets.length === 0) return null;
14002
+ const abrir = (id) => eventBus?.emit(CHAT_EVENT.OPEN_CHAT, { chatId: id, closed: false });
14003
+ if (targets.length === 1) {
14004
+ const t = targets[0];
14005
+ const texto = t.label ? `Abrir chat ${t.label}` : "Abrir chat";
14006
+ return /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: CLASS$a, "aria-label": texto, title: texto, onClick: () => abrir(t.id), children: /* @__PURE__ */ jsxRuntime.jsx(Burbuja, {}) });
14007
+ }
14008
+ return /* @__PURE__ */ jsxRuntime.jsx(
14009
+ DropdownMenu,
14010
+ {
14011
+ options: targets.map((t) => ({
14012
+ label: t.label ?? "Chat",
14013
+ icon: "message-square",
14014
+ onClick: () => abrir(t.id)
14015
+ })),
14016
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(
14017
+ "button",
14018
+ {
14019
+ type: "button",
14020
+ className: CLASS$a,
14021
+ "aria-label": `Abrir chat (${targets.length} conversaciones)`,
14022
+ title: "Abrir chat",
14023
+ children: /* @__PURE__ */ jsxRuntime.jsx(Burbuja, {})
14024
+ }
14025
+ ),
14026
+ position: "bottom-left",
14027
+ usePortal: true
14028
+ }
14029
+ );
14030
+ };
14031
+ ChatLinkCell.displayName = "ChatLinkCell";
14032
+
13727
14033
  function buildCrudYupSchema(fields) {
13728
14034
  const shape = {};
13729
14035
  for (const f of fields) {
@@ -13804,8 +14110,21 @@ function unwrap$3(raw) {
13804
14110
  }
13805
14111
  return raw;
13806
14112
  }
13807
- async function loadOptions(eventBus, ep) {
13808
- const raw = await fetchViaEventBus(eventBus, ep.service, ep.url, ep.method, ep.body ?? {});
14113
+ function resolveBody(body, ctx) {
14114
+ if (!body) return {};
14115
+ if (!ctx) return body;
14116
+ return Object.fromEntries(
14117
+ Object.entries(body).map(([k, v]) => {
14118
+ if (typeof v === "string" && v.startsWith("$")) {
14119
+ const key = v.slice(1);
14120
+ return [k, key in ctx ? ctx[key] : v];
14121
+ }
14122
+ return [k, v];
14123
+ })
14124
+ );
14125
+ }
14126
+ async function loadOptions(eventBus, ep, ctx) {
14127
+ const raw = await fetchViaEventBus(eventBus, ep.service, ep.url, ep.method, resolveBody(ep.body, ctx));
13809
14128
  let data = unwrap$3(raw);
13810
14129
  if (ep.response_path && data && typeof data === "object" && !Array.isArray(data)) {
13811
14130
  data = data[ep.response_path];
@@ -13881,7 +14200,7 @@ function useReactiveCrud(config, eventBus) {
13881
14200
  const loadList = React.useCallback(async () => {
13882
14201
  if (!eventBus) return;
13883
14202
  const myReq = ++reqIdRef.current;
13884
- setState((s) => ({ ...s, loading: true, error: null }));
14203
+ setState((s) => s.rows.length === 0 ? { ...s, loading: true, error: null } : { ...s, error: null });
13885
14204
  try {
13886
14205
  const { service, url, method } = config.data.list;
13887
14206
  const raw = await fetchViaEventBus(eventBus, service, url, method, {
@@ -14132,7 +14451,7 @@ function ActionFormDrawer({ action, row, idField, eventBus, onExecute, onClose }
14132
14451
  void Promise.all(
14133
14452
  toLoad.map(async (f) => {
14134
14453
  try {
14135
- const opts = await loadOptions(eventBus, f.optionsEndpoint);
14454
+ const opts = await loadOptions(eventBus, f.optionsEndpoint, { ...row, id: row[idField] });
14136
14455
  return [f.name, opts];
14137
14456
  } catch {
14138
14457
  return [f.name, []];
@@ -14821,6 +15140,7 @@ function unwrap$1(raw) {
14821
15140
  }
14822
15141
  return raw;
14823
15142
  }
15143
+ const DETALLE_OCULTOS = /* @__PURE__ */ new Set(["LOCATION", "CHAT_LINK", "FILE"]);
14824
15144
  function DetailDrawer({ row, config, eventBus, onClose, loadedOptions }) {
14825
15145
  const detail = config.detail;
14826
15146
  const [files, setFiles] = React.useState([]);
@@ -14919,7 +15239,7 @@ function DetailDrawer({ row, config, eventBus, onClose, loadedOptions }) {
14919
15239
  }
14920
15240
  };
14921
15241
  return /* @__PURE__ */ jsxRuntime.jsx(Drawer$1, { isOpen: true, onClose, title, placement: "right", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-reactive-crud-detail", children: [
14922
- /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "px-chat-reactive-crud-detail__fields", children: config.fields.map((f) => {
15242
+ /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "px-chat-reactive-crud-detail__fields", children: config.fields.filter((f) => !DETALLE_OCULTOS.has(f.moleculeType)).map((f) => {
14923
15243
  const { node, copyText } = renderField(f);
14924
15244
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-reactive-crud-detail__row", children: [
14925
15245
  /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "px-chat-reactive-crud-detail__label", children: f.label }),
@@ -15581,35 +15901,13 @@ const LocationField = ({
15581
15901
  };
15582
15902
  LocationField.displayName = "LocationField";
15583
15903
 
15584
- function chatLinkButton(id, eventBus, label) {
15585
- return React.createElement(
15586
- "button",
15587
- {
15588
- key: label ?? id,
15589
- type: "button",
15590
- className: "px-chat-reactive-crud__chat-link",
15591
- "aria-label": label ? `Abrir chat ${label}` : "Abrir chat",
15592
- title: label ?? "Abrir chat",
15593
- onClick: () => eventBus?.emit(CHAT_EVENT.OPEN_CHAT, { chatId: id, closed: false })
15594
- },
15595
- React.createElement(
15596
- "svg",
15597
- { width: 18, height: 18, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" },
15598
- React.createElement("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
15599
- ),
15600
- label ? React.createElement("span", { className: "px-chat-reactive-crud__chat-link-label" }, label) : null
15601
- );
15602
- }
15603
15904
  function chatLinkFormatter(field, eventBus) {
15604
15905
  const sources = field.chats;
15605
15906
  return (value, row) => {
15606
15907
  const r = row;
15607
- if (sources && sources.length) {
15608
- const btns = sources.map((s) => ({ id: r ? r[s.field] : void 0, label: s.label })).filter((s) => s.id != null && s.id !== "").map((s) => chatLinkButton(String(s.id), eventBus, s.label));
15609
- return btns.length ? React.createElement("div", { className: "px-chat-reactive-crud__chat-links" }, btns) : null;
15610
- }
15611
- const id = value != null && value !== "" ? String(value) : "";
15612
- return id ? chatLinkButton(id, eventBus) : null;
15908
+ const targets = sources?.length ? sources.map((s) => ({ id: r ? r[s.field] : void 0, label: s.label })).filter((s) => s.id != null && s.id !== "").map((s) => ({ id: String(s.id), label: s.label })) : value != null && value !== "" ? [{ id: String(value) }] : [];
15909
+ if (!targets.length) return null;
15910
+ return React.createElement(ChatLinkCell, { targets, eventBus });
15613
15911
  };
15614
15912
  }
15615
15913
  function toRenderField(f, loaded, eventBus, mapsApiKey) {
@@ -15648,6 +15946,8 @@ function toRenderField(f, loaded, eventBus, mapsApiKey) {
15648
15946
  // FILE/CHAT_LINK solo son columnas → INPUT (el render lo define formatValue/build).
15649
15947
  moleculeType: f.moleculeType === "FILE" || isChatLink ? "INPUT" : f.moleculeType,
15650
15948
  showInTable: f.showInTable,
15949
+ summary: f.summary,
15950
+ // SPEC-093
15651
15951
  showInForm: f.showInForm,
15652
15952
  showInModes: f.showInModes,
15653
15953
  required: f.required,
@@ -15777,6 +16077,8 @@ function ReactiveCrud({ config, eventBus, autoOpenCreate, prefill, onCreated, on
15777
16077
  loading: crud.loading,
15778
16078
  filters: { skip: crud.skip, limit: crud.pageSize, all_data: false, filters: crud.filters },
15779
16079
  tableScroll: config.tableScroll,
16080
+ layout: config.layout,
16081
+ cardsBelow: config.cardsBelow,
15780
16082
  validationSchema,
15781
16083
  getRowKey: (row) => String(row[config.idField]),
15782
16084
  onCreate: config.data.create ? async (rows) => {
@@ -16046,12 +16348,18 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
16046
16348
  "button",
16047
16349
  {
16048
16350
  type: "button",
16049
- className: `px-chat-crud-item__action${a.intent === "danger" ? " is-danger" : ""}`,
16351
+ className: [
16352
+ "px-chat-crud-item__action",
16353
+ a.intent === "danger" ? "is-danger" : "",
16354
+ disabled && a.disabledReason ? "is-con-motivo" : ""
16355
+ ].filter(Boolean).join(" "),
16050
16356
  disabled,
16051
- title: disabled ? a.disabledReason : void 0,
16052
16357
  onClick: () => a.form ? setActiveAction({ action: a, row }) : crud.runEndpoints(a.endpoints ?? [a.endpoint], { ...row, id: row[config.idField] }),
16053
16358
  children: [
16054
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: a.label }),
16359
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-chat-crud-item__action-texts", children: [
16360
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: a.label }),
16361
+ disabled && a.disabledReason && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-crud-item__action-reason", children: a.disabledReason })
16362
+ ] }),
16055
16363
  done && /* @__PURE__ */ jsxRuntime.jsx(Icon$1, { type: "check-circle", width: 18, height: 18, className: "px-chat-crud-item__done", "aria-label": "Hecho" })
16056
16364
  ]
16057
16365
  },
@@ -16686,6 +16994,7 @@ function ChatNavigator({ config, eventBus }) {
16686
16994
  React.useEffect(() => {
16687
16995
  void load();
16688
16996
  }, [load]);
16997
+ const listaRef = React.useRef(null);
16689
16998
  const loadRef = React.useRef(load);
16690
16999
  loadRef.current = load;
16691
17000
  React.useEffect(() => {
@@ -16733,8 +17042,6 @@ function ChatNavigator({ config, eventBus }) {
16733
17042
  };
16734
17043
  const page = Math.floor(skip / pageSize) + 1;
16735
17044
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
16736
- const canPrev = skip > 0;
16737
- const canNext = skip + pageSize < total;
16738
17045
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-chat-navigator", children: [
16739
17046
  config.search && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-chat-navigator__search", children: [
16740
17047
  /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "px-chat-chat-navigator__search-icon", children: [
@@ -16814,143 +17121,126 @@ function ChatNavigator({ config, eventBus }) {
16814
17121
  loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-chat-navigator__state", children: "Cargando conversaciones\u2026" }),
16815
17122
  error && !loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-chat-navigator__state px-chat-chat-navigator__state--error", children: error }),
16816
17123
  !loading && !error && items.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-chat-navigator__state", children: labels.emptyText ?? "Sin conversaciones" }),
16817
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "px-chat-chat-navigator__list", children: items.map((item) => {
16818
- const isRenaming = renamingId === item.id;
16819
- const displayTitle = item.title?.trim() || fallbackTitle(item);
16820
- return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "px-chat-chat-navigator__item", children: [
16821
- isRenaming ? /* @__PURE__ */ jsxRuntime.jsx(
16822
- "form",
16823
- {
16824
- className: "px-chat-chat-navigator__rename",
16825
- onSubmit: (e) => {
16826
- e.preventDefault();
16827
- void submitRename(item.id);
16828
- },
16829
- children: /* @__PURE__ */ jsxRuntime.jsx(
16830
- "input",
16831
- {
16832
- autoFocus: true,
16833
- className: "px-chat-chat-navigator__rename-input",
16834
- value: renameValue,
16835
- onChange: (e) => setRenameValue(e.target.value),
16836
- onBlur: () => void submitRename(item.id),
16837
- "aria-label": labels.renameTitle ?? "Nuevo t\xEDtulo"
16838
- }
16839
- )
16840
- }
16841
- ) : /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "px-chat-chat-navigator__open", onClick: () => open(item), children: [
16842
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-chat-chat-navigator__title", children: [
16843
- displayTitle,
16844
- item.unread_proactive_count && item.unread_proactive_count >= 1 ? /* @__PURE__ */ jsxRuntime.jsx(
16845
- "span",
16846
- {
16847
- className: "px-chat-chat-navigator__badge-count",
16848
- "aria-label": `${item.unread_proactive_count} mensajes nuevos sin leer`,
16849
- title: `${item.unread_proactive_count} sin leer`,
16850
- children: item.unread_proactive_count >= 100 ? "100+" : item.unread_proactive_count
16851
- }
16852
- ) : null
16853
- ] }),
16854
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-chat-chat-navigator__meta", children: [
16855
- item.agent_name ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-chat-navigator__agent", children: item.agent_name }) : null,
16856
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-chat-navigator__time", children: relativeTime(item.updated_date) }),
16857
- (() => {
16858
- const opt = item.rol_code ? (config.originActorOptions ?? []).find((o) => o.value === item.rol_code) : void 0;
16859
- if (!opt) return null;
16860
- const c = opt.color;
16861
- const nameField = config.originActorNameField;
16862
- const allyName = nameField ? item[nameField] : void 0;
16863
- const label = allyName ? String(allyName) : opt.label;
16864
- return /* @__PURE__ */ jsxRuntime.jsx(
16865
- "span",
16866
- {
16867
- className: "px-chat-chat-navigator__origin-badge",
16868
- style: c ? { color: c, background: `${c}22`, borderColor: `${c}55` } : void 0,
16869
- title: label,
16870
- children: label
16871
- }
16872
- );
16873
- })(),
16874
- (() => {
16875
- const field = config.tagField;
16876
- if (!field) return null;
16877
- const value = item[field];
16878
- if (!value) return null;
16879
- const opt = (config.tagOptions ?? []).find((o) => o.value === String(value));
16880
- if (!opt) return null;
16881
- const c = opt.color;
16882
- return /* @__PURE__ */ jsxRuntime.jsx(
16883
- "span",
17124
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-chat-navigator__list-wrap", ref: listaRef, children: [
17125
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "px-chat-chat-navigator__list", children: items.map((item) => {
17126
+ const isRenaming = renamingId === item.id;
17127
+ const displayTitle = item.title?.trim() || fallbackTitle(item);
17128
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "px-chat-chat-navigator__item", children: [
17129
+ isRenaming ? /* @__PURE__ */ jsxRuntime.jsx(
17130
+ "form",
17131
+ {
17132
+ className: "px-chat-chat-navigator__rename",
17133
+ onSubmit: (e) => {
17134
+ e.preventDefault();
17135
+ void submitRename(item.id);
17136
+ },
17137
+ children: /* @__PURE__ */ jsxRuntime.jsx(
17138
+ "input",
16884
17139
  {
16885
- className: "px-chat-chat-navigator__origin-badge",
16886
- style: c ? { color: c, background: `${c}22`, borderColor: `${c}55` } : void 0,
16887
- title: opt.label,
16888
- children: opt.label
17140
+ autoFocus: true,
17141
+ className: "px-chat-chat-navigator__rename-input",
17142
+ value: renameValue,
17143
+ onChange: (e) => setRenameValue(e.target.value),
17144
+ onBlur: () => void submitRename(item.id),
17145
+ "aria-label": labels.renameTitle ?? "Nuevo t\xEDtulo"
16889
17146
  }
16890
- );
16891
- })(),
16892
- item.service_status ? (() => {
16893
- const opt = (config.serviceStatusOptions ?? []).find(
16894
- (o) => o.value === item.service_status
16895
- );
16896
- const c = opt?.color;
16897
- return /* @__PURE__ */ jsxRuntime.jsx(
17147
+ )
17148
+ }
17149
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "px-chat-chat-navigator__open", onClick: () => open(item), children: [
17150
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-chat-chat-navigator__title", children: [
17151
+ displayTitle,
17152
+ item.unread_proactive_count && item.unread_proactive_count >= 1 ? /* @__PURE__ */ jsxRuntime.jsx(
16898
17153
  "span",
16899
17154
  {
16900
- className: "px-chat-chat-navigator__service-badge",
16901
- style: c ? { color: c, background: `${c}22`, borderColor: `${c}55` } : void 0,
16902
- children: opt?.label ?? item.service_status
17155
+ className: "px-chat-chat-navigator__badge-count",
17156
+ "aria-label": `${item.unread_proactive_count} mensajes nuevos sin leer`,
17157
+ title: `${item.unread_proactive_count} sin leer`,
17158
+ children: item.unread_proactive_count >= 100 ? "100+" : item.unread_proactive_count
16903
17159
  }
16904
- );
16905
- })() : null,
16906
- item.closed ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-chat-navigator__badge", children: labels.closedBadge ?? "Cerrado" }) : null
16907
- ] })
16908
- ] }),
16909
- config.data.rename && !isRenaming && /* @__PURE__ */ jsxRuntime.jsx(
16910
- "button",
16911
- {
16912
- type: "button",
16913
- className: "px-chat-chat-navigator__rename-btn",
16914
- "aria-label": "Renombrar",
16915
- title: "Renombrar",
16916
- onClick: () => startRename(item),
16917
- children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
16918
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 20h9" }),
16919
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z" })
17160
+ ) : null
17161
+ ] }),
17162
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-chat-chat-navigator__meta", children: [
17163
+ item.agent_name ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-chat-navigator__agent", children: item.agent_name }) : null,
17164
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-chat-navigator__time", children: relativeTime(item.updated_date) }),
17165
+ (() => {
17166
+ const opt = item.rol_code ? (config.originActorOptions ?? []).find((o) => o.value === item.rol_code) : void 0;
17167
+ if (!opt) return null;
17168
+ const c = opt.color;
17169
+ const nameField = config.originActorNameField;
17170
+ const allyName = nameField ? item[nameField] : void 0;
17171
+ const label = allyName ? String(allyName) : opt.label;
17172
+ return /* @__PURE__ */ jsxRuntime.jsx(
17173
+ "span",
17174
+ {
17175
+ className: "px-chat-chat-navigator__origin-badge",
17176
+ style: c ? { color: c, background: `${c}22`, borderColor: `${c}55` } : void 0,
17177
+ title: label,
17178
+ children: label
17179
+ }
17180
+ );
17181
+ })(),
17182
+ (() => {
17183
+ const field = config.tagField;
17184
+ if (!field) return null;
17185
+ const value = item[field];
17186
+ if (!value) return null;
17187
+ const opt = (config.tagOptions ?? []).find((o) => o.value === String(value));
17188
+ if (!opt) return null;
17189
+ const c = opt.color;
17190
+ return /* @__PURE__ */ jsxRuntime.jsx(
17191
+ "span",
17192
+ {
17193
+ className: "px-chat-chat-navigator__origin-badge",
17194
+ style: c ? { color: c, background: `${c}22`, borderColor: `${c}55` } : void 0,
17195
+ title: opt.label,
17196
+ children: opt.label
17197
+ }
17198
+ );
17199
+ })(),
17200
+ item.service_status ? (() => {
17201
+ const opt = (config.serviceStatusOptions ?? []).find(
17202
+ (o) => o.value === item.service_status
17203
+ );
17204
+ const c = opt?.color;
17205
+ return /* @__PURE__ */ jsxRuntime.jsx(
17206
+ "span",
17207
+ {
17208
+ className: "px-chat-chat-navigator__service-badge",
17209
+ style: c ? { color: c, background: `${c}22`, borderColor: `${c}55` } : void 0,
17210
+ children: opt?.label ?? item.service_status
17211
+ }
17212
+ );
17213
+ })() : null,
17214
+ item.closed ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-chat-navigator__badge", children: labels.closedBadge ?? "Cerrado" }) : null
16920
17215
  ] })
16921
- }
16922
- )
16923
- ] }, item.id);
16924
- }) }),
16925
- total > pageSize && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-chat-navigator__pager", children: [
16926
- /* @__PURE__ */ jsxRuntime.jsx(
16927
- "button",
16928
- {
16929
- type: "button",
16930
- className: "px-chat-chat-navigator__pager-btn",
16931
- disabled: !canPrev,
16932
- onClick: () => setSkip(Math.max(0, skip - pageSize)),
16933
- "aria-label": "P\xE1gina anterior",
16934
- children: "\u2039"
16935
- }
16936
- ),
16937
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-chat-chat-navigator__pager-info", children: [
16938
- page,
16939
- " / ",
16940
- totalPages
16941
- ] }),
16942
- /* @__PURE__ */ jsxRuntime.jsx(
16943
- "button",
16944
- {
16945
- type: "button",
16946
- className: "px-chat-chat-navigator__pager-btn",
16947
- disabled: !canNext,
16948
- onClick: () => setSkip(skip + pageSize),
16949
- "aria-label": "P\xE1gina siguiente",
16950
- children: "\u203A"
16951
- }
16952
- )
17216
+ ] }),
17217
+ config.data.rename && !isRenaming && /* @__PURE__ */ jsxRuntime.jsx(
17218
+ "button",
17219
+ {
17220
+ type: "button",
17221
+ className: "px-chat-chat-navigator__rename-btn",
17222
+ "aria-label": "Renombrar",
17223
+ title: "Renombrar",
17224
+ onClick: () => startRename(item),
17225
+ children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
17226
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 20h9" }),
17227
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z" })
17228
+ ] })
17229
+ }
17230
+ )
17231
+ ] }, item.id);
17232
+ }) }),
17233
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollToEnd, { hostRef: listaRef, selector: ".px-chat-chat-navigator__list", dep: `${items.length}:${loading}` })
16953
17234
  ] }),
17235
+ total > pageSize && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-chat-navigator__pager", children: /* @__PURE__ */ jsxRuntime.jsx(
17236
+ Pagination,
17237
+ {
17238
+ current: page,
17239
+ total: totalPages,
17240
+ onChange: (p) => setSkip((p - 1) * pageSize),
17241
+ showTotal: false
17242
+ }
17243
+ ) }),
16954
17244
  config.advancedFilters?.length ? /* @__PURE__ */ jsxRuntime.jsx(Drawer$1, { isOpen: advOpen, onClose: () => setAdvOpen(false), title: "Filtros avanzados", children: /* @__PURE__ */ jsxRuntime.jsx(
16955
17245
  Filter,
16956
17246
  {
@@ -24355,6 +24645,7 @@ exports.LumiDateInput = DateInput$1;
24355
24645
  exports.LumiDrawer = Drawer;
24356
24646
  exports.LumiDropdownMenu = DropdownMenu;
24357
24647
  exports.LumiDynamicForm = DynamicForm;
24648
+ exports.LumiFieldValue = FieldValue;
24358
24649
  exports.LumiFilter = Filter;
24359
24650
  exports.LumiFilterRow = FilterRow;
24360
24651
  exports.LumiFormCheckbox = Checkbox;
@@ -24373,6 +24664,7 @@ exports.LumiNotification = Notification;
24373
24664
  exports.LumiPagination = Pagination;
24374
24665
  exports.LumiPopover = Popover;
24375
24666
  exports.LumiQRCode = QRCode;
24667
+ exports.LumiRecordCard = RecordCard;
24376
24668
  exports.LumiSelect = Select$1;
24377
24669
  exports.LumiStepIndicator = StepIndicator;
24378
24670
  exports.LumiStepper = Stepper;