analytica-frontend-lib
Version:
Repositório público dos componentes utilizados nas plataformas da Analytica Ensino
5,034 lines • 184 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/components/Quiz/Quiz.tsx
var Quiz_exports = {};
__export(Quiz_exports, {
Quiz: () => Quiz,
QuizAlternative: () => QuizAlternative,
QuizContent: () => QuizContent,
QuizFooter: () => QuizFooter,
QuizHeader: () => QuizHeader,
QuizHeaderResult: () => QuizHeaderResult,
QuizListResult: () => QuizListResult,
QuizListResultByMateria: () => QuizListResultByMateria,
QuizMultipleChoice: () => QuizMultipleChoice,
QuizQuestionList: () => QuizQuestionList,
QuizResultHeaderTitle: () => QuizResultHeaderTitle,
QuizResultPerformance: () => QuizResultPerformance,
QuizResultTitle: () => QuizResultTitle,
QuizTitle: () => QuizTitle
});
module.exports = __toCommonJS(Quiz_exports);
var import_phosphor_react8 = require("phosphor-react");
// src/components/Badge/Badge.tsx
var import_phosphor_react = require("phosphor-react");
// src/utils/utils.ts
var import_clsx = require("clsx");
var import_tailwind_merge = require("tailwind-merge");
function cn(...inputs) {
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
}
// src/components/Badge/Badge.tsx
var import_jsx_runtime = require("react/jsx-runtime");
var VARIANT_ACTION_CLASSES = {
solid: {
error: "bg-error-background text-error-700 focus-visible:outline-none",
warning: "bg-warning text-warning-800 focus-visible:outline-none",
success: "bg-success text-success-800 focus-visible:outline-none",
info: "bg-info text-info-800 focus-visible:outline-none",
muted: "bg-background-muted text-background-800 focus-visible:outline-none"
},
outlined: {
error: "bg-error text-error-700 border border-error-300 focus-visible:outline-none",
warning: "bg-warning text-warning-800 border border-warning-300 focus-visible:outline-none",
success: "bg-success text-success-800 border border-success-300 focus-visible:outline-none",
info: "bg-info text-info-800 border border-info-300 focus-visible:outline-none",
muted: "bg-background-muted text-background-800 border border-border-300 focus-visible:outline-none"
},
exams: {
exam1: "bg-exam-1 text-info-700 focus-visible:outline-none",
exam2: "bg-exam-2 text-typography-1 focus-visible:outline-none",
exam3: "bg-exam-3 text-typography-2 focus-visible:outline-none",
exam4: "bg-exam-4 text-success-700 focus-visible:outline-none"
},
examsOutlined: {
exam1: "bg-exam-1 text-info-700 border border-info-700 focus-visible:outline-none",
exam2: "bg-exam-2 text-typography-1 border border-typography-1 focus-visible:outline-none",
exam3: "bg-exam-3 text-typography-2 border border-typography-2 focus-visible:outline-none",
exam4: "bg-exam-4 text-success-700 border border-success-700 focus-visible:outline-none"
},
resultStatus: {
negative: "bg-error text-error-800 focus-visible:outline-none",
positive: "bg-success text-success-800 focus-visible:outline-none"
},
notification: "text-primary"
};
var SIZE_CLASSES = {
small: "text-2xs px-2 py-1",
medium: "text-xs px-2 py-1",
large: "text-sm px-2 py-1"
};
var SIZE_CLASSES_ICON = {
small: "size-3",
medium: "size-3.5",
large: "size-4"
};
var Badge = ({
children,
iconLeft,
iconRight,
size = "medium",
variant = "solid",
action = "error",
className = "",
notificationActive = false,
...props
}) => {
const sizeClasses = SIZE_CLASSES[size];
const sizeClassesIcon = SIZE_CLASSES_ICON[size];
const variantActionMap = VARIANT_ACTION_CLASSES[variant] || {};
const variantClasses = typeof variantActionMap === "string" ? variantActionMap : variantActionMap[action] ?? variantActionMap.muted ?? "";
const baseClasses = "inline-flex items-center justify-center rounded-xs font-normal gap-1 relative";
const baseClassesIcon = "flex items-center";
if (variant === "notification") {
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
"div",
{
className: cn(baseClasses, variantClasses, sizeClasses, className),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_phosphor_react.Bell, { size: 24, className: "text-current", "aria-hidden": "true" }),
notificationActive && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"span",
{
"data-testid": "notification-dot",
className: "absolute top-[5px] right-[10px] block h-2 w-2 rounded-full bg-indicator-error ring-2 ring-white"
}
)
]
}
);
}
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
"div",
{
className: cn(baseClasses, variantClasses, sizeClasses, className),
...props,
children: [
iconLeft && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn(baseClassesIcon, sizeClassesIcon), children: iconLeft }),
children,
iconRight && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cn(baseClassesIcon, sizeClassesIcon), children: iconRight })
]
}
);
};
var Badge_default = Badge;
// src/components/Alternative/Alternative.tsx
var import_phosphor_react2 = require("phosphor-react");
// src/components/Radio/Radio.tsx
var import_react = require("react");
var import_zustand = require("zustand");
// src/components/Text/Text.tsx
var import_jsx_runtime2 = require("react/jsx-runtime");
var Text = ({
children,
size = "md",
weight = "normal",
color = "text-text-950",
as,
className = "",
...props
}) => {
let sizeClasses = "";
let weightClasses = "";
const sizeClassMap = {
"2xs": "text-2xs",
xs: "text-xs",
sm: "text-sm",
md: "text-md",
lg: "text-lg",
xl: "text-xl",
"2xl": "text-2xl",
"3xl": "text-3xl",
"4xl": "text-4xl",
"5xl": "text-5xl",
"6xl": "text-6xl"
};
sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;
const weightClassMap = {
hairline: "font-hairline",
light: "font-light",
normal: "font-normal",
medium: "font-medium",
semibold: "font-semibold",
bold: "font-bold",
extrabold: "font-extrabold",
black: "font-black"
};
weightClasses = weightClassMap[weight] ?? weightClassMap.normal;
const baseClasses = "font-primary";
const Component = as ?? "p";
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
Component,
{
className: cn(baseClasses, sizeClasses, weightClasses, color, className),
...props,
children
}
);
};
var Text_default = Text;
// src/components/Radio/Radio.tsx
var import_jsx_runtime3 = require("react/jsx-runtime");
var SIZE_CLASSES2 = {
small: {
radio: "w-5 h-5",
textSize: "sm",
spacing: "gap-1.5",
borderWidth: "border-2",
dotSize: "w-2.5 h-2.5",
labelHeight: "h-5"
},
medium: {
radio: "w-6 h-6",
textSize: "md",
spacing: "gap-2",
borderWidth: "border-2",
dotSize: "w-3 h-3",
labelHeight: "h-6"
},
large: {
radio: "w-7 h-7",
textSize: "lg",
spacing: "gap-2",
borderWidth: "border-2",
dotSize: "w-3.5 h-3.5",
labelHeight: "h-7"
},
extraLarge: {
radio: "w-8 h-8",
textSize: "xl",
spacing: "gap-3",
borderWidth: "border-2",
dotSize: "w-4 h-4",
labelHeight: "h-8"
}
};
var BASE_RADIO_CLASSES = "rounded-full border cursor-pointer transition-all duration-200 flex items-center justify-center focus:outline-none";
var STATE_CLASSES = {
default: {
unchecked: "border-border-400 bg-background hover:border-border-500",
checked: "border-primary-950 bg-background hover:border-primary-800"
},
hovered: {
unchecked: "border-border-500 bg-background",
checked: "border-info-700 bg-background"
},
focused: {
unchecked: "border-border-400 bg-background",
checked: "border-primary-950 bg-background"
},
invalid: {
unchecked: "border-border-400 bg-background",
checked: "border-primary-950 bg-background"
},
disabled: {
unchecked: "border-border-400 bg-background cursor-not-allowed",
checked: "border-primary-950 bg-background cursor-not-allowed"
}
};
var DOT_CLASSES = {
default: "bg-primary-950",
hovered: "bg-info-700",
focused: "bg-primary-950",
invalid: "bg-primary-950",
disabled: "bg-primary-950"
};
var Radio = (0, import_react.forwardRef)(
({
label,
size = "medium",
state = "default",
errorMessage,
helperText,
className = "",
labelClassName = "",
checked: checkedProp,
defaultChecked = false,
disabled,
id,
name,
value,
onChange,
...props
}, ref) => {
const generatedId = (0, import_react.useId)();
const inputId = id ?? `radio-${generatedId}`;
const inputRef = (0, import_react.useRef)(null);
const [internalChecked, setInternalChecked] = (0, import_react.useState)(defaultChecked);
const isControlled = checkedProp !== void 0;
const checked = isControlled ? checkedProp : internalChecked;
const handleChange = (event) => {
const newChecked = event.target.checked;
if (!isControlled) {
setInternalChecked(newChecked);
}
if (event.target) {
event.target.blur();
}
onChange?.(event);
};
const currentState = disabled ? "disabled" : state;
const sizeClasses = SIZE_CLASSES2[size];
const actualRadioSize = sizeClasses.radio;
const actualDotSize = sizeClasses.dotSize;
const radioVariant = checked ? "checked" : "unchecked";
const stylingClasses = STATE_CLASSES[currentState][radioVariant];
const getBorderWidth = () => {
if (currentState === "focused") {
return "border-2";
}
return sizeClasses.borderWidth;
};
const borderWidthClass = getBorderWidth();
const radioClasses = cn(
BASE_RADIO_CLASSES,
actualRadioSize,
borderWidthClass,
stylingClasses,
className
);
const dotClasses = cn(
actualDotSize,
"rounded-full",
DOT_CLASSES[currentState],
"transition-all duration-200"
);
const isWrapperNeeded = currentState === "focused" || currentState === "invalid";
const wrapperBorderColor = currentState === "focused" ? "border-indicator-info" : "border-indicator-error";
const getTextColor = () => {
if (currentState === "disabled") {
return checked ? "text-text-900" : "text-text-600";
}
if (currentState === "focused") {
return "text-text-900";
}
return checked ? "text-text-900" : "text-text-600";
};
const getCursorClass = () => {
return currentState === "disabled" ? "cursor-not-allowed" : "cursor-pointer";
};
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-col", children: [
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
"div",
{
className: cn(
"flex flex-row items-center",
isWrapperNeeded ? cn("p-1 border-2", wrapperBorderColor, "rounded-lg gap-1.5") : sizeClasses.spacing,
disabled ? "opacity-40" : ""
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
"input",
{
ref: (node) => {
inputRef.current = node;
if (typeof ref === "function") ref(node);
else if (ref) ref.current = node;
},
type: "radio",
id: inputId,
checked,
disabled,
name,
value,
onChange: handleChange,
className: "sr-only",
style: {
position: "absolute",
left: "-9999px",
visibility: "hidden"
},
...props
}
),
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
"button",
{
type: "button",
className: radioClasses,
disabled,
"aria-pressed": checked,
onClick: (e) => {
e.preventDefault();
if (!disabled) {
if (inputRef.current) {
inputRef.current.click();
inputRef.current.blur();
}
}
},
onKeyDown: (e) => {
if ((e.key === "Enter" || e.key === " ") && !disabled) {
e.preventDefault();
if (inputRef.current) {
inputRef.current.click();
inputRef.current.blur();
}
}
},
children: checked && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: dotClasses })
}
),
label && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
"div",
{
className: cn(
"flex flex-row items-center",
sizeClasses.labelHeight,
"flex-1 min-w-0"
),
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
Text_default,
{
as: "label",
htmlFor: inputId,
size: sizeClasses.textSize,
weight: "normal",
className: cn(
getCursorClass(),
"select-none leading-normal flex items-center font-roboto truncate",
labelClassName
),
color: getTextColor(),
children: label
}
)
}
)
]
}
),
errorMessage && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
Text_default,
{
size: "sm",
weight: "normal",
className: "mt-1.5 truncate",
color: "text-error-600",
children: errorMessage
}
),
helperText && !errorMessage && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
Text_default,
{
size: "sm",
weight: "normal",
className: "mt-1.5 truncate",
color: "text-text-500",
children: helperText
}
)
] });
}
);
Radio.displayName = "Radio";
var createRadioGroupStore = (name, defaultValue, disabled, onValueChange) => (0, import_zustand.create)((set, get) => ({
value: defaultValue,
setValue: (value) => {
if (!get().disabled) {
set({ value });
get().onValueChange?.(value);
}
},
onValueChange,
disabled,
name
}));
var useRadioGroupStore = (externalStore) => {
if (!externalStore) {
throw new Error("RadioGroupItem must be used within a RadioGroup");
}
return externalStore;
};
var injectStore = (children, store) => import_react.Children.map(children, (child) => {
if (!(0, import_react.isValidElement)(child)) return child;
const typedChild = child;
const shouldInject = typedChild.type === RadioGroupItem;
return (0, import_react.cloneElement)(typedChild, {
...shouldInject ? { store } : {},
...typedChild.props.children ? { children: injectStore(typedChild.props.children, store) } : {}
});
});
var RadioGroup = (0, import_react.forwardRef)(
({
value: propValue,
defaultValue = "",
onValueChange,
name: propName,
disabled = false,
className = "",
children,
...props
}, ref) => {
const generatedId = (0, import_react.useId)();
const name = propName || `radio-group-${generatedId}`;
const storeRef = (0, import_react.useRef)(null);
storeRef.current ??= createRadioGroupStore(
name,
defaultValue,
disabled,
onValueChange
);
const store = storeRef.current;
const { setValue } = (0, import_zustand.useStore)(store, (s) => s);
(0, import_react.useEffect)(() => {
const currentValue = store.getState().value;
if (currentValue && onValueChange) {
onValueChange(currentValue);
}
}, []);
(0, import_react.useEffect)(() => {
if (propValue !== void 0) {
setValue(propValue);
}
}, [propValue, setValue]);
(0, import_react.useEffect)(() => {
store.setState({ disabled });
}, [disabled, store]);
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
"div",
{
ref,
className,
role: "radiogroup",
"aria-label": name,
...props,
children: injectStore(children, store)
}
);
}
);
RadioGroup.displayName = "RadioGroup";
var RadioGroupItem = (0, import_react.forwardRef)(
({
value,
store: externalStore,
disabled: itemDisabled,
size = "medium",
state = "default",
className = "",
id,
...props
}, ref) => {
const store = useRadioGroupStore(externalStore);
const {
value: groupValue,
setValue,
disabled: groupDisabled,
name
} = (0, import_zustand.useStore)(store);
const generatedId = (0, import_react.useId)();
const inputId = id ?? `radio-item-${generatedId}`;
const isChecked = groupValue === value;
const isDisabled = groupDisabled || itemDisabled;
const currentState = isDisabled ? "disabled" : state;
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
Radio,
{
ref,
id: inputId,
name,
value,
checked: isChecked,
disabled: isDisabled,
size,
state: currentState,
className,
onChange: (e) => {
if (e.target.checked && !isDisabled) {
setValue(value);
}
},
...props
}
);
}
);
RadioGroupItem.displayName = "RadioGroupItem";
// src/components/Alternative/Alternative.tsx
var import_react2 = require("react");
var import_jsx_runtime4 = require("react/jsx-runtime");
var AlternativesList = ({
alternatives,
name,
defaultValue,
value,
onValueChange,
disabled = false,
layout = "default",
className = "",
mode = "interactive",
selectedValue
}) => {
const uniqueId = (0, import_react2.useId)();
const groupName = name || `alternatives-${uniqueId}`;
const [actualValue, setActualValue] = (0, import_react2.useState)(value);
const isReadonly = mode === "readonly";
const getStatusStyles = (status, isReadonly2) => {
const hoverClass = isReadonly2 ? "" : "hover:bg-background-50";
switch (status) {
case "correct":
return "bg-success-background border-success-300";
case "incorrect":
return "bg-error-background border-error-300";
default:
return `bg-background border-border-100 ${hoverClass}`;
}
};
const getStatusBadge = (status) => {
switch (status) {
case "correct":
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Badge_default, { variant: "solid", action: "success", iconLeft: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_phosphor_react2.CheckCircle, {}), children: "Resposta correta" });
case "incorrect":
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Badge_default, { variant: "solid", action: "error", iconLeft: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_phosphor_react2.XCircle, {}), children: "Resposta incorreta" });
default:
return null;
}
};
const getLayoutClasses = () => {
switch (layout) {
case "compact":
return "gap-2";
case "detailed":
return "gap-4";
default:
return "gap-3.5";
}
};
const renderReadonlyAlternative = (alternative) => {
const alternativeId = alternative.value;
const isUserSelected = selectedValue === alternative.value;
const isCorrectAnswer = alternative.status === "correct";
let displayStatus = void 0;
if (isUserSelected && !isCorrectAnswer) {
displayStatus = "incorrect";
} else if (isCorrectAnswer) {
displayStatus = "correct";
}
const statusStyles = getStatusStyles(displayStatus, true);
const statusBadge = getStatusBadge(displayStatus);
const renderRadio = () => {
const radioClasses = `w-6 h-6 rounded-full border-2 cursor-default transition-all duration-200 flex items-center justify-center ${isUserSelected ? "border-primary-950 bg-background" : "border-border-400 bg-background"}`;
const dotClasses = "w-3 h-3 rounded-full bg-primary-950 transition-all duration-200";
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: radioClasses, children: isUserSelected && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: dotClasses }) });
};
if (layout === "detailed") {
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
"div",
{
className: cn(
"border-2 rounded-lg p-4 w-full",
statusStyles,
alternative.disabled ? "opacity-50" : ""
),
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-start gap-3 flex-1", children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mt-1", children: renderRadio() }),
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex-1", children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
"p",
{
className: cn(
"block font-medium",
selectedValue === alternative.value || statusBadge ? "text-text-950" : "text-text-600"
),
children: alternative.label
}
),
alternative.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-sm text-text-600 mt-1", children: alternative.description })
] })
] }),
statusBadge && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-shrink-0", children: statusBadge })
] })
},
alternativeId
);
}
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
"div",
{
className: cn(
"flex flex-row justify-between items-start gap-2 p-2 rounded-lg w-full",
statusStyles,
alternative.disabled ? "opacity-50" : ""
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2 flex-1", children: [
renderRadio(),
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
"span",
{
className: cn(
"flex-1",
selectedValue === alternative.value || statusBadge ? "text-text-950" : "text-text-600"
),
children: alternative.label
}
)
] }),
statusBadge && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-shrink-0", children: statusBadge })
]
},
alternativeId
);
};
if (isReadonly) {
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
"div",
{
className: cn("flex flex-col", getLayoutClasses(), "w-full", className),
children: alternatives.map(
(alternative) => renderReadonlyAlternative(alternative)
)
}
);
}
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
RadioGroup,
{
name: groupName,
defaultValue,
value,
onValueChange: (value2) => {
setActualValue(value2);
onValueChange?.(value2);
},
disabled,
className: cn("flex flex-col", getLayoutClasses(), className),
children: alternatives.map((alternative, index) => {
const alternativeId = alternative.value || `alt-${index}`;
const statusStyles = getStatusStyles(alternative.status, false);
const statusBadge = getStatusBadge(alternative.status);
if (layout === "detailed") {
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
"div",
{
className: cn(
"border-2 rounded-lg p-4 transition-all",
statusStyles,
alternative.disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"
),
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-start gap-3 flex-1", children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
RadioGroupItem,
{
value: alternative.value,
id: alternativeId,
disabled: alternative.disabled,
className: "mt-1"
}
),
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex-1", children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
"label",
{
htmlFor: alternativeId,
className: cn(
"block font-medium",
actualValue === alternative.value ? "text-text-950" : "text-text-600",
alternative.disabled ? "cursor-not-allowed" : "cursor-pointer"
),
children: alternative.label
}
),
alternative.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-sm text-text-600 mt-1", children: alternative.description })
] })
] }),
statusBadge && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-shrink-0", children: statusBadge })
] })
},
alternativeId
);
}
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
"div",
{
className: cn(
"flex flex-row justify-between gap-2 items-start p-2 rounded-lg transition-all",
statusStyles,
alternative.disabled ? "opacity-50 cursor-not-allowed" : ""
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-2 flex-1", children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
RadioGroupItem,
{
value: alternative.value,
id: alternativeId,
disabled: alternative.disabled
}
),
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
"label",
{
htmlFor: alternativeId,
className: cn(
"flex-1",
actualValue === alternative.value ? "text-text-950" : "text-text-600",
alternative.disabled ? "cursor-not-allowed" : "cursor-pointer"
),
children: alternative.label
}
)
] }),
statusBadge && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex-shrink-0", children: statusBadge })
]
},
alternativeId
);
})
}
);
};
var HeaderAlternative = (0, import_react2.forwardRef)(
({ className, title, subTitle, content, ...props }, ref) => {
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
"div",
{
ref,
className: cn(
"bg-background p-4 flex flex-col gap-4 rounded-xl",
className
),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "flex flex-col", children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-text-950 font-bold text-lg", children: title }),
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-text-700 text-sm ", children: subTitle })
] }),
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-text-950 text-md", children: content })
]
}
);
}
);
// src/components/Button/Button.tsx
var import_jsx_runtime5 = require("react/jsx-runtime");
var VARIANT_ACTION_CLASSES2 = {
solid: {
primary: "bg-primary-950 text-text border border-primary-950 hover:bg-primary-800 hover:border-primary-800 focus-visible:outline-none focus-visible:bg-primary-950 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-primary-700 active:border-primary-700 disabled:bg-primary-500 disabled:border-primary-500 disabled:opacity-40 disabled:cursor-not-allowed",
positive: "bg-success-500 text-text border border-success-500 hover:bg-success-600 hover:border-success-600 focus-visible:outline-none focus-visible:bg-success-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-success-700 active:border-success-700 disabled:bg-success-500 disabled:border-success-500 disabled:opacity-40 disabled:cursor-not-allowed",
negative: "bg-error-500 text-text border border-error-500 hover:bg-error-600 hover:border-error-600 focus-visible:outline-none focus-visible:bg-error-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-error-700 active:border-error-700 disabled:bg-error-500 disabled:border-error-500 disabled:opacity-40 disabled:cursor-not-allowed"
},
outline: {
primary: "bg-transparent text-primary-950 border border-primary-950 hover:bg-background-50 hover:text-primary-400 hover:border-primary-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 active:border-primary-700 disabled:opacity-40 disabled:cursor-not-allowed",
positive: "bg-transparent text-success-500 border border-success-300 hover:bg-background-50 hover:text-success-400 hover:border-success-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 active:border-success-700 disabled:opacity-40 disabled:cursor-not-allowed",
negative: "bg-transparent text-error-500 border border-error-300 hover:bg-background-50 hover:text-error-400 hover:border-error-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 active:border-error-700 disabled:opacity-40 disabled:cursor-not-allowed"
},
link: {
primary: "bg-transparent text-primary-950 hover:text-primary-400 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 disabled:opacity-40 disabled:cursor-not-allowed",
positive: "bg-transparent text-success-500 hover:text-success-400 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 disabled:opacity-40 disabled:cursor-not-allowed",
negative: "bg-transparent text-error-500 hover:text-error-400 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 disabled:opacity-40 disabled:cursor-not-allowed"
}
};
var SIZE_CLASSES3 = {
"extra-small": "text-xs px-3.5 py-2",
small: "text-sm px-4 py-2.5",
medium: "text-md px-5 py-2.5",
large: "text-lg px-6 py-3",
"extra-large": "text-lg px-7 py-3.5"
};
var Button = ({
children,
iconLeft,
iconRight,
size = "medium",
variant = "solid",
action = "primary",
className = "",
disabled,
type = "button",
...props
}) => {
const sizeClasses = SIZE_CLASSES3[size];
const variantClasses = VARIANT_ACTION_CLASSES2[variant][action];
const baseClasses = "inline-flex items-center justify-center rounded-full cursor-pointer font-medium";
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
"button",
{
className: cn(baseClasses, variantClasses, sizeClasses, className),
disabled,
type,
...props,
children: [
iconLeft && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "mr-2 flex items-center", children: iconLeft }),
children,
iconRight && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ml-2 flex items-center", children: iconRight })
]
}
);
};
var Button_default = Button;
// src/components/IconButton/IconButton.tsx
var import_react3 = require("react");
var import_jsx_runtime6 = require("react/jsx-runtime");
var IconButton = (0, import_react3.forwardRef)(
({ icon, size = "md", active = false, className = "", disabled, ...props }, ref) => {
const baseClasses = [
"inline-flex",
"items-center",
"justify-center",
"rounded-lg",
"font-medium",
"bg-transparent",
"text-text-950",
"cursor-pointer",
"hover:bg-primary-600",
"hover:text-text",
"focus-visible:outline-none",
"focus-visible:ring-2",
"focus-visible:ring-offset-0",
"focus-visible:ring-indicator-info",
"disabled:opacity-50",
"disabled:cursor-not-allowed",
"disabled:pointer-events-none"
];
const sizeClasses = {
sm: ["w-6", "h-6", "text-sm"],
md: ["w-10", "h-10", "text-base"]
};
const activeClasses = active ? ["!bg-primary-50", "!text-primary-950", "hover:!bg-primary-100"] : [];
const allClasses = [
...baseClasses,
...sizeClasses[size],
...activeClasses
].join(" ");
const ariaLabel = props["aria-label"] ?? "Bot\xE3o de a\xE7\xE3o";
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
"button",
{
ref,
type: "button",
className: cn(allClasses, className),
disabled,
"aria-pressed": active,
"aria-label": ariaLabel,
...props,
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "flex items-center justify-center", children: icon })
}
);
}
);
IconButton.displayName = "IconButton";
var IconButton_default = IconButton;
// src/components/Quiz/Quiz.tsx
var import_react11 = require("react");
// src/components/Quiz/useQuizStore.ts
var import_zustand2 = require("zustand");
var import_middleware = require("zustand/middleware");
var useQuizStore = (0, import_zustand2.create)()(
(0, import_middleware.devtools)(
(set, get) => {
let timerInterval = null;
const startTimer = () => {
if (get().isFinished) {
return;
}
if (timerInterval) {
clearInterval(timerInterval);
}
timerInterval = setInterval(() => {
const { timeElapsed } = get();
set({ timeElapsed: timeElapsed + 1 });
}, 1e3);
};
const stopTimer = () => {
if (timerInterval) {
clearInterval(timerInterval);
timerInterval = null;
}
};
return {
// Initial State
currentQuestionIndex: 0,
selectedAnswers: {},
userAnswers: [],
timeElapsed: 0,
isStarted: false,
isFinished: false,
userId: "",
// Setters
setBySimulated: (simulado) => set({ bySimulated: simulado }),
setByActivity: (atividade) => set({ byActivity: atividade }),
setByQuestionary: (aula) => set({ byQuestionary: aula }),
setUserId: (userId) => set({ userId }),
setUserAnswers: (userAnswers) => set({ userAnswers }),
getUserId: () => get().userId,
// Navigation
goToNextQuestion: () => {
const { currentQuestionIndex, getTotalQuestions } = get();
const totalQuestions = getTotalQuestions();
if (currentQuestionIndex < totalQuestions - 1) {
set({ currentQuestionIndex: currentQuestionIndex + 1 });
}
},
goToPreviousQuestion: () => {
const { currentQuestionIndex } = get();
if (currentQuestionIndex > 0) {
set({ currentQuestionIndex: currentQuestionIndex - 1 });
}
},
goToQuestion: (index) => {
const { getTotalQuestions } = get();
const totalQuestions = getTotalQuestions();
if (index >= 0 && index < totalQuestions) {
set({ currentQuestionIndex: index });
}
},
getActiveQuiz: () => {
const { bySimulated, byActivity, byQuestionary } = get();
if (bySimulated)
return { quiz: bySimulated, type: "bySimulated" };
if (byActivity)
return { quiz: byActivity, type: "byActivity" };
if (byQuestionary)
return { quiz: byQuestionary, type: "byQuestionary" };
return null;
},
selectAnswer: (questionId, answerId) => {
const { getActiveQuiz, userAnswers } = get();
const activeQuiz = getActiveQuiz();
if (!activeQuiz) return;
const activityId = activeQuiz.quiz.id;
const userId = get().getUserId();
if (!userId || userId === "") {
console.warn("selectAnswer called before userId is set");
return;
}
const existingAnswerIndex = userAnswers.findIndex(
(answer) => answer.questionId === questionId
);
const newUserAnswer = {
questionId,
activityId,
userId,
answer: null,
optionId: answerId
};
let updatedUserAnswers;
if (existingAnswerIndex !== -1) {
updatedUserAnswers = [...userAnswers];
updatedUserAnswers[existingAnswerIndex] = newUserAnswer;
} else {
updatedUserAnswers = [...userAnswers, newUserAnswer];
}
set({
userAnswers: updatedUserAnswers
});
},
selectMultipleAnswer: (questionId, answerIds) => {
const { getActiveQuiz, userAnswers } = get();
const activeQuiz = getActiveQuiz();
if (!activeQuiz) return;
const activityId = activeQuiz.quiz.id;
const userId = get().getUserId();
if (!userId || userId === "") {
console.warn("selectMultipleAnswer called before userId is set");
return;
}
const filteredUserAnswers = userAnswers.filter(
(answer) => answer.questionId !== questionId
);
const newUserAnswers = answerIds.map(
(answerId) => ({
questionId,
activityId,
userId,
answer: null,
optionId: answerId
})
);
const updatedUserAnswers = [
...filteredUserAnswers,
...newUserAnswers
];
set({
userAnswers: updatedUserAnswers
});
},
skipQuestion: () => {
const { getCurrentQuestion, userAnswers, getActiveQuiz } = get();
const currentQuestion = getCurrentQuestion();
const activeQuiz = getActiveQuiz();
if (!activeQuiz) return;
if (currentQuestion) {
const activityId = activeQuiz.quiz.id;
const userId = get().getUserId();
if (!userId || userId === "") {
console.warn("skipQuestion called before userId is set");
return;
}
const existingAnswerIndex = userAnswers.findIndex(
(answer) => answer.questionId === currentQuestion.id
);
const newUserAnswer = {
questionId: currentQuestion.id,
activityId,
userId,
answer: null,
optionId: null
};
let updatedUserAnswers;
if (existingAnswerIndex !== -1) {
updatedUserAnswers = [...userAnswers];
updatedUserAnswers[existingAnswerIndex] = newUserAnswer;
} else {
updatedUserAnswers = [...userAnswers, newUserAnswer];
}
set({
userAnswers: updatedUserAnswers
});
}
},
addUserAnswer: (questionId, answerId) => {
const { getActiveQuiz, userAnswers } = get();
const activeQuiz = getActiveQuiz();
if (!activeQuiz) return;
const activityId = activeQuiz.quiz.id;
const userId = get().getUserId();
if (!userId || userId === "") {
console.warn("addUserAnswer called before userId is set");
return;
}
const existingAnswerIndex = userAnswers.findIndex(
(answer) => answer.questionId === questionId
);
const newUserAnswer = {
questionId,
activityId,
userId,
answer: null,
optionId: answerId || null
};
if (existingAnswerIndex !== -1) {
const updatedUserAnswers = [...userAnswers];
updatedUserAnswers[existingAnswerIndex] = newUserAnswer;
set({ userAnswers: updatedUserAnswers });
} else {
set({ userAnswers: [...userAnswers, newUserAnswer] });
}
},
startQuiz: () => {
set({ isStarted: true, timeElapsed: 0 });
startTimer();
},
finishQuiz: () => {
set({ isFinished: true });
stopTimer();
},
resetQuiz: () => {
stopTimer();
set({
currentQuestionIndex: 0,
selectedAnswers: {},
userAnswers: [],
timeElapsed: 0,
isStarted: false,
isFinished: false,
userId: ""
});
},
// Timer
updateTime: (time) => set({ timeElapsed: time }),
startTimer,
stopTimer,
// Getters
getCurrentQuestion: () => {
const { currentQuestionIndex, getActiveQuiz } = get();
const activeQuiz = getActiveQuiz();
if (!activeQuiz) {
return null;
}
return activeQuiz.quiz.questions[currentQuestionIndex];
},
getTotalQuestions: () => {
const { getActiveQuiz } = get();
const activeQuiz = getActiveQuiz();
return activeQuiz?.quiz?.questions?.length || 0;
},
getAnsweredQuestions: () => {
const { userAnswers } = get();
return userAnswers.filter((answer) => answer.optionId !== null).length;
},
getUnansweredQuestions: () => {
const { getActiveQuiz, userAnswers } = get();
const activeQuiz = getActiveQuiz();
if (!activeQuiz) return [];
const unansweredQuestions = [];
activeQuiz.quiz.questions.forEach((question, index) => {
const userAnswer = userAnswers.find(
(answer) => answer.questionId === question.id
);
const isAnswered = userAnswer && userAnswer.optionId !== null;
const isSkipped = userAnswer && userAnswer.optionId === null;
if (!isAnswered && !isSkipped) {
unansweredQuestions.push(index + 1);
}
});
return unansweredQuestions;
},
getSkippedQuestions: () => {
const { userAnswers } = get();
return userAnswers.filter((answer) => answer.optionId === null).length;
},
getProgress: () => {
const { getTotalQuestions, getAnsweredQuestions } = get();
const total = getTotalQuestions();
const answered = getAnsweredQuestions();
return total > 0 ? answered / total * 100 : 0;
},
isQuestionAnswered: (questionId) => {
const { userAnswers } = get();
const userAnswer = userAnswers.find(
(answer) => answer.questionId === questionId
);
return userAnswer ? userAnswer.optionId !== null : false;
},
isQuestionSkipped: (questionId) => {
const { userAnswers } = get();
const userAnswer = userAnswers.find(
(answer) => answer.questionId === questionId
);
return userAnswer ? userAnswer.optionId === null : false;
},
getCurrentAnswer: () => {
const { getCurrentQuestion, userAnswers } = get();
const currentQuestion = getCurrentQuestion();
if (!currentQuestion) return void 0;
const userAnswer = userAnswers.find(
(answer) => answer.questionId === currentQuestion.id
);
return userAnswer?.optionId;
},
getAllCurrentAnswer: () => {
const { getCurrentQuestion, userAnswers } = get();
const currentQuestion = getCurrentQuestion();
if (!currentQuestion) return void 0;
const userAnswer = userAnswers.filter(
(answer) => answer.questionId === currentQuestion.id
);
return userAnswer;
},
getQuizTitle: () => {
const { getActiveQuiz } = get();
const activeQuiz = getActiveQuiz();
return activeQuiz?.quiz?.title || "Quiz";
},
formatTime: (seconds) => {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
return `${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")}`;
},
getUserAnswers: () => {
const { userAnswers } = get();
return userAnswers;
},
getUnansweredQuestionsFromUserAnswers: () => {
const { getActiveQuiz, userAnswers } = get();
const activeQuiz = getActiveQuiz();
if (!activeQuiz) return [];
const unansweredQuestions = [];
activeQuiz.quiz.questions.forEach((question, index) => {
const userAnswer = userAnswers.find(
(answer) => answer.questionId === question.id
);
const hasAnswer = userAnswer && userAnswer.optionId !== null;
const isSkipped = userAnswer && userAnswer.optionId === null;
if (!hasAnswer || isSkipped) {
unansweredQuestions.push(index + 1);
}
});
return unansweredQuestions;
},
getQuestionsGroupedBySubject: () => {
const { getActiveQuiz } = get();
const activeQuiz = getActiveQuiz();
if (!activeQuiz) return {};
const groupedQuestions = {};
activeQuiz.quiz.questions.forEach((question) => {
const subjectId = question.knowledgeMatrix?.[0]?.subjectId || "Sem mat\xE9ria";
if (!groupedQuestions[subjectId]) {
groupedQuestions[subjectId] = [];
}
groupedQuestions[subjectId].push(question);
});
return groupedQuestions;
},
// New methods for userAnswers
getUserAnswerByQuestionId: (questionId) => {
const { userAnswers } = get();
return userAnswers.find((answer) => answer.questionId === questionId) || null;
},
isQuestionAnsweredByUserAnswers: (questionId) => {
const { userAnswers } = get();
const answer = userAnswers.find(
(answer2) => answer2.questionId === questionId
);
return answer ? answer.optionId !== null : false;
},
getQuestionStatusFromUserAnswers: (questionId) => {
const { userAnswers } = get();
const answer = userAnswers.find(
(answer2) => answer2.questionId === questionId
);
if (!answer) return "unanswered";
if (answer.optionId === null) return "skipped";
return "answered";
},
getUserAnswersForActivity: () => {
const { userAnswers } = get();
return userAnswers;
},
setCurrentQuestion: (question) => {
const { getActiveQuiz } = get();
const activeQuiz = getActiveQuiz();
if (!activeQuiz) return;
const questionIndex = activeQuiz.quiz.questions.findIndex(
(q) => q.id === question.id
);
if (questionIndex === -1) {
console.warn(
`Question with id "${question.id}" not found in active quiz`
);
return;
}
set({ currentQuestionIndex: questionIndex });
}
};
},
{
name: "quiz-store"
}
)
);
// src/components/AlertDialog/AlertDialog.tsx
var import_react4 = require("react");
var import_jsx_runtime7 = require("react/jsx-runtime");
var SIZE_CLASSES4 = {
"extra-small": "w-screen max-w-[324px]",
small: "w-screen max-w-[378px]",
medium: "w-screen max-w-[459px]",
large: "w-screen max-w-[578px]",
"extra-large": "w-screen max-w-[912px]"
};
var AlertDialog = (0, import_react4.forwardRef)(
({
description,
cancelButtonLabel = "Cancelar",
submitButtonLabel = "Deletar",
title,
isOpen,
closeOnBackdropClick = true,
closeOnEscape = true,
className = "",
onSubmit,
onChangeOpen,
submitValue,
onCancel,
cancelValue,
size = "medium",
...props
}, ref) => {
(0, import_react4.useEffect)(() => {
if (!isOpen || !closeOnEscape) return;
const handleEscape = (event) => {
if (event.key === "Escape") {
onChangeOpen(false);
}
};
document.addEventListener("keydown", handleEscape);
return () => document.removeEventListener("keydown", handleEscape);
}, [isOpen, closeOnEscape]);
(0, import_react4.useEffect)(() => {
if (isOpen) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "unset";
}
return () => {
document.body.style.overflow = "unset";
};
}, [isOpen]);
const handleBackdropClick = (event) => {
if (event.target === event.currentTarget && closeOnBackdropClick) {
onChangeOpen(false);
}
};
const handleBackdropKeyDown = (event) => {
if (event.key === "Escape" && closeOnEscape) {
onChangeOpen(false);
}
};
const handleSubmit = () => {
onChangeOpen(false);
onSubmit?.(submitValue);
};
const handleCancel = () => {
onChangeOpen(false);
onCancel?.(cancelValue);
};
const sizeClasses = SIZE_CLASSES4[size];
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: isOpen && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
"div",
{
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm",
onClick: handleBackdropClick,
onKeyDown: handleBackdropKeyDown,
"data-testid": "alert-dialog-overlay",
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
"div",
{
ref,
className: cn(
"bg-background border border-border-100 rounded-lg shadow-lg p-6 m-3",
sizeClasses,
className
),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
"h2",
{
id: "alert-dialog-title",
className: "pb-3 text-xl font-semibold text-text-950",
children: title
}
),
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
"p",
{
id: "alert-dialog-description",
className: "text-text-700 text-sm",
children: description
}
),
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-row items-center justify-end pt-4 gap-3", children: [
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button_default, { variant: "outline", size: "small", onClick: handleCancel, children: cancelButtonLabel }),
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
Button_default,
{
variant: "solid",
size: "small",
action: "negative",
onClick: handleSubmit,
children: submitButtonLabel
}
)
] })
]
}
)
}
) });
}
);
AlertDialog.displayName = "AlertDialog";
// src/components/Modal/Modal.tsx
var import_react5 = require("react");
var import_phosphor_react3 = require("phosphor-react");
var import_jsx_runtime8 = require("react/jsx-runtime");
var SIZE_CLASSES5 = {
xs: "max-w-[360px]",
sm: "max-w-[420px]",
md: "max-w-[510px]",
lg: "max-w-[640px]",
xl: "max-w-[970px]"
};
var Modal = ({
isOpen,
onClose,
title,
children,
size = "md",
className = "",
closeOnBackdropClick = true,
closeOnEscape = true,
footer,
hideCloseButton = false
}) => {
(0, import_react5.useEffect)(() => {
if (!isOpen || !closeOnEscape) return;
const handleEscape = (event) => {
if (event.key === "Escape") {
onClose();
}
};
document.addEventListener("keydown", handleEscape);
return () => document.removeEventListener("keydown", handleEscape);
}, [isOpen, closeOnEscape, onClose]);
(0, import_react5.useEffect)(() => {
const originalOverflow = document.body.style.overflow;
if (isOpen) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = originalOverflow;
}
return () => {
document.body.style.overflow = originalOverflow;
};
}, [isOpen]);
const handleBackdropClick = (event) => {
if (closeOnBackdropClick && event.target === event.currentTarget) {
onClose();
}
};
const handleBackdropKeyDown = (event) => {
if (closeOnBackdropClick && (event.key === "Enter" || event.key === " ")) {
onClose();
}
};
if (!isOpen) return null;
const sizeClasses = SIZE_CLASSES5[size];
const baseClasses = "bg-secondary-50 rounded-3xl shadow-hard-shadow-2 border border-border-100 w-full mx-4";
const dialogResetClasses = "p-0 m-0 border-none outline-none max-h-none static";
const modalClasses = cn(
baseClasses,
sizeClasses,
dialogResetClasses,
className
);
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
"div",
{
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs",
onClick: handleBackdropClick,
onKeyDown: handleBackdropKeyDown,
role: "button",
tabIndex: closeOnBackdropClick ? 0 : -1,
"aria-label": "Fechar modal clicando no fundo",
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("dialog", { className: modalClasses, "aria-labelledby": "modal-title", open: true, children: [
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between px-6 py-6", children: [
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { id: "modal-title", className: "text-lg font-semibold text-text-950", children: title }),
!hideCloseButton && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
"button",
{
onClick: onClose,
className: "p-1 text-text-500 hover:text-text-700 hover:bg-background-50 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-indicator-info focus:ring-offset-2",
"aria-label": "Fechar modal",
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_phosphor_react3.X, { size: 18 })
}
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "px-6 pb-6", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "text-text-500 font-normal text-sm leading-6", children }) }),
footer && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex justify-end gap-3 px-6 pb-6", children: footer })
] })
}
);
};
var Modal_default = Modal;
// src/assets/img/simulated-result.png
var simulated_result_default = "../simulated-result-QN5HCUY5.png";
// src/components/Select/Select.tsx
var import_zustand3 = require("zustand");
var import_react6 = require("react");
var import_phosphor_react4 = require("phosphor-react");
var import_jsx_runtime9 = require("react/jsx-runtime");
var VARIANT_CLASSES = {
outlined: "border rounded-lg focus:border-primary-950",
underlined: "border-b focus:border-primary-950",
rounded: "border rounded-full focus:border-primary-950"
};
var SIZE_CLASSES6 = {
small: "text-sm",
medium: "text-md",
large: "text-lg",
"extra-large": "text-lg"
};
var HEIGHT_CLASSES = {
small: "h-8",
medium: "h-9",
large: "h-10",
"extra-large": "h-12"
};
var PADDING_CLASSES = {
small: "px-2 py-1",
medium: "px-3 py-2",
large: "px-4 py-3",
"extra-large": "px-5 py-4"
};
var SIDE_CLASSES = {
top: "bottom-full -translate-y-1",
right: "top-full translate-y-1",
bottom: "top-full translate-y-1",
left: "top-full translate-y-1"
};
var ALIGN_CLASSES = {
start: "left-0",
center: "left-1/2 -translate-x-1/2",
end: "right-0"
};
function createSelectStore(onValueChange) {
return (0, import_zustand3.create)((set) => ({
open: false,
setOpen: (open) => set({ open }),
value: "",
setValue: (value) => set({ value }),
selectedLabel: "",
setSelectedLabel: (label) => set({ selectedLabel: label }),
onValueChange
}));
}
var useSelectStore = (externalStore) => {
if (!externalStore) {
throw new Error(
"Component must be used within a Select (store is missing)"
);
}
return externalStore;
};
function getLabelAsNode(children) {
if (typeof children === "string" || typeof children === "number") {
return children;
}
const flattened = import_react6.Children.toArray(children);
if (flattened.length === 1) return flattened[0];
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: flattened });
}
var injectStore2 = (children, store, size, selectId) => {
return import_react6.Children.map(children, (child) => {
if ((0, import_react6.isValidElement)(child)) {
const typedChild = child;
const newProps = {
store
};
if (typedChild.type === SelectTrigger) {
newProps.size = size;
newProps.selectId = selectId;
}
if (typedChild.props.children) {
newProps.children = injectStore2(
typedChild.props.children,
store,
size,
selectId
);
}
return (0, import_react6.cloneElement)(typedChild, newProps);
}
return child;
});
};
var Select = ({
children,
defaultValue = "",
value: propValue,
onValueChange,
size = "small",
label,
helperText,
errorMessage,
id
}) => {
const storeRef = (0, import_react6.useRef)(null);
storeRef.current ??= createSelectStore(onValueChange);
const store = storeRef.current;
const selectRef = (0, import_react6.useRef)(null);
const { open, setOpen, setValue, selectedLabel } = (0, import_zustand3.useStore)(store, (s) => s);
const generatedId = (0, import_react6.useId)();
const selectId = id ?? `select-${generatedId}`;
const findLabelForValue = (children2, targetValue) => {
let found = null;
const search = (nodes) => {
import_react6.Children.forEach(nodes, (child) => {
if (!(0, import_react6.isValidElement)(child)) return;
const typedChild = child;
if (typedChild.type === SelectItem && typedChild.props.value === targetValue) {
if (typeof typedChild.props.children === "string")
found = typedChild.props.children;
}
if (typedChild.props.children && !found)
search(typedChild.props.children);
});
};
search(children2);
return found;
};
(0, import_react6.useEffect)(() => {
if (!selectedLabel && defaultValue) {
const label2 = findLabelForValue(children, defaultValue);
if (label2) store.setState({ selectedLabel: label2 });
}
}, [children, defaultValue, selectedLabel]);
(0, import_react6.useEffect)(() => {
const handleClickOutside = (event) => {
if (selectRef.current && !selectRef.current.contains(event.target)) {
setOpen(false);
}
};
const handleArrowKeys = (event) => {
const selectContent = selectRef.current?.querySelector('[role="menu"]');
if (selectContent) {
event.preventDefault();
const items = Array.from(
selectContent.querySelectorAll(
'[role="menuitem"]:not([aria-disabled="true"])'
)
).filter((el) => el instanceof HTMLElement);
const focused = document.activeElement;
const currentIndex = items.findIndex((item) => item === focused);
let nextIndex = 0;
if (event.key === "ArrowDown") {
nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % items.length;
} else {
nextIndex = currentIndex === -1 ? items.length - 1 : (currentIndex - 1 + items.length) % items.length;
}
items[nextIndex]?.focus();
}
};
if (open) {
document.addEventListener("mousedown", handleClickOutside);
document.addEventListener("keydown", handleArrowKeys);
}
return () => {
document.removeEventListener("mousedown", handleClickOutside);
document.removeEventListener("keydown", handleArrowKeys);
};
}, [open]);
(0, import_react6.useEffect)(() => {
if (propValue) {
setValue(propValue);
const label2 = findLabelForValue(children, propValue);
if (label2) store.setState({ selectedLabel: label2 });
}
}, [propValue]);
const sizeClasses = SIZE_CLASSES6[size];
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "w-full", children: [
label && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
"label",
{
htmlFor: selectId,
className: cn("block font-bold text-text-900 mb-1.5", sizeClasses),
children: label
}
),
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: cn("relative", sizeClasses), ref: selectRef, children: injectStore2(children, store, size, selectId) }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "mt-1.5 gap-1.5", children: [
helperText && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-sm text-text-500", children: helperText }),
errorMessage && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("p", { className: "flex gap-1 items-center text-sm text-indicator-error", children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_phosphor_react4.WarningCircle, { size: 16 }),
" ",
errorMessage
] })
] })
] });
};
var SelectValue = ({
placeholder,
store: externalStore
}) => {
const store = useSelectStore(externalStore);
const selectedLabel = (0, import_zustand3.useStore)(store, (s) => s.selectedLabel);
const value = (0, import_zustand3.useStore)(store, (s) => s.value);
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-inherit", children: selectedLabel || placeholder || value });
};
var SelectTrigger = (0, import_react6.forwardRef)(
({
className,
invalid = false,
variant = "outlined",
store: externalStore,
disabled,
size = "medium",
selectId,
...props
}, ref) => {
const store = useSelectStore(externalStore);
const open = (0, import_zustand3.useStore)(store, (s) => s.open);
const toggleOpen = () => store.setState({ open: !open });
const variantClasses = VARIANT_CLASSES[variant];
const heightClasses = HEIGHT_CLASSES[size];
const paddingClasses = PADDING_CLASSES[size];
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
"button",
{
ref,
id: selectId,
className: `
flex min-w-[220px] w-full items-center justify-between border-border-300
${heightClasses} ${paddingClasses}
${invalid && `${variant == "underlined" ? "border-b-2" : "border-2"} border-indicator-error text-text-600`}
${disabled ? "cursor-not-allowed text-text-400 pointer-events-none opacity-50" : "cursor-pointer hover:bg-background-50 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground"}
${!invalid && !disabled ? "text-text-700" : ""}
${variantClasses}
${className}
`,
onClick: toggleOpen,
"aria-expanded": open,
"aria-haspopup": "listbox",
"aria-controls": open ? "select-content" : void 0,
...props,
children: [
props.children,
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
import_phosphor_react4.CaretDown,
{
className: cn(
"h-[1em] w-[1em] opacity-50 transition-transform",
open ? "rotate-180" : ""
)
}
)
]
}
);
}
);
SelectTrigger.displayName = "SelectTrigger";
var SelectContent = (0, import_react6.forwardRef)(
({
children,
className,
align = "start",
side = "bottom",
store: externalStore,
...props
}, ref) => {
const store = useSelectStore(externalStore);
const open = (0, import_zustand3.useStore)(store, (s) => s.open);
if (!open) return null;
const getPositionClasses = () => `w-full min-w-full absolute ${SIDE_CLASSES[side]} ${ALIGN_CLASSES[align]}`;
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
"div",
{
role: "menu",
ref,
className: cn(
"bg-secondary z-50 min-w-[210px] overflow-hidden rounded-md border p-1 shadow-md border-border-100",
getPositionClasses(),
className
),
...props,
children
}
);
}
);
SelectContent.displayName = "SelectContent";
var SelectItem = (0, import_react6.forwardRef)(
({
className,
children,
value,
disabled = false,
store: externalStore,
...props
}, ref) => {
const store = useSelectStore(externalStore);
const {
value: selectedValue,
setValue,
setOpen,
setSelectedLabel,
onValueChange
} = (0, import_zustand3.useStore)(store, (s) => s);
const handleClick = (e) => {
const labelNode = getLabelAsNode(children);
if (!disabled) {
setValue(value);
setSelectedLabel(labelNode);
setOpen(false);
onValueChange?.(value);
}
props.onClick?.(e);
};
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
"div",
{
role: "menuitem",
"aria-disabled": disabled,
ref,
className: `
bg-secondary focus-visible:bg-background-50
relative flex select-none items-center gap-2 rounded-sm p-3 outline-none transition-colors [&>svg]:size-4 [&>svg]:shrink-0
${className}
${disabled ? "cursor-not-allowed text-text-400 pointer-events-none opacity-50" : "cursor-pointer hover:bg-background-50 text-text-700 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground"}
${selectedValue === value && "bg-background-50"}
`,
onClick: handleClick,
onKeyDown: (e) => {
if (e.key === "Enter" || e.key === " ") handleClick(e);
},
tabIndex: disabled ? -1 : 0,
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: selectedValue === value && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_phosphor_react4.Check, { className: "" }) }),
children
]
}
);
}
);
SelectItem.displayName = "SelectItem";
var Select_default = Select;
// src/components/Card/Card.tsx
var import_react7 = require("react");
// src/components/ProgressBar/ProgressBar.tsx
var import_jsx_runtime10 = require("react/jsx-runtime");
var SIZE_CLASSES7 = {
small: {
container: "h-1",
// 4px height (h-1 = 4px in Tailwind)
bar: "h-1",
// 4px height for the fill bar
spacing: "gap-2",
// 8px gap between label and progress bar
layout: "flex-col",
// vertical layout for small
borderRadius: "rounded-full"
// 9999px border radius
},
medium: {
container: "h-2",
// 8px height (h-2 = 8px in Tailwind)
bar: "h-2",
// 8px height for the fill bar
spacing: "gap-2",
// 8px gap between progress bar and label
layout: "flex-row items-center",
// horizontal layout for medium
borderRadius: "rounded-lg"
// 8px border radius
}
};
var VARIANT_CLASSES2 = {
blue: {
background: "bg-background-300",
// Background track color (#D5D4D4)
fill: "bg-primary-700"
// Blue for activity progress (#2271C4)
},
green: {
background: "bg-background-300",
// Background track color (#D5D4D4)
fill: "bg-success-200"
// Green for performance (#84D3A2)
}
};
var calculateProgressValues = (value, max) => {
const safeValue = isNaN(value) ? 0 : value;
const clampedValue = Math.max(0, Math.min(safeValue, max));
const percentage = max === 0 ? 0 : clampedValue / max * 100;
return { clampedValue, percentage };
};
var shouldShowHeader = (label, showPercentage, showHitCount) => {
return !!(label || showPercentage || showHitCount);
};
var getDisplayPriority = (showHitCount, showPercentage, label, clampedValue, max, percentage) => {
if (showHitCount) {
return {
type: "hitCount",
content: `${Math.round(clampedValue)} de ${max}`,
hasMetrics: true
};
}
if (showPercentage) {
return {
type: "percentage",
content: `${Math.round(percentage)}%`,
hasMetrics: true
};
}
return {
type: "label",
content: label,
hasMetrics: false
};
};
var getCompactLayoutConfig = ({
showPercentage,
showHitCount,
percentage,
clampedValue,
max,
label,
percentageClassName,
labelClassName
}) => {
const displayPriority = getDisplayPriority(
showHitCount,
showPercentage,
label,
clampedValue,
max,
percentage
);
return {
color: displayPriority.hasMetrics ? "text-primary-600" : "text-primary-700",
className: displayPriority.hasMetrics ? percentageClassName : labelClassName,
content: displayPriority.content
};
};
var getDefaultLayoutDisplayConfig = (size, label, showPercentage) => ({
showHeader: size === "small" && !!(label || showPercentage),
showPercentage: size === "medium" && showPercentage,
showLabel: size === "medium" && !!label && !showPercentage
// Only show label when percentage is not shown
});
var renderStackedHitCountDisplay = (showHitCount, showPercentage, clampedValue, max, percentage, percentageClassName) => {
if (!showHitCount && !showPercentage) return null;
const displayPriority = getDisplayPriority(
showHitCount,
showPercentage,
null,
// label is not relevant for stacked layout metrics display
clampedValue,
max,
percentage
);
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
"div",
{
className: cn(
"text-xs font-medium leading-[14px] text-right",
percentageClassName
),
children: displayPriority.type === "hitCount" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-success-200", children: Math.round(clampedValue) }),
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "text-text-600", children: [
" de ",
max
] })
] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(Text_default, { size: "xs", weight: "medium", className: "text-success-200", children: [
Math.round(percentage),
"%"
] })
}
);
};
var ProgressBarBase = ({
clampedValue,
max,
percentage,
label,
variantClasses,
containerClassName,
fillClassName
}) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
"div",
{
className: cn(
containerClassName,
variantClasses.background,
"overflow-hidden relative"
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
"progress",
{
value: clampedValue,
max,
"aria-label": typeof label === "string" ? `${label}: ${Math.round(percentage)}% complete` : `Progress: ${Math.round(percentage)}% of ${max}`,
className: "absolute inset-0 w-full h-full opacity-0"
}
),
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
"div",
{
className: cn(
fillClassName,
variantClasses.fill,
"transition-all duration-300 ease-out"
),
style: { width: `${percentage}%` }
}
)
]
}
);
var StackedLayout = ({
className,
label,
showPercentage,
showHitCount,
labelClassName,
percentageClassName,
clampedValue,
max,
percentage,
variantClasses,
dimensions
}) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
"div",
{
className: cn(
"flex flex-col items-start gap-2",
dimensions.width,
dimensions.height,
className
),
children: [
shouldShowHeader(label, showPercentage, showHitCount) && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-row justify-between items-center w-full h-[19px]", children: [
label && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
Text_default,
{
as: "div",
size: "md",
weight: "medium",
className: cn("text-text-600 leading-[19px]", labelClassName),
children: label
}
),
renderStackedHitCountDisplay(
showHitCount,
showPercentage,
clampedValue,
max,
percentage,
percentageClassName
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
ProgressBarBase,
{
clampedValue,
max,
percentage,
label,
variantClasses,
containerClassName: "w-full h-2 rounded-lg",
fillClassName: "h-2 rounded-lg shadow-hard-shadow-3"
}
)
]
}
);
var CompactLayout = ({
className,
label,
showPercentage,
showHitCount,
labelClassName,
percentageClassName,
clampedValue,
max,
percentage,
variantClasses,
dimensions
}) => {
const {
color,
className: compactClassName,
content
} = getCompactLayoutConfig({
showPercentage,
showHitCount,
percentage,
clampedValue,
max,
label,
percentageClassName,
labelClassName
});
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
"div",
{
className: cn(
"flex flex-col items-start gap-1",
dimensions.width,
dimensions.height,
className
),
children: [
shouldShowHeader(label, showPercentage, showHitCount) && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
Text_default,
{
as: "div",
size: "sm",
weight: "medium",
color,
className: cn("leading-4 w-full", compactClassName),
children: content
}
),
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
ProgressBarBase,
{
clampedValue,
max,
percentage,
label,
variantClasses,
containerClassName: "w-full h-1 rounded-full",
fillClassName: "h-1 rounded-full"
}
)
]
}
);
};
var DefaultLayout = ({
className,
size,
sizeClasses,
variantClasses,
label,
showPercentage,
labelClassName,
percentageClassName,
clampedValue,
max,
percentage
}) => {
const gapClass = size === "medium" ? "gap-2" : sizeClasses.spacing;
const progressBarClass = size === "medium" ? "flex-grow" : "w-full";
const displayConfig = getDefaultLayoutDisplayConfig(
size,
label,
showPercentage
);
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: cn("flex", sizeClasses.layout, gapClass, className), children: [
displayConfig.showHeader && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-row items-center justify-between w-full", children: [
label && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
Text_default,
{
as: "div",
size: "xs",
weight: "medium",
className: cn(
"text-text-950 leading-none tracking-normal text-center",
labelClassName
),
children: label
}
),
showPercentage && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
Text_default,
{
size: "xs",
weight: "medium",
className: cn(
"text-text-950 leading-none tracking-normal text-center",
percentageClassName
),
children: [
Math.round(percentage),
"%"
]
}
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
ProgressBarBase,
{
clampedValue,
max,
percentage,
label,
variantClasses,
containerClassName: cn(
progressBarClass,
sizeClasses.container,
sizeClasses.borderRadius
),
fillClassName: cn(
sizeClasses.bar,
sizeClasses.borderRadius,
"shadow-hard-shadow-3"
)
}
),
displayConfig.showPercentage && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
Text_default,
{
size: "xs",
weight: "medium",
className: cn(
"text-text-950 leading-none tracking-normal text-center flex-none",
percentageClassName
),
children: [
Math.round(percentage),
"%"
]
}
),
displayConfig.showLabel && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
Text_default,
{
as: "div",
size: "xs",
weight: "medium",
className: cn(
"text-text-950 leading-none tracking-normal text-center flex-none",
labelClassName
),
children: label
}
)
] });
};
var ProgressBar = ({
value,
max = 100,
size = "medium",
variant = "blue",
layout = "default",
label,
showPercentage = false,
showHitCount = false,
className = "",
labelClassName = "",
percentageClassName = "",
stackedWidth,
stackedHeight,
compactWidth,
compactHeight
}) => {
const { clampedValue, percentage } = calculateProgressValues(value, max);
const sizeClasses = SIZE_CLASSES7[size];
const variantClasses = VARIANT_CLASSES2[variant];
if (layout === "stacked") {
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
StackedLayout,
{
className,
label,
showPercentage,
showHitCount,
labelClassName,
percentageClassName,
clampedValue,
max,
percentage,
variantClasses,
dimensions: {
width: stackedWidth ?? "w-[380px]",
height: stackedHeight ?? "h-[35px]"
}
}
);
}
if (layout === "compact") {
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
CompactLayout,
{
className,
label,
showPercentage,
showHitCount,
labelClassName,
percentageClassName,
clampedValue,
max,
percentage,
variantClasses,
dimensions: {
width: compactWidth ?? "w-[131px]",
height: compactHeight ?? "h-[24px]"
}
}
);
}
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
DefaultLayout,
{
className,
size,
sizeClasses,
variantClasses,
label,
showPercentage,
labelClassName,
percentageClassName,
clampedValue,
max,
percentage
}
);
};
var ProgressBar_default = ProgressBar;
// src/components/Card/Card.tsx
var import_phosphor_react5 = require("phosphor-react");
var import_jsx_runtime11 = require("react/jsx-runtime");
var CARD_BASE_CLASSES = {
default: "w-full bg-background border border-border-50 rounded-xl",
compact: "w-full bg-background border border-border-50 rounded-lg",
minimal: "w-full bg-background border border-border-100 rounded-md"
};
var CARD_PADDING_CLASSES = {
none: "",
small: "p-2",
medium: "p-4",
large: "p-6"
};
var CARD_MIN_HEIGHT_CLASSES = {
none: "",
small: "min-h-16",
medium: "min-h-20",
large: "min-h-24"
};
var CARD_LAYOUT_CLASSES = {
horizontal: "flex flex-row",
vertical: "flex flex-col"
};
var CARD_CURSOR_CLASSES = {
default: "",
pointer: "cursor-pointer"
};
var CardBase = (0, import_react7.forwardRef)(
({
children,
variant = "default",
layout = "horizontal",
padding = "medium",
minHeight = "medium",
cursor = "default",
className = "",
...props
}, ref) => {
const baseClasses = CARD_BASE_CLASSES[variant];
const paddingClasses = CARD_PADDING_CLASSES[padding];
const minHeightClasses = CARD_MIN_HEIGHT_CLASSES[minHeight];
const layoutClasses = CARD_LAYOUT_CLASSES[layout];
const cursorClasses = CARD_CURSOR_CLASSES[cursor];
const combinedClasses = [
baseClasses,
paddingClasses,
minHeightClasses,
layoutClasses,
cursorClasses,
className
].filter(Boolean).join(" ");
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { ref, className: combinedClasses, ...props, children });
}
);
var ACTION_CARD_CLASSES = {
warning: "bg-warning-background",
success: "bg-success-300",
error: "bg-error-100",
info: "bg-info-background"
};
var ACTION_ICON_CLASSES = {
warning: "bg-warning-300 text-text",
success: "bg-yellow-300 text-text-950",
error: "bg-error-500 text-text",
info: "bg-info-500 text-text"
};
var ACTION_SUBTITLE_CLASSES = {
warning: "text-warning-600",
success: "text-success-700",
error: "text-error-700",
info: "text-info-700"
};
var ACTION_HEADER_CLASSES = {
warning: "text-warning-300",
success: "text-success-300",
error: "text-error-300",
info: "text-info-300"
};
var CardActivitiesResults = (0, import_react7.forwardRef)(
({
icon,
title,
subTitle,
header,
extended = false,
action = "success",
description,
className,
...props
}, ref) => {
const actionCardClasses = ACTION_CARD_CLASSES[action];
const actionIconClasses = ACTION_ICON_CLASSES[action];
const actionSubTitleClasses = ACTION_SUBTITLE_CLASSES[action];
const actionHeaderClasses = ACTION_HEADER_CLASSES[action];
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
"div",
{
ref,
className: cn(
"w-full flex flex-col border border-border-50 bg-background rounded-xl",
className
),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
"div",
{
className: cn(
"flex flex-col gap-1 items-center justify-center p-4",
actionCardClasses,
extended ? "rounded-t-xl" : "rounded-xl"
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"span",
{
className: cn(
"size-7.5 rounded-full flex items-center justify-center",
actionIconClasses
),
children: icon
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Text_default,
{
size: "2xs",
weight: "medium",
className: "text-text-800 uppercase truncate",
children: title
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"p",
{
className: cn("text-lg font-bold truncate", actionSubTitleClasses),
children: subTitle
}
)
]
}
),
extended && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col items-center gap-2.5 pb-9.5 pt-2.5", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"p",
{
className: cn(
"text-2xs font-medium uppercase truncate",
actionHeaderClasses
),
children: header
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Badge_default, { size: "large", action: "info", children: description })
] })
]
}
);
}
);
var CardQuestions = (0, import_react7.forwardRef)(
({
header,
state = "undone",
className,
onClickButton,
valueButton,
...props
}, ref) => {
const isDone = state === "done";
const stateLabel = isDone ? "Realizado" : "N\xE3o Realizado";
const buttonLabel = isDone ? "Ver Quest\xE3o" : "Responder";
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
CardBase,
{
ref,
layout: "horizontal",
padding: "medium",
minHeight: "medium",
className: cn("justify-between gap-4", className),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { className: "flex flex-col gap-1 flex-1 min-w-0", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "font-bold text-xs text-text-950 truncate", children: header }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-row gap-6 items-center", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Badge_default,
{
size: "medium",
variant: "solid",
action: isDone ? "success" : "error",
children: stateLabel
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "flex flex-row items-center gap-1 text-text-700 text-xs", children: [
isDone ? "Nota" : "Sem nota",
isDone && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Badge_default, { size: "medium", action: "success", children: "00" })
] })
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "flex-shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Button_default,
{
size: "extra-small",
onClick: () => onClickButton?.(valueButton),
className: "min-w-fit",
children: buttonLabel
}
) })
]
}
);
}
);
var CardProgress = (0, import_react7.forwardRef)(
({
header,
subhead,
initialDate,
endDate,
progress = 0,
direction = "horizontal",
icon,
color = "#B7DFFF",
progressVariant = "blue",
showDates = true,
className,
...props
}, ref) => {
const isHorizontal = direction === "horizontal";
const contentComponent = {
horizontal: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
showDates && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-row gap-6 items-center", children: [
initialDate && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "flex flex-row gap-1 items-center text-2xs", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-text-800 font-semibold", children: "In\xEDcio" }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-text-600", children: initialDate })
] }),
endDate && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "flex flex-row gap-1 items-center text-2xs", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-text-800 font-semibold", children: "Fim" }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-text-600", children: endDate })
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "grid grid-cols-[1fr_auto] items-center gap-2", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
ProgressBar_default,
{
size: "small",
value: progress,
variant: progressVariant,
"data-testid": "progress-bar"
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
Text_default,
{
size: "xs",
weight: "medium",
className: cn(
"text-text-950 leading-none tracking-normal text-center flex-none"
),
children: [
Math.round(progress),
"%"
]
}
)
] })
] }),
vertical: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-text-800", children: subhead })
};
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
CardBase,
{
ref,
layout: isHorizontal ? "horizontal" : "vertical",
padding: "none",
minHeight: "medium",
cursor: "pointer",
className: cn(isHorizontal ? "h-20" : "", className),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"div",
{
className: cn(
"flex justify-center items-center [&>svg]:size-6 text-text-950",
isHorizontal ? "min-w-[80px] min-h-[80px] rounded-l-xl" : "min-h-[50px] w-full rounded-t-xl",
!color.startsWith("#") ? `bg-${color}` : ""
),
style: color.startsWith("#") ? { backgroundColor: color } : void 0,
"data-testid": "icon-container",
children: icon
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
"div",
{
className: cn(
"p-4 flex flex-col justify-between w-full h-full",
!isHorizontal && "gap-4"
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text_default, { size: "sm", weight: "bold", className: "text-text-950 truncate", children: header }),
contentComponent[direction]
]
}
)
]
}
);
}
);
var CardTopic = (0, import_react7.forwardRef)(
({
header,
subHead,
progress,
showPercentage = false,
progressVariant = "blue",
className = "",
...props
}, ref) => {
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
CardBase,
{
ref,
layout: "vertical",
padding: "small",
minHeight: "medium",
cursor: "pointer",
className: cn("justify-center gap-2 py-2 px-4", className),
...props,
children: [
subHead && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-text-600 text-2xs flex flex-row gap-1", children: subHead.map((text, index) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react7.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { children: text }),
index < subHead.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { children: "\u2022" })
] }, `${text} - ${index}`)) }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-text-950 font-bold truncate", children: header }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "grid grid-cols-[1fr_auto] items-center gap-2", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
ProgressBar_default,
{
size: "small",
value: progress,
variant: progressVariant,
"data-testid": "progress-bar"
}
),
showPercentage && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
Text_default,
{
size: "xs",
weight: "medium",
className: cn(
"text-text-950 leading-none tracking-normal text-center flex-none"
),
children: [
Math.round(progress),
"%"
]
}
)
] })
]
}
);
}
);
var CardPerformance = (0, import_react7.forwardRef)(
({
header,
progress,
description = "Sem dados ainda! Voc\xEA ainda n\xE3o fez um question\xE1rio neste assunto.",
actionVariant = "button",
progressVariant = "blue",
labelProgress = "",
className = "",
onClickButton,
valueButton,
...props
}, ref) => {
const hasProgress = progress !== void 0;
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
CardBase,
{
ref,
layout: "horizontal",
padding: "medium",
minHeight: "none",
className: cn(
actionVariant == "caret" ? "cursor-pointer" : "",
className
),
onClick: () => actionVariant == "caret" && onClickButton?.(valueButton),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "w-full flex flex-col justify-between gap-2", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-row justify-between items-center gap-2", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-lg font-bold text-text-950 truncate flex-1 min-w-0", children: header }),
actionVariant === "button" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Button_default,
{
variant: "outline",
size: "extra-small",
onClick: () => onClickButton?.(valueButton),
className: "min-w-fit flex-shrink-0",
children: "Ver Aula"
}
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-full", children: hasProgress ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
ProgressBar_default,
{
value: progress,
label: `${progress}% ${labelProgress}`,
variant: progressVariant
}
) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-text-600 truncate", children: description }) })
] }),
actionVariant == "caret" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
import_phosphor_react5.CaretRight,
{
className: "size-4.5 text-text-800 cursor-pointer",
"data-testid": "caret-icon"
}
)
]
}
);
}
);
var CardResults = (0, import_react7.forwardRef)(
({
header,
correct_answers,
incorrect_answers,
icon,
direction = "col",
color = "#B7DFFF",
className,
...props
}, ref) => {
const isRow = direction == "row";
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
CardBase,
{
ref,
layout: "horizontal",
padding: "none",
minHeight: "medium",
className: cn("items-center cursor-pointer pr-4", className),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"div",
{
className: cn(
"flex justify-center items-center [&>svg]:size-8 text-text-950 min-w-20 max-w-20 min-h-20 h-full rounded-l-xl"
),
style: {
backgroundColor: color
},
children: icon
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
"div",
{
className: cn(
"p-4 flex justify-between w-full h-full",
isRow ? "flex-row items-center gap-2" : "flex-col"
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm font-bold text-text-950 truncate flex-1 min-w-0", children: header }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "flex flex-row gap-1 items-center", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
Badge_default,
{
action: "success",
variant: "solid",
size: "large",
iconLeft: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.CheckCircle, {}),
children: [
correct_answers,
" Corretas"
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
Badge_default,
{
action: "error",
variant: "solid",
size: "large",
iconLeft: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.XCircle, {}),
children: [
incorrect_answers,
" Incorretas"
]
}
)
] })
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.CaretRight, { className: "min-w-6 min-h-6 text-text-800" })
]
}
);
}
);
var CardStatus = (0, import_react7.forwardRef)(
({ header, className, status, label, ...props }, ref) => {
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
CardBase,
{
ref,
layout: "horizontal",
padding: "medium",
minHeight: "medium",
className: cn("items-center cursor-pointer", className),
...props,
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-between w-full h-full flex-row items-center gap-2", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm font-bold text-text-950 truncate flex-1 min-w-0", children: header }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "flex flex-row gap-1 items-center flex-shrink-0", children: [
status && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Badge_default,
{
action: status == "correct" ? "success" : "error",
variant: "solid",
size: "medium",
iconLeft: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.CheckCircle, {}),
children: status == "correct" ? "Correta" : "Incorreta"
}
),
label && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-text-800", children: label })
] }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.CaretRight, { className: "min-w-6 min-h-6 text-text-800 cursor-pointer flex-shrink-0 ml-2" })
] })
}
);
}
);
var CardSettings = (0, import_react7.forwardRef)(
({ header, className, icon, ...props }, ref) => {
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
CardBase,
{
ref,
layout: "horizontal",
padding: "small",
minHeight: "none",
className: cn(
"border-none items-center gap-2 text-text-700",
className
),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "[&>svg]:size-6", children: icon }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "w-full text-sm truncate", children: header }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.CaretRight, { size: 24, className: "cursor-pointer" })
]
}
);
}
);
var CardSupport = (0, import_react7.forwardRef)(
({ header, className, direction = "col", children, ...props }, ref) => {
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
CardBase,
{
ref,
layout: "horizontal",
padding: "medium",
minHeight: "none",
className: cn(
"border-none items-center gap-2 text-text-700",
className
),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
"div",
{
className: cn(
"w-full flex",
direction == "col" ? "flex-col" : "flex-row items-center"
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "w-full min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-sm text-text-950 font-bold truncate", children: header }) }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "flex flex-row gap-1", children })
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.CaretRight, { className: "text-text-800 cursor-pointer", size: 24 })
]
}
);
}
);
var CardForum = (0, import_react7.forwardRef)(
({
title,
content,
comments,
onClickComments,
valueComments,
onClickProfile,
valueProfile,
className = "",
date,
hour,
...props
}, ref) => {
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
CardBase,
{
ref,
layout: "horizontal",
padding: "medium",
minHeight: "none",
variant: "minimal",
className: cn("w-auto h-auto gap-3", className),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"button",
{
type: "button",
"aria-label": "Ver perfil",
onClick: () => onClickProfile?.(valueProfile),
className: "min-w-8 h-8 rounded-full bg-background-950"
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-2 flex-1 min-w-0", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-row gap-1 items-center flex-wrap", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs font-semibold text-primary-700 truncate", children: title }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-xs text-text-600", children: [
"\u2022 ",
date,
" \u2022 ",
hour
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-text-950 text-sm line-clamp-2 truncate", children: content }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
"button",
{
type: "button",
"aria-label": "Ver coment\xE1rios",
onClick: () => onClickComments?.(valueComments),
className: "text-text-600 flex flex-row gap-2 items-center",
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.ChatCircleText, { "aria-hidden": "true", size: 16 }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-xs", children: [
comments,
" respostas"
] })
]
}
)
] })
]
}
);
}
);
var CardAudio = (0, import_react7.forwardRef)(
({
src,
title,
onPlay,
onPause,
onEnded,
onAudioTimeUpdate,
loop = false,
preload = "metadata",
tracks,
className,
...props
}, ref) => {
const [isPlaying, setIsPlaying] = (0, import_react7.useState)(false);
const [currentTime, setCurrentTime] = (0, import_react7.useState)(0);
const [duration, setDuration] = (0, import_react7.useState)(0);
const [volume, setVolume] = (0, import_react7.useState)(1);
const [showVolumeControl, setShowVolumeControl] = (0, import_react7.useState)(false);
const audioRef = (0, import_react7.useRef)(null);
const formatTime = (time) => {
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
return `${minutes}:${seconds.toString().padStart(2, "0")}`;
};
const handlePlayPause = () => {
if (isPlaying) {
audioRef.current?.pause();
setIsPlaying(false);
onPause?.();
} else {
audioRef.current?.play();
setIsPlaying(true);
onPlay?.();
}
};
const handleTimeUpdate = () => {
const current = audioRef.current?.currentTime ?? 0;
const total = audioRef.current?.duration ?? 0;
setCurrentTime(current);
setDuration(total);
onAudioTimeUpdate?.(current, total);
};
const handleLoadedMetadata = () => {
setDuration(audioRef.current?.duration ?? 0);
};
const handleEnded = () => {
setIsPlaying(false);
setCurrentTime(0);
onEnded?.();
};
const handleProgressClick = (e) => {
const rect = e.currentTarget.getBoundingClientRect();
const clickX = e.clientX - rect.left;
const width = rect.width;
const percentage = clickX / width;
const newTime = percentage * duration;
if (audioRef.current) {
audioRef.current.currentTime = newTime;
}
setCurrentTime(newTime);
};
const handleVolumeChange = (e) => {
const newVolume = parseFloat(e.target.value);
setVolume(newVolume);
if (audioRef.current) {
audioRef.current.volume = newVolume;
}
};
const toggleVolumeControl = () => {
setShowVolumeControl(!showVolumeControl);
};
const getVolumeIcon = () => {
if (volume === 0) {
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.SpeakerSimpleX, {});
}
if (volume < 0.5) {
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.SpeakerLow, {});
}
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.SpeakerHigh, {});
};
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
CardBase,
{
ref,
layout: "horizontal",
padding: "medium",
minHeight: "none",
className: cn("w-auto h-14 items-center gap-2", className),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"audio",
{
ref: audioRef,
src,
loop,
preload,
onTimeUpdate: handleTimeUpdate,
onLoadedMetadata: handleLoadedMetadata,
onEnded: handleEnded,
"data-testid": "audio-element",
"aria-label": title,
children: tracks ? tracks.map((track) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"track",
{
kind: track.kind,
src: track.src,
srcLang: track.srcLang,
label: track.label,
default: track.default
},
track.src
)) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"track",
{
kind: "captions",
src: "data:text/vtt;base64,",
srcLang: "pt",
label: "Sem legendas dispon\xEDveis"
}
)
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"button",
{
type: "button",
onClick: handlePlayPause,
disabled: !src,
className: "cursor-pointer text-text-950 hover:text-primary-600 disabled:text-text-400 disabled:cursor-not-allowed",
"aria-label": isPlaying ? "Pausar" : "Reproduzir",
children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-6 h-6 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex gap-0.5", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-1 h-4 bg-current rounded-sm" }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-1 h-4 bg-current rounded-sm" })
] }) }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.Play, { size: 24 })
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-text-800 text-sm font-medium min-w-[2.5rem]", children: formatTime(currentTime) }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex-1 relative", "data-testid": "progress-bar", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"button",
{
type: "button",
className: "w-full h-2 bg-border-100 rounded-full cursor-pointer",
onClick: handleProgressClick,
onKeyDown: (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
handleProgressClick(
e
);
}
},
"aria-label": "Barra de progresso do \xE1udio",
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"div",
{
className: "h-full bg-primary-600 rounded-full transition-all duration-100",
style: {
width: duration > 0 ? `${currentTime / duration * 100}%` : "0%"
}
}
)
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-text-800 text-sm font-medium min-w-[2.5rem]", children: formatTime(duration) }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "relative", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"button",
{
type: "button",
onClick: toggleVolumeControl,
className: "cursor-pointer text-text-950 hover:text-primary-600",
"aria-label": "Controle de volume",
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-6 h-6 flex items-center justify-center", children: getVolumeIcon() })
}
),
showVolumeControl && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"button",
{
type: "button",
className: "absolute bottom-full right-0 mb-2 p-2 bg-background border border-border-100 rounded-lg shadow-lg focus:outline-none focus:ring-2 focus:ring-primary-500",
onKeyDown: (e) => {
if (e.key === "Escape") {
setShowVolumeControl(false);
}
},
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"input",
{
type: "range",
min: "0",
max: "1",
step: "0.1",
value: volume,
onChange: handleVolumeChange,
onKeyDown: (e) => {
if (e.key === "ArrowUp" || e.key === "ArrowRight") {
e.preventDefault();
const newVolume = Math.min(
1,
Math.round((volume + 0.1) * 10) / 10
);
setVolume(newVolume);
if (audioRef.current) audioRef.current.volume = newVolume;
} else if (e.key === "ArrowDown" || e.key === "ArrowLeft") {
e.preventDefault();
const newVolume = Math.max(
0,
Math.round((volume - 0.1) * 10) / 10
);
setVolume(newVolume);
if (audioRef.current) audioRef.current.volume = newVolume;
}
},
className: "w-20 h-2 bg-border-100 rounded-lg appearance-none cursor-pointer",
style: {
background: `linear-gradient(to right, #3b82f6 0%, #3b82f6 ${volume * 100}%, #e5e7eb ${volume * 100}%, #e5e7eb 100%)`
},
"aria-label": "Volume",
"aria-valuenow": Math.round(volume * 100),
"aria-valuemin": 0,
"aria-valuemax": 100
}
)
}
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
import_phosphor_react5.DotsThreeVertical,
{
size: 24,
className: "text-text-950 cursor-pointer hover:text-primary-600"
}
)
]
}
);
}
);
var SIMULADO_BACKGROUND_CLASSES = {
enem: "bg-exam-1",
prova: "bg-exam-2",
simuladao: "bg-exam-3",
vestibular: "bg-exam-4"
};
var CardSimulado = (0, import_react7.forwardRef)(
({ title, duration, info, backgroundColor, className, ...props }, ref) => {
const backgroundClass = SIMULADO_BACKGROUND_CLASSES[backgroundColor];
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
CardBase,
{
ref,
layout: "horizontal",
padding: "medium",
minHeight: "none",
cursor: "pointer",
className: cn(
`${backgroundClass} hover:shadow-soft-shadow-2 transition-shadow duration-200`,
className
),
...props,
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-between items-center w-full gap-4", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-1 flex-1 min-w-0", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text_default, { size: "lg", weight: "bold", className: "text-text-950 truncate", children: title }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-4 text-text-700", children: [
duration && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-1", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.Clock, { size: 16, className: "flex-shrink-0" }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text_default, { size: "sm", children: duration })
] }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text_default, { size: "sm", className: "truncate", children: info })
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
import_phosphor_react5.CaretRight,
{
size: 24,
className: "text-text-800 flex-shrink-0",
"data-testid": "caret-icon"
}
)
] })
}
);
}
);
var CardTest = (0, import_react7.forwardRef)(
({
title,
duration,
questionsCount,
additionalInfo,
selected = false,
onSelect,
className = "",
...props
}, ref) => {
const handleClick = () => {
if (onSelect) {
onSelect(!selected);
}
};
const handleKeyDown = (event) => {
if ((event.key === "Enter" || event.key === " ") && onSelect) {
event.preventDefault();
onSelect(!selected);
}
};
const isSelectable = !!onSelect;
const getQuestionsText = (count) => {
const singular = count === 1 ? "quest\xE3o" : "quest\xF5es";
return `${count} ${singular}`;
};
const displayInfo = questionsCount ? getQuestionsText(questionsCount) : additionalInfo || "";
const baseClasses = "flex flex-row items-center p-4 gap-2 w-full max-w-full bg-background shadow-soft-shadow-1 rounded-xl isolate border-0 text-left";
const interactiveClasses = isSelectable ? "cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-950 focus:ring-offset-2" : "";
const selectedClasses = selected ? "ring-2 ring-primary-950 ring-offset-2" : "";
if (isSelectable) {
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"button",
{
ref,
type: "button",
className: cn(
`${baseClasses} ${interactiveClasses} ${selectedClasses} ${className}`.trim()
),
onClick: handleClick,
onKeyDown: handleKeyDown,
"aria-pressed": selected,
...props,
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col justify-between gap-[27px] flex-grow min-h-[67px] w-full min-w-0", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Text_default,
{
size: "md",
weight: "bold",
className: "text-text-950 tracking-[0.2px] leading-[19px] truncate",
children: title
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-row justify-start items-end gap-4 w-full", children: [
duration && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-row items-center gap-1 flex-shrink-0", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.Clock, { size: 16, className: "text-text-700" }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Text_default,
{
size: "sm",
className: "text-text-700 leading-[21px] whitespace-nowrap",
children: duration
}
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Text_default,
{
size: "sm",
className: "text-text-700 leading-[21px] flex-grow truncate",
children: displayInfo
}
)
] })
] })
}
);
}
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"div",
{
ref,
className: cn(`${baseClasses} ${className}`.trim()),
...props,
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col justify-between gap-[27px] flex-grow min-h-[67px] w-full min-w-0", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Text_default,
{
size: "md",
weight: "bold",
className: "text-text-950 tracking-[0.2px] leading-[19px] truncate",
children: title
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-row justify-start items-end gap-4 w-full", children: [
duration && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-row items-center gap-1 flex-shrink-0", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_phosphor_react5.Clock, { size: 16, className: "text-text-700" }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Text_default,
{
size: "sm",
className: "text-text-700 leading-[21px] whitespace-nowrap",
children: duration
}
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Text_default,
{
size: "sm",
className: "text-text-700 leading-[21px] flex-grow truncate min-w-0",
children: displayInfo
}
)
] })
] })
}
);
}
);
var SIMULATION_TYPE_STYLES = {
enem: {
background: "bg-exam-1",
badge: "exam1",
text: "Enem"
},
prova: {
background: "bg-exam-2",
badge: "exam2",
text: "Prova"
},
simulado: {
background: "bg-exam-3",
badge: "exam3",
text: "Simulado"
},
vestibular: {
background: "bg-exam-4",
badge: "exam4",
text: "Vestibular"
}
};
var CardSimulationHistory = (0, import_react7.forwardRef)(({ data, onSimulationClick, className, ...props }, ref) => {
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
"div",
{
ref,
className: cn("w-full max-w-[992px] h-auto", className),
...props,
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-0", children: [
data.map((section, sectionIndex) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
"div",
{
className: cn(
"flex flex-row justify-center items-start px-4 py-6 gap-2 w-full bg-white",
sectionIndex === 0 ? "rounded-t-3xl" : ""
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Text_default,
{
size: "xs",
weight: "bold",
className: "text-text-800 w-11 flex-shrink-0",
children: section.date
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex flex-col gap-2 flex-1", children: section.simulations.map((simulation) => {
const typeStyles = SIMULATION_TYPE_STYLES[simulation.type];
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
CardBase,
{
layout: "horizontal",
padding: "medium",
minHeight: "none",
cursor: "pointer",
className: cn(
`${typeStyles.background} rounded-xl hover:shadow-soft-shadow-2
transition-shadow duration-200 h-auto min-h-[61px]`
),
onClick: () => onSimulationClick?.(simulation),
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex justify-between items-center w-full gap-2", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-2 flex-1 min-w-0", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Text_default,
{
size: "lg",
weight: "bold",
className: "text-text-950 truncate",
children: simulation.title
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2", children: [
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Badge_default,
{
variant: "examsOutlined",
action: typeStyles.badge,
size: "medium",
children: typeStyles.text
}
),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text_default, { size: "sm", className: "text-text-800 truncate", children: simulation.info })
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
import_phosphor_react5.CaretRight,
{
size: 24,
className: "text-text-800 flex-shrink-0",
"data-testid": "caret-icon"
}
)
] })
},
simulation.id
);
}) })
]
}
) }, section.date)),
data.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-full h-6 bg-white rounded-b-3xl" })
] })
}
);
});
// src/components/ProgressCircle/ProgressCircle.tsx
var import_jsx_runtime12 = require("react/jsx-runtime");
var SIZE_CLASSES8 = {
small: {
container: "w-[90px] h-[90px]",
// 90px circle from design specs
strokeWidth: 4,
// 4px stroke width - matches ProgressBar small (h-1)
textSize: "2xl",
// 24px for percentage (font-size: 24px)
textWeight: "medium",
// font-weight: 500
labelSize: "2xs",
// Will be overridden with custom 8px in className
labelWeight: "bold",
// font-weight: 700
spacing: "gap-0",
// Reduced gap between percentage and label for better spacing
contentWidth: "max-w-[50px]"
// Reduced width to fit text inside circle
},
medium: {
container: "w-[152px] h-[152px]",
// 151.67px ≈ 152px circle from design specs
strokeWidth: 8,
// 8px stroke width - matches ProgressBar medium (h-2)
textSize: "2xl",
// 24px for percentage (font-size: 24px)
textWeight: "medium",
// font-weight: 500
labelSize: "xs",
// 12px for status label (font-size: 12px)
labelWeight: "medium",
// font-weight: 500 (changed from bold)
spacing: "gap-1",
// 4px gap between percentage and label
contentWidth: "max-w-[90px]"
// Reduced width to fit text inside circle
}
};
var VARIANT_CLASSES3 = {
blue: {
background: "stroke-primary-100",
// Light blue background (#BBDCF7)
fill: "stroke-primary-700",
// Blue for activity progress (#2271C4)
textColor: "text-primary-700",
// Blue text color (#2271C4)
labelColor: "text-text-700"
// Gray text for label (#525252)
},
green: {
background: "stroke-background-300",
// Gray background (#D5D4D4 - matches design)
fill: "stroke-success-200",
// Green for performance (#84D3A2 - matches design)
textColor: "text-text-800",
// Dark gray text (#404040 - matches design)
labelColor: "text-text-600"
// Medium gray text for label (#737373 - matches design)
}
};
var ProgressCircle = ({
value,
max = 100,
size = "small",
variant = "blue",
label,
showPercentage = true,
className = "",
labelClassName = "",
percentageClassName = ""
}) => {
const safeValue = isNaN(value) ? 0 : value;
const clampedValue = Math.max(0, Math.min(safeValue, max));
const percentage = max === 0 ? 0 : clampedValue / max * 100;
const sizeClasses = SIZE_CLASSES8[size];
const variantClasses = VARIANT_CLASSES3[variant];
const radius = size === "small" ? 37 : 64;
const circumference = 2 * Math.PI * radius;
const strokeDashoffset = circumference - percentage / 100 * circumference;
const center = size === "small" ? 45 : 76;
const svgSize = size === "small" ? 90 : 152;
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
"div",
{
className: cn(
"relative flex flex-col items-center justify-center",
sizeClasses.container,
"rounded-lg",
className
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
"svg",
{
className: "absolute inset-0 transform -rotate-90",
width: svgSize,
height: svgSize,
viewBox: `0 0 ${svgSize} ${svgSize}`,
"aria-hidden": "true",
children: [
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
"circle",
{
cx: center,
cy: center,
r: radius,
fill: "none",
strokeWidth: sizeClasses.strokeWidth,
className: cn(variantClasses.background, "rounded-lg")
}
),
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
"circle",
{
cx: center,
cy: center,
r: radius,
fill: "none",
strokeWidth: sizeClasses.strokeWidth,
strokeLinecap: "round",
strokeDasharray: circumference,
strokeDashoffset,
className: cn(
variantClasses.fill,
"transition-all duration-500 ease-out shadow-soft-shadow-3 rounded-lg"
)
}
)
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
"progress",
{
value: clampedValue,
max,
"aria-label": typeof label === "string" ? label : "Progress",
className: "absolute opacity-0 w-0 h-0"
}
),
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
"div",
{
className: cn(
"relative z-10 flex flex-col items-center justify-center",
sizeClasses.spacing,
sizeClasses.contentWidth
),
children: [
showPercentage && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
Text_default,
{
size: sizeClasses.textSize,
weight: sizeClasses.textWeight,
className: cn(
"text-center w-full",
variantClasses.textColor,
percentageClassName
),
children: [
Math.round(percentage),
"%"
]
}
),
label && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
Text_default,
{
as: "span",
size: sizeClasses.labelSize,
weight: sizeClasses.labelWeight,
className: cn(
variantClasses.labelColor,
"text-center uppercase tracking-wide truncate w-full",
labelClassName
),
children: label
}
)
]
}
)
]
}
);
};
var ProgressCircle_default = ProgressCircle;
// src/components/MultipleChoice/MultipleChoice.tsx
var import_react10 = require("react");
// src/components/CheckBox/CheckboxList.tsx
var import_react9 = require("react");
var import_zustand4 = require("zustand");
// src/components/CheckBox/CheckBox.tsx
var import_react8 = require("react");
var import_phosphor_react6 = require("phosphor-react");
var import_jsx_runtime13 = require("react/jsx-runtime");
var SIZE_CLASSES9 = {
small: {
checkbox: "w-4 h-4",
// 16px x 16px
textSize: "sm",
spacing: "gap-1.5",
// 6px
borderWidth: "border-2",
iconSize: 14,
// pixels for Phosphor icons
labelHeight: "h-[21px]"
},
medium: {
checkbox: "w-5 h-5",
// 20px x 20px
textSize: "md",
spacing: "gap-2",
// 8px
borderWidth: "border-2",
iconSize: 16,
// pixels for Phosphor icons
labelHeight: "h-6"
},
large: {
checkbox: "w-6 h-6",
// 24px x 24px
textSize: "lg",
spacing: "gap-2",
// 8px
borderWidth: "border-[3px]",
// 3px border
iconSize: 20,
// pixels for Phosphor icons
labelHeight: "h-[27px]"
}
};
var BASE_CHECKBOX_CLASSES = "rounded border cursor-pointer transition-all duration-200 flex items-center justify-center focus:outline-none";
var STATE_CLASSES2 = {
default: {
unchecked: "border-border-400 bg-background hover:border-border-500",
checked: "border-primary-950 bg-primary-950 text-text hover:border-primary-800 hover:bg-primary-800"
},
hovered: {
unchecked: "border-border-500 bg-background",
checked: "border-primary-800 bg-primary-800 text-text"
},
focused: {
unchecked: "border-indicator-info bg-background ring-2 ring-indicator-info/20",
checked: "border-indicator-info bg-primary-950 text-text ring-2 ring-indicator-info/20"
},
invalid: {
unchecked: "border-error-700 bg-background hover:border-error-600",
checked: "border-error-700 bg-primary-950 text-text"
},
disabled: {
unchecked: "border-border-400 bg-background cursor-not-allowed opacity-40",
checked: "border-primary-600 bg-primary-600 text-text cursor-not-allowed opacity-40"
}
};
var CheckBox = (0, import_react8.forwardRef)(
({
label,
size = "medium",
state = "default",
indeterminate = false,
errorMessage,
helperText,
className = "",
labelClassName = "",
checked: checkedProp,
disabled,
id,
onChange,
...props
}, ref) => {
const generatedId = (0, import_react8.useId)();
const inputId = id ?? `checkbox-${generatedId}`;
const [internalChecked, setInternalChecked] = (0, import_react8.useState)(false);
const isControlled = checkedProp !== void 0;
const checked = isControlled ? checkedProp : internalChecked;
const handleChange = (event) => {
if (!isControlled) {
setInternalChecked(event.target.checked);
}
onChange?.(event);
};
const currentState = disabled ? "disabled" : state;
const sizeClasses = SIZE_CLASSES9[size];
const checkVariant = checked || indeterminate ? "checked" : "unchecked";
const stylingClasses = STATE_CLASSES2[currentState][checkVariant];
const borderWidthClass = state === "focused" || state === "hovered" && size === "large" ? "border-[3px]" : sizeClasses.borderWidth;
const checkboxClasses = cn(
BASE_CHECKBOX_CLASSES,
sizeClasses.checkbox,
borderWidthClass,
stylingClasses,
className
);
const renderIcon = () => {
if (indeterminate) {
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
import_phosphor_react6.Minus,
{
size: sizeClasses.iconSize,
weight: "bold",
color: "currentColor"
}
);
}
if (checked) {
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
import_phosphor_react6.Check,
{
size: sizeClasses.iconSize,
weight: "bold",
color: "currentColor"
}
);
}
return null;
};
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col", children: [
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
"div",
{
className: cn(
"flex flex-row items-center",
sizeClasses.spacing,
disabled ? "opacity-40" : ""
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
"input",
{
ref,
type: "checkbox",
id: inputId,
checked,
disabled,
onChange: handleChange,
className: "sr-only",
...props
}
),
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("label", { htmlFor: inputId, className: checkboxClasses, children: renderIcon() }),
label && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
"div",
{
className: cn(
"flex flex-row items-center",
sizeClasses.labelHeight
),
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
Text_default,
{
as: "label",
htmlFor: inputId,
size: sizeClasses.textSize,
weight: "normal",
className: cn(
"cursor-pointer select-none leading-[150%] flex items-center font-roboto",
labelClassName
),
children: label
}
)
}
)
]
}
),
errorMessage && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
Text_default,
{
size: "sm",
weight: "normal",
className: "mt-1.5",
color: "text-error-600",
children: errorMessage
}
),
helperText && !errorMessage && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
Text_default,
{
size: "sm",
weight: "normal",
className: "mt-1.5",
color: "text-text-500",
children: helperText
}
)
] });
}
);
CheckBox.displayName = "CheckBox";
var CheckBox_default = CheckBox;
// src/components/CheckBox/CheckboxList.tsx
var import_jsx_runtime14 = require("react/jsx-runtime");
var createCheckboxListStore = (name, defaultValues, disabled, onValuesChange) => (0, import_zustand4.create)((set, get) => ({
values: defaultValues,
setValues: (values) => {
if (!get().disabled) {
set({ values });
get().onValuesChange?.(values);
}
},
toggleValue: (value) => {
if (!get().disabled) {
const currentValues = get().values;
const newValues = currentValues.includes(value) ? currentValues.filter((v) => v !== value) : [...currentValues, value];
set({ values: newValues });
get().onValuesChange?.(newValues);
}
},
onValuesChange,
disabled,
name
}));
var useCheckboxListStore = (externalStore) => {
if (!externalStore) {
throw new Error("CheckboxListItem must be used within a CheckboxList");
}
return externalStore;
};
var injectStore3 = (children, store) => import_react9.Children.map(children, (child) => {
if (!(0, import_react9.isValidElement)(child)) return child;
const typedChild = child;
const shouldInject = typedChild.type === CheckboxListItem;
return (0, import_react9.cloneElement)(typedChild, {
...shouldInject ? { store } : {},
...typedChild.props.children ? { children: injectStore3(typedChild.props.children, store) } : {}
});
});
var CheckboxList = (0, import_react9.forwardRef)(
({
values: propValues,
defaultValues = [],
onValuesChange,
name: propName,
disabled = false,
className = "",
children,
...props
}, ref) => {
const generatedId = (0, import_react9.useId)();
const name = propName || `checkbox-list-${generatedId}`;
const storeRef = (0, import_react9.useRef)(null);
storeRef.current ??= createCheckboxListStore(
name,
defaultValues,
disabled,
onValuesChange
);
const store = storeRef.current;
const { setValues } = (0, import_zustand4.useStore)(store, (s) => s);
(0, import_react9.useEffect)(() => {
const currentValues = store.getState().values;
if (currentValues.length > 0 && onValuesChange) {
onValuesChange(currentValues);
}
}, []);
(0, import_react9.useEffect)(() => {
if (propValues !== void 0) {
setValues(propValues);
}
}, [propValues, setValues]);
(0, import_react9.useEffect)(() => {
store.setState({ disabled });
}, [disabled, store]);
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
"div",
{
ref,
className: cn("flex flex-col gap-2 w-full", className),
"aria-label": name,
...props,
children: injectStore3(children, store)
}
);
}
);
CheckboxList.displayName = "CheckboxList";
var CheckboxListItem = (0, import_react9.forwardRef)(
({
value,
store: externalStore,
disabled: itemDisabled,
size = "medium",
state = "default",
className = "",
id,
...props
}, ref) => {
const store = useCheckboxListStore(externalStore);
const {
values: groupValues,
toggleValue,
disabled: groupDisabled,
name
} = (0, import_zustand4.useStore)(store);
const generatedId = (0, import_react9.useId)();
const inputId = id ?? `checkbox-item-${generatedId}`;
const isChecked = groupValues.includes(value);
const isDisabled = groupDisabled || itemDisabled;
const currentState = isDisabled ? "disabled" : state;
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
CheckBox_default,
{
ref,
id: inputId,
name,
value,
checked: isChecked,
disabled: isDisabled,
size,
state: currentState,
className,
onChange: () => {
if (!isDisabled) {
toggleValue(value);
}
},
...props
}
);
}
);
CheckboxListItem.displayName = "CheckboxListItem";
var CheckboxList_default = CheckboxList;
// src/components/MultipleChoice/MultipleChoice.tsx
var import_phosphor_react7 = require("phosphor-react");
var import_jsx_runtime15 = require("react/jsx-runtime");
var MultipleChoiceList = ({
disabled = false,
className = "",
choices,
name,
selectedValues,
onHandleSelectedValues,
mode = "interactive"
}) => {
const [actualValue, setActualValue] = (0, import_react10.useState)(selectedValues);
(0, import_react10.useEffect)(() => {
setActualValue(selectedValues);
}, [selectedValues]);
const getStatusBadge = (status) => {
switch (status) {
case "correct":
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Badge_default, { variant: "solid", action: "success", iconLeft: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_phosphor_react7.CheckCircle, {}), children: "Resposta correta" });
case "incorrect":
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Badge_default, { variant: "solid", action: "error", iconLeft: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_phosphor_react7.XCircle, {}), children: "Resposta incorreta" });
default:
return null;
}
};
const getStatusStyles = (status) => {
switch (status) {
case "correct":
return "bg-success-background border-success-300";
case "incorrect":
return "bg-error-background border-error-300";
default:
return `bg-background border-border-100`;
}
};
const renderVisualCheckbox = (isSelected, isDisabled) => {
const checkboxClasses = cn(
"w-5 h-5 rounded border-2 cursor-default transition-all duration-200 flex items-center justify-center",
isSelected ? "border-primary-950 bg-primary-950 text-text" : "border-border-400 bg-background",
isDisabled && "opacity-40 cursor-not-allowed"
);
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: checkboxClasses, children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_phosphor_react7.Check, { size: 16, weight: "bold" }) });
};
if (mode === "readonly") {
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: cn("flex flex-col gap-2", className), children: choices.map((choice, i) => {
const isSelected = actualValue?.includes(choice.value) || false;
const statusStyles = getStatusStyles(choice.status);
const statusBadge = getStatusBadge(choice.status);
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
"div",
{
className: cn(
"flex flex-row justify-between gap-2 items-start p-2 rounded-lg transition-all",
statusStyles,
choice.disabled ? "opacity-50 cursor-not-allowed" : ""
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 flex-1", children: [
renderVisualCheckbox(isSelected, choice.disabled || disabled),
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
"span",
{
className: cn(
"flex-1",
isSelected || choice.status && choice.status != "neutral" ? "text-text-950" : "text-text-600",
choice.disabled || disabled ? "cursor-not-allowed" : "cursor-default"
),
children: choice.label
}
)
] }),
statusBadge && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex-shrink-0", children: statusBadge })
]
},
`readonly-${choice.value}-${i}`
);
}) });
}
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
"div",
{
className: cn(
"flex flex-row justify-between gap-2 items-start p-2 rounded-lg transition-all",
disabled ? "opacity-50 cursor-not-allowed" : "",
className
),
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
CheckboxList_default,
{
name,
values: actualValue,
onValuesChange: (v) => {
setActualValue(v);
onHandleSelectedValues?.(v);
},
disabled,
children: choices.map((choice, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
"div",
{
className: "flex flex-row gap-2 items-center",
children: [
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
CheckboxListItem,
{
value: choice.value,
id: `interactive-${choice.value}-${i}`,
disabled: choice.disabled || disabled
}
),
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
"label",
{
htmlFor: `interactive-${choice.value}-${i}`,
className: cn(
"flex-1",
actualValue?.includes(choice.value) ? "text-text-950" : "text-text-600",
choice.disabled || disabled ? "cursor-not-allowed" : "cursor-pointer"
),
children: choice.label
}
)
]
},
`interactive-${choice.value}-${i}`
))
}
)
}
);
};
// src/components/Quiz/Quiz.tsx
var import_jsx_runtime16 = require("react/jsx-runtime");
var Quiz = (0, import_react11.forwardRef)(({ children, className, ...props }, ref) => {
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
"div",
{
ref,
className: cn(
"w-full max-w-[1000px] flex flex-col mx-auto h-full relative not-lg:px-6",
className
),
...props,
children
}
);
});
var QuizHeaderResult = (0, import_react11.forwardRef)(
({ className, ...props }, ref) => {
const { getCurrentQuestion, getCurrentAnswer, getAllCurrentAnswer } = useQuizStore();
const currentQuestion = getCurrentQuestion();
const userAnswer = getCurrentAnswer();
const [isCorrect, setIsCorrect] = (0, import_react11.useState)(false);
(0, import_react11.useEffect)(() => {
if (currentQuestion?.type === "MULTIPLA_CHOICE" /* MULTIPLA_CHOICE */) {
const allCurrentAnswers = getAllCurrentAnswer();
const isCorrectOption = currentQuestion.options.filter(
(op) => op.isCorrect
);
if (allCurrentAnswers?.length !== isCorrectOption.length) {
setIsCorrect(false);
return;
}
setIsCorrect(true);
allCurrentAnswers.forEach((answer) => {
const findInCorrectOptions = isCorrectOption.find(
(op) => op.id === answer.optionId
);
if (!findInCorrectOptions) {
setIsCorrect(false);
}
});
} else {
setIsCorrect(
currentQuestion?.options.find((op) => op.id === userAnswer)?.isCorrect || false
);
}
}, [currentQuestion, getAllCurrentAnswer]);
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
"div",
{
ref,
className: cn(
"flex flex-row items-center gap-10 p-3.5 rounded-xl mb-4",
isCorrect ? "bg-success-background" : "bg-error-background",
className
),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-text-950 font-bold text-lg", children: "Resultado" }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-text-700 text-md", children: isCorrect ? "\u{1F389} Parab\xE9ns!!" : "N\xE3o foi dessa vez..." })
]
}
);
}
);
var QuizTitle = (0, import_react11.forwardRef)(
({ className, ...props }, ref) => {
const {
currentQuestionIndex,
getTotalQuestions,
getQuizTitle,
timeElapsed,
formatTime,
isStarted
} = useQuizStore();
const totalQuestions = getTotalQuestions();
const quizTitle = getQuizTitle();
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
"div",
{
ref,
className: cn(
"flex flex-row justify-center items-center relative p-2",
className
),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "flex flex-col gap-2 text-center", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-text-950 font-bold text-md", children: quizTitle }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-text-600 text-xs", children: totalQuestions > 0 ? `${currentQuestionIndex + 1} de ${totalQuestions}` : "0 de 0" })
] }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "absolute right-2", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Badge_default, { variant: "outlined", action: "info", iconLeft: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_phosphor_react8.Clock, {}), children: isStarted ? formatTime(timeElapsed) : "00:00" }) })
]
}
);
}
);
var QuizHeader = () => {
const { getCurrentQuestion } = useQuizStore();
const currentQuestion = getCurrentQuestion();
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
HeaderAlternative,
{
title: currentQuestion ? `Quest\xE3o ${currentQuestion.id}` : "Quest\xE3o",
subTitle: currentQuestion?.knowledgeMatrix?.[0]?.topicId ?? "",
content: currentQuestion?.questionText ?? ""
}
);
};
var QuizContent = (0, import_react11.forwardRef)(({ type = "Alternativas", className, variant, ...props }, ref) => {
const { getCurrentQuestion } = useQuizStore();
const currentQuestion = getCurrentQuestion();
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "px-4 pb-2 pt-6", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "font-bold text-lg text-text-950", children: type }) }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
"div",
{
ref,
className: cn(
"rounded-t-xl px-4 pt-4 pb-[80px] h-full flex flex-col gap-4 mb-auto",
className
),
...props,
children: currentQuestion && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
currentQuestion.type === "ALTERNATIVA" /* ALTERNATIVA */ && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(QuizAlternative, { variant }),
currentQuestion.type === "MULTIPLA_CHOICE" /* MULTIPLA_CHOICE */ && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(QuizMultipleChoice, { variant }),
currentQuestion.type === "DISSERTATIVA" /* DISSERTATIVA */ && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { children: "Componente de dissertativa" })
] })
}
)
] });
});
var QuizAlternative = ({ variant = "default" }) => {
const { getCurrentQuestion, selectAnswer, getCurrentAnswer } = useQuizStore();
const currentQuestion = getCurrentQuestion();
const currentAnswer = getCurrentAnswer();
const alternatives = currentQuestion?.options?.map((option) => {
let status = "neutral" /* NEUTRAL */;
if (variant === "result") {
const isCorrectOption = currentQuestion.options.find(
(op) => op.isCorrect
);
if (isCorrectOption?.id === option.id) {
status = "correct" /* CORRECT */;
} else if (currentAnswer === option.id && option.id !== isCorrectOption?.id) {
status = "incorrect" /* INCORRECT */;
}
}
return {
label: option.option,
value: option.id,
status
};
});
if (!alternatives)
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { children: "N\xE3o h\xE1 Alternativas" }) });
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "space-y-4", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
AlternativesList,
{
mode: variant === "default" ? "interactive" : "readonly",
name: `question-${currentQuestion?.id || "1"}`,
layout: "compact",
alternatives,
value: currentAnswer,
selectedValue: currentAnswer,
onValueChange: (value) => {
if (currentQuestion) {
selectAnswer(currentQuestion.id, value);
}
}
},
`question-${currentQuestion?.id || "1"}`
) });
};
var QuizMultipleChoice = ({
variant = "default"
}) => {
const { getCurrentQuestion, selectMultipleAnswer, getAllCurrentAnswer } = useQuizStore();
const currentQuestion = getCurrentQuestion();
const allCurrentAnswers = getAllCurrentAnswer();
const prevSelectedValuesRef = (0, import_react11.useRef)([]);
const prevQuestionIdRef = (0, import_react11.useRef)("");
const allCurrentAnswerIds = (0, import_react11.useMemo)(() => {
return allCurrentAnswers?.map((answer) => answer.optionId) || [];
}, [allCurrentAnswers]);
const selectedValues = (0, import_react11.useMemo)(() => {
return allCurrentAnswerIds?.filter((id) => id !== null) || [];
}, [allCurrentAnswerIds]);
const stableSelectedValues = (0, import_react11.useMemo)(() => {
const currentQuestionId = currentQuestion?.id || "";
const hasQuestionChanged = prevQuestionIdRef.current !== currentQuestionId;
if (hasQuestionChanged) {
prevQuestionIdRef.current = currentQuestionId;
prevSelectedValuesRef.current = selectedValues;
return selectedValues;
}
const hasValuesChanged = JSON.stringify(prevSelectedValuesRef.current) !== JSON.stringify(selectedValues);
if (hasValuesChanged) {
prevSelectedValuesRef.current = selectedValues;
return selectedValues;
}
return prevSelectedValuesRef.current;
}, [selectedValues, currentQuestion?.id]);
const handleSelectedValues = (0, import_react11.useCallback)(
(values) => {
if (currentQuestion) {
selectMultipleAnswer(currentQuestion.id, values);
}
},
[currentQuestion, selectMultipleAnswer]
);
const questionKey = (0, import_react11.useMemo)(
() => `question-${currentQuestion?.id || "1"}`,
[currentQuestion?.id]
);
const choices = currentQuestion?.options?.map((option) => {
let status = "neutral" /* NEUTRAL */;
if (variant === "result") {
const isAllCorrectOptionId = currentQuestion.options.filter((op) => op.isCorrect).map((op) => op.id);
if (isAllCorrectOptionId.includes(option.id)) {
status = "correct" /* CORRECT */;
} else if (allCurrentAnswerIds?.includes(option.id) && !isAllCorrectOptionId.includes(option.id)) {
status = "incorrect" /* INCORRECT */;
}
}
return {
label: option.option,
value: option.id,
status
};
});
if (!choices)
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { children: "N\xE3o h\xE1 Escolhas Multiplas" }) });
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "space-y-4", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
MultipleChoiceList,
{
choices,
name: questionKey,
selectedValues: stableSelectedValues,
onHandleSelectedValues: handleSelectedValues,
mode: variant === "default" ? "interactive" : "readonly"
},
questionKey
) });
};
var QuizQuestionList = ({
filterType = "all",
onQuestionClick
} = {}) => {
const {
getQuestionsGroupedBySubject,
goToQuestion,
getQuestionStatusFromUserAnswers
} = useQuizStore();
const groupedQuestions = getQuestionsGroupedBySubject();
const getQuestionStatus = (questionId) => {
return getQuestionStatusFromUserAnswers(questionId);
};
const filteredGroupedQuestions = Object.entries(groupedQuestions).reduce(
(acc, [subjectId, questions]) => {
const filteredQuestions = questions.filter((question) => {
const status = getQuestionStatus(question.id);
switch (filterType) {
case "answered":
return status === "answered";
case "unanswered":
return status === "unanswered";
default:
return true;
}
});
if (filteredQuestions.length > 0) {
acc[subjectId] = filteredQuestions;
}
return acc;
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{}
);
const getQuestionIndex = (questionId) => {
const { bySimulated, byActivity, byQuestionary } = useQuizStore.getState();
const quiz = bySimulated ?? byActivity ?? byQuestionary;
if (!quiz) return 0;
const index = quiz.questions.findIndex((q) => q.id === questionId);
return index + 1;
};
const getStatusLabel = (status) => {
switch (status) {
case "answered":
return "Respondida";
case "skipped":
return "N\xE3o respondida";
default:
return "Em branco";
}
};
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "space-y-6 px-4", children: Object.entries(filteredGroupedQuestions).map(
([subjectId, questions]) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("section", { className: "flex flex-col gap-2", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: "pt-6 pb-4 flex flex-row gap-2", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "bg-primary-500 p-1 rounded-sm flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_phosphor_react8.BookOpen, { size: 17, className: "text-white" }) }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-text-800 font-bold text-lg", children: subjectId })
] }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-2", children: questions.map((question) => {
const status = getQuestionStatus(question.id);
const questionNumber = getQuestionIndex(question.id);
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
CardStatus,
{
header: `Quest\xE3o ${questionNumber.toString().padStart(2, "0")}`,
label: getStatusLabel(status),
onClick: () => {
goToQuestion(questionNumber - 1);
onQuestionClick?.();
}
},
question.id
);
}) })
] }, subjectId)
) });
};
var QuizFooter = (0, import_react11.forwardRef)(
({
className,
onGoToSimulated,
onDetailResult,
variant = "default",
...props
}, ref) => {
const {
currentQuestionIndex,
getUserAnswers,
getTotalQuestions,
goToNextQuestion,
goToPreviousQuestion,
getUnansweredQuestionsFromUserAnswers,
getCurrentAnswer,
skipQuestion,
getCurrentQuestion,
getQuestionStatusFromUserAnswers,
getActiveQuiz
} = useQuizStore();
const totalQuestions = getTotalQuestions();
const isFirstQuestion = currentQuestionIndex === 0;
const isLastQuestion = currentQuestionIndex === totalQuestions - 1;
const currentAnswer = getCurrentAnswer();
const currentQuestion = getCurrentQuestion();
const isCurrentQuestionSkipped = currentQuestion ? getQuestionStatusFromUserAnswers(currentQuestion.id) === "skipped" : false;
const [alertDialogOpen, setAlertDialogOpen] = (0, import_react11.useState)(false);
const [modalResultOpen, setModalResultOpen] = (0, import_react11.useState)(false);
const [modalNavigateOpen, setModalNavigateOpen] = (0, import_react11.useState)(false);
const [filterType, setFilterType] = (0, import_react11.useState)("all");
const unansweredQuestions = getUnansweredQuestionsFromUserAnswers();
const userAnswers = getUserAnswers();
const allQuestions = getTotalQuestions();
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
"footer",
{
ref,
className: cn(
"w-full px-2 bg-background lg:max-w-[1000px] not-lg:max-w-[calc(100vw-32px)] border-t border-border-50 fixed bottom-0 min-h-[80px] flex flex-row justify-between items-center",
className
),
...props,
children: variant === "default" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-row items-center gap-1", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
IconButton_default,
{
icon: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_phosphor_react8.SquaresFour, { size: 24, className: "text-text-950" }),
size: "md",
onClick: () => setModalNavigateOpen(true)
}
),
isFirstQuestion ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
Button_default,
{
variant: "outline",
size: "small",
onClick: () => {
skipQuestion();
goToNextQuestion();
},
children: "Pular"
}
) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
Button_default,
{
size: "medium",
variant: "link",
action: "primary",
iconLeft: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_phosphor_react8.CaretLeft, { size: 18 }),
onClick: () => {
goToPreviousQuestion();
},
children: "Voltar"
}
)
] }),
!isFirstQuestion && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
Button_default,
{
size: "small",
variant: "outline",
action: "primary",
onClick: () => {
skipQuestion();
goToNextQuestion();
},
children: "Pular"
}
),
isLastQuestion ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
Button_default,
{
size: "medium",
variant: "solid",
action: "primary",
disabled: !currentAnswer && !isCurrentQuestionSkipped,
onClick: () => {
if (unansweredQuestions.length > 0) {
setAlertDialogOpen(true);
} else {
setModalResultOpen(true);
}
},
children: "Finalizar"
}
) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
Button_default,
{
size: "medium",
variant: "link",
action: "primary",
iconRight: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_phosphor_react8.CaretRight, { size: 18 }),
disabled: !currentAnswer && !isCurrentQuestionSkipped,
onClick: () => {
goToNextQuestion();
},
children: "Avan\xE7ar"
}
)
] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex flex-row items-center justify-end w-full", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Button_default, { variant: "solid", action: "primary", size: "medium", children: "Ver Resolu\xE7\xE3o" }) })
}
),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
AlertDialog,
{
isOpen: alertDialogOpen,
onChangeOpen: setAlertDialogOpen,
title: "Finalizar simulado?",
description: unansweredQuestions.length > 0 ? `Voc\xEA deixou as quest\xF5es ${unansweredQuestions.join(", ")} sem resposta. Finalizar agora pode impactar seu desempenho.` : "Tem certeza que deseja finalizar o simulado?",
cancelButtonLabel: "Voltar e revisar",
submitButtonLabel: "Finalizar Mesmo Assim",
onSubmit: () => {
setModalResultOpen(true);
}
}
),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
Modal_default,
{
isOpen: modalResultOpen,
onClose: () => setModalResultOpen(false),
title: "",
closeOnBackdropClick: false,
closeOnEscape: false,
hideCloseButton: true,
size: "md",
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col w-full h-full items-center justify-center gap-4", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
"img",
{
src: simulated_result_default,
alt: "Simulated Result",
className: "w-[282px] h-auto object-cover"
}
),
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col gap-2 text-center", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h2", { className: "text-text-950 font-bold text-lg", children: "Voc\xEA concluiu o simulado!" }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("p", { className: "text-text-500 font-sm", children: [
"Voc\xEA acertou",
" ",
(() => {
const activeQuiz = getActiveQuiz();
if (!activeQuiz) return 0;
return userAnswers.filter((answer) => {
const question = activeQuiz.quiz.questions.find(
(q) => q.id === answer.questionId
);
const isCorrectOption = question?.options.find(
(op) => op.isCorrect
);
return question && answer.optionId === isCorrectOption?.id;
}).length;
})(),
" ",
"de ",
allQuestions,
" quest\xF5es."
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "px-6 flex flex-row items-center gap-2 w-full", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
Button_default,
{
variant: "outline",
className: "w-full",
size: "small",
onClick: onGoToSimulated,
children: "Ir para simulados"
}
),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Button_default, { className: "w-full", onClick: onDetailResult, children: "Detalhar resultado" })
] })
] })
}
),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
Modal_default,
{
isOpen: modalNavigateOpen,
onClose: () => setModalNavigateOpen(false),
title: "Quest\xF5es",
size: "lg",
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col w-full h-full", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-row justify-between items-center py-6 pt-6 pb-4 border-b border-border-200", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-text-950 font-bold text-lg", children: "Filtrar por" }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "max-w-[266px]", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Select_default, { value: filterType, onValueChange: setFilterType, children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SelectTrigger, { variant: "rounded", className: "max-w-[266px]", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SelectValue, { placeholder: "Selecione uma op\xE7\xE3o" }) }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(SelectContent, { children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SelectItem, { value: "all", children: "Todas" }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SelectItem, { value: "unanswered", children: "Em branco" }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SelectItem, { value: "answered", children: "Respondidas" })
] })
] }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex flex-col gap-2 not-lg:h-[calc(100vh-200px)] lg:max-h-[687px] overflow-y-auto", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
QuizQuestionList,
{
filterType,
onQuestionClick: () => setModalNavigateOpen(false)
}
) })
] })
}
)
] });
}
);
var QuizResultHeaderTitle = (0, import_react11.forwardRef)(({ className, ...props }, ref) => {
const { bySimulated } = useQuizStore();
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
"div",
{
ref,
className: cn("flex flex-row pt-4 justify-between", className),
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-text-950 font-bold text-2xl", children: "Resultado" }),
bySimulated && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Badge_default, { variant: "solid", action: "info", children: bySimulated.category })
]
}
);
});
var QuizResultTitle = (0, import_react11.forwardRef)(({ className, ...props }, ref) => {
const { getQuizTitle } = useQuizStore();
const quizTitle = getQuizTitle();
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
"p",
{
className: cn("pt-6 pb-4 text-text-950 font-bold text-lg", className),
ref,
...props,
children: quizTitle
}
);
});
var QuizResultPerformance = (0, import_react11.forwardRef)(
({ ...props }, ref) => {
const {
getTotalQuestions,
timeElapsed,
formatTime,
bySimulated,
byActivity,
byQuestionary,
getUserAnswerByQuestionId
} = useQuizStore();
const totalQuestions = getTotalQuestions();
const quiz = bySimulated || byActivity || byQuestionary;
let correctAnswers = 0;
let correctEasyAnswers = 0;
let correctMediumAnswers = 0;
let correctDifficultAnswers = 0;
let totalEasyQuestions = 0;
let totalMediumQuestions = 0;
let totalDifficultQuestions = 0;
if (quiz) {
quiz.questions.forEach((question) => {
const userAnswerItem = getUserAnswerByQuestionId(question.id);
const userAnswer = userAnswerItem?.optionId;
const isCorrectOption = question?.options.find((op) => op.isCorrect);
const isCorrect = userAnswer && userAnswer === isCorrectOption?.id;
if (isCorrect) {
correctAnswers++;
}
if (question.difficulty === "FACIL" /* FACIL */) {
totalEasyQuestions++;
if (isCorrect) {
correctEasyAnswers++;
}
} else if (question.difficulty === "MEDIO" /* MEDIO */) {
totalMediumQuestions++;
if (isCorrect) {
correctMediumAnswers++;
}
} else if (question.difficulty === "DIFICIL" /* DIFICIL */) {
totalDifficultQuestions++;
if (isCorrect) {
correctDifficultAnswers++;
}
}
});
}
const percentage = totalQuestions > 0 ? Math.round(correctAnswers / totalQuestions * 100) : 0;
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
"div",
{
className: "flex flex-row gap-6 p-6 rounded-xl bg-background justify-between",
ref,
...props,
children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "relative", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
ProgressCircle_default,
{
size: "medium",
variant: "green",
value: percentage,
showPercentage: false,
label: ""
}
),
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex items-center gap-1 mb-1", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_phosphor_react8.Clock, { size: 12, weight: "regular", className: "text-text-800" }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-2xs font-medium text-text-800", children: formatTime(timeElapsed) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "text-2xl font-medium text-text-800 leading-7", children: [
correctAnswers,
" de ",
totalQuestions
] }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "text-2xs font-medium text-text-600 mt-1", children: "Corretas" })
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col gap-4 w-full", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
ProgressBar_default,
{
className: "w-full",
layout: "stacked",
variant: "green",
value: correctEasyAnswers,
max: totalEasyQuestions,
label: "F\xE1ceis",
showHitCount: true,
labelClassName: "text-base font-medium text-text-800 leading-none",
percentageClassName: "text-xs font-medium leading-[14px] text-right"
}
),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
ProgressBar_default,
{
className: "w-full",
layout: "stacked",
variant: "green",
value: correctMediumAnswers,
max: totalMediumQuestions,
label: "M\xE9dias",
showHitCount: true,
labelClassName: "text-base font-medium text-text-800 leading-none",
percentageClassName: "text-xs font-medium leading-[14px] text-right"
}
),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
ProgressBar_default,
{
className: "w-full",
layout: "stacked",
variant: "green",
value: correctDifficultAnswers,
max: totalDifficultQuestions,
label: "Dif\xEDceis",
showHitCount: true,
labelClassName: "text-base font-medium text-text-800 leading-none",
percentageClassName: "text-xs font-medium leading-[14px] text-right"
}
)
] })
]
}
);
}
);
var QuizListResult = (0, import_react11.forwardRef)(({ className, onSubjectClick, ...props }, ref) => {
const {
getQuestionsGroupedBySubject,
isQuestionAnswered,
getUserAnswerByQuestionId
} = useQuizStore();
const groupedQuestions = getQuestionsGroupedBySubject();
const subjectsStats = Object.entries(groupedQuestions).map(
([subjectId, questions]) => {
let correct = 0;
let incorrect = 0;
questions.forEach((question) => {
if (isQuestionAnswered(question.id)) {
const userAnswerItem = getUserAnswerByQuestionId(question.id);
const userAnswer = userAnswerItem?.optionId;
const isCorrectOption = question?.options.find((op) => op.isCorrect);
if (userAnswer === isCorrectOption?.id) {
correct++;
} else {
incorrect++;
}
}
});
return {
subject: subjectId,
correct,
incorrect,
total: questions.length
};
}
);
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("section", { ref, className, ...props, children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "pt-6 pb-4 text-text-950 font-bold text-lg", children: "Mat\xE9rias" }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-2", children: subjectsStats.map((subject) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
CardResults,
{
onClick: () => onSubjectClick?.(subject.subject),
className: "max-w-full",
header: subject.subject,
correct_answers: subject.correct,
incorrect_answers: subject.incorrect,
icon: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_phosphor_react8.Book, { size: 20 }),
direction: "row"
}
) }, subject.subject)) })
] });
});
var QuizListResultByMateria = ({
subject,
onQuestionClick
}) => {
const { getQuestionsGroupedBySubject, getUserAnswerByQuestionId } = useQuizStore();
const groupedQuestions = getQuestionsGroupedBySubject();
const answeredQuestions = groupedQuestions[subject] || [];
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "w-full max-w-[1000px] flex flex-col mx-auto h-full relative not-lg:px-6", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex flex-row pt-4 justify-between", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-text-950 font-bold text-2xl", children: subject }) }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("section", { className: "flex flex-col ", children: [
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "pt-6 pb-4 text-text-950 font-bold text-lg", children: "Resultado das quest\xF5es" }),
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "flex flex-col gap-2 pt-4", children: answeredQuestions.map((question) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
CardStatus,
{
className: "max-w-full",
header: `Quest\xE3o ${question.id}`,
status: (() => {
const userAnswer = getUserAnswerByQuestionId(question.id);
const isCorrectOption = question?.options.find(
(op) => op.isCorrect
);
return userAnswer && userAnswer.optionId === isCorrectOption?.id ? "correct" : "incorrect";
})(),
onClick: () => onQuestionClick?.(question)
}
) }, question.id)) })
] })
] });
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Quiz,
QuizAlternative,
QuizContent,
QuizFooter,
QuizHeader,
QuizHeaderResult,
QuizListResult,
QuizListResultByMateria,
QuizMultipleChoice,
QuizQuestionList,
QuizResultHeaderTitle,
QuizResultPerformance,
QuizResultTitle,
QuizTitle
});
//# sourceMappingURL=index.js.map