pixelon 0.3.28 → 0.3.29

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
+ import React from 'react';
2
+ import type { UIComponentData } from './types';
3
+ export declare const AnsweredSummary: React.FC<{
4
+ component: UIComponentData;
5
+ }>;
@@ -1,7 +1,24 @@
1
1
  import type { ChatEventBus } from '../../../../../core/eventBus';
2
+ export interface ComponentAction {
3
+ id: string;
4
+ label: string;
5
+ kind?: string;
6
+ emits_value?: boolean;
7
+ value?: unknown;
8
+ intent?: 'primary' | 'secondary' | 'danger';
9
+ }
2
10
  export interface UIComponentData {
3
11
  type: string;
4
12
  props: Record<string, unknown>;
13
+ status?: string;
14
+ visibility_policy?: 'hide_on_terminal' | 'keep_visible' | 'collapse';
15
+ actions?: ComponentAction[];
16
+ /** SPEC-023: respuesta persistida por el backend al cerrar el componente */
17
+ selected_values?: Array<{
18
+ value: unknown;
19
+ label: string;
20
+ }>;
21
+ /** @deprecated contrato legacy (pre SPEC-023); usar status */
5
22
  answered?: boolean;
6
23
  }
7
24
  export interface ChatComponentProps {
@@ -10,4 +27,5 @@ export interface ChatComponentProps {
10
27
  assistantMessageId?: string;
11
28
  }
12
29
  export type OnConfirm = (value: unknown, label: string) => void;
30
+ export declare const TERMINAL_STATUS: Set<string>;
13
31
  export declare const DISPLAY_COMPONENTS: Set<string>;
@@ -6,7 +6,7 @@ interface UseComponentInteractionParams {
6
6
  assistantMessageId: string;
7
7
  }
8
8
  export declare function useComponentInteraction({ eventBus, fieldName, componentType, assistantMessageId, }: UseComponentInteractionParams): {
9
- emitInteraction: (value: unknown, label: string) => void;
9
+ emitInteraction: (value: unknown, label: string, actionId?: string) => void;
10
10
  isConfirmed: boolean;
11
11
  };
12
12
  export {};
@@ -26,6 +26,8 @@ export type ChatEvents = {
26
26
  label: string;
27
27
  componentType: string;
28
28
  assistantMessageId: string;
29
+ /** SPEC-023 T8: id de la Action disparada (opcional) */
30
+ actionId?: string;
29
31
  };
30
32
  /** Consumer -> Chat: update the displayed messages */
31
33
  messagesUpdated: {
package/dist/index.esm.js CHANGED
@@ -1618,8 +1618,47 @@ function parseContent(content) {
1618
1618
  return null;
1619
1619
  }
1620
1620
 
1621
+ const TERMINAL_STATUS = /* @__PURE__ */ new Set(["answered", "cancelled", "expired"]);
1621
1622
  const DISPLAY_COMPONENTS = /* @__PURE__ */ new Set(["card", "badge", "reactive_form", "carousel"]);
1622
1623
 
1624
+ const AnsweredSummary = ({ component }) => {
1625
+ const status = component.status;
1626
+ const values = component.selected_values ?? [];
1627
+ const labels = values.map((v) => v.label).filter(Boolean).join(", ");
1628
+ let icon = "\u2713";
1629
+ let text = labels || "Respondido";
1630
+ let color = "#3a8a3a";
1631
+ if (status === "cancelled") {
1632
+ icon = "\u2715";
1633
+ text = "Cancelado";
1634
+ color = "#999";
1635
+ } else if (status === "expired") {
1636
+ icon = "\u231B";
1637
+ text = "Expir\xF3";
1638
+ color = "#b58a00";
1639
+ }
1640
+ return /* @__PURE__ */ jsxs(
1641
+ "div",
1642
+ {
1643
+ style: {
1644
+ display: "inline-flex",
1645
+ alignItems: "center",
1646
+ gap: "6px",
1647
+ padding: "6px 12px",
1648
+ borderRadius: "999px",
1649
+ background: "rgba(0,0,0,0.04)",
1650
+ color,
1651
+ fontSize: "0.875rem",
1652
+ fontWeight: 500
1653
+ },
1654
+ children: [
1655
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: icon }),
1656
+ /* @__PURE__ */ jsx("span", { children: text })
1657
+ ]
1658
+ }
1659
+ );
1660
+ };
1661
+
1623
1662
  const useAppHeight$5 = () => {
1624
1663
  useEffect(() => {
1625
1664
  const setDrawerHeight = () => {
@@ -1796,13 +1835,14 @@ function useComponentInteraction({
1796
1835
  }) {
1797
1836
  const [isConfirmed, setIsConfirmed] = useState(false);
1798
1837
  const emitInteraction = useCallback(
1799
- (value, label) => {
1838
+ (value, label, actionId) => {
1800
1839
  eventBus?.emit(CHAT_EVENT.COMPONENT_INTERACTION, {
1801
1840
  fieldName,
1802
1841
  value,
1803
1842
  label,
1804
1843
  componentType,
1805
- assistantMessageId
1844
+ assistantMessageId,
1845
+ actionId
1806
1846
  });
1807
1847
  setIsConfirmed(true);
1808
1848
  },
@@ -2639,6 +2679,22 @@ const ConfirmButton = ({
2639
2679
  assistantMessageId
2640
2680
  });
2641
2681
  if (isConfirmed) return null;
2682
+ const actions = component.actions;
2683
+ if (actions && actions.length > 0) {
2684
+ return /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap" }, children: actions.map((a) => /* @__PURE__ */ jsx(
2685
+ Button$1,
2686
+ {
2687
+ variant: a.intent === "primary" ? "primary" : "secondary",
2688
+ onClick: () => emitInteraction(
2689
+ a.emits_value ? component.props.value : a.value,
2690
+ a.label,
2691
+ a.id
2692
+ ),
2693
+ children: a.label
2694
+ },
2695
+ a.id
2696
+ )) });
2697
+ }
2642
2698
  return /* @__PURE__ */ jsx(
2643
2699
  Button$1,
2644
2700
  {
@@ -5098,7 +5154,15 @@ const COMPONENT_MAP = {
5098
5154
  };
5099
5155
  function renderComponent(props) {
5100
5156
  const { component } = props;
5101
- if (component.answered && !DISPLAY_COMPONENTS.has(component.type)) {
5157
+ if (component.status) {
5158
+ if (TERMINAL_STATUS.has(component.status)) {
5159
+ const policy = component.visibility_policy ?? "hide_on_terminal";
5160
+ if (policy === "hide_on_terminal") return null;
5161
+ if (component.selected_values && component.selected_values.length > 0) {
5162
+ return /* @__PURE__ */ jsx(AnsweredSummary, { component });
5163
+ }
5164
+ }
5165
+ } else if (component.answered && !DISPLAY_COMPONENTS.has(component.type)) {
5102
5166
  return null;
5103
5167
  }
5104
5168
  const Component = COMPONENT_MAP[component.type];
package/dist/index.js CHANGED
@@ -1639,8 +1639,47 @@ function parseContent(content) {
1639
1639
  return null;
1640
1640
  }
1641
1641
 
1642
+ const TERMINAL_STATUS = /* @__PURE__ */ new Set(["answered", "cancelled", "expired"]);
1642
1643
  const DISPLAY_COMPONENTS = /* @__PURE__ */ new Set(["card", "badge", "reactive_form", "carousel"]);
1643
1644
 
1645
+ const AnsweredSummary = ({ component }) => {
1646
+ const status = component.status;
1647
+ const values = component.selected_values ?? [];
1648
+ const labels = values.map((v) => v.label).filter(Boolean).join(", ");
1649
+ let icon = "\u2713";
1650
+ let text = labels || "Respondido";
1651
+ let color = "#3a8a3a";
1652
+ if (status === "cancelled") {
1653
+ icon = "\u2715";
1654
+ text = "Cancelado";
1655
+ color = "#999";
1656
+ } else if (status === "expired") {
1657
+ icon = "\u231B";
1658
+ text = "Expir\xF3";
1659
+ color = "#b58a00";
1660
+ }
1661
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1662
+ "div",
1663
+ {
1664
+ style: {
1665
+ display: "inline-flex",
1666
+ alignItems: "center",
1667
+ gap: "6px",
1668
+ padding: "6px 12px",
1669
+ borderRadius: "999px",
1670
+ background: "rgba(0,0,0,0.04)",
1671
+ color,
1672
+ fontSize: "0.875rem",
1673
+ fontWeight: 500
1674
+ },
1675
+ children: [
1676
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: icon }),
1677
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: text })
1678
+ ]
1679
+ }
1680
+ );
1681
+ };
1682
+
1644
1683
  const useAppHeight$5 = () => {
1645
1684
  React.useEffect(() => {
1646
1685
  const setDrawerHeight = () => {
@@ -1817,13 +1856,14 @@ function useComponentInteraction({
1817
1856
  }) {
1818
1857
  const [isConfirmed, setIsConfirmed] = React.useState(false);
1819
1858
  const emitInteraction = React.useCallback(
1820
- (value, label) => {
1859
+ (value, label, actionId) => {
1821
1860
  eventBus?.emit(CHAT_EVENT.COMPONENT_INTERACTION, {
1822
1861
  fieldName,
1823
1862
  value,
1824
1863
  label,
1825
1864
  componentType,
1826
- assistantMessageId
1865
+ assistantMessageId,
1866
+ actionId
1827
1867
  });
1828
1868
  setIsConfirmed(true);
1829
1869
  },
@@ -2660,6 +2700,22 @@ const ConfirmButton = ({
2660
2700
  assistantMessageId
2661
2701
  });
2662
2702
  if (isConfirmed) return null;
2703
+ const actions = component.actions;
2704
+ if (actions && actions.length > 0) {
2705
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: "8px", flexWrap: "wrap" }, children: actions.map((a) => /* @__PURE__ */ jsxRuntime.jsx(
2706
+ Button$1,
2707
+ {
2708
+ variant: a.intent === "primary" ? "primary" : "secondary",
2709
+ onClick: () => emitInteraction(
2710
+ a.emits_value ? component.props.value : a.value,
2711
+ a.label,
2712
+ a.id
2713
+ ),
2714
+ children: a.label
2715
+ },
2716
+ a.id
2717
+ )) });
2718
+ }
2663
2719
  return /* @__PURE__ */ jsxRuntime.jsx(
2664
2720
  Button$1,
2665
2721
  {
@@ -5119,7 +5175,15 @@ const COMPONENT_MAP = {
5119
5175
  };
5120
5176
  function renderComponent(props) {
5121
5177
  const { component } = props;
5122
- if (component.answered && !DISPLAY_COMPONENTS.has(component.type)) {
5178
+ if (component.status) {
5179
+ if (TERMINAL_STATUS.has(component.status)) {
5180
+ const policy = component.visibility_policy ?? "hide_on_terminal";
5181
+ if (policy === "hide_on_terminal") return null;
5182
+ if (component.selected_values && component.selected_values.length > 0) {
5183
+ return /* @__PURE__ */ jsxRuntime.jsx(AnsweredSummary, { component });
5184
+ }
5185
+ }
5186
+ } else if (component.answered && !DISPLAY_COMPONENTS.has(component.type)) {
5123
5187
  return null;
5124
5188
  }
5125
5189
  const Component = COMPONENT_MAP[component.type];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixelon",
3
- "version": "0.3.28",
3
+ "version": "0.3.29",
4
4
  "description": "Design system de la marca goluti",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,6 +24,8 @@
24
24
  "test:run": "vitest run",
25
25
  "mobile": "npx storybook dev -p 6006",
26
26
  "get:ip": "ipconfig getifaddr en0",
27
+ "gen:ux-types": "node scripts/gen-ux-types.mjs",
28
+ "gen:ux-types:check": "node scripts/gen-ux-types.mjs --check",
27
29
  "prepublishOnly": "npm run build:lib"
28
30
  },
29
31
  "keywords": [