@cuemath/leap 4.0.5-as3 → 4.0.5-as5

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.
@@ -1 +1 @@
1
- {"version":3,"file":"fraud-alert-modal-helpers.js","sources":["../../../../src/features/fraud-detection/fraud-alert-modal/fraud-alert-modal-helpers.ts"],"sourcesContent":["/**\n * Converts a base64 image string to a File object\n * @param base64String - Base64 encoded image string (with or without data URI prefix)\n * @param fileName - Name for the generated file\n * @returns File object or null if conversion fails\n */\nexport const base64ToFile = (base64String: string | null, fileName: string): File | null => {\n if (!base64String) return null;\n\n try {\n const base64Data = base64String.includes(',') ? base64String.split(',')[1] : base64String;\n\n const mimeType = base64String.includes('data:')\n ? base64String.split(';')[0]?.split(':')[1]\n : 'image/png';\n\n if (base64Data) {\n const byteCharacters = atob(base64Data);\n const byteNumbers = new Array(byteCharacters.length);\n\n for (let i = 0; i < byteCharacters.length; i++) {\n byteNumbers[i] = byteCharacters.charCodeAt(i);\n }\n\n const byteArray = new Uint8Array(byteNumbers);\n\n // Create File directly from byte array\n return new File([byteArray], fileName, { type: mimeType });\n }\n\n return null;\n } catch (error) {\n console.error('Failed to convert base64 to file:', error);\n\n return null;\n }\n};\n"],"names":["base64ToFile","base64String","fileName","_a","base64Data","mimeType","byteCharacters","byteNumbers","i","byteArray","error"],"mappings":"AAMa,MAAAA,IAAe,CAACC,GAA6BC,MAAkC;AAA/E,MAAAC;AACP,MAAA,CAACF,EAAqB,QAAA;AAEtB,MAAA;AACI,UAAAG,IAAaH,EAAa,SAAS,GAAG,IAAIA,EAAa,MAAM,GAAG,EAAE,CAAC,IAAIA,GAEvEI,IAAWJ,EAAa,SAAS,OAAO,KAC1CE,IAAAF,EAAa,MAAM,GAAG,EAAE,CAAC,MAAzB,gBAAAE,EAA4B,MAAM,KAAK,KACvC;AAEJ,QAAIC,GAAY;AACR,YAAAE,IAAiB,KAAKF,CAAU,GAChCG,IAAc,IAAI,MAAMD,EAAe,MAAM;AAEnD,eAASE,IAAI,GAAGA,IAAIF,EAAe,QAAQE;AACzC,QAAAD,EAAYC,CAAC,IAAIF,EAAe,WAAWE,CAAC;AAGxC,YAAAC,IAAY,IAAI,WAAWF,CAAW;AAGrC,aAAA,IAAI,KAAK,CAACE,CAAS,GAAGP,GAAU,EAAE,MAAMG,EAAA,CAAU;AAAA,IAC3D;AAEO,WAAA;AAAA,WACAK,GAAO;AACN,mBAAA,MAAM,qCAAqCA,CAAK,GAEjD;AAAA,EACT;AACF;"}
1
+ {"version":3,"file":"fraud-alert-modal-helpers.js","sources":["../../../../src/features/fraud-detection/fraud-alert-modal/fraud-alert-modal-helpers.ts"],"sourcesContent":["/**\n * Converts a base64 image string to a File object\n * @param base64String - Base64 encoded image string (with or without data URI prefix)\n * @param fileName - Name for the generated file\n * @returns File object or null if conversion fails\n */\nexport const base64ToFile = (base64String: string | null, fileName: string): File | null => {\n if (!base64String) return null;\n\n try {\n const base64Data = base64String.includes(',') ? base64String.split(',')[1] : base64String;\n\n const mimeType = base64String.includes('data:')\n ? base64String.split(';')[0]?.split(':')[1]\n : 'image/png';\n\n if (base64Data) {\n const byteCharacters = atob(base64Data);\n const byteNumbers = new Array(byteCharacters.length);\n\n for (let i = 0; i < byteCharacters.length; i++) {\n byteNumbers[i] = byteCharacters.charCodeAt(i);\n }\n\n const byteArray = new Uint8Array(byteNumbers);\n\n // Create File directly from byte array\n return new File([byteArray], fileName, { type: mimeType });\n }\n\n return null;\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error('Failed to convert base64 to file:', error);\n\n return null;\n }\n};\n"],"names":["base64ToFile","base64String","fileName","_a","base64Data","mimeType","byteCharacters","byteNumbers","i","byteArray","error"],"mappings":"AAMa,MAAAA,IAAe,CAACC,GAA6BC,MAAkC;AAA/E,MAAAC;AACP,MAAA,CAACF,EAAqB,QAAA;AAEtB,MAAA;AACI,UAAAG,IAAaH,EAAa,SAAS,GAAG,IAAIA,EAAa,MAAM,GAAG,EAAE,CAAC,IAAIA,GAEvEI,IAAWJ,EAAa,SAAS,OAAO,KAC1CE,IAAAF,EAAa,MAAM,GAAG,EAAE,CAAC,MAAzB,gBAAAE,EAA4B,MAAM,KAAK,KACvC;AAEJ,QAAIC,GAAY;AACR,YAAAE,IAAiB,KAAKF,CAAU,GAChCG,IAAc,IAAI,MAAMD,EAAe,MAAM;AAEnD,eAASE,IAAI,GAAGA,IAAIF,EAAe,QAAQE;AACzC,QAAAD,EAAYC,CAAC,IAAIF,EAAe,WAAWE,CAAC;AAGxC,YAAAC,IAAY,IAAI,WAAWF,CAAW;AAGrC,aAAA,IAAI,KAAK,CAACE,CAAS,GAAGP,GAAU,EAAE,MAAMG,EAAA,CAAU;AAAA,IAC3D;AAEO,WAAA;AAAA,WACAK,GAAO;AAEN,mBAAA,MAAM,qCAAqCA,CAAK,GAEjD;AAAA,EACT;AACF;"}
@@ -1,41 +1,79 @@
1
- import { jsxs as g, jsx as e } from "react/jsx-runtime";
2
- import { memo as b, useCallback as I, useEffect as L } from "react";
3
- import { useLocalPeer as y, useRemotePeers as C, useCaptureMediaStreamImage as F } from "@cuemath/av";
4
- import { ILLUSTRATIONS as R } from "../../../assets/illustrations/illustrations.js";
5
- import X from "../../ui/modals/use-modal-actions.js";
6
- import o from "../../ui/text/text.js";
7
- import _ from "../../ui/image/image.js";
8
- import d from "../../ui/layout/flex-view.js";
9
- import w from "../../ui/buttons/button/button.js";
10
- import s from "../../ui/separator/separator.js";
11
- import E from "../../ui/modals/use-modal-params.js";
12
- import M from "../../worksheet/worksheet/hooks/use-s3-helper.js";
13
- import { base64ToFile as f } from "./fraud-alert-modal-helpers.js";
14
- const B = { type: "fraud-login" }, D = b(function() {
15
- const { closeModal: u } = X(), { teacherId: $, studentId: i, handleLogout: c } = E(), p = I(() => {
16
- u(), c();
17
- }, [u, c]), t = y(), r = C().find((a) => a.userId === $), n = F(), l = M({
18
- studentId: i,
19
- query: B
20
- });
21
- return L(() => {
1
+ import { jsxs as L, jsx as e } from "react/jsx-runtime";
2
+ import { memo as R, useCallback as c, useEffect as X } from "react";
3
+ import { useLocalPeer as w, useRemotePeers as F, useCaptureMediaStreamImage as S } from "@cuemath/av";
4
+ import { ILLUSTRATIONS as M } from "../../../assets/illustrations/illustrations.js";
5
+ import x from "../../ui/modals/use-modal-actions.js";
6
+ import i from "../../ui/text/text.js";
7
+ import B from "../../ui/image/image.js";
8
+ import l from "../../ui/layout/flex-view.js";
9
+ import D from "../../ui/buttons/button/button.js";
10
+ import m from "../../ui/separator/separator.js";
11
+ import T from "../../ui/modals/use-modal-params.js";
12
+ import k from "../../worksheet/worksheet/hooks/use-s3-helper.js";
13
+ import { base64ToFile as C } from "./fraud-alert-modal-helpers.js";
14
+ import { useUIContext as K } from "../../ui/context/context.js";
15
+ const U = { type: "class_fraud_login" }, j = R(function() {
16
+ const { closeModal: g } = x(), {
17
+ teacherId: o,
18
+ studentId: n,
19
+ handleLogout: h,
20
+ studentClassroomId: f,
21
+ teacherClassroomId: $,
22
+ classStartTs: s
23
+ } = T(), { onEvent: d } = K(), t = w(), r = F().find((a) => a.userId === o), u = S(), p = k({
24
+ studentId: n,
25
+ query: U
26
+ }), y = c(() => {
27
+ g(), h();
28
+ }, [g, h]), _ = c(
29
+ (a) => {
30
+ d(
31
+ "fraud_alert_images_uploaded",
32
+ { urls: a, classStartTs: s, teacherId: o, studentId: n },
33
+ {
34
+ webengage: !0
35
+ }
36
+ );
37
+ },
38
+ [s, d, n, o]
39
+ ), b = c(() => {
40
+ d("fraud_alert_images_failed", { classStartTs: s, teacherId: o, studentId: n });
41
+ }, [s, d, n, o]);
42
+ return X(() => {
22
43
  if (!(r != null && r.id) || !(t != null && t.id))
23
44
  return;
24
- const { image: a } = n(r == null ? void 0 : r.id, "camera"), { image: A } = n(t == null ? void 0 : t.id, "camera"), m = f(a, `teacher-${Date.now()}.png`), h = f(A, `student-${Date.now()}.png`);
25
- if (!m || !h) {
26
- console.error("Failed to convert images to files");
27
- return;
28
- }
29
- l({
30
- fileKey: `media/fraud-login/${i}/`,
45
+ const { image: a } = u(r == null ? void 0 : r.id, "camera"), { image: E } = u(t == null ? void 0 : t.id, "camera"), A = C(a, `teacher-${Date.now()}.png`), I = C(E, `student-${Date.now()}.png`);
46
+ !A || !I || p({
47
+ fileKey: "media/class_fraud_login/",
48
+ onSuccess: _,
49
+ onError: b,
31
50
  images: [
32
- { file: m, url: "teacher" },
33
- { file: h, url: "student" }
51
+ {
52
+ file: A,
53
+ name: `teacher_${$}_${o}`,
54
+ url: ""
55
+ },
56
+ {
57
+ file: I,
58
+ name: `student_${f}_${n}`,
59
+ url: ""
60
+ }
34
61
  ]
35
62
  });
36
- }, [n, t == null ? void 0 : t.id, r == null ? void 0 : r.id, i, l]), /* @__PURE__ */ g(d, { $gapX: 1.5, $gutterX: 1.5, $background: "RED_1", children: [
63
+ }, [
64
+ u,
65
+ t == null ? void 0 : t.id,
66
+ b,
67
+ _,
68
+ r == null ? void 0 : r.id,
69
+ f,
70
+ n,
71
+ $,
72
+ o,
73
+ p
74
+ ]), /* @__PURE__ */ L(l, { $gapX: 1.5, $gutterX: 1.5, $background: "RED_1", children: [
37
75
  /* @__PURE__ */ e(
38
- d,
76
+ l,
39
77
  {
40
78
  $justifyContent: "center",
41
79
  $alignItems: "center",
@@ -44,7 +82,7 @@ const B = { type: "fraud-login" }, D = b(function() {
44
82
  $widthX: 4.5,
45
83
  $background: "RED_2",
46
84
  children: /* @__PURE__ */ e(
47
- d,
85
+ l,
48
86
  {
49
87
  $justifyContent: "center",
50
88
  $alignItems: "center",
@@ -52,44 +90,44 @@ const B = { type: "fraud-login" }, D = b(function() {
52
90
  $heightX: 3.5,
53
91
  $widthX: 3.5,
54
92
  $background: "RED_4",
55
- children: /* @__PURE__ */ e(_, { src: R.ALERT_BULB, alt: "Fraud Alert", height: 40, width: 40 })
93
+ children: /* @__PURE__ */ e(B, { src: M.ALERT_BULB, alt: "Fraud Alert", height: 40, width: 40 })
56
94
  }
57
95
  )
58
96
  }
59
97
  ),
60
- /* @__PURE__ */ e(s, { heightX: 2 }),
61
- /* @__PURE__ */ e(o, { $renderAs: "ah4-bold", $color: "RED_6", children: "Fraudulent Login Detected!" }),
62
- /* @__PURE__ */ e(s, { height: 12 }),
63
- /* @__PURE__ */ g(o, { $renderAs: "ub2", color: "BLACK_1", children: [
98
+ /* @__PURE__ */ e(m, { heightX: 2 }),
99
+ /* @__PURE__ */ e(i, { $renderAs: "ah4-bold", $color: "RED_6", children: "Fraudulent Login Detected!" }),
100
+ /* @__PURE__ */ e(m, { height: 12 }),
101
+ /* @__PURE__ */ L(i, { $renderAs: "ub2", color: "BLACK_1", children: [
64
102
  "You have logged into a student's LEAP account, which is strictly prohibited. This is considered",
65
103
  " ",
66
- /* @__PURE__ */ e(o, { as: "span", $renderAs: "ub2-bold", color: "BLACK_1", $inline: !0, children: "fraudulent activity" }),
104
+ /* @__PURE__ */ e(i, { as: "span", $renderAs: "ub2-bold", color: "BLACK_1", $inline: !0, children: "fraudulent activity" }),
67
105
  " ",
68
106
  "and may result in strict action, including a",
69
107
  " ",
70
- /* @__PURE__ */ e(o, { as: "span", $renderAs: "ub2-bold", color: "BLACK_1", $inline: !0, children: "heavy penalty" }),
108
+ /* @__PURE__ */ e(i, { as: "span", $renderAs: "ub2-bold", color: "BLACK_1", $inline: !0, children: "heavy penalty" }),
71
109
  " ",
72
110
  "or",
73
111
  " ",
74
- /* @__PURE__ */ e(o, { as: "span", $renderAs: "ub2-bold", color: "BLACK_1", $inline: !0, children: "discontinuation" }),
112
+ /* @__PURE__ */ e(i, { as: "span", $renderAs: "ub2-bold", color: "BLACK_1", $inline: !0, children: "discontinuation" }),
75
113
  " ",
76
114
  "from conducting Cuemath classes."
77
115
  ] }),
78
- /* @__PURE__ */ e(s, { heightX: 2 }),
116
+ /* @__PURE__ */ e(m, { heightX: 2 }),
79
117
  /* @__PURE__ */ e(
80
- w,
118
+ D,
81
119
  {
82
120
  renderAs: "primary",
83
121
  alignSelf: "flex-end",
84
122
  shape: "square",
85
- onClick: p,
123
+ onClick: y,
86
124
  label: "Log out",
87
125
  width: "50%"
88
126
  }
89
127
  )
90
128
  ] });
91
- }), V = D;
129
+ }), te = j;
92
130
  export {
93
- V as default
131
+ te as default
94
132
  };
95
133
  //# sourceMappingURL=fraud-alert-modal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fraud-alert-modal.js","sources":["../../../../src/features/fraud-detection/fraud-alert-modal/fraud-alert-modal.tsx"],"sourcesContent":["/**\n * Fraud Alert Modal Component\n * Displays fraud detection details and auto-logout countdown\n */\n\nimport { memo, useCallback, useEffect, useState, type FC } from 'react';\n\nimport { useCaptureMediaStreamImage, useLocalPeer, useRemotePeers } from '@cuemath/av';\n\nimport { ILLUSTRATIONS } from '../../../assets/illustrations/illustrations';\nimport useModalActions from '../../ui/modals/use-modal-actions';\nimport Text from '../../ui/text/text';\nimport Image from '../../ui/image/image';\nimport FlexView from '../../ui/layout/flex-view';\nimport Button from '../../ui/buttons/button/button';\nimport Separator from '../../ui/separator/separator';\nimport useModalParams from '../../ui/modals/use-modal-params';\nimport useS3ImageUploadHelper from '../../worksheet/worksheet/hooks/use-s3-helper';\nimport { base64ToFile } from './fraud-alert-modal-helpers';\n\nconst QUERY = { type: 'fraud-login' };\n\nconst FraudAlertModal: FC = memo(function FraudAlertModal() {\n const { closeModal } = useModalActions();\n const { teacherId, studentId, handleLogout } = useModalParams<{\n teacherId: string;\n studentId: string;\n handleLogout: () => void;\n }>();\n const onLogout = useCallback(() => {\n closeModal();\n handleLogout();\n }, [closeModal, handleLogout]);\n\n const localpeer = useLocalPeer();\n const remotePeer = useRemotePeers().find(peer => peer.userId === teacherId);\n const captureMediaStreamImage = useCaptureMediaStreamImage();\n const uploadImages = useS3ImageUploadHelper({\n studentId,\n query: QUERY,\n });\n\n useEffect(() => {\n if (!remotePeer?.id || !localpeer?.id) {\n return;\n }\n\n const { image: teacherImage } = captureMediaStreamImage(remotePeer?.id, 'camera');\n const { image: studentImage } = captureMediaStreamImage(localpeer?.id, 'camera');\n const teacherFile = base64ToFile(teacherImage, `teacher-${Date.now()}.png`);\n const studentFile = base64ToFile(studentImage, `student-${Date.now()}.png`);\n\n if (!teacherFile || !studentFile) {\n console.error('Failed to convert images to files');\n\n return;\n }\n\n uploadImages({\n fileKey: `media/fraud-login/${studentId}/`,\n images: [\n { file: teacherFile, url: 'teacher' },\n { file: studentFile, url: 'student' },\n ],\n });\n }, [captureMediaStreamImage, localpeer?.id, remotePeer?.id, studentId, uploadImages]);\n\n return (\n <FlexView $gapX={1.5} $gutterX={1.5} $background=\"RED_1\">\n <FlexView\n $justifyContent=\"center\"\n $alignItems=\"center\"\n $borderRadiusX={4}\n $heightX={4.5}\n $widthX={4.5}\n $background=\"RED_2\"\n >\n <FlexView\n $justifyContent=\"center\"\n $alignItems=\"center\"\n $borderRadiusX={4}\n $heightX={3.5}\n $widthX={3.5}\n $background=\"RED_4\"\n >\n <Image src={ILLUSTRATIONS.ALERT_BULB} alt=\"Fraud Alert\" height={40} width={40} />\n </FlexView>\n </FlexView>\n <Separator heightX={2} />\n <Text $renderAs=\"ah4-bold\" $color=\"RED_6\">\n Fraudulent Login Detected!\n </Text>\n <Separator height={12} />\n <Text $renderAs=\"ub2\" color=\"BLACK_1\">\n You have logged into a student's LEAP account, which is strictly prohibited. This is\n considered{' '}\n <Text as=\"span\" $renderAs=\"ub2-bold\" color=\"BLACK_1\" $inline>\n fraudulent activity\n </Text>{' '}\n and may result in strict action, including a{' '}\n <Text as=\"span\" $renderAs=\"ub2-bold\" color=\"BLACK_1\" $inline>\n heavy penalty\n </Text>{' '}\n or{' '}\n <Text as=\"span\" $renderAs=\"ub2-bold\" color=\"BLACK_1\" $inline>\n discontinuation\n </Text>{' '}\n from conducting Cuemath classes.\n </Text>\n <Separator heightX={2} />\n <Button\n renderAs=\"primary\"\n alignSelf=\"flex-end\"\n shape=\"square\"\n onClick={onLogout}\n label=\"Log out\"\n width=\"50%\"\n />\n </FlexView>\n );\n});\n\nexport default FraudAlertModal;\n"],"names":["QUERY","FraudAlertModal","memo","closeModal","useModalActions","teacherId","studentId","handleLogout","useModalParams","onLogout","useCallback","localpeer","useLocalPeer","remotePeer","useRemotePeers","peer","captureMediaStreamImage","useCaptureMediaStreamImage","uploadImages","useS3ImageUploadHelper","useEffect","teacherImage","studentImage","teacherFile","base64ToFile","studentFile","FlexView","jsx","Image","ILLUSTRATIONS","Separator","Text","jsxs","Button","FraudAlertModal$1"],"mappings":"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"}
1
+ {"version":3,"file":"fraud-alert-modal.js","sources":["../../../../src/features/fraud-detection/fraud-alert-modal/fraud-alert-modal.tsx"],"sourcesContent":["import { memo, useCallback, useEffect, type FC } from 'react';\n\nimport { useCaptureMediaStreamImage, useLocalPeer, useRemotePeers } from '@cuemath/av';\n\nimport { ILLUSTRATIONS } from '../../../assets/illustrations/illustrations';\nimport useModalActions from '../../ui/modals/use-modal-actions';\nimport Text from '../../ui/text/text';\nimport Image from '../../ui/image/image';\nimport FlexView from '../../ui/layout/flex-view';\nimport Button from '../../ui/buttons/button/button';\nimport Separator from '../../ui/separator/separator';\nimport useModalParams from '../../ui/modals/use-modal-params';\nimport useS3ImageUploadHelper from '../../worksheet/worksheet/hooks/use-s3-helper';\nimport { base64ToFile } from './fraud-alert-modal-helpers';\nimport { useUIContext } from '../../ui/context/context';\n\nconst QUERY = { type: 'class_fraud_login' };\n\nconst FraudAlertModal: FC = memo(function FraudAlertModal() {\n const { closeModal } = useModalActions();\n const {\n teacherId,\n studentId,\n handleLogout,\n studentClassroomId,\n teacherClassroomId,\n classStartTs,\n } = useModalParams<{\n teacherId: string;\n studentId: string;\n studentClassroomId: string;\n teacherClassroomId: string;\n handleLogout: () => void;\n classStartTs: number;\n }>();\n const { onEvent } = useUIContext();\n const localpeer = useLocalPeer();\n const remotePeer = useRemotePeers().find(peer => peer.userId === teacherId);\n const captureMediaStreamImage = useCaptureMediaStreamImage();\n const uploadImages = useS3ImageUploadHelper({\n studentId,\n query: QUERY,\n });\n\n const onLogout = useCallback(() => {\n closeModal();\n handleLogout();\n }, [closeModal, handleLogout]);\n\n const onSuccess = useCallback(\n (urls: string[]) => {\n onEvent(\n 'fraud_alert_images_uploaded',\n { urls, classStartTs, teacherId, studentId },\n {\n webengage: true,\n },\n );\n },\n [classStartTs, onEvent, studentId, teacherId],\n );\n\n const onError = useCallback(() => {\n onEvent('fraud_alert_images_failed', { classStartTs, teacherId, studentId });\n }, [classStartTs, onEvent, studentId, teacherId]);\n\n useEffect(() => {\n if (!remotePeer?.id || !localpeer?.id) {\n return;\n }\n\n const { image: teacherImage } = captureMediaStreamImage(remotePeer?.id, 'camera');\n const { image: studentImage } = captureMediaStreamImage(localpeer?.id, 'camera');\n const teacherFile = base64ToFile(teacherImage, `teacher-${Date.now()}.png`);\n const studentFile = base64ToFile(studentImage, `student-${Date.now()}.png`);\n\n if (!teacherFile || !studentFile) {\n return;\n }\n\n uploadImages({\n fileKey: `media/class_fraud_login/`,\n onSuccess,\n onError,\n images: [\n {\n file: teacherFile,\n name: `teacher_${teacherClassroomId}_${teacherId}`,\n url: '',\n },\n {\n file: studentFile,\n name: `student_${studentClassroomId}_${studentId}`,\n url: '',\n },\n ],\n });\n }, [\n captureMediaStreamImage,\n localpeer?.id,\n onError,\n onSuccess,\n remotePeer?.id,\n studentClassroomId,\n studentId,\n teacherClassroomId,\n teacherId,\n uploadImages,\n ]);\n\n return (\n <FlexView $gapX={1.5} $gutterX={1.5} $background=\"RED_1\">\n <FlexView\n $justifyContent=\"center\"\n $alignItems=\"center\"\n $borderRadiusX={4}\n $heightX={4.5}\n $widthX={4.5}\n $background=\"RED_2\"\n >\n <FlexView\n $justifyContent=\"center\"\n $alignItems=\"center\"\n $borderRadiusX={4}\n $heightX={3.5}\n $widthX={3.5}\n $background=\"RED_4\"\n >\n <Image src={ILLUSTRATIONS.ALERT_BULB} alt=\"Fraud Alert\" height={40} width={40} />\n </FlexView>\n </FlexView>\n <Separator heightX={2} />\n <Text $renderAs=\"ah4-bold\" $color=\"RED_6\">\n Fraudulent Login Detected!\n </Text>\n <Separator height={12} />\n <Text $renderAs=\"ub2\" color=\"BLACK_1\">\n You have logged into a student's LEAP account, which is strictly prohibited. This is\n considered{' '}\n <Text as=\"span\" $renderAs=\"ub2-bold\" color=\"BLACK_1\" $inline>\n fraudulent activity\n </Text>{' '}\n and may result in strict action, including a{' '}\n <Text as=\"span\" $renderAs=\"ub2-bold\" color=\"BLACK_1\" $inline>\n heavy penalty\n </Text>{' '}\n or{' '}\n <Text as=\"span\" $renderAs=\"ub2-bold\" color=\"BLACK_1\" $inline>\n discontinuation\n </Text>{' '}\n from conducting Cuemath classes.\n </Text>\n <Separator heightX={2} />\n <Button\n renderAs=\"primary\"\n alignSelf=\"flex-end\"\n shape=\"square\"\n onClick={onLogout}\n label=\"Log out\"\n width=\"50%\"\n />\n </FlexView>\n );\n});\n\nexport default FraudAlertModal;\n"],"names":["QUERY","FraudAlertModal","memo","closeModal","useModalActions","teacherId","studentId","handleLogout","studentClassroomId","teacherClassroomId","classStartTs","useModalParams","onEvent","useUIContext","localpeer","useLocalPeer","remotePeer","useRemotePeers","peer","captureMediaStreamImage","useCaptureMediaStreamImage","uploadImages","useS3ImageUploadHelper","onLogout","useCallback","onSuccess","urls","onError","useEffect","teacherImage","studentImage","teacherFile","base64ToFile","studentFile","FlexView","jsx","Image","ILLUSTRATIONS","Separator","Text","jsxs","Button","FraudAlertModal$1"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"device-fingerprint.js","sources":["../../../../src/features/fraud-detection/utils/device-fingerprint.ts"],"sourcesContent":["/**\n * Device fingerprinting utility for fraud detection and session tracking.\n * Generates a unique fingerprint based on device and browser characteristics.\n */\n\nimport type { IDeviceFingerprint } from './fraud-detection-types';\n\n/**\n * Create a hash from component values using djb2 hash algorithm.\n */\nconst hashComponents = (components: Record<string, unknown>): string => {\n const str = Object.keys(components)\n .sort()\n .map(key => `${key}:${components[key]}`)\n .join('|');\n\n let hash = 5381;\n\n for (let i = 0; i < str.length; i++) {\n hash = (hash * 33) ^ str.charCodeAt(i);\n }\n\n return Math.abs(hash).toString(36);\n};\n\n/**\n * Generate a device fingerprint based on browser and device characteristics.\n * Only collects properties used for fraud detection.\n */\nexport const generateDeviceFingerprint = (ipAddress: string): IDeviceFingerprint => {\n const components: Record<string, unknown> = {\n screen: `${window.screen.width}x${window.screen.height}`,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n platform: navigator.platform,\n userAgent: navigator.userAgent,\n };\n\n if ('deviceMemory' in navigator) {\n components.deviceMemory = (navigator as Navigator & { deviceMemory?: number }).deviceMemory;\n }\n\n if ('hardwareConcurrency' in navigator) {\n components.hardwareConcurrency = navigator.hardwareConcurrency;\n }\n\n const fingerprint = hashComponents(components);\n\n return {\n fingerprint,\n screen: components.screen as string,\n timezone: components.timezone as string,\n platform: components.platform as string,\n userAgent: components.userAgent as string,\n deviceMemory: components.deviceMemory as number | undefined,\n hardwareConcurrency: components.hardwareConcurrency as number | undefined,\n ip_address: ipAddress,\n };\n};\n"],"names":["hashComponents","components","str","key","hash","i","generateDeviceFingerprint","ipAddress"],"mappings":"AAUA,MAAMA,IAAiB,CAACC,MAAgD;AACtE,QAAMC,IAAM,OAAO,KAAKD,CAAU,EAC/B,OACA,IAAI,CAAAE,MAAO,GAAGA,CAAG,IAAIF,EAAWE,CAAG,CAAC,EAAE,EACtC,KAAK,GAAG;AAEX,MAAIC,IAAO;AAEX,WAASC,IAAI,GAAGA,IAAIH,EAAI,QAAQG;AAC9B,IAAAD,IAAQA,IAAO,KAAMF,EAAI,WAAWG,CAAC;AAGvC,SAAO,KAAK,IAAID,CAAI,EAAE,SAAS,EAAE;AACnC,GAMaE,IAA4B,CAACC,MAA0C;AAClF,QAAMN,IAAsC;AAAA,IAC1C,QAAQ,GAAG,OAAO,OAAO,KAAK,IAAI,OAAO,OAAO,MAAM;AAAA,IACtD,UAAU,KAAK,iBAAiB,gBAAkB,EAAA;AAAA,IAClD,UAAU,UAAU;AAAA,IACpB,WAAW,UAAU;AAAA,EAAA;AAGvB,SAAI,kBAAkB,cACpBA,EAAW,eAAgB,UAAoD,eAG7E,yBAAyB,cAC3BA,EAAW,sBAAsB,UAAU,sBAKtC;AAAA,IACL,aAHkBD,EAAeC,CAAU;AAAA,IAI3C,QAAQA,EAAW;AAAA,IACnB,UAAUA,EAAW;AAAA,IACrB,UAAUA,EAAW;AAAA,IACrB,WAAWA,EAAW;AAAA,IACtB,cAAcA,EAAW;AAAA,IACzB,qBAAqBA,EAAW;AAAA,IAChC,YAAYM;AAAA,EAAA;AAEhB;"}
1
+ {"version":3,"file":"device-fingerprint.js","sources":["../../../../src/features/fraud-detection/utils/device-fingerprint.ts"],"sourcesContent":["import type { IDeviceFingerprint } from './fraud-detection-types';\n\n/**\n * Create a hash from component values using djb2 hash algorithm.\n */\nconst hashComponents = (components: Record<string, unknown>): string => {\n const str = Object.keys(components)\n .sort()\n .map(key => `${key}:${components[key]}`)\n .join('|');\n\n let hash = 5381;\n\n for (let i = 0; i < str.length; i++) {\n hash = (hash * 33) ^ str.charCodeAt(i);\n }\n\n return Math.abs(hash).toString(36);\n};\n\n/**\n * Generate a device fingerprint based on browser and device characteristics.\n * Only collects properties used for fraud detection.\n */\nexport const generateDeviceFingerprint = (ipAddress: string): IDeviceFingerprint => {\n const components: Record<string, unknown> = {\n screen: `${window.screen.width}x${window.screen.height}`,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n platform: navigator.platform,\n userAgent: navigator.userAgent,\n };\n\n if ('deviceMemory' in navigator) {\n components.deviceMemory = (navigator as Navigator & { deviceMemory?: number }).deviceMemory;\n }\n\n if ('hardwareConcurrency' in navigator) {\n components.hardwareConcurrency = navigator.hardwareConcurrency;\n }\n\n const fingerprint = hashComponents(components);\n\n return {\n fingerprint,\n screen: components.screen as string,\n timezone: components.timezone as string,\n platform: components.platform as string,\n userAgent: components.userAgent as string,\n deviceMemory: components.deviceMemory as number | undefined,\n hardwareConcurrency: components.hardwareConcurrency as number | undefined,\n ip_address: ipAddress,\n };\n};\n"],"names":["hashComponents","components","str","key","hash","i","generateDeviceFingerprint","ipAddress"],"mappings":"AAKA,MAAMA,IAAiB,CAACC,MAAgD;AACtE,QAAMC,IAAM,OAAO,KAAKD,CAAU,EAC/B,OACA,IAAI,CAAAE,MAAO,GAAGA,CAAG,IAAIF,EAAWE,CAAG,CAAC,EAAE,EACtC,KAAK,GAAG;AAEX,MAAIC,IAAO;AAEX,WAASC,IAAI,GAAGA,IAAIH,EAAI,QAAQG;AAC9B,IAAAD,IAAQA,IAAO,KAAMF,EAAI,WAAWG,CAAC;AAGvC,SAAO,KAAK,IAAID,CAAI,EAAE,SAAS,EAAE;AACnC,GAMaE,IAA4B,CAACC,MAA0C;AAClF,QAAMN,IAAsC;AAAA,IAC1C,QAAQ,GAAG,OAAO,OAAO,KAAK,IAAI,OAAO,OAAO,MAAM;AAAA,IACtD,UAAU,KAAK,iBAAiB,gBAAkB,EAAA;AAAA,IAClD,UAAU,UAAU;AAAA,IACpB,WAAW,UAAU;AAAA,EAAA;AAGvB,SAAI,kBAAkB,cACpBA,EAAW,eAAgB,UAAoD,eAG7E,yBAAyB,cAC3BA,EAAW,sBAAsB,UAAU,sBAKtC;AAAA,IACL,aAHkBD,EAAeC,CAAU;AAAA,IAI3C,QAAQA,EAAW;AAAA,IACnB,UAAUA,EAAW;AAAA,IACrB,UAAUA,EAAW;AAAA,IACrB,WAAWA,EAAW;AAAA,IACtB,cAAcA,EAAW;AAAA,IACzB,qBAAqBA,EAAW;AAAA,IAChC,YAAYM;AAAA,EAAA;AAEhB;"}
@@ -1 +1 @@
1
- {"version":3,"file":"hardware-profile-matcher.js","sources":["../../../../src/features/fraud-detection/utils/hardware-profile-matcher.ts"],"sourcesContent":["/**\n * Hardware profile matching utility for detecting same physical device\n * across different browsers, incognito modes, or user profiles.\n *\n * Focuses on hardware characteristics that remain constant on the same device:\n * - IP address\n * - Screen resolution\n * - Platform (OS)\n * - Timezone\n * - Hardware specs (memory, CPU cores)\n * - User agent base (ignore version numbers)\n */\n\nimport type { IDeviceFingerprint, IHardwareProfile } from './fraud-detection-types';\n\n/**\n * Extract hardware profile from device fingerprint\n */\nexport const extractHardwareProfile = (\n fingerprint: IDeviceFingerprint,\n ipAddress: string,\n): IHardwareProfile => {\n // Extract browser name without version\n const userAgent = fingerprint.userAgent.toLowerCase();\n let userAgentBase = 'unknown';\n\n if (userAgent.includes('chrome') && !userAgent.includes('edg')) {\n userAgentBase = 'chrome';\n } else if (userAgent.includes('safari') && !userAgent.includes('chrome')) {\n userAgentBase = 'safari';\n } else if (userAgent.includes('firefox')) {\n userAgentBase = 'firefox';\n } else if (userAgent.includes('edg')) {\n userAgentBase = 'edge';\n }\n\n return {\n ip_address: ipAddress,\n screen: fingerprint.screen,\n platform: fingerprint.platform,\n timezone: fingerprint.timezone,\n deviceMemory: fingerprint.deviceMemory,\n hardwareConcurrency: fingerprint.hardwareConcurrency,\n userAgentBase,\n };\n};\n\n/**\n * Check if profiles match on critical hardware fields\n * These fields must ALL match for same device detection\n */\nexport const matchesCriticalHardware = (\n profile1: IHardwareProfile,\n profile2: IHardwareProfile,\n): boolean => {\n // Must have same IP\n if (profile1.ip_address !== profile2.ip_address || profile1.ip_address === 'unknown') {\n return false;\n }\n\n // Must have same screen resolution\n if (profile1.screen !== profile2.screen) {\n return false;\n }\n\n // Must have same platform\n if (profile1.platform !== profile2.platform) {\n return false;\n }\n\n // Must have same timezone\n if (profile1.timezone !== profile2.timezone) {\n return false;\n }\n\n // If both have hardware specs, they must match\n if (profile1.deviceMemory && profile2.deviceMemory) {\n if (profile1.deviceMemory !== profile2.deviceMemory) {\n return false;\n }\n }\n\n if (profile1.hardwareConcurrency && profile2.hardwareConcurrency) {\n if (profile1.hardwareConcurrency !== profile2.hardwareConcurrency) {\n return false;\n }\n }\n\n return true;\n};\n\n/**\n * Get detailed comparison for logging/debugging\n */\nexport const getHardwareComparison = (\n profile1: IHardwareProfile,\n profile2: IHardwareProfile,\n): number => {\n let score = 0;\n const weights = {\n ip_address: 30, // IP is strong indicator\n screen: 20, // Screen resolution is unique\n platform: 15, // Platform is important\n timezone: 10, // Timezone helps\n deviceMemory: 10, // Hardware specs\n hardwareConcurrency: 10, // CPU cores\n userAgentBase: 5, // Browser family (less important)\n };\n\n // IP address (exact match)\n if (profile1.ip_address === profile2.ip_address && profile1.ip_address !== 'unknown') {\n score += weights.ip_address;\n }\n\n // Screen resolution (exact match)\n if (profile1.screen === profile2.screen) {\n score += weights.screen;\n }\n\n // Platform (exact match)\n if (profile1.platform === profile2.platform) {\n score += weights.platform;\n }\n\n // Timezone (exact match)\n if (profile1.timezone === profile2.timezone) {\n score += weights.timezone;\n }\n\n // Device memory (exact match)\n if (\n profile1.deviceMemory &&\n profile2.deviceMemory &&\n profile1.deviceMemory === profile2.deviceMemory\n ) {\n score += weights.deviceMemory;\n }\n\n // Hardware concurrency (exact match)\n if (\n profile1.hardwareConcurrency &&\n profile2.hardwareConcurrency &&\n profile1.hardwareConcurrency === profile2.hardwareConcurrency\n ) {\n score += weights.hardwareConcurrency;\n }\n\n // User agent base (same browser family)\n if (profile1.userAgentBase === profile2.userAgentBase) {\n score += weights.userAgentBase;\n }\n\n return score;\n};\n"],"names":["extractHardwareProfile","fingerprint","ipAddress","userAgent","userAgentBase","matchesCriticalHardware","profile1","profile2","getHardwareComparison","score","weights"],"mappings":"AAkBa,MAAAA,IAAyB,CACpCC,GACAC,MACqB;AAEf,QAAAC,IAAYF,EAAY,UAAU,YAAY;AACpD,MAAIG,IAAgB;AAEhB,SAAAD,EAAU,SAAS,QAAQ,KAAK,CAACA,EAAU,SAAS,KAAK,IAC3CC,IAAA,WACPD,EAAU,SAAS,QAAQ,KAAK,CAACA,EAAU,SAAS,QAAQ,IACrDC,IAAA,WACPD,EAAU,SAAS,SAAS,IACrBC,IAAA,YACPD,EAAU,SAAS,KAAK,MACjBC,IAAA,SAGX;AAAA,IACL,YAAYF;AAAA,IACZ,QAAQD,EAAY;AAAA,IACpB,UAAUA,EAAY;AAAA,IACtB,UAAUA,EAAY;AAAA,IACtB,cAAcA,EAAY;AAAA,IAC1B,qBAAqBA,EAAY;AAAA,IACjC,eAAAG;AAAA,EAAA;AAEJ,GAMaC,IAA0B,CACrCC,GACAC,MAGI,EAAAD,EAAS,eAAeC,EAAS,cAAcD,EAAS,eAAe,aAKvEA,EAAS,WAAWC,EAAS,UAK7BD,EAAS,aAAaC,EAAS,YAK/BD,EAAS,aAAaC,EAAS,YAK/BD,EAAS,gBAAgBC,EAAS,gBAChCD,EAAS,iBAAiBC,EAAS,gBAKrCD,EAAS,uBAAuBC,EAAS,uBACvCD,EAAS,wBAAwBC,EAAS,sBAWrCC,IAAwB,CACnCF,GACAC,MACW;AACX,MAAIE,IAAQ;AACZ,QAAMC,IAAU;AAAA,IACd,YAAY;AAAA;AAAA,IACZ,QAAQ;AAAA;AAAA,IACR,UAAU;AAAA;AAAA,IACV,UAAU;AAAA;AAAA,IACV,cAAc;AAAA;AAAA,IACd,qBAAqB;AAAA;AAAA,IACrB,eAAe;AAAA;AAAA,EAAA;AAIjB,SAAIJ,EAAS,eAAeC,EAAS,cAAcD,EAAS,eAAe,cACzEG,KAASC,EAAQ,aAIfJ,EAAS,WAAWC,EAAS,WAC/BE,KAASC,EAAQ,SAIfJ,EAAS,aAAaC,EAAS,aACjCE,KAASC,EAAQ,WAIfJ,EAAS,aAAaC,EAAS,aACjCE,KAASC,EAAQ,WAKjBJ,EAAS,gBACTC,EAAS,gBACTD,EAAS,iBAAiBC,EAAS,iBAEnCE,KAASC,EAAQ,eAKjBJ,EAAS,uBACTC,EAAS,uBACTD,EAAS,wBAAwBC,EAAS,wBAE1CE,KAASC,EAAQ,sBAIfJ,EAAS,kBAAkBC,EAAS,kBACtCE,KAASC,EAAQ,gBAGZD;AACT;"}
1
+ {"version":3,"file":"hardware-profile-matcher.js","sources":["../../../../src/features/fraud-detection/utils/hardware-profile-matcher.ts"],"sourcesContent":["import type { IDeviceFingerprint, IHardwareProfile } from './fraud-detection-types';\n\n/**\n * Extract hardware profile from device fingerprint\n */\nexport const extractHardwareProfile = (\n fingerprint: IDeviceFingerprint,\n ipAddress: string,\n): IHardwareProfile => {\n // Extract browser name without version\n const userAgent = fingerprint.userAgent.toLowerCase();\n let userAgentBase = 'unknown';\n\n if (userAgent.includes('chrome') && !userAgent.includes('edg')) {\n userAgentBase = 'chrome';\n } else if (userAgent.includes('safari') && !userAgent.includes('chrome')) {\n userAgentBase = 'safari';\n } else if (userAgent.includes('firefox')) {\n userAgentBase = 'firefox';\n } else if (userAgent.includes('edg')) {\n userAgentBase = 'edge';\n }\n\n return {\n ip_address: ipAddress,\n screen: fingerprint.screen,\n platform: fingerprint.platform,\n timezone: fingerprint.timezone,\n deviceMemory: fingerprint.deviceMemory,\n hardwareConcurrency: fingerprint.hardwareConcurrency,\n userAgentBase,\n };\n};\n\n/**\n * Check if profiles match on critical hardware fields\n * These fields must ALL match for same device detection\n */\nexport const matchesCriticalHardware = (\n profile1: IHardwareProfile,\n profile2: IHardwareProfile,\n): boolean => {\n // Must have same IP\n if (profile1.ip_address !== profile2.ip_address || profile1.ip_address === 'unknown') {\n return false;\n }\n\n // Must have same screen resolution\n if (profile1.screen !== profile2.screen) {\n return false;\n }\n\n // Must have same platform\n if (profile1.platform !== profile2.platform) {\n return false;\n }\n\n // Must have same timezone\n if (profile1.timezone !== profile2.timezone) {\n return false;\n }\n\n // If both have hardware specs, they must match\n if (profile1.deviceMemory && profile2.deviceMemory) {\n if (profile1.deviceMemory !== profile2.deviceMemory) {\n return false;\n }\n }\n\n if (profile1.hardwareConcurrency && profile2.hardwareConcurrency) {\n if (profile1.hardwareConcurrency !== profile2.hardwareConcurrency) {\n return false;\n }\n }\n\n return true;\n};\n\n/**\n * Get detailed comparison for logging/debugging\n */\nexport const getHardwareComparison = (\n profile1: IHardwareProfile,\n profile2: IHardwareProfile,\n): number => {\n let score = 0;\n const weights = {\n ip_address: 30, // IP is strong indicator\n screen: 20, // Screen resolution is unique\n platform: 15, // Platform is important\n timezone: 10, // Timezone helps\n deviceMemory: 10, // Hardware specs\n hardwareConcurrency: 10, // CPU cores\n userAgentBase: 5, // Browser family (less important)\n };\n\n // IP address (exact match)\n if (profile1.ip_address === profile2.ip_address && profile1.ip_address !== 'unknown') {\n score += weights.ip_address;\n }\n\n // Screen resolution (exact match)\n if (profile1.screen === profile2.screen) {\n score += weights.screen;\n }\n\n // Platform (exact match)\n if (profile1.platform === profile2.platform) {\n score += weights.platform;\n }\n\n // Timezone (exact match)\n if (profile1.timezone === profile2.timezone) {\n score += weights.timezone;\n }\n\n // Device memory (exact match)\n if (\n profile1.deviceMemory &&\n profile2.deviceMemory &&\n profile1.deviceMemory === profile2.deviceMemory\n ) {\n score += weights.deviceMemory;\n }\n\n // Hardware concurrency (exact match)\n if (\n profile1.hardwareConcurrency &&\n profile2.hardwareConcurrency &&\n profile1.hardwareConcurrency === profile2.hardwareConcurrency\n ) {\n score += weights.hardwareConcurrency;\n }\n\n // User agent base (same browser family)\n if (profile1.userAgentBase === profile2.userAgentBase) {\n score += weights.userAgentBase;\n }\n\n return score;\n};\n"],"names":["extractHardwareProfile","fingerprint","ipAddress","userAgent","userAgentBase","matchesCriticalHardware","profile1","profile2","getHardwareComparison","score","weights"],"mappings":"AAKa,MAAAA,IAAyB,CACpCC,GACAC,MACqB;AAEf,QAAAC,IAAYF,EAAY,UAAU,YAAY;AACpD,MAAIG,IAAgB;AAEhB,SAAAD,EAAU,SAAS,QAAQ,KAAK,CAACA,EAAU,SAAS,KAAK,IAC3CC,IAAA,WACPD,EAAU,SAAS,QAAQ,KAAK,CAACA,EAAU,SAAS,QAAQ,IACrDC,IAAA,WACPD,EAAU,SAAS,SAAS,IACrBC,IAAA,YACPD,EAAU,SAAS,KAAK,MACjBC,IAAA,SAGX;AAAA,IACL,YAAYF;AAAA,IACZ,QAAQD,EAAY;AAAA,IACpB,UAAUA,EAAY;AAAA,IACtB,UAAUA,EAAY;AAAA,IACtB,cAAcA,EAAY;AAAA,IAC1B,qBAAqBA,EAAY;AAAA,IACjC,eAAAG;AAAA,EAAA;AAEJ,GAMaC,IAA0B,CACrCC,GACAC,MAGI,EAAAD,EAAS,eAAeC,EAAS,cAAcD,EAAS,eAAe,aAKvEA,EAAS,WAAWC,EAAS,UAK7BD,EAAS,aAAaC,EAAS,YAK/BD,EAAS,aAAaC,EAAS,YAK/BD,EAAS,gBAAgBC,EAAS,gBAChCD,EAAS,iBAAiBC,EAAS,gBAKrCD,EAAS,uBAAuBC,EAAS,uBACvCD,EAAS,wBAAwBC,EAAS,sBAWrCC,IAAwB,CACnCF,GACAC,MACW;AACX,MAAIE,IAAQ;AACZ,QAAMC,IAAU;AAAA,IACd,YAAY;AAAA;AAAA,IACZ,QAAQ;AAAA;AAAA,IACR,UAAU;AAAA;AAAA,IACV,UAAU;AAAA;AAAA,IACV,cAAc;AAAA;AAAA,IACd,qBAAqB;AAAA;AAAA,IACrB,eAAe;AAAA;AAAA,EAAA;AAIjB,SAAIJ,EAAS,eAAeC,EAAS,cAAcD,EAAS,eAAe,cACzEG,KAASC,EAAQ,aAIfJ,EAAS,WAAWC,EAAS,WAC/BE,KAASC,EAAQ,SAIfJ,EAAS,aAAaC,EAAS,aACjCE,KAASC,EAAQ,WAIfJ,EAAS,aAAaC,EAAS,aACjCE,KAASC,EAAQ,WAKjBJ,EAAS,gBACTC,EAAS,gBACTD,EAAS,iBAAiBC,EAAS,iBAEnCE,KAASC,EAAQ,eAKjBJ,EAAS,uBACTC,EAAS,uBACTD,EAAS,wBAAwBC,EAAS,wBAE1CE,KAASC,EAAQ,sBAIfJ,EAAS,kBAAkBC,EAAS,kBACtCE,KAASC,EAAQ,gBAGZD;AACT;"}
@@ -1,34 +1,31 @@
1
- import { useState as g, useMemo as h, useCallback as k, useEffect as D } from "react";
1
+ import { useState as g, useMemo as k, useCallback as h, useEffect as D } from "react";
2
2
  import { generateDeviceFingerprint as C } from "../../fraud-detection/utils/device-fingerprint.js";
3
- import { extractHardwareProfile as u, getHardwareComparison as P, matchesCriticalHardware as v } from "../../fraud-detection/utils/hardware-profile-matcher.js";
3
+ import { extractHardwareProfile as u, matchesCriticalHardware as P, getHardwareComparison as v } from "../../fraud-detection/utils/hardware-profile-matcher.js";
4
4
  const x = (f) => {
5
- const { enabled: s = !1, deviceFingerprint: e, ipAddress: t } = f, [d, l] = g(!1), r = h(() => C(t || "unknown"), [t]), o = k(async () => {
6
- if (r)
7
- try {
8
- let n = 0;
9
- const p = 3;
10
- for (; !e && n < p; )
11
- await new Promise((F) => setTimeout(F, 500)), n++;
12
- if (!e)
13
- return;
14
- const a = u(r, t || "unknown"), c = u(
15
- e,
16
- e.ip_address || "unknown"
17
- ), m = P(a, c), w = v(a, c);
18
- let i = !1;
19
- (w || r.fingerprint === e.fingerprint || t === e.ip_address && t !== "unknown" && m > 70) && (i = !0), i && l(!0);
20
- } catch (n) {
21
- console.error("[FraudDetection] Error during fraud check:", n);
22
- }
23
- }, [e, r, t]);
5
+ const { enabled: i = !1, deviceFingerprint: e, ipAddress: r } = f, [d, l] = g(!1), t = k(() => C(r || "unknown"), [r]), s = h(async () => {
6
+ if (!t)
7
+ return;
8
+ let a = 0;
9
+ const p = 3;
10
+ for (; !e && a < p; )
11
+ await new Promise((F) => setTimeout(F, 500)), a++;
12
+ if (!e)
13
+ return;
14
+ const o = u(t, r || "unknown"), c = u(
15
+ e,
16
+ e.ip_address || "unknown"
17
+ ), m = v(o, c), w = P(o, c);
18
+ let n = !1;
19
+ (w || t.fingerprint === e.fingerprint || r === e.ip_address && r !== "unknown" && m > 70) && (n = !0), n && l(!0);
20
+ }, [e, t, r]);
24
21
  return D(() => {
25
- r && e && s && o();
26
- }, [r, e, o, s]), {
22
+ t && e && i && s();
23
+ }, [t, e, s, i]), {
27
24
  isFraud: d,
28
- deviceFingerprint: r
25
+ deviceFingerprint: t
29
26
  };
30
- }, A = x;
27
+ }, M = x;
31
28
  export {
32
- A as default
29
+ M as default
33
30
  };
34
31
  //# sourceMappingURL=use-fraud-detection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-fraud-detection.js","sources":["../../../../src/features/hooks/use-fraud-detection/use-fraud-detection.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport type { IUseFraudDetection } from './use-fraud-detection-types';\nimport { generateDeviceFingerprint } from '../../fraud-detection/utils/device-fingerprint';\nimport {\n extractHardwareProfile,\n getHardwareComparison,\n matchesCriticalHardware,\n} from '../../fraud-detection/utils/hardware-profile-matcher';\n\nconst useFraudDetection: IUseFraudDetection = props => {\n const { enabled = false, deviceFingerprint, ipAddress } = props;\n const [isFraud, setIsFraud] = useState(false);\n const fp = useMemo(() => generateDeviceFingerprint(ipAddress || 'unknown'), [ipAddress]);\n\n const checkFraudDetection = useCallback(async () => {\n if (!fp) {\n return;\n }\n\n try {\n let attempts = 0;\n const maxAttempts = 3;\n\n while (!deviceFingerprint && attempts < maxAttempts) {\n await new Promise(resolve => setTimeout(resolve, 500));\n attempts++;\n }\n\n if (!deviceFingerprint) {\n return;\n }\n\n const studentProfile = extractHardwareProfile(fp, ipAddress || 'unknown');\n const teacherProfile = extractHardwareProfile(\n deviceFingerprint,\n deviceFingerprint.ip_address || 'unknown',\n );\n\n const comparison = getHardwareComparison(studentProfile, teacherProfile);\n const isCriticalMatch = matchesCriticalHardware(studentProfile, teacherProfile);\n\n let fraudDetected = false;\n\n if (isCriticalMatch) {\n fraudDetected = true;\n } else if (fp.fingerprint === deviceFingerprint.fingerprint) {\n fraudDetected = true;\n } else if (ipAddress === deviceFingerprint.ip_address && ipAddress !== 'unknown') {\n if (comparison > 70) {\n fraudDetected = true;\n }\n }\n\n if (fraudDetected) {\n setIsFraud(true);\n }\n } catch (error) {\n console.error('[FraudDetection] Error during fraud check:', error);\n }\n }, [deviceFingerprint, fp, ipAddress]);\n\n // Automatically run fraud detection when fingerprint and teacher fingerprint are both available\n useEffect(() => {\n if (fp && deviceFingerprint && enabled) {\n checkFraudDetection();\n }\n }, [fp, deviceFingerprint, checkFraudDetection, enabled]);\n\n return {\n isFraud,\n deviceFingerprint: fp,\n };\n};\n\nexport default useFraudDetection;\n"],"names":["useFraudDetection","props","enabled","deviceFingerprint","ipAddress","isFraud","setIsFraud","useState","fp","useMemo","generateDeviceFingerprint","checkFraudDetection","useCallback","attempts","maxAttempts","resolve","studentProfile","extractHardwareProfile","teacherProfile","comparison","getHardwareComparison","isCriticalMatch","matchesCriticalHardware","fraudDetected","error","useEffect","useFraudDetection$1"],"mappings":";;;AAUA,MAAMA,IAAwC,CAASC,MAAA;AACrD,QAAM,EAAE,SAAAC,IAAU,IAAO,mBAAAC,GAAmB,WAAAC,MAAcH,GACpD,CAACI,GAASC,CAAU,IAAIC,EAAS,EAAK,GACtCC,IAAKC,EAAQ,MAAMC,EAA0BN,KAAa,SAAS,GAAG,CAACA,CAAS,CAAC,GAEjFO,IAAsBC,EAAY,YAAY;AAClD,QAAKJ;AAID,UAAA;AACF,YAAIK,IAAW;AACf,cAAMC,IAAc;AAEb,eAAA,CAACX,KAAqBU,IAAWC;AACtC,gBAAM,IAAI,QAAQ,CAAAC,MAAW,WAAWA,GAAS,GAAG,CAAC,GACrDF;AAGF,YAAI,CAACV;AACH;AAGF,cAAMa,IAAiBC,EAAuBT,GAAIJ,KAAa,SAAS,GAClEc,IAAiBD;AAAA,UACrBd;AAAA,UACAA,EAAkB,cAAc;AAAA,QAAA,GAG5BgB,IAAaC,EAAsBJ,GAAgBE,CAAc,GACjEG,IAAkBC,EAAwBN,GAAgBE,CAAc;AAE9E,YAAIK,IAAgB;AAEpB,SAAIF,KAEOb,EAAG,gBAAgBL,EAAkB,eAErCC,MAAcD,EAAkB,cAAcC,MAAc,aACjEe,IAAa,QACCI,IAAA,KAIhBA,KACFjB,EAAW,EAAI;AAAA,eAEVkB,GAAO;AACN,gBAAA,MAAM,8CAA8CA,CAAK;AAAA,MACnE;AAAA,EACC,GAAA,CAACrB,GAAmBK,GAAIJ,CAAS,CAAC;AAGrC,SAAAqB,EAAU,MAAM;AACV,IAAAjB,KAAML,KAAqBD,KACTS;KAErB,CAACH,GAAIL,GAAmBQ,GAAqBT,CAAO,CAAC,GAEjD;AAAA,IACL,SAAAG;AAAA,IACA,mBAAmBG;AAAA,EAAA;AAEvB,GAEAkB,IAAe1B;"}
1
+ {"version":3,"file":"use-fraud-detection.js","sources":["../../../../src/features/hooks/use-fraud-detection/use-fraud-detection.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport type { IUseFraudDetection } from './use-fraud-detection-types';\nimport { generateDeviceFingerprint } from '../../fraud-detection/utils/device-fingerprint';\nimport {\n extractHardwareProfile,\n getHardwareComparison,\n matchesCriticalHardware,\n} from '../../fraud-detection/utils/hardware-profile-matcher';\n\nconst useFraudDetection: IUseFraudDetection = props => {\n const { enabled = false, deviceFingerprint, ipAddress } = props;\n const [isFraud, setIsFraud] = useState(false);\n const fp = useMemo(() => generateDeviceFingerprint(ipAddress || 'unknown'), [ipAddress]);\n\n const checkFraudDetection = useCallback(async () => {\n if (!fp) {\n return;\n }\n\n let attempts = 0;\n const maxAttempts = 3;\n\n while (!deviceFingerprint && attempts < maxAttempts) {\n await new Promise(resolve => setTimeout(resolve, 500));\n attempts++;\n }\n\n if (!deviceFingerprint) {\n return;\n }\n\n const studentProfile = extractHardwareProfile(fp, ipAddress || 'unknown');\n const teacherProfile = extractHardwareProfile(\n deviceFingerprint,\n deviceFingerprint.ip_address || 'unknown',\n );\n\n const comparison = getHardwareComparison(studentProfile, teacherProfile);\n const isCriticalMatch = matchesCriticalHardware(studentProfile, teacherProfile);\n\n let fraudDetected = false;\n\n if (isCriticalMatch) {\n fraudDetected = true;\n } else if (fp.fingerprint === deviceFingerprint.fingerprint) {\n fraudDetected = true;\n } else if (ipAddress === deviceFingerprint.ip_address && ipAddress !== 'unknown') {\n if (comparison > 70) {\n fraudDetected = true;\n }\n }\n\n if (fraudDetected) {\n setIsFraud(true);\n }\n }, [deviceFingerprint, fp, ipAddress]);\n\n useEffect(() => {\n if (fp && deviceFingerprint && enabled) {\n checkFraudDetection();\n }\n }, [fp, deviceFingerprint, checkFraudDetection, enabled]);\n\n return {\n isFraud,\n deviceFingerprint: fp,\n };\n};\n\nexport default useFraudDetection;\n"],"names":["useFraudDetection","props","enabled","deviceFingerprint","ipAddress","isFraud","setIsFraud","useState","fp","useMemo","generateDeviceFingerprint","checkFraudDetection","useCallback","attempts","maxAttempts","resolve","studentProfile","extractHardwareProfile","teacherProfile","comparison","getHardwareComparison","isCriticalMatch","matchesCriticalHardware","fraudDetected","useEffect","useFraudDetection$1"],"mappings":";;;AAUA,MAAMA,IAAwC,CAASC,MAAA;AACrD,QAAM,EAAE,SAAAC,IAAU,IAAO,mBAAAC,GAAmB,WAAAC,MAAcH,GACpD,CAACI,GAASC,CAAU,IAAIC,EAAS,EAAK,GACtCC,IAAKC,EAAQ,MAAMC,EAA0BN,KAAa,SAAS,GAAG,CAACA,CAAS,CAAC,GAEjFO,IAAsBC,EAAY,YAAY;AAClD,QAAI,CAACJ;AACH;AAGF,QAAIK,IAAW;AACf,UAAMC,IAAc;AAEb,WAAA,CAACX,KAAqBU,IAAWC;AACtC,YAAM,IAAI,QAAQ,CAAAC,MAAW,WAAWA,GAAS,GAAG,CAAC,GACrDF;AAGF,QAAI,CAACV;AACH;AAGF,UAAMa,IAAiBC,EAAuBT,GAAIJ,KAAa,SAAS,GAClEc,IAAiBD;AAAA,MACrBd;AAAA,MACAA,EAAkB,cAAc;AAAA,IAAA,GAG5BgB,IAAaC,EAAsBJ,GAAgBE,CAAc,GACjEG,IAAkBC,EAAwBN,GAAgBE,CAAc;AAE9E,QAAIK,IAAgB;AAEpB,KAAIF,KAEOb,EAAG,gBAAgBL,EAAkB,eAErCC,MAAcD,EAAkB,cAAcC,MAAc,aACjEe,IAAa,QACCI,IAAA,KAIhBA,KACFjB,EAAW,EAAI;AAAA,EAEhB,GAAA,CAACH,GAAmBK,GAAIJ,CAAS,CAAC;AAErC,SAAAoB,EAAU,MAAM;AACV,IAAAhB,KAAML,KAAqBD,KACTS;KAErB,CAACH,GAAIL,GAAmBQ,GAAqBT,CAAO,CAAC,GAEjD;AAAA,IACL,SAAAG;AAAA,IACA,mBAAmBG;AAAA,EAAA;AAEvB,GAEAiB,IAAezB;"}
@@ -1,27 +1,27 @@
1
- import { useRef as h, useEffect as m, useCallback as k } from "react";
1
+ import { useRef as h, useEffect as f, useCallback as k } from "react";
2
2
  import { useAwsSignedKey as I } from "../api/subjective-review.js";
3
3
  import S from "../../../../node_modules/uuid/dist/esm-browser/v4.js";
4
- const C = (f) => {
5
- const { studentId: t, query: d, enabled: o = !0 } = f, { data: s, get: r } = I(), c = h(s);
6
- return m(() => {
4
+ const C = (y) => {
5
+ const { studentId: t, query: d, enabled: o = !0 } = y, { data: s, get: r } = I(), c = h(s);
6
+ return f(() => {
7
7
  c.current = s;
8
- }, [s]), m(() => {
8
+ }, [s]), f(() => {
9
9
  o && t && r(t, d);
10
10
  }, [d, r, t, o]), k(
11
- async ({ images: y, onSuccess: n, onError: p, fileKey: g, fileName: w }) => {
11
+ async ({ images: g, onSuccess: n, onError: p, fileKey: w, fileName: K }) => {
12
12
  const a = c.current;
13
13
  if (!a) return;
14
14
  const i = `https://${a.bucketName}.s3.amazonaws.com/`;
15
15
  try {
16
- const K = y.map(async (b) => {
17
- const e = new FormData(), u = `${g}${w ?? S()}`;
18
- if (e.append("key", u), e.append("AWSAccessKeyId", a.awsKey), e.append("acl", "public-read"), e.append("success_action_redirect", ""), e.append("policy", a.policy), e.append("signature", a.signature), e.append("Content-Type", "image/jpeg"), e.append("file", b.file), !(await fetch(i, {
16
+ const b = g.map(async (u) => {
17
+ const e = new FormData(), m = `${w}${u.name ?? K ?? S()}`;
18
+ if (e.append("key", m), e.append("AWSAccessKeyId", a.awsKey), e.append("acl", "public-read"), e.append("success_action_redirect", ""), e.append("policy", a.policy), e.append("signature", a.signature), e.append("Content-Type", "image/jpeg"), e.append("file", u.file), !(await fetch(i, {
19
19
  method: "POST",
20
20
  body: e
21
21
  })).ok)
22
22
  throw new Error("Upload failed");
23
- return `${i}${u}`;
24
- }), l = await Promise.all(K);
23
+ return `${i}${m}`;
24
+ }), l = await Promise.all(b);
25
25
  return n == null || n(l), l;
26
26
  } catch {
27
27
  p == null || p();
@@ -1 +1 @@
1
- {"version":3,"file":"use-s3-helper.js","sources":["../../../../../src/features/worksheet/worksheet/hooks/use-s3-helper.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { useAwsSignedKey } from '../api/subjective-review';\nimport type { IFile } from '../worksheet-question/subjective-review';\n\ninterface IUseS3helperProps {\n studentId: string;\n query: {\n type: string;\n };\n enabled?: boolean;\n}\n\ninterface IUploadImageProps {\n images: IFile[];\n onSuccess?: (urls: string[]) => void;\n onError?: () => void;\n fileKey: string;\n fileName?: string;\n}\n\nconst useS3ImageUploadHelper = (props: IUseS3helperProps) => {\n const { studentId, query, enabled = true } = props;\n const { data, get: getAwsSignedKey } = useAwsSignedKey();\n const awsSignedKeyRef = useRef(data);\n\n useEffect(() => {\n awsSignedKeyRef.current = data;\n }, [data]);\n\n useEffect(() => {\n if (enabled && studentId) {\n getAwsSignedKey(studentId, query);\n }\n }, [query, getAwsSignedKey, studentId, enabled]);\n\n const uploadImagesToS3 = useCallback(\n async ({ images, onSuccess, onError, fileKey, fileName }: IUploadImageProps) => {\n const awsSignedKey = awsSignedKeyRef.current;\n\n if (!awsSignedKey) return;\n\n const url = `https://${awsSignedKey.bucketName}.s3.amazonaws.com/`;\n\n try {\n const uploadPromises = images.map(async (item: { file: string | Blob }) => {\n const formData = new FormData();\n const key = `${fileKey}${fileName ?? uuidv4()}`;\n\n formData.append('key', key);\n formData.append('AWSAccessKeyId', awsSignedKey.awsKey);\n formData.append('acl', 'public-read');\n formData.append('success_action_redirect', '');\n formData.append('policy', awsSignedKey.policy);\n formData.append('signature', awsSignedKey.signature);\n formData.append('Content-Type', 'image/jpeg');\n formData.append('file', item.file);\n\n const res = await fetch(url, {\n method: 'POST',\n body: formData,\n });\n\n if (!res.ok) {\n throw new Error('Upload failed');\n }\n\n return `${url}${key}`;\n });\n\n const uploadedUrls = await Promise.all(uploadPromises);\n\n onSuccess?.(uploadedUrls);\n\n return uploadedUrls;\n } catch {\n onError?.();\n }\n },\n [],\n );\n\n return uploadImagesToS3;\n};\n\nexport default useS3ImageUploadHelper;\n"],"names":["useS3ImageUploadHelper","props","studentId","query","enabled","data","getAwsSignedKey","useAwsSignedKey","awsSignedKeyRef","useRef","useEffect","useCallback","images","onSuccess","onError","fileKey","fileName","awsSignedKey","url","uploadPromises","item","formData","key","uuidv4","uploadedUrls"],"mappings":";;;AAsBM,MAAAA,IAAyB,CAACC,MAA6B;AAC3D,QAAM,EAAE,WAAAC,GAAW,OAAAC,GAAO,SAAAC,IAAU,OAASH,GACvC,EAAE,MAAAI,GAAM,KAAKC,MAAoBC,EAAgB,GACjDC,IAAkBC,EAAOJ,CAAI;AAEnC,SAAAK,EAAU,MAAM;AACd,IAAAF,EAAgB,UAAUH;AAAA,EAAA,GACzB,CAACA,CAAI,CAAC,GAETK,EAAU,MAAM;AACd,IAAIN,KAAWF,KACbI,EAAgBJ,GAAWC,CAAK;AAAA,KAEjC,CAACA,GAAOG,GAAiBJ,GAAWE,CAAO,CAAC,GAEtBO;AAAA,IACvB,OAAO,EAAE,QAAAC,GAAQ,WAAAC,GAAW,SAAAC,GAAS,SAAAC,GAAS,UAAAC,QAAkC;AAC9E,YAAMC,IAAeT,EAAgB;AAErC,UAAI,CAACS,EAAc;AAEb,YAAAC,IAAM,WAAWD,EAAa,UAAU;AAE1C,UAAA;AACF,cAAME,IAAiBP,EAAO,IAAI,OAAOQ,MAAkC;AACnE,gBAAAC,IAAW,IAAI,YACfC,IAAM,GAAGP,CAAO,GAAGC,KAAYO,EAAQ,CAAA;AAgBzC,cAdKF,EAAA,OAAO,OAAOC,CAAG,GACjBD,EAAA,OAAO,kBAAkBJ,EAAa,MAAM,GAC5CI,EAAA,OAAO,OAAO,aAAa,GAC3BA,EAAA,OAAO,2BAA2B,EAAE,GACpCA,EAAA,OAAO,UAAUJ,EAAa,MAAM,GACpCI,EAAA,OAAO,aAAaJ,EAAa,SAAS,GAC1CI,EAAA,OAAO,gBAAgB,YAAY,GACnCA,EAAA,OAAO,QAAQD,EAAK,IAAI,GAO7B,EALQ,MAAM,MAAMF,GAAK;AAAA,YAC3B,QAAQ;AAAA,YACR,MAAMG;AAAA,UAAA,CACP,GAEQ;AACD,kBAAA,IAAI,MAAM,eAAe;AAG1B,iBAAA,GAAGH,CAAG,GAAGI,CAAG;AAAA,QAAA,CACpB,GAEKE,IAAe,MAAM,QAAQ,IAAIL,CAAc;AAErD,eAAAN,KAAA,QAAAA,EAAYW,IAELA;AAAA,MAAA,QACD;AACI,QAAAV,KAAA,QAAAA;AAAA,MACZ;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EAAA;AAIL;"}
1
+ {"version":3,"file":"use-s3-helper.js","sources":["../../../../../src/features/worksheet/worksheet/hooks/use-s3-helper.ts"],"sourcesContent":["import { useCallback, useEffect, useRef } from 'react';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { useAwsSignedKey } from '../api/subjective-review';\nimport type { IFile } from '../worksheet-question/subjective-review';\n\ninterface IUseS3helperProps {\n studentId: string;\n query: {\n type: string;\n };\n enabled?: boolean;\n}\n\ninterface IUploadImageProps {\n images: IFile[];\n onSuccess?: (urls: string[]) => void;\n onError?: () => void;\n fileKey: string;\n fileName?: string;\n}\n\nconst useS3ImageUploadHelper = (props: IUseS3helperProps) => {\n const { studentId, query, enabled = true } = props;\n const { data, get: getAwsSignedKey } = useAwsSignedKey();\n const awsSignedKeyRef = useRef(data);\n\n useEffect(() => {\n awsSignedKeyRef.current = data;\n }, [data]);\n\n useEffect(() => {\n if (enabled && studentId) {\n getAwsSignedKey(studentId, query);\n }\n }, [query, getAwsSignedKey, studentId, enabled]);\n\n const uploadImagesToS3 = useCallback(\n async ({ images, onSuccess, onError, fileKey, fileName }: IUploadImageProps) => {\n const awsSignedKey = awsSignedKeyRef.current;\n\n if (!awsSignedKey) return;\n\n const url = `https://${awsSignedKey.bucketName}.s3.amazonaws.com/`;\n\n try {\n const uploadPromises = images.map(async (item: { file: string | Blob; name?: string }) => {\n const formData = new FormData();\n const key = `${fileKey}${item.name ?? fileName ?? uuidv4()}`;\n\n formData.append('key', key);\n formData.append('AWSAccessKeyId', awsSignedKey.awsKey);\n formData.append('acl', 'public-read');\n formData.append('success_action_redirect', '');\n formData.append('policy', awsSignedKey.policy);\n formData.append('signature', awsSignedKey.signature);\n formData.append('Content-Type', 'image/jpeg');\n formData.append('file', item.file);\n\n const res = await fetch(url, {\n method: 'POST',\n body: formData,\n });\n\n if (!res.ok) {\n throw new Error('Upload failed');\n }\n\n return `${url}${key}`;\n });\n\n const uploadedUrls = await Promise.all(uploadPromises);\n\n onSuccess?.(uploadedUrls);\n\n return uploadedUrls;\n } catch {\n onError?.();\n }\n },\n [],\n );\n\n return uploadImagesToS3;\n};\n\nexport default useS3ImageUploadHelper;\n"],"names":["useS3ImageUploadHelper","props","studentId","query","enabled","data","getAwsSignedKey","useAwsSignedKey","awsSignedKeyRef","useRef","useEffect","useCallback","images","onSuccess","onError","fileKey","fileName","awsSignedKey","url","uploadPromises","item","formData","key","uuidv4","uploadedUrls"],"mappings":";;;AAsBM,MAAAA,IAAyB,CAACC,MAA6B;AAC3D,QAAM,EAAE,WAAAC,GAAW,OAAAC,GAAO,SAAAC,IAAU,OAASH,GACvC,EAAE,MAAAI,GAAM,KAAKC,MAAoBC,EAAgB,GACjDC,IAAkBC,EAAOJ,CAAI;AAEnC,SAAAK,EAAU,MAAM;AACd,IAAAF,EAAgB,UAAUH;AAAA,EAAA,GACzB,CAACA,CAAI,CAAC,GAETK,EAAU,MAAM;AACd,IAAIN,KAAWF,KACbI,EAAgBJ,GAAWC,CAAK;AAAA,KAEjC,CAACA,GAAOG,GAAiBJ,GAAWE,CAAO,CAAC,GAEtBO;AAAA,IACvB,OAAO,EAAE,QAAAC,GAAQ,WAAAC,GAAW,SAAAC,GAAS,SAAAC,GAAS,UAAAC,QAAkC;AAC9E,YAAMC,IAAeT,EAAgB;AAErC,UAAI,CAACS,EAAc;AAEb,YAAAC,IAAM,WAAWD,EAAa,UAAU;AAE1C,UAAA;AACF,cAAME,IAAiBP,EAAO,IAAI,OAAOQ,MAAiD;AAClF,gBAAAC,IAAW,IAAI,YACfC,IAAM,GAAGP,CAAO,GAAGK,EAAK,QAAQJ,KAAYO,EAAQ,CAAA;AAgBtD,cAdKF,EAAA,OAAO,OAAOC,CAAG,GACjBD,EAAA,OAAO,kBAAkBJ,EAAa,MAAM,GAC5CI,EAAA,OAAO,OAAO,aAAa,GAC3BA,EAAA,OAAO,2BAA2B,EAAE,GACpCA,EAAA,OAAO,UAAUJ,EAAa,MAAM,GACpCI,EAAA,OAAO,aAAaJ,EAAa,SAAS,GAC1CI,EAAA,OAAO,gBAAgB,YAAY,GACnCA,EAAA,OAAO,QAAQD,EAAK,IAAI,GAO7B,EALQ,MAAM,MAAMF,GAAK;AAAA,YAC3B,QAAQ;AAAA,YACR,MAAMG;AAAA,UAAA,CACP,GAEQ;AACD,kBAAA,IAAI,MAAM,eAAe;AAG1B,iBAAA,GAAGH,CAAG,GAAGI,CAAG;AAAA,QAAA,CACpB,GAEKE,IAAe,MAAM,QAAQ,IAAIL,CAAc;AAErD,eAAAN,KAAA,QAAAA,EAAYW,IAELA;AAAA,MAAA,QACD;AACI,QAAAV,KAAA,QAAAA;AAAA,MACZ;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EAAA;AAIL;"}
@@ -1 +1 @@
1
- {"version":3,"file":"subjective-review.js","sources":["../../../../../src/features/worksheet/worksheet/worksheet-question/subjective-review.tsx"],"sourcesContent":["import { memo, type FC } from 'react';\n\nimport FlexView from '../../../ui/layout/flex-view';\nimport Separator from '../../../ui/separator/separator';\nimport Text from '../../../ui/text/text';\nimport type { ISubjectiveSheetProps, IWorksheetResponse } from '../worksheet-types';\nimport SubjectiveQuestionFeedback from './subjective-feedback';\nimport { Img, Thumbnail, Wrapper } from './subjective-styled';\nimport { useWorksheetStore } from '../hooks/use-worksheet-store';\n\nexport interface ISubjectiveQuestionReviewProps extends ISubjectiveSheetProps {\n responseId: string;\n response?: IWorksheetResponse;\n nextQuestionId?: string;\n studentId: string;\n responses?: Record<string, IWorksheetResponse>;\n}\n\nexport interface IFile {\n file: File;\n url: string;\n}\n\nconst SubjectiveQuestionReview: FC<ISubjectiveQuestionReviewProps> = memo(\n function SubjectiveQuestionReview(props) {\n const { userType } = useWorksheetStore(state => ({\n userType: state.userType,\n }));\n const { response, openImagesReviewModal, isReviewPending } = props;\n\n const comment = response?.teacherReview?.reviewComment;\n const images = response?.teacherReview?.images ?? [];\n\n if (isReviewPending && userType === 'TEACHER') {\n return <SubjectiveQuestionFeedback {...props} />;\n }\n\n return (\n <Wrapper $gapX={0.5}>\n <FlexView $background=\"ORANGE_5\" $widthX={3.8} $heightX={1.5} $justifyContent=\"center\">\n <Text $renderAs=\"ub2-bold\" $color=\"WHITE\">\n Review\n </Text>\n </FlexView>\n <Separator heightX={1} />\n <FlexView $flexDirection=\"row\">\n <Text $renderAs=\"ub2\" $widthX={8}>\n Score\n </Text>\n <Text $renderAs=\"ub2\">{response?.score?.score ?? 0}</Text>\n </FlexView>\n <Separator heightX={1} />\n <FlexView $flexDirection=\"row\">\n <Text $renderAs=\"ub2\" $widthX={8}>\n Comments\n </Text>\n {comment ? <Text $renderAs=\"ub2\">{comment}</Text> : <Text $renderAs=\"ub2\"> -- </Text>}\n </FlexView>\n <Separator heightX={1} />\n <FlexView $flexDirection=\"row\">\n <Text $renderAs=\"ub2\" $widthX={8}>\n Files\n </Text>\n <FlexView $flexDirection=\"row\" $flexWrap>\n {images.map(img => {\n return (\n <span key={img}>\n <Thumbnail>\n <Img src={img} onClick={() => openImagesReviewModal?.({ image: img })} />\n </Thumbnail>\n </span>\n );\n })}\n </FlexView>\n </FlexView>\n </Wrapper>\n );\n },\n);\n\nexport default SubjectiveQuestionReview;\n"],"names":["SubjectiveQuestionReview","memo","props","userType","useWorksheetStore","state","response","openImagesReviewModal","isReviewPending","comment","_a","images","_b","jsx","SubjectiveQuestionFeedback","jsxs","Wrapper","FlexView","Text","Separator","_c","img","Thumbnail","Img"],"mappings":";;;;;;;;AAuBA,MAAMA,IAA+DC;AAAA,EACnE,SAAkCC,GAAO;;AACvC,UAAM,EAAE,UAAAC,EAAA,IAAaC,EAAkB,CAAUC,OAAA;AAAA,MAC/C,UAAUA,EAAM;AAAA,IAChB,EAAA,GACI,EAAE,UAAAC,GAAU,uBAAAC,GAAuB,iBAAAC,EAAA,IAAoBN,GAEvDO,KAAUC,IAAAJ,KAAA,gBAAAA,EAAU,kBAAV,gBAAAI,EAAyB,eACnCC,MAASC,IAAAN,KAAA,gBAAAA,EAAU,kBAAV,gBAAAM,EAAyB,WAAU,CAAA;AAE9C,WAAAJ,KAAmBL,MAAa,YAC3B,gBAAAU,EAACC,GAA4B,EAAA,GAAGZ,EAAO,CAAA,IAI9C,gBAAAa,EAACC,GAAQ,EAAA,OAAO,KACd,UAAA;AAAA,MAAA,gBAAAH,EAACI,KAAS,aAAY,YAAW,SAAS,KAAK,UAAU,KAAK,iBAAgB,UAC5E,UAAA,gBAAAJ,EAACK,KAAK,WAAU,YAAW,QAAO,SAAQ,mBAE1C,CAAA,GACF;AAAA,MACA,gBAAAL,EAACM,GAAU,EAAA,SAAS,EAAG,CAAA;AAAA,MACvB,gBAAAJ,EAACE,GAAS,EAAA,gBAAe,OACvB,UAAA;AAAA,QAAA,gBAAAJ,EAACK,GAAK,EAAA,WAAU,OAAM,SAAS,GAAG,UAElC,SAAA;AAAA,0BACCA,GAAK,EAAA,WAAU,OAAO,YAAUE,IAAAd,KAAA,gBAAAA,EAAA,UAAA,gBAAAc,EAAO,UAAS,GAAE;AAAA,MAAA,GACrD;AAAA,MACA,gBAAAP,EAACM,GAAU,EAAA,SAAS,EAAG,CAAA;AAAA,MACvB,gBAAAJ,EAACE,GAAS,EAAA,gBAAe,OACvB,UAAA;AAAA,QAAA,gBAAAJ,EAACK,GAAK,EAAA,WAAU,OAAM,SAAS,GAAG,UAElC,YAAA;AAAA,QACCT,IAAW,gBAAAI,EAAAK,GAAA,EAAK,WAAU,OAAO,UAAQT,EAAA,CAAA,IAAW,gBAAAI,EAAAK,GAAA,EAAK,WAAU,OAAM,UAAI,QAAA;AAAA,MAAA,GAChF;AAAA,MACA,gBAAAL,EAACM,GAAU,EAAA,SAAS,EAAG,CAAA;AAAA,MACvB,gBAAAJ,EAACE,GAAS,EAAA,gBAAe,OACvB,UAAA;AAAA,QAAA,gBAAAJ,EAACK,GAAK,EAAA,WAAU,OAAM,SAAS,GAAG,UAElC,SAAA;AAAA,QACA,gBAAAL,EAACI,KAAS,gBAAe,OAAM,WAAS,IACrC,UAAAN,EAAO,IAAI,CAAOU,wBAEd,QACC,EAAA,UAAA,gBAAAR,EAACS,KACC,UAAC,gBAAAT,EAAAU,GAAA,EAAI,KAAKF,GAAK,SAAS,MAAMd,KAAA,gBAAAA,EAAwB,EAAE,OAAOc,KAAQ,CAAA,EACzE,CAAA,KAHSA,CAIX,CAEH,GACH;AAAA,MAAA,GACF;AAAA,IACF,EAAA,CAAA;AAAA,EAEJ;AACF;"}
1
+ {"version":3,"file":"subjective-review.js","sources":["../../../../../src/features/worksheet/worksheet/worksheet-question/subjective-review.tsx"],"sourcesContent":["import { memo, type FC } from 'react';\n\nimport FlexView from '../../../ui/layout/flex-view';\nimport Separator from '../../../ui/separator/separator';\nimport Text from '../../../ui/text/text';\nimport type { ISubjectiveSheetProps, IWorksheetResponse } from '../worksheet-types';\nimport SubjectiveQuestionFeedback from './subjective-feedback';\nimport { Img, Thumbnail, Wrapper } from './subjective-styled';\nimport { useWorksheetStore } from '../hooks/use-worksheet-store';\n\nexport interface ISubjectiveQuestionReviewProps extends ISubjectiveSheetProps {\n responseId: string;\n response?: IWorksheetResponse;\n nextQuestionId?: string;\n studentId: string;\n responses?: Record<string, IWorksheetResponse>;\n}\n\nexport interface IFile {\n file: File;\n url: string;\n name?: string;\n}\n\nconst SubjectiveQuestionReview: FC<ISubjectiveQuestionReviewProps> = memo(\n function SubjectiveQuestionReview(props) {\n const { userType } = useWorksheetStore(state => ({\n userType: state.userType,\n }));\n const { response, openImagesReviewModal, isReviewPending } = props;\n\n const comment = response?.teacherReview?.reviewComment;\n const images = response?.teacherReview?.images ?? [];\n\n if (isReviewPending && userType === 'TEACHER') {\n return <SubjectiveQuestionFeedback {...props} />;\n }\n\n return (\n <Wrapper $gapX={0.5}>\n <FlexView $background=\"ORANGE_5\" $widthX={3.8} $heightX={1.5} $justifyContent=\"center\">\n <Text $renderAs=\"ub2-bold\" $color=\"WHITE\">\n Review\n </Text>\n </FlexView>\n <Separator heightX={1} />\n <FlexView $flexDirection=\"row\">\n <Text $renderAs=\"ub2\" $widthX={8}>\n Score\n </Text>\n <Text $renderAs=\"ub2\">{response?.score?.score ?? 0}</Text>\n </FlexView>\n <Separator heightX={1} />\n <FlexView $flexDirection=\"row\">\n <Text $renderAs=\"ub2\" $widthX={8}>\n Comments\n </Text>\n {comment ? <Text $renderAs=\"ub2\">{comment}</Text> : <Text $renderAs=\"ub2\"> -- </Text>}\n </FlexView>\n <Separator heightX={1} />\n <FlexView $flexDirection=\"row\">\n <Text $renderAs=\"ub2\" $widthX={8}>\n Files\n </Text>\n <FlexView $flexDirection=\"row\" $flexWrap>\n {images.map(img => {\n return (\n <span key={img}>\n <Thumbnail>\n <Img src={img} onClick={() => openImagesReviewModal?.({ image: img })} />\n </Thumbnail>\n </span>\n );\n })}\n </FlexView>\n </FlexView>\n </Wrapper>\n );\n },\n);\n\nexport default SubjectiveQuestionReview;\n"],"names":["SubjectiveQuestionReview","memo","props","userType","useWorksheetStore","state","response","openImagesReviewModal","isReviewPending","comment","_a","images","_b","jsx","SubjectiveQuestionFeedback","jsxs","Wrapper","FlexView","Text","Separator","_c","img","Thumbnail","Img"],"mappings":";;;;;;;;AAwBA,MAAMA,IAA+DC;AAAA,EACnE,SAAkCC,GAAO;;AACvC,UAAM,EAAE,UAAAC,EAAA,IAAaC,EAAkB,CAAUC,OAAA;AAAA,MAC/C,UAAUA,EAAM;AAAA,IAChB,EAAA,GACI,EAAE,UAAAC,GAAU,uBAAAC,GAAuB,iBAAAC,EAAA,IAAoBN,GAEvDO,KAAUC,IAAAJ,KAAA,gBAAAA,EAAU,kBAAV,gBAAAI,EAAyB,eACnCC,MAASC,IAAAN,KAAA,gBAAAA,EAAU,kBAAV,gBAAAM,EAAyB,WAAU,CAAA;AAE9C,WAAAJ,KAAmBL,MAAa,YAC3B,gBAAAU,EAACC,GAA4B,EAAA,GAAGZ,EAAO,CAAA,IAI9C,gBAAAa,EAACC,GAAQ,EAAA,OAAO,KACd,UAAA;AAAA,MAAA,gBAAAH,EAACI,KAAS,aAAY,YAAW,SAAS,KAAK,UAAU,KAAK,iBAAgB,UAC5E,UAAA,gBAAAJ,EAACK,KAAK,WAAU,YAAW,QAAO,SAAQ,mBAE1C,CAAA,GACF;AAAA,MACA,gBAAAL,EAACM,GAAU,EAAA,SAAS,EAAG,CAAA;AAAA,MACvB,gBAAAJ,EAACE,GAAS,EAAA,gBAAe,OACvB,UAAA;AAAA,QAAA,gBAAAJ,EAACK,GAAK,EAAA,WAAU,OAAM,SAAS,GAAG,UAElC,SAAA;AAAA,0BACCA,GAAK,EAAA,WAAU,OAAO,YAAUE,IAAAd,KAAA,gBAAAA,EAAA,UAAA,gBAAAc,EAAO,UAAS,GAAE;AAAA,MAAA,GACrD;AAAA,MACA,gBAAAP,EAACM,GAAU,EAAA,SAAS,EAAG,CAAA;AAAA,MACvB,gBAAAJ,EAACE,GAAS,EAAA,gBAAe,OACvB,UAAA;AAAA,QAAA,gBAAAJ,EAACK,GAAK,EAAA,WAAU,OAAM,SAAS,GAAG,UAElC,YAAA;AAAA,QACCT,IAAW,gBAAAI,EAAAK,GAAA,EAAK,WAAU,OAAO,UAAQT,EAAA,CAAA,IAAW,gBAAAI,EAAAK,GAAA,EAAK,WAAU,OAAM,UAAI,QAAA;AAAA,MAAA,GAChF;AAAA,MACA,gBAAAL,EAACM,GAAU,EAAA,SAAS,EAAG,CAAA;AAAA,MACvB,gBAAAJ,EAACE,GAAS,EAAA,gBAAe,OACvB,UAAA;AAAA,QAAA,gBAAAJ,EAACK,GAAK,EAAA,WAAU,OAAM,SAAS,GAAG,UAElC,SAAA;AAAA,QACA,gBAAAL,EAACI,KAAS,gBAAe,OAAM,WAAS,IACrC,UAAAN,EAAO,IAAI,CAAOU,wBAEd,QACC,EAAA,UAAA,gBAAAR,EAACS,KACC,UAAC,gBAAAT,EAAAU,GAAA,EAAI,KAAKF,GAAK,SAAS,MAAMd,KAAA,gBAAAA,EAAwB,EAAE,OAAOc,KAAQ,CAAA,EACzE,CAAA,KAHSA,CAIX,CAEH,GACH;AAAA,MAAA,GACF;AAAA,IACF,EAAA,CAAA;AAAA,EAEJ;AACF;"}
package/dist/index.d.ts CHANGED
@@ -1885,6 +1885,7 @@ declare interface IExtraPracticeSection extends ILPARMilestoneSheetItemCallbacks
1885
1885
  declare interface IFile {
1886
1886
  file: File;
1887
1887
  url: string;
1888
+ name?: string;
1888
1889
  }
1889
1890
 
1890
1891
  declare interface IFirstSessionIntroductionProps {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cuemath/leap",
3
- "version": "4.0.5-as3",
3
+ "version": "4.0.5-as5",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"