framepexls-ui-lib 0.1.23 → 0.1.25
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/dist/AnalyticsCharts.d.mts +24 -0
- package/dist/AnalyticsCharts.d.ts +24 -0
- package/dist/AnalyticsCharts.js +307 -0
- package/dist/AnalyticsCharts.mjs +295 -0
- package/dist/AppTopbar.d.mts +5 -1
- package/dist/AppTopbar.d.ts +5 -1
- package/dist/AppTopbar.js +8 -5
- package/dist/AppTopbar.mjs +8 -5
- package/dist/BadgeCluster.js +1 -1
- package/dist/BadgeCluster.mjs +1 -1
- package/dist/Breadcrumb.js +1 -1
- package/dist/Breadcrumb.mjs +1 -1
- package/dist/Button.d.mts +1 -1
- package/dist/Button.d.ts +1 -1
- package/dist/Button.js +1 -1
- package/dist/Button.mjs +1 -1
- package/dist/CalendarPanel.js +1 -1
- package/dist/CalendarPanel.mjs +1 -1
- package/dist/Card.js +1 -1
- package/dist/Card.mjs +1 -1
- package/dist/ChartCard.js +2 -2
- package/dist/ChartCard.mjs +2 -2
- package/dist/Checkbox.d.mts +26 -0
- package/dist/Checkbox.d.ts +26 -0
- package/dist/Checkbox.js +100 -0
- package/dist/Checkbox.mjs +70 -0
- package/dist/ColumnSelector.js +1 -1
- package/dist/ColumnSelector.mjs +1 -1
- package/dist/ComboSelect.js +3 -3
- package/dist/ComboSelect.mjs +3 -3
- package/dist/DateTimeField.js +1 -1
- package/dist/DateTimeField.mjs +1 -1
- package/dist/Dialog.js +2 -2
- package/dist/Dialog.mjs +2 -2
- package/dist/Dropdown.js +1 -1
- package/dist/Dropdown.mjs +1 -1
- package/dist/EmptyState.js +1 -1
- package/dist/EmptyState.mjs +1 -1
- package/dist/FiltersMultiSelect.js +1 -1
- package/dist/FiltersMultiSelect.mjs +1 -1
- package/dist/MediaCard.js +1 -1
- package/dist/MediaCard.mjs +1 -1
- package/dist/MediaSelector.js +1 -1
- package/dist/MediaSelector.mjs +1 -1
- package/dist/Pagination.js +1 -1
- package/dist/Pagination.mjs +1 -1
- package/dist/Select.js +3 -3
- package/dist/Select.mjs +3 -3
- package/dist/Sidebar.js +7 -7
- package/dist/Sidebar.mjs +7 -7
- package/dist/StatCard.js +1 -1
- package/dist/StatCard.mjs +1 -1
- package/dist/Table.js +2 -2
- package/dist/Table.mjs +2 -2
- package/dist/TimePanel.js +1 -1
- package/dist/TimePanel.mjs +1 -1
- package/dist/TimePopover.js +3 -3
- package/dist/TimePopover.mjs +3 -3
- package/dist/TimeRangeField.js +1 -1
- package/dist/TimeRangeField.mjs +1 -1
- package/dist/Toast.js +1 -1
- package/dist/Toast.mjs +1 -1
- package/dist/UploadCard.js +2 -2
- package/dist/UploadCard.mjs +2 -2
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +10 -0
- package/dist/index.mjs +86 -80
- package/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
type Campania = {
|
|
4
|
+
id: number;
|
|
5
|
+
titulo: string;
|
|
6
|
+
destino: string;
|
|
7
|
+
prioridad: number;
|
|
8
|
+
impresiones: number;
|
|
9
|
+
clicks: number;
|
|
10
|
+
ctr: number;
|
|
11
|
+
};
|
|
12
|
+
declare function TopImpresionesBar({ data, limit, }: {
|
|
13
|
+
data: Campania[];
|
|
14
|
+
limit?: number;
|
|
15
|
+
}): react_jsx_runtime.JSX.Element;
|
|
16
|
+
declare function CtrHorizontalBar({ data, limit }: {
|
|
17
|
+
data: Campania[];
|
|
18
|
+
limit?: number;
|
|
19
|
+
}): react_jsx_runtime.JSX.Element;
|
|
20
|
+
declare function DestinoDonut({ data }: {
|
|
21
|
+
data: Campania[];
|
|
22
|
+
}): react_jsx_runtime.JSX.Element;
|
|
23
|
+
|
|
24
|
+
export { type Campania, CtrHorizontalBar, DestinoDonut, TopImpresionesBar };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
type Campania = {
|
|
4
|
+
id: number;
|
|
5
|
+
titulo: string;
|
|
6
|
+
destino: string;
|
|
7
|
+
prioridad: number;
|
|
8
|
+
impresiones: number;
|
|
9
|
+
clicks: number;
|
|
10
|
+
ctr: number;
|
|
11
|
+
};
|
|
12
|
+
declare function TopImpresionesBar({ data, limit, }: {
|
|
13
|
+
data: Campania[];
|
|
14
|
+
limit?: number;
|
|
15
|
+
}): react_jsx_runtime.JSX.Element;
|
|
16
|
+
declare function CtrHorizontalBar({ data, limit }: {
|
|
17
|
+
data: Campania[];
|
|
18
|
+
limit?: number;
|
|
19
|
+
}): react_jsx_runtime.JSX.Element;
|
|
20
|
+
declare function DestinoDonut({ data }: {
|
|
21
|
+
data: Campania[];
|
|
22
|
+
}): react_jsx_runtime.JSX.Element;
|
|
23
|
+
|
|
24
|
+
export { type Campania, CtrHorizontalBar, DestinoDonut, TopImpresionesBar };
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var AnalyticsCharts_exports = {};
|
|
21
|
+
__export(AnalyticsCharts_exports, {
|
|
22
|
+
CtrHorizontalBar: () => CtrHorizontalBar,
|
|
23
|
+
DestinoDonut: () => DestinoDonut,
|
|
24
|
+
TopImpresionesBar: () => TopImpresionesBar
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(AnalyticsCharts_exports);
|
|
27
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
28
|
+
var import_react = require("react");
|
|
29
|
+
var import_recharts = require("recharts");
|
|
30
|
+
const COLORS = [
|
|
31
|
+
"#0ea5e9",
|
|
32
|
+
"#22c55e",
|
|
33
|
+
"#a855f7",
|
|
34
|
+
"#ef4444",
|
|
35
|
+
"#f59e0b",
|
|
36
|
+
"#6366f1",
|
|
37
|
+
"#06b6d4",
|
|
38
|
+
"#84cc16"
|
|
39
|
+
];
|
|
40
|
+
const clamp01 = (n) => Math.max(0, Math.min(1, n));
|
|
41
|
+
const hexToRgb = (hex) => {
|
|
42
|
+
const h = hex.replace("#", "");
|
|
43
|
+
const v = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
44
|
+
const num = parseInt(v, 16);
|
|
45
|
+
return { r: num >> 16 & 255, g: num >> 8 & 255, b: num & 255 };
|
|
46
|
+
};
|
|
47
|
+
const hexToRgba = (hex, a = 1) => {
|
|
48
|
+
const { r, g, b } = hexToRgb(hex);
|
|
49
|
+
return `rgba(${r},${g},${b},${clamp01(a)})`;
|
|
50
|
+
};
|
|
51
|
+
const textOnBg = (hex) => {
|
|
52
|
+
const { r, g, b } = hexToRgb(hex);
|
|
53
|
+
const lin = [r, g, b].map((v) => v / 255).map((v) => v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4));
|
|
54
|
+
const L = 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2];
|
|
55
|
+
return L > 0.5 ? "#0f172a" : "#ffffff";
|
|
56
|
+
};
|
|
57
|
+
const axisStroke = "var(--grid, rgba(148,163,184,.18))";
|
|
58
|
+
const tickStyle = { fill: "var(--muted, rgba(100,116,139,1))", fontSize: 12 };
|
|
59
|
+
const nf = (n) => Intl.NumberFormat().format(n);
|
|
60
|
+
const nfc = (n) => Intl.NumberFormat(void 0, { notation: "compact", maximumFractionDigits: 1 }).format(n);
|
|
61
|
+
function ChartPanel({
|
|
62
|
+
title,
|
|
63
|
+
subtitle,
|
|
64
|
+
right,
|
|
65
|
+
children
|
|
66
|
+
}) {
|
|
67
|
+
const cardVars = " [--card-bg:theme(colors.white/0.7)] [--card-border:theme(colors.slate.200/0.8)] [--fg:theme(colors.slate.800)] [--muted:theme(colors.slate.500)] [--grid:rgba(148,163,184,.18)] dark:[--card-bg:theme(colors.white/0.06)] dark:[--card-border:theme(colors.white/0.08)] dark:[--fg:theme(colors.slate.100)] dark:[--muted:theme(colors.slate.400)] dark:[--grid:rgba(255,255,255,.12)]";
|
|
68
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
69
|
+
"div",
|
|
70
|
+
{
|
|
71
|
+
className: `rounded-2xl border p-4 md:p-5 backdrop-blur h-full${cardVars}`,
|
|
72
|
+
style: { background: "var(--card-bg)", borderColor: "var(--card-border)" },
|
|
73
|
+
"aria-label": title,
|
|
74
|
+
children: [
|
|
75
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mb-3 flex items-start justify-between gap-4", children: [
|
|
76
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
77
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm font-semibold", style: { color: "var(--fg)" }, children: title }),
|
|
78
|
+
subtitle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-xs", style: { color: "var(--muted)" }, children: subtitle })
|
|
79
|
+
] }),
|
|
80
|
+
right
|
|
81
|
+
] }),
|
|
82
|
+
children
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
function TooltipBox(props) {
|
|
88
|
+
const { label, payload, unit, valueFormatter } = props || {};
|
|
89
|
+
if (!(payload == null ? void 0 : payload.length)) return null;
|
|
90
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
91
|
+
"div",
|
|
92
|
+
{
|
|
93
|
+
className: "rounded-xl border px-3 py-2 text-xs shadow-xl backdrop-blur",
|
|
94
|
+
style: {
|
|
95
|
+
background: "color-mix(in oklab, var(--card-bg) 85%, #ffffff)",
|
|
96
|
+
borderColor: "var(--card-border)"
|
|
97
|
+
},
|
|
98
|
+
children: [
|
|
99
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mb-1 font-medium", style: { color: "var(--fg)" }, children: label }),
|
|
100
|
+
payload.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
101
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "inline-block h-2 w-2 rounded-full", style: { backgroundColor: p.color } }),
|
|
102
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-[11px]", style: { color: "var(--muted)" }, children: [
|
|
103
|
+
p.name,
|
|
104
|
+
":"
|
|
105
|
+
] }),
|
|
106
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-semibold", style: { color: "var(--fg)" }, children: [
|
|
107
|
+
valueFormatter ? valueFormatter(p.value) : nf(p.value),
|
|
108
|
+
unit != null ? unit : ""
|
|
109
|
+
] })
|
|
110
|
+
] }, p.dataKey))
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
const TopCompactLabel = (props) => {
|
|
116
|
+
const { x, y, width, value } = props;
|
|
117
|
+
const tx = (x != null ? x : 0) + (width != null ? width : 0) / 2;
|
|
118
|
+
const ty = (y != null ? y : 0) - 6;
|
|
119
|
+
const text = typeof value === "number" ? nfc(value) : String(value != null ? value : "");
|
|
120
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: tx, y: ty, textAnchor: "middle", fill: "var(--fg)", fontSize: 11, children: text });
|
|
121
|
+
};
|
|
122
|
+
function TopImpresionesBar({
|
|
123
|
+
data,
|
|
124
|
+
limit = 10
|
|
125
|
+
}) {
|
|
126
|
+
const rows = (0, import_react.useMemo)(
|
|
127
|
+
() => [...data].sort((a, b) => b.impresiones - a.impresiones).slice(0, limit).map((d) => ({ name: d.titulo, impresiones: d.impresiones })),
|
|
128
|
+
[data, limit]
|
|
129
|
+
);
|
|
130
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ChartPanel, { title: "Top campa\xF1as por impresiones", subtitle: "\xDAltimas campa\xF1as con mayor alcance", children: [
|
|
131
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-56 w-full", children: rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid h-full place-items-center text-sm", style: { color: "var(--muted)" }, children: "Sin datos." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_recharts.BarChart, { data: rows, margin: { top: 8, right: 8, bottom: 0, left: 8 }, barCategoryGap: "28%", barSize: 16, children: [
|
|
132
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.CartesianGrid, { vertical: false, strokeDasharray: "3 3", stroke: axisStroke }),
|
|
133
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.XAxis, { dataKey: "name", hide: true, stroke: axisStroke }),
|
|
134
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.YAxis, { stroke: axisStroke, tick: tickStyle, tickFormatter: nfc, width: 56 }),
|
|
135
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipBox, { valueFormatter: nf }) }),
|
|
136
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
137
|
+
import_recharts.Bar,
|
|
138
|
+
{
|
|
139
|
+
dataKey: "impresiones",
|
|
140
|
+
name: "Impresiones",
|
|
141
|
+
radius: [10, 10, 10, 10],
|
|
142
|
+
isAnimationActive: true,
|
|
143
|
+
animationDuration: 700,
|
|
144
|
+
animationEasing: "ease-out",
|
|
145
|
+
children: [
|
|
146
|
+
rows.map((_, i) => {
|
|
147
|
+
const base = COLORS[i % COLORS.length];
|
|
148
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.Cell, { fill: hexToRgba(base, 0.9), stroke: base, strokeOpacity: 0.95 }, i);
|
|
149
|
+
}),
|
|
150
|
+
rows.length <= 8 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.LabelList, { dataKey: "impresiones", content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TopCompactLabel, {}) })
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
] }) }) }),
|
|
155
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-2 text-xs", style: { color: "var(--muted)" }, children: "Pasa el cursor para ver el detalle. Valores compactos cuando hay muchas barras." })
|
|
156
|
+
] });
|
|
157
|
+
}
|
|
158
|
+
const InsideRightPctLabel = (props) => {
|
|
159
|
+
const { x, y, width, height, value, index } = props;
|
|
160
|
+
const base = COLORS[(index != null ? index : 0) % COLORS.length];
|
|
161
|
+
const fill = textOnBg(base);
|
|
162
|
+
const tx = (x != null ? x : 0) + (width != null ? width : 0) - 6;
|
|
163
|
+
const ty = (y != null ? y : 0) + (height != null ? height : 0) / 2 + 4;
|
|
164
|
+
const text = typeof value === "number" ? `${value.toFixed(1)}%` : String(value != null ? value : "");
|
|
165
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: tx, y: ty, textAnchor: "end", fill, fontSize: 11, children: text });
|
|
166
|
+
};
|
|
167
|
+
function CtrHorizontalBar({ data, limit = 10 }) {
|
|
168
|
+
const rows = (0, import_react.useMemo)(
|
|
169
|
+
() => [...data].filter((d) => d.impresiones > 0).sort((a, b) => {
|
|
170
|
+
var _a, _b;
|
|
171
|
+
return ((_a = b.ctr) != null ? _a : 0) - ((_b = a.ctr) != null ? _b : 0);
|
|
172
|
+
}).slice(0, limit).map((d) => {
|
|
173
|
+
var _a;
|
|
174
|
+
return { name: d.titulo, ctr: ((_a = d.ctr) != null ? _a : 0) * 100 };
|
|
175
|
+
}),
|
|
176
|
+
[data, limit]
|
|
177
|
+
);
|
|
178
|
+
if (rows.length === 0) {
|
|
179
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartPanel, { title: "Mejor CTR por campa\xF1a", subtitle: "% de clics sobre impresiones", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-56 grid place-items-center text-sm", style: { color: "var(--muted)" }, children: "Sin datos." }) });
|
|
180
|
+
}
|
|
181
|
+
const maxCtr = Math.max(5, Math.ceil(Math.max(...rows.map((r) => r.ctr))));
|
|
182
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartPanel, { title: "Mejor CTR por campa\xF1a", subtitle: "% de clics sobre impresiones", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-56 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
183
|
+
import_recharts.BarChart,
|
|
184
|
+
{
|
|
185
|
+
data: rows,
|
|
186
|
+
layout: "vertical",
|
|
187
|
+
margin: { top: 8, right: 12, bottom: 0, left: 8 },
|
|
188
|
+
barCategoryGap: "28%",
|
|
189
|
+
barSize: 14,
|
|
190
|
+
children: [
|
|
191
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.CartesianGrid, { horizontal: true, strokeDasharray: "3 3", stroke: axisStroke }),
|
|
192
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.XAxis, { type: "number", stroke: axisStroke, tick: tickStyle, domain: [0, maxCtr], tickFormatter: (v) => `${v}%` }),
|
|
193
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.YAxis, { type: "category", dataKey: "name", width: 160, stroke: axisStroke, tick: tickStyle, interval: 0 }),
|
|
194
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.ReferenceLine, { x: 0, stroke: axisStroke }),
|
|
195
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipBox, { unit: "%", valueFormatter: (v) => v.toFixed(2) }) }),
|
|
196
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_recharts.Bar, { dataKey: "ctr", name: "CTR", radius: [6, 12, 12, 6], isAnimationActive: true, animationDuration: 750, animationEasing: "ease-out", children: [
|
|
197
|
+
rows.map((_, i) => {
|
|
198
|
+
const base = COLORS[i % COLORS.length];
|
|
199
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.Cell, { fill: hexToRgba(base, 0.9), stroke: base, strokeOpacity: 0.95 }, i);
|
|
200
|
+
}),
|
|
201
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.LabelList, { dataKey: "ctr", content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsideRightPctLabel, {}) })
|
|
202
|
+
] })
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
) }) }) });
|
|
206
|
+
}
|
|
207
|
+
function DestinoDonut({ data }) {
|
|
208
|
+
const rows = (0, import_react.useMemo)(() => {
|
|
209
|
+
const grouped = data.reduce((acc, d) => {
|
|
210
|
+
var _a;
|
|
211
|
+
acc[d.destino] = ((_a = acc[d.destino]) != null ? _a : 0) + d.impresiones;
|
|
212
|
+
return acc;
|
|
213
|
+
}, {});
|
|
214
|
+
return Object.entries(grouped).map(([k, v]) => ({ name: k, value: v }));
|
|
215
|
+
}, [data]);
|
|
216
|
+
const total = rows.reduce((a, b) => a + b.value, 0);
|
|
217
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ChartPanel, { title: "Distribuci\xF3n por destino", subtitle: "Participaci\xF3n de impresiones por ubicaci\xF3n", children: [
|
|
218
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-56 w-full", children: rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "grid h-full place-items-center text-sm", style: { color: "var(--muted)" }, children: "Sin datos suficientes." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_recharts.PieChart, { children: [
|
|
219
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
220
|
+
import_recharts.Pie,
|
|
221
|
+
{
|
|
222
|
+
data: rows,
|
|
223
|
+
dataKey: "value",
|
|
224
|
+
nameKey: "name",
|
|
225
|
+
innerRadius: 70,
|
|
226
|
+
outerRadius: 88,
|
|
227
|
+
cornerRadius: 8,
|
|
228
|
+
startAngle: 210,
|
|
229
|
+
endAngle: -150,
|
|
230
|
+
paddingAngle: 2.5,
|
|
231
|
+
stroke: "rgba(255,255,255,.85)",
|
|
232
|
+
isAnimationActive: true,
|
|
233
|
+
animationDuration: 850,
|
|
234
|
+
animationEasing: "ease-out",
|
|
235
|
+
children: [
|
|
236
|
+
rows.map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
237
|
+
import_recharts.Cell,
|
|
238
|
+
{
|
|
239
|
+
fill: COLORS[i % COLORS.length],
|
|
240
|
+
fillOpacity: 0.9,
|
|
241
|
+
stroke: COLORS[i % COLORS.length],
|
|
242
|
+
strokeOpacity: 0.95
|
|
243
|
+
},
|
|
244
|
+
i
|
|
245
|
+
)),
|
|
246
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
247
|
+
import_recharts.Label,
|
|
248
|
+
{
|
|
249
|
+
content: (props) => {
|
|
250
|
+
const { viewBox } = props;
|
|
251
|
+
if (!viewBox || !("cx" in viewBox)) return null;
|
|
252
|
+
const cx = viewBox.cx, cy = viewBox.cy;
|
|
253
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
|
|
254
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: cx, y: cy - 2, textAnchor: "middle", fill: "var(--muted)", fontSize: 12, children: "Total" }),
|
|
255
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
256
|
+
"text",
|
|
257
|
+
{
|
|
258
|
+
x: cx,
|
|
259
|
+
y: cy + 16,
|
|
260
|
+
textAnchor: "middle",
|
|
261
|
+
fill: "var(--fg)",
|
|
262
|
+
fontSize: 16,
|
|
263
|
+
className: "font-semibold",
|
|
264
|
+
children: nfc(total)
|
|
265
|
+
}
|
|
266
|
+
)
|
|
267
|
+
] });
|
|
268
|
+
},
|
|
269
|
+
position: "center"
|
|
270
|
+
}
|
|
271
|
+
)
|
|
272
|
+
]
|
|
273
|
+
}
|
|
274
|
+
),
|
|
275
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_recharts.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipBox, { valueFormatter: nf }) })
|
|
276
|
+
] }) }) }),
|
|
277
|
+
rows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
278
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mt-2 text-xs", style: { color: "var(--muted)" }, children: [
|
|
279
|
+
"Total impresiones: ",
|
|
280
|
+
nf(total)
|
|
281
|
+
] }),
|
|
282
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-2 flex flex-wrap gap-2", children: rows.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
283
|
+
"div",
|
|
284
|
+
{
|
|
285
|
+
className: "inline-flex items-center gap-2 rounded-full border px-2.5 py-1 text-xs",
|
|
286
|
+
style: { borderColor: "var(--card-border)", color: "var(--fg)" },
|
|
287
|
+
children: [
|
|
288
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "h-2.5 w-2.5 rounded-full", style: { background: COLORS[i % COLORS.length] } }),
|
|
289
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "capitalize", children: r.name }),
|
|
290
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "text-[11px]", style: { color: "var(--muted)" }, children: [
|
|
291
|
+
"(",
|
|
292
|
+
Math.round(r.value / (total || 1) * 100),
|
|
293
|
+
"%)"
|
|
294
|
+
] })
|
|
295
|
+
]
|
|
296
|
+
},
|
|
297
|
+
r.name
|
|
298
|
+
)) })
|
|
299
|
+
] })
|
|
300
|
+
] });
|
|
301
|
+
}
|
|
302
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
303
|
+
0 && (module.exports = {
|
|
304
|
+
CtrHorizontalBar,
|
|
305
|
+
DestinoDonut,
|
|
306
|
+
TopImpresionesBar
|
|
307
|
+
});
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import {
|
|
5
|
+
ResponsiveContainer,
|
|
6
|
+
BarChart,
|
|
7
|
+
Bar,
|
|
8
|
+
XAxis,
|
|
9
|
+
YAxis,
|
|
10
|
+
Tooltip,
|
|
11
|
+
PieChart,
|
|
12
|
+
Pie,
|
|
13
|
+
Cell,
|
|
14
|
+
CartesianGrid,
|
|
15
|
+
Label,
|
|
16
|
+
LabelList,
|
|
17
|
+
ReferenceLine
|
|
18
|
+
} from "recharts";
|
|
19
|
+
const COLORS = [
|
|
20
|
+
"#0ea5e9",
|
|
21
|
+
"#22c55e",
|
|
22
|
+
"#a855f7",
|
|
23
|
+
"#ef4444",
|
|
24
|
+
"#f59e0b",
|
|
25
|
+
"#6366f1",
|
|
26
|
+
"#06b6d4",
|
|
27
|
+
"#84cc16"
|
|
28
|
+
];
|
|
29
|
+
const clamp01 = (n) => Math.max(0, Math.min(1, n));
|
|
30
|
+
const hexToRgb = (hex) => {
|
|
31
|
+
const h = hex.replace("#", "");
|
|
32
|
+
const v = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
33
|
+
const num = parseInt(v, 16);
|
|
34
|
+
return { r: num >> 16 & 255, g: num >> 8 & 255, b: num & 255 };
|
|
35
|
+
};
|
|
36
|
+
const hexToRgba = (hex, a = 1) => {
|
|
37
|
+
const { r, g, b } = hexToRgb(hex);
|
|
38
|
+
return `rgba(${r},${g},${b},${clamp01(a)})`;
|
|
39
|
+
};
|
|
40
|
+
const textOnBg = (hex) => {
|
|
41
|
+
const { r, g, b } = hexToRgb(hex);
|
|
42
|
+
const lin = [r, g, b].map((v) => v / 255).map((v) => v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4));
|
|
43
|
+
const L = 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2];
|
|
44
|
+
return L > 0.5 ? "#0f172a" : "#ffffff";
|
|
45
|
+
};
|
|
46
|
+
const axisStroke = "var(--grid, rgba(148,163,184,.18))";
|
|
47
|
+
const tickStyle = { fill: "var(--muted, rgba(100,116,139,1))", fontSize: 12 };
|
|
48
|
+
const nf = (n) => Intl.NumberFormat().format(n);
|
|
49
|
+
const nfc = (n) => Intl.NumberFormat(void 0, { notation: "compact", maximumFractionDigits: 1 }).format(n);
|
|
50
|
+
function ChartPanel({
|
|
51
|
+
title,
|
|
52
|
+
subtitle,
|
|
53
|
+
right,
|
|
54
|
+
children
|
|
55
|
+
}) {
|
|
56
|
+
const cardVars = " [--card-bg:theme(colors.white/0.7)] [--card-border:theme(colors.slate.200/0.8)] [--fg:theme(colors.slate.800)] [--muted:theme(colors.slate.500)] [--grid:rgba(148,163,184,.18)] dark:[--card-bg:theme(colors.white/0.06)] dark:[--card-border:theme(colors.white/0.08)] dark:[--fg:theme(colors.slate.100)] dark:[--muted:theme(colors.slate.400)] dark:[--grid:rgba(255,255,255,.12)]";
|
|
57
|
+
return /* @__PURE__ */ jsxs(
|
|
58
|
+
"div",
|
|
59
|
+
{
|
|
60
|
+
className: `rounded-2xl border p-4 md:p-5 backdrop-blur h-full${cardVars}`,
|
|
61
|
+
style: { background: "var(--card-bg)", borderColor: "var(--card-border)" },
|
|
62
|
+
"aria-label": title,
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-start justify-between gap-4", children: [
|
|
65
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
66
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold", style: { color: "var(--fg)" }, children: title }),
|
|
67
|
+
subtitle && /* @__PURE__ */ jsx("div", { className: "text-xs", style: { color: "var(--muted)" }, children: subtitle })
|
|
68
|
+
] }),
|
|
69
|
+
right
|
|
70
|
+
] }),
|
|
71
|
+
children
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
function TooltipBox(props) {
|
|
77
|
+
const { label, payload, unit, valueFormatter } = props || {};
|
|
78
|
+
if (!(payload == null ? void 0 : payload.length)) return null;
|
|
79
|
+
return /* @__PURE__ */ jsxs(
|
|
80
|
+
"div",
|
|
81
|
+
{
|
|
82
|
+
className: "rounded-xl border px-3 py-2 text-xs shadow-xl backdrop-blur",
|
|
83
|
+
style: {
|
|
84
|
+
background: "color-mix(in oklab, var(--card-bg) 85%, #ffffff)",
|
|
85
|
+
borderColor: "var(--card-border)"
|
|
86
|
+
},
|
|
87
|
+
children: [
|
|
88
|
+
label && /* @__PURE__ */ jsx("div", { className: "mb-1 font-medium", style: { color: "var(--fg)" }, children: label }),
|
|
89
|
+
payload.map((p) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
90
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block h-2 w-2 rounded-full", style: { backgroundColor: p.color } }),
|
|
91
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[11px]", style: { color: "var(--muted)" }, children: [
|
|
92
|
+
p.name,
|
|
93
|
+
":"
|
|
94
|
+
] }),
|
|
95
|
+
/* @__PURE__ */ jsxs("span", { className: "font-semibold", style: { color: "var(--fg)" }, children: [
|
|
96
|
+
valueFormatter ? valueFormatter(p.value) : nf(p.value),
|
|
97
|
+
unit != null ? unit : ""
|
|
98
|
+
] })
|
|
99
|
+
] }, p.dataKey))
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const TopCompactLabel = (props) => {
|
|
105
|
+
const { x, y, width, value } = props;
|
|
106
|
+
const tx = (x != null ? x : 0) + (width != null ? width : 0) / 2;
|
|
107
|
+
const ty = (y != null ? y : 0) - 6;
|
|
108
|
+
const text = typeof value === "number" ? nfc(value) : String(value != null ? value : "");
|
|
109
|
+
return /* @__PURE__ */ jsx("text", { x: tx, y: ty, textAnchor: "middle", fill: "var(--fg)", fontSize: 11, children: text });
|
|
110
|
+
};
|
|
111
|
+
function TopImpresionesBar({
|
|
112
|
+
data,
|
|
113
|
+
limit = 10
|
|
114
|
+
}) {
|
|
115
|
+
const rows = useMemo(
|
|
116
|
+
() => [...data].sort((a, b) => b.impresiones - a.impresiones).slice(0, limit).map((d) => ({ name: d.titulo, impresiones: d.impresiones })),
|
|
117
|
+
[data, limit]
|
|
118
|
+
);
|
|
119
|
+
return /* @__PURE__ */ jsxs(ChartPanel, { title: "Top campa\xF1as por impresiones", subtitle: "\xDAltimas campa\xF1as con mayor alcance", children: [
|
|
120
|
+
/* @__PURE__ */ jsx("div", { className: "h-56 w-full", children: rows.length === 0 ? /* @__PURE__ */ jsx("div", { className: "grid h-full place-items-center text-sm", style: { color: "var(--muted)" }, children: "Sin datos." }) : /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(BarChart, { data: rows, margin: { top: 8, right: 8, bottom: 0, left: 8 }, barCategoryGap: "28%", barSize: 16, children: [
|
|
121
|
+
/* @__PURE__ */ jsx(CartesianGrid, { vertical: false, strokeDasharray: "3 3", stroke: axisStroke }),
|
|
122
|
+
/* @__PURE__ */ jsx(XAxis, { dataKey: "name", hide: true, stroke: axisStroke }),
|
|
123
|
+
/* @__PURE__ */ jsx(YAxis, { stroke: axisStroke, tick: tickStyle, tickFormatter: nfc, width: 56 }),
|
|
124
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(TooltipBox, { valueFormatter: nf }) }),
|
|
125
|
+
/* @__PURE__ */ jsxs(
|
|
126
|
+
Bar,
|
|
127
|
+
{
|
|
128
|
+
dataKey: "impresiones",
|
|
129
|
+
name: "Impresiones",
|
|
130
|
+
radius: [10, 10, 10, 10],
|
|
131
|
+
isAnimationActive: true,
|
|
132
|
+
animationDuration: 700,
|
|
133
|
+
animationEasing: "ease-out",
|
|
134
|
+
children: [
|
|
135
|
+
rows.map((_, i) => {
|
|
136
|
+
const base = COLORS[i % COLORS.length];
|
|
137
|
+
return /* @__PURE__ */ jsx(Cell, { fill: hexToRgba(base, 0.9), stroke: base, strokeOpacity: 0.95 }, i);
|
|
138
|
+
}),
|
|
139
|
+
rows.length <= 8 && /* @__PURE__ */ jsx(LabelList, { dataKey: "impresiones", content: /* @__PURE__ */ jsx(TopCompactLabel, {}) })
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
] }) }) }),
|
|
144
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 text-xs", style: { color: "var(--muted)" }, children: "Pasa el cursor para ver el detalle. Valores compactos cuando hay muchas barras." })
|
|
145
|
+
] });
|
|
146
|
+
}
|
|
147
|
+
const InsideRightPctLabel = (props) => {
|
|
148
|
+
const { x, y, width, height, value, index } = props;
|
|
149
|
+
const base = COLORS[(index != null ? index : 0) % COLORS.length];
|
|
150
|
+
const fill = textOnBg(base);
|
|
151
|
+
const tx = (x != null ? x : 0) + (width != null ? width : 0) - 6;
|
|
152
|
+
const ty = (y != null ? y : 0) + (height != null ? height : 0) / 2 + 4;
|
|
153
|
+
const text = typeof value === "number" ? `${value.toFixed(1)}%` : String(value != null ? value : "");
|
|
154
|
+
return /* @__PURE__ */ jsx("text", { x: tx, y: ty, textAnchor: "end", fill, fontSize: 11, children: text });
|
|
155
|
+
};
|
|
156
|
+
function CtrHorizontalBar({ data, limit = 10 }) {
|
|
157
|
+
const rows = useMemo(
|
|
158
|
+
() => [...data].filter((d) => d.impresiones > 0).sort((a, b) => {
|
|
159
|
+
var _a, _b;
|
|
160
|
+
return ((_a = b.ctr) != null ? _a : 0) - ((_b = a.ctr) != null ? _b : 0);
|
|
161
|
+
}).slice(0, limit).map((d) => {
|
|
162
|
+
var _a;
|
|
163
|
+
return { name: d.titulo, ctr: ((_a = d.ctr) != null ? _a : 0) * 100 };
|
|
164
|
+
}),
|
|
165
|
+
[data, limit]
|
|
166
|
+
);
|
|
167
|
+
if (rows.length === 0) {
|
|
168
|
+
return /* @__PURE__ */ jsx(ChartPanel, { title: "Mejor CTR por campa\xF1a", subtitle: "% de clics sobre impresiones", children: /* @__PURE__ */ jsx("div", { className: "h-56 grid place-items-center text-sm", style: { color: "var(--muted)" }, children: "Sin datos." }) });
|
|
169
|
+
}
|
|
170
|
+
const maxCtr = Math.max(5, Math.ceil(Math.max(...rows.map((r) => r.ctr))));
|
|
171
|
+
return /* @__PURE__ */ jsx(ChartPanel, { title: "Mejor CTR por campa\xF1a", subtitle: "% de clics sobre impresiones", children: /* @__PURE__ */ jsx("div", { className: "h-56 w-full", children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
|
|
172
|
+
BarChart,
|
|
173
|
+
{
|
|
174
|
+
data: rows,
|
|
175
|
+
layout: "vertical",
|
|
176
|
+
margin: { top: 8, right: 12, bottom: 0, left: 8 },
|
|
177
|
+
barCategoryGap: "28%",
|
|
178
|
+
barSize: 14,
|
|
179
|
+
children: [
|
|
180
|
+
/* @__PURE__ */ jsx(CartesianGrid, { horizontal: true, strokeDasharray: "3 3", stroke: axisStroke }),
|
|
181
|
+
/* @__PURE__ */ jsx(XAxis, { type: "number", stroke: axisStroke, tick: tickStyle, domain: [0, maxCtr], tickFormatter: (v) => `${v}%` }),
|
|
182
|
+
/* @__PURE__ */ jsx(YAxis, { type: "category", dataKey: "name", width: 160, stroke: axisStroke, tick: tickStyle, interval: 0 }),
|
|
183
|
+
/* @__PURE__ */ jsx(ReferenceLine, { x: 0, stroke: axisStroke }),
|
|
184
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(TooltipBox, { unit: "%", valueFormatter: (v) => v.toFixed(2) }) }),
|
|
185
|
+
/* @__PURE__ */ jsxs(Bar, { dataKey: "ctr", name: "CTR", radius: [6, 12, 12, 6], isAnimationActive: true, animationDuration: 750, animationEasing: "ease-out", children: [
|
|
186
|
+
rows.map((_, i) => {
|
|
187
|
+
const base = COLORS[i % COLORS.length];
|
|
188
|
+
return /* @__PURE__ */ jsx(Cell, { fill: hexToRgba(base, 0.9), stroke: base, strokeOpacity: 0.95 }, i);
|
|
189
|
+
}),
|
|
190
|
+
/* @__PURE__ */ jsx(LabelList, { dataKey: "ctr", content: /* @__PURE__ */ jsx(InsideRightPctLabel, {}) })
|
|
191
|
+
] })
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
) }) }) });
|
|
195
|
+
}
|
|
196
|
+
function DestinoDonut({ data }) {
|
|
197
|
+
const rows = useMemo(() => {
|
|
198
|
+
const grouped = data.reduce((acc, d) => {
|
|
199
|
+
var _a;
|
|
200
|
+
acc[d.destino] = ((_a = acc[d.destino]) != null ? _a : 0) + d.impresiones;
|
|
201
|
+
return acc;
|
|
202
|
+
}, {});
|
|
203
|
+
return Object.entries(grouped).map(([k, v]) => ({ name: k, value: v }));
|
|
204
|
+
}, [data]);
|
|
205
|
+
const total = rows.reduce((a, b) => a + b.value, 0);
|
|
206
|
+
return /* @__PURE__ */ jsxs(ChartPanel, { title: "Distribuci\xF3n por destino", subtitle: "Participaci\xF3n de impresiones por ubicaci\xF3n", children: [
|
|
207
|
+
/* @__PURE__ */ jsx("div", { className: "h-56 w-full", children: rows.length === 0 ? /* @__PURE__ */ jsx("div", { className: "grid h-full place-items-center text-sm", style: { color: "var(--muted)" }, children: "Sin datos suficientes." }) : /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(PieChart, { children: [
|
|
208
|
+
/* @__PURE__ */ jsxs(
|
|
209
|
+
Pie,
|
|
210
|
+
{
|
|
211
|
+
data: rows,
|
|
212
|
+
dataKey: "value",
|
|
213
|
+
nameKey: "name",
|
|
214
|
+
innerRadius: 70,
|
|
215
|
+
outerRadius: 88,
|
|
216
|
+
cornerRadius: 8,
|
|
217
|
+
startAngle: 210,
|
|
218
|
+
endAngle: -150,
|
|
219
|
+
paddingAngle: 2.5,
|
|
220
|
+
stroke: "rgba(255,255,255,.85)",
|
|
221
|
+
isAnimationActive: true,
|
|
222
|
+
animationDuration: 850,
|
|
223
|
+
animationEasing: "ease-out",
|
|
224
|
+
children: [
|
|
225
|
+
rows.map((_, i) => /* @__PURE__ */ jsx(
|
|
226
|
+
Cell,
|
|
227
|
+
{
|
|
228
|
+
fill: COLORS[i % COLORS.length],
|
|
229
|
+
fillOpacity: 0.9,
|
|
230
|
+
stroke: COLORS[i % COLORS.length],
|
|
231
|
+
strokeOpacity: 0.95
|
|
232
|
+
},
|
|
233
|
+
i
|
|
234
|
+
)),
|
|
235
|
+
/* @__PURE__ */ jsx(
|
|
236
|
+
Label,
|
|
237
|
+
{
|
|
238
|
+
content: (props) => {
|
|
239
|
+
const { viewBox } = props;
|
|
240
|
+
if (!viewBox || !("cx" in viewBox)) return null;
|
|
241
|
+
const cx = viewBox.cx, cy = viewBox.cy;
|
|
242
|
+
return /* @__PURE__ */ jsxs("g", { children: [
|
|
243
|
+
/* @__PURE__ */ jsx("text", { x: cx, y: cy - 2, textAnchor: "middle", fill: "var(--muted)", fontSize: 12, children: "Total" }),
|
|
244
|
+
/* @__PURE__ */ jsx(
|
|
245
|
+
"text",
|
|
246
|
+
{
|
|
247
|
+
x: cx,
|
|
248
|
+
y: cy + 16,
|
|
249
|
+
textAnchor: "middle",
|
|
250
|
+
fill: "var(--fg)",
|
|
251
|
+
fontSize: 16,
|
|
252
|
+
className: "font-semibold",
|
|
253
|
+
children: nfc(total)
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
] });
|
|
257
|
+
},
|
|
258
|
+
position: "center"
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
),
|
|
264
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(TooltipBox, { valueFormatter: nf }) })
|
|
265
|
+
] }) }) }),
|
|
266
|
+
rows.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
267
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 text-xs", style: { color: "var(--muted)" }, children: [
|
|
268
|
+
"Total impresiones: ",
|
|
269
|
+
nf(total)
|
|
270
|
+
] }),
|
|
271
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 flex flex-wrap gap-2", children: rows.map((r, i) => /* @__PURE__ */ jsxs(
|
|
272
|
+
"div",
|
|
273
|
+
{
|
|
274
|
+
className: "inline-flex items-center gap-2 rounded-full border px-2.5 py-1 text-xs",
|
|
275
|
+
style: { borderColor: "var(--card-border)", color: "var(--fg)" },
|
|
276
|
+
children: [
|
|
277
|
+
/* @__PURE__ */ jsx("span", { className: "h-2.5 w-2.5 rounded-full", style: { background: COLORS[i % COLORS.length] } }),
|
|
278
|
+
/* @__PURE__ */ jsx("span", { className: "capitalize", children: r.name }),
|
|
279
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[11px]", style: { color: "var(--muted)" }, children: [
|
|
280
|
+
"(",
|
|
281
|
+
Math.round(r.value / (total || 1) * 100),
|
|
282
|
+
"%)"
|
|
283
|
+
] })
|
|
284
|
+
]
|
|
285
|
+
},
|
|
286
|
+
r.name
|
|
287
|
+
)) })
|
|
288
|
+
] })
|
|
289
|
+
] });
|
|
290
|
+
}
|
|
291
|
+
export {
|
|
292
|
+
CtrHorizontalBar,
|
|
293
|
+
DestinoDonut,
|
|
294
|
+
TopImpresionesBar
|
|
295
|
+
};
|
package/dist/AppTopbar.d.mts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import Button from './Button.mjs';
|
|
3
|
+
import React__default from 'react';
|
|
2
4
|
|
|
3
5
|
type TopbarAction = {
|
|
4
6
|
label: string;
|
|
5
7
|
onClick?: () => void;
|
|
6
8
|
};
|
|
9
|
+
type ButtonColor = NonNullable<React__default.ComponentProps<typeof Button>["color"]>;
|
|
7
10
|
type Props = {
|
|
8
11
|
title: string;
|
|
9
12
|
subtitle?: string;
|
|
10
13
|
primary?: TopbarAction;
|
|
11
14
|
secondary?: TopbarAction;
|
|
12
15
|
actions?: TopbarAction[];
|
|
16
|
+
color?: ButtonColor;
|
|
13
17
|
};
|
|
14
|
-
declare function AppTopbar({ title, subtitle, secondary, primary, actions }: Props): react_jsx_runtime.JSX.Element;
|
|
18
|
+
declare function AppTopbar({ title, subtitle, secondary, primary, actions, color }: Props): react_jsx_runtime.JSX.Element;
|
|
15
19
|
|
|
16
20
|
export { AppTopbar as default };
|