@speakableio/core 0.1.57 → 0.1.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.native.d.mts +93 -1
- package/dist/index.native.mjs +1342 -1327
- package/dist/index.native.mjs.map +1 -1
- package/dist/index.web.d.mts +93 -1
- package/dist/index.web.js +1342 -1327
- package/dist/index.web.js.map +1 -1
- package/package.json +1 -1
package/dist/index.web.js
CHANGED
|
@@ -363,890 +363,63 @@ function useAssignment({
|
|
|
363
363
|
});
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
-
// src/domains/
|
|
367
|
-
import { useMutation,
|
|
368
|
-
import { useMemo } from "react";
|
|
369
|
-
|
|
370
|
-
// src/domains/cards/card.constants.ts
|
|
371
|
-
var FeedbackTypesCard = /* @__PURE__ */ ((FeedbackTypesCard2) => {
|
|
372
|
-
FeedbackTypesCard2["SuggestedResponse"] = "suggested_response";
|
|
373
|
-
FeedbackTypesCard2["Wida"] = "wida";
|
|
374
|
-
FeedbackTypesCard2["GrammarInsights"] = "grammar_insights";
|
|
375
|
-
FeedbackTypesCard2["Actfl"] = "actfl";
|
|
376
|
-
FeedbackTypesCard2["ProficiencyLevel"] = "proficiency_level";
|
|
377
|
-
return FeedbackTypesCard2;
|
|
378
|
-
})(FeedbackTypesCard || {});
|
|
379
|
-
var LeniencyCard = /* @__PURE__ */ ((LeniencyCard2) => {
|
|
380
|
-
LeniencyCard2["CONFIDENCE"] = "confidence";
|
|
381
|
-
LeniencyCard2["EASY"] = "easy";
|
|
382
|
-
LeniencyCard2["NORMAL"] = "normal";
|
|
383
|
-
LeniencyCard2["HARD"] = "hard";
|
|
384
|
-
return LeniencyCard2;
|
|
385
|
-
})(LeniencyCard || {});
|
|
386
|
-
var LENIENCY_OPTIONS = [
|
|
387
|
-
{
|
|
388
|
-
label: "Build Confidence - most lenient",
|
|
389
|
-
value: "confidence" /* CONFIDENCE */
|
|
390
|
-
},
|
|
391
|
-
{
|
|
392
|
-
label: "Very Lenient",
|
|
393
|
-
value: "easy" /* EASY */
|
|
394
|
-
},
|
|
395
|
-
{
|
|
396
|
-
label: "Normal",
|
|
397
|
-
value: "normal" /* NORMAL */
|
|
398
|
-
},
|
|
399
|
-
{
|
|
400
|
-
label: "No leniency - most strict",
|
|
401
|
-
value: "hard" /* HARD */
|
|
402
|
-
}
|
|
403
|
-
];
|
|
404
|
-
var STUDENT_LEVELS_OPTIONS = [
|
|
405
|
-
{
|
|
406
|
-
label: "Beginner",
|
|
407
|
-
description: "Beginner Level: Just starting out. Can say a few basic words and phrases.",
|
|
408
|
-
value: "beginner"
|
|
409
|
-
},
|
|
410
|
-
{
|
|
411
|
-
label: "Elementary",
|
|
412
|
-
description: "Elementary Level: Can understand simple sentences and have very basic conversations.",
|
|
413
|
-
value: "elementary"
|
|
414
|
-
},
|
|
415
|
-
{
|
|
416
|
-
label: "Intermediate",
|
|
417
|
-
description: "Intermediate Level: Can talk about everyday topics and handle common situations.",
|
|
418
|
-
value: "intermediate"
|
|
419
|
-
},
|
|
420
|
-
{
|
|
421
|
-
label: "Advanced",
|
|
422
|
-
description: "Advanced Level: Can speak and understand with ease, and explain ideas clearly.",
|
|
423
|
-
value: "advanced"
|
|
424
|
-
},
|
|
425
|
-
{
|
|
426
|
-
label: "Fluent",
|
|
427
|
-
description: "Fluent Level: Speaks naturally and easily. Can use the language in work or school settings.",
|
|
428
|
-
value: "fluent"
|
|
429
|
-
},
|
|
430
|
-
{
|
|
431
|
-
label: "Native-like",
|
|
432
|
-
description: "Native-like Level: Understands and speaks like a native. Can discuss complex ideas accurately.",
|
|
433
|
-
value: "nativeLike"
|
|
434
|
-
}
|
|
435
|
-
];
|
|
436
|
-
var BASE_RESPOND_FIELD_VALUES = {
|
|
437
|
-
title: "",
|
|
438
|
-
allowRetries: true,
|
|
439
|
-
respondTime: 180,
|
|
440
|
-
maxCharacters: 1e3
|
|
441
|
-
};
|
|
442
|
-
var BASE_REPEAT_FIELD_VALUES = {
|
|
443
|
-
repeat: 1
|
|
444
|
-
};
|
|
445
|
-
var BASE_MULTIPLE_CHOICE_FIELD_VALUES = {
|
|
446
|
-
MCQType: "single",
|
|
447
|
-
answer: ["A"],
|
|
448
|
-
choices: [
|
|
449
|
-
{ option: "A", value: "Option A" },
|
|
450
|
-
{ option: "B", value: "Option B" },
|
|
451
|
-
{ option: "C", value: "Option C" }
|
|
452
|
-
]
|
|
453
|
-
};
|
|
454
|
-
var VerificationCardStatus = /* @__PURE__ */ ((VerificationCardStatus2) => {
|
|
455
|
-
VerificationCardStatus2["VERIFIED"] = "VERIFIED";
|
|
456
|
-
VerificationCardStatus2["WARNING"] = "WARNING";
|
|
457
|
-
VerificationCardStatus2["NOT_RECOMMENDED"] = "NOT_RECOMMENDED";
|
|
458
|
-
VerificationCardStatus2["NOT_WORKING"] = "NOT_WORKING";
|
|
459
|
-
VerificationCardStatus2["NOT_CHECKED"] = "NOT_CHECKED";
|
|
460
|
-
return VerificationCardStatus2;
|
|
461
|
-
})(VerificationCardStatus || {});
|
|
462
|
-
var CARDS_COLLECTION = "flashcards";
|
|
463
|
-
var refsCardsFiresotre = {
|
|
464
|
-
allCards: CARDS_COLLECTION,
|
|
465
|
-
card: (id) => `${CARDS_COLLECTION}/${id}`
|
|
466
|
-
};
|
|
366
|
+
// src/domains/assignment/hooks/score-hooks.ts
|
|
367
|
+
import { useMutation, useQuery as useQuery2 } from "@tanstack/react-query";
|
|
467
368
|
|
|
468
|
-
// src/
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
369
|
+
// src/utils/debounce.utils.ts
|
|
370
|
+
function debounce(func, waitFor) {
|
|
371
|
+
let timeoutId;
|
|
372
|
+
return (...args) => new Promise((resolve, reject) => {
|
|
373
|
+
if (timeoutId) {
|
|
374
|
+
clearTimeout(timeoutId);
|
|
375
|
+
}
|
|
376
|
+
timeoutId = setTimeout(async () => {
|
|
377
|
+
try {
|
|
378
|
+
const result = await func(...args);
|
|
379
|
+
resolve(result);
|
|
380
|
+
} catch (error) {
|
|
381
|
+
reject(error);
|
|
382
|
+
}
|
|
383
|
+
}, waitFor);
|
|
384
|
+
});
|
|
474
385
|
}
|
|
475
|
-
var getCard = withErrorHandler(_getCard, "getCard");
|
|
476
|
-
|
|
477
|
-
// src/domains/cards/services/create-card.service.ts
|
|
478
|
-
import { v4 } from "uuid";
|
|
479
|
-
|
|
480
|
-
// src/domains/cards/card.model.ts
|
|
481
|
-
var CardActivityType = /* @__PURE__ */ ((CardActivityType2) => {
|
|
482
|
-
CardActivityType2["READ_REPEAT"] = "READ_REPEAT";
|
|
483
|
-
CardActivityType2["VIDEO"] = "VIDEO";
|
|
484
|
-
CardActivityType2["TEXT"] = "TEXT";
|
|
485
|
-
CardActivityType2["READ_RESPOND"] = "READ_RESPOND";
|
|
486
|
-
CardActivityType2["FREE_RESPONSE"] = "FREE_RESPONSE";
|
|
487
|
-
CardActivityType2["REPEAT"] = "REPEAT";
|
|
488
|
-
CardActivityType2["RESPOND"] = "RESPOND";
|
|
489
|
-
CardActivityType2["RESPOND_WRITE"] = "RESPOND_WRITE";
|
|
490
|
-
CardActivityType2["TEXT_TO_SPEECH"] = "TEXT_TO_SPEECH";
|
|
491
|
-
CardActivityType2["MULTIPLE_CHOICE"] = "MULTIPLE_CHOICE";
|
|
492
|
-
CardActivityType2["PODCAST"] = "PODCAST";
|
|
493
|
-
CardActivityType2["MEDIA_PAGE"] = "MEDIA_PAGE";
|
|
494
|
-
CardActivityType2["WRITE"] = "WRITE";
|
|
495
|
-
CardActivityType2["SHORT_ANSWER"] = "SHORT_ANSWER";
|
|
496
|
-
CardActivityType2["SHORT_STORY"] = "SHORT_STORY";
|
|
497
|
-
CardActivityType2["SPEAK"] = "SPEAK";
|
|
498
|
-
CardActivityType2["CONVERSATION"] = "CONVERSATION";
|
|
499
|
-
CardActivityType2["CONVERSATION_WRITE"] = "CONVERSATION_WRITE";
|
|
500
|
-
CardActivityType2["DIALOGUE"] = "DIALOGUE";
|
|
501
|
-
CardActivityType2["INSTRUCTION"] = "INSTRUCTION";
|
|
502
|
-
CardActivityType2["LISTEN"] = "LISTEN";
|
|
503
|
-
CardActivityType2["READ"] = "READ";
|
|
504
|
-
CardActivityType2["ANSWER"] = "ANSWER";
|
|
505
|
-
return CardActivityType2;
|
|
506
|
-
})(CardActivityType || {});
|
|
507
|
-
var RESPOND_CARD_ACTIVITY_TYPES = [
|
|
508
|
-
"READ_RESPOND" /* READ_RESPOND */,
|
|
509
|
-
"RESPOND" /* RESPOND */,
|
|
510
|
-
"RESPOND_WRITE" /* RESPOND_WRITE */,
|
|
511
|
-
"FREE_RESPONSE" /* FREE_RESPONSE */
|
|
512
|
-
];
|
|
513
|
-
var MULTIPLE_CHOICE_CARD_ACTIVITY_TYPES = ["MULTIPLE_CHOICE" /* MULTIPLE_CHOICE */];
|
|
514
|
-
var REPEAT_CARD_ACTIVITY_TYPES = ["READ_REPEAT" /* READ_REPEAT */, "REPEAT" /* REPEAT */];
|
|
515
|
-
var RESPOND_WRITE_CARD_ACTIVITY_TYPES = [
|
|
516
|
-
"RESPOND_WRITE" /* RESPOND_WRITE */,
|
|
517
|
-
"FREE_RESPONSE" /* FREE_RESPONSE */
|
|
518
|
-
];
|
|
519
|
-
var RESPOND_AUDIO_CARD_ACTIVITY_TYPES = [
|
|
520
|
-
"RESPOND" /* RESPOND */,
|
|
521
|
-
"READ_RESPOND" /* READ_RESPOND */
|
|
522
|
-
];
|
|
523
|
-
var ALLOWED_CARD_ACTIVITY_TYPES_FOR_SUMMARY = [
|
|
524
|
-
"REPEAT" /* REPEAT */,
|
|
525
|
-
"RESPOND" /* RESPOND */,
|
|
526
|
-
"READ_REPEAT" /* READ_REPEAT */,
|
|
527
|
-
"READ_RESPOND" /* READ_RESPOND */,
|
|
528
|
-
"FREE_RESPONSE" /* FREE_RESPONSE */,
|
|
529
|
-
"RESPOND_WRITE" /* RESPOND_WRITE */,
|
|
530
|
-
"MULTIPLE_CHOICE" /* MULTIPLE_CHOICE */
|
|
531
|
-
];
|
|
532
386
|
|
|
533
|
-
// src/
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
return split;
|
|
387
|
+
// src/lib/tanstack/handle-optimistic-update-query.ts
|
|
388
|
+
var handleOptimisticUpdate = async ({
|
|
389
|
+
queryClient,
|
|
390
|
+
queryKey,
|
|
391
|
+
newData
|
|
392
|
+
}) => {
|
|
393
|
+
await queryClient.cancelQueries({
|
|
394
|
+
queryKey
|
|
395
|
+
});
|
|
396
|
+
const previousData = queryClient.getQueryData(queryKey);
|
|
397
|
+
if (previousData === void 0) {
|
|
398
|
+
queryClient.setQueryData(queryKey, newData);
|
|
546
399
|
} else {
|
|
547
|
-
|
|
400
|
+
queryClient.setQueryData(queryKey, { ...previousData, ...newData });
|
|
548
401
|
}
|
|
549
|
-
};
|
|
550
|
-
var getWordHash = (word, language) => {
|
|
551
|
-
const cleanedWord = cleanString(word);
|
|
552
|
-
const wordHash = sha1(`${language}-${cleanedWord}`);
|
|
553
|
-
console.log("wordHash core library", wordHash);
|
|
554
|
-
return wordHash;
|
|
402
|
+
return { previousData };
|
|
555
403
|
};
|
|
556
404
|
|
|
557
|
-
// src/
|
|
558
|
-
var
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
var processRecord = (data) => {
|
|
576
|
-
const { pronunciations = 0, fails = 0 } = data;
|
|
577
|
-
const attempts = pronunciations + fails;
|
|
578
|
-
const successRate = attempts > 0 ? pronunciations / attempts * 100 : 0;
|
|
579
|
-
let newStatus = null;
|
|
580
|
-
if (attempts < 6) {
|
|
581
|
-
return "NOT_CHECKED" /* NOT_CHECKED */;
|
|
582
|
-
}
|
|
583
|
-
if (successRate > 25) {
|
|
584
|
-
newStatus = "VERIFIED" /* VERIFIED */;
|
|
585
|
-
} else if (successRate > 10) {
|
|
586
|
-
newStatus = "WARNING" /* WARNING */;
|
|
587
|
-
} else if (fails > 20 && successRate < 10 && pronunciations > 1) {
|
|
588
|
-
newStatus = "NOT_RECOMMENDED" /* NOT_RECOMMENDED */;
|
|
589
|
-
} else if (pronunciations === 0 && fails > 20) {
|
|
590
|
-
newStatus = "NOT_WORKING" /* NOT_WORKING */;
|
|
591
|
-
} else {
|
|
592
|
-
newStatus = "NOT_CHECKED" /* NOT_CHECKED */;
|
|
593
|
-
}
|
|
594
|
-
return newStatus;
|
|
595
|
-
};
|
|
596
|
-
|
|
597
|
-
// src/domains/cards/services/create-card.service.ts
|
|
598
|
-
async function _createCard({ data }) {
|
|
599
|
-
const response = await api.addDoc(refsCardsFiresotre.allCards, data);
|
|
600
|
-
return response;
|
|
601
|
-
}
|
|
602
|
-
var createCard = withErrorHandler(_createCard, "createCard");
|
|
603
|
-
async function _createCards({ cards }) {
|
|
604
|
-
const { writeBatch: writeBatch2, doc: doc2 } = api.accessHelpers();
|
|
605
|
-
const batch = writeBatch2();
|
|
606
|
-
const cardsWithId = [];
|
|
607
|
-
for (const card of cards) {
|
|
608
|
-
const cardId = v4();
|
|
609
|
-
const ref = doc2(refsCardsFiresotre.card(cardId));
|
|
610
|
-
const newCardObject = {
|
|
611
|
-
...card,
|
|
612
|
-
id: cardId
|
|
613
|
-
};
|
|
614
|
-
if (card.type === "READ_REPEAT" /* READ_REPEAT */ && card.target_text && card.language) {
|
|
615
|
-
const verificationStatus = await getVerificationStatus(card.target_text, card.language);
|
|
616
|
-
newCardObject.verificationStatus = verificationStatus || null;
|
|
617
|
-
}
|
|
618
|
-
cardsWithId.push(newCardObject);
|
|
619
|
-
batch.set(ref, newCardObject);
|
|
620
|
-
}
|
|
621
|
-
await batch.commit();
|
|
622
|
-
return cardsWithId;
|
|
623
|
-
}
|
|
624
|
-
var createCards = withErrorHandler(_createCards, "createCards");
|
|
625
|
-
|
|
626
|
-
// src/domains/cards/card.hooks.ts
|
|
627
|
-
var cardsQueryKeys = {
|
|
628
|
-
all: ["cards"],
|
|
629
|
-
one: (params) => [...cardsQueryKeys.all, params.cardId]
|
|
630
|
-
};
|
|
631
|
-
function useCards({
|
|
632
|
-
cardIds,
|
|
633
|
-
enabled = true,
|
|
634
|
-
asObject
|
|
635
|
-
}) {
|
|
636
|
-
const queries = useQueries({
|
|
637
|
-
queries: cardIds.map((cardId) => ({
|
|
638
|
-
enabled: enabled && cardIds.length > 0,
|
|
639
|
-
queryKey: cardsQueryKeys.one({
|
|
640
|
-
cardId
|
|
641
|
-
}),
|
|
642
|
-
queryFn: () => getCard({ cardId })
|
|
643
|
-
}))
|
|
644
|
-
});
|
|
645
|
-
const cards = queries.map((query2) => query2.data).filter(Boolean);
|
|
646
|
-
const cardsObject = useMemo(() => {
|
|
647
|
-
if (!asObject) return null;
|
|
648
|
-
return cards.reduce((acc, card) => {
|
|
649
|
-
acc[card.id] = card;
|
|
650
|
-
return acc;
|
|
651
|
-
}, {});
|
|
652
|
-
}, [asObject, cards]);
|
|
653
|
-
return {
|
|
654
|
-
cards,
|
|
655
|
-
cardsObject,
|
|
656
|
-
cardsQueries: queries
|
|
657
|
-
};
|
|
658
|
-
}
|
|
659
|
-
function useCreateCard() {
|
|
660
|
-
const { queryClient } = useSpeakableApi();
|
|
661
|
-
const mutationCreateCard = useMutation({
|
|
662
|
-
mutationFn: createCard,
|
|
663
|
-
onSuccess: (cardCreated) => {
|
|
664
|
-
queryClient.invalidateQueries({ queryKey: cardsQueryKeys.one({ cardId: cardCreated.id }) });
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
return {
|
|
668
|
-
mutationCreateCard
|
|
669
|
-
};
|
|
670
|
-
}
|
|
671
|
-
function useCreateCards() {
|
|
672
|
-
const mutationCreateCards = useMutation({
|
|
673
|
-
mutationFn: createCards
|
|
674
|
-
});
|
|
675
|
-
return {
|
|
676
|
-
mutationCreateCards
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
function getCardFromCache({
|
|
680
|
-
cardId,
|
|
681
|
-
queryClient
|
|
682
|
-
}) {
|
|
683
|
-
return queryClient.getQueryData(cardsQueryKeys.one({ cardId }));
|
|
684
|
-
}
|
|
685
|
-
function updateCardInCache({
|
|
686
|
-
cardId,
|
|
687
|
-
card,
|
|
688
|
-
queryClient
|
|
689
|
-
}) {
|
|
690
|
-
queryClient.setQueryData(cardsQueryKeys.one({ cardId }), card);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
// src/domains/cards/card.repo.ts
|
|
694
|
-
var createCardRepo = () => {
|
|
695
|
-
return {
|
|
696
|
-
createCard,
|
|
697
|
-
createCards,
|
|
698
|
-
getCard
|
|
699
|
-
};
|
|
700
|
-
};
|
|
701
|
-
|
|
702
|
-
// src/domains/sets/set.hooks.ts
|
|
703
|
-
import { useQuery as useQuery2 } from "@tanstack/react-query";
|
|
704
|
-
|
|
705
|
-
// src/domains/sets/set.constants.ts
|
|
706
|
-
var SETS_COLLECTION = "sets";
|
|
707
|
-
var refsSetsFirestore = {
|
|
708
|
-
allSets: SETS_COLLECTION,
|
|
709
|
-
set: (id) => `${SETS_COLLECTION}/${id}`
|
|
710
|
-
};
|
|
711
|
-
|
|
712
|
-
// src/domains/sets/services/get-set.service.ts
|
|
713
|
-
async function _getSet({ setId }) {
|
|
714
|
-
const response = await api.getDoc(refsSetsFirestore.set(setId));
|
|
715
|
-
return response.data;
|
|
716
|
-
}
|
|
717
|
-
var getSet = withErrorHandler(_getSet, "getSet");
|
|
718
|
-
|
|
719
|
-
// src/domains/sets/set.hooks.ts
|
|
720
|
-
var setsQueryKeys = {
|
|
721
|
-
all: ["sets"],
|
|
722
|
-
one: (params) => [...setsQueryKeys.all, params.setId]
|
|
723
|
-
};
|
|
724
|
-
var useSet = ({ setId, enabled }) => {
|
|
725
|
-
return useQuery2({
|
|
726
|
-
queryKey: setsQueryKeys.one({ setId }),
|
|
727
|
-
queryFn: () => getSet({ setId }),
|
|
728
|
-
enabled: setId !== void 0 && setId !== "" && enabled
|
|
729
|
-
});
|
|
730
|
-
};
|
|
731
|
-
function getSetFromCache({
|
|
732
|
-
setId,
|
|
733
|
-
queryClient
|
|
734
|
-
}) {
|
|
735
|
-
if (!setId) return null;
|
|
736
|
-
return queryClient.getQueryData(setsQueryKeys.one({ setId }));
|
|
737
|
-
}
|
|
738
|
-
function updateSetInCache({
|
|
739
|
-
set,
|
|
740
|
-
queryClient
|
|
741
|
-
}) {
|
|
742
|
-
const { id, ...setData } = set;
|
|
743
|
-
queryClient.setQueryData(setsQueryKeys.one({ setId: id }), setData);
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
// src/domains/sets/set.repo.ts
|
|
747
|
-
var createSetRepo = () => {
|
|
748
|
-
return {
|
|
749
|
-
getSet
|
|
750
|
-
};
|
|
751
|
-
};
|
|
752
|
-
|
|
753
|
-
// src/domains/notification/notification.constants.ts
|
|
754
|
-
var SPEAKABLE_NOTIFICATIONS = {
|
|
755
|
-
NEW_ASSIGNMENT: "new_assignment",
|
|
756
|
-
ASSESSMENT_SUBMITTED: "assessment_submitted",
|
|
757
|
-
ASSESSMENT_SCORED: "assessment_scored",
|
|
758
|
-
NEW_COMMENT: "NEW_COMMENT"
|
|
759
|
-
};
|
|
760
|
-
var SpeakableNotificationTypes = {
|
|
761
|
-
NEW_ASSIGNMENT: "NEW_ASSIGNMENT",
|
|
762
|
-
FEEDBACK_FROM_TEACHER: "FEEDBACK_FROM_TEACHER",
|
|
763
|
-
MESSAGE_FROM_STUDENT: "MESSAGE_FROM_STUDENT",
|
|
764
|
-
PHRASE_MARKED_CORRECT: "PHRASE_MARKED_CORRECT",
|
|
765
|
-
STUDENT_PROGRESS: "STUDENT_PROGRESS",
|
|
766
|
-
PLAYLIST_FOLLOWERS: "PLAYLIST_FOLLOWERS",
|
|
767
|
-
PLAYLIST_PLAYS: "PLAYLIST_PLAYS",
|
|
768
|
-
// New notifications
|
|
769
|
-
ASSESSMENT_SUBMITTED: "ASSESSMENT_SUBMITTED",
|
|
770
|
-
// Notification FOR TEACHER when student submits assessment
|
|
771
|
-
ASSESSMENT_SCORED: "ASSESSMENT_SCORED",
|
|
772
|
-
// Notification FOR STUDENT when teacher scores assessment
|
|
773
|
-
// Comment
|
|
774
|
-
NEW_COMMENT: "NEW_COMMENT"
|
|
775
|
-
};
|
|
776
|
-
|
|
777
|
-
// src/domains/notification/services/create-notification.service.ts
|
|
778
|
-
import dayjs2 from "dayjs";
|
|
779
|
-
|
|
780
|
-
// src/constants/web.constants.ts
|
|
781
|
-
var WEB_BASE_URL = "https://app.speakable.io";
|
|
782
|
-
|
|
783
|
-
// src/domains/notification/services/send-notification.service.ts
|
|
784
|
-
var _sendNotification = async (sendTo, notification) => {
|
|
785
|
-
var _a, _b, _c;
|
|
786
|
-
const results = await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "createNotificationV2")) == null ? void 0 : _c({
|
|
787
|
-
sendTo,
|
|
788
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
789
|
-
notification
|
|
790
|
-
}));
|
|
791
|
-
return results;
|
|
792
|
-
};
|
|
793
|
-
var sendNotification = withErrorHandler(_sendNotification, "sendNotification");
|
|
794
|
-
|
|
795
|
-
// src/domains/notification/services/create-notification.service.ts
|
|
796
|
-
var createNotification = async ({
|
|
797
|
-
data,
|
|
798
|
-
type,
|
|
799
|
-
userId,
|
|
800
|
-
profile
|
|
801
|
-
}) => {
|
|
802
|
-
let result;
|
|
803
|
-
switch (type) {
|
|
804
|
-
case SPEAKABLE_NOTIFICATIONS.NEW_ASSIGNMENT:
|
|
805
|
-
result = await handleAssignNotifPromise({ data, profile });
|
|
806
|
-
break;
|
|
807
|
-
case SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SUBMITTED:
|
|
808
|
-
result = await createAssessmentSubmissionNotification({
|
|
809
|
-
data,
|
|
810
|
-
profile,
|
|
811
|
-
userId
|
|
812
|
-
});
|
|
813
|
-
break;
|
|
814
|
-
case SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SCORED:
|
|
815
|
-
result = await createAssessmentScoredNotification({
|
|
816
|
-
data,
|
|
817
|
-
profile
|
|
818
|
-
});
|
|
819
|
-
break;
|
|
820
|
-
default:
|
|
821
|
-
result = null;
|
|
822
|
-
break;
|
|
823
|
-
}
|
|
824
|
-
return result;
|
|
825
|
-
};
|
|
826
|
-
var handleAssignNotifPromise = async ({
|
|
827
|
-
data: assignments,
|
|
828
|
-
profile
|
|
829
|
-
}) => {
|
|
830
|
-
if (!assignments.length) return;
|
|
831
|
-
try {
|
|
832
|
-
const notifsPromises = assignments.map(async (assignment) => {
|
|
833
|
-
const {
|
|
834
|
-
section,
|
|
835
|
-
section: { members },
|
|
836
|
-
...rest
|
|
837
|
-
} = assignment;
|
|
838
|
-
if (!section || !members) throw new Error("Invalid assignment data");
|
|
839
|
-
const data = { section, sendTo: members, assignment: { ...rest } };
|
|
840
|
-
return createNewAssignmentNotification({ data, profile });
|
|
841
|
-
});
|
|
842
|
-
await Promise.all(notifsPromises);
|
|
843
|
-
return {
|
|
844
|
-
success: true,
|
|
845
|
-
message: "Assignment notifications sent successfully"
|
|
846
|
-
};
|
|
847
|
-
} catch (error) {
|
|
848
|
-
console.error("Error in handleAssignNotifPromise:", error);
|
|
849
|
-
throw error;
|
|
850
|
-
}
|
|
851
|
-
};
|
|
852
|
-
var createNewAssignmentNotification = async ({
|
|
853
|
-
data,
|
|
854
|
-
profile
|
|
855
|
-
}) => {
|
|
856
|
-
var _a;
|
|
857
|
-
const { assignment, sendTo } = data;
|
|
858
|
-
const teacherName = profile.displayName || "Your teacher";
|
|
859
|
-
const dueDate = assignment.dueDateTimestamp ? dayjs2(assignment.dueDateTimestamp.toDate()).format("MMM Do") : null;
|
|
860
|
-
const results = await sendNotification(sendTo, {
|
|
861
|
-
courseId: assignment.courseId,
|
|
862
|
-
type: SPEAKABLE_NOTIFICATIONS.NEW_ASSIGNMENT,
|
|
863
|
-
senderName: teacherName,
|
|
864
|
-
link: `${WEB_BASE_URL}/assignment/${assignment.id}`,
|
|
865
|
-
messagePreview: `A new assignment "${assignment.name}" is now available. ${dueDate ? `Due ${dueDate}` : ""}`,
|
|
866
|
-
title: "New Assignment Available!",
|
|
867
|
-
imageUrl: (_a = profile.image) == null ? void 0 : _a.url
|
|
868
|
-
});
|
|
869
|
-
return results;
|
|
870
|
-
};
|
|
871
|
-
var createAssessmentSubmissionNotification = async ({
|
|
872
|
-
data: assignment,
|
|
873
|
-
profile,
|
|
874
|
-
userId
|
|
875
|
-
}) => {
|
|
876
|
-
var _a;
|
|
877
|
-
const studentName = profile.displayName || "Your student";
|
|
878
|
-
const results = await sendNotification(assignment.owners, {
|
|
879
|
-
courseId: assignment.courseId,
|
|
880
|
-
type: SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SUBMITTED,
|
|
881
|
-
link: `${WEB_BASE_URL}/a/${assignment.id}?studentId=${userId}`,
|
|
882
|
-
title: `Assessment Submitted!`,
|
|
883
|
-
senderName: studentName,
|
|
884
|
-
messagePreview: `${studentName} has submitted the assessment "${assignment.name}"`,
|
|
885
|
-
imageUrl: (_a = profile.image) == null ? void 0 : _a.url
|
|
886
|
-
});
|
|
887
|
-
return results;
|
|
888
|
-
};
|
|
889
|
-
var createAssessmentScoredNotification = async ({
|
|
890
|
-
data,
|
|
891
|
-
profile
|
|
892
|
-
}) => {
|
|
893
|
-
var _a, _b, _c, _d, _e;
|
|
894
|
-
const { assignment, sendTo } = data;
|
|
895
|
-
const teacherName = profile.displayName || "Your teacher";
|
|
896
|
-
const title = `${assignment.isAssessment ? "Assessment" : "Assignment"} Reviewed!`;
|
|
897
|
-
const messagePreview = `Your ${assignment.isAssessment ? "assessment" : "assignment"} has been reviewed by your teacher. Click to view the feedback.`;
|
|
898
|
-
const results = await sendNotification(sendTo, {
|
|
899
|
-
courseId: assignment.courseId,
|
|
900
|
-
type: SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SCORED,
|
|
901
|
-
link: `${WEB_BASE_URL}/assignment/${assignment.id}`,
|
|
902
|
-
title,
|
|
903
|
-
messagePreview,
|
|
904
|
-
imageUrl: (_a = profile.image) == null ? void 0 : _a.url,
|
|
905
|
-
senderName: teacherName
|
|
906
|
-
});
|
|
907
|
-
await ((_e = (_c = (_b = api).httpsCallable) == null ? void 0 : _c.call(_b, "sendAssessmentScoredEmail")) == null ? void 0 : _e({
|
|
908
|
-
assessmentTitle: assignment.name,
|
|
909
|
-
link: `${WEB_BASE_URL}/assignment/${assignment.id}`,
|
|
910
|
-
senderImage: ((_d = profile.image) == null ? void 0 : _d.url) || "",
|
|
911
|
-
studentId: sendTo[0],
|
|
912
|
-
teacherName: profile.displayName
|
|
913
|
-
}));
|
|
914
|
-
return results;
|
|
915
|
-
};
|
|
916
|
-
|
|
917
|
-
// src/domains/notification/hooks/notification.hooks.ts
|
|
918
|
-
var notificationQueryKeys = {
|
|
919
|
-
all: ["notifications"],
|
|
920
|
-
byId: (id) => [...notificationQueryKeys.all, id]
|
|
921
|
-
};
|
|
922
|
-
var useCreateNotification = () => {
|
|
923
|
-
const { user, queryClient } = useSpeakableApi();
|
|
924
|
-
const handleCreateNotifications = async (type, data) => {
|
|
925
|
-
var _a, _b;
|
|
926
|
-
const result = await createNotification({
|
|
927
|
-
type,
|
|
928
|
-
userId: user.auth.uid,
|
|
929
|
-
profile: (_a = user == null ? void 0 : user.profile) != null ? _a : {},
|
|
930
|
-
data
|
|
931
|
-
});
|
|
932
|
-
queryClient.invalidateQueries({
|
|
933
|
-
queryKey: notificationQueryKeys.byId((_b = user == null ? void 0 : user.auth.uid) != null ? _b : "")
|
|
934
|
-
});
|
|
935
|
-
return result;
|
|
936
|
-
};
|
|
937
|
-
return {
|
|
938
|
-
createNotification: handleCreateNotifications
|
|
939
|
-
};
|
|
940
|
-
};
|
|
941
|
-
|
|
942
|
-
// src/constants/all-langs.json
|
|
943
|
-
var all_langs_default = {
|
|
944
|
-
af: "Afrikaans",
|
|
945
|
-
sq: "Albanian",
|
|
946
|
-
am: "Amharic",
|
|
947
|
-
ar: "Arabic",
|
|
948
|
-
hy: "Armenian",
|
|
949
|
-
az: "Azerbaijani",
|
|
950
|
-
eu: "Basque",
|
|
951
|
-
be: "Belarusian",
|
|
952
|
-
bn: "Bengali",
|
|
953
|
-
bs: "Bosnian",
|
|
954
|
-
bg: "Bulgarian",
|
|
955
|
-
ca: "Catalan",
|
|
956
|
-
ceb: "Cebuano",
|
|
957
|
-
zh: "Chinese",
|
|
958
|
-
co: "Corsican",
|
|
959
|
-
hr: "Croatian",
|
|
960
|
-
cs: "Czech",
|
|
961
|
-
da: "Danish",
|
|
962
|
-
nl: "Dutch",
|
|
963
|
-
en: "English",
|
|
964
|
-
eo: "Esperanto",
|
|
965
|
-
et: "Estonian",
|
|
966
|
-
fi: "Finnish",
|
|
967
|
-
fr: "French",
|
|
968
|
-
fy: "Frisian",
|
|
969
|
-
gl: "Galician",
|
|
970
|
-
ka: "Georgian",
|
|
971
|
-
de: "German",
|
|
972
|
-
el: "Greek",
|
|
973
|
-
gu: "Gujarati",
|
|
974
|
-
ht: "Haitian Creole",
|
|
975
|
-
ha: "Hausa",
|
|
976
|
-
haw: "Hawaiian",
|
|
977
|
-
he: "Hebrew",
|
|
978
|
-
hi: "Hindi",
|
|
979
|
-
hmn: "Hmong",
|
|
980
|
-
hu: "Hungarian",
|
|
981
|
-
is: "Icelandic",
|
|
982
|
-
ig: "Igbo",
|
|
983
|
-
id: "Indonesian",
|
|
984
|
-
ga: "Irish",
|
|
985
|
-
it: "Italian",
|
|
986
|
-
ja: "Japanese",
|
|
987
|
-
jv: "Javanese",
|
|
988
|
-
kn: "Kannada",
|
|
989
|
-
kk: "Kazakh",
|
|
990
|
-
km: "Khmer",
|
|
991
|
-
ko: "Korean",
|
|
992
|
-
ku: "Kurdish",
|
|
993
|
-
ky: "Kyrgyz",
|
|
994
|
-
lo: "Lao",
|
|
995
|
-
la: "Latin",
|
|
996
|
-
lv: "Latvian",
|
|
997
|
-
lt: "Lithuanian",
|
|
998
|
-
lb: "Luxembourgish",
|
|
999
|
-
mk: "Macedonian",
|
|
1000
|
-
mg: "Malagasy",
|
|
1001
|
-
ms: "Malay",
|
|
1002
|
-
ml: "Malayalam",
|
|
1003
|
-
mt: "Maltese",
|
|
1004
|
-
mi: "Maori",
|
|
1005
|
-
mr: "Marathi",
|
|
1006
|
-
mn: "Mongolian",
|
|
1007
|
-
my: "Myanmar (Burmese)",
|
|
1008
|
-
ne: "Nepali",
|
|
1009
|
-
no: "Norwegian",
|
|
1010
|
-
ny: "Nyanja (Chichewa)",
|
|
1011
|
-
ps: "Pashto",
|
|
1012
|
-
fa: "Persian",
|
|
1013
|
-
pl: "Polish",
|
|
1014
|
-
pt: "Portuguese",
|
|
1015
|
-
pa: "Punjabi",
|
|
1016
|
-
ro: "Romanian",
|
|
1017
|
-
ru: "Russian",
|
|
1018
|
-
sm: "Samoan",
|
|
1019
|
-
gd: "Scots Gaelic",
|
|
1020
|
-
sr: "Serbian",
|
|
1021
|
-
st: "Sesotho",
|
|
1022
|
-
sn: "Shona",
|
|
1023
|
-
sd: "Sindhi",
|
|
1024
|
-
si: "Sinhala (Sinhalese)",
|
|
1025
|
-
sk: "Slovak",
|
|
1026
|
-
sl: "Slovenian",
|
|
1027
|
-
so: "Somali",
|
|
1028
|
-
es: "Spanish",
|
|
1029
|
-
su: "Sundanese",
|
|
1030
|
-
sw: "Swahili",
|
|
1031
|
-
sv: "Swedish",
|
|
1032
|
-
tl: "Tagalog (Filipino)",
|
|
1033
|
-
tg: "Tajik",
|
|
1034
|
-
ta: "Tamil",
|
|
1035
|
-
te: "Telugu",
|
|
1036
|
-
th: "Thai",
|
|
1037
|
-
tr: "Turkish",
|
|
1038
|
-
uk: "Ukrainian",
|
|
1039
|
-
ur: "Urdu",
|
|
1040
|
-
uz: "Uzbek",
|
|
1041
|
-
vi: "Vietnamese",
|
|
1042
|
-
cy: "Welsh",
|
|
1043
|
-
xh: "Xhosa",
|
|
1044
|
-
yi: "Yiddish",
|
|
1045
|
-
yo: "Yoruba",
|
|
1046
|
-
zu: "Zulu"
|
|
1047
|
-
};
|
|
1048
|
-
|
|
1049
|
-
// src/utils/ai/get-respond-card-tool.ts
|
|
1050
|
-
var getRespondCardTool = ({
|
|
1051
|
-
language,
|
|
1052
|
-
standard = "actfl"
|
|
1053
|
-
}) => {
|
|
1054
|
-
const lang = all_langs_default[language] || "English";
|
|
1055
|
-
const tool = {
|
|
1056
|
-
tool_choice: {
|
|
1057
|
-
type: "function",
|
|
1058
|
-
function: { name: "get_feedback" }
|
|
1059
|
-
},
|
|
1060
|
-
tools: [
|
|
1061
|
-
{
|
|
1062
|
-
type: "function",
|
|
1063
|
-
function: {
|
|
1064
|
-
name: "get_feedback",
|
|
1065
|
-
description: "Get feedback on a student's response",
|
|
1066
|
-
parameters: {
|
|
1067
|
-
type: "object",
|
|
1068
|
-
required: [
|
|
1069
|
-
"success",
|
|
1070
|
-
"score",
|
|
1071
|
-
"score_justification",
|
|
1072
|
-
"errors",
|
|
1073
|
-
"improvedResponse",
|
|
1074
|
-
"compliments"
|
|
1075
|
-
],
|
|
1076
|
-
properties: {
|
|
1077
|
-
success: {
|
|
1078
|
-
type: "boolean",
|
|
1079
|
-
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."
|
|
1080
|
-
},
|
|
1081
|
-
errors: {
|
|
1082
|
-
type: "array",
|
|
1083
|
-
items: {
|
|
1084
|
-
type: "object",
|
|
1085
|
-
required: ["error", "grammar_error_type", "correction", "justification"],
|
|
1086
|
-
properties: {
|
|
1087
|
-
error: {
|
|
1088
|
-
type: "string",
|
|
1089
|
-
description: "The grammatical error in the student's response."
|
|
1090
|
-
},
|
|
1091
|
-
correction: {
|
|
1092
|
-
type: "string",
|
|
1093
|
-
description: "The suggested correction to the error"
|
|
1094
|
-
},
|
|
1095
|
-
justification: {
|
|
1096
|
-
type: "string",
|
|
1097
|
-
description: `An explanation of the rationale behind the suggested correction. WRITE THIS IN ${lang}!`
|
|
1098
|
-
},
|
|
1099
|
-
grammar_error_type: {
|
|
1100
|
-
type: "string",
|
|
1101
|
-
enum: [
|
|
1102
|
-
"subjVerbAgree",
|
|
1103
|
-
"tenseErrors",
|
|
1104
|
-
"articleMisuse",
|
|
1105
|
-
"prepositionErrors",
|
|
1106
|
-
"adjNounAgree",
|
|
1107
|
-
"pronounErrors",
|
|
1108
|
-
"wordOrder",
|
|
1109
|
-
"verbConjugation",
|
|
1110
|
-
"pluralization",
|
|
1111
|
-
"negationErrors",
|
|
1112
|
-
"modalVerbMisuse",
|
|
1113
|
-
"relativeClause",
|
|
1114
|
-
"auxiliaryVerb",
|
|
1115
|
-
"complexSentenceAgreement",
|
|
1116
|
-
"idiomaticExpression",
|
|
1117
|
-
"registerInconsistency",
|
|
1118
|
-
"voiceMisuse"
|
|
1119
|
-
],
|
|
1120
|
-
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"
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
},
|
|
1124
|
-
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."
|
|
1125
|
-
},
|
|
1126
|
-
compliments: {
|
|
1127
|
-
type: "array",
|
|
1128
|
-
items: {
|
|
1129
|
-
type: "string"
|
|
1130
|
-
},
|
|
1131
|
-
description: `An array of strings, each representing something the student did well. Each string should be WRITTEN IN ${lang}!`
|
|
1132
|
-
},
|
|
1133
|
-
improvedResponse: {
|
|
1134
|
-
type: "string",
|
|
1135
|
-
description: "An improved response with proper grammar and more detail, if applicable."
|
|
1136
|
-
},
|
|
1137
|
-
score: {
|
|
1138
|
-
type: "number",
|
|
1139
|
-
description: "A score between 0 and 100, reflecting the overall quality of the response"
|
|
1140
|
-
},
|
|
1141
|
-
score_justification: {
|
|
1142
|
-
type: "string",
|
|
1143
|
-
description: "An explanation of the rationale behind the assigned score, considering both accuracy and fluency"
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
]
|
|
1150
|
-
};
|
|
1151
|
-
if (standard === "wida") {
|
|
1152
|
-
const wida_level = {
|
|
1153
|
-
type: "number",
|
|
1154
|
-
enum: [1, 2, 3, 4, 5, 6],
|
|
1155
|
-
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
|
|
1156
|
-
|
|
1157
|
-
1 - Entering
|
|
1158
|
-
2 - Emerging
|
|
1159
|
-
3 - Developing
|
|
1160
|
-
4 - Expanding
|
|
1161
|
-
5 - Bridging
|
|
1162
|
-
6 - Reaching
|
|
1163
|
-
|
|
1164
|
-
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.
|
|
1165
|
-
`
|
|
1166
|
-
};
|
|
1167
|
-
const wida_justification = {
|
|
1168
|
-
type: "string",
|
|
1169
|
-
description: `An explanation of the rationale behind the assigned WIDA level of the response, considering both accuracy and fluency. WRITE THIS IN ENGLISH!`
|
|
1170
|
-
};
|
|
1171
|
-
tool.tools[0].function.parameters.required.push("wida_level");
|
|
1172
|
-
tool.tools[0].function.parameters.required.push("wida_justification");
|
|
1173
|
-
tool.tools[0].function.parameters.properties.wida_level = wida_level;
|
|
1174
|
-
tool.tools[0].function.parameters.properties.wida_justification = wida_justification;
|
|
1175
|
-
} else {
|
|
1176
|
-
const actfl_level = {
|
|
1177
|
-
type: "string",
|
|
1178
|
-
enum: ["NL", "NM", "NH", "IL", "IM", "IH", "AL", "AM", "AH", "S", "D"],
|
|
1179
|
-
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"
|
|
1180
|
-
};
|
|
1181
|
-
const actfl_justification = {
|
|
1182
|
-
type: "string",
|
|
1183
|
-
description: "An explanation of the rationale behind the assigned ACTFL level, considering both accuracy and fluency"
|
|
1184
|
-
};
|
|
1185
|
-
tool.tools[0].function.parameters.required.push("actfl_level");
|
|
1186
|
-
tool.tools[0].function.parameters.required.push("actfl_justification");
|
|
1187
|
-
tool.tools[0].function.parameters.properties.actfl_level = actfl_level;
|
|
1188
|
-
tool.tools[0].function.parameters.properties.actfl_justification = actfl_justification;
|
|
1189
|
-
}
|
|
1190
|
-
return tool;
|
|
1191
|
-
};
|
|
1192
|
-
|
|
1193
|
-
// src/utils/debounce.utils.ts
|
|
1194
|
-
function debounce(func, waitFor) {
|
|
1195
|
-
let timeoutId;
|
|
1196
|
-
return (...args) => new Promise((resolve, reject) => {
|
|
1197
|
-
if (timeoutId) {
|
|
1198
|
-
clearTimeout(timeoutId);
|
|
1199
|
-
}
|
|
1200
|
-
timeoutId = setTimeout(async () => {
|
|
1201
|
-
try {
|
|
1202
|
-
const result = await func(...args);
|
|
1203
|
-
resolve(result);
|
|
1204
|
-
} catch (error) {
|
|
1205
|
-
reject(error);
|
|
1206
|
-
}
|
|
1207
|
-
}, waitFor);
|
|
1208
|
-
});
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
// src/domains/assignment/hooks/score-hooks.ts
|
|
1212
|
-
import { useMutation as useMutation2, useQuery as useQuery3 } from "@tanstack/react-query";
|
|
1213
|
-
|
|
1214
|
-
// src/lib/tanstack/handle-optimistic-update-query.ts
|
|
1215
|
-
var handleOptimisticUpdate = async ({
|
|
1216
|
-
queryClient,
|
|
1217
|
-
queryKey,
|
|
1218
|
-
newData
|
|
1219
|
-
}) => {
|
|
1220
|
-
await queryClient.cancelQueries({
|
|
1221
|
-
queryKey
|
|
1222
|
-
});
|
|
1223
|
-
const previousData = queryClient.getQueryData(queryKey);
|
|
1224
|
-
if (previousData === void 0) {
|
|
1225
|
-
queryClient.setQueryData(queryKey, newData);
|
|
1226
|
-
} else {
|
|
1227
|
-
queryClient.setQueryData(queryKey, { ...previousData, ...newData });
|
|
1228
|
-
}
|
|
1229
|
-
return { previousData };
|
|
1230
|
-
};
|
|
1231
|
-
|
|
1232
|
-
// src/constants/speakable-plans.ts
|
|
1233
|
-
var FEEDBACK_PLANS = {
|
|
1234
|
-
FEEDBACK_TRANSCRIPT: "FEEDBACK_TRANSCRIPT",
|
|
1235
|
-
// Transcript from the audio
|
|
1236
|
-
FEEDBACK_SUMMARY: "FEEDBACK_SUMMARY",
|
|
1237
|
-
// Chatty summary (Free plan)
|
|
1238
|
-
FEEDBACK_GRAMMAR_INSIGHTS: "FEEDBACK_GRAMMAR_INSIGHTS",
|
|
1239
|
-
// Grammar insights
|
|
1240
|
-
FEEDBACK_SUGGESTED_RESPONSE: "FEEDBACK_SUGGESTED_RESPONSE",
|
|
1241
|
-
// Suggested Response
|
|
1242
|
-
FEEDBACK_RUBRIC: "FEEDBACK_RUBRIC",
|
|
1243
|
-
// Suggested Response
|
|
1244
|
-
FEEDBACK_GRADING_STANDARDS: "FEEDBACK_GRADING_STANDARDS",
|
|
1245
|
-
// ACTFL / WIDA Estimate
|
|
1246
|
-
FEEDBACK_TARGET_LANGUAGE: "FEEDBACK_TARGET_LANGUAGE",
|
|
1247
|
-
// Ability to set the feedback language to the target language of the student
|
|
1248
|
-
FEEDBACK_DISABLE_ALLOW_RETRIES: "FEEDBACK_DISABLE_ALLOW_RETRIES"
|
|
1249
|
-
// Turn of allow retries
|
|
405
|
+
// src/constants/speakable-plans.ts
|
|
406
|
+
var FEEDBACK_PLANS = {
|
|
407
|
+
FEEDBACK_TRANSCRIPT: "FEEDBACK_TRANSCRIPT",
|
|
408
|
+
// Transcript from the audio
|
|
409
|
+
FEEDBACK_SUMMARY: "FEEDBACK_SUMMARY",
|
|
410
|
+
// Chatty summary (Free plan)
|
|
411
|
+
FEEDBACK_GRAMMAR_INSIGHTS: "FEEDBACK_GRAMMAR_INSIGHTS",
|
|
412
|
+
// Grammar insights
|
|
413
|
+
FEEDBACK_SUGGESTED_RESPONSE: "FEEDBACK_SUGGESTED_RESPONSE",
|
|
414
|
+
// Suggested Response
|
|
415
|
+
FEEDBACK_RUBRIC: "FEEDBACK_RUBRIC",
|
|
416
|
+
// Suggested Response
|
|
417
|
+
FEEDBACK_GRADING_STANDARDS: "FEEDBACK_GRADING_STANDARDS",
|
|
418
|
+
// ACTFL / WIDA Estimate
|
|
419
|
+
FEEDBACK_TARGET_LANGUAGE: "FEEDBACK_TARGET_LANGUAGE",
|
|
420
|
+
// Ability to set the feedback language to the target language of the student
|
|
421
|
+
FEEDBACK_DISABLE_ALLOW_RETRIES: "FEEDBACK_DISABLE_ALLOW_RETRIES"
|
|
422
|
+
// Turn of allow retries
|
|
1250
423
|
};
|
|
1251
424
|
var AUTO_GRADING_PLANS = {
|
|
1252
425
|
AUTO_GRADING_PASS_FAIL: "AUTO_GRADING_PASS_FAIL",
|
|
@@ -1366,52 +539,241 @@ var SpeakablePlanTypes = {
|
|
|
1366
539
|
growth: "growth",
|
|
1367
540
|
professional: "professional"
|
|
1368
541
|
};
|
|
1369
|
-
var SpeakablePermissionsMap = {
|
|
1370
|
-
[SpeakablePlanTypes.basic]: FREE_PLAN,
|
|
1371
|
-
[SpeakablePlanTypes.starter]: TEACHER_PRO_PLAN,
|
|
1372
|
-
[SpeakablePlanTypes.teacher_pro]: TEACHER_PRO_PLAN,
|
|
1373
|
-
[SpeakablePlanTypes.growth]: ORGANIZATION_PLAN,
|
|
1374
|
-
[SpeakablePlanTypes.professional]: ORGANIZATION_PLAN,
|
|
1375
|
-
[SpeakablePlanTypes.organization]: ORGANIZATION_PLAN,
|
|
1376
|
-
[SpeakablePlanTypes.school_starter]: SCHOOL_STARTER
|
|
542
|
+
var SpeakablePermissionsMap = {
|
|
543
|
+
[SpeakablePlanTypes.basic]: FREE_PLAN,
|
|
544
|
+
[SpeakablePlanTypes.starter]: TEACHER_PRO_PLAN,
|
|
545
|
+
[SpeakablePlanTypes.teacher_pro]: TEACHER_PRO_PLAN,
|
|
546
|
+
[SpeakablePlanTypes.growth]: ORGANIZATION_PLAN,
|
|
547
|
+
[SpeakablePlanTypes.professional]: ORGANIZATION_PLAN,
|
|
548
|
+
[SpeakablePlanTypes.organization]: ORGANIZATION_PLAN,
|
|
549
|
+
[SpeakablePlanTypes.school_starter]: SCHOOL_STARTER
|
|
550
|
+
};
|
|
551
|
+
var SpeakablePlanHierarchy = [
|
|
552
|
+
SpeakablePlanTypes.basic,
|
|
553
|
+
SpeakablePlanTypes.starter,
|
|
554
|
+
SpeakablePlanTypes.teacher_pro,
|
|
555
|
+
SpeakablePlanTypes.growth,
|
|
556
|
+
SpeakablePlanTypes.professional,
|
|
557
|
+
SpeakablePlanTypes.school_starter,
|
|
558
|
+
SpeakablePlanTypes.organization
|
|
559
|
+
];
|
|
560
|
+
|
|
561
|
+
// src/hooks/usePermissions.ts
|
|
562
|
+
var usePermissions = () => {
|
|
563
|
+
const { permissions } = useSpeakableApi();
|
|
564
|
+
const has = (permission) => {
|
|
565
|
+
var _a;
|
|
566
|
+
return (_a = permissions.permissions) == null ? void 0 : _a.includes(permission);
|
|
567
|
+
};
|
|
568
|
+
return {
|
|
569
|
+
plan: permissions.plan,
|
|
570
|
+
permissionsLoaded: permissions.loaded,
|
|
571
|
+
isStripePlan: permissions.isStripePlan,
|
|
572
|
+
refreshDate: permissions.refreshDate,
|
|
573
|
+
isInstitutionPlan: permissions.isInstitutionPlan,
|
|
574
|
+
subscriptionId: permissions.subscriptionId,
|
|
575
|
+
contact: permissions.contact,
|
|
576
|
+
hasGradebook: has(ANALYTICS_PLANS.ANALYTICS_GRADEBOOK),
|
|
577
|
+
hasGoogleClassroomGradePassback: has(ASSIGNMENT_SETTINGS_PLANS.GOOGLE_CLASSROOM_GRADE_PASSBACK),
|
|
578
|
+
hasAssessments: has(ASSIGNMENT_SETTINGS_PLANS.ASSESSMENTS),
|
|
579
|
+
hasSectionAnalytics: has(ANALYTICS_PLANS.ANALYTICS_CLASSROOM_ANALYTICS),
|
|
580
|
+
hasStudentReports: has(ANALYTICS_PLANS.ANALYTICS_STUDENT_PROGRESS_REPORTS),
|
|
581
|
+
permissions: permissions || [],
|
|
582
|
+
hasStudentPortfolios: permissions.hasStudentPortfolios,
|
|
583
|
+
isFreeOrgTrial: permissions.type === "free_org_trial",
|
|
584
|
+
freeOrgTrialExpired: permissions.freeOrgTrialExpired
|
|
585
|
+
};
|
|
586
|
+
};
|
|
587
|
+
var usePermissions_default = usePermissions;
|
|
588
|
+
|
|
589
|
+
// src/domains/notification/notification.constants.ts
|
|
590
|
+
var SPEAKABLE_NOTIFICATIONS = {
|
|
591
|
+
NEW_ASSIGNMENT: "new_assignment",
|
|
592
|
+
ASSESSMENT_SUBMITTED: "assessment_submitted",
|
|
593
|
+
ASSESSMENT_SCORED: "assessment_scored",
|
|
594
|
+
NEW_COMMENT: "NEW_COMMENT"
|
|
595
|
+
};
|
|
596
|
+
var SpeakableNotificationTypes = {
|
|
597
|
+
NEW_ASSIGNMENT: "NEW_ASSIGNMENT",
|
|
598
|
+
FEEDBACK_FROM_TEACHER: "FEEDBACK_FROM_TEACHER",
|
|
599
|
+
MESSAGE_FROM_STUDENT: "MESSAGE_FROM_STUDENT",
|
|
600
|
+
PHRASE_MARKED_CORRECT: "PHRASE_MARKED_CORRECT",
|
|
601
|
+
STUDENT_PROGRESS: "STUDENT_PROGRESS",
|
|
602
|
+
PLAYLIST_FOLLOWERS: "PLAYLIST_FOLLOWERS",
|
|
603
|
+
PLAYLIST_PLAYS: "PLAYLIST_PLAYS",
|
|
604
|
+
// New notifications
|
|
605
|
+
ASSESSMENT_SUBMITTED: "ASSESSMENT_SUBMITTED",
|
|
606
|
+
// Notification FOR TEACHER when student submits assessment
|
|
607
|
+
ASSESSMENT_SCORED: "ASSESSMENT_SCORED",
|
|
608
|
+
// Notification FOR STUDENT when teacher scores assessment
|
|
609
|
+
// Comment
|
|
610
|
+
NEW_COMMENT: "NEW_COMMENT"
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
// src/domains/notification/services/create-notification.service.ts
|
|
614
|
+
import dayjs2 from "dayjs";
|
|
615
|
+
|
|
616
|
+
// src/constants/web.constants.ts
|
|
617
|
+
var WEB_BASE_URL = "https://app.speakable.io";
|
|
618
|
+
|
|
619
|
+
// src/domains/notification/services/send-notification.service.ts
|
|
620
|
+
var _sendNotification = async (sendTo, notification) => {
|
|
621
|
+
var _a, _b, _c;
|
|
622
|
+
const results = await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "createNotificationV2")) == null ? void 0 : _c({
|
|
623
|
+
sendTo,
|
|
624
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
625
|
+
notification
|
|
626
|
+
}));
|
|
627
|
+
return results;
|
|
628
|
+
};
|
|
629
|
+
var sendNotification = withErrorHandler(_sendNotification, "sendNotification");
|
|
630
|
+
|
|
631
|
+
// src/domains/notification/services/create-notification.service.ts
|
|
632
|
+
var createNotification = async ({
|
|
633
|
+
data,
|
|
634
|
+
type,
|
|
635
|
+
userId,
|
|
636
|
+
profile
|
|
637
|
+
}) => {
|
|
638
|
+
let result;
|
|
639
|
+
switch (type) {
|
|
640
|
+
case SPEAKABLE_NOTIFICATIONS.NEW_ASSIGNMENT:
|
|
641
|
+
result = await handleAssignNotifPromise({ data, profile });
|
|
642
|
+
break;
|
|
643
|
+
case SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SUBMITTED:
|
|
644
|
+
result = await createAssessmentSubmissionNotification({
|
|
645
|
+
data,
|
|
646
|
+
profile,
|
|
647
|
+
userId
|
|
648
|
+
});
|
|
649
|
+
break;
|
|
650
|
+
case SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SCORED:
|
|
651
|
+
result = await createAssessmentScoredNotification({
|
|
652
|
+
data,
|
|
653
|
+
profile
|
|
654
|
+
});
|
|
655
|
+
break;
|
|
656
|
+
default:
|
|
657
|
+
result = null;
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
return result;
|
|
661
|
+
};
|
|
662
|
+
var handleAssignNotifPromise = async ({
|
|
663
|
+
data: assignments,
|
|
664
|
+
profile
|
|
665
|
+
}) => {
|
|
666
|
+
if (!assignments.length) return;
|
|
667
|
+
try {
|
|
668
|
+
const notifsPromises = assignments.map(async (assignment) => {
|
|
669
|
+
const {
|
|
670
|
+
section,
|
|
671
|
+
section: { members },
|
|
672
|
+
...rest
|
|
673
|
+
} = assignment;
|
|
674
|
+
if (!section || !members) throw new Error("Invalid assignment data");
|
|
675
|
+
const data = { section, sendTo: members, assignment: { ...rest } };
|
|
676
|
+
return createNewAssignmentNotification({ data, profile });
|
|
677
|
+
});
|
|
678
|
+
await Promise.all(notifsPromises);
|
|
679
|
+
return {
|
|
680
|
+
success: true,
|
|
681
|
+
message: "Assignment notifications sent successfully"
|
|
682
|
+
};
|
|
683
|
+
} catch (error) {
|
|
684
|
+
console.error("Error in handleAssignNotifPromise:", error);
|
|
685
|
+
throw error;
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
var createNewAssignmentNotification = async ({
|
|
689
|
+
data,
|
|
690
|
+
profile
|
|
691
|
+
}) => {
|
|
692
|
+
var _a;
|
|
693
|
+
const { assignment, sendTo } = data;
|
|
694
|
+
const teacherName = profile.displayName || "Your teacher";
|
|
695
|
+
const dueDate = assignment.dueDateTimestamp ? dayjs2(assignment.dueDateTimestamp.toDate()).format("MMM Do") : null;
|
|
696
|
+
const results = await sendNotification(sendTo, {
|
|
697
|
+
courseId: assignment.courseId,
|
|
698
|
+
type: SPEAKABLE_NOTIFICATIONS.NEW_ASSIGNMENT,
|
|
699
|
+
senderName: teacherName,
|
|
700
|
+
link: `${WEB_BASE_URL}/assignment/${assignment.id}`,
|
|
701
|
+
messagePreview: `A new assignment "${assignment.name}" is now available. ${dueDate ? `Due ${dueDate}` : ""}`,
|
|
702
|
+
title: "New Assignment Available!",
|
|
703
|
+
imageUrl: (_a = profile.image) == null ? void 0 : _a.url
|
|
704
|
+
});
|
|
705
|
+
return results;
|
|
706
|
+
};
|
|
707
|
+
var createAssessmentSubmissionNotification = async ({
|
|
708
|
+
data: assignment,
|
|
709
|
+
profile,
|
|
710
|
+
userId
|
|
711
|
+
}) => {
|
|
712
|
+
var _a;
|
|
713
|
+
const studentName = profile.displayName || "Your student";
|
|
714
|
+
const results = await sendNotification(assignment.owners, {
|
|
715
|
+
courseId: assignment.courseId,
|
|
716
|
+
type: SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SUBMITTED,
|
|
717
|
+
link: `${WEB_BASE_URL}/a/${assignment.id}?studentId=${userId}`,
|
|
718
|
+
title: `Assessment Submitted!`,
|
|
719
|
+
senderName: studentName,
|
|
720
|
+
messagePreview: `${studentName} has submitted the assessment "${assignment.name}"`,
|
|
721
|
+
imageUrl: (_a = profile.image) == null ? void 0 : _a.url
|
|
722
|
+
});
|
|
723
|
+
return results;
|
|
724
|
+
};
|
|
725
|
+
var createAssessmentScoredNotification = async ({
|
|
726
|
+
data,
|
|
727
|
+
profile
|
|
728
|
+
}) => {
|
|
729
|
+
var _a, _b, _c, _d, _e;
|
|
730
|
+
const { assignment, sendTo } = data;
|
|
731
|
+
const teacherName = profile.displayName || "Your teacher";
|
|
732
|
+
const title = `${assignment.isAssessment ? "Assessment" : "Assignment"} Reviewed!`;
|
|
733
|
+
const messagePreview = `Your ${assignment.isAssessment ? "assessment" : "assignment"} has been reviewed by your teacher. Click to view the feedback.`;
|
|
734
|
+
const results = await sendNotification(sendTo, {
|
|
735
|
+
courseId: assignment.courseId,
|
|
736
|
+
type: SPEAKABLE_NOTIFICATIONS.ASSESSMENT_SCORED,
|
|
737
|
+
link: `${WEB_BASE_URL}/assignment/${assignment.id}`,
|
|
738
|
+
title,
|
|
739
|
+
messagePreview,
|
|
740
|
+
imageUrl: (_a = profile.image) == null ? void 0 : _a.url,
|
|
741
|
+
senderName: teacherName
|
|
742
|
+
});
|
|
743
|
+
await ((_e = (_c = (_b = api).httpsCallable) == null ? void 0 : _c.call(_b, "sendAssessmentScoredEmail")) == null ? void 0 : _e({
|
|
744
|
+
assessmentTitle: assignment.name,
|
|
745
|
+
link: `${WEB_BASE_URL}/assignment/${assignment.id}`,
|
|
746
|
+
senderImage: ((_d = profile.image) == null ? void 0 : _d.url) || "",
|
|
747
|
+
studentId: sendTo[0],
|
|
748
|
+
teacherName: profile.displayName
|
|
749
|
+
}));
|
|
750
|
+
return results;
|
|
1377
751
|
};
|
|
1378
|
-
var SpeakablePlanHierarchy = [
|
|
1379
|
-
SpeakablePlanTypes.basic,
|
|
1380
|
-
SpeakablePlanTypes.starter,
|
|
1381
|
-
SpeakablePlanTypes.teacher_pro,
|
|
1382
|
-
SpeakablePlanTypes.growth,
|
|
1383
|
-
SpeakablePlanTypes.professional,
|
|
1384
|
-
SpeakablePlanTypes.school_starter,
|
|
1385
|
-
SpeakablePlanTypes.organization
|
|
1386
|
-
];
|
|
1387
752
|
|
|
1388
|
-
// src/hooks/
|
|
1389
|
-
var
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
753
|
+
// src/domains/notification/hooks/notification.hooks.ts
|
|
754
|
+
var notificationQueryKeys = {
|
|
755
|
+
all: ["notifications"],
|
|
756
|
+
byId: (id) => [...notificationQueryKeys.all, id]
|
|
757
|
+
};
|
|
758
|
+
var useCreateNotification = () => {
|
|
759
|
+
const { user, queryClient } = useSpeakableApi();
|
|
760
|
+
const handleCreateNotifications = async (type, data) => {
|
|
761
|
+
var _a, _b;
|
|
762
|
+
const result = await createNotification({
|
|
763
|
+
type,
|
|
764
|
+
userId: user.auth.uid,
|
|
765
|
+
profile: (_a = user == null ? void 0 : user.profile) != null ? _a : {},
|
|
766
|
+
data
|
|
767
|
+
});
|
|
768
|
+
queryClient.invalidateQueries({
|
|
769
|
+
queryKey: notificationQueryKeys.byId((_b = user == null ? void 0 : user.auth.uid) != null ? _b : "")
|
|
770
|
+
});
|
|
771
|
+
return result;
|
|
1394
772
|
};
|
|
1395
773
|
return {
|
|
1396
|
-
|
|
1397
|
-
permissionsLoaded: permissions.loaded,
|
|
1398
|
-
isStripePlan: permissions.isStripePlan,
|
|
1399
|
-
refreshDate: permissions.refreshDate,
|
|
1400
|
-
isInstitutionPlan: permissions.isInstitutionPlan,
|
|
1401
|
-
subscriptionId: permissions.subscriptionId,
|
|
1402
|
-
contact: permissions.contact,
|
|
1403
|
-
hasGradebook: has(ANALYTICS_PLANS.ANALYTICS_GRADEBOOK),
|
|
1404
|
-
hasGoogleClassroomGradePassback: has(ASSIGNMENT_SETTINGS_PLANS.GOOGLE_CLASSROOM_GRADE_PASSBACK),
|
|
1405
|
-
hasAssessments: has(ASSIGNMENT_SETTINGS_PLANS.ASSESSMENTS),
|
|
1406
|
-
hasSectionAnalytics: has(ANALYTICS_PLANS.ANALYTICS_CLASSROOM_ANALYTICS),
|
|
1407
|
-
hasStudentReports: has(ANALYTICS_PLANS.ANALYTICS_STUDENT_PROGRESS_REPORTS),
|
|
1408
|
-
permissions: permissions || [],
|
|
1409
|
-
hasStudentPortfolios: permissions.hasStudentPortfolios,
|
|
1410
|
-
isFreeOrgTrial: permissions.type === "free_org_trial",
|
|
1411
|
-
freeOrgTrialExpired: permissions.freeOrgTrialExpired
|
|
774
|
+
createNotification: handleCreateNotifications
|
|
1412
775
|
};
|
|
1413
776
|
};
|
|
1414
|
-
var usePermissions_default = usePermissions;
|
|
1415
777
|
|
|
1416
778
|
// src/hooks/useGoogleClassroom.ts
|
|
1417
779
|
var useGoogleClassroom = () => {
|
|
@@ -1685,446 +1047,1091 @@ var calculateScoreAndProgress = (scores, cardsList, weights) => {
|
|
|
1685
1047
|
};
|
|
1686
1048
|
var calculateScoreAndProgress_default = calculateScoreAndProgress;
|
|
1687
1049
|
|
|
1688
|
-
// src/domains/assignment/services/update-score.service.ts
|
|
1689
|
-
async function _updateScore(params) {
|
|
1690
|
-
const path = params.isAssignment ? refsAssignmentFiresotre.assignmentScores({
|
|
1691
|
-
id: params.activityId,
|
|
1692
|
-
userId: params.userId
|
|
1693
|
-
}) : refsScoresPractice.practiceScores({
|
|
1694
|
-
setId: params.activityId,
|
|
1695
|
-
userId: params.userId
|
|
1050
|
+
// src/domains/assignment/services/update-score.service.ts
|
|
1051
|
+
async function _updateScore(params) {
|
|
1052
|
+
const path = params.isAssignment ? refsAssignmentFiresotre.assignmentScores({
|
|
1053
|
+
id: params.activityId,
|
|
1054
|
+
userId: params.userId
|
|
1055
|
+
}) : refsScoresPractice.practiceScores({
|
|
1056
|
+
setId: params.activityId,
|
|
1057
|
+
userId: params.userId
|
|
1058
|
+
});
|
|
1059
|
+
await api.updateDoc(path, {
|
|
1060
|
+
...params.data
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
var updateScore = withErrorHandler(_updateScore, "updateScore");
|
|
1064
|
+
async function _updateCardScore(params) {
|
|
1065
|
+
const path = params.isAssignment ? refsAssignmentFiresotre.assignmentScores({
|
|
1066
|
+
id: params.activityId,
|
|
1067
|
+
userId: params.userId
|
|
1068
|
+
}) : refsScoresPractice.practiceScores({
|
|
1069
|
+
setId: params.activityId,
|
|
1070
|
+
userId: params.userId
|
|
1071
|
+
});
|
|
1072
|
+
const updates = Object.keys(params.updates.cardScore).reduce(
|
|
1073
|
+
(acc, key) => {
|
|
1074
|
+
acc[`cards.${params.cardId}.${key}`] = params.updates.cardScore[key];
|
|
1075
|
+
return acc;
|
|
1076
|
+
},
|
|
1077
|
+
{}
|
|
1078
|
+
);
|
|
1079
|
+
if (params.updates.progress) {
|
|
1080
|
+
updates.progress = params.updates.progress;
|
|
1081
|
+
}
|
|
1082
|
+
if (params.updates.score) {
|
|
1083
|
+
updates.score = params.updates.score;
|
|
1084
|
+
}
|
|
1085
|
+
await api.updateDoc(path, {
|
|
1086
|
+
...updates
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
var updateCardScore = withErrorHandler(_updateCardScore, "updateCardScore");
|
|
1090
|
+
|
|
1091
|
+
// src/domains/assignment/services/clear-score.service.ts
|
|
1092
|
+
import dayjs3 from "dayjs";
|
|
1093
|
+
async function clearScore(params) {
|
|
1094
|
+
var _a, _b, _c, _d, _e;
|
|
1095
|
+
const update = {
|
|
1096
|
+
[`cards.${params.cardId}`]: {
|
|
1097
|
+
attempts: (_a = params.cardScores.attempts) != null ? _a : 1,
|
|
1098
|
+
correct: (_b = params.cardScores.correct) != null ? _b : 0,
|
|
1099
|
+
// save old score history
|
|
1100
|
+
history: [
|
|
1101
|
+
{
|
|
1102
|
+
...params.cardScores,
|
|
1103
|
+
attempts: (_c = params.cardScores.attempts) != null ? _c : 1,
|
|
1104
|
+
correct: (_d = params.cardScores.correct) != null ? _d : 0,
|
|
1105
|
+
retryTime: dayjs3().format("YYYY-MM-DD HH:mm:ss"),
|
|
1106
|
+
history: null
|
|
1107
|
+
},
|
|
1108
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1109
|
+
...(_e = params.cardScores.history) != null ? _e : []
|
|
1110
|
+
]
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
const path = params.isAssignment ? refsAssignmentFiresotre.assignmentScores({
|
|
1114
|
+
id: params.activityId,
|
|
1115
|
+
userId: params.userId
|
|
1116
|
+
}) : refsScoresPractice.practiceScores({
|
|
1117
|
+
setId: params.activityId,
|
|
1118
|
+
userId: params.userId
|
|
1119
|
+
});
|
|
1120
|
+
await api.updateDoc(path, update);
|
|
1121
|
+
return {
|
|
1122
|
+
update,
|
|
1123
|
+
activityId: params.activityId
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
// src/domains/assignment/services/submit-assignment-score.service.ts
|
|
1128
|
+
import dayjs4 from "dayjs";
|
|
1129
|
+
async function _submitAssignmentScore({
|
|
1130
|
+
cardIds,
|
|
1131
|
+
assignment,
|
|
1132
|
+
weights,
|
|
1133
|
+
userId,
|
|
1134
|
+
status,
|
|
1135
|
+
studentName
|
|
1136
|
+
}) {
|
|
1137
|
+
const { serverTimestamp: serverTimestamp2 } = api.accessHelpers();
|
|
1138
|
+
const path = refsAssignmentFiresotre.assignmentScores({ id: assignment.id, userId });
|
|
1139
|
+
const fieldsUpdated = {
|
|
1140
|
+
submitted: true,
|
|
1141
|
+
progress: 100,
|
|
1142
|
+
submissionDate: serverTimestamp2(),
|
|
1143
|
+
status
|
|
1144
|
+
};
|
|
1145
|
+
if (assignment.isAssessment) {
|
|
1146
|
+
const result = await handleAssessment(
|
|
1147
|
+
assignment,
|
|
1148
|
+
userId,
|
|
1149
|
+
cardIds,
|
|
1150
|
+
weights,
|
|
1151
|
+
fieldsUpdated,
|
|
1152
|
+
studentName
|
|
1153
|
+
);
|
|
1154
|
+
return result;
|
|
1155
|
+
} else if (assignment.courseId) {
|
|
1156
|
+
await handleCourseAssignment(assignment, userId);
|
|
1157
|
+
}
|
|
1158
|
+
await api.updateDoc(path, { ...fieldsUpdated });
|
|
1159
|
+
return { success: true, fieldsUpdated };
|
|
1160
|
+
}
|
|
1161
|
+
var submitAssignmentScore = withErrorHandler(
|
|
1162
|
+
_submitAssignmentScore,
|
|
1163
|
+
"submitAssignmentScore"
|
|
1164
|
+
);
|
|
1165
|
+
async function handleAssessment(assignment, userId, cardIds, weights, fieldsUpdated, studentName) {
|
|
1166
|
+
var _a, _b, _c;
|
|
1167
|
+
const path = refsAssignmentFiresotre.assignmentScores({ id: assignment.id, userId });
|
|
1168
|
+
const response = await api.getDoc(path);
|
|
1169
|
+
if (!response.data) {
|
|
1170
|
+
throw new Error("Score not found");
|
|
1171
|
+
}
|
|
1172
|
+
const { score: scoreCalculated } = calculateScoreAndProgress_default(response.data, cardIds, weights);
|
|
1173
|
+
await api.updateDoc(path, { score: scoreCalculated, status: "PENDING_REVIEW" });
|
|
1174
|
+
await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "submitAssessment")) == null ? void 0 : _c({
|
|
1175
|
+
assignmentId: assignment.id,
|
|
1176
|
+
assignmentTitle: assignment.name,
|
|
1177
|
+
userId,
|
|
1178
|
+
teacherId: assignment.owners[0],
|
|
1179
|
+
studentName
|
|
1180
|
+
}));
|
|
1181
|
+
fieldsUpdated.status = "PENDING_REVIEW";
|
|
1182
|
+
return { success: true, fieldsUpdated };
|
|
1183
|
+
}
|
|
1184
|
+
async function handleCourseAssignment(assignment, userId) {
|
|
1185
|
+
var _a, _b, _c;
|
|
1186
|
+
await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "submitAssignmentV2")) == null ? void 0 : _c({
|
|
1187
|
+
assignmentId: assignment.id,
|
|
1188
|
+
userId
|
|
1189
|
+
}));
|
|
1190
|
+
}
|
|
1191
|
+
async function submitPracticeScore({
|
|
1192
|
+
setId,
|
|
1193
|
+
userId,
|
|
1194
|
+
scores
|
|
1195
|
+
}) {
|
|
1196
|
+
const { serverTimestamp: serverTimestamp2 } = api.accessHelpers();
|
|
1197
|
+
const date = dayjs4().format("YYYY-MM-DD-HH-mm");
|
|
1198
|
+
const ref = refsScoresPractice.practiceScoreHistoryRefDoc({ setId, userId, date });
|
|
1199
|
+
const fieldsUpdated = {
|
|
1200
|
+
...scores,
|
|
1201
|
+
submitted: true,
|
|
1202
|
+
progress: 100,
|
|
1203
|
+
submissionDate: serverTimestamp2(),
|
|
1204
|
+
status: "SUBMITTED"
|
|
1205
|
+
};
|
|
1206
|
+
await api.setDoc(ref, { ...fieldsUpdated });
|
|
1207
|
+
const refScores = refsScoresPractice.practiceScores({ userId, setId });
|
|
1208
|
+
await api.deleteDoc(refScores);
|
|
1209
|
+
return { success: true, fieldsUpdated };
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
// src/domains/assignment/hooks/score-hooks.ts
|
|
1213
|
+
var scoreQueryKeys = {
|
|
1214
|
+
all: ["scores"],
|
|
1215
|
+
byId: (id) => [...scoreQueryKeys.all, id],
|
|
1216
|
+
list: () => [...scoreQueryKeys.all, "list"]
|
|
1217
|
+
};
|
|
1218
|
+
function useScore({
|
|
1219
|
+
isAssignment,
|
|
1220
|
+
activityId,
|
|
1221
|
+
userId = "",
|
|
1222
|
+
courseId,
|
|
1223
|
+
enabled = true,
|
|
1224
|
+
googleClassroomUserId
|
|
1225
|
+
}) {
|
|
1226
|
+
return useQuery2({
|
|
1227
|
+
queryFn: () => getScore({
|
|
1228
|
+
userId,
|
|
1229
|
+
courseId,
|
|
1230
|
+
activityId,
|
|
1231
|
+
googleClassroomUserId,
|
|
1232
|
+
isAssignment
|
|
1233
|
+
}),
|
|
1234
|
+
queryKey: scoreQueryKeys.byId(activityId),
|
|
1235
|
+
enabled
|
|
1696
1236
|
});
|
|
1697
|
-
|
|
1698
|
-
|
|
1237
|
+
}
|
|
1238
|
+
var debounceUpdateScore = debounce(updateScore, 1e3);
|
|
1239
|
+
function useUpdateScore() {
|
|
1240
|
+
const { queryClient } = useSpeakableApi();
|
|
1241
|
+
const mutation = useMutation({
|
|
1242
|
+
mutationFn: debounceUpdateScore,
|
|
1243
|
+
onMutate: (variables) => {
|
|
1244
|
+
return handleOptimisticUpdate({
|
|
1245
|
+
queryClient,
|
|
1246
|
+
queryKey: scoreQueryKeys.byId(variables.activityId),
|
|
1247
|
+
newData: variables.data
|
|
1248
|
+
});
|
|
1249
|
+
},
|
|
1250
|
+
onError: (_, variables, context) => {
|
|
1251
|
+
if (context == null ? void 0 : context.previousData)
|
|
1252
|
+
queryClient.setQueryData(scoreQueryKeys.byId(variables.activityId), context.previousData);
|
|
1253
|
+
},
|
|
1254
|
+
onSettled: (_, err, variables) => {
|
|
1255
|
+
queryClient.invalidateQueries({
|
|
1256
|
+
queryKey: scoreQueryKeys.byId(variables.activityId)
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1699
1259
|
});
|
|
1260
|
+
return {
|
|
1261
|
+
mutationUpdateScore: mutation
|
|
1262
|
+
};
|
|
1700
1263
|
}
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1264
|
+
function useUpdateCardScore({
|
|
1265
|
+
isAssignment,
|
|
1266
|
+
activityId,
|
|
1267
|
+
userId,
|
|
1268
|
+
cardIds,
|
|
1269
|
+
weights
|
|
1270
|
+
}) {
|
|
1271
|
+
const { queryClient } = useSpeakableApi();
|
|
1272
|
+
const queryKey = scoreQueryKeys.byId(activityId);
|
|
1273
|
+
const mutation = useMutation({
|
|
1274
|
+
mutationFn: async ({ cardId, cardScore }) => {
|
|
1275
|
+
const previousScores = queryClient.getQueryData(queryKey);
|
|
1276
|
+
const { progress, score, newScoreUpdated, updatedCardScore } = getScoreUpdated({
|
|
1277
|
+
previousScores: previousScores != null ? previousScores : {},
|
|
1278
|
+
cardId,
|
|
1279
|
+
cardScore,
|
|
1280
|
+
cardIds,
|
|
1281
|
+
weights
|
|
1282
|
+
});
|
|
1283
|
+
await updateCardScore({
|
|
1284
|
+
userId,
|
|
1285
|
+
cardId,
|
|
1286
|
+
isAssignment,
|
|
1287
|
+
activityId,
|
|
1288
|
+
updates: {
|
|
1289
|
+
cardScore: updatedCardScore,
|
|
1290
|
+
progress,
|
|
1291
|
+
score
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
return { cardId, scoresUpdated: newScoreUpdated };
|
|
1295
|
+
},
|
|
1296
|
+
onMutate: ({ cardId, cardScore }) => {
|
|
1297
|
+
queryClient.setQueryData(queryKey, (previousScore) => {
|
|
1298
|
+
const updates = handleOptimisticScore({
|
|
1299
|
+
score: previousScore,
|
|
1300
|
+
cardId,
|
|
1301
|
+
cardScore,
|
|
1302
|
+
cardIds,
|
|
1303
|
+
weights
|
|
1304
|
+
});
|
|
1305
|
+
return {
|
|
1306
|
+
...previousScore,
|
|
1307
|
+
...updates
|
|
1308
|
+
};
|
|
1309
|
+
});
|
|
1310
|
+
},
|
|
1311
|
+
// eslint-disable-next-line no-unused-vars
|
|
1312
|
+
onError: (error) => {
|
|
1313
|
+
console.log(error.message);
|
|
1314
|
+
const previousData = queryClient.getQueryData(queryKey);
|
|
1315
|
+
if (previousData) {
|
|
1316
|
+
queryClient.setQueryData(queryKey, previousData);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1709
1319
|
});
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1320
|
+
return {
|
|
1321
|
+
mutationUpdateCardScore: mutation
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
var getScoreUpdated = ({
|
|
1325
|
+
cardId,
|
|
1326
|
+
cardScore,
|
|
1327
|
+
previousScores,
|
|
1328
|
+
cardIds,
|
|
1329
|
+
weights
|
|
1330
|
+
}) => {
|
|
1331
|
+
var _a, _b;
|
|
1332
|
+
const previousCard = (_a = previousScores.cards) == null ? void 0 : _a[cardId];
|
|
1333
|
+
const newCardScore = {
|
|
1334
|
+
...previousCard != null ? previousCard : {},
|
|
1335
|
+
...cardScore
|
|
1336
|
+
};
|
|
1337
|
+
const newScores = {
|
|
1338
|
+
...previousScores,
|
|
1339
|
+
cards: {
|
|
1340
|
+
...(_b = previousScores.cards) != null ? _b : {},
|
|
1341
|
+
[cardId]: newCardScore
|
|
1342
|
+
}
|
|
1343
|
+
};
|
|
1344
|
+
const { score, progress } = calculateScoreAndProgress_default(newScores, cardIds, weights);
|
|
1345
|
+
return {
|
|
1346
|
+
newScoreUpdated: newScores,
|
|
1347
|
+
updatedCardScore: cardScore,
|
|
1348
|
+
score,
|
|
1349
|
+
progress
|
|
1350
|
+
};
|
|
1351
|
+
};
|
|
1352
|
+
var handleOptimisticScore = ({
|
|
1353
|
+
score,
|
|
1354
|
+
cardId,
|
|
1355
|
+
cardScore,
|
|
1356
|
+
cardIds,
|
|
1357
|
+
weights
|
|
1358
|
+
}) => {
|
|
1359
|
+
var _a;
|
|
1360
|
+
let cards = { ...(_a = score == null ? void 0 : score.cards) != null ? _a : {} };
|
|
1361
|
+
cards = {
|
|
1362
|
+
...cards,
|
|
1363
|
+
[cardId]: {
|
|
1364
|
+
...cards[cardId],
|
|
1365
|
+
...cardScore
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
const { score: scoreValue, progress } = calculateScoreAndProgress_default(
|
|
1369
|
+
// @ts-ignore
|
|
1370
|
+
{
|
|
1371
|
+
...score != null ? score : {},
|
|
1372
|
+
cards
|
|
1714
1373
|
},
|
|
1715
|
-
|
|
1374
|
+
cardIds,
|
|
1375
|
+
weights
|
|
1716
1376
|
);
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1377
|
+
return { cards, score: scoreValue, progress };
|
|
1378
|
+
};
|
|
1379
|
+
function useClearScore() {
|
|
1380
|
+
const { queryClient } = useSpeakableApi();
|
|
1381
|
+
const mutation = useMutation({
|
|
1382
|
+
mutationFn: clearScore,
|
|
1383
|
+
onSettled: (result) => {
|
|
1384
|
+
var _a;
|
|
1385
|
+
queryClient.invalidateQueries({
|
|
1386
|
+
queryKey: scoreQueryKeys.byId((_a = result == null ? void 0 : result.activityId) != null ? _a : "")
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
});
|
|
1390
|
+
return {
|
|
1391
|
+
mutationClearScore: mutation
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
function useSubmitAssignmentScore({
|
|
1395
|
+
onAssignmentSubmitted,
|
|
1396
|
+
studentName
|
|
1397
|
+
}) {
|
|
1398
|
+
const { queryClient } = useSpeakableApi();
|
|
1399
|
+
const { hasGoogleClassroomGradePassback } = usePermissions_default();
|
|
1400
|
+
const { submitAssignmentToGoogleClassroom } = useGoogleClassroom();
|
|
1401
|
+
const { createNotification: createNotification2 } = useCreateNotification();
|
|
1402
|
+
const mutation = useMutation({
|
|
1403
|
+
mutationFn: async ({
|
|
1404
|
+
assignment,
|
|
1405
|
+
userId,
|
|
1406
|
+
cardIds,
|
|
1407
|
+
weights,
|
|
1408
|
+
scores,
|
|
1409
|
+
status
|
|
1410
|
+
}) => {
|
|
1411
|
+
try {
|
|
1412
|
+
const scoreUpdated = await submitAssignmentScore({
|
|
1413
|
+
assignment,
|
|
1414
|
+
userId,
|
|
1415
|
+
cardIds,
|
|
1416
|
+
weights,
|
|
1417
|
+
status,
|
|
1418
|
+
studentName
|
|
1419
|
+
});
|
|
1420
|
+
if (assignment.courseWorkId != null && !assignment.isAssessment && hasGoogleClassroomGradePassback) {
|
|
1421
|
+
await submitAssignmentToGoogleClassroom({
|
|
1422
|
+
assignment,
|
|
1423
|
+
scores
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
if (assignment.isAssessment) {
|
|
1427
|
+
createNotification2(SpeakableNotificationTypes.ASSESSMENT_SUBMITTED, assignment);
|
|
1428
|
+
}
|
|
1429
|
+
if (assignment == null ? void 0 : assignment.id) {
|
|
1430
|
+
logSubmitAssignment({
|
|
1431
|
+
courseId: assignment == null ? void 0 : assignment.courseId
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
onAssignmentSubmitted(assignment.id);
|
|
1435
|
+
queryClient.setQueryData(scoreQueryKeys.byId(assignment.id), {
|
|
1436
|
+
...scores,
|
|
1437
|
+
...scoreUpdated.fieldsUpdated
|
|
1438
|
+
});
|
|
1439
|
+
return {
|
|
1440
|
+
success: true,
|
|
1441
|
+
message: "Score submitted successfully"
|
|
1442
|
+
};
|
|
1443
|
+
} catch (error) {
|
|
1444
|
+
return {
|
|
1445
|
+
success: false,
|
|
1446
|
+
error
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1725
1450
|
});
|
|
1451
|
+
return {
|
|
1452
|
+
submitAssignmentScore: mutation.mutateAsync,
|
|
1453
|
+
isLoading: mutation.isPending
|
|
1454
|
+
};
|
|
1726
1455
|
}
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1456
|
+
function useSubmitPracticeScore() {
|
|
1457
|
+
const { queryClient } = useSpeakableApi();
|
|
1458
|
+
const mutation = useMutation({
|
|
1459
|
+
mutationFn: async ({
|
|
1460
|
+
setId,
|
|
1461
|
+
userId,
|
|
1462
|
+
scores
|
|
1463
|
+
}) => {
|
|
1464
|
+
try {
|
|
1465
|
+
await submitPracticeScore({
|
|
1466
|
+
setId,
|
|
1467
|
+
userId,
|
|
1468
|
+
scores
|
|
1469
|
+
});
|
|
1470
|
+
queryClient.invalidateQueries({
|
|
1471
|
+
queryKey: scoreQueryKeys.byId(setId)
|
|
1472
|
+
});
|
|
1473
|
+
return {
|
|
1474
|
+
success: true,
|
|
1475
|
+
message: "Score submitted successfully"
|
|
1476
|
+
};
|
|
1477
|
+
} catch (error) {
|
|
1478
|
+
return {
|
|
1479
|
+
success: false,
|
|
1480
|
+
error
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1749
1483
|
}
|
|
1750
|
-
};
|
|
1751
|
-
const path = params.isAssignment ? refsAssignmentFiresotre.assignmentScores({
|
|
1752
|
-
id: params.activityId,
|
|
1753
|
-
userId: params.userId
|
|
1754
|
-
}) : refsScoresPractice.practiceScores({
|
|
1755
|
-
setId: params.activityId,
|
|
1756
|
-
userId: params.userId
|
|
1757
1484
|
});
|
|
1758
|
-
await api.updateDoc(path, update);
|
|
1759
1485
|
return {
|
|
1760
|
-
|
|
1761
|
-
|
|
1486
|
+
submitPracticeScore: mutation.mutateAsync,
|
|
1487
|
+
isLoading: mutation.isPending
|
|
1762
1488
|
};
|
|
1763
1489
|
}
|
|
1764
1490
|
|
|
1765
|
-
// src/domains/
|
|
1766
|
-
import
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
}
|
|
1794
|
-
|
|
1491
|
+
// src/domains/cards/card.hooks.ts
|
|
1492
|
+
import { useMutation as useMutation2, useQueries, useQuery as useQuery3 } from "@tanstack/react-query";
|
|
1493
|
+
import { useMemo } from "react";
|
|
1494
|
+
|
|
1495
|
+
// src/domains/cards/card.constants.ts
|
|
1496
|
+
var FeedbackTypesCard = /* @__PURE__ */ ((FeedbackTypesCard2) => {
|
|
1497
|
+
FeedbackTypesCard2["SuggestedResponse"] = "suggested_response";
|
|
1498
|
+
FeedbackTypesCard2["Wida"] = "wida";
|
|
1499
|
+
FeedbackTypesCard2["GrammarInsights"] = "grammar_insights";
|
|
1500
|
+
FeedbackTypesCard2["Actfl"] = "actfl";
|
|
1501
|
+
FeedbackTypesCard2["ProficiencyLevel"] = "proficiency_level";
|
|
1502
|
+
return FeedbackTypesCard2;
|
|
1503
|
+
})(FeedbackTypesCard || {});
|
|
1504
|
+
var LeniencyCard = /* @__PURE__ */ ((LeniencyCard2) => {
|
|
1505
|
+
LeniencyCard2["CONFIDENCE"] = "confidence";
|
|
1506
|
+
LeniencyCard2["EASY"] = "easy";
|
|
1507
|
+
LeniencyCard2["NORMAL"] = "normal";
|
|
1508
|
+
LeniencyCard2["HARD"] = "hard";
|
|
1509
|
+
return LeniencyCard2;
|
|
1510
|
+
})(LeniencyCard || {});
|
|
1511
|
+
var LENIENCY_OPTIONS = [
|
|
1512
|
+
{
|
|
1513
|
+
label: "Build Confidence - most lenient",
|
|
1514
|
+
value: "confidence" /* CONFIDENCE */
|
|
1515
|
+
},
|
|
1516
|
+
{
|
|
1517
|
+
label: "Very Lenient",
|
|
1518
|
+
value: "easy" /* EASY */
|
|
1519
|
+
},
|
|
1520
|
+
{
|
|
1521
|
+
label: "Normal",
|
|
1522
|
+
value: "normal" /* NORMAL */
|
|
1523
|
+
},
|
|
1524
|
+
{
|
|
1525
|
+
label: "No leniency - most strict",
|
|
1526
|
+
value: "hard" /* HARD */
|
|
1527
|
+
}
|
|
1528
|
+
];
|
|
1529
|
+
var STUDENT_LEVELS_OPTIONS = [
|
|
1530
|
+
{
|
|
1531
|
+
label: "Beginner",
|
|
1532
|
+
description: "Beginner Level: Just starting out. Can say a few basic words and phrases.",
|
|
1533
|
+
value: "beginner"
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
label: "Elementary",
|
|
1537
|
+
description: "Elementary Level: Can understand simple sentences and have very basic conversations.",
|
|
1538
|
+
value: "elementary"
|
|
1539
|
+
},
|
|
1540
|
+
{
|
|
1541
|
+
label: "Intermediate",
|
|
1542
|
+
description: "Intermediate Level: Can talk about everyday topics and handle common situations.",
|
|
1543
|
+
value: "intermediate"
|
|
1544
|
+
},
|
|
1545
|
+
{
|
|
1546
|
+
label: "Advanced",
|
|
1547
|
+
description: "Advanced Level: Can speak and understand with ease, and explain ideas clearly.",
|
|
1548
|
+
value: "advanced"
|
|
1549
|
+
},
|
|
1550
|
+
{
|
|
1551
|
+
label: "Fluent",
|
|
1552
|
+
description: "Fluent Level: Speaks naturally and easily. Can use the language in work or school settings.",
|
|
1553
|
+
value: "fluent"
|
|
1554
|
+
},
|
|
1555
|
+
{
|
|
1556
|
+
label: "Native-like",
|
|
1557
|
+
description: "Native-like Level: Understands and speaks like a native. Can discuss complex ideas accurately.",
|
|
1558
|
+
value: "nativeLike"
|
|
1559
|
+
}
|
|
1560
|
+
];
|
|
1561
|
+
var BASE_RESPOND_FIELD_VALUES = {
|
|
1562
|
+
title: "",
|
|
1563
|
+
allowRetries: true,
|
|
1564
|
+
respondTime: 180,
|
|
1565
|
+
maxCharacters: 1e3
|
|
1566
|
+
};
|
|
1567
|
+
var BASE_REPEAT_FIELD_VALUES = {
|
|
1568
|
+
repeat: 1
|
|
1569
|
+
};
|
|
1570
|
+
var BASE_MULTIPLE_CHOICE_FIELD_VALUES = {
|
|
1571
|
+
MCQType: "single",
|
|
1572
|
+
answer: ["A"],
|
|
1573
|
+
choices: [
|
|
1574
|
+
{ option: "A", value: "Option A" },
|
|
1575
|
+
{ option: "B", value: "Option B" },
|
|
1576
|
+
{ option: "C", value: "Option C" }
|
|
1577
|
+
]
|
|
1578
|
+
};
|
|
1579
|
+
var VerificationCardStatus = /* @__PURE__ */ ((VerificationCardStatus2) => {
|
|
1580
|
+
VerificationCardStatus2["VERIFIED"] = "VERIFIED";
|
|
1581
|
+
VerificationCardStatus2["WARNING"] = "WARNING";
|
|
1582
|
+
VerificationCardStatus2["NOT_RECOMMENDED"] = "NOT_RECOMMENDED";
|
|
1583
|
+
VerificationCardStatus2["NOT_WORKING"] = "NOT_WORKING";
|
|
1584
|
+
VerificationCardStatus2["NOT_CHECKED"] = "NOT_CHECKED";
|
|
1585
|
+
return VerificationCardStatus2;
|
|
1586
|
+
})(VerificationCardStatus || {});
|
|
1587
|
+
var CARDS_COLLECTION = "flashcards";
|
|
1588
|
+
var refsCardsFiresotre = {
|
|
1589
|
+
allCards: CARDS_COLLECTION,
|
|
1590
|
+
card: (id) => `${CARDS_COLLECTION}/${id}`
|
|
1591
|
+
};
|
|
1592
|
+
|
|
1593
|
+
// src/domains/cards/services/get-card.service.ts
|
|
1594
|
+
async function _getCard(params) {
|
|
1595
|
+
const ref = refsCardsFiresotre.card(params.cardId);
|
|
1596
|
+
const response = await api.getDoc(ref);
|
|
1597
|
+
if (!response.data) return null;
|
|
1598
|
+
return response.data;
|
|
1599
|
+
}
|
|
1600
|
+
var getCard = withErrorHandler(_getCard, "getCard");
|
|
1601
|
+
|
|
1602
|
+
// src/domains/cards/services/create-card.service.ts
|
|
1603
|
+
import { v4 } from "uuid";
|
|
1604
|
+
|
|
1605
|
+
// src/domains/cards/card.model.ts
|
|
1606
|
+
var CardActivityType = /* @__PURE__ */ ((CardActivityType2) => {
|
|
1607
|
+
CardActivityType2["READ_REPEAT"] = "READ_REPEAT";
|
|
1608
|
+
CardActivityType2["VIDEO"] = "VIDEO";
|
|
1609
|
+
CardActivityType2["TEXT"] = "TEXT";
|
|
1610
|
+
CardActivityType2["READ_RESPOND"] = "READ_RESPOND";
|
|
1611
|
+
CardActivityType2["FREE_RESPONSE"] = "FREE_RESPONSE";
|
|
1612
|
+
CardActivityType2["REPEAT"] = "REPEAT";
|
|
1613
|
+
CardActivityType2["RESPOND"] = "RESPOND";
|
|
1614
|
+
CardActivityType2["RESPOND_WRITE"] = "RESPOND_WRITE";
|
|
1615
|
+
CardActivityType2["TEXT_TO_SPEECH"] = "TEXT_TO_SPEECH";
|
|
1616
|
+
CardActivityType2["MULTIPLE_CHOICE"] = "MULTIPLE_CHOICE";
|
|
1617
|
+
CardActivityType2["PODCAST"] = "PODCAST";
|
|
1618
|
+
CardActivityType2["MEDIA_PAGE"] = "MEDIA_PAGE";
|
|
1619
|
+
CardActivityType2["WRITE"] = "WRITE";
|
|
1620
|
+
CardActivityType2["SHORT_ANSWER"] = "SHORT_ANSWER";
|
|
1621
|
+
CardActivityType2["SHORT_STORY"] = "SHORT_STORY";
|
|
1622
|
+
CardActivityType2["SPEAK"] = "SPEAK";
|
|
1623
|
+
CardActivityType2["CONVERSATION"] = "CONVERSATION";
|
|
1624
|
+
CardActivityType2["CONVERSATION_WRITE"] = "CONVERSATION_WRITE";
|
|
1625
|
+
CardActivityType2["DIALOGUE"] = "DIALOGUE";
|
|
1626
|
+
CardActivityType2["INSTRUCTION"] = "INSTRUCTION";
|
|
1627
|
+
CardActivityType2["LISTEN"] = "LISTEN";
|
|
1628
|
+
CardActivityType2["READ"] = "READ";
|
|
1629
|
+
CardActivityType2["ANSWER"] = "ANSWER";
|
|
1630
|
+
return CardActivityType2;
|
|
1631
|
+
})(CardActivityType || {});
|
|
1632
|
+
var RESPOND_CARD_ACTIVITY_TYPES = [
|
|
1633
|
+
"READ_RESPOND" /* READ_RESPOND */,
|
|
1634
|
+
"RESPOND" /* RESPOND */,
|
|
1635
|
+
"RESPOND_WRITE" /* RESPOND_WRITE */,
|
|
1636
|
+
"FREE_RESPONSE" /* FREE_RESPONSE */
|
|
1637
|
+
];
|
|
1638
|
+
var MULTIPLE_CHOICE_CARD_ACTIVITY_TYPES = ["MULTIPLE_CHOICE" /* MULTIPLE_CHOICE */];
|
|
1639
|
+
var REPEAT_CARD_ACTIVITY_TYPES = ["READ_REPEAT" /* READ_REPEAT */, "REPEAT" /* REPEAT */];
|
|
1640
|
+
var RESPOND_WRITE_CARD_ACTIVITY_TYPES = [
|
|
1641
|
+
"RESPOND_WRITE" /* RESPOND_WRITE */,
|
|
1642
|
+
"FREE_RESPONSE" /* FREE_RESPONSE */
|
|
1643
|
+
];
|
|
1644
|
+
var RESPOND_AUDIO_CARD_ACTIVITY_TYPES = [
|
|
1645
|
+
"RESPOND" /* RESPOND */,
|
|
1646
|
+
"READ_RESPOND" /* READ_RESPOND */
|
|
1647
|
+
];
|
|
1648
|
+
var ALLOWED_CARD_ACTIVITY_TYPES_FOR_SUMMARY = [
|
|
1649
|
+
"REPEAT" /* REPEAT */,
|
|
1650
|
+
"RESPOND" /* RESPOND */,
|
|
1651
|
+
"READ_REPEAT" /* READ_REPEAT */,
|
|
1652
|
+
"READ_RESPOND" /* READ_RESPOND */,
|
|
1653
|
+
"FREE_RESPONSE" /* FREE_RESPONSE */,
|
|
1654
|
+
"RESPOND_WRITE" /* RESPOND_WRITE */,
|
|
1655
|
+
"MULTIPLE_CHOICE" /* MULTIPLE_CHOICE */
|
|
1656
|
+
];
|
|
1657
|
+
|
|
1658
|
+
// src/utils/text-utils.ts
|
|
1659
|
+
import sha1 from "js-sha1";
|
|
1660
|
+
var purify = (word) => {
|
|
1661
|
+
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();
|
|
1662
|
+
};
|
|
1663
|
+
var cleanString = (words) => {
|
|
1664
|
+
const splitWords = words == null ? void 0 : words.split("+");
|
|
1665
|
+
if (splitWords && splitWords.length === 1) {
|
|
1666
|
+
const newWord = purify(words);
|
|
1667
|
+
return newWord;
|
|
1668
|
+
} else if (splitWords && splitWords.length > 1) {
|
|
1669
|
+
const split = splitWords.map((w) => purify(w));
|
|
1670
|
+
return split;
|
|
1671
|
+
} else {
|
|
1672
|
+
return "";
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
var getWordHash = (word, language) => {
|
|
1676
|
+
const cleanedWord = cleanString(word);
|
|
1677
|
+
const wordHash = sha1(`${language}-${cleanedWord}`);
|
|
1678
|
+
console.log("wordHash core library", wordHash);
|
|
1679
|
+
return wordHash;
|
|
1680
|
+
};
|
|
1681
|
+
|
|
1682
|
+
// src/domains/cards/services/get-card-verification-status.service.ts
|
|
1683
|
+
var charactarLanguages = ["zh", "ja", "ko"];
|
|
1684
|
+
var getVerificationStatus = async (target_text, language) => {
|
|
1685
|
+
if ((target_text == null ? void 0 : target_text.length) < 3 && !charactarLanguages.includes(language)) {
|
|
1686
|
+
return "NOT_RECOMMENDED" /* NOT_RECOMMENDED */;
|
|
1795
1687
|
}
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
const response = await api.getDoc(path);
|
|
1807
|
-
if (!response.data) {
|
|
1808
|
-
throw new Error("Score not found");
|
|
1688
|
+
const hash = getWordHash(target_text, language);
|
|
1689
|
+
const response = await api.getDoc(`checked-pronunciations/${hash}`);
|
|
1690
|
+
try {
|
|
1691
|
+
if (response.data) {
|
|
1692
|
+
return processRecord(response.data);
|
|
1693
|
+
} else {
|
|
1694
|
+
return "NOT_CHECKED" /* NOT_CHECKED */;
|
|
1695
|
+
}
|
|
1696
|
+
} catch (e) {
|
|
1697
|
+
return "NOT_CHECKED" /* NOT_CHECKED */;
|
|
1809
1698
|
}
|
|
1810
|
-
const { score: scoreCalculated } = calculateScoreAndProgress_default(response.data, cardIds, weights);
|
|
1811
|
-
await api.updateDoc(path, { score: scoreCalculated, status: "PENDING_REVIEW" });
|
|
1812
|
-
await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "submitAssessment")) == null ? void 0 : _c({
|
|
1813
|
-
assignmentId: assignment.id,
|
|
1814
|
-
assignmentTitle: assignment.name,
|
|
1815
|
-
userId,
|
|
1816
|
-
teacherId: assignment.owners[0],
|
|
1817
|
-
studentName
|
|
1818
|
-
}));
|
|
1819
|
-
fieldsUpdated.status = "PENDING_REVIEW";
|
|
1820
|
-
return { success: true, fieldsUpdated };
|
|
1821
|
-
}
|
|
1822
|
-
async function handleCourseAssignment(assignment, userId) {
|
|
1823
|
-
var _a, _b, _c;
|
|
1824
|
-
await ((_c = (_b = (_a = api).httpsCallable) == null ? void 0 : _b.call(_a, "submitAssignmentV2")) == null ? void 0 : _c({
|
|
1825
|
-
assignmentId: assignment.id,
|
|
1826
|
-
userId
|
|
1827
|
-
}));
|
|
1828
|
-
}
|
|
1829
|
-
async function submitPracticeScore({
|
|
1830
|
-
setId,
|
|
1831
|
-
userId,
|
|
1832
|
-
scores
|
|
1833
|
-
}) {
|
|
1834
|
-
const { serverTimestamp: serverTimestamp2 } = api.accessHelpers();
|
|
1835
|
-
const date = dayjs4().format("YYYY-MM-DD-HH-mm");
|
|
1836
|
-
const ref = refsScoresPractice.practiceScoreHistoryRefDoc({ setId, userId, date });
|
|
1837
|
-
const fieldsUpdated = {
|
|
1838
|
-
...scores,
|
|
1839
|
-
submitted: true,
|
|
1840
|
-
progress: 100,
|
|
1841
|
-
submissionDate: serverTimestamp2(),
|
|
1842
|
-
status: "SUBMITTED"
|
|
1843
|
-
};
|
|
1844
|
-
await api.setDoc(ref, { ...fieldsUpdated });
|
|
1845
|
-
const refScores = refsScoresPractice.practiceScores({ userId, setId });
|
|
1846
|
-
await api.deleteDoc(refScores);
|
|
1847
|
-
return { success: true, fieldsUpdated };
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
// src/domains/assignment/hooks/score-hooks.ts
|
|
1851
|
-
var scoreQueryKeys = {
|
|
1852
|
-
all: ["scores"],
|
|
1853
|
-
byId: (id) => [...scoreQueryKeys.all, id],
|
|
1854
|
-
list: () => [...scoreQueryKeys.all, "list"]
|
|
1855
1699
|
};
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
}
|
|
1700
|
+
var processRecord = (data) => {
|
|
1701
|
+
const { pronunciations = 0, fails = 0 } = data;
|
|
1702
|
+
const attempts = pronunciations + fails;
|
|
1703
|
+
const successRate = attempts > 0 ? pronunciations / attempts * 100 : 0;
|
|
1704
|
+
let newStatus = null;
|
|
1705
|
+
if (attempts < 6) {
|
|
1706
|
+
return "NOT_CHECKED" /* NOT_CHECKED */;
|
|
1707
|
+
}
|
|
1708
|
+
if (successRate > 25) {
|
|
1709
|
+
newStatus = "VERIFIED" /* VERIFIED */;
|
|
1710
|
+
} else if (successRate > 10) {
|
|
1711
|
+
newStatus = "WARNING" /* WARNING */;
|
|
1712
|
+
} else if (fails > 20 && successRate < 10 && pronunciations > 1) {
|
|
1713
|
+
newStatus = "NOT_RECOMMENDED" /* NOT_RECOMMENDED */;
|
|
1714
|
+
} else if (pronunciations === 0 && fails > 20) {
|
|
1715
|
+
newStatus = "NOT_WORKING" /* NOT_WORKING */;
|
|
1716
|
+
} else {
|
|
1717
|
+
newStatus = "NOT_CHECKED" /* NOT_CHECKED */;
|
|
1718
|
+
}
|
|
1719
|
+
return newStatus;
|
|
1720
|
+
};
|
|
1721
|
+
|
|
1722
|
+
// src/domains/cards/services/create-card.service.ts
|
|
1723
|
+
async function _createCard({ data }) {
|
|
1724
|
+
const response = await api.addDoc(refsCardsFiresotre.allCards, data);
|
|
1725
|
+
return response;
|
|
1875
1726
|
}
|
|
1876
|
-
var
|
|
1877
|
-
function
|
|
1878
|
-
const {
|
|
1879
|
-
const
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
},
|
|
1892
|
-
onSettled: (_, err, variables) => {
|
|
1893
|
-
queryClient.invalidateQueries({
|
|
1894
|
-
queryKey: scoreQueryKeys.byId(variables.activityId)
|
|
1895
|
-
});
|
|
1727
|
+
var createCard = withErrorHandler(_createCard, "createCard");
|
|
1728
|
+
async function _createCards({ cards }) {
|
|
1729
|
+
const { writeBatch: writeBatch2, doc: doc2 } = api.accessHelpers();
|
|
1730
|
+
const batch = writeBatch2();
|
|
1731
|
+
const cardsWithId = [];
|
|
1732
|
+
for (const card of cards) {
|
|
1733
|
+
const cardId = v4();
|
|
1734
|
+
const ref = doc2(refsCardsFiresotre.card(cardId));
|
|
1735
|
+
const newCardObject = {
|
|
1736
|
+
...card,
|
|
1737
|
+
id: cardId
|
|
1738
|
+
};
|
|
1739
|
+
if (card.type === "READ_REPEAT" /* READ_REPEAT */ && card.target_text && card.language) {
|
|
1740
|
+
const verificationStatus = await getVerificationStatus(card.target_text, card.language);
|
|
1741
|
+
newCardObject.verificationStatus = verificationStatus || null;
|
|
1896
1742
|
}
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1743
|
+
cardsWithId.push(newCardObject);
|
|
1744
|
+
batch.set(ref, newCardObject);
|
|
1745
|
+
}
|
|
1746
|
+
await batch.commit();
|
|
1747
|
+
return cardsWithId;
|
|
1901
1748
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1749
|
+
var createCards = withErrorHandler(_createCards, "createCards");
|
|
1750
|
+
|
|
1751
|
+
// src/domains/cards/card.hooks.ts
|
|
1752
|
+
var cardsQueryKeys = {
|
|
1753
|
+
all: ["cards"],
|
|
1754
|
+
one: (params) => [...cardsQueryKeys.all, params.cardId]
|
|
1755
|
+
};
|
|
1756
|
+
function useCards({
|
|
1906
1757
|
cardIds,
|
|
1907
|
-
|
|
1758
|
+
enabled = true,
|
|
1759
|
+
asObject
|
|
1908
1760
|
}) {
|
|
1909
|
-
const
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
cardScore,
|
|
1918
|
-
cardIds,
|
|
1919
|
-
weights
|
|
1920
|
-
});
|
|
1921
|
-
await updateCardScore({
|
|
1922
|
-
userId,
|
|
1923
|
-
cardId,
|
|
1924
|
-
isAssignment,
|
|
1925
|
-
activityId,
|
|
1926
|
-
updates: {
|
|
1927
|
-
cardScore: updatedCardScore,
|
|
1928
|
-
progress,
|
|
1929
|
-
score
|
|
1930
|
-
}
|
|
1931
|
-
});
|
|
1932
|
-
return { cardId, scoresUpdated: newScoreUpdated };
|
|
1933
|
-
},
|
|
1934
|
-
onMutate: ({ cardId, cardScore }) => {
|
|
1935
|
-
queryClient.setQueryData(queryKey, (previousScore) => {
|
|
1936
|
-
const updates = handleOptimisticScore({
|
|
1937
|
-
score: previousScore,
|
|
1938
|
-
cardId,
|
|
1939
|
-
cardScore,
|
|
1940
|
-
cardIds,
|
|
1941
|
-
weights
|
|
1942
|
-
});
|
|
1943
|
-
return {
|
|
1944
|
-
...previousScore,
|
|
1945
|
-
...updates
|
|
1946
|
-
};
|
|
1947
|
-
});
|
|
1948
|
-
},
|
|
1949
|
-
// eslint-disable-next-line no-unused-vars
|
|
1950
|
-
onError: (error) => {
|
|
1951
|
-
console.log(error.message);
|
|
1952
|
-
const previousData = queryClient.getQueryData(queryKey);
|
|
1953
|
-
if (previousData) {
|
|
1954
|
-
queryClient.setQueryData(queryKey, previousData);
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
1761
|
+
const queries = useQueries({
|
|
1762
|
+
queries: cardIds.map((cardId) => ({
|
|
1763
|
+
enabled: enabled && cardIds.length > 0,
|
|
1764
|
+
queryKey: cardsQueryKeys.one({
|
|
1765
|
+
cardId
|
|
1766
|
+
}),
|
|
1767
|
+
queryFn: () => getCard({ cardId })
|
|
1768
|
+
}))
|
|
1957
1769
|
});
|
|
1770
|
+
const cards = queries.map((query2) => query2.data).filter(Boolean);
|
|
1771
|
+
const cardsObject = useMemo(() => {
|
|
1772
|
+
if (!asObject) return null;
|
|
1773
|
+
return cards.reduce((acc, card) => {
|
|
1774
|
+
acc[card.id] = card;
|
|
1775
|
+
return acc;
|
|
1776
|
+
}, {});
|
|
1777
|
+
}, [asObject, cards]);
|
|
1958
1778
|
return {
|
|
1959
|
-
|
|
1779
|
+
cards,
|
|
1780
|
+
cardsObject,
|
|
1781
|
+
cardsQueries: queries
|
|
1960
1782
|
};
|
|
1961
1783
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
}) => {
|
|
1969
|
-
var _a, _b;
|
|
1970
|
-
const previousCard = (_a = previousScores.cards) == null ? void 0 : _a[cardId];
|
|
1971
|
-
const newCardScore = {
|
|
1972
|
-
...previousCard != null ? previousCard : {},
|
|
1973
|
-
...cardScore
|
|
1974
|
-
};
|
|
1975
|
-
const newScores = {
|
|
1976
|
-
...previousScores,
|
|
1977
|
-
cards: {
|
|
1978
|
-
...(_b = previousScores.cards) != null ? _b : {},
|
|
1979
|
-
[cardId]: newCardScore
|
|
1784
|
+
function useCreateCard() {
|
|
1785
|
+
const { queryClient } = useSpeakableApi();
|
|
1786
|
+
const mutationCreateCard = useMutation2({
|
|
1787
|
+
mutationFn: createCard,
|
|
1788
|
+
onSuccess: (cardCreated) => {
|
|
1789
|
+
queryClient.invalidateQueries({ queryKey: cardsQueryKeys.one({ cardId: cardCreated.id }) });
|
|
1980
1790
|
}
|
|
1791
|
+
});
|
|
1792
|
+
return {
|
|
1793
|
+
mutationCreateCard
|
|
1981
1794
|
};
|
|
1982
|
-
|
|
1795
|
+
}
|
|
1796
|
+
function useCreateCards() {
|
|
1797
|
+
const mutationCreateCards = useMutation2({
|
|
1798
|
+
mutationFn: createCards
|
|
1799
|
+
});
|
|
1983
1800
|
return {
|
|
1984
|
-
|
|
1985
|
-
updatedCardScore: cardScore,
|
|
1986
|
-
score,
|
|
1987
|
-
progress
|
|
1801
|
+
mutationCreateCards
|
|
1988
1802
|
};
|
|
1989
|
-
}
|
|
1990
|
-
|
|
1991
|
-
score,
|
|
1803
|
+
}
|
|
1804
|
+
function getCardFromCache({
|
|
1992
1805
|
cardId,
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
}
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
1806
|
+
queryClient
|
|
1807
|
+
}) {
|
|
1808
|
+
return queryClient.getQueryData(cardsQueryKeys.one({ cardId }));
|
|
1809
|
+
}
|
|
1810
|
+
function updateCardInCache({
|
|
1811
|
+
cardId,
|
|
1812
|
+
card,
|
|
1813
|
+
queryClient
|
|
1814
|
+
}) {
|
|
1815
|
+
queryClient.setQueryData(cardsQueryKeys.one({ cardId }), card);
|
|
1816
|
+
}
|
|
1817
|
+
function useGetCard({ cardId }) {
|
|
1818
|
+
const query2 = useQuery3({
|
|
1819
|
+
queryKey: cardsQueryKeys.one({ cardId }),
|
|
1820
|
+
queryFn: () => getCard({ cardId })
|
|
1821
|
+
});
|
|
1822
|
+
return query2;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
// src/domains/cards/card.repo.ts
|
|
1826
|
+
var createCardRepo = () => {
|
|
1827
|
+
return {
|
|
1828
|
+
createCard,
|
|
1829
|
+
createCards,
|
|
1830
|
+
getCard
|
|
2005
1831
|
};
|
|
2006
|
-
const { score: scoreValue, progress } = calculateScoreAndProgress_default(
|
|
2007
|
-
// @ts-ignore
|
|
2008
|
-
{
|
|
2009
|
-
...score != null ? score : {},
|
|
2010
|
-
cards
|
|
2011
|
-
},
|
|
2012
|
-
cardIds,
|
|
2013
|
-
weights
|
|
2014
|
-
);
|
|
2015
|
-
return { cards, score: scoreValue, progress };
|
|
2016
1832
|
};
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
1833
|
+
|
|
1834
|
+
// src/domains/sets/set.hooks.ts
|
|
1835
|
+
import { useQuery as useQuery4 } from "@tanstack/react-query";
|
|
1836
|
+
|
|
1837
|
+
// src/domains/sets/set.constants.ts
|
|
1838
|
+
var SETS_COLLECTION = "sets";
|
|
1839
|
+
var refsSetsFirestore = {
|
|
1840
|
+
allSets: SETS_COLLECTION,
|
|
1841
|
+
set: (id) => `${SETS_COLLECTION}/${id}`
|
|
1842
|
+
};
|
|
1843
|
+
|
|
1844
|
+
// src/domains/sets/services/get-set.service.ts
|
|
1845
|
+
async function _getSet({ setId }) {
|
|
1846
|
+
const response = await api.getDoc(refsSetsFirestore.set(setId));
|
|
1847
|
+
return response.data;
|
|
1848
|
+
}
|
|
1849
|
+
var getSet = withErrorHandler(_getSet, "getSet");
|
|
1850
|
+
|
|
1851
|
+
// src/domains/sets/set.hooks.ts
|
|
1852
|
+
var setsQueryKeys = {
|
|
1853
|
+
all: ["sets"],
|
|
1854
|
+
one: (params) => [...setsQueryKeys.all, params.setId]
|
|
1855
|
+
};
|
|
1856
|
+
var useSet = ({ setId, enabled }) => {
|
|
1857
|
+
return useQuery4({
|
|
1858
|
+
queryKey: setsQueryKeys.one({ setId }),
|
|
1859
|
+
queryFn: () => getSet({ setId }),
|
|
1860
|
+
enabled: setId !== void 0 && setId !== "" && enabled
|
|
1861
|
+
});
|
|
1862
|
+
};
|
|
1863
|
+
function getSetFromCache({
|
|
1864
|
+
setId,
|
|
1865
|
+
queryClient
|
|
1866
|
+
}) {
|
|
1867
|
+
if (!setId) return null;
|
|
1868
|
+
return queryClient.getQueryData(setsQueryKeys.one({ setId }));
|
|
1869
|
+
}
|
|
1870
|
+
function updateSetInCache({
|
|
1871
|
+
set,
|
|
1872
|
+
queryClient
|
|
1873
|
+
}) {
|
|
1874
|
+
const { id, ...setData } = set;
|
|
1875
|
+
queryClient.setQueryData(setsQueryKeys.one({ setId: id }), setData);
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// src/domains/sets/set.repo.ts
|
|
1879
|
+
var createSetRepo = () => {
|
|
1880
|
+
return {
|
|
1881
|
+
getSet
|
|
1882
|
+
};
|
|
1883
|
+
};
|
|
1884
|
+
|
|
1885
|
+
// src/constants/all-langs.json
|
|
1886
|
+
var all_langs_default = {
|
|
1887
|
+
af: "Afrikaans",
|
|
1888
|
+
sq: "Albanian",
|
|
1889
|
+
am: "Amharic",
|
|
1890
|
+
ar: "Arabic",
|
|
1891
|
+
hy: "Armenian",
|
|
1892
|
+
az: "Azerbaijani",
|
|
1893
|
+
eu: "Basque",
|
|
1894
|
+
be: "Belarusian",
|
|
1895
|
+
bn: "Bengali",
|
|
1896
|
+
bs: "Bosnian",
|
|
1897
|
+
bg: "Bulgarian",
|
|
1898
|
+
ca: "Catalan",
|
|
1899
|
+
ceb: "Cebuano",
|
|
1900
|
+
zh: "Chinese",
|
|
1901
|
+
co: "Corsican",
|
|
1902
|
+
hr: "Croatian",
|
|
1903
|
+
cs: "Czech",
|
|
1904
|
+
da: "Danish",
|
|
1905
|
+
nl: "Dutch",
|
|
1906
|
+
en: "English",
|
|
1907
|
+
eo: "Esperanto",
|
|
1908
|
+
et: "Estonian",
|
|
1909
|
+
fi: "Finnish",
|
|
1910
|
+
fr: "French",
|
|
1911
|
+
fy: "Frisian",
|
|
1912
|
+
gl: "Galician",
|
|
1913
|
+
ka: "Georgian",
|
|
1914
|
+
de: "German",
|
|
1915
|
+
el: "Greek",
|
|
1916
|
+
gu: "Gujarati",
|
|
1917
|
+
ht: "Haitian Creole",
|
|
1918
|
+
ha: "Hausa",
|
|
1919
|
+
haw: "Hawaiian",
|
|
1920
|
+
he: "Hebrew",
|
|
1921
|
+
hi: "Hindi",
|
|
1922
|
+
hmn: "Hmong",
|
|
1923
|
+
hu: "Hungarian",
|
|
1924
|
+
is: "Icelandic",
|
|
1925
|
+
ig: "Igbo",
|
|
1926
|
+
id: "Indonesian",
|
|
1927
|
+
ga: "Irish",
|
|
1928
|
+
it: "Italian",
|
|
1929
|
+
ja: "Japanese",
|
|
1930
|
+
jv: "Javanese",
|
|
1931
|
+
kn: "Kannada",
|
|
1932
|
+
kk: "Kazakh",
|
|
1933
|
+
km: "Khmer",
|
|
1934
|
+
ko: "Korean",
|
|
1935
|
+
ku: "Kurdish",
|
|
1936
|
+
ky: "Kyrgyz",
|
|
1937
|
+
lo: "Lao",
|
|
1938
|
+
la: "Latin",
|
|
1939
|
+
lv: "Latvian",
|
|
1940
|
+
lt: "Lithuanian",
|
|
1941
|
+
lb: "Luxembourgish",
|
|
1942
|
+
mk: "Macedonian",
|
|
1943
|
+
mg: "Malagasy",
|
|
1944
|
+
ms: "Malay",
|
|
1945
|
+
ml: "Malayalam",
|
|
1946
|
+
mt: "Maltese",
|
|
1947
|
+
mi: "Maori",
|
|
1948
|
+
mr: "Marathi",
|
|
1949
|
+
mn: "Mongolian",
|
|
1950
|
+
my: "Myanmar (Burmese)",
|
|
1951
|
+
ne: "Nepali",
|
|
1952
|
+
no: "Norwegian",
|
|
1953
|
+
ny: "Nyanja (Chichewa)",
|
|
1954
|
+
ps: "Pashto",
|
|
1955
|
+
fa: "Persian",
|
|
1956
|
+
pl: "Polish",
|
|
1957
|
+
pt: "Portuguese",
|
|
1958
|
+
pa: "Punjabi",
|
|
1959
|
+
ro: "Romanian",
|
|
1960
|
+
ru: "Russian",
|
|
1961
|
+
sm: "Samoan",
|
|
1962
|
+
gd: "Scots Gaelic",
|
|
1963
|
+
sr: "Serbian",
|
|
1964
|
+
st: "Sesotho",
|
|
1965
|
+
sn: "Shona",
|
|
1966
|
+
sd: "Sindhi",
|
|
1967
|
+
si: "Sinhala (Sinhalese)",
|
|
1968
|
+
sk: "Slovak",
|
|
1969
|
+
sl: "Slovenian",
|
|
1970
|
+
so: "Somali",
|
|
1971
|
+
es: "Spanish",
|
|
1972
|
+
su: "Sundanese",
|
|
1973
|
+
sw: "Swahili",
|
|
1974
|
+
sv: "Swedish",
|
|
1975
|
+
tl: "Tagalog (Filipino)",
|
|
1976
|
+
tg: "Tajik",
|
|
1977
|
+
ta: "Tamil",
|
|
1978
|
+
te: "Telugu",
|
|
1979
|
+
th: "Thai",
|
|
1980
|
+
tr: "Turkish",
|
|
1981
|
+
uk: "Ukrainian",
|
|
1982
|
+
ur: "Urdu",
|
|
1983
|
+
uz: "Uzbek",
|
|
1984
|
+
vi: "Vietnamese",
|
|
1985
|
+
cy: "Welsh",
|
|
1986
|
+
xh: "Xhosa",
|
|
1987
|
+
yi: "Yiddish",
|
|
1988
|
+
yo: "Yoruba",
|
|
1989
|
+
zu: "Zulu"
|
|
1990
|
+
};
|
|
1991
|
+
|
|
1992
|
+
// src/utils/ai/get-respond-card-tool.ts
|
|
1993
|
+
var getRespondCardTool = ({
|
|
1994
|
+
language,
|
|
1995
|
+
standard = "actfl"
|
|
1996
|
+
}) => {
|
|
1997
|
+
const lang = all_langs_default[language] || "English";
|
|
1998
|
+
const tool = {
|
|
1999
|
+
tool_choice: {
|
|
2000
|
+
type: "function",
|
|
2001
|
+
function: { name: "get_feedback" }
|
|
2002
|
+
},
|
|
2003
|
+
tools: [
|
|
2004
|
+
{
|
|
2005
|
+
type: "function",
|
|
2006
|
+
function: {
|
|
2007
|
+
name: "get_feedback",
|
|
2008
|
+
description: "Get feedback on a student's response",
|
|
2009
|
+
parameters: {
|
|
2010
|
+
type: "object",
|
|
2011
|
+
required: [
|
|
2012
|
+
"success",
|
|
2013
|
+
"score",
|
|
2014
|
+
"score_justification",
|
|
2015
|
+
"errors",
|
|
2016
|
+
"improvedResponse",
|
|
2017
|
+
"compliments"
|
|
2018
|
+
],
|
|
2019
|
+
properties: {
|
|
2020
|
+
success: {
|
|
2021
|
+
type: "boolean",
|
|
2022
|
+
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."
|
|
2023
|
+
},
|
|
2024
|
+
errors: {
|
|
2025
|
+
type: "array",
|
|
2026
|
+
items: {
|
|
2027
|
+
type: "object",
|
|
2028
|
+
required: ["error", "grammar_error_type", "correction", "justification"],
|
|
2029
|
+
properties: {
|
|
2030
|
+
error: {
|
|
2031
|
+
type: "string",
|
|
2032
|
+
description: "The grammatical error in the student's response."
|
|
2033
|
+
},
|
|
2034
|
+
correction: {
|
|
2035
|
+
type: "string",
|
|
2036
|
+
description: "The suggested correction to the error"
|
|
2037
|
+
},
|
|
2038
|
+
justification: {
|
|
2039
|
+
type: "string",
|
|
2040
|
+
description: `An explanation of the rationale behind the suggested correction. WRITE THIS IN ${lang}!`
|
|
2041
|
+
},
|
|
2042
|
+
grammar_error_type: {
|
|
2043
|
+
type: "string",
|
|
2044
|
+
enum: [
|
|
2045
|
+
"subjVerbAgree",
|
|
2046
|
+
"tenseErrors",
|
|
2047
|
+
"articleMisuse",
|
|
2048
|
+
"prepositionErrors",
|
|
2049
|
+
"adjNounAgree",
|
|
2050
|
+
"pronounErrors",
|
|
2051
|
+
"wordOrder",
|
|
2052
|
+
"verbConjugation",
|
|
2053
|
+
"pluralization",
|
|
2054
|
+
"negationErrors",
|
|
2055
|
+
"modalVerbMisuse",
|
|
2056
|
+
"relativeClause",
|
|
2057
|
+
"auxiliaryVerb",
|
|
2058
|
+
"complexSentenceAgreement",
|
|
2059
|
+
"idiomaticExpression",
|
|
2060
|
+
"registerInconsistency",
|
|
2061
|
+
"voiceMisuse"
|
|
2062
|
+
],
|
|
2063
|
+
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"
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
},
|
|
2067
|
+
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."
|
|
2068
|
+
},
|
|
2069
|
+
compliments: {
|
|
2070
|
+
type: "array",
|
|
2071
|
+
items: {
|
|
2072
|
+
type: "string"
|
|
2073
|
+
},
|
|
2074
|
+
description: `An array of strings, each representing something the student did well. Each string should be WRITTEN IN ${lang}!`
|
|
2075
|
+
},
|
|
2076
|
+
improvedResponse: {
|
|
2077
|
+
type: "string",
|
|
2078
|
+
description: "An improved response with proper grammar and more detail, if applicable."
|
|
2079
|
+
},
|
|
2080
|
+
score: {
|
|
2081
|
+
type: "number",
|
|
2082
|
+
description: "A score between 0 and 100, reflecting the overall quality of the response"
|
|
2083
|
+
},
|
|
2084
|
+
score_justification: {
|
|
2085
|
+
type: "string",
|
|
2086
|
+
description: "An explanation of the rationale behind the assigned score, considering both accuracy and fluency"
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2071
2090
|
}
|
|
2072
|
-
onAssignmentSubmitted(assignment.id);
|
|
2073
|
-
queryClient.setQueryData(scoreQueryKeys.byId(assignment.id), {
|
|
2074
|
-
...scores,
|
|
2075
|
-
...scoreUpdated.fieldsUpdated
|
|
2076
|
-
});
|
|
2077
|
-
return {
|
|
2078
|
-
success: true,
|
|
2079
|
-
message: "Score submitted successfully"
|
|
2080
|
-
};
|
|
2081
|
-
} catch (error) {
|
|
2082
|
-
return {
|
|
2083
|
-
success: false,
|
|
2084
|
-
error
|
|
2085
|
-
};
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
|
-
});
|
|
2089
|
-
return {
|
|
2090
|
-
submitAssignmentScore: mutation.mutateAsync,
|
|
2091
|
-
isLoading: mutation.isPending
|
|
2092
|
-
};
|
|
2093
|
-
}
|
|
2094
|
-
function useSubmitPracticeScore() {
|
|
2095
|
-
const { queryClient } = useSpeakableApi();
|
|
2096
|
-
const mutation = useMutation2({
|
|
2097
|
-
mutationFn: async ({
|
|
2098
|
-
setId,
|
|
2099
|
-
userId,
|
|
2100
|
-
scores
|
|
2101
|
-
}) => {
|
|
2102
|
-
try {
|
|
2103
|
-
await submitPracticeScore({
|
|
2104
|
-
setId,
|
|
2105
|
-
userId,
|
|
2106
|
-
scores
|
|
2107
|
-
});
|
|
2108
|
-
queryClient.invalidateQueries({
|
|
2109
|
-
queryKey: scoreQueryKeys.byId(setId)
|
|
2110
|
-
});
|
|
2111
|
-
return {
|
|
2112
|
-
success: true,
|
|
2113
|
-
message: "Score submitted successfully"
|
|
2114
|
-
};
|
|
2115
|
-
} catch (error) {
|
|
2116
|
-
return {
|
|
2117
|
-
success: false,
|
|
2118
|
-
error
|
|
2119
|
-
};
|
|
2120
2091
|
}
|
|
2121
|
-
|
|
2122
|
-
});
|
|
2123
|
-
return {
|
|
2124
|
-
submitPracticeScore: mutation.mutateAsync,
|
|
2125
|
-
isLoading: mutation.isPending
|
|
2092
|
+
]
|
|
2126
2093
|
};
|
|
2127
|
-
|
|
2094
|
+
if (standard === "wida") {
|
|
2095
|
+
const wida_level = {
|
|
2096
|
+
type: "number",
|
|
2097
|
+
enum: [1, 2, 3, 4, 5, 6],
|
|
2098
|
+
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
|
|
2099
|
+
|
|
2100
|
+
1 - Entering
|
|
2101
|
+
2 - Emerging
|
|
2102
|
+
3 - Developing
|
|
2103
|
+
4 - Expanding
|
|
2104
|
+
5 - Bridging
|
|
2105
|
+
6 - Reaching
|
|
2106
|
+
|
|
2107
|
+
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.
|
|
2108
|
+
`
|
|
2109
|
+
};
|
|
2110
|
+
const wida_justification = {
|
|
2111
|
+
type: "string",
|
|
2112
|
+
description: `An explanation of the rationale behind the assigned WIDA level of the response, considering both accuracy and fluency. WRITE THIS IN ENGLISH!`
|
|
2113
|
+
};
|
|
2114
|
+
tool.tools[0].function.parameters.required.push("wida_level");
|
|
2115
|
+
tool.tools[0].function.parameters.required.push("wida_justification");
|
|
2116
|
+
tool.tools[0].function.parameters.properties.wida_level = wida_level;
|
|
2117
|
+
tool.tools[0].function.parameters.properties.wida_justification = wida_justification;
|
|
2118
|
+
} else {
|
|
2119
|
+
const actfl_level = {
|
|
2120
|
+
type: "string",
|
|
2121
|
+
enum: ["NL", "NM", "NH", "IL", "IM", "IH", "AL", "AM", "AH", "S", "D"],
|
|
2122
|
+
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"
|
|
2123
|
+
};
|
|
2124
|
+
const actfl_justification = {
|
|
2125
|
+
type: "string",
|
|
2126
|
+
description: "An explanation of the rationale behind the assigned ACTFL level, considering both accuracy and fluency"
|
|
2127
|
+
};
|
|
2128
|
+
tool.tools[0].function.parameters.required.push("actfl_level");
|
|
2129
|
+
tool.tools[0].function.parameters.required.push("actfl_justification");
|
|
2130
|
+
tool.tools[0].function.parameters.properties.actfl_level = actfl_level;
|
|
2131
|
+
tool.tools[0].function.parameters.properties.actfl_justification = actfl_justification;
|
|
2132
|
+
}
|
|
2133
|
+
return tool;
|
|
2134
|
+
};
|
|
2128
2135
|
|
|
2129
2136
|
// src/hooks/useActivity.ts
|
|
2130
2137
|
import { useEffect as useEffect2 } from "react";
|
|
@@ -2444,7 +2451,7 @@ var submitLTIScore = async ({
|
|
|
2444
2451
|
};
|
|
2445
2452
|
|
|
2446
2453
|
// src/hooks/useCredits.ts
|
|
2447
|
-
import { useQuery as
|
|
2454
|
+
import { useQuery as useQuery5 } from "@tanstack/react-query";
|
|
2448
2455
|
var creditQueryKeys = {
|
|
2449
2456
|
userCredits: (uid) => ["userCredits", uid]
|
|
2450
2457
|
};
|
|
@@ -2452,7 +2459,7 @@ var useUserCredits = () => {
|
|
|
2452
2459
|
const { user } = useSpeakableApi();
|
|
2453
2460
|
const email = user.auth.email;
|
|
2454
2461
|
const uid = user.auth.uid;
|
|
2455
|
-
const query2 =
|
|
2462
|
+
const query2 = useQuery5({
|
|
2456
2463
|
queryKey: creditQueryKeys.userCredits(uid),
|
|
2457
2464
|
queryFn: () => fetchUserCredits({ uid, email }),
|
|
2458
2465
|
enabled: !!uid,
|
|
@@ -2502,11 +2509,11 @@ var fetchUserCredits = async ({ uid, email }) => {
|
|
|
2502
2509
|
};
|
|
2503
2510
|
|
|
2504
2511
|
// src/hooks/useOrganizationAccess.ts
|
|
2505
|
-
import { useQuery as
|
|
2512
|
+
import { useQuery as useQuery6 } from "@tanstack/react-query";
|
|
2506
2513
|
var useOrganizationAccess = () => {
|
|
2507
2514
|
const { user } = useSpeakableApi();
|
|
2508
2515
|
const email = user.auth.email;
|
|
2509
|
-
const query2 =
|
|
2516
|
+
const query2 = useQuery6({
|
|
2510
2517
|
queryKey: ["organizationAccess", email],
|
|
2511
2518
|
queryFn: async () => {
|
|
2512
2519
|
if (!email) {
|
|
@@ -2594,7 +2601,7 @@ var getOrganizationAccess = async (email) => {
|
|
|
2594
2601
|
};
|
|
2595
2602
|
|
|
2596
2603
|
// src/hooks/useActivityFeedbackAccess.ts
|
|
2597
|
-
import { useQuery as
|
|
2604
|
+
import { useQuery as useQuery7 } from "@tanstack/react-query";
|
|
2598
2605
|
var activityFeedbackAccessQueryKeys = {
|
|
2599
2606
|
activityFeedbackAccess: (args) => ["activityFeedbackAccess", ...Object.values(args)]
|
|
2600
2607
|
};
|
|
@@ -2608,7 +2615,7 @@ var useActivityFeedbackAccess = ({
|
|
|
2608
2615
|
const isTeacher = (_a = user.profile) == null ? void 0 : _a.isTeacher;
|
|
2609
2616
|
const isStudent = (_b = user.profile) == null ? void 0 : _b.isStudent;
|
|
2610
2617
|
const userRoles = ((_c = user.profile) == null ? void 0 : _c.roles) || [];
|
|
2611
|
-
const query2 =
|
|
2618
|
+
const query2 = useQuery7({
|
|
2612
2619
|
queryKey: activityFeedbackAccessQueryKeys.activityFeedbackAccess({
|
|
2613
2620
|
aiEnabled,
|
|
2614
2621
|
isActivityRoute
|
|
@@ -3020,6 +3027,7 @@ export {
|
|
|
3020
3027
|
purify,
|
|
3021
3028
|
refsCardsFiresotre,
|
|
3022
3029
|
refsSetsFirestore,
|
|
3030
|
+
scoreQueryKeys,
|
|
3023
3031
|
setsQueryKeys,
|
|
3024
3032
|
updateCardInCache,
|
|
3025
3033
|
updateSetInCache,
|
|
@@ -3028,12 +3036,19 @@ export {
|
|
|
3028
3036
|
useAssignment,
|
|
3029
3037
|
useBaseOpenAI,
|
|
3030
3038
|
useCards,
|
|
3039
|
+
useClearScore,
|
|
3031
3040
|
useCreateCard,
|
|
3032
3041
|
useCreateCards,
|
|
3033
3042
|
useCreateNotification,
|
|
3043
|
+
useGetCard,
|
|
3034
3044
|
useOrganizationAccess,
|
|
3045
|
+
useScore,
|
|
3035
3046
|
useSet,
|
|
3036
3047
|
useSpeakableApi,
|
|
3048
|
+
useSubmitAssignmentScore,
|
|
3049
|
+
useSubmitPracticeScore,
|
|
3050
|
+
useUpdateCardScore,
|
|
3051
|
+
useUpdateScore,
|
|
3037
3052
|
useUserCredits
|
|
3038
3053
|
};
|
|
3039
3054
|
//# sourceMappingURL=index.web.js.map
|