pixelon 0.3.76 → 0.3.77

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.
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Mensaje que se le muestra a la persona cuando una acción falla.
3
+ * `respaldo` es lo que se dice cuando no hay nada legible que contar.
4
+ */
5
+ export declare const mensajeDeError: (e: unknown, respaldo: string) => string;
@@ -11,6 +11,12 @@ export interface DropdownMenuOption {
11
11
  icon?: LumiIconType;
12
12
  /** Si la opción está deshabilitada */
13
13
  disabled?: boolean;
14
+ /**
15
+ * SPEC-090 T3 — por qué está deshabilitada. Se muestra bajo la etiqueta, no como tooltip:
16
+ * este menú se usa sobre todo desde el teléfono, y ahí un `title` no se lee nunca.
17
+ * Sin esto, una opción apagada es indistinguible de una rota.
18
+ */
19
+ disabledReason?: string;
14
20
  }
15
21
  /**
16
22
  * Props del componente DropdownMenu
@@ -239,6 +239,8 @@ export interface CrudProps<T extends Record<string, unknown>> {
239
239
  drawerId?: string;
240
240
  /** Función para determinar si la acción está deshabilitada */
241
241
  disabled?: (row: T) => boolean;
242
+ /** SPEC-090 T3 — por qué está deshabilitada, para esa fila. Se muestra bajo la etiqueta. */
243
+ disabledReason?: (row: T) => string | undefined;
242
244
  /** Función para determinar si la acción debe mostrarse */
243
245
  show?: (row: T) => boolean;
244
246
  }>;
package/dist/index.esm.js CHANGED
@@ -11686,11 +11686,12 @@ const DropdownMenu = ({
11686
11686
  children: options.map((option, index) => /* @__PURE__ */ jsxs(
11687
11687
  "button",
11688
11688
  {
11689
- className: `${baseClass}__option ${option.disabled ? `${baseClass}__option--disabled` : ""}`,
11689
+ className: `${baseClass}__option ${option.disabled ? `${baseClass}__option--disabled` : ""} ${option.disabled && option.disabledReason ? `${baseClass}__option--con-motivo` : ""}`,
11690
11690
  onClick: () => !option.disabled && handleOptionClick(option.onClick),
11691
11691
  disabled: option.disabled,
11692
11692
  role: "menuitem",
11693
11693
  type: "button",
11694
+ title: option.disabled && option.disabledReason ? option.disabledReason : void 0,
11694
11695
  children: [
11695
11696
  option.icon && /* @__PURE__ */ jsx(
11696
11697
  Icon,
@@ -11700,7 +11701,10 @@ const DropdownMenu = ({
11700
11701
  className: `${baseClass}__option-icon`
11701
11702
  }
11702
11703
  ),
11703
- /* @__PURE__ */ jsx("span", { className: `${baseClass}__option-label`, children: option.label })
11704
+ option.disabled && option.disabledReason ? /* @__PURE__ */ jsxs("span", { className: `${baseClass}__option-texts`, children: [
11705
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__option-label`, children: option.label }),
11706
+ /* @__PURE__ */ jsx("span", { className: `${baseClass}__option-reason`, children: option.disabledReason })
11707
+ ] }) : /* @__PURE__ */ jsx("span", { className: `${baseClass}__option-label`, children: option.label })
11704
11708
  ]
11705
11709
  },
11706
11710
  index
@@ -13316,7 +13320,9 @@ function Crud({
13316
13320
  action.onClick(row);
13317
13321
  }
13318
13322
  },
13319
- disabled: action.disabled ? action.disabled(row) : false
13323
+ disabled: action.disabled ? action.disabled(row) : false,
13324
+ // SPEC-090 T3 — una acción apagada sin motivo es indistinguible de una rota.
13325
+ disabledReason: action.disabledReason?.(row)
13320
13326
  });
13321
13327
  });
13322
13328
  if (menuOptions.length === 0) return null;
@@ -13790,6 +13796,30 @@ async function loadOptions(eventBus, ep) {
13790
13796
  }));
13791
13797
  }
13792
13798
 
13799
+ const esTextoUtil = (v) => typeof v === "string" && v.trim().length > 0;
13800
+ const buscarEnCuerpo = (cuerpo) => {
13801
+ if (!cuerpo || typeof cuerpo !== "object") return null;
13802
+ const c = cuerpo;
13803
+ const detalle = c.detail;
13804
+ if (esTextoUtil(detalle)) return detalle;
13805
+ if (detalle && typeof detalle === "object") {
13806
+ const d = detalle;
13807
+ if (esTextoUtil(d.message)) return d.message;
13808
+ }
13809
+ if (esTextoUtil(c.message)) return c.message;
13810
+ return null;
13811
+ };
13812
+ const ES_JERGA_HTTP = /^Request failed with status code \d+$/i;
13813
+ const mensajeDeError = (e, respaldo) => {
13814
+ const err = e;
13815
+ const delServidor = buscarEnCuerpo(err?.response?.data);
13816
+ if (delServidor) return delServidor;
13817
+ if (esTextoUtil(err?.message) && !ES_JERGA_HTTP.test(err.message.trim())) {
13818
+ return err.message;
13819
+ }
13820
+ return respaldo;
13821
+ };
13822
+
13793
13823
  const REFRESH_INTERVAL_MS = 2e4;
13794
13824
  function unwrap$2(raw) {
13795
13825
  if (raw && typeof raw === "object" && "response" in raw) {
@@ -13854,7 +13884,7 @@ function useReactiveCrud(config, eventBus) {
13854
13884
  if (myReq === reqIdRef.current) setState({ rows, total, loading: false, error: null });
13855
13885
  } catch (e) {
13856
13886
  if (myReq === reqIdRef.current) {
13857
- setState((s) => ({ ...s, loading: false, error: e instanceof Error ? e.message : "Error al cargar" }));
13887
+ setState((s) => ({ ...s, loading: false, error: mensajeDeError(e, "Error al cargar") }));
13858
13888
  }
13859
13889
  }
13860
13890
  }, [eventBus, config.data.list, skip, pageSize, filters]);
@@ -13898,7 +13928,7 @@ function useReactiveCrud(config, eventBus) {
13898
13928
  }
13899
13929
  await loadList();
13900
13930
  } catch (e) {
13901
- setActionError(e instanceof Error ? e.message : "La acci\xF3n no se pudo completar");
13931
+ setActionError(mensajeDeError(e, "La acci\xF3n no se pudo completar"));
13902
13932
  throw e;
13903
13933
  }
13904
13934
  },
@@ -14159,7 +14189,7 @@ function ActionFormDrawer({ action, row, idField, eventBus, onExecute, onClose }
14159
14189
  await onExecute(ctx);
14160
14190
  onClose();
14161
14191
  } catch (e) {
14162
- setFormError(e instanceof Error ? e.message : "La acci\xF3n no se pudo completar");
14192
+ setFormError(mensajeDeError(e, "La acci\xF3n no se pudo completar"));
14163
14193
  } finally {
14164
14194
  setSubmitting(false);
14165
14195
  }
@@ -15681,6 +15711,10 @@ function ReactiveCrud({ config, eventBus, autoOpenCreate, prefill, onCreated, on
15681
15711
  icon: a.icon,
15682
15712
  show: (row) => matchesVisibleWhen(a, row),
15683
15713
  disabled: (row) => matchesDisabledWhen(a, row),
15714
+ // SPEC-090 T3 — el `disabledReason` de la config ya existía, pero solo lo pintaba el panel
15715
+ // del chat; en la tabla el botón se apagaba mudo. Afecta también al de la inspección
15716
+ // («Falta el comprobante de pago del cliente», SPEC-066 T7), que tampoco se veía aquí.
15717
+ disabledReason: (row) => matchesDisabledWhen(a, row) ? a.disabledReason : void 0,
15684
15718
  onClick: (row) => a.form ? setActiveAction({ action: a, row }) : crud.runEndpoints(a.endpoints ?? [a.endpoint], { ...row, id: row[config.idField] })
15685
15719
  }));
15686
15720
  if (config.detail) {
@@ -15689,6 +15723,7 @@ function ReactiveCrud({ config, eventBus, autoOpenCreate, prefill, onCreated, on
15689
15723
  icon: "eye",
15690
15724
  show: () => true,
15691
15725
  disabled: () => false,
15726
+ disabledReason: () => void 0,
15692
15727
  onClick: (row) => setDetailRow(row)
15693
15728
  });
15694
15729
  }
package/dist/index.js CHANGED
@@ -11707,11 +11707,12 @@ const DropdownMenu = ({
11707
11707
  children: options.map((option, index) => /* @__PURE__ */ jsxRuntime.jsxs(
11708
11708
  "button",
11709
11709
  {
11710
- className: `${baseClass}__option ${option.disabled ? `${baseClass}__option--disabled` : ""}`,
11710
+ className: `${baseClass}__option ${option.disabled ? `${baseClass}__option--disabled` : ""} ${option.disabled && option.disabledReason ? `${baseClass}__option--con-motivo` : ""}`,
11711
11711
  onClick: () => !option.disabled && handleOptionClick(option.onClick),
11712
11712
  disabled: option.disabled,
11713
11713
  role: "menuitem",
11714
11714
  type: "button",
11715
+ title: option.disabled && option.disabledReason ? option.disabledReason : void 0,
11715
11716
  children: [
11716
11717
  option.icon && /* @__PURE__ */ jsxRuntime.jsx(
11717
11718
  Icon,
@@ -11721,7 +11722,10 @@ const DropdownMenu = ({
11721
11722
  className: `${baseClass}__option-icon`
11722
11723
  }
11723
11724
  ),
11724
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${baseClass}__option-label`, children: option.label })
11725
+ option.disabled && option.disabledReason ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `${baseClass}__option-texts`, children: [
11726
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${baseClass}__option-label`, children: option.label }),
11727
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${baseClass}__option-reason`, children: option.disabledReason })
11728
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${baseClass}__option-label`, children: option.label })
11725
11729
  ]
11726
11730
  },
11727
11731
  index
@@ -13337,7 +13341,9 @@ function Crud({
13337
13341
  action.onClick(row);
13338
13342
  }
13339
13343
  },
13340
- disabled: action.disabled ? action.disabled(row) : false
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)
13341
13347
  });
13342
13348
  });
13343
13349
  if (menuOptions.length === 0) return null;
@@ -13811,6 +13817,30 @@ async function loadOptions(eventBus, ep) {
13811
13817
  }));
13812
13818
  }
13813
13819
 
13820
+ const esTextoUtil = (v) => typeof v === "string" && v.trim().length > 0;
13821
+ const buscarEnCuerpo = (cuerpo) => {
13822
+ if (!cuerpo || typeof cuerpo !== "object") return null;
13823
+ const c = cuerpo;
13824
+ const detalle = c.detail;
13825
+ if (esTextoUtil(detalle)) return detalle;
13826
+ if (detalle && typeof detalle === "object") {
13827
+ const d = detalle;
13828
+ if (esTextoUtil(d.message)) return d.message;
13829
+ }
13830
+ if (esTextoUtil(c.message)) return c.message;
13831
+ return null;
13832
+ };
13833
+ const ES_JERGA_HTTP = /^Request failed with status code \d+$/i;
13834
+ const mensajeDeError = (e, respaldo) => {
13835
+ const err = e;
13836
+ const delServidor = buscarEnCuerpo(err?.response?.data);
13837
+ if (delServidor) return delServidor;
13838
+ if (esTextoUtil(err?.message) && !ES_JERGA_HTTP.test(err.message.trim())) {
13839
+ return err.message;
13840
+ }
13841
+ return respaldo;
13842
+ };
13843
+
13814
13844
  const REFRESH_INTERVAL_MS = 2e4;
13815
13845
  function unwrap$2(raw) {
13816
13846
  if (raw && typeof raw === "object" && "response" in raw) {
@@ -13875,7 +13905,7 @@ function useReactiveCrud(config, eventBus) {
13875
13905
  if (myReq === reqIdRef.current) setState({ rows, total, loading: false, error: null });
13876
13906
  } catch (e) {
13877
13907
  if (myReq === reqIdRef.current) {
13878
- setState((s) => ({ ...s, loading: false, error: e instanceof Error ? e.message : "Error al cargar" }));
13908
+ setState((s) => ({ ...s, loading: false, error: mensajeDeError(e, "Error al cargar") }));
13879
13909
  }
13880
13910
  }
13881
13911
  }, [eventBus, config.data.list, skip, pageSize, filters]);
@@ -13919,7 +13949,7 @@ function useReactiveCrud(config, eventBus) {
13919
13949
  }
13920
13950
  await loadList();
13921
13951
  } catch (e) {
13922
- setActionError(e instanceof Error ? e.message : "La acci\xF3n no se pudo completar");
13952
+ setActionError(mensajeDeError(e, "La acci\xF3n no se pudo completar"));
13923
13953
  throw e;
13924
13954
  }
13925
13955
  },
@@ -14180,7 +14210,7 @@ function ActionFormDrawer({ action, row, idField, eventBus, onExecute, onClose }
14180
14210
  await onExecute(ctx);
14181
14211
  onClose();
14182
14212
  } catch (e) {
14183
- setFormError(e instanceof Error ? e.message : "La acci\xF3n no se pudo completar");
14213
+ setFormError(mensajeDeError(e, "La acci\xF3n no se pudo completar"));
14184
14214
  } finally {
14185
14215
  setSubmitting(false);
14186
14216
  }
@@ -15702,6 +15732,10 @@ function ReactiveCrud({ config, eventBus, autoOpenCreate, prefill, onCreated, on
15702
15732
  icon: a.icon,
15703
15733
  show: (row) => matchesVisibleWhen(a, row),
15704
15734
  disabled: (row) => matchesDisabledWhen(a, row),
15735
+ // SPEC-090 T3 — el `disabledReason` de la config ya existía, pero solo lo pintaba el panel
15736
+ // del chat; en la tabla el botón se apagaba mudo. Afecta también al de la inspección
15737
+ // («Falta el comprobante de pago del cliente», SPEC-066 T7), que tampoco se veía aquí.
15738
+ disabledReason: (row) => matchesDisabledWhen(a, row) ? a.disabledReason : void 0,
15705
15739
  onClick: (row) => a.form ? setActiveAction({ action: a, row }) : crud.runEndpoints(a.endpoints ?? [a.endpoint], { ...row, id: row[config.idField] })
15706
15740
  }));
15707
15741
  if (config.detail) {
@@ -15710,6 +15744,7 @@ function ReactiveCrud({ config, eventBus, autoOpenCreate, prefill, onCreated, on
15710
15744
  icon: "eye",
15711
15745
  show: () => true,
15712
15746
  disabled: () => false,
15747
+ disabledReason: () => void 0,
15713
15748
  onClick: (row) => setDetailRow(row)
15714
15749
  });
15715
15750
  }