l-min-components 1.7.1420 → 1.7.1422

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "l-min-components",
3
- "version": "1.7.1420",
3
+ "version": "1.7.1422",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src/assets",
@@ -13,6 +13,7 @@ import {
13
13
  LeftLayout,
14
14
  RightLayout,
15
15
  MainLayout,
16
+ ChildrenBox,
16
17
  } from "./index.styled";
17
18
  import {
18
19
  Header as HeaderComponent,
@@ -532,7 +533,7 @@ const AppMainLayout = ({ children }) => {
532
533
  </div>
533
534
  </div>
534
535
  ) : (
535
- children ?? <Outlet />
536
+ <ChildrenBox> {children ?? <Outlet />} </ChildrenBox>
536
537
  )}
537
538
  </>
538
539
  )}
@@ -55,6 +55,11 @@ export const LeftLayout = styled.div`
55
55
  min-height: 100vh;
56
56
  `;
57
57
 
58
+ export const ChildrenBox = styled.div`
59
+ padding-bottom: 60px;
60
+ width: 100%;
61
+ `;
62
+
58
63
  export const CenterLayout = styled.div`
59
64
  width: 100%;
60
65
  /* max-width: 900px; */
@@ -1,3 +1,4 @@
1
+ import classNames from "classnames";
1
2
  import React from "react";
2
3
  import styled from "styled-components";
3
4
 
@@ -6,36 +7,71 @@ const ClarityItem = ({
6
7
  title = "",
7
8
  message = "",
8
9
  value = "",
10
+ alt,
9
11
  }) => {
10
12
  return (
11
13
  <Container>
12
- {type === "green" ? (
13
- <SpeedIcon />
14
- ) : type === "yellow" ? (
15
- <FluencyIcon />
16
- ) : type === "blue" ? (
17
- <UserIcon />
18
- ) : (
19
- <MicIcon />
14
+ {alt && (
15
+ <AltHeader>
16
+ {type === "pink" ? <PaperIcon /> : <BookIcon />} <h3>{title}</h3>
17
+ </AltHeader>
20
18
  )}
21
- <Content>
22
- <h3>{title}</h3>
23
- <p>{message}</p>
24
- </Content>
25
- <Value className={type} dangerouslySetInnerHTML={{ __html: value }} />
19
+ <Body className={classNames({ "alt-header": alt })}>
20
+ {!alt && (
21
+ <>
22
+ {type === "green" ? (
23
+ <SpeedIcon />
24
+ ) : type === "yellow" ? (
25
+ <FluencyIcon />
26
+ ) : type === "blue" ? (
27
+ <UserIcon />
28
+ ) : (
29
+ <MicIcon />
30
+ )}
31
+ </>
32
+ )}
33
+
34
+ <Content>
35
+ {!alt && <h3>{title}</h3>}
36
+ <p className={classNames({ "alt-header": alt })}>{message}</p>
37
+ </Content>
38
+ {!alt && (
39
+ <Value className={type} dangerouslySetInnerHTML={{ __html: value }} />
40
+ )}
41
+ </Body>
26
42
  </Container>
27
43
  );
28
44
  };
29
45
 
30
46
  const Container = styled.div`
31
- padding: 20px;
32
47
  border-radius: 20px;
33
48
  border: 1px solid #dfe5e5;
34
49
  background: #fff;
35
50
  width: 100%;
51
+ min-height: 120px;
52
+ `;
53
+ export const AltHeader = styled.div`
54
+ padding: 7px 10px;
55
+ display: flex;
56
+ align-items: center;
57
+ width: 100%;
58
+ gap: 10px;
59
+ border-bottom: 1px solid #dfe5e5;
60
+ h3 {
61
+ font-size: 16px;
62
+ font-weight: 600;
63
+ }
64
+ `;
65
+ const Body = styled.div`
36
66
  display: grid;
37
67
  grid-template-columns: 48px 1fr 60px;
38
68
  gap: 10px;
69
+ width: 100%;
70
+ padding: 20px;
71
+ &.alt-header {
72
+ grid-template-columns: 1fr;
73
+ padding-top: 12px;
74
+ }
39
75
  `;
40
76
 
41
77
  const Content = styled.div`
@@ -50,6 +86,12 @@ const Content = styled.div`
50
86
  font-size: 14px;
51
87
  line-height: 24px;
52
88
  max-width: 200px;
89
+ &::first-letter {
90
+ text-transform: capitalize;
91
+ }
92
+ &.alt-header {
93
+ max-width: none;
94
+ }
53
95
  }
54
96
  `;
55
97
 
@@ -84,6 +126,96 @@ const Value = styled.p`
84
126
 
85
127
  export default ClarityItem;
86
128
 
129
+ const BookIcon = () => (
130
+ <svg
131
+ width="31"
132
+ height="31"
133
+ viewBox="0 0 31 31"
134
+ fill="none"
135
+ xmlns="http://www.w3.org/2000/svg"
136
+ >
137
+ <rect
138
+ opacity="0.1"
139
+ y="0.318359"
140
+ width="30.3643"
141
+ height="30.3643"
142
+ rx="15.1822"
143
+ fill="#54B3F9"
144
+ />
145
+ <path
146
+ d="M22.6875 19.0582V10.0057C22.6875 9.10569 21.9525 8.43819 21.06 8.51319H21.015C19.44 8.64819 17.0475 9.45069 15.7125 10.2907L15.585 10.3732C15.3675 10.5082 15.0075 10.5082 14.79 10.3732L14.6025 10.2607C13.2675 9.42819 10.8825 8.63319 9.3075 8.50569C8.415 8.43069 7.6875 9.10569 7.6875 9.99819V19.0582C7.6875 19.7782 8.2725 20.4532 8.9925 20.5432L9.21 20.5732C10.8375 20.7907 13.35 21.6157 14.79 22.4032L14.82 22.4182C15.0225 22.5307 15.345 22.5307 15.54 22.4182C16.98 21.6232 19.5 20.7907 21.135 20.5732L21.3825 20.5432C22.1025 20.4532 22.6875 19.7782 22.6875 19.0582Z"
147
+ stroke="#54B3F9"
148
+ stroke-width="1.2"
149
+ stroke-linecap="round"
150
+ stroke-linejoin="round"
151
+ />
152
+ <path
153
+ d="M15.1875 10.6191V21.8691"
154
+ stroke="#54B3F9"
155
+ stroke-width="1.2"
156
+ stroke-linecap="round"
157
+ stroke-linejoin="round"
158
+ />
159
+ <path
160
+ d="M12 12.8691H10.3125"
161
+ stroke="#54B3F9"
162
+ stroke-width="1.2"
163
+ stroke-linecap="round"
164
+ stroke-linejoin="round"
165
+ />
166
+ <path
167
+ d="M12.5625 15.1191H10.3125"
168
+ stroke="#54B3F9"
169
+ stroke-width="1.2"
170
+ stroke-linecap="round"
171
+ stroke-linejoin="round"
172
+ />
173
+ </svg>
174
+ );
175
+
176
+ const PaperIcon = () => (
177
+ <svg
178
+ width="31"
179
+ height="31"
180
+ viewBox="0 0 31 31"
181
+ fill="none"
182
+ xmlns="http://www.w3.org/2000/svg"
183
+ >
184
+ <rect
185
+ opacity="0.1"
186
+ y="0.318359"
187
+ width="30.3643"
188
+ height="30.3643"
189
+ rx="15.1822"
190
+ fill="#F954D4"
191
+ />
192
+ <path
193
+ d="M22.4326 14.3309L21.6976 17.4659C21.0676 20.1734 19.8226 21.2684 17.4826 21.0434C17.1076 21.0134 16.7026 20.9459 16.2676 20.8409L15.0076 20.5409C11.8801 19.7984 10.9126 18.2534 11.6476 15.1184L12.3826 11.9759C12.5326 11.3384 12.7126 10.7834 12.9376 10.3259C13.8151 8.51086 15.3076 8.02336 17.8126 8.61586L19.0651 8.90836C22.2076 9.64336 23.1676 11.1959 22.4326 14.3309Z"
194
+ stroke="#F954D4"
195
+ stroke-linecap="round"
196
+ stroke-linejoin="round"
197
+ />
198
+ <path
199
+ d="M17.4852 21.0456C17.0202 21.3606 16.4352 21.6231 15.7227 21.8556L14.5377 22.2456C11.5602 23.2056 9.9927 22.4031 9.0252 19.4256L8.0652 16.4631C7.1052 13.4856 7.9002 11.9106 10.8777 10.9506L12.0627 10.5606C12.3702 10.4631 12.6627 10.3806 12.9402 10.3281C12.7152 10.7856 12.5352 11.3406 12.3852 11.9781L11.6502 15.1206C10.9152 18.2556 11.8827 19.8006 15.0102 20.5431L16.2702 20.8431C16.7052 20.9481 17.1102 21.0156 17.4852 21.0456Z"
200
+ stroke="#F954D4"
201
+ stroke-linecap="round"
202
+ stroke-linejoin="round"
203
+ />
204
+ <path
205
+ d="M15.6719 12.9004L19.3094 13.8229"
206
+ stroke="#F954D4"
207
+ stroke-linecap="round"
208
+ stroke-linejoin="round"
209
+ />
210
+ <path
211
+ d="M14.9375 15.8027L17.1125 16.3577"
212
+ stroke="#F954D4"
213
+ stroke-linecap="round"
214
+ stroke-linejoin="round"
215
+ />
216
+ </svg>
217
+ );
218
+
87
219
  const SpeedIcon = () => (
88
220
  <svg
89
221
  width="48"
@@ -26,23 +26,22 @@ const Grammar = ({ Aidata }) => {
26
26
  const originalText = data?.["Original Speech"] || "";
27
27
  const correctText = data?.["Grammatical Correct Version"] || "";
28
28
  const feedbacks = data?.["Feedback"];
29
- const {
30
- grammarFeekbackResult,
31
- grammerCorrectParagraph,
32
- grammarCorrectResult,
33
- } = useReportUtils();
29
+ const { grammarFeekbackResult, renderCorrectedSentence } = useReportUtils();
34
30
 
35
31
  const wordsFeedback = grammarFeekbackResult(
36
32
  originalText,
37
33
  feedbackCorrection,
38
34
  feedbacks
39
35
  );
40
- const correctWods = grammarCorrectResult(correctText, feedbackCorrection);
41
- const correctParagraph = correctWods
42
- ? grammerCorrectParagraph(correctWods)
43
- : "";
36
+
37
+ const sentenceParts = renderCorrectedSentence(wordsFeedback);
38
+
44
39
  useEffect(() => {
45
- if (wordsFeedback?.length) setSelectedWord(wordsFeedback?.[0]);
40
+ if (wordsFeedback?.length) {
41
+ const findWord = wordsFeedback?.find((word) => word?.operation);
42
+
43
+ setSelectedWord(findWord);
44
+ }
46
45
  }, [wordsFeedback?.length]);
47
46
 
48
47
  const tabs = [
@@ -50,6 +49,7 @@ const Grammar = ({ Aidata }) => {
50
49
  { label: "View script", value: 2, icon: AIcon },
51
50
  { label: "Corrected version", value: 3, icon: MarsIcon },
52
51
  ];
52
+
53
53
  return (
54
54
  <ModelComtainer>
55
55
  <ModelContent>
@@ -61,14 +61,16 @@ const Grammar = ({ Aidata }) => {
61
61
  <GrammarHeaderContent>
62
62
  {wordsFeedback?.map((word, idx) => (
63
63
  <span
64
- className={classNames({ "error-text": word?.position })}
64
+ // This now correctly checks if the word has an operation (i.e., a correction)
65
+ className={classNames({ "error-text": word?.operation })}
65
66
  key={idx}
66
67
  onClick={() => {
68
+ if (!word?.operation) return;
67
69
  setSelectedWord(word);
68
-
69
70
  setSelectedSection(1);
70
71
  }}
71
72
  >
73
+ {/* This will only be true for words with a correction (and a position) */}
72
74
  {word?.position && <span>{word?.position}</span>}
73
75
  {word?.text}
74
76
  </span>
@@ -95,7 +97,7 @@ const Grammar = ({ Aidata }) => {
95
97
  </ul>
96
98
  </GrammarHeader>
97
99
  <GrammarContent>
98
- {selectedSection === 1 && selectedWord && selectedWord?.position && (
100
+ {selectedSection === 1 && selectedWord && selectedWord?.operation && (
99
101
  <div className="feedback_section">
100
102
  <div className="failed">
101
103
  <RedX />
@@ -117,7 +119,29 @@ const Grammar = ({ Aidata }) => {
117
119
  <WarningGrey />
118
120
  <p>Introduced words are highlighted in green</p>
119
121
  </div>
120
- <p dangerouslySetInnerHTML={{ __html: correctParagraph }}></p>
122
+ <p>
123
+ {sentenceParts.map((part, index) => {
124
+ const nextPart = sentenceParts[index + 1];
125
+ const needsSpace =
126
+ nextPart && !/^[.,!?;:]/.test(nextPart.text);
127
+
128
+ if (part.highlight) {
129
+ return (
130
+ <span key={index}>
131
+ {part.text}
132
+ {needsSpace ? " " : ""}
133
+ </span>
134
+ );
135
+ } else {
136
+ return (
137
+ <React.Fragment key={index}>
138
+ {part.text}
139
+ {needsSpace ? " " : ""}
140
+ </React.Fragment>
141
+ );
142
+ }
143
+ })}
144
+ </p>
121
145
  </div>
122
146
  )}
123
147
  </GrammarContent>
@@ -7,11 +7,12 @@ import { useSpeechSynthesis } from "react-speech-kit";
7
7
  import useAudioPlayer from "../../../useAudioPlayer";
8
8
  import AudioWaveComponent from "../../../useAudioPlayer/audioWave";
9
9
 
10
- const PlayButton = ({ type, refernceText, audio }) => {
10
+ const PlayButton = ({ type, refernceText, audio, notPlay, onPlaying }) => {
11
11
  const [isPlayingSpeak, setIsPlayingSpeak] = useState(false);
12
12
  const { speak, cancel } = useSpeechSynthesis({
13
13
  onEnd: () => {
14
14
  setIsPlayingSpeak(false);
15
+ if (type === "ai") onPlaying?.(false);
15
16
  },
16
17
  });
17
18
 
@@ -29,23 +30,41 @@ const PlayButton = ({ type, refernceText, audio }) => {
29
30
  if (showWave) {
30
31
  cancel();
31
32
  stop();
33
+ onPlaying?.(false);
32
34
  }
33
35
  };
34
36
  }, [showWave]);
35
37
 
38
+ useEffect(() => {
39
+ if (type !== "ai") {
40
+ onPlaying?.(isPlaying);
41
+ } else {
42
+ onPlaying?.(isPlayingSpeak);
43
+ }
44
+ }, [isPlaying, type, isPlayingSpeak]);
45
+
36
46
  return (
37
47
  <Container
38
48
  disabled={disabled}
39
- style={showWave ? { cursor: "not-allowed" } : undefined}
49
+ style={notPlay ? { cursor: "not-allowed" } : undefined}
40
50
  className={classNames({ "ai-mode": type === "ai" })}
41
51
  onClick={() => {
42
- if (!showWave) {
43
- if (type === "ai" && refernceText) {
44
- speak({ text: refernceText });
45
- setIsPlayingSpeak(true);
46
- } else {
47
- audio && play();
52
+ if (notPlay) return;
53
+ if (type === "ai" && refernceText) {
54
+ if (isPlayingSpeak) {
55
+ setIsPlayingSpeak(false);
48
56
  cancel();
57
+ } else {
58
+ setIsPlayingSpeak(true);
59
+ speak({ text: refernceText });
60
+ }
61
+ } else {
62
+ if (audio) {
63
+ if (isPlaying) {
64
+ stop();
65
+ } else {
66
+ play();
67
+ }
49
68
  }
50
69
  }
51
70
  }}
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from "react";
1
+ import React, { useEffect, useMemo, useState } from "react";
2
2
  import {
3
3
  ClarityLevelSection,
4
4
  ModelComtainer,
@@ -24,7 +24,12 @@ import ClarityItem from "./ClarityItem";
24
24
  import InlineClampedText from "./InlineClampedText";
25
25
  import PlayButton from "./PlayButton";
26
26
 
27
- const SpeechAnalysis = ({ Aidata, isPersonal }) => {
27
+ const SpeechAnalysis = ({
28
+ Aidata,
29
+ isPersonal,
30
+ setCheckingPlayingAudio,
31
+ checkinhPlayingAudio,
32
+ }) => {
28
33
  const [selectedWord, setSelectedWord] = useState(1);
29
34
  const [selectedSection, setSelectedSection] = useState(null);
30
35
 
@@ -33,6 +38,7 @@ const SpeechAnalysis = ({ Aidata, isPersonal }) => {
33
38
  text: item?.wordText,
34
39
  value: item?.id,
35
40
  isCorrect: item?.isGood,
41
+ color: item?.color,
36
42
  }));
37
43
 
38
44
  const selectedWordValue = wordData?.find((item) => item?.id === selectedWord);
@@ -63,6 +69,28 @@ const SpeechAnalysis = ({ Aidata, isPersonal }) => {
63
69
  (item) => item?.id === selectedSection
64
70
  );
65
71
 
72
+ const colors = useMemo(
73
+ () => ({
74
+ poor: "#F95454",
75
+ excellent: "#30D468",
76
+ fair: "#9747FF",
77
+ // fair2: "#0D5FDB",
78
+ good: "#0D7D96",
79
+ veryGood: "#1A9243",
80
+ pass: "#F39B33",
81
+ okay: "#FEBF10",
82
+ }),
83
+ []
84
+ );
85
+
86
+ const backgroundColors = useMemo(() => {
87
+ const bg = { ...colors };
88
+ for (let x of Object.keys(bg)) {
89
+ bg[x] = bg[x] + 10;
90
+ }
91
+ return bg;
92
+ }, []);
93
+
66
94
  return (
67
95
  <ModelComtainer>
68
96
  <ModelContent>
@@ -74,8 +102,27 @@ const SpeechAnalysis = ({ Aidata, isPersonal }) => {
74
102
  </div>
75
103
  {isPersonal && (
76
104
  <div className="">
77
- <PlayButton audio={Aidata?.audioData} />
78
- <PlayButton type="ai" refernceText={Aidata?.data?.Reference} />
105
+ <PlayButton
106
+ audio={Aidata?.audioData}
107
+ notPlay={checkinhPlayingAudio.ai}
108
+ onPlaying={(value) => {
109
+ setCheckingPlayingAudio((prev) => ({
110
+ ...prev,
111
+ normal: value,
112
+ }));
113
+ }}
114
+ />
115
+ <PlayButton
116
+ type="ai"
117
+ refernceText={Aidata?.data?.Reference}
118
+ notPlay={checkinhPlayingAudio.normal}
119
+ onPlaying={(value) => {
120
+ setCheckingPlayingAudio((prev) => ({
121
+ ...prev,
122
+ ai: value,
123
+ }));
124
+ }}
125
+ />
79
126
  </div>
80
127
  )}
81
128
  </SpeechTitle>
@@ -85,9 +132,10 @@ const SpeechAnalysis = ({ Aidata, isPersonal }) => {
85
132
  <span>
86
133
  <span
87
134
  key={word?.value}
135
+ style={{ color: word?.color }}
88
136
  className={classNames({
89
137
  selected: selectedWord === word?.value,
90
- correct: word?.isCorrect,
138
+
91
139
  incorrect: !word?.isCorrect,
92
140
  capitalise: word?.value === 1,
93
141
  })}
@@ -117,7 +165,12 @@ const SpeechAnalysis = ({ Aidata, isPersonal }) => {
117
165
  </ul>
118
166
  <SelectedWord>
119
167
  Showing breakdown for:{" "}
120
- <span className="capitalise">{selectedWordValue?.wordText}</span>
168
+ <span
169
+ className="capitalise"
170
+ style={{ color: selectedWordValue?.color }}
171
+ >
172
+ {selectedWordValue?.wordText}
173
+ </span>
121
174
  </SelectedWord>
122
175
  <SpeechAnalysisContent>
123
176
  {selectionSelectionValue?.id === "phoneme" && (
@@ -135,19 +188,33 @@ const SpeechAnalysis = ({ Aidata, isPersonal }) => {
135
188
  </div>
136
189
  <div className="table_row">
137
190
  {selectionSelectionValue?.data?.map((item) => {
191
+ const colorName = item.Grade.toLowerCase()?.replace(
192
+ / ([a-z])/gi,
193
+ (match, p1) => p1.toUpperCase()
194
+ );
138
195
  const isExcellent = item?.Grade === "Excellent";
139
- const isPass = item?.Grade === "Pass";
196
+
140
197
  return (
141
198
  <div className="table_body" key={item?.id}>
142
199
  <p>/{item?.Phoneme}/</p>
143
200
  <p>{item?.Score || 0}%</p>
144
- {isExcellent ? (
145
- <p className="status">Excellent</p>
146
- ) : isPass ? (
147
- <p className="status warning">Pass</p>
148
- ) : (
149
- <p className="status failed">Fail</p>
150
- )}
201
+
202
+ <p
203
+ className="status"
204
+ style={{
205
+ textTransform: "capitalize",
206
+ background: backgroundColors?.[colorName],
207
+ color: colors?.[colorName],
208
+ }}
209
+ >
210
+ {colorName === "veryGood" ? (
211
+ <span style={{ textTransform: "none" }}>
212
+ Very good
213
+ </span>
214
+ ) : (
215
+ colorName
216
+ )}
217
+ </p>
151
218
 
152
219
  {isExcellent ? (
153
220
  <p className="good">
@@ -281,6 +348,18 @@ const SpeechAnalysis = ({ Aidata, isPersonal }) => {
281
348
  type="blue"
282
349
  value={`<span>${selectionSelectionValue?.data?.intonation}%</span>`}
283
350
  />
351
+ <ClarityItem
352
+ title="What we heard"
353
+ message={Aidata?.data?.Hypothesis}
354
+ type="pink"
355
+ alt
356
+ />
357
+ <ClarityItem
358
+ title="Reference"
359
+ message={Aidata?.data?.Reference}
360
+ type="blue"
361
+ alt
362
+ />
284
363
  </ClarityLevelSection>
285
364
  )}
286
365
  {selectionSelectionValue?.id === "standard_assessment" && (