@speakableio/core 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics.d.mts +38 -1
- package/dist/analytics.js +117 -1
- package/dist/analytics.js.map +1 -1
- package/dist/index.native.js +15 -1
- package/dist/index.native.js.map +1 -1
- package/dist/index.native.mjs +15 -1
- package/dist/index.native.mjs.map +1 -1
- package/dist/index.web.js +15 -1
- package/dist/index.web.js.map +1 -1
- package/package.json +2 -2
- package/dist/analytics-Bj2i88Zk.d.ts +0 -89
- package/dist/analytics.d.ts +0 -88
- package/dist/assignment.constants-BIKM6fYi.d.ts +0 -32
- package/dist/assignment.model-BRS4h8gX.d.ts +0 -301
- package/dist/assignment.model-Bc61gBHl.d.ts +0 -302
- package/dist/assignment.model-Bm9gE2YK.d.ts +0 -301
- package/dist/card.constants-DhKFipX3.d.ts +0 -54
- package/dist/const.d.ts +0 -261
- package/dist/const.js +0 -503
- package/dist/const.js.map +0 -1
- package/dist/everything.d.ts +0 -1348
- package/dist/everything.js +0 -3509
- package/dist/everything.js.map +0 -1
- package/dist/hooks.d.ts +0 -911
- package/dist/hooks.js +0 -2699
- package/dist/hooks.js.map +0 -1
- package/dist/index.web-DNYJV_41.d.ts +0 -469
- package/dist/index.web.d.ts +0 -471
- package/dist/models.d.ts +0 -56
- package/dist/models.js +0 -52
- package/dist/models.js.map +0 -1
- package/dist/notification.constants-B72fb734.d.ts +0 -21
- package/dist/repos.d.ts +0 -209
- package/dist/repos.js +0 -453
- package/dist/repos.js.map +0 -1
- package/dist/speakable-plans-BjWWEWrQ.d.ts +0 -72
- package/dist/speakable-plans-DR1cQ6IK.d.ts +0 -92
- package/dist/speakable-plans-Dq9nRefI.d.ts +0 -72
- package/dist/utils.d.ts +0 -121
- package/dist/utils.js +0 -646
- package/dist/utils.js.map +0 -1
- package/dist/web.constants-qmx4rGyO.d.ts +0 -26
package/dist/hooks.js
DELETED
|
@@ -1,2699 +0,0 @@
|
|
|
1
|
-
// src/providers/SpeakableProvider.tsx
|
|
2
|
-
import { createContext, useContext, useEffect, useState } from "react";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
var FsCtx = createContext(null);
|
|
5
|
-
function useSpeakableApi() {
|
|
6
|
-
const ctx = useContext(FsCtx);
|
|
7
|
-
if (!ctx) throw new Error("useSpeakableApi must be used within a SpeakableProvider");
|
|
8
|
-
return ctx;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// src/domains/assignment/hooks/assignment.hooks.ts
|
|
12
|
-
import { useQuery } from "@tanstack/react-query";
|
|
13
|
-
var assignmentQueryKeys = {
|
|
14
|
-
all: ["assignments"],
|
|
15
|
-
byId: (id) => [...assignmentQueryKeys.all, id],
|
|
16
|
-
list: () => [...assignmentQueryKeys.all, "list"]
|
|
17
|
-
};
|
|
18
|
-
function useAssignment({
|
|
19
|
-
assignmentId,
|
|
20
|
-
enabled = true,
|
|
21
|
-
analyticType,
|
|
22
|
-
userId
|
|
23
|
-
}) {
|
|
24
|
-
const { speakableApi } = useSpeakableApi();
|
|
25
|
-
return useQuery({
|
|
26
|
-
queryKey: assignmentQueryKeys.byId(assignmentId),
|
|
27
|
-
queryFn: () => speakableApi.assignmentRepo.getAssignment({
|
|
28
|
-
assignmentId,
|
|
29
|
-
analyticType,
|
|
30
|
-
currentUserId: userId
|
|
31
|
-
}),
|
|
32
|
-
enabled
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// src/domains/assignment/hooks/score-hooks.ts
|
|
37
|
-
import { useMutation, useQuery as useQuery2 } from "@tanstack/react-query";
|
|
38
|
-
|
|
39
|
-
// src/utils/debounce.utils.ts
|
|
40
|
-
function debounce(func, waitFor) {
|
|
41
|
-
let timeoutId;
|
|
42
|
-
return (...args) => new Promise((resolve, reject) => {
|
|
43
|
-
if (timeoutId) {
|
|
44
|
-
clearTimeout(timeoutId);
|
|
45
|
-
}
|
|
46
|
-
timeoutId = setTimeout(async () => {
|
|
47
|
-
try {
|
|
48
|
-
const result = await func(...args);
|
|
49
|
-
resolve(result);
|
|
50
|
-
} catch (error) {
|
|
51
|
-
reject(error);
|
|
52
|
-
}
|
|
53
|
-
}, waitFor);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// src/lib/tanstack/handle-optimistic-update-query.ts
|
|
58
|
-
var handleOptimisticUpdate = async ({
|
|
59
|
-
queryClient,
|
|
60
|
-
queryKey,
|
|
61
|
-
newData
|
|
62
|
-
}) => {
|
|
63
|
-
await queryClient.cancelQueries({
|
|
64
|
-
queryKey
|
|
65
|
-
});
|
|
66
|
-
const previousData = queryClient.getQueryData(queryKey);
|
|
67
|
-
if (previousData === void 0) {
|
|
68
|
-
queryClient.setQueryData(queryKey, newData);
|
|
69
|
-
} else {
|
|
70
|
-
queryClient.setQueryData(queryKey, { ...previousData, ...newData });
|
|
71
|
-
}
|
|
72
|
-
return { previousData };
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// src/constants/speakable-plans.ts
|
|
76
|
-
var FEEDBACK_PLANS = {
|
|
77
|
-
FEEDBACK_TRANSCRIPT: "FEEDBACK_TRANSCRIPT",
|
|
78
|
-
// Transcript from the audio
|
|
79
|
-
FEEDBACK_SUMMARY: "FEEDBACK_SUMMARY",
|
|
80
|
-
// Chatty summary (Free plan)
|
|
81
|
-
FEEDBACK_GRAMMAR_INSIGHTS: "FEEDBACK_GRAMMAR_INSIGHTS",
|
|
82
|
-
// Grammar insights
|
|
83
|
-
FEEDBACK_SUGGESTED_RESPONSE: "FEEDBACK_SUGGESTED_RESPONSE",
|
|
84
|
-
// Suggested Response
|
|
85
|
-
FEEDBACK_RUBRIC: "FEEDBACK_RUBRIC",
|
|
86
|
-
// Suggested Response
|
|
87
|
-
FEEDBACK_GRADING_STANDARDS: "FEEDBACK_GRADING_STANDARDS",
|
|
88
|
-
// ACTFL / WIDA Estimate
|
|
89
|
-
FEEDBACK_TARGET_LANGUAGE: "FEEDBACK_TARGET_LANGUAGE",
|
|
90
|
-
// Ability to set the feedback language to the target language of the student
|
|
91
|
-
FEEDBACK_DISABLE_ALLOW_RETRIES: "FEEDBACK_DISABLE_ALLOW_RETRIES"
|
|
92
|
-
// Turn of allow retries
|
|
93
|
-
};
|
|
94
|
-
var AUTO_GRADING_PLANS = {
|
|
95
|
-
AUTO_GRADING_PASS_FAIL: "AUTO_GRADING_PASS_FAIL",
|
|
96
|
-
// Pass / fail grading
|
|
97
|
-
AUTO_GRADING_RUBRICS: "AUTO_GRADING_RUBRICS",
|
|
98
|
-
// Autograded rubrics
|
|
99
|
-
AUTO_GRADING_STANDARDS_BASED: "AUTO_GRADING_STANDARDS_BASED"
|
|
100
|
-
// Standards based grading
|
|
101
|
-
};
|
|
102
|
-
var AI_ASSISTANT_PLANS = {
|
|
103
|
-
AI_ASSISTANT_DOCUMENT_UPLOADS: "AI_ASSISTANT_DOCUMENT_UPLOADS",
|
|
104
|
-
// Allow document uploading
|
|
105
|
-
AI_ASSISTANT_UNLIMITED_USE: "AI_ASSISTANT_UNLIMITED_USE"
|
|
106
|
-
// Allow unlimited use of AI assistant. Otherwise, limits are used.
|
|
107
|
-
};
|
|
108
|
-
var ASSIGNMENT_SETTINGS_PLANS = {
|
|
109
|
-
ASSESSMENTS: "ASSESSMENTS",
|
|
110
|
-
// Ability to create assessment assignment types
|
|
111
|
-
GOOGLE_CLASSROOM_GRADE_PASSBACK: "GOOGLE_CLASSROOM_GRADE_PASSBACK"
|
|
112
|
-
// Assignment scores can sync with classroom
|
|
113
|
-
};
|
|
114
|
-
var ANALYTICS_PLANS = {
|
|
115
|
-
ANALYTICS_GRADEBOOK: "ANALYTICS_GRADEBOOK",
|
|
116
|
-
// Access to the gradebook page
|
|
117
|
-
ANALYTICS_CLASSROOM_ANALYTICS: "ANALYTICS_CLASSROOM_ANALYTICS",
|
|
118
|
-
// Access to the classroom analytics page
|
|
119
|
-
ANALYTICS_STUDENT_PROGRESS_REPORTS: "ANALYTICS_STUDENT_PROGRESS_REPORTS",
|
|
120
|
-
// Access to the panel that shows an individual student's progress and assignments
|
|
121
|
-
ANALYTICS_ASSIGNMENT_RESULTS: "ANALYTICS_ASSIGNMENT_RESULTS",
|
|
122
|
-
// Access to the assigment RESULTS page
|
|
123
|
-
ANALYTICS_ORGANIZATION: "ANALYTICS_ORGANIZATION"
|
|
124
|
-
// Access to the organization analytics panel (for permitted admins)
|
|
125
|
-
};
|
|
126
|
-
var SPACES_PLANS = {
|
|
127
|
-
SPACES_CREATE_SPACE: "SPACES_CREATE_SPACE",
|
|
128
|
-
// Ability to create spaces
|
|
129
|
-
SPACES_CHECK_POINTS: "SPACES_CHECK_POINTS"
|
|
130
|
-
// Feature not available yet. Ability to create checkpoints for spaces for data aggregation
|
|
131
|
-
};
|
|
132
|
-
var DISCOVER_PLANS = {
|
|
133
|
-
DISCOVER_ORGANIZATION_LIBRARY: "DISCOVER_ORGANIZATION_LIBRARY"
|
|
134
|
-
// Access to the organizations shared library
|
|
135
|
-
};
|
|
136
|
-
var MEDIA_AREA_PLANS = {
|
|
137
|
-
MEDIA_AREA_DOCUMENT_UPLOAD: "MEDIA_AREA_DOCUMENT_UPLOAD",
|
|
138
|
-
MEDIA_AREA_AUDIO_FILES: "MEDIA_AREA_AUDIO_FILES"
|
|
139
|
-
};
|
|
140
|
-
var FREE_PLAN = [];
|
|
141
|
-
var TEACHER_PRO_PLAN = [
|
|
142
|
-
FEEDBACK_PLANS.FEEDBACK_TRANSCRIPT,
|
|
143
|
-
FEEDBACK_PLANS.FEEDBACK_SUMMARY,
|
|
144
|
-
FEEDBACK_PLANS.FEEDBACK_TARGET_LANGUAGE,
|
|
145
|
-
AUTO_GRADING_PLANS.AUTO_GRADING_PASS_FAIL,
|
|
146
|
-
ANALYTICS_PLANS.ANALYTICS_GRADEBOOK,
|
|
147
|
-
SPACES_PLANS.SPACES_CREATE_SPACE
|
|
148
|
-
// AUTO_GRADING_PLANS.AUTO_GRADING_STANDARDS_BASED,
|
|
149
|
-
];
|
|
150
|
-
var SCHOOL_STARTER = [
|
|
151
|
-
FEEDBACK_PLANS.FEEDBACK_TRANSCRIPT,
|
|
152
|
-
FEEDBACK_PLANS.FEEDBACK_SUMMARY,
|
|
153
|
-
FEEDBACK_PLANS.FEEDBACK_GRAMMAR_INSIGHTS,
|
|
154
|
-
FEEDBACK_PLANS.FEEDBACK_SUGGESTED_RESPONSE,
|
|
155
|
-
FEEDBACK_PLANS.FEEDBACK_RUBRIC,
|
|
156
|
-
FEEDBACK_PLANS.FEEDBACK_GRADING_STANDARDS,
|
|
157
|
-
FEEDBACK_PLANS.FEEDBACK_DISABLE_ALLOW_RETRIES,
|
|
158
|
-
FEEDBACK_PLANS.FEEDBACK_TARGET_LANGUAGE,
|
|
159
|
-
AUTO_GRADING_PLANS.AUTO_GRADING_PASS_FAIL,
|
|
160
|
-
AUTO_GRADING_PLANS.AUTO_GRADING_RUBRICS,
|
|
161
|
-
AUTO_GRADING_PLANS.AUTO_GRADING_STANDARDS_BASED,
|
|
162
|
-
AI_ASSISTANT_PLANS.AI_ASSISTANT_DOCUMENT_UPLOADS,
|
|
163
|
-
AI_ASSISTANT_PLANS.AI_ASSISTANT_UNLIMITED_USE,
|
|
164
|
-
// ASSIGNMENT_SETTINGS_PLANS.ASSESSMENTS,
|
|
165
|
-
ASSIGNMENT_SETTINGS_PLANS.GOOGLE_CLASSROOM_GRADE_PASSBACK,
|
|
166
|
-
ANALYTICS_PLANS.ANALYTICS_GRADEBOOK,
|
|
167
|
-
ANALYTICS_PLANS.ANALYTICS_STUDENT_PROGRESS_REPORTS,
|
|
168
|
-
ANALYTICS_PLANS.ANALYTICS_CLASSROOM_ANALYTICS,
|
|
169
|
-
// ANALYTICS_PLANS.ANALYTICS_ORGANIZATION,
|
|
170
|
-
SPACES_PLANS.SPACES_CREATE_SPACE,
|
|
171
|
-
SPACES_PLANS.SPACES_CHECK_POINTS,
|
|
172
|
-
// DISCOVER_PLANS.DISCOVER_ORGANIZATION_LIBRARY,
|
|
173
|
-
MEDIA_AREA_PLANS.MEDIA_AREA_DOCUMENT_UPLOAD,
|
|
174
|
-
MEDIA_AREA_PLANS.MEDIA_AREA_AUDIO_FILES
|
|
175
|
-
];
|
|
176
|
-
var ORGANIZATION_PLAN = [
|
|
177
|
-
FEEDBACK_PLANS.FEEDBACK_TRANSCRIPT,
|
|
178
|
-
FEEDBACK_PLANS.FEEDBACK_SUMMARY,
|
|
179
|
-
FEEDBACK_PLANS.FEEDBACK_GRAMMAR_INSIGHTS,
|
|
180
|
-
FEEDBACK_PLANS.FEEDBACK_SUGGESTED_RESPONSE,
|
|
181
|
-
FEEDBACK_PLANS.FEEDBACK_RUBRIC,
|
|
182
|
-
FEEDBACK_PLANS.FEEDBACK_GRADING_STANDARDS,
|
|
183
|
-
FEEDBACK_PLANS.FEEDBACK_DISABLE_ALLOW_RETRIES,
|
|
184
|
-
FEEDBACK_PLANS.FEEDBACK_TARGET_LANGUAGE,
|
|
185
|
-
AUTO_GRADING_PLANS.AUTO_GRADING_PASS_FAIL,
|
|
186
|
-
AUTO_GRADING_PLANS.AUTO_GRADING_RUBRICS,
|
|
187
|
-
AUTO_GRADING_PLANS.AUTO_GRADING_STANDARDS_BASED,
|
|
188
|
-
AI_ASSISTANT_PLANS.AI_ASSISTANT_DOCUMENT_UPLOADS,
|
|
189
|
-
AI_ASSISTANT_PLANS.AI_ASSISTANT_UNLIMITED_USE,
|
|
190
|
-
ASSIGNMENT_SETTINGS_PLANS.ASSESSMENTS,
|
|
191
|
-
ASSIGNMENT_SETTINGS_PLANS.GOOGLE_CLASSROOM_GRADE_PASSBACK,
|
|
192
|
-
ANALYTICS_PLANS.ANALYTICS_GRADEBOOK,
|
|
193
|
-
ANALYTICS_PLANS.ANALYTICS_STUDENT_PROGRESS_REPORTS,
|
|
194
|
-
ANALYTICS_PLANS.ANALYTICS_CLASSROOM_ANALYTICS,
|
|
195
|
-
ANALYTICS_PLANS.ANALYTICS_ORGANIZATION,
|
|
196
|
-
SPACES_PLANS.SPACES_CREATE_SPACE,
|
|
197
|
-
SPACES_PLANS.SPACES_CHECK_POINTS,
|
|
198
|
-
DISCOVER_PLANS.DISCOVER_ORGANIZATION_LIBRARY,
|
|
199
|
-
MEDIA_AREA_PLANS.MEDIA_AREA_DOCUMENT_UPLOAD,
|
|
200
|
-
MEDIA_AREA_PLANS.MEDIA_AREA_AUDIO_FILES
|
|
201
|
-
];
|
|
202
|
-
var SpeakablePlanTypes = {
|
|
203
|
-
basic: "basic",
|
|
204
|
-
teacher_pro: "teacher_pro",
|
|
205
|
-
school_starter: "school_starter",
|
|
206
|
-
organization: "organization",
|
|
207
|
-
// OLD PLANS
|
|
208
|
-
starter: "starter",
|
|
209
|
-
growth: "growth",
|
|
210
|
-
professional: "professional"
|
|
211
|
-
};
|
|
212
|
-
var SpeakablePermissionsMap = {
|
|
213
|
-
[SpeakablePlanTypes.basic]: FREE_PLAN,
|
|
214
|
-
[SpeakablePlanTypes.starter]: TEACHER_PRO_PLAN,
|
|
215
|
-
[SpeakablePlanTypes.teacher_pro]: TEACHER_PRO_PLAN,
|
|
216
|
-
[SpeakablePlanTypes.growth]: ORGANIZATION_PLAN,
|
|
217
|
-
[SpeakablePlanTypes.professional]: ORGANIZATION_PLAN,
|
|
218
|
-
[SpeakablePlanTypes.organization]: ORGANIZATION_PLAN,
|
|
219
|
-
[SpeakablePlanTypes.school_starter]: SCHOOL_STARTER
|
|
220
|
-
};
|
|
221
|
-
var SpeakablePlanHierarchy = [
|
|
222
|
-
SpeakablePlanTypes.basic,
|
|
223
|
-
SpeakablePlanTypes.starter,
|
|
224
|
-
SpeakablePlanTypes.teacher_pro,
|
|
225
|
-
SpeakablePlanTypes.growth,
|
|
226
|
-
SpeakablePlanTypes.professional,
|
|
227
|
-
SpeakablePlanTypes.school_starter,
|
|
228
|
-
SpeakablePlanTypes.organization
|
|
229
|
-
];
|
|
230
|
-
|
|
231
|
-
// src/hooks/usePermissions.ts
|
|
232
|
-
var usePermissions = () => {
|
|
233
|
-
const { permissions } = useSpeakableApi();
|
|
234
|
-
const has = (permission) => {
|
|
235
|
-
var _a;
|
|
236
|
-
return (_a = permissions.permissions) == null ? void 0 : _a.includes(permission);
|
|
237
|
-
};
|
|
238
|
-
return {
|
|
239
|
-
plan: permissions.plan,
|
|
240
|
-
permissionsLoaded: permissions.loaded,
|
|
241
|
-
isStripePlan: permissions.isStripePlan,
|
|
242
|
-
refreshDate: permissions.refreshDate,
|
|
243
|
-
isInstitutionPlan: permissions.isInstitutionPlan,
|
|
244
|
-
subscriptionId: permissions.subscriptionId,
|
|
245
|
-
contact: permissions.contact,
|
|
246
|
-
hasGradebook: has(ANALYTICS_PLANS.ANALYTICS_GRADEBOOK),
|
|
247
|
-
hasGoogleClassroomGradePassback: has(ASSIGNMENT_SETTINGS_PLANS.GOOGLE_CLASSROOM_GRADE_PASSBACK),
|
|
248
|
-
hasAssessments: has(ASSIGNMENT_SETTINGS_PLANS.ASSESSMENTS),
|
|
249
|
-
hasSectionAnalytics: has(ANALYTICS_PLANS.ANALYTICS_CLASSROOM_ANALYTICS),
|
|
250
|
-
hasStudentReports: has(ANALYTICS_PLANS.ANALYTICS_STUDENT_PROGRESS_REPORTS),
|
|
251
|
-
permissions: permissions || [],
|
|
252
|
-
hasStudentPortfolios: permissions.hasStudentPortfolios,
|
|
253
|
-
isFreeOrgTrial: permissions.type === "free_org_trial",
|
|
254
|
-
freeOrgTrialExpired: permissions.freeOrgTrialExpired
|
|
255
|
-
};
|
|
256
|
-
};
|
|
257
|
-
var usePermissions_default = usePermissions;
|
|
258
|
-
|
|
259
|
-
// src/lib/firebase/api.ts
|
|
260
|
-
var FirebaseAPI = class _FirebaseAPI {
|
|
261
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
262
|
-
constructor() {
|
|
263
|
-
this.config = null;
|
|
264
|
-
}
|
|
265
|
-
static getInstance() {
|
|
266
|
-
if (!_FirebaseAPI.instance) {
|
|
267
|
-
_FirebaseAPI.instance = new _FirebaseAPI();
|
|
268
|
-
}
|
|
269
|
-
return _FirebaseAPI.instance;
|
|
270
|
-
}
|
|
271
|
-
initialize(config) {
|
|
272
|
-
this.config = config;
|
|
273
|
-
}
|
|
274
|
-
get db() {
|
|
275
|
-
if (!this.config) throw new Error("Firebase API not initialized");
|
|
276
|
-
return this.config.db;
|
|
277
|
-
}
|
|
278
|
-
get helpers() {
|
|
279
|
-
if (!this.config) throw new Error("Firebase API not initialized");
|
|
280
|
-
return this.config.helpers;
|
|
281
|
-
}
|
|
282
|
-
get httpsCallable() {
|
|
283
|
-
var _a;
|
|
284
|
-
return (_a = this.config) == null ? void 0 : _a.httpsCallable;
|
|
285
|
-
}
|
|
286
|
-
logEvent(name, data) {
|
|
287
|
-
var _a;
|
|
288
|
-
(_a = this.config) == null ? void 0 : _a.logEvent(name, data);
|
|
289
|
-
}
|
|
290
|
-
accessQueryConstraints() {
|
|
291
|
-
const { query, orderBy, limit, startAt, startAfter, endAt, endBefore, where, increment } = this.helpers;
|
|
292
|
-
return {
|
|
293
|
-
query,
|
|
294
|
-
orderBy,
|
|
295
|
-
limit,
|
|
296
|
-
startAt,
|
|
297
|
-
startAfter,
|
|
298
|
-
endAt,
|
|
299
|
-
endBefore,
|
|
300
|
-
where,
|
|
301
|
-
increment
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
accessHelpers() {
|
|
305
|
-
const { doc, collection, writeBatch, serverTimestamp, setDoc } = this.helpers;
|
|
306
|
-
return {
|
|
307
|
-
doc: (path) => doc(this.db, path),
|
|
308
|
-
collection: (path) => collection(this.db, path),
|
|
309
|
-
writeBatch: () => writeBatch(this.db),
|
|
310
|
-
serverTimestamp,
|
|
311
|
-
setDoc
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
async getDoc(path) {
|
|
315
|
-
const { getDoc, doc } = this.helpers;
|
|
316
|
-
const docRef = doc(this.db, path);
|
|
317
|
-
const docSnap = await getDoc(docRef);
|
|
318
|
-
const data = docSnap.exists() ? {
|
|
319
|
-
...docSnap.data(),
|
|
320
|
-
id: docSnap.id
|
|
321
|
-
} : null;
|
|
322
|
-
return {
|
|
323
|
-
id: docSnap.id,
|
|
324
|
-
data
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
async getDocs(path, ...queryConstraints) {
|
|
328
|
-
const { getDocs, query, collection } = this.helpers;
|
|
329
|
-
const collectionRef = collection(this.db, path);
|
|
330
|
-
const q = queryConstraints.length > 0 ? query(collectionRef, ...queryConstraints) : collectionRef;
|
|
331
|
-
const querySnapshot = await getDocs(q);
|
|
332
|
-
const data = querySnapshot.docs.map((doc) => ({
|
|
333
|
-
data: doc.data(),
|
|
334
|
-
id: doc.id
|
|
335
|
-
}));
|
|
336
|
-
return {
|
|
337
|
-
data,
|
|
338
|
-
querySnapshot,
|
|
339
|
-
empty: querySnapshot.empty
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
async addDoc(path, data) {
|
|
343
|
-
const { addDoc, collection } = this.helpers;
|
|
344
|
-
const collectionRef = collection(this.db, path);
|
|
345
|
-
const docRef = await addDoc(collectionRef, data);
|
|
346
|
-
return {
|
|
347
|
-
...data,
|
|
348
|
-
id: docRef.id
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
async setDoc(path, data, options = {}) {
|
|
352
|
-
const { setDoc, doc } = this.helpers;
|
|
353
|
-
const docRef = doc(this.db, path);
|
|
354
|
-
await setDoc(docRef, data, options);
|
|
355
|
-
}
|
|
356
|
-
async updateDoc(path, data) {
|
|
357
|
-
const { updateDoc, doc } = this.helpers;
|
|
358
|
-
const docRef = doc(this.db, path);
|
|
359
|
-
await updateDoc(docRef, data);
|
|
360
|
-
}
|
|
361
|
-
async deleteDoc(path) {
|
|
362
|
-
const { deleteDoc, doc } = this.helpers;
|
|
363
|
-
const docRef = doc(this.db, path);
|
|
364
|
-
await deleteDoc(docRef);
|
|
365
|
-
}
|
|
366
|
-
async runTransaction(updateFunction) {
|
|
367
|
-
const { runTransaction } = this.helpers;
|
|
368
|
-
return runTransaction(this.db, updateFunction);
|
|
369
|
-
}
|
|
370
|
-
async runBatch(operations) {
|
|
371
|
-
const { writeBatch } = this.helpers;
|
|
372
|
-
const batch = writeBatch(this.db);
|
|
373
|
-
await Promise.all(operations.map((op) => op()));
|
|
374
|
-
await batch.commit();
|
|
375
|
-
}
|
|
376
|
-
writeBatch() {
|
|
377
|
-
const { writeBatch } = this.helpers;
|
|
378
|
-
const batch = writeBatch(this.db);
|
|
379
|
-
return batch;
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
var api = FirebaseAPI.getInstance();
|
|
383
|
-
|
|
384
|
-
// src/hooks/useGoogleClassroom.ts
|
|
385
|
-
var useGoogleClassroom = () => {
|
|
386
|
-
const submitAssignmentToGoogleClassroom = async ({
|
|
387
|
-
assignment,
|
|
388
|
-
scores,
|
|
389
|
-
googleUserId = null
|
|
390
|
-
// optional to override the user's googleUserId
|
|
391
|
-
}) => {
|
|
392
|
-
var _a, _b, _c;
|
|
393
|
-
try {
|
|
394
|
-
const { googleClassroomUserId = null } = scores;
|
|
395
|
-
const googleId = googleUserId || googleClassroomUserId;
|
|
396
|
-
if (!googleId)
|
|
397
|
-
return {
|
|
398
|
-
error: true,
|
|
399
|
-
message: "No Google Classroom ID found"
|
|
400
|
-
};
|
|
401
|
-
const { courseWorkId, maxPoints, owners, courseId } = assignment;
|
|
402
|
-
const draftGrade = (scores == null ? void 0 : scores.score) ? (scores == null ? void 0 : scores.score) / 100 * maxPoints : 0;
|
|
403
|
-
const result = await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "submitAssignmentToGoogleClassroomV2")) == null ? void 0 : _c({
|
|
404
|
-
teacherId: owners[0],
|
|
405
|
-
courseId,
|
|
406
|
-
courseWorkId,
|
|
407
|
-
userId: googleId,
|
|
408
|
-
draftGrade
|
|
409
|
-
}));
|
|
410
|
-
return result;
|
|
411
|
-
} catch (error) {
|
|
412
|
-
if (error instanceof Error) {
|
|
413
|
-
return { error: true, message: error.message };
|
|
414
|
-
}
|
|
415
|
-
return { error: true, message: "Unknown error" };
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
return {
|
|
419
|
-
submitAssignmentToGoogleClassroom
|
|
420
|
-
};
|
|
421
|
-
};
|
|
422
|
-
|
|
423
|
-
// src/lib/firebase/firebase-analytics/grading-standard.ts
|
|
424
|
-
var logGradingStandardLog = (data) => {
|
|
425
|
-
var _a, _b, _c;
|
|
426
|
-
if (data.courseId && data.type && data.level) {
|
|
427
|
-
(_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "handleCouresAnalyticsEvent")) == null ? void 0 : _c({
|
|
428
|
-
eventType: data.type || "custom",
|
|
429
|
-
level: data.level,
|
|
430
|
-
courseId: data.courseId
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
api.logEvent("logGradingStandard", data);
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
// src/constants/analytics.constants.ts
|
|
437
|
-
var ANALYTICS_EVENT_TYPES = {
|
|
438
|
-
VOICE_SUCCESS: "voice_success",
|
|
439
|
-
VOICE_FAIL: "voice_fail",
|
|
440
|
-
RESPOND_CARD_SUCCESS: "respond_card_success",
|
|
441
|
-
RESPOND_CARD_FAIL: "respond_card_fail",
|
|
442
|
-
RESPOND_WRITE_CARD_SUCCESS: "respond_write_card_success",
|
|
443
|
-
RESPOND_WRITE_CARD_FAIL: "respond_write_card_fail",
|
|
444
|
-
RESPOND_WRITE_CARD_SUBMITTED: "respond_write_card_submitted",
|
|
445
|
-
RESPOND_WRITE_CARD_ERROR: "respond_write_card_error",
|
|
446
|
-
RESPOND_CARD_ERROR: "respond_card_error",
|
|
447
|
-
RESPOND_CARD_SUBMITTED: "respond_card_submitted",
|
|
448
|
-
RESPOND_FREE_PLAN: "respond_free_plan",
|
|
449
|
-
RESPOND_WRITE_FREE_PLAN: "respond_write_free_plan",
|
|
450
|
-
SUBMISSION: "assignment_submitted",
|
|
451
|
-
ASSIGNMENT_STARTED: "assignment_started",
|
|
452
|
-
CREATE_ASSIGNMENT: "create_assignment",
|
|
453
|
-
MC_SUCCESS: "multiple_choice_success",
|
|
454
|
-
MC_FAIL: "multiple_choice_fail",
|
|
455
|
-
MC_ERROR: "multiple_choice_error",
|
|
456
|
-
ACTFL_LEVEL: "actfl_level",
|
|
457
|
-
WIDA_LEVEL: "wida_level"
|
|
458
|
-
};
|
|
459
|
-
|
|
460
|
-
// src/lib/firebase/firebase-analytics/assignment.ts
|
|
461
|
-
var logOpenAssignment = (data = {}) => {
|
|
462
|
-
api.logEvent("open_assignment", data);
|
|
463
|
-
};
|
|
464
|
-
var logOpenActivityPreview = (data = {}) => {
|
|
465
|
-
api.logEvent("open_activity_preview", data);
|
|
466
|
-
};
|
|
467
|
-
var logSubmitAssignment = (data = {}) => {
|
|
468
|
-
var _a, _b, _c;
|
|
469
|
-
(_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "handleCouresAnalyticsEvent")) == null ? void 0 : _c({
|
|
470
|
-
eventType: ANALYTICS_EVENT_TYPES.SUBMISSION,
|
|
471
|
-
...data
|
|
472
|
-
});
|
|
473
|
-
api.logEvent(ANALYTICS_EVENT_TYPES.SUBMISSION, data);
|
|
474
|
-
};
|
|
475
|
-
var logStartAssignment = (data = {}) => {
|
|
476
|
-
var _a, _b, _c;
|
|
477
|
-
if (data.courseId) {
|
|
478
|
-
(_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "handleCouresAnalyticsEvent")) == null ? void 0 : _c({
|
|
479
|
-
eventType: ANALYTICS_EVENT_TYPES.ASSIGNMENT_STARTED,
|
|
480
|
-
...data
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
api.logEvent(ANALYTICS_EVENT_TYPES.ASSIGNMENT_STARTED, data);
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
// src/domains/notification/services/create-notification.service.ts
|
|
487
|
-
import dayjs from "dayjs";
|
|
488
|
-
|
|
489
|
-
// src/constants/web.constants.ts
|
|
490
|
-
var WEB_BASE_URL = "https://app.speakable.io";
|
|
491
|
-
|
|
492
|
-
// src/domains/notification/notification.constants.ts
|
|
493
|
-
var SPEAKABLE_NOTIFICATIONS = {
|
|
494
|
-
NEW_ASSIGNMENT: "new_assignment",
|
|
495
|
-
ASSESSMENT_SUBMITTED: "assessment_submitted",
|
|
496
|
-
ASSESSMENT_SCORED: "assessment_scored",
|
|
497
|
-
NEW_COMMENT: "NEW_COMMENT"
|
|
498
|
-
};
|
|
499
|
-
var SpeakableNotificationTypes = {
|
|
500
|
-
NEW_ASSIGNMENT: "NEW_ASSIGNMENT",
|
|
501
|
-
FEEDBACK_FROM_TEACHER: "FEEDBACK_FROM_TEACHER",
|
|
502
|
-
MESSAGE_FROM_STUDENT: "MESSAGE_FROM_STUDENT",
|
|
503
|
-
PHRASE_MARKED_CORRECT: "PHRASE_MARKED_CORRECT",
|
|
504
|
-
STUDENT_PROGRESS: "STUDENT_PROGRESS",
|
|
505
|
-
PLAYLIST_FOLLOWERS: "PLAYLIST_FOLLOWERS",
|
|
506
|
-
PLAYLIST_PLAYS: "PLAYLIST_PLAYS",
|
|
507
|
-
// New notifications
|
|
508
|
-
ASSESSMENT_SUBMITTED: "ASSESSMENT_SUBMITTED",
|
|
509
|
-
// Notification FOR TEACHER when student submits assessment
|
|
510
|
-
ASSESSMENT_SCORED: "ASSESSMENT_SCORED",
|
|
511
|
-
// Notification FOR STUDENT when teacher scores assessment
|
|
512
|
-
// Comment
|
|
513
|
-
NEW_COMMENT: "NEW_COMMENT"
|
|
514
|
-
};
|
|
515
|
-
|
|
516
|
-
// src/utils/error-handler.ts
|
|
517
|
-
var ServiceError = class extends Error {
|
|
518
|
-
constructor(message, originalError, code) {
|
|
519
|
-
super(message);
|
|
520
|
-
this.originalError = originalError;
|
|
521
|
-
this.code = code;
|
|
522
|
-
this.name = "ServiceError";
|
|
523
|
-
}
|
|
524
|
-
};
|
|
525
|
-
function withErrorHandler(fn, serviceName) {
|
|
526
|
-
return async (...args) => {
|
|
527
|
-
try {
|
|
528
|
-
return await fn(...args);
|
|
529
|
-
} catch (error) {
|
|
530
|
-
if (error instanceof Error && "code" in error) {
|
|
531
|
-
const firebaseError = error;
|
|
532
|
-
throw new ServiceError(
|
|
533
|
-
`Error in ${serviceName}: ${firebaseError.message}`,
|
|
534
|
-
error,
|
|
535
|
-
firebaseError.code
|
|
536
|
-
);
|
|
537
|
-
}
|
|
538
|
-
if (error instanceof Error) {
|
|
539
|
-
throw new ServiceError(`Error in ${serviceName}: ${error.message}`, error);
|
|
540
|
-
}
|
|
541
|
-
throw new ServiceError(`Unknown error in ${serviceName}`, error);
|
|
542
|
-
}
|
|
543
|
-
};
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
// src/domains/notification/services/send-notification.service.ts
|
|
547
|
-
var _sendNotification = async (sendTo, notification) => {
|
|
548
|
-
var _a, _b, _c;
|
|
549
|
-
const results = await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "createNotificationV2")) == null ? void 0 : _c({
|
|
550
|
-
sendTo,
|
|
551
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
552
|
-
notification
|
|
553
|
-
}));
|
|
554
|
-
return results;
|
|
555
|
-
};
|
|
556
|
-
var sendNotification = withErrorHandler(_sendNotification, "sendNotification");
|
|
557
|
-
|
|
558
|
-
// src/domains/notification/services/create-notification.service.ts
|
|
559
|
-
var createNotification = async ({
|
|
560
|
-
data,
|
|
561
|
-
type,
|
|
562
|
-
userId,
|
|
563
|
-
profile
|
|
564
|
-
}) => {
|
|
565
|
-
let result;
|
|
566
|
-
switch (type) {
|
|
567
|
-
case SPEAKABLE_NOTIFICATIONS.NEW_ASSIGNMENT:
|
|
568
|
-
result = await handleAssignNotifPromise({ data, profile });
|
|
569
|
-
break;
|
|
570
|
-
case SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SUBMITTED:
|
|
571
|
-
result = await createAssessmentSubmissionNotification({
|
|
572
|
-
data,
|
|
573
|
-
profile,
|
|
574
|
-
userId
|
|
575
|
-
});
|
|
576
|
-
break;
|
|
577
|
-
case SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SCORED:
|
|
578
|
-
result = await createAssessmentScoredNotification({
|
|
579
|
-
data,
|
|
580
|
-
profile
|
|
581
|
-
});
|
|
582
|
-
break;
|
|
583
|
-
default:
|
|
584
|
-
result = null;
|
|
585
|
-
break;
|
|
586
|
-
}
|
|
587
|
-
return result;
|
|
588
|
-
};
|
|
589
|
-
var handleAssignNotifPromise = async ({
|
|
590
|
-
data: assignments,
|
|
591
|
-
profile
|
|
592
|
-
}) => {
|
|
593
|
-
if (!assignments.length) return;
|
|
594
|
-
try {
|
|
595
|
-
const notifsPromises = assignments.map(async (assignment) => {
|
|
596
|
-
const {
|
|
597
|
-
section,
|
|
598
|
-
section: { members },
|
|
599
|
-
...rest
|
|
600
|
-
} = assignment;
|
|
601
|
-
if (!section || !members) throw new Error("Invalid assignment data");
|
|
602
|
-
const data = { section, sendTo: members, assignment: { ...rest } };
|
|
603
|
-
return createNewAssignmentNotification({ data, profile });
|
|
604
|
-
});
|
|
605
|
-
await Promise.all(notifsPromises);
|
|
606
|
-
return {
|
|
607
|
-
success: true,
|
|
608
|
-
message: "Assignment notifications sent successfully"
|
|
609
|
-
};
|
|
610
|
-
} catch (error) {
|
|
611
|
-
console.error("Error in handleAssignNotifPromise:", error);
|
|
612
|
-
throw error;
|
|
613
|
-
}
|
|
614
|
-
};
|
|
615
|
-
var createNewAssignmentNotification = async ({
|
|
616
|
-
data,
|
|
617
|
-
profile
|
|
618
|
-
}) => {
|
|
619
|
-
var _a;
|
|
620
|
-
const { assignment, sendTo } = data;
|
|
621
|
-
const teacherName = profile.displayName || "Your teacher";
|
|
622
|
-
const dueDate = assignment.dueDateTimestamp ? dayjs(assignment.dueDateTimestamp.toDate()).format("MMM Do") : null;
|
|
623
|
-
const results = await sendNotification(sendTo, {
|
|
624
|
-
courseId: assignment.courseId,
|
|
625
|
-
type: SPEAKABLE_NOTIFICATIONS.NEW_ASSIGNMENT,
|
|
626
|
-
senderName: teacherName,
|
|
627
|
-
link: `${WEB_BASE_URL}/assignment/${assignment.id}`,
|
|
628
|
-
messagePreview: `A new assignment "${assignment.name}" is now available. ${dueDate ? `Due ${dueDate}` : ""}`,
|
|
629
|
-
title: "New Assignment Available!",
|
|
630
|
-
imageUrl: (_a = profile.image) == null ? void 0 : _a.url
|
|
631
|
-
});
|
|
632
|
-
return results;
|
|
633
|
-
};
|
|
634
|
-
var createAssessmentSubmissionNotification = async ({
|
|
635
|
-
data: assignment,
|
|
636
|
-
profile,
|
|
637
|
-
userId
|
|
638
|
-
}) => {
|
|
639
|
-
var _a;
|
|
640
|
-
const studentName = profile.displayName || "Your student";
|
|
641
|
-
const results = await sendNotification(assignment.owners, {
|
|
642
|
-
courseId: assignment.courseId,
|
|
643
|
-
type: SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SUBMITTED,
|
|
644
|
-
link: `${WEB_BASE_URL}/a/${assignment.id}?studentId=${userId}`,
|
|
645
|
-
title: `Assessment Submitted!`,
|
|
646
|
-
senderName: studentName,
|
|
647
|
-
messagePreview: `${studentName} has submitted the assessment "${assignment.name}"`,
|
|
648
|
-
imageUrl: (_a = profile.image) == null ? void 0 : _a.url
|
|
649
|
-
});
|
|
650
|
-
return results;
|
|
651
|
-
};
|
|
652
|
-
var createAssessmentScoredNotification = async ({
|
|
653
|
-
data,
|
|
654
|
-
profile
|
|
655
|
-
}) => {
|
|
656
|
-
var _a, _b, _c, _d, _e;
|
|
657
|
-
const { assignment, sendTo } = data;
|
|
658
|
-
const teacherName = profile.displayName || "Your teacher";
|
|
659
|
-
const title = `${assignment.isAssessment ? "Assessment" : "Assignment"} Reviewed!`;
|
|
660
|
-
const messagePreview = `Your ${assignment.isAssessment ? "assessment" : "assignment"} has been reviewed by your teacher. Click to view the feedback.`;
|
|
661
|
-
const results = await sendNotification(sendTo, {
|
|
662
|
-
courseId: assignment.courseId,
|
|
663
|
-
type: SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SCORED,
|
|
664
|
-
link: `${WEB_BASE_URL}/assignment/${assignment.id}`,
|
|
665
|
-
title,
|
|
666
|
-
messagePreview,
|
|
667
|
-
imageUrl: (_a = profile.image) == null ? void 0 : _a.url,
|
|
668
|
-
senderName: teacherName
|
|
669
|
-
});
|
|
670
|
-
await ((_e = (_c = (_b = api).httpsCallable) == null ? void 0 : _c.call(_b, "sendAssessmentScoredEmail")) == null ? void 0 : _e({
|
|
671
|
-
assessmentTitle: assignment.name,
|
|
672
|
-
link: `${WEB_BASE_URL}/assignment/${assignment.id}`,
|
|
673
|
-
senderImage: ((_d = profile.image) == null ? void 0 : _d.url) || "",
|
|
674
|
-
studentId: sendTo[0],
|
|
675
|
-
teacherName: profile.displayName
|
|
676
|
-
}));
|
|
677
|
-
return results;
|
|
678
|
-
};
|
|
679
|
-
|
|
680
|
-
// src/domains/notification/hooks/notification.hooks.ts
|
|
681
|
-
var notificationQueryKeys = {
|
|
682
|
-
all: ["notifications"],
|
|
683
|
-
byId: (id) => [...notificationQueryKeys.all, id]
|
|
684
|
-
};
|
|
685
|
-
var useCreateNotification = () => {
|
|
686
|
-
const { user, queryClient } = useSpeakableApi();
|
|
687
|
-
const handleCreateNotifications = async (type, data) => {
|
|
688
|
-
var _a, _b;
|
|
689
|
-
const result = await createNotification({
|
|
690
|
-
type,
|
|
691
|
-
userId: user.auth.uid,
|
|
692
|
-
profile: (_a = user == null ? void 0 : user.profile) != null ? _a : {},
|
|
693
|
-
data
|
|
694
|
-
});
|
|
695
|
-
queryClient.invalidateQueries({
|
|
696
|
-
queryKey: notificationQueryKeys.byId((_b = user == null ? void 0 : user.auth.uid) != null ? _b : "")
|
|
697
|
-
});
|
|
698
|
-
return result;
|
|
699
|
-
};
|
|
700
|
-
return {
|
|
701
|
-
createNotification: handleCreateNotifications
|
|
702
|
-
};
|
|
703
|
-
};
|
|
704
|
-
|
|
705
|
-
// src/domains/assignment/assignment.constants.ts
|
|
706
|
-
var ASSIGNMENTS_COLLECTION = "assignments";
|
|
707
|
-
var ANALYTICS_SUBCOLLECTION = "analytics";
|
|
708
|
-
var SCORES_SUBCOLLECTION = "scores";
|
|
709
|
-
var refsAssignmentFiresotre = {
|
|
710
|
-
allAssignments: () => ASSIGNMENTS_COLLECTION,
|
|
711
|
-
assignment: (params) => `${ASSIGNMENTS_COLLECTION}/${params.id}`,
|
|
712
|
-
assignmentAllAnalytics: (params) => `${ASSIGNMENTS_COLLECTION}/${params.id}/${ANALYTICS_SUBCOLLECTION}`,
|
|
713
|
-
assignmentAnalytics: (params) => `${ASSIGNMENTS_COLLECTION}/${params.id}/${ANALYTICS_SUBCOLLECTION}/${params.type}`,
|
|
714
|
-
assignmentScores: (params) => `${ASSIGNMENTS_COLLECTION}/${params.id}/${SCORES_SUBCOLLECTION}/${params.userId}`
|
|
715
|
-
};
|
|
716
|
-
|
|
717
|
-
// src/domains/assignment/utils/create-default-score.ts
|
|
718
|
-
var defaultScore = (props) => {
|
|
719
|
-
const { serverTimestamp } = api.accessHelpers();
|
|
720
|
-
const score = {
|
|
721
|
-
progress: 0,
|
|
722
|
-
score: 0,
|
|
723
|
-
startDate: serverTimestamp(),
|
|
724
|
-
status: "IN_PROGRESS",
|
|
725
|
-
submitted: false,
|
|
726
|
-
cards: {},
|
|
727
|
-
lastPlayed: serverTimestamp(),
|
|
728
|
-
owners: props.owners,
|
|
729
|
-
userId: props.userId
|
|
730
|
-
};
|
|
731
|
-
if (props.googleClassroomUserId) {
|
|
732
|
-
score.googleClassroomUserId = props.googleClassroomUserId;
|
|
733
|
-
}
|
|
734
|
-
if (props.courseId) {
|
|
735
|
-
score.courseId = props.courseId;
|
|
736
|
-
}
|
|
737
|
-
return score;
|
|
738
|
-
};
|
|
739
|
-
|
|
740
|
-
// src/domains/assignment/score-practice.constants.ts
|
|
741
|
-
var SCORES_PRACTICE_COLLECTION = "users";
|
|
742
|
-
var SCORES_PRACTICE_SUBCOLLECTION = "practice";
|
|
743
|
-
var refsScoresPractice = {
|
|
744
|
-
practiceScores: (params) => `${SCORES_PRACTICE_COLLECTION}/${params.userId}/${SCORES_PRACTICE_SUBCOLLECTION}/${params.setId}`,
|
|
745
|
-
practiceScoreHistoryRefDoc: (params) => `${SCORES_PRACTICE_COLLECTION}/${params.userId}/${SCORES_PRACTICE_SUBCOLLECTION}/${params.setId}/attempts/${params.date}`
|
|
746
|
-
};
|
|
747
|
-
|
|
748
|
-
// src/domains/assignment/services/create-score.service.ts
|
|
749
|
-
async function _createScore(params) {
|
|
750
|
-
var _a, _b, _c;
|
|
751
|
-
if (params.isAssignment) {
|
|
752
|
-
const ref = refsAssignmentFiresotre.assignmentScores({
|
|
753
|
-
id: params.activityId,
|
|
754
|
-
userId: params.userId
|
|
755
|
-
});
|
|
756
|
-
await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "updateAssignmentGradebookStatus")) == null ? void 0 : _c({
|
|
757
|
-
assignmentId: params.activityId,
|
|
758
|
-
userId: params.userId,
|
|
759
|
-
status: "IN_PROGRESS",
|
|
760
|
-
score: null
|
|
761
|
-
}));
|
|
762
|
-
await api.setDoc(ref, params.scoreData, { merge: true });
|
|
763
|
-
return {
|
|
764
|
-
id: params.userId
|
|
765
|
-
};
|
|
766
|
-
} else {
|
|
767
|
-
const ref = refsScoresPractice.practiceScores({
|
|
768
|
-
userId: params.userId,
|
|
769
|
-
setId: params.activityId
|
|
770
|
-
});
|
|
771
|
-
await api.setDoc(ref, params.scoreData);
|
|
772
|
-
return {
|
|
773
|
-
id: params.userId
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
var createScore = withErrorHandler(_createScore, "createScore");
|
|
778
|
-
|
|
779
|
-
// src/domains/assignment/services/get-score.service.ts
|
|
780
|
-
async function getAssignmentScore({
|
|
781
|
-
userId,
|
|
782
|
-
assignment,
|
|
783
|
-
googleClassroomUserId
|
|
784
|
-
}) {
|
|
785
|
-
const path = refsAssignmentFiresotre.assignmentScores({
|
|
786
|
-
id: assignment.id,
|
|
787
|
-
userId
|
|
788
|
-
});
|
|
789
|
-
const response = await api.getDoc(path);
|
|
790
|
-
if (response.data == null) {
|
|
791
|
-
const newScore = {
|
|
792
|
-
...defaultScore({
|
|
793
|
-
owners: [userId],
|
|
794
|
-
userId,
|
|
795
|
-
courseId: assignment.courseId,
|
|
796
|
-
googleClassroomUserId
|
|
797
|
-
}),
|
|
798
|
-
assignmentId: assignment.id
|
|
799
|
-
};
|
|
800
|
-
logStartAssignment({
|
|
801
|
-
courseId: assignment.courseId
|
|
802
|
-
});
|
|
803
|
-
const result = await createScore({
|
|
804
|
-
activityId: assignment.id,
|
|
805
|
-
userId,
|
|
806
|
-
isAssignment: true,
|
|
807
|
-
scoreData: newScore
|
|
808
|
-
});
|
|
809
|
-
return {
|
|
810
|
-
...newScore,
|
|
811
|
-
id: result.id
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
return response.data;
|
|
815
|
-
}
|
|
816
|
-
async function getPracticeScore({ userId, setId }) {
|
|
817
|
-
const path = refsScoresPractice.practiceScores({ userId, setId });
|
|
818
|
-
const response = await api.getDoc(path);
|
|
819
|
-
if (response.data == null) {
|
|
820
|
-
const newScore = {
|
|
821
|
-
...defaultScore({
|
|
822
|
-
owners: [userId],
|
|
823
|
-
userId
|
|
824
|
-
}),
|
|
825
|
-
setId
|
|
826
|
-
};
|
|
827
|
-
const result = await createScore({
|
|
828
|
-
activityId: setId,
|
|
829
|
-
userId,
|
|
830
|
-
isAssignment: false,
|
|
831
|
-
scoreData: newScore
|
|
832
|
-
});
|
|
833
|
-
return {
|
|
834
|
-
...newScore,
|
|
835
|
-
id: result.id
|
|
836
|
-
};
|
|
837
|
-
}
|
|
838
|
-
return response.data;
|
|
839
|
-
}
|
|
840
|
-
async function _getScore(params) {
|
|
841
|
-
if (params.isAssignment) {
|
|
842
|
-
return await getAssignmentScore({
|
|
843
|
-
userId: params.userId,
|
|
844
|
-
assignment: {
|
|
845
|
-
id: params.activityId,
|
|
846
|
-
courseId: params.courseId
|
|
847
|
-
},
|
|
848
|
-
googleClassroomUserId: params.googleClassroomUserId
|
|
849
|
-
});
|
|
850
|
-
} else {
|
|
851
|
-
return await getPracticeScore({
|
|
852
|
-
userId: params.userId,
|
|
853
|
-
setId: params.activityId
|
|
854
|
-
});
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
var getScore = withErrorHandler(_getScore, "getScore");
|
|
858
|
-
|
|
859
|
-
// src/domains/assignment/utils/calculateScoreAndProgress.ts
|
|
860
|
-
var calculateScoreAndProgress = (scores, cardsList, weights) => {
|
|
861
|
-
const totalSetPoints = cardsList.reduce((acc, cardId) => {
|
|
862
|
-
acc += (weights == null ? void 0 : weights[cardId]) || 1;
|
|
863
|
-
return acc;
|
|
864
|
-
}, 0);
|
|
865
|
-
const totalPointsAwarded = Object.keys((scores == null ? void 0 : scores.cards) || {}).reduce((acc, cardId) => {
|
|
866
|
-
var _a, _b;
|
|
867
|
-
const cardScores = (_a = scores == null ? void 0 : scores.cards) == null ? void 0 : _a[cardId];
|
|
868
|
-
if ((cardScores == null ? void 0 : cardScores.completed) || (cardScores == null ? void 0 : cardScores.score) || (cardScores == null ? void 0 : cardScores.score) === 0) {
|
|
869
|
-
const score2 = (cardScores == null ? void 0 : cardScores.score) || (cardScores == null ? void 0 : cardScores.score) === 0 ? Number((_b = cardScores == null ? void 0 : cardScores.score) != null ? _b : 0) : null;
|
|
870
|
-
const weight = (weights == null ? void 0 : weights[cardId]) || 1;
|
|
871
|
-
const fraction = (score2 != null ? score2 : 0) / 100;
|
|
872
|
-
if (score2 || score2 === 0) {
|
|
873
|
-
acc += weight * fraction;
|
|
874
|
-
} else {
|
|
875
|
-
acc += weight;
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
return acc;
|
|
879
|
-
}, 0);
|
|
880
|
-
const totalCompletedCards = Object.keys((scores == null ? void 0 : scores.cards) || {}).reduce((acc, cardId) => {
|
|
881
|
-
var _a;
|
|
882
|
-
const cardScores = (_a = scores == null ? void 0 : scores.cards) == null ? void 0 : _a[cardId];
|
|
883
|
-
if ((cardScores == null ? void 0 : cardScores.completed) || (cardScores == null ? void 0 : cardScores.score) || (cardScores == null ? void 0 : cardScores.score) === 0) {
|
|
884
|
-
acc += 1;
|
|
885
|
-
}
|
|
886
|
-
return acc;
|
|
887
|
-
}, 0);
|
|
888
|
-
const percent = totalPointsAwarded / totalSetPoints;
|
|
889
|
-
const score = Math.round(percent * 100);
|
|
890
|
-
const progress = Math.round(totalCompletedCards / (cardsList.length || 1) * 100);
|
|
891
|
-
return { score, progress };
|
|
892
|
-
};
|
|
893
|
-
var calculateScoreAndProgress_default = calculateScoreAndProgress;
|
|
894
|
-
|
|
895
|
-
// src/domains/assignment/services/update-score.service.ts
|
|
896
|
-
async function _updateScore(params) {
|
|
897
|
-
const path = params.isAssignment ? refsAssignmentFiresotre.assignmentScores({
|
|
898
|
-
id: params.activityId,
|
|
899
|
-
userId: params.userId
|
|
900
|
-
}) : refsScoresPractice.practiceScores({
|
|
901
|
-
setId: params.activityId,
|
|
902
|
-
userId: params.userId
|
|
903
|
-
});
|
|
904
|
-
await api.updateDoc(path, {
|
|
905
|
-
...params.data
|
|
906
|
-
});
|
|
907
|
-
}
|
|
908
|
-
var updateScore = withErrorHandler(_updateScore, "updateScore");
|
|
909
|
-
async function _updateCardScore(params) {
|
|
910
|
-
const path = params.isAssignment ? refsAssignmentFiresotre.assignmentScores({
|
|
911
|
-
id: params.activityId,
|
|
912
|
-
userId: params.userId
|
|
913
|
-
}) : refsScoresPractice.practiceScores({
|
|
914
|
-
setId: params.activityId,
|
|
915
|
-
userId: params.userId
|
|
916
|
-
});
|
|
917
|
-
const updates = Object.keys(params.updates.cardScore).reduce(
|
|
918
|
-
(acc, key) => {
|
|
919
|
-
acc[`cards.${params.cardId}.${key}`] = params.updates.cardScore[key];
|
|
920
|
-
return acc;
|
|
921
|
-
},
|
|
922
|
-
{}
|
|
923
|
-
);
|
|
924
|
-
if (params.updates.progress) {
|
|
925
|
-
updates.progress = params.updates.progress;
|
|
926
|
-
}
|
|
927
|
-
if (params.updates.score) {
|
|
928
|
-
updates.score = params.updates.score;
|
|
929
|
-
}
|
|
930
|
-
await api.updateDoc(path, {
|
|
931
|
-
...updates
|
|
932
|
-
});
|
|
933
|
-
}
|
|
934
|
-
var updateCardScore = withErrorHandler(_updateCardScore, "updateCardScore");
|
|
935
|
-
|
|
936
|
-
// src/domains/assignment/services/clear-score.service.ts
|
|
937
|
-
import dayjs2 from "dayjs";
|
|
938
|
-
async function clearScore(params) {
|
|
939
|
-
var _a, _b, _c, _d, _e;
|
|
940
|
-
const update = {
|
|
941
|
-
[`cards.${params.cardId}`]: {
|
|
942
|
-
attempts: ((_a = params.cardScores.attempts) != null ? _a : 1) + 1,
|
|
943
|
-
correct: (_b = params.cardScores.correct) != null ? _b : 0,
|
|
944
|
-
// save old score history
|
|
945
|
-
history: [
|
|
946
|
-
{
|
|
947
|
-
...params.cardScores,
|
|
948
|
-
attempts: (_c = params.cardScores.attempts) != null ? _c : 1,
|
|
949
|
-
correct: (_d = params.cardScores.correct) != null ? _d : 0,
|
|
950
|
-
retryTime: dayjs2().format("YYYY-MM-DD HH:mm:ss"),
|
|
951
|
-
history: null
|
|
952
|
-
},
|
|
953
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
954
|
-
...(_e = params.cardScores.history) != null ? _e : []
|
|
955
|
-
]
|
|
956
|
-
}
|
|
957
|
-
};
|
|
958
|
-
const path = params.isAssignment ? refsAssignmentFiresotre.assignmentScores({
|
|
959
|
-
id: params.activityId,
|
|
960
|
-
userId: params.userId
|
|
961
|
-
}) : refsScoresPractice.practiceScores({
|
|
962
|
-
setId: params.activityId,
|
|
963
|
-
userId: params.userId
|
|
964
|
-
});
|
|
965
|
-
await api.updateDoc(path, update);
|
|
966
|
-
return {
|
|
967
|
-
update,
|
|
968
|
-
activityId: params.activityId
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
// src/domains/assignment/services/submit-assignment-score.service.ts
|
|
973
|
-
import dayjs3 from "dayjs";
|
|
974
|
-
async function _submitAssignmentScore({
|
|
975
|
-
cardIds,
|
|
976
|
-
assignment,
|
|
977
|
-
weights,
|
|
978
|
-
userId,
|
|
979
|
-
status,
|
|
980
|
-
studentName
|
|
981
|
-
}) {
|
|
982
|
-
const { serverTimestamp } = api.accessHelpers();
|
|
983
|
-
const path = refsAssignmentFiresotre.assignmentScores({ id: assignment.id, userId });
|
|
984
|
-
const fieldsUpdated = {
|
|
985
|
-
submitted: true,
|
|
986
|
-
progress: 100,
|
|
987
|
-
submissionDate: serverTimestamp(),
|
|
988
|
-
status
|
|
989
|
-
};
|
|
990
|
-
if (assignment.isAssessment) {
|
|
991
|
-
const result = await handleAssessment(
|
|
992
|
-
assignment,
|
|
993
|
-
userId,
|
|
994
|
-
cardIds,
|
|
995
|
-
weights,
|
|
996
|
-
fieldsUpdated,
|
|
997
|
-
studentName
|
|
998
|
-
);
|
|
999
|
-
return result;
|
|
1000
|
-
} else if (assignment.courseId) {
|
|
1001
|
-
await handleCourseAssignment(assignment, userId);
|
|
1002
|
-
}
|
|
1003
|
-
await api.updateDoc(path, { ...fieldsUpdated });
|
|
1004
|
-
return { success: true, fieldsUpdated };
|
|
1005
|
-
}
|
|
1006
|
-
var submitAssignmentScore = withErrorHandler(
|
|
1007
|
-
_submitAssignmentScore,
|
|
1008
|
-
"submitAssignmentScore"
|
|
1009
|
-
);
|
|
1010
|
-
async function handleAssessment(assignment, userId, cardIds, weights, fieldsUpdated, studentName) {
|
|
1011
|
-
var _a, _b, _c;
|
|
1012
|
-
const path = refsAssignmentFiresotre.assignmentScores({ id: assignment.id, userId });
|
|
1013
|
-
const response = await api.getDoc(path);
|
|
1014
|
-
if (!response.data) {
|
|
1015
|
-
throw new Error("Score not found");
|
|
1016
|
-
}
|
|
1017
|
-
const { score: scoreCalculated } = calculateScoreAndProgress_default(response.data, cardIds, weights);
|
|
1018
|
-
await api.updateDoc(path, { score: scoreCalculated, status: "PENDING_REVIEW" });
|
|
1019
|
-
await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "submitAssessment")) == null ? void 0 : _c({
|
|
1020
|
-
assignmentId: assignment.id,
|
|
1021
|
-
assignmentTitle: assignment.name,
|
|
1022
|
-
userId,
|
|
1023
|
-
teacherId: assignment.owners[0],
|
|
1024
|
-
studentName
|
|
1025
|
-
}));
|
|
1026
|
-
fieldsUpdated.status = "PENDING_REVIEW";
|
|
1027
|
-
return { success: true, fieldsUpdated };
|
|
1028
|
-
}
|
|
1029
|
-
async function handleCourseAssignment(assignment, userId) {
|
|
1030
|
-
var _a, _b, _c;
|
|
1031
|
-
await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "submitAssignmentV2")) == null ? void 0 : _c({
|
|
1032
|
-
assignmentId: assignment.id,
|
|
1033
|
-
userId
|
|
1034
|
-
}));
|
|
1035
|
-
}
|
|
1036
|
-
async function submitPracticeScore({
|
|
1037
|
-
setId,
|
|
1038
|
-
userId,
|
|
1039
|
-
scores
|
|
1040
|
-
}) {
|
|
1041
|
-
const { serverTimestamp } = api.accessHelpers();
|
|
1042
|
-
const date = dayjs3().format("YYYY-MM-DD-HH-mm");
|
|
1043
|
-
const ref = refsScoresPractice.practiceScoreHistoryRefDoc({ setId, userId, date });
|
|
1044
|
-
const fieldsUpdated = {
|
|
1045
|
-
...scores,
|
|
1046
|
-
submitted: true,
|
|
1047
|
-
progress: 100,
|
|
1048
|
-
submissionDate: serverTimestamp(),
|
|
1049
|
-
status: "SUBMITTED"
|
|
1050
|
-
};
|
|
1051
|
-
await api.setDoc(ref, { ...fieldsUpdated });
|
|
1052
|
-
const refScores = refsScoresPractice.practiceScores({ userId, setId });
|
|
1053
|
-
await api.deleteDoc(refScores);
|
|
1054
|
-
return { success: true, fieldsUpdated };
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
// src/domains/assignment/hooks/score-hooks.ts
|
|
1058
|
-
var scoreQueryKeys = {
|
|
1059
|
-
all: ["scores"],
|
|
1060
|
-
byId: (id) => [...scoreQueryKeys.all, id],
|
|
1061
|
-
list: () => [...scoreQueryKeys.all, "list"]
|
|
1062
|
-
};
|
|
1063
|
-
function useScore({
|
|
1064
|
-
isAssignment,
|
|
1065
|
-
activityId,
|
|
1066
|
-
userId = "",
|
|
1067
|
-
courseId,
|
|
1068
|
-
enabled = true,
|
|
1069
|
-
googleClassroomUserId
|
|
1070
|
-
}) {
|
|
1071
|
-
return useQuery2({
|
|
1072
|
-
queryFn: () => getScore({
|
|
1073
|
-
userId,
|
|
1074
|
-
courseId,
|
|
1075
|
-
activityId,
|
|
1076
|
-
googleClassroomUserId,
|
|
1077
|
-
isAssignment
|
|
1078
|
-
}),
|
|
1079
|
-
queryKey: scoreQueryKeys.byId(activityId),
|
|
1080
|
-
enabled
|
|
1081
|
-
});
|
|
1082
|
-
}
|
|
1083
|
-
var debounceUpdateScore = debounce(updateScore, 1e3);
|
|
1084
|
-
function useUpdateScore() {
|
|
1085
|
-
const { queryClient } = useSpeakableApi();
|
|
1086
|
-
const mutation = useMutation({
|
|
1087
|
-
mutationFn: debounceUpdateScore,
|
|
1088
|
-
onMutate: (variables) => {
|
|
1089
|
-
return handleOptimisticUpdate({
|
|
1090
|
-
queryClient,
|
|
1091
|
-
queryKey: scoreQueryKeys.byId(variables.activityId),
|
|
1092
|
-
newData: variables.data
|
|
1093
|
-
});
|
|
1094
|
-
},
|
|
1095
|
-
onError: (_, variables, context) => {
|
|
1096
|
-
if (context == null ? void 0 : context.previousData)
|
|
1097
|
-
queryClient.setQueryData(scoreQueryKeys.byId(variables.activityId), context.previousData);
|
|
1098
|
-
},
|
|
1099
|
-
onSettled: (_, err, variables) => {
|
|
1100
|
-
queryClient.invalidateQueries({
|
|
1101
|
-
queryKey: scoreQueryKeys.byId(variables.activityId)
|
|
1102
|
-
});
|
|
1103
|
-
}
|
|
1104
|
-
});
|
|
1105
|
-
return {
|
|
1106
|
-
mutationUpdateScore: mutation
|
|
1107
|
-
};
|
|
1108
|
-
}
|
|
1109
|
-
function useUpdateCardScore({
|
|
1110
|
-
isAssignment,
|
|
1111
|
-
activityId,
|
|
1112
|
-
userId,
|
|
1113
|
-
cardIds,
|
|
1114
|
-
weights
|
|
1115
|
-
}) {
|
|
1116
|
-
const { queryClient } = useSpeakableApi();
|
|
1117
|
-
const queryKey = scoreQueryKeys.byId(activityId);
|
|
1118
|
-
const mutation = useMutation({
|
|
1119
|
-
mutationFn: async ({ cardId, cardScore }) => {
|
|
1120
|
-
const previousScores = queryClient.getQueryData(queryKey);
|
|
1121
|
-
const { progress, score, newScoreUpdated, updatedCardScore } = getScoreUpdated({
|
|
1122
|
-
previousScores: previousScores != null ? previousScores : {},
|
|
1123
|
-
cardId,
|
|
1124
|
-
cardScore,
|
|
1125
|
-
cardIds,
|
|
1126
|
-
weights
|
|
1127
|
-
});
|
|
1128
|
-
await updateCardScore({
|
|
1129
|
-
userId,
|
|
1130
|
-
cardId,
|
|
1131
|
-
isAssignment,
|
|
1132
|
-
activityId,
|
|
1133
|
-
updates: {
|
|
1134
|
-
cardScore: updatedCardScore,
|
|
1135
|
-
progress,
|
|
1136
|
-
score
|
|
1137
|
-
}
|
|
1138
|
-
});
|
|
1139
|
-
return { cardId, scoresUpdated: newScoreUpdated };
|
|
1140
|
-
},
|
|
1141
|
-
onMutate: ({ cardId, cardScore }) => {
|
|
1142
|
-
queryClient.setQueryData(queryKey, (previousScore) => {
|
|
1143
|
-
const updates = handleOptimisticScore({
|
|
1144
|
-
score: previousScore,
|
|
1145
|
-
cardId,
|
|
1146
|
-
cardScore,
|
|
1147
|
-
cardIds,
|
|
1148
|
-
weights
|
|
1149
|
-
});
|
|
1150
|
-
return {
|
|
1151
|
-
...previousScore,
|
|
1152
|
-
...updates
|
|
1153
|
-
};
|
|
1154
|
-
});
|
|
1155
|
-
},
|
|
1156
|
-
// eslint-disable-next-line no-unused-vars
|
|
1157
|
-
onError: (error) => {
|
|
1158
|
-
console.log(error.message);
|
|
1159
|
-
const previousData = queryClient.getQueryData(queryKey);
|
|
1160
|
-
if (previousData) {
|
|
1161
|
-
queryClient.setQueryData(queryKey, previousData);
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
});
|
|
1165
|
-
return {
|
|
1166
|
-
mutationUpdateCardScore: mutation
|
|
1167
|
-
};
|
|
1168
|
-
}
|
|
1169
|
-
var getScoreUpdated = ({
|
|
1170
|
-
cardId,
|
|
1171
|
-
cardScore,
|
|
1172
|
-
previousScores,
|
|
1173
|
-
cardIds,
|
|
1174
|
-
weights
|
|
1175
|
-
}) => {
|
|
1176
|
-
var _a, _b;
|
|
1177
|
-
const previousCard = (_a = previousScores.cards) == null ? void 0 : _a[cardId];
|
|
1178
|
-
const newCardScore = {
|
|
1179
|
-
...previousCard != null ? previousCard : {},
|
|
1180
|
-
...cardScore
|
|
1181
|
-
};
|
|
1182
|
-
const newScores = {
|
|
1183
|
-
...previousScores,
|
|
1184
|
-
cards: {
|
|
1185
|
-
...(_b = previousScores.cards) != null ? _b : {},
|
|
1186
|
-
[cardId]: newCardScore
|
|
1187
|
-
}
|
|
1188
|
-
};
|
|
1189
|
-
const { score, progress } = calculateScoreAndProgress_default(newScores, cardIds, weights);
|
|
1190
|
-
return {
|
|
1191
|
-
newScoreUpdated: newScores,
|
|
1192
|
-
updatedCardScore: cardScore,
|
|
1193
|
-
score,
|
|
1194
|
-
progress
|
|
1195
|
-
};
|
|
1196
|
-
};
|
|
1197
|
-
var handleOptimisticScore = ({
|
|
1198
|
-
score,
|
|
1199
|
-
cardId,
|
|
1200
|
-
cardScore,
|
|
1201
|
-
cardIds,
|
|
1202
|
-
weights
|
|
1203
|
-
}) => {
|
|
1204
|
-
var _a;
|
|
1205
|
-
let cards = { ...(_a = score == null ? void 0 : score.cards) != null ? _a : {} };
|
|
1206
|
-
cards = {
|
|
1207
|
-
...cards,
|
|
1208
|
-
[cardId]: {
|
|
1209
|
-
...cards[cardId],
|
|
1210
|
-
...cardScore
|
|
1211
|
-
}
|
|
1212
|
-
};
|
|
1213
|
-
const { score: scoreValue, progress } = calculateScoreAndProgress_default(
|
|
1214
|
-
// @ts-ignore
|
|
1215
|
-
{
|
|
1216
|
-
...score != null ? score : {},
|
|
1217
|
-
cards
|
|
1218
|
-
},
|
|
1219
|
-
cardIds,
|
|
1220
|
-
weights
|
|
1221
|
-
);
|
|
1222
|
-
return { cards, score: scoreValue, progress };
|
|
1223
|
-
};
|
|
1224
|
-
function useClearScore() {
|
|
1225
|
-
const { queryClient } = useSpeakableApi();
|
|
1226
|
-
const mutation = useMutation({
|
|
1227
|
-
mutationFn: clearScore,
|
|
1228
|
-
onSettled: (result) => {
|
|
1229
|
-
var _a;
|
|
1230
|
-
queryClient.invalidateQueries({
|
|
1231
|
-
queryKey: scoreQueryKeys.byId((_a = result == null ? void 0 : result.activityId) != null ? _a : "")
|
|
1232
|
-
});
|
|
1233
|
-
}
|
|
1234
|
-
});
|
|
1235
|
-
return {
|
|
1236
|
-
mutationClearScore: mutation
|
|
1237
|
-
};
|
|
1238
|
-
}
|
|
1239
|
-
function useSubmitAssignmentScore({
|
|
1240
|
-
onAssignmentSubmitted,
|
|
1241
|
-
studentName
|
|
1242
|
-
}) {
|
|
1243
|
-
const { queryClient } = useSpeakableApi();
|
|
1244
|
-
const { hasGoogleClassroomGradePassback } = usePermissions_default();
|
|
1245
|
-
const { submitAssignmentToGoogleClassroom } = useGoogleClassroom();
|
|
1246
|
-
const { createNotification: createNotification2 } = useCreateNotification();
|
|
1247
|
-
const mutation = useMutation({
|
|
1248
|
-
mutationFn: async ({
|
|
1249
|
-
assignment,
|
|
1250
|
-
userId,
|
|
1251
|
-
cardIds,
|
|
1252
|
-
weights,
|
|
1253
|
-
scores,
|
|
1254
|
-
status
|
|
1255
|
-
}) => {
|
|
1256
|
-
try {
|
|
1257
|
-
const scoreUpdated = await submitAssignmentScore({
|
|
1258
|
-
assignment,
|
|
1259
|
-
userId,
|
|
1260
|
-
cardIds,
|
|
1261
|
-
weights,
|
|
1262
|
-
status,
|
|
1263
|
-
studentName
|
|
1264
|
-
});
|
|
1265
|
-
if (assignment.courseWorkId != null && !assignment.isAssessment && hasGoogleClassroomGradePassback) {
|
|
1266
|
-
await submitAssignmentToGoogleClassroom({
|
|
1267
|
-
assignment,
|
|
1268
|
-
scores
|
|
1269
|
-
});
|
|
1270
|
-
}
|
|
1271
|
-
if (assignment.isAssessment) {
|
|
1272
|
-
createNotification2(SpeakableNotificationTypes.ASSESSMENT_SUBMITTED, assignment);
|
|
1273
|
-
}
|
|
1274
|
-
if (assignment == null ? void 0 : assignment.id) {
|
|
1275
|
-
logSubmitAssignment({
|
|
1276
|
-
courseId: assignment == null ? void 0 : assignment.courseId
|
|
1277
|
-
});
|
|
1278
|
-
}
|
|
1279
|
-
onAssignmentSubmitted(assignment.id);
|
|
1280
|
-
queryClient.setQueryData(scoreQueryKeys.byId(assignment.id), {
|
|
1281
|
-
...scores,
|
|
1282
|
-
...scoreUpdated.fieldsUpdated
|
|
1283
|
-
});
|
|
1284
|
-
return {
|
|
1285
|
-
success: true,
|
|
1286
|
-
message: "Score submitted successfully"
|
|
1287
|
-
};
|
|
1288
|
-
} catch (error) {
|
|
1289
|
-
return {
|
|
1290
|
-
success: false,
|
|
1291
|
-
error
|
|
1292
|
-
};
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
});
|
|
1296
|
-
return {
|
|
1297
|
-
submitAssignmentScore: mutation.mutateAsync,
|
|
1298
|
-
isLoading: mutation.isPending
|
|
1299
|
-
};
|
|
1300
|
-
}
|
|
1301
|
-
function useSubmitPracticeScore() {
|
|
1302
|
-
const { queryClient } = useSpeakableApi();
|
|
1303
|
-
const mutation = useMutation({
|
|
1304
|
-
mutationFn: async ({
|
|
1305
|
-
setId,
|
|
1306
|
-
userId,
|
|
1307
|
-
scores
|
|
1308
|
-
}) => {
|
|
1309
|
-
try {
|
|
1310
|
-
await submitPracticeScore({
|
|
1311
|
-
setId,
|
|
1312
|
-
userId,
|
|
1313
|
-
scores
|
|
1314
|
-
});
|
|
1315
|
-
queryClient.invalidateQueries({
|
|
1316
|
-
queryKey: scoreQueryKeys.byId(setId)
|
|
1317
|
-
});
|
|
1318
|
-
return {
|
|
1319
|
-
success: true,
|
|
1320
|
-
message: "Score submitted successfully"
|
|
1321
|
-
};
|
|
1322
|
-
} catch (error) {
|
|
1323
|
-
return {
|
|
1324
|
-
success: false,
|
|
1325
|
-
error
|
|
1326
|
-
};
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1329
|
-
});
|
|
1330
|
-
return {
|
|
1331
|
-
submitPracticeScore: mutation.mutateAsync,
|
|
1332
|
-
isLoading: mutation.isPending
|
|
1333
|
-
};
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
// src/hooks/useActivity.ts
|
|
1337
|
-
import { useEffect as useEffect2 } from "react";
|
|
1338
|
-
|
|
1339
|
-
// src/domains/sets/set.hooks.ts
|
|
1340
|
-
import { useQuery as useQuery3 } from "@tanstack/react-query";
|
|
1341
|
-
|
|
1342
|
-
// src/domains/sets/set.constants.ts
|
|
1343
|
-
var SETS_COLLECTION = "sets";
|
|
1344
|
-
var refsSetsFirestore = {
|
|
1345
|
-
allSets: SETS_COLLECTION,
|
|
1346
|
-
set: (id) => `${SETS_COLLECTION}/${id}`
|
|
1347
|
-
};
|
|
1348
|
-
|
|
1349
|
-
// src/domains/sets/services/get-set.service.ts
|
|
1350
|
-
async function _getSet({ setId }) {
|
|
1351
|
-
const response = await api.getDoc(refsSetsFirestore.set(setId));
|
|
1352
|
-
return response.data;
|
|
1353
|
-
}
|
|
1354
|
-
var getSet = withErrorHandler(_getSet, "getSet");
|
|
1355
|
-
|
|
1356
|
-
// src/domains/sets/set.hooks.ts
|
|
1357
|
-
var setsQueryKeys = {
|
|
1358
|
-
all: ["sets"],
|
|
1359
|
-
one: (params) => [...setsQueryKeys.all, params.setId]
|
|
1360
|
-
};
|
|
1361
|
-
var useSet = ({ setId, enabled }) => {
|
|
1362
|
-
return useQuery3({
|
|
1363
|
-
queryKey: setsQueryKeys.one({ setId }),
|
|
1364
|
-
queryFn: () => getSet({ setId }),
|
|
1365
|
-
enabled: setId !== void 0 && setId !== "" && enabled
|
|
1366
|
-
});
|
|
1367
|
-
};
|
|
1368
|
-
function getSetFromCache({
|
|
1369
|
-
setId,
|
|
1370
|
-
queryClient
|
|
1371
|
-
}) {
|
|
1372
|
-
if (!setId) return null;
|
|
1373
|
-
return queryClient.getQueryData(setsQueryKeys.one({ setId }));
|
|
1374
|
-
}
|
|
1375
|
-
function updateSetInCache({
|
|
1376
|
-
set,
|
|
1377
|
-
queryClient
|
|
1378
|
-
}) {
|
|
1379
|
-
const { id, ...setData } = set;
|
|
1380
|
-
queryClient.setQueryData(setsQueryKeys.one({ setId: id }), setData);
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
// src/domains/cards/card.hooks.ts
|
|
1384
|
-
import { useMutation as useMutation2, useQueries, useQuery as useQuery4 } from "@tanstack/react-query";
|
|
1385
|
-
import { useMemo } from "react";
|
|
1386
|
-
|
|
1387
|
-
// src/domains/cards/card.constants.ts
|
|
1388
|
-
var CARDS_COLLECTION = "flashcards";
|
|
1389
|
-
var refsCardsFiresotre = {
|
|
1390
|
-
allCards: CARDS_COLLECTION,
|
|
1391
|
-
card: (id) => `${CARDS_COLLECTION}/${id}`
|
|
1392
|
-
};
|
|
1393
|
-
|
|
1394
|
-
// src/domains/cards/services/get-card.service.ts
|
|
1395
|
-
async function _getCard(params) {
|
|
1396
|
-
const ref = refsCardsFiresotre.card(params.cardId);
|
|
1397
|
-
const response = await api.getDoc(ref);
|
|
1398
|
-
if (!response.data) return null;
|
|
1399
|
-
return response.data;
|
|
1400
|
-
}
|
|
1401
|
-
var getCard = withErrorHandler(_getCard, "getCard");
|
|
1402
|
-
|
|
1403
|
-
// src/domains/cards/services/create-card.service.ts
|
|
1404
|
-
import { v4 } from "uuid";
|
|
1405
|
-
|
|
1406
|
-
// src/utils/text-utils.ts
|
|
1407
|
-
import sha1 from "js-sha1";
|
|
1408
|
-
var purify = (word) => {
|
|
1409
|
-
return word.normalize("NFD").replace(/\/([^" "]*)/g, "").replace(/\([^()]*\)/g, "").replace(/([^()]*)/g, "").replace(/[\u0300-\u036f]/g, "").replace(/[-]/g, " ").replace(/[.,/#!¡¿?؟。,.?$%^&*;:{}=\-_`~()’'…\s]/g, "").replace(/\s\s+/g, " ").toLowerCase().trim();
|
|
1410
|
-
};
|
|
1411
|
-
var cleanString = (words) => {
|
|
1412
|
-
const splitWords = words == null ? void 0 : words.split("+");
|
|
1413
|
-
if (splitWords && splitWords.length === 1) {
|
|
1414
|
-
const newWord = purify(words);
|
|
1415
|
-
return newWord;
|
|
1416
|
-
} else if (splitWords && splitWords.length > 1) {
|
|
1417
|
-
const split = splitWords.map((w) => purify(w));
|
|
1418
|
-
return split;
|
|
1419
|
-
} else {
|
|
1420
|
-
return "";
|
|
1421
|
-
}
|
|
1422
|
-
};
|
|
1423
|
-
var getWordHash = (word, language) => {
|
|
1424
|
-
const cleanedWord = cleanString(word);
|
|
1425
|
-
const wordHash = sha1(`${language}-${cleanedWord}`);
|
|
1426
|
-
console.log("wordHash core library", wordHash);
|
|
1427
|
-
return wordHash;
|
|
1428
|
-
};
|
|
1429
|
-
function getPhraseLength(phrase, input) {
|
|
1430
|
-
if (Array.isArray(phrase) && phrase.includes(input)) {
|
|
1431
|
-
return phrase[phrase.indexOf(input)].split(" ").length;
|
|
1432
|
-
} else {
|
|
1433
|
-
return phrase ? phrase.split(" ").length : 0;
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
// src/domains/cards/services/get-card-verification-status.service.ts
|
|
1438
|
-
var charactarLanguages = ["zh", "ja", "ko"];
|
|
1439
|
-
var getVerificationStatus = async (target_text, language) => {
|
|
1440
|
-
if ((target_text == null ? void 0 : target_text.length) < 3 && !charactarLanguages.includes(language)) {
|
|
1441
|
-
return "NOT_RECOMMENDED" /* NOT_RECOMMENDED */;
|
|
1442
|
-
}
|
|
1443
|
-
const hash = getWordHash(target_text, language);
|
|
1444
|
-
const response = await api.getDoc(`checked-pronunciations/${hash}`);
|
|
1445
|
-
try {
|
|
1446
|
-
if (response.data) {
|
|
1447
|
-
return processRecord(response.data);
|
|
1448
|
-
} else {
|
|
1449
|
-
return "NOT_CHECKED" /* NOT_CHECKED */;
|
|
1450
|
-
}
|
|
1451
|
-
} catch (e) {
|
|
1452
|
-
return "NOT_CHECKED" /* NOT_CHECKED */;
|
|
1453
|
-
}
|
|
1454
|
-
};
|
|
1455
|
-
var processRecord = (data) => {
|
|
1456
|
-
const { pronunciations = 0, fails = 0 } = data;
|
|
1457
|
-
const attempts = pronunciations + fails;
|
|
1458
|
-
const successRate = attempts > 0 ? pronunciations / attempts * 100 : 0;
|
|
1459
|
-
let newStatus = null;
|
|
1460
|
-
if (attempts < 6) {
|
|
1461
|
-
return "NOT_CHECKED" /* NOT_CHECKED */;
|
|
1462
|
-
}
|
|
1463
|
-
if (successRate > 25) {
|
|
1464
|
-
newStatus = "VERIFIED" /* VERIFIED */;
|
|
1465
|
-
} else if (successRate > 10) {
|
|
1466
|
-
newStatus = "WARNING" /* WARNING */;
|
|
1467
|
-
} else if (fails > 20 && successRate < 10 && pronunciations > 1) {
|
|
1468
|
-
newStatus = "NOT_RECOMMENDED" /* NOT_RECOMMENDED */;
|
|
1469
|
-
} else if (pronunciations === 0 && fails > 20) {
|
|
1470
|
-
newStatus = "NOT_WORKING" /* NOT_WORKING */;
|
|
1471
|
-
} else {
|
|
1472
|
-
newStatus = "NOT_CHECKED" /* NOT_CHECKED */;
|
|
1473
|
-
}
|
|
1474
|
-
return newStatus;
|
|
1475
|
-
};
|
|
1476
|
-
|
|
1477
|
-
// src/domains/cards/services/create-card.service.ts
|
|
1478
|
-
async function _createCard({ data }) {
|
|
1479
|
-
const response = await api.addDoc(refsCardsFiresotre.allCards, data);
|
|
1480
|
-
return response;
|
|
1481
|
-
}
|
|
1482
|
-
var createCard = withErrorHandler(_createCard, "createCard");
|
|
1483
|
-
async function _createCards({ cards }) {
|
|
1484
|
-
const { writeBatch, doc } = api.accessHelpers();
|
|
1485
|
-
const batch = writeBatch();
|
|
1486
|
-
const cardsWithId = [];
|
|
1487
|
-
for (const card of cards) {
|
|
1488
|
-
const cardId = v4();
|
|
1489
|
-
const ref = doc(refsCardsFiresotre.card(cardId));
|
|
1490
|
-
const newCardObject = {
|
|
1491
|
-
...card,
|
|
1492
|
-
id: cardId
|
|
1493
|
-
};
|
|
1494
|
-
if (card.type === "READ_REPEAT" /* READ_REPEAT */ && card.target_text && card.language) {
|
|
1495
|
-
const verificationStatus = await getVerificationStatus(card.target_text, card.language);
|
|
1496
|
-
newCardObject.verificationStatus = verificationStatus || null;
|
|
1497
|
-
}
|
|
1498
|
-
cardsWithId.push(newCardObject);
|
|
1499
|
-
batch.set(ref, newCardObject);
|
|
1500
|
-
}
|
|
1501
|
-
await batch.commit();
|
|
1502
|
-
return cardsWithId;
|
|
1503
|
-
}
|
|
1504
|
-
var createCards = withErrorHandler(_createCards, "createCards");
|
|
1505
|
-
|
|
1506
|
-
// src/domains/cards/card.hooks.ts
|
|
1507
|
-
var cardsQueryKeys = {
|
|
1508
|
-
all: ["cards"],
|
|
1509
|
-
one: (params) => [...cardsQueryKeys.all, params.cardId]
|
|
1510
|
-
};
|
|
1511
|
-
function useCards({
|
|
1512
|
-
cardIds,
|
|
1513
|
-
enabled = true,
|
|
1514
|
-
asObject
|
|
1515
|
-
}) {
|
|
1516
|
-
const queries = useQueries({
|
|
1517
|
-
queries: cardIds.map((cardId) => ({
|
|
1518
|
-
enabled: enabled && cardIds.length > 0,
|
|
1519
|
-
queryKey: cardsQueryKeys.one({
|
|
1520
|
-
cardId
|
|
1521
|
-
}),
|
|
1522
|
-
queryFn: () => getCard({ cardId })
|
|
1523
|
-
}))
|
|
1524
|
-
});
|
|
1525
|
-
const cards = queries.map((query) => query.data).filter(Boolean);
|
|
1526
|
-
const cardsObject = useMemo(() => {
|
|
1527
|
-
if (!asObject) return null;
|
|
1528
|
-
return cards.reduce((acc, card) => {
|
|
1529
|
-
acc[card.id] = card;
|
|
1530
|
-
return acc;
|
|
1531
|
-
}, {});
|
|
1532
|
-
}, [asObject, cards]);
|
|
1533
|
-
return {
|
|
1534
|
-
cards,
|
|
1535
|
-
cardsObject,
|
|
1536
|
-
cardsQueries: queries
|
|
1537
|
-
};
|
|
1538
|
-
}
|
|
1539
|
-
function useCreateCard() {
|
|
1540
|
-
const { queryClient } = useSpeakableApi();
|
|
1541
|
-
const mutationCreateCard = useMutation2({
|
|
1542
|
-
mutationFn: createCard,
|
|
1543
|
-
onSuccess: (cardCreated) => {
|
|
1544
|
-
queryClient.invalidateQueries({ queryKey: cardsQueryKeys.one({ cardId: cardCreated.id }) });
|
|
1545
|
-
}
|
|
1546
|
-
});
|
|
1547
|
-
return {
|
|
1548
|
-
mutationCreateCard
|
|
1549
|
-
};
|
|
1550
|
-
}
|
|
1551
|
-
function useCreateCards() {
|
|
1552
|
-
const mutationCreateCards = useMutation2({
|
|
1553
|
-
mutationFn: createCards
|
|
1554
|
-
});
|
|
1555
|
-
return {
|
|
1556
|
-
mutationCreateCards
|
|
1557
|
-
};
|
|
1558
|
-
}
|
|
1559
|
-
function getCardFromCache({
|
|
1560
|
-
cardId,
|
|
1561
|
-
queryClient
|
|
1562
|
-
}) {
|
|
1563
|
-
return queryClient.getQueryData(cardsQueryKeys.one({ cardId }));
|
|
1564
|
-
}
|
|
1565
|
-
function updateCardInCache({
|
|
1566
|
-
cardId,
|
|
1567
|
-
card,
|
|
1568
|
-
queryClient
|
|
1569
|
-
}) {
|
|
1570
|
-
queryClient.setQueryData(cardsQueryKeys.one({ cardId }), card);
|
|
1571
|
-
}
|
|
1572
|
-
function useGetCard({ cardId, enabled = true }) {
|
|
1573
|
-
const query = useQuery4({
|
|
1574
|
-
queryKey: cardsQueryKeys.one({ cardId }),
|
|
1575
|
-
queryFn: () => getCard({ cardId }),
|
|
1576
|
-
enabled: enabled && !!cardId
|
|
1577
|
-
});
|
|
1578
|
-
return query;
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
// src/services/add-grading-standard.ts
|
|
1582
|
-
var addGradingStandardLog = async (gradingStandard, userId) => {
|
|
1583
|
-
logGradingStandardLog(gradingStandard);
|
|
1584
|
-
const path = `users/${userId}/grading_standard_logs`;
|
|
1585
|
-
await api.addDoc(path, gradingStandard);
|
|
1586
|
-
};
|
|
1587
|
-
|
|
1588
|
-
// src/hooks/useActivityTracker.ts
|
|
1589
|
-
import { v4 as v42 } from "uuid";
|
|
1590
|
-
function useActivityTracker({ userId }) {
|
|
1591
|
-
const trackActivity = async ({
|
|
1592
|
-
activityName,
|
|
1593
|
-
activityType,
|
|
1594
|
-
id = v42(),
|
|
1595
|
-
language = ""
|
|
1596
|
-
}) => {
|
|
1597
|
-
if (userId) {
|
|
1598
|
-
const { doc, serverTimestamp, setDoc } = api.accessHelpers();
|
|
1599
|
-
const activityRef = doc(`users/${userId}/activity/${id}`);
|
|
1600
|
-
const timestamp = serverTimestamp();
|
|
1601
|
-
await setDoc(activityRef, {
|
|
1602
|
-
name: activityName,
|
|
1603
|
-
type: activityType,
|
|
1604
|
-
lastSeen: timestamp,
|
|
1605
|
-
id,
|
|
1606
|
-
language
|
|
1607
|
-
});
|
|
1608
|
-
}
|
|
1609
|
-
};
|
|
1610
|
-
return {
|
|
1611
|
-
trackActivity
|
|
1612
|
-
};
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
// src/hooks/useActivity.ts
|
|
1616
|
-
function useActivity({
|
|
1617
|
-
id,
|
|
1618
|
-
isAssignment,
|
|
1619
|
-
onAssignmentSubmitted,
|
|
1620
|
-
ltiData
|
|
1621
|
-
}) {
|
|
1622
|
-
var _a, _b;
|
|
1623
|
-
const { queryClient, user } = useSpeakableApi();
|
|
1624
|
-
const userId = user.auth.uid;
|
|
1625
|
-
const assignmentQuery = useAssignment({
|
|
1626
|
-
assignmentId: id,
|
|
1627
|
-
userId,
|
|
1628
|
-
enabled: isAssignment
|
|
1629
|
-
});
|
|
1630
|
-
const activeAssignment = assignmentQuery.data;
|
|
1631
|
-
const setId = isAssignment ? (_a = activeAssignment == null ? void 0 : activeAssignment.setId) != null ? _a : "" : id;
|
|
1632
|
-
const querySet = useSet({ setId });
|
|
1633
|
-
const setData = querySet.data;
|
|
1634
|
-
const assignmentContent = activeAssignment == null ? void 0 : activeAssignment.content;
|
|
1635
|
-
const assignmentWeights = activeAssignment == null ? void 0 : activeAssignment.weights;
|
|
1636
|
-
const setContent = setData == null ? void 0 : setData.content;
|
|
1637
|
-
const setWeights = setData == null ? void 0 : setData.weights;
|
|
1638
|
-
const contentCardsToUse = isAssignment ? assignmentContent != null ? assignmentContent : setContent : setContent;
|
|
1639
|
-
const weightsToUse = isAssignment ? assignmentWeights != null ? assignmentWeights : setWeights : setWeights;
|
|
1640
|
-
const activityId = isAssignment ? (_b = activeAssignment == null ? void 0 : activeAssignment.id) != null ? _b : "" : setId;
|
|
1641
|
-
const { cardsObject, cardsQueries, cards } = useCards({
|
|
1642
|
-
cardIds: contentCardsToUse != null ? contentCardsToUse : [],
|
|
1643
|
-
enabled: querySet.isSuccess,
|
|
1644
|
-
asObject: true
|
|
1645
|
-
});
|
|
1646
|
-
const scoreQuery = useScore({
|
|
1647
|
-
isAssignment,
|
|
1648
|
-
activityId: id,
|
|
1649
|
-
userId,
|
|
1650
|
-
courseId: activeAssignment == null ? void 0 : activeAssignment.courseId,
|
|
1651
|
-
googleClassroomUserId: user.profile.googleClassroomUserId,
|
|
1652
|
-
enabled: isAssignment ? assignmentQuery.isSuccess : querySet.isSuccess
|
|
1653
|
-
});
|
|
1654
|
-
const { mutationUpdateScore } = useUpdateScore();
|
|
1655
|
-
const { mutationUpdateCardScore } = useUpdateCardScore({
|
|
1656
|
-
activityId,
|
|
1657
|
-
isAssignment,
|
|
1658
|
-
userId,
|
|
1659
|
-
cardIds: contentCardsToUse != null ? contentCardsToUse : [],
|
|
1660
|
-
weights: weightsToUse != null ? weightsToUse : {}
|
|
1661
|
-
});
|
|
1662
|
-
const { mutationClearScore } = useClearScore();
|
|
1663
|
-
const { submitAssignmentScore: submitAssignmentScore2 } = useSubmitAssignmentScore({
|
|
1664
|
-
onAssignmentSubmitted,
|
|
1665
|
-
studentName: user.profile.displayName
|
|
1666
|
-
});
|
|
1667
|
-
const { submitPracticeScore: submitPracticeScore2 } = useSubmitPracticeScore();
|
|
1668
|
-
const handleUpdateScore = (data) => {
|
|
1669
|
-
mutationUpdateScore.mutate({
|
|
1670
|
-
data,
|
|
1671
|
-
isAssignment,
|
|
1672
|
-
activityId,
|
|
1673
|
-
userId
|
|
1674
|
-
});
|
|
1675
|
-
};
|
|
1676
|
-
const handleUpdateCardScore = (cardId, cardScore) => {
|
|
1677
|
-
mutationUpdateCardScore.mutate({ cardId, cardScore });
|
|
1678
|
-
if (cardScore.proficiency_level) {
|
|
1679
|
-
logGradingStandardEntry({
|
|
1680
|
-
type: cardScore.proficiency_level.standardId,
|
|
1681
|
-
cardId,
|
|
1682
|
-
gradingStandard: cardScore.proficiency_level
|
|
1683
|
-
});
|
|
1684
|
-
} else if (cardScore.wida || cardScore.actfl) {
|
|
1685
|
-
logGradingStandardEntry({
|
|
1686
|
-
type: cardScore.wida ? "wida" : "actfl",
|
|
1687
|
-
cardId,
|
|
1688
|
-
gradingStandard: cardScore.wida || cardScore.actfl || { level: "", justification: "" }
|
|
1689
|
-
});
|
|
1690
|
-
}
|
|
1691
|
-
};
|
|
1692
|
-
const onClearScore = ({
|
|
1693
|
-
cardId,
|
|
1694
|
-
wasCompleted = true
|
|
1695
|
-
}) => {
|
|
1696
|
-
var _a2, _b2;
|
|
1697
|
-
const currentCard = cardsObject == null ? void 0 : cardsObject[cardId];
|
|
1698
|
-
if ((currentCard == null ? void 0 : currentCard.type) === "MULTIPLE_CHOICE" /* MULTIPLE_CHOICE */ || (currentCard == null ? void 0 : currentCard.type) === "READ_REPEAT" /* READ_REPEAT */) {
|
|
1699
|
-
return;
|
|
1700
|
-
}
|
|
1701
|
-
const queryKeys = scoreQueryKeys.byId(activityId);
|
|
1702
|
-
const activeCardScores = (_b2 = (_a2 = queryClient.getQueryData(queryKeys)) == null ? void 0 : _a2.cards) == null ? void 0 : _b2[cardId];
|
|
1703
|
-
if (activeCardScores === void 0) return;
|
|
1704
|
-
mutationClearScore.mutate({
|
|
1705
|
-
isAssignment,
|
|
1706
|
-
activityId,
|
|
1707
|
-
cardScores: activeCardScores,
|
|
1708
|
-
cardId,
|
|
1709
|
-
userId
|
|
1710
|
-
});
|
|
1711
|
-
};
|
|
1712
|
-
const onSubmitScore = async () => {
|
|
1713
|
-
var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
|
|
1714
|
-
try {
|
|
1715
|
-
let results;
|
|
1716
|
-
if (isAssignment) {
|
|
1717
|
-
const cardScores = ((_a2 = scoreQuery.data) == null ? void 0 : _a2.cards) || {};
|
|
1718
|
-
const hasPendingReview = Object.values(cardScores).some(
|
|
1719
|
-
(cardScore) => cardScore.status === "pending_review"
|
|
1720
|
-
);
|
|
1721
|
-
results = await submitAssignmentScore2({
|
|
1722
|
-
assignment: {
|
|
1723
|
-
id: (_c = (_b2 = assignmentQuery.data) == null ? void 0 : _b2.id) != null ? _c : "",
|
|
1724
|
-
name: (_e = (_d = assignmentQuery.data) == null ? void 0 : _d.name) != null ? _e : "",
|
|
1725
|
-
owners: (_g = (_f = assignmentQuery.data) == null ? void 0 : _f.owners) != null ? _g : [],
|
|
1726
|
-
courseId: (_i = (_h = assignmentQuery.data) == null ? void 0 : _h.courseId) != null ? _i : "",
|
|
1727
|
-
courseWorkId: (_k = (_j = assignmentQuery.data) == null ? void 0 : _j.courseWorkId) != null ? _k : "",
|
|
1728
|
-
isAssessment: (_m = (_l = assignmentQuery.data) == null ? void 0 : _l.isAssessment) != null ? _m : false,
|
|
1729
|
-
maxPoints: (_o = (_n = assignmentQuery.data) == null ? void 0 : _n.maxPoints) != null ? _o : 0
|
|
1730
|
-
},
|
|
1731
|
-
userId,
|
|
1732
|
-
cardIds: contentCardsToUse != null ? contentCardsToUse : [],
|
|
1733
|
-
scores: scoreQuery.data,
|
|
1734
|
-
weights: weightsToUse != null ? weightsToUse : {},
|
|
1735
|
-
status: hasPendingReview ? "PENDING_REVIEW" : "FINALIZED"
|
|
1736
|
-
});
|
|
1737
|
-
if ((_p = assignmentQuery.data) == null ? void 0 : _p.ltiDeeplink) {
|
|
1738
|
-
submitLTIScore({
|
|
1739
|
-
maxPoints: (_q = assignmentQuery.data) == null ? void 0 : _q.maxPoints,
|
|
1740
|
-
score: (_s = (_r = scoreQuery.data) == null ? void 0 : _r.score) != null ? _s : 0,
|
|
1741
|
-
SERVICE_KEY: (_t = ltiData == null ? void 0 : ltiData.serviceKey) != null ? _t : "",
|
|
1742
|
-
lineItemId: (_u = ltiData == null ? void 0 : ltiData.lineItemId) != null ? _u : "",
|
|
1743
|
-
lti_id: (_v = ltiData == null ? void 0 : ltiData.lti_id) != null ? _v : ""
|
|
1744
|
-
});
|
|
1745
|
-
}
|
|
1746
|
-
} else {
|
|
1747
|
-
results = await submitPracticeScore2({
|
|
1748
|
-
setId: (_x = (_w = querySet.data) == null ? void 0 : _w.id) != null ? _x : "",
|
|
1749
|
-
userId,
|
|
1750
|
-
scores: scoreQuery.data
|
|
1751
|
-
});
|
|
1752
|
-
}
|
|
1753
|
-
return results;
|
|
1754
|
-
} catch (error) {
|
|
1755
|
-
return {
|
|
1756
|
-
success: false,
|
|
1757
|
-
error
|
|
1758
|
-
};
|
|
1759
|
-
}
|
|
1760
|
-
};
|
|
1761
|
-
const logGradingStandardEntry = ({
|
|
1762
|
-
cardId,
|
|
1763
|
-
gradingStandard,
|
|
1764
|
-
type
|
|
1765
|
-
}) => {
|
|
1766
|
-
var _a2, _b2, _c, _d, _e, _f, _g, _h, _i;
|
|
1767
|
-
const card = cardsObject == null ? void 0 : cardsObject[cardId];
|
|
1768
|
-
const scoresObject = queryClient.getQueryData(scoreQueryKeys.byId(activityId));
|
|
1769
|
-
const cardScore = (_a2 = scoresObject == null ? void 0 : scoresObject.cards) == null ? void 0 : _a2[cardId];
|
|
1770
|
-
const serverTimestamp = api.helpers.serverTimestamp;
|
|
1771
|
-
addGradingStandardLog(
|
|
1772
|
-
{
|
|
1773
|
-
assignmentId: (_b2 = activeAssignment == null ? void 0 : activeAssignment.id) != null ? _b2 : "",
|
|
1774
|
-
courseId: (_c = activeAssignment == null ? void 0 : activeAssignment.courseId) != null ? _c : "",
|
|
1775
|
-
teacherId: (_d = activeAssignment == null ? void 0 : activeAssignment.owners[0]) != null ? _d : "",
|
|
1776
|
-
setId: (_e = setData == null ? void 0 : setData.id) != null ? _e : "",
|
|
1777
|
-
cardId,
|
|
1778
|
-
level: gradingStandard.level,
|
|
1779
|
-
justification: gradingStandard.justification,
|
|
1780
|
-
transcript: (_f = cardScore == null ? void 0 : cardScore.transcript) != null ? _f : "",
|
|
1781
|
-
audioUrl: (_g = cardScore == null ? void 0 : cardScore.audio) != null ? _g : "",
|
|
1782
|
-
prompt: (_h = card == null ? void 0 : card.prompt) != null ? _h : "",
|
|
1783
|
-
responseType: (card == null ? void 0 : card.type) === "RESPOND_WRITE" /* RESPOND_WRITE */ ? "written" : "spoken",
|
|
1784
|
-
type,
|
|
1785
|
-
dateMade: serverTimestamp(),
|
|
1786
|
-
language: (_i = card == null ? void 0 : card.language) != null ? _i : ""
|
|
1787
|
-
},
|
|
1788
|
-
userId
|
|
1789
|
-
);
|
|
1790
|
-
};
|
|
1791
|
-
useEffect2(() => {
|
|
1792
|
-
if (isAssignment) {
|
|
1793
|
-
logOpenAssignment({ assignmentId: id });
|
|
1794
|
-
} else {
|
|
1795
|
-
logOpenActivityPreview({ setId: id });
|
|
1796
|
-
}
|
|
1797
|
-
}, []);
|
|
1798
|
-
useInitActivity({
|
|
1799
|
-
assignment: activeAssignment != null ? activeAssignment : void 0,
|
|
1800
|
-
set: setData != null ? setData : void 0,
|
|
1801
|
-
enabled: !!setData,
|
|
1802
|
-
userId
|
|
1803
|
-
});
|
|
1804
|
-
return {
|
|
1805
|
-
set: {
|
|
1806
|
-
data: setData,
|
|
1807
|
-
query: querySet
|
|
1808
|
-
},
|
|
1809
|
-
cards: {
|
|
1810
|
-
data: cardsObject,
|
|
1811
|
-
query: cardsQueries,
|
|
1812
|
-
cardsArray: cards
|
|
1813
|
-
},
|
|
1814
|
-
assignment: {
|
|
1815
|
-
data: isAssignment ? activeAssignment : void 0,
|
|
1816
|
-
query: assignmentQuery
|
|
1817
|
-
},
|
|
1818
|
-
scores: {
|
|
1819
|
-
data: scoreQuery.data,
|
|
1820
|
-
query: scoreQuery,
|
|
1821
|
-
actions: {
|
|
1822
|
-
update: handleUpdateScore,
|
|
1823
|
-
clear: onClearScore,
|
|
1824
|
-
submit: onSubmitScore,
|
|
1825
|
-
updateCard: handleUpdateCardScore,
|
|
1826
|
-
logGradingStandardEntry
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
1829
|
-
};
|
|
1830
|
-
}
|
|
1831
|
-
var useInitActivity = ({
|
|
1832
|
-
assignment,
|
|
1833
|
-
set,
|
|
1834
|
-
enabled,
|
|
1835
|
-
userId
|
|
1836
|
-
}) => {
|
|
1837
|
-
const { trackActivity } = useActivityTracker({ userId });
|
|
1838
|
-
const init = () => {
|
|
1839
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
1840
|
-
if (!enabled) return;
|
|
1841
|
-
if (!assignment) {
|
|
1842
|
-
trackActivity({
|
|
1843
|
-
activityName: (_a = set == null ? void 0 : set.name) != null ? _a : "",
|
|
1844
|
-
activityType: "set",
|
|
1845
|
-
id: set == null ? void 0 : set.id,
|
|
1846
|
-
language: set == null ? void 0 : set.language
|
|
1847
|
-
});
|
|
1848
|
-
} else if (assignment.name) {
|
|
1849
|
-
trackActivity({
|
|
1850
|
-
activityName: assignment.name,
|
|
1851
|
-
activityType: assignment.isAssessment ? "assessment" : "assignment",
|
|
1852
|
-
id: assignment.id,
|
|
1853
|
-
language: set == null ? void 0 : set.language
|
|
1854
|
-
});
|
|
1855
|
-
}
|
|
1856
|
-
if (set == null ? void 0 : set.public) {
|
|
1857
|
-
(_d = (_c = (_b = api).httpsCallable) == null ? void 0 : _c.call(_b, "onSetOpened")) == null ? void 0 : _d({
|
|
1858
|
-
setId: set.id,
|
|
1859
|
-
language: set.language
|
|
1860
|
-
});
|
|
1861
|
-
}
|
|
1862
|
-
(_g = (_f = (_e = api).httpsCallable) == null ? void 0 : _f.call(_e, "updateAlgoliaIndex")) == null ? void 0 : _g({
|
|
1863
|
-
updatePlays: true,
|
|
1864
|
-
objectID: set == null ? void 0 : set.id
|
|
1865
|
-
});
|
|
1866
|
-
};
|
|
1867
|
-
useEffect2(() => {
|
|
1868
|
-
init();
|
|
1869
|
-
}, [set]);
|
|
1870
|
-
};
|
|
1871
|
-
var submitLTIScore = async ({
|
|
1872
|
-
maxPoints,
|
|
1873
|
-
score,
|
|
1874
|
-
SERVICE_KEY,
|
|
1875
|
-
lineItemId,
|
|
1876
|
-
lti_id
|
|
1877
|
-
}) => {
|
|
1878
|
-
var _a, _b, _c;
|
|
1879
|
-
try {
|
|
1880
|
-
if (!SERVICE_KEY || !lineItemId || !lti_id) {
|
|
1881
|
-
throw new Error("Missing required LTI credentials");
|
|
1882
|
-
}
|
|
1883
|
-
const earnedPoints = score ? score / 100 * maxPoints : 0;
|
|
1884
|
-
const { data } = await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "submitLTIAssignmentScore")) == null ? void 0 : _c({
|
|
1885
|
-
SERVICE_KEY,
|
|
1886
|
-
scoreData: {
|
|
1887
|
-
lineItemId,
|
|
1888
|
-
userId: lti_id,
|
|
1889
|
-
maxPoints,
|
|
1890
|
-
earnedPoints
|
|
1891
|
-
}
|
|
1892
|
-
}));
|
|
1893
|
-
return { success: true, data };
|
|
1894
|
-
} catch (error) {
|
|
1895
|
-
console.error("Failed to submit LTI score:", error);
|
|
1896
|
-
return {
|
|
1897
|
-
success: false,
|
|
1898
|
-
error: error instanceof Error ? error : new Error("Unknown error occurred")
|
|
1899
|
-
};
|
|
1900
|
-
}
|
|
1901
|
-
};
|
|
1902
|
-
|
|
1903
|
-
// src/hooks/useActivityFeedbackAccess.ts
|
|
1904
|
-
import { useQuery as useQuery5 } from "@tanstack/react-query";
|
|
1905
|
-
var activityFeedbackAccessQueryKeys = {
|
|
1906
|
-
activityFeedbackAccess: (args) => ["activityFeedbackAccess", ...Object.values(args)]
|
|
1907
|
-
};
|
|
1908
|
-
var useActivityFeedbackAccess = ({
|
|
1909
|
-
aiEnabled = false,
|
|
1910
|
-
isActivityRoute = false
|
|
1911
|
-
}) => {
|
|
1912
|
-
var _a, _b, _c;
|
|
1913
|
-
const { user } = useSpeakableApi();
|
|
1914
|
-
const uid = user.auth.uid;
|
|
1915
|
-
const isTeacher = (_a = user.profile) == null ? void 0 : _a.isTeacher;
|
|
1916
|
-
const isStudent = (_b = user.profile) == null ? void 0 : _b.isStudent;
|
|
1917
|
-
const userRoles = ((_c = user.profile) == null ? void 0 : _c.roles) || [];
|
|
1918
|
-
const query = useQuery5({
|
|
1919
|
-
queryKey: activityFeedbackAccessQueryKeys.activityFeedbackAccess({
|
|
1920
|
-
aiEnabled,
|
|
1921
|
-
isActivityRoute
|
|
1922
|
-
}),
|
|
1923
|
-
queryFn: async () => {
|
|
1924
|
-
var _a2, _b2, _c2;
|
|
1925
|
-
if (!uid) {
|
|
1926
|
-
return {
|
|
1927
|
-
canAccessFeedback: false,
|
|
1928
|
-
reason: "Missing user ID",
|
|
1929
|
-
isUnlimited: false,
|
|
1930
|
-
accessType: "none"
|
|
1931
|
-
};
|
|
1932
|
-
}
|
|
1933
|
-
try {
|
|
1934
|
-
if (aiEnabled) {
|
|
1935
|
-
return {
|
|
1936
|
-
canAccessFeedback: true,
|
|
1937
|
-
reason: "AI feedback enabled",
|
|
1938
|
-
isUnlimited: true,
|
|
1939
|
-
accessType: "ai_enabled"
|
|
1940
|
-
};
|
|
1941
|
-
}
|
|
1942
|
-
if (isTeacher || userRoles.includes("ADMIN")) {
|
|
1943
|
-
return {
|
|
1944
|
-
canAccessFeedback: true,
|
|
1945
|
-
reason: "Teacher preview access",
|
|
1946
|
-
isUnlimited: true,
|
|
1947
|
-
accessType: "teacher_preview"
|
|
1948
|
-
};
|
|
1949
|
-
}
|
|
1950
|
-
if (isStudent && isActivityRoute) {
|
|
1951
|
-
try {
|
|
1952
|
-
const result = await ((_c2 = (_b2 = (_a2 = api).httpsCallable) == null ? void 0 : _b2.call(_a2, "checkStudentTeacherPlan")) == null ? void 0 : _c2({
|
|
1953
|
-
studentId: uid
|
|
1954
|
-
}));
|
|
1955
|
-
const planCheckResult = result.data;
|
|
1956
|
-
if (planCheckResult.canAccessFeedback) {
|
|
1957
|
-
return {
|
|
1958
|
-
canAccessFeedback: true,
|
|
1959
|
-
reason: planCheckResult.reason || "Student access via teacher with active plan",
|
|
1960
|
-
isUnlimited: planCheckResult.hasTeacherWithUnlimitedAccess,
|
|
1961
|
-
accessType: "student_with_teacher_plan"
|
|
1962
|
-
};
|
|
1963
|
-
} else {
|
|
1964
|
-
return {
|
|
1965
|
-
canAccessFeedback: false,
|
|
1966
|
-
reason: planCheckResult.reason || "No teacher with active plan found",
|
|
1967
|
-
isUnlimited: false,
|
|
1968
|
-
accessType: "none"
|
|
1969
|
-
};
|
|
1970
|
-
}
|
|
1971
|
-
} catch (error) {
|
|
1972
|
-
console.error("Error checking student teacher plan:", error);
|
|
1973
|
-
return {
|
|
1974
|
-
canAccessFeedback: false,
|
|
1975
|
-
reason: "Error checking teacher plans",
|
|
1976
|
-
isUnlimited: false,
|
|
1977
|
-
accessType: "none"
|
|
1978
|
-
};
|
|
1979
|
-
}
|
|
1980
|
-
}
|
|
1981
|
-
return {
|
|
1982
|
-
canAccessFeedback: false,
|
|
1983
|
-
reason: "No access permissions found for current context",
|
|
1984
|
-
isUnlimited: false,
|
|
1985
|
-
accessType: "none"
|
|
1986
|
-
};
|
|
1987
|
-
} catch (error) {
|
|
1988
|
-
console.error("Error checking activity feedback access:", error);
|
|
1989
|
-
return {
|
|
1990
|
-
canAccessFeedback: false,
|
|
1991
|
-
reason: "Error checking access permissions",
|
|
1992
|
-
isUnlimited: false,
|
|
1993
|
-
accessType: "none"
|
|
1994
|
-
};
|
|
1995
|
-
}
|
|
1996
|
-
},
|
|
1997
|
-
enabled: !!uid,
|
|
1998
|
-
staleTime: 5 * 60 * 1e3,
|
|
1999
|
-
// 5 minutes
|
|
2000
|
-
gcTime: 10 * 60 * 1e3
|
|
2001
|
-
// 10 minutes
|
|
2002
|
-
});
|
|
2003
|
-
return {
|
|
2004
|
-
...query
|
|
2005
|
-
};
|
|
2006
|
-
};
|
|
2007
|
-
|
|
2008
|
-
// src/constants/all-langs.json
|
|
2009
|
-
var all_langs_default = {
|
|
2010
|
-
af: "Afrikaans",
|
|
2011
|
-
sq: "Albanian",
|
|
2012
|
-
am: "Amharic",
|
|
2013
|
-
ar: "Arabic",
|
|
2014
|
-
hy: "Armenian",
|
|
2015
|
-
az: "Azerbaijani",
|
|
2016
|
-
eu: "Basque",
|
|
2017
|
-
be: "Belarusian",
|
|
2018
|
-
bn: "Bengali",
|
|
2019
|
-
bs: "Bosnian",
|
|
2020
|
-
bg: "Bulgarian",
|
|
2021
|
-
ca: "Catalan",
|
|
2022
|
-
ceb: "Cebuano",
|
|
2023
|
-
zh: "Chinese",
|
|
2024
|
-
co: "Corsican",
|
|
2025
|
-
hr: "Croatian",
|
|
2026
|
-
cs: "Czech",
|
|
2027
|
-
da: "Danish",
|
|
2028
|
-
nl: "Dutch",
|
|
2029
|
-
en: "English",
|
|
2030
|
-
eo: "Esperanto",
|
|
2031
|
-
et: "Estonian",
|
|
2032
|
-
fi: "Finnish",
|
|
2033
|
-
fr: "French",
|
|
2034
|
-
fy: "Frisian",
|
|
2035
|
-
gl: "Galician",
|
|
2036
|
-
ka: "Georgian",
|
|
2037
|
-
de: "German",
|
|
2038
|
-
el: "Greek",
|
|
2039
|
-
gu: "Gujarati",
|
|
2040
|
-
ht: "Haitian Creole",
|
|
2041
|
-
ha: "Hausa",
|
|
2042
|
-
haw: "Hawaiian",
|
|
2043
|
-
he: "Hebrew",
|
|
2044
|
-
hi: "Hindi",
|
|
2045
|
-
hmn: "Hmong",
|
|
2046
|
-
hu: "Hungarian",
|
|
2047
|
-
is: "Icelandic",
|
|
2048
|
-
ig: "Igbo",
|
|
2049
|
-
id: "Indonesian",
|
|
2050
|
-
ga: "Irish",
|
|
2051
|
-
it: "Italian",
|
|
2052
|
-
ja: "Japanese",
|
|
2053
|
-
jv: "Javanese",
|
|
2054
|
-
kn: "Kannada",
|
|
2055
|
-
kk: "Kazakh",
|
|
2056
|
-
km: "Khmer",
|
|
2057
|
-
ko: "Korean",
|
|
2058
|
-
ku: "Kurdish",
|
|
2059
|
-
ky: "Kyrgyz",
|
|
2060
|
-
lo: "Lao",
|
|
2061
|
-
la: "Latin",
|
|
2062
|
-
lv: "Latvian",
|
|
2063
|
-
lt: "Lithuanian",
|
|
2064
|
-
lb: "Luxembourgish",
|
|
2065
|
-
mk: "Macedonian",
|
|
2066
|
-
mg: "Malagasy",
|
|
2067
|
-
ms: "Malay",
|
|
2068
|
-
ml: "Malayalam",
|
|
2069
|
-
mt: "Maltese",
|
|
2070
|
-
mi: "Maori",
|
|
2071
|
-
mr: "Marathi",
|
|
2072
|
-
mn: "Mongolian",
|
|
2073
|
-
my: "Myanmar (Burmese)",
|
|
2074
|
-
ne: "Nepali",
|
|
2075
|
-
no: "Norwegian",
|
|
2076
|
-
ny: "Nyanja (Chichewa)",
|
|
2077
|
-
ps: "Pashto",
|
|
2078
|
-
fa: "Persian",
|
|
2079
|
-
pl: "Polish",
|
|
2080
|
-
pt: "Portuguese",
|
|
2081
|
-
pa: "Punjabi",
|
|
2082
|
-
ro: "Romanian",
|
|
2083
|
-
ru: "Russian",
|
|
2084
|
-
sm: "Samoan",
|
|
2085
|
-
gd: "Scots Gaelic",
|
|
2086
|
-
sr: "Serbian",
|
|
2087
|
-
st: "Sesotho",
|
|
2088
|
-
sn: "Shona",
|
|
2089
|
-
sd: "Sindhi",
|
|
2090
|
-
si: "Sinhala (Sinhalese)",
|
|
2091
|
-
sk: "Slovak",
|
|
2092
|
-
sl: "Slovenian",
|
|
2093
|
-
so: "Somali",
|
|
2094
|
-
es: "Spanish",
|
|
2095
|
-
su: "Sundanese",
|
|
2096
|
-
sw: "Swahili",
|
|
2097
|
-
sv: "Swedish",
|
|
2098
|
-
tl: "Tagalog (Filipino)",
|
|
2099
|
-
tg: "Tajik",
|
|
2100
|
-
ta: "Tamil",
|
|
2101
|
-
te: "Telugu",
|
|
2102
|
-
th: "Thai",
|
|
2103
|
-
tr: "Turkish",
|
|
2104
|
-
uk: "Ukrainian",
|
|
2105
|
-
ur: "Urdu",
|
|
2106
|
-
uz: "Uzbek",
|
|
2107
|
-
vi: "Vietnamese",
|
|
2108
|
-
cy: "Welsh",
|
|
2109
|
-
xh: "Xhosa",
|
|
2110
|
-
yi: "Yiddish",
|
|
2111
|
-
yo: "Yoruba",
|
|
2112
|
-
zu: "Zulu"
|
|
2113
|
-
};
|
|
2114
|
-
|
|
2115
|
-
// src/utils/ai/get-respond-card-tool.ts
|
|
2116
|
-
var getRespondCardTool = ({
|
|
2117
|
-
language,
|
|
2118
|
-
standard = "actfl"
|
|
2119
|
-
}) => {
|
|
2120
|
-
const lang = all_langs_default[language] || "English";
|
|
2121
|
-
const tool = {
|
|
2122
|
-
tool_choice: {
|
|
2123
|
-
type: "function",
|
|
2124
|
-
function: { name: "get_feedback" }
|
|
2125
|
-
},
|
|
2126
|
-
tools: [
|
|
2127
|
-
{
|
|
2128
|
-
type: "function",
|
|
2129
|
-
function: {
|
|
2130
|
-
name: "get_feedback",
|
|
2131
|
-
description: "Get feedback on a student's response",
|
|
2132
|
-
parameters: {
|
|
2133
|
-
type: "object",
|
|
2134
|
-
required: [
|
|
2135
|
-
"success",
|
|
2136
|
-
"score",
|
|
2137
|
-
"score_justification",
|
|
2138
|
-
"errors",
|
|
2139
|
-
"improvedResponse",
|
|
2140
|
-
"compliments"
|
|
2141
|
-
],
|
|
2142
|
-
properties: {
|
|
2143
|
-
success: {
|
|
2144
|
-
type: "boolean",
|
|
2145
|
-
description: "Mark true if the student's response was on-topic and generally demonstrated understanding. A few grammar mistakes are acceptable. Mark false if the student's response was off-topic or did not demonstrate understanding."
|
|
2146
|
-
},
|
|
2147
|
-
errors: {
|
|
2148
|
-
type: "array",
|
|
2149
|
-
items: {
|
|
2150
|
-
type: "object",
|
|
2151
|
-
required: ["error", "grammar_error_type", "correction", "justification"],
|
|
2152
|
-
properties: {
|
|
2153
|
-
error: {
|
|
2154
|
-
type: "string",
|
|
2155
|
-
description: "The grammatical error in the student's response."
|
|
2156
|
-
},
|
|
2157
|
-
correction: {
|
|
2158
|
-
type: "string",
|
|
2159
|
-
description: "The suggested correction to the error"
|
|
2160
|
-
},
|
|
2161
|
-
justification: {
|
|
2162
|
-
type: "string",
|
|
2163
|
-
description: `An explanation of the rationale behind the suggested correction. WRITE THIS IN ${lang}!`
|
|
2164
|
-
},
|
|
2165
|
-
grammar_error_type: {
|
|
2166
|
-
type: "string",
|
|
2167
|
-
enum: [
|
|
2168
|
-
"subjVerbAgree",
|
|
2169
|
-
"tenseErrors",
|
|
2170
|
-
"articleMisuse",
|
|
2171
|
-
"prepositionErrors",
|
|
2172
|
-
"adjNounAgree",
|
|
2173
|
-
"pronounErrors",
|
|
2174
|
-
"wordOrder",
|
|
2175
|
-
"verbConjugation",
|
|
2176
|
-
"pluralization",
|
|
2177
|
-
"negationErrors",
|
|
2178
|
-
"modalVerbMisuse",
|
|
2179
|
-
"relativeClause",
|
|
2180
|
-
"auxiliaryVerb",
|
|
2181
|
-
"complexSentenceAgreement",
|
|
2182
|
-
"idiomaticExpression",
|
|
2183
|
-
"registerInconsistency",
|
|
2184
|
-
"voiceMisuse"
|
|
2185
|
-
],
|
|
2186
|
-
description: "The type of grammatical error found. It should be one of the following categories: subject-verb agreement, tense errors, article misuse, preposition errors, adjective-noun agreement, pronoun errors, word order, verb conjugation, pluralization errors, negation errors, modal verb misuse, relative clause errors, auxiliary verb misuse, complex sentence agreement, idiomatic expression, register inconsistency, or voice misuse"
|
|
2187
|
-
}
|
|
2188
|
-
}
|
|
2189
|
-
},
|
|
2190
|
-
description: "An array of objects, each representing a grammatical error in the student's response. Each object should have the following properties: error, grammar_error_type, correction, and justification. If there were no errors, return an empty array."
|
|
2191
|
-
},
|
|
2192
|
-
compliments: {
|
|
2193
|
-
type: "array",
|
|
2194
|
-
items: {
|
|
2195
|
-
type: "string"
|
|
2196
|
-
},
|
|
2197
|
-
description: `An array of strings, each representing something the student did well. Each string should be WRITTEN IN ${lang}!`
|
|
2198
|
-
},
|
|
2199
|
-
improvedResponse: {
|
|
2200
|
-
type: "string",
|
|
2201
|
-
description: "An improved response with proper grammar and more detail, if applicable."
|
|
2202
|
-
},
|
|
2203
|
-
score: {
|
|
2204
|
-
type: "number",
|
|
2205
|
-
description: "A score between 0 and 100, reflecting the overall quality of the response"
|
|
2206
|
-
},
|
|
2207
|
-
score_justification: {
|
|
2208
|
-
type: "string",
|
|
2209
|
-
description: "An explanation of the rationale behind the assigned score, considering both accuracy and fluency"
|
|
2210
|
-
}
|
|
2211
|
-
}
|
|
2212
|
-
}
|
|
2213
|
-
}
|
|
2214
|
-
}
|
|
2215
|
-
]
|
|
2216
|
-
};
|
|
2217
|
-
if (standard === "wida") {
|
|
2218
|
-
const wida_level = {
|
|
2219
|
-
type: "number",
|
|
2220
|
-
enum: [1, 2, 3, 4, 5, 6],
|
|
2221
|
-
description: `The student's WIDA (World-Class Instructional Design and Assessment) proficiency level. Choose one of the following options: 1, 2, 3, 4, 5, 6 which corresponds to
|
|
2222
|
-
|
|
2223
|
-
1 - Entering
|
|
2224
|
-
2 - Emerging
|
|
2225
|
-
3 - Developing
|
|
2226
|
-
4 - Expanding
|
|
2227
|
-
5 - Bridging
|
|
2228
|
-
6 - Reaching
|
|
2229
|
-
|
|
2230
|
-
This is an estimate based on the level of the student's response. Use the descriptions of the WIDA speaking standards to guide your decision.
|
|
2231
|
-
`
|
|
2232
|
-
};
|
|
2233
|
-
const wida_justification = {
|
|
2234
|
-
type: "string",
|
|
2235
|
-
description: `An explanation of the rationale behind the assigned WIDA level of the response, considering both accuracy and fluency. WRITE THIS IN ENGLISH!`
|
|
2236
|
-
};
|
|
2237
|
-
tool.tools[0].function.parameters.required.push("wida_level");
|
|
2238
|
-
tool.tools[0].function.parameters.required.push("wida_justification");
|
|
2239
|
-
tool.tools[0].function.parameters.properties.wida_level = wida_level;
|
|
2240
|
-
tool.tools[0].function.parameters.properties.wida_justification = wida_justification;
|
|
2241
|
-
} else {
|
|
2242
|
-
const actfl_level = {
|
|
2243
|
-
type: "string",
|
|
2244
|
-
enum: ["NL", "NM", "NH", "IL", "IM", "IH", "AL", "AM", "AH", "S", "D"],
|
|
2245
|
-
description: "The student's ACTFL (American Council on the Teaching of Foreign Languages) proficiency level. Choose one of the following options: NL, NM, NH, IL, IM, IH, AL, AM, AH, S, or D"
|
|
2246
|
-
};
|
|
2247
|
-
const actfl_justification = {
|
|
2248
|
-
type: "string",
|
|
2249
|
-
description: "An explanation of the rationale behind the assigned ACTFL level, considering both accuracy and fluency"
|
|
2250
|
-
};
|
|
2251
|
-
tool.tools[0].function.parameters.required.push("actfl_level");
|
|
2252
|
-
tool.tools[0].function.parameters.required.push("actfl_justification");
|
|
2253
|
-
tool.tools[0].function.parameters.properties.actfl_level = actfl_level;
|
|
2254
|
-
tool.tools[0].function.parameters.properties.actfl_justification = actfl_justification;
|
|
2255
|
-
}
|
|
2256
|
-
return tool;
|
|
2257
|
-
};
|
|
2258
|
-
|
|
2259
|
-
// src/hooks/useOpenAI.ts
|
|
2260
|
-
var useBaseOpenAI = ({
|
|
2261
|
-
onTranscriptSuccess,
|
|
2262
|
-
onTranscriptError,
|
|
2263
|
-
onCompletionSuccess,
|
|
2264
|
-
onCompletionError,
|
|
2265
|
-
aiEnabled,
|
|
2266
|
-
submitAudioResponse,
|
|
2267
|
-
uploadAudioAndGetTranscript,
|
|
2268
|
-
onGetAudioUrlAndTranscript
|
|
2269
|
-
}) => {
|
|
2270
|
-
const { user, queryClient } = useSpeakableApi();
|
|
2271
|
-
const currentUserId = user.auth.uid;
|
|
2272
|
-
const { data: feedbackAccess } = useActivityFeedbackAccess({
|
|
2273
|
-
aiEnabled
|
|
2274
|
-
});
|
|
2275
|
-
const getTranscript = async (audioUrl, language) => {
|
|
2276
|
-
var _a, _b;
|
|
2277
|
-
try {
|
|
2278
|
-
const getAssemblyAITranscript = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "transcribeAssemblyAIAudio");
|
|
2279
|
-
const response = await (getAssemblyAITranscript == null ? void 0 : getAssemblyAITranscript({
|
|
2280
|
-
audioUrl,
|
|
2281
|
-
language
|
|
2282
|
-
}));
|
|
2283
|
-
const transcript = response == null ? void 0 : response.data;
|
|
2284
|
-
return transcript;
|
|
2285
|
-
} catch (error) {
|
|
2286
|
-
console.log("error", error);
|
|
2287
|
-
onTranscriptError({
|
|
2288
|
-
type: "TRANSCRIPT",
|
|
2289
|
-
message: (error == null ? void 0 : error.message) || "Error getting transcript"
|
|
2290
|
-
});
|
|
2291
|
-
throw new Error(error);
|
|
2292
|
-
}
|
|
2293
|
-
};
|
|
2294
|
-
const getFreeResponseCompletion = async (messages, isFreeResponse, feedbackLanguage, gradingStandard = "actfl") => {
|
|
2295
|
-
var _a, _b, _c, _d, _e;
|
|
2296
|
-
const responseTool = getRespondCardTool({
|
|
2297
|
-
language: feedbackLanguage,
|
|
2298
|
-
standard: gradingStandard
|
|
2299
|
-
});
|
|
2300
|
-
try {
|
|
2301
|
-
const createChatCompletion = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "createChatCompletion");
|
|
2302
|
-
const {
|
|
2303
|
-
data: {
|
|
2304
|
-
response,
|
|
2305
|
-
prompt_tokens = 0,
|
|
2306
|
-
completion_tokens = 0,
|
|
2307
|
-
success: aiSuccess = false
|
|
2308
|
-
// the AI was able to generate a response
|
|
2309
|
-
}
|
|
2310
|
-
} = await (createChatCompletion == null ? void 0 : createChatCompletion({
|
|
2311
|
-
chat: {
|
|
2312
|
-
model: isFreeResponse ? "gpt-4-1106-preview" : "gpt-3.5-turbo-1106",
|
|
2313
|
-
messages,
|
|
2314
|
-
temperature: 0.7,
|
|
2315
|
-
...responseTool
|
|
2316
|
-
},
|
|
2317
|
-
type: isFreeResponse ? "LONG_RESPONSE" : "SHORT_RESPONSE"
|
|
2318
|
-
}));
|
|
2319
|
-
const functionArguments = JSON.parse(((_e = (_d = (_c = response == null ? void 0 : response.tool_calls) == null ? void 0 : _c[0]) == null ? void 0 : _d.function) == null ? void 0 : _e.arguments) || "{}");
|
|
2320
|
-
const result = {
|
|
2321
|
-
...functionArguments,
|
|
2322
|
-
prompt_tokens,
|
|
2323
|
-
completion_tokens,
|
|
2324
|
-
aiSuccess
|
|
2325
|
-
};
|
|
2326
|
-
onCompletionSuccess(result);
|
|
2327
|
-
return result;
|
|
2328
|
-
} catch (error) {
|
|
2329
|
-
onCompletionError({
|
|
2330
|
-
type: "COMPLETION",
|
|
2331
|
-
message: (error == null ? void 0 : error.message) || "Error getting completion"
|
|
2332
|
-
});
|
|
2333
|
-
throw new Error(error);
|
|
2334
|
-
}
|
|
2335
|
-
};
|
|
2336
|
-
const getFeedback = async ({
|
|
2337
|
-
cardId,
|
|
2338
|
-
language = "en",
|
|
2339
|
-
// required
|
|
2340
|
-
writtenResponse = null,
|
|
2341
|
-
// if the type = RESPOND_WRITE
|
|
2342
|
-
audio = null,
|
|
2343
|
-
autoGrade = true,
|
|
2344
|
-
file = null
|
|
2345
|
-
}) => {
|
|
2346
|
-
try {
|
|
2347
|
-
if (!(feedbackAccess == null ? void 0 : feedbackAccess.canAccessFeedback)) {
|
|
2348
|
-
const result = {
|
|
2349
|
-
noFeedbackAvailable: true,
|
|
2350
|
-
success: true,
|
|
2351
|
-
reason: (feedbackAccess == null ? void 0 : feedbackAccess.reason) || "No feedback access",
|
|
2352
|
-
accessType: (feedbackAccess == null ? void 0 : feedbackAccess.accessType) || "none"
|
|
2353
|
-
};
|
|
2354
|
-
onCompletionSuccess(result);
|
|
2355
|
-
return result;
|
|
2356
|
-
}
|
|
2357
|
-
let transcript;
|
|
2358
|
-
let audioUrl = void 0;
|
|
2359
|
-
if (writtenResponse) {
|
|
2360
|
-
transcript = writtenResponse;
|
|
2361
|
-
onTranscriptSuccess(writtenResponse);
|
|
2362
|
-
} else if (typeof audio === "string" && file) {
|
|
2363
|
-
if (feedbackAccess == null ? void 0 : feedbackAccess.canAccessFeedback) {
|
|
2364
|
-
transcript = await getTranscript(audio, language);
|
|
2365
|
-
audioUrl = audio;
|
|
2366
|
-
onTranscriptSuccess(transcript);
|
|
2367
|
-
} else {
|
|
2368
|
-
console.info(
|
|
2369
|
-
`Transcript not available: ${(feedbackAccess == null ? void 0 : feedbackAccess.reason) || "No feedback access"}`
|
|
2370
|
-
);
|
|
2371
|
-
}
|
|
2372
|
-
} else {
|
|
2373
|
-
const response = await uploadAudioAndGetTranscript(audio || "", language);
|
|
2374
|
-
transcript = response.transcript;
|
|
2375
|
-
audioUrl = response.audioUrl;
|
|
2376
|
-
}
|
|
2377
|
-
onGetAudioUrlAndTranscript == null ? void 0 : onGetAudioUrlAndTranscript({ transcript, audioUrl });
|
|
2378
|
-
if (feedbackAccess == null ? void 0 : feedbackAccess.canAccessFeedback) {
|
|
2379
|
-
const results = await getAIResponse({
|
|
2380
|
-
cardId,
|
|
2381
|
-
transcript: transcript || ""
|
|
2382
|
-
});
|
|
2383
|
-
let output = results;
|
|
2384
|
-
if (!autoGrade) {
|
|
2385
|
-
output = {
|
|
2386
|
-
...output,
|
|
2387
|
-
noFeedbackAvailable: true,
|
|
2388
|
-
success: true
|
|
2389
|
-
};
|
|
2390
|
-
}
|
|
2391
|
-
onCompletionSuccess(output);
|
|
2392
|
-
return output;
|
|
2393
|
-
} else {
|
|
2394
|
-
const result = {
|
|
2395
|
-
noFeedbackAvailable: true,
|
|
2396
|
-
success: true,
|
|
2397
|
-
reason: (feedbackAccess == null ? void 0 : feedbackAccess.reason) || "No feedback access",
|
|
2398
|
-
accessType: (feedbackAccess == null ? void 0 : feedbackAccess.accessType) || "none"
|
|
2399
|
-
};
|
|
2400
|
-
onCompletionSuccess(result);
|
|
2401
|
-
return result;
|
|
2402
|
-
}
|
|
2403
|
-
} catch (error) {
|
|
2404
|
-
console.error("Error getting feedback:", error);
|
|
2405
|
-
throw new Error(error);
|
|
2406
|
-
}
|
|
2407
|
-
};
|
|
2408
|
-
const getAIResponse = async ({ cardId, transcript }) => {
|
|
2409
|
-
var _a, _b, _c, _d, _e;
|
|
2410
|
-
try {
|
|
2411
|
-
const getGeminiFeedback = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "callGetFeedback");
|
|
2412
|
-
const getProficiencyEstimate = (_d = (_c = api).httpsCallable) == null ? void 0 : _d.call(_c, "getProficiencyEstimate");
|
|
2413
|
-
const card = getCardFromCache({
|
|
2414
|
-
cardId,
|
|
2415
|
-
queryClient
|
|
2416
|
-
});
|
|
2417
|
-
let feedbackData;
|
|
2418
|
-
let proficiencyData = {};
|
|
2419
|
-
if (card && card.grading_method === "manual") {
|
|
2420
|
-
} else if (card && card.grading_method !== "standards_based") {
|
|
2421
|
-
const [geminiResult, proficiencyResult] = await Promise.all([
|
|
2422
|
-
getGeminiFeedback == null ? void 0 : getGeminiFeedback({
|
|
2423
|
-
cardId,
|
|
2424
|
-
studentId: currentUserId,
|
|
2425
|
-
studentResponse: transcript
|
|
2426
|
-
}),
|
|
2427
|
-
getProficiencyEstimate == null ? void 0 : getProficiencyEstimate({
|
|
2428
|
-
cardId,
|
|
2429
|
-
studentId: currentUserId,
|
|
2430
|
-
studentResponse: transcript
|
|
2431
|
-
})
|
|
2432
|
-
]);
|
|
2433
|
-
proficiencyData = (proficiencyResult == null ? void 0 : proficiencyResult.data) || {};
|
|
2434
|
-
feedbackData = {
|
|
2435
|
-
...(_e = geminiResult == null ? void 0 : geminiResult.data) != null ? _e : {},
|
|
2436
|
-
// @ts-ignore
|
|
2437
|
-
proficiency_level: (proficiencyData == null ? void 0 : proficiencyData.proficiency_level) || null
|
|
2438
|
-
};
|
|
2439
|
-
} else {
|
|
2440
|
-
const geminiResult = await (getGeminiFeedback == null ? void 0 : getGeminiFeedback({
|
|
2441
|
-
cardId,
|
|
2442
|
-
studentId: currentUserId,
|
|
2443
|
-
studentResponse: transcript
|
|
2444
|
-
}));
|
|
2445
|
-
feedbackData = geminiResult == null ? void 0 : geminiResult.data;
|
|
2446
|
-
}
|
|
2447
|
-
const results = {
|
|
2448
|
-
...feedbackData,
|
|
2449
|
-
// ...proficiencyData,
|
|
2450
|
-
aiSuccess: true,
|
|
2451
|
-
promptSuccess: (feedbackData == null ? void 0 : feedbackData.success) || false,
|
|
2452
|
-
transcript
|
|
2453
|
-
};
|
|
2454
|
-
return results;
|
|
2455
|
-
} catch (error) {
|
|
2456
|
-
onCompletionError({
|
|
2457
|
-
type: "AI_FEEDBACK",
|
|
2458
|
-
message: (error == null ? void 0 : error.message) || "Error getting ai feedback"
|
|
2459
|
-
});
|
|
2460
|
-
throw new Error(error);
|
|
2461
|
-
}
|
|
2462
|
-
};
|
|
2463
|
-
return {
|
|
2464
|
-
submitAudioResponse,
|
|
2465
|
-
uploadAudioAndGetTranscript,
|
|
2466
|
-
getTranscript,
|
|
2467
|
-
getFreeResponseCompletion,
|
|
2468
|
-
getFeedback
|
|
2469
|
-
};
|
|
2470
|
-
};
|
|
2471
|
-
|
|
2472
|
-
// src/hooks/useUserPlan.ts
|
|
2473
|
-
function useUserPlan() {
|
|
2474
|
-
const { permissions } = useSpeakableApi();
|
|
2475
|
-
const checkIsTeacherProPlan = (plan) => plan === SpeakablePlanTypes.teacher_pro || plan === SpeakablePlanTypes.starter;
|
|
2476
|
-
const checkIsOrganizationPlan = (plan) => plan === SpeakablePlanTypes.organization || plan === SpeakablePlanTypes.professional || plan === SpeakablePlanTypes.growth;
|
|
2477
|
-
const checkIsSchoolStarter = (plan) => plan === SpeakablePlanTypes.school_starter;
|
|
2478
|
-
const checkIsFreePlan = (plan) => plan === SpeakablePlanTypes.basic || !plan;
|
|
2479
|
-
return {
|
|
2480
|
-
userPlan: permissions.plan,
|
|
2481
|
-
loaded: !!permissions.plan,
|
|
2482
|
-
isFreePlan: checkIsFreePlan(permissions.plan),
|
|
2483
|
-
isTeacherProPlan: checkIsTeacherProPlan(permissions.plan),
|
|
2484
|
-
isOrganizationPlan: checkIsOrganizationPlan(permissions.plan),
|
|
2485
|
-
isSchoolStarter: checkIsSchoolStarter(permissions.plan),
|
|
2486
|
-
checkIsFreePlan,
|
|
2487
|
-
checkIsTeacherProPlan,
|
|
2488
|
-
checkIsOrganizationPlan
|
|
2489
|
-
};
|
|
2490
|
-
}
|
|
2491
|
-
|
|
2492
|
-
// src/hooks/useOrganizationAccess.ts
|
|
2493
|
-
import { useQuery as useQuery6 } from "@tanstack/react-query";
|
|
2494
|
-
var useOrganizationAccess = () => {
|
|
2495
|
-
const { user } = useSpeakableApi();
|
|
2496
|
-
const email = user.auth.email;
|
|
2497
|
-
const query = useQuery6({
|
|
2498
|
-
queryKey: ["organizationAccess", email],
|
|
2499
|
-
queryFn: async () => {
|
|
2500
|
-
if (!email) {
|
|
2501
|
-
return {
|
|
2502
|
-
hasUnlimitedAccess: false,
|
|
2503
|
-
subscriptionId: null,
|
|
2504
|
-
organizationId: null,
|
|
2505
|
-
organizationName: null,
|
|
2506
|
-
subscriptionEndDate: null,
|
|
2507
|
-
accessType: "individual"
|
|
2508
|
-
};
|
|
2509
|
-
}
|
|
2510
|
-
return getOrganizationAccess(email);
|
|
2511
|
-
},
|
|
2512
|
-
enabled: !!email,
|
|
2513
|
-
// Only run query if we have a user email
|
|
2514
|
-
staleTime: 5 * 60 * 1e3,
|
|
2515
|
-
// Consider data fresh for 5 minutes
|
|
2516
|
-
gcTime: 10 * 60 * 1e3,
|
|
2517
|
-
// Keep in cache for 10 minutes
|
|
2518
|
-
retry: 2
|
|
2519
|
-
// Retry failed requests twice
|
|
2520
|
-
});
|
|
2521
|
-
return {
|
|
2522
|
-
...query
|
|
2523
|
-
};
|
|
2524
|
-
};
|
|
2525
|
-
var getOrganizationAccess = async (email) => {
|
|
2526
|
-
const { limit, where } = api.accessQueryConstraints();
|
|
2527
|
-
try {
|
|
2528
|
-
const organizationSnapshot = await api.getDocs(
|
|
2529
|
-
"organizations",
|
|
2530
|
-
where("members", "array-contains", email),
|
|
2531
|
-
where("masterSubscriptionStatus", "==", "active"),
|
|
2532
|
-
limit(1)
|
|
2533
|
-
);
|
|
2534
|
-
if (!organizationSnapshot.empty) {
|
|
2535
|
-
const orgData = organizationSnapshot.data[0];
|
|
2536
|
-
return {
|
|
2537
|
-
hasUnlimitedAccess: true,
|
|
2538
|
-
subscriptionId: orgData == null ? void 0 : orgData.masterSubscriptionId,
|
|
2539
|
-
organizationId: orgData.id,
|
|
2540
|
-
organizationName: orgData.name || "Unknown Organization",
|
|
2541
|
-
subscriptionEndDate: orgData.masterSubscriptionEndDate || null,
|
|
2542
|
-
accessType: "organization"
|
|
2543
|
-
};
|
|
2544
|
-
}
|
|
2545
|
-
const institutionSnapshot = await api.getDocs(
|
|
2546
|
-
"institution_subscriptions",
|
|
2547
|
-
where("users", "array-contains", email),
|
|
2548
|
-
where("active", "==", true),
|
|
2549
|
-
limit(1)
|
|
2550
|
-
);
|
|
2551
|
-
if (!institutionSnapshot.empty) {
|
|
2552
|
-
const institutionData = institutionSnapshot.data[0];
|
|
2553
|
-
const isUnlimited = (institutionData == null ? void 0 : institutionData.plan) === "organization" || (institutionData == null ? void 0 : institutionData.plan) === "school_starter";
|
|
2554
|
-
return {
|
|
2555
|
-
hasUnlimitedAccess: isUnlimited,
|
|
2556
|
-
subscriptionId: institutionData.id,
|
|
2557
|
-
organizationId: institutionData == null ? void 0 : institutionData.institutionId,
|
|
2558
|
-
organizationName: institutionData.name || institutionData.institutionId || "Legacy Institution",
|
|
2559
|
-
subscriptionEndDate: institutionData.endDate || null,
|
|
2560
|
-
accessType: "institution_subscriptions"
|
|
2561
|
-
};
|
|
2562
|
-
}
|
|
2563
|
-
return {
|
|
2564
|
-
hasUnlimitedAccess: false,
|
|
2565
|
-
subscriptionId: null,
|
|
2566
|
-
organizationId: null,
|
|
2567
|
-
organizationName: null,
|
|
2568
|
-
subscriptionEndDate: null,
|
|
2569
|
-
accessType: "individual"
|
|
2570
|
-
};
|
|
2571
|
-
} catch (error) {
|
|
2572
|
-
console.error("Error checking organization access:", error);
|
|
2573
|
-
return {
|
|
2574
|
-
hasUnlimitedAccess: false,
|
|
2575
|
-
subscriptionId: null,
|
|
2576
|
-
organizationId: null,
|
|
2577
|
-
organizationName: null,
|
|
2578
|
-
subscriptionEndDate: null,
|
|
2579
|
-
accessType: "individual"
|
|
2580
|
-
};
|
|
2581
|
-
}
|
|
2582
|
-
};
|
|
2583
|
-
|
|
2584
|
-
// src/hooks/useUpdateStudentVoc.ts
|
|
2585
|
-
var useUpdateStudentVocab = (page) => {
|
|
2586
|
-
const { user } = useSpeakableApi();
|
|
2587
|
-
const currentUserId = user == null ? void 0 : user.auth.uid;
|
|
2588
|
-
if (!page || !currentUserId || !page.target_text || !page.language) {
|
|
2589
|
-
return {
|
|
2590
|
-
studentVocabMarkVoiceSuccess: void 0,
|
|
2591
|
-
studentVocabMarkVoiceFail: void 0
|
|
2592
|
-
};
|
|
2593
|
-
}
|
|
2594
|
-
const getDataObject = () => {
|
|
2595
|
-
var _a, _b;
|
|
2596
|
-
const { serverTimestamp } = api.accessHelpers();
|
|
2597
|
-
const language = (_a = page.language) != null ? _a : "en";
|
|
2598
|
-
const word = (_b = page.target_text) != null ? _b : "";
|
|
2599
|
-
const phrase_length = getPhraseLength(word);
|
|
2600
|
-
const wordHash = getWordHash(word, language);
|
|
2601
|
-
const docPath = `users/${currentUserId}/vocab/${wordHash}`;
|
|
2602
|
-
const communityPath = `checked-pronunciations/${wordHash}`;
|
|
2603
|
-
const id = `${language}-${cleanString(word)}`;
|
|
2604
|
-
const data = {
|
|
2605
|
-
id,
|
|
2606
|
-
word,
|
|
2607
|
-
words: (word == null ? void 0 : word.split(" ")) || [],
|
|
2608
|
-
wordHash,
|
|
2609
|
-
language,
|
|
2610
|
-
lastSeen: serverTimestamp(),
|
|
2611
|
-
phrase_length
|
|
2612
|
-
};
|
|
2613
|
-
return {
|
|
2614
|
-
docPath,
|
|
2615
|
-
communityPath,
|
|
2616
|
-
data
|
|
2617
|
-
};
|
|
2618
|
-
};
|
|
2619
|
-
const markVoiceSuccess = async () => {
|
|
2620
|
-
const { docPath, communityPath, data } = getDataObject();
|
|
2621
|
-
const { increment } = api.accessQueryConstraints();
|
|
2622
|
-
const { serverTimestamp } = api.accessHelpers();
|
|
2623
|
-
data.voiceSuccess = increment(1);
|
|
2624
|
-
try {
|
|
2625
|
-
await api.updateDoc(docPath, data);
|
|
2626
|
-
} catch (error) {
|
|
2627
|
-
if (error instanceof Error && error.message === "not-found") {
|
|
2628
|
-
data.firstSeen = serverTimestamp();
|
|
2629
|
-
await api.setDoc(docPath, data, { merge: true });
|
|
2630
|
-
} else {
|
|
2631
|
-
console.log(error);
|
|
2632
|
-
}
|
|
2633
|
-
}
|
|
2634
|
-
try {
|
|
2635
|
-
data.pronunciations = increment(1);
|
|
2636
|
-
await api.setDoc(communityPath, data, { merge: true });
|
|
2637
|
-
} catch (error) {
|
|
2638
|
-
console.log(error);
|
|
2639
|
-
}
|
|
2640
|
-
};
|
|
2641
|
-
const markVoiceFail = async () => {
|
|
2642
|
-
const { docPath, communityPath, data } = getDataObject();
|
|
2643
|
-
const { increment } = api.accessQueryConstraints();
|
|
2644
|
-
const { serverTimestamp } = api.accessHelpers();
|
|
2645
|
-
data.voiceFail = increment(1);
|
|
2646
|
-
try {
|
|
2647
|
-
await api.updateDoc(docPath, data);
|
|
2648
|
-
} catch (error) {
|
|
2649
|
-
if (error instanceof Error && error.message === "not-found") {
|
|
2650
|
-
data.firstSeen = serverTimestamp();
|
|
2651
|
-
await api.setDoc(docPath, data, { merge: true });
|
|
2652
|
-
} else {
|
|
2653
|
-
console.log(error);
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
try {
|
|
2657
|
-
data.fails = increment(1);
|
|
2658
|
-
await api.setDoc(communityPath, data, { merge: true });
|
|
2659
|
-
} catch (error) {
|
|
2660
|
-
console.log(error);
|
|
2661
|
-
}
|
|
2662
|
-
};
|
|
2663
|
-
return {
|
|
2664
|
-
studentVocabMarkVoiceSuccess: markVoiceSuccess,
|
|
2665
|
-
studentVocabMarkVoiceFail: markVoiceFail
|
|
2666
|
-
};
|
|
2667
|
-
};
|
|
2668
|
-
export {
|
|
2669
|
-
activityFeedbackAccessQueryKeys,
|
|
2670
|
-
assignmentQueryKeys,
|
|
2671
|
-
cardsQueryKeys,
|
|
2672
|
-
getCardFromCache,
|
|
2673
|
-
getSetFromCache,
|
|
2674
|
-
scoreQueryKeys,
|
|
2675
|
-
setsQueryKeys,
|
|
2676
|
-
updateCardInCache,
|
|
2677
|
-
updateSetInCache,
|
|
2678
|
-
useActivity,
|
|
2679
|
-
useActivityFeedbackAccess,
|
|
2680
|
-
useAssignment,
|
|
2681
|
-
useBaseOpenAI,
|
|
2682
|
-
useCards,
|
|
2683
|
-
useClearScore,
|
|
2684
|
-
useCreateCard,
|
|
2685
|
-
useCreateCards,
|
|
2686
|
-
useCreateNotification,
|
|
2687
|
-
useGetCard,
|
|
2688
|
-
useGoogleClassroom,
|
|
2689
|
-
useOrganizationAccess,
|
|
2690
|
-
useScore,
|
|
2691
|
-
useSet,
|
|
2692
|
-
useSubmitAssignmentScore,
|
|
2693
|
-
useSubmitPracticeScore,
|
|
2694
|
-
useUpdateCardScore,
|
|
2695
|
-
useUpdateScore,
|
|
2696
|
-
useUpdateStudentVocab,
|
|
2697
|
-
useUserPlan
|
|
2698
|
-
};
|
|
2699
|
-
//# sourceMappingURL=hooks.js.map
|