analytica-frontend-lib 1.3.15 → 1.3.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/StudentRanking/index.d.ts +7 -35
- package/dist/StudentRanking/index.d.ts.map +1 -1
- package/dist/StudentRanking/index.js +120 -90
- package/dist/StudentRanking/index.js.map +1 -1
- package/dist/StudentRanking/index.mjs +119 -89
- package/dist/StudentRanking/index.mjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1448 -1256
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1424 -1228
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { HTMLAttributes } from 'react';
|
|
1
|
+
import { type HTMLAttributes } from 'react';
|
|
2
2
|
import type { StudentHighlightItem } from '../../hooks/useStudentsHighlight';
|
|
3
|
+
import { type RankingVariant } from '../shared/RankingShared';
|
|
4
|
+
/**
|
|
5
|
+
* Re-export RankingVariant as StudentRankingVariant for backwards compatibility
|
|
6
|
+
*/
|
|
7
|
+
export type StudentRankingVariant = RankingVariant;
|
|
3
8
|
/**
|
|
4
9
|
* Re-export StudentHighlightItem as StudentRankingItem for backwards compatibility
|
|
5
10
|
* and direct usage with the component
|
|
6
11
|
*/
|
|
7
12
|
export type StudentRankingItem = Pick<StudentHighlightItem, 'position' | 'name' | 'percentage'>;
|
|
8
|
-
/**
|
|
9
|
-
* Card variant type
|
|
10
|
-
*/
|
|
11
|
-
export type StudentRankingVariant = 'highlight' | 'attention';
|
|
12
13
|
/**
|
|
13
14
|
* Props for a single ranking card
|
|
14
15
|
*/
|
|
@@ -16,7 +17,7 @@ export interface RankingCardProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
16
17
|
/** Card title */
|
|
17
18
|
title: string;
|
|
18
19
|
/** Card variant: highlight (best students) or attention (needs attention) */
|
|
19
|
-
variant:
|
|
20
|
+
variant: RankingVariant;
|
|
20
21
|
/** List of students to display */
|
|
21
22
|
students: StudentRankingItem[];
|
|
22
23
|
}
|
|
@@ -43,7 +44,6 @@ export interface StudentRankingProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
43
44
|
*
|
|
44
45
|
* @example
|
|
45
46
|
* ```tsx
|
|
46
|
-
* // Basic usage with static data
|
|
47
47
|
* <StudentRanking
|
|
48
48
|
* highlightTitle="Estudantes em destaque"
|
|
49
49
|
* attentionTitle="Estudantes precisando de atenção"
|
|
@@ -59,34 +59,6 @@ export interface StudentRankingProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
59
59
|
* ]}
|
|
60
60
|
* />
|
|
61
61
|
* ```
|
|
62
|
-
*
|
|
63
|
-
* @example
|
|
64
|
-
* ```tsx
|
|
65
|
-
* // Usage with useStudentsHighlight hook (direct usage - no transformation needed)
|
|
66
|
-
* const fetchStudentsHighlight = async (filters) => {
|
|
67
|
-
* const response = await api.get('/performance/students-highlight', { params: filters });
|
|
68
|
-
* return response.data;
|
|
69
|
-
* };
|
|
70
|
-
*
|
|
71
|
-
* const useStudentsHighlight = createUseStudentsHighlight(fetchStudentsHighlight);
|
|
72
|
-
*
|
|
73
|
-
* function MyComponent() {
|
|
74
|
-
* const { topStudents, bottomStudents, loading, fetchStudentsHighlight } = useStudentsHighlight();
|
|
75
|
-
*
|
|
76
|
-
* useEffect(() => {
|
|
77
|
-
* fetchStudentsHighlight({ period: '1_MONTH' });
|
|
78
|
-
* }, [fetchStudentsHighlight]);
|
|
79
|
-
*
|
|
80
|
-
* if (loading) return <Skeleton />;
|
|
81
|
-
*
|
|
82
|
-
* return (
|
|
83
|
-
* <StudentRanking
|
|
84
|
-
* highlightStudents={topStudents}
|
|
85
|
-
* attentionStudents={bottomStudents}
|
|
86
|
-
* />
|
|
87
|
-
* );
|
|
88
|
-
* }
|
|
89
|
-
* ```
|
|
90
62
|
*/
|
|
91
63
|
export declare const StudentRanking: ({ highlightTitle, attentionTitle, highlightStudents, attentionStudents, className, ...props }: StudentRankingProps) => import("react/jsx-runtime").JSX.Element;
|
|
92
64
|
export default StudentRanking;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StudentRanking.d.ts","sourceRoot":"","sources":["../../../src/components/StudentRanking/StudentRanking.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"StudentRanking.d.ts","sourceRoot":"","sources":["../../../src/components/StudentRanking/StudentRanking.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AAI5C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,OAAO,EACL,KAAK,cAAc,EAMpB,MAAM,yBAAyB,CAAC;AAEjC;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC;AAEnD;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,oBAAoB,EACpB,UAAU,GAAG,MAAM,GAAG,YAAY,CACnC,CAAC;AA6DF;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,cAAc,CAAC,cAAc,CAAC;IACtE,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,OAAO,EAAE,cAAc,CAAC;IACxB,kCAAkC;IAClC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAChC;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,mDAMzB,gBAAgB,4CAelB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,cAAc,CAAC,cAAc,CAAC;IACzE,mDAAmD;IACnD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,iBAAiB,EAAE,kBAAkB,EAAE,CAAC;IACxC,6DAA6D;IAC7D,iBAAiB,EAAE,kBAAkB,EAAE,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,cAAc,GAAI,+FAO5B,mBAAmB,4CAerB,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -25,7 +25,7 @@ __export(StudentRanking_exports, {
|
|
|
25
25
|
default: () => StudentRanking_default
|
|
26
26
|
});
|
|
27
27
|
module.exports = __toCommonJS(StudentRanking_exports);
|
|
28
|
-
var
|
|
28
|
+
var import_phosphor_react2 = require("phosphor-react");
|
|
29
29
|
|
|
30
30
|
// src/utils/utils.ts
|
|
31
31
|
var import_clsx = require("clsx");
|
|
@@ -85,7 +85,8 @@ var Text = ({
|
|
|
85
85
|
};
|
|
86
86
|
var Text_default = Text;
|
|
87
87
|
|
|
88
|
-
// src/components/
|
|
88
|
+
// src/components/shared/RankingShared.tsx
|
|
89
|
+
var import_phosphor_react = require("phosphor-react");
|
|
89
90
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
90
91
|
var CARD_BACKGROUND_CLASSES = {
|
|
91
92
|
highlight: {
|
|
@@ -107,18 +108,88 @@ var PERCENTAGE_BADGE_CLASSES = {
|
|
|
107
108
|
highlight: "bg-success-700",
|
|
108
109
|
attention: "bg-indicator-negative"
|
|
109
110
|
};
|
|
110
|
-
var HEADER_BADGE_CLASSES =
|
|
111
|
-
highlight: "bg-indicator-positive",
|
|
112
|
-
attention: "bg-indicator-negative"
|
|
113
|
-
};
|
|
111
|
+
var HEADER_BADGE_CLASSES = BADGE_BACKGROUND_CLASSES;
|
|
114
112
|
var getPositionBackgroundClass = (variant, position) => {
|
|
115
113
|
const positionKey = Math.max(1, Math.min(position, 3));
|
|
116
114
|
return CARD_BACKGROUND_CLASSES[variant][positionKey];
|
|
117
115
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
function BaseRankingCard({
|
|
117
|
+
title,
|
|
118
|
+
variant,
|
|
119
|
+
items,
|
|
120
|
+
renderItem,
|
|
121
|
+
headerIcon,
|
|
122
|
+
className,
|
|
123
|
+
...props
|
|
124
|
+
}) {
|
|
125
|
+
const DefaultIcon = variant === "highlight" ? import_phosphor_react.Trophy : import_phosphor_react.Warning;
|
|
121
126
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
127
|
+
"div",
|
|
128
|
+
{
|
|
129
|
+
className: cn(
|
|
130
|
+
"flex flex-col flex-1 min-h-[254px] p-5 gap-4 bg-background border border-border-50 rounded-xl",
|
|
131
|
+
className
|
|
132
|
+
),
|
|
133
|
+
...props,
|
|
134
|
+
children: [
|
|
135
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-row justify-between items-center h-6 gap-4", children: [
|
|
136
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
137
|
+
Text_default,
|
|
138
|
+
{
|
|
139
|
+
as: "h3",
|
|
140
|
+
size: "lg",
|
|
141
|
+
weight: "bold",
|
|
142
|
+
className: "text-text-950 tracking-[0.2px]",
|
|
143
|
+
children: title
|
|
144
|
+
}
|
|
145
|
+
),
|
|
146
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
147
|
+
"span",
|
|
148
|
+
{
|
|
149
|
+
className: cn(
|
|
150
|
+
"w-6 h-6 rounded-full flex items-center justify-center",
|
|
151
|
+
HEADER_BADGE_CLASSES[variant]
|
|
152
|
+
),
|
|
153
|
+
children: headerIcon ?? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
154
|
+
DefaultIcon,
|
|
155
|
+
{
|
|
156
|
+
size: 14,
|
|
157
|
+
weight: "fill",
|
|
158
|
+
className: variant === "highlight" ? "text-text-950" : "text-text"
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
] }),
|
|
164
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-col gap-2", children: items.map((item, index) => renderItem(item, variant, index)) })
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
function RankingLayout({
|
|
170
|
+
children,
|
|
171
|
+
className,
|
|
172
|
+
...props
|
|
173
|
+
}) {
|
|
174
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
175
|
+
"div",
|
|
176
|
+
{
|
|
177
|
+
className: cn("flex flex-col md:flex-row w-full gap-4", className),
|
|
178
|
+
...props,
|
|
179
|
+
children
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/components/StudentRanking/StudentRanking.tsx
|
|
185
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
186
|
+
var StudentCard = ({
|
|
187
|
+
student,
|
|
188
|
+
variant
|
|
189
|
+
}) => {
|
|
190
|
+
const TrendIcon = variant === "highlight" ? import_phosphor_react2.TrendUp : import_phosphor_react2.TrendDown;
|
|
191
|
+
const backgroundClass = getPositionBackgroundClass(variant, student.position);
|
|
192
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
122
193
|
"div",
|
|
123
194
|
{
|
|
124
195
|
className: cn(
|
|
@@ -126,7 +197,7 @@ var StudentCard = ({ student, variant }) => {
|
|
|
126
197
|
backgroundClass
|
|
127
198
|
),
|
|
128
199
|
children: [
|
|
129
|
-
/* @__PURE__ */ (0,
|
|
200
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
130
201
|
Text_default,
|
|
131
202
|
{
|
|
132
203
|
size: "xs",
|
|
@@ -139,7 +210,7 @@ var StudentCard = ({ student, variant }) => {
|
|
|
139
210
|
children: student.position
|
|
140
211
|
}
|
|
141
212
|
),
|
|
142
|
-
/* @__PURE__ */ (0,
|
|
213
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
143
214
|
Text_default,
|
|
144
215
|
{
|
|
145
216
|
size: "sm",
|
|
@@ -148,7 +219,7 @@ var StudentCard = ({ student, variant }) => {
|
|
|
148
219
|
children: student.name
|
|
149
220
|
}
|
|
150
221
|
),
|
|
151
|
-
/* @__PURE__ */ (0,
|
|
222
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
152
223
|
Text_default,
|
|
153
224
|
{
|
|
154
225
|
size: "xs",
|
|
@@ -159,7 +230,7 @@ var StudentCard = ({ student, variant }) => {
|
|
|
159
230
|
PERCENTAGE_BADGE_CLASSES[variant]
|
|
160
231
|
),
|
|
161
232
|
children: [
|
|
162
|
-
/* @__PURE__ */ (0,
|
|
233
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TrendIcon, { size: 16, weight: "bold", "aria-hidden": "true" }),
|
|
163
234
|
student.percentage,
|
|
164
235
|
"%"
|
|
165
236
|
]
|
|
@@ -175,58 +246,24 @@ var RankingCard = ({
|
|
|
175
246
|
students,
|
|
176
247
|
className,
|
|
177
248
|
...props
|
|
178
|
-
}) =>
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
className: "text-text-950 tracking-[0.2px]",
|
|
197
|
-
children: title
|
|
198
|
-
}
|
|
199
|
-
),
|
|
200
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
201
|
-
"span",
|
|
202
|
-
{
|
|
203
|
-
className: cn(
|
|
204
|
-
"w-6 h-6 rounded-full flex items-center justify-center",
|
|
205
|
-
HEADER_BADGE_CLASSES[variant]
|
|
206
|
-
),
|
|
207
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
208
|
-
HeaderIcon,
|
|
209
|
-
{
|
|
210
|
-
size: 14,
|
|
211
|
-
weight: "fill",
|
|
212
|
-
className: variant === "highlight" ? "text-text-950" : "text-text"
|
|
213
|
-
}
|
|
214
|
-
)
|
|
215
|
-
}
|
|
216
|
-
)
|
|
217
|
-
] }),
|
|
218
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-col gap-2", children: students.map((student, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
219
|
-
StudentCard,
|
|
220
|
-
{
|
|
221
|
-
student,
|
|
222
|
-
variant
|
|
223
|
-
},
|
|
224
|
-
`${variant}-${index}-${student.position}`
|
|
225
|
-
)) })
|
|
226
|
-
]
|
|
227
|
-
}
|
|
228
|
-
);
|
|
229
|
-
};
|
|
249
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
250
|
+
BaseRankingCard,
|
|
251
|
+
{
|
|
252
|
+
title,
|
|
253
|
+
variant,
|
|
254
|
+
items: students,
|
|
255
|
+
renderItem: (student, v, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
256
|
+
StudentCard,
|
|
257
|
+
{
|
|
258
|
+
student,
|
|
259
|
+
variant: v
|
|
260
|
+
},
|
|
261
|
+
`${v}-${index}-${student.position}`
|
|
262
|
+
),
|
|
263
|
+
className,
|
|
264
|
+
...props
|
|
265
|
+
}
|
|
266
|
+
);
|
|
230
267
|
var StudentRanking = ({
|
|
231
268
|
highlightTitle = "Estudantes em destaque",
|
|
232
269
|
attentionTitle = "Estudantes precisando de aten\xE7\xE3o",
|
|
@@ -235,31 +272,24 @@ var StudentRanking = ({
|
|
|
235
272
|
className,
|
|
236
273
|
...props
|
|
237
274
|
}) => {
|
|
238
|
-
return /* @__PURE__ */ (0,
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
variant: "attention",
|
|
257
|
-
students: attentionStudents
|
|
258
|
-
}
|
|
259
|
-
)
|
|
260
|
-
]
|
|
261
|
-
}
|
|
262
|
-
);
|
|
275
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(RankingLayout, { className, ...props, children: [
|
|
276
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
277
|
+
RankingCard,
|
|
278
|
+
{
|
|
279
|
+
title: highlightTitle,
|
|
280
|
+
variant: "highlight",
|
|
281
|
+
students: highlightStudents
|
|
282
|
+
}
|
|
283
|
+
),
|
|
284
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
285
|
+
RankingCard,
|
|
286
|
+
{
|
|
287
|
+
title: attentionTitle,
|
|
288
|
+
variant: "attention",
|
|
289
|
+
students: attentionStudents
|
|
290
|
+
}
|
|
291
|
+
)
|
|
292
|
+
] });
|
|
263
293
|
};
|
|
264
294
|
var StudentRanking_default = StudentRanking;
|
|
265
295
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/StudentRanking/StudentRanking.tsx","../../src/utils/utils.ts","../../src/components/Text/Text.tsx"],"sourcesContent":["import { HTMLAttributes } from 'react';\nimport { Trophy, Warning, TrendUp, TrendDown } from 'phosphor-react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport type { StudentHighlightItem } from '../../hooks/useStudentsHighlight';\n\n/**\n * Re-export StudentHighlightItem as StudentRankingItem for backwards compatibility\n * and direct usage with the component\n */\nexport type StudentRankingItem = Pick<\n StudentHighlightItem,\n 'position' | 'name' | 'percentage'\n>;\n\n/**\n * Card variant type\n */\nexport type StudentRankingVariant = 'highlight' | 'attention';\n\n/**\n * Props for individual student card\n */\ninterface StudentCardProps {\n student: StudentRankingItem;\n variant: StudentRankingVariant;\n}\n\n/**\n * Lookup table for card background colors by position\n * Position 1 = most intense, Position 3+ = lightest\n */\nconst CARD_BACKGROUND_CLASSES = {\n highlight: {\n 1: 'bg-success-200',\n 2: 'bg-success-100',\n 3: 'bg-success-background',\n },\n attention: {\n 1: 'bg-error-200',\n 2: 'bg-error-100',\n 3: 'bg-error-background',\n },\n} as const;\n\n/**\n * Lookup table for badge background colors\n */\nconst BADGE_BACKGROUND_CLASSES = {\n highlight: 'bg-indicator-positive',\n attention: 'bg-indicator-negative',\n} as const;\n\n/**\n * Lookup table for percentage badge background colors\n */\nconst PERCENTAGE_BADGE_CLASSES = {\n highlight: 'bg-success-700',\n attention: 'bg-indicator-negative',\n} as const;\n\n/**\n * Lookup table for header badge background colors\n */\nconst HEADER_BADGE_CLASSES = {\n highlight: 'bg-indicator-positive',\n attention: 'bg-indicator-negative',\n} as const;\n\n/**\n * Get background class based on position (1, 2, or 3+)\n */\nconst getPositionBackgroundClass = (\n variant: StudentRankingVariant,\n position: number\n): string => {\n const positionKey = Math.max(1, Math.min(position, 3)) as 1 | 2 | 3;\n return CARD_BACKGROUND_CLASSES[variant][positionKey];\n};\n\n/**\n * Individual student card component\n */\nconst StudentCard = ({ student, variant }: StudentCardProps) => {\n const TrendIcon = variant === 'highlight' ? TrendUp : TrendDown;\n const backgroundClass = getPositionBackgroundClass(variant, student.position);\n\n return (\n <div\n className={cn(\n 'flex flex-row items-center w-full p-4 gap-2 rounded-xl',\n backgroundClass\n )}\n >\n {/* Position badge */}\n <Text\n size=\"xs\"\n weight=\"bold\"\n aria-label={`Posição ${student.position}`}\n className={cn(\n 'w-5 h-5 rounded-full flex items-center justify-center text-text',\n BADGE_BACKGROUND_CLASSES[variant]\n )}\n >\n {student.position}\n </Text>\n\n {/* Student name */}\n <Text\n size=\"sm\"\n weight=\"bold\"\n className=\"flex-1 min-w-0 text-text-950 tracking-[0.2px] truncate\"\n >\n {student.name}\n </Text>\n\n {/* Percentage badge */}\n <Text\n size=\"xs\"\n weight=\"bold\"\n aria-label={`Desempenho ${student.percentage}%`}\n className={cn(\n 'flex flex-row items-center h-[22px] px-2 gap-1 rounded text-text',\n PERCENTAGE_BADGE_CLASSES[variant]\n )}\n >\n <TrendIcon size={16} weight=\"bold\" aria-hidden=\"true\" />\n {student.percentage}%\n </Text>\n </div>\n );\n};\n\n/**\n * Props for a single ranking card\n */\nexport interface RankingCardProps extends HTMLAttributes<HTMLDivElement> {\n /** Card title */\n title: string;\n /** Card variant: highlight (best students) or attention (needs attention) */\n variant: StudentRankingVariant;\n /** List of students to display */\n students: StudentRankingItem[];\n}\n\n/**\n * Single ranking card component (can be used independently)\n */\nexport const RankingCard = ({\n title,\n variant,\n students,\n className,\n ...props\n}: RankingCardProps) => {\n const HeaderIcon = variant === 'highlight' ? Trophy : Warning;\n\n return (\n <div\n className={cn(\n 'flex flex-col flex-1 min-h-[254px] p-5 gap-4 bg-background border border-border-50 rounded-xl',\n className\n )}\n {...props}\n >\n {/* Header */}\n <div className=\"flex flex-row justify-between items-center h-6 gap-4\">\n <Text\n as=\"h3\"\n size=\"lg\"\n weight=\"bold\"\n className=\"text-text-950 tracking-[0.2px]\"\n >\n {title}\n </Text>\n\n {/* Header badge with icon */}\n <span\n className={cn(\n 'w-6 h-6 rounded-full flex items-center justify-center',\n HEADER_BADGE_CLASSES[variant]\n )}\n >\n <HeaderIcon\n size={14}\n weight=\"fill\"\n className={variant === 'highlight' ? 'text-text-950' : 'text-text'}\n />\n </span>\n </div>\n\n {/* Students list */}\n <div className=\"flex flex-col gap-2\">\n {students.map((student, index) => (\n <StudentCard\n key={`${variant}-${index}-${student.position}`}\n student={student}\n variant={variant}\n />\n ))}\n </div>\n </div>\n );\n};\n\n/**\n * Props for the StudentRanking component\n */\nexport interface StudentRankingProps extends HTMLAttributes<HTMLDivElement> {\n /** Title for the highlight (best students) card */\n highlightTitle?: string;\n /** Title for the attention (needs attention) card */\n attentionTitle?: string;\n /** List of highlighted (best performing) students */\n highlightStudents: StudentRankingItem[];\n /** List of students needing attention (lowest performing) */\n attentionStudents: StudentRankingItem[];\n}\n\n/**\n * StudentRanking component - displays two cards side by side showing\n * the best performing students and students that need attention.\n *\n * @example\n * ```tsx\n * // Basic usage with static data\n * <StudentRanking\n * highlightTitle=\"Estudantes em destaque\"\n * attentionTitle=\"Estudantes precisando de atenção\"\n * highlightStudents={[\n * { position: 1, name: 'Valentina Ribeiro', percentage: 100 },\n * { position: 2, name: 'Lucas Almeida', percentage: 100 },\n * { position: 3, name: 'Fernanda Costa', percentage: 100 },\n * ]}\n * attentionStudents={[\n * { position: 1, name: 'Ricardo Silva', percentage: 80 },\n * { position: 2, name: 'Juliana Santos', percentage: 50 },\n * { position: 3, name: 'Gabriel Oliveira', percentage: 40 },\n * ]}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Usage with useStudentsHighlight hook (direct usage - no transformation needed)\n * const fetchStudentsHighlight = async (filters) => {\n * const response = await api.get('/performance/students-highlight', { params: filters });\n * return response.data;\n * };\n *\n * const useStudentsHighlight = createUseStudentsHighlight(fetchStudentsHighlight);\n *\n * function MyComponent() {\n * const { topStudents, bottomStudents, loading, fetchStudentsHighlight } = useStudentsHighlight();\n *\n * useEffect(() => {\n * fetchStudentsHighlight({ period: '1_MONTH' });\n * }, [fetchStudentsHighlight]);\n *\n * if (loading) return <Skeleton />;\n *\n * return (\n * <StudentRanking\n * highlightStudents={topStudents}\n * attentionStudents={bottomStudents}\n * />\n * );\n * }\n * ```\n */\nexport const StudentRanking = ({\n highlightTitle = 'Estudantes em destaque',\n attentionTitle = 'Estudantes precisando de atenção',\n highlightStudents,\n attentionStudents,\n className,\n ...props\n}: StudentRankingProps) => {\n return (\n <div\n className={cn('flex flex-col md:flex-row w-full gap-4', className)}\n {...props}\n >\n <RankingCard\n title={highlightTitle}\n variant=\"highlight\"\n students={highlightStudents}\n />\n <RankingCard\n title={attentionTitle}\n variant=\"attention\"\n students={attentionStudents}\n />\n </div>\n );\n};\n\nexport default StudentRanking;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,4BAAoD;;;ACDpD,kBAAsC;AACtC,4BAAwB;AAEjB,SAAS,MAAM,QAAsB;AAC1C,aAAO,mCAAQ,kBAAK,MAAM,CAAC;AAC7B;;;ACsHI;AA/CJ,IAAM,OAAO,CAA8B;AAAA,EACzC;AAAA,EACA,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR;AAAA,EACA,YAAY;AAAA,EACZ,GAAG;AACL,MAAoB;AAClB,MAAI,cAAc;AAClB,MAAI,gBAAgB;AAGpB,QAAM,eAAe;AAAA,IACnB,OAAO;AAAA,IACP,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AAEA,gBAAc,aAAa,IAAI,KAAK,aAAa;AAGjD,QAAM,iBAAiB;AAAA,IACrB,UAAU;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,MAAM;AAAA,IACN,WAAW;AAAA,IACX,OAAO;AAAA,EACT;AAEA,kBAAgB,eAAe,MAAM,KAAK,eAAe;AAEzD,QAAM,cAAc;AACpB,QAAM,YAAY,MAAO;AAEzB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,aAAa,aAAa,eAAe,OAAO,SAAS;AAAA,MACtE,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,eAAQ;;;AFrCT,IAAAA,sBAAA;AA/DN,IAAM,0BAA0B;AAAA,EAC9B,WAAW;AAAA,IACT,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AAAA,EACA,WAAW;AAAA,IACT,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AACF;AAKA,IAAM,2BAA2B;AAAA,EAC/B,WAAW;AAAA,EACX,WAAW;AACb;AAKA,IAAM,2BAA2B;AAAA,EAC/B,WAAW;AAAA,EACX,WAAW;AACb;AAKA,IAAM,uBAAuB;AAAA,EAC3B,WAAW;AAAA,EACX,WAAW;AACb;AAKA,IAAM,6BAA6B,CACjC,SACA,aACW;AACX,QAAM,cAAc,KAAK,IAAI,GAAG,KAAK,IAAI,UAAU,CAAC,CAAC;AACrD,SAAO,wBAAwB,OAAO,EAAE,WAAW;AACrD;AAKA,IAAM,cAAc,CAAC,EAAE,SAAS,QAAQ,MAAwB;AAC9D,QAAM,YAAY,YAAY,cAAc,gCAAU;AACtD,QAAM,kBAAkB,2BAA2B,SAAS,QAAQ,QAAQ;AAE5E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAGA;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,QAAO;AAAA,YACP,cAAY,iBAAW,QAAQ,QAAQ;AAAA,YACvC,WAAW;AAAA,cACT;AAAA,cACA,yBAAyB,OAAO;AAAA,YAClC;AAAA,YAEC,kBAAQ;AAAA;AAAA,QACX;AAAA,QAGA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,QAAO;AAAA,YACP,WAAU;AAAA,YAET,kBAAQ;AAAA;AAAA,QACX;AAAA,QAGA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,QAAO;AAAA,YACP,cAAY,cAAc,QAAQ,UAAU;AAAA,YAC5C,WAAW;AAAA,cACT;AAAA,cACA,yBAAyB,OAAO;AAAA,YAClC;AAAA,YAEA;AAAA,2DAAC,aAAU,MAAM,IAAI,QAAO,QAAO,eAAY,QAAO;AAAA,cACrD,QAAQ;AAAA,cAAW;AAAA;AAAA;AAAA,QACtB;AAAA;AAAA;AAAA,EACF;AAEJ;AAiBO,IAAM,cAAc,CAAC;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAAwB;AACtB,QAAM,aAAa,YAAY,cAAc,+BAAS;AAEtD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAGJ;AAAA,sDAAC,SAAI,WAAU,wDACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,MAAK;AAAA,cACL,QAAO;AAAA,cACP,WAAU;AAAA,cAET;AAAA;AAAA,UACH;AAAA,UAGA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,qBAAqB,OAAO;AAAA,cAC9B;AAAA,cAEA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAM;AAAA,kBACN,QAAO;AAAA,kBACP,WAAW,YAAY,cAAc,kBAAkB;AAAA;AAAA,cACzD;AAAA;AAAA,UACF;AAAA,WACF;AAAA,QAGA,6CAAC,SAAI,WAAU,uBACZ,mBAAS,IAAI,CAAC,SAAS,UACtB;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA;AAAA;AAAA,UAFK,GAAG,OAAO,IAAI,KAAK,IAAI,QAAQ,QAAQ;AAAA,QAG9C,CACD,GACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAmEO,IAAM,iBAAiB,CAAC;AAAA,EAC7B,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAA2B;AACzB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,0CAA0C,SAAS;AAAA,MAChE,GAAG;AAAA,MAEJ;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,SAAQ;AAAA,YACR,UAAU;AAAA;AAAA,QACZ;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,SAAQ;AAAA,YACR,UAAU;AAAA;AAAA,QACZ;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,yBAAQ;","names":["import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/StudentRanking/StudentRanking.tsx","../../src/utils/utils.ts","../../src/components/Text/Text.tsx","../../src/components/shared/RankingShared.tsx"],"sourcesContent":["import { type HTMLAttributes } from 'react';\nimport { TrendUp, TrendDown } from 'phosphor-react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport type { StudentHighlightItem } from '../../hooks/useStudentsHighlight';\nimport {\n type RankingVariant,\n BADGE_BACKGROUND_CLASSES,\n PERCENTAGE_BADGE_CLASSES,\n getPositionBackgroundClass,\n BaseRankingCard,\n RankingLayout,\n} from '../shared/RankingShared';\n\n/**\n * Re-export RankingVariant as StudentRankingVariant for backwards compatibility\n */\nexport type StudentRankingVariant = RankingVariant;\n\n/**\n * Re-export StudentHighlightItem as StudentRankingItem for backwards compatibility\n * and direct usage with the component\n */\nexport type StudentRankingItem = Pick<\n StudentHighlightItem,\n 'position' | 'name' | 'percentage'\n>;\n\n/**\n * Individual student card component\n */\nconst StudentCard = ({\n student,\n variant,\n}: {\n student: StudentRankingItem;\n variant: RankingVariant;\n}) => {\n const TrendIcon = variant === 'highlight' ? TrendUp : TrendDown;\n const backgroundClass = getPositionBackgroundClass(variant, student.position);\n\n return (\n <div\n className={cn(\n 'flex flex-row items-center w-full p-4 gap-2 rounded-xl',\n backgroundClass\n )}\n >\n {/* Position badge */}\n <Text\n size=\"xs\"\n weight=\"bold\"\n aria-label={`Posição ${student.position}`}\n className={cn(\n 'w-5 h-5 rounded-full flex items-center justify-center text-text',\n BADGE_BACKGROUND_CLASSES[variant]\n )}\n >\n {student.position}\n </Text>\n\n {/* Student name */}\n <Text\n size=\"sm\"\n weight=\"bold\"\n className=\"flex-1 min-w-0 text-text-950 tracking-[0.2px] truncate\"\n >\n {student.name}\n </Text>\n\n {/* Percentage badge */}\n <Text\n size=\"xs\"\n weight=\"bold\"\n aria-label={`Desempenho ${student.percentage}%`}\n className={cn(\n 'flex flex-row items-center h-[22px] px-2 gap-1 rounded text-text',\n PERCENTAGE_BADGE_CLASSES[variant]\n )}\n >\n <TrendIcon size={16} weight=\"bold\" aria-hidden=\"true\" />\n {student.percentage}%\n </Text>\n </div>\n );\n};\n\n/**\n * Props for a single ranking card\n */\nexport interface RankingCardProps extends HTMLAttributes<HTMLDivElement> {\n /** Card title */\n title: string;\n /** Card variant: highlight (best students) or attention (needs attention) */\n variant: RankingVariant;\n /** List of students to display */\n students: StudentRankingItem[];\n}\n\n/**\n * Single ranking card component (can be used independently)\n */\nexport const RankingCard = ({\n title,\n variant,\n students,\n className,\n ...props\n}: RankingCardProps) => (\n <BaseRankingCard\n title={title}\n variant={variant}\n items={students}\n renderItem={(student, v, index) => (\n <StudentCard\n key={`${v}-${index}-${student.position}`}\n student={student}\n variant={v}\n />\n )}\n className={className}\n {...props}\n />\n);\n\n/**\n * Props for the StudentRanking component\n */\nexport interface StudentRankingProps extends HTMLAttributes<HTMLDivElement> {\n /** Title for the highlight (best students) card */\n highlightTitle?: string;\n /** Title for the attention (needs attention) card */\n attentionTitle?: string;\n /** List of highlighted (best performing) students */\n highlightStudents: StudentRankingItem[];\n /** List of students needing attention (lowest performing) */\n attentionStudents: StudentRankingItem[];\n}\n\n/**\n * StudentRanking component - displays two cards side by side showing\n * the best performing students and students that need attention.\n *\n * @example\n * ```tsx\n * <StudentRanking\n * highlightTitle=\"Estudantes em destaque\"\n * attentionTitle=\"Estudantes precisando de atenção\"\n * highlightStudents={[\n * { position: 1, name: 'Valentina Ribeiro', percentage: 100 },\n * { position: 2, name: 'Lucas Almeida', percentage: 100 },\n * { position: 3, name: 'Fernanda Costa', percentage: 100 },\n * ]}\n * attentionStudents={[\n * { position: 1, name: 'Ricardo Silva', percentage: 80 },\n * { position: 2, name: 'Juliana Santos', percentage: 50 },\n * { position: 3, name: 'Gabriel Oliveira', percentage: 40 },\n * ]}\n * />\n * ```\n */\nexport const StudentRanking = ({\n highlightTitle = 'Estudantes em destaque',\n attentionTitle = 'Estudantes precisando de atenção',\n highlightStudents,\n attentionStudents,\n className,\n ...props\n}: StudentRankingProps) => {\n return (\n <RankingLayout className={className} {...props}>\n <RankingCard\n title={highlightTitle}\n variant=\"highlight\"\n students={highlightStudents}\n />\n <RankingCard\n title={attentionTitle}\n variant=\"attention\"\n students={attentionStudents}\n />\n </RankingLayout>\n );\n};\n\nexport default StudentRanking;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses = '';\n let weightClasses = '';\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n","import { type HTMLAttributes, type ReactNode } from 'react';\nimport { Trophy, Warning } from 'phosphor-react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Shared ranking variant type\n */\nexport type RankingVariant = 'highlight' | 'attention';\n\n/**\n * Card background colors by position (1 = most intense, 3+ = lightest)\n */\nexport const CARD_BACKGROUND_CLASSES = {\n highlight: {\n 1: 'bg-success-200',\n 2: 'bg-success-100',\n 3: 'bg-success-background',\n },\n attention: {\n 1: 'bg-error-200',\n 2: 'bg-error-100',\n 3: 'bg-error-background',\n },\n} as const;\n\n/**\n * Position badge background colors\n */\nexport const BADGE_BACKGROUND_CLASSES = {\n highlight: 'bg-indicator-positive',\n attention: 'bg-indicator-negative',\n} as const;\n\n/**\n * Percentage badge background colors\n */\nexport const PERCENTAGE_BADGE_CLASSES = {\n highlight: 'bg-success-700',\n attention: 'bg-indicator-negative',\n} as const;\n\n/** Header icon badge uses the same palette as position badges */\nexport const HEADER_BADGE_CLASSES = BADGE_BACKGROUND_CLASSES;\n\n/**\n * Get background class based on position (1, 2, or 3+)\n */\nexport const getPositionBackgroundClass = (\n variant: RankingVariant,\n position: number\n): string => {\n const positionKey = Math.max(1, Math.min(position, 3)) as 1 | 2 | 3;\n return CARD_BACKGROUND_CLASSES[variant][positionKey];\n};\n\n/**\n * Generic ranking card wrapper — renders a header (title + icon badge)\n * followed by a list of items using the provided render function.\n */\nexport interface BaseRankingCardProps<T>\n extends HTMLAttributes<HTMLDivElement> {\n title: string;\n variant: RankingVariant;\n items: T[];\n renderItem: (item: T, variant: RankingVariant, index: number) => ReactNode;\n /** Override the default header icon (Trophy/Warning) */\n headerIcon?: ReactNode;\n}\n\nexport function BaseRankingCard<T>({\n title,\n variant,\n items,\n renderItem,\n headerIcon,\n className,\n ...props\n}: Readonly<BaseRankingCardProps<T>>) {\n const DefaultIcon = variant === 'highlight' ? Trophy : Warning;\n\n return (\n <div\n className={cn(\n 'flex flex-col flex-1 min-h-[254px] p-5 gap-4 bg-background border border-border-50 rounded-xl',\n className\n )}\n {...props}\n >\n {/* Header */}\n <div className=\"flex flex-row justify-between items-center h-6 gap-4\">\n <Text\n as=\"h3\"\n size=\"lg\"\n weight=\"bold\"\n className=\"text-text-950 tracking-[0.2px]\"\n >\n {title}\n </Text>\n\n <span\n className={cn(\n 'w-6 h-6 rounded-full flex items-center justify-center',\n HEADER_BADGE_CLASSES[variant]\n )}\n >\n {headerIcon ?? (\n <DefaultIcon\n size={14}\n weight=\"fill\"\n className={\n variant === 'highlight' ? 'text-text-950' : 'text-text'\n }\n />\n )}\n </span>\n </div>\n\n {/* Items list */}\n <div className=\"flex flex-col gap-2\">\n {items.map((item, index) => renderItem(item, variant, index))}\n </div>\n </div>\n );\n}\n\n/**\n * Two-card side-by-side responsive layout for ranking components\n */\nexport interface RankingLayoutProps extends HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n}\n\nexport function RankingLayout({\n children,\n className,\n ...props\n}: Readonly<RankingLayoutProps>) {\n return (\n <div\n className={cn('flex flex-col md:flex-row w-full gap-4', className)}\n {...props}\n >\n {children}\n </div>\n );\n}\n"],"mappings":"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","names":["import_phosphor_react","import_jsx_runtime","import_jsx_runtime"]}
|