@roax/ui 0.1.0 → 0.3.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/dist/index.js CHANGED
@@ -29,11 +29,31 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
29
29
  // src/index.js
30
30
  var index_exports = {};
31
31
  __export(index_exports, {
32
+ BackButton: () => BackButton,
33
+ CardSkeleton: () => CardSkeleton,
32
34
  CustomTooltip: () => CustomTooltip,
35
+ FunnelChart: () => FunnelChart,
36
+ GeneralCard: () => GeneralCard,
33
37
  IconButton: () => IconButton,
38
+ InvestmentByPlatformChart: () => InvestmentByPlatformChart,
39
+ MetricCard: () => MetricCard,
40
+ MetricsSkeleton: () => MetricsSkeleton,
41
+ Modal: () => Modal,
42
+ NoDataMessage: () => NoDataMessage,
34
43
  OutlineButton: () => OutlineButton,
44
+ PaginatedGrid: () => PaginatedGrid,
45
+ PaginatedTable: () => PaginatedTable,
46
+ ReportTableSkeleton: () => ReportTableSkeleton,
47
+ SalesByChannelChart: () => SalesByChannelChart,
35
48
  SolidButton: () => SolidButton,
36
- TextButton: () => TextButton
49
+ SummaryReportSkeleton: () => SummaryReportSkeleton,
50
+ TextButton: () => TextButton,
51
+ TextSkeleton: () => TextSkeleton,
52
+ TimeSeriesComparisonChart: () => TimeSeriesComparisonChart,
53
+ TopProductsChart: () => TopProductsChart,
54
+ formatDateRange: () => formatDateRange,
55
+ formatValue: () => formatValue,
56
+ getBadgeStyles: () => getBadgeStyles
37
57
  });
38
58
  module.exports = __toCommonJS(index_exports);
39
59
 
@@ -41,19 +61,7 @@ module.exports = __toCommonJS(index_exports);
41
61
  var import_react_pro = require("@coreui/react-pro");
42
62
  var import_icons_react = __toESM(require("@coreui/icons-react"));
43
63
  var import_SolidButton = require("./SolidButton.scss");
44
- function SolidButton({
45
- title = "Button",
46
- onClick,
47
- color = "custom-primary",
48
- textColor = "custom-white",
49
- className = "",
50
- loading = false,
51
- icon,
52
- svgComponent: SvgComponent,
53
- type = "submit",
54
- badge,
55
- disabled
56
- }) {
64
+ function SolidButton({ title = "Button", onClick, color = "custom-primary", textColor = "custom-white", className = "", loading = false, icon, svgComponent: SvgComponent, type = "submit", badge, disabled }) {
57
65
  return /* @__PURE__ */ React.createElement(
58
66
  import_react_pro.CLoadingButton,
59
67
  {
@@ -63,12 +71,7 @@ function SolidButton({
63
71
  loading,
64
72
  disabledOnLoading: true,
65
73
  disabled,
66
- style: {
67
- minHeight: "44px",
68
- minWidth: "fit-content",
69
- fontSize: "0.95rem",
70
- letterSpacing: "0.3px"
71
- }
74
+ style: { minHeight: "44px", minWidth: "fit-content", fontSize: "0.95rem", letterSpacing: "0.3px" }
72
75
  },
73
76
  icon && /* @__PURE__ */ React.createElement(import_icons_react.default, { icon, className: "btn-icon" }),
74
77
  SvgComponent && !icon && /* @__PURE__ */ React.createElement(SvgComponent, null),
@@ -81,16 +84,7 @@ function SolidButton({
81
84
  var import_react_pro2 = require("@coreui/react-pro");
82
85
  var import_icons_react2 = __toESM(require("@coreui/icons-react"));
83
86
  var import_OutlineButton = require("./OutlineButton.scss");
84
- function OutlineButton({
85
- title = "Button",
86
- onClick,
87
- color = "custom-primary",
88
- className = "",
89
- loading = false,
90
- icon,
91
- type = "submit",
92
- disabled
93
- }) {
87
+ function OutlineButton({ title = "Button", onClick, color = "custom-primary", className = "", loading = false, icon, type = "submit", disabled }) {
94
88
  return /* @__PURE__ */ React.createElement(
95
89
  import_react_pro2.CLoadingButton,
96
90
  {
@@ -101,10 +95,7 @@ function OutlineButton({
101
95
  disabledOnLoading: true,
102
96
  disabled,
103
97
  variant: "outline",
104
- style: {
105
- maxHeight: "40px",
106
- minWidth: "fit-content"
107
- }
98
+ style: { maxHeight: "40px", minWidth: "fit-content" }
108
99
  },
109
100
  icon && /* @__PURE__ */ React.createElement(import_icons_react2.default, { icon }),
110
101
  /* @__PURE__ */ React.createElement("span", null, title)
@@ -114,17 +105,7 @@ function OutlineButton({
114
105
  // src/components/buttons/TextButton.jsx
115
106
  var import_react_pro3 = require("@coreui/react-pro");
116
107
  var import_icons_react3 = __toESM(require("@coreui/icons-react"));
117
- function TextButton({
118
- disabled,
119
- title,
120
- onClick,
121
- color = "dark",
122
- className = "",
123
- loading = false,
124
- icon,
125
- children
126
- }) {
127
- const buttonClasses = `rounded-3 px-2 d-flex justify-content-center gap-2 fw-medium ${className}`;
108
+ function TextButton({ disabled, title, onClick, color = "dark", className = "", loading = false, icon, children }) {
128
109
  return /* @__PURE__ */ React.createElement("span", { className: "d-inline-block", tabIndex: 0 }, /* @__PURE__ */ React.createElement(
129
110
  import_react_pro3.CLoadingButton,
130
111
  {
@@ -133,7 +114,7 @@ function TextButton({
133
114
  variant: "ghost",
134
115
  type: "button",
135
116
  color,
136
- className: buttonClasses,
117
+ className: `rounded-3 px-2 d-flex justify-content-center gap-2 fw-medium ${className}`,
137
118
  loading
138
119
  },
139
120
  icon && /* @__PURE__ */ React.createElement(import_icons_react3.default, { size: "xl", icon }),
@@ -148,37 +129,12 @@ var import_icons_react4 = __toESM(require("@coreui/icons-react"));
148
129
  // src/components/custom-tooltip/CustomTooltip.jsx
149
130
  var import_react = __toESM(require("@tippyjs/react"));
150
131
  var import_tippy = require("tippy.js/dist/tippy.css");
151
- function CustomTooltip({
152
- content,
153
- placement = "bottom",
154
- delay = 0,
155
- className = "",
156
- children
157
- }) {
158
- return /* @__PURE__ */ React.createElement(
159
- import_react.default,
160
- {
161
- content,
162
- placement,
163
- className,
164
- delay
165
- },
166
- children
167
- );
132
+ function CustomTooltip({ content, placement = "bottom", delay = 0, className = "", children }) {
133
+ return /* @__PURE__ */ React.createElement(import_react.default, { content, placement, className, delay }, children);
168
134
  }
169
135
 
170
136
  // src/components/buttons/IconButton.jsx
171
- function IconButton({
172
- title,
173
- onClick,
174
- color = "dark",
175
- className = "",
176
- loading = false,
177
- icon,
178
- tooltip = "",
179
- disabled
180
- }) {
181
- const buttonClasses = `d-flex justify-content-center align-items-center gap-1 m-0 p-1 ${className}`;
137
+ function IconButton({ title, onClick, color = "dark", className = "", loading = false, icon, tooltip = "", disabled }) {
182
138
  return /* @__PURE__ */ React.createElement(CustomTooltip, { content: tooltip, placement: "bottom" }, /* @__PURE__ */ React.createElement(
183
139
  import_react_pro4.CLoadingButton,
184
140
  {
@@ -186,7 +142,7 @@ function IconButton({
186
142
  variant: "ghost",
187
143
  type: "button",
188
144
  color,
189
- className: buttonClasses,
145
+ className: `d-flex justify-content-center align-items-center gap-1 m-0 p-1 ${className}`,
190
146
  loading,
191
147
  disabled
192
148
  },
@@ -194,12 +150,571 @@ function IconButton({
194
150
  title && /* @__PURE__ */ React.createElement("span", { className: "ml-2 d-none d-md-block" }, title)
195
151
  ));
196
152
  }
153
+
154
+ // src/components/modal/Modal.jsx
155
+ var import_icons_react5 = __toESM(require("@coreui/icons-react"));
156
+ var import_react_pro5 = require("@coreui/react-pro");
157
+ var import_icons = require("@coreui/icons");
158
+ function Modal({ title, titleIcon, visible, setVisible, alignment = "center", size, children }) {
159
+ return /* @__PURE__ */ React.createElement(import_react_pro5.CModal, { alignment, visible, onClose: () => setVisible(false), size, focus: true, keyboard: true, "aria-labelledby": "roax-modal-title" }, /* @__PURE__ */ React.createElement(import_react_pro5.CModalHeader, { className: "position-relative" }, /* @__PURE__ */ React.createElement(import_react_pro5.CModalTitle, { id: "roax-modal-title", className: "d-flex align-items-center gap-2" }, titleIcon && /* @__PURE__ */ React.createElement(import_icons_react5.default, { icon: titleIcon, size: "lg", className: "text-muted" }), title), /* @__PURE__ */ React.createElement(
160
+ "button",
161
+ {
162
+ onClick: () => setVisible(false),
163
+ type: "button",
164
+ className: "position-absolute end-0 top-0 mt-3 me-3 p-0 bg-transparent border-0",
165
+ style: { fontSize: "1.25rem", color: "var(--cui-body-color)", zIndex: 10 },
166
+ "aria-label": "Cerrar"
167
+ },
168
+ /* @__PURE__ */ React.createElement(import_icons_react5.default, { icon: import_icons.cilX, size: "lg" })
169
+ )), /* @__PURE__ */ React.createElement(import_react_pro5.CModalBody, null, children));
170
+ }
171
+
172
+ // src/components/card/MetricCard.jsx
173
+ var import_react_pro6 = require("@coreui/react-pro");
174
+ var import_icons_react6 = __toESM(require("@coreui/icons-react"));
175
+ var import_icons2 = require("@coreui/icons");
176
+
177
+ // src/components/card/card-utils.js
178
+ var formatValue = (val, unit = "", title = "") => {
179
+ if (val == null || typeof val !== "number" || val === 0) return "0";
180
+ if (title.includes("ROAS")) return `x ${val.toFixed(2)}`;
181
+ if (title.includes("CAC %")) return `${val.toFixed(2)}%`;
182
+ if (unit === "%" || title.includes("%")) return `${val.toFixed(2)}%`;
183
+ if (unit === "x") return `x ${val.toFixed(2)}`;
184
+ if (unit === "COP") return `COP ${Math.round(val).toLocaleString("es-CO")}`;
185
+ return val.toLocaleString(void 0, { minimumFractionDigits: 0, maximumFractionDigits: 2 });
186
+ };
187
+ var getBadgeStyles = (isPositive) => ({
188
+ backgroundColor: isPositive ? "#55ed7b" : "#f74a4a",
189
+ color: isPositive ? "#0b3e26" : "#842029",
190
+ borderRadius: "9999px",
191
+ padding: "4px 10px",
192
+ fontSize: "0.75rem",
193
+ fontWeight: 700,
194
+ display: "inline-flex",
195
+ alignItems: "center",
196
+ gap: "4px"
197
+ });
198
+ function formatDateRange(previousDateRange) {
199
+ if (!previousDateRange) return { formattedRange: "" };
200
+ const parts = previousDateRange.split(" - ");
201
+ const parseDate = (str) => {
202
+ const [y, m, d] = str.split("-").map(Number);
203
+ return new Date(y, m - 1, d);
204
+ };
205
+ const format = (date) => {
206
+ const day = String(date.getDate()).padStart(2, "0");
207
+ const month = date.toLocaleString("es-CO", { month: "short" }).replace(".", "");
208
+ const year = String(date.getFullYear()).slice(-2);
209
+ return `${month} ${day},${year}`;
210
+ };
211
+ const since = parseDate(parts[0]);
212
+ const until = parts[1] ? parseDate(parts[1]) : null;
213
+ return { formattedRange: until ? `${format(since)} - ${format(until)}` : format(since) };
214
+ }
215
+
216
+ // src/components/card/MetricCard.jsx
217
+ var import_MetricCard = require("./MetricCard.scss");
218
+ function MetricCard({ title, current, previous, change, isPositive, unit = "", previousDateRange = "", tooltipText = "", backgroundColor }) {
219
+ const isColored = !!backgroundColor;
220
+ return /* @__PURE__ */ React.createElement(
221
+ import_react_pro6.CCard,
222
+ {
223
+ className: `metric-card w-100 h-100 d-flex flex-column border-0${isColored ? " metric-card--colored" : ""}`,
224
+ style: isColored ? { background: backgroundColor } : void 0
225
+ },
226
+ /* @__PURE__ */ React.createElement(import_react_pro6.CCardBody, { className: "d-flex flex-column justify-content-between h-100 p-4 position-relative" }, tooltipText && /* @__PURE__ */ React.createElement("div", { className: "position-absolute top-0 end-0 m-2" }, /* @__PURE__ */ React.createElement(CustomTooltip, { content: tooltipText, placement: "top" }, /* @__PURE__ */ React.createElement(import_icons_react6.default, { icon: import_icons2.cilInfo, style: { cursor: "pointer", opacity: 0.5, width: 16, height: 16 } }))), /* @__PURE__ */ React.createElement("p", { className: "metric-card__title m-0" }, title), /* @__PURE__ */ React.createElement("div", { className: "d-flex align-items-center gap-2 mt-1" }, /* @__PURE__ */ React.createElement("span", { className: "metric-card__value" }, formatValue(current, unit, title)), typeof change === "number" && isFinite(change) && current > 0 && /* @__PURE__ */ React.createElement("div", { style: getBadgeStyles(isPositive) }, /* @__PURE__ */ React.createElement(import_icons_react6.default, { icon: isPositive ? import_icons2.cilArrowTop : import_icons2.cilArrowBottom, size: "sm" }), Math.abs(change), "%")), /* @__PURE__ */ React.createElement("hr", { className: "metric-card__divider" }), (() => {
227
+ const { formattedRange } = formatDateRange(previousDateRange);
228
+ return /* @__PURE__ */ React.createElement("div", { className: "d-flex justify-content-between align-items-center metric-card__footer" }, /* @__PURE__ */ React.createElement("strong", null, formatValue(previous, unit, title)), /* @__PURE__ */ React.createElement("span", null, formattedRange));
229
+ })())
230
+ );
231
+ }
232
+
233
+ // src/components/card/GeneralCard.jsx
234
+ var import_react_pro7 = require("@coreui/react-pro");
235
+ var import_icons_react7 = __toESM(require("@coreui/icons-react"));
236
+ var import_icons3 = require("@coreui/icons");
237
+ var import_lucide_react = require("lucide-react");
238
+ var import_general_card = require("./general-card.scss");
239
+ function GeneralCard({
240
+ title,
241
+ current,
242
+ previous,
243
+ change,
244
+ isPositive,
245
+ unit = "",
246
+ previousDateRange = "",
247
+ tooltipText = "",
248
+ backgroundColor
249
+ }) {
250
+ const isColored = !!backgroundColor;
251
+ const cardClass = `general-card w-100 h-100 d-flex flex-column border-0${isColored ? " general-card--colored" : ""}`;
252
+ return /* @__PURE__ */ React.createElement(
253
+ import_react_pro7.CCard,
254
+ {
255
+ className: cardClass,
256
+ style: isColored ? { background: backgroundColor } : void 0
257
+ },
258
+ /* @__PURE__ */ React.createElement(import_react_pro7.CCardBody, { className: "d-flex flex-column justify-content-between h-100 p-4 position-relative" }, tooltipText && /* @__PURE__ */ React.createElement("div", { className: "position-absolute top-0 end-0 m-2" }, /* @__PURE__ */ React.createElement(CustomTooltip, { content: tooltipText, placement: "top" }, /* @__PURE__ */ React.createElement(
259
+ import_lucide_react.Info,
260
+ {
261
+ size: 16,
262
+ strokeWidth: 2,
263
+ style: { cursor: "pointer", opacity: 0.5 }
264
+ }
265
+ ))), /* @__PURE__ */ React.createElement("p", { className: "general-card__title m-0" }, title), /* @__PURE__ */ React.createElement("div", { className: "d-flex align-items-center gap-2 mt-1" }, /* @__PURE__ */ React.createElement("span", { className: "general-card__value" }, formatValue(current, unit, title)), typeof change === "number" && isFinite(change) && current > 0 && /* @__PURE__ */ React.createElement("div", { style: getBadgeStyles(isPositive) }, /* @__PURE__ */ React.createElement(import_icons_react7.default, { icon: isPositive ? import_icons3.cilArrowTop : import_icons3.cilArrowBottom, size: "sm" }), Math.abs(change), "%")), /* @__PURE__ */ React.createElement("hr", { className: "general-card__divider" }), (() => {
266
+ const { formattedRange } = formatDateRange(previousDateRange);
267
+ return /* @__PURE__ */ React.createElement("div", { className: "d-flex justify-content-between align-items-center general-card__footer" }, /* @__PURE__ */ React.createElement("strong", null, formatValue(previous, unit, title)), /* @__PURE__ */ React.createElement("span", null, formattedRange));
268
+ })())
269
+ );
270
+ }
271
+
272
+ // src/components/charts/FunnelChart.jsx
273
+ var import_recharts = require("recharts");
274
+ var import_react_pro8 = require("@coreui/react-pro");
275
+ function FunnelChart({ data = [] }) {
276
+ return /* @__PURE__ */ React.createElement(import_react_pro8.CCard, { className: "mt-4 rounded-4 shadow-sm p-3" }, /* @__PURE__ */ React.createElement(import_react_pro8.CCardBody, null, /* @__PURE__ */ React.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: data.length * 45 + 50 }, /* @__PURE__ */ React.createElement(
277
+ import_recharts.BarChart,
278
+ {
279
+ data,
280
+ layout: "vertical",
281
+ margin: { top: 10, right: 60, left: 20, bottom: 0 },
282
+ barCategoryGap: 15
283
+ },
284
+ /* @__PURE__ */ React.createElement("defs", null, /* @__PURE__ */ React.createElement("linearGradient", { id: "funnelGradient", x1: "0", y1: "0", x2: "1", y2: "0" }, /* @__PURE__ */ React.createElement("stop", { offset: "0%", stopColor: "#FF8AAE" }), /* @__PURE__ */ React.createElement("stop", { offset: "100%", stopColor: "#FF1F3D" }))),
285
+ /* @__PURE__ */ React.createElement(import_recharts.XAxis, { type: "number", hide: true, domain: [0, "dataMax"] }),
286
+ /* @__PURE__ */ React.createElement(
287
+ import_recharts.YAxis,
288
+ {
289
+ dataKey: "title",
290
+ type: "category",
291
+ width: 130,
292
+ axisLine: false,
293
+ tickLine: false,
294
+ style: { fontWeight: 600, fill: "#333", fontSize: 13 }
295
+ }
296
+ ),
297
+ /* @__PURE__ */ React.createElement(
298
+ import_recharts.Tooltip,
299
+ {
300
+ content: ({ active, payload }) => {
301
+ if (active && payload && payload.length > 0) {
302
+ const { value, payload: item } = payload[0];
303
+ return /* @__PURE__ */ React.createElement("div", { className: "bg-white shadow-sm p-2 rounded" }, /* @__PURE__ */ React.createElement("strong", null, item.title), ":", " ", /* @__PURE__ */ React.createElement("span", null, formatValue(value, item.unit, item.title)));
304
+ }
305
+ return null;
306
+ }
307
+ }
308
+ ),
309
+ /* @__PURE__ */ React.createElement(import_recharts.Bar, { dataKey: "value", fill: "url(#funnelGradient)", radius: [0, 10, 10, 0] }, /* @__PURE__ */ React.createElement(
310
+ import_recharts.LabelList,
311
+ {
312
+ dataKey: "value",
313
+ position: "right",
314
+ formatter: (val) => formatValue(val),
315
+ style: { fill: "#333", fontWeight: 500, fontSize: 12 }
316
+ }
317
+ ), data.map((entry, index) => /* @__PURE__ */ React.createElement(
318
+ import_recharts.Cell,
319
+ {
320
+ key: `cell-${entry.title || index}`,
321
+ fill: entry.color || "url(#funnelGradient)"
322
+ }
323
+ )))
324
+ )), /* @__PURE__ */ React.createElement(import_react_pro8.CRow, { className: "pt-4 g-3" }, data.slice(0, 3).map((item, index) => /* @__PURE__ */ React.createElement(import_react_pro8.CCol, { xs: 12, md: 4, key: `card-top-${index}` }, /* @__PURE__ */ React.createElement(
325
+ GeneralCard,
326
+ {
327
+ title: item.title,
328
+ current: item.value,
329
+ previous: item.previous,
330
+ change: item.percentageChange,
331
+ isPositive: item.isPositive,
332
+ unit: item.unit,
333
+ previousDateRange: item.previousDateRange,
334
+ tooltipText: item.title
335
+ }
336
+ )))), data.length > 3 && /* @__PURE__ */ React.createElement(import_react_pro8.CRow, { className: "pt-3 g-3" }, data.slice(3).map((item, index) => /* @__PURE__ */ React.createElement(import_react_pro8.CCol, { xs: 12, md: 5, lg: 4, key: `card-bottom-${index}` }, /* @__PURE__ */ React.createElement(
337
+ GeneralCard,
338
+ {
339
+ title: item.title,
340
+ current: item.value,
341
+ previous: item.previous,
342
+ change: item.percentageChange,
343
+ isPositive: item.isPositive,
344
+ unit: item.unit,
345
+ previousDateRange: item.previousDateRange,
346
+ tooltipText: item.title
347
+ }
348
+ ))))));
349
+ }
350
+
351
+ // src/components/charts/TimeSeriesComparisonChart.jsx
352
+ var import_recharts2 = require("recharts");
353
+ var import_react_pro9 = require("@coreui/react-pro");
354
+ function TimeSeriesComparisonChart({
355
+ data = [],
356
+ lines = [],
357
+ colors = {},
358
+ cards = [],
359
+ currency = "",
360
+ yAxisLabel = "",
361
+ displayNames = {}
362
+ }) {
363
+ const detectUnit = (name = "") => {
364
+ const lower = name.toLowerCase();
365
+ if (lower.includes("%")) return "%";
366
+ if (lower.includes("venta") || lower.includes("inversi\xF3n")) return currency || "";
367
+ return "";
368
+ };
369
+ const maxY = data.length ? Math.ceil(
370
+ Math.max(
371
+ ...lines.map((lineName) => Math.max(...data.map((d) => d[lineName] || 0)))
372
+ ) * 1.1
373
+ ) : 100;
374
+ return /* @__PURE__ */ React.createElement(import_react_pro9.CCard, { className: "mt-4 rounded-4 shadow-sm p-3" }, /* @__PURE__ */ React.createElement(import_react_pro9.CCardBody, null, cards.length > 0 && /* @__PURE__ */ React.createElement(import_react_pro9.CRow, { className: "pt-4 g-3 justify-content-center text-center mb-4" }, cards.map((card, index) => /* @__PURE__ */ React.createElement(import_react_pro9.CCol, { xs: 12, md: 4, lg: 5, key: index }, /* @__PURE__ */ React.createElement(
375
+ GeneralCard,
376
+ {
377
+ title: card.title,
378
+ current: card.value,
379
+ previous: card.previous,
380
+ change: card.percentageChange,
381
+ isPositive: card.isPositive,
382
+ unit: card.unit,
383
+ previousDateRange: card.previousDateRange,
384
+ tooltipText: card.title
385
+ }
386
+ )))), /* @__PURE__ */ React.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 340 }, /* @__PURE__ */ React.createElement(import_recharts2.LineChart, { data, margin: { top: 20, right: 30, left: 10, bottom: 10 } }, /* @__PURE__ */ React.createElement(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", stroke: "#e5e5e5" }), /* @__PURE__ */ React.createElement(import_recharts2.XAxis, { dataKey: "date", stroke: "#333", tick: { fontSize: 12 } }), /* @__PURE__ */ React.createElement(
387
+ import_recharts2.YAxis,
388
+ {
389
+ yAxisId: "left",
390
+ stroke: "#333",
391
+ tickFormatter: (val) => `${currency ? currency + " " : ""}${val >= 1e6 ? (val / 1e6).toFixed(1) + "M" : val.toLocaleString("es-CO")}`,
392
+ tick: { fontSize: 12 },
393
+ width: 100,
394
+ domain: [0, maxY],
395
+ allowDecimals: false,
396
+ label: yAxisLabel ? {
397
+ value: yAxisLabel,
398
+ angle: -90,
399
+ position: "insideLeft",
400
+ style: { textAnchor: "middle", fill: "#333", fontSize: 13 }
401
+ } : void 0
402
+ }
403
+ ), /* @__PURE__ */ React.createElement(
404
+ import_recharts2.Tooltip,
405
+ {
406
+ formatter: (val, name) => {
407
+ const unit = detectUnit(name);
408
+ return [formatValue(val, unit), displayNames[name] || name];
409
+ },
410
+ labelClassName: "fw-bold",
411
+ wrapperStyle: { zIndex: 1e3 }
412
+ }
413
+ ), /* @__PURE__ */ React.createElement(
414
+ import_recharts2.Legend,
415
+ {
416
+ verticalAlign: "bottom",
417
+ height: 36,
418
+ wrapperStyle: { marginTop: 20 },
419
+ iconType: "circle",
420
+ formatter: (value) => displayNames[value] || value
421
+ }
422
+ ), lines.map((lineName) => /* @__PURE__ */ React.createElement(
423
+ import_recharts2.Line,
424
+ {
425
+ key: lineName,
426
+ yAxisId: "left",
427
+ type: "monotone",
428
+ dataKey: lineName,
429
+ stroke: colors[lineName] || "#000",
430
+ strokeWidth: 2.5,
431
+ dot: { r: 4 },
432
+ activeDot: { r: 6 }
433
+ }
434
+ ))))));
435
+ }
436
+
437
+ // src/components/charts/TopProductsChart.jsx
438
+ var import_recharts3 = require("recharts");
439
+ var import_react_pro10 = require("@coreui/react-pro");
440
+ var COLORS = ["#FF6A8D", "#FF8AAE", "#FFA3BC", "#FFC3D0", "#FFE0E7"];
441
+ var ChartTooltip = ({ active, payload }) => {
442
+ if (active && payload && payload.length) {
443
+ return /* @__PURE__ */ React.createElement("div", { className: "bg-white p-2 shadow rounded border border-light" }, /* @__PURE__ */ React.createElement("p", { className: "mb-1 fw-semibold" }, payload[0].payload.name), /* @__PURE__ */ React.createElement("p", { className: "mb-0 text-muted" }, "Cantidad: ", payload[0].value.toLocaleString()));
444
+ }
445
+ return null;
446
+ };
447
+ function TopProductsChart({
448
+ data = [],
449
+ title = "Productos M\xE1s Vendidos",
450
+ emptyText = "No se registraron productos vendidos en este rango de fechas.",
451
+ maxItems = 5
452
+ }) {
453
+ const processedData = (data || []).filter((item) => {
454
+ var _a;
455
+ return item.quantity > 0 && ((_a = item.name) == null ? void 0 : _a.trim());
456
+ }).sort((a, b) => b.quantity - a.quantity).slice(0, maxItems).map((item) => ({ name: item.name, quantity: item.quantity }));
457
+ if (!processedData.length) {
458
+ return /* @__PURE__ */ React.createElement(import_react_pro10.CCard, { className: "mt-4 rounded-4 shadow-sm p-3" }, /* @__PURE__ */ React.createElement(import_react_pro10.CCardBody, { className: "text-center py-5" }, /* @__PURE__ */ React.createElement("p", { className: "text-muted mb-2" }, /* @__PURE__ */ React.createElement("i", { className: "bi bi-box-seam fs-4 d-block mb-2" }), emptyText)));
459
+ }
460
+ return /* @__PURE__ */ React.createElement(import_react_pro10.CCard, { className: "mt-4 rounded-4 shadow-sm p-3" }, /* @__PURE__ */ React.createElement(import_react_pro10.CCardBody, null, /* @__PURE__ */ React.createElement("h5", { className: "fw-semibold text-center fs-5 mb-4" }, title), /* @__PURE__ */ React.createElement("div", { style: { height: `${Math.max(processedData.length * 50, 80)}px` } }, /* @__PURE__ */ React.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React.createElement(
461
+ import_recharts3.BarChart,
462
+ {
463
+ layout: "vertical",
464
+ data: processedData,
465
+ margin: { top: 10, right: 40, left: 20, bottom: 10 }
466
+ },
467
+ /* @__PURE__ */ React.createElement("defs", null, /* @__PURE__ */ React.createElement("linearGradient", { id: "barGradient", x1: "0", y1: "0", x2: "1", y2: "0" }, /* @__PURE__ */ React.createElement("stop", { offset: "0%", stopColor: "#FF8AAE" }), /* @__PURE__ */ React.createElement("stop", { offset: "100%", stopColor: "#FF1F3D" }))),
468
+ /* @__PURE__ */ React.createElement(
469
+ import_recharts3.XAxis,
470
+ {
471
+ type: "number",
472
+ tickFormatter: (value) => value.toLocaleString(),
473
+ axisLine: false,
474
+ tick: { fontSize: 12 }
475
+ }
476
+ ),
477
+ /* @__PURE__ */ React.createElement(
478
+ import_recharts3.YAxis,
479
+ {
480
+ type: "category",
481
+ dataKey: "name",
482
+ width: 150,
483
+ tick: { fontSize: 13 },
484
+ tickFormatter: (value) => value.length > 30 ? value.substring(0, 30) + "..." : value
485
+ }
486
+ ),
487
+ /* @__PURE__ */ React.createElement(import_recharts3.Tooltip, { content: /* @__PURE__ */ React.createElement(ChartTooltip, null) }),
488
+ /* @__PURE__ */ React.createElement(import_recharts3.Bar, { dataKey: "quantity", fill: "url(#barGradient)", radius: [0, 8, 8, 0] }, /* @__PURE__ */ React.createElement(
489
+ import_recharts3.LabelList,
490
+ {
491
+ dataKey: "quantity",
492
+ position: "right",
493
+ formatter: (value) => value.toLocaleString(),
494
+ style: { fill: "#333", fontWeight: 500, fontSize: 12 }
495
+ }
496
+ ), processedData.map((_, index) => /* @__PURE__ */ React.createElement(import_recharts3.Cell, { key: `cell-${index}`, fill: COLORS[index % COLORS.length] })))
497
+ )))));
498
+ }
499
+
500
+ // src/components/charts/SalesByChannelChart.jsx
501
+ var import_recharts4 = require("recharts");
502
+ var import_react_pro11 = require("@coreui/react-pro");
503
+ function SalesByChannelChart({
504
+ data = [],
505
+ title = "Ventas por canal",
506
+ emptyText = "Sin datos para este per\xEDodo",
507
+ channels = ["Meta", "TikTok", "Google"],
508
+ channelColors = { Meta: "#FF2F86", TikTok: "#C0C0C0", Google: "#333333" }
509
+ }) {
510
+ const hasData = data.length > 0;
511
+ return /* @__PURE__ */ React.createElement(import_react_pro11.CCard, { className: "rounded-4 shadow-sm p-3" }, /* @__PURE__ */ React.createElement(import_react_pro11.CCardBody, null, /* @__PURE__ */ React.createElement("h5", { className: "fw-bold text-center" }, title), !hasData ? /* @__PURE__ */ React.createElement("p", { className: "text-center text-muted" }, emptyText) : /* @__PURE__ */ React.createElement(import_recharts4.ResponsiveContainer, { width: "100%", height: 300 }, /* @__PURE__ */ React.createElement(import_recharts4.BarChart, { data, margin: { top: 20, right: 30, left: 10, bottom: 10 } }, /* @__PURE__ */ React.createElement(import_recharts4.CartesianGrid, { strokeDasharray: "3 3", stroke: "#e5e5e5" }), /* @__PURE__ */ React.createElement(
512
+ import_recharts4.XAxis,
513
+ {
514
+ dataKey: "fecha",
515
+ tick: { fontSize: 12 },
516
+ angle: -45,
517
+ textAnchor: "end",
518
+ interval: Math.ceil(data.length / 10)
519
+ }
520
+ ), /* @__PURE__ */ React.createElement(import_recharts4.YAxis, { tick: { fontSize: 12 } }), /* @__PURE__ */ React.createElement(import_recharts4.Tooltip, null), /* @__PURE__ */ React.createElement(import_recharts4.Legend, { verticalAlign: "top", height: 36, iconType: "circle" }), channels.map((channel) => /* @__PURE__ */ React.createElement(
521
+ import_recharts4.Bar,
522
+ {
523
+ key: channel,
524
+ dataKey: channel,
525
+ stackId: "ventas",
526
+ fill: channelColors[channel] || "#888"
527
+ }
528
+ ))))));
529
+ }
530
+
531
+ // src/components/charts/InvestmentByPlatformChart.jsx
532
+ var import_recharts5 = require("recharts");
533
+ var import_react_pro12 = require("@coreui/react-pro");
534
+ var DEFAULT_COLORS = ["#FF2F86", "#D9D9D9", "#333333"];
535
+ function InvestmentByPlatformChart({
536
+ data = [],
537
+ title = "Inversi\xF3n por plataforma",
538
+ colors = DEFAULT_COLORS,
539
+ currencySymbol = "COP",
540
+ locale = "es-CO"
541
+ }) {
542
+ return /* @__PURE__ */ React.createElement(import_react_pro12.CCard, { className: "rounded-4 shadow-sm p-3" }, /* @__PURE__ */ React.createElement(import_react_pro12.CCardBody, null, /* @__PURE__ */ React.createElement("h5", { className: "text-center fw-bold" }, title), /* @__PURE__ */ React.createElement(import_recharts5.ResponsiveContainer, { width: "100%", height: 280 }, /* @__PURE__ */ React.createElement(import_recharts5.PieChart, null, /* @__PURE__ */ React.createElement(
543
+ import_recharts5.Pie,
544
+ {
545
+ data,
546
+ dataKey: "value",
547
+ nameKey: "name",
548
+ cx: "50%",
549
+ cy: "50%",
550
+ innerRadius: 60,
551
+ outerRadius: 100,
552
+ paddingAngle: 5,
553
+ label: ({ name, percent }) => `${name} ${(percent * 100).toFixed(1)}%`
554
+ },
555
+ data.map((_, index) => /* @__PURE__ */ React.createElement(import_recharts5.Cell, { key: `cell-${index}`, fill: colors[index % colors.length] }))
556
+ ), /* @__PURE__ */ React.createElement(
557
+ import_recharts5.Tooltip,
558
+ {
559
+ formatter: (value) => `${currencySymbol} ${value.toLocaleString(locale)}`
560
+ }
561
+ ), /* @__PURE__ */ React.createElement(import_recharts5.Legend, { verticalAlign: "bottom", height: 36 })))));
562
+ }
563
+
564
+ // src/components/pagination/PaginatedTable.jsx
565
+ var import_react_pro13 = require("@coreui/react-pro");
566
+ function PaginatedTable({ data = [], itemsPerPage = 10, currentPage = 1, setCurrentPage = () => {
567
+ }, columns = [], renderRow = () => null, loading = false, emptyMessage = "No hay datos disponibles." }) {
568
+ const totalPages = Math.ceil(data.length / itemsPerPage);
569
+ const paginatedData = data.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage);
570
+ if (loading) return /* @__PURE__ */ React.createElement("div", { className: "text-center py-5" }, /* @__PURE__ */ React.createElement(import_react_pro13.CSpinner, { color: "secondary" }));
571
+ if (!data.length) return /* @__PURE__ */ React.createElement("div", { className: "text-center py-5 text-muted" }, emptyMessage);
572
+ const renderPagination = () => {
573
+ const pages = Array.from({ length: totalPages }, (_, i) => i + 1);
574
+ return /* @__PURE__ */ React.createElement("div", { className: "d-flex justify-content-center align-items-center gap-2 mt-3 flex-wrap" }, currentPage > 1 && /* @__PURE__ */ React.createElement("button", { onClick: () => setCurrentPage(currentPage - 1), className: "px-3 py-1 rounded-pill border bg-white text-dark", style: { minWidth: 80, fontWeight: 500 } }, "Anterior"), pages.map((i) => /* @__PURE__ */ React.createElement("button", { key: i, onClick: () => setCurrentPage(i), className: `px-3 py-1 rounded-pill border ${i === currentPage ? "bg-dark text-white" : "bg-white text-dark"}` }, i)), currentPage < totalPages && /* @__PURE__ */ React.createElement("button", { onClick: () => setCurrentPage(currentPage + 1), className: "px-3 py-1 rounded-pill border bg-white text-dark", style: { minWidth: 80, fontWeight: 500 } }, "Siguiente"));
575
+ };
576
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(import_react_pro13.CTable, { hover: true, responsive: true }, /* @__PURE__ */ React.createElement(import_react_pro13.CTableHead, { className: "bg-dark text-white" }, /* @__PURE__ */ React.createElement(import_react_pro13.CTableRow, null, columns.map((col, i) => /* @__PURE__ */ React.createElement(import_react_pro13.CTableHeaderCell, { key: i }, col)))), /* @__PURE__ */ React.createElement(import_react_pro13.CTableBody, null, paginatedData.map(renderRow))), totalPages > 1 && renderPagination());
577
+ }
578
+
579
+ // src/components/pagination/PaginatedGrid.jsx
580
+ var import_react_pro14 = require("@coreui/react-pro");
581
+ function PaginatedGrid({ data = [], itemsPerPage = 6, currentPage = 1, setCurrentPage = () => {
582
+ }, renderItem = () => null, loading = false, emptyMessage = "No hay elementos." }) {
583
+ const totalPages = Math.ceil(data.length / itemsPerPage);
584
+ const paginatedData = data.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage);
585
+ if (loading) return /* @__PURE__ */ React.createElement("div", { className: "text-center py-5" }, /* @__PURE__ */ React.createElement(import_react_pro14.CSpinner, { color: "secondary" }));
586
+ if (!data.length) return /* @__PURE__ */ React.createElement("div", { className: "text-center py-5 text-muted" }, emptyMessage);
587
+ const renderPagination = () => {
588
+ const pages = Array.from({ length: totalPages }, (_, i) => i + 1);
589
+ return /* @__PURE__ */ React.createElement("div", { className: "d-flex justify-content-center gap-2 mt-4 flex-wrap" }, currentPage > 1 && /* @__PURE__ */ React.createElement("button", { onClick: () => setCurrentPage(currentPage - 1), className: "px-3 py-1 rounded-pill border bg-white text-dark" }, "Anterior"), pages.map((i) => /* @__PURE__ */ React.createElement("button", { key: i, onClick: () => setCurrentPage(i), className: `px-3 py-1 rounded-pill border ${i === currentPage ? "bg-dark text-white" : "bg-white text-dark"}` }, i)), currentPage < totalPages && /* @__PURE__ */ React.createElement("button", { onClick: () => setCurrentPage(currentPage + 1), className: "px-3 py-1 rounded-pill border bg-white text-dark" }, "Siguiente"));
590
+ };
591
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "row g-4" }, paginatedData.map(renderItem)), totalPages > 1 && renderPagination());
592
+ }
593
+
594
+ // src/components/skeletons/CardSkeleton.jsx
595
+ var import_react_pro15 = require("@coreui/react-pro");
596
+ function CardSkeleton() {
597
+ return /* @__PURE__ */ React.createElement(import_react_pro15.CCard, { className: "shadow-none p-0 m-0 bg-transparent border-0" }, /* @__PURE__ */ React.createElement(import_react_pro15.CPlaceholder, { component: "div", animation: "glow", className: "d-flex flex-column gap-2 bg-transparent" }, /* @__PURE__ */ React.createElement(import_react_pro15.CPlaceholder, { className: "p-4 mt-2 rounded-1", size: "lg", xs: 12, color: "skeleton" })));
598
+ }
599
+
600
+ // src/components/skeletons/TextSkeleton.jsx
601
+ var import_react_pro16 = require("@coreui/react-pro");
602
+ function TextSkeleton() {
603
+ return /* @__PURE__ */ React.createElement(import_react_pro16.CCard, { className: "shadow-none p-0 m-0 bg-transparent border-0" }, /* @__PURE__ */ React.createElement(import_react_pro16.CPlaceholder, { component: "div", animation: "glow", className: "d-flex flex-column gap-2 bg-transparent" }, /* @__PURE__ */ React.createElement(import_react_pro16.CPlaceholder, { className: "p-0 my-2 rounded-1", size: "lg", xs: 8, color: "skeleton" })));
604
+ }
605
+
606
+ // src/components/skeletons/MetricsSkeleton.jsx
607
+ var import_react_pro17 = require("@coreui/react-pro");
608
+ function MetricsSkeleton() {
609
+ return /* @__PURE__ */ React.createElement(import_react_pro17.CCard, { className: "shadow-none p-0 m-0 bg-transparent border-0" }, /* @__PURE__ */ React.createElement(import_react_pro17.CPlaceholder, { component: "div", animation: "glow", className: "d-flex flex-column gap-2 bg-transparent" }, /* @__PURE__ */ React.createElement(import_react_pro17.CPlaceholder, { className: "p-5 mt-2 rounded-1", size: "lg", xs: 12, color: "skeleton" }), /* @__PURE__ */ React.createElement(import_react_pro17.CPlaceholder, { className: "p-3 mt-2 rounded-1", size: "xs", xs: 12, color: "skeleton" })));
610
+ }
611
+
612
+ // src/components/skeletons/ReportTableSkeleton.jsx
613
+ var import_react_pro18 = require("@coreui/react-pro");
614
+ function ReportTableSkeleton() {
615
+ return /* @__PURE__ */ React.createElement(import_react_pro18.CCard, { className: "shadow-none p-0 m-0 bg-transparent border-0" }, /* @__PURE__ */ React.createElement(
616
+ import_react_pro18.CPlaceholder,
617
+ {
618
+ component: "div",
619
+ animation: "glow",
620
+ className: "d-flex flex-column gap-2 bg-transparent"
621
+ },
622
+ /* @__PURE__ */ React.createElement(import_react_pro18.CPlaceholder, { className: "p-4 mt-2 rounded-1", size: "lg", xs: 12, color: "skeleton" }),
623
+ /* @__PURE__ */ React.createElement(import_react_pro18.CPlaceholder, { className: "p-5 mt-2 rounded-1", size: "lg", xs: 12, color: "skeleton" })
624
+ ));
625
+ }
626
+
627
+ // src/components/skeletons/SummaryReportSkeleton.jsx
628
+ var import_react_pro19 = require("@coreui/react-pro");
629
+ var CardPlaceholder = ({ className = "" }) => /* @__PURE__ */ React.createElement(import_react_pro19.CCard, { className: "shadow-none p-0 m-0 bg-transparent border-0" }, /* @__PURE__ */ React.createElement(
630
+ import_react_pro19.CPlaceholder,
631
+ {
632
+ component: "div",
633
+ animation: "glow",
634
+ className: `d-flex flex-column gap-2 bg-transparent ${className}`
635
+ },
636
+ /* @__PURE__ */ React.createElement(import_react_pro19.CPlaceholder, { className: "p-4 mt-2 rounded-2", size: "lg", xs: 12, color: "skeleton" })
637
+ ));
638
+ function SummaryReportSkeleton({
639
+ layout = [
640
+ { sm: 6, md: 6 },
641
+ { sm: 6, md: 6 },
642
+ { sm: 6, md: 4 },
643
+ { sm: 6, md: 4 },
644
+ { sm: 6, md: 4 },
645
+ { sm: 6, md: 6 },
646
+ { sm: 6, md: 4 },
647
+ { sm: 6, md: 2 }
648
+ ]
649
+ }) {
650
+ return /* @__PURE__ */ React.createElement(import_react_pro19.CRow, { className: "g-md-3 px-4 px-sm-2 g-sm-2 pt-2 pb-4" }, layout.map((col, index) => /* @__PURE__ */ React.createElement(import_react_pro19.CCol, { sm: col.sm, md: col.md, key: index }, /* @__PURE__ */ React.createElement(CardPlaceholder, null))));
651
+ }
652
+
653
+ // ../../node_modules/@coreui/icons-pro/dist/esm/duotone/cid-exclamation-circle.js
654
+ var cidExclamationCircle = ["512 512", "<path fill='var(--ci-secondary-color, currentColor)' d='M256,16C123.452,16,16,123.452,16,256S123.452,496,256,496a238.867,238.867,0,0,0,132.685-40q1.668-1.108,3.315-2.246A239.721,239.721,0,0,0,496,256C496,123.452,388.548,16,256,16Z' class='ci-secondary' opacity='var(--ci-secondary-opacity, 0.25)'/><polygon fill='var(--ci-primary-color, currentColor)' points='224 232 238 304 274 304 288 232 288 120 224 120 224 232' class='ci-primary'/><rect width='40' height='40' x='236' y='344' fill='var(--ci-primary-color, currentColor)' class='ci-primary'/>"];
655
+
656
+ // src/components/feedback/NoDataMessage.jsx
657
+ var import_icons_react8 = __toESM(require("@coreui/icons-react"));
658
+ function NoDataMessage({
659
+ message = "No se obtuvieron datos. Revisa el rango de fechas y la integraci\xF3n."
660
+ }) {
661
+ return /* @__PURE__ */ React.createElement(
662
+ "div",
663
+ {
664
+ className: "d-flex align-items-center gap-2 rounded-3 px-3 py-3 mt-3 mb-3",
665
+ style: {
666
+ backgroundColor: "var(--cui-warning-bg-subtle)",
667
+ border: "1px solid var(--cui-warning)",
668
+ color: "var(--cui-body-color)"
669
+ }
670
+ },
671
+ /* @__PURE__ */ React.createElement(
672
+ import_icons_react8.default,
673
+ {
674
+ icon: cidExclamationCircle,
675
+ className: "flex-shrink-0 me-2 text-warning",
676
+ size: "xxl"
677
+ }
678
+ ),
679
+ message
680
+ );
681
+ }
682
+
683
+ // src/components/back-button/BackButton.jsx
684
+ var import_navigation = require("next/navigation");
685
+ var import_react_pro20 = require("@coreui/react-pro");
686
+ var import_icons4 = require("@coreui/icons");
687
+ var import_icons_react9 = __toESM(require("@coreui/icons-react"));
688
+ function BackButton({ title, color = "dark", className = "", path }) {
689
+ const { back, push } = (0, import_navigation.useRouter)();
690
+ return /* @__PURE__ */ React.createElement(CustomTooltip, { content: "Volver atr\xE1s", placement: "bottom" }, /* @__PURE__ */ React.createElement(import_react_pro20.CButton, { color, variant: "ghost", className: `back-button d-flex align-items-center px-0 ${className}`, onClick: () => path ? push(path) : back() }, /* @__PURE__ */ React.createElement(import_icons_react9.default, { icon: import_icons4.cilArrowLeft, size: "lg", className: "mx-1" }), title && /* @__PURE__ */ React.createElement("span", { className: "px-2" }, title)));
691
+ }
197
692
  // Annotate the CommonJS export names for ESM import in node:
198
693
  0 && (module.exports = {
694
+ BackButton,
695
+ CardSkeleton,
199
696
  CustomTooltip,
697
+ FunnelChart,
698
+ GeneralCard,
200
699
  IconButton,
700
+ InvestmentByPlatformChart,
701
+ MetricCard,
702
+ MetricsSkeleton,
703
+ Modal,
704
+ NoDataMessage,
201
705
  OutlineButton,
706
+ PaginatedGrid,
707
+ PaginatedTable,
708
+ ReportTableSkeleton,
709
+ SalesByChannelChart,
202
710
  SolidButton,
203
- TextButton
711
+ SummaryReportSkeleton,
712
+ TextButton,
713
+ TextSkeleton,
714
+ TimeSeriesComparisonChart,
715
+ TopProductsChart,
716
+ formatDateRange,
717
+ formatValue,
718
+ getBadgeStyles
204
719
  });
205
720
  //# sourceMappingURL=index.js.map