@iclips/ui 2.0.2 → 3.0.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/MIGRATION-v3.md +244 -0
- package/README.md +1 -1
- package/dist/alert-theme.css +20 -79
- package/dist/components/ui/charts/donut-chart.cjs +1 -1
- package/dist/components/ui/charts/donut-chart.cjs.map +1 -1
- package/dist/components/ui/charts/donut-chart.js +39 -60
- package/dist/components/ui/charts/donut-chart.js.map +1 -1
- package/dist/components/ui/charts/kpi-card.cjs +1 -1
- package/dist/components/ui/charts/kpi-card.cjs.map +1 -1
- package/dist/components/ui/charts/kpi-card.js +19 -19
- package/dist/components/ui/charts/kpi-card.js.map +1 -1
- package/dist/components/ui/data/comment-system.cjs +1 -1
- package/dist/components/ui/data/comment-system.cjs.map +1 -1
- package/dist/components/ui/data/comment-system.js +67 -67
- package/dist/components/ui/data/comment-system.js.map +1 -1
- package/dist/components/ui/data/data-table.cjs +1 -1
- package/dist/components/ui/data/data-table.cjs.map +1 -1
- package/dist/components/ui/data/data-table.js +112 -101
- package/dist/components/ui/data/data-table.js.map +1 -1
- package/dist/components/ui/data/editorial-calendar.cjs +1 -1
- package/dist/components/ui/data/editorial-calendar.cjs.map +1 -1
- package/dist/components/ui/data/editorial-calendar.js +291 -291
- package/dist/components/ui/data/editorial-calendar.js.map +1 -1
- package/dist/components/ui/data/filter-builder.cjs +1 -1
- package/dist/components/ui/data/filter-builder.cjs.map +1 -1
- package/dist/components/ui/data/filter-builder.js +1 -1
- package/dist/components/ui/data/filter-builder.js.map +1 -1
- package/dist/components/ui/feedback/empty-state.cjs +1 -1
- package/dist/components/ui/feedback/empty-state.cjs.map +1 -1
- package/dist/components/ui/feedback/empty-state.js +14 -14
- package/dist/components/ui/feedback/empty-state.js.map +1 -1
- package/dist/components/ui/feedback/toast.cjs +1 -1
- package/dist/components/ui/feedback/toast.cjs.map +1 -1
- package/dist/components/ui/feedback/toast.js +53 -22
- package/dist/components/ui/feedback/toast.js.map +1 -1
- package/dist/components/ui/forms/calendar.cjs +1 -1
- package/dist/components/ui/forms/calendar.cjs.map +1 -1
- package/dist/components/ui/forms/calendar.js +16 -16
- package/dist/components/ui/forms/calendar.js.map +1 -1
- package/dist/components/ui/forms/combobox.cjs +1 -1
- package/dist/components/ui/forms/combobox.cjs.map +1 -1
- package/dist/components/ui/forms/combobox.js +78 -76
- package/dist/components/ui/forms/combobox.js.map +1 -1
- package/dist/components/ui/forms/file-attachment-list.cjs +1 -1
- package/dist/components/ui/forms/file-attachment-list.cjs.map +1 -1
- package/dist/components/ui/forms/file-attachment-list.js +4 -4
- package/dist/components/ui/forms/file-attachment-list.js.map +1 -1
- package/dist/components/ui/forms/file-upload-zone.cjs +2 -2
- package/dist/components/ui/forms/file-upload-zone.cjs.map +1 -1
- package/dist/components/ui/forms/file-upload-zone.js +55 -49
- package/dist/components/ui/forms/file-upload-zone.js.map +1 -1
- package/dist/components/ui/forms/form.cjs +1 -1
- package/dist/components/ui/forms/form.cjs.map +1 -1
- package/dist/components/ui/forms/form.js +11 -11
- package/dist/components/ui/forms/form.js.map +1 -1
- package/dist/components/ui/forms/input-validation.cjs +1 -1
- package/dist/components/ui/forms/input-validation.cjs.map +1 -1
- package/dist/components/ui/forms/input-validation.js +20 -20
- package/dist/components/ui/forms/input-validation.js.map +1 -1
- package/dist/components/ui/forms/multi-select.cjs +1 -1
- package/dist/components/ui/forms/multi-select.cjs.map +1 -1
- package/dist/components/ui/forms/multi-select.js +69 -67
- package/dist/components/ui/forms/multi-select.js.map +1 -1
- package/dist/components/ui/forms/radio-group.cjs +1 -1
- package/dist/components/ui/forms/radio-group.cjs.map +1 -1
- package/dist/components/ui/forms/radio-group.js +19 -19
- package/dist/components/ui/forms/radio-group.js.map +1 -1
- package/dist/components/ui/forms/social-button.cjs +1 -1
- package/dist/components/ui/forms/social-button.cjs.map +1 -1
- package/dist/components/ui/forms/social-button.js +6 -6
- package/dist/components/ui/forms/social-button.js.map +1 -1
- package/dist/components/ui/forms/toggle.cjs +1 -1
- package/dist/components/ui/forms/toggle.cjs.map +1 -1
- package/dist/components/ui/forms/toggle.js +7 -7
- package/dist/components/ui/forms/toggle.js.map +1 -1
- package/dist/components/ui/navigation/navigation-menu.cjs +1 -1
- package/dist/components/ui/navigation/navigation-menu.cjs.map +1 -1
- package/dist/components/ui/navigation/navigation-menu.js +14 -14
- package/dist/components/ui/navigation/navigation-menu.js.map +1 -1
- package/dist/components/ui/navigation/sidebar.cjs +1 -1
- package/dist/components/ui/navigation/sidebar.cjs.map +1 -1
- package/dist/components/ui/navigation/sidebar.js +44 -44
- package/dist/components/ui/navigation/sidebar.js.map +1 -1
- package/dist/components/ui/overlays/context-menu.cjs +1 -1
- package/dist/components/ui/overlays/context-menu.cjs.map +1 -1
- package/dist/components/ui/overlays/context-menu.js +1 -1
- package/dist/components/ui/overlays/context-menu.js.map +1 -1
- package/dist/components/ui/overlays/dropdown-menu.cjs +1 -1
- package/dist/components/ui/overlays/dropdown-menu.cjs.map +1 -1
- package/dist/components/ui/overlays/dropdown-menu.js +5 -5
- package/dist/components/ui/overlays/dropdown-menu.js.map +1 -1
- package/dist/components/ui/overlays/menubar.cjs +1 -1
- package/dist/components/ui/overlays/menubar.cjs.map +1 -1
- package/dist/components/ui/overlays/menubar.js +29 -29
- package/dist/components/ui/overlays/menubar.js.map +1 -1
- package/dist/components/ui/overlays/tooltip.cjs +1 -1
- package/dist/components/ui/overlays/tooltip.cjs.map +1 -1
- package/dist/components/ui/overlays/tooltip.js +12 -12
- package/dist/components/ui/overlays/tooltip.js.map +1 -1
- package/dist/components/ui/primitives/avatar.cjs +1 -1
- package/dist/components/ui/primitives/avatar.cjs.map +1 -1
- package/dist/components/ui/primitives/avatar.js +20 -27
- package/dist/components/ui/primitives/avatar.js.map +1 -1
- package/dist/components/ui/primitives/badge.cjs +1 -1
- package/dist/components/ui/primitives/badge.cjs.map +1 -1
- package/dist/components/ui/primitives/badge.js +13 -13
- package/dist/components/ui/primitives/badge.js.map +1 -1
- package/dist/components/ui/primitives/button.cjs +1 -1
- package/dist/components/ui/primitives/button.cjs.map +1 -1
- package/dist/components/ui/primitives/button.js +11 -8
- package/dist/components/ui/primitives/button.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +34 -35
- package/dist/index.js.map +1 -1
- package/dist/preset.css +1 -2
- package/dist/toast-theme.css +100 -64
- package/dist/tokens.css +59 -159
- package/package.json +5 -3
- package/scripts/check-colors.mjs +208 -0
|
@@ -1,30 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs as l, jsx as e } from "react/jsx-runtime";
|
|
3
3
|
import * as N from "react";
|
|
4
|
-
import { ResponsiveContainer as
|
|
5
|
-
import { cn as
|
|
6
|
-
const
|
|
7
|
-
"#3b82f6",
|
|
8
|
-
// blue-500
|
|
9
|
-
"#10b981",
|
|
10
|
-
// emerald-500
|
|
11
|
-
"#f59e0b",
|
|
12
|
-
// amber-500
|
|
13
|
-
"#ef4444",
|
|
14
|
-
// red-500
|
|
15
|
-
"#8b5cf6",
|
|
16
|
-
// violet-500
|
|
17
|
-
"#06b6d4",
|
|
18
|
-
// cyan-500
|
|
19
|
-
"#f97316",
|
|
20
|
-
// orange-500
|
|
21
|
-
"#14b8a6",
|
|
22
|
-
// teal-500
|
|
23
|
-
"#ec4899",
|
|
24
|
-
// pink-500
|
|
25
|
-
"#6366f1"
|
|
26
|
-
// indigo-500
|
|
27
|
-
];
|
|
4
|
+
import { ResponsiveContainer as A, PieChart as z, Pie as k, Label as D, Sector as L, Tooltip as M } from "recharts";
|
|
5
|
+
import { cn as v } from "../utils.js";
|
|
6
|
+
const b = Array.from({ length: 8 }, (n, t) => `var(--chart-${t + 1})`);
|
|
28
7
|
function C(n) {
|
|
29
8
|
return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(n);
|
|
30
9
|
}
|
|
@@ -77,15 +56,15 @@ function F({
|
|
|
77
56
|
] });
|
|
78
57
|
}
|
|
79
58
|
function T({ viewBox: n, activeIndex: t, chartData: s, formatValue: i, total: o }) {
|
|
80
|
-
const r = (n == null ? void 0 : n.cx) ?? 0,
|
|
81
|
-
if (
|
|
82
|
-
const
|
|
59
|
+
const r = (n == null ? void 0 : n.cx) ?? 0, a = (n == null ? void 0 : n.cy) ?? 0, c = t !== null && t >= 0 ? s[t] : null;
|
|
60
|
+
if (c) {
|
|
61
|
+
const d = o > 0 ? (c.value / o * 100).toFixed(1) : "0.0", f = 13, g = c.name.length > f ? c.name.substring(0, f - 1) + "…" : c.name;
|
|
83
62
|
return /* @__PURE__ */ l("g", { children: [
|
|
84
63
|
/* @__PURE__ */ e(
|
|
85
64
|
"text",
|
|
86
65
|
{
|
|
87
66
|
x: r,
|
|
88
|
-
y:
|
|
67
|
+
y: a - 14,
|
|
89
68
|
textAnchor: "middle",
|
|
90
69
|
dominantBaseline: "middle",
|
|
91
70
|
style: { fontSize: 10, fill: "var(--muted-foreground)" },
|
|
@@ -96,23 +75,23 @@ function T({ viewBox: n, activeIndex: t, chartData: s, formatValue: i, total: o
|
|
|
96
75
|
"text",
|
|
97
76
|
{
|
|
98
77
|
x: r,
|
|
99
|
-
y:
|
|
78
|
+
y: a + 2,
|
|
100
79
|
textAnchor: "middle",
|
|
101
80
|
dominantBaseline: "middle",
|
|
102
|
-
style: { fontSize: 11, fontWeight: 700, fill:
|
|
103
|
-
children: i(
|
|
81
|
+
style: { fontSize: 11, fontWeight: 700, fill: c.color },
|
|
82
|
+
children: i(c.value)
|
|
104
83
|
}
|
|
105
84
|
),
|
|
106
85
|
/* @__PURE__ */ l(
|
|
107
86
|
"text",
|
|
108
87
|
{
|
|
109
88
|
x: r,
|
|
110
|
-
y:
|
|
89
|
+
y: a + 18,
|
|
111
90
|
textAnchor: "middle",
|
|
112
91
|
dominantBaseline: "middle",
|
|
113
92
|
style: { fontSize: 11, fill: "var(--muted-foreground)" },
|
|
114
93
|
children: [
|
|
115
|
-
|
|
94
|
+
d,
|
|
116
95
|
"%"
|
|
117
96
|
]
|
|
118
97
|
}
|
|
@@ -124,7 +103,7 @@ function T({ viewBox: n, activeIndex: t, chartData: s, formatValue: i, total: o
|
|
|
124
103
|
"text",
|
|
125
104
|
{
|
|
126
105
|
x: r,
|
|
127
|
-
y:
|
|
106
|
+
y: a - 8,
|
|
128
107
|
textAnchor: "middle",
|
|
129
108
|
dominantBaseline: "middle",
|
|
130
109
|
style: { fontSize: 10, fill: "var(--muted-foreground)" },
|
|
@@ -135,7 +114,7 @@ function T({ viewBox: n, activeIndex: t, chartData: s, formatValue: i, total: o
|
|
|
135
114
|
"text",
|
|
136
115
|
{
|
|
137
116
|
x: r,
|
|
138
|
-
y:
|
|
117
|
+
y: a + 8,
|
|
139
118
|
textAnchor: "middle",
|
|
140
119
|
dominantBaseline: "middle",
|
|
141
120
|
style: { fontSize: 13, fontWeight: 700, fill: "currentColor" },
|
|
@@ -152,14 +131,14 @@ function E({
|
|
|
152
131
|
onHover: o
|
|
153
132
|
}) {
|
|
154
133
|
return /* @__PURE__ */ l("div", { className: "flex flex-col gap-1.5", children: [
|
|
155
|
-
n.map((r,
|
|
156
|
-
const
|
|
134
|
+
n.map((r, a) => {
|
|
135
|
+
const c = t > 0 ? (r.value / t * 100).toFixed(1) : "0.0", d = i === a;
|
|
157
136
|
return /* @__PURE__ */ l(
|
|
158
137
|
"div",
|
|
159
138
|
{
|
|
160
139
|
className: "flex items-center gap-2 text-xs cursor-default rounded px-1 py-0.5 transition-opacity",
|
|
161
|
-
style: { opacity: i !== null && !
|
|
162
|
-
onMouseEnter: () => o(
|
|
140
|
+
style: { opacity: i !== null && !d ? 0.4 : 1 },
|
|
141
|
+
onMouseEnter: () => o(a),
|
|
163
142
|
onMouseLeave: () => o(null),
|
|
164
143
|
children: [
|
|
165
144
|
/* @__PURE__ */ e(
|
|
@@ -168,19 +147,19 @@ function E({
|
|
|
168
147
|
className: "inline-block w-2.5 h-2.5 rounded-full shrink-0 transition-transform",
|
|
169
148
|
style: {
|
|
170
149
|
background: r.color,
|
|
171
|
-
transform:
|
|
150
|
+
transform: d ? "scale(1.3)" : "scale(1)"
|
|
172
151
|
}
|
|
173
152
|
}
|
|
174
153
|
),
|
|
175
154
|
/* @__PURE__ */ e("span", { className: "flex-1 truncate text-muted-foreground", children: r.name }),
|
|
176
155
|
/* @__PURE__ */ e("span", { className: "font-mono text-foreground", children: s(r.value) }),
|
|
177
156
|
/* @__PURE__ */ l("span", { className: "text-muted-foreground w-10.5 text-right", children: [
|
|
178
|
-
|
|
157
|
+
c,
|
|
179
158
|
"%"
|
|
180
159
|
] })
|
|
181
160
|
]
|
|
182
161
|
},
|
|
183
|
-
|
|
162
|
+
a
|
|
184
163
|
);
|
|
185
164
|
}),
|
|
186
165
|
/* @__PURE__ */ l("div", { className: "flex items-center gap-2 text-xs border-t border-border pt-1.5 mt-0.5", children: [
|
|
@@ -189,33 +168,33 @@ function E({
|
|
|
189
168
|
] })
|
|
190
169
|
] });
|
|
191
170
|
}
|
|
192
|
-
function
|
|
171
|
+
function P({
|
|
193
172
|
data: n,
|
|
194
173
|
title: t,
|
|
195
174
|
formatValue: s = C,
|
|
196
175
|
className: i,
|
|
197
176
|
isLoading: o = !1,
|
|
198
177
|
layout: r = "vertical",
|
|
199
|
-
activeLabel:
|
|
200
|
-
chartSize:
|
|
178
|
+
activeLabel: a = !1,
|
|
179
|
+
chartSize: c
|
|
201
180
|
}) {
|
|
202
|
-
const [
|
|
181
|
+
const [d, f] = N.useState(null), g = N.useMemo(() => n.reduce((m, u) => m + u.value, 0), [n]), p = N.useMemo(
|
|
203
182
|
() => n.map((m, u) => ({
|
|
204
183
|
name: m.label,
|
|
205
184
|
value: m.value,
|
|
206
|
-
color: m.color ??
|
|
185
|
+
color: m.color ?? b[u % b.length]
|
|
207
186
|
})),
|
|
208
187
|
[n]
|
|
209
|
-
), h = r === "horizontal", x =
|
|
210
|
-
return /* @__PURE__ */ l("div", { className:
|
|
188
|
+
), h = r === "horizontal", x = c ?? (h ? 140 : 220), w = h ? Math.round(x * 0.34) : 55, S = h ? Math.round(x * 0.51) : 85;
|
|
189
|
+
return /* @__PURE__ */ l("div", { className: v("flex flex-col gap-4 w-full", i), children: [
|
|
211
190
|
t && /* @__PURE__ */ e("div", { className: "text-sm font-semibold text-foreground", children: t }),
|
|
212
|
-
o ? /* @__PURE__ */ e(R, { layout: r, chartSize: x }) : n.length === 0 ? /* @__PURE__ */ e(j, {}) : /* @__PURE__ */ l("div", { className:
|
|
191
|
+
o ? /* @__PURE__ */ e(R, { layout: r, chartSize: x }) : n.length === 0 ? /* @__PURE__ */ e(j, {}) : /* @__PURE__ */ l("div", { className: v(h ? "flex items-center gap-4" : "flex flex-col gap-4"), children: [
|
|
213
192
|
/* @__PURE__ */ e(
|
|
214
193
|
"div",
|
|
215
194
|
{
|
|
216
195
|
className: "shrink-0",
|
|
217
196
|
style: h ? { width: x, height: x } : { width: "100%", height: x },
|
|
218
|
-
children: /* @__PURE__ */ e(
|
|
197
|
+
children: /* @__PURE__ */ e(A, { width: "100%", height: "100%", children: /* @__PURE__ */ l(z, { children: [
|
|
219
198
|
/* @__PURE__ */ e(
|
|
220
199
|
k,
|
|
221
200
|
{
|
|
@@ -230,24 +209,24 @@ function W({
|
|
|
230
209
|
onMouseEnter: (m, u) => f(u),
|
|
231
210
|
onMouseLeave: () => f(null),
|
|
232
211
|
shape: (m, u) => {
|
|
233
|
-
var
|
|
212
|
+
var y;
|
|
234
213
|
return /* @__PURE__ */ e(
|
|
235
214
|
L,
|
|
236
215
|
{
|
|
237
216
|
...m,
|
|
238
|
-
fill: (
|
|
217
|
+
fill: (y = p[u]) == null ? void 0 : y.color,
|
|
239
218
|
stroke: "transparent",
|
|
240
|
-
opacity:
|
|
219
|
+
opacity: d !== null && d !== u ? 0.4 : 1
|
|
241
220
|
}
|
|
242
221
|
);
|
|
243
222
|
},
|
|
244
|
-
children:
|
|
223
|
+
children: a && /* @__PURE__ */ e(
|
|
245
224
|
D,
|
|
246
225
|
{
|
|
247
226
|
content: /* @__PURE__ */ e(
|
|
248
227
|
T,
|
|
249
228
|
{
|
|
250
|
-
activeIndex:
|
|
229
|
+
activeIndex: d,
|
|
251
230
|
chartData: p,
|
|
252
231
|
formatValue: s,
|
|
253
232
|
total: g
|
|
@@ -257,7 +236,7 @@ function W({
|
|
|
257
236
|
)
|
|
258
237
|
}
|
|
259
238
|
),
|
|
260
|
-
!
|
|
239
|
+
!a && /* @__PURE__ */ e(
|
|
261
240
|
M,
|
|
262
241
|
{
|
|
263
242
|
content: /* @__PURE__ */ e(F, { formatValue: s, total: g })
|
|
@@ -266,13 +245,13 @@ function W({
|
|
|
266
245
|
] }) })
|
|
267
246
|
}
|
|
268
247
|
),
|
|
269
|
-
/* @__PURE__ */ e("div", { className:
|
|
248
|
+
/* @__PURE__ */ e("div", { className: v(h ? "flex-1 min-w-0" : ""), children: /* @__PURE__ */ e(
|
|
270
249
|
E,
|
|
271
250
|
{
|
|
272
251
|
chartData: p,
|
|
273
252
|
total: g,
|
|
274
253
|
formatValue: s,
|
|
275
|
-
activeIndex:
|
|
254
|
+
activeIndex: d,
|
|
276
255
|
onHover: f
|
|
277
256
|
}
|
|
278
257
|
) })
|
|
@@ -280,6 +259,6 @@ function W({
|
|
|
280
259
|
] });
|
|
281
260
|
}
|
|
282
261
|
export {
|
|
283
|
-
|
|
262
|
+
P as DonutChart
|
|
284
263
|
};
|
|
285
264
|
//# sourceMappingURL=donut-chart.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"donut-chart.js","sources":["../../../../src/components/ui/charts/donut-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Label, Pie, PieChart, ResponsiveContainer, Sector, Tooltip } from \"recharts\";\nimport { cn } from \"../utils\";\n\n// ── Paleta padrão ─────────────────────────────────────────────────────────────\nconst DEFAULT_COLORS = [\n \"#3b82f6\", // blue-500\n \"#10b981\", // emerald-500\n \"#f59e0b\", // amber-500\n \"#ef4444\", // red-500\n \"#8b5cf6\", // violet-500\n \"#06b6d4\", // cyan-500\n \"#f97316\", // orange-500\n \"#14b8a6\", // teal-500\n \"#ec4899\", // pink-500\n \"#6366f1\", // indigo-500\n];\n\n// ── Tipos ─────────────────────────────────────────────────────────────────────\n\nexport interface DonutChartItem {\n label: string;\n value: number;\n color?: string;\n}\n\nexport interface DonutChartProps {\n data: DonutChartItem[];\n title?: string;\n formatValue?: (v: number) => string;\n className?: string;\n isLoading?: boolean;\n /**\n * Orientação da legenda em relação ao gráfico.\n * - \"vertical\": legenda abaixo do donut (padrão)\n * - \"horizontal\": legenda ao lado direito do donut\n */\n layout?: \"vertical\" | \"horizontal\";\n /**\n * Quando true, exibe o nome, valor e % da categoria no centro do\n * donut ao passar o mouse — desabilita o tooltip flutuante padrão.\n */\n activeLabel?: boolean;\n /**\n * Tamanho (px) do container do gráfico no eixo fixo.\n * - horizontal: largura e altura do donut (padrão: 140)\n * - vertical: altura do donut (padrão: 220)\n */\n chartSize?: number;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction defaultFormat(v: number): string {\n return new Intl.NumberFormat(\"pt-BR\", { style: \"currency\", currency: \"BRL\" }).format(v);\n}\n\n// ── Skeleton ──────────────────────────────────────────────────────────────────\n\nfunction DonutSkeleton({ layout, chartSize }: { layout: \"vertical\" | \"horizontal\"; chartSize: number }) {\n const legendItems = [1, 2, 3].map((i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <div className=\"w-2.5 h-2.5 rounded-full bg-muted shrink-0\" />\n <div className=\"h-3 bg-muted rounded flex-1\" />\n </div>\n ));\n\n if (layout === \"horizontal\") {\n return (\n <div className=\"flex items-center gap-6 w-full animate-pulse\">\n <div className=\"rounded-full bg-muted shrink-0\" style={{ width: chartSize, height: chartSize }} />\n <div className=\"flex flex-col gap-2 flex-1\">{legendItems}</div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col items-center justify-center w-full animate-pulse gap-4\">\n <div className=\"rounded-full bg-muted\" style={{ width: 140, height: 140 }} />\n <div className=\"flex flex-col gap-2 w-full max-w-50\">{legendItems}</div>\n </div>\n );\n}\n\n// ── Empty state ───────────────────────────────────────────────────────────────\n\nfunction DonutEmpty() {\n return (\n <div className=\"flex flex-col items-center justify-center w-full h-full text-muted-foreground gap-2\">\n <div\n className=\"rounded-full border-4 border-dashed border-muted\"\n style={{ width: 100, height: 100 }}\n />\n <span className=\"text-sm\">Sem dados para exibir</span>\n </div>\n );\n}\n\n// ── Tooltip flutuante (usado quando activeLabel=false) ────────────────────────\n\nfunction CustomTooltip({\n active,\n payload,\n formatValue,\n total,\n}: {\n active?: boolean;\n payload?: any[];\n formatValue: (v: number) => string;\n total: number;\n}) {\n if (!active || !payload?.length) return null;\n const item = payload[0];\n const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n return (\n <div className=\"bg-popover border border-border rounded shadow-lg p-2 text-xs min-w-35\">\n <div className=\"font-medium mb-1\">{item.name}</div>\n <div className=\"flex justify-between gap-4\">\n <span className=\"text-muted-foreground\">Valor</span>\n <span className=\"font-mono\">{formatValue(item.value)}</span>\n </div>\n <div className=\"flex justify-between gap-4\">\n <span className=\"text-muted-foreground\">%</span>\n <span className=\"font-mono\">{pct}%</span>\n </div>\n </div>\n );\n}\n\n// ── Label central (usado quando activeLabel=true) ─────────────────────────────\n\ninterface CenterLabelProps {\n viewBox?: { cx: number; cy: number; innerRadius?: number; outerRadius?: number };\n activeIndex: number | null;\n chartData: Array<{ name: string; value: number; color: string }>;\n formatValue: (v: number) => string;\n total: number;\n}\n\nfunction CenterLabel({ viewBox, activeIndex, chartData, formatValue, total }: CenterLabelProps) {\n const cx = viewBox?.cx ?? 0;\n const cy = viewBox?.cy ?? 0;\n\n const item = activeIndex !== null && activeIndex >= 0 ? chartData[activeIndex] : null;\n\n if (item) {\n const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n const maxLen = 13;\n const name = item.name.length > maxLen ? item.name.substring(0, maxLen - 1) + \"…\" : item.name;\n return (\n <g>\n <text\n x={cx}\n y={cy - 14}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 10, fill: \"var(--muted-foreground)\" }}\n >\n {name}\n </text>\n <text\n x={cx}\n y={cy + 2}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 11, fontWeight: 700, fill: item.color }}\n >\n {formatValue(item.value)}\n </text>\n <text\n x={cx}\n y={cy + 18}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 11, fill: \"var(--muted-foreground)\" }}\n >\n {pct}%\n </text>\n </g>\n );\n }\n\n return (\n <g>\n <text\n x={cx}\n y={cy - 8}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 10, fill: \"var(--muted-foreground)\" }}\n >\n Total\n </text>\n <text\n x={cx}\n y={cy + 8}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 13, fontWeight: 700, fill: \"currentColor\" }}\n >\n {formatValue(total)}\n </text>\n </g>\n );\n}\n\n// ── Legenda ───────────────────────────────────────────────────────────────────\n\nfunction DonutLegend({\n chartData,\n total,\n formatValue,\n activeIndex,\n onHover,\n}: {\n chartData: Array<{ name: string; value: number; color: string }>;\n total: number;\n formatValue: (v: number) => string;\n activeIndex: number | null;\n onHover: (index: number | null) => void;\n}) {\n return (\n <div className=\"flex flex-col gap-1.5\">\n {chartData.map((item, i) => {\n const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n const isActive = activeIndex === i;\n const isDimmed = activeIndex !== null && !isActive;\n return (\n <div\n key={i}\n className=\"flex items-center gap-2 text-xs cursor-default rounded px-1 py-0.5 transition-opacity\"\n style={{ opacity: isDimmed ? 0.4 : 1 }}\n onMouseEnter={() => onHover(i)}\n onMouseLeave={() => onHover(null)}\n >\n <span\n className=\"inline-block w-2.5 h-2.5 rounded-full shrink-0 transition-transform\"\n style={{\n background: item.color,\n transform: isActive ? \"scale(1.3)\" : \"scale(1)\",\n }}\n />\n <span className=\"flex-1 truncate text-muted-foreground\">{item.name}</span>\n <span className=\"font-mono text-foreground\">{formatValue(item.value)}</span>\n <span className=\"text-muted-foreground w-10.5 text-right\">{pct}%</span>\n </div>\n );\n })}\n <div className=\"flex items-center gap-2 text-xs border-t border-border pt-1.5 mt-0.5\">\n <span className=\"flex-1 font-medium\">Total</span>\n <span className=\"font-mono font-semibold\">{formatValue(total)}</span>\n </div>\n </div>\n );\n}\n\n// ── Componente principal ──────────────────────────────────────────────────────\n\nexport function DonutChart({\n data,\n title,\n formatValue = defaultFormat,\n className,\n isLoading = false,\n layout = \"vertical\",\n activeLabel = false,\n chartSize: chartSizeProp,\n}: DonutChartProps) {\n const [activeIndex, setActiveIndex] = React.useState<number | null>(null);\n\n const total = React.useMemo(() => data.reduce((acc, d) => acc + d.value, 0), [data]);\n\n const chartData = React.useMemo(\n () =>\n data.map((item, i) => ({\n name: item.label,\n value: item.value,\n color: item.color ?? DEFAULT_COLORS[i % DEFAULT_COLORS.length],\n })),\n [data],\n );\n\n const isHorizontal = layout === \"horizontal\";\n const chartSize = chartSizeProp ?? (isHorizontal ? 140 : 220);\n const innerRadius = isHorizontal ? Math.round(chartSize * 0.34) : 55;\n const outerRadius = isHorizontal ? Math.round(chartSize * 0.51) : 85;\n\n return (\n <div className={cn(\"flex flex-col gap-4 w-full\", className)}>\n {title && <div className=\"text-sm font-semibold text-foreground\">{title}</div>}\n\n {isLoading ? (\n <DonutSkeleton layout={layout} chartSize={chartSize} />\n ) : data.length === 0 ? (\n <DonutEmpty />\n ) : (\n <div className={cn(isHorizontal ? \"flex items-center gap-4\" : \"flex flex-col gap-4\")}>\n {/* Gráfico */}\n <div\n className=\"shrink-0\"\n style={\n isHorizontal\n ? { width: chartSize, height: chartSize }\n : { width: \"100%\", height: chartSize }\n }\n >\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <PieChart>\n <Pie\n data={chartData}\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n paddingAngle={2}\n dataKey=\"value\"\n nameKey=\"name\"\n onMouseEnter={(_, index) => setActiveIndex(index)}\n onMouseLeave={() => setActiveIndex(null)}\n shape={(props, index) => (\n <Sector\n {...props}\n fill={chartData[index]?.color}\n stroke=\"transparent\"\n opacity={activeIndex !== null && activeIndex !== index ? 0.4 : 1}\n />\n )}\n >\n {activeLabel && (\n <Label\n content={\n <CenterLabel\n activeIndex={activeIndex}\n chartData={chartData}\n formatValue={formatValue}\n total={total}\n />\n }\n />\n )}\n </Pie>\n {!activeLabel && (\n <Tooltip\n content={<CustomTooltip formatValue={formatValue} total={total} />}\n />\n )}\n </PieChart>\n </ResponsiveContainer>\n </div>\n\n {/* Legenda */}\n <div className={cn(isHorizontal ? \"flex-1 min-w-0\" : \"\")}>\n <DonutLegend\n chartData={chartData}\n total={total}\n formatValue={formatValue}\n activeIndex={activeIndex}\n onHover={setActiveIndex}\n />\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"names":["DEFAULT_COLORS","defaultFormat","v","DonutSkeleton","layout","chartSize","legendItems","jsxs","jsx","DonutEmpty","CustomTooltip","active","payload","formatValue","total","item","pct","CenterLabel","viewBox","activeIndex","chartData","cx","cy","maxLen","name","DonutLegend","onHover","i","isActive","DonutChart","data","title","className","isLoading","activeLabel","chartSizeProp","setActiveIndex","React","acc","d","isHorizontal","innerRadius","outerRadius","cn","ResponsiveContainer","PieChart","Pie","_","index","props","Sector","_a","Label","Tooltip"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"donut-chart.js","sources":["../../../../src/components/ui/charts/donut-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Label, Pie, PieChart, ResponsiveContainer, Sector, Tooltip } from \"recharts\";\nimport { cn } from \"../utils\";\n\n// ── Paleta padrão ─────────────────────────────────────────────────────────────\n// Séries de gráfico da paleta iClips (roxo + cinza, vizinhas distintas em luminância).\nconst DEFAULT_COLORS = Array.from({ length: 8 }, (_, i) => `var(--chart-${i + 1})`);\n\n// ── Tipos ─────────────────────────────────────────────────────────────────────\n\nexport interface DonutChartItem {\n label: string;\n value: number;\n color?: string;\n}\n\nexport interface DonutChartProps {\n data: DonutChartItem[];\n title?: string;\n formatValue?: (v: number) => string;\n className?: string;\n isLoading?: boolean;\n /**\n * Orientação da legenda em relação ao gráfico.\n * - \"vertical\": legenda abaixo do donut (padrão)\n * - \"horizontal\": legenda ao lado direito do donut\n */\n layout?: \"vertical\" | \"horizontal\";\n /**\n * Quando true, exibe o nome, valor e % da categoria no centro do\n * donut ao passar o mouse — desabilita o tooltip flutuante padrão.\n */\n activeLabel?: boolean;\n /**\n * Tamanho (px) do container do gráfico no eixo fixo.\n * - horizontal: largura e altura do donut (padrão: 140)\n * - vertical: altura do donut (padrão: 220)\n */\n chartSize?: number;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction defaultFormat(v: number): string {\n return new Intl.NumberFormat(\"pt-BR\", { style: \"currency\", currency: \"BRL\" }).format(v);\n}\n\n// ── Skeleton ──────────────────────────────────────────────────────────────────\n\nfunction DonutSkeleton({ layout, chartSize }: { layout: \"vertical\" | \"horizontal\"; chartSize: number }) {\n const legendItems = [1, 2, 3].map((i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <div className=\"w-2.5 h-2.5 rounded-full bg-muted shrink-0\" />\n <div className=\"h-3 bg-muted rounded flex-1\" />\n </div>\n ));\n\n if (layout === \"horizontal\") {\n return (\n <div className=\"flex items-center gap-6 w-full animate-pulse\">\n <div className=\"rounded-full bg-muted shrink-0\" style={{ width: chartSize, height: chartSize }} />\n <div className=\"flex flex-col gap-2 flex-1\">{legendItems}</div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col items-center justify-center w-full animate-pulse gap-4\">\n <div className=\"rounded-full bg-muted\" style={{ width: 140, height: 140 }} />\n <div className=\"flex flex-col gap-2 w-full max-w-50\">{legendItems}</div>\n </div>\n );\n}\n\n// ── Empty state ───────────────────────────────────────────────────────────────\n\nfunction DonutEmpty() {\n return (\n <div className=\"flex flex-col items-center justify-center w-full h-full text-muted-foreground gap-2\">\n <div\n className=\"rounded-full border-4 border-dashed border-muted\"\n style={{ width: 100, height: 100 }}\n />\n <span className=\"text-sm\">Sem dados para exibir</span>\n </div>\n );\n}\n\n// ── Tooltip flutuante (usado quando activeLabel=false) ────────────────────────\n\nfunction CustomTooltip({\n active,\n payload,\n formatValue,\n total,\n}: {\n active?: boolean;\n payload?: any[];\n formatValue: (v: number) => string;\n total: number;\n}) {\n if (!active || !payload?.length) return null;\n const item = payload[0];\n const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n return (\n <div className=\"bg-popover border border-border rounded shadow-lg p-2 text-xs min-w-35\">\n <div className=\"font-medium mb-1\">{item.name}</div>\n <div className=\"flex justify-between gap-4\">\n <span className=\"text-muted-foreground\">Valor</span>\n <span className=\"font-mono\">{formatValue(item.value)}</span>\n </div>\n <div className=\"flex justify-between gap-4\">\n <span className=\"text-muted-foreground\">%</span>\n <span className=\"font-mono\">{pct}%</span>\n </div>\n </div>\n );\n}\n\n// ── Label central (usado quando activeLabel=true) ─────────────────────────────\n\ninterface CenterLabelProps {\n viewBox?: { cx: number; cy: number; innerRadius?: number; outerRadius?: number };\n activeIndex: number | null;\n chartData: Array<{ name: string; value: number; color: string }>;\n formatValue: (v: number) => string;\n total: number;\n}\n\nfunction CenterLabel({ viewBox, activeIndex, chartData, formatValue, total }: CenterLabelProps) {\n const cx = viewBox?.cx ?? 0;\n const cy = viewBox?.cy ?? 0;\n\n const item = activeIndex !== null && activeIndex >= 0 ? chartData[activeIndex] : null;\n\n if (item) {\n const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n const maxLen = 13;\n const name = item.name.length > maxLen ? item.name.substring(0, maxLen - 1) + \"…\" : item.name;\n return (\n <g>\n <text\n x={cx}\n y={cy - 14}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 10, fill: \"var(--muted-foreground)\" }}\n >\n {name}\n </text>\n <text\n x={cx}\n y={cy + 2}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 11, fontWeight: 700, fill: item.color }}\n >\n {formatValue(item.value)}\n </text>\n <text\n x={cx}\n y={cy + 18}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 11, fill: \"var(--muted-foreground)\" }}\n >\n {pct}%\n </text>\n </g>\n );\n }\n\n return (\n <g>\n <text\n x={cx}\n y={cy - 8}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 10, fill: \"var(--muted-foreground)\" }}\n >\n Total\n </text>\n <text\n x={cx}\n y={cy + 8}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 13, fontWeight: 700, fill: \"currentColor\" }}\n >\n {formatValue(total)}\n </text>\n </g>\n );\n}\n\n// ── Legenda ───────────────────────────────────────────────────────────────────\n\nfunction DonutLegend({\n chartData,\n total,\n formatValue,\n activeIndex,\n onHover,\n}: {\n chartData: Array<{ name: string; value: number; color: string }>;\n total: number;\n formatValue: (v: number) => string;\n activeIndex: number | null;\n onHover: (index: number | null) => void;\n}) {\n return (\n <div className=\"flex flex-col gap-1.5\">\n {chartData.map((item, i) => {\n const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n const isActive = activeIndex === i;\n const isDimmed = activeIndex !== null && !isActive;\n return (\n <div\n key={i}\n className=\"flex items-center gap-2 text-xs cursor-default rounded px-1 py-0.5 transition-opacity\"\n style={{ opacity: isDimmed ? 0.4 : 1 }}\n onMouseEnter={() => onHover(i)}\n onMouseLeave={() => onHover(null)}\n >\n <span\n className=\"inline-block w-2.5 h-2.5 rounded-full shrink-0 transition-transform\"\n style={{\n background: item.color,\n transform: isActive ? \"scale(1.3)\" : \"scale(1)\",\n }}\n />\n <span className=\"flex-1 truncate text-muted-foreground\">{item.name}</span>\n <span className=\"font-mono text-foreground\">{formatValue(item.value)}</span>\n <span className=\"text-muted-foreground w-10.5 text-right\">{pct}%</span>\n </div>\n );\n })}\n <div className=\"flex items-center gap-2 text-xs border-t border-border pt-1.5 mt-0.5\">\n <span className=\"flex-1 font-medium\">Total</span>\n <span className=\"font-mono font-semibold\">{formatValue(total)}</span>\n </div>\n </div>\n );\n}\n\n// ── Componente principal ──────────────────────────────────────────────────────\n\nexport function DonutChart({\n data,\n title,\n formatValue = defaultFormat,\n className,\n isLoading = false,\n layout = \"vertical\",\n activeLabel = false,\n chartSize: chartSizeProp,\n}: DonutChartProps) {\n const [activeIndex, setActiveIndex] = React.useState<number | null>(null);\n\n const total = React.useMemo(() => data.reduce((acc, d) => acc + d.value, 0), [data]);\n\n const chartData = React.useMemo(\n () =>\n data.map((item, i) => ({\n name: item.label,\n value: item.value,\n color: item.color ?? DEFAULT_COLORS[i % DEFAULT_COLORS.length],\n })),\n [data],\n );\n\n const isHorizontal = layout === \"horizontal\";\n const chartSize = chartSizeProp ?? (isHorizontal ? 140 : 220);\n const innerRadius = isHorizontal ? Math.round(chartSize * 0.34) : 55;\n const outerRadius = isHorizontal ? Math.round(chartSize * 0.51) : 85;\n\n return (\n <div className={cn(\"flex flex-col gap-4 w-full\", className)}>\n {title && <div className=\"text-sm font-semibold text-foreground\">{title}</div>}\n\n {isLoading ? (\n <DonutSkeleton layout={layout} chartSize={chartSize} />\n ) : data.length === 0 ? (\n <DonutEmpty />\n ) : (\n <div className={cn(isHorizontal ? \"flex items-center gap-4\" : \"flex flex-col gap-4\")}>\n {/* Gráfico */}\n <div\n className=\"shrink-0\"\n style={\n isHorizontal\n ? { width: chartSize, height: chartSize }\n : { width: \"100%\", height: chartSize }\n }\n >\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <PieChart>\n <Pie\n data={chartData}\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n paddingAngle={2}\n dataKey=\"value\"\n nameKey=\"name\"\n onMouseEnter={(_, index) => setActiveIndex(index)}\n onMouseLeave={() => setActiveIndex(null)}\n shape={(props, index) => (\n <Sector\n {...props}\n fill={chartData[index]?.color}\n stroke=\"transparent\"\n opacity={activeIndex !== null && activeIndex !== index ? 0.4 : 1}\n />\n )}\n >\n {activeLabel && (\n <Label\n content={\n <CenterLabel\n activeIndex={activeIndex}\n chartData={chartData}\n formatValue={formatValue}\n total={total}\n />\n }\n />\n )}\n </Pie>\n {!activeLabel && (\n <Tooltip\n content={<CustomTooltip formatValue={formatValue} total={total} />}\n />\n )}\n </PieChart>\n </ResponsiveContainer>\n </div>\n\n {/* Legenda */}\n <div className={cn(isHorizontal ? \"flex-1 min-w-0\" : \"\")}>\n <DonutLegend\n chartData={chartData}\n total={total}\n formatValue={formatValue}\n activeIndex={activeIndex}\n onHover={setActiveIndex}\n />\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"names":["DEFAULT_COLORS","_","i","defaultFormat","v","DonutSkeleton","layout","chartSize","legendItems","jsxs","jsx","DonutEmpty","CustomTooltip","active","payload","formatValue","total","item","pct","CenterLabel","viewBox","activeIndex","chartData","cx","cy","maxLen","name","DonutLegend","onHover","isActive","DonutChart","data","title","className","isLoading","activeLabel","chartSizeProp","setActiveIndex","React","acc","d","isHorizontal","innerRadius","outerRadius","cn","ResponsiveContainer","PieChart","Pie","index","props","Sector","_a","Label","Tooltip"],"mappings":";;;;;AAQA,MAAMA,IAAiB,MAAM,KAAK,EAAE,QAAQ,EAAA,GAAK,CAACC,GAAGC,MAAM,eAAeA,IAAI,CAAC,GAAG;AAqClF,SAASC,EAAcC,GAAmB;AACxC,SAAO,IAAI,KAAK,aAAa,SAAS,EAAE,OAAO,YAAY,UAAU,MAAA,CAAO,EAAE,OAAOA,CAAC;AACxF;AAIA,SAASC,EAAc,EAAE,QAAAC,GAAQ,WAAAC,KAAuE;AACtG,QAAMC,IAAc,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,MACjC,gBAAAC,EAAC,OAAA,EAAY,WAAU,2BACrB,UAAA;AAAA,IAAA,gBAAAC,EAAC,OAAA,EAAI,WAAU,6CAAA,CAA6C;AAAA,IAC5D,gBAAAA,EAAC,OAAA,EAAI,WAAU,8BAAA,CAA8B;AAAA,EAAA,EAAA,GAFrC,CAGV,CACD;AAED,SAAIJ,MAAW,eAEX,gBAAAG,EAAC,OAAA,EAAI,WAAU,gDACb,UAAA;AAAA,IAAA,gBAAAC,EAAC,OAAA,EAAI,WAAU,kCAAiC,OAAO,EAAE,OAAOH,GAAW,QAAQA,EAAA,EAAU,CAAG;AAAA,IAChG,gBAAAG,EAAC,OAAA,EAAI,WAAU,8BAA8B,UAAAF,EAAA,CAAY;AAAA,EAAA,GAC3D,IAKF,gBAAAC,EAAC,OAAA,EAAI,WAAU,wEACb,UAAA;AAAA,IAAA,gBAAAC,EAAC,OAAA,EAAI,WAAU,yBAAwB,OAAO,EAAE,OAAO,KAAK,QAAQ,IAAA,EAAI,CAAG;AAAA,IAC3E,gBAAAA,EAAC,OAAA,EAAI,WAAU,uCAAuC,UAAAF,EAAA,CAAY;AAAA,EAAA,GACpE;AAEJ;AAIA,SAASG,IAAa;AACpB,SACE,gBAAAF,EAAC,OAAA,EAAI,WAAU,uFACb,UAAA;AAAA,IAAA,gBAAAC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO,EAAE,OAAO,KAAK,QAAQ,IAAA;AAAA,MAAI;AAAA,IAAA;AAAA,IAEnC,gBAAAA,EAAC,QAAA,EAAK,WAAU,WAAU,UAAA,wBAAA,CAAqB;AAAA,EAAA,GACjD;AAEJ;AAIA,SAASE,EAAc;AAAA,EACrB,QAAAC;AAAA,EACA,SAAAC;AAAA,EACA,aAAAC;AAAA,EACA,OAAAC;AACF,GAKG;AACD,MAAI,CAACH,KAAU,EAACC,KAAA,QAAAA,EAAS,QAAQ,QAAO;AACxC,QAAMG,IAAOH,EAAQ,CAAC,GAChBI,IAAMF,IAAQ,KAAMC,EAAK,QAAQD,IAAS,KAAK,QAAQ,CAAC,IAAI;AAClE,SACE,gBAAAP,EAAC,OAAA,EAAI,WAAU,0EACb,UAAA;AAAA,IAAA,gBAAAC,EAAC,OAAA,EAAI,WAAU,oBAAoB,UAAAO,EAAK,MAAK;AAAA,IAC7C,gBAAAR,EAAC,OAAA,EAAI,WAAU,8BACb,UAAA;AAAA,MAAA,gBAAAC,EAAC,QAAA,EAAK,WAAU,yBAAwB,UAAA,SAAK;AAAA,wBAC5C,QAAA,EAAK,WAAU,aAAa,UAAAK,EAAYE,EAAK,KAAK,EAAA,CAAE;AAAA,IAAA,GACvD;AAAA,IACA,gBAAAR,EAAC,OAAA,EAAI,WAAU,8BACb,UAAA;AAAA,MAAA,gBAAAC,EAAC,QAAA,EAAK,WAAU,yBAAwB,UAAA,KAAC;AAAA,MACzC,gBAAAD,EAAC,QAAA,EAAK,WAAU,aAAa,UAAA;AAAA,QAAAS;AAAA,QAAI;AAAA,MAAA,EAAA,CAAC;AAAA,IAAA,EAAA,CACpC;AAAA,EAAA,GACF;AAEJ;AAYA,SAASC,EAAY,EAAE,SAAAC,GAAS,aAAAC,GAAa,WAAAC,GAAW,aAAAP,GAAa,OAAAC,KAA2B;AAC9F,QAAMO,KAAKH,KAAA,gBAAAA,EAAS,OAAM,GACpBI,KAAKJ,KAAA,gBAAAA,EAAS,OAAM,GAEpBH,IAAOI,MAAgB,QAAQA,KAAe,IAAIC,EAAUD,CAAW,IAAI;AAEjF,MAAIJ,GAAM;AACR,UAAMC,IAAMF,IAAQ,KAAMC,EAAK,QAAQD,IAAS,KAAK,QAAQ,CAAC,IAAI,OAC5DS,IAAS,IACTC,IAAOT,EAAK,KAAK,SAASQ,IAASR,EAAK,KAAK,UAAU,GAAGQ,IAAS,CAAC,IAAI,MAAMR,EAAK;AACzF,6BACG,KAAA,EACC,UAAA;AAAA,MAAA,gBAAAP;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,GAAGa;AAAA,UACH,GAAGC,IAAK;AAAA,UACR,YAAW;AAAA,UACX,kBAAiB;AAAA,UACjB,OAAO,EAAE,UAAU,IAAI,MAAM,0BAAA;AAAA,UAE5B,UAAAE;AAAA,QAAA;AAAA,MAAA;AAAA,MAEH,gBAAAhB;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,GAAGa;AAAA,UACH,GAAGC,IAAK;AAAA,UACR,YAAW;AAAA,UACX,kBAAiB;AAAA,UACjB,OAAO,EAAE,UAAU,IAAI,YAAY,KAAK,MAAMP,EAAK,MAAA;AAAA,UAElD,UAAAF,EAAYE,EAAK,KAAK;AAAA,QAAA;AAAA,MAAA;AAAA,MAEzB,gBAAAR;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,GAAGc;AAAA,UACH,GAAGC,IAAK;AAAA,UACR,YAAW;AAAA,UACX,kBAAiB;AAAA,UACjB,OAAO,EAAE,UAAU,IAAI,MAAM,0BAAA;AAAA,UAE5B,UAAA;AAAA,YAAAN;AAAA,YAAI;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IACP,GACF;AAAA,EAEJ;AAEA,2BACG,KAAA,EACC,UAAA;AAAA,IAAA,gBAAAR;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,GAAGa;AAAA,QACH,GAAGC,IAAK;AAAA,QACR,YAAW;AAAA,QACX,kBAAiB;AAAA,QACjB,OAAO,EAAE,UAAU,IAAI,MAAM,0BAAA;AAAA,QAC9B,UAAA;AAAA,MAAA;AAAA,IAAA;AAAA,IAGD,gBAAAd;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,GAAGa;AAAA,QACH,GAAGC,IAAK;AAAA,QACR,YAAW;AAAA,QACX,kBAAiB;AAAA,QACjB,OAAO,EAAE,UAAU,IAAI,YAAY,KAAK,MAAM,eAAA;AAAA,QAE7C,YAAYR,CAAK;AAAA,MAAA;AAAA,IAAA;AAAA,EACpB,GACF;AAEJ;AAIA,SAASW,EAAY;AAAA,EACnB,WAAAL;AAAA,EACA,OAAAN;AAAA,EACA,aAAAD;AAAA,EACA,aAAAM;AAAA,EACA,SAAAO;AACF,GAMG;AACD,SACE,gBAAAnB,EAAC,OAAA,EAAI,WAAU,yBACZ,UAAA;AAAA,IAAAa,EAAU,IAAI,CAACL,GAAMf,MAAM;AAC1B,YAAMgB,IAAMF,IAAQ,KAAMC,EAAK,QAAQD,IAAS,KAAK,QAAQ,CAAC,IAAI,OAC5Da,IAAWR,MAAgBnB;AAEjC,aACE,gBAAAO;AAAA,QAAC;AAAA,QAAA;AAAA,UAEC,WAAU;AAAA,UACV,OAAO,EAAE,SALIY,MAAgB,QAAQ,CAACQ,IAKT,MAAM,EAAA;AAAA,UACnC,cAAc,MAAMD,EAAQ1B,CAAC;AAAA,UAC7B,cAAc,MAAM0B,EAAQ,IAAI;AAAA,UAEhC,UAAA;AAAA,YAAA,gBAAAlB;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,WAAU;AAAA,gBACV,OAAO;AAAA,kBACL,YAAYO,EAAK;AAAA,kBACjB,WAAWY,IAAW,eAAe;AAAA,gBAAA;AAAA,cACvC;AAAA,YAAA;AAAA,YAEF,gBAAAnB,EAAC,QAAA,EAAK,WAAU,yCAAyC,YAAK,MAAK;AAAA,8BAClE,QAAA,EAAK,WAAU,6BAA6B,UAAAK,EAAYE,EAAK,KAAK,GAAE;AAAA,YACrE,gBAAAR,EAAC,QAAA,EAAK,WAAU,2CAA2C,UAAA;AAAA,cAAAS;AAAA,cAAI;AAAA,YAAA,EAAA,CAAC;AAAA,UAAA;AAAA,QAAA;AAAA,QAf3DhB;AAAA,MAAA;AAAA,IAkBX,CAAC;AAAA,IACD,gBAAAO,EAAC,OAAA,EAAI,WAAU,wEACb,UAAA;AAAA,MAAA,gBAAAC,EAAC,QAAA,EAAK,WAAU,sBAAqB,UAAA,SAAK;AAAA,wBACzC,QAAA,EAAK,WAAU,2BAA2B,UAAAK,EAAYC,CAAK,EAAA,CAAE;AAAA,IAAA,EAAA,CAChE;AAAA,EAAA,GACF;AAEJ;AAIO,SAASc,EAAW;AAAA,EACzB,MAAAC;AAAA,EACA,OAAAC;AAAA,EACA,aAAAjB,IAAcZ;AAAA,EACd,WAAA8B;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,QAAA5B,IAAS;AAAA,EACT,aAAA6B,IAAc;AAAA,EACd,WAAWC;AACb,GAAoB;AAClB,QAAM,CAACf,GAAagB,CAAc,IAAIC,EAAM,SAAwB,IAAI,GAElEtB,IAAQsB,EAAM,QAAQ,MAAMP,EAAK,OAAO,CAACQ,GAAKC,MAAMD,IAAMC,EAAE,OAAO,CAAC,GAAG,CAACT,CAAI,CAAC,GAE7ET,IAAYgB,EAAM;AAAA,IACtB,MACEP,EAAK,IAAI,CAACd,GAAMf,OAAO;AAAA,MACrB,MAAMe,EAAK;AAAA,MACX,OAAOA,EAAK;AAAA,MACZ,OAAOA,EAAK,SAASjB,EAAeE,IAAIF,EAAe,MAAM;AAAA,IAAA,EAC7D;AAAA,IACJ,CAAC+B,CAAI;AAAA,EAAA,GAGDU,IAAenC,MAAW,cAC1BC,IAAY6B,MAAkBK,IAAe,MAAM,MACnDC,IAAcD,IAAe,KAAK,MAAMlC,IAAY,IAAI,IAAI,IAC5DoC,IAAcF,IAAe,KAAK,MAAMlC,IAAY,IAAI,IAAI;AAElE,2BACG,OAAA,EAAI,WAAWqC,EAAG,8BAA8BX,CAAS,GACvD,UAAA;AAAA,IAAAD,KAAS,gBAAAtB,EAAC,OAAA,EAAI,WAAU,yCAAyC,UAAAsB,GAAM;AAAA,IAEvEE,IACC,gBAAAxB,EAACL,GAAA,EAAc,QAAAC,GAAgB,WAAAC,EAAA,CAAsB,IACnDwB,EAAK,WAAW,sBACjBpB,GAAA,CAAA,CAAW,sBAEX,OAAA,EAAI,WAAWiC,EAAGH,IAAe,4BAA4B,qBAAqB,GAEjF,UAAA;AAAA,MAAA,gBAAA/B;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,WAAU;AAAA,UACV,OACE+B,IACI,EAAE,OAAOlC,GAAW,QAAQA,EAAA,IAC5B,EAAE,OAAO,QAAQ,QAAQA,EAAA;AAAA,UAG/B,4BAACsC,GAAA,EAAoB,OAAM,QAAO,QAAO,QACvC,4BAACC,GAAA,EACC,UAAA;AAAA,YAAA,gBAAApC;AAAA,cAACqC;AAAA,cAAA;AAAA,gBACC,MAAMzB;AAAA,gBACN,IAAG;AAAA,gBACH,IAAG;AAAA,gBACH,aAAAoB;AAAA,gBACA,aAAAC;AAAA,gBACA,cAAc;AAAA,gBACd,SAAQ;AAAA,gBACR,SAAQ;AAAA,gBACR,cAAc,CAAC1C,GAAG+C,MAAUX,EAAeW,CAAK;AAAA,gBAChD,cAAc,MAAMX,EAAe,IAAI;AAAA,gBACvC,OAAO,CAACY,GAAOD;;AACb,yCAAAtC;AAAA,oBAACwC;AAAA,oBAAA;AAAA,sBACE,GAAGD;AAAA,sBACJ,OAAME,IAAA7B,EAAU0B,CAAK,MAAf,gBAAAG,EAAkB;AAAA,sBACxB,QAAO;AAAA,sBACP,SAAS9B,MAAgB,QAAQA,MAAgB2B,IAAQ,MAAM;AAAA,oBAAA;AAAA,kBAAA;AAAA;AAAA,gBAIlE,UAAAb,KACC,gBAAAzB;AAAA,kBAAC0C;AAAA,kBAAA;AAAA,oBACC,SACE,gBAAA1C;AAAA,sBAACS;AAAA,sBAAA;AAAA,wBACC,aAAAE;AAAA,wBACA,WAAAC;AAAA,wBACA,aAAAP;AAAA,wBACA,OAAAC;AAAA,sBAAA;AAAA,oBAAA;AAAA,kBACF;AAAA,gBAAA;AAAA,cAEJ;AAAA,YAAA;AAAA,YAGH,CAACmB,KACA,gBAAAzB;AAAA,cAAC2C;AAAA,cAAA;AAAA,gBACC,SAAS,gBAAA3C,EAACE,GAAA,EAAc,aAAAG,GAA0B,OAAAC,EAAA,CAAc;AAAA,cAAA;AAAA,YAAA;AAAA,UAClE,EAAA,CAEJ,EAAA,CACF;AAAA,QAAA;AAAA,MAAA;AAAA,wBAID,OAAA,EAAI,WAAW4B,EAAGH,IAAe,mBAAmB,EAAE,GACrD,UAAA,gBAAA/B;AAAA,QAACiB;AAAA,QAAA;AAAA,UACC,WAAAL;AAAA,UACA,OAAAN;AAAA,UACA,aAAAD;AAAA,UACA,aAAAM;AAAA,UACA,SAASgB;AAAA,QAAA;AAAA,MAAA,EACX,CACF;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GAEJ;AAEJ;"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),
|
|
1
|
+
"use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),f=require("react"),n=require("../layout/card.cjs"),N=require("../primitives/badge.cjs"),l=require("../primitives/skeleton.cjs"),i=require("lucide-react"),m=require("../utils.cjs");function b(s){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(s){for(const t in s)if(t!=="default"){const a=Object.getOwnPropertyDescriptor(s,t);Object.defineProperty(r,t,a.get?a:{enumerable:!0,get:()=>s[t]})}}return r.default=s,Object.freeze(r)}const g=b(f),u=g.forwardRef(({title:s,primaryContent:r,secondaryContent:t,trendValue:a,trendLabel:x,icon:h=i.Activity,isLoading:c=!1,onClick:o,className:j},p)=>{const d=!c&&!!o;return e.jsxs(n.Card,{ref:p,onClick:d?o:void 0,className:m.cn(d&&"cursor-pointer hover:bg-accent/50 transition-colors",j),children:[e.jsx(n.CardHeader,{className:"pb-1",children:e.jsx(n.CardTitle,{className:"flex items-center gap-2 text-sm text-muted-foreground",children:c?e.jsxs(e.Fragment,{children:[e.jsx(l.Skeleton,{className:"size-4 shrink-0 rounded"}),e.jsx(l.Skeleton,{className:"h-4 w-24"})]}):e.jsxs(e.Fragment,{children:[e.jsx(h,{className:"size-4 shrink-0 text-muted-foreground"}),e.jsx("span",{className:"truncate",children:s})]})})}),e.jsxs(n.CardContent,{className:"space-y-1 pt-0",children:[e.jsxs("div",{className:"flex items-baseline gap-2",children:[c?e.jsx(l.Skeleton,{className:"h-8 w-28"}):e.jsx("span",{className:"min-w-0 truncate text-2xl font-bold",title:r,children:r}),!c&&a!==void 0&&e.jsxs(N.Badge,{variant:"secondary",className:m.cn("h-auto shrink-0 px-1.5 py-0 text-xs",a>0?"text-success-emphasis":"text-destructive-emphasis"),children:[a>0?e.jsx(i.TrendingUp,{className:"mr-0.5 inline h-3 w-3"}):e.jsx(i.TrendingDown,{className:"mr-0.5 inline h-3 w-3"}),x]})]}),e.jsx("div",{className:"min-h-4 text-xs text-muted-foreground",children:c?e.jsx(l.Skeleton,{className:"h-3 w-full max-w-40"}):t&&e.jsx("span",{className:"block truncate",children:t})})]})]})});u.displayName="KPICard";exports.KPICard=u;
|
|
2
2
|
//# sourceMappingURL=kpi-card.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kpi-card.cjs","sources":["../../../../src/components/ui/charts/kpi-card.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../layout/card\";\nimport { Badge } from \"../primitives/badge\";\nimport { Skeleton } from \"../primitives/skeleton\";\nimport { TrendingUp, TrendingDown, Activity } from \"lucide-react\";\nimport { cn } from \"../utils\";\n\nexport interface KPICardProps {\n /**\n * Título do KPI (ex: \"Receita Mensal\"). Fica em uma linha (trunca com \"…\").\n */\n title: string;\n\n /**\n * Valor principal em destaque (ex: \"R$ 127.361,23\"). Fica em uma linha\n * (trunca com \"…\"; o valor completo aparece no `title` nativo ao passar o mouse).\n */\n primaryContent: string;\n\n /**\n * Texto secundário descritivo (ex: \"vs. mês anterior\"). Uma linha.\n */\n secondaryContent?: string;\n\n /**\n * Valor de tendência para exibir badge com seta\n * Positivo = verde + seta para cima\n * Negativo = vermelho + seta para baixo\n */\n trendValue?: number;\n\n /**\n * Label customizado para tendência (default: \"+X%\" ou \"-X%\")\n */\n trendLabel?: string;\n\n /**\n * Ícone a ser exibido no header (componente Lucide React)\n */\n icon?: React.ComponentType<{ className?: string }>;\n\n /**\n * Estado de loading - exibe skeleton com a mesma altura do estado final.\n */\n isLoading?: boolean;\n\n /**\n * Handler de click - torna o card clicável com hover\n */\n onClick?: () => void;\n\n /**\n * Classes CSS adicionais\n */\n className?: string;\n}\n\n/**\n * KPICard — card de métrica com título, valor em destaque, tendência e texto\n * secundário. O estado de `isLoading` reserva exatamente a mesma altura do\n * estado final (mesma árvore de layout, só troca o conteúdo por `Skeleton`),\n * então não há salto quando os dados chegam. Título, valor e texto secundário\n * ficam sempre em uma linha.\n */\nexport const KPICard = React.forwardRef<HTMLDivElement, KPICardProps>(\n (\n {\n title,\n primaryContent,\n secondaryContent,\n trendValue,\n trendLabel,\n icon: Icon = Activity,\n isLoading = false,\n onClick,\n className,\n },\n ref\n ) => {\n const clickable = !isLoading && !!onClick;\n\n return (\n <Card\n ref={ref}\n onClick={clickable ? onClick : undefined}\n className={cn(\n clickable && \"cursor-pointer hover:bg-accent/50 transition-colors\",\n className\n )}\n >\n <CardHeader className=\"pb-1\">\n <CardTitle className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n {isLoading ? (\n <>\n <Skeleton className=\"size-4 shrink-0 rounded\" />\n <Skeleton className=\"h-4 w-24\" />\n </>\n ) : (\n <>\n <Icon className=\"size-4 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{title}</span>\n </>\n )}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-1 pt-0\">\n <div className=\"flex items-baseline gap-2\">\n {isLoading ? (\n <Skeleton className=\"h-8 w-28\" />\n ) : (\n <span\n className=\"min-w-0 truncate text-2xl font-bold\"\n title={primaryContent}\n >\n {primaryContent}\n </span>\n )}\n {!isLoading && trendValue !== undefined && (\n <Badge\n variant=\"secondary\"\n className={cn(\n \"h-auto shrink-0 px-1.5 py-0 text-xs\",\n trendValue > 0 ? \"text-
|
|
1
|
+
{"version":3,"file":"kpi-card.cjs","sources":["../../../../src/components/ui/charts/kpi-card.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../layout/card\";\nimport { Badge } from \"../primitives/badge\";\nimport { Skeleton } from \"../primitives/skeleton\";\nimport { TrendingUp, TrendingDown, Activity } from \"lucide-react\";\nimport { cn } from \"../utils\";\n\nexport interface KPICardProps {\n /**\n * Título do KPI (ex: \"Receita Mensal\"). Fica em uma linha (trunca com \"…\").\n */\n title: string;\n\n /**\n * Valor principal em destaque (ex: \"R$ 127.361,23\"). Fica em uma linha\n * (trunca com \"…\"; o valor completo aparece no `title` nativo ao passar o mouse).\n */\n primaryContent: string;\n\n /**\n * Texto secundário descritivo (ex: \"vs. mês anterior\"). Uma linha.\n */\n secondaryContent?: string;\n\n /**\n * Valor de tendência para exibir badge com seta\n * Positivo = verde + seta para cima\n * Negativo = vermelho + seta para baixo\n */\n trendValue?: number;\n\n /**\n * Label customizado para tendência (default: \"+X%\" ou \"-X%\")\n */\n trendLabel?: string;\n\n /**\n * Ícone a ser exibido no header (componente Lucide React)\n */\n icon?: React.ComponentType<{ className?: string }>;\n\n /**\n * Estado de loading - exibe skeleton com a mesma altura do estado final.\n */\n isLoading?: boolean;\n\n /**\n * Handler de click - torna o card clicável com hover\n */\n onClick?: () => void;\n\n /**\n * Classes CSS adicionais\n */\n className?: string;\n}\n\n/**\n * KPICard — card de métrica com título, valor em destaque, tendência e texto\n * secundário. O estado de `isLoading` reserva exatamente a mesma altura do\n * estado final (mesma árvore de layout, só troca o conteúdo por `Skeleton`),\n * então não há salto quando os dados chegam. Título, valor e texto secundário\n * ficam sempre em uma linha.\n */\nexport const KPICard = React.forwardRef<HTMLDivElement, KPICardProps>(\n (\n {\n title,\n primaryContent,\n secondaryContent,\n trendValue,\n trendLabel,\n icon: Icon = Activity,\n isLoading = false,\n onClick,\n className,\n },\n ref\n ) => {\n const clickable = !isLoading && !!onClick;\n\n return (\n <Card\n ref={ref}\n onClick={clickable ? onClick : undefined}\n className={cn(\n clickable && \"cursor-pointer hover:bg-accent/50 transition-colors\",\n className\n )}\n >\n <CardHeader className=\"pb-1\">\n <CardTitle className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n {isLoading ? (\n <>\n <Skeleton className=\"size-4 shrink-0 rounded\" />\n <Skeleton className=\"h-4 w-24\" />\n </>\n ) : (\n <>\n <Icon className=\"size-4 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{title}</span>\n </>\n )}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-1 pt-0\">\n <div className=\"flex items-baseline gap-2\">\n {isLoading ? (\n <Skeleton className=\"h-8 w-28\" />\n ) : (\n <span\n className=\"min-w-0 truncate text-2xl font-bold\"\n title={primaryContent}\n >\n {primaryContent}\n </span>\n )}\n {!isLoading && trendValue !== undefined && (\n <Badge\n variant=\"secondary\"\n className={cn(\n \"h-auto shrink-0 px-1.5 py-0 text-xs\",\n trendValue > 0 ? \"text-success-emphasis\" : \"text-destructive-emphasis\"\n )}\n >\n {trendValue > 0 ? (\n <TrendingUp className=\"mr-0.5 inline h-3 w-3\" />\n ) : (\n <TrendingDown className=\"mr-0.5 inline h-3 w-3\" />\n )}\n {trendLabel}\n </Badge>\n )}\n </div>\n <div className=\"min-h-4 text-xs text-muted-foreground\">\n {isLoading ? (\n <Skeleton className=\"h-3 w-full max-w-40\" />\n ) : (\n secondaryContent && (\n <span className=\"block truncate\">{secondaryContent}</span>\n )\n )}\n </div>\n </CardContent>\n </Card>\n );\n }\n);\n\nKPICard.displayName = \"KPICard\";\n"],"names":["KPICard","React","title","primaryContent","secondaryContent","trendValue","trendLabel","Icon","Activity","isLoading","onClick","className","ref","clickable","jsxs","Card","cn","jsx","CardHeader","CardTitle","Fragment","Skeleton","CardContent","Badge","TrendingUp","TrendingDown"],"mappings":"glBAkEaA,EAAUC,EAAM,WAC3B,CACE,CACE,MAAAC,EACA,eAAAC,EACA,iBAAAC,EACA,WAAAC,EACA,WAAAC,EACA,KAAMC,EAAOC,EAAAA,SACb,UAAAC,EAAY,GACZ,QAAAC,EACA,UAAAC,CAAA,EAEFC,IACG,CACH,MAAMC,EAAY,CAACJ,GAAa,CAAC,CAACC,EAElC,OACEI,EAAAA,KAACC,EAAAA,KAAA,CACC,IAAAH,EACA,QAASC,EAAYH,EAAU,OAC/B,UAAWM,EAAAA,GACTH,GAAa,sDACbF,CAAA,EAGF,SAAA,CAAAM,EAAAA,IAACC,EAAAA,WAAA,CAAW,UAAU,OACpB,SAAAD,EAAAA,IAACE,EAAAA,WAAU,UAAU,wDAClB,WACCL,EAAAA,KAAAM,EAAAA,SAAA,CACE,SAAA,CAAAH,EAAAA,IAACI,EAAAA,SAAA,CAAS,UAAU,yBAAA,CAA0B,EAC9CJ,EAAAA,IAACI,EAAAA,SAAA,CAAS,UAAU,UAAA,CAAW,CAAA,CAAA,CACjC,EAEAP,EAAAA,KAAAM,EAAAA,SAAA,CACE,SAAA,CAAAH,EAAAA,IAACV,EAAA,CAAK,UAAU,uCAAA,CAAwC,EACxDU,EAAAA,IAAC,OAAA,CAAK,UAAU,WAAY,SAAAf,CAAA,CAAM,CAAA,CAAA,CACpC,EAEJ,EACF,EACAY,EAAAA,KAACQ,EAAAA,YAAA,CAAY,UAAU,iBACrB,SAAA,CAAAR,EAAAA,KAAC,MAAA,CAAI,UAAU,4BACZ,SAAA,CAAAL,EACCQ,EAAAA,IAACI,EAAAA,SAAA,CAAS,UAAU,UAAA,CAAW,EAE/BJ,EAAAA,IAAC,OAAA,CACC,UAAU,sCACV,MAAOd,EAEN,SAAAA,CAAA,CAAA,EAGJ,CAACM,GAAaJ,IAAe,QAC5BS,EAAAA,KAACS,EAAAA,MAAA,CACC,QAAQ,YACR,UAAWP,EAAAA,GACT,sCACAX,EAAa,EAAI,wBAA0B,2BAAA,EAG5C,SAAA,CAAAA,EAAa,QACXmB,EAAAA,WAAA,CAAW,UAAU,wBAAwB,EAE9CP,EAAAA,IAACQ,EAAAA,aAAA,CAAa,UAAU,uBAAA,CAAwB,EAEjDnB,CAAA,CAAA,CAAA,CACH,EAEJ,QACC,MAAA,CAAI,UAAU,wCACZ,SAAAG,QACEY,EAAAA,SAAA,CAAS,UAAU,qBAAA,CAAsB,EAE1CjB,GACEa,EAAAA,IAAC,OAAA,CAAK,UAAU,iBAAkB,WAAiB,CAAA,CAGzD,CAAA,CAAA,CACF,CAAA,CAAA,CAAA,CAGN,CACF,EAEAjB,EAAQ,YAAc"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsxs as
|
|
2
|
+
import { jsxs as s, jsx as e, Fragment as n } from "react/jsx-runtime";
|
|
3
3
|
import * as f from "react";
|
|
4
|
-
import { Card as u, CardHeader as w, CardTitle as
|
|
5
|
-
import { Badge as
|
|
6
|
-
import { Skeleton as
|
|
4
|
+
import { Card as u, CardHeader as w, CardTitle as v, CardContent as b } from "../layout/card.js";
|
|
5
|
+
import { Badge as g } from "../primitives/badge.js";
|
|
6
|
+
import { Skeleton as r } from "../primitives/skeleton.js";
|
|
7
7
|
import { Activity as k, TrendingUp as C, TrendingDown as y } from "lucide-react";
|
|
8
8
|
import { cn as o } from "../utils.js";
|
|
9
9
|
const T = f.forwardRef(
|
|
@@ -15,30 +15,30 @@ const T = f.forwardRef(
|
|
|
15
15
|
trendLabel: h,
|
|
16
16
|
icon: p = k,
|
|
17
17
|
isLoading: a = !1,
|
|
18
|
-
onClick:
|
|
18
|
+
onClick: i,
|
|
19
19
|
className: x
|
|
20
20
|
}, N) => {
|
|
21
|
-
const m = !a && !!
|
|
22
|
-
return /* @__PURE__ */
|
|
21
|
+
const m = !a && !!i;
|
|
22
|
+
return /* @__PURE__ */ s(
|
|
23
23
|
u,
|
|
24
24
|
{
|
|
25
25
|
ref: N,
|
|
26
|
-
onClick: m ?
|
|
26
|
+
onClick: m ? i : void 0,
|
|
27
27
|
className: o(
|
|
28
28
|
m && "cursor-pointer hover:bg-accent/50 transition-colors",
|
|
29
29
|
x
|
|
30
30
|
),
|
|
31
31
|
children: [
|
|
32
|
-
/* @__PURE__ */ e(w, { className: "pb-1", children: /* @__PURE__ */ e(
|
|
33
|
-
/* @__PURE__ */ e(
|
|
34
|
-
/* @__PURE__ */ e(
|
|
35
|
-
] }) : /* @__PURE__ */
|
|
32
|
+
/* @__PURE__ */ e(w, { className: "pb-1", children: /* @__PURE__ */ e(v, { className: "flex items-center gap-2 text-sm text-muted-foreground", children: a ? /* @__PURE__ */ s(n, { children: [
|
|
33
|
+
/* @__PURE__ */ e(r, { className: "size-4 shrink-0 rounded" }),
|
|
34
|
+
/* @__PURE__ */ e(r, { className: "h-4 w-24" })
|
|
35
|
+
] }) : /* @__PURE__ */ s(n, { children: [
|
|
36
36
|
/* @__PURE__ */ e(p, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
37
37
|
/* @__PURE__ */ e("span", { className: "truncate", children: d })
|
|
38
38
|
] }) }) }),
|
|
39
|
-
/* @__PURE__ */
|
|
40
|
-
/* @__PURE__ */
|
|
41
|
-
a ? /* @__PURE__ */ e(
|
|
39
|
+
/* @__PURE__ */ s(b, { className: "space-y-1 pt-0", children: [
|
|
40
|
+
/* @__PURE__ */ s("div", { className: "flex items-baseline gap-2", children: [
|
|
41
|
+
a ? /* @__PURE__ */ e(r, { className: "h-8 w-28" }) : /* @__PURE__ */ e(
|
|
42
42
|
"span",
|
|
43
43
|
{
|
|
44
44
|
className: "min-w-0 truncate text-2xl font-bold",
|
|
@@ -46,13 +46,13 @@ const T = f.forwardRef(
|
|
|
46
46
|
children: c
|
|
47
47
|
}
|
|
48
48
|
),
|
|
49
|
-
!a && t !== void 0 && /* @__PURE__ */
|
|
50
|
-
|
|
49
|
+
!a && t !== void 0 && /* @__PURE__ */ s(
|
|
50
|
+
g,
|
|
51
51
|
{
|
|
52
52
|
variant: "secondary",
|
|
53
53
|
className: o(
|
|
54
54
|
"h-auto shrink-0 px-1.5 py-0 text-xs",
|
|
55
|
-
t > 0 ? "text-
|
|
55
|
+
t > 0 ? "text-success-emphasis" : "text-destructive-emphasis"
|
|
56
56
|
),
|
|
57
57
|
children: [
|
|
58
58
|
t > 0 ? /* @__PURE__ */ e(C, { className: "mr-0.5 inline h-3 w-3" }) : /* @__PURE__ */ e(y, { className: "mr-0.5 inline h-3 w-3" }),
|
|
@@ -61,7 +61,7 @@ const T = f.forwardRef(
|
|
|
61
61
|
}
|
|
62
62
|
)
|
|
63
63
|
] }),
|
|
64
|
-
/* @__PURE__ */ e("div", { className: "min-h-4 text-xs text-muted-foreground", children: a ? /* @__PURE__ */ e(
|
|
64
|
+
/* @__PURE__ */ e("div", { className: "min-h-4 text-xs text-muted-foreground", children: a ? /* @__PURE__ */ e(r, { className: "h-3 w-full max-w-40" }) : l && /* @__PURE__ */ e("span", { className: "block truncate", children: l }) })
|
|
65
65
|
] })
|
|
66
66
|
]
|
|
67
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kpi-card.js","sources":["../../../../src/components/ui/charts/kpi-card.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../layout/card\";\nimport { Badge } from \"../primitives/badge\";\nimport { Skeleton } from \"../primitives/skeleton\";\nimport { TrendingUp, TrendingDown, Activity } from \"lucide-react\";\nimport { cn } from \"../utils\";\n\nexport interface KPICardProps {\n /**\n * Título do KPI (ex: \"Receita Mensal\"). Fica em uma linha (trunca com \"…\").\n */\n title: string;\n\n /**\n * Valor principal em destaque (ex: \"R$ 127.361,23\"). Fica em uma linha\n * (trunca com \"…\"; o valor completo aparece no `title` nativo ao passar o mouse).\n */\n primaryContent: string;\n\n /**\n * Texto secundário descritivo (ex: \"vs. mês anterior\"). Uma linha.\n */\n secondaryContent?: string;\n\n /**\n * Valor de tendência para exibir badge com seta\n * Positivo = verde + seta para cima\n * Negativo = vermelho + seta para baixo\n */\n trendValue?: number;\n\n /**\n * Label customizado para tendência (default: \"+X%\" ou \"-X%\")\n */\n trendLabel?: string;\n\n /**\n * Ícone a ser exibido no header (componente Lucide React)\n */\n icon?: React.ComponentType<{ className?: string }>;\n\n /**\n * Estado de loading - exibe skeleton com a mesma altura do estado final.\n */\n isLoading?: boolean;\n\n /**\n * Handler de click - torna o card clicável com hover\n */\n onClick?: () => void;\n\n /**\n * Classes CSS adicionais\n */\n className?: string;\n}\n\n/**\n * KPICard — card de métrica com título, valor em destaque, tendência e texto\n * secundário. O estado de `isLoading` reserva exatamente a mesma altura do\n * estado final (mesma árvore de layout, só troca o conteúdo por `Skeleton`),\n * então não há salto quando os dados chegam. Título, valor e texto secundário\n * ficam sempre em uma linha.\n */\nexport const KPICard = React.forwardRef<HTMLDivElement, KPICardProps>(\n (\n {\n title,\n primaryContent,\n secondaryContent,\n trendValue,\n trendLabel,\n icon: Icon = Activity,\n isLoading = false,\n onClick,\n className,\n },\n ref\n ) => {\n const clickable = !isLoading && !!onClick;\n\n return (\n <Card\n ref={ref}\n onClick={clickable ? onClick : undefined}\n className={cn(\n clickable && \"cursor-pointer hover:bg-accent/50 transition-colors\",\n className\n )}\n >\n <CardHeader className=\"pb-1\">\n <CardTitle className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n {isLoading ? (\n <>\n <Skeleton className=\"size-4 shrink-0 rounded\" />\n <Skeleton className=\"h-4 w-24\" />\n </>\n ) : (\n <>\n <Icon className=\"size-4 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{title}</span>\n </>\n )}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-1 pt-0\">\n <div className=\"flex items-baseline gap-2\">\n {isLoading ? (\n <Skeleton className=\"h-8 w-28\" />\n ) : (\n <span\n className=\"min-w-0 truncate text-2xl font-bold\"\n title={primaryContent}\n >\n {primaryContent}\n </span>\n )}\n {!isLoading && trendValue !== undefined && (\n <Badge\n variant=\"secondary\"\n className={cn(\n \"h-auto shrink-0 px-1.5 py-0 text-xs\",\n trendValue > 0 ? \"text-
|
|
1
|
+
{"version":3,"file":"kpi-card.js","sources":["../../../../src/components/ui/charts/kpi-card.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../layout/card\";\nimport { Badge } from \"../primitives/badge\";\nimport { Skeleton } from \"../primitives/skeleton\";\nimport { TrendingUp, TrendingDown, Activity } from \"lucide-react\";\nimport { cn } from \"../utils\";\n\nexport interface KPICardProps {\n /**\n * Título do KPI (ex: \"Receita Mensal\"). Fica em uma linha (trunca com \"…\").\n */\n title: string;\n\n /**\n * Valor principal em destaque (ex: \"R$ 127.361,23\"). Fica em uma linha\n * (trunca com \"…\"; o valor completo aparece no `title` nativo ao passar o mouse).\n */\n primaryContent: string;\n\n /**\n * Texto secundário descritivo (ex: \"vs. mês anterior\"). Uma linha.\n */\n secondaryContent?: string;\n\n /**\n * Valor de tendência para exibir badge com seta\n * Positivo = verde + seta para cima\n * Negativo = vermelho + seta para baixo\n */\n trendValue?: number;\n\n /**\n * Label customizado para tendência (default: \"+X%\" ou \"-X%\")\n */\n trendLabel?: string;\n\n /**\n * Ícone a ser exibido no header (componente Lucide React)\n */\n icon?: React.ComponentType<{ className?: string }>;\n\n /**\n * Estado de loading - exibe skeleton com a mesma altura do estado final.\n */\n isLoading?: boolean;\n\n /**\n * Handler de click - torna o card clicável com hover\n */\n onClick?: () => void;\n\n /**\n * Classes CSS adicionais\n */\n className?: string;\n}\n\n/**\n * KPICard — card de métrica com título, valor em destaque, tendência e texto\n * secundário. O estado de `isLoading` reserva exatamente a mesma altura do\n * estado final (mesma árvore de layout, só troca o conteúdo por `Skeleton`),\n * então não há salto quando os dados chegam. Título, valor e texto secundário\n * ficam sempre em uma linha.\n */\nexport const KPICard = React.forwardRef<HTMLDivElement, KPICardProps>(\n (\n {\n title,\n primaryContent,\n secondaryContent,\n trendValue,\n trendLabel,\n icon: Icon = Activity,\n isLoading = false,\n onClick,\n className,\n },\n ref\n ) => {\n const clickable = !isLoading && !!onClick;\n\n return (\n <Card\n ref={ref}\n onClick={clickable ? onClick : undefined}\n className={cn(\n clickable && \"cursor-pointer hover:bg-accent/50 transition-colors\",\n className\n )}\n >\n <CardHeader className=\"pb-1\">\n <CardTitle className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n {isLoading ? (\n <>\n <Skeleton className=\"size-4 shrink-0 rounded\" />\n <Skeleton className=\"h-4 w-24\" />\n </>\n ) : (\n <>\n <Icon className=\"size-4 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{title}</span>\n </>\n )}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-1 pt-0\">\n <div className=\"flex items-baseline gap-2\">\n {isLoading ? (\n <Skeleton className=\"h-8 w-28\" />\n ) : (\n <span\n className=\"min-w-0 truncate text-2xl font-bold\"\n title={primaryContent}\n >\n {primaryContent}\n </span>\n )}\n {!isLoading && trendValue !== undefined && (\n <Badge\n variant=\"secondary\"\n className={cn(\n \"h-auto shrink-0 px-1.5 py-0 text-xs\",\n trendValue > 0 ? \"text-success-emphasis\" : \"text-destructive-emphasis\"\n )}\n >\n {trendValue > 0 ? (\n <TrendingUp className=\"mr-0.5 inline h-3 w-3\" />\n ) : (\n <TrendingDown className=\"mr-0.5 inline h-3 w-3\" />\n )}\n {trendLabel}\n </Badge>\n )}\n </div>\n <div className=\"min-h-4 text-xs text-muted-foreground\">\n {isLoading ? (\n <Skeleton className=\"h-3 w-full max-w-40\" />\n ) : (\n secondaryContent && (\n <span className=\"block truncate\">{secondaryContent}</span>\n )\n )}\n </div>\n </CardContent>\n </Card>\n );\n }\n);\n\nKPICard.displayName = \"KPICard\";\n"],"names":["KPICard","React","title","primaryContent","secondaryContent","trendValue","trendLabel","Icon","Activity","isLoading","onClick","className","ref","clickable","jsxs","Card","cn","jsx","CardHeader","CardTitle","Fragment","Skeleton","CardContent","Badge","TrendingUp","TrendingDown"],"mappings":";;;;;;;;AAkEO,MAAMA,IAAUC,EAAM;AAAA,EAC3B,CACE;AAAA,IACE,OAAAC;AAAA,IACA,gBAAAC;AAAA,IACA,kBAAAC;AAAA,IACA,YAAAC;AAAA,IACA,YAAAC;AAAA,IACA,MAAMC,IAAOC;AAAA,IACb,WAAAC,IAAY;AAAA,IACZ,SAAAC;AAAA,IACA,WAAAC;AAAA,EAAA,GAEFC,MACG;AACH,UAAMC,IAAY,CAACJ,KAAa,CAAC,CAACC;AAElC,WACE,gBAAAI;AAAA,MAACC;AAAA,MAAA;AAAA,QACC,KAAAH;AAAA,QACA,SAASC,IAAYH,IAAU;AAAA,QAC/B,WAAWM;AAAA,UACTH,KAAa;AAAA,UACbF;AAAA,QAAA;AAAA,QAGF,UAAA;AAAA,UAAA,gBAAAM,EAACC,GAAA,EAAW,WAAU,QACpB,UAAA,gBAAAD,EAACE,KAAU,WAAU,yDAClB,cACC,gBAAAL,EAAAM,GAAA,EACE,UAAA;AAAA,YAAA,gBAAAH,EAACI,GAAA,EAAS,WAAU,0BAAA,CAA0B;AAAA,YAC9C,gBAAAJ,EAACI,GAAA,EAAS,WAAU,WAAA,CAAW;AAAA,UAAA,EAAA,CACjC,IAEA,gBAAAP,EAAAM,GAAA,EACE,UAAA;AAAA,YAAA,gBAAAH,EAACV,GAAA,EAAK,WAAU,wCAAA,CAAwC;AAAA,YACxD,gBAAAU,EAAC,QAAA,EAAK,WAAU,YAAY,UAAAf,EAAA,CAAM;AAAA,UAAA,EAAA,CACpC,GAEJ,GACF;AAAA,UACA,gBAAAY,EAACQ,GAAA,EAAY,WAAU,kBACrB,UAAA;AAAA,YAAA,gBAAAR,EAAC,OAAA,EAAI,WAAU,6BACZ,UAAA;AAAA,cAAAL,IACC,gBAAAQ,EAACI,GAAA,EAAS,WAAU,WAAA,CAAW,IAE/B,gBAAAJ;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,WAAU;AAAA,kBACV,OAAOd;AAAA,kBAEN,UAAAA;AAAA,gBAAA;AAAA,cAAA;AAAA,cAGJ,CAACM,KAAaJ,MAAe,UAC5B,gBAAAS;AAAA,gBAACS;AAAA,gBAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,WAAWP;AAAA,oBACT;AAAA,oBACAX,IAAa,IAAI,0BAA0B;AAAA,kBAAA;AAAA,kBAG5C,UAAA;AAAA,oBAAAA,IAAa,sBACXmB,GAAA,EAAW,WAAU,yBAAwB,IAE9C,gBAAAP,EAACQ,GAAA,EAAa,WAAU,wBAAA,CAAwB;AAAA,oBAEjDnB;AAAA,kBAAA;AAAA,gBAAA;AAAA,cAAA;AAAA,YACH,GAEJ;AAAA,8BACC,OAAA,EAAI,WAAU,yCACZ,UAAAG,sBACEY,GAAA,EAAS,WAAU,sBAAA,CAAsB,IAE1CjB,KACE,gBAAAa,EAAC,QAAA,EAAK,WAAU,kBAAkB,aAAiB,EAAA,CAGzD;AAAA,UAAA,EAAA,CACF;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGN;AACF;AAEAjB,EAAQ,cAAc;"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),E=require("react"),I=require("date-fns"),z=require("date-fns/locale"),u=require("../utils.cjs"),N=require("../primitives/avatar.cjs"),c=require("../primitives/button.cjs"),T=require("../primitives/badge.cjs"),g=require("../overlays/tooltip.cjs"),S=require("../primitives/separator.cjs"),n=require("lucide-react"),w=require("@tiptap/react"),M=require("@tiptap/starter-kit"),O=require("@tiptap/extension-underline"),U=require("@tiptap/extension-link"),_=require("@tiptap/extension-image"),H=require("@tiptap/extension-mention"),D=require("tippy.js"),F=require("./mention-list.cjs"),y=require("emoji-picker-react"),f=require("../overlays/popover.cjs");function K(t){const l=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const o in t)if(o!=="default"){const r=Object.getOwnPropertyDescriptor(t,o);Object.defineProperty(l,o,r.get?r:{enumerable:!0,get:()=>t[o]})}}return l.default=t,Object.freeze(l)}const v=K(E),W=({role:t})=>{const l={internal:{label:"Equipe iClips",className:"bg-primary/10 text-primary hover:bg-primary/20 border-primary/20"},client:{label:"Cliente",className:"bg-info/10 text-info-emphasis hover:bg-info/20 border-info/20"},freelancer:{label:"Freelancer",className:"bg-warning/10 text-warning-emphasis hover:bg-warning/20 border-warning/20"}},{label:o,className:r}=l[t];return e.jsx(T.Badge,{variant:"outline",className:u.cn("text-[10px] h-5 px-1.5 font-medium border-0",r),children:o})},X=({editor:t,visible:l})=>!t||!l?null:e.jsxs("div",{className:"flex items-center gap-1 border-b p-1 bg-muted/20 animate-in slide-in-from-bottom-2 duration-200",children:[e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("bold")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleBold().run(),title:"Negrito (Ctrl+B)","aria-label":"Negrito",children:e.jsx(n.Bold,{className:"h-3.5 w-3.5"})}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("italic")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleItalic().run(),title:"Itálico (Ctrl+I)","aria-label":"Itálico",children:e.jsx(n.Italic,{className:"h-3.5 w-3.5"})}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("underline")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleUnderline().run(),title:"Sublinhado (Ctrl+U)","aria-label":"Sublinhado",children:e.jsx(n.Underline,{className:"h-3.5 w-3.5"})}),e.jsx(S.Separator,{orientation:"vertical",className:"h-4 mx-1"}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("bulletList")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleBulletList().run(),"aria-label":"Lista com marcadores",children:e.jsx(n.List,{className:"h-3.5 w-3.5"})}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("orderedList")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleOrderedList().run(),"aria-label":"Lista numerada",children:e.jsx(n.ListOrdered,{className:"h-3.5 w-3.5"})}),e.jsx(S.Separator,{orientation:"vertical",className:"h-4 mx-1"}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("link")&&"bg-muted text-primary"),"aria-label":"Inserir link",onClick:()=>{const o=t.getAttributes("link").href,r=window.prompt("URL",o);if(r!==null){if(r===""){t.chain().focus().extendMarkRange("link").unsetLink().run();return}t.chain().focus().extendMarkRange("link").setLink({href:r}).run()}},children:e.jsx(n.Link,{className:"h-3.5 w-3.5"})})]}),G=({currentUser:t,onSubmit:l,users:o=[]})=>{const[r,b]=v.useState(!1),[p,i]=v.useState(!1),[h,m]=v.useState([]),[R,C]=v.useState(!0),k=v.useRef(null),A=v.useMemo(()=>[M.configure({link:!1,underline:!1}),O,U.configure({openOnClick:!1}),_,H.configure({HTMLAttributes:{class:"bg-accent text-accent-foreground rounded-sm px-1 py-0.5 font-medium decoration-clone"},suggestion:{items:({query:s})=>o.filter(a=>a.name.toLowerCase().startsWith(s.toLowerCase())).slice(0,5),render:()=>{let s=null,a=null;return{onStart:d=>{s=new w.ReactRenderer(F.MentionList,{props:d,editor:d.editor}),d.clientRect&&(a=D("body",{getReferenceClientRect:d.clientRect,appendTo:()=>document.body,content:s.element,showOnCreate:!0,interactive:!0,trigger:"manual",placement:"bottom-start"}))},onUpdate(d){s==null||s.updateProps(d),d.clientRect&&(a==null||a[0].setProps({getReferenceClientRect:d.clientRect}))},onKeyDown(d){var j,q;return d.event.key==="Escape"?(a==null||a[0].hide(),!0):!!((q=(j=s==null?void 0:s.ref)==null?void 0:j.onKeyDown)!=null&&q.call(j,d))},onExit(){a==null||a[0].destroy(),s==null||s.destroy()}}}}})],[o]),x=w.useEditor({extensions:A,content:"",onUpdate:({editor:s})=>{C(s.isEmpty)},editorProps:{attributes:{class:"prose prose-sm dark:prose-invert max-w-none focus:outline-none min-h-[80px] px-3 py-2 text-sm"}}}),B=s=>{if(s.target.files&&s.target.files[0]){const a=s.target.files[0],d=URL.createObjectURL(a),j={id:Math.random().toString(36).substr(2,9),name:a.name,url:d,type:a.type.startsWith("image/")?"image":"file"};m([...h,j]),s.target.value=""}},L=s=>{m(h.filter(a=>a.id!==s))},P=()=>{!x||x.isEmpty&&h.length===0||(l({content:x.getHTML(),isRevisionRequest:r,attachments:h}),x.commands.clearContent(),m([]),b(!1),C(!0))};return e.jsxs("div",{className:"flex gap-3 items-start",children:[e.jsxs(N.Avatar,{className:"h-8 w-8 mt-1 border",children:[e.jsx(N.AvatarImage,{src:t.avatar}),e.jsx(N.AvatarFallback,{children:t.name.substring(0,2).toUpperCase()})]}),e.jsxs("div",{className:u.cn("flex-1 rounded-xl border bg-background shadow-sm overflow-hidden transition-colors",r&&"border-destructive/40 ring-1 ring-destructive/20"),children:[e.jsx(X,{editor:x,visible:p}),e.jsx(w.EditorContent,{editor:x}),h.length>0&&e.jsx("div",{className:"px-3 pb-2 flex flex-wrap gap-2",children:h.map(s=>e.jsxs("div",{className:"relative group",children:[s.type==="image"?e.jsx("div",{className:"h-16 w-16 rounded-md border overflow-hidden relative",children:e.jsx("img",{src:s.url,alt:s.name,className:"h-full w-full object-cover"})}):e.jsx("div",{className:"h-16 w-16 rounded-md border bg-muted flex items-center justify-center",children:e.jsx(n.Paperclip,{className:"h-6 w-6 text-muted-foreground"})}),e.jsx("button",{onClick:()=>L(s.id),className:"absolute -top-1.5 -right-1.5 bg-destructive text-destructive-foreground rounded-full p-0.5 opacity-0 group-hover:opacity-100 transition-opacity shadow-sm",children:e.jsx(n.X,{className:"h-3 w-3"})})]},s.id))}),e.jsxs("div",{className:"flex items-center justify-between p-2 bg-muted/10 border-t",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(g.Tooltip,{children:[e.jsx(g.TooltipTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-8 w-8 transition-colors",p?"bg-muted text-foreground":"text-muted-foreground hover:text-foreground"),onClick:()=>i(!p),"aria-label":"Opções de formatação",children:e.jsx(n.Type,{className:"h-4 w-4"})})}),e.jsx(g.TooltipContent,{children:"Opções de formatação"})]}),e.jsx("div",{className:"w-px h-4 bg-border mx-1"}),e.jsxs(f.Popover,{children:[e.jsx(f.PopoverTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-8 w-8 text-muted-foreground hover:text-foreground","aria-label":"Inserir emoji",children:e.jsx(n.Smile,{className:"h-4 w-4"})})}),e.jsx(f.PopoverContent,{className:"w-auto p-0 border-none",align:"start",side:"top",children:e.jsx(y,{onEmojiClick:s=>x==null?void 0:x.chain().focus().insertContent(s.emoji).run(),width:300,height:350})})]}),e.jsx("input",{type:"file",className:"hidden",ref:k,onChange:B,accept:"image/*,.pdf,.doc,.docx"}),e.jsxs(g.Tooltip,{children:[e.jsx(g.TooltipTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-8 w-8 text-muted-foreground hover:text-foreground",onClick:()=>{var s;return(s=k.current)==null?void 0:s.click()},"aria-label":"Anexar arquivo",children:e.jsx(n.Paperclip,{className:"h-4 w-4"})})}),e.jsx(g.TooltipContent,{children:"Anexar arquivo"})]}),e.jsxs(g.Tooltip,{children:[e.jsx(g.TooltipTrigger,{asChild:!0,children:e.jsxs(c.Button,{variant:"ghost",size:"sm",className:u.cn("h-8 gap-2 px-2 transition-colors",r?"bg-destructive/10 text-destructive-emphasis hover:bg-destructive/20":"text-muted-foreground hover:text-foreground"),onClick:()=>b(!r),children:[e.jsx(n.AlertCircle,{className:"h-4 w-4"}),e.jsx("span",{className:"text-xs font-medium",children:"Solicitar Refação"})]})}),e.jsx(g.TooltipContent,{children:"Marca este comentário como um pedido de alteração"})]})]}),e.jsxs(c.Button,{size:"sm",onClick:P,disabled:R&&h.length===0,children:["Enviar ",e.jsx(n.Send,{className:"h-3.5 w-3.5 ml-2"})]})]})]})]})},J=({comment:t,currentUserId:l,onDelete:o,onReact:r})=>{var p;const b=t.isRevisionRequest;return e.jsxs("div",{className:u.cn("group flex gap-3 p-4 rounded-lg transition-colors",b?"bg-destructive/5 border border-destructive/20":"hover:bg-muted/20"),children:[e.jsxs(N.Avatar,{className:"h-8 w-8 mt-0.5 border",children:[e.jsx(N.AvatarImage,{src:t.user.avatar}),e.jsx(N.AvatarFallback,{children:t.user.name.substring(0,2).toUpperCase()})]}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center justify-between mb-1.5",children:[e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx("span",{className:"font-semibold text-sm text-foreground",children:t.user.name}),e.jsx(W,{role:t.user.role}),e.jsx("span",{className:"text-xs text-foreground/65",children:I.format(t.createdAt,"dd MMM 'às' HH:mm",{locale:z.ptBR})})]}),e.jsxs("div",{className:"flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity",children:[e.jsxs(f.Popover,{children:[e.jsx(f.PopoverTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-6 w-6 text-muted-foreground hover:text-foreground","aria-label":"Reagir com emoji",children:e.jsx(n.Smile,{className:"h-3.5 w-3.5","aria-hidden":"true"})})}),e.jsx(f.PopoverContent,{className:"w-auto p-0 border-none",align:"end",children:e.jsx(y,{onEmojiClick:i=>r==null?void 0:r(t.id,i.emoji),width:300,height:350})})]}),o&&e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-6 w-6 text-muted-foreground hover:text-destructive",onClick:()=>o(t.id),"aria-label":"Excluir comentário",children:e.jsx(n.Trash2,{className:"h-3.5 w-3.5","aria-hidden":"true"})})]})]}),b&&e.jsxs("div",{className:"inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-destructive/10 text-destructive-emphasis text-xs font-medium mb-2",children:[e.jsx(n.AlertCircle,{className:"h-3.5 w-3.5"}),"Solicitação de Refação"]}),e.jsx("div",{className:"prose prose-sm dark:prose-invert max-w-none text-sm text-foreground/90 leading-relaxed break-words [&_p]:mb-2 [&_p:last-child]:mb-0",dangerouslySetInnerHTML:{__html:t.content}}),t.attachments&&t.attachments.length>0&&e.jsx("div",{className:"mt-3 flex flex-wrap gap-2",children:t.attachments.map(i=>e.jsx("div",{className:"group/att relative rounded-lg border overflow-hidden bg-background hover:shadow-sm transition-all cursor-pointer",onClick:()=>window.open(i.url,"_blank"),children:i.type==="image"?e.jsxs("div",{className:"h-32 aspect-[4/3]",children:[e.jsx("img",{src:i.url,alt:i.name,className:"h-full w-full object-cover"}),e.jsx("div",{className:"absolute inset-0 bg-foreground/0 group-hover/att:bg-foreground/10 transition-colors"})]}):e.jsxs("div",{className:"h-12 px-3 flex items-center gap-2 bg-muted/30",children:[e.jsx(n.Paperclip,{className:"h-4 w-4 text-muted-foreground"}),e.jsx("span",{className:"text-xs font-medium truncate max-w-[150px]",children:i.name})]})},i.id))}),e.jsxs("div",{className:"mt-3 flex flex-wrap gap-2 items-center",children:[(p=t.reactions)==null?void 0:p.map(i=>e.jsxs("button",{onClick:()=>r==null?void 0:r(t.id,i.emoji),className:u.cn("inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-medium border transition-colors",i.users.includes(l)?"bg-accent border-accent text-accent-foreground":"bg-muted/30 border-transparent hover:bg-muted/50 text-muted-foreground"),children:[e.jsx("span",{children:i.emoji}),e.jsx("span",{children:i.users.length})]},i.emoji)),r&&e.jsxs(f.Popover,{children:[e.jsx(f.PopoverTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-6 w-6 rounded-full text-muted-foreground hover:text-foreground hover:bg-muted/50","aria-label":"Adicionar reação",children:e.jsx(n.Plus,{className:"h-3.5 w-3.5"})})}),e.jsx(f.PopoverContent,{className:"w-auto p-0 border-none",align:"start",children:e.jsx(y,{onEmojiClick:i=>r(t.id,i.emoji),width:300,height:350})})]})]})]})]})},Q=({comments:t,currentUser:l,users:o=[],onAddComment:r,onDeleteComment:b,onReact:p,className:i})=>{const h=m=>{r({content:m.content,user:l,isRevisionRequest:m.isRevisionRequest,attachments:m.attachments})};return e.jsxs("div",{className:u.cn("flex flex-col h-full",i),children:[e.jsxs("div",{className:"px-4 py-3 border-b flex items-center justify-between bg-card",children:[e.jsx("h3",{className:"font-semibold text-base",children:"Comentários da Atividade"}),e.jsx(T.Badge,{variant:"secondary",className:"bg-muted text-muted-foreground hover:bg-muted",children:t.length})]}),e.jsx("div",{className:"flex-1 overflow-y-auto p-4 space-y-2 bg-muted/5",children:t.length===0?e.jsxs("div",{className:"h-full flex flex-col items-center justify-center text-muted-foreground min-h-[200px]",children:[e.jsx("div",{className:"bg-muted rounded-full p-3 mb-3",children:e.jsx(n.MoreHorizontal,{className:"h-6 w-6","aria-hidden":"true"})}),e.jsx("p",{className:"text-sm font-medium text-foreground/70",children:"Nenhum comentário ainda"}),e.jsx("p",{className:"text-xs",children:"Inicie a conversa sobre esta tarefa"})]}):t.map(m=>e.jsx(J,{comment:m,currentUserId:l.id,onDelete:b,onReact:p},m.id))}),e.jsx("div",{className:"p-4 border-t bg-card",children:e.jsx(G,{currentUser:l,onSubmit:h,users:o})})]})};exports.CommentSystem=Q;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),E=require("react"),I=require("date-fns"),z=require("date-fns/locale"),u=require("../utils.cjs"),N=require("../primitives/avatar.cjs"),c=require("../primitives/button.cjs"),T=require("../primitives/badge.cjs"),g=require("../overlays/tooltip.cjs"),S=require("../primitives/separator.cjs"),a=require("lucide-react"),w=require("@tiptap/react"),M=require("@tiptap/starter-kit"),O=require("@tiptap/extension-underline"),U=require("@tiptap/extension-link"),_=require("@tiptap/extension-image"),H=require("@tiptap/extension-mention"),D=require("tippy.js"),F=require("./mention-list.cjs"),y=require("emoji-picker-react"),f=require("../overlays/popover.cjs");function K(t){const l=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const o in t)if(o!=="default"){const r=Object.getOwnPropertyDescriptor(t,o);Object.defineProperty(l,o,r.get?r:{enumerable:!0,get:()=>t[o]})}}return l.default=t,Object.freeze(l)}const v=K(E),W=({role:t})=>{const l={internal:{label:"Equipe iClips",className:"bg-primary/10 text-primary border-primary/20"},client:{label:"Cliente",className:"bg-info/10 text-info-emphasis border-info/20"},freelancer:{label:"Freelancer",className:"bg-warning/10 text-warning-emphasis border-warning/20"}},{label:o,className:r}=l[t];return e.jsx(T.Badge,{variant:"outline",className:u.cn("text-[10px] h-5 px-1.5 font-medium border-0",r),children:o})},X=({editor:t,visible:l})=>!t||!l?null:e.jsxs("div",{className:"flex items-center gap-1 border-b p-1 bg-muted/20 animate-in slide-in-from-bottom-2 duration-200",children:[e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("bold")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleBold().run(),title:"Negrito (Ctrl+B)","aria-label":"Negrito",children:e.jsx(a.Bold,{className:"h-3.5 w-3.5"})}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("italic")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleItalic().run(),title:"Itálico (Ctrl+I)","aria-label":"Itálico",children:e.jsx(a.Italic,{className:"h-3.5 w-3.5"})}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("underline")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleUnderline().run(),title:"Sublinhado (Ctrl+U)","aria-label":"Sublinhado",children:e.jsx(a.Underline,{className:"h-3.5 w-3.5"})}),e.jsx(S.Separator,{orientation:"vertical",className:"h-4 mx-1"}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("bulletList")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleBulletList().run(),"aria-label":"Lista com marcadores",children:e.jsx(a.List,{className:"h-3.5 w-3.5"})}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("orderedList")&&"bg-muted text-primary"),onClick:()=>t.chain().focus().toggleOrderedList().run(),"aria-label":"Lista numerada",children:e.jsx(a.ListOrdered,{className:"h-3.5 w-3.5"})}),e.jsx(S.Separator,{orientation:"vertical",className:"h-4 mx-1"}),e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-7 w-7",t.isActive("link")&&"bg-muted text-primary"),"aria-label":"Inserir link",onClick:()=>{const o=t.getAttributes("link").href,r=window.prompt("URL",o);if(r!==null){if(r===""){t.chain().focus().extendMarkRange("link").unsetLink().run();return}t.chain().focus().extendMarkRange("link").setLink({href:r}).run()}},children:e.jsx(a.Link,{className:"h-3.5 w-3.5"})})]}),G=({currentUser:t,onSubmit:l,users:o=[]})=>{const[r,b]=v.useState(!1),[p,i]=v.useState(!1),[h,m]=v.useState([]),[R,C]=v.useState(!0),k=v.useRef(null),A=v.useMemo(()=>[M.configure({link:!1,underline:!1}),O,U.configure({openOnClick:!1}),_,H.configure({HTMLAttributes:{class:"bg-secondary text-secondary-foreground rounded-sm px-1 py-0.5 font-medium decoration-clone"},suggestion:{items:({query:s})=>o.filter(n=>n.name.toLowerCase().startsWith(s.toLowerCase())).slice(0,5),render:()=>{let s=null,n=null;return{onStart:d=>{s=new w.ReactRenderer(F.MentionList,{props:d,editor:d.editor}),d.clientRect&&(n=D("body",{getReferenceClientRect:d.clientRect,appendTo:()=>document.body,content:s.element,showOnCreate:!0,interactive:!0,trigger:"manual",placement:"bottom-start"}))},onUpdate(d){s==null||s.updateProps(d),d.clientRect&&(n==null||n[0].setProps({getReferenceClientRect:d.clientRect}))},onKeyDown(d){var j,q;return d.event.key==="Escape"?(n==null||n[0].hide(),!0):!!((q=(j=s==null?void 0:s.ref)==null?void 0:j.onKeyDown)!=null&&q.call(j,d))},onExit(){n==null||n[0].destroy(),s==null||s.destroy()}}}}})],[o]),x=w.useEditor({extensions:A,content:"",onUpdate:({editor:s})=>{C(s.isEmpty)},editorProps:{attributes:{class:"prose prose-sm dark:prose-invert max-w-none focus:outline-none min-h-[80px] px-3 py-2 text-sm"}}}),B=s=>{if(s.target.files&&s.target.files[0]){const n=s.target.files[0],d=URL.createObjectURL(n),j={id:Math.random().toString(36).substr(2,9),name:n.name,url:d,type:n.type.startsWith("image/")?"image":"file"};m([...h,j]),s.target.value=""}},L=s=>{m(h.filter(n=>n.id!==s))},P=()=>{!x||x.isEmpty&&h.length===0||(l({content:x.getHTML(),isRevisionRequest:r,attachments:h}),x.commands.clearContent(),m([]),b(!1),C(!0))};return e.jsxs("div",{className:"flex gap-3 items-start",children:[e.jsxs(N.Avatar,{className:"h-8 w-8 mt-1 border",children:[e.jsx(N.AvatarImage,{src:t.avatar}),e.jsx(N.AvatarFallback,{children:t.name.substring(0,2).toUpperCase()})]}),e.jsxs("div",{className:u.cn("flex-1 rounded-xl border bg-background shadow-sm overflow-hidden transition-colors",r&&"border-destructive/40 ring-1 ring-destructive/20"),children:[e.jsx(X,{editor:x,visible:p}),e.jsx(w.EditorContent,{editor:x}),h.length>0&&e.jsx("div",{className:"px-3 pb-2 flex flex-wrap gap-2",children:h.map(s=>e.jsxs("div",{className:"relative group",children:[s.type==="image"?e.jsx("div",{className:"h-16 w-16 rounded-md border overflow-hidden relative",children:e.jsx("img",{src:s.url,alt:s.name,className:"h-full w-full object-cover"})}):e.jsx("div",{className:"h-16 w-16 rounded-md border bg-muted flex items-center justify-center",children:e.jsx(a.Paperclip,{className:"h-6 w-6 text-muted-foreground"})}),e.jsx("button",{onClick:()=>L(s.id),className:"absolute -top-1.5 -right-1.5 border bg-background text-destructive-emphasis hover:bg-destructive/10 rounded-full p-0.5 opacity-100 md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100 transition-opacity shadow-sm",children:e.jsx(a.X,{className:"h-3 w-3"})})]},s.id))}),e.jsxs("div",{className:"flex items-center justify-between p-2 bg-muted/10 border-t",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(g.Tooltip,{children:[e.jsx(g.TooltipTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:u.cn("h-8 w-8 transition-colors",p?"bg-muted text-foreground":"text-muted-foreground hover:text-foreground"),onClick:()=>i(!p),"aria-label":"Opções de formatação",children:e.jsx(a.Type,{className:"h-4 w-4"})})}),e.jsx(g.TooltipContent,{children:"Opções de formatação"})]}),e.jsx("div",{className:"w-px h-4 bg-border mx-1"}),e.jsxs(f.Popover,{children:[e.jsx(f.PopoverTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-8 w-8 text-muted-foreground hover:text-foreground","aria-label":"Inserir emoji",children:e.jsx(a.Smile,{className:"h-4 w-4"})})}),e.jsx(f.PopoverContent,{className:"w-auto p-0 border-none",align:"start",side:"top",children:e.jsx(y,{onEmojiClick:s=>x==null?void 0:x.chain().focus().insertContent(s.emoji).run(),width:300,height:350})})]}),e.jsx("input",{type:"file",className:"hidden",ref:k,onChange:B,accept:"image/*,.pdf,.doc,.docx"}),e.jsxs(g.Tooltip,{children:[e.jsx(g.TooltipTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-8 w-8 text-muted-foreground hover:text-foreground",onClick:()=>{var s;return(s=k.current)==null?void 0:s.click()},"aria-label":"Anexar arquivo",children:e.jsx(a.Paperclip,{className:"h-4 w-4"})})}),e.jsx(g.TooltipContent,{children:"Anexar arquivo"})]}),e.jsxs(g.Tooltip,{children:[e.jsx(g.TooltipTrigger,{asChild:!0,children:e.jsxs(c.Button,{variant:"ghost",size:"sm",className:u.cn("h-8 gap-2 px-2 transition-colors",r?"bg-destructive/10 text-destructive-emphasis hover:bg-destructive/20":"text-muted-foreground hover:text-foreground"),onClick:()=>b(!r),children:[e.jsx(a.AlertCircle,{className:"h-4 w-4"}),e.jsx("span",{className:"text-xs font-medium",children:"Solicitar Refação"})]})}),e.jsx(g.TooltipContent,{children:"Marca este comentário como um pedido de alteração"})]})]}),e.jsxs(c.Button,{size:"sm",onClick:P,disabled:R&&h.length===0,children:["Enviar ",e.jsx(a.Send,{className:"h-3.5 w-3.5 ml-2"})]})]})]})]})},J=({comment:t,currentUserId:l,onDelete:o,onReact:r})=>{var p;const b=t.isRevisionRequest;return e.jsxs("div",{className:u.cn("group flex gap-3 p-4 rounded-lg transition-colors",b?"bg-destructive/5 border border-destructive/20":"hover:bg-muted/20"),children:[e.jsxs(N.Avatar,{className:"h-8 w-8 mt-0.5 border",children:[e.jsx(N.AvatarImage,{src:t.user.avatar}),e.jsx(N.AvatarFallback,{children:t.user.name.substring(0,2).toUpperCase()})]}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center justify-between mb-1.5",children:[e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx("span",{className:"font-semibold text-sm text-foreground",children:t.user.name}),e.jsx(W,{role:t.user.role}),e.jsx("span",{className:"text-xs text-foreground/65",children:I.format(t.createdAt,"dd MMM 'às' HH:mm",{locale:z.ptBR})})]}),e.jsxs("div",{className:"flex items-center gap-2 opacity-100 md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100 transition-opacity has-[[data-state=open]]:opacity-100",children:[e.jsxs(f.Popover,{children:[e.jsx(f.PopoverTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-6 w-6 relative before:absolute before:-inset-1 before:content-[''] text-muted-foreground hover:text-foreground","aria-label":"Reagir com emoji",children:e.jsx(a.Smile,{className:"h-3.5 w-3.5","aria-hidden":"true"})})}),e.jsx(f.PopoverContent,{className:"w-auto p-0 border-none",align:"end",children:e.jsx(y,{onEmojiClick:i=>r==null?void 0:r(t.id,i.emoji),width:300,height:350})})]}),o&&e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-6 w-6 relative before:absolute before:-inset-1 before:content-[''] text-muted-foreground hover:text-destructive-emphasis",onClick:()=>o(t.id),"aria-label":"Excluir comentário",children:e.jsx(a.Trash2,{className:"h-3.5 w-3.5","aria-hidden":"true"})})]})]}),b&&e.jsxs("div",{className:"inline-flex items-center gap-1.5 px-2 py-1 rounded-md bg-destructive/10 text-destructive-emphasis text-xs font-medium mb-2",children:[e.jsx(a.AlertCircle,{className:"h-3.5 w-3.5"}),"Solicitação de Refação"]}),e.jsx("div",{className:"prose prose-sm dark:prose-invert max-w-none text-sm text-foreground/90 leading-relaxed break-words [&_p]:mb-2 [&_p:last-child]:mb-0",dangerouslySetInnerHTML:{__html:t.content}}),t.attachments&&t.attachments.length>0&&e.jsx("div",{className:"mt-3 flex flex-wrap gap-2",children:t.attachments.map(i=>e.jsx("div",{className:"group/att relative rounded-lg border overflow-hidden bg-background hover:shadow-sm transition-all cursor-pointer",onClick:()=>window.open(i.url,"_blank"),children:i.type==="image"?e.jsxs("div",{className:"h-32 aspect-[4/3]",children:[e.jsx("img",{src:i.url,alt:i.name,className:"h-full w-full object-cover"}),e.jsx("div",{className:"absolute inset-0 bg-foreground/0 group-hover/att:bg-foreground/10 transition-colors"})]}):e.jsxs("div",{className:"h-12 px-3 flex items-center gap-2 bg-muted/30",children:[e.jsx(a.Paperclip,{className:"h-4 w-4 text-muted-foreground"}),e.jsx("span",{className:"text-xs font-medium truncate max-w-[150px]",children:i.name})]})},i.id))}),e.jsxs("div",{className:"mt-3 flex flex-wrap gap-2 items-center",children:[(p=t.reactions)==null?void 0:p.map(i=>e.jsxs("button",{onClick:()=>r==null?void 0:r(t.id,i.emoji),className:u.cn("inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full text-xs font-medium border transition-colors",i.users.includes(l)?"bg-secondary border-secondary text-secondary-foreground":"bg-muted/30 border-transparent hover:bg-muted/50 text-muted-foreground"),children:[e.jsx("span",{children:i.emoji}),e.jsx("span",{children:i.users.length})]},i.emoji)),r&&e.jsxs(f.Popover,{children:[e.jsx(f.PopoverTrigger,{asChild:!0,children:e.jsx(c.Button,{variant:"ghost",size:"icon",className:"h-6 w-6 rounded-full text-muted-foreground hover:text-foreground hover:bg-muted/50","aria-label":"Adicionar reação",children:e.jsx(a.Plus,{className:"h-3.5 w-3.5"})})}),e.jsx(f.PopoverContent,{className:"w-auto p-0 border-none",align:"start",children:e.jsx(y,{onEmojiClick:i=>r(t.id,i.emoji),width:300,height:350})})]})]})]})]})},Q=({comments:t,currentUser:l,users:o=[],onAddComment:r,onDeleteComment:b,onReact:p,className:i})=>{const h=m=>{r({content:m.content,user:l,isRevisionRequest:m.isRevisionRequest,attachments:m.attachments})};return e.jsxs("div",{className:u.cn("flex flex-col h-full",i),children:[e.jsxs("div",{className:"px-4 py-3 border-b flex items-center justify-between bg-card",children:[e.jsx("h3",{className:"font-semibold text-base",children:"Comentários da Atividade"}),e.jsx(T.Badge,{variant:"secondary",className:"bg-muted text-muted-foreground hover:bg-muted",children:t.length})]}),e.jsx("div",{className:"flex-1 overflow-y-auto p-4 space-y-2 bg-muted/5",children:t.length===0?e.jsxs("div",{className:"h-full flex flex-col items-center justify-center text-muted-foreground min-h-[200px]",children:[e.jsx("div",{className:"bg-muted rounded-full p-3 mb-3",children:e.jsx(a.MoreHorizontal,{className:"h-6 w-6","aria-hidden":"true"})}),e.jsx("p",{className:"text-sm font-medium text-foreground/70",children:"Nenhum comentário ainda"}),e.jsx("p",{className:"text-xs",children:"Inicie a conversa sobre esta tarefa"})]}):t.map(m=>e.jsx(J,{comment:m,currentUserId:l.id,onDelete:b,onReact:p},m.id))}),e.jsx("div",{className:"p-4 border-t bg-card",children:e.jsx(G,{currentUser:l,onSubmit:h,users:o})})]})};exports.CommentSystem=Q;
|
|
2
2
|
//# sourceMappingURL=comment-system.cjs.map
|