pixelon 0.3.74 → 0.3.76

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,9 @@
1
+ import './DeadlineCountdown.scss';
2
+ interface Props {
3
+ /** Hora límite en ISO. */
4
+ deadline: string;
5
+ label?: string;
6
+ expiredLabel?: string;
7
+ }
8
+ export declare function DeadlineCountdown({ deadline, label, expiredLabel }: Props): import("react/jsx-runtime").JSX.Element | null;
9
+ export default DeadlineCountdown;
@@ -144,5 +144,22 @@ export interface ReactiveCrudConfig {
144
144
  x?: number | string;
145
145
  y?: number | string;
146
146
  };
147
+ /** SPEC-087 — cuenta atrás en el panel de un registro. Ver `DeadlineConfig`. */
148
+ deadline?: DeadlineConfig;
149
+ }
150
+ /** SPEC-087 — fecha límite de un registro, pintada como cuenta atrás junto a sus acciones.
151
+ *
152
+ * Es deliberadamente genérico: dice «esta tarjeta tiene una fecha límite», no de qué es el plazo.
153
+ * El campo apunta a un dato de la fila con un INSTANTE en ISO, nunca a una duración: una duración
154
+ * se queda vieja en cuanto se guarda, y estas cards viven para siempre en el chat.
155
+ *
156
+ * Si el campo viene vacío no se pinta nada — no hay plazo que enseñar. */
157
+ export interface DeadlineConfig {
158
+ /** Campo de la fila con la hora límite en ISO. */
159
+ field: string;
160
+ /** Texto sobre el contador. Ej: 'Para que salga la grúa'. */
161
+ label?: string;
162
+ /** Qué decir cuando ya venció. Se sigue mostrando: desaparecer sería volver al silencio. */
163
+ expiredLabel?: string;
147
164
  }
148
165
  export type { FilterManager };
package/dist/index.d.ts CHANGED
@@ -40,7 +40,7 @@ export type { ChatNavigatorConfig, ChatNavigatorItem, ChatNavigatorEndpoint } fr
40
40
  export { UserTools } from './components/molecules/chat/StructuredContent/components/UserTools/UserTools';
41
41
  export type { UserToolsConfig, UserToolItem } from './components/molecules/chat/StructuredContent/components/UserTools/types';
42
42
  export { ReactiveCrud } from './components/molecules/chat/StructuredContent/components/ReactiveCrud/ReactiveCrud';
43
- export type { ReactiveCrudConfig, ReactiveCrudField, RowActionDef, EndpointRef, CrudMolecule, CrudFieldOptionsEndpoint, RowActionVisibleWhen, } from './components/molecules/chat/StructuredContent/components/ReactiveCrud/types';
43
+ export type { ReactiveCrudConfig, ReactiveCrudField, DeadlineConfig, RowActionDef, EndpointRef, CrudMolecule, CrudFieldOptionsEndpoint, RowActionVisibleWhen, } from './components/molecules/chat/StructuredContent/components/ReactiveCrud/types';
44
44
  export { Chat } from './components/organisms/chat/Chat';
45
45
  export type { ChatProps } from './components/organisms/chat/Chat';
46
46
  export type { ChatMessage } from './core/chat';
package/dist/index.esm.js CHANGED
@@ -15871,6 +15871,41 @@ function ReactiveCrudChat({ component, eventBus, assistantMessageId }) {
15871
15871
  ] });
15872
15872
  }
15873
15873
 
15874
+ function restante(hasta) {
15875
+ return Math.max(0, hasta - Date.now());
15876
+ }
15877
+ function formatear(ms) {
15878
+ const total = Math.floor(ms / 1e3);
15879
+ const min = Math.floor(total / 60);
15880
+ const seg = total % 60;
15881
+ return `${String(min).padStart(2, "0")}:${String(seg).padStart(2, "0")}`;
15882
+ }
15883
+ function DeadlineCountdown({ deadline, label, expiredLabel }) {
15884
+ const hasta = new Date(deadline).getTime();
15885
+ const valida = !Number.isNaN(hasta);
15886
+ const [ms, setMs] = useState(() => valida ? restante(hasta) : 0);
15887
+ useEffect(() => {
15888
+ if (!valida) return;
15889
+ setMs(restante(hasta));
15890
+ const id = setInterval(() => setMs(restante(hasta)), 1e3);
15891
+ return () => clearInterval(id);
15892
+ }, [hasta, valida]);
15893
+ if (!valida) return null;
15894
+ const vencido = ms <= 0;
15895
+ const apurado = !vencido && ms <= 6e4;
15896
+ return /* @__PURE__ */ jsx(
15897
+ "div",
15898
+ {
15899
+ className: `px-deadline${vencido ? " is-expired" : ""}${apurado ? " is-urgent" : ""}`,
15900
+ role: "status",
15901
+ children: vencido ? /* @__PURE__ */ jsx("span", { className: "px-deadline__expired", children: expiredLabel ?? "El plazo venci\xF3" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
15902
+ label && /* @__PURE__ */ jsx("span", { className: "px-deadline__label", children: label }),
15903
+ /* @__PURE__ */ jsx("span", { className: "px-deadline__time", children: formatear(ms) })
15904
+ ] })
15905
+ }
15906
+ );
15907
+ }
15908
+
15874
15909
  function ReactiveCrudItemChat({ component, eventBus }) {
15875
15910
  const { resolveModuleConfig } = useChatConfig();
15876
15911
  const props = component.props ?? {};
@@ -15938,6 +15973,14 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
15938
15973
  ] }),
15939
15974
  crud.loading && !row && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-item__hint", children: "Cargando\u2026" }),
15940
15975
  !crud.loading && !row && /* @__PURE__ */ jsx("div", { className: "px-chat-crud-item__hint", children: "No se encontr\xF3 el servicio." }),
15976
+ row && config.deadline && typeof row[config.deadline.field] === "string" && /* @__PURE__ */ jsx(
15977
+ DeadlineCountdown,
15978
+ {
15979
+ deadline: row[config.deadline.field],
15980
+ label: config.deadline.label,
15981
+ expiredLabel: config.deadline.expiredLabel
15982
+ }
15983
+ ),
15941
15984
  row && /* @__PURE__ */ jsxs("div", { className: "px-chat-crud-item__actions", children: [
15942
15985
  config.detail && /* @__PURE__ */ jsx("button", { type: "button", className: "px-chat-crud-item__action", onClick: () => setDetailRow(row), children: "Ver" }),
15943
15986
  actions.map((a) => {
@@ -16018,7 +16061,8 @@ function NegotiationPanel({ config, recordId, recordLabel, allowed, eventBus })
16018
16061
  const hasOffer = proposed != null && proposed !== "";
16019
16062
  const canPropose = !allowed || allowed.includes("propose_price");
16020
16063
  const agreed = row?.price_amount;
16021
- const cerrada = agreed != null && agreed !== "";
16064
+ const esperandoRespuesta = ["QUOTED", "PRE_ASSIGNED"].includes(String(row?.status ?? ""));
16065
+ const cerrada = !esperandoRespuesta && agreed != null && agreed !== "";
16022
16066
  const ofertaMia = hasOffer && !myTurn;
16023
16067
  const quienOfrece = ofertaMia ? "tu oferta" : `oferta de ${ACTOR_LABEL[proposer ?? ""] ?? "la otra parte"}`;
16024
16068
  return /* @__PURE__ */ jsxs("div", { className: "px-chat-price-neg", children: [
package/dist/index.js CHANGED
@@ -15892,6 +15892,41 @@ function ReactiveCrudChat({ component, eventBus, assistantMessageId }) {
15892
15892
  ] });
15893
15893
  }
15894
15894
 
15895
+ function restante(hasta) {
15896
+ return Math.max(0, hasta - Date.now());
15897
+ }
15898
+ function formatear(ms) {
15899
+ const total = Math.floor(ms / 1e3);
15900
+ const min = Math.floor(total / 60);
15901
+ const seg = total % 60;
15902
+ return `${String(min).padStart(2, "0")}:${String(seg).padStart(2, "0")}`;
15903
+ }
15904
+ function DeadlineCountdown({ deadline, label, expiredLabel }) {
15905
+ const hasta = new Date(deadline).getTime();
15906
+ const valida = !Number.isNaN(hasta);
15907
+ const [ms, setMs] = React.useState(() => valida ? restante(hasta) : 0);
15908
+ React.useEffect(() => {
15909
+ if (!valida) return;
15910
+ setMs(restante(hasta));
15911
+ const id = setInterval(() => setMs(restante(hasta)), 1e3);
15912
+ return () => clearInterval(id);
15913
+ }, [hasta, valida]);
15914
+ if (!valida) return null;
15915
+ const vencido = ms <= 0;
15916
+ const apurado = !vencido && ms <= 6e4;
15917
+ return /* @__PURE__ */ jsxRuntime.jsx(
15918
+ "div",
15919
+ {
15920
+ className: `px-deadline${vencido ? " is-expired" : ""}${apurado ? " is-urgent" : ""}`,
15921
+ role: "status",
15922
+ children: vencido ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-deadline__expired", children: expiredLabel ?? "El plazo venci\xF3" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15923
+ label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-deadline__label", children: label }),
15924
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-deadline__time", children: formatear(ms) })
15925
+ ] })
15926
+ }
15927
+ );
15928
+ }
15929
+
15895
15930
  function ReactiveCrudItemChat({ component, eventBus }) {
15896
15931
  const { resolveModuleConfig } = useChatConfig();
15897
15932
  const props = component.props ?? {};
@@ -15959,6 +15994,14 @@ function ServiceActionsPanel({ config, recordId, recordLabel, allowed, eventBus
15959
15994
  ] }),
15960
15995
  crud.loading && !row && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-item__hint", children: "Cargando\u2026" }),
15961
15996
  !crud.loading && !row && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-chat-crud-item__hint", children: "No se encontr\xF3 el servicio." }),
15997
+ row && config.deadline && typeof row[config.deadline.field] === "string" && /* @__PURE__ */ jsxRuntime.jsx(
15998
+ DeadlineCountdown,
15999
+ {
16000
+ deadline: row[config.deadline.field],
16001
+ label: config.deadline.label,
16002
+ expiredLabel: config.deadline.expiredLabel
16003
+ }
16004
+ ),
15962
16005
  row && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-crud-item__actions", children: [
15963
16006
  config.detail && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "px-chat-crud-item__action", onClick: () => setDetailRow(row), children: "Ver" }),
15964
16007
  actions.map((a) => {
@@ -16039,7 +16082,8 @@ function NegotiationPanel({ config, recordId, recordLabel, allowed, eventBus })
16039
16082
  const hasOffer = proposed != null && proposed !== "";
16040
16083
  const canPropose = !allowed || allowed.includes("propose_price");
16041
16084
  const agreed = row?.price_amount;
16042
- const cerrada = agreed != null && agreed !== "";
16085
+ const esperandoRespuesta = ["QUOTED", "PRE_ASSIGNED"].includes(String(row?.status ?? ""));
16086
+ const cerrada = !esperandoRespuesta && agreed != null && agreed !== "";
16043
16087
  const ofertaMia = hasOffer && !myTurn;
16044
16088
  const quienOfrece = ofertaMia ? "tu oferta" : `oferta de ${ACTOR_LABEL[proposer ?? ""] ?? "la otra parte"}`;
16045
16089
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-chat-price-neg", children: [