analytica-frontend-lib 1.1.90 → 1.1.92

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8389,7 +8389,6 @@ var DownloadButton = ({
8389
8389
  await new Promise((resolve) => setTimeout(resolve, 200));
8390
8390
  }
8391
8391
  } catch (error) {
8392
- console.error(`Erro ao baixar ${item.label}:`, error);
8393
8392
  onDownloadError?.(
8394
8393
  item.type,
8395
8394
  error instanceof Error ? error : new Error(`Falha ao baixar ${item.label}`)
@@ -8615,6 +8614,7 @@ var VideoPlayer = ({
8615
8614
  const [showControls, setShowControls] = (0, import_react27.useState)(true);
8616
8615
  const [hasCompleted, setHasCompleted] = (0, import_react27.useState)(false);
8617
8616
  const [showCaptions, setShowCaptions] = (0, import_react27.useState)(false);
8617
+ const [subtitlesValidation, setSubtitlesValidation] = (0, import_react27.useState)("idle");
8618
8618
  (0, import_react27.useEffect)(() => {
8619
8619
  setHasCompleted(false);
8620
8620
  }, [src]);
@@ -8900,11 +8900,12 @@ var VideoPlayer = ({
8900
8900
  setShowSpeedMenu(!showSpeedMenu);
8901
8901
  }, [showSpeedMenu]);
8902
8902
  const toggleCaptions = (0, import_react27.useCallback)(() => {
8903
- if (!trackRef.current?.track || !subtitles) return;
8903
+ if (!trackRef.current?.track || !subtitles || subtitlesValidation !== "valid")
8904
+ return;
8904
8905
  const newShowCaptions = !showCaptions;
8905
8906
  setShowCaptions(newShowCaptions);
8906
- trackRef.current.track.mode = newShowCaptions && subtitles ? "showing" : "hidden";
8907
- }, [showCaptions, subtitles]);
8907
+ trackRef.current.track.mode = newShowCaptions ? "showing" : "hidden";
8908
+ }, [showCaptions, subtitles, subtitlesValidation]);
8908
8909
  const checkVideoCompletion = (0, import_react27.useCallback)(
8909
8910
  (progressPercent) => {
8910
8911
  if (progressPercent >= 95 && !hasCompleted) {
@@ -8932,11 +8933,58 @@ var VideoPlayer = ({
8932
8933
  setDuration(videoRef.current.duration);
8933
8934
  }
8934
8935
  }, []);
8936
+ (0, import_react27.useEffect)(() => {
8937
+ const controller = new AbortController();
8938
+ const validateSubtitles = async () => {
8939
+ if (!subtitles) {
8940
+ setSubtitlesValidation("idle");
8941
+ return;
8942
+ }
8943
+ setSubtitlesValidation("validating");
8944
+ try {
8945
+ if (subtitles.startsWith("data:")) {
8946
+ setSubtitlesValidation("valid");
8947
+ return;
8948
+ }
8949
+ const response = await fetch(subtitles, {
8950
+ method: "HEAD",
8951
+ signal: controller.signal
8952
+ });
8953
+ if (response.ok) {
8954
+ const contentType = response.headers.get("content-type");
8955
+ const isValidType = !contentType || contentType.includes("text/vtt") || contentType.includes("text/plain") || contentType.includes("application/octet-stream");
8956
+ if (isValidType) {
8957
+ setSubtitlesValidation("valid");
8958
+ } else {
8959
+ setSubtitlesValidation("invalid");
8960
+ console.warn(
8961
+ `Subtitles URL has invalid content type: ${contentType}`
8962
+ );
8963
+ }
8964
+ } else {
8965
+ setSubtitlesValidation("invalid");
8966
+ console.warn(
8967
+ `Subtitles URL returned status: ${response.status} ${response.statusText}`
8968
+ );
8969
+ }
8970
+ } catch (error) {
8971
+ if (error instanceof Error && error.name === "AbortError") {
8972
+ return;
8973
+ }
8974
+ console.warn("Subtitles URL validation failed:", error);
8975
+ setSubtitlesValidation("invalid");
8976
+ }
8977
+ };
8978
+ validateSubtitles();
8979
+ return () => {
8980
+ controller.abort();
8981
+ };
8982
+ }, [subtitles]);
8935
8983
  (0, import_react27.useEffect)(() => {
8936
8984
  if (trackRef.current?.track) {
8937
- trackRef.current.track.mode = showCaptions && subtitles ? "showing" : "hidden";
8985
+ trackRef.current.track.mode = showCaptions && subtitles && subtitlesValidation === "valid" ? "showing" : "hidden";
8938
8986
  }
8939
- }, [subtitles, showCaptions]);
8987
+ }, [subtitles, showCaptions, subtitlesValidation]);
8940
8988
  (0, import_react27.useEffect)(() => {
8941
8989
  const handleVisibilityChange = () => {
8942
8990
  if (document.hidden && isPlaying && videoRef.current) {
@@ -8990,61 +9038,57 @@ var VideoPlayer = ({
8990
9038
  const getBottomControlsOpacity = (0, import_react27.useCallback)(() => {
8991
9039
  return showControls ? "opacity-100" : "opacity-0";
8992
9040
  }, [showControls]);
9041
+ const seekBackward = (0, import_react27.useCallback)(() => {
9042
+ if (videoRef.current) {
9043
+ videoRef.current.currentTime -= 10;
9044
+ }
9045
+ }, []);
9046
+ const seekForward = (0, import_react27.useCallback)(() => {
9047
+ if (videoRef.current) {
9048
+ videoRef.current.currentTime += 10;
9049
+ }
9050
+ }, []);
9051
+ const increaseVolume = (0, import_react27.useCallback)(() => {
9052
+ handleVolumeChange(Math.min(100, volume * 100 + 10));
9053
+ }, [handleVolumeChange, volume]);
9054
+ const decreaseVolume = (0, import_react27.useCallback)(() => {
9055
+ handleVolumeChange(Math.max(0, volume * 100 - 10));
9056
+ }, [handleVolumeChange, volume]);
8993
9057
  const handleVideoKeyDown = (0, import_react27.useCallback)(
8994
9058
  (e) => {
8995
- if (e.key) {
8996
- e.stopPropagation();
8997
- showControlsWithTimer();
8998
- }
8999
- switch (e.key) {
9000
- case " ":
9001
- case "Enter":
9002
- e.preventDefault();
9003
- togglePlayPause();
9004
- break;
9005
- case "ArrowLeft":
9006
- e.preventDefault();
9007
- if (videoRef.current) {
9008
- videoRef.current.currentTime -= 10;
9009
- }
9010
- break;
9011
- case "ArrowRight":
9012
- e.preventDefault();
9013
- if (videoRef.current) {
9014
- videoRef.current.currentTime += 10;
9015
- }
9016
- break;
9017
- case "ArrowUp":
9018
- e.preventDefault();
9019
- handleVolumeChange(Math.min(100, volume * 100 + 10));
9020
- break;
9021
- case "ArrowDown":
9022
- e.preventDefault();
9023
- handleVolumeChange(Math.max(0, volume * 100 - 10));
9024
- break;
9025
- case "m":
9026
- case "M":
9027
- e.preventDefault();
9028
- toggleMute();
9029
- break;
9030
- case "f":
9031
- case "F":
9032
- e.preventDefault();
9033
- toggleFullscreen();
9034
- break;
9035
- default:
9036
- break;
9059
+ if (!e.key) return;
9060
+ e.stopPropagation();
9061
+ showControlsWithTimer();
9062
+ const keyHandlers = {
9063
+ " ": togglePlayPause,
9064
+ Enter: togglePlayPause,
9065
+ ArrowLeft: seekBackward,
9066
+ ArrowRight: seekForward,
9067
+ ArrowUp: increaseVolume,
9068
+ ArrowDown: decreaseVolume,
9069
+ m: toggleMute,
9070
+ M: toggleMute,
9071
+ f: toggleFullscreen,
9072
+ F: toggleFullscreen
9073
+ };
9074
+ const handler = keyHandlers[e.key];
9075
+ if (handler) {
9076
+ e.preventDefault();
9077
+ handler();
9037
9078
  }
9038
9079
  },
9039
9080
  [
9040
9081
  showControlsWithTimer,
9041
9082
  togglePlayPause,
9042
- handleVolumeChange,
9043
- volume,
9083
+ seekBackward,
9084
+ seekForward,
9085
+ increaseVolume,
9086
+ decreaseVolume,
9044
9087
  toggleMute,
9045
9088
  toggleFullscreen
9046
9089
  ]
9047
9090
  );
9091
+ const groupedSubTitleValid = subtitles && subtitlesValidation === "valid";
9048
9092
  return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: cn("flex flex-col", className), children: [
9049
9093
  (title || subtitleText) && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "bg-subject-1 px-8 py-4 flex items-end justify-between min-h-20", children: [
9050
9094
  /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex flex-col gap-1", children: [
@@ -9118,9 +9162,9 @@ var VideoPlayer = ({
9118
9162
  {
9119
9163
  ref: trackRef,
9120
9164
  kind: "captions",
9121
- src: subtitles || "data:text/vtt;charset=utf-8,WEBVTT",
9165
+ src: groupedSubTitleValid ? subtitles : "data:text/vtt;charset=utf-8,WEBVTT",
9122
9166
  srcLang: "pt-br",
9123
- label: subtitles ? "Legendas em Portugu\xEAs" : "Sem legendas dispon\xEDveis",
9167
+ label: groupedSubTitleValid ? "Legendas em Portugu\xEAs" : "Sem legendas dispon\xEDveis",
9124
9168
  default: false
9125
9169
  }
9126
9170
  )
@@ -9209,7 +9253,7 @@ var VideoPlayer = ({
9209
9253
  showSlider: !isUltraSmallMobile
9210
9254
  }
9211
9255
  ),
9212
- subtitles && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
9256
+ groupedSubTitleValid && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
9213
9257
  IconButton_default,
9214
9258
  {
9215
9259
  icon: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_phosphor_react19.ClosedCaptioning, { size: getIconSize2() }),
@@ -10207,7 +10251,6 @@ var useQuizStore = (0, import_zustand9.create)()(
10207
10251
  const activityId = quiz.id;
10208
10252
  const userId = get().getUserId();
10209
10253
  if (!userId || userId === "") {
10210
- console.warn("selectAnswer called before userId is set");
10211
10254
  return;
10212
10255
  }
10213
10256
  const question = quiz.questions.find((q) => q.id === questionId);
@@ -10241,7 +10284,6 @@ var useQuizStore = (0, import_zustand9.create)()(
10241
10284
  const activityId = quiz.id;
10242
10285
  const userId = get().getUserId();
10243
10286
  if (!userId || userId === "") {
10244
- console.warn("selectMultipleAnswer called before userId is set");
10245
10287
  return;
10246
10288
  }
10247
10289
  const question = quiz.questions.find((q) => q.id === questionId);
@@ -10276,16 +10318,10 @@ var useQuizStore = (0, import_zustand9.create)()(
10276
10318
  const activityId = quiz.id;
10277
10319
  const userId = get().getUserId();
10278
10320
  if (!userId || userId === "") {
10279
- console.warn(
10280
- "selectDissertativeAnswer called before userId is set"
10281
- );
10282
10321
  return;
10283
10322
  }
10284
10323
  const question = quiz.questions.find((q) => q.id === questionId);
10285
10324
  if (!question || question.questionType !== "DISSERTATIVA" /* DISSERTATIVA */) {
10286
- console.warn(
10287
- "selectDissertativeAnswer called for non-dissertative question"
10288
- );
10289
10325
  return;
10290
10326
  }
10291
10327
  const existingAnswerIndex = userAnswers.findIndex(
@@ -10319,7 +10355,6 @@ var useQuizStore = (0, import_zustand9.create)()(
10319
10355
  const activityId = quiz.id;
10320
10356
  const userId = get().getUserId();
10321
10357
  if (!userId || userId === "") {
10322
- console.warn("skipQuestion called before userId is set");
10323
10358
  return;
10324
10359
  }
10325
10360
  const existingAnswerIndex = userAnswers.findIndex(
@@ -10352,7 +10387,6 @@ var useQuizStore = (0, import_zustand9.create)()(
10352
10387
  const activityId = quiz.id;
10353
10388
  const userId = get().getUserId();
10354
10389
  if (!userId || userId === "") {
10355
- console.warn("addUserAnswer called before userId is set");
10356
10390
  return;
10357
10391
  }
10358
10392
  const question = quiz.questions.find((q) => q.id === questionId);
@@ -10580,9 +10614,6 @@ var useQuizStore = (0, import_zustand9.create)()(
10580
10614
  );
10581
10615
  }
10582
10616
  if (questionIndex === -1) {
10583
- console.warn(
10584
- `Question with id "${question.id}" not found in active quiz`
10585
- );
10586
10617
  return;
10587
10618
  }
10588
10619
  set({ currentQuestionIndex: questionIndex });
@@ -11939,34 +11970,16 @@ var QuizFooter = (0, import_react36.forwardRef)(
11939
11970
  children: "Avan\xE7ar"
11940
11971
  }
11941
11972
  )
11942
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "flex flex-row items-center justify-between w-full", children: [
11943
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { children: quiz?.canRetry && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
11944
- Button_default,
11945
- {
11946
- variant: "link",
11947
- action: "primary",
11948
- size: "medium",
11949
- onClick: () => openModal("modalResolution"),
11950
- children: "Ver Resolu\xE7\xE3o"
11951
- }
11952
- ) }),
11953
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
11954
- Button_default,
11955
- {
11956
- variant: "solid",
11957
- action: "primary",
11958
- size: "medium",
11959
- onClick: () => {
11960
- if (quiz?.canRetry) {
11961
- onRepeat?.();
11962
- } else {
11963
- openModal("modalResolution");
11964
- }
11965
- },
11966
- children: quiz?.canRetry ? `Repetir ${getTypeLabel(quiz.type)}` : "Ver Resolu\xE7\xE3o"
11967
- }
11968
- )
11969
- ] })
11973
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "flex flex-row items-center justify-center w-full", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
11974
+ Button_default,
11975
+ {
11976
+ variant: "link",
11977
+ action: "primary",
11978
+ size: "medium",
11979
+ onClick: () => openModal("modalResolution"),
11980
+ children: "Ver resolu\xE7\xE3o"
11981
+ }
11982
+ ) })
11970
11983
  }
11971
11984
  ),
11972
11985
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
@@ -12245,17 +12258,32 @@ var QuizHeaderResult = (0, import_react37.forwardRef)(
12245
12258
  );
12246
12259
  }
12247
12260
  );
12248
- var QuizResultHeaderTitle = (0, import_react37.forwardRef)(({ className, showBadge = true, ...props }, ref) => {
12261
+ var QuizResultHeaderTitle = (0, import_react37.forwardRef)(({ className, showBadge = true, onRepeat, canRetry, ...props }, ref) => {
12249
12262
  const { quiz } = useQuizStore();
12250
12263
  return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
12251
12264
  "div",
12252
12265
  {
12253
12266
  ref,
12254
- className: cn("flex flex-row pt-4 justify-between", className),
12267
+ className: cn(
12268
+ "flex flex-row pt-4 justify-between items-center",
12269
+ className
12270
+ ),
12255
12271
  ...props,
12256
12272
  children: [
12257
12273
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("p", { className: "text-text-950 font-bold text-2xl", children: "Resultado" }),
12258
- showBadge && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(QuizBadge, { subtype: quiz?.subtype || void 0 })
12274
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex flex-row gap-3 items-center", children: [
12275
+ canRetry && onRepeat && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
12276
+ Button_default,
12277
+ {
12278
+ variant: "solid",
12279
+ action: "primary",
12280
+ size: "medium",
12281
+ onClick: onRepeat,
12282
+ children: "Repetir question\xE1rio"
12283
+ }
12284
+ ),
12285
+ showBadge && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(QuizBadge, { subtype: quiz?.subtype || void 0 })
12286
+ ] })
12259
12287
  ]
12260
12288
  }
12261
12289
  );
@@ -12447,14 +12475,15 @@ var QuizListResult = (0, import_react37.forwardRef)(({ className, onSubjectClick
12447
12475
  });
12448
12476
  var QuizListResultByMateria = ({
12449
12477
  subject,
12450
- onQuestionClick
12478
+ onQuestionClick,
12479
+ subjectName
12451
12480
  }) => {
12452
12481
  const { getQuestionsGroupedBySubject, getQuestionIndex } = useQuizStore();
12453
12482
  const groupedQuestions = getQuestionsGroupedBySubject();
12454
12483
  const answeredQuestions = groupedQuestions[subject] || [];
12455
12484
  const formattedQuestions = subject == "all" ? Object.values(groupedQuestions).flat() : answeredQuestions;
12456
12485
  return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex flex-col", children: [
12457
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "flex flex-row pt-4 justify-between", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("p", { className: "text-text-950 font-bold text-2xl", children: answeredQuestions?.[0]?.knowledgeMatrix?.[0]?.subject?.name ?? "Sem mat\xE9ria" }) }),
12486
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "flex flex-row pt-4 justify-between", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("p", { className: "text-text-950 font-bold text-2xl", children: subjectName || formattedQuestions?.[0]?.knowledgeMatrix?.[0]?.subject?.name || "Sem mat\xE9ria" }) }),
12458
12487
  /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("section", { className: "flex flex-col ", children: [
12459
12488
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("p", { className: "pt-6 pb-4 text-text-950 font-bold text-lg", children: "Resultado das quest\xF5es" }),
12460
12489
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("ul", { className: "flex flex-col gap-2 pt-4", children: formattedQuestions.map((question) => {