@roax/ui 0.2.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/README.md +186 -170
- package/dist/index.js +461 -250
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +492 -254
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -5
- package/src/styles/_roax-colors.scss +107 -8
- package/src/styles/index.scss +3 -8
package/dist/index.mjs
CHANGED
|
@@ -2,19 +2,7 @@
|
|
|
2
2
|
import { CLoadingButton } from "@coreui/react-pro";
|
|
3
3
|
import CIcon from "@coreui/icons-react";
|
|
4
4
|
import "./SolidButton.scss";
|
|
5
|
-
function SolidButton({
|
|
6
|
-
title = "Button",
|
|
7
|
-
onClick,
|
|
8
|
-
color = "custom-primary",
|
|
9
|
-
textColor = "custom-white",
|
|
10
|
-
className = "",
|
|
11
|
-
loading = false,
|
|
12
|
-
icon,
|
|
13
|
-
svgComponent: SvgComponent,
|
|
14
|
-
type = "submit",
|
|
15
|
-
badge,
|
|
16
|
-
disabled
|
|
17
|
-
}) {
|
|
5
|
+
function SolidButton({ title = "Button", onClick, color = "custom-primary", textColor = "custom-white", className = "", loading = false, icon, svgComponent: SvgComponent, type = "submit", badge, disabled }) {
|
|
18
6
|
return /* @__PURE__ */ React.createElement(
|
|
19
7
|
CLoadingButton,
|
|
20
8
|
{
|
|
@@ -24,12 +12,7 @@ function SolidButton({
|
|
|
24
12
|
loading,
|
|
25
13
|
disabledOnLoading: true,
|
|
26
14
|
disabled,
|
|
27
|
-
style: {
|
|
28
|
-
minHeight: "44px",
|
|
29
|
-
minWidth: "fit-content",
|
|
30
|
-
fontSize: "0.95rem",
|
|
31
|
-
letterSpacing: "0.3px"
|
|
32
|
-
}
|
|
15
|
+
style: { minHeight: "44px", minWidth: "fit-content", fontSize: "0.95rem", letterSpacing: "0.3px" }
|
|
33
16
|
},
|
|
34
17
|
icon && /* @__PURE__ */ React.createElement(CIcon, { icon, className: "btn-icon" }),
|
|
35
18
|
SvgComponent && !icon && /* @__PURE__ */ React.createElement(SvgComponent, null),
|
|
@@ -42,16 +25,7 @@ function SolidButton({
|
|
|
42
25
|
import { CLoadingButton as CLoadingButton2 } from "@coreui/react-pro";
|
|
43
26
|
import CIcon2 from "@coreui/icons-react";
|
|
44
27
|
import "./OutlineButton.scss";
|
|
45
|
-
function OutlineButton({
|
|
46
|
-
title = "Button",
|
|
47
|
-
onClick,
|
|
48
|
-
color = "custom-primary",
|
|
49
|
-
className = "",
|
|
50
|
-
loading = false,
|
|
51
|
-
icon,
|
|
52
|
-
type = "submit",
|
|
53
|
-
disabled
|
|
54
|
-
}) {
|
|
28
|
+
function OutlineButton({ title = "Button", onClick, color = "custom-primary", className = "", loading = false, icon, type = "submit", disabled }) {
|
|
55
29
|
return /* @__PURE__ */ React.createElement(
|
|
56
30
|
CLoadingButton2,
|
|
57
31
|
{
|
|
@@ -62,10 +36,7 @@ function OutlineButton({
|
|
|
62
36
|
disabledOnLoading: true,
|
|
63
37
|
disabled,
|
|
64
38
|
variant: "outline",
|
|
65
|
-
style: {
|
|
66
|
-
maxHeight: "40px",
|
|
67
|
-
minWidth: "fit-content"
|
|
68
|
-
}
|
|
39
|
+
style: { maxHeight: "40px", minWidth: "fit-content" }
|
|
69
40
|
},
|
|
70
41
|
icon && /* @__PURE__ */ React.createElement(CIcon2, { icon }),
|
|
71
42
|
/* @__PURE__ */ React.createElement("span", null, title)
|
|
@@ -75,17 +46,7 @@ function OutlineButton({
|
|
|
75
46
|
// src/components/buttons/TextButton.jsx
|
|
76
47
|
import { CLoadingButton as CLoadingButton3 } from "@coreui/react-pro";
|
|
77
48
|
import CIcon3 from "@coreui/icons-react";
|
|
78
|
-
function TextButton({
|
|
79
|
-
disabled,
|
|
80
|
-
title,
|
|
81
|
-
onClick,
|
|
82
|
-
color = "dark",
|
|
83
|
-
className = "",
|
|
84
|
-
loading = false,
|
|
85
|
-
icon,
|
|
86
|
-
children
|
|
87
|
-
}) {
|
|
88
|
-
const buttonClasses = `rounded-3 px-2 d-flex justify-content-center gap-2 fw-medium ${className}`;
|
|
49
|
+
function TextButton({ disabled, title, onClick, color = "dark", className = "", loading = false, icon, children }) {
|
|
89
50
|
return /* @__PURE__ */ React.createElement("span", { className: "d-inline-block", tabIndex: 0 }, /* @__PURE__ */ React.createElement(
|
|
90
51
|
CLoadingButton3,
|
|
91
52
|
{
|
|
@@ -94,7 +55,7 @@ function TextButton({
|
|
|
94
55
|
variant: "ghost",
|
|
95
56
|
type: "button",
|
|
96
57
|
color,
|
|
97
|
-
className:
|
|
58
|
+
className: `rounded-3 px-2 d-flex justify-content-center gap-2 fw-medium ${className}`,
|
|
98
59
|
loading
|
|
99
60
|
},
|
|
100
61
|
icon && /* @__PURE__ */ React.createElement(CIcon3, { size: "xl", icon }),
|
|
@@ -109,37 +70,12 @@ import CIcon4 from "@coreui/icons-react";
|
|
|
109
70
|
// src/components/custom-tooltip/CustomTooltip.jsx
|
|
110
71
|
import Tippy from "@tippyjs/react";
|
|
111
72
|
import "tippy.js/dist/tippy.css";
|
|
112
|
-
function CustomTooltip({
|
|
113
|
-
content,
|
|
114
|
-
placement = "bottom",
|
|
115
|
-
delay = 0,
|
|
116
|
-
className = "",
|
|
117
|
-
children
|
|
118
|
-
}) {
|
|
119
|
-
return /* @__PURE__ */ React.createElement(
|
|
120
|
-
Tippy,
|
|
121
|
-
{
|
|
122
|
-
content,
|
|
123
|
-
placement,
|
|
124
|
-
className,
|
|
125
|
-
delay
|
|
126
|
-
},
|
|
127
|
-
children
|
|
128
|
-
);
|
|
73
|
+
function CustomTooltip({ content, placement = "bottom", delay = 0, className = "", children }) {
|
|
74
|
+
return /* @__PURE__ */ React.createElement(Tippy, { content, placement, className, delay }, children);
|
|
129
75
|
}
|
|
130
76
|
|
|
131
77
|
// src/components/buttons/IconButton.jsx
|
|
132
|
-
function IconButton({
|
|
133
|
-
title,
|
|
134
|
-
onClick,
|
|
135
|
-
color = "dark",
|
|
136
|
-
className = "",
|
|
137
|
-
loading = false,
|
|
138
|
-
icon,
|
|
139
|
-
tooltip = "",
|
|
140
|
-
disabled
|
|
141
|
-
}) {
|
|
142
|
-
const buttonClasses = `d-flex justify-content-center align-items-center gap-1 m-0 p-1 ${className}`;
|
|
78
|
+
function IconButton({ title, onClick, color = "dark", className = "", loading = false, icon, tooltip = "", disabled }) {
|
|
143
79
|
return /* @__PURE__ */ React.createElement(CustomTooltip, { content: tooltip, placement: "bottom" }, /* @__PURE__ */ React.createElement(
|
|
144
80
|
CLoadingButton4,
|
|
145
81
|
{
|
|
@@ -147,7 +83,7 @@ function IconButton({
|
|
|
147
83
|
variant: "ghost",
|
|
148
84
|
type: "button",
|
|
149
85
|
color,
|
|
150
|
-
className:
|
|
86
|
+
className: `d-flex justify-content-center align-items-center gap-1 m-0 p-1 ${className}`,
|
|
151
87
|
loading,
|
|
152
88
|
disabled
|
|
153
89
|
},
|
|
@@ -159,61 +95,25 @@ function IconButton({
|
|
|
159
95
|
// src/components/modal/Modal.jsx
|
|
160
96
|
import CIcon5 from "@coreui/icons-react";
|
|
161
97
|
import { CModal, CModalBody, CModalHeader, CModalTitle } from "@coreui/react-pro";
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// ../../node_modules/@coreui/icons/dist/esm/free/cil-arrow-left.js
|
|
167
|
-
var cilArrowLeft = ["512 512", "<polygon fill='var(--ci-primary-color, currentColor)' points='497.333 239.999 80.092 239.999 176.087 144.004 153.46 121.377 18.837 256 153.46 390.623 176.087 367.996 80.09 271.999 497.333 271.999 497.333 239.999' class='ci-primary'/>"];
|
|
168
|
-
|
|
169
|
-
// ../../node_modules/@coreui/icons/dist/esm/free/cil-arrow-top.js
|
|
170
|
-
var cilArrowTop = ["512 512", "<polygon fill='var(--ci-primary-color, currentColor)' points='390.624 150.625 256 16 121.376 150.625 144.004 173.252 240.001 77.254 240.001 495.236 272.001 495.236 272.001 77.257 367.996 173.252 390.624 150.625' class='ci-primary'/>"];
|
|
171
|
-
|
|
172
|
-
// ../../node_modules/@coreui/icons/dist/esm/free/cil-info.js
|
|
173
|
-
var cilInfo = ["512 512", "<rect width='34.924' height='34.924' x='256' y='95.998' fill='var(--ci-primary-color, currentColor)' class='ci-primary'/><path fill='var(--ci-primary-color, currentColor)' d='M16,496H496V16H16ZM48,48H464V464H48Z' class='ci-primary'/><path fill='var(--ci-primary-color, currentColor)' d='M285.313,359.032a18.123,18.123,0,0,1-15.6,8.966,18.061,18.061,0,0,1-17.327-23.157l35.67-121.277A49.577,49.577,0,0,0,194.7,190.572l-11.718,28.234,29.557,12.266,11.718-28.235a17.577,17.577,0,0,1,33.1,11.7l-35.67,121.277A50.061,50.061,0,0,0,269.709,400a50.227,50.227,0,0,0,43.25-24.853l15.1-25.913-27.646-16.115Z' class='ci-primary'/>"];
|
|
174
|
-
|
|
175
|
-
// ../../node_modules/@coreui/icons/dist/esm/free/cil-x.js
|
|
176
|
-
var cilX = ["512 512", "<polygon fill='var(--ci-primary-color, currentColor)' points='427.314 107.313 404.686 84.687 256 233.373 107.314 84.687 84.686 107.313 233.373 256 84.686 404.687 107.314 427.313 256 278.627 404.686 427.313 427.314 404.687 278.627 256 427.314 107.313' class='ci-primary'/>"];
|
|
177
|
-
|
|
178
|
-
// src/components/modal/Modal.jsx
|
|
179
|
-
function Modal({
|
|
180
|
-
title,
|
|
181
|
-
titleIcon,
|
|
182
|
-
visible,
|
|
183
|
-
setVisible,
|
|
184
|
-
alignment = "center",
|
|
185
|
-
size,
|
|
186
|
-
children
|
|
187
|
-
}) {
|
|
188
|
-
return /* @__PURE__ */ React.createElement(
|
|
189
|
-
CModal,
|
|
98
|
+
import { cilX } from "@coreui/icons";
|
|
99
|
+
function Modal({ title, titleIcon, visible, setVisible, alignment = "center", size, children }) {
|
|
100
|
+
return /* @__PURE__ */ React.createElement(CModal, { alignment, visible, onClose: () => setVisible(false), size, focus: true, keyboard: true, "aria-labelledby": "roax-modal-title" }, /* @__PURE__ */ React.createElement(CModalHeader, { className: "position-relative" }, /* @__PURE__ */ React.createElement(CModalTitle, { id: "roax-modal-title", className: "d-flex align-items-center gap-2" }, titleIcon && /* @__PURE__ */ React.createElement(CIcon5, { icon: titleIcon, size: "lg", className: "text-muted" }), title), /* @__PURE__ */ React.createElement(
|
|
101
|
+
"button",
|
|
190
102
|
{
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
keyboard: true,
|
|
197
|
-
"aria-labelledby": "roax-modal-title"
|
|
103
|
+
onClick: () => setVisible(false),
|
|
104
|
+
type: "button",
|
|
105
|
+
className: "position-absolute end-0 top-0 mt-3 me-3 p-0 bg-transparent border-0",
|
|
106
|
+
style: { fontSize: "1.25rem", color: "var(--cui-body-color)", zIndex: 10 },
|
|
107
|
+
"aria-label": "Cerrar"
|
|
198
108
|
},
|
|
199
|
-
/* @__PURE__ */ React.createElement(
|
|
200
|
-
|
|
201
|
-
{
|
|
202
|
-
onClick: () => setVisible(false),
|
|
203
|
-
type: "button",
|
|
204
|
-
className: "position-absolute end-0 top-0 mt-3 me-3 p-0 bg-transparent border-0",
|
|
205
|
-
style: { fontSize: "1.25rem", color: "var(--cui-body-color)", zIndex: 10 },
|
|
206
|
-
"aria-label": "Cerrar"
|
|
207
|
-
},
|
|
208
|
-
/* @__PURE__ */ React.createElement(CIcon5, { icon: cilX, size: "lg" })
|
|
209
|
-
)),
|
|
210
|
-
/* @__PURE__ */ React.createElement(CModalBody, null, children)
|
|
211
|
-
);
|
|
109
|
+
/* @__PURE__ */ React.createElement(CIcon5, { icon: cilX, size: "lg" })
|
|
110
|
+
)), /* @__PURE__ */ React.createElement(CModalBody, null, children));
|
|
212
111
|
}
|
|
213
112
|
|
|
214
113
|
// src/components/card/MetricCard.jsx
|
|
215
114
|
import { CCard, CCardBody } from "@coreui/react-pro";
|
|
216
115
|
import CIcon6 from "@coreui/icons-react";
|
|
116
|
+
import { cilArrowTop, cilArrowBottom, cilInfo } from "@coreui/icons";
|
|
217
117
|
|
|
218
118
|
// src/components/card/card-utils.js
|
|
219
119
|
var formatValue = (val, unit = "", title = "") => {
|
|
@@ -240,8 +140,8 @@ function formatDateRange(previousDateRange) {
|
|
|
240
140
|
if (!previousDateRange) return { formattedRange: "" };
|
|
241
141
|
const parts = previousDateRange.split(" - ");
|
|
242
142
|
const parseDate = (str) => {
|
|
243
|
-
const [
|
|
244
|
-
return new Date(
|
|
143
|
+
const [y, m, d] = str.split("-").map(Number);
|
|
144
|
+
return new Date(y, m - 1, d);
|
|
245
145
|
};
|
|
246
146
|
const format = (date) => {
|
|
247
147
|
const day = String(date.getDate()).padStart(2, "0");
|
|
@@ -251,13 +151,33 @@ function formatDateRange(previousDateRange) {
|
|
|
251
151
|
};
|
|
252
152
|
const since = parseDate(parts[0]);
|
|
253
153
|
const until = parts[1] ? parseDate(parts[1]) : null;
|
|
254
|
-
|
|
255
|
-
return { formattedRange };
|
|
154
|
+
return { formattedRange: until ? `${format(since)} - ${format(until)}` : format(since) };
|
|
256
155
|
}
|
|
257
156
|
|
|
258
157
|
// src/components/card/MetricCard.jsx
|
|
259
158
|
import "./MetricCard.scss";
|
|
260
|
-
function MetricCard({
|
|
159
|
+
function MetricCard({ title, current, previous, change, isPositive, unit = "", previousDateRange = "", tooltipText = "", backgroundColor }) {
|
|
160
|
+
const isColored = !!backgroundColor;
|
|
161
|
+
return /* @__PURE__ */ React.createElement(
|
|
162
|
+
CCard,
|
|
163
|
+
{
|
|
164
|
+
className: `metric-card w-100 h-100 d-flex flex-column border-0${isColored ? " metric-card--colored" : ""}`,
|
|
165
|
+
style: isColored ? { background: backgroundColor } : void 0
|
|
166
|
+
},
|
|
167
|
+
/* @__PURE__ */ React.createElement(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(CIcon6, { icon: 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(CIcon6, { icon: isPositive ? cilArrowTop : cilArrowBottom, size: "sm" }), Math.abs(change), "%")), /* @__PURE__ */ React.createElement("hr", { className: "metric-card__divider" }), (() => {
|
|
168
|
+
const { formattedRange } = formatDateRange(previousDateRange);
|
|
169
|
+
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));
|
|
170
|
+
})())
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/components/card/GeneralCard.jsx
|
|
175
|
+
import { CCard as CCard2, CCardBody as CCardBody2 } from "@coreui/react-pro";
|
|
176
|
+
import CIcon7 from "@coreui/icons-react";
|
|
177
|
+
import { cilArrowTop as cilArrowTop2, cilArrowBottom as cilArrowBottom2 } from "@coreui/icons";
|
|
178
|
+
import { Info } from "lucide-react";
|
|
179
|
+
import "./general-card.scss";
|
|
180
|
+
function GeneralCard({
|
|
261
181
|
title,
|
|
262
182
|
current,
|
|
263
183
|
previous,
|
|
@@ -269,195 +189,513 @@ function MetricCard({
|
|
|
269
189
|
backgroundColor
|
|
270
190
|
}) {
|
|
271
191
|
const isColored = !!backgroundColor;
|
|
272
|
-
const cardClass = `
|
|
192
|
+
const cardClass = `general-card w-100 h-100 d-flex flex-column border-0${isColored ? " general-card--colored" : ""}`;
|
|
273
193
|
return /* @__PURE__ */ React.createElement(
|
|
274
|
-
|
|
194
|
+
CCard2,
|
|
275
195
|
{
|
|
276
196
|
className: cardClass,
|
|
277
197
|
style: isColored ? { background: backgroundColor } : void 0
|
|
278
198
|
},
|
|
279
|
-
/* @__PURE__ */ React.createElement(
|
|
280
|
-
|
|
199
|
+
/* @__PURE__ */ React.createElement(CCardBody2, { 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(
|
|
200
|
+
Info,
|
|
281
201
|
{
|
|
282
|
-
|
|
283
|
-
|
|
202
|
+
size: 16,
|
|
203
|
+
strokeWidth: 2,
|
|
204
|
+
style: { cursor: "pointer", opacity: 0.5 }
|
|
284
205
|
}
|
|
285
|
-
))), /* @__PURE__ */ React.createElement("p", { className: "
|
|
206
|
+
))), /* @__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(CIcon7, { icon: isPositive ? cilArrowTop2 : cilArrowBottom2, size: "sm" }), Math.abs(change), "%")), /* @__PURE__ */ React.createElement("hr", { className: "general-card__divider" }), (() => {
|
|
286
207
|
const { formattedRange } = formatDateRange(previousDateRange);
|
|
287
|
-
return /* @__PURE__ */ React.createElement("div", { className: "d-flex justify-content-between align-items-center
|
|
208
|
+
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));
|
|
288
209
|
})())
|
|
289
210
|
);
|
|
290
211
|
}
|
|
291
212
|
|
|
292
|
-
// src/components/
|
|
213
|
+
// src/components/charts/FunnelChart.jsx
|
|
293
214
|
import {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
215
|
+
ResponsiveContainer,
|
|
216
|
+
BarChart,
|
|
217
|
+
Bar,
|
|
218
|
+
XAxis,
|
|
219
|
+
YAxis,
|
|
220
|
+
Tooltip,
|
|
221
|
+
Cell,
|
|
222
|
+
LabelList
|
|
223
|
+
} from "recharts";
|
|
224
|
+
import { CCard as CCard3, CCardBody as CCardBody3, CRow, CCol } from "@coreui/react-pro";
|
|
225
|
+
function FunnelChart({ data = [] }) {
|
|
226
|
+
return /* @__PURE__ */ React.createElement(CCard3, { className: "mt-4 rounded-4 shadow-sm p-3" }, /* @__PURE__ */ React.createElement(CCardBody3, null, /* @__PURE__ */ React.createElement(ResponsiveContainer, { width: "100%", height: data.length * 45 + 50 }, /* @__PURE__ */ React.createElement(
|
|
227
|
+
BarChart,
|
|
228
|
+
{
|
|
229
|
+
data,
|
|
230
|
+
layout: "vertical",
|
|
231
|
+
margin: { top: 10, right: 60, left: 20, bottom: 0 },
|
|
232
|
+
barCategoryGap: 15
|
|
233
|
+
},
|
|
234
|
+
/* @__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" }))),
|
|
235
|
+
/* @__PURE__ */ React.createElement(XAxis, { type: "number", hide: true, domain: [0, "dataMax"] }),
|
|
236
|
+
/* @__PURE__ */ React.createElement(
|
|
237
|
+
YAxis,
|
|
238
|
+
{
|
|
239
|
+
dataKey: "title",
|
|
240
|
+
type: "category",
|
|
241
|
+
width: 130,
|
|
242
|
+
axisLine: false,
|
|
243
|
+
tickLine: false,
|
|
244
|
+
style: { fontWeight: 600, fill: "#333", fontSize: 13 }
|
|
245
|
+
}
|
|
246
|
+
),
|
|
247
|
+
/* @__PURE__ */ React.createElement(
|
|
248
|
+
Tooltip,
|
|
249
|
+
{
|
|
250
|
+
content: ({ active, payload }) => {
|
|
251
|
+
if (active && payload && payload.length > 0) {
|
|
252
|
+
const { value, payload: item } = payload[0];
|
|
253
|
+
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)));
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
),
|
|
259
|
+
/* @__PURE__ */ React.createElement(Bar, { dataKey: "value", fill: "url(#funnelGradient)", radius: [0, 10, 10, 0] }, /* @__PURE__ */ React.createElement(
|
|
260
|
+
LabelList,
|
|
261
|
+
{
|
|
262
|
+
dataKey: "value",
|
|
263
|
+
position: "right",
|
|
264
|
+
formatter: (val) => formatValue(val),
|
|
265
|
+
style: { fill: "#333", fontWeight: 500, fontSize: 12 }
|
|
266
|
+
}
|
|
267
|
+
), data.map((entry, index) => /* @__PURE__ */ React.createElement(
|
|
268
|
+
Cell,
|
|
269
|
+
{
|
|
270
|
+
key: `cell-${entry.title || index}`,
|
|
271
|
+
fill: entry.color || "url(#funnelGradient)"
|
|
272
|
+
}
|
|
273
|
+
)))
|
|
274
|
+
)), /* @__PURE__ */ React.createElement(CRow, { className: "pt-4 g-3" }, data.slice(0, 3).map((item, index) => /* @__PURE__ */ React.createElement(CCol, { xs: 12, md: 4, key: `card-top-${index}` }, /* @__PURE__ */ React.createElement(
|
|
275
|
+
GeneralCard,
|
|
276
|
+
{
|
|
277
|
+
title: item.title,
|
|
278
|
+
current: item.value,
|
|
279
|
+
previous: item.previous,
|
|
280
|
+
change: item.percentageChange,
|
|
281
|
+
isPositive: item.isPositive,
|
|
282
|
+
unit: item.unit,
|
|
283
|
+
previousDateRange: item.previousDateRange,
|
|
284
|
+
tooltipText: item.title
|
|
285
|
+
}
|
|
286
|
+
)))), data.length > 3 && /* @__PURE__ */ React.createElement(CRow, { className: "pt-3 g-3" }, data.slice(3).map((item, index) => /* @__PURE__ */ React.createElement(CCol, { xs: 12, md: 5, lg: 4, key: `card-bottom-${index}` }, /* @__PURE__ */ React.createElement(
|
|
287
|
+
GeneralCard,
|
|
288
|
+
{
|
|
289
|
+
title: item.title,
|
|
290
|
+
current: item.value,
|
|
291
|
+
previous: item.previous,
|
|
292
|
+
change: item.percentageChange,
|
|
293
|
+
isPositive: item.isPositive,
|
|
294
|
+
unit: item.unit,
|
|
295
|
+
previousDateRange: item.previousDateRange,
|
|
296
|
+
tooltipText: item.title
|
|
297
|
+
}
|
|
298
|
+
))))));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/components/charts/TimeSeriesComparisonChart.jsx
|
|
302
|
+
import {
|
|
303
|
+
ResponsiveContainer as ResponsiveContainer2,
|
|
304
|
+
LineChart,
|
|
305
|
+
Line,
|
|
306
|
+
XAxis as XAxis2,
|
|
307
|
+
YAxis as YAxis2,
|
|
308
|
+
Tooltip as Tooltip2,
|
|
309
|
+
Legend,
|
|
310
|
+
CartesianGrid
|
|
311
|
+
} from "recharts";
|
|
312
|
+
import { CCard as CCard4, CCardBody as CCardBody4, CRow as CRow2, CCol as CCol2 } from "@coreui/react-pro";
|
|
313
|
+
function TimeSeriesComparisonChart({
|
|
302
314
|
data = [],
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
loading = false,
|
|
310
|
-
emptyMessage = "No hay datos disponibles."
|
|
315
|
+
lines = [],
|
|
316
|
+
colors = {},
|
|
317
|
+
cards = [],
|
|
318
|
+
currency = "",
|
|
319
|
+
yAxisLabel = "",
|
|
320
|
+
displayNames = {}
|
|
311
321
|
}) {
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
322
|
+
const detectUnit = (name = "") => {
|
|
323
|
+
const lower = name.toLowerCase();
|
|
324
|
+
if (lower.includes("%")) return "%";
|
|
325
|
+
if (lower.includes("venta") || lower.includes("inversi\xF3n")) return currency || "";
|
|
326
|
+
return "";
|
|
327
|
+
};
|
|
328
|
+
const maxY = data.length ? Math.ceil(
|
|
329
|
+
Math.max(
|
|
330
|
+
...lines.map((lineName) => Math.max(...data.map((d) => d[lineName] || 0)))
|
|
331
|
+
) * 1.1
|
|
332
|
+
) : 100;
|
|
333
|
+
return /* @__PURE__ */ React.createElement(CCard4, { className: "mt-4 rounded-4 shadow-sm p-3" }, /* @__PURE__ */ React.createElement(CCardBody4, null, cards.length > 0 && /* @__PURE__ */ React.createElement(CRow2, { className: "pt-4 g-3 justify-content-center text-center mb-4" }, cards.map((card, index) => /* @__PURE__ */ React.createElement(CCol2, { xs: 12, md: 4, lg: 5, key: index }, /* @__PURE__ */ React.createElement(
|
|
334
|
+
GeneralCard,
|
|
335
|
+
{
|
|
336
|
+
title: card.title,
|
|
337
|
+
current: card.value,
|
|
338
|
+
previous: card.previous,
|
|
339
|
+
change: card.percentageChange,
|
|
340
|
+
isPositive: card.isPositive,
|
|
341
|
+
unit: card.unit,
|
|
342
|
+
previousDateRange: card.previousDateRange,
|
|
343
|
+
tooltipText: card.title
|
|
344
|
+
}
|
|
345
|
+
)))), /* @__PURE__ */ React.createElement(ResponsiveContainer2, { width: "100%", height: 340 }, /* @__PURE__ */ React.createElement(LineChart, { data, margin: { top: 20, right: 30, left: 10, bottom: 10 } }, /* @__PURE__ */ React.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: "#e5e5e5" }), /* @__PURE__ */ React.createElement(XAxis2, { dataKey: "date", stroke: "#333", tick: { fontSize: 12 } }), /* @__PURE__ */ React.createElement(
|
|
346
|
+
YAxis2,
|
|
347
|
+
{
|
|
348
|
+
yAxisId: "left",
|
|
349
|
+
stroke: "#333",
|
|
350
|
+
tickFormatter: (val) => `${currency ? currency + " " : ""}${val >= 1e6 ? (val / 1e6).toFixed(1) + "M" : val.toLocaleString("es-CO")}`,
|
|
351
|
+
tick: { fontSize: 12 },
|
|
352
|
+
width: 100,
|
|
353
|
+
domain: [0, maxY],
|
|
354
|
+
allowDecimals: false,
|
|
355
|
+
label: yAxisLabel ? {
|
|
356
|
+
value: yAxisLabel,
|
|
357
|
+
angle: -90,
|
|
358
|
+
position: "insideLeft",
|
|
359
|
+
style: { textAnchor: "middle", fill: "#333", fontSize: 13 }
|
|
360
|
+
} : void 0
|
|
361
|
+
}
|
|
362
|
+
), /* @__PURE__ */ React.createElement(
|
|
363
|
+
Tooltip2,
|
|
364
|
+
{
|
|
365
|
+
formatter: (val, name) => {
|
|
366
|
+
const unit = detectUnit(name);
|
|
367
|
+
return [formatValue(val, unit), displayNames[name] || name];
|
|
368
|
+
},
|
|
369
|
+
labelClassName: "fw-bold",
|
|
370
|
+
wrapperStyle: { zIndex: 1e3 }
|
|
371
|
+
}
|
|
372
|
+
), /* @__PURE__ */ React.createElement(
|
|
373
|
+
Legend,
|
|
374
|
+
{
|
|
375
|
+
verticalAlign: "bottom",
|
|
376
|
+
height: 36,
|
|
377
|
+
wrapperStyle: { marginTop: 20 },
|
|
378
|
+
iconType: "circle",
|
|
379
|
+
formatter: (value) => displayNames[value] || value
|
|
380
|
+
}
|
|
381
|
+
), lines.map((lineName) => /* @__PURE__ */ React.createElement(
|
|
382
|
+
Line,
|
|
383
|
+
{
|
|
384
|
+
key: lineName,
|
|
385
|
+
yAxisId: "left",
|
|
386
|
+
type: "monotone",
|
|
387
|
+
dataKey: lineName,
|
|
388
|
+
stroke: colors[lineName] || "#000",
|
|
389
|
+
strokeWidth: 2.5,
|
|
390
|
+
dot: { r: 4 },
|
|
391
|
+
activeDot: { r: 6 }
|
|
392
|
+
}
|
|
393
|
+
))))));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/components/charts/TopProductsChart.jsx
|
|
397
|
+
import {
|
|
398
|
+
BarChart as BarChart2,
|
|
399
|
+
Bar as Bar2,
|
|
400
|
+
XAxis as XAxis3,
|
|
401
|
+
YAxis as YAxis3,
|
|
402
|
+
Tooltip as Tooltip3,
|
|
403
|
+
ResponsiveContainer as ResponsiveContainer3,
|
|
404
|
+
LabelList as LabelList2,
|
|
405
|
+
Cell as Cell2
|
|
406
|
+
} from "recharts";
|
|
407
|
+
import { CCard as CCard5, CCardBody as CCardBody5 } from "@coreui/react-pro";
|
|
408
|
+
var COLORS = ["#FF6A8D", "#FF8AAE", "#FFA3BC", "#FFC3D0", "#FFE0E7"];
|
|
409
|
+
var ChartTooltip = ({ active, payload }) => {
|
|
410
|
+
if (active && payload && payload.length) {
|
|
411
|
+
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()));
|
|
319
412
|
}
|
|
320
|
-
|
|
321
|
-
|
|
413
|
+
return null;
|
|
414
|
+
};
|
|
415
|
+
function TopProductsChart({
|
|
416
|
+
data = [],
|
|
417
|
+
title = "Productos M\xE1s Vendidos",
|
|
418
|
+
emptyText = "No se registraron productos vendidos en este rango de fechas.",
|
|
419
|
+
maxItems = 5
|
|
420
|
+
}) {
|
|
421
|
+
const processedData = (data || []).filter((item) => {
|
|
422
|
+
var _a;
|
|
423
|
+
return item.quantity > 0 && ((_a = item.name) == null ? void 0 : _a.trim());
|
|
424
|
+
}).sort((a, b) => b.quantity - a.quantity).slice(0, maxItems).map((item) => ({ name: item.name, quantity: item.quantity }));
|
|
425
|
+
if (!processedData.length) {
|
|
426
|
+
return /* @__PURE__ */ React.createElement(CCard5, { className: "mt-4 rounded-4 shadow-sm p-3" }, /* @__PURE__ */ React.createElement(CCardBody5, { 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)));
|
|
322
427
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
},
|
|
334
|
-
i
|
|
335
|
-
)
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
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(
|
|
339
|
-
"button",
|
|
428
|
+
return /* @__PURE__ */ React.createElement(CCard5, { className: "mt-4 rounded-4 shadow-sm p-3" }, /* @__PURE__ */ React.createElement(CCardBody5, 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(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React.createElement(
|
|
429
|
+
BarChart2,
|
|
430
|
+
{
|
|
431
|
+
layout: "vertical",
|
|
432
|
+
data: processedData,
|
|
433
|
+
margin: { top: 10, right: 40, left: 20, bottom: 10 }
|
|
434
|
+
},
|
|
435
|
+
/* @__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" }))),
|
|
436
|
+
/* @__PURE__ */ React.createElement(
|
|
437
|
+
XAxis3,
|
|
340
438
|
{
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
),
|
|
347
|
-
|
|
439
|
+
type: "number",
|
|
440
|
+
tickFormatter: (value) => value.toLocaleString(),
|
|
441
|
+
axisLine: false,
|
|
442
|
+
tick: { fontSize: 12 }
|
|
443
|
+
}
|
|
444
|
+
),
|
|
445
|
+
/* @__PURE__ */ React.createElement(
|
|
446
|
+
YAxis3,
|
|
348
447
|
{
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
448
|
+
type: "category",
|
|
449
|
+
dataKey: "name",
|
|
450
|
+
width: 150,
|
|
451
|
+
tick: { fontSize: 13 },
|
|
452
|
+
tickFormatter: (value) => value.length > 30 ? value.substring(0, 30) + "..." : value
|
|
453
|
+
}
|
|
454
|
+
),
|
|
455
|
+
/* @__PURE__ */ React.createElement(Tooltip3, { content: /* @__PURE__ */ React.createElement(ChartTooltip, null) }),
|
|
456
|
+
/* @__PURE__ */ React.createElement(Bar2, { dataKey: "quantity", fill: "url(#barGradient)", radius: [0, 8, 8, 0] }, /* @__PURE__ */ React.createElement(
|
|
457
|
+
LabelList2,
|
|
458
|
+
{
|
|
459
|
+
dataKey: "quantity",
|
|
460
|
+
position: "right",
|
|
461
|
+
formatter: (value) => value.toLocaleString(),
|
|
462
|
+
style: { fill: "#333", fontWeight: 500, fontSize: 12 }
|
|
463
|
+
}
|
|
464
|
+
), processedData.map((_, index) => /* @__PURE__ */ React.createElement(Cell2, { key: `cell-${index}`, fill: COLORS[index % COLORS.length] })))
|
|
465
|
+
)))));
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// src/components/charts/SalesByChannelChart.jsx
|
|
469
|
+
import {
|
|
470
|
+
ResponsiveContainer as ResponsiveContainer4,
|
|
471
|
+
BarChart as BarChart3,
|
|
472
|
+
Bar as Bar3,
|
|
473
|
+
XAxis as XAxis4,
|
|
474
|
+
YAxis as YAxis4,
|
|
475
|
+
Tooltip as Tooltip4,
|
|
476
|
+
Legend as Legend2,
|
|
477
|
+
CartesianGrid as CartesianGrid2
|
|
478
|
+
} from "recharts";
|
|
479
|
+
import { CCard as CCard6, CCardBody as CCardBody6 } from "@coreui/react-pro";
|
|
480
|
+
function SalesByChannelChart({
|
|
481
|
+
data = [],
|
|
482
|
+
title = "Ventas por canal",
|
|
483
|
+
emptyText = "Sin datos para este per\xEDodo",
|
|
484
|
+
channels = ["Meta", "TikTok", "Google"],
|
|
485
|
+
channelColors = { Meta: "#FF2F86", TikTok: "#C0C0C0", Google: "#333333" }
|
|
486
|
+
}) {
|
|
487
|
+
const hasData = data.length > 0;
|
|
488
|
+
return /* @__PURE__ */ React.createElement(CCard6, { className: "rounded-4 shadow-sm p-3" }, /* @__PURE__ */ React.createElement(CCardBody6, 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(ResponsiveContainer4, { width: "100%", height: 300 }, /* @__PURE__ */ React.createElement(BarChart3, { data, margin: { top: 20, right: 30, left: 10, bottom: 10 } }, /* @__PURE__ */ React.createElement(CartesianGrid2, { strokeDasharray: "3 3", stroke: "#e5e5e5" }), /* @__PURE__ */ React.createElement(
|
|
489
|
+
XAxis4,
|
|
490
|
+
{
|
|
491
|
+
dataKey: "fecha",
|
|
492
|
+
tick: { fontSize: 12 },
|
|
493
|
+
angle: -45,
|
|
494
|
+
textAnchor: "end",
|
|
495
|
+
interval: Math.ceil(data.length / 10)
|
|
496
|
+
}
|
|
497
|
+
), /* @__PURE__ */ React.createElement(YAxis4, { tick: { fontSize: 12 } }), /* @__PURE__ */ React.createElement(Tooltip4, null), /* @__PURE__ */ React.createElement(Legend2, { verticalAlign: "top", height: 36, iconType: "circle" }), channels.map((channel) => /* @__PURE__ */ React.createElement(
|
|
498
|
+
Bar3,
|
|
499
|
+
{
|
|
500
|
+
key: channel,
|
|
501
|
+
dataKey: channel,
|
|
502
|
+
stackId: "ventas",
|
|
503
|
+
fill: channelColors[channel] || "#888"
|
|
504
|
+
}
|
|
505
|
+
))))));
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// src/components/charts/InvestmentByPlatformChart.jsx
|
|
509
|
+
import {
|
|
510
|
+
PieChart,
|
|
511
|
+
Pie,
|
|
512
|
+
Cell as Cell3,
|
|
513
|
+
Legend as Legend3,
|
|
514
|
+
Tooltip as Tooltip5,
|
|
515
|
+
ResponsiveContainer as ResponsiveContainer5
|
|
516
|
+
} from "recharts";
|
|
517
|
+
import { CCard as CCard7, CCardBody as CCardBody7 } from "@coreui/react-pro";
|
|
518
|
+
var DEFAULT_COLORS = ["#FF2F86", "#D9D9D9", "#333333"];
|
|
519
|
+
function InvestmentByPlatformChart({
|
|
520
|
+
data = [],
|
|
521
|
+
title = "Inversi\xF3n por plataforma",
|
|
522
|
+
colors = DEFAULT_COLORS,
|
|
523
|
+
currencySymbol = "COP",
|
|
524
|
+
locale = "es-CO"
|
|
525
|
+
}) {
|
|
526
|
+
return /* @__PURE__ */ React.createElement(CCard7, { className: "rounded-4 shadow-sm p-3" }, /* @__PURE__ */ React.createElement(CCardBody7, null, /* @__PURE__ */ React.createElement("h5", { className: "text-center fw-bold" }, title), /* @__PURE__ */ React.createElement(ResponsiveContainer5, { width: "100%", height: 280 }, /* @__PURE__ */ React.createElement(PieChart, null, /* @__PURE__ */ React.createElement(
|
|
527
|
+
Pie,
|
|
528
|
+
{
|
|
529
|
+
data,
|
|
530
|
+
dataKey: "value",
|
|
531
|
+
nameKey: "name",
|
|
532
|
+
cx: "50%",
|
|
533
|
+
cy: "50%",
|
|
534
|
+
innerRadius: 60,
|
|
535
|
+
outerRadius: 100,
|
|
536
|
+
paddingAngle: 5,
|
|
537
|
+
label: ({ name, percent }) => `${name} ${(percent * 100).toFixed(1)}%`
|
|
538
|
+
},
|
|
539
|
+
data.map((_, index) => /* @__PURE__ */ React.createElement(Cell3, { key: `cell-${index}`, fill: colors[index % colors.length] }))
|
|
540
|
+
), /* @__PURE__ */ React.createElement(
|
|
541
|
+
Tooltip5,
|
|
542
|
+
{
|
|
543
|
+
formatter: (value) => `${currencySymbol} ${value.toLocaleString(locale)}`
|
|
544
|
+
}
|
|
545
|
+
), /* @__PURE__ */ React.createElement(Legend3, { verticalAlign: "bottom", height: 36 })))));
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// src/components/pagination/PaginatedTable.jsx
|
|
549
|
+
import { CTable, CTableHead, CTableBody, CTableRow, CTableHeaderCell, CSpinner } from "@coreui/react-pro";
|
|
550
|
+
function PaginatedTable({ data = [], itemsPerPage = 10, currentPage = 1, setCurrentPage = () => {
|
|
551
|
+
}, columns = [], renderRow = () => null, loading = false, emptyMessage = "No hay datos disponibles." }) {
|
|
552
|
+
const totalPages = Math.ceil(data.length / itemsPerPage);
|
|
553
|
+
const paginatedData = data.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage);
|
|
554
|
+
if (loading) return /* @__PURE__ */ React.createElement("div", { className: "text-center py-5" }, /* @__PURE__ */ React.createElement(CSpinner, { color: "secondary" }));
|
|
555
|
+
if (!data.length) return /* @__PURE__ */ React.createElement("div", { className: "text-center py-5 text-muted" }, emptyMessage);
|
|
556
|
+
const renderPagination = () => {
|
|
557
|
+
const pages = Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
558
|
+
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"));
|
|
355
559
|
};
|
|
356
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(CTable, { hover: true, responsive: true }, /* @__PURE__ */ React.createElement(CTableHead, { className: "bg-dark text-white" }, /* @__PURE__ */ React.createElement(CTableRow, null, columns.map((col,
|
|
560
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(CTable, { hover: true, responsive: true }, /* @__PURE__ */ React.createElement(CTableHead, { className: "bg-dark text-white" }, /* @__PURE__ */ React.createElement(CTableRow, null, columns.map((col, i) => /* @__PURE__ */ React.createElement(CTableHeaderCell, { key: i }, col)))), /* @__PURE__ */ React.createElement(CTableBody, null, paginatedData.map(renderRow))), totalPages > 1 && renderPagination());
|
|
357
561
|
}
|
|
358
562
|
|
|
359
563
|
// src/components/pagination/PaginatedGrid.jsx
|
|
360
564
|
import { CSpinner as CSpinner2 } from "@coreui/react-pro";
|
|
361
|
-
function PaginatedGrid({
|
|
362
|
-
|
|
363
|
-
itemsPerPage = 6,
|
|
364
|
-
currentPage = 1,
|
|
365
|
-
setCurrentPage = () => {
|
|
366
|
-
},
|
|
367
|
-
renderItem = () => null,
|
|
368
|
-
loading = false,
|
|
369
|
-
emptyMessage = "No hay elementos."
|
|
370
|
-
}) {
|
|
565
|
+
function PaginatedGrid({ data = [], itemsPerPage = 6, currentPage = 1, setCurrentPage = () => {
|
|
566
|
+
}, renderItem = () => null, loading = false, emptyMessage = "No hay elementos." }) {
|
|
371
567
|
const totalPages = Math.ceil(data.length / itemsPerPage);
|
|
372
|
-
const paginatedData = data.slice(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
);
|
|
376
|
-
if (loading) {
|
|
377
|
-
return /* @__PURE__ */ React.createElement("div", { className: "text-center py-5" }, /* @__PURE__ */ React.createElement(CSpinner2, { color: "secondary" }));
|
|
378
|
-
}
|
|
379
|
-
if (!data.length) {
|
|
380
|
-
return /* @__PURE__ */ React.createElement("div", { className: "text-center py-5 text-muted" }, emptyMessage);
|
|
381
|
-
}
|
|
568
|
+
const paginatedData = data.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage);
|
|
569
|
+
if (loading) return /* @__PURE__ */ React.createElement("div", { className: "text-center py-5" }, /* @__PURE__ */ React.createElement(CSpinner2, { color: "secondary" }));
|
|
570
|
+
if (!data.length) return /* @__PURE__ */ React.createElement("div", { className: "text-center py-5 text-muted" }, emptyMessage);
|
|
382
571
|
const renderPagination = () => {
|
|
383
|
-
const pages =
|
|
384
|
-
|
|
385
|
-
pages.push(
|
|
386
|
-
/* @__PURE__ */ React.createElement(
|
|
387
|
-
"button",
|
|
388
|
-
{
|
|
389
|
-
key: i,
|
|
390
|
-
onClick: () => setCurrentPage(i),
|
|
391
|
-
className: `px-3 py-1 rounded-pill border ${i === currentPage ? "bg-dark text-white" : "bg-white text-dark"}`
|
|
392
|
-
},
|
|
393
|
-
i
|
|
394
|
-
)
|
|
395
|
-
);
|
|
396
|
-
}
|
|
397
|
-
return /* @__PURE__ */ React.createElement("div", { className: "d-flex justify-content-center gap-2 mt-4 flex-wrap" }, currentPage > 1 && /* @__PURE__ */ React.createElement(
|
|
398
|
-
"button",
|
|
399
|
-
{
|
|
400
|
-
onClick: () => setCurrentPage(currentPage - 1),
|
|
401
|
-
className: "px-3 py-1 rounded-pill border bg-white text-dark"
|
|
402
|
-
},
|
|
403
|
-
"Anterior"
|
|
404
|
-
), pages, currentPage < totalPages && /* @__PURE__ */ React.createElement(
|
|
405
|
-
"button",
|
|
406
|
-
{
|
|
407
|
-
onClick: () => setCurrentPage(currentPage + 1),
|
|
408
|
-
className: "px-3 py-1 rounded-pill border bg-white text-dark"
|
|
409
|
-
},
|
|
410
|
-
"Siguiente"
|
|
411
|
-
));
|
|
572
|
+
const pages = Array.from({ length: totalPages }, (_, i) => i + 1);
|
|
573
|
+
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"));
|
|
412
574
|
};
|
|
413
575
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "row g-4" }, paginatedData.map(renderItem)), totalPages > 1 && renderPagination());
|
|
414
576
|
}
|
|
415
577
|
|
|
416
578
|
// src/components/skeletons/CardSkeleton.jsx
|
|
417
|
-
import { CCard as
|
|
579
|
+
import { CCard as CCard8, CPlaceholder } from "@coreui/react-pro";
|
|
418
580
|
function CardSkeleton() {
|
|
419
|
-
return /* @__PURE__ */ React.createElement(
|
|
581
|
+
return /* @__PURE__ */ React.createElement(CCard8, { className: "shadow-none p-0 m-0 bg-transparent border-0" }, /* @__PURE__ */ React.createElement(CPlaceholder, { component: "div", animation: "glow", className: "d-flex flex-column gap-2 bg-transparent" }, /* @__PURE__ */ React.createElement(CPlaceholder, { className: "p-4 mt-2 rounded-1", size: "lg", xs: 12, color: "skeleton" })));
|
|
420
582
|
}
|
|
421
583
|
|
|
422
584
|
// src/components/skeletons/TextSkeleton.jsx
|
|
423
|
-
import { CCard as
|
|
585
|
+
import { CCard as CCard9, CPlaceholder as CPlaceholder2 } from "@coreui/react-pro";
|
|
424
586
|
function TextSkeleton() {
|
|
425
|
-
return /* @__PURE__ */ React.createElement(
|
|
587
|
+
return /* @__PURE__ */ React.createElement(CCard9, { className: "shadow-none p-0 m-0 bg-transparent border-0" }, /* @__PURE__ */ React.createElement(CPlaceholder2, { component: "div", animation: "glow", className: "d-flex flex-column gap-2 bg-transparent" }, /* @__PURE__ */ React.createElement(CPlaceholder2, { className: "p-0 my-2 rounded-1", size: "lg", xs: 8, color: "skeleton" })));
|
|
426
588
|
}
|
|
427
589
|
|
|
428
590
|
// src/components/skeletons/MetricsSkeleton.jsx
|
|
429
|
-
import { CCard as
|
|
591
|
+
import { CCard as CCard10, CPlaceholder as CPlaceholder3 } from "@coreui/react-pro";
|
|
430
592
|
function MetricsSkeleton() {
|
|
431
|
-
return /* @__PURE__ */ React.createElement(
|
|
593
|
+
return /* @__PURE__ */ React.createElement(CCard10, { className: "shadow-none p-0 m-0 bg-transparent border-0" }, /* @__PURE__ */ React.createElement(CPlaceholder3, { component: "div", animation: "glow", className: "d-flex flex-column gap-2 bg-transparent" }, /* @__PURE__ */ React.createElement(CPlaceholder3, { className: "p-5 mt-2 rounded-1", size: "lg", xs: 12, color: "skeleton" }), /* @__PURE__ */ React.createElement(CPlaceholder3, { className: "p-3 mt-2 rounded-1", size: "xs", xs: 12, color: "skeleton" })));
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// src/components/skeletons/ReportTableSkeleton.jsx
|
|
597
|
+
import { CCard as CCard11, CPlaceholder as CPlaceholder4 } from "@coreui/react-pro";
|
|
598
|
+
function ReportTableSkeleton() {
|
|
599
|
+
return /* @__PURE__ */ React.createElement(CCard11, { className: "shadow-none p-0 m-0 bg-transparent border-0" }, /* @__PURE__ */ React.createElement(
|
|
600
|
+
CPlaceholder4,
|
|
601
|
+
{
|
|
602
|
+
component: "div",
|
|
603
|
+
animation: "glow",
|
|
604
|
+
className: "d-flex flex-column gap-2 bg-transparent"
|
|
605
|
+
},
|
|
606
|
+
/* @__PURE__ */ React.createElement(CPlaceholder4, { className: "p-4 mt-2 rounded-1", size: "lg", xs: 12, color: "skeleton" }),
|
|
607
|
+
/* @__PURE__ */ React.createElement(CPlaceholder4, { className: "p-5 mt-2 rounded-1", size: "lg", xs: 12, color: "skeleton" })
|
|
608
|
+
));
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// src/components/skeletons/SummaryReportSkeleton.jsx
|
|
612
|
+
import { CCard as CCard12, CCol as CCol3, CPlaceholder as CPlaceholder5, CRow as CRow3 } from "@coreui/react-pro";
|
|
613
|
+
var CardPlaceholder = ({ className = "" }) => /* @__PURE__ */ React.createElement(CCard12, { className: "shadow-none p-0 m-0 bg-transparent border-0" }, /* @__PURE__ */ React.createElement(
|
|
614
|
+
CPlaceholder5,
|
|
615
|
+
{
|
|
616
|
+
component: "div",
|
|
617
|
+
animation: "glow",
|
|
618
|
+
className: `d-flex flex-column gap-2 bg-transparent ${className}`
|
|
619
|
+
},
|
|
620
|
+
/* @__PURE__ */ React.createElement(CPlaceholder5, { className: "p-4 mt-2 rounded-2", size: "lg", xs: 12, color: "skeleton" })
|
|
621
|
+
));
|
|
622
|
+
function SummaryReportSkeleton({
|
|
623
|
+
layout = [
|
|
624
|
+
{ sm: 6, md: 6 },
|
|
625
|
+
{ sm: 6, md: 6 },
|
|
626
|
+
{ sm: 6, md: 4 },
|
|
627
|
+
{ sm: 6, md: 4 },
|
|
628
|
+
{ sm: 6, md: 4 },
|
|
629
|
+
{ sm: 6, md: 6 },
|
|
630
|
+
{ sm: 6, md: 4 },
|
|
631
|
+
{ sm: 6, md: 2 }
|
|
632
|
+
]
|
|
633
|
+
}) {
|
|
634
|
+
return /* @__PURE__ */ React.createElement(CRow3, { className: "g-md-3 px-4 px-sm-2 g-sm-2 pt-2 pb-4" }, layout.map((col, index) => /* @__PURE__ */ React.createElement(CCol3, { sm: col.sm, md: col.md, key: index }, /* @__PURE__ */ React.createElement(CardPlaceholder, null))));
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// ../../node_modules/@coreui/icons-pro/dist/esm/duotone/cid-exclamation-circle.js
|
|
638
|
+
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'/>"];
|
|
639
|
+
|
|
640
|
+
// src/components/feedback/NoDataMessage.jsx
|
|
641
|
+
import CIcon8 from "@coreui/icons-react";
|
|
642
|
+
function NoDataMessage({
|
|
643
|
+
message = "No se obtuvieron datos. Revisa el rango de fechas y la integraci\xF3n."
|
|
644
|
+
}) {
|
|
645
|
+
return /* @__PURE__ */ React.createElement(
|
|
646
|
+
"div",
|
|
647
|
+
{
|
|
648
|
+
className: "d-flex align-items-center gap-2 rounded-3 px-3 py-3 mt-3 mb-3",
|
|
649
|
+
style: {
|
|
650
|
+
backgroundColor: "var(--cui-warning-bg-subtle)",
|
|
651
|
+
border: "1px solid var(--cui-warning)",
|
|
652
|
+
color: "var(--cui-body-color)"
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
/* @__PURE__ */ React.createElement(
|
|
656
|
+
CIcon8,
|
|
657
|
+
{
|
|
658
|
+
icon: cidExclamationCircle,
|
|
659
|
+
className: "flex-shrink-0 me-2 text-warning",
|
|
660
|
+
size: "xxl"
|
|
661
|
+
}
|
|
662
|
+
),
|
|
663
|
+
message
|
|
664
|
+
);
|
|
432
665
|
}
|
|
433
666
|
|
|
434
667
|
// src/components/back-button/BackButton.jsx
|
|
435
668
|
import { useRouter } from "next/navigation";
|
|
436
669
|
import { CButton } from "@coreui/react-pro";
|
|
437
|
-
import
|
|
670
|
+
import { cilArrowLeft } from "@coreui/icons";
|
|
671
|
+
import CIcon9 from "@coreui/icons-react";
|
|
438
672
|
function BackButton({ title, color = "dark", className = "", path }) {
|
|
439
673
|
const { back, push } = useRouter();
|
|
440
|
-
|
|
441
|
-
function handleBack() {
|
|
442
|
-
if (path) push(path);
|
|
443
|
-
else back();
|
|
444
|
-
}
|
|
445
|
-
return /* @__PURE__ */ React.createElement(CustomTooltip, { content: "Volver atr\xE1s", placement: "bottom" }, /* @__PURE__ */ React.createElement(CButton, { color, variant: "ghost", className: buttonClasses, onClick: handleBack }, /* @__PURE__ */ React.createElement(CIcon7, { icon: cilArrowLeft, size: "lg", className: "mx-1" }), title && /* @__PURE__ */ React.createElement("span", { className: "px-2" }, title)));
|
|
674
|
+
return /* @__PURE__ */ React.createElement(CustomTooltip, { content: "Volver atr\xE1s", placement: "bottom" }, /* @__PURE__ */ React.createElement(CButton, { color, variant: "ghost", className: `back-button d-flex align-items-center px-0 ${className}`, onClick: () => path ? push(path) : back() }, /* @__PURE__ */ React.createElement(CIcon9, { icon: cilArrowLeft, size: "lg", className: "mx-1" }), title && /* @__PURE__ */ React.createElement("span", { className: "px-2" }, title)));
|
|
446
675
|
}
|
|
447
676
|
export {
|
|
448
677
|
BackButton,
|
|
449
678
|
CardSkeleton,
|
|
450
679
|
CustomTooltip,
|
|
680
|
+
FunnelChart,
|
|
681
|
+
GeneralCard,
|
|
451
682
|
IconButton,
|
|
683
|
+
InvestmentByPlatformChart,
|
|
452
684
|
MetricCard,
|
|
453
685
|
MetricsSkeleton,
|
|
454
686
|
Modal,
|
|
687
|
+
NoDataMessage,
|
|
455
688
|
OutlineButton,
|
|
456
689
|
PaginatedGrid,
|
|
457
690
|
PaginatedTable,
|
|
691
|
+
ReportTableSkeleton,
|
|
692
|
+
SalesByChannelChart,
|
|
458
693
|
SolidButton,
|
|
694
|
+
SummaryReportSkeleton,
|
|
459
695
|
TextButton,
|
|
460
696
|
TextSkeleton,
|
|
697
|
+
TimeSeriesComparisonChart,
|
|
698
|
+
TopProductsChart,
|
|
461
699
|
formatDateRange,
|
|
462
700
|
formatValue,
|
|
463
701
|
getBadgeStyles
|