@devpablocristo/modules-ai-console 0.1.0

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.
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@devpablocristo/modules-ai-console",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/index.ts"
8
+ },
9
+ "peerDependencies": {
10
+ "react": "^18.0.0 || ^19.0.0",
11
+ "react-dom": "^18.0.0 || ^19.0.0"
12
+ },
13
+ "scripts": {
14
+ "typecheck": "tsc --noEmit",
15
+ "test": "vitest run --environment jsdom --globals --passWithNoTests"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^24.0.0",
19
+ "@types/react": "^18.3.0",
20
+ "@types/react-dom": "^18.3.0",
21
+ "jsdom": "^26.1.0",
22
+ "react": "^18.3.1",
23
+ "react-dom": "^18.3.1",
24
+ "typescript": "^5.8.0",
25
+ "vitest": "^3.2.4"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "files": [
31
+ "src"
32
+ ]
33
+ }
@@ -0,0 +1,80 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ export type CopilotRelatedInsightItem = {
4
+ id: string;
5
+ title: string;
6
+ href?: string;
7
+ };
8
+
9
+ export type CopilotResponsePanelProps = {
10
+ answer: string;
11
+ data: unknown;
12
+ sources: unknown;
13
+ warnings: unknown;
14
+ relatedInsightsCount: number;
15
+ relatedInsights: CopilotRelatedInsightItem[];
16
+ relatedInsightsTitle?: string;
17
+ relatedInsightsAction?: ReactNode;
18
+ emptyRelatedInsightsMessage?: string;
19
+ renderRelatedInsight?: (item: CopilotRelatedInsightItem) => ReactNode;
20
+ };
21
+
22
+ function JsonSection({ title, value }: { title: string; value: unknown }) {
23
+ return (
24
+ <div className="border rounded-md p-4">
25
+ <h3 className="font-semibold">{title}</h3>
26
+ <pre className="text-xs text-slate-700 whitespace-pre-wrap">{JSON.stringify(value, null, 2)}</pre>
27
+ </div>
28
+ );
29
+ }
30
+
31
+ export function CopilotResponsePanel({
32
+ answer,
33
+ data,
34
+ sources,
35
+ warnings,
36
+ relatedInsightsCount,
37
+ relatedInsights,
38
+ relatedInsightsTitle = "Insights Relacionados",
39
+ relatedInsightsAction,
40
+ emptyRelatedInsightsMessage = "No hay insights activos.",
41
+ renderRelatedInsight,
42
+ }: CopilotResponsePanelProps) {
43
+ return (
44
+ <div className="grid grid-cols-1 gap-4">
45
+ <div className="border rounded-md p-4">
46
+ <h3 className="font-semibold">Respuesta</h3>
47
+ <p className="text-sm text-slate-700">{answer}</p>
48
+ </div>
49
+ <JsonSection title="Datos" value={data} />
50
+ <JsonSection title="Fuentes" value={sources} />
51
+ <JsonSection title="Advertencias" value={warnings} />
52
+ <div className="border rounded-md p-4">
53
+ <div className="flex items-center justify-between">
54
+ <h3 className="font-semibold">{relatedInsightsTitle}</h3>
55
+ {relatedInsightsAction}
56
+ </div>
57
+ <div className="mt-2 text-sm text-slate-700">Relacionados: {relatedInsightsCount}</div>
58
+ {relatedInsights.length === 0 ? (
59
+ <div className="mt-2 text-sm text-slate-500">{emptyRelatedInsightsMessage}</div>
60
+ ) : (
61
+ <div className="mt-3 grid grid-cols-1 gap-2">
62
+ {relatedInsights.map((item) =>
63
+ renderRelatedInsight ? (
64
+ renderRelatedInsight(item)
65
+ ) : (
66
+ <a
67
+ key={item.id}
68
+ className="rounded-md border px-3 py-2 text-sm text-slate-700 hover:bg-slate-50"
69
+ href={item.href || "#"}
70
+ >
71
+ {item.title}
72
+ </a>
73
+ ),
74
+ )}
75
+ </div>
76
+ )}
77
+ </div>
78
+ </div>
79
+ );
80
+ }
@@ -0,0 +1,83 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ export type InsightListItem = {
4
+ id: string;
5
+ title: string;
6
+ summary: string;
7
+ badge?: string;
8
+ impact?: string | null;
9
+ metadata?: string[];
10
+ ctaLabel?: string | null;
11
+ action?: ReactNode;
12
+ };
13
+
14
+ export type InsightCardsListProps = {
15
+ title: string;
16
+ items: InsightListItem[];
17
+ emptyMessage: string;
18
+ collapsedCount?: number;
19
+ expanded?: boolean;
20
+ onToggleExpanded?: () => void;
21
+ showAllLabel?: string;
22
+ showLessLabel?: string;
23
+ };
24
+
25
+ export function InsightCardsList({
26
+ title,
27
+ items,
28
+ emptyMessage,
29
+ collapsedCount,
30
+ expanded = false,
31
+ onToggleExpanded,
32
+ showAllLabel = "Mostrar todos",
33
+ showLessLabel = "Mostrar menos",
34
+ }: InsightCardsListProps) {
35
+ const visibleItems =
36
+ collapsedCount && !expanded ? items.slice(0, collapsedCount) : items;
37
+
38
+ return (
39
+ <div className="border rounded-md p-4">
40
+ <h3 className="font-semibold mb-2">{title}</h3>
41
+ {items.length === 0 ? (
42
+ <div className="text-sm text-slate-500">{emptyMessage}</div>
43
+ ) : (
44
+ <div className="grid grid-cols-1 gap-3">
45
+ {visibleItems.map((item) => (
46
+ <div key={item.id} className="rounded-md border p-3">
47
+ <div className="flex items-center justify-between gap-3">
48
+ <div className="font-semibold">{item.title}</div>
49
+ {item.badge ? (
50
+ <div className="text-xs text-slate-500">{item.badge}</div>
51
+ ) : null}
52
+ </div>
53
+ <div className="text-sm text-slate-600">{item.summary}</div>
54
+ {item.ctaLabel ? (
55
+ <div className="mt-2 text-sm text-slate-700">CTA: {item.ctaLabel}</div>
56
+ ) : null}
57
+ {item.impact ? (
58
+ <div className="mt-2 text-xs text-slate-500">{item.impact}</div>
59
+ ) : null}
60
+ {item.metadata?.length ? (
61
+ <div className="mt-2 flex items-center gap-3 flex-wrap text-xs text-slate-500">
62
+ {item.metadata.map((entry) => (
63
+ <span key={entry}>{entry}</span>
64
+ ))}
65
+ </div>
66
+ ) : null}
67
+ {item.action ? <div className="mt-2">{item.action}</div> : null}
68
+ </div>
69
+ ))}
70
+ {collapsedCount && items.length > collapsedCount && onToggleExpanded ? (
71
+ <button
72
+ type="button"
73
+ className="text-sm text-blue-600 hover:underline"
74
+ onClick={onToggleExpanded}
75
+ >
76
+ {expanded ? showLessLabel : showAllLabel}
77
+ </button>
78
+ ) : null}
79
+ </div>
80
+ )}
81
+ </div>
82
+ );
83
+ }
@@ -0,0 +1,21 @@
1
+ export type InsightSummaryCard = {
2
+ label: string;
3
+ value: string | number;
4
+ };
5
+
6
+ export type InsightSummaryCardsProps = {
7
+ cards: InsightSummaryCard[];
8
+ };
9
+
10
+ export function InsightSummaryCards({ cards }: InsightSummaryCardsProps) {
11
+ return (
12
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
13
+ {cards.map((card) => (
14
+ <div key={card.label} className="border rounded-md p-4">
15
+ <div className="text-sm text-slate-500">{card.label}</div>
16
+ <div className="text-2xl font-semibold">{card.value}</div>
17
+ </div>
18
+ ))}
19
+ </div>
20
+ );
21
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { CopilotResponsePanel, type CopilotRelatedInsightItem, type CopilotResponsePanelProps } from "./CopilotResponsePanel";
2
+ export { InsightSummaryCards, type InsightSummaryCard, type InsightSummaryCardsProps } from "./InsightSummaryCards";
3
+ export { InsightCardsList, type InsightListItem, type InsightCardsListProps } from "./InsightCardsList";