@speakableio/core 0.1.99 → 0.1.101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,37 +1,26 @@
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 SpeakableProvider({
6
- user,
7
- children,
8
- queryClient,
9
- permissions,
10
- fsClient
11
- }) {
12
- const [speakableApi, setSpeakableApi] = useState(null);
13
- useEffect(() => {
14
- setSpeakableApi(fsClient);
15
- }, [fsClient]);
16
- if (!speakableApi) return null;
17
- return /* @__PURE__ */ jsx(
18
- FsCtx.Provider,
19
- {
20
- value: {
21
- speakableApi,
22
- queryClient,
23
- user,
24
- permissions
25
- },
26
- children
27
- }
28
- );
29
- }
30
- function useSpeakableApi() {
31
- const ctx = useContext(FsCtx);
32
- if (!ctx) throw new Error("useSpeakableApi must be used within a SpeakableProvider");
33
- return ctx;
34
- }
1
+ // src/lib/create-firebase-client-native.ts
2
+ import {
3
+ getDoc,
4
+ getDocs,
5
+ addDoc,
6
+ setDoc,
7
+ updateDoc,
8
+ deleteDoc,
9
+ runTransaction,
10
+ writeBatch,
11
+ doc,
12
+ collection,
13
+ query,
14
+ serverTimestamp,
15
+ orderBy,
16
+ limit,
17
+ startAt,
18
+ startAfter,
19
+ endAt,
20
+ endBefore,
21
+ where,
22
+ increment
23
+ } from "@react-native-firebase/firestore";
35
24
 
36
25
  // src/utils/error-handler.ts
37
26
  var ServiceError = class extends Error {
@@ -339,2859 +328,126 @@ var createAssignmentRepo = () => {
339
328
  };
340
329
  };
341
330
 
342
- // src/domains/assignment/hooks/assignment.hooks.ts
343
- import { useQuery } from "@tanstack/react-query";
344
- var assignmentQueryKeys = {
345
- all: ["assignments"],
346
- byId: (id) => [...assignmentQueryKeys.all, id],
347
- list: () => [...assignmentQueryKeys.all, "list"]
348
- };
349
- function useAssignment({
350
- assignmentId,
351
- enabled = true,
352
- analyticType,
353
- userId
354
- }) {
355
- const { speakableApi } = useSpeakableApi();
356
- return useQuery({
357
- queryKey: assignmentQueryKeys.byId(assignmentId),
358
- queryFn: () => speakableApi.assignmentRepo.getAssignment({
359
- assignmentId,
360
- analyticType,
361
- currentUserId: userId
362
- }),
363
- enabled
364
- });
365
- }
366
-
367
- // src/domains/assignment/hooks/score-hooks.ts
368
- import { useMutation, useQuery as useQuery2 } from "@tanstack/react-query";
369
-
370
- // src/utils/debounce.utils.ts
371
- function debounce(func, waitFor) {
372
- let timeoutId;
373
- return (...args) => new Promise((resolve, reject) => {
374
- if (timeoutId) {
375
- clearTimeout(timeoutId);
376
- }
377
- timeoutId = setTimeout(async () => {
378
- try {
379
- const result = await func(...args);
380
- resolve(result);
381
- } catch (error) {
382
- reject(error);
383
- }
384
- }, waitFor);
385
- });
386
- }
387
-
388
- // src/lib/tanstack/handle-optimistic-update-query.ts
389
- var handleOptimisticUpdate = async ({
390
- queryClient,
391
- queryKey,
392
- newData
393
- }) => {
394
- await queryClient.cancelQueries({
395
- queryKey
396
- });
397
- const previousData = queryClient.getQueryData(queryKey);
398
- if (previousData === void 0) {
399
- queryClient.setQueryData(queryKey, newData);
400
- } else {
401
- queryClient.setQueryData(queryKey, { ...previousData, ...newData });
402
- }
403
- return { previousData };
404
- };
405
-
406
- // src/constants/speakable-plans.ts
407
- var FEEDBACK_PLANS = {
408
- FEEDBACK_TRANSCRIPT: "FEEDBACK_TRANSCRIPT",
409
- // Transcript from the audio
410
- FEEDBACK_SUMMARY: "FEEDBACK_SUMMARY",
411
- // Chatty summary (Free plan)
412
- FEEDBACK_GRAMMAR_INSIGHTS: "FEEDBACK_GRAMMAR_INSIGHTS",
413
- // Grammar insights
414
- FEEDBACK_SUGGESTED_RESPONSE: "FEEDBACK_SUGGESTED_RESPONSE",
415
- // Suggested Response
416
- FEEDBACK_RUBRIC: "FEEDBACK_RUBRIC",
417
- // Suggested Response
418
- FEEDBACK_GRADING_STANDARDS: "FEEDBACK_GRADING_STANDARDS",
419
- // ACTFL / WIDA Estimate
420
- FEEDBACK_TARGET_LANGUAGE: "FEEDBACK_TARGET_LANGUAGE",
421
- // Ability to set the feedback language to the target language of the student
422
- FEEDBACK_DISABLE_ALLOW_RETRIES: "FEEDBACK_DISABLE_ALLOW_RETRIES"
423
- // Turn of allow retries
424
- };
425
- var AUTO_GRADING_PLANS = {
426
- AUTO_GRADING_PASS_FAIL: "AUTO_GRADING_PASS_FAIL",
427
- // Pass / fail grading
428
- AUTO_GRADING_RUBRICS: "AUTO_GRADING_RUBRICS",
429
- // Autograded rubrics
430
- AUTO_GRADING_STANDARDS_BASED: "AUTO_GRADING_STANDARDS_BASED"
431
- // Standards based grading
432
- };
433
- var AI_ASSISTANT_PLANS = {
434
- AI_ASSISTANT_DOCUMENT_UPLOADS: "AI_ASSISTANT_DOCUMENT_UPLOADS",
435
- // Allow document uploading
436
- AI_ASSISTANT_UNLIMITED_USE: "AI_ASSISTANT_UNLIMITED_USE"
437
- // Allow unlimited use of AI assistant. Otherwise, limits are used.
438
- };
439
- var ASSIGNMENT_SETTINGS_PLANS = {
440
- ASSESSMENTS: "ASSESSMENTS",
441
- // Ability to create assessment assignment types
442
- GOOGLE_CLASSROOM_GRADE_PASSBACK: "GOOGLE_CLASSROOM_GRADE_PASSBACK"
443
- // Assignment scores can sync with classroom
444
- };
445
- var ANALYTICS_PLANS = {
446
- ANALYTICS_GRADEBOOK: "ANALYTICS_GRADEBOOK",
447
- // Access to the gradebook page
448
- ANALYTICS_CLASSROOM_ANALYTICS: "ANALYTICS_CLASSROOM_ANALYTICS",
449
- // Access to the classroom analytics page
450
- ANALYTICS_STUDENT_PROGRESS_REPORTS: "ANALYTICS_STUDENT_PROGRESS_REPORTS",
451
- // Access to the panel that shows an individual student's progress and assignments
452
- ANALYTICS_ASSIGNMENT_RESULTS: "ANALYTICS_ASSIGNMENT_RESULTS",
453
- // Access to the assigment RESULTS page
454
- ANALYTICS_ORGANIZATION: "ANALYTICS_ORGANIZATION"
455
- // Access to the organization analytics panel (for permitted admins)
456
- };
457
- var SPACES_PLANS = {
458
- SPACES_CREATE_SPACE: "SPACES_CREATE_SPACE",
459
- // Ability to create spaces
460
- SPACES_CHECK_POINTS: "SPACES_CHECK_POINTS"
461
- // Feature not available yet. Ability to create checkpoints for spaces for data aggregation
462
- };
463
- var DISCOVER_PLANS = {
464
- DISCOVER_ORGANIZATION_LIBRARY: "DISCOVER_ORGANIZATION_LIBRARY"
465
- // Access to the organizations shared library
466
- };
467
- var MEDIA_AREA_PLANS = {
468
- MEDIA_AREA_DOCUMENT_UPLOAD: "MEDIA_AREA_DOCUMENT_UPLOAD",
469
- MEDIA_AREA_AUDIO_FILES: "MEDIA_AREA_AUDIO_FILES"
470
- };
471
- var FREE_PLAN = [];
472
- var TEACHER_PRO_PLAN = [
473
- FEEDBACK_PLANS.FEEDBACK_TRANSCRIPT,
474
- FEEDBACK_PLANS.FEEDBACK_SUMMARY,
475
- FEEDBACK_PLANS.FEEDBACK_TARGET_LANGUAGE,
476
- AUTO_GRADING_PLANS.AUTO_GRADING_PASS_FAIL,
477
- ANALYTICS_PLANS.ANALYTICS_GRADEBOOK,
478
- SPACES_PLANS.SPACES_CREATE_SPACE
479
- // AUTO_GRADING_PLANS.AUTO_GRADING_STANDARDS_BASED,
480
- ];
481
- var SCHOOL_STARTER = [
482
- FEEDBACK_PLANS.FEEDBACK_TRANSCRIPT,
483
- FEEDBACK_PLANS.FEEDBACK_SUMMARY,
484
- FEEDBACK_PLANS.FEEDBACK_GRAMMAR_INSIGHTS,
485
- FEEDBACK_PLANS.FEEDBACK_SUGGESTED_RESPONSE,
486
- FEEDBACK_PLANS.FEEDBACK_RUBRIC,
487
- FEEDBACK_PLANS.FEEDBACK_GRADING_STANDARDS,
488
- FEEDBACK_PLANS.FEEDBACK_DISABLE_ALLOW_RETRIES,
489
- FEEDBACK_PLANS.FEEDBACK_TARGET_LANGUAGE,
490
- AUTO_GRADING_PLANS.AUTO_GRADING_PASS_FAIL,
491
- AUTO_GRADING_PLANS.AUTO_GRADING_RUBRICS,
492
- AUTO_GRADING_PLANS.AUTO_GRADING_STANDARDS_BASED,
493
- AI_ASSISTANT_PLANS.AI_ASSISTANT_DOCUMENT_UPLOADS,
494
- AI_ASSISTANT_PLANS.AI_ASSISTANT_UNLIMITED_USE,
495
- // ASSIGNMENT_SETTINGS_PLANS.ASSESSMENTS,
496
- ASSIGNMENT_SETTINGS_PLANS.GOOGLE_CLASSROOM_GRADE_PASSBACK,
497
- ANALYTICS_PLANS.ANALYTICS_GRADEBOOK,
498
- ANALYTICS_PLANS.ANALYTICS_STUDENT_PROGRESS_REPORTS,
499
- ANALYTICS_PLANS.ANALYTICS_CLASSROOM_ANALYTICS,
500
- // ANALYTICS_PLANS.ANALYTICS_ORGANIZATION,
501
- SPACES_PLANS.SPACES_CREATE_SPACE,
502
- SPACES_PLANS.SPACES_CHECK_POINTS,
503
- // DISCOVER_PLANS.DISCOVER_ORGANIZATION_LIBRARY,
504
- MEDIA_AREA_PLANS.MEDIA_AREA_DOCUMENT_UPLOAD,
505
- MEDIA_AREA_PLANS.MEDIA_AREA_AUDIO_FILES
506
- ];
507
- var ORGANIZATION_PLAN = [
508
- FEEDBACK_PLANS.FEEDBACK_TRANSCRIPT,
509
- FEEDBACK_PLANS.FEEDBACK_SUMMARY,
510
- FEEDBACK_PLANS.FEEDBACK_GRAMMAR_INSIGHTS,
511
- FEEDBACK_PLANS.FEEDBACK_SUGGESTED_RESPONSE,
512
- FEEDBACK_PLANS.FEEDBACK_RUBRIC,
513
- FEEDBACK_PLANS.FEEDBACK_GRADING_STANDARDS,
514
- FEEDBACK_PLANS.FEEDBACK_DISABLE_ALLOW_RETRIES,
515
- FEEDBACK_PLANS.FEEDBACK_TARGET_LANGUAGE,
516
- AUTO_GRADING_PLANS.AUTO_GRADING_PASS_FAIL,
517
- AUTO_GRADING_PLANS.AUTO_GRADING_RUBRICS,
518
- AUTO_GRADING_PLANS.AUTO_GRADING_STANDARDS_BASED,
519
- AI_ASSISTANT_PLANS.AI_ASSISTANT_DOCUMENT_UPLOADS,
520
- AI_ASSISTANT_PLANS.AI_ASSISTANT_UNLIMITED_USE,
521
- ASSIGNMENT_SETTINGS_PLANS.ASSESSMENTS,
522
- ASSIGNMENT_SETTINGS_PLANS.GOOGLE_CLASSROOM_GRADE_PASSBACK,
523
- ANALYTICS_PLANS.ANALYTICS_GRADEBOOK,
524
- ANALYTICS_PLANS.ANALYTICS_STUDENT_PROGRESS_REPORTS,
525
- ANALYTICS_PLANS.ANALYTICS_CLASSROOM_ANALYTICS,
526
- ANALYTICS_PLANS.ANALYTICS_ORGANIZATION,
527
- SPACES_PLANS.SPACES_CREATE_SPACE,
528
- SPACES_PLANS.SPACES_CHECK_POINTS,
529
- DISCOVER_PLANS.DISCOVER_ORGANIZATION_LIBRARY,
530
- MEDIA_AREA_PLANS.MEDIA_AREA_DOCUMENT_UPLOAD,
531
- MEDIA_AREA_PLANS.MEDIA_AREA_AUDIO_FILES
532
- ];
533
- var SpeakablePlanTypes = {
534
- basic: "basic",
535
- teacher_pro: "teacher_pro",
536
- school_starter: "school_starter",
537
- organization: "organization",
538
- // OLD PLANS
539
- starter: "starter",
540
- growth: "growth",
541
- professional: "professional"
542
- };
543
- var SpeakablePermissionsMap = {
544
- [SpeakablePlanTypes.basic]: FREE_PLAN,
545
- [SpeakablePlanTypes.starter]: TEACHER_PRO_PLAN,
546
- [SpeakablePlanTypes.teacher_pro]: TEACHER_PRO_PLAN,
547
- [SpeakablePlanTypes.growth]: ORGANIZATION_PLAN,
548
- [SpeakablePlanTypes.professional]: ORGANIZATION_PLAN,
549
- [SpeakablePlanTypes.organization]: ORGANIZATION_PLAN,
550
- [SpeakablePlanTypes.school_starter]: SCHOOL_STARTER
551
- };
552
- var SpeakablePlanHierarchy = [
553
- SpeakablePlanTypes.basic,
554
- SpeakablePlanTypes.starter,
555
- SpeakablePlanTypes.teacher_pro,
556
- SpeakablePlanTypes.growth,
557
- SpeakablePlanTypes.professional,
558
- SpeakablePlanTypes.school_starter,
559
- SpeakablePlanTypes.organization
560
- ];
331
+ // src/domains/cards/services/create-card.service.ts
332
+ import { v4 } from "uuid";
561
333
 
562
- // src/hooks/usePermissions.ts
563
- var usePermissions = () => {
564
- const { permissions } = useSpeakableApi();
565
- const has = (permission) => {
566
- var _a;
567
- return (_a = permissions.permissions) == null ? void 0 : _a.includes(permission);
568
- };
569
- return {
570
- plan: permissions.plan,
571
- permissionsLoaded: permissions.loaded,
572
- isStripePlan: permissions.isStripePlan,
573
- refreshDate: permissions.refreshDate,
574
- isInstitutionPlan: permissions.isInstitutionPlan,
575
- subscriptionId: permissions.subscriptionId,
576
- contact: permissions.contact,
577
- hasGradebook: has(ANALYTICS_PLANS.ANALYTICS_GRADEBOOK),
578
- hasGoogleClassroomGradePassback: has(ASSIGNMENT_SETTINGS_PLANS.GOOGLE_CLASSROOM_GRADE_PASSBACK),
579
- hasAssessments: has(ASSIGNMENT_SETTINGS_PLANS.ASSESSMENTS),
580
- hasSectionAnalytics: has(ANALYTICS_PLANS.ANALYTICS_CLASSROOM_ANALYTICS),
581
- hasStudentReports: has(ANALYTICS_PLANS.ANALYTICS_STUDENT_PROGRESS_REPORTS),
582
- permissions: permissions || [],
583
- hasStudentPortfolios: permissions.hasStudentPortfolios,
584
- isFreeOrgTrial: permissions.type === "free_org_trial",
585
- freeOrgTrialExpired: permissions.freeOrgTrialExpired
586
- };
334
+ // src/domains/cards/card.constants.ts
335
+ var CARDS_COLLECTION = "flashcards";
336
+ var refsCardsFiresotre = {
337
+ allCards: CARDS_COLLECTION,
338
+ card: (id) => `${CARDS_COLLECTION}/${id}`
587
339
  };
588
- var usePermissions_default = usePermissions;
589
340
 
590
- // src/domains/notification/notification.constants.ts
591
- var SPEAKABLE_NOTIFICATIONS = {
592
- NEW_ASSIGNMENT: "new_assignment",
593
- ASSESSMENT_SUBMITTED: "assessment_submitted",
594
- ASSESSMENT_SCORED: "assessment_scored",
595
- NEW_COMMENT: "NEW_COMMENT"
341
+ // src/utils/text-utils.ts
342
+ import sha1 from "js-sha1";
343
+ var purify = (word) => {
344
+ 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();
596
345
  };
597
- var SpeakableNotificationTypes = {
598
- NEW_ASSIGNMENT: "NEW_ASSIGNMENT",
599
- FEEDBACK_FROM_TEACHER: "FEEDBACK_FROM_TEACHER",
600
- MESSAGE_FROM_STUDENT: "MESSAGE_FROM_STUDENT",
601
- PHRASE_MARKED_CORRECT: "PHRASE_MARKED_CORRECT",
602
- STUDENT_PROGRESS: "STUDENT_PROGRESS",
603
- PLAYLIST_FOLLOWERS: "PLAYLIST_FOLLOWERS",
604
- PLAYLIST_PLAYS: "PLAYLIST_PLAYS",
605
- // New notifications
606
- ASSESSMENT_SUBMITTED: "ASSESSMENT_SUBMITTED",
607
- // Notification FOR TEACHER when student submits assessment
608
- ASSESSMENT_SCORED: "ASSESSMENT_SCORED",
609
- // Notification FOR STUDENT when teacher scores assessment
610
- // Comment
611
- NEW_COMMENT: "NEW_COMMENT"
346
+ var cleanString = (words) => {
347
+ const splitWords = words == null ? void 0 : words.split("+");
348
+ if (splitWords && splitWords.length === 1) {
349
+ const newWord = purify(words);
350
+ return newWord;
351
+ } else if (splitWords && splitWords.length > 1) {
352
+ const split = splitWords.map((w) => purify(w));
353
+ return split;
354
+ } else {
355
+ return "";
356
+ }
612
357
  };
613
-
614
- // src/domains/notification/services/create-notification.service.ts
615
- import dayjs2 from "dayjs";
616
-
617
- // src/constants/web.constants.ts
618
- var WEB_BASE_URL = "https://app.speakable.io";
619
-
620
- // src/domains/notification/services/send-notification.service.ts
621
- var _sendNotification = async (sendTo, notification) => {
622
- var _a, _b, _c;
623
- const results = await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "createNotificationV2")) == null ? void 0 : _c({
624
- sendTo,
625
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
626
- notification
627
- }));
628
- return results;
358
+ var getWordHash = (word, language) => {
359
+ const cleanedWord = cleanString(word);
360
+ const wordHash = sha1(`${language}-${cleanedWord}`);
361
+ console.log("wordHash core library", wordHash);
362
+ return wordHash;
629
363
  };
630
- var sendNotification = withErrorHandler(_sendNotification, "sendNotification");
631
364
 
632
- // src/domains/notification/services/create-notification.service.ts
633
- var createNotification = async ({
634
- data,
635
- type,
636
- userId,
637
- profile
638
- }) => {
639
- let result;
640
- switch (type) {
641
- case SPEAKABLE_NOTIFICATIONS.NEW_ASSIGNMENT:
642
- result = await handleAssignNotifPromise({ data, profile });
643
- break;
644
- case SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SUBMITTED:
645
- result = await createAssessmentSubmissionNotification({
646
- data,
647
- profile,
648
- userId
649
- });
650
- break;
651
- case SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SCORED:
652
- result = await createAssessmentScoredNotification({
653
- data,
654
- profile
655
- });
656
- break;
657
- default:
658
- result = null;
659
- break;
365
+ // src/domains/cards/services/get-card-verification-status.service.ts
366
+ var charactarLanguages = ["zh", "ja", "ko"];
367
+ var getVerificationStatus = async (target_text, language) => {
368
+ if ((target_text == null ? void 0 : target_text.length) < 3 && !charactarLanguages.includes(language)) {
369
+ return "NOT_RECOMMENDED" /* NOT_RECOMMENDED */;
660
370
  }
661
- return result;
662
- };
663
- var handleAssignNotifPromise = async ({
664
- data: assignments,
665
- profile
666
- }) => {
667
- if (!assignments.length) return;
371
+ const hash = getWordHash(target_text, language);
372
+ const response = await api.getDoc(`checked-pronunciations/${hash}`);
668
373
  try {
669
- const notifsPromises = assignments.map(async (assignment) => {
670
- const {
671
- section,
672
- section: { members },
673
- ...rest
674
- } = assignment;
675
- if (!section || !members) throw new Error("Invalid assignment data");
676
- const data = { section, sendTo: members, assignment: { ...rest } };
677
- return createNewAssignmentNotification({ data, profile });
678
- });
679
- await Promise.all(notifsPromises);
680
- return {
681
- success: true,
682
- message: "Assignment notifications sent successfully"
683
- };
684
- } catch (error) {
685
- console.error("Error in handleAssignNotifPromise:", error);
686
- throw error;
687
- }
688
- };
689
- var createNewAssignmentNotification = async ({
690
- data,
691
- profile
692
- }) => {
693
- var _a;
694
- const { assignment, sendTo } = data;
695
- const teacherName = profile.displayName || "Your teacher";
696
- const dueDate = assignment.dueDateTimestamp ? dayjs2(assignment.dueDateTimestamp.toDate()).format("MMM Do") : null;
697
- const results = await sendNotification(sendTo, {
698
- courseId: assignment.courseId,
699
- type: SPEAKABLE_NOTIFICATIONS.NEW_ASSIGNMENT,
700
- senderName: teacherName,
701
- link: `${WEB_BASE_URL}/assignment/${assignment.id}`,
702
- messagePreview: `A new assignment "${assignment.name}" is now available. ${dueDate ? `Due ${dueDate}` : ""}`,
703
- title: "New Assignment Available!",
704
- imageUrl: (_a = profile.image) == null ? void 0 : _a.url
705
- });
706
- return results;
707
- };
708
- var createAssessmentSubmissionNotification = async ({
709
- data: assignment,
710
- profile,
711
- userId
712
- }) => {
713
- var _a;
714
- const studentName = profile.displayName || "Your student";
715
- const results = await sendNotification(assignment.owners, {
716
- courseId: assignment.courseId,
717
- type: SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SUBMITTED,
718
- link: `${WEB_BASE_URL}/a/${assignment.id}?studentId=${userId}`,
719
- title: `Assessment Submitted!`,
720
- senderName: studentName,
721
- messagePreview: `${studentName} has submitted the assessment "${assignment.name}"`,
722
- imageUrl: (_a = profile.image) == null ? void 0 : _a.url
723
- });
724
- return results;
725
- };
726
- var createAssessmentScoredNotification = async ({
727
- data,
728
- profile
729
- }) => {
730
- var _a, _b, _c, _d, _e;
731
- const { assignment, sendTo } = data;
732
- const teacherName = profile.displayName || "Your teacher";
733
- const title = `${assignment.isAssessment ? "Assessment" : "Assignment"} Reviewed!`;
734
- const messagePreview = `Your ${assignment.isAssessment ? "assessment" : "assignment"} has been reviewed by your teacher. Click to view the feedback.`;
735
- const results = await sendNotification(sendTo, {
736
- courseId: assignment.courseId,
737
- type: SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SCORED,
738
- link: `${WEB_BASE_URL}/assignment/${assignment.id}`,
739
- title,
740
- messagePreview,
741
- imageUrl: (_a = profile.image) == null ? void 0 : _a.url,
742
- senderName: teacherName
743
- });
744
- await ((_e = (_c = (_b = api).httpsCallable) == null ? void 0 : _c.call(_b, "sendAssessmentScoredEmail")) == null ? void 0 : _e({
745
- assessmentTitle: assignment.name,
746
- link: `${WEB_BASE_URL}/assignment/${assignment.id}`,
747
- senderImage: ((_d = profile.image) == null ? void 0 : _d.url) || "",
748
- studentId: sendTo[0],
749
- teacherName: profile.displayName
750
- }));
751
- return results;
752
- };
753
-
754
- // src/domains/notification/hooks/notification.hooks.ts
755
- var notificationQueryKeys = {
756
- all: ["notifications"],
757
- byId: (id) => [...notificationQueryKeys.all, id]
758
- };
759
- var useCreateNotification = () => {
760
- const { user, queryClient } = useSpeakableApi();
761
- const handleCreateNotifications = async (type, data) => {
762
- var _a, _b;
763
- const result = await createNotification({
764
- type,
765
- userId: user.auth.uid,
766
- profile: (_a = user == null ? void 0 : user.profile) != null ? _a : {},
767
- data
768
- });
769
- queryClient.invalidateQueries({
770
- queryKey: notificationQueryKeys.byId((_b = user == null ? void 0 : user.auth.uid) != null ? _b : "")
771
- });
772
- return result;
773
- };
774
- return {
775
- createNotification: handleCreateNotifications
776
- };
777
- };
778
-
779
- // src/hooks/useGoogleClassroom.ts
780
- var useGoogleClassroom = () => {
781
- const submitAssignmentToGoogleClassroom = async ({
782
- assignment,
783
- scores,
784
- googleUserId = null
785
- // optional to override the user's googleUserId
786
- }) => {
787
- var _a, _b, _c;
788
- try {
789
- const { googleClassroomUserId = null } = scores;
790
- const googleId = googleUserId || googleClassroomUserId;
791
- if (!googleId)
792
- return {
793
- error: true,
794
- message: "No Google Classroom ID found"
795
- };
796
- const { courseWorkId, maxPoints, owners, courseId } = assignment;
797
- const draftGrade = (scores == null ? void 0 : scores.score) ? (scores == null ? void 0 : scores.score) / 100 * maxPoints : 0;
798
- const result = await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "submitAssignmentToGoogleClassroomV2")) == null ? void 0 : _c({
799
- teacherId: owners[0],
800
- courseId,
801
- courseWorkId,
802
- userId: googleId,
803
- draftGrade
804
- }));
805
- return result;
806
- } catch (error) {
807
- return { error: true, message: error.message };
374
+ if (response.data) {
375
+ return processRecord(response.data);
376
+ } else {
377
+ return "NOT_CHECKED" /* NOT_CHECKED */;
808
378
  }
809
- };
810
- return {
811
- submitAssignmentToGoogleClassroom
812
- };
813
- };
814
-
815
- // src/lib/firebase/firebase-analytics/grading-standard.ts
816
- var logGradingStandardLog = (data) => {
817
- var _a, _b, _c;
818
- if (data.courseId && data.type && data.level) {
819
- (_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "handleCouresAnalyticsEvent")) == null ? void 0 : _c({
820
- eventType: data.type || "custom",
821
- level: data.level,
822
- courseId: data.courseId
823
- });
824
- }
825
- api.logEvent("logGradingStandard", data);
826
- };
827
-
828
- // src/constants/analytics.constants.ts
829
- var ANALYTICS_EVENT_TYPES = {
830
- VOICE_SUCCESS: "voice_success",
831
- VOICE_FAIL: "voice_fail",
832
- RESPOND_CARD_SUCCESS: "respond_card_success",
833
- RESPOND_CARD_FAIL: "respond_card_fail",
834
- RESPOND_WRITE_CARD_SUCCESS: "respond_write_card_success",
835
- RESPOND_WRITE_CARD_FAIL: "respond_write_card_fail",
836
- RESPOND_WRITE_CARD_SUBMITTED: "respond_write_card_submitted",
837
- RESPOND_WRITE_CARD_ERROR: "respond_write_card_error",
838
- RESPOND_CARD_ERROR: "respond_card_error",
839
- RESPOND_CARD_SUBMITTED: "respond_card_submitted",
840
- RESPOND_FREE_PLAN: "respond_free_plan",
841
- RESPOND_WRITE_FREE_PLAN: "respond_write_free_plan",
842
- SUBMISSION: "assignment_submitted",
843
- ASSIGNMENT_STARTED: "assignment_started",
844
- CREATE_ASSIGNMENT: "create_assignment",
845
- MC_SUCCESS: "multiple_choice_success",
846
- MC_FAIL: "multiple_choice_fail",
847
- MC_ERROR: "multiple_choice_error",
848
- ACTFL_LEVEL: "actfl_level",
849
- WIDA_LEVEL: "wida_level"
850
- };
851
-
852
- // src/lib/firebase/firebase-analytics/assignment.ts
853
- var logOpenAssignment = (data = {}) => {
854
- api.logEvent("open_assignment", data);
855
- };
856
- var logOpenActivityPreview = (data = {}) => {
857
- api.logEvent("open_activity_preview", data);
858
- };
859
- var logSubmitAssignment = (data = {}) => {
860
- var _a, _b, _c;
861
- (_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "handleCouresAnalyticsEvent")) == null ? void 0 : _c({
862
- eventType: ANALYTICS_EVENT_TYPES.SUBMISSION,
863
- ...data
864
- });
865
- api.logEvent(ANALYTICS_EVENT_TYPES.SUBMISSION, data);
866
- };
867
- var logStartAssignment = (data = {}) => {
868
- var _a, _b, _c;
869
- if (data.courseId) {
870
- (_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "handleCouresAnalyticsEvent")) == null ? void 0 : _c({
871
- eventType: ANALYTICS_EVENT_TYPES.ASSIGNMENT_STARTED,
872
- ...data
873
- });
379
+ } catch (e) {
380
+ return "NOT_CHECKED" /* NOT_CHECKED */;
874
381
  }
875
- api.logEvent(ANALYTICS_EVENT_TYPES.ASSIGNMENT_STARTED, data);
876
382
  };
877
-
878
- // src/domains/assignment/utils/create-default-score.ts
879
- var defaultScore = (props) => {
880
- const { serverTimestamp: serverTimestamp2 } = api.accessHelpers();
881
- const score = {
882
- progress: 0,
883
- score: 0,
884
- startDate: serverTimestamp2(),
885
- status: "IN_PROGRESS",
886
- submitted: false,
887
- cards: {},
888
- lastPlayed: serverTimestamp2(),
889
- owners: props.owners,
890
- userId: props.userId
891
- };
892
- if (props.googleClassroomUserId) {
893
- score.googleClassroomUserId = props.googleClassroomUserId;
383
+ var processRecord = (data) => {
384
+ const { pronunciations = 0, fails = 0 } = data;
385
+ const attempts = pronunciations + fails;
386
+ const successRate = attempts > 0 ? pronunciations / attempts * 100 : 0;
387
+ let newStatus = null;
388
+ if (attempts < 6) {
389
+ return "NOT_CHECKED" /* NOT_CHECKED */;
894
390
  }
895
- if (props.courseId) {
896
- score.courseId = props.courseId;
391
+ if (successRate > 25) {
392
+ newStatus = "VERIFIED" /* VERIFIED */;
393
+ } else if (successRate > 10) {
394
+ newStatus = "WARNING" /* WARNING */;
395
+ } else if (fails > 20 && successRate < 10 && pronunciations > 1) {
396
+ newStatus = "NOT_RECOMMENDED" /* NOT_RECOMMENDED */;
397
+ } else if (pronunciations === 0 && fails > 20) {
398
+ newStatus = "NOT_WORKING" /* NOT_WORKING */;
399
+ } else {
400
+ newStatus = "NOT_CHECKED" /* NOT_CHECKED */;
897
401
  }
898
- return score;
899
- };
900
-
901
- // src/domains/assignment/score-practice.constants.ts
902
- var SCORES_PRACTICE_COLLECTION = "users";
903
- var SCORES_PRACTICE_SUBCOLLECTION = "practice";
904
- var refsScoresPractice = {
905
- practiceScores: (params) => `${SCORES_PRACTICE_COLLECTION}/${params.userId}/${SCORES_PRACTICE_SUBCOLLECTION}/${params.setId}`,
906
- practiceScoreHistoryRefDoc: (params) => `${SCORES_PRACTICE_COLLECTION}/${params.userId}/${SCORES_PRACTICE_SUBCOLLECTION}/${params.setId}/attempts/${params.date}`
402
+ return newStatus;
907
403
  };
908
404
 
909
- // src/domains/assignment/services/create-score.service.ts
910
- async function _createScore(params) {
911
- var _a, _b, _c;
912
- if (params.isAssignment) {
913
- const ref = refsAssignmentFiresotre.assignmentScores({
914
- id: params.activityId,
915
- userId: params.userId
916
- });
917
- await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "updateAssignmentGradebookStatus")) == null ? void 0 : _c({
918
- assignmentId: params.activityId,
919
- userId: params.userId,
920
- status: "IN_PROGRESS",
921
- score: null
922
- }));
923
- await api.setDoc(ref, params.scoreData, { merge: true });
924
- return {
925
- id: params.userId
926
- };
927
- } else {
928
- const ref = refsScoresPractice.practiceScores({
929
- userId: params.userId,
930
- setId: params.activityId
931
- });
932
- await api.setDoc(ref, params.scoreData);
933
- return {
934
- id: params.userId
935
- };
936
- }
405
+ // src/domains/cards/services/create-card.service.ts
406
+ async function _createCard({ data }) {
407
+ const response = await api.addDoc(refsCardsFiresotre.allCards, data);
408
+ return response;
937
409
  }
938
- var createScore = withErrorHandler(_createScore, "createScore");
939
-
940
- // src/domains/assignment/services/get-score.service.ts
941
- async function getAssignmentScore({
942
- userId,
943
- assignment,
944
- googleClassroomUserId
945
- }) {
946
- const path = refsAssignmentFiresotre.assignmentScores({
947
- id: assignment.id,
948
- userId
949
- });
950
- const response = await api.getDoc(path);
951
- if (response.data == null) {
952
- const newScore = {
953
- ...defaultScore({
954
- owners: [userId],
955
- userId,
956
- courseId: assignment.courseId,
957
- googleClassroomUserId
958
- }),
959
- assignmentId: assignment.id
960
- };
961
- logStartAssignment({
962
- courseId: assignment.courseId
963
- });
964
- const result = await createScore({
965
- activityId: assignment.id,
966
- userId,
967
- isAssignment: true,
968
- scoreData: newScore
969
- });
970
- return {
971
- ...newScore,
972
- id: result.id
410
+ var createCard = withErrorHandler(_createCard, "createCard");
411
+ async function _createCards({ cards }) {
412
+ const { writeBatch: writeBatch2, doc: doc2 } = api.accessHelpers();
413
+ const batch = writeBatch2();
414
+ const cardsWithId = [];
415
+ for (const card of cards) {
416
+ const cardId = v4();
417
+ const ref = doc2(refsCardsFiresotre.card(cardId));
418
+ const newCardObject = {
419
+ ...card,
420
+ id: cardId
973
421
  };
422
+ if (card.type === "READ_REPEAT" /* READ_REPEAT */ && card.target_text && card.language) {
423
+ const verificationStatus = await getVerificationStatus(card.target_text, card.language);
424
+ newCardObject.verificationStatus = verificationStatus || null;
425
+ }
426
+ cardsWithId.push(newCardObject);
427
+ batch.set(ref, newCardObject);
974
428
  }
975
- return response.data;
429
+ await batch.commit();
430
+ return cardsWithId;
976
431
  }
977
- async function getPracticeScore({ userId, setId }) {
978
- const path = refsScoresPractice.practiceScores({ userId, setId });
979
- const response = await api.getDoc(path);
980
- if (response.data == null) {
981
- const newScore = {
982
- ...defaultScore({
983
- owners: [userId],
984
- userId
985
- }),
986
- setId
987
- };
988
- const result = await createScore({
989
- activityId: setId,
990
- userId,
991
- isAssignment: false,
992
- scoreData: newScore
993
- });
994
- return {
995
- ...newScore,
996
- id: result.id
997
- };
998
- }
432
+ var createCards = withErrorHandler(_createCards, "createCards");
433
+
434
+ // src/domains/cards/services/get-card.service.ts
435
+ async function _getCard(params) {
436
+ const ref = refsCardsFiresotre.card(params.cardId);
437
+ const response = await api.getDoc(ref);
438
+ if (!response.data) return null;
999
439
  return response.data;
1000
440
  }
1001
- async function _getScore(params) {
1002
- if (params.isAssignment) {
1003
- return await getAssignmentScore({
1004
- userId: params.userId,
1005
- assignment: {
1006
- id: params.activityId,
1007
- courseId: params.courseId
1008
- },
1009
- googleClassroomUserId: params.googleClassroomUserId
1010
- });
1011
- } else {
1012
- return await getPracticeScore({
1013
- userId: params.userId,
1014
- setId: params.activityId
1015
- });
1016
- }
1017
- }
1018
- var getScore = withErrorHandler(_getScore, "getScore");
441
+ var getCard = withErrorHandler(_getCard, "getCard");
1019
442
 
1020
- // src/domains/assignment/utils/calculateScoreAndProgress.ts
1021
- var calculateScoreAndProgress = (scores, cardsList, weights) => {
1022
- const totalSetPoints = cardsList.reduce((acc, cardId) => {
1023
- acc += (weights == null ? void 0 : weights[cardId]) || 1;
1024
- return acc;
1025
- }, 0);
1026
- const totalPointsAwarded = Object.keys((scores == null ? void 0 : scores.cards) || {}).reduce((acc, cardId) => {
1027
- var _a, _b;
1028
- const cardScores = (_a = scores == null ? void 0 : scores.cards) == null ? void 0 : _a[cardId];
1029
- if ((cardScores == null ? void 0 : cardScores.completed) || (cardScores == null ? void 0 : cardScores.score) || (cardScores == null ? void 0 : cardScores.score) === 0) {
1030
- 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;
1031
- const weight = (weights == null ? void 0 : weights[cardId]) || 1;
1032
- const fraction = (score2 != null ? score2 : 0) / 100;
1033
- if (score2 || score2 === 0) {
1034
- acc += weight * fraction;
1035
- } else {
1036
- acc += weight;
1037
- }
1038
- }
1039
- return acc;
1040
- }, 0);
1041
- const totalCompletedCards = Object.keys((scores == null ? void 0 : scores.cards) || {}).reduce((acc, cardId) => {
1042
- var _a;
1043
- const cardScores = (_a = scores == null ? void 0 : scores.cards) == null ? void 0 : _a[cardId];
1044
- if ((cardScores == null ? void 0 : cardScores.completed) || (cardScores == null ? void 0 : cardScores.score) || (cardScores == null ? void 0 : cardScores.score) === 0) {
1045
- acc += 1;
1046
- }
1047
- return acc;
1048
- }, 0);
1049
- const percent = totalPointsAwarded / totalSetPoints;
1050
- const score = Math.round(percent * 100);
1051
- const progress = Math.round(totalCompletedCards / (cardsList.length || 1) * 100);
1052
- return { score, progress };
443
+ // src/domains/cards/card.repo.ts
444
+ var createCardRepo = () => {
445
+ return {
446
+ createCard,
447
+ createCards,
448
+ getCard
449
+ };
1053
450
  };
1054
- var calculateScoreAndProgress_default = calculateScoreAndProgress;
1055
-
1056
- // src/domains/assignment/services/update-score.service.ts
1057
- async function _updateScore(params) {
1058
- const path = params.isAssignment ? refsAssignmentFiresotre.assignmentScores({
1059
- id: params.activityId,
1060
- userId: params.userId
1061
- }) : refsScoresPractice.practiceScores({
1062
- setId: params.activityId,
1063
- userId: params.userId
1064
- });
1065
- await api.updateDoc(path, {
1066
- ...params.data
1067
- });
1068
- }
1069
- var updateScore = withErrorHandler(_updateScore, "updateScore");
1070
- async function _updateCardScore(params) {
1071
- const path = params.isAssignment ? refsAssignmentFiresotre.assignmentScores({
1072
- id: params.activityId,
1073
- userId: params.userId
1074
- }) : refsScoresPractice.practiceScores({
1075
- setId: params.activityId,
1076
- userId: params.userId
1077
- });
1078
- const updates = Object.keys(params.updates.cardScore).reduce(
1079
- (acc, key) => {
1080
- acc[`cards.${params.cardId}.${key}`] = params.updates.cardScore[key];
1081
- return acc;
1082
- },
1083
- {}
1084
- );
1085
- if (params.updates.progress) {
1086
- updates.progress = params.updates.progress;
1087
- }
1088
- if (params.updates.score) {
1089
- updates.score = params.updates.score;
1090
- }
1091
- await api.updateDoc(path, {
1092
- ...updates
1093
- });
1094
- }
1095
- var updateCardScore = withErrorHandler(_updateCardScore, "updateCardScore");
1096
-
1097
- // src/domains/assignment/services/clear-score.service.ts
1098
- import dayjs3 from "dayjs";
1099
- async function clearScore(params) {
1100
- var _a, _b, _c, _d, _e;
1101
- const update = {
1102
- [`cards.${params.cardId}`]: {
1103
- attempts: ((_a = params.cardScores.attempts) != null ? _a : 1) + 1,
1104
- correct: (_b = params.cardScores.correct) != null ? _b : 0,
1105
- // save old score history
1106
- history: [
1107
- {
1108
- ...params.cardScores,
1109
- attempts: (_c = params.cardScores.attempts) != null ? _c : 1,
1110
- correct: (_d = params.cardScores.correct) != null ? _d : 0,
1111
- retryTime: dayjs3().format("YYYY-MM-DD HH:mm:ss"),
1112
- history: null
1113
- },
1114
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1115
- ...(_e = params.cardScores.history) != null ? _e : []
1116
- ]
1117
- }
1118
- };
1119
- const path = params.isAssignment ? refsAssignmentFiresotre.assignmentScores({
1120
- id: params.activityId,
1121
- userId: params.userId
1122
- }) : refsScoresPractice.practiceScores({
1123
- setId: params.activityId,
1124
- userId: params.userId
1125
- });
1126
- await api.updateDoc(path, update);
1127
- return {
1128
- update,
1129
- activityId: params.activityId
1130
- };
1131
- }
1132
-
1133
- // src/domains/assignment/services/submit-assignment-score.service.ts
1134
- import dayjs4 from "dayjs";
1135
- async function _submitAssignmentScore({
1136
- cardIds,
1137
- assignment,
1138
- weights,
1139
- userId,
1140
- status,
1141
- studentName
1142
- }) {
1143
- const { serverTimestamp: serverTimestamp2 } = api.accessHelpers();
1144
- const path = refsAssignmentFiresotre.assignmentScores({ id: assignment.id, userId });
1145
- const fieldsUpdated = {
1146
- submitted: true,
1147
- progress: 100,
1148
- submissionDate: serverTimestamp2(),
1149
- status
1150
- };
1151
- if (assignment.isAssessment) {
1152
- const result = await handleAssessment(
1153
- assignment,
1154
- userId,
1155
- cardIds,
1156
- weights,
1157
- fieldsUpdated,
1158
- studentName
1159
- );
1160
- return result;
1161
- } else if (assignment.courseId) {
1162
- await handleCourseAssignment(assignment, userId);
1163
- }
1164
- await api.updateDoc(path, { ...fieldsUpdated });
1165
- return { success: true, fieldsUpdated };
1166
- }
1167
- var submitAssignmentScore = withErrorHandler(
1168
- _submitAssignmentScore,
1169
- "submitAssignmentScore"
1170
- );
1171
- async function handleAssessment(assignment, userId, cardIds, weights, fieldsUpdated, studentName) {
1172
- var _a, _b, _c;
1173
- const path = refsAssignmentFiresotre.assignmentScores({ id: assignment.id, userId });
1174
- const response = await api.getDoc(path);
1175
- if (!response.data) {
1176
- throw new Error("Score not found");
1177
- }
1178
- const { score: scoreCalculated } = calculateScoreAndProgress_default(response.data, cardIds, weights);
1179
- await api.updateDoc(path, { score: scoreCalculated, status: "PENDING_REVIEW" });
1180
- await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "submitAssessment")) == null ? void 0 : _c({
1181
- assignmentId: assignment.id,
1182
- assignmentTitle: assignment.name,
1183
- userId,
1184
- teacherId: assignment.owners[0],
1185
- studentName
1186
- }));
1187
- fieldsUpdated.status = "PENDING_REVIEW";
1188
- return { success: true, fieldsUpdated };
1189
- }
1190
- async function handleCourseAssignment(assignment, userId) {
1191
- var _a, _b, _c;
1192
- await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "submitAssignmentV2")) == null ? void 0 : _c({
1193
- assignmentId: assignment.id,
1194
- userId
1195
- }));
1196
- }
1197
- async function submitPracticeScore({
1198
- setId,
1199
- userId,
1200
- scores
1201
- }) {
1202
- const { serverTimestamp: serverTimestamp2 } = api.accessHelpers();
1203
- const date = dayjs4().format("YYYY-MM-DD-HH-mm");
1204
- const ref = refsScoresPractice.practiceScoreHistoryRefDoc({ setId, userId, date });
1205
- const fieldsUpdated = {
1206
- ...scores,
1207
- submitted: true,
1208
- progress: 100,
1209
- submissionDate: serverTimestamp2(),
1210
- status: "SUBMITTED"
1211
- };
1212
- await api.setDoc(ref, { ...fieldsUpdated });
1213
- const refScores = refsScoresPractice.practiceScores({ userId, setId });
1214
- await api.deleteDoc(refScores);
1215
- return { success: true, fieldsUpdated };
1216
- }
1217
-
1218
- // src/domains/assignment/hooks/score-hooks.ts
1219
- var scoreQueryKeys = {
1220
- all: ["scores"],
1221
- byId: (id) => [...scoreQueryKeys.all, id],
1222
- list: () => [...scoreQueryKeys.all, "list"]
1223
- };
1224
- function useScore({
1225
- isAssignment,
1226
- activityId,
1227
- userId = "",
1228
- courseId,
1229
- enabled = true,
1230
- googleClassroomUserId
1231
- }) {
1232
- return useQuery2({
1233
- queryFn: () => getScore({
1234
- userId,
1235
- courseId,
1236
- activityId,
1237
- googleClassroomUserId,
1238
- isAssignment
1239
- }),
1240
- queryKey: scoreQueryKeys.byId(activityId),
1241
- enabled
1242
- });
1243
- }
1244
- var debounceUpdateScore = debounce(updateScore, 1e3);
1245
- function useUpdateScore() {
1246
- const { queryClient } = useSpeakableApi();
1247
- const mutation = useMutation({
1248
- mutationFn: debounceUpdateScore,
1249
- onMutate: (variables) => {
1250
- return handleOptimisticUpdate({
1251
- queryClient,
1252
- queryKey: scoreQueryKeys.byId(variables.activityId),
1253
- newData: variables.data
1254
- });
1255
- },
1256
- onError: (_, variables, context) => {
1257
- if (context == null ? void 0 : context.previousData)
1258
- queryClient.setQueryData(scoreQueryKeys.byId(variables.activityId), context.previousData);
1259
- },
1260
- onSettled: (_, err, variables) => {
1261
- queryClient.invalidateQueries({
1262
- queryKey: scoreQueryKeys.byId(variables.activityId)
1263
- });
1264
- }
1265
- });
1266
- return {
1267
- mutationUpdateScore: mutation
1268
- };
1269
- }
1270
- function useUpdateCardScore({
1271
- isAssignment,
1272
- activityId,
1273
- userId,
1274
- cardIds,
1275
- weights
1276
- }) {
1277
- const { queryClient } = useSpeakableApi();
1278
- const queryKey = scoreQueryKeys.byId(activityId);
1279
- const mutation = useMutation({
1280
- mutationFn: async ({ cardId, cardScore }) => {
1281
- const previousScores = queryClient.getQueryData(queryKey);
1282
- const { progress, score, newScoreUpdated, updatedCardScore } = getScoreUpdated({
1283
- previousScores: previousScores != null ? previousScores : {},
1284
- cardId,
1285
- cardScore,
1286
- cardIds,
1287
- weights
1288
- });
1289
- await updateCardScore({
1290
- userId,
1291
- cardId,
1292
- isAssignment,
1293
- activityId,
1294
- updates: {
1295
- cardScore: updatedCardScore,
1296
- progress,
1297
- score
1298
- }
1299
- });
1300
- return { cardId, scoresUpdated: newScoreUpdated };
1301
- },
1302
- onMutate: ({ cardId, cardScore }) => {
1303
- queryClient.setQueryData(queryKey, (previousScore) => {
1304
- const updates = handleOptimisticScore({
1305
- score: previousScore,
1306
- cardId,
1307
- cardScore,
1308
- cardIds,
1309
- weights
1310
- });
1311
- return {
1312
- ...previousScore,
1313
- ...updates
1314
- };
1315
- });
1316
- },
1317
- // eslint-disable-next-line no-unused-vars
1318
- onError: (error) => {
1319
- console.log(error.message);
1320
- const previousData = queryClient.getQueryData(queryKey);
1321
- if (previousData) {
1322
- queryClient.setQueryData(queryKey, previousData);
1323
- }
1324
- }
1325
- });
1326
- return {
1327
- mutationUpdateCardScore: mutation
1328
- };
1329
- }
1330
- var getScoreUpdated = ({
1331
- cardId,
1332
- cardScore,
1333
- previousScores,
1334
- cardIds,
1335
- weights
1336
- }) => {
1337
- var _a, _b;
1338
- const previousCard = (_a = previousScores.cards) == null ? void 0 : _a[cardId];
1339
- const newCardScore = {
1340
- ...previousCard != null ? previousCard : {},
1341
- ...cardScore
1342
- };
1343
- const newScores = {
1344
- ...previousScores,
1345
- cards: {
1346
- ...(_b = previousScores.cards) != null ? _b : {},
1347
- [cardId]: newCardScore
1348
- }
1349
- };
1350
- const { score, progress } = calculateScoreAndProgress_default(newScores, cardIds, weights);
1351
- return {
1352
- newScoreUpdated: newScores,
1353
- updatedCardScore: cardScore,
1354
- score,
1355
- progress
1356
- };
1357
- };
1358
- var handleOptimisticScore = ({
1359
- score,
1360
- cardId,
1361
- cardScore,
1362
- cardIds,
1363
- weights
1364
- }) => {
1365
- var _a;
1366
- let cards = { ...(_a = score == null ? void 0 : score.cards) != null ? _a : {} };
1367
- cards = {
1368
- ...cards,
1369
- [cardId]: {
1370
- ...cards[cardId],
1371
- ...cardScore
1372
- }
1373
- };
1374
- const { score: scoreValue, progress } = calculateScoreAndProgress_default(
1375
- // @ts-ignore
1376
- {
1377
- ...score != null ? score : {},
1378
- cards
1379
- },
1380
- cardIds,
1381
- weights
1382
- );
1383
- return { cards, score: scoreValue, progress };
1384
- };
1385
- function useClearScore() {
1386
- const { queryClient } = useSpeakableApi();
1387
- const mutation = useMutation({
1388
- mutationFn: clearScore,
1389
- onSettled: (result) => {
1390
- var _a;
1391
- queryClient.invalidateQueries({
1392
- queryKey: scoreQueryKeys.byId((_a = result == null ? void 0 : result.activityId) != null ? _a : "")
1393
- });
1394
- }
1395
- });
1396
- return {
1397
- mutationClearScore: mutation
1398
- };
1399
- }
1400
- function useSubmitAssignmentScore({
1401
- onAssignmentSubmitted,
1402
- studentName
1403
- }) {
1404
- const { queryClient } = useSpeakableApi();
1405
- const { hasGoogleClassroomGradePassback } = usePermissions_default();
1406
- const { submitAssignmentToGoogleClassroom } = useGoogleClassroom();
1407
- const { createNotification: createNotification2 } = useCreateNotification();
1408
- const mutation = useMutation({
1409
- mutationFn: async ({
1410
- assignment,
1411
- userId,
1412
- cardIds,
1413
- weights,
1414
- scores,
1415
- status
1416
- }) => {
1417
- try {
1418
- const scoreUpdated = await submitAssignmentScore({
1419
- assignment,
1420
- userId,
1421
- cardIds,
1422
- weights,
1423
- status,
1424
- studentName
1425
- });
1426
- if (assignment.courseWorkId != null && !assignment.isAssessment && hasGoogleClassroomGradePassback) {
1427
- await submitAssignmentToGoogleClassroom({
1428
- assignment,
1429
- scores
1430
- });
1431
- }
1432
- if (assignment.isAssessment) {
1433
- createNotification2(SpeakableNotificationTypes.ASSESSMENT_SUBMITTED, assignment);
1434
- }
1435
- if (assignment == null ? void 0 : assignment.id) {
1436
- logSubmitAssignment({
1437
- courseId: assignment == null ? void 0 : assignment.courseId
1438
- });
1439
- }
1440
- onAssignmentSubmitted(assignment.id);
1441
- queryClient.setQueryData(scoreQueryKeys.byId(assignment.id), {
1442
- ...scores,
1443
- ...scoreUpdated.fieldsUpdated
1444
- });
1445
- return {
1446
- success: true,
1447
- message: "Score submitted successfully"
1448
- };
1449
- } catch (error) {
1450
- return {
1451
- success: false,
1452
- error
1453
- };
1454
- }
1455
- }
1456
- });
1457
- return {
1458
- submitAssignmentScore: mutation.mutateAsync,
1459
- isLoading: mutation.isPending
1460
- };
1461
- }
1462
- function useSubmitPracticeScore() {
1463
- const { queryClient } = useSpeakableApi();
1464
- const mutation = useMutation({
1465
- mutationFn: async ({
1466
- setId,
1467
- userId,
1468
- scores
1469
- }) => {
1470
- try {
1471
- await submitPracticeScore({
1472
- setId,
1473
- userId,
1474
- scores
1475
- });
1476
- queryClient.invalidateQueries({
1477
- queryKey: scoreQueryKeys.byId(setId)
1478
- });
1479
- return {
1480
- success: true,
1481
- message: "Score submitted successfully"
1482
- };
1483
- } catch (error) {
1484
- return {
1485
- success: false,
1486
- error
1487
- };
1488
- }
1489
- }
1490
- });
1491
- return {
1492
- submitPracticeScore: mutation.mutateAsync,
1493
- isLoading: mutation.isPending
1494
- };
1495
- }
1496
-
1497
- // src/domains/cards/card.hooks.ts
1498
- import { useMutation as useMutation2, useQueries, useQuery as useQuery3 } from "@tanstack/react-query";
1499
- import { useMemo } from "react";
1500
-
1501
- // src/domains/cards/card.constants.ts
1502
- var FeedbackTypesCard = /* @__PURE__ */ ((FeedbackTypesCard2) => {
1503
- FeedbackTypesCard2["SuggestedResponse"] = "suggested_response";
1504
- FeedbackTypesCard2["Wida"] = "wida";
1505
- FeedbackTypesCard2["GrammarInsights"] = "grammar_insights";
1506
- FeedbackTypesCard2["Actfl"] = "actfl";
1507
- FeedbackTypesCard2["ProficiencyLevel"] = "proficiency_level";
1508
- return FeedbackTypesCard2;
1509
- })(FeedbackTypesCard || {});
1510
- var LeniencyCard = /* @__PURE__ */ ((LeniencyCard2) => {
1511
- LeniencyCard2["CONFIDENCE"] = "confidence";
1512
- LeniencyCard2["EASY"] = "easy";
1513
- LeniencyCard2["NORMAL"] = "normal";
1514
- LeniencyCard2["HARD"] = "hard";
1515
- return LeniencyCard2;
1516
- })(LeniencyCard || {});
1517
- var LENIENCY_OPTIONS = [
1518
- {
1519
- label: "Build Confidence - most lenient",
1520
- value: "confidence" /* CONFIDENCE */
1521
- },
1522
- {
1523
- label: "Very Lenient",
1524
- value: "easy" /* EASY */
1525
- },
1526
- {
1527
- label: "Normal",
1528
- value: "normal" /* NORMAL */
1529
- },
1530
- {
1531
- label: "No leniency - most strict",
1532
- value: "hard" /* HARD */
1533
- }
1534
- ];
1535
- var STUDENT_LEVELS_OPTIONS = [
1536
- {
1537
- label: "Beginner",
1538
- description: "Beginner Level: Just starting out. Can say a few basic words and phrases.",
1539
- value: "beginner"
1540
- },
1541
- {
1542
- label: "Elementary",
1543
- description: "Elementary Level: Can understand simple sentences and have very basic conversations.",
1544
- value: "elementary"
1545
- },
1546
- {
1547
- label: "Intermediate",
1548
- description: "Intermediate Level: Can talk about everyday topics and handle common situations.",
1549
- value: "intermediate"
1550
- },
1551
- {
1552
- label: "Advanced",
1553
- description: "Advanced Level: Can speak and understand with ease, and explain ideas clearly.",
1554
- value: "advanced"
1555
- },
1556
- {
1557
- label: "Fluent",
1558
- description: "Fluent Level: Speaks naturally and easily. Can use the language in work or school settings.",
1559
- value: "fluent"
1560
- },
1561
- {
1562
- label: "Native-like",
1563
- description: "Native-like Level: Understands and speaks like a native. Can discuss complex ideas accurately.",
1564
- value: "nativeLike"
1565
- }
1566
- ];
1567
- var BASE_RESPOND_FIELD_VALUES = {
1568
- title: "",
1569
- allowRetries: true,
1570
- respondTime: 180,
1571
- maxCharacters: 1e3
1572
- };
1573
- var BASE_REPEAT_FIELD_VALUES = {
1574
- repeat: 1
1575
- };
1576
- var BASE_MULTIPLE_CHOICE_FIELD_VALUES = {
1577
- MCQType: "single",
1578
- answer: ["A"],
1579
- choices: [
1580
- { option: "A", value: "Option A" },
1581
- { option: "B", value: "Option B" },
1582
- { option: "C", value: "Option C" }
1583
- ]
1584
- };
1585
- var VerificationCardStatus = /* @__PURE__ */ ((VerificationCardStatus2) => {
1586
- VerificationCardStatus2["VERIFIED"] = "VERIFIED";
1587
- VerificationCardStatus2["WARNING"] = "WARNING";
1588
- VerificationCardStatus2["NOT_RECOMMENDED"] = "NOT_RECOMMENDED";
1589
- VerificationCardStatus2["NOT_WORKING"] = "NOT_WORKING";
1590
- VerificationCardStatus2["NOT_CHECKED"] = "NOT_CHECKED";
1591
- return VerificationCardStatus2;
1592
- })(VerificationCardStatus || {});
1593
- var CARDS_COLLECTION = "flashcards";
1594
- var refsCardsFiresotre = {
1595
- allCards: CARDS_COLLECTION,
1596
- card: (id) => `${CARDS_COLLECTION}/${id}`
1597
- };
1598
-
1599
- // src/domains/cards/services/get-card.service.ts
1600
- async function _getCard(params) {
1601
- const ref = refsCardsFiresotre.card(params.cardId);
1602
- const response = await api.getDoc(ref);
1603
- if (!response.data) return null;
1604
- return response.data;
1605
- }
1606
- var getCard = withErrorHandler(_getCard, "getCard");
1607
-
1608
- // src/domains/cards/services/create-card.service.ts
1609
- import { v4 } from "uuid";
1610
-
1611
- // src/domains/cards/card.model.ts
1612
- var ActivityPageType = /* @__PURE__ */ ((ActivityPageType2) => {
1613
- ActivityPageType2["READ_REPEAT"] = "READ_REPEAT";
1614
- ActivityPageType2["VIDEO"] = "VIDEO";
1615
- ActivityPageType2["TEXT"] = "TEXT";
1616
- ActivityPageType2["READ_RESPOND"] = "READ_RESPOND";
1617
- ActivityPageType2["FREE_RESPONSE"] = "FREE_RESPONSE";
1618
- ActivityPageType2["REPEAT"] = "REPEAT";
1619
- ActivityPageType2["RESPOND"] = "RESPOND";
1620
- ActivityPageType2["RESPOND_WRITE"] = "RESPOND_WRITE";
1621
- ActivityPageType2["TEXT_TO_SPEECH"] = "TEXT_TO_SPEECH";
1622
- ActivityPageType2["MULTIPLE_CHOICE"] = "MULTIPLE_CHOICE";
1623
- ActivityPageType2["PODCAST"] = "PODCAST";
1624
- ActivityPageType2["MEDIA_PAGE"] = "MEDIA_PAGE";
1625
- ActivityPageType2["WRITE"] = "WRITE";
1626
- ActivityPageType2["SHORT_ANSWER"] = "SHORT_ANSWER";
1627
- ActivityPageType2["SHORT_STORY"] = "SHORT_STORY";
1628
- ActivityPageType2["SPEAK"] = "SPEAK";
1629
- ActivityPageType2["CONVERSATION"] = "CONVERSATION";
1630
- ActivityPageType2["CONVERSATION_WRITE"] = "CONVERSATION_WRITE";
1631
- ActivityPageType2["DIALOGUE"] = "DIALOGUE";
1632
- ActivityPageType2["INSTRUCTION"] = "INSTRUCTION";
1633
- ActivityPageType2["LISTEN"] = "LISTEN";
1634
- ActivityPageType2["READ"] = "READ";
1635
- ActivityPageType2["ANSWER"] = "ANSWER";
1636
- return ActivityPageType2;
1637
- })(ActivityPageType || {});
1638
- var RESPOND_PAGE_ACTIVITY_TYPES = [
1639
- "READ_RESPOND" /* READ_RESPOND */,
1640
- "RESPOND" /* RESPOND */,
1641
- "RESPOND_WRITE" /* RESPOND_WRITE */,
1642
- "FREE_RESPONSE" /* FREE_RESPONSE */
1643
- ];
1644
- var MULTIPLE_CHOICE_PAGE_ACTIVITY_TYPES = ["MULTIPLE_CHOICE" /* MULTIPLE_CHOICE */];
1645
- var REPEAT_PAGE_ACTIVITY_TYPES = ["READ_REPEAT" /* READ_REPEAT */, "REPEAT" /* REPEAT */];
1646
- var RESPOND_WRITE_PAGE_ACTIVITY_TYPES = [
1647
- "RESPOND_WRITE" /* RESPOND_WRITE */,
1648
- "FREE_RESPONSE" /* FREE_RESPONSE */
1649
- ];
1650
- var RESPOND_AUDIO_PAGE_ACTIVITY_TYPES = [
1651
- "RESPOND" /* RESPOND */,
1652
- "READ_RESPOND" /* READ_RESPOND */
1653
- ];
1654
-
1655
- // src/utils/text-utils.ts
1656
- import sha1 from "js-sha1";
1657
- var purify = (word) => {
1658
- 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();
1659
- };
1660
- var cleanString = (words) => {
1661
- const splitWords = words == null ? void 0 : words.split("+");
1662
- if (splitWords && splitWords.length === 1) {
1663
- const newWord = purify(words);
1664
- return newWord;
1665
- } else if (splitWords && splitWords.length > 1) {
1666
- const split = splitWords.map((w) => purify(w));
1667
- return split;
1668
- } else {
1669
- return "";
1670
- }
1671
- };
1672
- var getWordHash = (word, language) => {
1673
- const cleanedWord = cleanString(word);
1674
- const wordHash = sha1(`${language}-${cleanedWord}`);
1675
- console.log("wordHash core library", wordHash);
1676
- return wordHash;
1677
- };
1678
- function getPhraseLength(phrase, input) {
1679
- if (Array.isArray(phrase) && phrase.includes(input)) {
1680
- return phrase[phrase.indexOf(input)].split(" ").length;
1681
- } else {
1682
- return phrase ? phrase.split(" ").length : 0;
1683
- }
1684
- }
1685
-
1686
- // src/domains/cards/services/get-card-verification-status.service.ts
1687
- var charactarLanguages = ["zh", "ja", "ko"];
1688
- var getVerificationStatus = async (target_text, language) => {
1689
- if ((target_text == null ? void 0 : target_text.length) < 3 && !charactarLanguages.includes(language)) {
1690
- return "NOT_RECOMMENDED" /* NOT_RECOMMENDED */;
1691
- }
1692
- const hash = getWordHash(target_text, language);
1693
- const response = await api.getDoc(`checked-pronunciations/${hash}`);
1694
- try {
1695
- if (response.data) {
1696
- return processRecord(response.data);
1697
- } else {
1698
- return "NOT_CHECKED" /* NOT_CHECKED */;
1699
- }
1700
- } catch (e) {
1701
- return "NOT_CHECKED" /* NOT_CHECKED */;
1702
- }
1703
- };
1704
- var processRecord = (data) => {
1705
- const { pronunciations = 0, fails = 0 } = data;
1706
- const attempts = pronunciations + fails;
1707
- const successRate = attempts > 0 ? pronunciations / attempts * 100 : 0;
1708
- let newStatus = null;
1709
- if (attempts < 6) {
1710
- return "NOT_CHECKED" /* NOT_CHECKED */;
1711
- }
1712
- if (successRate > 25) {
1713
- newStatus = "VERIFIED" /* VERIFIED */;
1714
- } else if (successRate > 10) {
1715
- newStatus = "WARNING" /* WARNING */;
1716
- } else if (fails > 20 && successRate < 10 && pronunciations > 1) {
1717
- newStatus = "NOT_RECOMMENDED" /* NOT_RECOMMENDED */;
1718
- } else if (pronunciations === 0 && fails > 20) {
1719
- newStatus = "NOT_WORKING" /* NOT_WORKING */;
1720
- } else {
1721
- newStatus = "NOT_CHECKED" /* NOT_CHECKED */;
1722
- }
1723
- return newStatus;
1724
- };
1725
-
1726
- // src/domains/cards/services/create-card.service.ts
1727
- async function _createCard({ data }) {
1728
- const response = await api.addDoc(refsCardsFiresotre.allCards, data);
1729
- return response;
1730
- }
1731
- var createCard = withErrorHandler(_createCard, "createCard");
1732
- async function _createCards({ cards }) {
1733
- const { writeBatch: writeBatch2, doc: doc2 } = api.accessHelpers();
1734
- const batch = writeBatch2();
1735
- const cardsWithId = [];
1736
- for (const card of cards) {
1737
- const cardId = v4();
1738
- const ref = doc2(refsCardsFiresotre.card(cardId));
1739
- const newCardObject = {
1740
- ...card,
1741
- id: cardId
1742
- };
1743
- if (card.type === "READ_REPEAT" /* READ_REPEAT */ && card.target_text && card.language) {
1744
- const verificationStatus = await getVerificationStatus(card.target_text, card.language);
1745
- newCardObject.verificationStatus = verificationStatus || null;
1746
- }
1747
- cardsWithId.push(newCardObject);
1748
- batch.set(ref, newCardObject);
1749
- }
1750
- await batch.commit();
1751
- return cardsWithId;
1752
- }
1753
- var createCards = withErrorHandler(_createCards, "createCards");
1754
-
1755
- // src/domains/cards/card.hooks.ts
1756
- var cardsQueryKeys = {
1757
- all: ["cards"],
1758
- one: (params) => [...cardsQueryKeys.all, params.cardId]
1759
- };
1760
- function useCards({
1761
- cardIds,
1762
- enabled = true,
1763
- asObject
1764
- }) {
1765
- const queries = useQueries({
1766
- queries: cardIds.map((cardId) => ({
1767
- enabled: enabled && cardIds.length > 0,
1768
- queryKey: cardsQueryKeys.one({
1769
- cardId
1770
- }),
1771
- queryFn: () => getCard({ cardId })
1772
- }))
1773
- });
1774
- const cards = queries.map((query2) => query2.data).filter(Boolean);
1775
- const cardsObject = useMemo(() => {
1776
- if (!asObject) return null;
1777
- return cards.reduce((acc, card) => {
1778
- acc[card.id] = card;
1779
- return acc;
1780
- }, {});
1781
- }, [asObject, cards]);
1782
- return {
1783
- cards,
1784
- cardsObject,
1785
- cardsQueries: queries
1786
- };
1787
- }
1788
- function useCreateCard() {
1789
- const { queryClient } = useSpeakableApi();
1790
- const mutationCreateCard = useMutation2({
1791
- mutationFn: createCard,
1792
- onSuccess: (cardCreated) => {
1793
- queryClient.invalidateQueries({ queryKey: cardsQueryKeys.one({ cardId: cardCreated.id }) });
1794
- }
1795
- });
1796
- return {
1797
- mutationCreateCard
1798
- };
1799
- }
1800
- function useCreateCards() {
1801
- const mutationCreateCards = useMutation2({
1802
- mutationFn: createCards
1803
- });
1804
- return {
1805
- mutationCreateCards
1806
- };
1807
- }
1808
- function getCardFromCache({
1809
- cardId,
1810
- queryClient
1811
- }) {
1812
- return queryClient.getQueryData(cardsQueryKeys.one({ cardId }));
1813
- }
1814
- function updateCardInCache({
1815
- cardId,
1816
- card,
1817
- queryClient
1818
- }) {
1819
- queryClient.setQueryData(cardsQueryKeys.one({ cardId }), card);
1820
- }
1821
- function useGetCard({ cardId, enabled = true }) {
1822
- const query2 = useQuery3({
1823
- queryKey: cardsQueryKeys.one({ cardId }),
1824
- queryFn: () => getCard({ cardId }),
1825
- enabled: enabled && !!cardId
1826
- });
1827
- return query2;
1828
- }
1829
-
1830
- // src/domains/cards/card.repo.ts
1831
- var createCardRepo = () => {
1832
- return {
1833
- createCard,
1834
- createCards,
1835
- getCard
1836
- };
1837
- };
1838
-
1839
- // src/domains/cards/utils/check-page-type.ts
1840
- function checkIsRepeatPage(cardType) {
1841
- if (cardType === void 0) return false;
1842
- return REPEAT_PAGE_ACTIVITY_TYPES.includes(cardType);
1843
- }
1844
- function checkIsMCPage(cardType) {
1845
- if (cardType === void 0) return false;
1846
- return MULTIPLE_CHOICE_PAGE_ACTIVITY_TYPES.includes(cardType);
1847
- }
1848
- function checkIsRespondPage(cardType) {
1849
- if (cardType === void 0) return false;
1850
- return RESPOND_PAGE_ACTIVITY_TYPES.includes(cardType);
1851
- }
1852
- function checkIsRespondWrittenPage(cardType) {
1853
- if (cardType === void 0) return false;
1854
- return RESPOND_WRITE_PAGE_ACTIVITY_TYPES.includes(cardType);
1855
- }
1856
- function checkIsRespondAudioPage(cardType) {
1857
- if (cardType === void 0) return false;
1858
- return RESPOND_AUDIO_PAGE_ACTIVITY_TYPES.includes(cardType);
1859
- }
1860
- var checkIsMediaPage = (cardType) => {
1861
- if (cardType === void 0) return false;
1862
- return cardType === "MEDIA_PAGE" /* MEDIA_PAGE */;
1863
- };
1864
- var checkIsShortAnswerPage = (cardType) => {
1865
- if (cardType === void 0) return false;
1866
- return cardType === "SHORT_ANSWER" /* SHORT_ANSWER */;
1867
- };
1868
- var checkTypePageActivity = (cardType) => {
1869
- const isRespondAudio = checkIsRespondAudioPage(cardType);
1870
- const isRespondWritten = checkIsRespondWrittenPage(cardType);
1871
- const isRespond = checkIsRespondPage(cardType);
1872
- const isMC = checkIsMCPage(cardType);
1873
- const isRepeat = checkIsRepeatPage(cardType);
1874
- const isMediaPage = checkIsMediaPage(cardType);
1875
- const isShortAnswer = checkIsShortAnswerPage(cardType);
1876
- return {
1877
- isRespondAudio,
1878
- isRespondWritten,
1879
- isRespond,
1880
- isMC,
1881
- isRepeat,
1882
- isMediaPage,
1883
- isShortAnswer
1884
- };
1885
- };
1886
-
1887
- // src/domains/cards/utils/get-page-prompt.ts
1888
- function getPagePrompt(card) {
1889
- if (!card) return { has: false, text: "" };
1890
- const { isMC, isRepeat, isRespond, isShortAnswer } = checkTypePageActivity(card == null ? void 0 : card.type);
1891
- const hidePrompt = (card == null ? void 0 : card.hidePrompt) === true;
1892
- if (isRepeat) {
1893
- return {
1894
- has: true,
1895
- text: card == null ? void 0 : card.target_text
1896
- };
1897
- }
1898
- if (isRespond && !hidePrompt) {
1899
- return {
1900
- has: true,
1901
- text: card == null ? void 0 : card.prompt
1902
- };
1903
- }
1904
- if (isMC) {
1905
- return {
1906
- has: true,
1907
- text: card == null ? void 0 : card.question
1908
- };
1909
- }
1910
- if (isShortAnswer && !hidePrompt) {
1911
- return {
1912
- has: true,
1913
- text: card == null ? void 0 : card.prompt
1914
- };
1915
- }
1916
- return {
1917
- has: false,
1918
- text: ""
1919
- };
1920
- }
1921
-
1922
- // src/domains/cards/utils/get-completed-pages.ts
1923
- var getTotalCompletedCards = (pageScores) => {
1924
- return Object.values(pageScores != null ? pageScores : {}).reduce((acc, cardScore) => {
1925
- var _a, _b;
1926
- if (((_b = (_a = cardScore.completed) != null ? _a : cardScore.score) != null ? _b : cardScore.score === 0) && !cardScore.media_area_opened) {
1927
- acc++;
1928
- }
1929
- return acc;
1930
- }, 0);
1931
- };
1932
-
1933
- // src/domains/cards/utils/get-label-page.ts
1934
- var labels = {
1935
- repeat: {
1936
- short: "Repeat",
1937
- long: "Listen & Repeat"
1938
- },
1939
- mc: {
1940
- short: "Multiple Choice",
1941
- long: "Multiple Choice"
1942
- },
1943
- mediaPage: {
1944
- short: "Media Page",
1945
- long: "Media Page"
1946
- },
1947
- shortAnswer: {
1948
- short: "Short Answer",
1949
- long: "Short Answer"
1950
- },
1951
- respondWritten: {
1952
- short: "Open Response",
1953
- long: "Written Open Response"
1954
- },
1955
- respondAudio: {
1956
- short: "Open Response",
1957
- long: "Spoken Open Response"
1958
- }
1959
- };
1960
- var getLabelPage = (pageType) => {
1961
- if (!pageType) {
1962
- return {
1963
- short: "",
1964
- long: ""
1965
- };
1966
- }
1967
- const { isRepeat, isMC, isMediaPage, isShortAnswer, isRespondWritten, isRespondAudio } = checkTypePageActivity(pageType);
1968
- if (isRepeat) {
1969
- return labels.repeat;
1970
- }
1971
- if (isMC) {
1972
- return labels.mc;
1973
- }
1974
- if (isMediaPage) {
1975
- return labels.mediaPage;
1976
- }
1977
- if (isShortAnswer) {
1978
- return labels.shortAnswer;
1979
- }
1980
- if (isRespondWritten) {
1981
- return labels.respondWritten;
1982
- }
1983
- if (isRespondAudio) {
1984
- return labels.respondAudio;
1985
- }
1986
- return {
1987
- short: "",
1988
- long: ""
1989
- };
1990
- };
1991
-
1992
- // src/domains/sets/set.hooks.ts
1993
- import { useQuery as useQuery4 } from "@tanstack/react-query";
1994
-
1995
- // src/domains/sets/set.constants.ts
1996
- var SETS_COLLECTION = "sets";
1997
- var refsSetsFirestore = {
1998
- allSets: SETS_COLLECTION,
1999
- set: (id) => `${SETS_COLLECTION}/${id}`
2000
- };
2001
-
2002
- // src/domains/sets/services/get-set.service.ts
2003
- async function _getSet({ setId }) {
2004
- const response = await api.getDoc(refsSetsFirestore.set(setId));
2005
- return response.data;
2006
- }
2007
- var getSet = withErrorHandler(_getSet, "getSet");
2008
-
2009
- // src/domains/sets/set.hooks.ts
2010
- var setsQueryKeys = {
2011
- all: ["sets"],
2012
- one: (params) => [...setsQueryKeys.all, params.setId]
2013
- };
2014
- var useSet = ({ setId, enabled }) => {
2015
- return useQuery4({
2016
- queryKey: setsQueryKeys.one({ setId }),
2017
- queryFn: () => getSet({ setId }),
2018
- enabled: setId !== void 0 && setId !== "" && enabled
2019
- });
2020
- };
2021
- function getSetFromCache({
2022
- setId,
2023
- queryClient
2024
- }) {
2025
- if (!setId) return null;
2026
- return queryClient.getQueryData(setsQueryKeys.one({ setId }));
2027
- }
2028
- function updateSetInCache({
2029
- set,
2030
- queryClient
2031
- }) {
2032
- const { id, ...setData } = set;
2033
- queryClient.setQueryData(setsQueryKeys.one({ setId: id }), setData);
2034
- }
2035
-
2036
- // src/domains/sets/set.repo.ts
2037
- var createSetRepo = () => {
2038
- return {
2039
- getSet
2040
- };
2041
- };
2042
-
2043
- // src/constants/all-langs.json
2044
- var all_langs_default = {
2045
- af: "Afrikaans",
2046
- sq: "Albanian",
2047
- am: "Amharic",
2048
- ar: "Arabic",
2049
- hy: "Armenian",
2050
- az: "Azerbaijani",
2051
- eu: "Basque",
2052
- be: "Belarusian",
2053
- bn: "Bengali",
2054
- bs: "Bosnian",
2055
- bg: "Bulgarian",
2056
- ca: "Catalan",
2057
- ceb: "Cebuano",
2058
- zh: "Chinese",
2059
- co: "Corsican",
2060
- hr: "Croatian",
2061
- cs: "Czech",
2062
- da: "Danish",
2063
- nl: "Dutch",
2064
- en: "English",
2065
- eo: "Esperanto",
2066
- et: "Estonian",
2067
- fi: "Finnish",
2068
- fr: "French",
2069
- fy: "Frisian",
2070
- gl: "Galician",
2071
- ka: "Georgian",
2072
- de: "German",
2073
- el: "Greek",
2074
- gu: "Gujarati",
2075
- ht: "Haitian Creole",
2076
- ha: "Hausa",
2077
- haw: "Hawaiian",
2078
- he: "Hebrew",
2079
- hi: "Hindi",
2080
- hmn: "Hmong",
2081
- hu: "Hungarian",
2082
- is: "Icelandic",
2083
- ig: "Igbo",
2084
- id: "Indonesian",
2085
- ga: "Irish",
2086
- it: "Italian",
2087
- ja: "Japanese",
2088
- jv: "Javanese",
2089
- kn: "Kannada",
2090
- kk: "Kazakh",
2091
- km: "Khmer",
2092
- ko: "Korean",
2093
- ku: "Kurdish",
2094
- ky: "Kyrgyz",
2095
- lo: "Lao",
2096
- la: "Latin",
2097
- lv: "Latvian",
2098
- lt: "Lithuanian",
2099
- lb: "Luxembourgish",
2100
- mk: "Macedonian",
2101
- mg: "Malagasy",
2102
- ms: "Malay",
2103
- ml: "Malayalam",
2104
- mt: "Maltese",
2105
- mi: "Maori",
2106
- mr: "Marathi",
2107
- mn: "Mongolian",
2108
- my: "Myanmar (Burmese)",
2109
- ne: "Nepali",
2110
- no: "Norwegian",
2111
- ny: "Nyanja (Chichewa)",
2112
- ps: "Pashto",
2113
- fa: "Persian",
2114
- pl: "Polish",
2115
- pt: "Portuguese",
2116
- pa: "Punjabi",
2117
- ro: "Romanian",
2118
- ru: "Russian",
2119
- sm: "Samoan",
2120
- gd: "Scots Gaelic",
2121
- sr: "Serbian",
2122
- st: "Sesotho",
2123
- sn: "Shona",
2124
- sd: "Sindhi",
2125
- si: "Sinhala (Sinhalese)",
2126
- sk: "Slovak",
2127
- sl: "Slovenian",
2128
- so: "Somali",
2129
- es: "Spanish",
2130
- su: "Sundanese",
2131
- sw: "Swahili",
2132
- sv: "Swedish",
2133
- tl: "Tagalog (Filipino)",
2134
- tg: "Tajik",
2135
- ta: "Tamil",
2136
- te: "Telugu",
2137
- th: "Thai",
2138
- tr: "Turkish",
2139
- uk: "Ukrainian",
2140
- ur: "Urdu",
2141
- uz: "Uzbek",
2142
- vi: "Vietnamese",
2143
- cy: "Welsh",
2144
- xh: "Xhosa",
2145
- yi: "Yiddish",
2146
- yo: "Yoruba",
2147
- zu: "Zulu"
2148
- };
2149
-
2150
- // src/utils/ai/get-respond-card-tool.ts
2151
- var getRespondCardTool = ({
2152
- language,
2153
- standard = "actfl"
2154
- }) => {
2155
- const lang = all_langs_default[language] || "English";
2156
- const tool = {
2157
- tool_choice: {
2158
- type: "function",
2159
- function: { name: "get_feedback" }
2160
- },
2161
- tools: [
2162
- {
2163
- type: "function",
2164
- function: {
2165
- name: "get_feedback",
2166
- description: "Get feedback on a student's response",
2167
- parameters: {
2168
- type: "object",
2169
- required: [
2170
- "success",
2171
- "score",
2172
- "score_justification",
2173
- "errors",
2174
- "improvedResponse",
2175
- "compliments"
2176
- ],
2177
- properties: {
2178
- success: {
2179
- type: "boolean",
2180
- 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."
2181
- },
2182
- errors: {
2183
- type: "array",
2184
- items: {
2185
- type: "object",
2186
- required: ["error", "grammar_error_type", "correction", "justification"],
2187
- properties: {
2188
- error: {
2189
- type: "string",
2190
- description: "The grammatical error in the student's response."
2191
- },
2192
- correction: {
2193
- type: "string",
2194
- description: "The suggested correction to the error"
2195
- },
2196
- justification: {
2197
- type: "string",
2198
- description: `An explanation of the rationale behind the suggested correction. WRITE THIS IN ${lang}!`
2199
- },
2200
- grammar_error_type: {
2201
- type: "string",
2202
- enum: [
2203
- "subjVerbAgree",
2204
- "tenseErrors",
2205
- "articleMisuse",
2206
- "prepositionErrors",
2207
- "adjNounAgree",
2208
- "pronounErrors",
2209
- "wordOrder",
2210
- "verbConjugation",
2211
- "pluralization",
2212
- "negationErrors",
2213
- "modalVerbMisuse",
2214
- "relativeClause",
2215
- "auxiliaryVerb",
2216
- "complexSentenceAgreement",
2217
- "idiomaticExpression",
2218
- "registerInconsistency",
2219
- "voiceMisuse"
2220
- ],
2221
- 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"
2222
- }
2223
- }
2224
- },
2225
- 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."
2226
- },
2227
- compliments: {
2228
- type: "array",
2229
- items: {
2230
- type: "string"
2231
- },
2232
- description: `An array of strings, each representing something the student did well. Each string should be WRITTEN IN ${lang}!`
2233
- },
2234
- improvedResponse: {
2235
- type: "string",
2236
- description: "An improved response with proper grammar and more detail, if applicable."
2237
- },
2238
- score: {
2239
- type: "number",
2240
- description: "A score between 0 and 100, reflecting the overall quality of the response"
2241
- },
2242
- score_justification: {
2243
- type: "string",
2244
- description: "An explanation of the rationale behind the assigned score, considering both accuracy and fluency"
2245
- }
2246
- }
2247
- }
2248
- }
2249
- }
2250
- ]
2251
- };
2252
- if (standard === "wida") {
2253
- const wida_level = {
2254
- type: "number",
2255
- enum: [1, 2, 3, 4, 5, 6],
2256
- 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
2257
-
2258
- 1 - Entering
2259
- 2 - Emerging
2260
- 3 - Developing
2261
- 4 - Expanding
2262
- 5 - Bridging
2263
- 6 - Reaching
2264
-
2265
- 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.
2266
- `
2267
- };
2268
- const wida_justification = {
2269
- type: "string",
2270
- description: `An explanation of the rationale behind the assigned WIDA level of the response, considering both accuracy and fluency. WRITE THIS IN ENGLISH!`
2271
- };
2272
- tool.tools[0].function.parameters.required.push("wida_level");
2273
- tool.tools[0].function.parameters.required.push("wida_justification");
2274
- tool.tools[0].function.parameters.properties.wida_level = wida_level;
2275
- tool.tools[0].function.parameters.properties.wida_justification = wida_justification;
2276
- } else {
2277
- const actfl_level = {
2278
- type: "string",
2279
- enum: ["NL", "NM", "NH", "IL", "IM", "IH", "AL", "AM", "AH", "S", "D"],
2280
- 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"
2281
- };
2282
- const actfl_justification = {
2283
- type: "string",
2284
- description: "An explanation of the rationale behind the assigned ACTFL level, considering both accuracy and fluency"
2285
- };
2286
- tool.tools[0].function.parameters.required.push("actfl_level");
2287
- tool.tools[0].function.parameters.required.push("actfl_justification");
2288
- tool.tools[0].function.parameters.properties.actfl_level = actfl_level;
2289
- tool.tools[0].function.parameters.properties.actfl_justification = actfl_justification;
2290
- }
2291
- return tool;
2292
- };
2293
-
2294
- // src/hooks/useActivity.ts
2295
- import { useEffect as useEffect2 } from "react";
2296
-
2297
- // src/services/add-grading-standard.ts
2298
- var addGradingStandardLog = async (gradingStandard, userId) => {
2299
- logGradingStandardLog(gradingStandard);
2300
- const path = `users/${userId}/grading_standard_logs`;
2301
- await api.addDoc(path, gradingStandard);
2302
- };
2303
-
2304
- // src/hooks/useActivityTracker.ts
2305
- import { v4 as v42 } from "uuid";
2306
- function useActivityTracker({ userId }) {
2307
- const trackActivity = async ({
2308
- activityName,
2309
- activityType,
2310
- id = v42(),
2311
- language = ""
2312
- }) => {
2313
- if (userId) {
2314
- const { doc: doc2, serverTimestamp: serverTimestamp2, setDoc: setDoc2 } = api.accessHelpers();
2315
- const activityRef = doc2(`users/${userId}/activity/${id}`);
2316
- const timestamp = serverTimestamp2();
2317
- await setDoc2(activityRef, {
2318
- name: activityName,
2319
- type: activityType,
2320
- lastSeen: timestamp,
2321
- id,
2322
- language
2323
- });
2324
- }
2325
- };
2326
- return {
2327
- trackActivity
2328
- };
2329
- }
2330
-
2331
- // src/hooks/useActivity.ts
2332
- function useActivity({
2333
- id,
2334
- isAssignment,
2335
- onAssignmentSubmitted,
2336
- ltiData
2337
- }) {
2338
- var _a, _b;
2339
- const { queryClient, user } = useSpeakableApi();
2340
- const userId = user.auth.uid;
2341
- const assignmentQuery = useAssignment({
2342
- assignmentId: id,
2343
- userId,
2344
- enabled: isAssignment
2345
- });
2346
- const activeAssignment = assignmentQuery.data;
2347
- const setId = isAssignment ? (_a = activeAssignment == null ? void 0 : activeAssignment.setId) != null ? _a : "" : id;
2348
- const querySet = useSet({ setId });
2349
- const setData = querySet.data;
2350
- const assignmentContent = activeAssignment == null ? void 0 : activeAssignment.content;
2351
- const assignmentWeights = activeAssignment == null ? void 0 : activeAssignment.weights;
2352
- const setContent = setData == null ? void 0 : setData.content;
2353
- const setWeights = setData == null ? void 0 : setData.weights;
2354
- const contentCardsToUse = isAssignment ? assignmentContent != null ? assignmentContent : setContent : setContent;
2355
- const weightsToUse = isAssignment ? assignmentWeights != null ? assignmentWeights : setWeights : setWeights;
2356
- const activityId = isAssignment ? (_b = activeAssignment == null ? void 0 : activeAssignment.id) != null ? _b : "" : setId;
2357
- const { cardsObject, cardsQueries, cards } = useCards({
2358
- cardIds: contentCardsToUse != null ? contentCardsToUse : [],
2359
- enabled: querySet.isSuccess,
2360
- asObject: true
2361
- });
2362
- const scoreQuery = useScore({
2363
- isAssignment,
2364
- activityId: id,
2365
- userId,
2366
- courseId: activeAssignment == null ? void 0 : activeAssignment.courseId,
2367
- googleClassroomUserId: user.profile.googleClassroomUserId,
2368
- enabled: isAssignment ? assignmentQuery.isSuccess : querySet.isSuccess
2369
- });
2370
- const { mutationUpdateScore } = useUpdateScore();
2371
- const { mutationUpdateCardScore } = useUpdateCardScore({
2372
- activityId,
2373
- isAssignment,
2374
- userId,
2375
- cardIds: contentCardsToUse != null ? contentCardsToUse : [],
2376
- weights: weightsToUse != null ? weightsToUse : {}
2377
- });
2378
- const { mutationClearScore } = useClearScore();
2379
- const { submitAssignmentScore: submitAssignmentScore2 } = useSubmitAssignmentScore({
2380
- onAssignmentSubmitted,
2381
- studentName: user.profile.displayName
2382
- });
2383
- const { submitPracticeScore: submitPracticeScore2 } = useSubmitPracticeScore();
2384
- const handleUpdateScore = (data) => {
2385
- mutationUpdateScore.mutate({
2386
- data,
2387
- isAssignment,
2388
- activityId,
2389
- userId
2390
- });
2391
- };
2392
- const handleUpdateCardScore = (cardId, cardScore) => {
2393
- mutationUpdateCardScore.mutate({ cardId, cardScore });
2394
- if (cardScore.proficiency_level) {
2395
- logGradingStandardEntry({
2396
- type: cardScore.proficiency_level.standardId,
2397
- cardId,
2398
- gradingStandard: cardScore.proficiency_level
2399
- });
2400
- } else if (cardScore.wida || cardScore.actfl) {
2401
- logGradingStandardEntry({
2402
- type: cardScore.wida ? "wida" : "actfl",
2403
- cardId,
2404
- gradingStandard: cardScore.wida || cardScore.actfl || { level: "", justification: "" }
2405
- });
2406
- }
2407
- };
2408
- const onClearScore = ({
2409
- cardId,
2410
- wasCompleted = true
2411
- }) => {
2412
- var _a2, _b2;
2413
- const currentCard = cardsObject == null ? void 0 : cardsObject[cardId];
2414
- if ((currentCard == null ? void 0 : currentCard.type) === "MULTIPLE_CHOICE" /* MULTIPLE_CHOICE */ || (currentCard == null ? void 0 : currentCard.type) === "READ_REPEAT" /* READ_REPEAT */) {
2415
- return;
2416
- }
2417
- const queryKeys = scoreQueryKeys.byId(activityId);
2418
- const activeCardScores = (_b2 = (_a2 = queryClient.getQueryData(queryKeys)) == null ? void 0 : _a2.cards) == null ? void 0 : _b2[cardId];
2419
- if (activeCardScores === void 0) return;
2420
- mutationClearScore.mutate({
2421
- isAssignment,
2422
- activityId,
2423
- cardScores: activeCardScores,
2424
- cardId,
2425
- userId
2426
- });
2427
- };
2428
- const onSubmitScore = async () => {
2429
- 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;
2430
- try {
2431
- let results;
2432
- if (isAssignment) {
2433
- const cardScores = ((_a2 = scoreQuery.data) == null ? void 0 : _a2.cards) || {};
2434
- const hasPendingReview = Object.values(cardScores).some(
2435
- (cardScore) => cardScore.status === "pending_review"
2436
- );
2437
- results = await submitAssignmentScore2({
2438
- assignment: {
2439
- id: (_c = (_b2 = assignmentQuery.data) == null ? void 0 : _b2.id) != null ? _c : "",
2440
- name: (_e = (_d = assignmentQuery.data) == null ? void 0 : _d.name) != null ? _e : "",
2441
- owners: (_g = (_f = assignmentQuery.data) == null ? void 0 : _f.owners) != null ? _g : [],
2442
- courseId: (_i = (_h = assignmentQuery.data) == null ? void 0 : _h.courseId) != null ? _i : "",
2443
- courseWorkId: (_k = (_j = assignmentQuery.data) == null ? void 0 : _j.courseWorkId) != null ? _k : "",
2444
- isAssessment: (_m = (_l = assignmentQuery.data) == null ? void 0 : _l.isAssessment) != null ? _m : false,
2445
- maxPoints: (_o = (_n = assignmentQuery.data) == null ? void 0 : _n.maxPoints) != null ? _o : 0
2446
- },
2447
- userId,
2448
- cardIds: contentCardsToUse != null ? contentCardsToUse : [],
2449
- scores: scoreQuery.data,
2450
- weights: weightsToUse != null ? weightsToUse : {},
2451
- status: hasPendingReview ? "PENDING_REVIEW" : "FINALIZED"
2452
- });
2453
- if ((_p = assignmentQuery.data) == null ? void 0 : _p.ltiDeeplink) {
2454
- submitLTIScore({
2455
- maxPoints: (_q = assignmentQuery.data) == null ? void 0 : _q.maxPoints,
2456
- score: (_s = (_r = scoreQuery.data) == null ? void 0 : _r.score) != null ? _s : 0,
2457
- SERVICE_KEY: (_t = ltiData == null ? void 0 : ltiData.serviceKey) != null ? _t : "",
2458
- lineItemId: (_u = ltiData == null ? void 0 : ltiData.lineItemId) != null ? _u : "",
2459
- lti_id: (_v = ltiData == null ? void 0 : ltiData.lti_id) != null ? _v : ""
2460
- });
2461
- }
2462
- } else {
2463
- results = await submitPracticeScore2({
2464
- setId: (_x = (_w = querySet.data) == null ? void 0 : _w.id) != null ? _x : "",
2465
- userId,
2466
- scores: scoreQuery.data
2467
- });
2468
- }
2469
- return results;
2470
- } catch (error) {
2471
- return {
2472
- success: false,
2473
- error
2474
- };
2475
- }
2476
- };
2477
- const logGradingStandardEntry = ({
2478
- cardId,
2479
- gradingStandard,
2480
- type
2481
- }) => {
2482
- var _a2, _b2, _c, _d, _e, _f, _g, _h, _i;
2483
- const card = cardsObject == null ? void 0 : cardsObject[cardId];
2484
- const scoresObject = queryClient.getQueryData(scoreQueryKeys.byId(activityId));
2485
- const cardScore = (_a2 = scoresObject == null ? void 0 : scoresObject.cards) == null ? void 0 : _a2[cardId];
2486
- const serverTimestamp2 = api.helpers.serverTimestamp;
2487
- addGradingStandardLog(
2488
- {
2489
- assignmentId: (_b2 = activeAssignment == null ? void 0 : activeAssignment.id) != null ? _b2 : "",
2490
- courseId: (_c = activeAssignment == null ? void 0 : activeAssignment.courseId) != null ? _c : "",
2491
- teacherId: (_d = activeAssignment == null ? void 0 : activeAssignment.owners[0]) != null ? _d : "",
2492
- setId: (_e = setData == null ? void 0 : setData.id) != null ? _e : "",
2493
- cardId,
2494
- level: gradingStandard.level,
2495
- justification: gradingStandard.justification,
2496
- transcript: (_f = cardScore == null ? void 0 : cardScore.transcript) != null ? _f : "",
2497
- audioUrl: (_g = cardScore == null ? void 0 : cardScore.audio) != null ? _g : "",
2498
- prompt: (_h = card == null ? void 0 : card.prompt) != null ? _h : "",
2499
- responseType: (card == null ? void 0 : card.type) === "RESPOND_WRITE" /* RESPOND_WRITE */ ? "written" : "spoken",
2500
- type,
2501
- dateMade: serverTimestamp2(),
2502
- language: (_i = card == null ? void 0 : card.language) != null ? _i : ""
2503
- },
2504
- userId
2505
- );
2506
- };
2507
- useEffect2(() => {
2508
- if (isAssignment) {
2509
- logOpenAssignment({ assignmentId: id });
2510
- } else {
2511
- logOpenActivityPreview({ setId: id });
2512
- }
2513
- }, []);
2514
- useInitActivity({
2515
- assignment: activeAssignment != null ? activeAssignment : void 0,
2516
- set: setData != null ? setData : void 0,
2517
- enabled: !!setData,
2518
- userId
2519
- });
2520
- return {
2521
- set: {
2522
- data: setData,
2523
- query: querySet
2524
- },
2525
- cards: {
2526
- data: cardsObject,
2527
- query: cardsQueries,
2528
- cardsArray: cards
2529
- },
2530
- assignment: {
2531
- data: isAssignment ? activeAssignment : void 0,
2532
- query: assignmentQuery
2533
- },
2534
- scores: {
2535
- data: scoreQuery.data,
2536
- query: scoreQuery,
2537
- actions: {
2538
- update: handleUpdateScore,
2539
- clear: onClearScore,
2540
- submit: onSubmitScore,
2541
- updateCard: handleUpdateCardScore,
2542
- logGradingStandardEntry
2543
- }
2544
- }
2545
- };
2546
- }
2547
- var useInitActivity = ({
2548
- assignment,
2549
- set,
2550
- enabled,
2551
- userId
2552
- }) => {
2553
- const { trackActivity } = useActivityTracker({ userId });
2554
- const init = () => {
2555
- var _a, _b, _c, _d, _e, _f, _g;
2556
- if (!enabled) return;
2557
- if (!assignment) {
2558
- trackActivity({
2559
- activityName: (_a = set == null ? void 0 : set.name) != null ? _a : "",
2560
- activityType: "set",
2561
- id: set == null ? void 0 : set.id,
2562
- language: set == null ? void 0 : set.language
2563
- });
2564
- } else if (assignment.name) {
2565
- trackActivity({
2566
- activityName: assignment.name,
2567
- activityType: assignment.isAssessment ? "assessment" : "assignment",
2568
- id: assignment.id,
2569
- language: set == null ? void 0 : set.language
2570
- });
2571
- }
2572
- if (set == null ? void 0 : set.public) {
2573
- (_d = (_c = (_b = api).httpsCallable) == null ? void 0 : _c.call(_b, "onSetOpened")) == null ? void 0 : _d({
2574
- setId: set.id,
2575
- language: set.language
2576
- });
2577
- }
2578
- (_g = (_f = (_e = api).httpsCallable) == null ? void 0 : _f.call(_e, "updateAlgoliaIndex")) == null ? void 0 : _g({
2579
- updatePlays: true,
2580
- objectID: set == null ? void 0 : set.id
2581
- });
2582
- };
2583
- useEffect2(() => {
2584
- init();
2585
- }, [set]);
2586
- };
2587
- var submitLTIScore = async ({
2588
- maxPoints,
2589
- score,
2590
- SERVICE_KEY,
2591
- lineItemId,
2592
- lti_id
2593
- }) => {
2594
- var _a, _b, _c;
2595
- try {
2596
- if (!SERVICE_KEY || !lineItemId || !lti_id) {
2597
- throw new Error("Missing required LTI credentials");
2598
- }
2599
- const earnedPoints = score ? score / 100 * maxPoints : 0;
2600
- const { data } = await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "submitLTIAssignmentScore")) == null ? void 0 : _c({
2601
- SERVICE_KEY,
2602
- scoreData: {
2603
- lineItemId,
2604
- userId: lti_id,
2605
- maxPoints,
2606
- earnedPoints
2607
- }
2608
- }));
2609
- return { success: true, data };
2610
- } catch (error) {
2611
- console.error("Failed to submit LTI score:", error);
2612
- return {
2613
- success: false,
2614
- error: error instanceof Error ? error : new Error("Unknown error occurred")
2615
- };
2616
- }
2617
- };
2618
-
2619
- // src/hooks/useCredits.ts
2620
- import { useQuery as useQuery5 } from "@tanstack/react-query";
2621
- var creditQueryKeys = {
2622
- userCredits: (uid) => ["userCredits", uid]
2623
- };
2624
- var useUserCredits = () => {
2625
- const { user } = useSpeakableApi();
2626
- const email = user.auth.email;
2627
- const uid = user.auth.uid;
2628
- const query2 = useQuery5({
2629
- queryKey: creditQueryKeys.userCredits(uid),
2630
- queryFn: () => fetchUserCredits({ uid, email }),
2631
- enabled: !!uid,
2632
- refetchInterval: 1e3 * 60 * 5
2633
- });
2634
- return {
2635
- ...query2
2636
- };
2637
- };
2638
- var fetchUserCredits = async ({ uid, email }) => {
2639
- if (!uid) {
2640
- throw new Error("User ID is required");
2641
- }
2642
- const contractSnap = await api.getDoc(`creditContracts/${uid}`);
2643
- if (contractSnap.data == null) {
2644
- return {
2645
- id: uid,
2646
- userId: uid,
2647
- email,
2648
- effectivePlanId: "free_tier",
2649
- status: "inactive",
2650
- isUnlimited: false,
2651
- creditsAvailable: 100,
2652
- creditsAllocatedThisPeriod: 100,
2653
- topOffCreditsAvailable: 0,
2654
- topOffCreditsTotal: 0,
2655
- allocationSource: "free_tier",
2656
- sourceDetails: {},
2657
- periodStart: null,
2658
- periodEnd: null,
2659
- planTermEndTimestamp: null,
2660
- ownerType: "individual",
2661
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2662
- lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString()
2663
- };
2664
- }
2665
- const contractData = contractSnap.data;
2666
- const monthlyCredits = (contractData == null ? void 0 : contractData.creditsAvailable) || 0;
2667
- const topOffCredits = (contractData == null ? void 0 : contractData.topOffCreditsAvailable) || 0;
2668
- const totalCredits = monthlyCredits + topOffCredits;
2669
- return {
2670
- id: contractSnap.id,
2671
- ...contractData,
2672
- // Add computed total for convenience
2673
- totalCreditsAvailable: totalCredits
2674
- };
2675
- };
2676
-
2677
- // src/hooks/useOrganizationAccess.ts
2678
- import { useQuery as useQuery6 } from "@tanstack/react-query";
2679
- var useOrganizationAccess = () => {
2680
- const { user } = useSpeakableApi();
2681
- const email = user.auth.email;
2682
- const query2 = useQuery6({
2683
- queryKey: ["organizationAccess", email],
2684
- queryFn: async () => {
2685
- if (!email) {
2686
- return {
2687
- hasUnlimitedAccess: false,
2688
- subscriptionId: null,
2689
- organizationId: null,
2690
- organizationName: null,
2691
- subscriptionEndDate: null,
2692
- accessType: "individual"
2693
- };
2694
- }
2695
- return getOrganizationAccess(email);
2696
- },
2697
- enabled: !!email,
2698
- // Only run query if we have a user email
2699
- staleTime: 5 * 60 * 1e3,
2700
- // Consider data fresh for 5 minutes
2701
- gcTime: 10 * 60 * 1e3,
2702
- // Keep in cache for 10 minutes
2703
- retry: 2
2704
- // Retry failed requests twice
2705
- });
2706
- return {
2707
- ...query2
2708
- };
2709
- };
2710
- var getOrganizationAccess = async (email) => {
2711
- const { limit: limit2, where: where2 } = api.accessQueryConstraints();
2712
- try {
2713
- const organizationSnapshot = await api.getDocs(
2714
- "organizations",
2715
- where2("members", "array-contains", email),
2716
- where2("masterSubscriptionStatus", "==", "active"),
2717
- limit2(1)
2718
- );
2719
- if (!organizationSnapshot.empty) {
2720
- const orgData = organizationSnapshot.data[0];
2721
- return {
2722
- hasUnlimitedAccess: true,
2723
- subscriptionId: orgData == null ? void 0 : orgData.masterSubscriptionId,
2724
- organizationId: orgData.id,
2725
- organizationName: orgData.name || "Unknown Organization",
2726
- subscriptionEndDate: orgData.masterSubscriptionEndDate || null,
2727
- accessType: "organization"
2728
- };
2729
- }
2730
- const institutionSnapshot = await api.getDocs(
2731
- "institution_subscriptions",
2732
- where2("users", "array-contains", email),
2733
- where2("active", "==", true),
2734
- limit2(1)
2735
- );
2736
- if (!institutionSnapshot.empty) {
2737
- const institutionData = institutionSnapshot.data[0];
2738
- const isUnlimited = (institutionData == null ? void 0 : institutionData.plan) === "organization" || (institutionData == null ? void 0 : institutionData.plan) === "school_starter";
2739
- return {
2740
- hasUnlimitedAccess: isUnlimited,
2741
- subscriptionId: institutionData.id,
2742
- organizationId: institutionData == null ? void 0 : institutionData.institutionId,
2743
- organizationName: institutionData.name || institutionData.institutionId || "Legacy Institution",
2744
- subscriptionEndDate: institutionData.endDate || null,
2745
- accessType: "institution_subscriptions"
2746
- };
2747
- }
2748
- return {
2749
- hasUnlimitedAccess: false,
2750
- subscriptionId: null,
2751
- organizationId: null,
2752
- organizationName: null,
2753
- subscriptionEndDate: null,
2754
- accessType: "individual"
2755
- };
2756
- } catch (error) {
2757
- console.error("Error checking organization access:", error);
2758
- return {
2759
- hasUnlimitedAccess: false,
2760
- subscriptionId: null,
2761
- organizationId: null,
2762
- organizationName: null,
2763
- subscriptionEndDate: null,
2764
- accessType: "individual"
2765
- };
2766
- }
2767
- };
2768
-
2769
- // src/hooks/useUpdateStudentVoc.ts
2770
- var useUpdateStudentVocab = (page) => {
2771
- const { user } = useSpeakableApi();
2772
- const currentUserId = user == null ? void 0 : user.auth.uid;
2773
- if (!page || !currentUserId || !page.target_text || !page.language) {
2774
- return {
2775
- studentVocabMarkVoiceSuccess: void 0,
2776
- studentVocabMarkVoiceFail: void 0
2777
- };
2778
- }
2779
- const getDataObject = () => {
2780
- var _a, _b;
2781
- const { serverTimestamp: serverTimestamp2 } = api.accessHelpers();
2782
- const language = (_a = page.language) != null ? _a : "en";
2783
- const word = (_b = page.target_text) != null ? _b : "";
2784
- const phrase_length = getPhraseLength(word);
2785
- const wordHash = getWordHash(word, language);
2786
- const docPath = `users/${currentUserId}/vocab/${wordHash}`;
2787
- const communityPath = `checked-pronunciations/${wordHash}`;
2788
- const id = `${language}-${cleanString(word)}`;
2789
- const data = {
2790
- id,
2791
- word,
2792
- words: (word == null ? void 0 : word.split(" ")) || [],
2793
- wordHash,
2794
- language,
2795
- lastSeen: serverTimestamp2(),
2796
- phrase_length
2797
- };
2798
- return {
2799
- docPath,
2800
- communityPath,
2801
- data
2802
- };
2803
- };
2804
- const markVoiceSuccess = async () => {
2805
- const { docPath, communityPath, data } = getDataObject();
2806
- const { increment: increment2 } = api.accessQueryConstraints();
2807
- const { serverTimestamp: serverTimestamp2 } = api.accessHelpers();
2808
- data.voiceSuccess = increment2(1);
2809
- try {
2810
- await api.updateDoc(docPath, data);
2811
- } catch (error) {
2812
- if (error instanceof Error && error.message === "not-found") {
2813
- data.firstSeen = serverTimestamp2();
2814
- await api.setDoc(docPath, data, { merge: true });
2815
- } else {
2816
- console.log(error);
2817
- }
2818
- }
2819
- try {
2820
- data.pronunciations = increment2(1);
2821
- await api.setDoc(communityPath, data, { merge: true });
2822
- } catch (error) {
2823
- console.log(error);
2824
- }
2825
- };
2826
- const markVoiceFail = async () => {
2827
- const { docPath, communityPath, data } = getDataObject();
2828
- const { increment: increment2 } = api.accessQueryConstraints();
2829
- const { serverTimestamp: serverTimestamp2 } = api.accessHelpers();
2830
- data.voiceFail = increment2(1);
2831
- try {
2832
- await api.updateDoc(docPath, data);
2833
- } catch (error) {
2834
- if (error instanceof Error && error.message === "not-found") {
2835
- data.firstSeen = serverTimestamp2();
2836
- await api.setDoc(docPath, data, { merge: true });
2837
- } else {
2838
- console.log(error);
2839
- }
2840
- }
2841
- try {
2842
- data.fails = increment2(1);
2843
- await api.setDoc(communityPath, data, { merge: true });
2844
- } catch (error) {
2845
- console.log(error);
2846
- }
2847
- };
2848
- return {
2849
- studentVocabMarkVoiceSuccess: markVoiceSuccess,
2850
- studentVocabMarkVoiceFail: markVoiceFail
2851
- };
2852
- };
2853
-
2854
- // src/hooks/useActivityFeedbackAccess.ts
2855
- import { useQuery as useQuery7 } from "@tanstack/react-query";
2856
- var activityFeedbackAccessQueryKeys = {
2857
- activityFeedbackAccess: (args) => ["activityFeedbackAccess", ...Object.values(args)]
2858
- };
2859
- var useActivityFeedbackAccess = ({
2860
- aiEnabled = false,
2861
- isActivityRoute = false
2862
- }) => {
2863
- var _a, _b, _c;
2864
- const { user } = useSpeakableApi();
2865
- const uid = user.auth.uid;
2866
- const isTeacher = (_a = user.profile) == null ? void 0 : _a.isTeacher;
2867
- const isStudent = (_b = user.profile) == null ? void 0 : _b.isStudent;
2868
- const userRoles = ((_c = user.profile) == null ? void 0 : _c.roles) || [];
2869
- const query2 = useQuery7({
2870
- queryKey: activityFeedbackAccessQueryKeys.activityFeedbackAccess({
2871
- aiEnabled,
2872
- isActivityRoute
2873
- }),
2874
- queryFn: async () => {
2875
- var _a2, _b2, _c2;
2876
- if (!uid) {
2877
- return {
2878
- canAccessFeedback: false,
2879
- reason: "Missing user ID",
2880
- isUnlimited: false,
2881
- accessType: "none"
2882
- };
2883
- }
2884
- try {
2885
- if (aiEnabled) {
2886
- return {
2887
- canAccessFeedback: true,
2888
- reason: "AI feedback enabled",
2889
- isUnlimited: true,
2890
- accessType: "ai_enabled"
2891
- };
2892
- }
2893
- if (isTeacher || userRoles.includes("ADMIN")) {
2894
- return {
2895
- canAccessFeedback: true,
2896
- reason: "Teacher preview access",
2897
- isUnlimited: true,
2898
- accessType: "teacher_preview"
2899
- };
2900
- }
2901
- if (isStudent && isActivityRoute) {
2902
- try {
2903
- const result = await ((_c2 = (_b2 = (_a2 = api).httpsCallable) == null ? void 0 : _b2.call(_a2, "checkStudentTeacherPlan")) == null ? void 0 : _c2({
2904
- studentId: uid
2905
- }));
2906
- const planCheckResult = result.data;
2907
- if (planCheckResult.canAccessFeedback) {
2908
- return {
2909
- canAccessFeedback: true,
2910
- reason: planCheckResult.reason || "Student access via teacher with active plan",
2911
- isUnlimited: planCheckResult.hasTeacherWithUnlimitedAccess,
2912
- accessType: "student_with_teacher_plan"
2913
- };
2914
- } else {
2915
- return {
2916
- canAccessFeedback: false,
2917
- reason: planCheckResult.reason || "No teacher with active plan found",
2918
- isUnlimited: false,
2919
- accessType: "none"
2920
- };
2921
- }
2922
- } catch (error) {
2923
- console.error("Error checking student teacher plan:", error);
2924
- return {
2925
- canAccessFeedback: false,
2926
- reason: "Error checking teacher plans",
2927
- isUnlimited: false,
2928
- accessType: "none"
2929
- };
2930
- }
2931
- }
2932
- return {
2933
- canAccessFeedback: false,
2934
- reason: "No access permissions found for current context",
2935
- isUnlimited: false,
2936
- accessType: "none"
2937
- };
2938
- } catch (error) {
2939
- console.error("Error checking activity feedback access:", error);
2940
- return {
2941
- canAccessFeedback: false,
2942
- reason: "Error checking access permissions",
2943
- isUnlimited: false,
2944
- accessType: "none"
2945
- };
2946
- }
2947
- },
2948
- enabled: !!uid,
2949
- staleTime: 5 * 60 * 1e3,
2950
- // 5 minutes
2951
- gcTime: 10 * 60 * 1e3
2952
- // 10 minutes
2953
- });
2954
- return {
2955
- ...query2
2956
- };
2957
- };
2958
-
2959
- // src/hooks/useOpenAI.ts
2960
- var useBaseOpenAI = ({
2961
- onTranscriptSuccess,
2962
- onTranscriptError,
2963
- onCompletionSuccess,
2964
- onCompletionError,
2965
- aiEnabled,
2966
- submitAudioResponse,
2967
- uploadAudioAndGetTranscript,
2968
- onGetAudioUrlAndTranscript
2969
- }) => {
2970
- const { user, queryClient } = useSpeakableApi();
2971
- const currentUserId = user.auth.uid;
2972
- const { data: feedbackAccess } = useActivityFeedbackAccess({
2973
- aiEnabled
2974
- });
2975
- const getTranscript = async (audioUrl, language) => {
2976
- var _a, _b;
2977
- try {
2978
- const getAssemblyAITranscript = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "transcribeAssemblyAIAudio");
2979
- const response = await (getAssemblyAITranscript == null ? void 0 : getAssemblyAITranscript({
2980
- audioUrl,
2981
- language
2982
- }));
2983
- const transcript = response == null ? void 0 : response.data;
2984
- return transcript;
2985
- } catch (error) {
2986
- console.log("error", error);
2987
- onTranscriptError({
2988
- type: "TRANSCRIPT",
2989
- message: (error == null ? void 0 : error.message) || "Error getting transcript"
2990
- });
2991
- throw new Error(error);
2992
- }
2993
- };
2994
- const getFreeResponseCompletion = async (messages, isFreeResponse, feedbackLanguage, gradingStandard = "actfl") => {
2995
- var _a, _b, _c, _d, _e;
2996
- const responseTool = getRespondCardTool({
2997
- language: feedbackLanguage,
2998
- standard: gradingStandard
2999
- });
3000
- try {
3001
- const createChatCompletion = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "createChatCompletion");
3002
- const {
3003
- data: {
3004
- response,
3005
- prompt_tokens = 0,
3006
- completion_tokens = 0,
3007
- success: aiSuccess = false
3008
- // the AI was able to generate a response
3009
- }
3010
- } = await (createChatCompletion == null ? void 0 : createChatCompletion({
3011
- chat: {
3012
- model: isFreeResponse ? "gpt-4-1106-preview" : "gpt-3.5-turbo-1106",
3013
- messages,
3014
- temperature: 0.7,
3015
- ...responseTool
3016
- },
3017
- type: isFreeResponse ? "LONG_RESPONSE" : "SHORT_RESPONSE"
3018
- }));
3019
- 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) || "{}");
3020
- const result = {
3021
- ...functionArguments,
3022
- prompt_tokens,
3023
- completion_tokens,
3024
- aiSuccess
3025
- };
3026
- onCompletionSuccess(result);
3027
- return result;
3028
- } catch (error) {
3029
- onCompletionError({
3030
- type: "COMPLETION",
3031
- message: (error == null ? void 0 : error.message) || "Error getting completion"
3032
- });
3033
- throw new Error(error);
3034
- }
3035
- };
3036
- const getFeedback = async ({
3037
- cardId,
3038
- language = "en",
3039
- // required
3040
- writtenResponse = null,
3041
- // if the type = RESPOND_WRITE
3042
- audio = null,
3043
- autoGrade = true,
3044
- file = null
3045
- }) => {
3046
- try {
3047
- if (!(feedbackAccess == null ? void 0 : feedbackAccess.canAccessFeedback)) {
3048
- const result = {
3049
- noFeedbackAvailable: true,
3050
- success: true,
3051
- reason: (feedbackAccess == null ? void 0 : feedbackAccess.reason) || "No feedback access",
3052
- accessType: (feedbackAccess == null ? void 0 : feedbackAccess.accessType) || "none"
3053
- };
3054
- onCompletionSuccess(result);
3055
- return result;
3056
- }
3057
- let transcript;
3058
- let audioUrl = void 0;
3059
- if (writtenResponse) {
3060
- transcript = writtenResponse;
3061
- onTranscriptSuccess(writtenResponse);
3062
- } else if (typeof audio === "string" && file) {
3063
- if (feedbackAccess == null ? void 0 : feedbackAccess.canAccessFeedback) {
3064
- transcript = await getTranscript(audio, language);
3065
- audioUrl = audio;
3066
- onTranscriptSuccess(transcript);
3067
- } else {
3068
- console.info(
3069
- `Transcript not available: ${(feedbackAccess == null ? void 0 : feedbackAccess.reason) || "No feedback access"}`
3070
- );
3071
- }
3072
- } else {
3073
- const response = await uploadAudioAndGetTranscript(audio || "", language);
3074
- transcript = response.transcript;
3075
- audioUrl = response.audioUrl;
3076
- }
3077
- onGetAudioUrlAndTranscript == null ? void 0 : onGetAudioUrlAndTranscript({ transcript, audioUrl });
3078
- if (feedbackAccess == null ? void 0 : feedbackAccess.canAccessFeedback) {
3079
- const results = await getAIResponse({
3080
- cardId,
3081
- transcript: transcript || ""
3082
- });
3083
- let output = results;
3084
- if (!autoGrade) {
3085
- output = {
3086
- ...output,
3087
- noFeedbackAvailable: true,
3088
- success: true
3089
- };
3090
- }
3091
- onCompletionSuccess(output);
3092
- return output;
3093
- } else {
3094
- const result = {
3095
- noFeedbackAvailable: true,
3096
- success: true,
3097
- reason: (feedbackAccess == null ? void 0 : feedbackAccess.reason) || "No feedback access",
3098
- accessType: (feedbackAccess == null ? void 0 : feedbackAccess.accessType) || "none"
3099
- };
3100
- onCompletionSuccess(result);
3101
- return result;
3102
- }
3103
- } catch (error) {
3104
- console.error("Error getting feedback:", error);
3105
- throw new Error(error);
3106
- }
3107
- };
3108
- const getAIResponse = async ({ cardId, transcript }) => {
3109
- var _a, _b, _c, _d, _e;
3110
- try {
3111
- const getGeminiFeedback = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "callGetFeedback");
3112
- const getProficiencyEstimate = (_d = (_c = api).httpsCallable) == null ? void 0 : _d.call(_c, "getProficiencyEstimate");
3113
- const card = getCardFromCache({
3114
- cardId,
3115
- queryClient
3116
- });
3117
- let feedbackData;
3118
- let proficiencyData = {};
3119
- if (card && card.grading_method === "manual") {
3120
- } else if (card && card.grading_method !== "standards_based") {
3121
- const [geminiResult, proficiencyResult] = await Promise.all([
3122
- getGeminiFeedback == null ? void 0 : getGeminiFeedback({
3123
- cardId,
3124
- studentId: currentUserId,
3125
- studentResponse: transcript
3126
- }),
3127
- getProficiencyEstimate == null ? void 0 : getProficiencyEstimate({
3128
- cardId,
3129
- studentId: currentUserId,
3130
- studentResponse: transcript
3131
- })
3132
- ]);
3133
- proficiencyData = (proficiencyResult == null ? void 0 : proficiencyResult.data) || {};
3134
- feedbackData = {
3135
- ...(_e = geminiResult == null ? void 0 : geminiResult.data) != null ? _e : {},
3136
- // @ts-ignore
3137
- proficiency_level: (proficiencyData == null ? void 0 : proficiencyData.proficiency_level) || null
3138
- };
3139
- } else {
3140
- const geminiResult = await (getGeminiFeedback == null ? void 0 : getGeminiFeedback({
3141
- cardId,
3142
- studentId: currentUserId,
3143
- studentResponse: transcript
3144
- }));
3145
- feedbackData = geminiResult == null ? void 0 : geminiResult.data;
3146
- }
3147
- const results = {
3148
- ...feedbackData,
3149
- // ...proficiencyData,
3150
- aiSuccess: true,
3151
- promptSuccess: (feedbackData == null ? void 0 : feedbackData.success) || false,
3152
- transcript
3153
- };
3154
- return results;
3155
- } catch (error) {
3156
- onCompletionError({
3157
- type: "AI_FEEDBACK",
3158
- message: (error == null ? void 0 : error.message) || "Error getting ai feedback"
3159
- });
3160
- throw new Error(error);
3161
- }
3162
- };
3163
- return {
3164
- submitAudioResponse,
3165
- uploadAudioAndGetTranscript,
3166
- getTranscript,
3167
- getFreeResponseCompletion,
3168
- getFeedback
3169
- };
3170
- };
3171
-
3172
- // src/lib/create-firebase-client-native.ts
3173
- import {
3174
- getDoc,
3175
- getDocs,
3176
- addDoc,
3177
- setDoc,
3178
- updateDoc,
3179
- deleteDoc,
3180
- runTransaction,
3181
- writeBatch,
3182
- doc,
3183
- collection,
3184
- query,
3185
- serverTimestamp,
3186
- orderBy,
3187
- limit,
3188
- startAt,
3189
- startAfter,
3190
- endAt,
3191
- endBefore,
3192
- where,
3193
- increment
3194
- } from "@react-native-firebase/firestore";
3195
451
 
3196
452
  // src/lib/create-firebase-client.ts
3197
453
  function createFsClientBase({
@@ -3244,75 +500,6 @@ var createFsClientNative = ({ db, httpsCallable, logEvent }) => {
3244
500
  });
3245
501
  };
3246
502
  export {
3247
- ActivityPageType,
3248
- BASE_MULTIPLE_CHOICE_FIELD_VALUES,
3249
- BASE_REPEAT_FIELD_VALUES,
3250
- BASE_RESPOND_FIELD_VALUES,
3251
- FeedbackTypesCard,
3252
- FsCtx,
3253
- LENIENCY_OPTIONS,
3254
- LeniencyCard,
3255
- MULTIPLE_CHOICE_PAGE_ACTIVITY_TYPES,
3256
- REPEAT_PAGE_ACTIVITY_TYPES,
3257
- RESPOND_AUDIO_PAGE_ACTIVITY_TYPES,
3258
- RESPOND_PAGE_ACTIVITY_TYPES,
3259
- RESPOND_WRITE_PAGE_ACTIVITY_TYPES,
3260
- SPEAKABLE_NOTIFICATIONS,
3261
- STUDENT_LEVELS_OPTIONS,
3262
- SpeakableNotificationTypes,
3263
- SpeakableProvider,
3264
- VerificationCardStatus,
3265
- assignmentQueryKeys,
3266
- cardsQueryKeys,
3267
- checkIsMCPage,
3268
- checkIsMediaPage,
3269
- checkIsRepeatPage,
3270
- checkIsRespondAudioPage,
3271
- checkIsRespondPage,
3272
- checkIsRespondWrittenPage,
3273
- checkIsShortAnswerPage,
3274
- checkTypePageActivity,
3275
- cleanString,
3276
- createAssignmentRepo,
3277
- createCardRepo,
3278
- createFsClientNative as createFsClient,
3279
- createSetRepo,
3280
- creditQueryKeys,
3281
- debounce,
3282
- getCardFromCache,
3283
- getLabelPage,
3284
- getPagePrompt,
3285
- getPhraseLength,
3286
- getRespondCardTool,
3287
- getSetFromCache,
3288
- getTotalCompletedCards,
3289
- getWordHash,
3290
- purify,
3291
- refsCardsFiresotre,
3292
- refsSetsFirestore,
3293
- scoreQueryKeys,
3294
- setsQueryKeys,
3295
- updateCardInCache,
3296
- updateSetInCache,
3297
- useActivity,
3298
- useActivityFeedbackAccess,
3299
- useAssignment,
3300
- useBaseOpenAI,
3301
- useCards,
3302
- useClearScore,
3303
- useCreateCard,
3304
- useCreateCards,
3305
- useCreateNotification,
3306
- useGetCard,
3307
- useOrganizationAccess,
3308
- useScore,
3309
- useSet,
3310
- useSpeakableApi,
3311
- useSubmitAssignmentScore,
3312
- useSubmitPracticeScore,
3313
- useUpdateCardScore,
3314
- useUpdateScore,
3315
- useUpdateStudentVocab,
3316
- useUserCredits
503
+ createFsClientNative as createFsClient
3317
504
  };
3318
505
  //# sourceMappingURL=index.native.mjs.map