@orjok/commons 1.0.2 → 1.1.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/chunk-DB3CMKPP.cjs +165 -0
- package/dist/chunk-DB3CMKPP.cjs.map +1 -0
- package/dist/chunk-ZDGYAVEC.js +165 -0
- package/dist/chunk-ZDGYAVEC.js.map +1 -0
- package/dist/hooks.cjs +492 -2
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +130 -2
- package/dist/hooks.d.ts +130 -2
- package/dist/hooks.js +491 -1
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +590 -224
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +86 -14
- package/dist/index.d.ts +86 -14
- package/dist/index.js +549 -183
- package/dist/index.js.map +1 -1
- package/dist/{client-Bm9aiyTx.d.ts → practice-Dh2Tv80G.d.ts} +36 -5
- package/dist/{client-BaOC79Nc.d.cts → practice-QnWtC5A7.d.cts} +36 -5
- package/dist/testing.cjs +10 -0
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +2 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +10 -0
- package/dist/testing.js.map +1 -1
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MediaUrlCache,
|
|
3
|
+
_resetMediaUrlWarning,
|
|
4
|
+
aggregateChapterMetrics,
|
|
5
|
+
aggregateSubjectMetrics,
|
|
6
|
+
aggregateTopicMetrics,
|
|
7
|
+
calculateAccuracy,
|
|
8
|
+
isDifficultyMatch,
|
|
9
|
+
isLanguageMatch,
|
|
10
|
+
isStorageKey,
|
|
11
|
+
isValidMCQ,
|
|
12
|
+
resolveMediaUrl,
|
|
13
|
+
resolveMediaUrls,
|
|
14
|
+
shuffle,
|
|
15
|
+
shuffleInPlace
|
|
16
|
+
} from "./chunk-ZDGYAVEC.js";
|
|
17
|
+
|
|
1
18
|
// src/services/user.service.ts
|
|
2
19
|
var UserService = class {
|
|
3
20
|
constructor(network) {
|
|
@@ -30,10 +47,10 @@ var UserService = class {
|
|
|
30
47
|
);
|
|
31
48
|
return result.data.updateUser;
|
|
32
49
|
}
|
|
33
|
-
async getQuestions(userId, nextToken) {
|
|
50
|
+
async getQuestions(userId, nextToken, limit = 10) {
|
|
34
51
|
const result = await this.network.query(
|
|
35
|
-
`query ListUserQuestions($owner: ID!, $nextToken: String, $sortDirection: ModelSortDirection) {
|
|
36
|
-
listQuestionObjectByOwnerAndCreatedAt(owner: $owner, sortDirection: $sortDirection, nextToken: $nextToken) {
|
|
52
|
+
`query ListUserQuestions($owner: ID!, $limit: Int, $nextToken: String, $sortDirection: ModelSortDirection) {
|
|
53
|
+
listQuestionObjectByOwnerAndCreatedAt(owner: $owner, limit: $limit, sortDirection: $sortDirection, nextToken: $nextToken) {
|
|
37
54
|
items {
|
|
38
55
|
id question imageUrl language level difficulty type voteCount
|
|
39
56
|
verificationStatus owner packId createdAt
|
|
@@ -43,14 +60,14 @@ var UserService = class {
|
|
|
43
60
|
nextToken
|
|
44
61
|
}
|
|
45
62
|
}`,
|
|
46
|
-
{ owner: userId, nextToken, sortDirection: "DESC" }
|
|
63
|
+
{ owner: userId, limit, nextToken, sortDirection: "DESC" }
|
|
47
64
|
);
|
|
48
65
|
return result.data.listQuestionObjectByOwnerAndCreatedAt;
|
|
49
66
|
}
|
|
50
|
-
async getPacks(userId, nextToken) {
|
|
67
|
+
async getPacks(userId, nextToken, limit = 10) {
|
|
51
68
|
const result = await this.network.query(
|
|
52
|
-
`query ListUserPacks($owner: ID!, $nextToken: String, $sortDirection: ModelSortDirection) {
|
|
53
|
-
listPackByOwnerAndCreatedAt(owner: $owner, sortDirection: $sortDirection, nextToken: $nextToken) {
|
|
69
|
+
`query ListUserPacks($owner: ID!, $limit: Int, $nextToken: String, $sortDirection: ModelSortDirection) {
|
|
70
|
+
listPackByOwnerAndCreatedAt(owner: $owner, limit: $limit, sortDirection: $sortDirection, nextToken: $nextToken) {
|
|
54
71
|
items {
|
|
55
72
|
id name language level difficulty questionCount automaticNumbering
|
|
56
73
|
owner subjectId chapterId topicId verificationStatus createdAt
|
|
@@ -59,7 +76,7 @@ var UserService = class {
|
|
|
59
76
|
nextToken
|
|
60
77
|
}
|
|
61
78
|
}`,
|
|
62
|
-
{ owner: userId, nextToken, sortDirection: "DESC" }
|
|
79
|
+
{ owner: userId, limit, nextToken, sortDirection: "DESC" }
|
|
63
80
|
);
|
|
64
81
|
return result.data.listPackByOwnerAndCreatedAt;
|
|
65
82
|
}
|
|
@@ -159,6 +176,23 @@ var QuestionService = class {
|
|
|
159
176
|
);
|
|
160
177
|
return result.data.listQuestionObjectByPackIdAndOrder.items[0]?.id ?? null;
|
|
161
178
|
}
|
|
179
|
+
async listByPack(packId, limit = 200, sortDirection = "ASC", nextToken) {
|
|
180
|
+
const result = await this.network.query(
|
|
181
|
+
`query ListQuestionsByPack($packId: ID!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {
|
|
182
|
+
listQuestionObjectByPackIdAndOrder(packId: $packId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
|
|
183
|
+
items {
|
|
184
|
+
id question answer explanation extra imageUrl language level difficulty type
|
|
185
|
+
markingInstructions voteCount owner packId subjectId chapterId topicId order createdAt
|
|
186
|
+
options { items { id content } }
|
|
187
|
+
tags { items { id tagId } }
|
|
188
|
+
}
|
|
189
|
+
nextToken
|
|
190
|
+
}
|
|
191
|
+
}`,
|
|
192
|
+
{ packId, sortDirection, limit, nextToken }
|
|
193
|
+
);
|
|
194
|
+
return result.data.listQuestionObjectByPackIdAndOrder;
|
|
195
|
+
}
|
|
162
196
|
async update(id, fields) {
|
|
163
197
|
const result = await this.network.mutate(
|
|
164
198
|
`mutation UpdateQuestion($input: UpdateQuestionObjectInput!) {
|
|
@@ -172,6 +206,41 @@ var QuestionService = class {
|
|
|
172
206
|
);
|
|
173
207
|
return result.data.updateQuestionObject;
|
|
174
208
|
}
|
|
209
|
+
async updateOption(id, content) {
|
|
210
|
+
const result = await this.network.mutate(
|
|
211
|
+
`mutation UpdateOption($input: UpdateOptionInput!) {
|
|
212
|
+
updateOption(input: $input) {
|
|
213
|
+
id
|
|
214
|
+
content
|
|
215
|
+
}
|
|
216
|
+
}`,
|
|
217
|
+
{ input: { id, content } }
|
|
218
|
+
);
|
|
219
|
+
return result.data.updateOption;
|
|
220
|
+
}
|
|
221
|
+
async createOption(input) {
|
|
222
|
+
const result = await this.network.mutate(
|
|
223
|
+
`mutation CreateOption($input: CreateOptionInput!) {
|
|
224
|
+
createOption(input: $input) {
|
|
225
|
+
id
|
|
226
|
+
content
|
|
227
|
+
}
|
|
228
|
+
}`,
|
|
229
|
+
{ input }
|
|
230
|
+
);
|
|
231
|
+
return result.data.createOption;
|
|
232
|
+
}
|
|
233
|
+
async deleteOption(id) {
|
|
234
|
+
const result = await this.network.mutate(
|
|
235
|
+
`mutation DeleteOption($input: DeleteOptionInput!) {
|
|
236
|
+
deleteOption(input: $input) {
|
|
237
|
+
id
|
|
238
|
+
}
|
|
239
|
+
}`,
|
|
240
|
+
{ input: { id } }
|
|
241
|
+
);
|
|
242
|
+
return result.data.deleteOption;
|
|
243
|
+
}
|
|
175
244
|
async delete(id) {
|
|
176
245
|
const result = await this.network.mutate(
|
|
177
246
|
`mutation DeleteQuestion($input: DeleteQuestionObjectInput!) {
|
|
@@ -239,7 +308,7 @@ var QuestionService = class {
|
|
|
239
308
|
listQuestionObjectBySubjectIdAndCreatedAt(subjectId: $subjectId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
|
|
240
309
|
items {
|
|
241
310
|
id question imageUrl language level difficulty type voteCount
|
|
242
|
-
verificationStatus owner packId createdAt
|
|
311
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
243
312
|
options { items { id content } }
|
|
244
313
|
}
|
|
245
314
|
nextToken
|
|
@@ -261,7 +330,7 @@ var QuestionService = class {
|
|
|
261
330
|
listQuestionObjectByChapterIdAndCreatedAt(chapterId: $chapterId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
|
|
262
331
|
items {
|
|
263
332
|
id question imageUrl language level difficulty type voteCount
|
|
264
|
-
verificationStatus owner packId createdAt
|
|
333
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
265
334
|
options { items { id content } }
|
|
266
335
|
}
|
|
267
336
|
nextToken
|
|
@@ -283,7 +352,7 @@ var QuestionService = class {
|
|
|
283
352
|
listQuestionObjectByTopicIdAndCreatedAt(topicId: $topicId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {
|
|
284
353
|
items {
|
|
285
354
|
id question imageUrl language level difficulty type voteCount
|
|
286
|
-
verificationStatus owner packId createdAt
|
|
355
|
+
verificationStatus owner packId subjectId chapterId topicId createdAt
|
|
287
356
|
options { items { id content } }
|
|
288
357
|
}
|
|
289
358
|
nextToken
|
|
@@ -368,7 +437,22 @@ var PackService = class {
|
|
|
368
437
|
}`,
|
|
369
438
|
{ id }
|
|
370
439
|
);
|
|
371
|
-
|
|
440
|
+
const pack = result.data.getPack;
|
|
441
|
+
if (pack && pack.questions && Array.isArray(pack.questions.items)) {
|
|
442
|
+
pack.questions.items.sort((a, b) => {
|
|
443
|
+
const orderA = typeof a.order === "number" ? a.order : a.order ? Number(a.order) : 0;
|
|
444
|
+
const orderB = typeof b.order === "number" ? b.order : b.order ? Number(b.order) : 0;
|
|
445
|
+
if (orderA !== orderB) {
|
|
446
|
+
if (orderA === 0) return 1;
|
|
447
|
+
if (orderB === 0) return -1;
|
|
448
|
+
return orderA - orderB;
|
|
449
|
+
}
|
|
450
|
+
const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
|
451
|
+
const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
|
452
|
+
return timeA - timeB;
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
return pack;
|
|
372
456
|
}
|
|
373
457
|
async update(id, fields) {
|
|
374
458
|
const result = await this.network.mutate(
|
|
@@ -954,13 +1038,34 @@ var MediaService = class {
|
|
|
954
1038
|
this.storage = storage;
|
|
955
1039
|
}
|
|
956
1040
|
async uploadImage(questionId, image) {
|
|
957
|
-
const
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1041
|
+
const payloadLength = image ? image.length : 0;
|
|
1042
|
+
console.log(`[MediaService.uploadImage] Initiating upload mutation: questionId="${questionId}", payloadLength=${payloadLength} chars`);
|
|
1043
|
+
try {
|
|
1044
|
+
const result = await this.network.mutate(
|
|
1045
|
+
`mutation UploadImage($questionId: ID!, $image: String!) {
|
|
1046
|
+
uploadImage(questionId: $questionId, image: $image) { status imageUrl }
|
|
1047
|
+
}`,
|
|
1048
|
+
{ questionId, image }
|
|
1049
|
+
);
|
|
1050
|
+
if (result.errors && result.errors.length > 0) {
|
|
1051
|
+
console.error(
|
|
1052
|
+
`[MediaService.uploadImage] GraphQL errors returned for questionId "${questionId}":`,
|
|
1053
|
+
JSON.stringify(result.errors, null, 2)
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
const response = result.data?.uploadImage;
|
|
1057
|
+
if (!response) {
|
|
1058
|
+
console.error(`[MediaService.uploadImage] No uploadImage object returned in GraphQL response for questionId "${questionId}". Full result:`, result);
|
|
1059
|
+
} else if (response.status === "Error" || response.status === "error" || !response.imageUrl) {
|
|
1060
|
+
console.error(`[MediaService.uploadImage] Backend returned error status or missing imageUrl for questionId "${questionId}":`, response);
|
|
1061
|
+
} else {
|
|
1062
|
+
console.log(`[MediaService.uploadImage] Upload mutation success for questionId "${questionId}": imageUrl="${response.imageUrl}"`);
|
|
1063
|
+
}
|
|
1064
|
+
return response;
|
|
1065
|
+
} catch (err) {
|
|
1066
|
+
console.error(`[MediaService.uploadImage] Network/GraphQL mutation failed for questionId "${questionId}":`, err);
|
|
1067
|
+
throw err;
|
|
1068
|
+
}
|
|
964
1069
|
}
|
|
965
1070
|
async deleteImage(imageUrl) {
|
|
966
1071
|
const result = await this.network.mutate(
|
|
@@ -1023,68 +1128,100 @@ var ProgressService = class {
|
|
|
1023
1128
|
this.network = network;
|
|
1024
1129
|
}
|
|
1025
1130
|
async track(input) {
|
|
1131
|
+
const variables = {
|
|
1132
|
+
questionId: input.questionId,
|
|
1133
|
+
isCorrect: input.isCorrect,
|
|
1134
|
+
subjectId: input.subjectId ?? null,
|
|
1135
|
+
chapterId: input.chapterId ?? null,
|
|
1136
|
+
topicId: input.topicId ?? null
|
|
1137
|
+
};
|
|
1026
1138
|
const result = await this.network.mutate(
|
|
1027
1139
|
`mutation TrackProgress($questionId: ID!, $isCorrect: Boolean!, $subjectId: ID, $chapterId: ID, $topicId: ID) {
|
|
1028
1140
|
trackPracticeProgress(questionId: $questionId, isCorrect: $isCorrect, subjectId: $subjectId, chapterId: $chapterId, topicId: $topicId) { status }
|
|
1029
1141
|
}`,
|
|
1030
|
-
|
|
1142
|
+
variables
|
|
1031
1143
|
);
|
|
1032
|
-
return result.data
|
|
1144
|
+
return result.data?.trackPracticeProgress || { status: "success" };
|
|
1033
1145
|
}
|
|
1034
1146
|
async getSubjectProgress(userId) {
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1147
|
+
try {
|
|
1148
|
+
const result = await this.network.query(
|
|
1149
|
+
`query ListSubjectProgress($userId: String!) {
|
|
1150
|
+
listUserSubjectProgressByUserIdAndSubjectId(userId: $userId) {
|
|
1151
|
+
items { id userId subjectId practicedCount mistakeCount correctedCount accuracyScore }
|
|
1152
|
+
}
|
|
1153
|
+
}`,
|
|
1154
|
+
{ userId }
|
|
1155
|
+
);
|
|
1156
|
+
return result.data?.listUserSubjectProgressByUserIdAndSubjectId?.items || result.data?.listUserSubjectProgressesByUserIdAndSubjectId?.items || [];
|
|
1157
|
+
} catch (e) {
|
|
1158
|
+
console.error("Error fetching subject progress:", e);
|
|
1159
|
+
return [];
|
|
1160
|
+
}
|
|
1044
1161
|
}
|
|
1045
1162
|
async getChapterProgress(userId) {
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1163
|
+
try {
|
|
1164
|
+
const result = await this.network.query(
|
|
1165
|
+
`query ListChapterProgress($userId: String!) {
|
|
1166
|
+
listUserChapterProgressByUserIdAndChapterId(userId: $userId) {
|
|
1167
|
+
items { id userId chapterId practicedCount mistakeCount correctedCount accuracyScore }
|
|
1168
|
+
}
|
|
1169
|
+
}`,
|
|
1170
|
+
{ userId }
|
|
1171
|
+
);
|
|
1172
|
+
return result.data?.listUserChapterProgressByUserIdAndChapterId?.items || result.data?.listUserChapterProgressesByUserIdAndChapterId?.items || [];
|
|
1173
|
+
} catch (e) {
|
|
1174
|
+
console.error("Error fetching chapter progress:", e);
|
|
1175
|
+
return [];
|
|
1176
|
+
}
|
|
1055
1177
|
}
|
|
1056
1178
|
async getTopicProgress(userId) {
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1179
|
+
try {
|
|
1180
|
+
const result = await this.network.query(
|
|
1181
|
+
`query ListTopicProgress($userId: String!) {
|
|
1182
|
+
listUserTopicProgressByUserIdAndTopicId(userId: $userId) {
|
|
1183
|
+
items { id userId topicId practicedCount mistakeCount correctedCount accuracyScore }
|
|
1184
|
+
}
|
|
1185
|
+
}`,
|
|
1186
|
+
{ userId }
|
|
1187
|
+
);
|
|
1188
|
+
return result.data?.listUserTopicProgressByUserIdAndTopicId?.items || result.data?.listUserTopicProgressesByUserIdAndTopicId?.items || [];
|
|
1189
|
+
} catch (e) {
|
|
1190
|
+
console.error("Error fetching topic progress:", e);
|
|
1191
|
+
return [];
|
|
1192
|
+
}
|
|
1066
1193
|
}
|
|
1067
1194
|
async getTopicMetrics(userId) {
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1195
|
+
try {
|
|
1196
|
+
const result = await this.network.query(
|
|
1197
|
+
`query ListTopicMetrics($filter: ModelUserTopicMetricFilterInput) {
|
|
1198
|
+
listUserTopicMetrics(filter: $filter) {
|
|
1199
|
+
items { id userId topicId totalAttempted totalCorrect accuracy }
|
|
1200
|
+
}
|
|
1201
|
+
}`,
|
|
1202
|
+
{ filter: { userId: { eq: userId } } }
|
|
1203
|
+
);
|
|
1204
|
+
return result.data?.listUserTopicMetrics?.items || [];
|
|
1205
|
+
} catch (e) {
|
|
1206
|
+
console.error("Error fetching topic metrics:", e);
|
|
1207
|
+
return [];
|
|
1208
|
+
}
|
|
1077
1209
|
}
|
|
1078
1210
|
async getMistakenQuestions(userId) {
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1211
|
+
try {
|
|
1212
|
+
const result = await this.network.query(
|
|
1213
|
+
`query ListMistakes($userId: String!, $filter: ModelQuestionTrackingFilterInput) {
|
|
1214
|
+
listQuestionTrackingByUserIdAndQuestionId(userId: $userId, filter: $filter) {
|
|
1215
|
+
items { id userId questionId subjectId chapterId topicId status attempts }
|
|
1216
|
+
}
|
|
1217
|
+
}`,
|
|
1218
|
+
{ userId, filter: { status: { eq: "MISTAKE" } } }
|
|
1219
|
+
);
|
|
1220
|
+
return result.data?.listQuestionTrackingByUserIdAndQuestionId?.items || result.data?.listQuestionTrackingsByUserIdAndQuestionId?.items || result.data?.listQuestionTrackings?.items || [];
|
|
1221
|
+
} catch (e) {
|
|
1222
|
+
console.error("Error fetching mistaken questions:", e);
|
|
1223
|
+
return [];
|
|
1224
|
+
}
|
|
1088
1225
|
}
|
|
1089
1226
|
};
|
|
1090
1227
|
|
|
@@ -1147,6 +1284,7 @@ var NewsService = class {
|
|
|
1147
1284
|
var OrjokClient = class {
|
|
1148
1285
|
constructor(config) {
|
|
1149
1286
|
this.auth = config.authProvider;
|
|
1287
|
+
this.storage = config.storageProvider;
|
|
1150
1288
|
this.users = new UserService(config.networkProvider);
|
|
1151
1289
|
this.questions = new QuestionService(config.networkProvider);
|
|
1152
1290
|
this.packs = new PackService(config.networkProvider);
|
|
@@ -1163,6 +1301,118 @@ function createOrjokClient(config) {
|
|
|
1163
1301
|
return new OrjokClient(config);
|
|
1164
1302
|
}
|
|
1165
1303
|
|
|
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
|
+
// src/logic/avatar.ts
|
|
1322
|
+
var HAIR_COLORS = [
|
|
1323
|
+
{ name: "Original", filter: "none", hex: "#e2e8f0", brightness: 1 },
|
|
1324
|
+
{ name: "White", filter: "grayscale(100%) brightness(500%)", hex: "#ffffff", brightness: 2 },
|
|
1325
|
+
{ name: "Black", filter: "grayscale(100%) brightness(40%)", hex: "#1a202c", brightness: 0.15 },
|
|
1326
|
+
{ name: "Brown", filter: "sepia(100%) saturate(300%) hue-rotate(330deg) brightness(80%)", hex: "#7b3f00", brightness: 0.35 },
|
|
1327
|
+
{ name: "Blonde", filter: "sepia(100%) saturate(400%) hue-rotate(20deg) brightness(130%)", hex: "#e6c27a", brightness: 0.8 },
|
|
1328
|
+
{ name: "Red", filter: "sepia(100%) saturate(500%) hue-rotate(320deg) brightness(90%)", hex: "#9b2c2c", brightness: 0.4 },
|
|
1329
|
+
{ name: "Blue", filter: "sepia(100%) saturate(500%) hue-rotate(180deg) brightness(90%)", hex: "#2b6cb0", brightness: 0.4 },
|
|
1330
|
+
{ name: "Green", filter: "sepia(100%) saturate(400%) hue-rotate(80deg) brightness(90%)", hex: "#2f855a", brightness: 0.45 }
|
|
1331
|
+
];
|
|
1332
|
+
var CLOTHES_COLORS = [
|
|
1333
|
+
{ name: "Original", filter: "none", hex: "#e2e8f0", brightness: 1 },
|
|
1334
|
+
{ name: "White", filter: "grayscale(100%) brightness(500%)", hex: "#ffffff", brightness: 2 },
|
|
1335
|
+
{ name: "Black", filter: "grayscale(100%) brightness(30%)", hex: "#1a202c", brightness: 0.12 },
|
|
1336
|
+
{ name: "Red", filter: "sepia(100%) saturate(500%) hue-rotate(320deg)", hex: "#c53030", brightness: 0.45 },
|
|
1337
|
+
{ name: "Orange", filter: "sepia(100%) saturate(500%) hue-rotate(350deg)", hex: "#dd6b20", brightness: 0.55 },
|
|
1338
|
+
{ name: "Yellow", filter: "sepia(100%) saturate(500%) hue-rotate(20deg) brightness(120%)", hex: "#d69e2e", brightness: 0.8 },
|
|
1339
|
+
{ name: "Green", filter: "sepia(100%) saturate(500%) hue-rotate(80deg)", hex: "#38a169", brightness: 0.55 },
|
|
1340
|
+
{ name: "Blue", filter: "sepia(100%) saturate(500%) hue-rotate(180deg)", hex: "#3182ce", brightness: 0.45 },
|
|
1341
|
+
{ name: "Purple", filter: "sepia(100%) saturate(500%) hue-rotate(240deg)", hex: "#805ad5", brightness: 0.45 },
|
|
1342
|
+
{ name: "Pink", filter: "sepia(100%) saturate(400%) hue-rotate(290deg)", hex: "#d53f8c", brightness: 0.55 }
|
|
1343
|
+
];
|
|
1344
|
+
var COLOR_HUES = {
|
|
1345
|
+
Original: 0,
|
|
1346
|
+
Red: 0,
|
|
1347
|
+
Orange: 24,
|
|
1348
|
+
Yellow: 45,
|
|
1349
|
+
Green: 140,
|
|
1350
|
+
Blue: 210,
|
|
1351
|
+
Purple: 270,
|
|
1352
|
+
Pink: 320,
|
|
1353
|
+
Brown: 25,
|
|
1354
|
+
Blonde: 45
|
|
1355
|
+
};
|
|
1356
|
+
var RENDER_ORDER = [
|
|
1357
|
+
"Background",
|
|
1358
|
+
"Hair Back",
|
|
1359
|
+
"Clothes Back",
|
|
1360
|
+
"Skin Color Body",
|
|
1361
|
+
"Clothes Front",
|
|
1362
|
+
"Skin Color Head",
|
|
1363
|
+
"Accessories",
|
|
1364
|
+
"Facial Expression",
|
|
1365
|
+
"Beard",
|
|
1366
|
+
"Hair Front",
|
|
1367
|
+
"Eyewear",
|
|
1368
|
+
"Headwears"
|
|
1369
|
+
];
|
|
1370
|
+
var INITIAL_AVATAR_DATA = {
|
|
1371
|
+
Background: [],
|
|
1372
|
+
Hair: [],
|
|
1373
|
+
"Skin Color": [],
|
|
1374
|
+
Clothes: [],
|
|
1375
|
+
Accessories: [],
|
|
1376
|
+
Headwears: [],
|
|
1377
|
+
Eyewear: [],
|
|
1378
|
+
"Facial Expression": [],
|
|
1379
|
+
Beard: []
|
|
1380
|
+
};
|
|
1381
|
+
function getActualOptionFileName(category, baseName, config, selections) {
|
|
1382
|
+
if (baseName === "None") return null;
|
|
1383
|
+
const items = config[category] || [];
|
|
1384
|
+
const matched = items.filter((item) => item.base_name === baseName);
|
|
1385
|
+
if (matched.length === 0) return null;
|
|
1386
|
+
for (const item of matched) {
|
|
1387
|
+
if (item.conditions && item.conditions.length > 0) {
|
|
1388
|
+
const allMet = item.conditions.every((cond) => {
|
|
1389
|
+
return Object.values(selections).some(
|
|
1390
|
+
(sel) => sel && (sel.includes(cond) || cond.includes(sel))
|
|
1391
|
+
);
|
|
1392
|
+
});
|
|
1393
|
+
if (allMet) return item;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
return matched.find((item) => !item.conditions || item.conditions.length === 0) || matched[0] || null;
|
|
1397
|
+
}
|
|
1398
|
+
function parseAvatarConfig(configStr) {
|
|
1399
|
+
if (!configStr) {
|
|
1400
|
+
return { selections: {}, colors: {} };
|
|
1401
|
+
}
|
|
1402
|
+
try {
|
|
1403
|
+
const parsed = JSON.parse(configStr);
|
|
1404
|
+
return {
|
|
1405
|
+
selections: parsed.selections || {},
|
|
1406
|
+
colors: parsed.colors || {}
|
|
1407
|
+
};
|
|
1408
|
+
} catch {
|
|
1409
|
+
return { selections: {}, colors: {} };
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
function serializeAvatarConfig(state) {
|
|
1413
|
+
return JSON.stringify(state);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1166
1416
|
// src/logic/elo.ts
|
|
1167
1417
|
var TIERS = [
|
|
1168
1418
|
{ name: "Iron", minRating: 0, maxRating: 799, color: "#6B7280" },
|
|
@@ -1263,6 +1513,40 @@ function detectQuestionType(option2, option3, option4) {
|
|
|
1263
1513
|
}
|
|
1264
1514
|
return "MCQ";
|
|
1265
1515
|
}
|
|
1516
|
+
function isCorrectAnswer(selected, correct) {
|
|
1517
|
+
if (!selected || !correct) return false;
|
|
1518
|
+
const selTrim = String(selected).trim();
|
|
1519
|
+
const corrTrim = String(correct).trim();
|
|
1520
|
+
if (selTrim === corrTrim) return true;
|
|
1521
|
+
const selNum = Number(selTrim);
|
|
1522
|
+
const corrNum = Number(corrTrim);
|
|
1523
|
+
if (!isNaN(selNum) && !isNaN(corrNum) && selNum === corrNum) {
|
|
1524
|
+
return true;
|
|
1525
|
+
}
|
|
1526
|
+
return false;
|
|
1527
|
+
}
|
|
1528
|
+
function calculateDefaultExamMinutes(mode, questions) {
|
|
1529
|
+
const mcqCount = questions.filter((q) => q.type !== "WRITTEN").length;
|
|
1530
|
+
const writtenCount = questions.filter((q) => q.type === "WRITTEN").length;
|
|
1531
|
+
if (mode === "MCQ") return Math.max(1, mcqCount * 1);
|
|
1532
|
+
if (mode === "WRITTEN") return Math.max(1, writtenCount * 10);
|
|
1533
|
+
return Math.max(1, mcqCount * 1 + writtenCount * 10);
|
|
1534
|
+
}
|
|
1535
|
+
function reorderObjectKeys(obj, oldIndex, newIndex, length) {
|
|
1536
|
+
const arr = [];
|
|
1537
|
+
for (let i = 0; i < length; i++) {
|
|
1538
|
+
arr.push(obj[i]);
|
|
1539
|
+
}
|
|
1540
|
+
const [movedItem] = arr.splice(oldIndex, 1);
|
|
1541
|
+
arr.splice(newIndex, 0, movedItem);
|
|
1542
|
+
const newObj = {};
|
|
1543
|
+
arr.forEach((item, idx) => {
|
|
1544
|
+
if (item !== void 0) {
|
|
1545
|
+
newObj[idx] = item;
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
return newObj;
|
|
1549
|
+
}
|
|
1266
1550
|
|
|
1267
1551
|
// src/logic/import-export.ts
|
|
1268
1552
|
var MAX_QUESTIONS = 200;
|
|
@@ -1468,126 +1752,37 @@ function formatQuestionsToCsv(questions, packSubjectId, packChapterId, packTopic
|
|
|
1468
1752
|
${rows.join("\n")}`;
|
|
1469
1753
|
}
|
|
1470
1754
|
|
|
1471
|
-
// src/
|
|
1472
|
-
function
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
constructor(ttlMs = 50 * 60 * 1e3) {
|
|
1477
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
1478
|
-
this.ttlMs = ttlMs;
|
|
1479
|
-
}
|
|
1480
|
-
get(key) {
|
|
1481
|
-
const entry = this.cache.get(key);
|
|
1482
|
-
if (!entry) return void 0;
|
|
1483
|
-
if (Date.now() >= entry.expiresAt) {
|
|
1484
|
-
this.cache.delete(key);
|
|
1485
|
-
return void 0;
|
|
1486
|
-
}
|
|
1487
|
-
return entry.url;
|
|
1755
|
+
// src/navigation.ts
|
|
1756
|
+
function getActiveTab(pathname) {
|
|
1757
|
+
const normalized = pathname.toLowerCase();
|
|
1758
|
+
if (normalized.startsWith("/contests") || normalized.startsWith("/contest")) {
|
|
1759
|
+
return "contests";
|
|
1488
1760
|
}
|
|
1489
|
-
|
|
1490
|
-
|
|
1761
|
+
if (normalized.startsWith("/courses") || normalized.startsWith("/course") || normalized.startsWith("/exams")) {
|
|
1762
|
+
return "courses";
|
|
1491
1763
|
}
|
|
1492
|
-
|
|
1493
|
-
|
|
1764
|
+
if (normalized.startsWith("/profile") || normalized.startsWith("/dashboard") || normalized.startsWith("/edit-profile")) {
|
|
1765
|
+
return "profile";
|
|
1494
1766
|
}
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
async function resolveMediaUrl(value, storage, cache) {
|
|
1498
|
-
if (!value || value.length === 0) return null;
|
|
1499
|
-
if (!isStorageKey(value)) return value;
|
|
1500
|
-
if (cache) {
|
|
1501
|
-
const cached = cache.get(value);
|
|
1502
|
-
if (cached) return cached;
|
|
1503
|
-
}
|
|
1504
|
-
if (!storage) {
|
|
1505
|
-
if (!warnedNoStorage) {
|
|
1506
|
-
warnedNoStorage = true;
|
|
1507
|
-
console.warn("@orjok/commons: resolveMediaUrl called without a StorageProvider \u2014 returning raw key");
|
|
1508
|
-
}
|
|
1509
|
-
return value;
|
|
1767
|
+
if (normalized.startsWith("/learn") || normalized.startsWith("/practice") || normalized.startsWith("/questions") || normalized.startsWith("/question") || normalized.startsWith("/bank") || normalized.startsWith("/question-bank") || normalized.startsWith("/packs") || normalized.startsWith("/pack") || normalized.startsWith("/taxonomy") || normalized.startsWith("/explore")) {
|
|
1768
|
+
return "learn";
|
|
1510
1769
|
}
|
|
1511
|
-
|
|
1512
|
-
cache?.set(value, url);
|
|
1513
|
-
return url;
|
|
1514
|
-
}
|
|
1515
|
-
async function resolveMediaUrls(obj, fields, storage, cache) {
|
|
1516
|
-
const copy = { ...obj };
|
|
1517
|
-
await Promise.all(
|
|
1518
|
-
fields.map(async (field) => {
|
|
1519
|
-
const val = copy[field];
|
|
1520
|
-
if (typeof val === "string") {
|
|
1521
|
-
copy[field] = await resolveMediaUrl(val, storage, cache);
|
|
1522
|
-
}
|
|
1523
|
-
})
|
|
1524
|
-
);
|
|
1525
|
-
return copy;
|
|
1526
|
-
}
|
|
1527
|
-
function _resetMediaUrlWarning() {
|
|
1528
|
-
warnedNoStorage = false;
|
|
1770
|
+
return "home";
|
|
1529
1771
|
}
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
totalCorrected += p.correctedCount ?? 0;
|
|
1540
|
-
}
|
|
1541
|
-
const accuracy = totalPracticed > 0 ? Math.round((totalPracticed - totalMistakes) / totalPracticed * 100) : 0;
|
|
1542
|
-
return { totalPracticed, totalMistakes, totalCorrected, accuracy };
|
|
1543
|
-
}
|
|
1544
|
-
function aggregateChapterMetrics(progress) {
|
|
1545
|
-
let totalPracticed = 0;
|
|
1546
|
-
let totalMistakes = 0;
|
|
1547
|
-
let totalCorrected = 0;
|
|
1548
|
-
for (const p of progress) {
|
|
1549
|
-
totalPracticed += p.practicedCount ?? 0;
|
|
1550
|
-
totalMistakes += p.mistakeCount ?? 0;
|
|
1551
|
-
totalCorrected += p.correctedCount ?? 0;
|
|
1552
|
-
}
|
|
1553
|
-
const accuracy = totalPracticed > 0 ? Math.round((totalPracticed - totalMistakes) / totalPracticed * 100) : 0;
|
|
1554
|
-
return { totalPracticed, totalMistakes, totalCorrected, accuracy };
|
|
1555
|
-
}
|
|
1556
|
-
function aggregateTopicMetrics(progress) {
|
|
1557
|
-
let totalPracticed = 0;
|
|
1558
|
-
let totalMistakes = 0;
|
|
1559
|
-
let totalCorrected = 0;
|
|
1560
|
-
for (const p of progress) {
|
|
1561
|
-
totalPracticed += p.practicedCount ?? 0;
|
|
1562
|
-
totalMistakes += p.mistakeCount ?? 0;
|
|
1563
|
-
totalCorrected += p.correctedCount ?? 0;
|
|
1564
|
-
}
|
|
1565
|
-
const accuracy = totalPracticed > 0 ? Math.round((totalPracticed - totalMistakes) / totalPracticed * 100) : 0;
|
|
1566
|
-
return { totalPracticed, totalMistakes, totalCorrected, accuracy };
|
|
1567
|
-
}
|
|
1568
|
-
function calculateAccuracy(practiced, mistakes) {
|
|
1569
|
-
if (practiced <= 0) return 0;
|
|
1570
|
-
return Math.round((practiced - mistakes) / practiced * 100);
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
// src/utils/shuffle.ts
|
|
1574
|
-
function shuffle(array) {
|
|
1575
|
-
const result = [...array];
|
|
1576
|
-
let currentIndex = result.length;
|
|
1577
|
-
while (currentIndex !== 0) {
|
|
1578
|
-
const randomIndex = Math.floor(Math.random() * currentIndex);
|
|
1579
|
-
currentIndex--;
|
|
1580
|
-
[result[currentIndex], result[randomIndex]] = [result[randomIndex], result[currentIndex]];
|
|
1581
|
-
}
|
|
1582
|
-
return result;
|
|
1772
|
+
function getCanonicalPath(pathname) {
|
|
1773
|
+
const normalized = pathname.toLowerCase();
|
|
1774
|
+
if (normalized === "/explore/questions") return "/learn/questions";
|
|
1775
|
+
if (normalized === "/explore/packs") return "/learn/packs";
|
|
1776
|
+
if (normalized === "/question-bank") return "/learn/bank";
|
|
1777
|
+
if (normalized === "/contest/all") return "/contests";
|
|
1778
|
+
if (normalized === "/dashboard") return "/profile";
|
|
1779
|
+
if (normalized === "/edit-profile") return "/profile/edit";
|
|
1780
|
+
return pathname;
|
|
1583
1781
|
}
|
|
1584
|
-
function
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
currentIndex--;
|
|
1589
|
-
[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
|
|
1590
|
-
}
|
|
1782
|
+
function isAuthGatedRoute(pathname) {
|
|
1783
|
+
const normalized = pathname.toLowerCase();
|
|
1784
|
+
const isProtectedPath = normalized.includes("/exam") || normalized.includes("/enroll") || normalized.startsWith("/profile") || normalized.startsWith("/dashboard") || normalized.startsWith("/edit-profile") || normalized.startsWith("/avatar-creator") || normalized.startsWith("/practice");
|
|
1785
|
+
return isProtectedPath;
|
|
1591
1786
|
}
|
|
1592
1787
|
|
|
1593
1788
|
// src/utils/bangla-numbers.ts
|
|
@@ -1600,15 +1795,51 @@ function engToBanglaNumber(num) {
|
|
|
1600
1795
|
}
|
|
1601
1796
|
|
|
1602
1797
|
// src/utils/format-curriculum.ts
|
|
1603
|
-
function
|
|
1604
|
-
if (
|
|
1798
|
+
function extractBilingualName(name, language) {
|
|
1799
|
+
if (!name) return "";
|
|
1800
|
+
const trimmed = name.trim();
|
|
1801
|
+
const match = trimmed.match(/^(.*?)\s*[\(\[(]\s*([^\)\])]+)\s*[\)\])]\s*$/);
|
|
1802
|
+
if (!match) {
|
|
1803
|
+
return trimmed;
|
|
1804
|
+
}
|
|
1805
|
+
const part1 = match[1]?.trim() || "";
|
|
1806
|
+
const part2 = match[2]?.trim() || "";
|
|
1807
|
+
if (!part1 && !part2) return trimmed;
|
|
1808
|
+
if (!part1) return part2;
|
|
1809
|
+
if (!part2) return part1;
|
|
1810
|
+
const hasBengali = (text) => /[\u0980-\u09FF]/.test(text);
|
|
1811
|
+
const isPart1Bengali = hasBengali(part1);
|
|
1812
|
+
const isPart2Bengali = hasBengali(part2);
|
|
1813
|
+
let banglaPart = part1;
|
|
1814
|
+
let englishPart = part2;
|
|
1815
|
+
if (!isPart1Bengali && isPart2Bengali) {
|
|
1816
|
+
banglaPart = part2;
|
|
1817
|
+
englishPart = part1;
|
|
1818
|
+
} else {
|
|
1819
|
+
banglaPart = part1;
|
|
1820
|
+
englishPart = part2;
|
|
1821
|
+
}
|
|
1822
|
+
const lang = language?.toLowerCase();
|
|
1823
|
+
if (lang === "en") {
|
|
1824
|
+
return englishPart || banglaPart || trimmed;
|
|
1825
|
+
}
|
|
1826
|
+
if (lang === "bn") {
|
|
1827
|
+
return banglaPart || englishPart || trimmed;
|
|
1828
|
+
}
|
|
1829
|
+
return trimmed;
|
|
1830
|
+
}
|
|
1831
|
+
function formatCurriculumName(idString, dbName, language) {
|
|
1832
|
+
if (dbName) {
|
|
1833
|
+
return extractBilingualName(dbName, language);
|
|
1834
|
+
}
|
|
1605
1835
|
if (!idString) return "";
|
|
1606
1836
|
const namePart = idString.split("::")[0] ?? "";
|
|
1607
1837
|
const clean = namePart.replace(/[-_]/g, " ");
|
|
1608
|
-
|
|
1838
|
+
const formatted = clean.split(/\s+/).map((word) => {
|
|
1609
1839
|
if (!word) return "";
|
|
1610
1840
|
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
1611
1841
|
}).join(" ");
|
|
1842
|
+
return extractBilingualName(formatted, language);
|
|
1612
1843
|
}
|
|
1613
1844
|
|
|
1614
1845
|
// src/utils/relative-time.ts
|
|
@@ -1673,11 +1904,119 @@ function processLatexText(str) {
|
|
|
1673
1904
|
result += remainingText.replace(/\\n/g, "\n");
|
|
1674
1905
|
return result;
|
|
1675
1906
|
}
|
|
1907
|
+
|
|
1908
|
+
// src/utils/time.ts
|
|
1909
|
+
function convertToBangladeshTime(isoString) {
|
|
1910
|
+
const options = {
|
|
1911
|
+
year: "numeric",
|
|
1912
|
+
month: "long",
|
|
1913
|
+
day: "numeric",
|
|
1914
|
+
hour: "numeric",
|
|
1915
|
+
minute: "numeric",
|
|
1916
|
+
hour12: true
|
|
1917
|
+
};
|
|
1918
|
+
const date = new Date(isoString);
|
|
1919
|
+
return date.toLocaleString("en-GB", {
|
|
1920
|
+
...options,
|
|
1921
|
+
timeZone: "Asia/Dhaka"
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1924
|
+
function getDhakaNow() {
|
|
1925
|
+
const now = /* @__PURE__ */ new Date();
|
|
1926
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
1927
|
+
timeZone: "Asia/Dhaka",
|
|
1928
|
+
year: "numeric",
|
|
1929
|
+
month: "2-digit",
|
|
1930
|
+
day: "2-digit",
|
|
1931
|
+
hour: "2-digit",
|
|
1932
|
+
minute: "2-digit",
|
|
1933
|
+
second: "2-digit",
|
|
1934
|
+
hour12: false
|
|
1935
|
+
}).formatToParts(now);
|
|
1936
|
+
const get = (type) => parts.find((p) => p.type === type)?.value ?? "00";
|
|
1937
|
+
return /* @__PURE__ */ new Date(`${get("year")}-${get("month")}-${get("day")}T${get("hour")}:${get("minute")}:${get("second")}`);
|
|
1938
|
+
}
|
|
1939
|
+
function calculateTimeLeft(endTime) {
|
|
1940
|
+
const target = new Date(endTime).getTime();
|
|
1941
|
+
const now = (/* @__PURE__ */ new Date()).getTime();
|
|
1942
|
+
const difference = target - now;
|
|
1943
|
+
if (difference <= 0 || isNaN(difference)) {
|
|
1944
|
+
return {
|
|
1945
|
+
days: 0,
|
|
1946
|
+
hours: 0,
|
|
1947
|
+
minutes: 0,
|
|
1948
|
+
seconds: 0,
|
|
1949
|
+
isEnded: true
|
|
1950
|
+
};
|
|
1951
|
+
}
|
|
1952
|
+
return {
|
|
1953
|
+
days: Math.floor(difference / (1e3 * 60 * 60 * 24)),
|
|
1954
|
+
hours: Math.floor(difference / (1e3 * 60 * 60) % 24),
|
|
1955
|
+
minutes: Math.floor(difference / 1e3 / 60 % 60),
|
|
1956
|
+
seconds: Math.floor(difference / 1e3 % 60),
|
|
1957
|
+
isEnded: false
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
function formatDate(date, locale = "en-GB") {
|
|
1961
|
+
const d = typeof date === "string" ? new Date(date) : date;
|
|
1962
|
+
return d.toLocaleDateString(locale, {
|
|
1963
|
+
day: "2-digit",
|
|
1964
|
+
month: "short",
|
|
1965
|
+
year: "numeric"
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
// src/utils/currency.ts
|
|
1970
|
+
function formatBDT(amount) {
|
|
1971
|
+
return amount.toLocaleString("en-US");
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
// src/utils/url.ts
|
|
1975
|
+
function getYouTubeId(url) {
|
|
1976
|
+
if (!url) return null;
|
|
1977
|
+
const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|[?&]v=)([^#&?]*).*/;
|
|
1978
|
+
const match = url.match(regExp);
|
|
1979
|
+
return match && match[2]?.length === 11 ? match[2] : null;
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
// src/utils/file.ts
|
|
1983
|
+
function sanitizeFileName(name) {
|
|
1984
|
+
return name.replace(/[\s,]+/g, "_");
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
// src/utils/guards.ts
|
|
1988
|
+
function isQuestionLevel(value) {
|
|
1989
|
+
return [
|
|
1990
|
+
"One",
|
|
1991
|
+
"Two",
|
|
1992
|
+
"Three",
|
|
1993
|
+
"Four",
|
|
1994
|
+
"Five",
|
|
1995
|
+
"Six",
|
|
1996
|
+
"Seven",
|
|
1997
|
+
"Eight",
|
|
1998
|
+
"SSC",
|
|
1999
|
+
"HSC",
|
|
2000
|
+
"Admission",
|
|
2001
|
+
"HSC+Admission",
|
|
2002
|
+
"BCS"
|
|
2003
|
+
].includes(value);
|
|
2004
|
+
}
|
|
2005
|
+
function isLanguage(value) {
|
|
2006
|
+
return ["Bangla", "English", "Any"].includes(value);
|
|
2007
|
+
}
|
|
2008
|
+
function isDifficulty(value) {
|
|
2009
|
+
return ["Easy", "Medium", "Hard"].includes(value);
|
|
2010
|
+
}
|
|
1676
2011
|
export {
|
|
1677
2012
|
AIService,
|
|
2013
|
+
CLOTHES_COLORS,
|
|
2014
|
+
COLOR_HUES,
|
|
1678
2015
|
ContestService,
|
|
1679
2016
|
CourseService,
|
|
1680
2017
|
CurriculumService,
|
|
2018
|
+
HAIR_COLORS,
|
|
2019
|
+
INITIAL_AVATAR_DATA,
|
|
1681
2020
|
MediaService,
|
|
1682
2021
|
MediaUrlCache,
|
|
1683
2022
|
NewsService,
|
|
@@ -1685,6 +2024,7 @@ export {
|
|
|
1685
2024
|
PackService,
|
|
1686
2025
|
ProgressService,
|
|
1687
2026
|
QuestionService,
|
|
2027
|
+
RENDER_ORDER,
|
|
1688
2028
|
UserService,
|
|
1689
2029
|
_resetMediaUrlWarning,
|
|
1690
2030
|
aggregateChapterMetrics,
|
|
@@ -1692,31 +2032,57 @@ export {
|
|
|
1692
2032
|
aggregateTopicMetrics,
|
|
1693
2033
|
buildImageAnswer,
|
|
1694
2034
|
calculateAccuracy,
|
|
2035
|
+
calculateDefaultExamMinutes,
|
|
1695
2036
|
calculateExamTime,
|
|
2037
|
+
calculateTimeLeft,
|
|
1696
2038
|
computeRatingDelta,
|
|
2039
|
+
convertToBangladeshTime,
|
|
1697
2040
|
createOrjokClient,
|
|
1698
2041
|
detectQuestionType,
|
|
2042
|
+
difficultyOptions,
|
|
1699
2043
|
engToBanglaNumber,
|
|
2044
|
+
extractBilingualName,
|
|
1700
2045
|
filterQuestionsByMode,
|
|
2046
|
+
formatBDT,
|
|
1701
2047
|
formatCurriculumName,
|
|
2048
|
+
formatDate,
|
|
1702
2049
|
formatExamTime,
|
|
1703
2050
|
formatQuestionsToCsv,
|
|
1704
2051
|
formatQuestionsToText,
|
|
2052
|
+
getActiveTab,
|
|
2053
|
+
getActualOptionFileName,
|
|
1705
2054
|
getAllTiers,
|
|
2055
|
+
getCanonicalPath,
|
|
2056
|
+
getDhakaNow,
|
|
1706
2057
|
getEloProgress,
|
|
1707
2058
|
getEloTier,
|
|
1708
2059
|
getRatingForSubject,
|
|
1709
2060
|
getRelativeTime,
|
|
2061
|
+
getYouTubeId,
|
|
2062
|
+
isAuthGatedRoute,
|
|
2063
|
+
isCorrectAnswer,
|
|
2064
|
+
isDifficulty,
|
|
2065
|
+
isDifficultyMatch,
|
|
1710
2066
|
isImageAnswer,
|
|
2067
|
+
isLanguage,
|
|
2068
|
+
isLanguageMatch,
|
|
2069
|
+
isQuestionLevel,
|
|
1711
2070
|
isStorageKey,
|
|
2071
|
+
isValidMCQ,
|
|
2072
|
+
languageOptions,
|
|
2073
|
+
levelOptions,
|
|
1712
2074
|
normalizeDifficulty,
|
|
2075
|
+
parseAvatarConfig,
|
|
1713
2076
|
parseCsvQuestions,
|
|
1714
2077
|
parseImageKeys,
|
|
1715
2078
|
parseTextQuestions,
|
|
1716
2079
|
processLatexText,
|
|
2080
|
+
reorderObjectKeys,
|
|
1717
2081
|
resolveMediaUrl,
|
|
1718
2082
|
resolveMediaUrls,
|
|
2083
|
+
sanitizeFileName,
|
|
1719
2084
|
scoreExam,
|
|
2085
|
+
serializeAvatarConfig,
|
|
1720
2086
|
shuffle,
|
|
1721
2087
|
shuffleInPlace
|
|
1722
2088
|
};
|