@orjok/commons 1.1.0 → 1.2.0
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/amplify.cjs +30 -5
- package/dist/amplify.cjs.map +1 -1
- package/dist/amplify.d.cts +5 -2
- package/dist/amplify.d.ts +5 -2
- package/dist/amplify.js +30 -5
- package/dist/amplify.js.map +1 -1
- package/dist/chunk-B4UJE3F4.js +529 -0
- package/dist/chunk-B4UJE3F4.js.map +1 -0
- package/dist/chunk-ODXD2WJN.cjs +529 -0
- package/dist/chunk-ODXD2WJN.cjs.map +1 -0
- package/dist/hooks.cjs +159 -12
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +34 -3
- package/dist/hooks.d.ts +34 -3
- package/dist/hooks.js +148 -1
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +1930 -205
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +230 -14
- package/dist/index.d.ts +230 -14
- package/dist/index.js +1875 -150
- package/dist/index.js.map +1 -1
- package/dist/{practice-Dh2Tv80G.d.ts → notification-BoqtkNrc.d.ts} +357 -8
- package/dist/{practice-QnWtC5A7.d.cts → notification-Ctfy0m05.d.cts} +357 -8
- package/dist/{storage-DLqdJgQt.d.cts → storage-9pK8KwIM.d.cts} +4 -2
- package/dist/{storage-DLqdJgQt.d.ts → storage-9pK8KwIM.d.ts} +4 -2
- package/dist/testing.cjs +24 -3
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +7 -2
- package/dist/testing.d.ts +7 -2
- package/dist/testing.js +24 -3
- package/dist/testing.js.map +1 -1
- package/package.json +2 -1
- package/dist/chunk-DB3CMKPP.cjs +0 -165
- package/dist/chunk-DB3CMKPP.cjs.map +0 -1
- package/dist/chunk-ZDGYAVEC.js +0 -165
- package/dist/chunk-ZDGYAVEC.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MediaUrlCache,
|
|
3
|
+
XP_REWARDS,
|
|
4
|
+
XP_TIERS,
|
|
3
5
|
_resetMediaUrlWarning,
|
|
4
6
|
aggregateChapterMetrics,
|
|
5
7
|
aggregateSubjectMetrics,
|
|
6
8
|
aggregateTopicMetrics,
|
|
7
9
|
calculateAccuracy,
|
|
10
|
+
calculateUnreadCount,
|
|
11
|
+
calculateXpReward,
|
|
12
|
+
difficultyOptions,
|
|
13
|
+
formatLevelUpNotification,
|
|
14
|
+
getAllXpTiers,
|
|
15
|
+
getLevelFromXp,
|
|
16
|
+
getPublicExamsByLevel,
|
|
17
|
+
getXpForLevel,
|
|
18
|
+
getXpProgress,
|
|
19
|
+
getXpRequiredForNextLevel,
|
|
20
|
+
getXpTier,
|
|
8
21
|
isDifficultyMatch,
|
|
9
22
|
isLanguageMatch,
|
|
10
23
|
isStorageKey,
|
|
11
24
|
isValidMCQ,
|
|
25
|
+
languageOptions,
|
|
26
|
+
levelOptions,
|
|
27
|
+
mergeNotificationTimeline,
|
|
28
|
+
publicExamOptions,
|
|
12
29
|
resolveMediaUrl,
|
|
13
30
|
resolveMediaUrls,
|
|
14
31
|
shuffle,
|
|
15
32
|
shuffleInPlace
|
|
16
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-B4UJE3F4.js";
|
|
17
34
|
|
|
18
35
|
// src/services/user.service.ts
|
|
19
36
|
var UserService = class {
|
|
@@ -25,11 +42,12 @@ var UserService = class {
|
|
|
25
42
|
`query GetUser($id: String!) {
|
|
26
43
|
getUser(id: $id) {
|
|
27
44
|
id fullName avatarUrl avatarConfig tier subscriptionExpiresAt
|
|
28
|
-
aiEvaluationRemaining questionCount packCount contestCount
|
|
45
|
+
aiEvaluationRemaining aiQuestionGenRemaining questionCount packCount contestCount
|
|
29
46
|
EngineeringRating MedicalRating VarsityRating BCSRating
|
|
30
47
|
sscOverallRating sscPhysicsRating sscChemistryRating sscMathRating sscBiologyRating
|
|
31
48
|
hscOverallRating hscPhysicsRating hscChemistryRating hscMathRating hscBiologyRating
|
|
32
49
|
bcsOverallRating
|
|
50
|
+
xp userLevel
|
|
33
51
|
}
|
|
34
52
|
}`,
|
|
35
53
|
{ id }
|
|
@@ -37,13 +55,16 @@ var UserService = class {
|
|
|
37
55
|
return result.data.getUser;
|
|
38
56
|
}
|
|
39
57
|
async updateAvatar(input) {
|
|
58
|
+
const updateInput = { id: input.userId };
|
|
59
|
+
if (input.avatarUrl !== void 0) updateInput.avatarUrl = input.avatarUrl;
|
|
60
|
+
if (input.avatarConfig !== void 0) updateInput.avatarConfig = input.avatarConfig;
|
|
40
61
|
const result = await this.network.mutate(
|
|
41
62
|
`mutation UpdateUser($input: UpdateUserInput!) {
|
|
42
63
|
updateUser(input: $input) {
|
|
43
64
|
id fullName avatarUrl avatarConfig
|
|
44
65
|
}
|
|
45
66
|
}`,
|
|
46
|
-
{ input:
|
|
67
|
+
{ input: updateInput }
|
|
47
68
|
);
|
|
48
69
|
return result.data.updateUser;
|
|
49
70
|
}
|
|
@@ -107,8 +128,59 @@ var UserService = class {
|
|
|
107
128
|
);
|
|
108
129
|
return result.data.listCourseEnrolledByUserIdAndCreatedAt.items;
|
|
109
130
|
}
|
|
131
|
+
async updateProfile(input) {
|
|
132
|
+
const result = await this.network.mutate(
|
|
133
|
+
`mutation UpdateUserProfile($input: UpdateUserInput!) {
|
|
134
|
+
updateUser(input: $input) {
|
|
135
|
+
id fullName avatarUrl avatarConfig tier subscriptionExpiresAt
|
|
136
|
+
aiEvaluationRemaining aiQuestionGenRemaining questionCount packCount contestCount
|
|
137
|
+
xp userLevel
|
|
138
|
+
}
|
|
139
|
+
}`,
|
|
140
|
+
{ input }
|
|
141
|
+
);
|
|
142
|
+
return result.data.updateUser;
|
|
143
|
+
}
|
|
144
|
+
async list(input = {}) {
|
|
145
|
+
const result = await this.network.query(
|
|
146
|
+
`query ListUsers($limit: Int, $nextToken: String, $filter: ModelUserFilterInput) {
|
|
147
|
+
listUsers(limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
148
|
+
items {
|
|
149
|
+
id fullName avatarUrl avatarConfig tier subscriptionExpiresAt
|
|
150
|
+
aiEvaluationRemaining aiQuestionGenRemaining questionCount packCount contestCount
|
|
151
|
+
xp userLevel
|
|
152
|
+
}
|
|
153
|
+
nextToken
|
|
154
|
+
}
|
|
155
|
+
}`,
|
|
156
|
+
{ limit: input.limit ?? 20, nextToken: input.nextToken, filter: input.filter }
|
|
157
|
+
);
|
|
158
|
+
return result.data.listUsers;
|
|
159
|
+
}
|
|
110
160
|
};
|
|
111
161
|
|
|
162
|
+
// src/utils/pagination.ts
|
|
163
|
+
async function paginateWithAccumulator(fetchPage, targetLimit = 12, initialNextToken, maxIterations = 10) {
|
|
164
|
+
const accumulated = [];
|
|
165
|
+
let currentNextToken = initialNextToken;
|
|
166
|
+
let iterations = 0;
|
|
167
|
+
while (accumulated.length < targetLimit && iterations < maxIterations) {
|
|
168
|
+
iterations++;
|
|
169
|
+
const remaining = targetLimit - accumulated.length;
|
|
170
|
+
const response = await fetchPage(remaining, currentNextToken);
|
|
171
|
+
const items = response?.items || [];
|
|
172
|
+
accumulated.push(...items);
|
|
173
|
+
currentNextToken = response?.nextToken;
|
|
174
|
+
if (!currentNextToken || accumulated.length >= targetLimit) {
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
items: accumulated.slice(0, targetLimit),
|
|
180
|
+
nextToken: currentNextToken ?? null
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
112
184
|
// src/services/question.service.ts
|
|
113
185
|
var QuestionService = class {
|
|
114
186
|
constructor(network) {
|
|
@@ -116,8 +188,8 @@ var QuestionService = class {
|
|
|
116
188
|
}
|
|
117
189
|
async create(input) {
|
|
118
190
|
const result = await this.network.mutate(
|
|
119
|
-
`mutation CreateQuestion($question: String!, $answer: String!, $options: String!, $language: String!, $level: String!, $difficulty: String!, $type: String, $explanation: String, $tags: String, $imagePayload: String, $optionsImagesPayload: String, $packId: ID, $order: Int, $contestId: ID, $subjectId: ID, $chapterId: ID, $topicId: ID, $extra: String, $markingInstructions: String) {
|
|
120
|
-
createQuestion(question: $question, answer: $answer, options: $options, language: $language, level: $level, difficulty: $difficulty, type: $type, explanation: $explanation, tags: $tags, imagePayload: $imagePayload, optionsImagesPayload: $optionsImagesPayload, packId: $packId, order: $order, contestId: $contestId, subjectId: $subjectId, chapterId: $chapterId, topicId: $topicId, extra: $extra, markingInstructions: $markingInstructions) {
|
|
191
|
+
`mutation CreateQuestion($question: String!, $answer: String!, $options: String!, $language: String!, $level: String!, $publicExam: String, $difficulty: String!, $type: String, $explanation: String, $tags: String, $imagePayload: String, $optionsImagesPayload: String, $packId: ID, $order: Int, $contestId: ID, $subjectId: ID, $chapterId: ID, $topicId: ID, $extra: String, $markingInstructions: String) {
|
|
192
|
+
createQuestion(question: $question, answer: $answer, options: $options, language: $language, level: $level, publicExam: $publicExam, difficulty: $difficulty, type: $type, explanation: $explanation, tags: $tags, imagePayload: $imagePayload, optionsImagesPayload: $optionsImagesPayload, packId: $packId, order: $order, contestId: $contestId, subjectId: $subjectId, chapterId: $chapterId, topicId: $topicId, extra: $extra, markingInstructions: $markingInstructions) {
|
|
121
193
|
status questionId
|
|
122
194
|
}
|
|
123
195
|
}`,
|
|
@@ -129,7 +201,7 @@ var QuestionService = class {
|
|
|
129
201
|
const result = await this.network.query(
|
|
130
202
|
`query GetQuestion($id: String!) {
|
|
131
203
|
getQuestionObject(id: $id) {
|
|
132
|
-
id question imageUrl language level difficulty type voteCount
|
|
204
|
+
id question imageUrl language level publicExam difficulty type voteCount
|
|
133
205
|
verificationStatus owner packId subjectId chapterId topicId order createdAt
|
|
134
206
|
options { items { id content } }
|
|
135
207
|
tags { items { id tagId } }
|
|
@@ -143,7 +215,7 @@ var QuestionService = class {
|
|
|
143
215
|
const result = await this.network.query(
|
|
144
216
|
`query GetFullQuestion($id: String!) {
|
|
145
217
|
getQuestionObject(id: $id) {
|
|
146
|
-
id question answer explanation extra imageUrl language level difficulty type
|
|
218
|
+
id question answer explanation extra imageUrl language level publicExam difficulty type
|
|
147
219
|
markingInstructions voteCount verificationStatus owner packId
|
|
148
220
|
subjectId chapterId topicId order createdAt
|
|
149
221
|
options { items { id content } }
|
|
@@ -181,7 +253,7 @@ var QuestionService = class {
|
|
|
181
253
|
`query ListQuestionsByPack($packId: ID!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {
|
|
182
254
|
listQuestionObjectByPackIdAndOrder(packId: $packId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
|
|
183
255
|
items {
|
|
184
|
-
id question answer explanation extra imageUrl language level difficulty type
|
|
256
|
+
id question answer explanation extra imageUrl language level publicExam difficulty type
|
|
185
257
|
markingInstructions voteCount owner packId subjectId chapterId topicId order createdAt
|
|
186
258
|
options { items { id content } }
|
|
187
259
|
tags { items { id tagId } }
|
|
@@ -197,7 +269,7 @@ var QuestionService = class {
|
|
|
197
269
|
const result = await this.network.mutate(
|
|
198
270
|
`mutation UpdateQuestion($input: UpdateQuestionObjectInput!) {
|
|
199
271
|
updateQuestionObject(input: $input) {
|
|
200
|
-
id question answer explanation extra imageUrl language level difficulty type
|
|
272
|
+
id question answer explanation extra imageUrl language level publicExam difficulty type
|
|
201
273
|
markingInstructions voteCount owner packId subjectId chapterId topicId order createdAt
|
|
202
274
|
options { items { id content } }
|
|
203
275
|
}
|
|
@@ -297,70 +369,288 @@ var QuestionService = class {
|
|
|
297
369
|
return result.data.addQuestionTag;
|
|
298
370
|
}
|
|
299
371
|
async listBySubject(input) {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
372
|
+
const hasLanguage = Boolean(input.language && input.language !== "All" && input.language !== "Any");
|
|
373
|
+
const hasDifficulty = Boolean(input.difficulty && input.difficulty !== "All");
|
|
374
|
+
const filter = {};
|
|
375
|
+
if (input.publicExam) {
|
|
376
|
+
filter.publicExam = { eq: input.publicExam };
|
|
377
|
+
}
|
|
378
|
+
if (input.questionType && input.questionType !== "Both") {
|
|
379
|
+
filter.type = { eq: input.questionType };
|
|
380
|
+
}
|
|
381
|
+
const targetLimit = input.limit ?? 12;
|
|
382
|
+
const executeQuery = async (pageLimit, nextToken) => {
|
|
383
|
+
if (hasLanguage) {
|
|
384
|
+
const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;
|
|
385
|
+
const variables = {
|
|
386
|
+
subjectId: input.id,
|
|
387
|
+
language_difficulty_createdAt: { beginsWith: rangeKey },
|
|
388
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
389
|
+
limit: pageLimit,
|
|
390
|
+
nextToken,
|
|
391
|
+
...Object.keys(filter).length > 0 ? { filter } : {}
|
|
392
|
+
};
|
|
393
|
+
const result = await this.network.query(
|
|
394
|
+
`query ListQuestionsBySubjectAndLangDiff($subjectId: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {
|
|
395
|
+
listQuestionObjectBySubjectIdAndLanguage_difficulty_createdAt(subjectId: $subjectId, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
396
|
+
items {
|
|
397
|
+
id question imageUrl language level publicExam difficulty type voteCount
|
|
398
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
399
|
+
options { items { id content } }
|
|
400
|
+
}
|
|
401
|
+
nextToken
|
|
402
|
+
}
|
|
403
|
+
}`,
|
|
404
|
+
variables
|
|
405
|
+
);
|
|
406
|
+
return result.data.listQuestionObjectBySubjectIdAndLanguage_difficulty_createdAt;
|
|
407
|
+
} else {
|
|
408
|
+
const queryFilter = { ...filter };
|
|
409
|
+
if (hasDifficulty) {
|
|
410
|
+
queryFilter.difficulty = { eq: input.difficulty };
|
|
315
411
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
412
|
+
const variables = {
|
|
413
|
+
subjectId: input.id,
|
|
414
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
415
|
+
limit: pageLimit,
|
|
416
|
+
nextToken,
|
|
417
|
+
...Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}
|
|
418
|
+
};
|
|
419
|
+
const result = await this.network.query(
|
|
420
|
+
`query ListQuestionsBySubject($subjectId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {
|
|
421
|
+
listQuestionObjectBySubjectIdAndCreatedAt(subjectId: $subjectId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
422
|
+
items {
|
|
423
|
+
id question imageUrl language level publicExam difficulty type voteCount
|
|
424
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
425
|
+
options { items { id content } }
|
|
426
|
+
}
|
|
427
|
+
nextToken
|
|
428
|
+
}
|
|
429
|
+
}`,
|
|
430
|
+
variables
|
|
431
|
+
);
|
|
432
|
+
return result.data.listQuestionObjectBySubjectIdAndCreatedAt;
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : Object.keys(filter).length > 0 || hasDifficulty;
|
|
436
|
+
if (!hasPostFilter) {
|
|
437
|
+
return executeQuery(targetLimit, input.nextToken);
|
|
438
|
+
}
|
|
439
|
+
return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);
|
|
320
440
|
}
|
|
321
441
|
async listByChapter(input) {
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
442
|
+
const hasLanguage = Boolean(input.language && input.language !== "All" && input.language !== "Any");
|
|
443
|
+
const hasDifficulty = Boolean(input.difficulty && input.difficulty !== "All");
|
|
444
|
+
const filter = {};
|
|
445
|
+
if (input.publicExam) {
|
|
446
|
+
filter.publicExam = { eq: input.publicExam };
|
|
447
|
+
}
|
|
448
|
+
if (input.questionType && input.questionType !== "Both") {
|
|
449
|
+
filter.type = { eq: input.questionType };
|
|
450
|
+
}
|
|
451
|
+
const targetLimit = input.limit ?? 12;
|
|
452
|
+
const executeQuery = async (pageLimit, nextToken) => {
|
|
453
|
+
if (hasLanguage) {
|
|
454
|
+
const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;
|
|
455
|
+
const variables = {
|
|
456
|
+
chapterId: input.id,
|
|
457
|
+
language_difficulty_createdAt: { beginsWith: rangeKey },
|
|
458
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
459
|
+
limit: pageLimit,
|
|
460
|
+
nextToken,
|
|
461
|
+
...Object.keys(filter).length > 0 ? { filter } : {}
|
|
462
|
+
};
|
|
463
|
+
const result = await this.network.query(
|
|
464
|
+
`query ListQuestionsByChapterAndLangDiff($chapterId: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {
|
|
465
|
+
listQuestionObjectByChapterIdAndLanguage_difficulty_createdAt(chapterId: $chapterId, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
466
|
+
items {
|
|
467
|
+
id question imageUrl language level publicExam difficulty type voteCount
|
|
468
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
469
|
+
options { items { id content } }
|
|
470
|
+
}
|
|
471
|
+
nextToken
|
|
472
|
+
}
|
|
473
|
+
}`,
|
|
474
|
+
variables
|
|
475
|
+
);
|
|
476
|
+
return result.data.listQuestionObjectByChapterIdAndLanguage_difficulty_createdAt;
|
|
477
|
+
} else {
|
|
478
|
+
const queryFilter = { ...filter };
|
|
479
|
+
if (hasDifficulty) {
|
|
480
|
+
queryFilter.difficulty = { eq: input.difficulty };
|
|
337
481
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
482
|
+
const variables = {
|
|
483
|
+
chapterId: input.id,
|
|
484
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
485
|
+
limit: pageLimit,
|
|
486
|
+
nextToken,
|
|
487
|
+
...Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}
|
|
488
|
+
};
|
|
489
|
+
const result = await this.network.query(
|
|
490
|
+
`query ListQuestionsByChapter($chapterId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {
|
|
491
|
+
listQuestionObjectByChapterIdAndCreatedAt(chapterId: $chapterId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
492
|
+
items {
|
|
493
|
+
id question imageUrl language level publicExam difficulty type voteCount
|
|
494
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
495
|
+
options { items { id content } }
|
|
496
|
+
}
|
|
497
|
+
nextToken
|
|
498
|
+
}
|
|
499
|
+
}`,
|
|
500
|
+
variables
|
|
501
|
+
);
|
|
502
|
+
return result.data.listQuestionObjectByChapterIdAndCreatedAt;
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : Object.keys(filter).length > 0 || hasDifficulty;
|
|
506
|
+
if (!hasPostFilter) {
|
|
507
|
+
return executeQuery(targetLimit, input.nextToken);
|
|
508
|
+
}
|
|
509
|
+
return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);
|
|
342
510
|
}
|
|
343
511
|
async listByTopic(input) {
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
512
|
+
const hasLanguage = Boolean(input.language && input.language !== "All" && input.language !== "Any");
|
|
513
|
+
const hasDifficulty = Boolean(input.difficulty && input.difficulty !== "All");
|
|
514
|
+
const filter = {};
|
|
515
|
+
if (input.publicExam) {
|
|
516
|
+
filter.publicExam = { eq: input.publicExam };
|
|
517
|
+
}
|
|
518
|
+
if (input.questionType && input.questionType !== "Both") {
|
|
519
|
+
filter.type = { eq: input.questionType };
|
|
520
|
+
}
|
|
521
|
+
const targetLimit = input.limit ?? 12;
|
|
522
|
+
const executeQuery = async (pageLimit, nextToken) => {
|
|
523
|
+
if (hasLanguage) {
|
|
524
|
+
const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;
|
|
525
|
+
const variables = {
|
|
526
|
+
topicId: input.id,
|
|
527
|
+
language_difficulty_createdAt: { beginsWith: rangeKey },
|
|
528
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
529
|
+
limit: pageLimit,
|
|
530
|
+
nextToken,
|
|
531
|
+
...Object.keys(filter).length > 0 ? { filter } : {}
|
|
532
|
+
};
|
|
533
|
+
const result = await this.network.query(
|
|
534
|
+
`query ListQuestionsByTopicAndLangDiff($topicId: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {
|
|
535
|
+
listQuestionObjectByTopicIdAndLanguage_difficulty_createdAt(topicId: $topicId, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
536
|
+
items {
|
|
537
|
+
id question imageUrl language level publicExam difficulty type voteCount
|
|
538
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
539
|
+
options { items { id content } }
|
|
540
|
+
}
|
|
541
|
+
nextToken
|
|
542
|
+
}
|
|
543
|
+
}`,
|
|
544
|
+
variables
|
|
545
|
+
);
|
|
546
|
+
return result.data.listQuestionObjectByTopicIdAndLanguage_difficulty_createdAt;
|
|
547
|
+
} else {
|
|
548
|
+
const queryFilter = { ...filter };
|
|
549
|
+
if (hasDifficulty) {
|
|
550
|
+
queryFilter.difficulty = { eq: input.difficulty };
|
|
551
|
+
}
|
|
552
|
+
const variables = {
|
|
553
|
+
topicId: input.id,
|
|
554
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
555
|
+
limit: pageLimit,
|
|
556
|
+
nextToken,
|
|
557
|
+
...Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}
|
|
558
|
+
};
|
|
559
|
+
const result = await this.network.query(
|
|
560
|
+
`query ListQuestionsByTopic($topicId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {
|
|
561
|
+
listQuestionObjectByTopicIdAndCreatedAt(topicId: $topicId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
562
|
+
items {
|
|
563
|
+
id question imageUrl language level publicExam difficulty type voteCount
|
|
564
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
565
|
+
options { items { id content } }
|
|
566
|
+
}
|
|
567
|
+
nextToken
|
|
568
|
+
}
|
|
569
|
+
}`,
|
|
570
|
+
variables
|
|
571
|
+
);
|
|
572
|
+
return result.data.listQuestionObjectByTopicIdAndCreatedAt;
|
|
573
|
+
}
|
|
349
574
|
};
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
575
|
+
const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : Object.keys(filter).length > 0 || hasDifficulty;
|
|
576
|
+
if (!hasPostFilter) {
|
|
577
|
+
return executeQuery(targetLimit, input.nextToken);
|
|
578
|
+
}
|
|
579
|
+
return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);
|
|
580
|
+
}
|
|
581
|
+
async listByPublicExam(input) {
|
|
582
|
+
const hasLanguage = Boolean(input.language && input.language !== "All" && input.language !== "Any");
|
|
583
|
+
const hasDifficulty = Boolean(input.difficulty && input.difficulty !== "All");
|
|
584
|
+
const filter = {};
|
|
585
|
+
if (input.questionType && input.questionType !== "Both") {
|
|
586
|
+
filter.type = { eq: input.questionType };
|
|
587
|
+
}
|
|
588
|
+
const targetLimit = input.limit ?? 12;
|
|
589
|
+
const executeQuery = async (pageLimit, nextToken) => {
|
|
590
|
+
if (hasLanguage) {
|
|
591
|
+
const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;
|
|
592
|
+
const variables2 = {
|
|
593
|
+
publicExam: input.publicExam,
|
|
594
|
+
language_difficulty_createdAt: { beginsWith: rangeKey },
|
|
595
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
596
|
+
limit: pageLimit,
|
|
597
|
+
nextToken,
|
|
598
|
+
...Object.keys(filter).length > 0 ? { filter } : {}
|
|
599
|
+
};
|
|
600
|
+
try {
|
|
601
|
+
const result2 = await this.network.query(
|
|
602
|
+
`query ListQuestionsByPublicExamAndLangDiff($publicExam: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {
|
|
603
|
+
listQuestionObjectByPublicExamAndLanguage_difficulty_createdAt(publicExam: $publicExam, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
604
|
+
items {
|
|
605
|
+
id question imageUrl language level publicExam difficulty type voteCount
|
|
606
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
607
|
+
options { items { id content } }
|
|
608
|
+
}
|
|
609
|
+
nextToken
|
|
610
|
+
}
|
|
611
|
+
}`,
|
|
612
|
+
variables2
|
|
613
|
+
);
|
|
614
|
+
if (result2.data?.listQuestionObjectByPublicExamAndLanguage_difficulty_createdAt) {
|
|
615
|
+
return result2.data.listQuestionObjectByPublicExamAndLanguage_difficulty_createdAt;
|
|
357
616
|
}
|
|
358
|
-
|
|
617
|
+
} catch {
|
|
359
618
|
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
619
|
+
}
|
|
620
|
+
const fallbackFilter = { ...filter };
|
|
621
|
+
if (hasLanguage) {
|
|
622
|
+
fallbackFilter.language = { eq: input.language };
|
|
623
|
+
}
|
|
624
|
+
if (hasDifficulty) {
|
|
625
|
+
fallbackFilter.difficulty = { eq: input.difficulty };
|
|
626
|
+
}
|
|
627
|
+
const variables = {
|
|
628
|
+
publicExam: input.publicExam,
|
|
629
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
630
|
+
limit: pageLimit,
|
|
631
|
+
nextToken,
|
|
632
|
+
...Object.keys(fallbackFilter).length > 0 ? { filter: fallbackFilter } : {}
|
|
633
|
+
};
|
|
634
|
+
const result = await this.network.query(
|
|
635
|
+
`query ListQuestionsByPublicExam($publicExam: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {
|
|
636
|
+
listQuestionObjectByPublicExamAndCreatedAt(publicExam: $publicExam, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
637
|
+
items {
|
|
638
|
+
id question imageUrl language level publicExam difficulty type voteCount
|
|
639
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
640
|
+
options { items { id content } }
|
|
641
|
+
}
|
|
642
|
+
nextToken
|
|
643
|
+
}
|
|
644
|
+
}`,
|
|
645
|
+
variables
|
|
646
|
+
);
|
|
647
|
+
return result.data.listQuestionObjectByPublicExamAndCreatedAt;
|
|
648
|
+
};
|
|
649
|
+
const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : Object.keys(filter).length > 0 || hasLanguage || hasDifficulty;
|
|
650
|
+
if (!hasPostFilter) {
|
|
651
|
+
return executeQuery(targetLimit, input.nextToken);
|
|
652
|
+
}
|
|
653
|
+
return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);
|
|
364
654
|
}
|
|
365
655
|
async listByRandomHash(input) {
|
|
366
656
|
const compositeKey = [input.language, input.difficulty].filter(Boolean).join("_");
|
|
@@ -374,7 +664,7 @@ var QuestionService = class {
|
|
|
374
664
|
`query ListByRandomHash($subjectId: String!, $language_difficulty_randomHash: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int) {
|
|
375
665
|
listQuestionObjectBySubjectIdAndLanguage_difficulty_randomHash(subjectId: $subjectId, language_difficulty_randomHash: $language_difficulty_randomHash, sortDirection: $sortDirection, limit: $limit) {
|
|
376
666
|
items {
|
|
377
|
-
id question imageUrl language level difficulty type voteCount
|
|
667
|
+
id question imageUrl language level publicExam difficulty type voteCount
|
|
378
668
|
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
379
669
|
options { items { id content } }
|
|
380
670
|
}
|
|
@@ -385,6 +675,76 @@ var QuestionService = class {
|
|
|
385
675
|
);
|
|
386
676
|
return result.data.listQuestionObjectBySubjectIdAndLanguage_difficulty_randomHash;
|
|
387
677
|
}
|
|
678
|
+
async listByLevel(input) {
|
|
679
|
+
const hasLanguage = Boolean(input.language && input.language !== "All" && input.language !== "Any");
|
|
680
|
+
const hasDifficulty = Boolean(input.difficulty && input.difficulty !== "All");
|
|
681
|
+
const filter = {};
|
|
682
|
+
if (input.publicExam) {
|
|
683
|
+
filter.publicExam = { eq: input.publicExam };
|
|
684
|
+
}
|
|
685
|
+
if (input.questionType && input.questionType !== "Both") {
|
|
686
|
+
filter.type = { eq: input.questionType };
|
|
687
|
+
}
|
|
688
|
+
const targetLimit = input.limit ?? 12;
|
|
689
|
+
const executeQuery = async (pageLimit, nextToken) => {
|
|
690
|
+
if (hasLanguage) {
|
|
691
|
+
const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;
|
|
692
|
+
const variables = {
|
|
693
|
+
level: input.level,
|
|
694
|
+
language_difficulty_createdAt: { beginsWith: rangeKey },
|
|
695
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
696
|
+
limit: pageLimit,
|
|
697
|
+
nextToken,
|
|
698
|
+
...Object.keys(filter).length > 0 ? { filter } : {}
|
|
699
|
+
};
|
|
700
|
+
const result = await this.network.query(
|
|
701
|
+
`query ListQuestionsByLevelAndLangDiff($level: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {
|
|
702
|
+
listQuestionObjectByLevelAndLanguage_difficulty_createdAt(level: $level, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
703
|
+
items {
|
|
704
|
+
id question imageUrl language level publicExam difficulty type voteCount
|
|
705
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
706
|
+
options { items { id content } }
|
|
707
|
+
}
|
|
708
|
+
nextToken
|
|
709
|
+
}
|
|
710
|
+
}`,
|
|
711
|
+
variables
|
|
712
|
+
);
|
|
713
|
+
return result.data.listQuestionObjectByLevelAndLanguage_difficulty_createdAt;
|
|
714
|
+
} else {
|
|
715
|
+
const queryFilter = { ...filter };
|
|
716
|
+
if (hasDifficulty) {
|
|
717
|
+
queryFilter.difficulty = { eq: input.difficulty };
|
|
718
|
+
}
|
|
719
|
+
const variables = {
|
|
720
|
+
level: input.level,
|
|
721
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
722
|
+
limit: pageLimit,
|
|
723
|
+
nextToken,
|
|
724
|
+
...Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}
|
|
725
|
+
};
|
|
726
|
+
const result = await this.network.query(
|
|
727
|
+
`query ListQuestionsByLevel($level: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {
|
|
728
|
+
listQuestionObjectByLevelAndCreatedAt(level: $level, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
729
|
+
items {
|
|
730
|
+
id question imageUrl language level publicExam difficulty type voteCount
|
|
731
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
732
|
+
options { items { id content } }
|
|
733
|
+
}
|
|
734
|
+
nextToken
|
|
735
|
+
}
|
|
736
|
+
}`,
|
|
737
|
+
variables
|
|
738
|
+
);
|
|
739
|
+
return result.data.listQuestionObjectByLevelAndCreatedAt;
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : Object.keys(filter).length > 0 || hasDifficulty;
|
|
743
|
+
if (!hasPostFilter) {
|
|
744
|
+
return executeQuery(targetLimit, input.nextToken);
|
|
745
|
+
}
|
|
746
|
+
return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);
|
|
747
|
+
}
|
|
388
748
|
};
|
|
389
749
|
|
|
390
750
|
// src/services/pack.service.ts
|
|
@@ -394,8 +754,8 @@ var PackService = class {
|
|
|
394
754
|
}
|
|
395
755
|
async create(input) {
|
|
396
756
|
const result = await this.network.mutate(
|
|
397
|
-
`mutation CreatePack($name: String!, $level: String!, $language: String!, $difficulty: String!, $automaticNumbering: Boolean, $subjectId: ID, $chapterId: ID, $topicId: ID) {
|
|
398
|
-
createPackObject(name: $name, level: $level, language: $language, difficulty: $difficulty, automaticNumbering: $automaticNumbering, subjectId: $subjectId, chapterId: $chapterId, topicId: $topicId) {
|
|
757
|
+
`mutation CreatePack($name: String!, $level: String!, $publicExam: String, $language: String!, $difficulty: String!, $automaticNumbering: Boolean, $subjectId: ID, $chapterId: ID, $topicId: ID) {
|
|
758
|
+
createPackObject(name: $name, level: $level, publicExam: $publicExam, language: $language, difficulty: $difficulty, automaticNumbering: $automaticNumbering, subjectId: $subjectId, chapterId: $chapterId, topicId: $topicId) {
|
|
399
759
|
status id
|
|
400
760
|
}
|
|
401
761
|
}`,
|
|
@@ -407,7 +767,7 @@ var PackService = class {
|
|
|
407
767
|
const result = await this.network.query(
|
|
408
768
|
`query GetPack($id: ID!) {
|
|
409
769
|
getPack(id: $id) {
|
|
410
|
-
id name language level difficulty questionCount automaticNumbering
|
|
770
|
+
id name language level publicExam difficulty questionCount automaticNumbering
|
|
411
771
|
owner subjectId chapterId topicId packGroupId verificationStatus createdAt
|
|
412
772
|
tags { items { id tagId } }
|
|
413
773
|
user { id fullName avatarUrl }
|
|
@@ -421,13 +781,13 @@ var PackService = class {
|
|
|
421
781
|
const result = await this.network.query(
|
|
422
782
|
`query GetFullPack($id: ID!) {
|
|
423
783
|
getPack(id: $id) {
|
|
424
|
-
id name language level difficulty questionCount automaticNumbering
|
|
784
|
+
id name language level publicExam difficulty questionCount automaticNumbering
|
|
425
785
|
owner subjectId chapterId topicId packGroupId verificationStatus createdAt
|
|
426
786
|
tags { items { id tagId } }
|
|
427
787
|
user { id fullName avatarUrl }
|
|
428
788
|
questions(sortDirection: ASC, limit: 200) {
|
|
429
789
|
items {
|
|
430
|
-
id question answer explanation extra imageUrl language level difficulty type
|
|
790
|
+
id question answer explanation extra imageUrl language level publicExam difficulty type
|
|
431
791
|
markingInstructions voteCount owner order createdAt
|
|
432
792
|
options { items { id content } }
|
|
433
793
|
tags { items { id tagId } }
|
|
@@ -440,6 +800,11 @@ var PackService = class {
|
|
|
440
800
|
const pack = result.data.getPack;
|
|
441
801
|
if (pack && pack.questions && Array.isArray(pack.questions.items)) {
|
|
442
802
|
pack.questions.items.sort((a, b) => {
|
|
803
|
+
const isWrittenA = a.type === "WRITTEN" ? 1 : 0;
|
|
804
|
+
const isWrittenB = b.type === "WRITTEN" ? 1 : 0;
|
|
805
|
+
if (isWrittenA !== isWrittenB) {
|
|
806
|
+
return isWrittenA - isWrittenB;
|
|
807
|
+
}
|
|
443
808
|
const orderA = typeof a.order === "number" ? a.order : a.order ? Number(a.order) : 0;
|
|
444
809
|
const orderB = typeof b.order === "number" ? b.order : b.order ? Number(b.order) : 0;
|
|
445
810
|
if (orderA !== orderB) {
|
|
@@ -458,7 +823,7 @@ var PackService = class {
|
|
|
458
823
|
const result = await this.network.mutate(
|
|
459
824
|
`mutation UpdatePack($input: UpdatePackInput!) {
|
|
460
825
|
updatePack(input: $input) {
|
|
461
|
-
id name language level difficulty questionCount automaticNumbering
|
|
826
|
+
id name language level publicExam difficulty questionCount automaticNumbering
|
|
462
827
|
owner subjectId chapterId topicId verificationStatus createdAt
|
|
463
828
|
}
|
|
464
829
|
}`,
|
|
@@ -468,12 +833,15 @@ var PackService = class {
|
|
|
468
833
|
}
|
|
469
834
|
async delete(id) {
|
|
470
835
|
const result = await this.network.mutate(
|
|
471
|
-
`mutation
|
|
472
|
-
|
|
836
|
+
`mutation DeletePackCascade($packId: ID!) {
|
|
837
|
+
deletePackCascade(packId: $packId) { status }
|
|
473
838
|
}`,
|
|
474
|
-
{
|
|
839
|
+
{ packId: id }
|
|
475
840
|
);
|
|
476
|
-
|
|
841
|
+
if (result.data.deletePackCascade?.status === "success") {
|
|
842
|
+
return { id };
|
|
843
|
+
}
|
|
844
|
+
return null;
|
|
477
845
|
}
|
|
478
846
|
async verify(id, userId) {
|
|
479
847
|
const result = await this.network.mutate(
|
|
@@ -493,7 +861,27 @@ var PackService = class {
|
|
|
493
861
|
);
|
|
494
862
|
return result.data.createPackReport;
|
|
495
863
|
}
|
|
496
|
-
async submitExam(packId, selectedOptions) {
|
|
864
|
+
async submitExam(packId, selectedOptions, options) {
|
|
865
|
+
if (options?.functionUrl) {
|
|
866
|
+
try {
|
|
867
|
+
const headers = { "Content-Type": "application/json" };
|
|
868
|
+
if (options.authToken) {
|
|
869
|
+
headers["Authorization"] = `Bearer ${options.authToken}`;
|
|
870
|
+
}
|
|
871
|
+
const response = await fetch(options.functionUrl, {
|
|
872
|
+
method: "POST",
|
|
873
|
+
headers,
|
|
874
|
+
body: JSON.stringify({ packId, selectedOptions: JSON.stringify(selectedOptions) })
|
|
875
|
+
});
|
|
876
|
+
if (response.ok) {
|
|
877
|
+
const data = await response.json();
|
|
878
|
+
return data;
|
|
879
|
+
}
|
|
880
|
+
console.warn(`[PackService] Function URL failed with status ${response.status}, falling back to GraphQL...`);
|
|
881
|
+
} catch (err) {
|
|
882
|
+
console.warn("[PackService] Function URL fetch error, falling back to GraphQL...", err);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
497
885
|
const result = await this.network.mutate(
|
|
498
886
|
`mutation SubmitPackExam($packId: ID!, $selectedOptions: String!) {
|
|
499
887
|
submitPackExam(packId: $packId, selectedOptions: $selectedOptions) {
|
|
@@ -510,7 +898,7 @@ var PackService = class {
|
|
|
510
898
|
listPackExamResultsByOwnerAndCreatedAt(owner: $owner, sortDirection: $sortDirection, nextToken: $nextToken) {
|
|
511
899
|
items {
|
|
512
900
|
id packId selectedOptions aiEvaluations owner createdAt
|
|
513
|
-
pack { id name language level difficulty questionCount }
|
|
901
|
+
pack { id name language level publicExam difficulty questionCount }
|
|
514
902
|
}
|
|
515
903
|
nextToken
|
|
516
904
|
}
|
|
@@ -524,7 +912,7 @@ var PackService = class {
|
|
|
524
912
|
`query GetPackExamResult($id: ID!) {
|
|
525
913
|
getPackExamResults(id: $id) {
|
|
526
914
|
id packId selectedOptions aiEvaluations owner createdAt
|
|
527
|
-
pack { id name language level difficulty questionCount }
|
|
915
|
+
pack { id name language level publicExam difficulty questionCount }
|
|
528
916
|
}
|
|
529
917
|
}`,
|
|
530
918
|
{ id: resultId }
|
|
@@ -532,79 +920,462 @@ var PackService = class {
|
|
|
532
920
|
return result.data.getPackExamResults;
|
|
533
921
|
}
|
|
534
922
|
async listBySubject(input) {
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
923
|
+
const hasLanguage = Boolean(input.language && input.language !== "All" && input.language !== "Any");
|
|
924
|
+
const hasDifficulty = Boolean(input.difficulty && input.difficulty !== "All");
|
|
925
|
+
const filter = {};
|
|
926
|
+
if (input.publicExam) {
|
|
927
|
+
filter.publicExam = { eq: input.publicExam };
|
|
928
|
+
}
|
|
929
|
+
const targetLimit = input.limit ?? 12;
|
|
930
|
+
const executeQuery = async (pageLimit, nextToken) => {
|
|
931
|
+
if (hasLanguage) {
|
|
932
|
+
const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;
|
|
933
|
+
const variables = {
|
|
934
|
+
subjectId: input.id,
|
|
935
|
+
language_difficulty_createdAt: { beginsWith: rangeKey },
|
|
936
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
937
|
+
limit: pageLimit,
|
|
938
|
+
nextToken,
|
|
939
|
+
...Object.keys(filter).length > 0 ? { filter } : {}
|
|
940
|
+
};
|
|
941
|
+
const result = await this.network.query(
|
|
942
|
+
`query ListPacksBySubjectAndLangDiff($subjectId: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {
|
|
943
|
+
listPackBySubjectIdAndLanguage_difficulty_createdAt(subjectId: $subjectId, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
944
|
+
items {
|
|
945
|
+
id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId
|
|
946
|
+
tags { items { id tagId } }
|
|
947
|
+
}
|
|
948
|
+
nextToken
|
|
949
|
+
}
|
|
950
|
+
}`,
|
|
951
|
+
variables
|
|
952
|
+
);
|
|
953
|
+
return result.data.listPackBySubjectIdAndLanguage_difficulty_createdAt;
|
|
954
|
+
} else {
|
|
955
|
+
const queryFilter = { ...filter };
|
|
956
|
+
if (hasDifficulty) {
|
|
957
|
+
queryFilter.difficulty = { eq: input.difficulty };
|
|
543
958
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
959
|
+
const variables = {
|
|
960
|
+
subjectId: input.id,
|
|
961
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
962
|
+
limit: pageLimit,
|
|
963
|
+
nextToken,
|
|
964
|
+
...Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}
|
|
965
|
+
};
|
|
966
|
+
const result = await this.network.query(
|
|
967
|
+
`query ListPacksBySubject($subjectId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {
|
|
968
|
+
listPackBySubjectIdAndCreatedAt(subjectId: $subjectId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
969
|
+
items {
|
|
970
|
+
id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId
|
|
971
|
+
tags { items { id tagId } }
|
|
972
|
+
}
|
|
973
|
+
nextToken
|
|
974
|
+
}
|
|
975
|
+
}`,
|
|
976
|
+
variables
|
|
977
|
+
);
|
|
978
|
+
return result.data.listPackBySubjectIdAndCreatedAt;
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : Object.keys(filter).length > 0 || hasDifficulty;
|
|
982
|
+
if (!hasPostFilter) {
|
|
983
|
+
return executeQuery(targetLimit, input.nextToken);
|
|
984
|
+
}
|
|
985
|
+
return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);
|
|
548
986
|
}
|
|
549
987
|
async listByChapter(input) {
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
988
|
+
const hasLanguage = Boolean(input.language && input.language !== "All" && input.language !== "Any");
|
|
989
|
+
const hasDifficulty = Boolean(input.difficulty && input.difficulty !== "All");
|
|
990
|
+
const filter = {};
|
|
991
|
+
if (input.publicExam) {
|
|
992
|
+
filter.publicExam = { eq: input.publicExam };
|
|
993
|
+
}
|
|
994
|
+
const targetLimit = input.limit ?? 12;
|
|
995
|
+
const executeQuery = async (pageLimit, nextToken) => {
|
|
996
|
+
if (hasLanguage) {
|
|
997
|
+
const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;
|
|
998
|
+
const variables = {
|
|
999
|
+
chapterId: input.id,
|
|
1000
|
+
language_difficulty_createdAt: { beginsWith: rangeKey },
|
|
1001
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
1002
|
+
limit: pageLimit,
|
|
1003
|
+
nextToken,
|
|
1004
|
+
...Object.keys(filter).length > 0 ? { filter } : {}
|
|
1005
|
+
};
|
|
1006
|
+
const result = await this.network.query(
|
|
1007
|
+
`query ListPacksByChapterAndLangDiff($chapterId: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {
|
|
1008
|
+
listPackByChapterIdAndLanguage_difficulty_createdAt(chapterId: $chapterId, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
1009
|
+
items {
|
|
1010
|
+
id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId
|
|
1011
|
+
tags { items { id tagId } }
|
|
1012
|
+
}
|
|
1013
|
+
nextToken
|
|
1014
|
+
}
|
|
1015
|
+
}`,
|
|
1016
|
+
variables
|
|
1017
|
+
);
|
|
1018
|
+
return result.data.listPackByChapterIdAndLanguage_difficulty_createdAt;
|
|
1019
|
+
} else {
|
|
1020
|
+
const queryFilter = { ...filter };
|
|
1021
|
+
if (hasDifficulty) {
|
|
1022
|
+
queryFilter.difficulty = { eq: input.difficulty };
|
|
558
1023
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
1024
|
+
const variables = {
|
|
1025
|
+
chapterId: input.id,
|
|
1026
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
1027
|
+
limit: pageLimit,
|
|
1028
|
+
nextToken,
|
|
1029
|
+
...Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}
|
|
1030
|
+
};
|
|
1031
|
+
const result = await this.network.query(
|
|
1032
|
+
`query ListPacksByChapter($chapterId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {
|
|
1033
|
+
listPackByChapterIdAndCreatedAt(chapterId: $chapterId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
1034
|
+
items {
|
|
1035
|
+
id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId
|
|
1036
|
+
tags { items { id tagId } }
|
|
1037
|
+
}
|
|
1038
|
+
nextToken
|
|
1039
|
+
}
|
|
1040
|
+
}`,
|
|
1041
|
+
variables
|
|
1042
|
+
);
|
|
1043
|
+
return result.data.listPackByChapterIdAndCreatedAt;
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : Object.keys(filter).length > 0 || hasDifficulty;
|
|
1047
|
+
if (!hasPostFilter) {
|
|
1048
|
+
return executeQuery(targetLimit, input.nextToken);
|
|
1049
|
+
}
|
|
1050
|
+
return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);
|
|
563
1051
|
}
|
|
564
1052
|
async listByTopic(input) {
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
1053
|
+
const hasLanguage = Boolean(input.language && input.language !== "All" && input.language !== "Any");
|
|
1054
|
+
const hasDifficulty = Boolean(input.difficulty && input.difficulty !== "All");
|
|
1055
|
+
const filter = {};
|
|
1056
|
+
if (input.publicExam) {
|
|
1057
|
+
filter.publicExam = { eq: input.publicExam };
|
|
1058
|
+
}
|
|
1059
|
+
const targetLimit = input.limit ?? 12;
|
|
1060
|
+
const executeQuery = async (pageLimit, nextToken) => {
|
|
1061
|
+
if (hasLanguage) {
|
|
1062
|
+
const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;
|
|
1063
|
+
const variables = {
|
|
1064
|
+
topicId: input.id,
|
|
1065
|
+
language_difficulty_createdAt: { beginsWith: rangeKey },
|
|
1066
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
1067
|
+
limit: pageLimit,
|
|
1068
|
+
nextToken,
|
|
1069
|
+
...Object.keys(filter).length > 0 ? { filter } : {}
|
|
1070
|
+
};
|
|
1071
|
+
const result = await this.network.query(
|
|
1072
|
+
`query ListPacksByTopicAndLangDiff($topicId: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {
|
|
1073
|
+
listPackByTopicIdAndLanguage_difficulty_createdAt(topicId: $topicId, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
1074
|
+
items {
|
|
1075
|
+
id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId
|
|
1076
|
+
tags { items { id tagId } }
|
|
1077
|
+
}
|
|
1078
|
+
nextToken
|
|
1079
|
+
}
|
|
1080
|
+
}`,
|
|
1081
|
+
variables
|
|
1082
|
+
);
|
|
1083
|
+
return result.data.listPackByTopicIdAndLanguage_difficulty_createdAt;
|
|
1084
|
+
} else {
|
|
1085
|
+
const queryFilter = { ...filter };
|
|
1086
|
+
if (hasDifficulty) {
|
|
1087
|
+
queryFilter.difficulty = { eq: input.difficulty };
|
|
1088
|
+
}
|
|
1089
|
+
const variables = {
|
|
1090
|
+
topicId: input.id,
|
|
1091
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
1092
|
+
limit: pageLimit,
|
|
1093
|
+
nextToken,
|
|
1094
|
+
...Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}
|
|
1095
|
+
};
|
|
1096
|
+
const result = await this.network.query(
|
|
1097
|
+
`query ListPacksByTopic($topicId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {
|
|
1098
|
+
listPackByTopicIdAndCreatedAt(topicId: $topicId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
1099
|
+
items {
|
|
1100
|
+
id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId
|
|
1101
|
+
tags { items { id tagId } }
|
|
1102
|
+
}
|
|
1103
|
+
nextToken
|
|
1104
|
+
}
|
|
1105
|
+
}`,
|
|
1106
|
+
variables
|
|
1107
|
+
);
|
|
1108
|
+
return result.data.listPackByTopicIdAndCreatedAt;
|
|
1109
|
+
}
|
|
1110
|
+
};
|
|
1111
|
+
const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : Object.keys(filter).length > 0 || hasDifficulty;
|
|
1112
|
+
if (!hasPostFilter) {
|
|
1113
|
+
return executeQuery(targetLimit, input.nextToken);
|
|
1114
|
+
}
|
|
1115
|
+
return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);
|
|
1116
|
+
}
|
|
1117
|
+
async listByPublicExam(input) {
|
|
1118
|
+
const hasLanguage = Boolean(input.language && input.language !== "All" && input.language !== "Any");
|
|
1119
|
+
const hasDifficulty = Boolean(input.difficulty && input.difficulty !== "All");
|
|
1120
|
+
const filter = {};
|
|
1121
|
+
const targetLimit = input.limit ?? 12;
|
|
1122
|
+
const executeQuery = async (pageLimit, nextToken) => {
|
|
1123
|
+
if (hasLanguage) {
|
|
1124
|
+
const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;
|
|
1125
|
+
const variables = {
|
|
1126
|
+
publicExam: input.publicExam,
|
|
1127
|
+
language_difficulty_createdAt: { beginsWith: rangeKey },
|
|
1128
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
1129
|
+
limit: pageLimit,
|
|
1130
|
+
nextToken,
|
|
1131
|
+
...Object.keys(filter).length > 0 ? { filter } : {}
|
|
1132
|
+
};
|
|
1133
|
+
try {
|
|
1134
|
+
const result2 = await this.network.query(
|
|
1135
|
+
`query ListPacksByPublicExamAndLangDiff($publicExam: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {
|
|
1136
|
+
listPackByPublicExamAndLanguage_difficulty_createdAt(publicExam: $publicExam, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
1137
|
+
items {
|
|
1138
|
+
id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId
|
|
1139
|
+
tags { items { id tagId } }
|
|
1140
|
+
}
|
|
1141
|
+
nextToken
|
|
1142
|
+
}
|
|
1143
|
+
}`,
|
|
1144
|
+
variables
|
|
1145
|
+
);
|
|
1146
|
+
if (result2.data?.listPackByPublicExamAndLanguage_difficulty_createdAt) {
|
|
1147
|
+
return result2.data.listPackByPublicExamAndLanguage_difficulty_createdAt;
|
|
571
1148
|
}
|
|
572
|
-
|
|
1149
|
+
} catch {
|
|
573
1150
|
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
1151
|
+
}
|
|
1152
|
+
const fallbackFilter = { ...filter };
|
|
1153
|
+
if (hasLanguage) {
|
|
1154
|
+
fallbackFilter.language = { eq: input.language };
|
|
1155
|
+
}
|
|
1156
|
+
if (hasDifficulty) {
|
|
1157
|
+
fallbackFilter.difficulty = { eq: input.difficulty };
|
|
1158
|
+
}
|
|
1159
|
+
try {
|
|
1160
|
+
const variables = {
|
|
1161
|
+
publicExam: input.publicExam,
|
|
1162
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
1163
|
+
limit: pageLimit,
|
|
1164
|
+
nextToken,
|
|
1165
|
+
...Object.keys(fallbackFilter).length > 0 ? { filter: fallbackFilter } : {}
|
|
1166
|
+
};
|
|
1167
|
+
const result2 = await this.network.query(
|
|
1168
|
+
`query ListPacksByPublicExam($publicExam: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {
|
|
1169
|
+
listPackByPublicExamAndCreatedAt(publicExam: $publicExam, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
1170
|
+
items {
|
|
1171
|
+
id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId
|
|
1172
|
+
tags { items { id tagId } }
|
|
1173
|
+
}
|
|
1174
|
+
nextToken
|
|
586
1175
|
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
|
|
1176
|
+
}`,
|
|
1177
|
+
variables
|
|
1178
|
+
);
|
|
1179
|
+
if (result2.data?.listPackByPublicExamAndCreatedAt) {
|
|
1180
|
+
return result2.data.listPackByPublicExamAndCreatedAt;
|
|
1181
|
+
}
|
|
1182
|
+
} catch {
|
|
1183
|
+
}
|
|
1184
|
+
const scanFilter = {
|
|
1185
|
+
publicExam: { eq: input.publicExam },
|
|
1186
|
+
...fallbackFilter
|
|
1187
|
+
};
|
|
1188
|
+
const result = await this.network.query(
|
|
1189
|
+
`query ListPacksFallback($filter: ModelPackFilterInput, $limit: Int, $nextToken: String) {
|
|
1190
|
+
listPacks(filter: $filter, limit: $limit, nextToken: $nextToken) {
|
|
1191
|
+
items {
|
|
1192
|
+
id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId
|
|
1193
|
+
tags { items { id tagId } }
|
|
1194
|
+
}
|
|
1195
|
+
nextToken
|
|
1196
|
+
}
|
|
1197
|
+
}`,
|
|
1198
|
+
{ filter: scanFilter, limit: pageLimit, nextToken }
|
|
1199
|
+
);
|
|
1200
|
+
return result.data.listPacks;
|
|
1201
|
+
};
|
|
1202
|
+
const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : Object.keys(filter).length > 0 || hasLanguage || hasDifficulty;
|
|
1203
|
+
if (!hasPostFilter) {
|
|
1204
|
+
return executeQuery(targetLimit, input.nextToken);
|
|
1205
|
+
}
|
|
1206
|
+
return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);
|
|
1207
|
+
}
|
|
1208
|
+
async listByLevel(input) {
|
|
1209
|
+
const hasLanguage = Boolean(input.language && input.language !== "All" && input.language !== "Any");
|
|
1210
|
+
const hasDifficulty = Boolean(input.difficulty && input.difficulty !== "All");
|
|
1211
|
+
const filter = {};
|
|
1212
|
+
if (input.publicExam) {
|
|
1213
|
+
filter.publicExam = { eq: input.publicExam };
|
|
1214
|
+
}
|
|
1215
|
+
const targetLimit = input.limit ?? 12;
|
|
1216
|
+
const executeQuery = async (pageLimit, nextToken) => {
|
|
1217
|
+
if (hasLanguage) {
|
|
1218
|
+
const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;
|
|
1219
|
+
const variables = {
|
|
1220
|
+
level: input.level,
|
|
1221
|
+
language_difficulty_createdAt: { beginsWith: rangeKey },
|
|
1222
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
1223
|
+
limit: pageLimit,
|
|
1224
|
+
nextToken,
|
|
1225
|
+
...Object.keys(filter).length > 0 ? { filter } : {}
|
|
1226
|
+
};
|
|
1227
|
+
const result = await this.network.query(
|
|
1228
|
+
`query ListPacksByLevelAndLangDiff($level: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {
|
|
1229
|
+
listPackByLevelAndLanguage_difficulty_createdAt(level: $level, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
1230
|
+
items {
|
|
1231
|
+
id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId
|
|
1232
|
+
tags { items { id tagId } }
|
|
1233
|
+
}
|
|
1234
|
+
nextToken
|
|
590
1235
|
}
|
|
591
1236
|
}`,
|
|
592
|
-
|
|
1237
|
+
variables
|
|
1238
|
+
);
|
|
1239
|
+
return result.data.listPackByLevelAndLanguage_difficulty_createdAt;
|
|
1240
|
+
} else {
|
|
1241
|
+
const queryFilter = { ...filter };
|
|
1242
|
+
if (hasDifficulty) {
|
|
1243
|
+
queryFilter.difficulty = { eq: input.difficulty };
|
|
1244
|
+
}
|
|
1245
|
+
const variables = {
|
|
1246
|
+
level: input.level,
|
|
1247
|
+
sortDirection: input.sortDirection ?? "DESC",
|
|
1248
|
+
limit: pageLimit,
|
|
1249
|
+
nextToken,
|
|
1250
|
+
...Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}
|
|
1251
|
+
};
|
|
1252
|
+
const result = await this.network.query(
|
|
1253
|
+
`query ListPacksByLevel($level: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {
|
|
1254
|
+
listPackByLevelAndCreatedAt(level: $level, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
1255
|
+
items {
|
|
1256
|
+
id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId
|
|
1257
|
+
tags { items { id tagId } }
|
|
1258
|
+
}
|
|
1259
|
+
nextToken
|
|
1260
|
+
}
|
|
1261
|
+
}`,
|
|
1262
|
+
variables
|
|
1263
|
+
);
|
|
1264
|
+
return result.data.listPackByLevelAndCreatedAt;
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : Object.keys(filter).length > 0 || hasDifficulty;
|
|
1268
|
+
if (!hasPostFilter) {
|
|
1269
|
+
return executeQuery(targetLimit, input.nextToken);
|
|
1270
|
+
}
|
|
1271
|
+
return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);
|
|
1272
|
+
}
|
|
1273
|
+
async createGroup(input) {
|
|
1274
|
+
const typeLevel = input.type && input.level ? `${input.type}_${input.level}` : void 0;
|
|
1275
|
+
const mutationInput = {
|
|
1276
|
+
name: input.name,
|
|
1277
|
+
description: input.description,
|
|
1278
|
+
imageUrl: input.imageUrl,
|
|
1279
|
+
type: input.type,
|
|
1280
|
+
level: input.level,
|
|
1281
|
+
typeLevel,
|
|
1282
|
+
order: input.order
|
|
1283
|
+
};
|
|
1284
|
+
const result = await this.network.mutate(
|
|
1285
|
+
`mutation CreatePackGroup($input: CreatePackGroupInput!) {
|
|
1286
|
+
createPackGroup(input: $input) {
|
|
1287
|
+
id name description imageUrl type level typeLevel packCount order
|
|
1288
|
+
}
|
|
1289
|
+
}`,
|
|
1290
|
+
{ input: mutationInput }
|
|
593
1291
|
);
|
|
594
|
-
|
|
1292
|
+
const created = result.data.createPackGroup;
|
|
1293
|
+
if (created && input.packIds && input.packIds.length > 0) {
|
|
1294
|
+
await Promise.all(
|
|
1295
|
+
input.packIds.map((pId) => this.update(pId, { packGroupId: created.id }))
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
return created;
|
|
1299
|
+
}
|
|
1300
|
+
async deleteGroup(id) {
|
|
1301
|
+
const result = await this.network.mutate(
|
|
1302
|
+
`mutation DeletePackGroup($input: DeletePackGroupInput!) {
|
|
1303
|
+
deletePackGroup(input: $input) {
|
|
1304
|
+
id
|
|
1305
|
+
}
|
|
1306
|
+
}`,
|
|
1307
|
+
{ input: { id } }
|
|
1308
|
+
);
|
|
1309
|
+
return result.data.deletePackGroup;
|
|
1310
|
+
}
|
|
1311
|
+
async listGroups(typeOrInput, level) {
|
|
1312
|
+
let type;
|
|
1313
|
+
let targetLevel;
|
|
1314
|
+
let limit;
|
|
1315
|
+
let nextToken;
|
|
1316
|
+
let sortDirection = "ASC";
|
|
1317
|
+
const isLegacyCall = typeof typeOrInput === "string";
|
|
1318
|
+
if (isLegacyCall) {
|
|
1319
|
+
type = typeOrInput;
|
|
1320
|
+
targetLevel = level;
|
|
1321
|
+
} else {
|
|
1322
|
+
type = typeOrInput.type ?? "QB";
|
|
1323
|
+
targetLevel = typeOrInput.level;
|
|
1324
|
+
limit = typeOrInput.limit;
|
|
1325
|
+
nextToken = typeOrInput.nextToken;
|
|
1326
|
+
sortDirection = typeOrInput.sortDirection ?? "ASC";
|
|
1327
|
+
}
|
|
1328
|
+
const queryField = targetLevel ? "listPackGroupByTypeLevelAndOrder" : "listPackGroupByTypeAndOrder";
|
|
1329
|
+
const variables = isLegacyCall ? targetLevel ? { typeLevel: `${type}_${targetLevel}`, sortDirection } : { type, sortDirection } : targetLevel ? { typeLevel: `${type}_${targetLevel}`, sortDirection, limit: limit ?? 20, nextToken } : { type, sortDirection, limit: limit ?? 20, nextToken };
|
|
1330
|
+
const queryString = isLegacyCall ? targetLevel ? `query ListPackGroups($typeLevel: String!, $sortDirection: ModelSortDirection) {
|
|
1331
|
+
listPackGroupByTypeLevelAndOrder(typeLevel: $typeLevel, sortDirection: $sortDirection) {
|
|
1332
|
+
items { id name description imageUrl type level typeLevel packCount order }
|
|
1333
|
+
}
|
|
1334
|
+
}` : `query ListPackGroups($type: String!, $sortDirection: ModelSortDirection) {
|
|
1335
|
+
listPackGroupByTypeAndOrder(type: $type, sortDirection: $sortDirection) {
|
|
1336
|
+
items { id name description imageUrl type level typeLevel packCount order }
|
|
1337
|
+
}
|
|
1338
|
+
}` : targetLevel ? `query ListPackGroupsByLevel($typeLevel: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {
|
|
1339
|
+
listPackGroupByTypeLevelAndOrder(typeLevel: $typeLevel, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
|
|
1340
|
+
items { id name description imageUrl type level typeLevel packCount order }
|
|
1341
|
+
nextToken
|
|
1342
|
+
}
|
|
1343
|
+
}` : `query ListPackGroupsByType($type: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {
|
|
1344
|
+
listPackGroupByTypeAndOrder(type: $type, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
|
|
1345
|
+
items { id name description imageUrl type level typeLevel packCount order }
|
|
1346
|
+
nextToken
|
|
1347
|
+
}
|
|
1348
|
+
}`;
|
|
1349
|
+
const result = await this.network.query(queryString, variables);
|
|
1350
|
+
const paginated = result.data[queryField];
|
|
1351
|
+
return isLegacyCall ? paginated.items : paginated;
|
|
595
1352
|
}
|
|
596
1353
|
async getGroup(id) {
|
|
597
1354
|
const result = await this.network.query(
|
|
598
1355
|
`query GetPackGroup($id: ID!) {
|
|
599
1356
|
getPackGroup(id: $id) {
|
|
600
1357
|
id name description imageUrl type level typeLevel packCount order
|
|
601
|
-
packs { items { id name language level difficulty questionCount owner createdAt verificationStatus } }
|
|
1358
|
+
packs { items { id name language level publicExam difficulty questionCount owner createdAt verificationStatus } }
|
|
602
1359
|
}
|
|
603
1360
|
}`,
|
|
604
1361
|
{ id }
|
|
605
1362
|
);
|
|
606
1363
|
return result.data.getPackGroup;
|
|
607
1364
|
}
|
|
1365
|
+
async list(input) {
|
|
1366
|
+
const result = await this.network.query(
|
|
1367
|
+
`query ListPacks($limit: Int, $nextToken: String) {
|
|
1368
|
+
listPacks(limit: $limit, nextToken: $nextToken) {
|
|
1369
|
+
items {
|
|
1370
|
+
id name language level publicExam difficulty questionCount owner createdAt verificationStatus packGroupId
|
|
1371
|
+
}
|
|
1372
|
+
nextToken
|
|
1373
|
+
}
|
|
1374
|
+
}`,
|
|
1375
|
+
{ limit: input?.limit ?? 20, nextToken: input?.nextToken }
|
|
1376
|
+
);
|
|
1377
|
+
return result.data.listPacks;
|
|
1378
|
+
}
|
|
608
1379
|
};
|
|
609
1380
|
|
|
610
1381
|
// src/services/contest.service.ts
|
|
@@ -962,6 +1733,28 @@ var CourseService = class {
|
|
|
962
1733
|
);
|
|
963
1734
|
return result.data.publishCourseLiveExamResults;
|
|
964
1735
|
}
|
|
1736
|
+
async uploadCourseItem(input) {
|
|
1737
|
+
const signed = await this.getSignedURL({
|
|
1738
|
+
courseId: input.courseId,
|
|
1739
|
+
fileName: input.fileName,
|
|
1740
|
+
fileType: input.fileType,
|
|
1741
|
+
parentId: input.parentId ?? void 0
|
|
1742
|
+
});
|
|
1743
|
+
if (signed && signed.signedURL && input.fileData) {
|
|
1744
|
+
await fetch(signed.signedURL, {
|
|
1745
|
+
method: "PUT",
|
|
1746
|
+
body: input.fileData,
|
|
1747
|
+
headers: { "Content-Type": input.fileType }
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1750
|
+
return this.createItem({
|
|
1751
|
+
courseId: input.courseId,
|
|
1752
|
+
name: input.itemName || input.fileName,
|
|
1753
|
+
order: input.itemOrder,
|
|
1754
|
+
type: "file",
|
|
1755
|
+
parentId: input.parentId ?? void 0
|
|
1756
|
+
});
|
|
1757
|
+
}
|
|
965
1758
|
};
|
|
966
1759
|
|
|
967
1760
|
// src/services/curriculum.service.ts
|
|
@@ -1029,6 +1822,67 @@ var CurriculumService = class {
|
|
|
1029
1822
|
);
|
|
1030
1823
|
return result.data.listTopicByChapterId.items;
|
|
1031
1824
|
}
|
|
1825
|
+
async recalculateQuestionCounts() {
|
|
1826
|
+
let nextToken = null;
|
|
1827
|
+
const counts = {};
|
|
1828
|
+
do {
|
|
1829
|
+
const res = await this.network.query(
|
|
1830
|
+
`query ListQuestionsForRecalc($nextToken: String) {
|
|
1831
|
+
listQuestionObjects(limit: 1000, nextToken: $nextToken) {
|
|
1832
|
+
items { id subjectId chapterId topicId }
|
|
1833
|
+
nextToken
|
|
1834
|
+
}
|
|
1835
|
+
}`,
|
|
1836
|
+
{ nextToken }
|
|
1837
|
+
);
|
|
1838
|
+
const items = res.data?.listQuestionObjects?.items || [];
|
|
1839
|
+
for (const q of items) {
|
|
1840
|
+
if (q.subjectId) counts[q.subjectId] = (counts[q.subjectId] || 0) + 1;
|
|
1841
|
+
}
|
|
1842
|
+
nextToken = res.data?.listQuestionObjects?.nextToken ?? null;
|
|
1843
|
+
} while (nextToken);
|
|
1844
|
+
const [hscSubjects, sscSubjects] = await Promise.all([
|
|
1845
|
+
this.getSubjects("HSC").catch(() => []),
|
|
1846
|
+
this.getSubjects("SSC").catch(() => [])
|
|
1847
|
+
]);
|
|
1848
|
+
const allSubjects = [...hscSubjects, ...sscSubjects];
|
|
1849
|
+
for (const s of allSubjects) {
|
|
1850
|
+
await this.network.mutate(
|
|
1851
|
+
`mutation UpdateSubjectCount($input: UpdateSubjectInput!) {
|
|
1852
|
+
updateSubject(input: $input) { id }
|
|
1853
|
+
}`,
|
|
1854
|
+
{ input: { id: s.id, questionCount: counts[s.id] || 0 } }
|
|
1855
|
+
);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
async recalculateCreatorStats() {
|
|
1859
|
+
let nextToken = null;
|
|
1860
|
+
const userQuestionCounts = {};
|
|
1861
|
+
do {
|
|
1862
|
+
const res = await this.network.query(
|
|
1863
|
+
`query ListQuestionsForCreatorStats($nextToken: String) {
|
|
1864
|
+
listQuestionObjects(limit: 1000, nextToken: $nextToken) {
|
|
1865
|
+
items { id owner }
|
|
1866
|
+
nextToken
|
|
1867
|
+
}
|
|
1868
|
+
}`,
|
|
1869
|
+
{ nextToken }
|
|
1870
|
+
);
|
|
1871
|
+
const items = res.data?.listQuestionObjects?.items || [];
|
|
1872
|
+
for (const q of items) {
|
|
1873
|
+
if (q.owner) userQuestionCounts[q.owner] = (userQuestionCounts[q.owner] || 0) + 1;
|
|
1874
|
+
}
|
|
1875
|
+
nextToken = res.data?.listQuestionObjects?.nextToken ?? null;
|
|
1876
|
+
} while (nextToken);
|
|
1877
|
+
for (const [userId, count] of Object.entries(userQuestionCounts)) {
|
|
1878
|
+
await this.network.mutate(
|
|
1879
|
+
`mutation UpdateCreatorStats($input: UpdateUserInput!) {
|
|
1880
|
+
updateUser(input: $input) { id }
|
|
1881
|
+
}`,
|
|
1882
|
+
{ input: { id: userId, questionCount: count } }
|
|
1883
|
+
);
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1032
1886
|
};
|
|
1033
1887
|
|
|
1034
1888
|
// src/services/media.service.ts
|
|
@@ -1207,7 +2061,7 @@ var ProgressService = class {
|
|
|
1207
2061
|
return [];
|
|
1208
2062
|
}
|
|
1209
2063
|
}
|
|
1210
|
-
async getMistakenQuestions(userId) {
|
|
2064
|
+
async getMistakenQuestions(userId, status = "MISTAKE") {
|
|
1211
2065
|
try {
|
|
1212
2066
|
const result = await this.network.query(
|
|
1213
2067
|
`query ListMistakes($userId: String!, $filter: ModelQuestionTrackingFilterInput) {
|
|
@@ -1215,7 +2069,7 @@ var ProgressService = class {
|
|
|
1215
2069
|
items { id userId questionId subjectId chapterId topicId status attempts }
|
|
1216
2070
|
}
|
|
1217
2071
|
}`,
|
|
1218
|
-
{ userId, filter: { status: { eq:
|
|
2072
|
+
{ userId, filter: { status: { eq: status } } }
|
|
1219
2073
|
);
|
|
1220
2074
|
return result.data?.listQuestionTrackingByUserIdAndQuestionId?.items || result.data?.listQuestionTrackingsByUserIdAndQuestionId?.items || result.data?.listQuestionTrackings?.items || [];
|
|
1221
2075
|
} catch (e) {
|
|
@@ -1223,6 +2077,9 @@ var ProgressService = class {
|
|
|
1223
2077
|
return [];
|
|
1224
2078
|
}
|
|
1225
2079
|
}
|
|
2080
|
+
async getCorrectedQuestions(userId) {
|
|
2081
|
+
return this.getMistakenQuestions(userId, "CORRECTED");
|
|
2082
|
+
}
|
|
1226
2083
|
};
|
|
1227
2084
|
|
|
1228
2085
|
// src/services/ai.service.ts
|
|
@@ -1230,7 +2087,27 @@ var AIService = class {
|
|
|
1230
2087
|
constructor(network) {
|
|
1231
2088
|
this.network = network;
|
|
1232
2089
|
}
|
|
1233
|
-
async evaluateWrittenAnswer(input) {
|
|
2090
|
+
async evaluateWrittenAnswer(input, options) {
|
|
2091
|
+
if (options?.functionUrl) {
|
|
2092
|
+
try {
|
|
2093
|
+
const headers = { "Content-Type": "application/json" };
|
|
2094
|
+
if (options.authToken) {
|
|
2095
|
+
headers["Authorization"] = `Bearer ${options.authToken}`;
|
|
2096
|
+
}
|
|
2097
|
+
const response = await fetch(options.functionUrl, {
|
|
2098
|
+
method: "POST",
|
|
2099
|
+
headers,
|
|
2100
|
+
body: JSON.stringify(input)
|
|
2101
|
+
});
|
|
2102
|
+
if (response.ok) {
|
|
2103
|
+
const data = await response.json();
|
|
2104
|
+
return data;
|
|
2105
|
+
}
|
|
2106
|
+
console.warn(`[AIService] Function URL failed with status ${response.status}, falling back to GraphQL...`);
|
|
2107
|
+
} catch (err) {
|
|
2108
|
+
console.warn("[AIService] Function URL fetch error, falling back to GraphQL...", err);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
1234
2111
|
const result = await this.network.query(
|
|
1235
2112
|
`query EvaluateWrittenAnswer($question: String!, $userAnswer: String!, $correctAnswer: String!, $markingInstructions: String, $topicId: String, $topicName: String, $questionImageUrl: String) {
|
|
1236
2113
|
evaluateWrittenAnswer(question: $question, userAnswer: $userAnswer, correctAnswer: $correctAnswer, markingInstructions: $markingInstructions, topicId: $topicId, topicName: $topicName, questionImageUrl: $questionImageUrl) {
|
|
@@ -1280,10 +2157,256 @@ var NewsService = class {
|
|
|
1280
2157
|
}
|
|
1281
2158
|
};
|
|
1282
2159
|
|
|
2160
|
+
// src/services/notification.service.ts
|
|
2161
|
+
var NotificationService = class {
|
|
2162
|
+
constructor(network) {
|
|
2163
|
+
this.network = network;
|
|
2164
|
+
}
|
|
2165
|
+
/**
|
|
2166
|
+
* List broadcast announcements (ordered by category and createdAt).
|
|
2167
|
+
*/
|
|
2168
|
+
async listAnnouncements(limit = 20, nextToken) {
|
|
2169
|
+
const result = await this.network.query(
|
|
2170
|
+
`query ListAnnouncements($limit: Int, $nextToken: String) {
|
|
2171
|
+
listAnnouncements(limit: $limit, nextToken: $nextToken) {
|
|
2172
|
+
items {
|
|
2173
|
+
id title message senderType senderName senderAvatar category actionUrl priority createdAt updatedAt
|
|
2174
|
+
}
|
|
2175
|
+
nextToken
|
|
2176
|
+
}
|
|
2177
|
+
}`,
|
|
2178
|
+
{ limit, nextToken }
|
|
2179
|
+
);
|
|
2180
|
+
return result.data.listAnnouncements ?? { items: [], nextToken: null };
|
|
2181
|
+
}
|
|
2182
|
+
/**
|
|
2183
|
+
* List announcements by category.
|
|
2184
|
+
*/
|
|
2185
|
+
async listAnnouncementsByCategory(category = "ANNOUNCEMENT", limit = 20, nextToken) {
|
|
2186
|
+
const result = await this.network.query(
|
|
2187
|
+
`query ListAnnouncementsByCategory($category: String!, $limit: Int, $nextToken: String, $sortDirection: ModelSortDirection) {
|
|
2188
|
+
listAnnouncementByCategoryAndCreatedAt(category: $category, limit: $limit, nextToken: $nextToken, sortDirection: $sortDirection) {
|
|
2189
|
+
items {
|
|
2190
|
+
id title message senderType senderName senderAvatar category actionUrl priority createdAt updatedAt
|
|
2191
|
+
}
|
|
2192
|
+
nextToken
|
|
2193
|
+
}
|
|
2194
|
+
}`,
|
|
2195
|
+
{ category, limit, nextToken, sortDirection: "DESC" }
|
|
2196
|
+
);
|
|
2197
|
+
return result.data.listAnnouncementByCategoryAndCreatedAt ?? {
|
|
2198
|
+
items: [],
|
|
2199
|
+
nextToken: null
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
/**
|
|
2203
|
+
* Create an announcement (Admins only).
|
|
2204
|
+
*/
|
|
2205
|
+
async createAnnouncement(input) {
|
|
2206
|
+
const result = await this.network.mutate(
|
|
2207
|
+
`mutation CreateAnnouncement($input: CreateAnnouncementInput!) {
|
|
2208
|
+
createAnnouncement(input: $input) {
|
|
2209
|
+
id title message senderType senderName senderAvatar category actionUrl priority createdAt updatedAt
|
|
2210
|
+
}
|
|
2211
|
+
}`,
|
|
2212
|
+
{
|
|
2213
|
+
input: {
|
|
2214
|
+
...input,
|
|
2215
|
+
senderType: input.senderType || "ORJOK",
|
|
2216
|
+
category: input.category || "ANNOUNCEMENT",
|
|
2217
|
+
priority: input.priority || "NORMAL"
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
);
|
|
2221
|
+
return result.data.createAnnouncement;
|
|
2222
|
+
}
|
|
2223
|
+
/**
|
|
2224
|
+
* Delete an announcement (Admins only).
|
|
2225
|
+
*/
|
|
2226
|
+
async deleteAnnouncement(id) {
|
|
2227
|
+
const result = await this.network.mutate(
|
|
2228
|
+
`mutation DeleteAnnouncement($input: DeleteAnnouncementInput!) {
|
|
2229
|
+
deleteAnnouncement(input: $input) { id }
|
|
2230
|
+
}`,
|
|
2231
|
+
{ input: { id } }
|
|
2232
|
+
);
|
|
2233
|
+
return Boolean(result.data.deleteAnnouncement?.id);
|
|
2234
|
+
}
|
|
2235
|
+
/**
|
|
2236
|
+
* List user notifications for a specific user ID.
|
|
2237
|
+
*/
|
|
2238
|
+
async listUserNotifications(userId, limit = 20, nextToken) {
|
|
2239
|
+
const result = await this.network.query(
|
|
2240
|
+
`query ListUserNotifications($userId: ID!, $limit: Int, $nextToken: String, $sortDirection: ModelSortDirection) {
|
|
2241
|
+
listUserNotificationByUserIdAndCreatedAt(userId: $userId, limit: $limit, nextToken: $nextToken, sortDirection: $sortDirection) {
|
|
2242
|
+
items {
|
|
2243
|
+
id userId title message type senderType senderName senderAvatar actionUrl isRead readAt metadata createdAt updatedAt
|
|
2244
|
+
}
|
|
2245
|
+
nextToken
|
|
2246
|
+
}
|
|
2247
|
+
}`,
|
|
2248
|
+
{ userId, limit, nextToken, sortDirection: "DESC" }
|
|
2249
|
+
);
|
|
2250
|
+
return result.data.listUserNotificationByUserIdAndCreatedAt ?? {
|
|
2251
|
+
items: [],
|
|
2252
|
+
nextToken: null
|
|
2253
|
+
};
|
|
2254
|
+
}
|
|
2255
|
+
/**
|
|
2256
|
+
* Create a user notification (Level up, direct message, system alert).
|
|
2257
|
+
*/
|
|
2258
|
+
async createUserNotification(input) {
|
|
2259
|
+
const result = await this.network.mutate(
|
|
2260
|
+
`mutation CreateUserNotification($input: CreateUserNotificationInput!) {
|
|
2261
|
+
createUserNotification(input: $input) {
|
|
2262
|
+
id userId title message type senderType senderName senderAvatar actionUrl isRead readAt metadata createdAt updatedAt
|
|
2263
|
+
}
|
|
2264
|
+
}`,
|
|
2265
|
+
{
|
|
2266
|
+
input: {
|
|
2267
|
+
...input,
|
|
2268
|
+
isRead: false
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
);
|
|
2272
|
+
return result.data.createUserNotification;
|
|
2273
|
+
}
|
|
2274
|
+
/**
|
|
2275
|
+
* Mark a personal notification as read.
|
|
2276
|
+
*/
|
|
2277
|
+
async markNotificationAsRead(id) {
|
|
2278
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2279
|
+
const result = await this.network.mutate(
|
|
2280
|
+
`mutation UpdateUserNotification($input: UpdateUserNotificationInput!) {
|
|
2281
|
+
updateUserNotification(input: $input) {
|
|
2282
|
+
id isRead readAt
|
|
2283
|
+
}
|
|
2284
|
+
}`,
|
|
2285
|
+
{
|
|
2286
|
+
input: {
|
|
2287
|
+
id,
|
|
2288
|
+
isRead: true,
|
|
2289
|
+
readAt: now
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
);
|
|
2293
|
+
return result.data.updateUserNotification;
|
|
2294
|
+
}
|
|
2295
|
+
/**
|
|
2296
|
+
* Delete a personal user notification.
|
|
2297
|
+
*/
|
|
2298
|
+
async deleteUserNotification(id) {
|
|
2299
|
+
const result = await this.network.mutate(
|
|
2300
|
+
`mutation DeleteUserNotification($input: DeleteUserNotificationInput!) {
|
|
2301
|
+
deleteUserNotification(input: $input) { id }
|
|
2302
|
+
}`,
|
|
2303
|
+
{ input: { id } }
|
|
2304
|
+
);
|
|
2305
|
+
return Boolean(result.data.deleteUserNotification?.id);
|
|
2306
|
+
}
|
|
2307
|
+
/**
|
|
2308
|
+
* Update the user's lastReadAnnouncementAt timestamp in DynamoDB.
|
|
2309
|
+
*/
|
|
2310
|
+
async updateLastReadAnnouncement(userId, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2311
|
+
const result = await this.network.mutate(
|
|
2312
|
+
`mutation UpdateUserLastRead($input: UpdateUserInput!) {
|
|
2313
|
+
updateUser(input: $input) { id lastReadAnnouncementAt }
|
|
2314
|
+
}`,
|
|
2315
|
+
{ input: { id: userId, lastReadAnnouncementAt: timestamp } }
|
|
2316
|
+
);
|
|
2317
|
+
return Boolean(result.data.updateUser?.id);
|
|
2318
|
+
}
|
|
2319
|
+
};
|
|
2320
|
+
|
|
2321
|
+
// src/services/subscription.service.ts
|
|
2322
|
+
var SubscriptionService = class {
|
|
2323
|
+
constructor(network) {
|
|
2324
|
+
this.network = network;
|
|
2325
|
+
}
|
|
2326
|
+
async createRequest(input) {
|
|
2327
|
+
const result = await this.network.mutate(
|
|
2328
|
+
`mutation CreateSubscriptionRequest($input: CreateSubscriptionRequestInput!) {
|
|
2329
|
+
createSubscriptionRequest(input: $input) {
|
|
2330
|
+
id userId userFullName userEmail itemType itemId itemTitle amount billingCycle paymentMethod senderPhone contactPhone trxId status aiEvals aiQuestions adminNotes processedAt processedBy createdAt updatedAt
|
|
2331
|
+
}
|
|
2332
|
+
}`,
|
|
2333
|
+
{ input }
|
|
2334
|
+
);
|
|
2335
|
+
return result.data.createSubscriptionRequest;
|
|
2336
|
+
}
|
|
2337
|
+
async getRequest(id) {
|
|
2338
|
+
const result = await this.network.query(
|
|
2339
|
+
`query GetSubscriptionRequest($id: ID!) {
|
|
2340
|
+
getSubscriptionRequest(id: $id) {
|
|
2341
|
+
id userId userFullName userEmail itemType itemId itemTitle amount billingCycle paymentMethod senderPhone contactPhone trxId status aiEvals aiQuestions adminNotes processedAt processedBy createdAt updatedAt
|
|
2342
|
+
}
|
|
2343
|
+
}`,
|
|
2344
|
+
{ id }
|
|
2345
|
+
);
|
|
2346
|
+
return result.data.getSubscriptionRequest;
|
|
2347
|
+
}
|
|
2348
|
+
async listRequests(input = {}) {
|
|
2349
|
+
const limit = input.limit ?? 20;
|
|
2350
|
+
const sortDirection = input.sortDirection ?? "DESC";
|
|
2351
|
+
if (input.status) {
|
|
2352
|
+
const result2 = await this.network.query(
|
|
2353
|
+
`query ListSubscriptionRequestsByStatus($status: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {
|
|
2354
|
+
listSubscriptionRequestByStatusAndCreatedAt(status: $status, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
|
|
2355
|
+
items {
|
|
2356
|
+
id userId userFullName userEmail itemType itemId itemTitle amount billingCycle paymentMethod senderPhone contactPhone trxId status aiEvals aiQuestions adminNotes processedAt processedBy createdAt updatedAt
|
|
2357
|
+
}
|
|
2358
|
+
nextToken
|
|
2359
|
+
}
|
|
2360
|
+
}`,
|
|
2361
|
+
{ status: input.status, sortDirection, limit, nextToken: input.nextToken }
|
|
2362
|
+
);
|
|
2363
|
+
return result2.data.listSubscriptionRequestByStatusAndCreatedAt;
|
|
2364
|
+
}
|
|
2365
|
+
if (input.userId) {
|
|
2366
|
+
const result2 = await this.network.query(
|
|
2367
|
+
`query ListSubscriptionRequestsByUser($userId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {
|
|
2368
|
+
listSubscriptionRequestByUserIdAndCreatedAt(userId: $userId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
|
|
2369
|
+
items {
|
|
2370
|
+
id userId userFullName userEmail itemType itemId itemTitle amount billingCycle paymentMethod senderPhone contactPhone trxId status aiEvals aiQuestions adminNotes processedAt processedBy createdAt updatedAt
|
|
2371
|
+
}
|
|
2372
|
+
nextToken
|
|
2373
|
+
}
|
|
2374
|
+
}`,
|
|
2375
|
+
{ userId: input.userId, sortDirection, limit, nextToken: input.nextToken }
|
|
2376
|
+
);
|
|
2377
|
+
return result2.data.listSubscriptionRequestByUserIdAndCreatedAt;
|
|
2378
|
+
}
|
|
2379
|
+
const result = await this.network.query(
|
|
2380
|
+
`query ListAllSubscriptionRequests($limit: Int, $nextToken: String, $filter: ModelSubscriptionRequestFilterInput) {
|
|
2381
|
+
listSubscriptionRequests(limit: $limit, nextToken: $nextToken, filter: $filter) {
|
|
2382
|
+
items {
|
|
2383
|
+
id userId userFullName userEmail itemType itemId itemTitle amount billingCycle paymentMethod senderPhone contactPhone trxId status aiEvals aiQuestions adminNotes processedAt processedBy createdAt updatedAt
|
|
2384
|
+
}
|
|
2385
|
+
nextToken
|
|
2386
|
+
}
|
|
2387
|
+
}`,
|
|
2388
|
+
{ limit, nextToken: input.nextToken, filter: input.filter }
|
|
2389
|
+
);
|
|
2390
|
+
return result.data.listSubscriptionRequests;
|
|
2391
|
+
}
|
|
2392
|
+
async updateRequest(input) {
|
|
2393
|
+
const result = await this.network.mutate(
|
|
2394
|
+
`mutation UpdateSubscriptionRequest($input: UpdateSubscriptionRequestInput!) {
|
|
2395
|
+
updateSubscriptionRequest(input: $input) {
|
|
2396
|
+
id userId userFullName userEmail itemType itemId itemTitle amount billingCycle paymentMethod senderPhone contactPhone trxId status aiEvals aiQuestions adminNotes processedAt processedBy createdAt updatedAt
|
|
2397
|
+
}
|
|
2398
|
+
}`,
|
|
2399
|
+
{ input }
|
|
2400
|
+
);
|
|
2401
|
+
return result.data.updateSubscriptionRequest;
|
|
2402
|
+
}
|
|
2403
|
+
};
|
|
2404
|
+
|
|
1283
2405
|
// src/client.ts
|
|
1284
2406
|
var OrjokClient = class {
|
|
1285
2407
|
constructor(config) {
|
|
1286
2408
|
this.auth = config.authProvider;
|
|
2409
|
+
this.network = config.networkProvider;
|
|
1287
2410
|
this.storage = config.storageProvider;
|
|
1288
2411
|
this.users = new UserService(config.networkProvider);
|
|
1289
2412
|
this.questions = new QuestionService(config.networkProvider);
|
|
@@ -1295,29 +2418,14 @@ var OrjokClient = class {
|
|
|
1295
2418
|
this.progress = new ProgressService(config.networkProvider);
|
|
1296
2419
|
this.ai = new AIService(config.networkProvider);
|
|
1297
2420
|
this.news = new NewsService(config.networkProvider);
|
|
2421
|
+
this.notifications = new NotificationService(config.networkProvider);
|
|
2422
|
+
this.subscriptions = new SubscriptionService(config.networkProvider);
|
|
1298
2423
|
}
|
|
1299
2424
|
};
|
|
1300
2425
|
function createOrjokClient(config) {
|
|
1301
2426
|
return new OrjokClient(config);
|
|
1302
2427
|
}
|
|
1303
2428
|
|
|
1304
|
-
// src/types/select-options.ts
|
|
1305
|
-
var levelOptions = [
|
|
1306
|
-
{ value: "SSC", label: "SSC" },
|
|
1307
|
-
{ value: "HSC+Admission", label: "HSC/Admission" },
|
|
1308
|
-
{ value: "BCS", label: "BCS" }
|
|
1309
|
-
];
|
|
1310
|
-
var languageOptions = [
|
|
1311
|
-
{ value: "Bangla", label: "Bangla" },
|
|
1312
|
-
{ value: "English", label: "English" },
|
|
1313
|
-
{ value: "Any", label: "Any" }
|
|
1314
|
-
];
|
|
1315
|
-
var difficultyOptions = [
|
|
1316
|
-
{ value: "Easy", label: "Easy" },
|
|
1317
|
-
{ value: "Medium", label: "Medium" },
|
|
1318
|
-
{ value: "Hard", label: "Hard" }
|
|
1319
|
-
];
|
|
1320
|
-
|
|
1321
2429
|
// src/logic/avatar.ts
|
|
1322
2430
|
var HAIR_COLORS = [
|
|
1323
2431
|
{ name: "Original", filter: "none", hex: "#e2e8f0", brightness: 1 },
|
|
@@ -1403,15 +2511,124 @@ function parseAvatarConfig(configStr) {
|
|
|
1403
2511
|
const parsed = JSON.parse(configStr);
|
|
1404
2512
|
return {
|
|
1405
2513
|
selections: parsed.selections || {},
|
|
1406
|
-
colors: parsed.colors || {}
|
|
2514
|
+
colors: parsed.colors || {},
|
|
2515
|
+
gender: parsed.gender
|
|
1407
2516
|
};
|
|
1408
2517
|
} catch {
|
|
1409
2518
|
return { selections: {}, colors: {} };
|
|
1410
2519
|
}
|
|
1411
2520
|
}
|
|
2521
|
+
function parseAvatarConfigWithDefaults(configStr, seed) {
|
|
2522
|
+
const parsed = parseAvatarConfig(configStr);
|
|
2523
|
+
const hasSelections = Object.keys(parsed.selections).length > 0;
|
|
2524
|
+
if (!hasSelections) {
|
|
2525
|
+
return generateRandomAvatarState({ seed });
|
|
2526
|
+
}
|
|
2527
|
+
return {
|
|
2528
|
+
selections: { ...parsed.selections },
|
|
2529
|
+
colors: { ...parsed.colors },
|
|
2530
|
+
gender: parsed.gender
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
1412
2533
|
function serializeAvatarConfig(state) {
|
|
1413
2534
|
return JSON.stringify(state);
|
|
1414
2535
|
}
|
|
2536
|
+
function createSeededRandom(seedStr) {
|
|
2537
|
+
let h = 1779033703 ^ seedStr.length;
|
|
2538
|
+
for (let i = 0; i < seedStr.length; i++) {
|
|
2539
|
+
h = Math.imul(h ^ seedStr.charCodeAt(i), 3432918353);
|
|
2540
|
+
h = h << 13 | h >>> 19;
|
|
2541
|
+
}
|
|
2542
|
+
return () => {
|
|
2543
|
+
h = Math.imul(h ^ h >>> 16, 2246822507);
|
|
2544
|
+
h = Math.imul(h ^ h >>> 13, 3266489909);
|
|
2545
|
+
return ((h ^= h >>> 16) >>> 0) / 4294967296;
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2548
|
+
function pickRandom(arr, fallback, rng = Math.random) {
|
|
2549
|
+
if (arr.length === 0) return fallback;
|
|
2550
|
+
const item = arr[Math.floor(rng() * arr.length)];
|
|
2551
|
+
return item !== void 0 ? item : fallback;
|
|
2552
|
+
}
|
|
2553
|
+
function generateRandomAvatarState(options) {
|
|
2554
|
+
const rng = options?.seed ? createSeededRandom(options.seed) : Math.random;
|
|
2555
|
+
const gender = options?.gender || (rng() > 0.5 ? "Male" : "Female");
|
|
2556
|
+
const backgrounds = ["Solid", "3 Stripes"];
|
|
2557
|
+
const skinColors = [
|
|
2558
|
+
"Light Peach",
|
|
2559
|
+
"Warm Peach",
|
|
2560
|
+
"Golden Tan",
|
|
2561
|
+
"Sand Beige",
|
|
2562
|
+
"Warm Brown",
|
|
2563
|
+
"Deep Brown"
|
|
2564
|
+
];
|
|
2565
|
+
const maleHairs = ["Clean", "Mid", "Curled", "Bald"];
|
|
2566
|
+
const femaleHairs = ["Bun", "Long", "Mid", "Short"];
|
|
2567
|
+
const clothesList = ["Tshirt", "Hoodie", "Turtleneck"];
|
|
2568
|
+
const expressions = ["Normal", "Happy", "Joyful", "Playful", "Surprised"];
|
|
2569
|
+
const eywears = ["None", "None", "None", "None", "Normal Glasses"];
|
|
2570
|
+
const headwears = ["None", "None", "None", "None", "Beanie", "HeadPhones"];
|
|
2571
|
+
const hairList = gender === "Male" ? maleHairs : femaleHairs;
|
|
2572
|
+
const bg = pickRandom(backgrounds, "Solid", rng);
|
|
2573
|
+
const skin = pickRandom(skinColors, "Light Peach", rng);
|
|
2574
|
+
const hair = pickRandom(hairList, gender === "Male" ? "Clean" : "Short", rng);
|
|
2575
|
+
const clothes = pickRandom(clothesList, "Tshirt", rng);
|
|
2576
|
+
const expression = pickRandom(expressions, "Normal", rng);
|
|
2577
|
+
const eyewear = pickRandom(eywears, "None", rng);
|
|
2578
|
+
const headwear = pickRandom(headwears, "None", rng);
|
|
2579
|
+
const hairColorPool = [
|
|
2580
|
+
"Black",
|
|
2581
|
+
"Black",
|
|
2582
|
+
"Brown",
|
|
2583
|
+
"Brown",
|
|
2584
|
+
"Blonde",
|
|
2585
|
+
"White",
|
|
2586
|
+
"Red",
|
|
2587
|
+
"Blue"
|
|
2588
|
+
];
|
|
2589
|
+
const hairColor = pickRandom(hairColorPool, "Black", rng);
|
|
2590
|
+
const clothesColorPool = [
|
|
2591
|
+
"Blue",
|
|
2592
|
+
"Red",
|
|
2593
|
+
"Green",
|
|
2594
|
+
"Orange",
|
|
2595
|
+
"Purple",
|
|
2596
|
+
"Black",
|
|
2597
|
+
"Yellow",
|
|
2598
|
+
"Pink"
|
|
2599
|
+
];
|
|
2600
|
+
const clothesColor = pickRandom(clothesColorPool, "Blue", rng);
|
|
2601
|
+
const bgColorPool = ["Blue", "Green", "Purple", "Orange", "Red", "Pink", "Original"];
|
|
2602
|
+
const bgColor = pickRandom(bgColorPool, "Blue", rng);
|
|
2603
|
+
return {
|
|
2604
|
+
selections: {
|
|
2605
|
+
Background: bg,
|
|
2606
|
+
"Hair Back": hair,
|
|
2607
|
+
"Skin Color": skin,
|
|
2608
|
+
"Clothes Back": clothes,
|
|
2609
|
+
"Clothes Front": clothes,
|
|
2610
|
+
Accessories: "None",
|
|
2611
|
+
Headwears: headwear,
|
|
2612
|
+
Eyewear: eyewear,
|
|
2613
|
+
"Facial Expression": expression,
|
|
2614
|
+
Beard: "None",
|
|
2615
|
+
"Hair Front": hair
|
|
2616
|
+
},
|
|
2617
|
+
colors: {
|
|
2618
|
+
"Background Color": bgColor,
|
|
2619
|
+
"Hair Color": hairColor,
|
|
2620
|
+
"Clothes Color": clothesColor,
|
|
2621
|
+
"Accessory Color": "Original",
|
|
2622
|
+
"Headwears Color": "Original",
|
|
2623
|
+
"Eyewear Color": "Original",
|
|
2624
|
+
"Facial Expression Color": "Original"
|
|
2625
|
+
},
|
|
2626
|
+
gender
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
function generateRandomAvatarConfig(options) {
|
|
2630
|
+
return JSON.stringify(generateRandomAvatarState(options));
|
|
2631
|
+
}
|
|
1415
2632
|
|
|
1416
2633
|
// src/logic/elo.ts
|
|
1417
2634
|
var TIERS = [
|
|
@@ -1548,6 +2765,212 @@ function reorderObjectKeys(obj, oldIndex, newIndex, length) {
|
|
|
1548
2765
|
return newObj;
|
|
1549
2766
|
}
|
|
1550
2767
|
|
|
2768
|
+
// src/logic/home.ts
|
|
2769
|
+
function mapCognitoLevelToStandardLevel(cognitoLevel) {
|
|
2770
|
+
if (!cognitoLevel) return "HSC+Admission";
|
|
2771
|
+
const normalized = cognitoLevel.trim().toLowerCase();
|
|
2772
|
+
if (normalized === "class_9" || normalized === "class_10" || normalized === "ssc") {
|
|
2773
|
+
return "SSC";
|
|
2774
|
+
}
|
|
2775
|
+
if (normalized === "class_11" || normalized === "class_12" || normalized === "admission" || normalized === "hsc" || normalized === "hsc+admission") {
|
|
2776
|
+
return "HSC+Admission";
|
|
2777
|
+
}
|
|
2778
|
+
if (normalized === "bcs" || normalized === "job") {
|
|
2779
|
+
return "BCS";
|
|
2780
|
+
}
|
|
2781
|
+
return "HSC+Admission";
|
|
2782
|
+
}
|
|
2783
|
+
function getTimeOfDayGreeting(customHour, lang = "en") {
|
|
2784
|
+
const hour = customHour !== void 0 ? customHour : (/* @__PURE__ */ new Date()).getHours();
|
|
2785
|
+
if (hour >= 5 && hour < 12) {
|
|
2786
|
+
return {
|
|
2787
|
+
greeting: lang === "bn" ? "\u09B6\u09C1\u09AD \u09B8\u0995\u09BE\u09B2" : "Good morning",
|
|
2788
|
+
period: "morning"
|
|
2789
|
+
};
|
|
2790
|
+
}
|
|
2791
|
+
if (hour >= 12 && hour < 17) {
|
|
2792
|
+
return {
|
|
2793
|
+
greeting: lang === "bn" ? "\u09B6\u09C1\u09AD \u09A6\u09C1\u09AA\u09C1\u09B0" : "Good afternoon",
|
|
2794
|
+
period: "afternoon"
|
|
2795
|
+
};
|
|
2796
|
+
}
|
|
2797
|
+
if (hour >= 17 && hour < 21) {
|
|
2798
|
+
return {
|
|
2799
|
+
greeting: lang === "bn" ? "\u09B6\u09C1\u09AD \u09B8\u09A8\u09CD\u09A7\u09CD\u09AF\u09BE" : "Good evening",
|
|
2800
|
+
period: "evening"
|
|
2801
|
+
};
|
|
2802
|
+
}
|
|
2803
|
+
return {
|
|
2804
|
+
greeting: lang === "bn" ? "\u09B6\u09C1\u09AD \u09B0\u09BE\u09A4\u09CD\u09B0\u09BF" : "Good evening",
|
|
2805
|
+
period: "night"
|
|
2806
|
+
};
|
|
2807
|
+
}
|
|
2808
|
+
function getRecommendedSubjectsForLevel(level, lang = "en") {
|
|
2809
|
+
const isBn = lang === "bn";
|
|
2810
|
+
const normalizedLevel = mapCognitoLevelToStandardLevel(level);
|
|
2811
|
+
if (normalizedLevel === "SSC") {
|
|
2812
|
+
return [
|
|
2813
|
+
{
|
|
2814
|
+
id: "ssc-physics",
|
|
2815
|
+
name: isBn ? "\u09AA\u09A6\u09BE\u09B0\u09CD\u09A5\u09AC\u09BF\u099C\u09CD\u099E\u09BE\u09A8" : "Physics",
|
|
2816
|
+
iconKey: "atom",
|
|
2817
|
+
colorClass: "from-blue-500 to-indigo-600"
|
|
2818
|
+
},
|
|
2819
|
+
{
|
|
2820
|
+
id: "ssc-chemistry",
|
|
2821
|
+
name: isBn ? "\u09B0\u09B8\u09BE\u09DF\u09A8" : "Chemistry",
|
|
2822
|
+
iconKey: "flask",
|
|
2823
|
+
colorClass: "from-emerald-500 to-teal-600"
|
|
2824
|
+
},
|
|
2825
|
+
{
|
|
2826
|
+
id: "ssc-higher-math",
|
|
2827
|
+
name: isBn ? "\u0989\u099A\u09CD\u099A\u09A4\u09B0 \u0997\u09A3\u09BF\u09A4" : "Higher Math",
|
|
2828
|
+
iconKey: "math",
|
|
2829
|
+
colorClass: "from-amber-500 to-orange-600"
|
|
2830
|
+
},
|
|
2831
|
+
{
|
|
2832
|
+
id: "ssc-general-math",
|
|
2833
|
+
name: isBn ? "\u09B8\u09BE\u09A7\u09BE\u09B0\u09A3 \u0997\u09A3\u09BF\u09A4" : "General Math",
|
|
2834
|
+
iconKey: "calculator",
|
|
2835
|
+
colorClass: "from-violet-500 to-purple-600"
|
|
2836
|
+
},
|
|
2837
|
+
{
|
|
2838
|
+
id: "ssc-biology",
|
|
2839
|
+
name: isBn ? "\u099C\u09C0\u09AC\u09AC\u09BF\u099C\u09CD\u099E\u09BE\u09A8" : "Biology",
|
|
2840
|
+
iconKey: "dna",
|
|
2841
|
+
colorClass: "from-rose-500 to-pink-600"
|
|
2842
|
+
},
|
|
2843
|
+
{
|
|
2844
|
+
id: "ssc-bangla",
|
|
2845
|
+
name: isBn ? "\u09AC\u09BE\u0982\u09B2\u09BE" : "Bangla",
|
|
2846
|
+
iconKey: "book",
|
|
2847
|
+
colorClass: "from-cyan-500 to-blue-600"
|
|
2848
|
+
}
|
|
2849
|
+
];
|
|
2850
|
+
}
|
|
2851
|
+
if (normalizedLevel === "BCS") {
|
|
2852
|
+
return [
|
|
2853
|
+
{
|
|
2854
|
+
id: "bcs-bangladesh-affairs",
|
|
2855
|
+
name: isBn ? "\u09AC\u09BE\u0982\u09B2\u09BE\u09A6\u09C7\u09B6 \u09AC\u09BF\u09B7\u09DF\u09BE\u09AC\u09B2\u09C0" : "Bangladesh Affairs",
|
|
2856
|
+
iconKey: "monument",
|
|
2857
|
+
colorClass: "from-emerald-500 to-green-600"
|
|
2858
|
+
},
|
|
2859
|
+
{
|
|
2860
|
+
id: "bcs-international-affairs",
|
|
2861
|
+
name: isBn ? "\u0986\u09A8\u09CD\u09A4\u09B0\u09CD\u099C\u09BE\u09A4\u09BF\u0995 \u09AC\u09BF\u09B7\u09DF\u09BE\u09AC\u09B2\u09C0" : "International Affairs",
|
|
2862
|
+
iconKey: "world",
|
|
2863
|
+
colorClass: "from-blue-500 to-cyan-600"
|
|
2864
|
+
},
|
|
2865
|
+
{
|
|
2866
|
+
id: "bcs-bangla",
|
|
2867
|
+
name: isBn ? "\u09AC\u09BE\u0982\u09B2\u09BE \u09AD\u09BE\u09B7\u09BE \u0993 \u09B8\u09BE\u09B9\u09BF\u09A4\u09CD\u09AF" : "Bangla Literature",
|
|
2868
|
+
iconKey: "book",
|
|
2869
|
+
colorClass: "from-amber-500 to-orange-600"
|
|
2870
|
+
},
|
|
2871
|
+
{
|
|
2872
|
+
id: "bcs-english",
|
|
2873
|
+
name: isBn ? "\u0987\u0982\u09B0\u09C7\u099C\u09BF \u09AD\u09BE\u09B7\u09BE \u0993 \u09B8\u09BE\u09B9\u09BF\u09A4\u09CD\u09AF" : "English Language",
|
|
2874
|
+
iconKey: "language",
|
|
2875
|
+
colorClass: "from-violet-500 to-purple-600"
|
|
2876
|
+
},
|
|
2877
|
+
{
|
|
2878
|
+
id: "bcs-math-mental-ability",
|
|
2879
|
+
name: isBn ? "\u0997\u09BE\u09A3\u09BF\u09A4\u09BF\u0995 \u0993 \u09AE\u09BE\u09A8\u09B8\u09BF\u0995 \u09A6\u0995\u09CD\u09B7\u09A4\u09BE" : "Math & Mental Ability",
|
|
2880
|
+
iconKey: "brain",
|
|
2881
|
+
colorClass: "from-pink-500 to-rose-600"
|
|
2882
|
+
},
|
|
2883
|
+
{
|
|
2884
|
+
id: "bcs-general-science",
|
|
2885
|
+
name: isBn ? "\u09B8\u09BE\u09A7\u09BE\u09B0\u09A3 \u09AC\u09BF\u099C\u09CD\u099E\u09BE\u09A8 \u0993 \u09AA\u09CD\u09B0\u09AF\u09C1\u0995\u09CD\u09A4\u09BF" : "General Science",
|
|
2886
|
+
iconKey: "atom",
|
|
2887
|
+
colorClass: "from-teal-500 to-emerald-600"
|
|
2888
|
+
}
|
|
2889
|
+
];
|
|
2890
|
+
}
|
|
2891
|
+
return [
|
|
2892
|
+
{
|
|
2893
|
+
id: "hsc-physics",
|
|
2894
|
+
name: isBn ? "\u09AA\u09A6\u09BE\u09B0\u09CD\u09A5\u09AC\u09BF\u099C\u09CD\u099E\u09BE\u09A8" : "Physics",
|
|
2895
|
+
iconKey: "atom",
|
|
2896
|
+
colorClass: "from-blue-500 to-indigo-600"
|
|
2897
|
+
},
|
|
2898
|
+
{
|
|
2899
|
+
id: "hsc-chemistry",
|
|
2900
|
+
name: isBn ? "\u09B0\u09B8\u09BE\u09DF\u09A8" : "Chemistry",
|
|
2901
|
+
iconKey: "flask",
|
|
2902
|
+
colorClass: "from-emerald-500 to-teal-600"
|
|
2903
|
+
},
|
|
2904
|
+
{
|
|
2905
|
+
id: "hsc-higher-math",
|
|
2906
|
+
name: isBn ? "\u0989\u099A\u09CD\u099A\u09A4\u09B0 \u0997\u09A3\u09BF\u09A4" : "Higher Math",
|
|
2907
|
+
iconKey: "math",
|
|
2908
|
+
colorClass: "from-amber-500 to-orange-600"
|
|
2909
|
+
},
|
|
2910
|
+
{
|
|
2911
|
+
id: "hsc-biology",
|
|
2912
|
+
name: isBn ? "\u099C\u09C0\u09AC\u09AC\u09BF\u099C\u09CD\u099E\u09BE\u09A8" : "Biology",
|
|
2913
|
+
iconKey: "dna",
|
|
2914
|
+
colorClass: "from-rose-500 to-pink-600"
|
|
2915
|
+
},
|
|
2916
|
+
{
|
|
2917
|
+
id: "hsc-ict",
|
|
2918
|
+
name: isBn ? "\u09A4\u09A5\u09CD\u09AF \u0993 \u09AF\u09CB\u0997\u09BE\u09AF\u09CB\u0997 \u09AA\u09CD\u09B0\u09AF\u09C1\u0995\u09CD\u09A4\u09BF" : "ICT",
|
|
2919
|
+
iconKey: "device-laptop",
|
|
2920
|
+
colorClass: "from-cyan-500 to-sky-600"
|
|
2921
|
+
},
|
|
2922
|
+
{
|
|
2923
|
+
id: "hsc-bangla",
|
|
2924
|
+
name: isBn ? "\u09AC\u09BE\u0982\u09B2\u09BE" : "Bangla",
|
|
2925
|
+
iconKey: "book",
|
|
2926
|
+
colorClass: "from-violet-500 to-purple-600"
|
|
2927
|
+
}
|
|
2928
|
+
];
|
|
2929
|
+
}
|
|
2930
|
+
function calculateStreakFromDates(activityDates) {
|
|
2931
|
+
if (!activityDates || activityDates.length === 0) {
|
|
2932
|
+
return { streak: 0, isActiveToday: false };
|
|
2933
|
+
}
|
|
2934
|
+
const uniqueDateStrings = Array.from(
|
|
2935
|
+
new Set(
|
|
2936
|
+
activityDates.map((d) => {
|
|
2937
|
+
try {
|
|
2938
|
+
return new Date(d).toISOString().split("T")[0];
|
|
2939
|
+
} catch {
|
|
2940
|
+
return null;
|
|
2941
|
+
}
|
|
2942
|
+
}).filter((d) => Boolean(d))
|
|
2943
|
+
)
|
|
2944
|
+
).sort((a, b) => a > b ? -1 : 1);
|
|
2945
|
+
if (uniqueDateStrings.length === 0) {
|
|
2946
|
+
return { streak: 0, isActiveToday: false };
|
|
2947
|
+
}
|
|
2948
|
+
const todayStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2949
|
+
const yesterdayDate = /* @__PURE__ */ new Date();
|
|
2950
|
+
yesterdayDate.setDate(yesterdayDate.getDate() - 1);
|
|
2951
|
+
const yesterdayStr = yesterdayDate.toISOString().split("T")[0];
|
|
2952
|
+
const latestDate = uniqueDateStrings[0];
|
|
2953
|
+
if (!latestDate) {
|
|
2954
|
+
return { streak: 0, isActiveToday: false };
|
|
2955
|
+
}
|
|
2956
|
+
const isActiveToday = latestDate === todayStr;
|
|
2957
|
+
if (latestDate !== todayStr && latestDate !== yesterdayStr) {
|
|
2958
|
+
return { streak: 0, isActiveToday: false };
|
|
2959
|
+
}
|
|
2960
|
+
let streak = 0;
|
|
2961
|
+
const checkDate = new Date(latestDate);
|
|
2962
|
+
for (const dateStr of uniqueDateStrings) {
|
|
2963
|
+
const expectedStr = checkDate.toISOString().split("T")[0];
|
|
2964
|
+
if (dateStr === expectedStr) {
|
|
2965
|
+
streak += 1;
|
|
2966
|
+
checkDate.setDate(checkDate.getDate() - 1);
|
|
2967
|
+
} else {
|
|
2968
|
+
break;
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
return { streak, isActiveToday };
|
|
2972
|
+
}
|
|
2973
|
+
|
|
1551
2974
|
// src/logic/import-export.ts
|
|
1552
2975
|
var MAX_QUESTIONS = 200;
|
|
1553
2976
|
function parseFields(parts) {
|
|
@@ -1752,6 +3175,250 @@ function formatQuestionsToCsv(questions, packSubjectId, packChapterId, packTopic
|
|
|
1752
3175
|
${rows.join("\n")}`;
|
|
1753
3176
|
}
|
|
1754
3177
|
|
|
3178
|
+
// src/logic/subscription.ts
|
|
3179
|
+
var MANUAL_PAYMENT_CONFIG = {
|
|
3180
|
+
phoneNumber: "01323567688",
|
|
3181
|
+
accountType: "Personal",
|
|
3182
|
+
methods: ["BKASH", "ROCKET"]
|
|
3183
|
+
};
|
|
3184
|
+
var SUBSCRIPTION_PLANS = {
|
|
3185
|
+
FREE: {
|
|
3186
|
+
id: "FREE",
|
|
3187
|
+
tier: "FREE",
|
|
3188
|
+
titleKey: "upgrade.freeTitle",
|
|
3189
|
+
descKey: "upgrade.freeDesc",
|
|
3190
|
+
pricing: {
|
|
3191
|
+
monthly: 0,
|
|
3192
|
+
quarterly: 0,
|
|
3193
|
+
biannually: 0,
|
|
3194
|
+
annually: 0
|
|
3195
|
+
},
|
|
3196
|
+
aiQuotas: {
|
|
3197
|
+
monthly: { aiEvals: 0, aiQuestions: 0 },
|
|
3198
|
+
quarterly: { aiEvals: 0, aiQuestions: 0 },
|
|
3199
|
+
biannually: { aiEvals: 0, aiQuestions: 0 },
|
|
3200
|
+
annually: { aiEvals: 0, aiQuestions: 0 }
|
|
3201
|
+
}
|
|
3202
|
+
},
|
|
3203
|
+
PRO: {
|
|
3204
|
+
id: "PRO",
|
|
3205
|
+
tier: "PRO",
|
|
3206
|
+
titleKey: "upgrade.proTitle",
|
|
3207
|
+
descKey: "upgrade.proDesc",
|
|
3208
|
+
pricing: {
|
|
3209
|
+
monthly: 149,
|
|
3210
|
+
quarterly: 399,
|
|
3211
|
+
biannually: 699,
|
|
3212
|
+
annually: 999
|
|
3213
|
+
},
|
|
3214
|
+
aiQuotas: {
|
|
3215
|
+
monthly: { aiEvals: 0, aiQuestions: 0 },
|
|
3216
|
+
quarterly: { aiEvals: 0, aiQuestions: 0 },
|
|
3217
|
+
biannually: { aiEvals: 0, aiQuestions: 0 },
|
|
3218
|
+
annually: { aiEvals: 0, aiQuestions: 0 }
|
|
3219
|
+
}
|
|
3220
|
+
},
|
|
3221
|
+
PRO_AI: {
|
|
3222
|
+
id: "PRO_AI",
|
|
3223
|
+
tier: "PRO_AI",
|
|
3224
|
+
titleKey: "upgrade.aiTitle",
|
|
3225
|
+
descKey: "upgrade.aiDesc",
|
|
3226
|
+
pricing: {
|
|
3227
|
+
monthly: 349,
|
|
3228
|
+
quarterly: 899,
|
|
3229
|
+
biannually: 1599,
|
|
3230
|
+
annually: 2499
|
|
3231
|
+
},
|
|
3232
|
+
aiQuotas: {
|
|
3233
|
+
monthly: { aiEvals: 100, aiQuestions: 450 },
|
|
3234
|
+
quarterly: { aiEvals: 280, aiQuestions: 1100 },
|
|
3235
|
+
biannually: { aiEvals: 500, aiQuestions: 2e3 },
|
|
3236
|
+
annually: { aiEvals: 800, aiQuestions: 3e3 }
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
};
|
|
3240
|
+
var AI_EVAL_BOOSTER_PACKS = [
|
|
3241
|
+
{
|
|
3242
|
+
id: "eval_booster_starter",
|
|
3243
|
+
type: "AI_EVAL",
|
|
3244
|
+
amount: 40,
|
|
3245
|
+
price: 69,
|
|
3246
|
+
titleKey: "upgrade.boosters.evalStarterTitle",
|
|
3247
|
+
descKey: "upgrade.boosters.evalStarterDesc"
|
|
3248
|
+
},
|
|
3249
|
+
{
|
|
3250
|
+
id: "eval_booster_standard",
|
|
3251
|
+
type: "AI_EVAL",
|
|
3252
|
+
amount: 100,
|
|
3253
|
+
price: 159,
|
|
3254
|
+
titleKey: "upgrade.boosters.evalStandardTitle",
|
|
3255
|
+
descKey: "upgrade.boosters.evalStandardDesc",
|
|
3256
|
+
badgeKey: "upgrade.popular"
|
|
3257
|
+
},
|
|
3258
|
+
{
|
|
3259
|
+
id: "eval_booster_mega",
|
|
3260
|
+
type: "AI_EVAL",
|
|
3261
|
+
amount: 220,
|
|
3262
|
+
price: 349,
|
|
3263
|
+
titleKey: "upgrade.boosters.evalMegaTitle",
|
|
3264
|
+
descKey: "upgrade.boosters.evalMegaDesc",
|
|
3265
|
+
badgeKey: "upgrade.bestValue"
|
|
3266
|
+
}
|
|
3267
|
+
];
|
|
3268
|
+
var AI_QUESTION_BOOSTER_PACKS = [
|
|
3269
|
+
{
|
|
3270
|
+
id: "question_booster_drill",
|
|
3271
|
+
type: "AI_QUESTION",
|
|
3272
|
+
amount: 100,
|
|
3273
|
+
price: 39,
|
|
3274
|
+
titleKey: "upgrade.boosters.questionDrillTitle",
|
|
3275
|
+
descKey: "upgrade.boosters.questionDrillDesc"
|
|
3276
|
+
},
|
|
3277
|
+
{
|
|
3278
|
+
id: "question_booster_chapter",
|
|
3279
|
+
type: "AI_QUESTION",
|
|
3280
|
+
amount: 250,
|
|
3281
|
+
price: 99,
|
|
3282
|
+
titleKey: "upgrade.boosters.questionChapterTitle",
|
|
3283
|
+
descKey: "upgrade.boosters.questionChapterDesc",
|
|
3284
|
+
badgeKey: "upgrade.popular"
|
|
3285
|
+
},
|
|
3286
|
+
{
|
|
3287
|
+
id: "question_booster_exam",
|
|
3288
|
+
type: "AI_QUESTION",
|
|
3289
|
+
amount: 700,
|
|
3290
|
+
price: 249,
|
|
3291
|
+
titleKey: "upgrade.boosters.questionExamTitle",
|
|
3292
|
+
descKey: "upgrade.boosters.questionExamDesc",
|
|
3293
|
+
badgeKey: "upgrade.bestValue"
|
|
3294
|
+
}
|
|
3295
|
+
];
|
|
3296
|
+
function getCycleMultiplier(cycle) {
|
|
3297
|
+
switch (cycle) {
|
|
3298
|
+
case "quarterly":
|
|
3299
|
+
return 3;
|
|
3300
|
+
case "biannually":
|
|
3301
|
+
return 6;
|
|
3302
|
+
case "annually":
|
|
3303
|
+
return 12;
|
|
3304
|
+
case "monthly":
|
|
3305
|
+
default:
|
|
3306
|
+
return 1;
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
function getPlanOriginalPrice(tier, cycle) {
|
|
3310
|
+
const normalizedTier = tier === "AI_ANNUAL" || tier === "AI_MONTHLY" ? "PRO_AI" : tier;
|
|
3311
|
+
const plan = SUBSCRIPTION_PLANS[normalizedTier] || SUBSCRIPTION_PLANS.FREE;
|
|
3312
|
+
const monthlyPrice = plan.pricing.monthly ?? 0;
|
|
3313
|
+
return monthlyPrice * getCycleMultiplier(cycle);
|
|
3314
|
+
}
|
|
3315
|
+
function getPlanPrice(tier, cycle) {
|
|
3316
|
+
const normalizedTier = tier === "AI_ANNUAL" || tier === "AI_MONTHLY" ? "PRO_AI" : tier;
|
|
3317
|
+
const plan = SUBSCRIPTION_PLANS[normalizedTier] || SUBSCRIPTION_PLANS.FREE;
|
|
3318
|
+
return plan.pricing[cycle] ?? 0;
|
|
3319
|
+
}
|
|
3320
|
+
function getPlanMonthlyEquivalentPrice(tier, cycle) {
|
|
3321
|
+
const normalizedTier = tier === "AI_ANNUAL" || tier === "AI_MONTHLY" ? "PRO_AI" : tier;
|
|
3322
|
+
const plan = SUBSCRIPTION_PLANS[normalizedTier] || SUBSCRIPTION_PLANS.FREE;
|
|
3323
|
+
const cyclePrice = plan.pricing[cycle] ?? 0;
|
|
3324
|
+
const multiplier = getCycleMultiplier(cycle);
|
|
3325
|
+
return Math.round(cyclePrice / multiplier);
|
|
3326
|
+
}
|
|
3327
|
+
function getPlanDiscountPercent(cycle) {
|
|
3328
|
+
switch (cycle) {
|
|
3329
|
+
case "quarterly":
|
|
3330
|
+
return 10;
|
|
3331
|
+
case "biannually":
|
|
3332
|
+
return 22;
|
|
3333
|
+
case "annually":
|
|
3334
|
+
return 44;
|
|
3335
|
+
case "monthly":
|
|
3336
|
+
default:
|
|
3337
|
+
return 0;
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
function getPlanAiQuotas(tier, cycle) {
|
|
3341
|
+
const normalizedTier = tier === "AI_ANNUAL" || tier === "AI_MONTHLY" ? "PRO_AI" : tier;
|
|
3342
|
+
const plan = SUBSCRIPTION_PLANS[normalizedTier] || SUBSCRIPTION_PLANS.FREE;
|
|
3343
|
+
return plan.aiQuotas[cycle] ?? { aiEvals: 0, aiQuestions: 0 };
|
|
3344
|
+
}
|
|
3345
|
+
function isValidBangladeshiPhone(phone) {
|
|
3346
|
+
if (!phone) return false;
|
|
3347
|
+
const cleaned = phone.replace(/[\s\-()]/g, "");
|
|
3348
|
+
const regex = /^(?:\+?88)?01[3-9]\d{8}$/;
|
|
3349
|
+
return regex.test(cleaned);
|
|
3350
|
+
}
|
|
3351
|
+
function isValidTrxId(trxId) {
|
|
3352
|
+
if (!trxId) return false;
|
|
3353
|
+
const cleaned = trxId.trim();
|
|
3354
|
+
return cleaned.length >= 4 && cleaned.length <= 40;
|
|
3355
|
+
}
|
|
3356
|
+
function hasProAccess(tier, expiresAt) {
|
|
3357
|
+
if (!tier) return false;
|
|
3358
|
+
const upper = tier.toUpperCase();
|
|
3359
|
+
const isTierPro = upper === "PRO" || upper === "PRO_AI";
|
|
3360
|
+
if (!isTierPro) return false;
|
|
3361
|
+
if (!expiresAt) return true;
|
|
3362
|
+
const expTime = new Date(expiresAt).getTime();
|
|
3363
|
+
if (isNaN(expTime)) return true;
|
|
3364
|
+
return expTime > Date.now();
|
|
3365
|
+
}
|
|
3366
|
+
function hasAiTierAccess(tier, expiresAt) {
|
|
3367
|
+
if (!tier) return false;
|
|
3368
|
+
const upper = tier.toUpperCase();
|
|
3369
|
+
const isTierAi = upper === "PRO_AI";
|
|
3370
|
+
if (!isTierAi) return false;
|
|
3371
|
+
if (!expiresAt) return true;
|
|
3372
|
+
const expTime = new Date(expiresAt).getTime();
|
|
3373
|
+
if (isNaN(expTime)) return true;
|
|
3374
|
+
return expTime > Date.now();
|
|
3375
|
+
}
|
|
3376
|
+
var FREE_DAILY_PRACTICE_LIMIT = 30;
|
|
3377
|
+
function canExportPack(tier, expiresAt, isCreator) {
|
|
3378
|
+
return Boolean(isCreator || hasProAccess(tier, expiresAt));
|
|
3379
|
+
}
|
|
3380
|
+
function canAccessMistakeVault(tier, expiresAt, isCreator) {
|
|
3381
|
+
return Boolean(isCreator || hasProAccess(tier, expiresAt));
|
|
3382
|
+
}
|
|
3383
|
+
function canWatchContestSolution(tier, expiresAt, isCreator) {
|
|
3384
|
+
return Boolean(isCreator || hasProAccess(tier, expiresAt));
|
|
3385
|
+
}
|
|
3386
|
+
function shouldTrackPracticeProgress(tier, expiresAt, isCreator) {
|
|
3387
|
+
return Boolean(isCreator || hasProAccess(tier, expiresAt));
|
|
3388
|
+
}
|
|
3389
|
+
function getDailyPracticeLimit(tier, expiresAt) {
|
|
3390
|
+
if (hasProAccess(tier, expiresAt)) return null;
|
|
3391
|
+
return FREE_DAILY_PRACTICE_LIMIT;
|
|
3392
|
+
}
|
|
3393
|
+
function checkDailyPracticeLimit(dailyCount, lastPracticeDate, tier, expiresAt, todayStr) {
|
|
3394
|
+
if (hasProAccess(tier, expiresAt)) {
|
|
3395
|
+
return {
|
|
3396
|
+
allowed: true,
|
|
3397
|
+
remaining: Infinity,
|
|
3398
|
+
currentCount: dailyCount,
|
|
3399
|
+
isUnlimited: true
|
|
3400
|
+
};
|
|
3401
|
+
}
|
|
3402
|
+
const today = todayStr || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3403
|
+
const isSameDay = lastPracticeDate ? lastPracticeDate.startsWith(today) : false;
|
|
3404
|
+
const currentCount = isSameDay ? Number.isFinite(dailyCount) ? dailyCount : 0 : 0;
|
|
3405
|
+
const remaining = Math.max(0, FREE_DAILY_PRACTICE_LIMIT - currentCount);
|
|
3406
|
+
const allowed = currentCount < FREE_DAILY_PRACTICE_LIMIT;
|
|
3407
|
+
return {
|
|
3408
|
+
allowed,
|
|
3409
|
+
remaining,
|
|
3410
|
+
currentCount,
|
|
3411
|
+
isUnlimited: false
|
|
3412
|
+
};
|
|
3413
|
+
}
|
|
3414
|
+
function hasSufficientAiQuestionQuota(quota, requested) {
|
|
3415
|
+
if (requested <= 0) return false;
|
|
3416
|
+
return (quota ?? 0) >= requested;
|
|
3417
|
+
}
|
|
3418
|
+
function hasSufficientAiEvalQuota(quota) {
|
|
3419
|
+
return (quota ?? 0) > 0;
|
|
3420
|
+
}
|
|
3421
|
+
|
|
1755
3422
|
// src/navigation.ts
|
|
1756
3423
|
function getActiveTab(pathname) {
|
|
1757
3424
|
const normalized = pathname.toLowerCase();
|
|
@@ -1906,7 +3573,7 @@ function processLatexText(str) {
|
|
|
1906
3573
|
}
|
|
1907
3574
|
|
|
1908
3575
|
// src/utils/time.ts
|
|
1909
|
-
function convertToBangladeshTime(isoString) {
|
|
3576
|
+
function convertToBangladeshTime(isoString, locale = "en-GB") {
|
|
1910
3577
|
const options = {
|
|
1911
3578
|
year: "numeric",
|
|
1912
3579
|
month: "long",
|
|
@@ -1916,7 +3583,8 @@ function convertToBangladeshTime(isoString) {
|
|
|
1916
3583
|
hour12: true
|
|
1917
3584
|
};
|
|
1918
3585
|
const date = new Date(isoString);
|
|
1919
|
-
|
|
3586
|
+
const resolvedLocale = locale === "bn" ? "bn-BD" : locale;
|
|
3587
|
+
return date.toLocaleString(resolvedLocale, {
|
|
1920
3588
|
...options,
|
|
1921
3589
|
timeZone: "Asia/Dhaka"
|
|
1922
3590
|
});
|
|
@@ -2008,24 +3676,42 @@ function isLanguage(value) {
|
|
|
2008
3676
|
function isDifficulty(value) {
|
|
2009
3677
|
return ["Easy", "Medium", "Hard"].includes(value);
|
|
2010
3678
|
}
|
|
3679
|
+
function normalizeLevel(value) {
|
|
3680
|
+
if (!value) return "";
|
|
3681
|
+
const clean = value.trim().replace(/\s+/g, "+");
|
|
3682
|
+
const upper = clean.toUpperCase();
|
|
3683
|
+
if (upper === "SSC" || upper.includes("CLASS_9") || upper.includes("CLASS_10")) return "SSC";
|
|
3684
|
+
if (upper === "HSC" || upper === "ADMISSION" || upper === "HSC+ADMISSION" || upper.includes("CLASS_11") || upper.includes("CLASS_12")) return "HSC+Admission";
|
|
3685
|
+
if (upper === "BCS") return "BCS";
|
|
3686
|
+
return clean;
|
|
3687
|
+
}
|
|
2011
3688
|
export {
|
|
2012
3689
|
AIService,
|
|
3690
|
+
AI_EVAL_BOOSTER_PACKS,
|
|
3691
|
+
AI_QUESTION_BOOSTER_PACKS,
|
|
2013
3692
|
CLOTHES_COLORS,
|
|
2014
3693
|
COLOR_HUES,
|
|
2015
3694
|
ContestService,
|
|
2016
3695
|
CourseService,
|
|
2017
3696
|
CurriculumService,
|
|
3697
|
+
FREE_DAILY_PRACTICE_LIMIT,
|
|
2018
3698
|
HAIR_COLORS,
|
|
2019
3699
|
INITIAL_AVATAR_DATA,
|
|
3700
|
+
MANUAL_PAYMENT_CONFIG,
|
|
2020
3701
|
MediaService,
|
|
2021
3702
|
MediaUrlCache,
|
|
2022
3703
|
NewsService,
|
|
3704
|
+
NotificationService,
|
|
2023
3705
|
OrjokClient,
|
|
2024
3706
|
PackService,
|
|
2025
3707
|
ProgressService,
|
|
2026
3708
|
QuestionService,
|
|
2027
3709
|
RENDER_ORDER,
|
|
3710
|
+
SUBSCRIPTION_PLANS,
|
|
3711
|
+
SubscriptionService,
|
|
2028
3712
|
UserService,
|
|
3713
|
+
XP_REWARDS,
|
|
3714
|
+
XP_TIERS,
|
|
2029
3715
|
_resetMediaUrlWarning,
|
|
2030
3716
|
aggregateChapterMetrics,
|
|
2031
3717
|
aggregateSubjectMetrics,
|
|
@@ -2034,7 +3720,14 @@ export {
|
|
|
2034
3720
|
calculateAccuracy,
|
|
2035
3721
|
calculateDefaultExamMinutes,
|
|
2036
3722
|
calculateExamTime,
|
|
3723
|
+
calculateStreakFromDates,
|
|
2037
3724
|
calculateTimeLeft,
|
|
3725
|
+
calculateUnreadCount,
|
|
3726
|
+
calculateXpReward,
|
|
3727
|
+
canAccessMistakeVault,
|
|
3728
|
+
canExportPack,
|
|
3729
|
+
canWatchContestSolution,
|
|
3730
|
+
checkDailyPracticeLimit,
|
|
2038
3731
|
computeRatingDelta,
|
|
2039
3732
|
convertToBangladeshTime,
|
|
2040
3733
|
createOrjokClient,
|
|
@@ -2047,18 +3740,41 @@ export {
|
|
|
2047
3740
|
formatCurriculumName,
|
|
2048
3741
|
formatDate,
|
|
2049
3742
|
formatExamTime,
|
|
3743
|
+
formatLevelUpNotification,
|
|
2050
3744
|
formatQuestionsToCsv,
|
|
2051
3745
|
formatQuestionsToText,
|
|
3746
|
+
generateRandomAvatarConfig,
|
|
3747
|
+
generateRandomAvatarState,
|
|
2052
3748
|
getActiveTab,
|
|
2053
3749
|
getActualOptionFileName,
|
|
2054
3750
|
getAllTiers,
|
|
3751
|
+
getAllXpTiers,
|
|
2055
3752
|
getCanonicalPath,
|
|
3753
|
+
getCycleMultiplier,
|
|
3754
|
+
getDailyPracticeLimit,
|
|
2056
3755
|
getDhakaNow,
|
|
2057
3756
|
getEloProgress,
|
|
2058
3757
|
getEloTier,
|
|
3758
|
+
getLevelFromXp,
|
|
3759
|
+
getPlanAiQuotas,
|
|
3760
|
+
getPlanDiscountPercent,
|
|
3761
|
+
getPlanMonthlyEquivalentPrice,
|
|
3762
|
+
getPlanOriginalPrice,
|
|
3763
|
+
getPlanPrice,
|
|
3764
|
+
getPublicExamsByLevel,
|
|
2059
3765
|
getRatingForSubject,
|
|
3766
|
+
getRecommendedSubjectsForLevel,
|
|
2060
3767
|
getRelativeTime,
|
|
3768
|
+
getTimeOfDayGreeting,
|
|
3769
|
+
getXpForLevel,
|
|
3770
|
+
getXpProgress,
|
|
3771
|
+
getXpRequiredForNextLevel,
|
|
3772
|
+
getXpTier,
|
|
2061
3773
|
getYouTubeId,
|
|
3774
|
+
hasAiTierAccess,
|
|
3775
|
+
hasProAccess,
|
|
3776
|
+
hasSufficientAiEvalQuota,
|
|
3777
|
+
hasSufficientAiQuestionQuota,
|
|
2062
3778
|
isAuthGatedRoute,
|
|
2063
3779
|
isCorrectAnswer,
|
|
2064
3780
|
isDifficulty,
|
|
@@ -2068,21 +3784,30 @@ export {
|
|
|
2068
3784
|
isLanguageMatch,
|
|
2069
3785
|
isQuestionLevel,
|
|
2070
3786
|
isStorageKey,
|
|
3787
|
+
isValidBangladeshiPhone,
|
|
2071
3788
|
isValidMCQ,
|
|
3789
|
+
isValidTrxId,
|
|
2072
3790
|
languageOptions,
|
|
2073
3791
|
levelOptions,
|
|
3792
|
+
mapCognitoLevelToStandardLevel,
|
|
3793
|
+
mergeNotificationTimeline,
|
|
2074
3794
|
normalizeDifficulty,
|
|
3795
|
+
normalizeLevel,
|
|
3796
|
+
paginateWithAccumulator,
|
|
2075
3797
|
parseAvatarConfig,
|
|
3798
|
+
parseAvatarConfigWithDefaults,
|
|
2076
3799
|
parseCsvQuestions,
|
|
2077
3800
|
parseImageKeys,
|
|
2078
3801
|
parseTextQuestions,
|
|
2079
3802
|
processLatexText,
|
|
3803
|
+
publicExamOptions,
|
|
2080
3804
|
reorderObjectKeys,
|
|
2081
3805
|
resolveMediaUrl,
|
|
2082
3806
|
resolveMediaUrls,
|
|
2083
3807
|
sanitizeFileName,
|
|
2084
3808
|
scoreExam,
|
|
2085
3809
|
serializeAvatarConfig,
|
|
3810
|
+
shouldTrackPracticeProgress,
|
|
2086
3811
|
shuffle,
|
|
2087
3812
|
shuffleInPlace
|
|
2088
3813
|
};
|