pixelon 0.3.77 → 0.3.78

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 (24) 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/molecules/chat/StructuredContent/components/ReactiveCrud/ChatLinkCell.d.ts +20 -0
  5. package/dist/components/molecules/chat/StructuredContent/components/ReactiveCrud/optionsLoader.d.ts +2 -1
  6. package/dist/components/molecules/chat/StructuredContent/components/ReactiveCrud/types.d.ts +6 -0
  7. package/dist/components/molecules/lumi/Drawer/Drawer.d.ts +8 -0
  8. package/dist/components/molecules/lumi/RecordCard/RecordCard.d.ts +14 -0
  9. package/dist/components/molecules/lumi/RecordCard/index.d.ts +2 -0
  10. package/dist/components/molecules/lumi/RecordCard/types.d.ts +16 -0
  11. package/dist/components/molecules/lumi/ScrollToEnd/ScrollToEnd.d.ts +21 -0
  12. package/dist/components/molecules/lumi/ScrollToEnd/index.d.ts +2 -0
  13. package/dist/components/templates/lumi/Crud/CardList.d.ts +34 -0
  14. package/dist/components/templates/lumi/Crud/Crud.d.ts +1 -1
  15. package/dist/components/templates/lumi/Crud/ScrollToEnd.d.ts +18 -0
  16. package/dist/components/templates/lumi/Crud/helpers/fieldValue.d.ts +12 -0
  17. package/dist/components/templates/lumi/Crud/types.d.ts +25 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.esm.js +545 -276
  20. package/dist/index.js +546 -275
  21. package/dist/styles.css +1 -1
  22. package/dist/styles.css.map +1 -1
  23. package/package.json +1 -1
  24. 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(
@@ -10886,6 +10886,7 @@ const Drawer = ({
10886
10886
  size = DRAWER_SIZES.RESPONSIVE,
10887
10887
  fixedSize = 400,
10888
10888
  responsivePercentage = 80,
10889
+ mobileSheet = false,
10889
10890
  title,
10890
10891
  headerExtra,
10891
10892
  showCloseButton = true,
@@ -10968,11 +10969,13 @@ const Drawer = ({
10968
10969
  `${COMPONENT_PREFIX}drawer--${size}`,
10969
10970
  isOpen && `${COMPONENT_PREFIX}drawer--open`,
10970
10971
  isMobile && `${COMPONENT_PREFIX}drawer--mobile`,
10972
+ mobileSheet && `${COMPONENT_PREFIX}drawer--sheet`,
10971
10973
  className
10972
10974
  ].filter(Boolean).join(" ");
10973
10975
  const overlayClasses = [
10974
10976
  `${COMPONENT_PREFIX}drawer-overlay`,
10975
- isOpen && showOverlay && `${COMPONENT_PREFIX}drawer-overlay--visible`
10977
+ isOpen && showOverlay && `${COMPONENT_PREFIX}drawer-overlay--visible`,
10978
+ mobileSheet && `${COMPONENT_PREFIX}drawer-overlay--sheet`
10976
10979
  ].filter(Boolean).join(" ");
10977
10980
  const getDrawerStyles = () => {
10978
10981
  if (isMobile) {
@@ -13079,36 +13082,45 @@ const Badge = ({
13079
13082
  };
13080
13083
  Badge.displayName = "LumiBadge";
13081
13084
 
13085
+ function fieldOptions(field) {
13086
+ return field.uiProps?.options;
13087
+ }
13088
+ function renderFieldValue(field, row) {
13089
+ const value = row[field.key];
13090
+ const options = fieldOptions(field);
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
+ }
13112
+ function hasFieldValue(field, row) {
13113
+ const value = row[field.key];
13114
+ return value != null && value !== "";
13115
+ }
13116
+
13082
13117
  function mapFieldsToTableColumns(fields) {
13083
13118
  return fields.filter((field) => field.showInTable !== false).map((field) => {
13084
13119
  let width = field.columnWidth;
13085
13120
  if (field.fixed && typeof field.columnWidth === "number") {
13086
13121
  width = `${field.columnWidth}px`;
13087
13122
  }
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
- };
13123
+ const build = (row) => renderFieldValue(field, row);
13112
13124
  return {
13113
13125
  key: field.key,
13114
13126
  title: field.label,
@@ -13139,10 +13151,201 @@ function mapFieldsToFormFields(fields, mode) {
13139
13151
  }));
13140
13152
  }
13141
13153
 
13154
+ const CLASS$e = `${COMPONENT_PREFIX}field-value`;
13155
+ const FieldValue = ({
13156
+ label,
13157
+ children,
13158
+ layout = "inline",
13159
+ className
13160
+ }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: [CLASS$e, `${CLASS$e}--${layout}`, className].filter(Boolean).join(" "), children: [
13161
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${CLASS$e}__label`, children: label }),
13162
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${CLASS$e}__value`, children })
13163
+ ] });
13164
+ FieldValue.displayName = "FieldValue";
13165
+
13166
+ const CLASS$d = `${COMPONENT_PREFIX}record-card`;
13167
+ const RecordCard = ({
13168
+ title,
13169
+ summary,
13170
+ children,
13171
+ actions,
13172
+ open = false,
13173
+ onToggle,
13174
+ ariaLabel,
13175
+ className
13176
+ }) => {
13177
+ const cuerpoId = React.useId();
13178
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: [CLASS$d, open ? `${CLASS$d}--open` : "", className].filter(Boolean).join(" "), children: [
13179
+ /* @__PURE__ */ jsxRuntime.jsxs(
13180
+ "button",
13181
+ {
13182
+ type: "button",
13183
+ className: `${CLASS$d}__header`,
13184
+ "aria-expanded": open,
13185
+ "aria-controls": cuerpoId,
13186
+ "aria-label": ariaLabel,
13187
+ onClick: onToggle,
13188
+ children: [
13189
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `${CLASS$d}__heading`, children: [
13190
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${CLASS$d}__title`, children: title }),
13191
+ summary && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${CLASS$d}__summary`, children: summary })
13192
+ ] }),
13193
+ /* @__PURE__ */ jsxRuntime.jsx(
13194
+ Icon,
13195
+ {
13196
+ type: open ? "chevron-up" : "chevron-down",
13197
+ width: 18,
13198
+ height: 18,
13199
+ className: `${CLASS$d}__chevron`,
13200
+ "aria-hidden": true
13201
+ }
13202
+ )
13203
+ ]
13204
+ }
13205
+ ),
13206
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${CLASS$d}__body`, id: cuerpoId, children: [
13207
+ children,
13208
+ actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${CLASS$d}__actions`, children: actions })
13209
+ ] })
13210
+ ] });
13211
+ };
13212
+ RecordCard.displayName = "RecordCard";
13213
+
13214
+ const CLASS$c = `${COMPONENT_PREFIX}crud-cards`;
13215
+ function summaryFields(fields) {
13216
+ const visibles = fields.filter((f) => f.showInTable !== false);
13217
+ const marcados = visibles.filter((f) => f.summary);
13218
+ return marcados.length > 0 ? marcados : visibles.slice(0, 3);
13219
+ }
13220
+ function CardList({
13221
+ fields,
13222
+ rows,
13223
+ rowKey,
13224
+ buildOptions,
13225
+ loading = false,
13226
+ emptyText = "No hay registros"
13227
+ }) {
13228
+ const [abierta, setAbierta] = React.useState(null);
13229
+ const resumen = summaryFields(fields);
13230
+ const claveResumen = new Set(resumen.map((f) => f.key));
13231
+ const cuerpo = fields.filter((f) => f.showInTable !== false && !claveResumen.has(f.key));
13232
+ if (loading && rows.length === 0) {
13233
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${CLASS$c}__estado`, children: "Cargando..." });
13234
+ }
13235
+ if (rows.length === 0) {
13236
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${CLASS$c}__estado`, children: emptyText });
13237
+ }
13238
+ const [principal, ...secundarios] = resumen;
13239
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: CLASS$c, children: rows.map((row) => {
13240
+ const key = rowKey(row);
13241
+ const opciones = buildOptions?.(row) ?? [];
13242
+ const titulo = principal ? renderFieldValue(principal, row) : key;
13243
+ return /* @__PURE__ */ jsxRuntime.jsx(
13244
+ RecordCard,
13245
+ {
13246
+ open: abierta === key,
13247
+ onToggle: () => setAbierta((actual) => actual === key ? null : key),
13248
+ ariaLabel: principal ? `${principal.label}: ${String(row[principal.key] ?? "")}` : void 0,
13249
+ title: titulo,
13250
+ 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,
13251
+ actions: opciones.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: opciones.map((o) => /* @__PURE__ */ jsxRuntime.jsxs(
13252
+ "button",
13253
+ {
13254
+ type: "button",
13255
+ className: [
13256
+ `${CLASS$c}__accion`,
13257
+ o.disabled && o.disabledReason ? "is-con-motivo" : ""
13258
+ ].filter(Boolean).join(" "),
13259
+ disabled: o.disabled,
13260
+ onClick: () => void o.onClick(),
13261
+ children: [
13262
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: o.label }),
13263
+ o.disabled && o.disabledReason && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${CLASS$c}__motivo`, children: o.disabledReason })
13264
+ ]
13265
+ },
13266
+ o.label
13267
+ )) }) : void 0,
13268
+ children: cuerpo.map(
13269
+ (f) => hasFieldValue(f, row) ? /* @__PURE__ */ jsxRuntime.jsx(FieldValue, { label: f.label, children: renderFieldValue(f, row) }, f.key) : null
13270
+ )
13271
+ },
13272
+ key
13273
+ );
13274
+ }) });
13275
+ }
13276
+ CardList.displayName = "CardList";
13277
+
13278
+ const CLASS$b = `${COMPONENT_PREFIX}crud-scroll-end`;
13279
+ const MARGEN = 24;
13280
+ const SELECTOR_CRUD = `.${COMPONENT_PREFIX}table-container, .${COMPONENT_PREFIX}crud-cards`;
13281
+ const ScrollToEnd = ({
13282
+ hostRef,
13283
+ selector = SELECTOR_CRUD,
13284
+ dep,
13285
+ label = "Ir al final de la lista"
13286
+ }) => {
13287
+ const [visible, setVisible] = React.useState(false);
13288
+ const scrollerRef = React.useRef(null);
13289
+ React.useEffect(() => {
13290
+ const host = hostRef.current;
13291
+ if (!host) return;
13292
+ const scroller = host.querySelector(selector) ?? host;
13293
+ scrollerRef.current = scroller;
13294
+ if (!scroller) {
13295
+ setVisible(false);
13296
+ return;
13297
+ }
13298
+ const revisar = () => {
13299
+ const s = scrollerRef.current;
13300
+ if (!s) return;
13301
+ setVisible(s.scrollHeight - s.clientHeight - s.scrollTop > MARGEN);
13302
+ };
13303
+ revisar();
13304
+ scroller.addEventListener("scroll", revisar, { passive: true });
13305
+ let observador;
13306
+ if (typeof ResizeObserver !== "undefined") {
13307
+ observador = new ResizeObserver(revisar);
13308
+ observador.observe(scroller);
13309
+ if (scroller.firstElementChild) observador.observe(scroller.firstElementChild);
13310
+ }
13311
+ return () => {
13312
+ scroller.removeEventListener("scroll", revisar);
13313
+ observador?.disconnect();
13314
+ };
13315
+ }, [hostRef, selector, dep]);
13316
+ const alFinal = React.useCallback(() => {
13317
+ const s = scrollerRef.current;
13318
+ if (!s) return;
13319
+ s.scrollTo({ top: s.scrollHeight, behavior: "smooth" });
13320
+ }, []);
13321
+ if (!visible) return null;
13322
+ 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 }) });
13323
+ };
13324
+ ScrollToEnd.displayName = "ScrollToEnd";
13325
+
13142
13326
  function calculateTotalPages(totalRecords, limit) {
13143
13327
  return Math.ceil(totalRecords / limit);
13144
13328
  }
13145
13329
 
13330
+ const useAnchoPropio = () => {
13331
+ const ref = React.useRef(null);
13332
+ const [ancho, setAncho] = React.useState(null);
13333
+ React.useEffect(() => {
13334
+ const nodo = ref.current;
13335
+ if (!nodo) return;
13336
+ if (typeof ResizeObserver === "undefined") {
13337
+ setAncho(nodo.getBoundingClientRect().width);
13338
+ return;
13339
+ }
13340
+ const observador = new ResizeObserver((entradas) => {
13341
+ const w = entradas[0]?.contentRect.width;
13342
+ if (typeof w === "number") setAncho(w);
13343
+ });
13344
+ observador.observe(nodo);
13345
+ return () => observador.disconnect();
13346
+ }, []);
13347
+ return [ref, ancho];
13348
+ };
13146
13349
  const useIsMobile = () => {
13147
13350
  const [isMobile, setIsMobile] = React.useState(false);
13148
13351
  React.useEffect(() => {
@@ -13182,6 +13385,8 @@ function Crud({
13182
13385
  defaultFilters,
13183
13386
  tableHeight,
13184
13387
  tableScroll,
13388
+ layout = "auto",
13389
+ cardsBelow = 768,
13185
13390
  hidePagination = false,
13186
13391
  formDrawerWidth = "80%",
13187
13392
  formColumns = 2,
@@ -13286,6 +13491,69 @@ function Crud({
13286
13491
  }
13287
13492
  return columns;
13288
13493
  }, [renderByFields, isMobile, tableScroll?.x]);
13494
+ const buildRowOptions = React.useCallback((row) => {
13495
+ const menuOptions = [];
13496
+ if (rowActionTypes.includes("edit") && !disabledActions?.edit && onEdit) {
13497
+ menuOptions.push({
13498
+ label: labels?.editButton || "Editar",
13499
+ icon: "edit",
13500
+ onClick: () => {
13501
+ openFormDrawer("edit", row);
13502
+ }
13503
+ });
13504
+ }
13505
+ if (rowActionTypes.includes("delete") && !disabledActions?.delete && onDelete) {
13506
+ menuOptions.push({
13507
+ label: labels?.deleteButton || "Eliminar",
13508
+ icon: "trash",
13509
+ onClick: () => {
13510
+ openConfirmDrawer("delete", void 0, [row]);
13511
+ }
13512
+ });
13513
+ }
13514
+ if (rowActionTypes.includes("view") && !disabledActions?.view) {
13515
+ menuOptions.push({
13516
+ label: labels?.viewButton || "Ver",
13517
+ icon: "eye",
13518
+ onClick: async () => {
13519
+ if (onView) {
13520
+ await onView([row]);
13521
+ }
13522
+ openFormDrawer("view", row);
13523
+ }
13524
+ });
13525
+ }
13526
+ rowActions?.forEach((action) => {
13527
+ if (action.show && !action.show(row)) return;
13528
+ menuOptions.push({
13529
+ label: action.label,
13530
+ icon: action.icon,
13531
+ onClick: () => {
13532
+ if (action.drawerId) {
13533
+ openCustomDrawer(action.drawerId, row);
13534
+ }
13535
+ if (action.onClick) {
13536
+ action.onClick(row);
13537
+ }
13538
+ },
13539
+ disabled: action.disabled ? action.disabled(row) : false,
13540
+ // SPEC-090 T3 — una acción apagada sin motivo es indistinguible de una rota.
13541
+ disabledReason: action.disabledReason?.(row)
13542
+ });
13543
+ });
13544
+ return menuOptions;
13545
+ }, [
13546
+ rowActionTypes,
13547
+ disabledActions,
13548
+ onEdit,
13549
+ onDelete,
13550
+ onView,
13551
+ labels,
13552
+ openFormDrawer,
13553
+ openConfirmDrawer,
13554
+ openCustomDrawer,
13555
+ rowActions
13556
+ ]);
13289
13557
  const tableColumns = React.useMemo(() => {
13290
13558
  if (!showRowActions) return baseTableColumns;
13291
13559
  const actionsColumn = {
@@ -13297,55 +13565,7 @@ function Crud({
13297
13565
  cellType: "CHILDREN",
13298
13566
  dataSource: {
13299
13567
  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
- });
13568
+ const menuOptions = buildRowOptions(row);
13349
13569
  if (menuOptions.length === 0) return null;
13350
13570
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", justifyContent: "center", alignItems: "center", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
13351
13571
  DropdownMenu,
@@ -13368,20 +13588,7 @@ function Crud({
13368
13588
  }
13369
13589
  };
13370
13590
  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
- ]);
13591
+ }, [baseTableColumns, showRowActions, buildRowOptions]);
13385
13592
  const formFields = React.useMemo(() => {
13386
13593
  return mapFieldsToFormFields(renderByFields, formMode);
13387
13594
  }, [renderByFields, formMode]);
@@ -13578,6 +13785,10 @@ function Crud({
13578
13785
  return "\xBFEst\xE1 seguro que desea realizar esta acci\xF3n?";
13579
13786
  }
13580
13787
  };
13788
+ const [refContenedor, anchoPropio] = useAnchoPropio();
13789
+ const enTarjetas = layout === "cards" || // Mientras no se ha medido (`null`) se mantiene la tabla: pintar tarjetas y saltar a tabla un
13790
+ // instante después es el parpadeo que se acaba de quitar en otro sitio.
13791
+ layout === "auto" && anchoPropio !== null && anchoPropio < cardsBelow;
13581
13792
  const crudClasses = [
13582
13793
  `${COMPONENT_PREFIX}crud`,
13583
13794
  loading && `${COMPONENT_PREFIX}crud--loading`,
@@ -13594,24 +13805,43 @@ function Crud({
13594
13805
  showLabels: false
13595
13806
  }
13596
13807
  ) }),
13597
- !formOnly && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${COMPONENT_PREFIX}crud__table`, children: /* @__PURE__ */ jsxRuntime.jsx(
13598
- Table$1,
13808
+ !formOnly && /* @__PURE__ */ jsxRuntime.jsxs(
13809
+ "div",
13599
13810
  {
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
13811
+ className: `${COMPONENT_PREFIX}crud__data${enTarjetas ? ` ${COMPONENT_PREFIX}crud__data--cards` : ""}`,
13812
+ children: [
13813
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${COMPONENT_PREFIX}crud__table`, ref: refContenedor, children: enTarjetas ? /* @__PURE__ */ jsxRuntime.jsx(
13814
+ CardList,
13815
+ {
13816
+ fields: renderByFields,
13817
+ rows: dataSource,
13818
+ rowKey: getRowKey,
13819
+ buildOptions: showRowActions ? buildRowOptions : void 0,
13820
+ loading,
13821
+ emptyText: labels?.emptyText
13822
+ }
13823
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
13824
+ Table$1,
13825
+ {
13826
+ dataSource,
13827
+ columns: tableColumns,
13828
+ selectable: showActionBar && actionBarMode === "full",
13829
+ selectedRowKeys: selectedKeys,
13830
+ onSelectionChange: handleSelectionChange,
13831
+ rowKey: getRowKey,
13832
+ loading,
13833
+ height: tableHeight,
13834
+ scroll: tableScroll,
13835
+ compact: size === "small",
13836
+ bordered: true,
13837
+ hoverable: true,
13838
+ emptyText: labels?.emptyText
13839
+ }
13840
+ ) }),
13841
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollToEnd, { hostRef: refContenedor, dep: `${enTarjetas}:${dataSource.length}:${loading}` })
13842
+ ]
13613
13843
  }
13614
- ) }),
13844
+ ),
13615
13845
  !formOnly && !filters.all_data && !hidePagination && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${COMPONENT_PREFIX}crud__pagination`, children: /* @__PURE__ */ jsxRuntime.jsx(
13616
13846
  Pagination,
13617
13847
  {
@@ -13676,6 +13906,7 @@ function Crud({
13676
13906
  position: "right",
13677
13907
  size: "responsive",
13678
13908
  responsivePercentage: 30,
13909
+ mobileSheet: true,
13679
13910
  className: `${COMPONENT_PREFIX}crud__confirm-drawer`,
13680
13911
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${COMPONENT_PREFIX}crud__confirm-content`, children: [
13681
13912
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: `${COMPONENT_PREFIX}crud__confirm-message`, children: getConfirmMessage() }),
@@ -13724,6 +13955,60 @@ function Crud({
13724
13955
  }
13725
13956
  Crud.displayName = "Crud";
13726
13957
 
13958
+ const CLASS$a = "px-chat-reactive-crud__chat-link";
13959
+ function Burbuja() {
13960
+ return /* @__PURE__ */ jsxRuntime.jsx(
13961
+ "svg",
13962
+ {
13963
+ width: 18,
13964
+ height: 18,
13965
+ viewBox: "0 0 24 24",
13966
+ fill: "none",
13967
+ stroke: "currentColor",
13968
+ strokeWidth: 2,
13969
+ strokeLinecap: "round",
13970
+ strokeLinejoin: "round",
13971
+ "aria-hidden": true,
13972
+ 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" })
13973
+ }
13974
+ );
13975
+ }
13976
+ const ChatLinkCell = ({
13977
+ targets,
13978
+ eventBus
13979
+ }) => {
13980
+ if (targets.length === 0) return null;
13981
+ const abrir = (id) => eventBus?.emit(CHAT_EVENT.OPEN_CHAT, { chatId: id, closed: false });
13982
+ if (targets.length === 1) {
13983
+ const t = targets[0];
13984
+ const texto = t.label ? `Abrir chat ${t.label}` : "Abrir chat";
13985
+ return /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: CLASS$a, "aria-label": texto, title: texto, onClick: () => abrir(t.id), children: /* @__PURE__ */ jsxRuntime.jsx(Burbuja, {}) });
13986
+ }
13987
+ return /* @__PURE__ */ jsxRuntime.jsx(
13988
+ DropdownMenu,
13989
+ {
13990
+ options: targets.map((t) => ({
13991
+ label: t.label ?? "Chat",
13992
+ icon: "message-square",
13993
+ onClick: () => abrir(t.id)
13994
+ })),
13995
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(
13996
+ "button",
13997
+ {
13998
+ type: "button",
13999
+ className: CLASS$a,
14000
+ "aria-label": `Abrir chat (${targets.length} conversaciones)`,
14001
+ title: "Abrir chat",
14002
+ children: /* @__PURE__ */ jsxRuntime.jsx(Burbuja, {})
14003
+ }
14004
+ ),
14005
+ position: "bottom-left",
14006
+ usePortal: true
14007
+ }
14008
+ );
14009
+ };
14010
+ ChatLinkCell.displayName = "ChatLinkCell";
14011
+
13727
14012
  function buildCrudYupSchema(fields) {
13728
14013
  const shape = {};
13729
14014
  for (const f of fields) {
@@ -13804,8 +14089,21 @@ function unwrap$3(raw) {
13804
14089
  }
13805
14090
  return raw;
13806
14091
  }
13807
- async function loadOptions(eventBus, ep) {
13808
- const raw = await fetchViaEventBus(eventBus, ep.service, ep.url, ep.method, ep.body ?? {});
14092
+ function resolveBody(body, ctx) {
14093
+ if (!body) return {};
14094
+ if (!ctx) return body;
14095
+ return Object.fromEntries(
14096
+ Object.entries(body).map(([k, v]) => {
14097
+ if (typeof v === "string" && v.startsWith("$")) {
14098
+ const key = v.slice(1);
14099
+ return [k, key in ctx ? ctx[key] : v];
14100
+ }
14101
+ return [k, v];
14102
+ })
14103
+ );
14104
+ }
14105
+ async function loadOptions(eventBus, ep, ctx) {
14106
+ const raw = await fetchViaEventBus(eventBus, ep.service, ep.url, ep.method, resolveBody(ep.body, ctx));
13809
14107
  let data = unwrap$3(raw);
13810
14108
  if (ep.response_path && data && typeof data === "object" && !Array.isArray(data)) {
13811
14109
  data = data[ep.response_path];
@@ -13881,7 +14179,7 @@ function useReactiveCrud(config, eventBus) {
13881
14179
  const loadList = React.useCallback(async () => {
13882
14180
  if (!eventBus) return;
13883
14181
  const myReq = ++reqIdRef.current;
13884
- setState((s) => ({ ...s, loading: true, error: null }));
14182
+ setState((s) => s.rows.length === 0 ? { ...s, loading: true, error: null } : { ...s, error: null });
13885
14183
  try {
13886
14184
  const { service, url, method } = config.data.list;
13887
14185
  const raw = await fetchViaEventBus(eventBus, service, url, method, {
@@ -14132,7 +14430,7 @@ function ActionFormDrawer({ action, row, idField, eventBus, onExecute, onClose }
14132
14430
  void Promise.all(
14133
14431
  toLoad.map(async (f) => {
14134
14432
  try {
14135
- const opts = await loadOptions(eventBus, f.optionsEndpoint);
14433
+ const opts = await loadOptions(eventBus, f.optionsEndpoint, { ...row, id: row[idField] });
14136
14434
  return [f.name, opts];
14137
14435
  } catch {
14138
14436
  return [f.name, []];
@@ -14821,6 +15119,7 @@ function unwrap$1(raw) {
14821
15119
  }
14822
15120
  return raw;
14823
15121
  }
15122
+ const DETALLE_OCULTOS = /* @__PURE__ */ new Set(["LOCATION", "CHAT_LINK", "FILE"]);
14824
15123
  function DetailDrawer({ row, config, eventBus, onClose, loadedOptions }) {
14825
15124
  const detail = config.detail;
14826
15125
  const [files, setFiles] = React.useState([]);
@@ -14919,7 +15218,7 @@ function DetailDrawer({ row, config, eventBus, onClose, loadedOptions }) {
14919
15218
  }
14920
15219
  };
14921
15220
  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) => {
15221
+ /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "px-chat-reactive-crud-detail__fields", children: config.fields.filter((f) => !DETALLE_OCULTOS.has(f.moleculeType)).map((f) => {
14923
15222
  const { node, copyText } = renderField(f);
14924
15223
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-reactive-crud-detail__row", children: [
14925
15224
  /* @__PURE__ */ jsxRuntime.jsx("dt", { className: "px-chat-reactive-crud-detail__label", children: f.label }),
@@ -15581,35 +15880,13 @@ const LocationField = ({
15581
15880
  };
15582
15881
  LocationField.displayName = "LocationField";
15583
15882
 
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
15883
  function chatLinkFormatter(field, eventBus) {
15604
15884
  const sources = field.chats;
15605
15885
  return (value, row) => {
15606
15886
  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;
15887
+ 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) }] : [];
15888
+ if (!targets.length) return null;
15889
+ return React.createElement(ChatLinkCell, { targets, eventBus });
15613
15890
  };
15614
15891
  }
15615
15892
  function toRenderField(f, loaded, eventBus, mapsApiKey) {
@@ -15648,6 +15925,8 @@ function toRenderField(f, loaded, eventBus, mapsApiKey) {
15648
15925
  // FILE/CHAT_LINK solo son columnas → INPUT (el render lo define formatValue/build).
15649
15926
  moleculeType: f.moleculeType === "FILE" || isChatLink ? "INPUT" : f.moleculeType,
15650
15927
  showInTable: f.showInTable,
15928
+ summary: f.summary,
15929
+ // SPEC-093
15651
15930
  showInForm: f.showInForm,
15652
15931
  showInModes: f.showInModes,
15653
15932
  required: f.required,
@@ -15777,6 +16056,8 @@ function ReactiveCrud({ config, eventBus, autoOpenCreate, prefill, onCreated, on
15777
16056
  loading: crud.loading,
15778
16057
  filters: { skip: crud.skip, limit: crud.pageSize, all_data: false, filters: crud.filters },
15779
16058
  tableScroll: config.tableScroll,
16059
+ layout: config.layout,
16060
+ cardsBelow: config.cardsBelow,
15780
16061
  validationSchema,
15781
16062
  getRowKey: (row) => String(row[config.idField]),
15782
16063
  onCreate: config.data.create ? async (rows) => {
@@ -16046,12 +16327,18 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
16046
16327
  "button",
16047
16328
  {
16048
16329
  type: "button",
16049
- className: `px-chat-crud-item__action${a.intent === "danger" ? " is-danger" : ""}`,
16330
+ className: [
16331
+ "px-chat-crud-item__action",
16332
+ a.intent === "danger" ? "is-danger" : "",
16333
+ disabled && a.disabledReason ? "is-con-motivo" : ""
16334
+ ].filter(Boolean).join(" "),
16050
16335
  disabled,
16051
- title: disabled ? a.disabledReason : void 0,
16052
16336
  onClick: () => a.form ? setActiveAction({ action: a, row }) : crud.runEndpoints(a.endpoints ?? [a.endpoint], { ...row, id: row[config.idField] }),
16053
16337
  children: [
16054
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: a.label }),
16338
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-chat-crud-item__action-texts", children: [
16339
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: a.label }),
16340
+ disabled && a.disabledReason && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-crud-item__action-reason", children: a.disabledReason })
16341
+ ] }),
16055
16342
  done && /* @__PURE__ */ jsxRuntime.jsx(Icon$1, { type: "check-circle", width: 18, height: 18, className: "px-chat-crud-item__done", "aria-label": "Hecho" })
16056
16343
  ]
16057
16344
  },
@@ -16686,6 +16973,7 @@ function ChatNavigator({ config, eventBus }) {
16686
16973
  React.useEffect(() => {
16687
16974
  void load();
16688
16975
  }, [load]);
16976
+ const listaRef = React.useRef(null);
16689
16977
  const loadRef = React.useRef(load);
16690
16978
  loadRef.current = load;
16691
16979
  React.useEffect(() => {
@@ -16733,8 +17021,6 @@ function ChatNavigator({ config, eventBus }) {
16733
17021
  };
16734
17022
  const page = Math.floor(skip / pageSize) + 1;
16735
17023
  const totalPages = Math.max(1, Math.ceil(total / pageSize));
16736
- const canPrev = skip > 0;
16737
- const canNext = skip + pageSize < total;
16738
17024
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-chat-navigator", children: [
16739
17025
  config.search && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-chat-navigator__search", children: [
16740
17026
  /* @__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 +17100,126 @@ function ChatNavigator({ config, eventBus }) {
16814
17100
  loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-chat-navigator__state", children: "Cargando conversaciones\u2026" }),
16815
17101
  error && !loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-chat-navigator__state px-chat-chat-navigator__state--error", children: error }),
16816
17102
  !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",
17103
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-chat-navigator__list-wrap", ref: listaRef, children: [
17104
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "px-chat-chat-navigator__list", children: items.map((item) => {
17105
+ const isRenaming = renamingId === item.id;
17106
+ const displayTitle = item.title?.trim() || fallbackTitle(item);
17107
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "px-chat-chat-navigator__item", children: [
17108
+ isRenaming ? /* @__PURE__ */ jsxRuntime.jsx(
17109
+ "form",
17110
+ {
17111
+ className: "px-chat-chat-navigator__rename",
17112
+ onSubmit: (e) => {
17113
+ e.preventDefault();
17114
+ void submitRename(item.id);
17115
+ },
17116
+ children: /* @__PURE__ */ jsxRuntime.jsx(
17117
+ "input",
16884
17118
  {
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
17119
+ autoFocus: true,
17120
+ className: "px-chat-chat-navigator__rename-input",
17121
+ value: renameValue,
17122
+ onChange: (e) => setRenameValue(e.target.value),
17123
+ onBlur: () => void submitRename(item.id),
17124
+ "aria-label": labels.renameTitle ?? "Nuevo t\xEDtulo"
16889
17125
  }
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(
17126
+ )
17127
+ }
17128
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "px-chat-chat-navigator__open", onClick: () => open(item), children: [
17129
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-chat-chat-navigator__title", children: [
17130
+ displayTitle,
17131
+ item.unread_proactive_count && item.unread_proactive_count >= 1 ? /* @__PURE__ */ jsxRuntime.jsx(
16898
17132
  "span",
16899
17133
  {
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
17134
+ className: "px-chat-chat-navigator__badge-count",
17135
+ "aria-label": `${item.unread_proactive_count} mensajes nuevos sin leer`,
17136
+ title: `${item.unread_proactive_count} sin leer`,
17137
+ children: item.unread_proactive_count >= 100 ? "100+" : item.unread_proactive_count
16903
17138
  }
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" })
17139
+ ) : null
17140
+ ] }),
17141
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "px-chat-chat-navigator__meta", children: [
17142
+ item.agent_name ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-chat-navigator__agent", children: item.agent_name }) : null,
17143
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-chat-navigator__time", children: relativeTime(item.updated_date) }),
17144
+ (() => {
17145
+ const opt = item.rol_code ? (config.originActorOptions ?? []).find((o) => o.value === item.rol_code) : void 0;
17146
+ if (!opt) return null;
17147
+ const c = opt.color;
17148
+ const nameField = config.originActorNameField;
17149
+ const allyName = nameField ? item[nameField] : void 0;
17150
+ const label = allyName ? String(allyName) : opt.label;
17151
+ return /* @__PURE__ */ jsxRuntime.jsx(
17152
+ "span",
17153
+ {
17154
+ className: "px-chat-chat-navigator__origin-badge",
17155
+ style: c ? { color: c, background: `${c}22`, borderColor: `${c}55` } : void 0,
17156
+ title: label,
17157
+ children: label
17158
+ }
17159
+ );
17160
+ })(),
17161
+ (() => {
17162
+ const field = config.tagField;
17163
+ if (!field) return null;
17164
+ const value = item[field];
17165
+ if (!value) return null;
17166
+ const opt = (config.tagOptions ?? []).find((o) => o.value === String(value));
17167
+ if (!opt) return null;
17168
+ const c = opt.color;
17169
+ return /* @__PURE__ */ jsxRuntime.jsx(
17170
+ "span",
17171
+ {
17172
+ className: "px-chat-chat-navigator__origin-badge",
17173
+ style: c ? { color: c, background: `${c}22`, borderColor: `${c}55` } : void 0,
17174
+ title: opt.label,
17175
+ children: opt.label
17176
+ }
17177
+ );
17178
+ })(),
17179
+ item.service_status ? (() => {
17180
+ const opt = (config.serviceStatusOptions ?? []).find(
17181
+ (o) => o.value === item.service_status
17182
+ );
17183
+ const c = opt?.color;
17184
+ return /* @__PURE__ */ jsxRuntime.jsx(
17185
+ "span",
17186
+ {
17187
+ className: "px-chat-chat-navigator__service-badge",
17188
+ style: c ? { color: c, background: `${c}22`, borderColor: `${c}55` } : void 0,
17189
+ children: opt?.label ?? item.service_status
17190
+ }
17191
+ );
17192
+ })() : null,
17193
+ item.closed ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-chat-chat-navigator__badge", children: labels.closedBadge ?? "Cerrado" }) : null
16920
17194
  ] })
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
- )
17195
+ ] }),
17196
+ config.data.rename && !isRenaming && /* @__PURE__ */ jsxRuntime.jsx(
17197
+ "button",
17198
+ {
17199
+ type: "button",
17200
+ className: "px-chat-chat-navigator__rename-btn",
17201
+ "aria-label": "Renombrar",
17202
+ title: "Renombrar",
17203
+ onClick: () => startRename(item),
17204
+ 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: [
17205
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 20h9" }),
17206
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z" })
17207
+ ] })
17208
+ }
17209
+ )
17210
+ ] }, item.id);
17211
+ }) }),
17212
+ /* @__PURE__ */ jsxRuntime.jsx(ScrollToEnd, { hostRef: listaRef, selector: ".px-chat-chat-navigator__list", dep: `${items.length}:${loading}` })
16953
17213
  ] }),
17214
+ total > pageSize && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-chat-navigator__pager", children: /* @__PURE__ */ jsxRuntime.jsx(
17215
+ Pagination,
17216
+ {
17217
+ current: page,
17218
+ total: totalPages,
17219
+ onChange: (p) => setSkip((p - 1) * pageSize),
17220
+ showTotal: false
17221
+ }
17222
+ ) }),
16954
17223
  config.advancedFilters?.length ? /* @__PURE__ */ jsxRuntime.jsx(Drawer$1, { isOpen: advOpen, onClose: () => setAdvOpen(false), title: "Filtros avanzados", children: /* @__PURE__ */ jsxRuntime.jsx(
16955
17224
  Filter,
16956
17225
  {
@@ -24355,6 +24624,7 @@ exports.LumiDateInput = DateInput$1;
24355
24624
  exports.LumiDrawer = Drawer;
24356
24625
  exports.LumiDropdownMenu = DropdownMenu;
24357
24626
  exports.LumiDynamicForm = DynamicForm;
24627
+ exports.LumiFieldValue = FieldValue;
24358
24628
  exports.LumiFilter = Filter;
24359
24629
  exports.LumiFilterRow = FilterRow;
24360
24630
  exports.LumiFormCheckbox = Checkbox;
@@ -24373,6 +24643,7 @@ exports.LumiNotification = Notification;
24373
24643
  exports.LumiPagination = Pagination;
24374
24644
  exports.LumiPopover = Popover;
24375
24645
  exports.LumiQRCode = QRCode;
24646
+ exports.LumiRecordCard = RecordCard;
24376
24647
  exports.LumiSelect = Select$1;
24377
24648
  exports.LumiStepIndicator = StepIndicator;
24378
24649
  exports.LumiStepper = Stepper;