l-min-components 1.7.1405 → 1.7.1407
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/package.json +1 -1
- package/src/components/reportsComponents/fullAnalysis/components/InlineClampedText.jsx +35 -62
- package/src/components/reportsComponents/fullAnalysis/components/SpeechAnalysis.jsx +8 -4
- package/src/components/reportsComponents/fullAnalysis/index.jsx +27 -22
- package/src/components/reportsComponents/reportQuestions/api/index.jsx +114 -0
- package/src/components/reportsComponents/reportQuestions/components/comment.jsx +98 -5
- package/src/components/reportsComponents/reportQuestions/components/modals/gradingModal.jsx +16 -2
- package/src/components/reportsComponents/reportQuestions/components/style/comment.style.jsx +4 -0
- package/src/components/reportsComponents/reportQuestions/contants.jsx +22 -16
- package/src/components/reportsComponents/reportQuestions/index.jsx +51 -6
package/package.json
CHANGED
|
@@ -1,78 +1,51 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
2
|
-
import Truncate from "react-truncate";
|
|
1
|
+
import React, { useState, useRef, useEffect } from "react";
|
|
3
2
|
import styled from "styled-components";
|
|
4
3
|
|
|
5
4
|
const InlineClampedText = ({ text }) => {
|
|
6
5
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
7
|
-
const [
|
|
6
|
+
const [isClamped, setIsClamped] = useState(false);
|
|
7
|
+
const textRef = useRef(null);
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const el = textRef.current;
|
|
11
|
+
if (el) {
|
|
12
|
+
// Check if the text is clamped by comparing scrollHeight and offsetHeight
|
|
13
|
+
setIsClamped(el.scrollHeight > el.offsetHeight);
|
|
14
|
+
}
|
|
15
|
+
}, [text]);
|
|
8
16
|
|
|
9
17
|
return (
|
|
10
18
|
<Container>
|
|
11
|
-
{
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<span
|
|
19
|
-
onClick={() => setIsExpanded(true)}
|
|
20
|
-
style={{
|
|
21
|
-
cursor: "pointer",
|
|
22
|
-
color: "#00C2C2",
|
|
23
|
-
fontSize: "14px",
|
|
24
|
-
}}
|
|
25
|
-
>
|
|
26
|
-
See more
|
|
27
|
-
</span>
|
|
28
|
-
</>
|
|
29
|
-
) : (
|
|
30
|
-
""
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
onTruncate={(trunc) => setTruncated(trunc)}
|
|
34
|
-
>
|
|
35
|
-
<span
|
|
36
|
-
style={{
|
|
37
|
-
color: "#4A4D4D",
|
|
38
|
-
fontSize: "14px",
|
|
39
|
-
}}
|
|
40
|
-
>
|
|
41
|
-
{text}
|
|
42
|
-
</span>
|
|
43
|
-
</Truncate>
|
|
44
|
-
) : (
|
|
45
|
-
<>
|
|
46
|
-
<span
|
|
47
|
-
style={{
|
|
48
|
-
color: "#4A4D4D",
|
|
49
|
-
fontSize: "14px",
|
|
50
|
-
}}
|
|
51
|
-
>
|
|
52
|
-
{text}
|
|
53
|
-
</span>{" "}
|
|
54
|
-
{truncated && (
|
|
55
|
-
<span
|
|
56
|
-
onClick={() => setIsExpanded(false)}
|
|
57
|
-
style={{
|
|
58
|
-
cursor: "pointer",
|
|
59
|
-
color: "#00C2C2",
|
|
60
|
-
fontSize: "14px",
|
|
61
|
-
}}
|
|
62
|
-
>
|
|
63
|
-
Show less
|
|
64
|
-
</span>
|
|
65
|
-
)}
|
|
66
|
-
</>
|
|
19
|
+
<TextWrapper isExpanded={isExpanded} ref={textRef}>
|
|
20
|
+
{text}
|
|
21
|
+
</TextWrapper>
|
|
22
|
+
{isClamped && (
|
|
23
|
+
<Toggle onClick={() => setIsExpanded(!isExpanded)}>
|
|
24
|
+
{isExpanded ? "Show less" : "See more"}
|
|
25
|
+
</Toggle>
|
|
67
26
|
)}
|
|
68
27
|
</Container>
|
|
69
28
|
);
|
|
70
29
|
};
|
|
71
30
|
|
|
72
31
|
const Container = styled.div`
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
32
|
+
font-size: 14px;
|
|
33
|
+
color: #4a4d4d;
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const TextWrapper = styled.div`
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
display: -webkit-box;
|
|
39
|
+
-webkit-line-clamp: ${(props) => (props.isExpanded ? "unset" : 2)};
|
|
40
|
+
-webkit-box-orient: vertical;
|
|
41
|
+
white-space: normal;
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
const Toggle = styled.span`
|
|
45
|
+
color: #00c2c2;
|
|
46
|
+
font-size: 14px;
|
|
47
|
+
cursor: pointer;
|
|
48
|
+
margin-left: 5px;
|
|
76
49
|
`;
|
|
77
50
|
|
|
78
51
|
export default InlineClampedText;
|
|
@@ -17,7 +17,7 @@ import PenIcon from "../icons/pen";
|
|
|
17
17
|
import Mircophone from "../icons/mircophone";
|
|
18
18
|
import { Se, Tr } from "react-flags-select";
|
|
19
19
|
import Table from "./table";
|
|
20
|
-
import TranslateDropdown from "./translateDropdown";
|
|
20
|
+
// import TranslateDropdown from "./translateDropdown";
|
|
21
21
|
import { Progress } from "rsuite";
|
|
22
22
|
import ClarityItem from "./ClarityItem";
|
|
23
23
|
import InlineClampedText from "./InlineClampedText";
|
|
@@ -119,7 +119,7 @@ const SpeechAnalysis = ({ Aidata }) => {
|
|
|
119
119
|
<span>Grade</span>
|
|
120
120
|
<div className="space-between">
|
|
121
121
|
<span>Feedback</span>
|
|
122
|
-
<TranslateDropdown />
|
|
122
|
+
{/* <TranslateDropdown /> */}
|
|
123
123
|
</div>
|
|
124
124
|
</div>
|
|
125
125
|
<div className="table_row">
|
|
@@ -157,6 +157,7 @@ const SpeechAnalysis = ({ Aidata }) => {
|
|
|
157
157
|
</div>
|
|
158
158
|
</div>
|
|
159
159
|
<InlineClampedText
|
|
160
|
+
key={item?.id}
|
|
160
161
|
text={item?.["Phoneme Feedback"] || ""}
|
|
161
162
|
/>
|
|
162
163
|
</div>
|
|
@@ -289,7 +290,7 @@ const SpeechAnalysis = ({ Aidata }) => {
|
|
|
289
290
|
|
|
290
291
|
<div className="space-between">
|
|
291
292
|
<span>Feedback</span>
|
|
292
|
-
<TranslateDropdown />
|
|
293
|
+
{/* <TranslateDropdown /> */}
|
|
293
294
|
</div>
|
|
294
295
|
</div>
|
|
295
296
|
<div className="table_row">
|
|
@@ -305,7 +306,10 @@ const SpeechAnalysis = ({ Aidata }) => {
|
|
|
305
306
|
{item?.score} {item?.total ? `of ${item?.total}` : ""}
|
|
306
307
|
</p>
|
|
307
308
|
<p>
|
|
308
|
-
<InlineClampedText
|
|
309
|
+
<InlineClampedText
|
|
310
|
+
key={item?.title}
|
|
311
|
+
text={item?.feedback || ""}
|
|
312
|
+
/>
|
|
309
313
|
</p>
|
|
310
314
|
</div>
|
|
311
315
|
))}
|
|
@@ -20,7 +20,6 @@ import Grammar from "./components/Grammar";
|
|
|
20
20
|
|
|
21
21
|
const FullAnalysis = ({ data, onClose }) => {
|
|
22
22
|
const { setRightLayout, setCenterLayoutStyle } = useContext(OutletContext);
|
|
23
|
-
const [selected, setSelected] = useState(null);
|
|
24
23
|
|
|
25
24
|
// const colors = [
|
|
26
25
|
// { main: "#009999", sub: "#E6F9F9" },
|
|
@@ -32,10 +31,11 @@ const FullAnalysis = ({ data, onClose }) => {
|
|
|
32
31
|
label: item?.label,
|
|
33
32
|
value: item?.id,
|
|
34
33
|
}));
|
|
34
|
+
const [selected, setSelected] = useState(tabs?.[0]?.value || "");
|
|
35
35
|
|
|
36
36
|
useEffect(() => {
|
|
37
37
|
if (tabs?.length) {
|
|
38
|
-
setSelected(tabs
|
|
38
|
+
setSelected(tabs[0].value);
|
|
39
39
|
}
|
|
40
40
|
}, [tabs?.length]);
|
|
41
41
|
|
|
@@ -127,25 +127,28 @@ const FullAnalysis = ({ data, onClose }) => {
|
|
|
127
127
|
</p>
|
|
128
128
|
</ProgressCircleSection>
|
|
129
129
|
<ul>
|
|
130
|
-
{data?.models?.map((model) =>
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
<
|
|
134
|
-
<
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
130
|
+
{data?.models?.map((model) => {
|
|
131
|
+
if (model?.id === "logic_evaluation") return null;
|
|
132
|
+
return (
|
|
133
|
+
<li key={model?.id}>
|
|
134
|
+
<p>{`${model?.label} point`}</p>
|
|
135
|
+
<div style={{ backgroundColor: model?.bgColor }}>
|
|
136
|
+
<span style={{ color: model?.primaryColor }}>
|
|
137
|
+
{model?.score || 0}%
|
|
138
|
+
</span>
|
|
139
|
+
<Progress.Line
|
|
140
|
+
percent={model?.score || 0}
|
|
141
|
+
strokeWidth={4}
|
|
142
|
+
trailWidth={4}
|
|
143
|
+
strokeColor={model?.primaryColor}
|
|
144
|
+
trailColor={"#fff"}
|
|
145
|
+
showInfo={false}
|
|
146
|
+
style={{ width: "100%" }}
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
149
|
+
</li>
|
|
150
|
+
);
|
|
151
|
+
})}
|
|
149
152
|
</ul>
|
|
150
153
|
</ProgressSidebar>
|
|
151
154
|
{selectedModel?.id === "speech_analysis" && (
|
|
@@ -159,7 +162,9 @@ const FullAnalysis = ({ data, onClose }) => {
|
|
|
159
162
|
)}
|
|
160
163
|
</Sidebar>
|
|
161
164
|
<MainSection>
|
|
162
|
-
|
|
165
|
+
{tabs?.length > 0 && (
|
|
166
|
+
<Tabs options={tabs} selected={selected} onChange={setSelected} />
|
|
167
|
+
)}
|
|
163
168
|
{selectedModel?.id === "logic_evaluation" && (
|
|
164
169
|
<Evaluation Aidata={selectedModel} />
|
|
165
170
|
)}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import useAxios from "axios-hooks";
|
|
2
|
+
|
|
3
|
+
const useApi = () => {
|
|
4
|
+
const [{ ...addCommentData }, addComment] = useAxios(
|
|
5
|
+
{
|
|
6
|
+
method: "POST",
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
manual: true,
|
|
10
|
+
}
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const handleAddComment = async (
|
|
14
|
+
question_activity_id,
|
|
15
|
+
test_id,
|
|
16
|
+
data,
|
|
17
|
+
enterprise_id
|
|
18
|
+
) => {
|
|
19
|
+
try {
|
|
20
|
+
const url = enterprise_id
|
|
21
|
+
? `/learn/v1/instructor/${enterprise_id}/tests/${test_id}/add_comment/${question_activity_id}/`
|
|
22
|
+
: `/learn/v1/instructor/tests/${test_id}/add_comment/${question_activity_id}/`;
|
|
23
|
+
return await addComment({
|
|
24
|
+
url,
|
|
25
|
+
data,
|
|
26
|
+
});
|
|
27
|
+
} catch (err) {
|
|
28
|
+
console.log(err);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const [{ ...gradeQuestionData }, gradeQuestion] = useAxios(
|
|
33
|
+
{
|
|
34
|
+
method: "POST",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
manual: true,
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const handleGradeQuestion = async (
|
|
42
|
+
question_activity_id,
|
|
43
|
+
session_id,
|
|
44
|
+
data,
|
|
45
|
+
enterprise_id
|
|
46
|
+
) => {
|
|
47
|
+
try {
|
|
48
|
+
const url = enterprise_id
|
|
49
|
+
? `/learn/v1/instructor/${enterprise_id}/tests/${question_activity_id}/submit_score/${session_id}/`
|
|
50
|
+
: `/learn/v1/instructor/tests/${question_activity_id}/submit_score/${session_id}/`;
|
|
51
|
+
return await gradeQuestion({
|
|
52
|
+
url,
|
|
53
|
+
data: {
|
|
54
|
+
...data,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.log(err);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const [{ ...deleteCommentData }, deleteComment] = useAxios(
|
|
63
|
+
{
|
|
64
|
+
method: "POST",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
manual: true,
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const handleDeleteComment = async (
|
|
72
|
+
question_activity_id,
|
|
73
|
+
test_id,
|
|
74
|
+
index,
|
|
75
|
+
enterprise_id
|
|
76
|
+
) => {
|
|
77
|
+
try {
|
|
78
|
+
const url = enterprise_id
|
|
79
|
+
? `/learn/v1/instructor/${enterprise_id}/tests/${test_id}/delete_comment/${question_activity_id}/?index=${index}`
|
|
80
|
+
: `/learn/v1/instructor/tests/${test_id}/delete_comment/${question_activity_id}/?index=${index}`;
|
|
81
|
+
return await deleteComment({
|
|
82
|
+
url,
|
|
83
|
+
});
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.log(err);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const [{ ...uploadData }, upload] = useAxios(
|
|
89
|
+
{
|
|
90
|
+
method: "POST",
|
|
91
|
+
},
|
|
92
|
+
{ manual: true }
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const handleUpload = async (data) => {
|
|
96
|
+
return await upload({
|
|
97
|
+
url: "/media/v1/instructor/files/",
|
|
98
|
+
data,
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
addCommentData,
|
|
104
|
+
handleAddComment,
|
|
105
|
+
handleGradeQuestion,
|
|
106
|
+
gradeQuestionData,
|
|
107
|
+
deleteCommentData,
|
|
108
|
+
handleDeleteComment,
|
|
109
|
+
handleUpload,
|
|
110
|
+
uploadData,
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export default useApi;
|
|
@@ -16,22 +16,38 @@ import DeleteIcon from "../../../../assets/svg/delete-icon";
|
|
|
16
16
|
import moment from "moment";
|
|
17
17
|
import ResponseAudio from "./responseAudio";
|
|
18
18
|
import DeleteModal from "./modals/deleteModal";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
AudioWaveComponent,
|
|
21
|
+
FullPageLoader,
|
|
22
|
+
OutletContext,
|
|
23
|
+
useAudioPlayer,
|
|
24
|
+
} from "../../..";
|
|
20
25
|
import { CloseIcon } from "../../../header/assets/svg/close";
|
|
21
26
|
import classNames from "classnames";
|
|
22
27
|
import { useRecorder } from "react-microphone-recorder";
|
|
23
28
|
import PlayIcon from "../../../../assets/svg/playIcon";
|
|
24
29
|
import styled from "styled-components";
|
|
25
30
|
import { FaPause } from "react-icons/fa";
|
|
31
|
+
import useApi from "../api";
|
|
26
32
|
/**
|
|
27
33
|
* @param {Object} props
|
|
28
34
|
* @param {boolean} props.editMode
|
|
29
35
|
* @returns {React.ReactNode}
|
|
30
36
|
*/
|
|
31
|
-
const Comment = ({ editMode = false, data,
|
|
37
|
+
const Comment = ({ editMode = false, data, testId, questionData, setData }) => {
|
|
32
38
|
const { affiliateAccount } = useContext(OutletContext);
|
|
33
39
|
const [toggleDelete, setToggleDelete] = useState(false);
|
|
34
40
|
const [showForm, setShowForm] = useState(null);
|
|
41
|
+
const [value, setValue] = useState("");
|
|
42
|
+
const {
|
|
43
|
+
handleAddComment,
|
|
44
|
+
addCommentData,
|
|
45
|
+
handleDeleteComment,
|
|
46
|
+
deleteCommentData,
|
|
47
|
+
handleUpload,
|
|
48
|
+
uploadData,
|
|
49
|
+
} = useApi();
|
|
50
|
+
const enterpriseId = affiliateAccount?.id;
|
|
35
51
|
|
|
36
52
|
const {
|
|
37
53
|
startRecording,
|
|
@@ -41,7 +57,6 @@ const Comment = ({ editMode = false, data, accountType }) => {
|
|
|
41
57
|
resetRecording,
|
|
42
58
|
recordingState,
|
|
43
59
|
} = useRecorder();
|
|
44
|
-
console.log(data);
|
|
45
60
|
|
|
46
61
|
if (!editMode && !data) return;
|
|
47
62
|
|
|
@@ -50,10 +65,76 @@ const Comment = ({ editMode = false, data, accountType }) => {
|
|
|
50
65
|
{ text: "Audio", icon: AudioIcon },
|
|
51
66
|
];
|
|
52
67
|
|
|
68
|
+
const handleComment = async () => {
|
|
69
|
+
if (!enterpriseId || !testId || !questionData?.question_activity_id) return;
|
|
70
|
+
|
|
71
|
+
if (showForm === "Text") {
|
|
72
|
+
const data = { data: { data: { type: "text", text: value } } };
|
|
73
|
+
const res = await handleAddComment(
|
|
74
|
+
questionData?.question_activity_id,
|
|
75
|
+
testId,
|
|
76
|
+
data,
|
|
77
|
+
enterpriseId
|
|
78
|
+
);
|
|
79
|
+
if (res?.data) {
|
|
80
|
+
const comment = res?.data?.data?.answer?.comments?.[0];
|
|
81
|
+
setData(comment);
|
|
82
|
+
setShowForm(null);
|
|
83
|
+
setValue("");
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
const formData = new FormData();
|
|
87
|
+
formData.append("upload", audioFile);
|
|
88
|
+
const resMedia = await handleUpload(formData);
|
|
89
|
+
const media = resMedia?.data?.results?.[0];
|
|
90
|
+
if (media) {
|
|
91
|
+
const data = { data: { data: { type: "audio", audio: media } } };
|
|
92
|
+
const res = await handleAddComment(
|
|
93
|
+
questionData?.question_activity_id,
|
|
94
|
+
testId,
|
|
95
|
+
data,
|
|
96
|
+
enterpriseId
|
|
97
|
+
);
|
|
98
|
+
if (res?.data) {
|
|
99
|
+
const comment = res?.data?.data?.answer?.comments?.[0];
|
|
100
|
+
setData(comment);
|
|
101
|
+
setShowForm(null);
|
|
102
|
+
resetRecording();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const disabled = showForm === "Text" ? !value : !audioFile;
|
|
109
|
+
|
|
110
|
+
const handleDelete = async () => {
|
|
111
|
+
if (!enterpriseId || !testId || !questionData?.question_activity_id) return;
|
|
112
|
+
|
|
113
|
+
const res = await handleDeleteComment(
|
|
114
|
+
questionData?.question_activity_id,
|
|
115
|
+
testId,
|
|
116
|
+
0,
|
|
117
|
+
enterpriseId
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
if (res?.data) {
|
|
121
|
+
setData(null);
|
|
122
|
+
setToggleDelete(false);
|
|
123
|
+
setValue("");
|
|
124
|
+
setShowForm(null);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
console.log(data);
|
|
129
|
+
|
|
53
130
|
return (
|
|
54
131
|
<CommentContainer>
|
|
132
|
+
{(addCommentData?.loading ||
|
|
133
|
+
deleteCommentData?.loading ||
|
|
134
|
+
uploadData?.loading) && <FullPageLoader fixed hasBackground />}
|
|
55
135
|
{toggleDelete && (
|
|
56
136
|
<DeleteModal
|
|
137
|
+
onClick={handleDelete}
|
|
57
138
|
close={() => {
|
|
58
139
|
setToggleDelete(false);
|
|
59
140
|
}}
|
|
@@ -126,7 +207,13 @@ const Comment = ({ editMode = false, data, accountType }) => {
|
|
|
126
207
|
<>
|
|
127
208
|
<CommentContentForm>
|
|
128
209
|
{showForm === "Text" && (
|
|
129
|
-
<textarea
|
|
210
|
+
<textarea
|
|
211
|
+
placeholder="Input comment here"
|
|
212
|
+
value={value}
|
|
213
|
+
onChange={(e) => {
|
|
214
|
+
setValue(e?.target?.value);
|
|
215
|
+
}}
|
|
216
|
+
/>
|
|
130
217
|
)}
|
|
131
218
|
{showForm === "Audio" && (
|
|
132
219
|
<AudioRecordPlayer
|
|
@@ -161,7 +248,13 @@ const Comment = ({ editMode = false, data, accountType }) => {
|
|
|
161
248
|
</li>
|
|
162
249
|
))}
|
|
163
250
|
</ul>
|
|
164
|
-
<button
|
|
251
|
+
<button
|
|
252
|
+
className="sm-btn"
|
|
253
|
+
disabled={disabled}
|
|
254
|
+
onClick={handleComment}
|
|
255
|
+
>
|
|
256
|
+
Send
|
|
257
|
+
</button>
|
|
165
258
|
</CommentContentAction>
|
|
166
259
|
</>
|
|
167
260
|
)}
|
|
@@ -4,7 +4,7 @@ import styled from "styled-components";
|
|
|
4
4
|
import Radio from "../radio";
|
|
5
5
|
import ButtonComponent from "../../../../button";
|
|
6
6
|
|
|
7
|
-
const GradingModal = ({ aiValue, onClose }) => {
|
|
7
|
+
const GradingModal = ({ aiValue, onClose, setScore }) => {
|
|
8
8
|
const [selected, setSelected] = useState("manually");
|
|
9
9
|
const [value, setValue] = useState("");
|
|
10
10
|
|
|
@@ -12,8 +12,18 @@ const GradingModal = ({ aiValue, onClose }) => {
|
|
|
12
12
|
setValue("");
|
|
13
13
|
}, [selected]);
|
|
14
14
|
|
|
15
|
+
const numberValue = Number(value || 0);
|
|
16
|
+
const disable = selected === "manually" && (numberValue > 100 || !value);
|
|
17
|
+
|
|
15
18
|
const isNumbter = typeof aiValue === "number";
|
|
16
19
|
|
|
20
|
+
const handleSetValue = () => {
|
|
21
|
+
setScore?.({
|
|
22
|
+
score_method: selected === "manually" ? "MANUAL" : "AI",
|
|
23
|
+
score: selected === "manually" ? numberValue : aiValue,
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
|
|
17
27
|
return (
|
|
18
28
|
<Container>
|
|
19
29
|
<Wrapper>
|
|
@@ -66,7 +76,11 @@ const GradingModal = ({ aiValue, onClose }) => {
|
|
|
66
76
|
)}
|
|
67
77
|
</div>
|
|
68
78
|
</TopSection>
|
|
69
|
-
<ButtonComponent
|
|
79
|
+
<ButtonComponent
|
|
80
|
+
text="Save score"
|
|
81
|
+
disabled={disable}
|
|
82
|
+
onClick={handleSetValue}
|
|
83
|
+
/>
|
|
70
84
|
</Content>
|
|
71
85
|
</Wrapper>
|
|
72
86
|
</Container>
|
|
@@ -248,8 +248,8 @@ const essayUnscripted = {
|
|
|
248
248
|
};
|
|
249
249
|
|
|
250
250
|
const soundPlay = {
|
|
251
|
-
question_id:
|
|
252
|
-
question_activity_id:
|
|
251
|
+
question_id: 3078,
|
|
252
|
+
question_activity_id: 8384,
|
|
253
253
|
position_index: 0,
|
|
254
254
|
title: "Say the sentence",
|
|
255
255
|
type: "SOUND_PLAY",
|
|
@@ -265,36 +265,42 @@ const soundPlay = {
|
|
|
265
265
|
instruction: "Say the sentence",
|
|
266
266
|
},
|
|
267
267
|
answer: {
|
|
268
|
-
session_id: "
|
|
268
|
+
session_id: "17c9403e6b2c4761a06bf1ac22fffaf2",
|
|
269
269
|
attempt_index: 1,
|
|
270
270
|
token_claimed: 0,
|
|
271
271
|
score: null,
|
|
272
272
|
marked: false,
|
|
273
273
|
expire_at: null,
|
|
274
|
-
created_at: "2025-07-
|
|
274
|
+
created_at: "2025-07-09T11:28:33.281076Z",
|
|
275
275
|
grading_date: null,
|
|
276
276
|
data: {
|
|
277
277
|
type: "SOUND_PLAY",
|
|
278
|
-
session_id: "
|
|
279
|
-
submited_datetime: "2025-07-
|
|
278
|
+
session_id: "b269deece28047f",
|
|
279
|
+
submited_datetime: "2025-07-09T09:57:10.44195Z",
|
|
280
280
|
audio: {
|
|
281
|
-
id: "
|
|
282
|
-
url: "https://dev-117782726-api.learngual.com/media/v1/files/
|
|
281
|
+
id: "1c06b5d866054d9fba5d67d14eeff436",
|
|
282
|
+
url: "https://dev-117782726-api.learngual.com/media/v1/files/1c06b5d866054d9fba5d67d14eeff436/stream/",
|
|
283
283
|
stream_url: null,
|
|
284
284
|
mimetype: "audio/mpeg",
|
|
285
|
-
size:
|
|
286
|
-
created_at: "2025-07-
|
|
287
|
-
updated_at: "2025-07-
|
|
285
|
+
size: 54500.0,
|
|
286
|
+
created_at: "2025-07-09T11:28:23.930419Z",
|
|
287
|
+
updated_at: "2025-07-14T06:00:38.441656Z",
|
|
288
288
|
uploaded_media_url: null,
|
|
289
289
|
convertion_status: "DONE",
|
|
290
290
|
},
|
|
291
291
|
},
|
|
292
|
-
comments:
|
|
292
|
+
comments: [],
|
|
293
293
|
},
|
|
294
|
-
score: 0,
|
|
295
|
-
ai_score:
|
|
296
|
-
grading_data: {
|
|
297
|
-
|
|
294
|
+
score: 100.0,
|
|
295
|
+
ai_score: 100.0,
|
|
296
|
+
grading_data: {
|
|
297
|
+
base_url: "https://dev-117782726-api.learngual.com/",
|
|
298
|
+
grader_id: "e4b92c77e5",
|
|
299
|
+
session_id: "17c9403e6b2c4761a06bf1ac22fffaf2",
|
|
300
|
+
score: 100.0,
|
|
301
|
+
grade_method: "AI",
|
|
302
|
+
},
|
|
303
|
+
grade_method: "AI",
|
|
298
304
|
suggested_score: null,
|
|
299
305
|
};
|
|
300
306
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useState } from "react";
|
|
1
|
+
import React, { useContext, useEffect, useState } from "react";
|
|
2
2
|
import constants, {
|
|
3
3
|
aiWordSample,
|
|
4
4
|
sampleResponse,
|
|
@@ -37,6 +37,9 @@ import DialogueUnscripted from "./questions/dialogueUnscripted";
|
|
|
37
37
|
import GradingModal from "./components/modals/gradingModal";
|
|
38
38
|
import classNames from "classnames";
|
|
39
39
|
import FullAnalysis from "../fullAnalysis";
|
|
40
|
+
import useApi from "./api";
|
|
41
|
+
import FullPageLoader from "../../fullPageLoader";
|
|
42
|
+
import { OutletContext } from "../..";
|
|
40
43
|
|
|
41
44
|
/**
|
|
42
45
|
* @param {Object} props
|
|
@@ -52,10 +55,15 @@ const ReportQuestions = ({
|
|
|
52
55
|
data,
|
|
53
56
|
AiData,
|
|
54
57
|
onClose,
|
|
58
|
+
testId,
|
|
55
59
|
}) => {
|
|
56
60
|
const [comment, setComment] = useState(null);
|
|
61
|
+
const [intructorScore, setInstructorScore] = useState(0);
|
|
57
62
|
const [toggleGrade, setToggleGrade] = useState(false);
|
|
58
63
|
const [toggle, setToggle] = useState(null);
|
|
64
|
+
const { affiliateAccount } = useContext(OutletContext);
|
|
65
|
+
const enterpriseId = affiliateAccount?.id;
|
|
66
|
+
const { handleGradeQuestion, gradeQuestionData } = useApi();
|
|
59
67
|
const question = constants.questions.find(
|
|
60
68
|
(question) => question.value === questionType
|
|
61
69
|
);
|
|
@@ -63,7 +71,32 @@ const ReportQuestions = ({
|
|
|
63
71
|
|
|
64
72
|
useEffect(() => {
|
|
65
73
|
if (commentData?.length) setComment(commentData?.[0]);
|
|
66
|
-
}, [commentData]);
|
|
74
|
+
}, [commentData, data?.question_id]);
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (data?.question_id) {
|
|
78
|
+
setInstructorScore(data?.score);
|
|
79
|
+
}
|
|
80
|
+
}, [data?.question_id]);
|
|
81
|
+
|
|
82
|
+
const handleScore = async (value) => {
|
|
83
|
+
if (
|
|
84
|
+
!data?.question_activity_id ||
|
|
85
|
+
!enterpriseId ||
|
|
86
|
+
!data?.answer?.session_id
|
|
87
|
+
)
|
|
88
|
+
return;
|
|
89
|
+
const res = await handleGradeQuestion(
|
|
90
|
+
data?.question_activity_id,
|
|
91
|
+
data?.answer?.session_id,
|
|
92
|
+
value,
|
|
93
|
+
enterpriseId
|
|
94
|
+
);
|
|
95
|
+
if (res?.data) {
|
|
96
|
+
setInstructorScore(value?.score);
|
|
97
|
+
setToggleGrade(false);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
67
100
|
|
|
68
101
|
if (toggle) {
|
|
69
102
|
return (
|
|
@@ -75,10 +108,14 @@ const ReportQuestions = ({
|
|
|
75
108
|
/>
|
|
76
109
|
);
|
|
77
110
|
}
|
|
111
|
+
|
|
78
112
|
return (
|
|
79
113
|
<Container>
|
|
114
|
+
{gradeQuestionData?.loading && <FullPageLoader fixed hasBackground />}
|
|
80
115
|
{toggleGrade && (
|
|
81
116
|
<GradingModal
|
|
117
|
+
aiValue={data?.ai_score}
|
|
118
|
+
setScore={handleScore}
|
|
82
119
|
onClose={() => {
|
|
83
120
|
setToggleGrade(false);
|
|
84
121
|
}}
|
|
@@ -96,7 +133,7 @@ const ReportQuestions = ({
|
|
|
96
133
|
<ScoreHeader>
|
|
97
134
|
{accountType === "personal" ? (
|
|
98
135
|
<ScoreBadge>
|
|
99
|
-
Instructor’s score: <span>{parseInt(
|
|
136
|
+
Instructor’s score: <span>{parseInt(intructorScore || 0)}%</span>
|
|
100
137
|
</ScoreBadge>
|
|
101
138
|
) : (
|
|
102
139
|
<>
|
|
@@ -121,7 +158,8 @@ const ReportQuestions = ({
|
|
|
121
158
|
accountType === "instructor-affiliate"
|
|
122
159
|
}
|
|
123
160
|
>
|
|
124
|
-
Instructor’s score:
|
|
161
|
+
Instructor’s score:{" "}
|
|
162
|
+
<span>{parseInt(intructorScore || 0)}%</span>
|
|
125
163
|
</ScoreBadge>
|
|
126
164
|
{(accountType === "instructor-personal" ||
|
|
127
165
|
accountType === "instructor-affiliate") && (
|
|
@@ -158,7 +196,11 @@ const ReportQuestions = ({
|
|
|
158
196
|
/>
|
|
159
197
|
)}
|
|
160
198
|
{questionType === "essay-unscripted" && (
|
|
161
|
-
<EssayUnscripted
|
|
199
|
+
<EssayUnscripted
|
|
200
|
+
data={data}
|
|
201
|
+
aiData={AiData}
|
|
202
|
+
setToggle={setToggle}
|
|
203
|
+
/>
|
|
162
204
|
)}
|
|
163
205
|
{questionType === "sound-play" && (
|
|
164
206
|
<SoundPlay data={data} aiData={AiData} setToggle={setToggle} />
|
|
@@ -211,6 +253,9 @@ const ReportQuestions = ({
|
|
|
211
253
|
|
|
212
254
|
<Comment
|
|
213
255
|
data={comment}
|
|
256
|
+
testId={testId}
|
|
257
|
+
questionData={data}
|
|
258
|
+
setData={setComment}
|
|
214
259
|
editMode={
|
|
215
260
|
accountType === "instructor-personal" ||
|
|
216
261
|
accountType === "instructor-affiliate"
|
|
@@ -219,7 +264,7 @@ const ReportQuestions = ({
|
|
|
219
264
|
{(accountType === "instructor-personal" ||
|
|
220
265
|
accountType === "instructor-affiliate") && (
|
|
221
266
|
<QuestionFooter>
|
|
222
|
-
<ButtonComponent text="Save" />
|
|
267
|
+
<ButtonComponent text="Save" onClick={onClose} />
|
|
223
268
|
</QuestionFooter>
|
|
224
269
|
)}
|
|
225
270
|
</Content>
|