pixelon 0.3.80 → 0.3.81

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.
@@ -42,6 +42,7 @@ export declare const LUMI_ICON_TYPES: {
42
42
  readonly MAP_PIN: "map-pin";
43
43
  readonly USER_X: "user-x";
44
44
  readonly REFRESH: "refresh";
45
+ readonly TRUCK: "truck";
45
46
  readonly MESSAGE_SQUARE: "message-square";
46
47
  readonly MESSAGE_CIRCLE: "message-circle";
47
48
  readonly MAP: "map";
@@ -5,6 +5,12 @@ export interface SelectOption {
5
5
  label: string;
6
6
  disabled?: boolean;
7
7
  icon?: ReactNode;
8
+ /** SPEC-104: segunda línea, en gris y más pequeña. Para el PORQUÉ de la opción —quién la lleva,
9
+ * a qué distancia, si hay algún aviso— sin que compita con su nombre. */
10
+ sublabel?: string;
11
+ /** SPEC-104: icono por nombre del set de lumi. Existe además de `icon` porque las opciones que
12
+ * llegan de un endpoint son JSON y no pueden traer un nodo de React. */
13
+ iconName?: string;
8
14
  }
9
15
  /** Tipo de valor según el modo */
10
16
  export type SelectValue = string | number | (string | number)[];
@@ -89,7 +89,13 @@ export interface RowActionDef {
89
89
  label: string;
90
90
  icon?: string;
91
91
  intent?: 'primary' | 'secondary' | 'danger';
92
- visibleWhen?: RowActionVisibleWhen;
92
+ /** SPEC-103: una condición, o VARIAS unidas por «y». */
93
+ visibleWhen?: RowActionVisibleWhen | RowActionVisibleWhen[];
94
+ /** SPEC-103: la excepción. Si se cumplen TODAS, la acción no se muestra aunque `visibleWhen` dé
95
+ * por buena la fila. Es para las reglas con forma de «se ve en estos estados, salvo este caso»
96
+ * —p.ej. asignar grúa en PENDING_APPROVAL, salvo si quien pide es un invitado que todavía no ha
97
+ * aceptado el precio—, que con condiciones unidas por «y» no se pueden escribir. */
98
+ hiddenWhen?: RowActionVisibleWhen | RowActionVisibleWhen[];
93
99
  /** SPEC-007: la acción se muestra pero queda DESHABILITADA cuando `String(row[field])` está en
94
100
  * `in` (ej. "Iniciar" deshabilitado hasta que `has_inspection` sea true). */
95
101
  disabledWhen?: RowActionVisibleWhen;
@@ -10,6 +10,10 @@ export interface SelectOption {
10
10
  value: string | number;
11
11
  label: string;
12
12
  disabled?: boolean;
13
+ /** SPEC-104: segunda línea en gris — el porqué de la opción, sin competir con su nombre. */
14
+ sublabel?: string;
15
+ /** SPEC-104: nombre de icono del set de lumi (no un nodo: esto viaja como JSON desde el backend). */
16
+ iconName?: string;
13
17
  }
14
18
  /**
15
19
  * Configuración de un campo del formulario
package/dist/index.esm.js CHANGED
@@ -5739,6 +5739,9 @@ const LUMI_ICON_TYPES = {
5739
5739
  // Iconos para Agenda
5740
5740
  USER_X: "user-x",
5741
5741
  REFRESH: "refresh",
5742
+ // SPEC-104 — grúas: el selector de despacho necesitaba distinguir de un vistazo qué es cada
5743
+ // opción, y no había ningún icono de vehículo en el set.
5744
+ TRUCK: "truck",
5742
5745
  // Iconos para Chat
5743
5746
  MESSAGE_SQUARE: "message-square",
5744
5747
  MESSAGE_CIRCLE: "message-circle",
@@ -6449,6 +6452,27 @@ const BuildingIcon = (props) => /* @__PURE__ */ jsxs(
6449
6452
  ]
6450
6453
  }
6451
6454
  );
6455
+ const TruckIcon = (props) => /* @__PURE__ */ jsxs(
6456
+ "svg",
6457
+ {
6458
+ width: "16",
6459
+ height: "16",
6460
+ viewBox: "0 0 24 24",
6461
+ fill: "none",
6462
+ stroke: "currentColor",
6463
+ strokeWidth: "2",
6464
+ strokeLinecap: "round",
6465
+ strokeLinejoin: "round",
6466
+ ...props,
6467
+ children: [
6468
+ /* @__PURE__ */ jsx("path", { d: "M2 17V8h10v9" }),
6469
+ /* @__PURE__ */ jsx("path", { d: "M12 11h4l3 3v3" }),
6470
+ /* @__PURE__ */ jsx("circle", { cx: "7", cy: "18", r: "2" }),
6471
+ /* @__PURE__ */ jsx("circle", { cx: "17", cy: "18", r: "2" }),
6472
+ /* @__PURE__ */ jsx("path", { d: "M9 18h6" })
6473
+ ]
6474
+ }
6475
+ );
6452
6476
  const MapPinIcon = (props) => /* @__PURE__ */ jsxs(
6453
6477
  "svg",
6454
6478
  {
@@ -6567,6 +6591,7 @@ const lumiIconMap = {
6567
6591
  // Iconos para Servicios
6568
6592
  [LUMI_ICON_TYPES.BUILDING]: BuildingIcon,
6569
6593
  [LUMI_ICON_TYPES.MAP_PIN]: MapPinIcon,
6594
+ [LUMI_ICON_TYPES.TRUCK]: TruckIcon,
6570
6595
  // Iconos para Agenda
6571
6596
  [LUMI_ICON_TYPES.USER_X]: UserXIcon,
6572
6597
  [LUMI_ICON_TYPES.REFRESH]: RefreshIcon,
@@ -7738,8 +7763,11 @@ const Select$1 = forwardRef(
7738
7763
  `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__checkbox`,
7739
7764
  isSelected && `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__checkbox--checked`
7740
7765
  ].filter(Boolean).join(" "), children: isSelected && /* @__PURE__ */ jsx(Icon, { type: LUMI_ICON_TYPES.CHECK, size: "small" }) }) }),
7741
- option.icon && /* @__PURE__ */ jsx("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-icon`, children: option.icon }),
7742
- /* @__PURE__ */ jsx("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-label`, children: option.label }),
7766
+ (option.icon || option.iconName) && /* @__PURE__ */ jsx("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-icon`, children: option.icon ?? /* @__PURE__ */ jsx(Icon, { type: option.iconName }) }),
7767
+ /* @__PURE__ */ jsxs("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-texts`, children: [
7768
+ /* @__PURE__ */ jsx("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-label`, children: option.label }),
7769
+ option.sublabel && /* @__PURE__ */ jsx("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-sublabel`, children: option.sublabel })
7770
+ ] }),
7743
7771
  mode === "single" && isSelected && /* @__PURE__ */ jsx("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-check`, children: /* @__PURE__ */ jsx(Icon, { type: LUMI_ICON_TYPES.CHECK, size: "small" }) })
7744
7772
  ]
7745
7773
  },
@@ -14111,7 +14139,9 @@ async function loadOptions(eventBus, ep, ctx) {
14111
14139
  const arr = Array.isArray(data) ? data : [];
14112
14140
  return arr.map((item) => ({
14113
14141
  value: String(item[ep.value_field] ?? ""),
14114
- label: String(item[ep.label_field] ?? "")
14142
+ label: String(item[ep.label_field] ?? ""),
14143
+ ...item.sublabel ? { sublabel: String(item.sublabel) } : {},
14144
+ ...item.icon ? { iconName: String(item.icon) } : {}
14115
14145
  }));
14116
14146
  }
14117
14147
 
@@ -15938,9 +15968,12 @@ function toRenderField(f, loaded, eventBus, mapsApiKey) {
15938
15968
  formatValue
15939
15969
  };
15940
15970
  }
15971
+ const cumpleCondicion = (c, row) => c.in.includes(String(row[c.field]));
15972
+ const cumplenTodas = (c, row) => (Array.isArray(c) ? c : [c]).every((x) => cumpleCondicion(x, row));
15941
15973
  function matchesVisibleWhen(a, row) {
15974
+ if (a.hiddenWhen && cumplenTodas(a.hiddenWhen, row)) return false;
15942
15975
  if (!a.visibleWhen) return true;
15943
- return a.visibleWhen.in.includes(String(row[a.visibleWhen.field]));
15976
+ return cumplenTodas(a.visibleWhen, row);
15944
15977
  }
15945
15978
  function matchesDisabledWhen(a, row) {
15946
15979
  if (!a.disabledWhen) return false;
@@ -16297,12 +16330,27 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
16297
16330
  () => crud.rows.find((r) => String(r[config.idField]) === recordId),
16298
16331
  [crud.rows, config.idField, recordId]
16299
16332
  );
16333
+ const raiz = useRef(null);
16334
+ const [esUltima, setEsUltima] = useState(true);
16335
+ useEffect(() => {
16336
+ const calcular = () => {
16337
+ const el = raiz.current;
16338
+ if (!el) return;
16339
+ const hermanas = document.querySelectorAll(`[data-panel-record="${recordId}"]`);
16340
+ setEsUltima(hermanas.length === 0 || hermanas[hermanas.length - 1] === el);
16341
+ };
16342
+ calcular();
16343
+ const observador = new MutationObserver(calcular);
16344
+ observador.observe(document.body, { childList: true, subtree: true });
16345
+ return () => observador.disconnect();
16346
+ }, [recordId]);
16347
+ const gastada = !esUltima;
16300
16348
  const actions = useMemo(() => {
16301
- if (!row) return [];
16349
+ if (!row || gastada) return [];
16302
16350
  return (config.rowActions ?? []).filter((a) => !allowed || allowed.includes(a.key)).filter((a) => matchesVisibleWhen(a, row));
16303
- }, [config.rowActions, row, allowed]);
16351
+ }, [config.rowActions, row, allowed, gastada]);
16304
16352
  const title = recordLabel ?? config.title ?? "Servicio";
16305
- return /* @__PURE__ */ jsxs("div", { className: "px-chat-crud-item", children: [
16353
+ return /* @__PURE__ */ jsxs("div", { className: "px-chat-crud-item", ref: raiz, "data-panel-record": recordId, children: [
16306
16354
  /* @__PURE__ */ jsx("div", { className: "px-chat-crud-item__header", children: title }),
16307
16355
  crud.actionError && /* @__PURE__ */ jsxs("div", { className: "px-chat-crud-item__error", role: "alert", children: [
16308
16356
  /* @__PURE__ */ jsx("span", { children: crud.actionError }),
@@ -16310,6 +16358,7 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
16310
16358
  ] }),
16311
16359
  crud.loading && !row && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-item__hint", children: "Cargando\u2026" }),
16312
16360
  !crud.loading && !row && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-item__hint", children: "No se encontr\xF3 el servicio." }),
16361
+ gastada && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-item__hint", children: "Este paso ya se gestion\xF3. Contin\xFAa en el mensaje m\xE1s reciente." }),
16313
16362
  row && config.deadline && typeof row[config.deadline.field] === "string" && /* @__PURE__ */ jsx(
16314
16363
  DeadlineCountdown,
16315
16364
  {
package/dist/index.js CHANGED
@@ -5760,6 +5760,9 @@ const LUMI_ICON_TYPES = {
5760
5760
  // Iconos para Agenda
5761
5761
  USER_X: "user-x",
5762
5762
  REFRESH: "refresh",
5763
+ // SPEC-104 — grúas: el selector de despacho necesitaba distinguir de un vistazo qué es cada
5764
+ // opción, y no había ningún icono de vehículo en el set.
5765
+ TRUCK: "truck",
5763
5766
  // Iconos para Chat
5764
5767
  MESSAGE_SQUARE: "message-square",
5765
5768
  MESSAGE_CIRCLE: "message-circle",
@@ -6470,6 +6473,27 @@ const BuildingIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
6470
6473
  ]
6471
6474
  }
6472
6475
  );
6476
+ const TruckIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
6477
+ "svg",
6478
+ {
6479
+ width: "16",
6480
+ height: "16",
6481
+ viewBox: "0 0 24 24",
6482
+ fill: "none",
6483
+ stroke: "currentColor",
6484
+ strokeWidth: "2",
6485
+ strokeLinecap: "round",
6486
+ strokeLinejoin: "round",
6487
+ ...props,
6488
+ children: [
6489
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 17V8h10v9" }),
6490
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 11h4l3 3v3" }),
6491
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "7", cy: "18", r: "2" }),
6492
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "17", cy: "18", r: "2" }),
6493
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 18h6" })
6494
+ ]
6495
+ }
6496
+ );
6473
6497
  const MapPinIcon = (props) => /* @__PURE__ */ jsxRuntime.jsxs(
6474
6498
  "svg",
6475
6499
  {
@@ -6588,6 +6612,7 @@ const lumiIconMap = {
6588
6612
  // Iconos para Servicios
6589
6613
  [LUMI_ICON_TYPES.BUILDING]: BuildingIcon,
6590
6614
  [LUMI_ICON_TYPES.MAP_PIN]: MapPinIcon,
6615
+ [LUMI_ICON_TYPES.TRUCK]: TruckIcon,
6591
6616
  // Iconos para Agenda
6592
6617
  [LUMI_ICON_TYPES.USER_X]: UserXIcon,
6593
6618
  [LUMI_ICON_TYPES.REFRESH]: RefreshIcon,
@@ -7759,8 +7784,11 @@ const Select$1 = React.forwardRef(
7759
7784
  `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__checkbox`,
7760
7785
  isSelected && `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__checkbox--checked`
7761
7786
  ].filter(Boolean).join(" "), children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(Icon, { type: LUMI_ICON_TYPES.CHECK, size: "small" }) }) }),
7762
- option.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-icon`, children: option.icon }),
7763
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-label`, children: option.label }),
7787
+ (option.icon || option.iconName) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-icon`, children: option.icon ?? /* @__PURE__ */ jsxRuntime.jsx(Icon, { type: option.iconName }) }),
7788
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-texts`, children: [
7789
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-label`, children: option.label }),
7790
+ option.sublabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-sublabel`, children: option.sublabel })
7791
+ ] }),
7764
7792
  mode === "single" && isSelected && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${COMPONENT_PREFIX}${COMPONENT_TYPES.SELECT}__option-check`, children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { type: LUMI_ICON_TYPES.CHECK, size: "small" }) })
7765
7793
  ]
7766
7794
  },
@@ -14132,7 +14160,9 @@ async function loadOptions(eventBus, ep, ctx) {
14132
14160
  const arr = Array.isArray(data) ? data : [];
14133
14161
  return arr.map((item) => ({
14134
14162
  value: String(item[ep.value_field] ?? ""),
14135
- label: String(item[ep.label_field] ?? "")
14163
+ label: String(item[ep.label_field] ?? ""),
14164
+ ...item.sublabel ? { sublabel: String(item.sublabel) } : {},
14165
+ ...item.icon ? { iconName: String(item.icon) } : {}
14136
14166
  }));
14137
14167
  }
14138
14168
 
@@ -15959,9 +15989,12 @@ function toRenderField(f, loaded, eventBus, mapsApiKey) {
15959
15989
  formatValue
15960
15990
  };
15961
15991
  }
15992
+ const cumpleCondicion = (c, row) => c.in.includes(String(row[c.field]));
15993
+ const cumplenTodas = (c, row) => (Array.isArray(c) ? c : [c]).every((x) => cumpleCondicion(x, row));
15962
15994
  function matchesVisibleWhen(a, row) {
15995
+ if (a.hiddenWhen && cumplenTodas(a.hiddenWhen, row)) return false;
15963
15996
  if (!a.visibleWhen) return true;
15964
- return a.visibleWhen.in.includes(String(row[a.visibleWhen.field]));
15997
+ return cumplenTodas(a.visibleWhen, row);
15965
15998
  }
15966
15999
  function matchesDisabledWhen(a, row) {
15967
16000
  if (!a.disabledWhen) return false;
@@ -16318,12 +16351,27 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
16318
16351
  () => crud.rows.find((r) => String(r[config.idField]) === recordId),
16319
16352
  [crud.rows, config.idField, recordId]
16320
16353
  );
16354
+ const raiz = React.useRef(null);
16355
+ const [esUltima, setEsUltima] = React.useState(true);
16356
+ React.useEffect(() => {
16357
+ const calcular = () => {
16358
+ const el = raiz.current;
16359
+ if (!el) return;
16360
+ const hermanas = document.querySelectorAll(`[data-panel-record="${recordId}"]`);
16361
+ setEsUltima(hermanas.length === 0 || hermanas[hermanas.length - 1] === el);
16362
+ };
16363
+ calcular();
16364
+ const observador = new MutationObserver(calcular);
16365
+ observador.observe(document.body, { childList: true, subtree: true });
16366
+ return () => observador.disconnect();
16367
+ }, [recordId]);
16368
+ const gastada = !esUltima;
16321
16369
  const actions = React.useMemo(() => {
16322
- if (!row) return [];
16370
+ if (!row || gastada) return [];
16323
16371
  return (config.rowActions ?? []).filter((a) => !allowed || allowed.includes(a.key)).filter((a) => matchesVisibleWhen(a, row));
16324
- }, [config.rowActions, row, allowed]);
16372
+ }, [config.rowActions, row, allowed, gastada]);
16325
16373
  const title = recordLabel ?? config.title ?? "Servicio";
16326
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-crud-item", children: [
16374
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-crud-item", ref: raiz, "data-panel-record": recordId, children: [
16327
16375
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-item__header", children: title }),
16328
16376
  crud.actionError && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-crud-item__error", role: "alert", children: [
16329
16377
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: crud.actionError }),
@@ -16331,6 +16379,7 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
16331
16379
  ] }),
16332
16380
  crud.loading && !row && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-item__hint", children: "Cargando\u2026" }),
16333
16381
  !crud.loading && !row && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-item__hint", children: "No se encontr\xF3 el servicio." }),
16382
+ gastada && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-item__hint", children: "Este paso ya se gestion\xF3. Contin\xFAa en el mensaje m\xE1s reciente." }),
16334
16383
  row && config.deadline && typeof row[config.deadline.field] === "string" && /* @__PURE__ */ jsxRuntime.jsx(
16335
16384
  DeadlineCountdown,
16336
16385
  {