@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.
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/orjok/orjok/packages/commons/dist/index.cjs","../src/services/user.service.ts","../src/services/question.service.ts","../src/services/pack.service.ts","../src/services/contest.service.ts","../src/services/course.service.ts","../src/services/curriculum.service.ts","../src/services/media.service.ts","../src/services/progress.service.ts","../src/services/ai.service.ts","../src/services/news.service.ts","../src/client.ts","../src/types/select-options.ts","../src/logic/avatar.ts","../src/logic/elo.ts","../src/logic/exam.ts","../src/logic/import-export.ts","../src/navigation.ts","../src/utils/bangla-numbers.ts","../src/utils/format-curriculum.ts","../src/utils/relative-time.ts","../src/utils/latex.ts","../src/utils/time.ts","../src/utils/currency.ts","../src/utils/url.ts","../src/utils/file.ts","../src/utils/guards.ts"],"names":[],"mappings":"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AACC,IAAA;AACC,IAAA;AACjC,IAAA;AACX,EAAA;AACF;AAEkF;AACvC,EAAA;AACL,EAAA;AAC7B,IAAA;AACE,IAAA;AACD,IAAA;AACP,EAAA;AACH;AtBo2D4C;AACA;AuBh7DM;AACZ,EAAA;AACtC;AvBk7D4C;AACA;AwBr7DgC;AACzD,EAAA;AACF,EAAA;AACe,EAAA;AACY,EAAA;AAC5C;AxBu7D4C;AACA;AyB77DW;AACnB,EAAA;AACpC;AzB+7D4C;AACA;A0Bh8DoB;AACvD,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACwB,EAAA;AAC5B;AAE8D;AACxB,EAAA;AACtC;AAEkE;AAC9B,EAAA;AACpC;A1Bg8D4C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/orjok/orjok/packages/commons/dist/index.cjs","sourcesContent":[null,"import type { NetworkProvider } from \"../providers/network\";\nimport type { UpdateUserAvatarInput } from \"../types/inputs\";\nimport type {\n User,\n PaginatedList,\n QuestionObject,\n Pack,\n Course,\n CourseEnrolled,\n} from \"../types/models\";\n\nexport class UserService {\n constructor(private network: NetworkProvider) {}\n\n async get(id: string): Promise<User | null> {\n const result = await this.network.query<{ getUser: User | null }>(\n `query GetUser($id: String!) {\n getUser(id: $id) {\n id fullName avatarUrl avatarConfig tier subscriptionExpiresAt\n aiEvaluationRemaining questionCount packCount contestCount\n EngineeringRating MedicalRating VarsityRating BCSRating\n sscOverallRating sscPhysicsRating sscChemistryRating sscMathRating sscBiologyRating\n hscOverallRating hscPhysicsRating hscChemistryRating hscMathRating hscBiologyRating\n bcsOverallRating\n }\n }`,\n { id }\n );\n return result.data.getUser;\n }\n\n async updateAvatar(input: UpdateUserAvatarInput): Promise<User | null> {\n const result = await this.network.mutate<{ updateUser: User | null }>(\n `mutation UpdateUser($input: UpdateUserInput!) {\n updateUser(input: $input) {\n id fullName avatarUrl avatarConfig\n }\n }`,\n { input: { id: input.userId, avatarUrl: input.avatarUrl, avatarConfig: input.avatarConfig } }\n );\n return result.data.updateUser;\n }\n\n async getQuestions(\n userId: string,\n nextToken?: string,\n limit: number = 10\n ): Promise<PaginatedList<QuestionObject>> {\n const result = await this.network.query<{\n listQuestionObjectByOwnerAndCreatedAt: PaginatedList<QuestionObject>;\n }>(\n `query ListUserQuestions($owner: ID!, $limit: Int, $nextToken: String, $sortDirection: ModelSortDirection) {\n listQuestionObjectByOwnerAndCreatedAt(owner: $owner, limit: $limit, sortDirection: $sortDirection, nextToken: $nextToken) {\n items {\n id question imageUrl language level difficulty type voteCount\n verificationStatus owner packId createdAt\n options { items { id content } }\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n { owner: userId, limit, nextToken, sortDirection: \"DESC\" }\n );\n return result.data.listQuestionObjectByOwnerAndCreatedAt;\n }\n\n async getPacks(\n userId: string,\n nextToken?: string,\n limit: number = 10\n ): Promise<PaginatedList<Pack>> {\n const result = await this.network.query<{\n listPackByOwnerAndCreatedAt: PaginatedList<Pack>;\n }>(\n `query ListUserPacks($owner: ID!, $limit: Int, $nextToken: String, $sortDirection: ModelSortDirection) {\n listPackByOwnerAndCreatedAt(owner: $owner, limit: $limit, sortDirection: $sortDirection, nextToken: $nextToken) {\n items {\n id name language level difficulty questionCount automaticNumbering\n owner subjectId chapterId topicId verificationStatus createdAt\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n { owner: userId, limit, nextToken, sortDirection: \"DESC\" }\n );\n return result.data.listPackByOwnerAndCreatedAt;\n }\n\n async getCourses(userId: string): Promise<Course[]> {\n const result = await this.network.query<{\n listCourseByOwnerAndCreatedAt: PaginatedList<Course>;\n }>(\n `query ListUserCourses($owner: ID!, $sortDirection: ModelSortDirection) {\n listCourseByOwnerAndCreatedAt(owner: $owner, sortDirection: $sortDirection) {\n items {\n id name description imageUrl videoCount fileCount price totalEnrolled owner createdAt\n }\n }\n }`,\n { owner: userId, sortDirection: \"DESC\" }\n );\n return result.data.listCourseByOwnerAndCreatedAt.items;\n }\n\n async getEnrolledCourses(userId: string): Promise<CourseEnrolled[]> {\n const result = await this.network.query<{\n listCourseEnrolledByUserIdAndCreatedAt: PaginatedList<CourseEnrolled>;\n }>(\n `query ListEnrolledCourses($userId: ID!, $sortDirection: ModelSortDirection) {\n listCourseEnrolledByUserIdAndCreatedAt(userId: $userId, sortDirection: $sortDirection) {\n items {\n id courseId userId score rank createdAt\n course { id name description imageUrl price totalEnrolled }\n }\n }\n }`,\n { userId, sortDirection: \"DESC\" }\n );\n return result.data.listCourseEnrolledByUserIdAndCreatedAt.items;\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type {\n CreateQuestionInput,\n VoteInput,\n AddQuestionTagInput,\n ListByCurriculumInput,\n ListByRandomHashInput,\n} from \"../types/inputs\";\nimport type {\n CreateQuestionResponse,\n StatusIdResponse,\n StatusResponse,\n} from \"../types/responses\";\nimport type {\n QuestionObject,\n PaginatedList,\n Option,\n Verification,\n} from \"../types/models\";\n\nexport class QuestionService {\n constructor(private network: NetworkProvider) {}\n\n async create(input: CreateQuestionInput): Promise<CreateQuestionResponse> {\n const result = await this.network.mutate<{ createQuestion: CreateQuestionResponse }>(\n `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) {\n 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) {\n status questionId\n }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.createQuestion;\n }\n\n async get(id: string): Promise<QuestionObject | null> {\n const result = await this.network.query<{ getQuestionObject: QuestionObject | null }>(\n `query GetQuestion($id: String!) {\n getQuestionObject(id: $id) {\n id question imageUrl language level difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId order createdAt\n options { items { id content } }\n tags { items { id tagId } }\n }\n }`,\n { id }\n );\n return result.data.getQuestionObject;\n }\n\n async getFull(id: string): Promise<QuestionObject | null> {\n const result = await this.network.query<{ getQuestionObject: QuestionObject | null }>(\n `query GetFullQuestion($id: String!) {\n getQuestionObject(id: $id) {\n id question answer explanation extra imageUrl language level difficulty type\n markingInstructions voteCount verificationStatus owner packId\n subjectId chapterId topicId order createdAt\n options { items { id content } }\n tags { items { id tagId } }\n user { id fullName avatarUrl }\n }\n }`,\n { id }\n );\n return result.data.getQuestionObject;\n }\n\n async getAnswer(id: string): Promise<Option | null> {\n const result = await this.network.query<{ getQuestionObject: { answer: string } | null }>(\n `query GetAnswer($id: String!) {\n getQuestionObject(id: $id) { answer }\n }`,\n { id }\n );\n if (!result.data.getQuestionObject) return null;\n return { id, content: result.data.getQuestionObject.answer } as Option;\n }\n\n async getQuestionId(packId: string, order: number): Promise<string | null> {\n const result = await this.network.query<{\n listQuestionObjectByPackIdAndOrder: PaginatedList<{ id: string }>;\n }>(\n `query GetQuestionId($packId: ID!, $order: ModelIntKeyConditionInput) {\n listQuestionObjectByPackIdAndOrder(packId: $packId, order: $order, limit: 1) {\n items { id }\n }\n }`,\n { packId, order: { eq: order } }\n );\n return result.data.listQuestionObjectByPackIdAndOrder.items[0]?.id ?? null;\n }\n\n async listByPack(\n packId: string,\n limit = 200,\n sortDirection: \"ASC\" | \"DESC\" = \"ASC\",\n nextToken?: string | null\n ): Promise<PaginatedList<QuestionObject>> {\n const result = await this.network.query<{\n listQuestionObjectByPackIdAndOrder: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsByPack($packId: ID!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {\n listQuestionObjectByPackIdAndOrder(packId: $packId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {\n items {\n id question answer explanation extra imageUrl language level difficulty type\n markingInstructions voteCount owner packId subjectId chapterId topicId order createdAt\n options { items { id content } }\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n { packId, sortDirection, limit, nextToken }\n );\n return result.data.listQuestionObjectByPackIdAndOrder;\n }\n\n async update(\n id: string,\n fields: Record<string, unknown>\n ): Promise<QuestionObject | null> {\n const result = await this.network.mutate<{ updateQuestionObject: QuestionObject | null }>(\n `mutation UpdateQuestion($input: UpdateQuestionObjectInput!) {\n updateQuestionObject(input: $input) {\n id question answer explanation extra imageUrl language level difficulty type\n markingInstructions voteCount owner packId subjectId chapterId topicId order createdAt\n options { items { id content } }\n }\n }`,\n { input: { id, ...fields } }\n );\n return result.data.updateQuestionObject;\n }\n\n async updateOption(id: string, content: string): Promise<{ id: string; content: string } | null> {\n const result = await this.network.mutate<{ updateOption: { id: string; content: string } | null }>(\n `mutation UpdateOption($input: UpdateOptionInput!) {\n updateOption(input: $input) {\n id\n content\n }\n }`,\n { input: { id, content } }\n );\n return result.data.updateOption;\n }\n\n async createOption(input: { id?: string; content: string; questionId: string }): Promise<{ id: string; content: string } | null> {\n const result = await this.network.mutate<{ createOption: { id: string; content: string } | null }>(\n `mutation CreateOption($input: CreateOptionInput!) {\n createOption(input: $input) {\n id\n content\n }\n }`,\n { input }\n );\n return result.data.createOption;\n }\n\n async deleteOption(id: string): Promise<{ id: string } | null> {\n const result = await this.network.mutate<{ deleteOption: { id: string } | null }>(\n `mutation DeleteOption($input: DeleteOptionInput!) {\n deleteOption(input: $input) {\n id\n }\n }`,\n { input: { id } }\n );\n return result.data.deleteOption;\n }\n\n async delete(id: string): Promise<QuestionObject | null> {\n const result = await this.network.mutate<{ deleteQuestionObject: QuestionObject | null }>(\n `mutation DeleteQuestion($input: DeleteQuestionObjectInput!) {\n deleteQuestionObject(input: $input) { id }\n }`,\n { input: { id } }\n );\n return result.data.deleteQuestionObject;\n }\n\n async vote(input: VoteInput): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ vote: StatusIdResponse }>(\n `mutation Vote($questionId: ID!, $type: String!, $modelType: String!) {\n vote(questionId: $questionId, type: $type, modelType: $modelType) {\n status id\n }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.vote;\n }\n\n async upVote(questionId: string): Promise<StatusIdResponse> {\n return this.vote({ questionId, type: \"upvote\", modelType: \"question\" });\n }\n\n async downVote(questionId: string): Promise<StatusIdResponse> {\n return this.vote({ questionId, type: \"downvote\", modelType: \"question\" });\n }\n\n async verify(id: string, userId: string): Promise<Verification | null> {\n const result = await this.network.mutate<{ createVerification: Verification | null }>(\n `mutation VerifyQuestion($input: CreateVerificationInput!) {\n createVerification(input: $input) { id verifiedObjectId type userId }\n }`,\n { input: { verifiedObjectId: id, type: \"question\", userId } }\n );\n return result.data.createVerification;\n }\n\n async report(id: string, reason: string): Promise<{ id: string }> {\n const result = await this.network.mutate<{ createQuestionReport: { id: string } }>(\n `mutation ReportQuestion($input: CreateQuestionReportInput!) {\n createQuestionReport(input: $input) { id }\n }`,\n { input: { questionId: id, reason } }\n );\n return result.data.createQuestionReport;\n }\n\n async addTags(input: AddQuestionTagInput): Promise<StatusResponse> {\n const result = await this.network.mutate<{ addQuestionTag: StatusResponse }>(\n `mutation AddQuestionTag($tags: String!, $packId: ID, $questionId: ID) {\n addQuestionTag(tags: $tags, packId: $packId, questionId: $questionId) {\n status\n }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.addQuestionTag;\n }\n\n async listBySubject(input: ListByCurriculumInput): Promise<PaginatedList<QuestionObject>> {\n const variables: Record<string, unknown> = {\n subjectId: input.id,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: input.limit ?? 20,\n nextToken: input.nextToken,\n };\n const result = await this.network.query<{\n listQuestionObjectBySubjectIdAndCreatedAt: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsBySubject($subjectId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {\n listQuestionObjectBySubjectIdAndCreatedAt(subjectId: $subjectId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {\n items {\n id question imageUrl language level difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectBySubjectIdAndCreatedAt;\n }\n\n async listByChapter(input: ListByCurriculumInput): Promise<PaginatedList<QuestionObject>> {\n const variables: Record<string, unknown> = {\n chapterId: input.id,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: input.limit ?? 20,\n nextToken: input.nextToken,\n };\n const result = await this.network.query<{\n listQuestionObjectByChapterIdAndCreatedAt: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsByChapter($chapterId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {\n listQuestionObjectByChapterIdAndCreatedAt(chapterId: $chapterId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {\n items {\n id question imageUrl language level difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectByChapterIdAndCreatedAt;\n }\n\n async listByTopic(input: ListByCurriculumInput): Promise<PaginatedList<QuestionObject>> {\n const variables: Record<string, unknown> = {\n topicId: input.id,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: input.limit ?? 20,\n nextToken: input.nextToken,\n };\n const result = await this.network.query<{\n listQuestionObjectByTopicIdAndCreatedAt: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsByTopic($topicId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {\n listQuestionObjectByTopicIdAndCreatedAt(topicId: $topicId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {\n items {\n id question imageUrl language level difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectByTopicIdAndCreatedAt;\n }\n\n async listByRandomHash(input: ListByRandomHashInput): Promise<PaginatedList<QuestionObject>> {\n const compositeKey = [input.language, input.difficulty].filter(Boolean).join(\"_\");\n const variables: Record<string, unknown> = {\n subjectId: input.id,\n sortDirection: input.sortDirection ?? \"ASC\",\n limit: input.limit ?? 1,\n language_difficulty_randomHash: input.randomHashFilter\n ? { beginsWith: compositeKey, ...input.randomHashFilter }\n : { beginsWith: compositeKey },\n };\n const result = await this.network.query<{\n listQuestionObjectBySubjectIdAndLanguage_difficulty_randomHash: PaginatedList<QuestionObject>;\n }>(\n `query ListByRandomHash($subjectId: String!, $language_difficulty_randomHash: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int) {\n listQuestionObjectBySubjectIdAndLanguage_difficulty_randomHash(subjectId: $subjectId, language_difficulty_randomHash: $language_difficulty_randomHash, sortDirection: $sortDirection, limit: $limit) {\n items {\n id question imageUrl language level difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectBySubjectIdAndLanguage_difficulty_randomHash;\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { CreatePackInput, ListByCurriculumInput } from \"../types/inputs\";\nimport type { StatusIdResponse } from \"../types/responses\";\nimport type {\n Pack,\n PaginatedList,\n PackExamResults,\n PackGroup,\n Verification,\n} from \"../types/models\";\n\nexport class PackService {\n constructor(private network: NetworkProvider) {}\n\n async create(input: CreatePackInput): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ createPackObject: StatusIdResponse }>(\n `mutation CreatePack($name: String!, $level: String!, $language: String!, $difficulty: String!, $automaticNumbering: Boolean, $subjectId: ID, $chapterId: ID, $topicId: ID) {\n createPackObject(name: $name, level: $level, language: $language, difficulty: $difficulty, automaticNumbering: $automaticNumbering, subjectId: $subjectId, chapterId: $chapterId, topicId: $topicId) {\n status id\n }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.createPackObject;\n }\n\n async get(id: string): Promise<Pack | null> {\n const result = await this.network.query<{ getPack: Pack | null }>(\n `query GetPack($id: ID!) {\n getPack(id: $id) {\n id name language level difficulty questionCount automaticNumbering\n owner subjectId chapterId topicId packGroupId verificationStatus createdAt\n tags { items { id tagId } }\n user { id fullName avatarUrl }\n }\n }`,\n { id }\n );\n return result.data.getPack;\n }\n\n async getFull(id: string): Promise<Pack | null> {\n const result = await this.network.query<{ getPack: Pack | null }>(\n `query GetFullPack($id: ID!) {\n getPack(id: $id) {\n id name language level difficulty questionCount automaticNumbering\n owner subjectId chapterId topicId packGroupId verificationStatus createdAt\n tags { items { id tagId } }\n user { id fullName avatarUrl }\n questions(sortDirection: ASC, limit: 200) {\n items {\n id question answer explanation extra imageUrl language level difficulty type\n markingInstructions voteCount owner order createdAt\n options { items { id content } }\n tags { items { id tagId } }\n }\n }\n }\n }`,\n { id }\n );\n const pack = result.data.getPack;\n if (pack && pack.questions && Array.isArray(pack.questions.items)) {\n pack.questions.items.sort((a, b) => {\n const orderA = typeof a.order === \"number\" ? a.order : (a.order ? Number(a.order) : 0);\n const orderB = typeof b.order === \"number\" ? b.order : (b.order ? Number(b.order) : 0);\n if (orderA !== orderB) {\n if (orderA === 0) return 1;\n if (orderB === 0) return -1;\n return orderA - orderB;\n }\n const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;\n const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;\n return timeA - timeB;\n });\n }\n return pack;\n }\n\n async update(id: string, fields: Record<string, unknown>): Promise<Pack | null> {\n const result = await this.network.mutate<{ updatePack: Pack | null }>(\n `mutation UpdatePack($input: UpdatePackInput!) {\n updatePack(input: $input) {\n id name language level difficulty questionCount automaticNumbering\n owner subjectId chapterId topicId verificationStatus createdAt\n }\n }`,\n { input: { id, ...fields } }\n );\n return result.data.updatePack;\n }\n\n async delete(id: string): Promise<Pack | null> {\n const result = await this.network.mutate<{ deletePack: Pack | null }>(\n `mutation DeletePack($input: DeletePackInput!) {\n deletePack(input: $input) { id }\n }`,\n { input: { id } }\n );\n return result.data.deletePack;\n }\n\n async verify(id: string, userId: string): Promise<Verification | null> {\n const result = await this.network.mutate<{ createVerification: Verification | null }>(\n `mutation VerifyPack($input: CreateVerificationInput!) {\n createVerification(input: $input) { id verifiedObjectId type userId }\n }`,\n { input: { verifiedObjectId: id, type: \"pack\", userId } }\n );\n return result.data.createVerification;\n }\n\n async report(id: string, reason: string): Promise<{ id: string }> {\n const result = await this.network.mutate<{ createPackReport: { id: string } }>(\n `mutation ReportPack($input: CreatePackReportInput!) {\n createPackReport(input: $input) { id }\n }`,\n { input: { packId: id, reason } }\n );\n return result.data.createPackReport;\n }\n\n async submitExam(packId: string, selectedOptions: (string | null)[]): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ submitPackExam: StatusIdResponse }>(\n `mutation SubmitPackExam($packId: ID!, $selectedOptions: String!) {\n submitPackExam(packId: $packId, selectedOptions: $selectedOptions) {\n status id\n }\n }`,\n { packId, selectedOptions: JSON.stringify(selectedOptions) }\n );\n return result.data.submitPackExam;\n }\n\n async getExams(userId: string, nextToken?: string): Promise<PaginatedList<PackExamResults>> {\n const result = await this.network.query<{\n listPackExamResultsByOwnerAndCreatedAt: PaginatedList<PackExamResults>;\n }>(\n `query ListPackExams($owner: ID!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listPackExamResultsByOwnerAndCreatedAt(owner: $owner, sortDirection: $sortDirection, nextToken: $nextToken) {\n items {\n id packId selectedOptions aiEvaluations owner createdAt\n pack { id name language level difficulty questionCount }\n }\n nextToken\n }\n }`,\n { owner: userId, sortDirection: \"DESC\", nextToken }\n );\n return result.data.listPackExamResultsByOwnerAndCreatedAt;\n }\n\n async getExamResult(resultId: string): Promise<PackExamResults | null> {\n const result = await this.network.query<{ getPackExamResults: PackExamResults | null }>(\n `query GetPackExamResult($id: ID!) {\n getPackExamResults(id: $id) {\n id packId selectedOptions aiEvaluations owner createdAt\n pack { id name language level difficulty questionCount }\n }\n }`,\n { id: resultId }\n );\n return result.data.getPackExamResults;\n }\n\n async listBySubject(input: ListByCurriculumInput): Promise<PaginatedList<Pack>> {\n const result = await this.network.query<{\n listPackBySubjectIdAndCreatedAt: PaginatedList<Pack>;\n }>(\n `query ListPacksBySubject($subjectId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {\n listPackBySubjectIdAndCreatedAt(subjectId: $subjectId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {\n items {\n id name language level difficulty questionCount owner createdAt verificationStatus\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n { subjectId: input.id, sortDirection: \"DESC\", limit: input.limit ?? 20, nextToken: input.nextToken }\n );\n return result.data.listPackBySubjectIdAndCreatedAt;\n }\n\n async listByChapter(input: ListByCurriculumInput): Promise<PaginatedList<Pack>> {\n const result = await this.network.query<{\n listPackByChapterIdAndCreatedAt: PaginatedList<Pack>;\n }>(\n `query ListPacksByChapter($chapterId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {\n listPackByChapterIdAndCreatedAt(chapterId: $chapterId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {\n items {\n id name language level difficulty questionCount owner createdAt verificationStatus\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n { chapterId: input.id, sortDirection: \"DESC\", limit: input.limit ?? 20, nextToken: input.nextToken }\n );\n return result.data.listPackByChapterIdAndCreatedAt;\n }\n\n async listByTopic(input: ListByCurriculumInput): Promise<PaginatedList<Pack>> {\n const result = await this.network.query<{\n listPackByTopicIdAndCreatedAt: PaginatedList<Pack>;\n }>(\n `query ListPacksByTopic($topicId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {\n listPackByTopicIdAndCreatedAt(topicId: $topicId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {\n items {\n id name language level difficulty questionCount owner createdAt verificationStatus\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n { topicId: input.id, sortDirection: \"DESC\", limit: input.limit ?? 20, nextToken: input.nextToken }\n );\n return result.data.listPackByTopicIdAndCreatedAt;\n }\n\n async listGroups(type: string, level?: string): Promise<PackGroup[]> {\n const queryField = level ? \"listPackGroupByTypeLevelAndOrder\" : \"listPackGroupByTypeAndOrder\";\n const variables: Record<string, unknown> = level\n ? { typeLevel: `${type}_${level}`, sortDirection: \"ASC\" }\n : { type, sortDirection: \"ASC\" };\n\n const result = await this.network.query<Record<string, PaginatedList<PackGroup>>>(\n level\n ? `query ListPackGroups($typeLevel: String!, $sortDirection: ModelSortDirection) {\n listPackGroupByTypeLevelAndOrder(typeLevel: $typeLevel, sortDirection: $sortDirection) {\n items { id name description imageUrl type level typeLevel packCount order }\n }\n }`\n : `query ListPackGroups($type: String!, $sortDirection: ModelSortDirection) {\n listPackGroupByTypeAndOrder(type: $type, sortDirection: $sortDirection) {\n items { id name description imageUrl type level typeLevel packCount order }\n }\n }`,\n variables\n );\n return (result.data[queryField] as PaginatedList<PackGroup>).items;\n }\n\n async getGroup(id: string): Promise<PackGroup | null> {\n const result = await this.network.query<{ getPackGroup: PackGroup | null }>(\n `query GetPackGroup($id: ID!) {\n getPackGroup(id: $id) {\n id name description imageUrl type level typeLevel packCount order\n packs { items { id name language level difficulty questionCount owner createdAt verificationStatus } }\n }\n }`,\n { id }\n );\n return result.data.getPackGroup;\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { CreateContestInput } from \"../types/inputs\";\nimport type {\n StatusIdResponse,\n StatusResponse,\n CheckParticipationResponse,\n ContestQuestionsResponse,\n ContestAnswersResponse,\n LeaderboardResponse,\n} from \"../types/responses\";\nimport type { Contest, PaginatedList, ContestParticipant } from \"../types/models\";\n\nexport class ContestService {\n constructor(private network: NetworkProvider) {}\n\n async create(input: CreateContestInput): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ makeContest: StatusIdResponse }>(\n `mutation MakeContest($name: String!, $description: String!, $level: String!, $language: String!, $startTime: AWSDateTime!, $endTime: AWSDateTime!, $subject: String!, $isRated: Boolean!, $questions: String!, $maximumParticipants: Int!, $solutionVideoUrl: String, $imageUrl: String) {\n makeContest(name: $name, description: $description, level: $level, language: $language, startTime: $startTime, endTime: $endTime, subject: $subject, isRated: $isRated, questions: $questions, maximumParticipants: $maximumParticipants, solutionVideoUrl: $solutionVideoUrl, imageUrl: $imageUrl) {\n status id\n }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.makeContest;\n }\n\n async get(id: string): Promise<Contest | null> {\n const result = await this.network.query<{ getContest: Contest | null }>(\n `query GetContest($id: ID!) {\n getContest(id: $id) {\n id name description startTime endTime level language owner isRated subject\n totalQuestions resultsPublished totalParticipants maximumParticipants\n rankings globalPk solutionVideoUrl imageUrl\n }\n }`,\n { id }\n );\n return result.data.getContest;\n }\n\n async getName(id: string): Promise<string | null> {\n const result = await this.network.query<{ getContest: { name: string } | null }>(\n `query GetContestName($id: ID!) { getContest(id: $id) { name } }`,\n { id }\n );\n return result.data.getContest?.name ?? null;\n }\n\n async list(nextToken?: string, level?: string): Promise<PaginatedList<Contest>> {\n const variables: Record<string, unknown> = level\n ? { level, sortDirection: \"DESC\", nextToken }\n : { globalPk: \"ALL\", sortDirection: \"DESC\", nextToken };\n\n const result = await this.network.query<Record<string, PaginatedList<Contest>>>(\n level\n ? `query ListContests($level: String!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listContestByLevelAndStartTime(level: $level, sortDirection: $sortDirection, nextToken: $nextToken) {\n items { id name description startTime endTime level language owner isRated subject totalQuestions resultsPublished totalParticipants maximumParticipants solutionVideoUrl imageUrl }\n nextToken\n }\n }`\n : `query ListContests($globalPk: String!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listContestByGlobalPkAndStartTime(globalPk: $globalPk, sortDirection: $sortDirection, nextToken: $nextToken) {\n items { id name description startTime endTime level language owner isRated subject totalQuestions resultsPublished totalParticipants maximumParticipants solutionVideoUrl imageUrl }\n nextToken\n }\n }`,\n variables\n );\n const key = level ? \"listContestByLevelAndStartTime\" : \"listContestByGlobalPkAndStartTime\";\n return result.data[key] as PaginatedList<Contest>;\n }\n\n async participate(contestId: string): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ participateContest: StatusIdResponse }>(\n `mutation ParticipateContest($contestId: ID!) {\n participateContest(contestId: $contestId) { status id }\n }`,\n { contestId }\n );\n return result.data.participateContest;\n }\n\n async checkParticipation(contestId: string): Promise<CheckParticipationResponse> {\n const result = await this.network.query<{ checkUserParticipation: CheckParticipationResponse }>(\n `query CheckParticipation($contestId: ID!) {\n checkUserParticipation(contestId: $contestId) {\n status participated selectedOptions score rank beforeRating afterRating\n }\n }`,\n { contestId }\n );\n return result.data.checkUserParticipation;\n }\n\n async selectOption(contestId: string, optionId: string, order: number): Promise<StatusResponse> {\n const result = await this.network.mutate<{ selectContestOption: StatusResponse }>(\n `mutation SelectContestOption($contestId: ID!, $optionId: String!, $order: Int!) {\n selectContestOption(contestId: $contestId, optionId: $optionId, order: $order) { status }\n }`,\n { contestId, optionId, order }\n );\n return result.data.selectContestOption;\n }\n\n async getQuestions(contestId: string): Promise<ContestQuestionsResponse> {\n const result = await this.network.query<{ getContestQuestions: ContestQuestionsResponse }>(\n `query GetContestQuestions($contestId: ID!) {\n getContestQuestions(contestId: $contestId) { status questions }\n }`,\n { contestId }\n );\n return result.data.getContestQuestions;\n }\n\n async getAnswers(contestId: string): Promise<ContestAnswersResponse> {\n const result = await this.network.query<{ getContestAnswers: ContestAnswersResponse }>(\n `query GetContestAnswers($contestId: ID!) {\n getContestAnswers(contestId: $contestId) { status answers }\n }`,\n { contestId }\n );\n return result.data.getContestAnswers;\n }\n\n async publish(contestId: string): Promise<StatusResponse> {\n const result = await this.network.mutate<{ publishContestResults: StatusResponse }>(\n `mutation PublishResults($contestId: ID!) {\n publishContestResults(contestId: $contestId) { status }\n }`,\n { contestId }\n );\n return result.data.publishContestResults;\n }\n\n async getLeaderboard(contestId: string, nextToken?: string | null): Promise<LeaderboardResponse> {\n const result = await this.network.query<{\n listContestParticipantByContestIdAndRank: PaginatedList<ContestParticipant>;\n }>(\n `query GetLeaderboard($contestId: ID!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listContestParticipantByContestIdAndRank(contestId: $contestId, sortDirection: $sortDirection, nextToken: $nextToken) {\n items { id contestId userId rank score afterRating beforeRating createdAt user { id fullName avatarUrl } }\n nextToken\n }\n }`,\n { contestId, sortDirection: \"ASC\", nextToken }\n );\n const data = result.data.listContestParticipantByContestIdAndRank;\n return {\n status: \"success\",\n leaderboard: JSON.stringify(data.items),\n nextToken: data.nextToken,\n };\n }\n\n async getUserResults(userId: string, nextToken?: string | null): Promise<PaginatedList<ContestParticipant>> {\n const result = await this.network.query<{\n listContestParticipantByUserIdAndCreatedAt: PaginatedList<ContestParticipant>;\n }>(\n `query GetUserResults($userId: ID!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listContestParticipantByUserIdAndCreatedAt(userId: $userId, sortDirection: $sortDirection, nextToken: $nextToken) {\n items { id contestId userId rank score afterRating beforeRating selectedOptions createdAt contest { id name subject level startTime } }\n nextToken\n }\n }`,\n { userId, sortDirection: \"DESC\", nextToken }\n );\n return result.data.listContestParticipantByUserIdAndCreatedAt;\n }\n\n async updateSolutionVideo(contestId: string, url: string | null): Promise<StatusResponse> {\n const result = await this.network.mutate<{ updateContest: Contest | null }>(\n `mutation UpdateSolutionVideo($input: UpdateContestInput!) {\n updateContest(input: $input) { id solutionVideoUrl }\n }`,\n { input: { id: contestId, solutionVideoUrl: url } }\n );\n return { status: result.data.updateContest ? \"success\" : \"error\" };\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type {\n CreateCourseInput,\n CreateCourseItemInput,\n CreateCourseLiveExamInput,\n DeleteCourseItemInput,\n EnrollStudentInput,\n SendEnrollmentRequestInput,\n GetSignedURLInput,\n GetSignedFileURLInput,\n CreateCourseVideoInput,\n UploadVideoToBunnyInput,\n} from \"../types/inputs\";\nimport type {\n CreateCourseResponse,\n StatusIdResponse,\n StatusResponse,\n SignedURLResponse,\n CreateCourseVideoResponse,\n CourseLiveExamQuestionsResponse,\n LeaderboardResponse,\n} from \"../types/responses\";\nimport type {\n Course,\n CourseItem,\n CourseEnrolled,\n CourseLiveExam,\n PaginatedList,\n} from \"../types/models\";\n\nexport class CourseService {\n constructor(private network: NetworkProvider) {}\n\n async create(input: CreateCourseInput): Promise<CreateCourseResponse> {\n const result = await this.network.mutate<{ makeCourse: CreateCourseResponse }>(\n `mutation MakeCourse($name: String!, $description: String!, $price: Int!, $imageUrl: String) {\n makeCourse(name: $name, description: $description, price: $price, imageUrl: $imageUrl) { status courseId }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.makeCourse;\n }\n\n async get(id: string): Promise<Course | null> {\n const result = await this.network.query<{ getCourse: Course | null }>(\n `query GetCourse($id: ID!) {\n getCourse(id: $id) { id name description imageUrl videoCount fileCount price totalEnrolled owner createdAt }\n }`,\n { id }\n );\n return result.data.getCourse;\n }\n\n async getItems(courseId: string): Promise<CourseItem[]> {\n const result = await this.network.query<{\n listCourseItemsByCourseIdAndOrder: PaginatedList<CourseItem>;\n }>(\n `query ListCourseItems($courseId: ID!, $sortDirection: ModelSortDirection) {\n listCourseItemsByCourseIdAndOrder(courseId: $courseId, sortDirection: $sortDirection) {\n items { id type name courseId parentId order owner }\n }\n }`,\n { courseId, sortDirection: \"ASC\" }\n );\n return result.data.listCourseItemsByCourseIdAndOrder.items;\n }\n\n async createItem(input: CreateCourseItemInput): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ createCourseItem: StatusIdResponse }>(\n `mutation CreateCourseItem($courseId: ID!, $name: String!, $order: Int!, $type: CourseItemsType, $parentId: ID, $id: ID) {\n createCourseItem(courseId: $courseId, name: $name, order: $order, type: $type, parentId: $parentId, id: $id) { status id }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.createCourseItem;\n }\n\n async updateItem(id: string, name: string, order: number): Promise<CourseItem | null> {\n const result = await this.network.mutate<{ updateCourseItems: CourseItem | null }>(\n `mutation UpdateCourseItem($input: UpdateCourseItemsInput!) {\n updateCourseItems(input: $input) { id type name courseId parentId order owner }\n }`,\n { input: { id, name, order } }\n );\n return result.data.updateCourseItems;\n }\n\n async deleteItem(input: DeleteCourseItemInput): Promise<StatusResponse> {\n const result = await this.network.mutate<{ deleteCourseItem: StatusResponse }>(\n `mutation DeleteCourseItem($id: ID!, $fileType: String!, $courseId: ID!, $parentId: ID) {\n deleteCourseItem(id: $id, fileType: $fileType, courseId: $courseId, parentId: $parentId) { status }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.deleteCourseItem;\n }\n\n async enroll(input: EnrollStudentInput): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ enrollStudent: StatusIdResponse }>(\n `mutation EnrollStudent($courseId: ID!, $userId: ID!) {\n enrollStudent(courseId: $courseId, userId: $userId) { status id }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.enrollStudent;\n }\n\n async getEnrolledStudent(courseId: string, userId: string): Promise<CourseEnrolled | null> {\n const result = await this.network.query<{\n listCourseEnrolledByCourseIdAndCreatedAt: PaginatedList<CourseEnrolled>;\n }>(\n `query GetEnrolledStudent($courseId: ID!, $filter: ModelCourseEnrolledFilterInput) {\n listCourseEnrolledByCourseIdAndCreatedAt(courseId: $courseId, filter: $filter) {\n items { id courseId userId owner score rank createdAt }\n }\n }`,\n { courseId, filter: { userId: { eq: userId } } }\n );\n return result.data.listCourseEnrolledByCourseIdAndCreatedAt.items[0] ?? null;\n }\n\n async getEnrolledStudents(courseId: string, nextToken?: string | null): Promise<PaginatedList<CourseEnrolled>> {\n const result = await this.network.query<{\n listCourseEnrolledByCourseIdAndCreatedAt: PaginatedList<CourseEnrolled>;\n }>(\n `query ListEnrolledStudents($courseId: ID!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listCourseEnrolledByCourseIdAndCreatedAt(courseId: $courseId, sortDirection: $sortDirection, nextToken: $nextToken) {\n items { id courseId userId owner score rank createdAt user { id fullName avatarUrl } }\n nextToken\n }\n }`,\n { courseId, sortDirection: \"DESC\", nextToken }\n );\n return result.data.listCourseEnrolledByCourseIdAndCreatedAt;\n }\n\n async sendEnrollmentRequest(input: SendEnrollmentRequestInput): Promise<{ id: string }> {\n const result = await this.network.mutate<{ createCourseEnrollmentRequest: { id: string } }>(\n `mutation SendEnrollmentRequest($input: CreateCourseEnrollmentRequestInput!) {\n createCourseEnrollmentRequest(input: $input) { id }\n }`,\n { input: { courseId: input.courseId, name: input.name, phone: input.phone, transactionID: input.transactionId, coupon: input.couponId } }\n );\n return result.data.createCourseEnrollmentRequest;\n }\n\n async getSignedURL(input: GetSignedURLInput): Promise<SignedURLResponse> {\n const result = await this.network.query<{ getR2SignedURL: SignedURLResponse }>(\n `query GetR2SignedURL($courseId: ID!, $fileName: String!, $fileType: String!, $parentId: ID) {\n getR2SignedURL(courseId: $courseId, fileName: $fileName, fileType: $fileType, parentId: $parentId) { status signedURL }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.getR2SignedURL;\n }\n\n async getSignedFileURL(input: GetSignedFileURLInput): Promise<SignedURLResponse> {\n const result = await this.network.query<{ getR2SignedFileURL: SignedURLResponse }>(\n `query GetR2SignedFileURL($courseId: ID!, $itemId: ID!, $key: ID!) {\n getR2SignedFileURL(courseId: $courseId, itemId: $itemId, key: $key) { status signedURL }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.getR2SignedFileURL;\n }\n\n async createVideo(input: CreateCourseVideoInput): Promise<CreateCourseVideoResponse> {\n const result = await this.network.query<{ createCourseVideo: CreateCourseVideoResponse }>(\n `query CreateCourseVideo($courseId: ID!, $fileName: String!, $fileType: String!) {\n createCourseVideo(courseId: $courseId, fileName: $fileName, fileType: $fileType) { status videoId }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.createCourseVideo;\n }\n\n async uploadVideoToBunny(input: UploadVideoToBunnyInput): Promise<StatusResponse> {\n const result = await this.network.mutate<{ uploadR2VideoToBunny: StatusResponse }>(\n `mutation UploadR2VideoToBunny($key: String!, $videoId: String!, $fileType: String!) {\n uploadR2VideoToBunny(key: $key, videoId: $videoId, fileType: $fileType) { status }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.uploadR2VideoToBunny;\n }\n\n async getLiveExams(courseId: string, nextToken?: string | null): Promise<PaginatedList<CourseLiveExam>> {\n const result = await this.network.query<{\n listCourseLiveExamByCourseIdAndStartTime: PaginatedList<CourseLiveExam>;\n }>(\n `query ListLiveExams($courseId: ID!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listCourseLiveExamByCourseIdAndStartTime(courseId: $courseId, sortDirection: $sortDirection, nextToken: $nextToken) {\n items { id courseId name startTime endTime owner totalParticipants resultsPublished }\n nextToken\n }\n }`,\n { courseId, sortDirection: \"DESC\", nextToken }\n );\n return result.data.listCourseLiveExamByCourseIdAndStartTime;\n }\n\n async createLiveExam(input: CreateCourseLiveExamInput): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ makeCourseLiveExam: StatusIdResponse }>(\n `mutation MakeCourseLiveExam($courseId: ID!, $name: String!, $questions: String!, $startTime: AWSDateTime!, $endTime: AWSDateTime!) {\n makeCourseLiveExam(courseId: $courseId, name: $name, questions: $questions, startTime: $startTime, endTime: $endTime) { status id }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.makeCourseLiveExam;\n }\n\n async getLiveExamQuestions(examId: string): Promise<CourseLiveExamQuestionsResponse> {\n const result = await this.network.query<{ getCourseLiveExamQuestions: CourseLiveExamQuestionsResponse }>(\n `query GetLiveExamQuestions($examId: ID!) {\n getCourseLiveExamQuestions(examId: $examId) { status name startTime endTime questions selectedOptions }\n }`,\n { examId }\n );\n return result.data.getCourseLiveExamQuestions;\n }\n\n async participateLiveExam(examId: string): Promise<StatusResponse> {\n const result = await this.network.mutate<{ participateCourseLiveExam: StatusResponse }>(\n `mutation ParticipateLiveExam($examId: ID!) {\n participateCourseLiveExam(examId: $examId) { status }\n }`,\n { examId }\n );\n return result.data.participateCourseLiveExam;\n }\n\n async selectLiveExamOption(examId: string, optionId: string, order: number): Promise<StatusResponse> {\n const result = await this.network.mutate<{ selectCourseLiveExamOption: StatusResponse }>(\n `mutation SelectLiveExamOption($examId: ID!, $optionId: ID!, $order: Int!) {\n selectCourseLiveExamOption(examId: $examId, optionId: $optionId, order: $order) { status }\n }`,\n { examId, optionId, order }\n );\n return result.data.selectCourseLiveExamOption;\n }\n\n async getLiveExamLeaderboard(examId: string, nextToken?: string | null): Promise<LeaderboardResponse> {\n const result = await this.network.query<{ getCourseLiveExamLeaderboard: LeaderboardResponse }>(\n `query GetLiveExamLeaderboard($examId: ID!, $nextToken: String) {\n getCourseLiveExamLeaderboard(examId: $examId, nextToken: $nextToken) { status leaderboard nextToken totalParticipants }\n }`,\n { examId, nextToken }\n );\n return result.data.getCourseLiveExamLeaderboard;\n }\n\n async publishLiveExamResults(examId: string): Promise<StatusResponse> {\n const result = await this.network.mutate<{ publishCourseLiveExamResults: StatusResponse }>(\n `mutation PublishLiveExamResults($examId: ID!) {\n publishCourseLiveExamResults(examId: $examId) { status }\n }`,\n { examId }\n );\n return result.data.publishCourseLiveExamResults;\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { StatusIdResponse } from \"../types/responses\";\nimport type { Subject, Chapter, Topic } from \"../types/models\";\n\nexport class CurriculumService {\n constructor(private network: NetworkProvider) { }\n\n async createSubject(name: string, level: string): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ makeSubject: StatusIdResponse }>(\n `mutation MakeSubject($name: String!, $level: String!) {\n makeSubject(name: $name, level: $level) { status id }\n }`,\n { name, level }\n );\n return result.data.makeSubject;\n }\n\n async createChapter(name: string, subjectId: string, order: number): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ makeChapter: StatusIdResponse }>(\n `mutation MakeChapter($name: String!, $subjectId: ID!, $order: Int!) {\n makeChapter(name: $name, subjectId: $subjectId, order: $order) { status id }\n }`,\n { name, subjectId, order }\n );\n return result.data.makeChapter;\n }\n\n async createTopic(name: string, chapterId: string): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ makeTopic: StatusIdResponse }>(\n `mutation MakeTopic($name: String!, $chapterId: ID!) {\n makeTopic(name: $name, chapterId: $chapterId) { status id }\n }`,\n { name, chapterId }\n );\n return result.data.makeTopic;\n }\n\n async getSubjects(level: string): Promise<Subject[]> {\n const result = await this.network.query<{\n listSubjectByLevel: { items: Subject[] };\n }>(\n `query ListSubjects($level: String!) {\n listSubjectByLevel(level: $level) {\n items { id name level questionCount }\n }\n }`,\n { level }\n );\n return result.data.listSubjectByLevel.items;\n }\n\n async getChapters(subjectId: string): Promise<Chapter[]> {\n const result = await this.network.query<{\n listChapterBySubjectIdAndOrder: { items: Chapter[] };\n }>(\n `query ListChapters($subjectId: ID!, $sortDirection: ModelSortDirection) {\n listChapterBySubjectIdAndOrder(subjectId: $subjectId, sortDirection: $sortDirection) {\n items { id name order subjectId questionCount }\n }\n }`,\n { subjectId, sortDirection: \"ASC\" }\n );\n return result.data.listChapterBySubjectIdAndOrder.items;\n }\n\n async getTopics(chapterId: string): Promise<Topic[]> {\n const result = await this.network.query<{\n listTopicByChapterId: { items: Topic[] };\n }>(\n `query ListTopics($chapterId: ID!) {\n listTopicByChapterId(chapterId: $chapterId) {\n items { id name chapterId questionCount }\n }\n }`,\n { chapterId }\n );\n return result.data.listTopicByChapterId.items;\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { StorageProvider } from \"../providers/storage\";\nimport type {\n GetSignedURLInput,\n GetSignedFileURLInput,\n CreateCourseVideoInput,\n UploadVideoToBunnyInput,\n} from \"../types/inputs\";\nimport type {\n UploadImageResponse,\n StatusResponse,\n SignedURLResponse,\n CreateCourseVideoResponse,\n} from \"../types/responses\";\n\nexport class MediaService {\n constructor(\n private network: NetworkProvider,\n private storage?: StorageProvider\n ) {}\n\n async uploadImage(questionId: string, image: string): Promise<UploadImageResponse> {\n const payloadLength = image ? image.length : 0;\n console.log(`[MediaService.uploadImage] Initiating upload mutation: questionId=\"${questionId}\", payloadLength=${payloadLength} chars`);\n\n try {\n const result = await this.network.mutate<{ uploadImage: UploadImageResponse }>(\n `mutation UploadImage($questionId: ID!, $image: String!) {\n uploadImage(questionId: $questionId, image: $image) { status imageUrl }\n }`,\n { questionId, image }\n );\n\n if (result.errors && result.errors.length > 0) {\n console.error(\n `[MediaService.uploadImage] GraphQL errors returned for questionId \"${questionId}\":`,\n JSON.stringify(result.errors, null, 2)\n );\n }\n\n const response = result.data?.uploadImage;\n if (!response) {\n console.error(`[MediaService.uploadImage] No uploadImage object returned in GraphQL response for questionId \"${questionId}\". Full result:`, result);\n } else if (response.status === \"Error\" || response.status === \"error\" || !response.imageUrl) {\n console.error(`[MediaService.uploadImage] Backend returned error status or missing imageUrl for questionId \"${questionId}\":`, response);\n } else {\n console.log(`[MediaService.uploadImage] Upload mutation success for questionId \"${questionId}\": imageUrl=\"${response.imageUrl}\"`);\n }\n\n return response;\n } catch (err) {\n console.error(`[MediaService.uploadImage] Network/GraphQL mutation failed for questionId \"${questionId}\":`, err);\n throw err;\n }\n }\n\n async deleteImage(imageUrl: string): Promise<StatusResponse> {\n const result = await this.network.mutate<{ deleteImage: StatusResponse }>(\n `mutation DeleteImage($imageUrl: ID!) {\n deleteImage(imageUrl: $imageUrl) { status }\n }`,\n { imageUrl }\n );\n return result.data.deleteImage;\n }\n\n async getSignedURL(input: GetSignedURLInput): Promise<SignedURLResponse> {\n const result = await this.network.query<{ getR2SignedURL: SignedURLResponse }>(\n `query GetR2SignedURL($courseId: ID!, $fileName: String!, $fileType: String!, $parentId: ID) {\n getR2SignedURL(courseId: $courseId, fileName: $fileName, fileType: $fileType, parentId: $parentId) { status signedURL }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.getR2SignedURL;\n }\n\n async getSignedFileURL(input: GetSignedFileURLInput): Promise<SignedURLResponse> {\n const result = await this.network.query<{ getR2SignedFileURL: SignedURLResponse }>(\n `query GetR2SignedFileURL($courseId: ID!, $itemId: ID!, $key: ID!) {\n getR2SignedFileURL(courseId: $courseId, itemId: $itemId, key: $key) { status signedURL }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.getR2SignedFileURL;\n }\n\n async getFileUrl(path: string): Promise<{ url: string }> {\n if (!this.storage) throw new Error(\"StorageProvider not configured\");\n return this.storage.getFileUrl(path);\n }\n\n async uploadFile(\n path: string,\n data: Blob | ArrayBuffer | string,\n options?: { contentType?: string; onProgress?: (progress: { loaded: number; total: number }) => void }\n ): Promise<{ path: string }> {\n if (!this.storage) throw new Error(\"StorageProvider not configured\");\n return this.storage.uploadFile(path, data, options);\n }\n\n async createVideo(input: CreateCourseVideoInput): Promise<CreateCourseVideoResponse> {\n const result = await this.network.query<{ createCourseVideo: CreateCourseVideoResponse }>(\n `query CreateCourseVideo($courseId: ID!, $fileName: String!, $fileType: String!) {\n createCourseVideo(courseId: $courseId, fileName: $fileName, fileType: $fileType) { status videoId }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.createCourseVideo;\n }\n\n async uploadVideoToBunny(input: UploadVideoToBunnyInput): Promise<StatusResponse> {\n const result = await this.network.mutate<{ uploadR2VideoToBunny: StatusResponse }>(\n `mutation UploadR2VideoToBunny($key: String!, $videoId: String!, $fileType: String!) {\n uploadR2VideoToBunny(key: $key, videoId: $videoId, fileType: $fileType) { status }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.uploadR2VideoToBunny;\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { TrackPracticeProgressInput } from \"../types/inputs\";\nimport type { StatusResponse } from \"../types/responses\";\nimport type {\n UserSubjectProgress,\n UserChapterProgress,\n UserTopicProgress,\n UserTopicMetric,\n QuestionTracking,\n PaginatedList,\n} from \"../types/models\";\n\nexport class ProgressService {\n constructor(private network: NetworkProvider) {}\n\n async track(input: TrackPracticeProgressInput): Promise<StatusResponse> {\n const variables: Record<string, unknown> = {\n questionId: input.questionId,\n isCorrect: input.isCorrect,\n subjectId: input.subjectId ?? null,\n chapterId: input.chapterId ?? null,\n topicId: input.topicId ?? null,\n };\n const result = await this.network.mutate<{ trackPracticeProgress: StatusResponse }>(\n `mutation TrackProgress($questionId: ID!, $isCorrect: Boolean!, $subjectId: ID, $chapterId: ID, $topicId: ID) {\n trackPracticeProgress(questionId: $questionId, isCorrect: $isCorrect, subjectId: $subjectId, chapterId: $chapterId, topicId: $topicId) { status }\n }`,\n variables\n );\n return result.data?.trackPracticeProgress || { status: \"success\" };\n }\n\n async getSubjectProgress(userId: string): Promise<UserSubjectProgress[]> {\n try {\n const result = await this.network.query<{\n listUserSubjectProgressByUserIdAndSubjectId?: PaginatedList<UserSubjectProgress>;\n listUserSubjectProgressesByUserIdAndSubjectId?: PaginatedList<UserSubjectProgress>;\n }>(\n `query ListSubjectProgress($userId: String!) {\n listUserSubjectProgressByUserIdAndSubjectId(userId: $userId) {\n items { id userId subjectId practicedCount mistakeCount correctedCount accuracyScore }\n }\n }`,\n { userId }\n );\n return (\n result.data?.listUserSubjectProgressByUserIdAndSubjectId?.items ||\n (result.data as any)?.listUserSubjectProgressesByUserIdAndSubjectId?.items ||\n []\n );\n } catch (e) {\n console.error(\"Error fetching subject progress:\", e);\n return [];\n }\n }\n\n async getChapterProgress(userId: string): Promise<UserChapterProgress[]> {\n try {\n const result = await this.network.query<{\n listUserChapterProgressByUserIdAndChapterId?: PaginatedList<UserChapterProgress>;\n listUserChapterProgressesByUserIdAndChapterId?: PaginatedList<UserChapterProgress>;\n }>(\n `query ListChapterProgress($userId: String!) {\n listUserChapterProgressByUserIdAndChapterId(userId: $userId) {\n items { id userId chapterId practicedCount mistakeCount correctedCount accuracyScore }\n }\n }`,\n { userId }\n );\n return (\n result.data?.listUserChapterProgressByUserIdAndChapterId?.items ||\n (result.data as any)?.listUserChapterProgressesByUserIdAndChapterId?.items ||\n []\n );\n } catch (e) {\n console.error(\"Error fetching chapter progress:\", e);\n return [];\n }\n }\n\n async getTopicProgress(userId: string): Promise<UserTopicProgress[]> {\n try {\n const result = await this.network.query<{\n listUserTopicProgressByUserIdAndTopicId?: PaginatedList<UserTopicProgress>;\n listUserTopicProgressesByUserIdAndTopicId?: PaginatedList<UserTopicProgress>;\n }>(\n `query ListTopicProgress($userId: String!) {\n listUserTopicProgressByUserIdAndTopicId(userId: $userId) {\n items { id userId topicId practicedCount mistakeCount correctedCount accuracyScore }\n }\n }`,\n { userId }\n );\n return (\n result.data?.listUserTopicProgressByUserIdAndTopicId?.items ||\n (result.data as any)?.listUserTopicProgressesByUserIdAndTopicId?.items ||\n []\n );\n } catch (e) {\n console.error(\"Error fetching topic progress:\", e);\n return [];\n }\n }\n\n async getTopicMetrics(userId: string): Promise<UserTopicMetric[]> {\n try {\n const result = await this.network.query<{\n listUserTopicMetrics?: PaginatedList<UserTopicMetric>;\n }>(\n `query ListTopicMetrics($filter: ModelUserTopicMetricFilterInput) {\n listUserTopicMetrics(filter: $filter) {\n items { id userId topicId totalAttempted totalCorrect accuracy }\n }\n }`,\n { filter: { userId: { eq: userId } } }\n );\n return result.data?.listUserTopicMetrics?.items || [];\n } catch (e) {\n console.error(\"Error fetching topic metrics:\", e);\n return [];\n }\n }\n\n async getMistakenQuestions(userId: string): Promise<QuestionTracking[]> {\n try {\n const result = await this.network.query<{\n listQuestionTrackingByUserIdAndQuestionId?: PaginatedList<QuestionTracking>;\n listQuestionTrackingsByUserIdAndQuestionId?: PaginatedList<QuestionTracking>;\n listQuestionTrackings?: PaginatedList<QuestionTracking>;\n }>(\n `query ListMistakes($userId: String!, $filter: ModelQuestionTrackingFilterInput) {\n listQuestionTrackingByUserIdAndQuestionId(userId: $userId, filter: $filter) {\n items { id userId questionId subjectId chapterId topicId status attempts }\n }\n }`,\n { userId, filter: { status: { eq: \"MISTAKE\" } } }\n );\n return (\n result.data?.listQuestionTrackingByUserIdAndQuestionId?.items ||\n (result.data as any)?.listQuestionTrackingsByUserIdAndQuestionId?.items ||\n (result.data as any)?.listQuestionTrackings?.items ||\n []\n );\n } catch (e) {\n console.error(\"Error fetching mistaken questions:\", e);\n return [];\n }\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { EvaluateWrittenAnswerInput } from \"../types/inputs\";\nimport type { EvaluateWrittenAnswerResponse } from \"../types/responses\";\n\nexport class AIService {\n constructor(private network: NetworkProvider) {}\n\n async evaluateWrittenAnswer(input: EvaluateWrittenAnswerInput): Promise<EvaluateWrittenAnswerResponse> {\n const result = await this.network.query<{ evaluateWrittenAnswer: EvaluateWrittenAnswerResponse }>(\n `query EvaluateWrittenAnswer($question: String!, $userAnswer: String!, $correctAnswer: String!, $markingInstructions: String, $topicId: String, $topicName: String, $questionImageUrl: String) {\n evaluateWrittenAnswer(question: $question, userAnswer: $userAnswer, correctAnswer: $correctAnswer, markingInstructions: $markingInstructions, topicId: $topicId, topicName: $topicName, questionImageUrl: $questionImageUrl) {\n score maxScore feedback isCorrect markingExplanation improvementFeedback subquestions markLocation\n }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.evaluateWrittenAnswer;\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { NewsArticle, PaginatedList } from \"../types/models\";\n\nexport class NewsService {\n constructor(private network: NetworkProvider) {}\n\n async create(\n title: string,\n content: string,\n image?: string,\n category?: string,\n excerpt?: string\n ): Promise<NewsArticle | null> {\n const result = await this.network.mutate<{ createNewsArticle: NewsArticle | null }>(\n `mutation CreateNews($input: CreateNewsArticleInput!) {\n createNewsArticle(input: $input) { id title content image category excerpt createdAt updatedAt }\n }`,\n { input: { title, content, image, category, excerpt } }\n );\n return result.data.createNewsArticle;\n }\n\n async list(nextToken?: string): Promise<PaginatedList<NewsArticle>> {\n const result = await this.network.query<{\n listNewsArticles: PaginatedList<NewsArticle>;\n }>(\n `query ListNews($nextToken: String) {\n listNewsArticles(nextToken: $nextToken) {\n items { id title content image category excerpt createdAt updatedAt }\n nextToken\n }\n }`,\n { nextToken }\n );\n return result.data.listNewsArticles;\n }\n\n async get(id: string): Promise<NewsArticle | null> {\n const result = await this.network.query<{ getNewsArticle: NewsArticle | null }>(\n `query GetNews($id: ID!) {\n getNewsArticle(id: $id) { id title content image category excerpt createdAt updatedAt }\n }`,\n { id }\n );\n return result.data.getNewsArticle;\n }\n}\n","import type { AuthProvider } from \"./providers/auth\";\nimport type { NetworkProvider } from \"./providers/network\";\nimport type { StorageProvider } from \"./providers/storage\";\nimport { UserService } from \"./services/user.service\";\nimport { QuestionService } from \"./services/question.service\";\nimport { PackService } from \"./services/pack.service\";\nimport { ContestService } from \"./services/contest.service\";\nimport { CourseService } from \"./services/course.service\";\nimport { CurriculumService } from \"./services/curriculum.service\";\nimport { MediaService } from \"./services/media.service\";\nimport { ProgressService } from \"./services/progress.service\";\nimport { AIService } from \"./services/ai.service\";\nimport { NewsService } from \"./services/news.service\";\n\nexport interface OrjokClientConfig {\n authProvider: AuthProvider;\n networkProvider: NetworkProvider;\n storageProvider?: StorageProvider;\n}\n\nexport class OrjokClient {\n readonly auth: AuthProvider;\n readonly users: UserService;\n readonly questions: QuestionService;\n readonly packs: PackService;\n readonly contests: ContestService;\n readonly courses: CourseService;\n readonly curriculum: CurriculumService;\n readonly media: MediaService;\n readonly progress: ProgressService;\n readonly ai: AIService;\n readonly news: NewsService;\n readonly storage?: StorageProvider;\n\n constructor(config: OrjokClientConfig) {\n this.auth = config.authProvider;\n this.storage = config.storageProvider;\n this.users = new UserService(config.networkProvider);\n this.questions = new QuestionService(config.networkProvider);\n this.packs = new PackService(config.networkProvider);\n this.contests = new ContestService(config.networkProvider);\n this.courses = new CourseService(config.networkProvider);\n this.curriculum = new CurriculumService(config.networkProvider);\n this.media = new MediaService(config.networkProvider, config.storageProvider);\n this.progress = new ProgressService(config.networkProvider);\n this.ai = new AIService(config.networkProvider);\n this.news = new NewsService(config.networkProvider);\n }\n}\n\nexport function createOrjokClient(config: OrjokClientConfig): OrjokClient {\n return new OrjokClient(config);\n}\n","import type { Difficulty, Language, Level } from \"./enums\";\n\nexport interface SelectOption<T = string> {\n value: T;\n label: string;\n}\n\nexport const levelOptions: SelectOption<Level>[] = [\n { value: \"SSC\", label: \"SSC\" },\n { value: \"HSC+Admission\", label: \"HSC/Admission\" },\n { value: \"BCS\", label: \"BCS\" },\n];\n\nexport const languageOptions: SelectOption<Language>[] = [\n { value: \"Bangla\", label: \"Bangla\" },\n { value: \"English\", label: \"English\" },\n { value: \"Any\", label: \"Any\" },\n];\n\nexport const difficultyOptions: SelectOption<Difficulty>[] = [\n { value: \"Easy\", label: \"Easy\" },\n { value: \"Medium\", label: \"Medium\" },\n { value: \"Hard\", label: \"Hard\" },\n];\n","import type {\n ColorOption,\n AvatarConfig,\n AvatarConfigItem,\n AvatarState,\n UICategory,\n} from \"../types/avatar\";\n\nexport const HAIR_COLORS: ColorOption[] = [\n { name: \"Original\", filter: \"none\", hex: \"#e2e8f0\", brightness: 1.0 },\n { name: \"White\", filter: \"grayscale(100%) brightness(500%)\", hex: \"#ffffff\", brightness: 2.0 },\n { name: \"Black\", filter: \"grayscale(100%) brightness(40%)\", hex: \"#1a202c\", brightness: 0.15 },\n { name: \"Brown\", filter: \"sepia(100%) saturate(300%) hue-rotate(330deg) brightness(80%)\", hex: \"#7b3f00\", brightness: 0.35 },\n { name: \"Blonde\", filter: \"sepia(100%) saturate(400%) hue-rotate(20deg) brightness(130%)\", hex: \"#e6c27a\", brightness: 0.8 },\n { name: \"Red\", filter: \"sepia(100%) saturate(500%) hue-rotate(320deg) brightness(90%)\", hex: \"#9b2c2c\", brightness: 0.4 },\n { name: \"Blue\", filter: \"sepia(100%) saturate(500%) hue-rotate(180deg) brightness(90%)\", hex: \"#2b6cb0\", brightness: 0.4 },\n { name: \"Green\", filter: \"sepia(100%) saturate(400%) hue-rotate(80deg) brightness(90%)\", hex: \"#2f855a\", brightness: 0.45 },\n];\n\nexport const CLOTHES_COLORS: ColorOption[] = [\n { name: \"Original\", filter: \"none\", hex: \"#e2e8f0\", brightness: 1.0 },\n { name: \"White\", filter: \"grayscale(100%) brightness(500%)\", hex: \"#ffffff\", brightness: 2.0 },\n { name: \"Black\", filter: \"grayscale(100%) brightness(30%)\", hex: \"#1a202c\", brightness: 0.12 },\n { name: \"Red\", filter: \"sepia(100%) saturate(500%) hue-rotate(320deg)\", hex: \"#c53030\", brightness: 0.45 },\n { name: \"Orange\", filter: \"sepia(100%) saturate(500%) hue-rotate(350deg)\", hex: \"#dd6b20\", brightness: 0.55 },\n { name: \"Yellow\", filter: \"sepia(100%) saturate(500%) hue-rotate(20deg) brightness(120%)\", hex: \"#d69e2e\", brightness: 0.8 },\n { name: \"Green\", filter: \"sepia(100%) saturate(500%) hue-rotate(80deg)\", hex: \"#38a169\", brightness: 0.55 },\n { name: \"Blue\", filter: \"sepia(100%) saturate(500%) hue-rotate(180deg)\", hex: \"#3182ce\", brightness: 0.45 },\n { name: \"Purple\", filter: \"sepia(100%) saturate(500%) hue-rotate(240deg)\", hex: \"#805ad5\", brightness: 0.45 },\n { name: \"Pink\", filter: \"sepia(100%) saturate(400%) hue-rotate(290deg)\", hex: \"#d53f8c\", brightness: 0.55 },\n];\n\nexport const COLOR_HUES: Record<string, number> = {\n Original: 0,\n Red: 0,\n Orange: 24,\n Yellow: 45,\n Green: 140,\n Blue: 210,\n Purple: 270,\n Pink: 320,\n Brown: 25,\n Blonde: 45,\n};\n\nexport const RENDER_ORDER = [\n \"Background\",\n \"Hair Back\",\n \"Clothes Back\",\n \"Skin Color Body\",\n \"Clothes Front\",\n \"Skin Color Head\",\n \"Accessories\",\n \"Facial Expression\",\n \"Beard\",\n \"Hair Front\",\n \"Eyewear\",\n \"Headwears\",\n] as const;\n\nexport const INITIAL_AVATAR_DATA: Record<UICategory, string[]> = {\n Background: [],\n Hair: [],\n \"Skin Color\": [],\n Clothes: [],\n Accessories: [],\n Headwears: [],\n Eyewear: [],\n \"Facial Expression\": [],\n Beard: [],\n};\n\nexport function getActualOptionFileName(\n category: string,\n baseName: string,\n config: AvatarConfig,\n selections: Record<string, string>\n): AvatarConfigItem | null {\n if (baseName === \"None\") return null;\n const items = config[category] || [];\n const matched = items.filter((item) => item.base_name === baseName);\n\n if (matched.length === 0) return null;\n\n for (const item of matched) {\n if (item.conditions && item.conditions.length > 0) {\n const allMet = item.conditions.every((cond) => {\n return Object.values(selections).some(\n (sel) => sel && (sel.includes(cond) || cond.includes(sel))\n );\n });\n if (allMet) return item;\n }\n }\n\n return matched.find((item) => !item.conditions || item.conditions.length === 0) || matched[0] || null;\n}\n\nexport function parseAvatarConfig(configStr: string | null | undefined): AvatarState {\n if (!configStr) {\n return { selections: {}, colors: {} };\n }\n try {\n const parsed = JSON.parse(configStr);\n return {\n selections: parsed.selections || {},\n colors: parsed.colors || {},\n };\n } catch {\n return { selections: {}, colors: {} };\n }\n}\n\nexport function serializeAvatarConfig(state: AvatarState): string {\n return JSON.stringify(state);\n}\n","export interface EloTier {\n name: string;\n minRating: number;\n maxRating: number;\n color: string;\n}\n\nconst TIERS: EloTier[] = [\n { name: \"Iron\", minRating: 0, maxRating: 799, color: \"#6B7280\" },\n { name: \"Bronze\", minRating: 800, maxRating: 999, color: \"#CD7F32\" },\n { name: \"Silver\", minRating: 1000, maxRating: 1199, color: \"#C0C0C0\" },\n { name: \"Gold\", minRating: 1200, maxRating: 1399, color: \"#FFD700\" },\n { name: \"Platinum\", minRating: 1400, maxRating: 1599, color: \"#00CED1\" },\n { name: \"Diamond\", minRating: 1600, maxRating: 1799, color: \"#B9F2FF\" },\n { name: \"Master\", minRating: 1800, maxRating: 1999, color: \"#9B59B6\" },\n { name: \"Grandmaster\", minRating: 2000, maxRating: 2399, color: \"#E74C3C\" },\n { name: \"Challenger\", minRating: 2400, maxRating: Infinity, color: \"#F1C40F\" },\n];\n\nexport function getEloTier(rating: number): EloTier {\n return TIERS.find((t) => rating >= t.minRating && rating <= t.maxRating) ?? TIERS[0]!;\n}\n\nexport function getEloProgress(rating: number): number {\n const tier = getEloTier(rating);\n if (tier.maxRating === Infinity) return 100;\n const range = tier.maxRating - tier.minRating;\n return Math.round(((rating - tier.minRating) / range) * 100);\n}\n\nexport function getAllTiers(): EloTier[] {\n return [...TIERS];\n}\n\nexport type RatingField =\n | \"sscOverallRating\"\n | \"sscPhysicsRating\"\n | \"sscChemistryRating\"\n | \"sscMathRating\"\n | \"sscBiologyRating\"\n | \"hscOverallRating\"\n | \"hscPhysicsRating\"\n | \"hscChemistryRating\"\n | \"hscMathRating\"\n | \"hscBiologyRating\"\n | \"bcsOverallRating\"\n | \"EngineeringRating\"\n | \"MedicalRating\"\n | \"VarsityRating\"\n | \"BCSRating\";\n\nexport function getRatingForSubject(\n user: Record<string, number | null | undefined>,\n subject: string\n): number {\n const key = subject as RatingField;\n return (user[key] as number | null | undefined) ?? 1000;\n}\n\nexport function computeRatingDelta(beforeRating: number | null | undefined, afterRating: number | null | undefined): number {\n if (beforeRating == null || afterRating == null) return 0;\n return afterRating - beforeRating;\n}\n","import type { QuestionObject } from \"../types/models\";\nimport type { Difficulty, QuestionType } from \"../types/enums\";\n\nexport type ExamMode = \"MCQ\" | \"WRITTEN\" | \"BOTH\";\n\nexport interface ExamConfig {\n mode: ExamMode;\n timePerQuestion: number;\n}\n\nexport function filterQuestionsByMode(\n questions: QuestionObject[],\n mode: ExamMode\n): QuestionObject[] {\n if (mode === \"BOTH\") return questions;\n return questions.filter((q) => q.type === mode || (!q.type && mode === \"MCQ\"));\n}\n\nexport function calculateExamTime(questionCount: number, secondsPerQuestion: number = 60): number {\n return questionCount * secondsPerQuestion;\n}\n\nexport function formatExamTime(totalSeconds: number): string {\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n if (hours > 0) {\n return `${hours}:${String(minutes).padStart(2, \"0\")}:${String(seconds).padStart(2, \"0\")}`;\n }\n return `${minutes}:${String(seconds).padStart(2, \"0\")}`;\n}\n\nexport function scoreExam(\n selectedOptions: (string | null)[],\n correctAnswers: string[]\n): { correct: number; incorrect: number; unanswered: number; total: number } {\n let correct = 0;\n let incorrect = 0;\n let unanswered = 0;\n\n for (let i = 0; i < correctAnswers.length; i++) {\n const selected = selectedOptions[i];\n if (!selected) {\n unanswered++;\n } else if (selected === correctAnswers[i]) {\n correct++;\n } else {\n incorrect++;\n }\n }\n\n return { correct, incorrect, unanswered, total: correctAnswers.length };\n}\n\nexport function isImageAnswer(answer: string | null | undefined): boolean {\n if (!answer) return false;\n return answer.startsWith(\"image::\") || answer.startsWith(\"images::\");\n}\n\nexport function parseImageKeys(answer: string): string[] {\n if (answer.startsWith(\"images::\")) {\n return answer.slice(\"images::\".length).split(\",\").filter(Boolean);\n }\n if (answer.startsWith(\"image::\")) {\n const key = answer.slice(\"image::\".length);\n return key ? [key] : [];\n }\n return [];\n}\n\nexport function buildImageAnswer(keys: string[]): string {\n if (keys.length === 0) return \"\";\n if (keys.length === 1) return `image::${keys[0]}`;\n return `images::${keys.join(\",\")}`;\n}\n\nexport function normalizeDifficulty(raw: string): Difficulty | undefined {\n const trimmed = raw.trim();\n if (!trimmed) return undefined;\n const capitalized = trimmed.charAt(0).toUpperCase() + trimmed.slice(1).toLowerCase();\n if (capitalized === \"Easy\" || capitalized === \"Medium\" || capitalized === \"Hard\") {\n return capitalized as Difficulty;\n }\n return undefined;\n}\n\nexport function detectQuestionType(\n option2: string,\n option3: string,\n option4: string\n): QuestionType {\n if (!option2.trim() && !option3.trim() && !option4.trim()) {\n return \"WRITTEN\";\n }\n return \"MCQ\";\n}\n\nexport function isCorrectAnswer(\n selected: string | null | undefined,\n correct: string | null | undefined\n): boolean {\n if (!selected || !correct) return false;\n const selTrim = String(selected).trim();\n const corrTrim = String(correct).trim();\n if (selTrim === corrTrim) return true;\n\n const selNum = Number(selTrim);\n const corrNum = Number(corrTrim);\n if (!isNaN(selNum) && !isNaN(corrNum) && selNum === corrNum) {\n return true;\n }\n return false;\n}\n\nexport function calculateDefaultExamMinutes(\n mode: ExamMode,\n questions: Array<{ type?: string | null }>\n): number {\n const mcqCount = questions.filter((q) => q.type !== \"WRITTEN\").length;\n const writtenCount = questions.filter((q) => q.type === \"WRITTEN\").length;\n\n if (mode === \"MCQ\") return Math.max(1, mcqCount * 1);\n if (mode === \"WRITTEN\") return Math.max(1, writtenCount * 10);\n return Math.max(1, mcqCount * 1 + writtenCount * 10);\n}\n\nexport function reorderObjectKeys<T>(\n obj: { [key: number]: T },\n oldIndex: number,\n newIndex: number,\n length: number\n): { [key: number]: T } {\n const arr: (T | undefined)[] = [];\n for (let i = 0; i < length; i++) {\n arr.push(obj[i]);\n }\n const [movedItem] = arr.splice(oldIndex, 1);\n arr.splice(newIndex, 0, movedItem);\n const newObj: { [key: number]: T } = {};\n arr.forEach((item, idx) => {\n if (item !== undefined) {\n newObj[idx] = item;\n }\n });\n return newObj;\n}\n\n","import type { Difficulty } from \"../types/enums\";\n\nexport interface ParsedQuestion {\n id?: string;\n imageUrl?: string;\n question: string;\n answer: string;\n option2: string;\n option3: string;\n option4: string;\n explanation: string;\n tags: string[];\n extra?: string;\n difficulty?: Difficulty;\n subjectId?: string;\n chapterId?: string;\n topicId?: string;\n type: \"MCQ\" | \"WRITTEN\";\n markingInstructions?: string;\n answerImageUrl?: string;\n option2ImageUrl?: string;\n option3ImageUrl?: string;\n option4ImageUrl?: string;\n}\n\nexport interface ParseResult {\n questions: ParsedQuestion[];\n errors: string[];\n}\n\nconst MAX_QUESTIONS = 200;\n\nfunction parseFields(parts: string[]): Omit<ParsedQuestion, \"question\" | \"answer\" | \"option2\" | \"option3\" | \"option4\" | \"explanation\" | \"tags\" | \"type\"> {\n let extra = \"\";\n let difficulty: Difficulty | undefined;\n let subjectId = \"\";\n let chapterId = \"\";\n let topicId = \"\";\n let markingInstructions = \"\";\n let id = \"\";\n let imageUrl = \"\";\n let answerImageUrl = \"\";\n let option2ImageUrl = \"\";\n let option3ImageUrl = \"\";\n let option4ImageUrl = \"\";\n\n if (parts.length >= 13) {\n extra = parts[7]?.trim() ?? \"\";\n const rawDiff = parts[8]?.trim() ?? \"\";\n if (rawDiff) {\n const cap = rawDiff.charAt(0).toUpperCase() + rawDiff.slice(1).toLowerCase();\n if (cap === \"Easy\" || cap === \"Medium\" || cap === \"Hard\") difficulty = cap as Difficulty;\n }\n subjectId = parts[9]?.trim() ?? \"\";\n chapterId = parts[10]?.trim() ?? \"\";\n topicId = parts[11]?.trim() ?? \"\";\n markingInstructions = parts[12]?.trim() ?? \"\";\n if (parts.length > 13) id = parts[13]?.trim() ?? \"\";\n if (parts.length > 14) imageUrl = parts[14]?.trim() ?? \"\";\n if (parts.length > 15) answerImageUrl = parts[15]?.trim() ?? \"\";\n if (parts.length > 16) option2ImageUrl = parts[16]?.trim() ?? \"\";\n if (parts.length > 17) option3ImageUrl = parts[17]?.trim() ?? \"\";\n if (parts.length > 18) option4ImageUrl = parts[18]?.trim() ?? \"\";\n } else if (parts.length === 8) {\n extra = parts[7]?.trim() ?? \"\";\n } else if (parts.length === 9) {\n extra = parts[7]?.trim() ?? \"\";\n const rawDiff = parts[8]?.trim() ?? \"\";\n if (rawDiff) {\n const cap = rawDiff.charAt(0).toUpperCase() + rawDiff.slice(1).toLowerCase();\n if (cap === \"Easy\" || cap === \"Medium\" || cap === \"Hard\") difficulty = cap as Difficulty;\n }\n } else if (parts.length === 10) {\n subjectId = parts[7]?.trim() ?? \"\";\n chapterId = parts[8]?.trim() ?? \"\";\n topicId = parts[9]?.trim() ?? \"\";\n } else if (parts.length === 11) {\n extra = parts[7]?.trim() ?? \"\";\n subjectId = parts[8]?.trim() ?? \"\";\n chapterId = parts[9]?.trim() ?? \"\";\n topicId = parts[10]?.trim() ?? \"\";\n } else if (parts.length === 12) {\n extra = parts[7]?.trim() ?? \"\";\n const rawDiff = parts[8]?.trim() ?? \"\";\n if (rawDiff) {\n const cap = rawDiff.charAt(0).toUpperCase() + rawDiff.slice(1).toLowerCase();\n if (cap === \"Easy\" || cap === \"Medium\" || cap === \"Hard\") difficulty = cap as Difficulty;\n }\n subjectId = parts[9]?.trim() ?? \"\";\n chapterId = parts[10]?.trim() ?? \"\";\n topicId = parts[11]?.trim() ?? \"\";\n }\n\n return {\n id: id || undefined,\n imageUrl: imageUrl || undefined,\n extra: extra || undefined,\n difficulty,\n subjectId: subjectId || undefined,\n chapterId: chapterId || undefined,\n topicId: topicId || undefined,\n markingInstructions: markingInstructions || undefined,\n answerImageUrl: answerImageUrl || undefined,\n option2ImageUrl: option2ImageUrl || undefined,\n option3ImageUrl: option3ImageUrl || undefined,\n option4ImageUrl: option4ImageUrl || undefined,\n };\n}\n\nfunction buildQuestion(parts: string[]): ParsedQuestion {\n const tags =\n parts.length >= 7 && parts[6]\n ? parts[6].split(\",\").map((t) => t.trim()).filter(Boolean)\n : [];\n\n const opt2 = parts[2]?.trim() ?? \"\";\n const opt3 = parts[3]?.trim() ?? \"\";\n const opt4 = parts[4]?.trim() ?? \"\";\n const isWritten = parts.length < 6 || (!opt2 && !opt3 && !opt4);\n\n const extra = parseFields(parts);\n\n return {\n question: parts[0]?.trim() ?? \"\",\n answer: parts[1]?.trim() ?? \"\",\n option2: opt2,\n option3: opt3,\n option4: opt4,\n explanation: parts[5]?.trim() ?? \"\",\n tags,\n type: isWritten ? \"WRITTEN\" : \"MCQ\",\n ...extra,\n };\n}\n\nexport function parseTextQuestions(text: string, keyword: string, minParts: number = 2): ParseResult {\n const lines = text.split(\"\\n\");\n const questions: ParsedQuestion[] = [];\n const errors: string[] = [];\n\n if (lines.length > MAX_QUESTIONS) {\n errors.push(`Can't have more than ${MAX_QUESTIONS} questions/lines`);\n return { questions, errors };\n }\n\n for (let idx = 0; idx < lines.length; idx++) {\n const line = lines[idx]!;\n const parts = line.split(keyword);\n if (parts.length === 1 && (parts[0] === \"\\r\" || parts[0] === \"\\n\" || parts[0] === \"\")) {\n continue;\n }\n if (parts.length < minParts || parts.length > 19) {\n errors.push(`Line ${idx + 1} doesn't have between ${minParts} and 19 parts`);\n continue;\n }\n questions.push(buildQuestion(parts));\n }\n\n return { questions, errors };\n}\n\nexport function parseCsvQuestions(rows: string[][]): ParseResult {\n const questions: ParsedQuestion[] = [];\n const errors: string[] = [];\n\n // Skip header row\n const dataRows = rows.slice(1);\n if (dataRows.length > MAX_QUESTIONS) {\n errors.push(`Can't have more than ${MAX_QUESTIONS} questions/lines`);\n return { questions, errors };\n }\n\n for (let idx = 0; idx < dataRows.length; idx++) {\n const line = dataRows[idx]!;\n if (line.length < 2 || line.length > 19) {\n errors.push(`Line ${idx + 2} doesn't have between 2 and 19 parts`);\n continue;\n }\n questions.push(buildQuestion(line));\n }\n\n return { questions, errors };\n}\n\nexport function formatQuestionsToText(\n questions: ParsedQuestion[],\n keyword: string,\n packSubjectId?: string,\n packChapterId?: string,\n packTopicId?: string\n): string {\n const lines = questions.map((q) => {\n const activeSubject = q.subjectId || packSubjectId;\n const activeChapter = q.chapterId || packChapterId;\n const activeTopic = q.topicId || packTopicId;\n\n const subjectSlug = activeSubject ? activeSubject.split(\"::\")[0]?.trim() ?? \"\" : \"\";\n const chapterSlug = activeChapter ? activeChapter.split(\"::\")[0]?.trim() ?? \"\" : \"\";\n const topicSlug = activeTopic ? activeTopic.split(\"::\")[0]?.trim() ?? \"\" : \"\";\n\n const fields = [\n q.question,\n q.answer,\n q.type === \"WRITTEN\" ? \"\" : q.option2,\n q.type === \"WRITTEN\" ? \"\" : q.option3,\n q.type === \"WRITTEN\" ? \"\" : q.option4,\n q.explanation,\n q.tags.join(\", \"),\n q.extra ?? \"\",\n q.difficulty ?? \"\",\n subjectSlug,\n chapterSlug,\n topicSlug,\n q.markingInstructions ?? \"\",\n q.id ?? \"\",\n q.imageUrl ?? \"\",\n q.answerImageUrl ?? \"\",\n q.option2ImageUrl ?? \"\",\n q.option3ImageUrl ?? \"\",\n q.option4ImageUrl ?? \"\",\n ];\n return fields.map((f) => (f || \"\").trim()).join(keyword);\n });\n\n return lines.join(\"\\n\");\n}\n\nexport function formatQuestionsToCsv(\n questions: ParsedQuestion[],\n packSubjectId?: string,\n packChapterId?: string,\n packTopicId?: string\n): string {\n const escapeCSV = (value: string | undefined) =>\n `\"${(value || \"\").replace(/\"/g, '\"\"')}\"`;\n\n const header =\n \"Question,Answer,Option2,Option3,Option4,Explanation,Tags,Extra,Difficulty,Subject,Chapter,Topic,MarkingInstructions,Id,ImageUrl,AnswerImageUrl,Option2ImageUrl,Option3ImageUrl,Option4ImageUrl\";\n\n const rows = questions.map((q) => {\n const activeSubject = q.subjectId || packSubjectId;\n const activeChapter = q.chapterId || packChapterId;\n const activeTopic = q.topicId || packTopicId;\n\n const subjectSlug = activeSubject ? activeSubject.split(\"::\")[0]?.trim() ?? \"\" : \"\";\n const chapterSlug = activeChapter ? activeChapter.split(\"::\")[0]?.trim() ?? \"\" : \"\";\n const topicSlug = activeTopic ? activeTopic.split(\"::\")[0]?.trim() ?? \"\" : \"\";\n\n return [\n escapeCSV(q.question),\n escapeCSV(q.answer),\n escapeCSV(q.type === \"WRITTEN\" ? \"\" : q.option2),\n escapeCSV(q.type === \"WRITTEN\" ? \"\" : q.option3),\n escapeCSV(q.type === \"WRITTEN\" ? \"\" : q.option4),\n escapeCSV(q.explanation),\n escapeCSV(q.tags.join(\", \")),\n escapeCSV(q.extra),\n escapeCSV(q.difficulty),\n escapeCSV(subjectSlug),\n escapeCSV(chapterSlug),\n escapeCSV(topicSlug),\n escapeCSV(q.markingInstructions),\n escapeCSV(q.id),\n escapeCSV(q.imageUrl),\n escapeCSV(q.answerImageUrl),\n escapeCSV(q.option2ImageUrl),\n escapeCSV(q.option3ImageUrl),\n escapeCSV(q.option4ImageUrl),\n ].join(\",\");\n });\n\n return `${header}\\n${rows.join(\"\\n\")}`;\n}\n","export type PrimaryTab = \"home\" | \"learn\" | \"contests\" | \"courses\" | \"profile\";\n\nexport function getActiveTab(pathname: string): PrimaryTab {\n const normalized = pathname.toLowerCase();\n\n if (\n normalized.startsWith(\"/contests\") ||\n normalized.startsWith(\"/contest\")\n ) {\n return \"contests\";\n }\n\n if (\n normalized.startsWith(\"/courses\") ||\n normalized.startsWith(\"/course\") ||\n normalized.startsWith(\"/exams\")\n ) {\n return \"courses\";\n }\n\n if (\n normalized.startsWith(\"/profile\") ||\n normalized.startsWith(\"/dashboard\") ||\n normalized.startsWith(\"/edit-profile\")\n ) {\n return \"profile\";\n }\n\n if (\n normalized.startsWith(\"/learn\") ||\n normalized.startsWith(\"/practice\") ||\n normalized.startsWith(\"/questions\") ||\n normalized.startsWith(\"/question\") ||\n normalized.startsWith(\"/bank\") ||\n normalized.startsWith(\"/question-bank\") ||\n normalized.startsWith(\"/packs\") ||\n normalized.startsWith(\"/pack\") ||\n normalized.startsWith(\"/taxonomy\") ||\n normalized.startsWith(\"/explore\")\n ) {\n return \"learn\";\n }\n\n return \"home\";\n}\n\nexport function getCanonicalPath(pathname: string): string {\n const normalized = pathname.toLowerCase();\n\n if (normalized === \"/explore/questions\") return \"/learn/questions\";\n if (normalized === \"/explore/packs\") return \"/learn/packs\";\n if (normalized === \"/question-bank\") return \"/learn/bank\";\n if (normalized === \"/contest/all\") return \"/contests\";\n if (normalized === \"/dashboard\") return \"/profile\";\n if (normalized === \"/edit-profile\") return \"/profile/edit\";\n\n return pathname;\n}\n\nexport function isAuthGatedRoute(pathname: string): boolean {\n const normalized = pathname.toLowerCase();\n\n const isProtectedPath =\n normalized.includes(\"/exam\") ||\n normalized.includes(\"/enroll\") ||\n normalized.startsWith(\"/profile\") ||\n normalized.startsWith(\"/dashboard\") ||\n normalized.startsWith(\"/edit-profile\") ||\n normalized.startsWith(\"/avatar-creator\") ||\n normalized.startsWith(\"/practice\");\n\n return isProtectedPath;\n}\n","const BANGLA_DIGITS = [\"০\", \"১\", \"২\", \"৩\", \"৪\", \"৫\", \"৬\", \"৭\", \"৮\", \"৯\"] as const;\n\nexport function engToBanglaNumber(num: number | string): string {\n return String(num)\n .split(\"\")\n .map((digit) => {\n const n = parseInt(digit, 10);\n return !isNaN(n) ? BANGLA_DIGITS[n]! : digit;\n })\n .join(\"\");\n}\n","/**\n * Splits a bilingual string formatted as \"Bangla Name (English Name)\"\n * into the requested language (\"bn\" or \"en\").\n * If the string does not follow the bilingual pattern, returns the clean string.\n */\nexport function extractBilingualName(name: string, language?: \"bn\" | \"en\" | string): string {\n if (!name) return \"\";\n const trimmed = name.trim();\n\n // Match \"Part1 (Part2)\" or \"Part1 [Part2]\" or full-width \"Part1 (Part2)\"\n const match = trimmed.match(/^(.*?)\\s*[\\(\\[(]\\s*([^\\)\\])]+)\\s*[\\)\\])]\\s*$/);\n if (!match) {\n return trimmed;\n }\n\n const part1 = match[1]?.trim() || \"\";\n const part2 = match[2]?.trim() || \"\";\n\n if (!part1 && !part2) return trimmed;\n if (!part1) return part2;\n if (!part2) return part1;\n\n const hasBengali = (text: string) => /[\\u0980-\\u09FF]/.test(text);\n\n const isPart1Bengali = hasBengali(part1);\n const isPart2Bengali = hasBengali(part2);\n\n let banglaPart = part1;\n let englishPart = part2;\n\n if (!isPart1Bengali && isPart2Bengali) {\n banglaPart = part2;\n englishPart = part1;\n } else {\n banglaPart = part1;\n englishPart = part2;\n }\n\n const lang = language?.toLowerCase();\n if (lang === \"en\") {\n return englishPart || banglaPart || trimmed;\n }\n if (lang === \"bn\") {\n return banglaPart || englishPart || trimmed;\n }\n\n return trimmed;\n}\n\nexport function formatCurriculumName(\n idString: string,\n dbName?: string | null,\n language?: \"bn\" | \"en\" | string\n): string {\n if (dbName) {\n return extractBilingualName(dbName, language);\n }\n if (!idString) return \"\";\n const namePart = idString.split(\"::\")[0] ?? \"\";\n const clean = namePart.replace(/[-_]/g, \" \");\n const formatted = clean\n .split(/\\s+/)\n .map((word) => {\n if (!word) return \"\";\n return word.charAt(0).toUpperCase() + word.slice(1);\n })\n .join(\" \");\n\n return extractBilingualName(formatted, language);\n}\n\n","export function getRelativeTime(startTime: string | Date, endTime?: string | Date): string {\n const now = new Date();\n const start = new Date(startTime);\n const end = endTime ? new Date(endTime) : null;\n\n if (end && now >= start && now <= end) {\n return \"\";\n }\n\n const diffInSeconds = Math.floor((start.getTime() - now.getTime()) / 1000);\n const absDiff = Math.abs(diffInSeconds);\n\n const minute = 60;\n const hour = minute * 60;\n const day = hour * 24;\n const week = day * 7;\n const month = day * 30;\n const year = day * 365;\n\n if (diffInSeconds < 0) {\n if (absDiff < minute) return \"just now\";\n if (absDiff < hour) return `${Math.floor(absDiff / minute)} minutes ago`;\n if (absDiff < day) return `${Math.floor(absDiff / hour)} hours ago`;\n if (absDiff < week) return `${Math.floor(absDiff / day)} days ago`;\n if (absDiff < month) return `${Math.floor(absDiff / week)} weeks ago`;\n if (absDiff < year) return `${Math.floor(absDiff / month)} months ago`;\n return `${Math.floor(absDiff / year)} years ago`;\n } else {\n if (absDiff < minute) return \"in a moment\";\n if (absDiff < hour) return `in ${Math.floor(absDiff / minute)} minutes`;\n if (absDiff < day) return `in ${Math.floor(absDiff / hour)} hours`;\n if (absDiff < week) return `in ${Math.floor(absDiff / day)} days`;\n if (absDiff < month) return `in ${Math.floor(absDiff / week)} weeks`;\n if (absDiff < year) return `in ${Math.floor(absDiff / month)} months`;\n return `in ${Math.floor(absDiff / year)} years`;\n }\n}\n","export function processLatexText(str: string): string {\n if (!str) return str;\n\n const regex = /(\\$\\$(.*?)\\$\\$)|(\\$(.*?)\\$)/gs;\n\n let lastIndex = 0;\n let result = \"\";\n let match;\n\n while ((match = regex.exec(str)) !== null) {\n const textBefore = str.substring(lastIndex, match.index);\n result += textBefore.replace(/\\\\n/g, \"\\n\");\n\n const isDisplay = match[1] !== undefined;\n const mathContent = isDisplay ? match[2]! : match[4]!;\n const delimiter = isDisplay ? \"$$\" : \"$\";\n\n const processedMath = mathContent.replace(\n /(\\\\text\\s*\\{[^{}]*\\})|([\\u0980-\\u09FF]+(?:\\s+[\\u0980-\\u09FF]+)*)/g,\n (_: string, p1: string | undefined, p2: string | undefined) => {\n if (p1) return p1;\n return `\\\\text{${p2}}`;\n }\n );\n\n result += delimiter + processedMath + delimiter;\n lastIndex = regex.lastIndex;\n }\n\n const remainingText = str.substring(lastIndex);\n result += remainingText.replace(/\\\\n/g, \"\\n\");\n\n return result;\n}\n","export interface TimeLeft {\n days: number;\n hours: number;\n minutes: number;\n seconds: number;\n isEnded: boolean;\n}\n\nexport function convertToBangladeshTime(isoString: string): string {\n const options: Intl.DateTimeFormatOptions = {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"numeric\",\n hour12: true,\n };\n\n const date = new Date(isoString);\n\n // Convert to Bangladesh Standard Time (BST), which is UTC +6\n return date.toLocaleString(\"en-GB\", {\n ...options,\n timeZone: \"Asia/Dhaka\",\n });\n}\n\nexport function getDhakaNow(): Date {\n const now = new Date();\n const parts = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: \"Asia/Dhaka\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false,\n }).formatToParts(now);\n\n const get = (type: string) => parts.find((p) => p.type === type)?.value ?? \"00\";\n return new Date(`${get(\"year\")}-${get(\"month\")}-${get(\"day\")}T${get(\"hour\")}:${get(\"minute\")}:${get(\"second\")}`);\n}\n\nexport function calculateTimeLeft(endTime: string | Date): TimeLeft {\n const target = new Date(endTime).getTime();\n const now = new Date().getTime();\n const difference = target - now;\n\n if (difference <= 0 || isNaN(difference)) {\n return {\n days: 0,\n hours: 0,\n minutes: 0,\n seconds: 0,\n isEnded: true,\n };\n }\n\n return {\n days: Math.floor(difference / (1000 * 60 * 60 * 24)),\n hours: Math.floor((difference / (1000 * 60 * 60)) % 24),\n minutes: Math.floor((difference / 1000 / 60) % 60),\n seconds: Math.floor((difference / 1000) % 60),\n isEnded: false,\n };\n}\n\nexport function formatDate(date: Date | string, locale: string = \"en-GB\"): string {\n const d = typeof date === \"string\" ? new Date(date) : date;\n return d.toLocaleDateString(locale, {\n day: \"2-digit\",\n month: \"short\",\n year: \"numeric\",\n });\n}\n","export function formatBDT(amount: number): string {\n return amount.toLocaleString(\"en-US\");\n}\n","export function getYouTubeId(url: string | null | undefined): string | null {\n if (!url) return null;\n const regExp = /^.*(youtu.be\\/|v\\/|u\\/\\w\\/|embed\\/|watch\\?v=|[?&]v=)([^#&?]*).*/;\n const match = url.match(regExp);\n return match && match[2]?.length === 11 ? match[2] : null;\n}\n","export function sanitizeFileName(name: string): string {\n return name.replace(/[\\s,]+/g, \"_\");\n}\n","import type { Level, Language, Difficulty } from \"../types/enums\";\n\nexport function isQuestionLevel(value: unknown): value is Level {\n return [\n \"One\",\n \"Two\",\n \"Three\",\n \"Four\",\n \"Five\",\n \"Six\",\n \"Seven\",\n \"Eight\",\n \"SSC\",\n \"HSC\",\n \"Admission\",\n \"HSC+Admission\",\n \"BCS\",\n ].includes(value as string);\n}\n\nexport function isLanguage(value: unknown): value is Language {\n return [\"Bangla\", \"English\", \"Any\"].includes(value as string);\n}\n\nexport function isDifficulty(value: unknown): value is Difficulty {\n return [\"Easy\", \"Medium\", \"Hard\"].includes(value as string);\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/orjok/orjok/packages/commons/dist/index.cjs","../src/services/user.service.ts","../src/utils/pagination.ts","../src/services/question.service.ts","../src/services/pack.service.ts","../src/services/contest.service.ts","../src/services/course.service.ts","../src/services/curriculum.service.ts","../src/services/media.service.ts","../src/services/progress.service.ts","../src/services/ai.service.ts","../src/services/news.service.ts","../src/services/notification.service.ts","../src/services/subscription.service.ts","../src/client.ts","../src/logic/avatar.ts","../src/logic/elo.ts","../src/logic/exam.ts","../src/logic/home.ts","../src/logic/import-export.ts","../src/logic/subscription.ts","../src/navigation.ts","../src/utils/bangla-numbers.ts","../src/utils/format-curriculum.ts","../src/utils/relative-time.ts","../src/utils/latex.ts","../src/utils/time.ts","../src/utils/currency.ts","../src/utils/url.ts","../src/utils/file.ts","../src/utils/guards.ts"],"names":["variables"],"mappings":"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IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACwB,EAAA;AAC5B;AAE8D;AACxB,EAAA;AACtC;AAEkE;AAC9B,EAAA;AACpC;AAE8D;AACzC,EAAA;AACgB,EAAA;AACH,EAAA;AACM,EAAA;AACL,EAAA;AACL,EAAA;AACrB,EAAA;AACT;A9BmkH0C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/orjok/orjok/packages/commons/dist/index.cjs","sourcesContent":[null,"import type { NetworkProvider } from \"../providers/network\";\nimport type { UpdateUserAvatarInput, UpdateUserProfileInput, ListUsersInput } from \"../types/inputs\";\nimport type {\n User,\n PaginatedList,\n QuestionObject,\n Pack,\n Course,\n CourseEnrolled,\n} from \"../types/models\";\n\nexport class UserService {\n constructor(private network: NetworkProvider) {}\n\n async get(id: string): Promise<User | null> {\n const result = await this.network.query<{ getUser: User | null }>(\n `query GetUser($id: String!) {\n getUser(id: $id) {\n id fullName avatarUrl avatarConfig tier subscriptionExpiresAt\n aiEvaluationRemaining aiQuestionGenRemaining questionCount packCount contestCount\n EngineeringRating MedicalRating VarsityRating BCSRating\n sscOverallRating sscPhysicsRating sscChemistryRating sscMathRating sscBiologyRating\n hscOverallRating hscPhysicsRating hscChemistryRating hscMathRating hscBiologyRating\n bcsOverallRating\n xp userLevel\n }\n }`,\n { id }\n );\n return result.data.getUser;\n }\n\n async updateAvatar(input: UpdateUserAvatarInput): Promise<User | null> {\n const updateInput: Record<string, string | null> = { id: input.userId };\n if (input.avatarUrl !== undefined) updateInput.avatarUrl = input.avatarUrl;\n if (input.avatarConfig !== undefined) updateInput.avatarConfig = input.avatarConfig;\n\n const result = await this.network.mutate<{ updateUser: User | null }>(\n `mutation UpdateUser($input: UpdateUserInput!) {\n updateUser(input: $input) {\n id fullName avatarUrl avatarConfig\n }\n }`,\n { input: updateInput }\n );\n return result.data.updateUser;\n }\n\n async getQuestions(\n userId: string,\n nextToken?: string,\n limit: number = 10\n ): Promise<PaginatedList<QuestionObject>> {\n const result = await this.network.query<{\n listQuestionObjectByOwnerAndCreatedAt: PaginatedList<QuestionObject>;\n }>(\n `query ListUserQuestions($owner: ID!, $limit: Int, $nextToken: String, $sortDirection: ModelSortDirection) {\n listQuestionObjectByOwnerAndCreatedAt(owner: $owner, limit: $limit, sortDirection: $sortDirection, nextToken: $nextToken) {\n items {\n id question imageUrl language level difficulty type voteCount\n verificationStatus owner packId createdAt\n options { items { id content } }\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n { owner: userId, limit, nextToken, sortDirection: \"DESC\" }\n );\n return result.data.listQuestionObjectByOwnerAndCreatedAt;\n }\n\n async getPacks(\n userId: string,\n nextToken?: string,\n limit: number = 10\n ): Promise<PaginatedList<Pack>> {\n const result = await this.network.query<{\n listPackByOwnerAndCreatedAt: PaginatedList<Pack>;\n }>(\n `query ListUserPacks($owner: ID!, $limit: Int, $nextToken: String, $sortDirection: ModelSortDirection) {\n listPackByOwnerAndCreatedAt(owner: $owner, limit: $limit, sortDirection: $sortDirection, nextToken: $nextToken) {\n items {\n id name language level difficulty questionCount automaticNumbering\n owner subjectId chapterId topicId verificationStatus createdAt\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n { owner: userId, limit, nextToken, sortDirection: \"DESC\" }\n );\n return result.data.listPackByOwnerAndCreatedAt;\n }\n\n async getCourses(userId: string): Promise<Course[]> {\n const result = await this.network.query<{\n listCourseByOwnerAndCreatedAt: PaginatedList<Course>;\n }>(\n `query ListUserCourses($owner: ID!, $sortDirection: ModelSortDirection) {\n listCourseByOwnerAndCreatedAt(owner: $owner, sortDirection: $sortDirection) {\n items {\n id name description imageUrl videoCount fileCount price totalEnrolled owner createdAt\n }\n }\n }`,\n { owner: userId, sortDirection: \"DESC\" }\n );\n return result.data.listCourseByOwnerAndCreatedAt.items;\n }\n\n async getEnrolledCourses(userId: string): Promise<CourseEnrolled[]> {\n const result = await this.network.query<{\n listCourseEnrolledByUserIdAndCreatedAt: PaginatedList<CourseEnrolled>;\n }>(\n `query ListEnrolledCourses($userId: ID!, $sortDirection: ModelSortDirection) {\n listCourseEnrolledByUserIdAndCreatedAt(userId: $userId, sortDirection: $sortDirection) {\n items {\n id courseId userId score rank createdAt\n course { id name description imageUrl price totalEnrolled }\n }\n }\n }`,\n { userId, sortDirection: \"DESC\" }\n );\n return result.data.listCourseEnrolledByUserIdAndCreatedAt.items;\n }\n\n async updateProfile(input: UpdateUserProfileInput): Promise<User | null> {\n const result = await this.network.mutate<{ updateUser: User | null }>(\n `mutation UpdateUserProfile($input: UpdateUserInput!) {\n updateUser(input: $input) {\n id fullName avatarUrl avatarConfig tier subscriptionExpiresAt\n aiEvaluationRemaining aiQuestionGenRemaining questionCount packCount contestCount\n xp userLevel\n }\n }`,\n { input }\n );\n return result.data.updateUser;\n }\n\n async list(input: ListUsersInput = {}): Promise<PaginatedList<User>> {\n const result = await this.network.query<{ listUsers: PaginatedList<User> }>(\n `query ListUsers($limit: Int, $nextToken: String, $filter: ModelUserFilterInput) {\n listUsers(limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id fullName avatarUrl avatarConfig tier subscriptionExpiresAt\n aiEvaluationRemaining aiQuestionGenRemaining questionCount packCount contestCount\n xp userLevel\n }\n nextToken\n }\n }`,\n { limit: input.limit ?? 20, nextToken: input.nextToken, filter: input.filter }\n );\n return result.data.listUsers;\n }\n}\n\n","import type { PaginatedList } from \"../types/models\";\n\n/**\n * Accumulates items across paginated GraphQL queries until targetLimit is reached\n * or nextToken is exhausted (null/undefined).\n *\n * This guarantees that even when DynamoDB evaluates filter expressions post-read\n * and yields fewer than requested items in a single scan batch, the consumer\n * receives up to `targetLimit` matching items and the exact continuation token.\n */\nexport async function paginateWithAccumulator<T>(\n fetchPage: (limit: number, nextToken?: string | null) => Promise<PaginatedList<T>>,\n targetLimit = 12,\n initialNextToken?: string | null,\n maxIterations = 10\n): Promise<PaginatedList<T>> {\n const accumulated: T[] = [];\n let currentNextToken: string | null | undefined = initialNextToken;\n let iterations = 0;\n\n while (accumulated.length < targetLimit && iterations < maxIterations) {\n iterations++;\n const remaining = targetLimit - accumulated.length;\n const response = await fetchPage(remaining, currentNextToken);\n const items = response?.items || [];\n accumulated.push(...items);\n currentNextToken = response?.nextToken;\n\n // If no more items to fetch in DynamoDB partition or target is reached, terminate\n if (!currentNextToken || accumulated.length >= targetLimit) {\n break;\n }\n }\n\n return {\n items: accumulated.slice(0, targetLimit),\n nextToken: currentNextToken ?? null,\n };\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport { paginateWithAccumulator } from \"../utils/pagination\";\nimport type {\n CreateQuestionInput,\n VoteInput,\n AddQuestionTagInput,\n ListByCurriculumInput,\n ListByRandomHashInput,\n ListByPublicExamInput,\n ListByLevelInput,\n} from \"../types/inputs\";\nimport type {\n CreateQuestionResponse,\n StatusIdResponse,\n StatusResponse,\n} from \"../types/responses\";\nimport type {\n QuestionObject,\n PaginatedList,\n Option,\n Verification,\n} from \"../types/models\";\n\nexport class QuestionService {\n constructor(private network: NetworkProvider) {}\n\n async create(input: CreateQuestionInput): Promise<CreateQuestionResponse> {\n const result = await this.network.mutate<{ createQuestion: CreateQuestionResponse }>(\n `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) {\n 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) {\n status questionId\n }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.createQuestion;\n }\n\n async get(id: string): Promise<QuestionObject | null> {\n const result = await this.network.query<{ getQuestionObject: QuestionObject | null }>(\n `query GetQuestion($id: String!) {\n getQuestionObject(id: $id) {\n id question imageUrl language level publicExam difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId order createdAt\n options { items { id content } }\n tags { items { id tagId } }\n }\n }`,\n { id }\n );\n return result.data.getQuestionObject;\n }\n\n async getFull(id: string): Promise<QuestionObject | null> {\n const result = await this.network.query<{ getQuestionObject: QuestionObject | null }>(\n `query GetFullQuestion($id: String!) {\n getQuestionObject(id: $id) {\n id question answer explanation extra imageUrl language level publicExam difficulty type\n markingInstructions voteCount verificationStatus owner packId\n subjectId chapterId topicId order createdAt\n options { items { id content } }\n tags { items { id tagId } }\n user { id fullName avatarUrl }\n }\n }`,\n { id }\n );\n return result.data.getQuestionObject;\n }\n\n async getAnswer(id: string): Promise<Option | null> {\n const result = await this.network.query<{ getQuestionObject: { answer: string } | null }>(\n `query GetAnswer($id: String!) {\n getQuestionObject(id: $id) { answer }\n }`,\n { id }\n );\n if (!result.data.getQuestionObject) return null;\n return { id, content: result.data.getQuestionObject.answer } as Option;\n }\n\n async getQuestionId(packId: string, order: number): Promise<string | null> {\n const result = await this.network.query<{\n listQuestionObjectByPackIdAndOrder: PaginatedList<{ id: string }>;\n }>(\n `query GetQuestionId($packId: ID!, $order: ModelIntKeyConditionInput) {\n listQuestionObjectByPackIdAndOrder(packId: $packId, order: $order, limit: 1) {\n items { id }\n }\n }`,\n { packId, order: { eq: order } }\n );\n return result.data.listQuestionObjectByPackIdAndOrder.items[0]?.id ?? null;\n }\n\n async listByPack(\n packId: string,\n limit = 200,\n sortDirection: \"ASC\" | \"DESC\" = \"ASC\",\n nextToken?: string | null\n ): Promise<PaginatedList<QuestionObject>> {\n const result = await this.network.query<{\n listQuestionObjectByPackIdAndOrder: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsByPack($packId: ID!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {\n listQuestionObjectByPackIdAndOrder(packId: $packId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {\n items {\n id question answer explanation extra imageUrl language level publicExam difficulty type\n markingInstructions voteCount owner packId subjectId chapterId topicId order createdAt\n options { items { id content } }\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n { packId, sortDirection, limit, nextToken }\n );\n return result.data.listQuestionObjectByPackIdAndOrder;\n }\n\n async update(\n id: string,\n fields: Record<string, unknown>\n ): Promise<QuestionObject | null> {\n const result = await this.network.mutate<{ updateQuestionObject: QuestionObject | null }>(\n `mutation UpdateQuestion($input: UpdateQuestionObjectInput!) {\n updateQuestionObject(input: $input) {\n id question answer explanation extra imageUrl language level publicExam difficulty type\n markingInstructions voteCount owner packId subjectId chapterId topicId order createdAt\n options { items { id content } }\n }\n }`,\n { input: { id, ...fields } }\n );\n return result.data.updateQuestionObject;\n }\n\n async updateOption(id: string, content: string): Promise<{ id: string; content: string } | null> {\n const result = await this.network.mutate<{ updateOption: { id: string; content: string } | null }>(\n `mutation UpdateOption($input: UpdateOptionInput!) {\n updateOption(input: $input) {\n id\n content\n }\n }`,\n { input: { id, content } }\n );\n return result.data.updateOption;\n }\n\n async createOption(input: { id?: string; content: string; questionId: string }): Promise<{ id: string; content: string } | null> {\n const result = await this.network.mutate<{ createOption: { id: string; content: string } | null }>(\n `mutation CreateOption($input: CreateOptionInput!) {\n createOption(input: $input) {\n id\n content\n }\n }`,\n { input }\n );\n return result.data.createOption;\n }\n\n async deleteOption(id: string): Promise<{ id: string } | null> {\n const result = await this.network.mutate<{ deleteOption: { id: string } | null }>(\n `mutation DeleteOption($input: DeleteOptionInput!) {\n deleteOption(input: $input) {\n id\n }\n }`,\n { input: { id } }\n );\n return result.data.deleteOption;\n }\n\n async delete(id: string): Promise<QuestionObject | null> {\n const result = await this.network.mutate<{ deleteQuestionObject: QuestionObject | null }>(\n `mutation DeleteQuestion($input: DeleteQuestionObjectInput!) {\n deleteQuestionObject(input: $input) { id }\n }`,\n { input: { id } }\n );\n return result.data.deleteQuestionObject;\n }\n\n async vote(input: VoteInput): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ vote: StatusIdResponse }>(\n `mutation Vote($questionId: ID!, $type: String!, $modelType: String!) {\n vote(questionId: $questionId, type: $type, modelType: $modelType) {\n status id\n }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.vote;\n }\n\n async upVote(questionId: string): Promise<StatusIdResponse> {\n return this.vote({ questionId, type: \"upvote\", modelType: \"question\" });\n }\n\n async downVote(questionId: string): Promise<StatusIdResponse> {\n return this.vote({ questionId, type: \"downvote\", modelType: \"question\" });\n }\n\n async verify(id: string, userId: string): Promise<Verification | null> {\n const result = await this.network.mutate<{ createVerification: Verification | null }>(\n `mutation VerifyQuestion($input: CreateVerificationInput!) {\n createVerification(input: $input) { id verifiedObjectId type userId }\n }`,\n { input: { verifiedObjectId: id, type: \"question\", userId } }\n );\n return result.data.createVerification;\n }\n\n async report(id: string, reason: string): Promise<{ id: string }> {\n const result = await this.network.mutate<{ createQuestionReport: { id: string } }>(\n `mutation ReportQuestion($input: CreateQuestionReportInput!) {\n createQuestionReport(input: $input) { id }\n }`,\n { input: { questionId: id, reason } }\n );\n return result.data.createQuestionReport;\n }\n\n async addTags(input: AddQuestionTagInput): Promise<StatusResponse> {\n const result = await this.network.mutate<{ addQuestionTag: StatusResponse }>(\n `mutation AddQuestionTag($tags: String!, $packId: ID, $questionId: ID) {\n addQuestionTag(tags: $tags, packId: $packId, questionId: $questionId) {\n status\n }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.addQuestionTag;\n }\n\n async listBySubject(input: ListByCurriculumInput): Promise<PaginatedList<QuestionObject>> {\n const hasLanguage = Boolean(input.language && input.language !== \"All\" && input.language !== \"Any\");\n const hasDifficulty = Boolean(input.difficulty && input.difficulty !== \"All\");\n const filter: Record<string, unknown> = {};\n if (input.publicExam) {\n filter.publicExam = { eq: input.publicExam };\n }\n if (input.questionType && input.questionType !== \"Both\") {\n filter.type = { eq: input.questionType };\n }\n\n const targetLimit = input.limit ?? 12;\n\n const executeQuery = async (pageLimit: number, nextToken?: string | null) => {\n if (hasLanguage) {\n const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;\n const variables: Record<string, unknown> = {\n subjectId: input.id,\n language_difficulty_createdAt: { beginsWith: rangeKey },\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(filter).length > 0 ? { filter } : {}),\n };\n const result = await this.network.query<{\n listQuestionObjectBySubjectIdAndLanguage_difficulty_createdAt: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsBySubjectAndLangDiff($subjectId: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {\n listQuestionObjectBySubjectIdAndLanguage_difficulty_createdAt(subjectId: $subjectId, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id question imageUrl language level publicExam difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectBySubjectIdAndLanguage_difficulty_createdAt;\n } else {\n const queryFilter = { ...filter };\n if (hasDifficulty) {\n queryFilter.difficulty = { eq: input.difficulty };\n }\n const variables: Record<string, unknown> = {\n subjectId: input.id,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}),\n };\n const result = await this.network.query<{\n listQuestionObjectBySubjectIdAndCreatedAt: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsBySubject($subjectId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {\n listQuestionObjectBySubjectIdAndCreatedAt(subjectId: $subjectId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id question imageUrl language level publicExam difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectBySubjectIdAndCreatedAt;\n }\n };\n\n const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : (Object.keys(filter).length > 0 || hasDifficulty);\n if (!hasPostFilter) {\n return executeQuery(targetLimit, input.nextToken);\n }\n return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);\n }\n\n async listByChapter(input: ListByCurriculumInput): Promise<PaginatedList<QuestionObject>> {\n const hasLanguage = Boolean(input.language && input.language !== \"All\" && input.language !== \"Any\");\n const hasDifficulty = Boolean(input.difficulty && input.difficulty !== \"All\");\n const filter: Record<string, unknown> = {};\n if (input.publicExam) {\n filter.publicExam = { eq: input.publicExam };\n }\n if (input.questionType && input.questionType !== \"Both\") {\n filter.type = { eq: input.questionType };\n }\n\n const targetLimit = input.limit ?? 12;\n\n const executeQuery = async (pageLimit: number, nextToken?: string | null) => {\n if (hasLanguage) {\n const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;\n const variables: Record<string, unknown> = {\n chapterId: input.id,\n language_difficulty_createdAt: { beginsWith: rangeKey },\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(filter).length > 0 ? { filter } : {}),\n };\n const result = await this.network.query<{\n listQuestionObjectByChapterIdAndLanguage_difficulty_createdAt: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsByChapterAndLangDiff($chapterId: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {\n listQuestionObjectByChapterIdAndLanguage_difficulty_createdAt(chapterId: $chapterId, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id question imageUrl language level publicExam difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectByChapterIdAndLanguage_difficulty_createdAt;\n } else {\n const queryFilter = { ...filter };\n if (hasDifficulty) {\n queryFilter.difficulty = { eq: input.difficulty };\n }\n const variables: Record<string, unknown> = {\n chapterId: input.id,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}),\n };\n const result = await this.network.query<{\n listQuestionObjectByChapterIdAndCreatedAt: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsByChapter($chapterId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {\n listQuestionObjectByChapterIdAndCreatedAt(chapterId: $chapterId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id question imageUrl language level publicExam difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectByChapterIdAndCreatedAt;\n }\n };\n\n const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : (Object.keys(filter).length > 0 || hasDifficulty);\n if (!hasPostFilter) {\n return executeQuery(targetLimit, input.nextToken);\n }\n return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);\n }\n\n async listByTopic(input: ListByCurriculumInput): Promise<PaginatedList<QuestionObject>> {\n const hasLanguage = Boolean(input.language && input.language !== \"All\" && input.language !== \"Any\");\n const hasDifficulty = Boolean(input.difficulty && input.difficulty !== \"All\");\n const filter: Record<string, unknown> = {};\n if (input.publicExam) {\n filter.publicExam = { eq: input.publicExam };\n }\n if (input.questionType && input.questionType !== \"Both\") {\n filter.type = { eq: input.questionType };\n }\n\n const targetLimit = input.limit ?? 12;\n\n const executeQuery = async (pageLimit: number, nextToken?: string | null) => {\n if (hasLanguage) {\n const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;\n const variables: Record<string, unknown> = {\n topicId: input.id,\n language_difficulty_createdAt: { beginsWith: rangeKey },\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(filter).length > 0 ? { filter } : {}),\n };\n const result = await this.network.query<{\n listQuestionObjectByTopicIdAndLanguage_difficulty_createdAt: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsByTopicAndLangDiff($topicId: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {\n listQuestionObjectByTopicIdAndLanguage_difficulty_createdAt(topicId: $topicId, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id question imageUrl language level publicExam difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectByTopicIdAndLanguage_difficulty_createdAt;\n } else {\n const queryFilter = { ...filter };\n if (hasDifficulty) {\n queryFilter.difficulty = { eq: input.difficulty };\n }\n const variables: Record<string, unknown> = {\n topicId: input.id,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}),\n };\n const result = await this.network.query<{\n listQuestionObjectByTopicIdAndCreatedAt: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsByTopic($topicId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {\n listQuestionObjectByTopicIdAndCreatedAt(topicId: $topicId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id question imageUrl language level publicExam difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectByTopicIdAndCreatedAt;\n }\n };\n\n const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : (Object.keys(filter).length > 0 || hasDifficulty);\n if (!hasPostFilter) {\n return executeQuery(targetLimit, input.nextToken);\n }\n return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);\n }\n\n async listByPublicExam(input: ListByPublicExamInput): Promise<PaginatedList<QuestionObject>> {\n const hasLanguage = Boolean(input.language && input.language !== \"All\" && input.language !== \"Any\");\n const hasDifficulty = Boolean(input.difficulty && input.difficulty !== \"All\");\n const filter: Record<string, unknown> = {};\n if (input.questionType && input.questionType !== \"Both\") {\n filter.type = { eq: input.questionType };\n }\n\n const targetLimit = input.limit ?? 12;\n\n const executeQuery = async (pageLimit: number, nextToken?: string | null) => {\n if (hasLanguage) {\n const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;\n const variables: Record<string, unknown> = {\n publicExam: input.publicExam,\n language_difficulty_createdAt: { beginsWith: rangeKey },\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(filter).length > 0 ? { filter } : {}),\n };\n try {\n const result = await this.network.query<{\n listQuestionObjectByPublicExamAndLanguage_difficulty_createdAt?: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsByPublicExamAndLangDiff($publicExam: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {\n listQuestionObjectByPublicExamAndLanguage_difficulty_createdAt(publicExam: $publicExam, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id question imageUrl language level publicExam difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n if (result.data?.listQuestionObjectByPublicExamAndLanguage_difficulty_createdAt) {\n return result.data.listQuestionObjectByPublicExamAndLanguage_difficulty_createdAt;\n }\n } catch {\n // Fallback to createdAt query if new GSI is not yet active\n }\n }\n\n const fallbackFilter: Record<string, unknown> = { ...filter };\n if (hasLanguage) {\n fallbackFilter.language = { eq: input.language };\n }\n if (hasDifficulty) {\n fallbackFilter.difficulty = { eq: input.difficulty };\n }\n const variables: Record<string, unknown> = {\n publicExam: input.publicExam,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(fallbackFilter).length > 0 ? { filter: fallbackFilter } : {}),\n };\n const result = await this.network.query<{\n listQuestionObjectByPublicExamAndCreatedAt: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsByPublicExam($publicExam: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {\n listQuestionObjectByPublicExamAndCreatedAt(publicExam: $publicExam, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id question imageUrl language level publicExam difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectByPublicExamAndCreatedAt;\n };\n\n const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : (Object.keys(filter).length > 0 || hasLanguage || hasDifficulty);\n if (!hasPostFilter) {\n return executeQuery(targetLimit, input.nextToken);\n }\n return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);\n }\n\n async listByRandomHash(input: ListByRandomHashInput): Promise<PaginatedList<QuestionObject>> {\n const compositeKey = [input.language, input.difficulty].filter(Boolean).join(\"_\");\n const variables: Record<string, unknown> = {\n subjectId: input.id,\n sortDirection: input.sortDirection ?? \"ASC\",\n limit: input.limit ?? 1,\n language_difficulty_randomHash: input.randomHashFilter\n ? { beginsWith: compositeKey, ...input.randomHashFilter }\n : { beginsWith: compositeKey },\n };\n const result = await this.network.query<{\n listQuestionObjectBySubjectIdAndLanguage_difficulty_randomHash: PaginatedList<QuestionObject>;\n }>(\n `query ListByRandomHash($subjectId: String!, $language_difficulty_randomHash: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int) {\n listQuestionObjectBySubjectIdAndLanguage_difficulty_randomHash(subjectId: $subjectId, language_difficulty_randomHash: $language_difficulty_randomHash, sortDirection: $sortDirection, limit: $limit) {\n items {\n id question imageUrl language level publicExam difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectBySubjectIdAndLanguage_difficulty_randomHash;\n }\n\n async listByLevel(input: ListByLevelInput): Promise<PaginatedList<QuestionObject>> {\n const hasLanguage = Boolean(input.language && input.language !== \"All\" && input.language !== \"Any\");\n const hasDifficulty = Boolean(input.difficulty && input.difficulty !== \"All\");\n const filter: Record<string, unknown> = {};\n if (input.publicExam) {\n filter.publicExam = { eq: input.publicExam };\n }\n if (input.questionType && input.questionType !== \"Both\") {\n filter.type = { eq: input.questionType };\n }\n\n const targetLimit = input.limit ?? 12;\n\n const executeQuery = async (pageLimit: number, nextToken?: string | null) => {\n if (hasLanguage) {\n const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;\n const variables: Record<string, unknown> = {\n level: input.level,\n language_difficulty_createdAt: { beginsWith: rangeKey },\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(filter).length > 0 ? { filter } : {}),\n };\n const result = await this.network.query<{\n listQuestionObjectByLevelAndLanguage_difficulty_createdAt: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsByLevelAndLangDiff($level: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {\n listQuestionObjectByLevelAndLanguage_difficulty_createdAt(level: $level, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id question imageUrl language level publicExam difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectByLevelAndLanguage_difficulty_createdAt;\n } else {\n const queryFilter = { ...filter };\n if (hasDifficulty) {\n queryFilter.difficulty = { eq: input.difficulty };\n }\n const variables: Record<string, unknown> = {\n level: input.level,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}),\n };\n const result = await this.network.query<{\n listQuestionObjectByLevelAndCreatedAt: PaginatedList<QuestionObject>;\n }>(\n `query ListQuestionsByLevel($level: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelQuestionObjectFilterInput) {\n listQuestionObjectByLevelAndCreatedAt(level: $level, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id question imageUrl language level publicExam difficulty type voteCount\n verificationStatus owner packId subjectId chapterId topicId createdAt\n options { items { id content } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listQuestionObjectByLevelAndCreatedAt;\n }\n };\n\n const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : (Object.keys(filter).length > 0 || hasDifficulty);\n if (!hasPostFilter) {\n return executeQuery(targetLimit, input.nextToken);\n }\n return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport { paginateWithAccumulator } from \"../utils/pagination\";\nimport type {\n CreatePackInput,\n ListByCurriculumInput,\n ListByPublicExamInput,\n ListByLevelInput,\n CreatePackGroupInput,\n ListPackGroupsInput,\n} from \"../types/inputs\";\nimport type { StatusIdResponse } from \"../types/responses\";\nimport type {\n Pack,\n PaginatedList,\n PackExamResults,\n PackGroup,\n Verification,\n} from \"../types/models\";\n\nexport class PackService {\n constructor(private network: NetworkProvider) {}\n\n async create(input: CreatePackInput): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ createPackObject: StatusIdResponse }>(\n `mutation CreatePack($name: String!, $level: String!, $publicExam: String, $language: String!, $difficulty: String!, $automaticNumbering: Boolean, $subjectId: ID, $chapterId: ID, $topicId: ID) {\n createPackObject(name: $name, level: $level, publicExam: $publicExam, language: $language, difficulty: $difficulty, automaticNumbering: $automaticNumbering, subjectId: $subjectId, chapterId: $chapterId, topicId: $topicId) {\n status id\n }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.createPackObject;\n }\n\n async get(id: string): Promise<Pack | null> {\n const result = await this.network.query<{ getPack: Pack | null }>(\n `query GetPack($id: ID!) {\n getPack(id: $id) {\n id name language level publicExam difficulty questionCount automaticNumbering\n owner subjectId chapterId topicId packGroupId verificationStatus createdAt\n tags { items { id tagId } }\n user { id fullName avatarUrl }\n }\n }`,\n { id }\n );\n return result.data.getPack;\n }\n\n async getFull(id: string): Promise<Pack | null> {\n const result = await this.network.query<{ getPack: Pack | null }>(\n `query GetFullPack($id: ID!) {\n getPack(id: $id) {\n id name language level publicExam difficulty questionCount automaticNumbering\n owner subjectId chapterId topicId packGroupId verificationStatus createdAt\n tags { items { id tagId } }\n user { id fullName avatarUrl }\n questions(sortDirection: ASC, limit: 200) {\n items {\n id question answer explanation extra imageUrl language level publicExam difficulty type\n markingInstructions voteCount owner order createdAt\n options { items { id content } }\n tags { items { id tagId } }\n }\n }\n }\n }`,\n { id }\n );\n const pack = result.data.getPack;\n if (pack && pack.questions && Array.isArray(pack.questions.items)) {\n pack.questions.items.sort((a, b) => {\n const isWrittenA = a.type === \"WRITTEN\" ? 1 : 0;\n const isWrittenB = b.type === \"WRITTEN\" ? 1 : 0;\n if (isWrittenA !== isWrittenB) {\n return isWrittenA - isWrittenB;\n }\n const orderA = typeof a.order === \"number\" ? a.order : (a.order ? Number(a.order) : 0);\n const orderB = typeof b.order === \"number\" ? b.order : (b.order ? Number(b.order) : 0);\n if (orderA !== orderB) {\n if (orderA === 0) return 1;\n if (orderB === 0) return -1;\n return orderA - orderB;\n }\n const timeA = a.createdAt ? new Date(a.createdAt).getTime() : 0;\n const timeB = b.createdAt ? new Date(b.createdAt).getTime() : 0;\n return timeA - timeB;\n });\n }\n return pack;\n }\n\n async update(id: string, fields: Record<string, unknown>): Promise<Pack | null> {\n const result = await this.network.mutate<{ updatePack: Pack | null }>(\n `mutation UpdatePack($input: UpdatePackInput!) {\n updatePack(input: $input) {\n id name language level publicExam difficulty questionCount automaticNumbering\n owner subjectId chapterId topicId verificationStatus createdAt\n }\n }`,\n { input: { id, ...fields } }\n );\n return result.data.updatePack;\n }\n\n async delete(id: string): Promise<Pack | null> {\n const result = await this.network.mutate<{ deletePackCascade: { status: string } | null }>(\n `mutation DeletePackCascade($packId: ID!) {\n deletePackCascade(packId: $packId) { status }\n }`,\n { packId: id }\n );\n if (result.data.deletePackCascade?.status === \"success\") {\n return { id } as Pack;\n }\n return null;\n }\n\n async verify(id: string, userId: string): Promise<Verification | null> {\n const result = await this.network.mutate<{ createVerification: Verification | null }>(\n `mutation VerifyPack($input: CreateVerificationInput!) {\n createVerification(input: $input) { id verifiedObjectId type userId }\n }`,\n { input: { verifiedObjectId: id, type: \"pack\", userId } }\n );\n return result.data.createVerification;\n }\n\n async report(id: string, reason: string): Promise<{ id: string }> {\n const result = await this.network.mutate<{ createPackReport: { id: string } }>(\n `mutation ReportPack($input: CreatePackReportInput!) {\n createPackReport(input: $input) { id }\n }`,\n { input: { packId: id, reason } }\n );\n return result.data.createPackReport;\n }\n\n async submitExam(\n packId: string,\n selectedOptions: (string | null)[],\n options?: { functionUrl?: string; authToken?: string }\n ): Promise<StatusIdResponse> {\n if (options?.functionUrl) {\n try {\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (options.authToken) {\n headers[\"Authorization\"] = `Bearer ${options.authToken}`;\n }\n const response = await fetch(options.functionUrl, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ packId, selectedOptions: JSON.stringify(selectedOptions) }),\n });\n if (response.ok) {\n const data = await response.json();\n return data as StatusIdResponse;\n }\n console.warn(`[PackService] Function URL failed with status ${response.status}, falling back to GraphQL...`);\n } catch (err) {\n console.warn(\"[PackService] Function URL fetch error, falling back to GraphQL...\", err);\n }\n }\n\n const result = await this.network.mutate<{ submitPackExam: StatusIdResponse }>(\n `mutation SubmitPackExam($packId: ID!, $selectedOptions: String!) {\n submitPackExam(packId: $packId, selectedOptions: $selectedOptions) {\n status id\n }\n }`,\n { packId, selectedOptions: JSON.stringify(selectedOptions) }\n );\n return result.data.submitPackExam;\n }\n\n async getExams(userId: string, nextToken?: string): Promise<PaginatedList<PackExamResults>> {\n const result = await this.network.query<{\n listPackExamResultsByOwnerAndCreatedAt: PaginatedList<PackExamResults>;\n }>(\n `query ListPackExams($owner: ID!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listPackExamResultsByOwnerAndCreatedAt(owner: $owner, sortDirection: $sortDirection, nextToken: $nextToken) {\n items {\n id packId selectedOptions aiEvaluations owner createdAt\n pack { id name language level publicExam difficulty questionCount }\n }\n nextToken\n }\n }`,\n { owner: userId, sortDirection: \"DESC\", nextToken }\n );\n return result.data.listPackExamResultsByOwnerAndCreatedAt;\n }\n\n async getExamResult(resultId: string): Promise<PackExamResults | null> {\n const result = await this.network.query<{ getPackExamResults: PackExamResults | null }>(\n `query GetPackExamResult($id: ID!) {\n getPackExamResults(id: $id) {\n id packId selectedOptions aiEvaluations owner createdAt\n pack { id name language level publicExam difficulty questionCount }\n }\n }`,\n { id: resultId }\n );\n return result.data.getPackExamResults;\n }\n\n async listBySubject(input: ListByCurriculumInput): Promise<PaginatedList<Pack>> {\n const hasLanguage = Boolean(input.language && input.language !== \"All\" && input.language !== \"Any\");\n const hasDifficulty = Boolean(input.difficulty && input.difficulty !== \"All\");\n const filter: Record<string, unknown> = {};\n if (input.publicExam) {\n filter.publicExam = { eq: input.publicExam };\n }\n\n const targetLimit = input.limit ?? 12;\n\n const executeQuery = async (pageLimit: number, nextToken?: string | null) => {\n if (hasLanguage) {\n const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;\n const variables: Record<string, unknown> = {\n subjectId: input.id,\n language_difficulty_createdAt: { beginsWith: rangeKey },\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(filter).length > 0 ? { filter } : {}),\n };\n const result = await this.network.query<{\n listPackBySubjectIdAndLanguage_difficulty_createdAt: PaginatedList<Pack>;\n }>(\n `query ListPacksBySubjectAndLangDiff($subjectId: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {\n listPackBySubjectIdAndLanguage_difficulty_createdAt(subjectId: $subjectId, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listPackBySubjectIdAndLanguage_difficulty_createdAt;\n } else {\n const queryFilter = { ...filter };\n if (hasDifficulty) {\n queryFilter.difficulty = { eq: input.difficulty };\n }\n const variables: Record<string, unknown> = {\n subjectId: input.id,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}),\n };\n const result = await this.network.query<{\n listPackBySubjectIdAndCreatedAt: PaginatedList<Pack>;\n }>(\n `query ListPacksBySubject($subjectId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {\n listPackBySubjectIdAndCreatedAt(subjectId: $subjectId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listPackBySubjectIdAndCreatedAt;\n }\n };\n\n const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : (Object.keys(filter).length > 0 || hasDifficulty);\n if (!hasPostFilter) {\n return executeQuery(targetLimit, input.nextToken);\n }\n return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);\n }\n\n async listByChapter(input: ListByCurriculumInput): Promise<PaginatedList<Pack>> {\n const hasLanguage = Boolean(input.language && input.language !== \"All\" && input.language !== \"Any\");\n const hasDifficulty = Boolean(input.difficulty && input.difficulty !== \"All\");\n const filter: Record<string, unknown> = {};\n if (input.publicExam) {\n filter.publicExam = { eq: input.publicExam };\n }\n\n const targetLimit = input.limit ?? 12;\n\n const executeQuery = async (pageLimit: number, nextToken?: string | null) => {\n if (hasLanguage) {\n const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;\n const variables: Record<string, unknown> = {\n chapterId: input.id,\n language_difficulty_createdAt: { beginsWith: rangeKey },\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(filter).length > 0 ? { filter } : {}),\n };\n const result = await this.network.query<{\n listPackByChapterIdAndLanguage_difficulty_createdAt: PaginatedList<Pack>;\n }>(\n `query ListPacksByChapterAndLangDiff($chapterId: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {\n listPackByChapterIdAndLanguage_difficulty_createdAt(chapterId: $chapterId, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listPackByChapterIdAndLanguage_difficulty_createdAt;\n } else {\n const queryFilter = { ...filter };\n if (hasDifficulty) {\n queryFilter.difficulty = { eq: input.difficulty };\n }\n const variables: Record<string, unknown> = {\n chapterId: input.id,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}),\n };\n const result = await this.network.query<{\n listPackByChapterIdAndCreatedAt: PaginatedList<Pack>;\n }>(\n `query ListPacksByChapter($chapterId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {\n listPackByChapterIdAndCreatedAt(chapterId: $chapterId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listPackByChapterIdAndCreatedAt;\n }\n };\n\n const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : (Object.keys(filter).length > 0 || hasDifficulty);\n if (!hasPostFilter) {\n return executeQuery(targetLimit, input.nextToken);\n }\n return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);\n }\n\n async listByTopic(input: ListByCurriculumInput): Promise<PaginatedList<Pack>> {\n const hasLanguage = Boolean(input.language && input.language !== \"All\" && input.language !== \"Any\");\n const hasDifficulty = Boolean(input.difficulty && input.difficulty !== \"All\");\n const filter: Record<string, unknown> = {};\n if (input.publicExam) {\n filter.publicExam = { eq: input.publicExam };\n }\n\n const targetLimit = input.limit ?? 12;\n\n const executeQuery = async (pageLimit: number, nextToken?: string | null) => {\n if (hasLanguage) {\n const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;\n const variables: Record<string, unknown> = {\n topicId: input.id,\n language_difficulty_createdAt: { beginsWith: rangeKey },\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(filter).length > 0 ? { filter } : {}),\n };\n const result = await this.network.query<{\n listPackByTopicIdAndLanguage_difficulty_createdAt: PaginatedList<Pack>;\n }>(\n `query ListPacksByTopicAndLangDiff($topicId: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {\n listPackByTopicIdAndLanguage_difficulty_createdAt(topicId: $topicId, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listPackByTopicIdAndLanguage_difficulty_createdAt;\n } else {\n const queryFilter = { ...filter };\n if (hasDifficulty) {\n queryFilter.difficulty = { eq: input.difficulty };\n }\n const variables: Record<string, unknown> = {\n topicId: input.id,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}),\n };\n const result = await this.network.query<{\n listPackByTopicIdAndCreatedAt: PaginatedList<Pack>;\n }>(\n `query ListPacksByTopic($topicId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {\n listPackByTopicIdAndCreatedAt(topicId: $topicId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listPackByTopicIdAndCreatedAt;\n }\n };\n\n const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : (Object.keys(filter).length > 0 || hasDifficulty);\n if (!hasPostFilter) {\n return executeQuery(targetLimit, input.nextToken);\n }\n return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);\n }\n\n async listByPublicExam(input: ListByPublicExamInput): Promise<PaginatedList<Pack>> {\n const hasLanguage = Boolean(input.language && input.language !== \"All\" && input.language !== \"Any\");\n const hasDifficulty = Boolean(input.difficulty && input.difficulty !== \"All\");\n const filter: Record<string, unknown> = {};\n\n const targetLimit = input.limit ?? 12;\n\n const executeQuery = async (pageLimit: number, nextToken?: string | null) => {\n if (hasLanguage) {\n const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;\n const variables: Record<string, unknown> = {\n publicExam: input.publicExam,\n language_difficulty_createdAt: { beginsWith: rangeKey },\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(filter).length > 0 ? { filter } : {}),\n };\n try {\n const result = await this.network.query<{\n listPackByPublicExamAndLanguage_difficulty_createdAt?: PaginatedList<Pack>;\n }>(\n `query ListPacksByPublicExamAndLangDiff($publicExam: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {\n listPackByPublicExamAndLanguage_difficulty_createdAt(publicExam: $publicExam, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n variables\n );\n if (result.data?.listPackByPublicExamAndLanguage_difficulty_createdAt) {\n return result.data.listPackByPublicExamAndLanguage_difficulty_createdAt;\n }\n } catch {\n // Fallback to createdAt or listPacks if new GSI is not yet active\n }\n }\n\n const fallbackFilter: Record<string, unknown> = { ...filter };\n if (hasLanguage) {\n fallbackFilter.language = { eq: input.language };\n }\n if (hasDifficulty) {\n fallbackFilter.difficulty = { eq: input.difficulty };\n }\n\n try {\n const variables: Record<string, unknown> = {\n publicExam: input.publicExam,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(fallbackFilter).length > 0 ? { filter: fallbackFilter } : {}),\n };\n const result = await this.network.query<{\n listPackByPublicExamAndCreatedAt?: PaginatedList<Pack>;\n }>(\n `query ListPacksByPublicExam($publicExam: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {\n listPackByPublicExamAndCreatedAt(publicExam: $publicExam, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n variables\n );\n if (result.data?.listPackByPublicExamAndCreatedAt) {\n return result.data.listPackByPublicExamAndCreatedAt;\n }\n } catch {\n // Fallback to listPacks table scan only if GSI is completely unavailable\n }\n\n const scanFilter: Record<string, unknown> = {\n publicExam: { eq: input.publicExam },\n ...fallbackFilter,\n };\n const result = await this.network.query<{\n listPacks: PaginatedList<Pack>;\n }>(\n `query ListPacksFallback($filter: ModelPackFilterInput, $limit: Int, $nextToken: String) {\n listPacks(filter: $filter, limit: $limit, nextToken: $nextToken) {\n items {\n id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n { filter: scanFilter, limit: pageLimit, nextToken }\n );\n return result.data.listPacks;\n };\n\n const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : (Object.keys(filter).length > 0 || hasLanguage || hasDifficulty);\n if (!hasPostFilter) {\n return executeQuery(targetLimit, input.nextToken);\n }\n return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);\n }\n\n async listByLevel(input: ListByLevelInput): Promise<PaginatedList<Pack>> {\n const hasLanguage = Boolean(input.language && input.language !== \"All\" && input.language !== \"Any\");\n const hasDifficulty = Boolean(input.difficulty && input.difficulty !== \"All\");\n const filter: Record<string, unknown> = {};\n if (input.publicExam) {\n filter.publicExam = { eq: input.publicExam };\n }\n\n const targetLimit = input.limit ?? 12;\n\n const executeQuery = async (pageLimit: number, nextToken?: string | null) => {\n if (hasLanguage) {\n const rangeKey = hasDifficulty ? `${input.language}#${input.difficulty}#` : `${input.language}#`;\n const variables: Record<string, unknown> = {\n level: input.level,\n language_difficulty_createdAt: { beginsWith: rangeKey },\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(filter).length > 0 ? { filter } : {}),\n };\n const result = await this.network.query<{\n listPackByLevelAndLanguage_difficulty_createdAt: PaginatedList<Pack>;\n }>(\n `query ListPacksByLevelAndLangDiff($level: String!, $language_difficulty_createdAt: ModelStringKeyConditionInput, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {\n listPackByLevelAndLanguage_difficulty_createdAt(level: $level, language_difficulty_createdAt: $language_difficulty_createdAt, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listPackByLevelAndLanguage_difficulty_createdAt;\n } else {\n const queryFilter = { ...filter };\n if (hasDifficulty) {\n queryFilter.difficulty = { eq: input.difficulty };\n }\n const variables: Record<string, unknown> = {\n level: input.level,\n sortDirection: input.sortDirection ?? \"DESC\",\n limit: pageLimit,\n nextToken,\n ...(Object.keys(queryFilter).length > 0 ? { filter: queryFilter } : {}),\n };\n const result = await this.network.query<{\n listPackByLevelAndCreatedAt: PaginatedList<Pack>;\n }>(\n `query ListPacksByLevel($level: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String, $filter: ModelPackFilterInput) {\n listPackByLevelAndCreatedAt(level: $level, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id name language level publicExam difficulty questionCount owner createdAt verificationStatus subjectId chapterId topicId\n tags { items { id tagId } }\n }\n nextToken\n }\n }`,\n variables\n );\n return result.data.listPackByLevelAndCreatedAt;\n }\n };\n\n const hasPostFilter = hasLanguage ? Object.keys(filter).length > 0 : (Object.keys(filter).length > 0 || hasDifficulty);\n if (!hasPostFilter) {\n return executeQuery(targetLimit, input.nextToken);\n }\n return paginateWithAccumulator(executeQuery, targetLimit, input.nextToken);\n }\n\n async createGroup(input: CreatePackGroupInput): Promise<PackGroup> {\n const typeLevel = input.type && input.level ? `${input.type}_${input.level}` : undefined;\n const mutationInput = {\n name: input.name,\n description: input.description,\n imageUrl: input.imageUrl,\n type: input.type,\n level: input.level,\n typeLevel,\n order: input.order,\n };\n const result = await this.network.mutate<{ createPackGroup: PackGroup }>(\n `mutation CreatePackGroup($input: CreatePackGroupInput!) {\n createPackGroup(input: $input) {\n id name description imageUrl type level typeLevel packCount order\n }\n }`,\n { input: mutationInput }\n );\n const created = result.data.createPackGroup;\n if (created && input.packIds && input.packIds.length > 0) {\n await Promise.all(\n input.packIds.map((pId) => this.update(pId, { packGroupId: created.id }))\n );\n }\n return created;\n }\n\n async deleteGroup(id: string): Promise<{ id: string }> {\n const result = await this.network.mutate<{ deletePackGroup: { id: string } }>(\n `mutation DeletePackGroup($input: DeletePackGroupInput!) {\n deletePackGroup(input: $input) {\n id\n }\n }`,\n { input: { id } }\n );\n return result.data.deletePackGroup;\n }\n\n async listGroups(type: string, level?: string): Promise<PackGroup[]>;\n async listGroups(input: ListPackGroupsInput): Promise<PaginatedList<PackGroup>>;\n async listGroups(typeOrInput: string | ListPackGroupsInput, level?: string): Promise<PackGroup[] | PaginatedList<PackGroup>> {\n let type: string;\n let targetLevel: string | undefined;\n let limit: number | undefined;\n let nextToken: string | null | undefined;\n let sortDirection: \"ASC\" | \"DESC\" = \"ASC\";\n const isLegacyCall = typeof typeOrInput === \"string\";\n\n if (isLegacyCall) {\n type = typeOrInput;\n targetLevel = level;\n } else {\n type = typeOrInput.type ?? \"QB\";\n targetLevel = typeOrInput.level;\n limit = typeOrInput.limit;\n nextToken = typeOrInput.nextToken;\n sortDirection = typeOrInput.sortDirection ?? \"ASC\";\n }\n\n const queryField = targetLevel ? \"listPackGroupByTypeLevelAndOrder\" : \"listPackGroupByTypeAndOrder\";\n const variables: Record<string, unknown> = isLegacyCall\n ? (targetLevel ? { typeLevel: `${type}_${targetLevel}`, sortDirection } : { type, sortDirection })\n : (targetLevel ? { typeLevel: `${type}_${targetLevel}`, sortDirection, limit: limit ?? 20, nextToken } : { type, sortDirection, limit: limit ?? 20, nextToken });\n\n const queryString = isLegacyCall\n ? (targetLevel\n ? `query ListPackGroups($typeLevel: String!, $sortDirection: ModelSortDirection) {\n listPackGroupByTypeLevelAndOrder(typeLevel: $typeLevel, sortDirection: $sortDirection) {\n items { id name description imageUrl type level typeLevel packCount order }\n }\n }`\n : `query ListPackGroups($type: String!, $sortDirection: ModelSortDirection) {\n listPackGroupByTypeAndOrder(type: $type, sortDirection: $sortDirection) {\n items { id name description imageUrl type level typeLevel packCount order }\n }\n }`)\n : (targetLevel\n ? `query ListPackGroupsByLevel($typeLevel: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {\n listPackGroupByTypeLevelAndOrder(typeLevel: $typeLevel, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {\n items { id name description imageUrl type level typeLevel packCount order }\n nextToken\n }\n }`\n : `query ListPackGroupsByType($type: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {\n listPackGroupByTypeAndOrder(type: $type, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {\n items { id name description imageUrl type level typeLevel packCount order }\n nextToken\n }\n }`);\n\n const result = await this.network.query<Record<string, PaginatedList<PackGroup>>>(queryString, variables);\n const paginated = result.data[queryField] as PaginatedList<PackGroup>;\n return isLegacyCall ? paginated.items : paginated;\n }\n\n async getGroup(id: string): Promise<PackGroup | null> {\n const result = await this.network.query<{ getPackGroup: PackGroup | null }>(\n `query GetPackGroup($id: ID!) {\n getPackGroup(id: $id) {\n id name description imageUrl type level typeLevel packCount order\n packs { items { id name language level publicExam difficulty questionCount owner createdAt verificationStatus } }\n }\n }`,\n { id }\n );\n return result.data.getPackGroup;\n }\n\n async list(input?: { limit?: number; nextToken?: string | null }): Promise<PaginatedList<Pack>> {\n const result = await this.network.query<{ listPacks: PaginatedList<Pack> }>(\n `query ListPacks($limit: Int, $nextToken: String) {\n listPacks(limit: $limit, nextToken: $nextToken) {\n items {\n id name language level publicExam difficulty questionCount owner createdAt verificationStatus packGroupId\n }\n nextToken\n }\n }`,\n { limit: input?.limit ?? 20, nextToken: input?.nextToken }\n );\n return result.data.listPacks;\n }\n}\n\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { CreateContestInput } from \"../types/inputs\";\nimport type {\n StatusIdResponse,\n StatusResponse,\n CheckParticipationResponse,\n ContestQuestionsResponse,\n ContestAnswersResponse,\n LeaderboardResponse,\n} from \"../types/responses\";\nimport type { Contest, PaginatedList, ContestParticipant } from \"../types/models\";\n\nexport class ContestService {\n constructor(private network: NetworkProvider) {}\n\n async create(input: CreateContestInput): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ makeContest: StatusIdResponse }>(\n `mutation MakeContest($name: String!, $description: String!, $level: String!, $language: String!, $startTime: AWSDateTime!, $endTime: AWSDateTime!, $subject: String!, $isRated: Boolean!, $questions: String!, $maximumParticipants: Int!, $solutionVideoUrl: String, $imageUrl: String) {\n makeContest(name: $name, description: $description, level: $level, language: $language, startTime: $startTime, endTime: $endTime, subject: $subject, isRated: $isRated, questions: $questions, maximumParticipants: $maximumParticipants, solutionVideoUrl: $solutionVideoUrl, imageUrl: $imageUrl) {\n status id\n }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.makeContest;\n }\n\n async get(id: string): Promise<Contest | null> {\n const result = await this.network.query<{ getContest: Contest | null }>(\n `query GetContest($id: ID!) {\n getContest(id: $id) {\n id name description startTime endTime level language owner isRated subject\n totalQuestions resultsPublished totalParticipants maximumParticipants\n rankings globalPk solutionVideoUrl imageUrl\n }\n }`,\n { id }\n );\n return result.data.getContest;\n }\n\n async getName(id: string): Promise<string | null> {\n const result = await this.network.query<{ getContest: { name: string } | null }>(\n `query GetContestName($id: ID!) { getContest(id: $id) { name } }`,\n { id }\n );\n return result.data.getContest?.name ?? null;\n }\n\n async list(nextToken?: string, level?: string): Promise<PaginatedList<Contest>> {\n const variables: Record<string, unknown> = level\n ? { level, sortDirection: \"DESC\", nextToken }\n : { globalPk: \"ALL\", sortDirection: \"DESC\", nextToken };\n\n const result = await this.network.query<Record<string, PaginatedList<Contest>>>(\n level\n ? `query ListContests($level: String!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listContestByLevelAndStartTime(level: $level, sortDirection: $sortDirection, nextToken: $nextToken) {\n items { id name description startTime endTime level language owner isRated subject totalQuestions resultsPublished totalParticipants maximumParticipants solutionVideoUrl imageUrl }\n nextToken\n }\n }`\n : `query ListContests($globalPk: String!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listContestByGlobalPkAndStartTime(globalPk: $globalPk, sortDirection: $sortDirection, nextToken: $nextToken) {\n items { id name description startTime endTime level language owner isRated subject totalQuestions resultsPublished totalParticipants maximumParticipants solutionVideoUrl imageUrl }\n nextToken\n }\n }`,\n variables\n );\n const key = level ? \"listContestByLevelAndStartTime\" : \"listContestByGlobalPkAndStartTime\";\n return result.data[key] as PaginatedList<Contest>;\n }\n\n async participate(contestId: string): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ participateContest: StatusIdResponse }>(\n `mutation ParticipateContest($contestId: ID!) {\n participateContest(contestId: $contestId) { status id }\n }`,\n { contestId }\n );\n return result.data.participateContest;\n }\n\n async checkParticipation(contestId: string): Promise<CheckParticipationResponse> {\n const result = await this.network.query<{ checkUserParticipation: CheckParticipationResponse }>(\n `query CheckParticipation($contestId: ID!) {\n checkUserParticipation(contestId: $contestId) {\n status participated selectedOptions score rank beforeRating afterRating\n }\n }`,\n { contestId }\n );\n return result.data.checkUserParticipation;\n }\n\n async selectOption(contestId: string, optionId: string, order: number): Promise<StatusResponse> {\n const result = await this.network.mutate<{ selectContestOption: StatusResponse }>(\n `mutation SelectContestOption($contestId: ID!, $optionId: String!, $order: Int!) {\n selectContestOption(contestId: $contestId, optionId: $optionId, order: $order) { status }\n }`,\n { contestId, optionId, order }\n );\n return result.data.selectContestOption;\n }\n\n async getQuestions(contestId: string): Promise<ContestQuestionsResponse> {\n const result = await this.network.query<{ getContestQuestions: ContestQuestionsResponse }>(\n `query GetContestQuestions($contestId: ID!) {\n getContestQuestions(contestId: $contestId) { status questions }\n }`,\n { contestId }\n );\n return result.data.getContestQuestions;\n }\n\n async getAnswers(contestId: string): Promise<ContestAnswersResponse> {\n const result = await this.network.query<{ getContestAnswers: ContestAnswersResponse }>(\n `query GetContestAnswers($contestId: ID!) {\n getContestAnswers(contestId: $contestId) { status answers }\n }`,\n { contestId }\n );\n return result.data.getContestAnswers;\n }\n\n async publish(contestId: string): Promise<StatusResponse> {\n const result = await this.network.mutate<{ publishContestResults: StatusResponse }>(\n `mutation PublishResults($contestId: ID!) {\n publishContestResults(contestId: $contestId) { status }\n }`,\n { contestId }\n );\n return result.data.publishContestResults;\n }\n\n async getLeaderboard(contestId: string, nextToken?: string | null): Promise<LeaderboardResponse> {\n const result = await this.network.query<{\n listContestParticipantByContestIdAndRank: PaginatedList<ContestParticipant>;\n }>(\n `query GetLeaderboard($contestId: ID!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listContestParticipantByContestIdAndRank(contestId: $contestId, sortDirection: $sortDirection, nextToken: $nextToken) {\n items { id contestId userId rank score afterRating beforeRating createdAt user { id fullName avatarUrl } }\n nextToken\n }\n }`,\n { contestId, sortDirection: \"ASC\", nextToken }\n );\n const data = result.data.listContestParticipantByContestIdAndRank;\n return {\n status: \"success\",\n leaderboard: JSON.stringify(data.items),\n nextToken: data.nextToken,\n };\n }\n\n async getUserResults(userId: string, nextToken?: string | null): Promise<PaginatedList<ContestParticipant>> {\n const result = await this.network.query<{\n listContestParticipantByUserIdAndCreatedAt: PaginatedList<ContestParticipant>;\n }>(\n `query GetUserResults($userId: ID!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listContestParticipantByUserIdAndCreatedAt(userId: $userId, sortDirection: $sortDirection, nextToken: $nextToken) {\n items { id contestId userId rank score afterRating beforeRating selectedOptions createdAt contest { id name subject level startTime } }\n nextToken\n }\n }`,\n { userId, sortDirection: \"DESC\", nextToken }\n );\n return result.data.listContestParticipantByUserIdAndCreatedAt;\n }\n\n async updateSolutionVideo(contestId: string, url: string | null): Promise<StatusResponse> {\n const result = await this.network.mutate<{ updateContest: Contest | null }>(\n `mutation UpdateSolutionVideo($input: UpdateContestInput!) {\n updateContest(input: $input) { id solutionVideoUrl }\n }`,\n { input: { id: contestId, solutionVideoUrl: url } }\n );\n return { status: result.data.updateContest ? \"success\" : \"error\" };\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type {\n CreateCourseInput,\n CreateCourseItemInput,\n CreateCourseLiveExamInput,\n DeleteCourseItemInput,\n EnrollStudentInput,\n SendEnrollmentRequestInput,\n GetSignedURLInput,\n GetSignedFileURLInput,\n CreateCourseVideoInput,\n UploadVideoToBunnyInput,\n UploadCourseItemInput,\n} from \"../types/inputs\";\nimport type {\n CreateCourseResponse,\n StatusIdResponse,\n StatusResponse,\n SignedURLResponse,\n CreateCourseVideoResponse,\n CourseLiveExamQuestionsResponse,\n LeaderboardResponse,\n} from \"../types/responses\";\nimport type {\n Course,\n CourseItem,\n CourseEnrolled,\n CourseLiveExam,\n PaginatedList,\n} from \"../types/models\";\n\nexport class CourseService {\n constructor(private network: NetworkProvider) {}\n\n async create(input: CreateCourseInput): Promise<CreateCourseResponse> {\n const result = await this.network.mutate<{ makeCourse: CreateCourseResponse }>(\n `mutation MakeCourse($name: String!, $description: String!, $price: Int!, $imageUrl: String) {\n makeCourse(name: $name, description: $description, price: $price, imageUrl: $imageUrl) { status courseId }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.makeCourse;\n }\n\n async get(id: string): Promise<Course | null> {\n const result = await this.network.query<{ getCourse: Course | null }>(\n `query GetCourse($id: ID!) {\n getCourse(id: $id) { id name description imageUrl videoCount fileCount price totalEnrolled owner createdAt }\n }`,\n { id }\n );\n return result.data.getCourse;\n }\n\n async getItems(courseId: string): Promise<CourseItem[]> {\n const result = await this.network.query<{\n listCourseItemsByCourseIdAndOrder: PaginatedList<CourseItem>;\n }>(\n `query ListCourseItems($courseId: ID!, $sortDirection: ModelSortDirection) {\n listCourseItemsByCourseIdAndOrder(courseId: $courseId, sortDirection: $sortDirection) {\n items { id type name courseId parentId order owner }\n }\n }`,\n { courseId, sortDirection: \"ASC\" }\n );\n return result.data.listCourseItemsByCourseIdAndOrder.items;\n }\n\n async createItem(input: CreateCourseItemInput): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ createCourseItem: StatusIdResponse }>(\n `mutation CreateCourseItem($courseId: ID!, $name: String!, $order: Int!, $type: CourseItemsType, $parentId: ID, $id: ID) {\n createCourseItem(courseId: $courseId, name: $name, order: $order, type: $type, parentId: $parentId, id: $id) { status id }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.createCourseItem;\n }\n\n async updateItem(id: string, name: string, order: number): Promise<CourseItem | null> {\n const result = await this.network.mutate<{ updateCourseItems: CourseItem | null }>(\n `mutation UpdateCourseItem($input: UpdateCourseItemsInput!) {\n updateCourseItems(input: $input) { id type name courseId parentId order owner }\n }`,\n { input: { id, name, order } }\n );\n return result.data.updateCourseItems;\n }\n\n async deleteItem(input: DeleteCourseItemInput): Promise<StatusResponse> {\n const result = await this.network.mutate<{ deleteCourseItem: StatusResponse }>(\n `mutation DeleteCourseItem($id: ID!, $fileType: String!, $courseId: ID!, $parentId: ID) {\n deleteCourseItem(id: $id, fileType: $fileType, courseId: $courseId, parentId: $parentId) { status }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.deleteCourseItem;\n }\n\n async enroll(input: EnrollStudentInput): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ enrollStudent: StatusIdResponse }>(\n `mutation EnrollStudent($courseId: ID!, $userId: ID!) {\n enrollStudent(courseId: $courseId, userId: $userId) { status id }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.enrollStudent;\n }\n\n async getEnrolledStudent(courseId: string, userId: string): Promise<CourseEnrolled | null> {\n const result = await this.network.query<{\n listCourseEnrolledByCourseIdAndCreatedAt: PaginatedList<CourseEnrolled>;\n }>(\n `query GetEnrolledStudent($courseId: ID!, $filter: ModelCourseEnrolledFilterInput) {\n listCourseEnrolledByCourseIdAndCreatedAt(courseId: $courseId, filter: $filter) {\n items { id courseId userId owner score rank createdAt }\n }\n }`,\n { courseId, filter: { userId: { eq: userId } } }\n );\n return result.data.listCourseEnrolledByCourseIdAndCreatedAt.items[0] ?? null;\n }\n\n async getEnrolledStudents(courseId: string, nextToken?: string | null): Promise<PaginatedList<CourseEnrolled>> {\n const result = await this.network.query<{\n listCourseEnrolledByCourseIdAndCreatedAt: PaginatedList<CourseEnrolled>;\n }>(\n `query ListEnrolledStudents($courseId: ID!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listCourseEnrolledByCourseIdAndCreatedAt(courseId: $courseId, sortDirection: $sortDirection, nextToken: $nextToken) {\n items { id courseId userId owner score rank createdAt user { id fullName avatarUrl } }\n nextToken\n }\n }`,\n { courseId, sortDirection: \"DESC\", nextToken }\n );\n return result.data.listCourseEnrolledByCourseIdAndCreatedAt;\n }\n\n async sendEnrollmentRequest(input: SendEnrollmentRequestInput): Promise<{ id: string }> {\n const result = await this.network.mutate<{ createCourseEnrollmentRequest: { id: string } }>(\n `mutation SendEnrollmentRequest($input: CreateCourseEnrollmentRequestInput!) {\n createCourseEnrollmentRequest(input: $input) { id }\n }`,\n { input: { courseId: input.courseId, name: input.name, phone: input.phone, transactionID: input.transactionId, coupon: input.couponId } }\n );\n return result.data.createCourseEnrollmentRequest;\n }\n\n async getSignedURL(input: GetSignedURLInput): Promise<SignedURLResponse> {\n const result = await this.network.query<{ getR2SignedURL: SignedURLResponse }>(\n `query GetR2SignedURL($courseId: ID!, $fileName: String!, $fileType: String!, $parentId: ID) {\n getR2SignedURL(courseId: $courseId, fileName: $fileName, fileType: $fileType, parentId: $parentId) { status signedURL }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.getR2SignedURL;\n }\n\n async getSignedFileURL(input: GetSignedFileURLInput): Promise<SignedURLResponse> {\n const result = await this.network.query<{ getR2SignedFileURL: SignedURLResponse }>(\n `query GetR2SignedFileURL($courseId: ID!, $itemId: ID!, $key: ID!) {\n getR2SignedFileURL(courseId: $courseId, itemId: $itemId, key: $key) { status signedURL }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.getR2SignedFileURL;\n }\n\n async createVideo(input: CreateCourseVideoInput): Promise<CreateCourseVideoResponse> {\n const result = await this.network.query<{ createCourseVideo: CreateCourseVideoResponse }>(\n `query CreateCourseVideo($courseId: ID!, $fileName: String!, $fileType: String!) {\n createCourseVideo(courseId: $courseId, fileName: $fileName, fileType: $fileType) { status videoId }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.createCourseVideo;\n }\n\n async uploadVideoToBunny(input: UploadVideoToBunnyInput): Promise<StatusResponse> {\n const result = await this.network.mutate<{ uploadR2VideoToBunny: StatusResponse }>(\n `mutation UploadR2VideoToBunny($key: String!, $videoId: String!, $fileType: String!) {\n uploadR2VideoToBunny(key: $key, videoId: $videoId, fileType: $fileType) { status }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.uploadR2VideoToBunny;\n }\n\n async getLiveExams(courseId: string, nextToken?: string | null): Promise<PaginatedList<CourseLiveExam>> {\n const result = await this.network.query<{\n listCourseLiveExamByCourseIdAndStartTime: PaginatedList<CourseLiveExam>;\n }>(\n `query ListLiveExams($courseId: ID!, $sortDirection: ModelSortDirection, $nextToken: String) {\n listCourseLiveExamByCourseIdAndStartTime(courseId: $courseId, sortDirection: $sortDirection, nextToken: $nextToken) {\n items { id courseId name startTime endTime owner totalParticipants resultsPublished }\n nextToken\n }\n }`,\n { courseId, sortDirection: \"DESC\", nextToken }\n );\n return result.data.listCourseLiveExamByCourseIdAndStartTime;\n }\n\n async createLiveExam(input: CreateCourseLiveExamInput): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ makeCourseLiveExam: StatusIdResponse }>(\n `mutation MakeCourseLiveExam($courseId: ID!, $name: String!, $questions: String!, $startTime: AWSDateTime!, $endTime: AWSDateTime!) {\n makeCourseLiveExam(courseId: $courseId, name: $name, questions: $questions, startTime: $startTime, endTime: $endTime) { status id }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.makeCourseLiveExam;\n }\n\n async getLiveExamQuestions(examId: string): Promise<CourseLiveExamQuestionsResponse> {\n const result = await this.network.query<{ getCourseLiveExamQuestions: CourseLiveExamQuestionsResponse }>(\n `query GetLiveExamQuestions($examId: ID!) {\n getCourseLiveExamQuestions(examId: $examId) { status name startTime endTime questions selectedOptions }\n }`,\n { examId }\n );\n return result.data.getCourseLiveExamQuestions;\n }\n\n async participateLiveExam(examId: string): Promise<StatusResponse> {\n const result = await this.network.mutate<{ participateCourseLiveExam: StatusResponse }>(\n `mutation ParticipateLiveExam($examId: ID!) {\n participateCourseLiveExam(examId: $examId) { status }\n }`,\n { examId }\n );\n return result.data.participateCourseLiveExam;\n }\n\n async selectLiveExamOption(examId: string, optionId: string, order: number): Promise<StatusResponse> {\n const result = await this.network.mutate<{ selectCourseLiveExamOption: StatusResponse }>(\n `mutation SelectLiveExamOption($examId: ID!, $optionId: ID!, $order: Int!) {\n selectCourseLiveExamOption(examId: $examId, optionId: $optionId, order: $order) { status }\n }`,\n { examId, optionId, order }\n );\n return result.data.selectCourseLiveExamOption;\n }\n\n async getLiveExamLeaderboard(examId: string, nextToken?: string | null): Promise<LeaderboardResponse> {\n const result = await this.network.query<{ getCourseLiveExamLeaderboard: LeaderboardResponse }>(\n `query GetLiveExamLeaderboard($examId: ID!, $nextToken: String) {\n getCourseLiveExamLeaderboard(examId: $examId, nextToken: $nextToken) { status leaderboard nextToken totalParticipants }\n }`,\n { examId, nextToken }\n );\n return result.data.getCourseLiveExamLeaderboard;\n }\n\n async publishLiveExamResults(examId: string): Promise<StatusResponse> {\n const result = await this.network.mutate<{ publishCourseLiveExamResults: StatusResponse }>(\n `mutation PublishLiveExamResults($examId: ID!) {\n publishCourseLiveExamResults(examId: $examId) { status }\n }`,\n { examId }\n );\n return result.data.publishCourseLiveExamResults;\n }\n\n async uploadCourseItem(input: UploadCourseItemInput): Promise<StatusIdResponse> {\n const signed = await this.getSignedURL({\n courseId: input.courseId,\n fileName: input.fileName,\n fileType: input.fileType,\n parentId: input.parentId ?? undefined,\n });\n if (signed && signed.signedURL && input.fileData) {\n await fetch(signed.signedURL, {\n method: \"PUT\",\n body: input.fileData as any,\n headers: { \"Content-Type\": input.fileType },\n });\n }\n return this.createItem({\n courseId: input.courseId,\n name: input.itemName || input.fileName,\n order: input.itemOrder,\n type: \"file\",\n parentId: input.parentId ?? undefined,\n });\n }\n}\n\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { StatusIdResponse } from \"../types/responses\";\nimport type { Subject, Chapter, Topic } from \"../types/models\";\n\ninterface QuestionCountsRecalcResponse {\n listQuestionObjects: {\n items: Array<{ id: string; subjectId?: string | null; chapterId?: string | null; topicId?: string | null }>;\n nextToken?: string | null;\n };\n}\n\ninterface CreatorStatsRecalcResponse {\n listQuestionObjects: {\n items: Array<{ id: string; owner?: string | null }>;\n nextToken?: string | null;\n };\n}\n\nexport class CurriculumService {\n constructor(private network: NetworkProvider) { }\n\n async createSubject(name: string, level: string): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ makeSubject: StatusIdResponse }>(\n `mutation MakeSubject($name: String!, $level: String!) {\n makeSubject(name: $name, level: $level) { status id }\n }`,\n { name, level }\n );\n return result.data.makeSubject;\n }\n\n async createChapter(name: string, subjectId: string, order: number): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ makeChapter: StatusIdResponse }>(\n `mutation MakeChapter($name: String!, $subjectId: ID!, $order: Int!) {\n makeChapter(name: $name, subjectId: $subjectId, order: $order) { status id }\n }`,\n { name, subjectId, order }\n );\n return result.data.makeChapter;\n }\n\n async createTopic(name: string, chapterId: string): Promise<StatusIdResponse> {\n const result = await this.network.mutate<{ makeTopic: StatusIdResponse }>(\n `mutation MakeTopic($name: String!, $chapterId: ID!) {\n makeTopic(name: $name, chapterId: $chapterId) { status id }\n }`,\n { name, chapterId }\n );\n return result.data.makeTopic;\n }\n\n async getSubjects(level: string): Promise<Subject[]> {\n const result = await this.network.query<{\n listSubjectByLevel: { items: Subject[] };\n }>(\n `query ListSubjects($level: String!) {\n listSubjectByLevel(level: $level) {\n items { id name level questionCount }\n }\n }`,\n { level }\n );\n return result.data.listSubjectByLevel.items;\n }\n\n async getChapters(subjectId: string): Promise<Chapter[]> {\n const result = await this.network.query<{\n listChapterBySubjectIdAndOrder: { items: Chapter[] };\n }>(\n `query ListChapters($subjectId: ID!, $sortDirection: ModelSortDirection) {\n listChapterBySubjectIdAndOrder(subjectId: $subjectId, sortDirection: $sortDirection) {\n items { id name order subjectId questionCount }\n }\n }`,\n { subjectId, sortDirection: \"ASC\" }\n );\n return result.data.listChapterBySubjectIdAndOrder.items;\n }\n\n async getTopics(chapterId: string): Promise<Topic[]> {\n const result = await this.network.query<{\n listTopicByChapterId: { items: Topic[] };\n }>(\n `query ListTopics($chapterId: ID!) {\n listTopicByChapterId(chapterId: $chapterId) {\n items { id name chapterId questionCount }\n }\n }`,\n { chapterId }\n );\n return result.data.listTopicByChapterId.items;\n }\n\n async recalculateQuestionCounts(): Promise<void> {\n let nextToken: string | null = null;\n const counts: Record<string, number> = {};\n do {\n const res: { data: QuestionCountsRecalcResponse } = await this.network.query<QuestionCountsRecalcResponse>(\n `query ListQuestionsForRecalc($nextToken: String) {\n listQuestionObjects(limit: 1000, nextToken: $nextToken) {\n items { id subjectId chapterId topicId }\n nextToken\n }\n }`,\n { nextToken }\n );\n const items = res.data?.listQuestionObjects?.items || [];\n for (const q of items) {\n if (q.subjectId) counts[q.subjectId] = (counts[q.subjectId] || 0) + 1;\n }\n nextToken = res.data?.listQuestionObjects?.nextToken ?? null;\n } while (nextToken);\n\n const [hscSubjects, sscSubjects] = await Promise.all([\n this.getSubjects(\"HSC\").catch(() => []),\n this.getSubjects(\"SSC\").catch(() => []),\n ]);\n const allSubjects = [...hscSubjects, ...sscSubjects];\n for (const s of allSubjects) {\n await this.network.mutate(\n `mutation UpdateSubjectCount($input: UpdateSubjectInput!) {\n updateSubject(input: $input) { id }\n }`,\n { input: { id: s.id, questionCount: counts[s.id] || 0 } }\n );\n }\n }\n\n async recalculateCreatorStats(): Promise<void> {\n let nextToken: string | null = null;\n const userQuestionCounts: Record<string, number> = {};\n do {\n const res: { data: CreatorStatsRecalcResponse } = await this.network.query<CreatorStatsRecalcResponse>(\n `query ListQuestionsForCreatorStats($nextToken: String) {\n listQuestionObjects(limit: 1000, nextToken: $nextToken) {\n items { id owner }\n nextToken\n }\n }`,\n { nextToken }\n );\n const items = res.data?.listQuestionObjects?.items || [];\n for (const q of items) {\n if (q.owner) userQuestionCounts[q.owner] = (userQuestionCounts[q.owner] || 0) + 1;\n }\n nextToken = res.data?.listQuestionObjects?.nextToken ?? null;\n } while (nextToken);\n\n for (const [userId, count] of Object.entries(userQuestionCounts)) {\n await this.network.mutate(\n `mutation UpdateCreatorStats($input: UpdateUserInput!) {\n updateUser(input: $input) { id }\n }`,\n { input: { id: userId, questionCount: count } }\n );\n }\n }\n}\n\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { StorageProvider } from \"../providers/storage\";\nimport type {\n GetSignedURLInput,\n GetSignedFileURLInput,\n CreateCourseVideoInput,\n UploadVideoToBunnyInput,\n} from \"../types/inputs\";\nimport type {\n UploadImageResponse,\n StatusResponse,\n SignedURLResponse,\n CreateCourseVideoResponse,\n} from \"../types/responses\";\n\nexport class MediaService {\n constructor(\n private network: NetworkProvider,\n private storage?: StorageProvider\n ) {}\n\n async uploadImage(questionId: string, image: string): Promise<UploadImageResponse> {\n const payloadLength = image ? image.length : 0;\n console.log(`[MediaService.uploadImage] Initiating upload mutation: questionId=\"${questionId}\", payloadLength=${payloadLength} chars`);\n\n try {\n const result = await this.network.mutate<{ uploadImage: UploadImageResponse }>(\n `mutation UploadImage($questionId: ID!, $image: String!) {\n uploadImage(questionId: $questionId, image: $image) { status imageUrl }\n }`,\n { questionId, image }\n );\n\n if (result.errors && result.errors.length > 0) {\n console.error(\n `[MediaService.uploadImage] GraphQL errors returned for questionId \"${questionId}\":`,\n JSON.stringify(result.errors, null, 2)\n );\n }\n\n const response = result.data?.uploadImage;\n if (!response) {\n console.error(`[MediaService.uploadImage] No uploadImage object returned in GraphQL response for questionId \"${questionId}\". Full result:`, result);\n } else if (response.status === \"Error\" || response.status === \"error\" || !response.imageUrl) {\n console.error(`[MediaService.uploadImage] Backend returned error status or missing imageUrl for questionId \"${questionId}\":`, response);\n } else {\n console.log(`[MediaService.uploadImage] Upload mutation success for questionId \"${questionId}\": imageUrl=\"${response.imageUrl}\"`);\n }\n\n return response;\n } catch (err) {\n console.error(`[MediaService.uploadImage] Network/GraphQL mutation failed for questionId \"${questionId}\":`, err);\n throw err;\n }\n }\n\n async deleteImage(imageUrl: string): Promise<StatusResponse> {\n const result = await this.network.mutate<{ deleteImage: StatusResponse }>(\n `mutation DeleteImage($imageUrl: ID!) {\n deleteImage(imageUrl: $imageUrl) { status }\n }`,\n { imageUrl }\n );\n return result.data.deleteImage;\n }\n\n async getSignedURL(input: GetSignedURLInput): Promise<SignedURLResponse> {\n const result = await this.network.query<{ getR2SignedURL: SignedURLResponse }>(\n `query GetR2SignedURL($courseId: ID!, $fileName: String!, $fileType: String!, $parentId: ID) {\n getR2SignedURL(courseId: $courseId, fileName: $fileName, fileType: $fileType, parentId: $parentId) { status signedURL }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.getR2SignedURL;\n }\n\n async getSignedFileURL(input: GetSignedFileURLInput): Promise<SignedURLResponse> {\n const result = await this.network.query<{ getR2SignedFileURL: SignedURLResponse }>(\n `query GetR2SignedFileURL($courseId: ID!, $itemId: ID!, $key: ID!) {\n getR2SignedFileURL(courseId: $courseId, itemId: $itemId, key: $key) { status signedURL }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.getR2SignedFileURL;\n }\n\n async getFileUrl(path: string): Promise<{ url: string }> {\n if (!this.storage) throw new Error(\"StorageProvider not configured\");\n return this.storage.getFileUrl(path);\n }\n\n async uploadFile(\n path: string,\n data: Blob | ArrayBuffer | string,\n options?: { contentType?: string; onProgress?: (progress: { loaded: number; total: number }) => void }\n ): Promise<{ path: string }> {\n if (!this.storage) throw new Error(\"StorageProvider not configured\");\n return this.storage.uploadFile(path, data, options);\n }\n\n async createVideo(input: CreateCourseVideoInput): Promise<CreateCourseVideoResponse> {\n const result = await this.network.query<{ createCourseVideo: CreateCourseVideoResponse }>(\n `query CreateCourseVideo($courseId: ID!, $fileName: String!, $fileType: String!) {\n createCourseVideo(courseId: $courseId, fileName: $fileName, fileType: $fileType) { status videoId }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.createCourseVideo;\n }\n\n async uploadVideoToBunny(input: UploadVideoToBunnyInput): Promise<StatusResponse> {\n const result = await this.network.mutate<{ uploadR2VideoToBunny: StatusResponse }>(\n `mutation UploadR2VideoToBunny($key: String!, $videoId: String!, $fileType: String!) {\n uploadR2VideoToBunny(key: $key, videoId: $videoId, fileType: $fileType) { status }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.uploadR2VideoToBunny;\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { TrackPracticeProgressInput } from \"../types/inputs\";\nimport type { StatusResponse } from \"../types/responses\";\nimport type { QuestionTrackingStatus } from \"../types/enums\";\nimport type {\n UserSubjectProgress,\n UserChapterProgress,\n UserTopicProgress,\n UserTopicMetric,\n QuestionTracking,\n PaginatedList,\n} from \"../types/models\";\n\nexport class ProgressService {\n constructor(private network: NetworkProvider) {}\n\n async track(input: TrackPracticeProgressInput): Promise<StatusResponse> {\n const variables: Record<string, unknown> = {\n questionId: input.questionId,\n isCorrect: input.isCorrect,\n subjectId: input.subjectId ?? null,\n chapterId: input.chapterId ?? null,\n topicId: input.topicId ?? null,\n };\n const result = await this.network.mutate<{ trackPracticeProgress: StatusResponse }>(\n `mutation TrackProgress($questionId: ID!, $isCorrect: Boolean!, $subjectId: ID, $chapterId: ID, $topicId: ID) {\n trackPracticeProgress(questionId: $questionId, isCorrect: $isCorrect, subjectId: $subjectId, chapterId: $chapterId, topicId: $topicId) { status }\n }`,\n variables\n );\n return result.data?.trackPracticeProgress || { status: \"success\" };\n }\n\n async getSubjectProgress(userId: string): Promise<UserSubjectProgress[]> {\n try {\n const result = await this.network.query<{\n listUserSubjectProgressByUserIdAndSubjectId?: PaginatedList<UserSubjectProgress>;\n listUserSubjectProgressesByUserIdAndSubjectId?: PaginatedList<UserSubjectProgress>;\n }>(\n `query ListSubjectProgress($userId: String!) {\n listUserSubjectProgressByUserIdAndSubjectId(userId: $userId) {\n items { id userId subjectId practicedCount mistakeCount correctedCount accuracyScore }\n }\n }`,\n { userId }\n );\n return (\n result.data?.listUserSubjectProgressByUserIdAndSubjectId?.items ||\n (result.data as any)?.listUserSubjectProgressesByUserIdAndSubjectId?.items ||\n []\n );\n } catch (e) {\n console.error(\"Error fetching subject progress:\", e);\n return [];\n }\n }\n\n async getChapterProgress(userId: string): Promise<UserChapterProgress[]> {\n try {\n const result = await this.network.query<{\n listUserChapterProgressByUserIdAndChapterId?: PaginatedList<UserChapterProgress>;\n listUserChapterProgressesByUserIdAndChapterId?: PaginatedList<UserChapterProgress>;\n }>(\n `query ListChapterProgress($userId: String!) {\n listUserChapterProgressByUserIdAndChapterId(userId: $userId) {\n items { id userId chapterId practicedCount mistakeCount correctedCount accuracyScore }\n }\n }`,\n { userId }\n );\n return (\n result.data?.listUserChapterProgressByUserIdAndChapterId?.items ||\n (result.data as any)?.listUserChapterProgressesByUserIdAndChapterId?.items ||\n []\n );\n } catch (e) {\n console.error(\"Error fetching chapter progress:\", e);\n return [];\n }\n }\n\n async getTopicProgress(userId: string): Promise<UserTopicProgress[]> {\n try {\n const result = await this.network.query<{\n listUserTopicProgressByUserIdAndTopicId?: PaginatedList<UserTopicProgress>;\n listUserTopicProgressesByUserIdAndTopicId?: PaginatedList<UserTopicProgress>;\n }>(\n `query ListTopicProgress($userId: String!) {\n listUserTopicProgressByUserIdAndTopicId(userId: $userId) {\n items { id userId topicId practicedCount mistakeCount correctedCount accuracyScore }\n }\n }`,\n { userId }\n );\n return (\n result.data?.listUserTopicProgressByUserIdAndTopicId?.items ||\n (result.data as any)?.listUserTopicProgressesByUserIdAndTopicId?.items ||\n []\n );\n } catch (e) {\n console.error(\"Error fetching topic progress:\", e);\n return [];\n }\n }\n\n async getTopicMetrics(userId: string): Promise<UserTopicMetric[]> {\n try {\n const result = await this.network.query<{\n listUserTopicMetrics?: PaginatedList<UserTopicMetric>;\n }>(\n `query ListTopicMetrics($filter: ModelUserTopicMetricFilterInput) {\n listUserTopicMetrics(filter: $filter) {\n items { id userId topicId totalAttempted totalCorrect accuracy }\n }\n }`,\n { filter: { userId: { eq: userId } } }\n );\n return result.data?.listUserTopicMetrics?.items || [];\n } catch (e) {\n console.error(\"Error fetching topic metrics:\", e);\n return [];\n }\n }\n\n async getMistakenQuestions(userId: string, status: QuestionTrackingStatus = \"MISTAKE\"): Promise<QuestionTracking[]> {\n try {\n const result = await this.network.query<{\n listQuestionTrackingByUserIdAndQuestionId?: PaginatedList<QuestionTracking>;\n listQuestionTrackingsByUserIdAndQuestionId?: PaginatedList<QuestionTracking>;\n listQuestionTrackings?: PaginatedList<QuestionTracking>;\n }>(\n `query ListMistakes($userId: String!, $filter: ModelQuestionTrackingFilterInput) {\n listQuestionTrackingByUserIdAndQuestionId(userId: $userId, filter: $filter) {\n items { id userId questionId subjectId chapterId topicId status attempts }\n }\n }`,\n { userId, filter: { status: { eq: status } } }\n );\n return (\n result.data?.listQuestionTrackingByUserIdAndQuestionId?.items ||\n (result.data as any)?.listQuestionTrackingsByUserIdAndQuestionId?.items ||\n (result.data as any)?.listQuestionTrackings?.items ||\n []\n );\n } catch (e) {\n console.error(\"Error fetching mistaken questions:\", e);\n return [];\n }\n }\n\n async getCorrectedQuestions(userId: string): Promise<QuestionTracking[]> {\n return this.getMistakenQuestions(userId, \"CORRECTED\");\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { EvaluateWrittenAnswerInput } from \"../types/inputs\";\nimport type { EvaluateWrittenAnswerResponse } from \"../types/responses\";\n\nexport interface FunctionUrlOptions {\n functionUrl?: string;\n authToken?: string;\n}\n\nexport class AIService {\n constructor(private network: NetworkProvider) {}\n\n async evaluateWrittenAnswer(\n input: EvaluateWrittenAnswerInput,\n options?: FunctionUrlOptions\n ): Promise<EvaluateWrittenAnswerResponse> {\n if (options?.functionUrl) {\n try {\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (options.authToken) {\n headers[\"Authorization\"] = `Bearer ${options.authToken}`;\n }\n const response = await fetch(options.functionUrl, {\n method: \"POST\",\n headers,\n body: JSON.stringify(input),\n });\n if (response.ok) {\n const data = await response.json();\n return data as EvaluateWrittenAnswerResponse;\n }\n console.warn(`[AIService] Function URL failed with status ${response.status}, falling back to GraphQL...`);\n } catch (err) {\n console.warn(\"[AIService] Function URL fetch error, falling back to GraphQL...\", err);\n }\n }\n\n const result = await this.network.query<{ evaluateWrittenAnswer: EvaluateWrittenAnswerResponse }>(\n `query EvaluateWrittenAnswer($question: String!, $userAnswer: String!, $correctAnswer: String!, $markingInstructions: String, $topicId: String, $topicName: String, $questionImageUrl: String) {\n evaluateWrittenAnswer(question: $question, userAnswer: $userAnswer, correctAnswer: $correctAnswer, markingInstructions: $markingInstructions, topicId: $topicId, topicName: $topicName, questionImageUrl: $questionImageUrl) {\n score maxScore feedback isCorrect markingExplanation improvementFeedback subquestions markLocation\n }\n }`,\n input as unknown as Record<string, unknown>\n );\n return result.data.evaluateWrittenAnswer;\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type { NewsArticle, PaginatedList } from \"../types/models\";\n\nexport class NewsService {\n constructor(private network: NetworkProvider) {}\n\n async create(\n title: string,\n content: string,\n image?: string,\n category?: string,\n excerpt?: string\n ): Promise<NewsArticle | null> {\n const result = await this.network.mutate<{ createNewsArticle: NewsArticle | null }>(\n `mutation CreateNews($input: CreateNewsArticleInput!) {\n createNewsArticle(input: $input) { id title content image category excerpt createdAt updatedAt }\n }`,\n { input: { title, content, image, category, excerpt } }\n );\n return result.data.createNewsArticle;\n }\n\n async list(nextToken?: string): Promise<PaginatedList<NewsArticle>> {\n const result = await this.network.query<{\n listNewsArticles: PaginatedList<NewsArticle>;\n }>(\n `query ListNews($nextToken: String) {\n listNewsArticles(nextToken: $nextToken) {\n items { id title content image category excerpt createdAt updatedAt }\n nextToken\n }\n }`,\n { nextToken }\n );\n return result.data.listNewsArticles;\n }\n\n async get(id: string): Promise<NewsArticle | null> {\n const result = await this.network.query<{ getNewsArticle: NewsArticle | null }>(\n `query GetNews($id: ID!) {\n getNewsArticle(id: $id) { id title content image category excerpt createdAt updatedAt }\n }`,\n { id }\n );\n return result.data.getNewsArticle;\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type {\n Announcement,\n UserNotification,\n PaginatedList,\n} from \"../types/models\";\nimport type {\n CreateAnnouncementInput,\n CreateUserNotificationInput,\n} from \"../types/inputs\";\n\nexport class NotificationService {\n constructor(private network: NetworkProvider) {}\n\n /**\n * List broadcast announcements (ordered by category and createdAt).\n */\n async listAnnouncements(\n limit: number = 20,\n nextToken?: string | null\n ): Promise<PaginatedList<Announcement>> {\n const result = await this.network.query<{\n listAnnouncements: PaginatedList<Announcement>;\n }>(\n `query ListAnnouncements($limit: Int, $nextToken: String) {\n listAnnouncements(limit: $limit, nextToken: $nextToken) {\n items {\n id title message senderType senderName senderAvatar category actionUrl priority createdAt updatedAt\n }\n nextToken\n }\n }`,\n { limit, nextToken }\n );\n return result.data.listAnnouncements ?? { items: [], nextToken: null };\n }\n\n /**\n * List announcements by category.\n */\n async listAnnouncementsByCategory(\n category: string = \"ANNOUNCEMENT\",\n limit: number = 20,\n nextToken?: string | null\n ): Promise<PaginatedList<Announcement>> {\n const result = await this.network.query<{\n listAnnouncementByCategoryAndCreatedAt: PaginatedList<Announcement>;\n }>(\n `query ListAnnouncementsByCategory($category: String!, $limit: Int, $nextToken: String, $sortDirection: ModelSortDirection) {\n listAnnouncementByCategoryAndCreatedAt(category: $category, limit: $limit, nextToken: $nextToken, sortDirection: $sortDirection) {\n items {\n id title message senderType senderName senderAvatar category actionUrl priority createdAt updatedAt\n }\n nextToken\n }\n }`,\n { category, limit, nextToken, sortDirection: \"DESC\" }\n );\n return (\n result.data.listAnnouncementByCategoryAndCreatedAt ?? {\n items: [],\n nextToken: null,\n }\n );\n }\n\n /**\n * Create an announcement (Admins only).\n */\n async createAnnouncement(\n input: CreateAnnouncementInput\n ): Promise<Announcement | null> {\n const result = await this.network.mutate<{\n createAnnouncement: Announcement | null;\n }>(\n `mutation CreateAnnouncement($input: CreateAnnouncementInput!) {\n createAnnouncement(input: $input) {\n id title message senderType senderName senderAvatar category actionUrl priority createdAt updatedAt\n }\n }`,\n {\n input: {\n ...input,\n senderType: input.senderType || \"ORJOK\",\n category: input.category || \"ANNOUNCEMENT\",\n priority: input.priority || \"NORMAL\",\n },\n }\n );\n return result.data.createAnnouncement;\n }\n\n /**\n * Delete an announcement (Admins only).\n */\n async deleteAnnouncement(id: string): Promise<boolean> {\n const result = await this.network.mutate<{\n deleteAnnouncement: { id: string } | null;\n }>(\n `mutation DeleteAnnouncement($input: DeleteAnnouncementInput!) {\n deleteAnnouncement(input: $input) { id }\n }`,\n { input: { id } }\n );\n return Boolean(result.data.deleteAnnouncement?.id);\n }\n\n /**\n * List user notifications for a specific user ID.\n */\n async listUserNotifications(\n userId: string,\n limit: number = 20,\n nextToken?: string | null\n ): Promise<PaginatedList<UserNotification>> {\n const result = await this.network.query<{\n listUserNotificationByUserIdAndCreatedAt: PaginatedList<UserNotification>;\n }>(\n `query ListUserNotifications($userId: ID!, $limit: Int, $nextToken: String, $sortDirection: ModelSortDirection) {\n listUserNotificationByUserIdAndCreatedAt(userId: $userId, limit: $limit, nextToken: $nextToken, sortDirection: $sortDirection) {\n items {\n id userId title message type senderType senderName senderAvatar actionUrl isRead readAt metadata createdAt updatedAt\n }\n nextToken\n }\n }`,\n { userId, limit, nextToken, sortDirection: \"DESC\" }\n );\n return (\n result.data.listUserNotificationByUserIdAndCreatedAt ?? {\n items: [],\n nextToken: null,\n }\n );\n }\n\n /**\n * Create a user notification (Level up, direct message, system alert).\n */\n async createUserNotification(\n input: CreateUserNotificationInput\n ): Promise<UserNotification | null> {\n const result = await this.network.mutate<{\n createUserNotification: UserNotification | null;\n }>(\n `mutation CreateUserNotification($input: CreateUserNotificationInput!) {\n createUserNotification(input: $input) {\n id userId title message type senderType senderName senderAvatar actionUrl isRead readAt metadata createdAt updatedAt\n }\n }`,\n {\n input: {\n ...input,\n isRead: false,\n },\n }\n );\n return result.data.createUserNotification;\n }\n\n /**\n * Mark a personal notification as read.\n */\n async markNotificationAsRead(id: string): Promise<UserNotification | null> {\n const now = new Date().toISOString();\n const result = await this.network.mutate<{\n updateUserNotification: UserNotification | null;\n }>(\n `mutation UpdateUserNotification($input: UpdateUserNotificationInput!) {\n updateUserNotification(input: $input) {\n id isRead readAt\n }\n }`,\n {\n input: {\n id,\n isRead: true,\n readAt: now,\n },\n }\n );\n return result.data.updateUserNotification;\n }\n\n /**\n * Delete a personal user notification.\n */\n async deleteUserNotification(id: string): Promise<boolean> {\n const result = await this.network.mutate<{\n deleteUserNotification: { id: string } | null;\n }>(\n `mutation DeleteUserNotification($input: DeleteUserNotificationInput!) {\n deleteUserNotification(input: $input) { id }\n }`,\n { input: { id } }\n );\n return Boolean(result.data.deleteUserNotification?.id);\n }\n\n /**\n * Update the user's lastReadAnnouncementAt timestamp in DynamoDB.\n */\n async updateLastReadAnnouncement(\n userId: string,\n timestamp: string = new Date().toISOString()\n ): Promise<boolean> {\n const result = await this.network.mutate<{\n updateUser: { id: string } | null;\n }>(\n `mutation UpdateUserLastRead($input: UpdateUserInput!) {\n updateUser(input: $input) { id lastReadAnnouncementAt }\n }`,\n { input: { id: userId, lastReadAnnouncementAt: timestamp } }\n );\n return Boolean(result.data.updateUser?.id);\n }\n}\n","import type { NetworkProvider } from \"../providers/network\";\nimport type {\n CreateSubscriptionRequestInput,\n UpdateSubscriptionRequestInput,\n ListSubscriptionRequestsInput,\n} from \"../types/inputs\";\nimport type { SubscriptionRequest, PaginatedList } from \"../types/models\";\n\nexport class SubscriptionService {\n constructor(private network: NetworkProvider) {}\n\n async createRequest(input: CreateSubscriptionRequestInput): Promise<SubscriptionRequest> {\n const result = await this.network.mutate<{\n createSubscriptionRequest: SubscriptionRequest;\n }>(\n `mutation CreateSubscriptionRequest($input: CreateSubscriptionRequestInput!) {\n createSubscriptionRequest(input: $input) {\n id userId userFullName userEmail itemType itemId itemTitle amount billingCycle paymentMethod senderPhone contactPhone trxId status aiEvals aiQuestions adminNotes processedAt processedBy createdAt updatedAt\n }\n }`,\n { input }\n );\n return result.data.createSubscriptionRequest;\n }\n\n async getRequest(id: string): Promise<SubscriptionRequest | null> {\n const result = await this.network.query<{\n getSubscriptionRequest: SubscriptionRequest | null;\n }>(\n `query GetSubscriptionRequest($id: ID!) {\n getSubscriptionRequest(id: $id) {\n id userId userFullName userEmail itemType itemId itemTitle amount billingCycle paymentMethod senderPhone contactPhone trxId status aiEvals aiQuestions adminNotes processedAt processedBy createdAt updatedAt\n }\n }`,\n { id }\n );\n return result.data.getSubscriptionRequest;\n }\n\n async listRequests(\n input: ListSubscriptionRequestsInput = {}\n ): Promise<PaginatedList<SubscriptionRequest>> {\n const limit = input.limit ?? 20;\n const sortDirection = input.sortDirection ?? \"DESC\";\n\n if (input.status) {\n const result = await this.network.query<{\n listSubscriptionRequestByStatusAndCreatedAt: PaginatedList<SubscriptionRequest>;\n }>(\n `query ListSubscriptionRequestsByStatus($status: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {\n listSubscriptionRequestByStatusAndCreatedAt(status: $status, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {\n items {\n id userId userFullName userEmail itemType itemId itemTitle amount billingCycle paymentMethod senderPhone contactPhone trxId status aiEvals aiQuestions adminNotes processedAt processedBy createdAt updatedAt\n }\n nextToken\n }\n }`,\n { status: input.status, sortDirection, limit, nextToken: input.nextToken }\n );\n return result.data.listSubscriptionRequestByStatusAndCreatedAt;\n }\n\n if (input.userId) {\n const result = await this.network.query<{\n listSubscriptionRequestByUserIdAndCreatedAt: PaginatedList<SubscriptionRequest>;\n }>(\n `query ListSubscriptionRequestsByUser($userId: String!, $sortDirection: ModelSortDirection, $limit: Int, $nextToken: String) {\n listSubscriptionRequestByUserIdAndCreatedAt(userId: $userId, sortDirection: $sortDirection, limit: $limit, nextToken: $nextToken) {\n items {\n id userId userFullName userEmail itemType itemId itemTitle amount billingCycle paymentMethod senderPhone contactPhone trxId status aiEvals aiQuestions adminNotes processedAt processedBy createdAt updatedAt\n }\n nextToken\n }\n }`,\n { userId: input.userId, sortDirection, limit, nextToken: input.nextToken }\n );\n return result.data.listSubscriptionRequestByUserIdAndCreatedAt;\n }\n\n const result = await this.network.query<{\n listSubscriptionRequests: PaginatedList<SubscriptionRequest>;\n }>(\n `query ListAllSubscriptionRequests($limit: Int, $nextToken: String, $filter: ModelSubscriptionRequestFilterInput) {\n listSubscriptionRequests(limit: $limit, nextToken: $nextToken, filter: $filter) {\n items {\n id userId userFullName userEmail itemType itemId itemTitle amount billingCycle paymentMethod senderPhone contactPhone trxId status aiEvals aiQuestions adminNotes processedAt processedBy createdAt updatedAt\n }\n nextToken\n }\n }`,\n { limit, nextToken: input.nextToken, filter: input.filter }\n );\n return result.data.listSubscriptionRequests;\n }\n\n async updateRequest(input: UpdateSubscriptionRequestInput): Promise<SubscriptionRequest> {\n const result = await this.network.mutate<{\n updateSubscriptionRequest: SubscriptionRequest;\n }>(\n `mutation UpdateSubscriptionRequest($input: UpdateSubscriptionRequestInput!) {\n updateSubscriptionRequest(input: $input) {\n id userId userFullName userEmail itemType itemId itemTitle amount billingCycle paymentMethod senderPhone contactPhone trxId status aiEvals aiQuestions adminNotes processedAt processedBy createdAt updatedAt\n }\n }`,\n { input }\n );\n return result.data.updateSubscriptionRequest;\n }\n}\n","import type { AuthProvider } from \"./providers/auth\";\nimport type { NetworkProvider } from \"./providers/network\";\nimport type { StorageProvider } from \"./providers/storage\";\nimport { UserService } from \"./services/user.service\";\nimport { QuestionService } from \"./services/question.service\";\nimport { PackService } from \"./services/pack.service\";\nimport { ContestService } from \"./services/contest.service\";\nimport { CourseService } from \"./services/course.service\";\nimport { CurriculumService } from \"./services/curriculum.service\";\nimport { MediaService } from \"./services/media.service\";\nimport { ProgressService } from \"./services/progress.service\";\nimport { AIService } from \"./services/ai.service\";\nimport { NewsService } from \"./services/news.service\";\nimport { NotificationService } from \"./services/notification.service\";\nimport { SubscriptionService } from \"./services/subscription.service\";\n\nexport interface OrjokClientConfig {\n authProvider: AuthProvider;\n networkProvider: NetworkProvider;\n storageProvider?: StorageProvider;\n}\n\nexport class OrjokClient {\n readonly auth: AuthProvider;\n readonly network: NetworkProvider;\n readonly users: UserService;\n readonly questions: QuestionService;\n readonly packs: PackService;\n readonly contests: ContestService;\n readonly courses: CourseService;\n readonly curriculum: CurriculumService;\n readonly media: MediaService;\n readonly progress: ProgressService;\n readonly ai: AIService;\n readonly news: NewsService;\n readonly notifications: NotificationService;\n readonly subscriptions: SubscriptionService;\n readonly storage?: StorageProvider;\n\n constructor(config: OrjokClientConfig) {\n this.auth = config.authProvider;\n this.network = config.networkProvider;\n this.storage = config.storageProvider;\n this.users = new UserService(config.networkProvider);\n this.questions = new QuestionService(config.networkProvider);\n this.packs = new PackService(config.networkProvider);\n this.contests = new ContestService(config.networkProvider);\n this.courses = new CourseService(config.networkProvider);\n this.curriculum = new CurriculumService(config.networkProvider);\n this.media = new MediaService(config.networkProvider, config.storageProvider);\n this.progress = new ProgressService(config.networkProvider);\n this.ai = new AIService(config.networkProvider);\n this.news = new NewsService(config.networkProvider);\n this.notifications = new NotificationService(config.networkProvider);\n this.subscriptions = new SubscriptionService(config.networkProvider);\n }\n}\n\nexport function createOrjokClient(config: OrjokClientConfig): OrjokClient {\n return new OrjokClient(config);\n}\n","import type {\n ColorOption,\n AvatarConfig,\n AvatarConfigItem,\n AvatarState,\n UICategory,\n} from \"../types/avatar\";\n\nexport const HAIR_COLORS: ColorOption[] = [\n { name: \"Original\", filter: \"none\", hex: \"#e2e8f0\", brightness: 1.0 },\n { name: \"White\", filter: \"grayscale(100%) brightness(500%)\", hex: \"#ffffff\", brightness: 2.0 },\n { name: \"Black\", filter: \"grayscale(100%) brightness(40%)\", hex: \"#1a202c\", brightness: 0.15 },\n { name: \"Brown\", filter: \"sepia(100%) saturate(300%) hue-rotate(330deg) brightness(80%)\", hex: \"#7b3f00\", brightness: 0.35 },\n { name: \"Blonde\", filter: \"sepia(100%) saturate(400%) hue-rotate(20deg) brightness(130%)\", hex: \"#e6c27a\", brightness: 0.8 },\n { name: \"Red\", filter: \"sepia(100%) saturate(500%) hue-rotate(320deg) brightness(90%)\", hex: \"#9b2c2c\", brightness: 0.4 },\n { name: \"Blue\", filter: \"sepia(100%) saturate(500%) hue-rotate(180deg) brightness(90%)\", hex: \"#2b6cb0\", brightness: 0.4 },\n { name: \"Green\", filter: \"sepia(100%) saturate(400%) hue-rotate(80deg) brightness(90%)\", hex: \"#2f855a\", brightness: 0.45 },\n];\n\nexport const CLOTHES_COLORS: ColorOption[] = [\n { name: \"Original\", filter: \"none\", hex: \"#e2e8f0\", brightness: 1.0 },\n { name: \"White\", filter: \"grayscale(100%) brightness(500%)\", hex: \"#ffffff\", brightness: 2.0 },\n { name: \"Black\", filter: \"grayscale(100%) brightness(30%)\", hex: \"#1a202c\", brightness: 0.12 },\n { name: \"Red\", filter: \"sepia(100%) saturate(500%) hue-rotate(320deg)\", hex: \"#c53030\", brightness: 0.45 },\n { name: \"Orange\", filter: \"sepia(100%) saturate(500%) hue-rotate(350deg)\", hex: \"#dd6b20\", brightness: 0.55 },\n { name: \"Yellow\", filter: \"sepia(100%) saturate(500%) hue-rotate(20deg) brightness(120%)\", hex: \"#d69e2e\", brightness: 0.8 },\n { name: \"Green\", filter: \"sepia(100%) saturate(500%) hue-rotate(80deg)\", hex: \"#38a169\", brightness: 0.55 },\n { name: \"Blue\", filter: \"sepia(100%) saturate(500%) hue-rotate(180deg)\", hex: \"#3182ce\", brightness: 0.45 },\n { name: \"Purple\", filter: \"sepia(100%) saturate(500%) hue-rotate(240deg)\", hex: \"#805ad5\", brightness: 0.45 },\n { name: \"Pink\", filter: \"sepia(100%) saturate(400%) hue-rotate(290deg)\", hex: \"#d53f8c\", brightness: 0.55 },\n];\n\nexport const COLOR_HUES: Record<string, number> = {\n Original: 0,\n Red: 0,\n Orange: 24,\n Yellow: 45,\n Green: 140,\n Blue: 210,\n Purple: 270,\n Pink: 320,\n Brown: 25,\n Blonde: 45,\n};\n\nexport const RENDER_ORDER = [\n \"Background\",\n \"Hair Back\",\n \"Clothes Back\",\n \"Skin Color Body\",\n \"Clothes Front\",\n \"Skin Color Head\",\n \"Accessories\",\n \"Facial Expression\",\n \"Beard\",\n \"Hair Front\",\n \"Eyewear\",\n \"Headwears\",\n] as const;\n\nexport const INITIAL_AVATAR_DATA: Record<UICategory, string[]> = {\n Background: [],\n Hair: [],\n \"Skin Color\": [],\n Clothes: [],\n Accessories: [],\n Headwears: [],\n Eyewear: [],\n \"Facial Expression\": [],\n Beard: [],\n};\n\nexport function getActualOptionFileName(\n category: string,\n baseName: string,\n config: AvatarConfig,\n selections: Record<string, string>\n): AvatarConfigItem | null {\n if (baseName === \"None\") return null;\n const items = config[category] || [];\n const matched = items.filter((item) => item.base_name === baseName);\n\n if (matched.length === 0) return null;\n\n for (const item of matched) {\n if (item.conditions && item.conditions.length > 0) {\n const allMet = item.conditions.every((cond) => {\n return Object.values(selections).some(\n (sel) => sel && (sel.includes(cond) || cond.includes(sel))\n );\n });\n if (allMet) return item;\n }\n }\n\n return matched.find((item) => !item.conditions || item.conditions.length === 0) || matched[0] || null;\n}\n\nexport function parseAvatarConfig(configStr: string | null | undefined): AvatarState {\n if (!configStr) {\n return { selections: {}, colors: {} };\n }\n try {\n const parsed = JSON.parse(configStr);\n return {\n selections: parsed.selections || {},\n colors: parsed.colors || {},\n gender: parsed.gender,\n };\n } catch {\n return { selections: {}, colors: {} };\n }\n}\n\nexport function parseAvatarConfigWithDefaults(\n configStr: string | null | undefined,\n seed?: string\n): AvatarState {\n const parsed = parseAvatarConfig(configStr);\n const hasSelections = Object.keys(parsed.selections).length > 0;\n if (!hasSelections) {\n return generateRandomAvatarState({ seed });\n }\n return {\n selections: { ...parsed.selections },\n colors: { ...parsed.colors },\n gender: parsed.gender,\n };\n}\n\nexport function serializeAvatarConfig(state: AvatarState): string {\n return JSON.stringify(state);\n}\n\nfunction createSeededRandom(seedStr: string): () => number {\n let h = 1779033703 ^ seedStr.length;\n for (let i = 0; i < seedStr.length; i++) {\n h = Math.imul(h ^ seedStr.charCodeAt(i), 3432918353);\n h = (h << 13) | (h >>> 19);\n }\n return () => {\n h = Math.imul(h ^ (h >>> 16), 2246822507);\n h = Math.imul(h ^ (h >>> 13), 3266489909);\n return ((h ^= h >>> 16) >>> 0) / 4294967296;\n };\n}\n\nfunction pickRandom<T>(arr: readonly T[], fallback: T, rng: () => number = Math.random): T {\n if (arr.length === 0) return fallback;\n const item = arr[Math.floor(rng() * arr.length)];\n return item !== undefined ? item : fallback;\n}\n\nexport function generateRandomAvatarState(options?: {\n gender?: \"Male\" | \"Female\";\n seed?: string;\n}): AvatarState {\n const rng = options?.seed ? createSeededRandom(options.seed) : Math.random;\n const gender: \"Male\" | \"Female\" =\n options?.gender || (rng() > 0.5 ? \"Male\" : \"Female\");\n\n const backgrounds = [\"Solid\", \"3 Stripes\"] as const;\n const skinColors = [\n \"Light Peach\",\n \"Warm Peach\",\n \"Golden Tan\",\n \"Sand Beige\",\n \"Warm Brown\",\n \"Deep Brown\",\n ] as const;\n const maleHairs = [\"Clean\", \"Mid\", \"Curled\", \"Bald\"] as const;\n const femaleHairs = [\"Bun\", \"Long\", \"Mid\", \"Short\"] as const;\n const clothesList = [\"Tshirt\", \"Hoodie\", \"Turtleneck\"] as const;\n const expressions = [\"Normal\", \"Happy\", \"Joyful\", \"Playful\", \"Surprised\"] as const;\n const eywears = [\"None\", \"None\", \"None\", \"None\", \"Normal Glasses\"] as const;\n const headwears = [\"None\", \"None\", \"None\", \"None\", \"Beanie\", \"HeadPhones\"] as const;\n\n const hairList = gender === \"Male\" ? maleHairs : femaleHairs;\n\n const bg = pickRandom(backgrounds, \"Solid\", rng);\n const skin = pickRandom(skinColors, \"Light Peach\", rng);\n const hair = pickRandom(hairList, gender === \"Male\" ? \"Clean\" : \"Short\", rng);\n const clothes = pickRandom(clothesList, \"Tshirt\", rng);\n const expression = pickRandom(expressions, \"Normal\", rng);\n const eyewear = pickRandom(eywears, \"None\", rng);\n const headwear = pickRandom(headwears, \"None\", rng);\n\n // Natural hair colors weighted higher\n const hairColorPool = [\n \"Black\",\n \"Black\",\n \"Brown\",\n \"Brown\",\n \"Blonde\",\n \"White\",\n \"Red\",\n \"Blue\",\n ] as const;\n const hairColor = pickRandom(hairColorPool, \"Black\", rng);\n\n const clothesColorPool = [\n \"Blue\",\n \"Red\",\n \"Green\",\n \"Orange\",\n \"Purple\",\n \"Black\",\n \"Yellow\",\n \"Pink\",\n ] as const;\n const clothesColor = pickRandom(clothesColorPool, \"Blue\", rng);\n\n const bgColorPool = [\"Blue\", \"Green\", \"Purple\", \"Orange\", \"Red\", \"Pink\", \"Original\"] as const;\n const bgColor = pickRandom(bgColorPool, \"Blue\", rng);\n\n return {\n selections: {\n Background: bg,\n \"Hair Back\": hair,\n \"Skin Color\": skin,\n \"Clothes Back\": clothes,\n \"Clothes Front\": clothes,\n Accessories: \"None\",\n Headwears: headwear,\n Eyewear: eyewear,\n \"Facial Expression\": expression,\n Beard: \"None\",\n \"Hair Front\": hair,\n },\n colors: {\n \"Background Color\": bgColor,\n \"Hair Color\": hairColor,\n \"Clothes Color\": clothesColor,\n \"Accessory Color\": \"Original\",\n \"Headwears Color\": \"Original\",\n \"Eyewear Color\": \"Original\",\n \"Facial Expression Color\": \"Original\",\n },\n gender,\n };\n}\n\nexport function generateRandomAvatarConfig(options?: {\n gender?: \"Male\" | \"Female\";\n seed?: string;\n}): string {\n return JSON.stringify(generateRandomAvatarState(options));\n}\n","export interface EloTier {\n name: string;\n minRating: number;\n maxRating: number;\n color: string;\n}\n\nconst TIERS: EloTier[] = [\n { name: \"Iron\", minRating: 0, maxRating: 799, color: \"#6B7280\" },\n { name: \"Bronze\", minRating: 800, maxRating: 999, color: \"#CD7F32\" },\n { name: \"Silver\", minRating: 1000, maxRating: 1199, color: \"#C0C0C0\" },\n { name: \"Gold\", minRating: 1200, maxRating: 1399, color: \"#FFD700\" },\n { name: \"Platinum\", minRating: 1400, maxRating: 1599, color: \"#00CED1\" },\n { name: \"Diamond\", minRating: 1600, maxRating: 1799, color: \"#B9F2FF\" },\n { name: \"Master\", minRating: 1800, maxRating: 1999, color: \"#9B59B6\" },\n { name: \"Grandmaster\", minRating: 2000, maxRating: 2399, color: \"#E74C3C\" },\n { name: \"Challenger\", minRating: 2400, maxRating: Infinity, color: \"#F1C40F\" },\n];\n\nexport function getEloTier(rating: number): EloTier {\n return TIERS.find((t) => rating >= t.minRating && rating <= t.maxRating) ?? TIERS[0]!;\n}\n\nexport function getEloProgress(rating: number): number {\n const tier = getEloTier(rating);\n if (tier.maxRating === Infinity) return 100;\n const range = tier.maxRating - tier.minRating;\n return Math.round(((rating - tier.minRating) / range) * 100);\n}\n\nexport function getAllTiers(): EloTier[] {\n return [...TIERS];\n}\n\nexport type RatingField =\n | \"sscOverallRating\"\n | \"sscPhysicsRating\"\n | \"sscChemistryRating\"\n | \"sscMathRating\"\n | \"sscBiologyRating\"\n | \"hscOverallRating\"\n | \"hscPhysicsRating\"\n | \"hscChemistryRating\"\n | \"hscMathRating\"\n | \"hscBiologyRating\"\n | \"bcsOverallRating\"\n | \"EngineeringRating\"\n | \"MedicalRating\"\n | \"VarsityRating\"\n | \"BCSRating\";\n\nexport function getRatingForSubject(\n user: Record<string, number | null | undefined>,\n subject: string\n): number {\n const key = subject as RatingField;\n return (user[key] as number | null | undefined) ?? 1000;\n}\n\nexport function computeRatingDelta(beforeRating: number | null | undefined, afterRating: number | null | undefined): number {\n if (beforeRating == null || afterRating == null) return 0;\n return afterRating - beforeRating;\n}\n","import type { QuestionObject } from \"../types/models\";\nimport type { Difficulty, QuestionType } from \"../types/enums\";\n\nexport type ExamMode = \"MCQ\" | \"WRITTEN\" | \"BOTH\";\n\nexport interface ExamConfig {\n mode: ExamMode;\n timePerQuestion: number;\n}\n\nexport function filterQuestionsByMode(\n questions: QuestionObject[],\n mode: ExamMode\n): QuestionObject[] {\n if (mode === \"BOTH\") return questions;\n return questions.filter((q) => q.type === mode || (!q.type && mode === \"MCQ\"));\n}\n\nexport function calculateExamTime(questionCount: number, secondsPerQuestion: number = 60): number {\n return questionCount * secondsPerQuestion;\n}\n\nexport function formatExamTime(totalSeconds: number): string {\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n if (hours > 0) {\n return `${hours}:${String(minutes).padStart(2, \"0\")}:${String(seconds).padStart(2, \"0\")}`;\n }\n return `${minutes}:${String(seconds).padStart(2, \"0\")}`;\n}\n\nexport function scoreExam(\n selectedOptions: (string | null)[],\n correctAnswers: string[]\n): { correct: number; incorrect: number; unanswered: number; total: number } {\n let correct = 0;\n let incorrect = 0;\n let unanswered = 0;\n\n for (let i = 0; i < correctAnswers.length; i++) {\n const selected = selectedOptions[i];\n if (!selected) {\n unanswered++;\n } else if (selected === correctAnswers[i]) {\n correct++;\n } else {\n incorrect++;\n }\n }\n\n return { correct, incorrect, unanswered, total: correctAnswers.length };\n}\n\nexport function isImageAnswer(answer: string | null | undefined): boolean {\n if (!answer) return false;\n return answer.startsWith(\"image::\") || answer.startsWith(\"images::\");\n}\n\nexport function parseImageKeys(answer: string): string[] {\n if (answer.startsWith(\"images::\")) {\n return answer.slice(\"images::\".length).split(\",\").filter(Boolean);\n }\n if (answer.startsWith(\"image::\")) {\n const key = answer.slice(\"image::\".length);\n return key ? [key] : [];\n }\n return [];\n}\n\nexport function buildImageAnswer(keys: string[]): string {\n if (keys.length === 0) return \"\";\n if (keys.length === 1) return `image::${keys[0]}`;\n return `images::${keys.join(\",\")}`;\n}\n\nexport function normalizeDifficulty(raw: string): Difficulty | undefined {\n const trimmed = raw.trim();\n if (!trimmed) return undefined;\n const capitalized = trimmed.charAt(0).toUpperCase() + trimmed.slice(1).toLowerCase();\n if (capitalized === \"Easy\" || capitalized === \"Medium\" || capitalized === \"Hard\") {\n return capitalized as Difficulty;\n }\n return undefined;\n}\n\nexport function detectQuestionType(\n option2: string,\n option3: string,\n option4: string\n): QuestionType {\n if (!option2.trim() && !option3.trim() && !option4.trim()) {\n return \"WRITTEN\";\n }\n return \"MCQ\";\n}\n\nexport function isCorrectAnswer(\n selected: string | null | undefined,\n correct: string | null | undefined\n): boolean {\n if (!selected || !correct) return false;\n const selTrim = String(selected).trim();\n const corrTrim = String(correct).trim();\n if (selTrim === corrTrim) return true;\n\n const selNum = Number(selTrim);\n const corrNum = Number(corrTrim);\n if (!isNaN(selNum) && !isNaN(corrNum) && selNum === corrNum) {\n return true;\n }\n return false;\n}\n\nexport function calculateDefaultExamMinutes(\n mode: ExamMode,\n questions: Array<{ type?: string | null }>\n): number {\n const mcqCount = questions.filter((q) => q.type !== \"WRITTEN\").length;\n const writtenCount = questions.filter((q) => q.type === \"WRITTEN\").length;\n\n if (mode === \"MCQ\") return Math.max(1, mcqCount * 1);\n if (mode === \"WRITTEN\") return Math.max(1, writtenCount * 10);\n return Math.max(1, mcqCount * 1 + writtenCount * 10);\n}\n\nexport function reorderObjectKeys<T>(\n obj: { [key: number]: T },\n oldIndex: number,\n newIndex: number,\n length: number\n): { [key: number]: T } {\n const arr: (T | undefined)[] = [];\n for (let i = 0; i < length; i++) {\n arr.push(obj[i]);\n }\n const [movedItem] = arr.splice(oldIndex, 1);\n arr.splice(newIndex, 0, movedItem);\n const newObj: { [key: number]: T } = {};\n arr.forEach((item, idx) => {\n if (item !== undefined) {\n newObj[idx] = item;\n }\n });\n return newObj;\n}\n\n","/**\n * Home Logic Utilities\n * Supports Signed-In User Homepage customization, greeting, streak calculation, and level mapping.\n */\n\nexport interface SubjectShortcut {\n id: string;\n name: string;\n iconKey: string;\n colorClass: string;\n}\n\n/**\n * Maps Cognito user level string to standard level enum values: \"SSC\" | \"HSC+Admission\" | \"BCS\"\n */\nexport function mapCognitoLevelToStandardLevel(cognitoLevel?: string | null): string {\n if (!cognitoLevel) return \"HSC+Admission\";\n const normalized = cognitoLevel.trim().toLowerCase();\n\n if (normalized === \"class_9\" || normalized === \"class_10\" || normalized === \"ssc\") {\n return \"SSC\";\n }\n\n if (\n normalized === \"class_11\" ||\n normalized === \"class_12\" ||\n normalized === \"admission\" ||\n normalized === \"hsc\" ||\n normalized === \"hsc+admission\"\n ) {\n return \"HSC+Admission\";\n }\n\n if (normalized === \"bcs\" || normalized === \"job\") {\n return \"BCS\";\n }\n\n return \"HSC+Admission\";\n}\n\n/**\n * Returns contextual time of day greeting\n */\nexport function getTimeOfDayGreeting(\n customHour?: number,\n lang: \"en\" | \"bn\" = \"en\"\n): { greeting: string; period: \"morning\" | \"afternoon\" | \"evening\" | \"night\" } {\n const hour = customHour !== undefined ? customHour : new Date().getHours();\n\n if (hour >= 5 && hour < 12) {\n return {\n greeting: lang === \"bn\" ? \"শুভ সকাল\" : \"Good morning\",\n period: \"morning\",\n };\n }\n\n if (hour >= 12 && hour < 17) {\n return {\n greeting: lang === \"bn\" ? \"শুভ দুপুর\" : \"Good afternoon\",\n period: \"afternoon\",\n };\n }\n\n if (hour >= 17 && hour < 21) {\n return {\n greeting: lang === \"bn\" ? \"শুভ সন্ধ্যা\" : \"Good evening\",\n period: \"evening\",\n };\n }\n\n return {\n greeting: lang === \"bn\" ? \"শুভ রাত্রি\" : \"Good evening\",\n period: \"night\",\n };\n}\n\n/**\n * Returns recommended subject shortcuts for the given level\n */\nexport function getRecommendedSubjectsForLevel(\n level: string,\n lang: \"en\" | \"bn\" = \"en\"\n): SubjectShortcut[] {\n const isBn = lang === \"bn\";\n const normalizedLevel = mapCognitoLevelToStandardLevel(level);\n\n if (normalizedLevel === \"SSC\") {\n return [\n {\n id: \"ssc-physics\",\n name: isBn ? \"পদার্থবিজ্ঞান\" : \"Physics\",\n iconKey: \"atom\",\n colorClass: \"from-blue-500 to-indigo-600\",\n },\n {\n id: \"ssc-chemistry\",\n name: isBn ? \"রসায়ন\" : \"Chemistry\",\n iconKey: \"flask\",\n colorClass: \"from-emerald-500 to-teal-600\",\n },\n {\n id: \"ssc-higher-math\",\n name: isBn ? \"উচ্চতর গণিত\" : \"Higher Math\",\n iconKey: \"math\",\n colorClass: \"from-amber-500 to-orange-600\",\n },\n {\n id: \"ssc-general-math\",\n name: isBn ? \"সাধারণ গণিত\" : \"General Math\",\n iconKey: \"calculator\",\n colorClass: \"from-violet-500 to-purple-600\",\n },\n {\n id: \"ssc-biology\",\n name: isBn ? \"জীববিজ্ঞান\" : \"Biology\",\n iconKey: \"dna\",\n colorClass: \"from-rose-500 to-pink-600\",\n },\n {\n id: \"ssc-bangla\",\n name: isBn ? \"বাংলা\" : \"Bangla\",\n iconKey: \"book\",\n colorClass: \"from-cyan-500 to-blue-600\",\n },\n ];\n }\n\n if (normalizedLevel === \"BCS\") {\n return [\n {\n id: \"bcs-bangladesh-affairs\",\n name: isBn ? \"বাংলাদেশ বিষয়াবলী\" : \"Bangladesh Affairs\",\n iconKey: \"monument\",\n colorClass: \"from-emerald-500 to-green-600\",\n },\n {\n id: \"bcs-international-affairs\",\n name: isBn ? \"আন্তর্জাতিক বিষয়াবলী\" : \"International Affairs\",\n iconKey: \"world\",\n colorClass: \"from-blue-500 to-cyan-600\",\n },\n {\n id: \"bcs-bangla\",\n name: isBn ? \"বাংলা ভাষা ও সাহিত্য\" : \"Bangla Literature\",\n iconKey: \"book\",\n colorClass: \"from-amber-500 to-orange-600\",\n },\n {\n id: \"bcs-english\",\n name: isBn ? \"ইংরেজি ভাষা ও সাহিত্য\" : \"English Language\",\n iconKey: \"language\",\n colorClass: \"from-violet-500 to-purple-600\",\n },\n {\n id: \"bcs-math-mental-ability\",\n name: isBn ? \"গাণিতিক ও মানসিক দক্ষতা\" : \"Math & Mental Ability\",\n iconKey: \"brain\",\n colorClass: \"from-pink-500 to-rose-600\",\n },\n {\n id: \"bcs-general-science\",\n name: isBn ? \"সাধারণ বিজ্ঞান ও প্রযুক্তি\" : \"General Science\",\n iconKey: \"atom\",\n colorClass: \"from-teal-500 to-emerald-600\",\n },\n ];\n }\n\n // HSC+Admission default\n return [\n {\n id: \"hsc-physics\",\n name: isBn ? \"পদার্থবিজ্ঞান\" : \"Physics\",\n iconKey: \"atom\",\n colorClass: \"from-blue-500 to-indigo-600\",\n },\n {\n id: \"hsc-chemistry\",\n name: isBn ? \"রসায়ন\" : \"Chemistry\",\n iconKey: \"flask\",\n colorClass: \"from-emerald-500 to-teal-600\",\n },\n {\n id: \"hsc-higher-math\",\n name: isBn ? \"উচ্চতর গণিত\" : \"Higher Math\",\n iconKey: \"math\",\n colorClass: \"from-amber-500 to-orange-600\",\n },\n {\n id: \"hsc-biology\",\n name: isBn ? \"জীববিজ্ঞান\" : \"Biology\",\n iconKey: \"dna\",\n colorClass: \"from-rose-500 to-pink-600\",\n },\n {\n id: \"hsc-ict\",\n name: isBn ? \"তথ্য ও যোগাযোগ প্রযুক্তি\" : \"ICT\",\n iconKey: \"device-laptop\",\n colorClass: \"from-cyan-500 to-sky-600\",\n },\n {\n id: \"hsc-bangla\",\n name: isBn ? \"বাংলা\" : \"Bangla\",\n iconKey: \"book\",\n colorClass: \"from-violet-500 to-purple-600\",\n },\n ];\n}\n\n/**\n * Calculates continuous consecutive active study streak in days\n */\nexport function calculateStreakFromDates(activityDates: string[]): {\n streak: number;\n isActiveToday: boolean;\n} {\n if (!activityDates || activityDates.length === 0) {\n return { streak: 0, isActiveToday: false };\n }\n\n const uniqueDateStrings = Array.from(\n new Set(\n activityDates\n .map((d) => {\n try {\n return new Date(d).toISOString().split(\"T\")[0];\n } catch {\n return null;\n }\n })\n .filter((d): d is string => Boolean(d))\n )\n ).sort((a, b) => (a > b ? -1 : 1)); // descending\n\n if (uniqueDateStrings.length === 0) {\n return { streak: 0, isActiveToday: false };\n }\n\n const todayStr = new Date().toISOString().split(\"T\")[0];\n const yesterdayDate = new Date();\n yesterdayDate.setDate(yesterdayDate.getDate() - 1);\n const yesterdayStr = yesterdayDate.toISOString().split(\"T\")[0];\n\n const latestDate = uniqueDateStrings[0];\n if (!latestDate) {\n return { streak: 0, isActiveToday: false };\n }\n const isActiveToday = latestDate === todayStr;\n\n if (latestDate !== todayStr && latestDate !== yesterdayStr) {\n return { streak: 0, isActiveToday: false };\n }\n\n let streak = 0;\n const checkDate = new Date(latestDate);\n\n for (const dateStr of uniqueDateStrings) {\n const expectedStr = checkDate.toISOString().split(\"T\")[0];\n if (dateStr === expectedStr) {\n streak += 1;\n checkDate.setDate(checkDate.getDate() - 1);\n } else {\n break;\n }\n }\n\n return { streak, isActiveToday };\n}\n","import type { Difficulty } from \"../types/enums\";\n\nexport interface ParsedQuestion {\n id?: string;\n imageUrl?: string;\n question: string;\n answer: string;\n option2: string;\n option3: string;\n option4: string;\n explanation: string;\n tags: string[];\n extra?: string;\n difficulty?: Difficulty;\n subjectId?: string;\n chapterId?: string;\n topicId?: string;\n publicExam?: string;\n type: \"MCQ\" | \"WRITTEN\";\n markingInstructions?: string;\n answerImageUrl?: string;\n option2ImageUrl?: string;\n option3ImageUrl?: string;\n option4ImageUrl?: string;\n}\n\nexport interface ParseResult {\n questions: ParsedQuestion[];\n errors: string[];\n}\n\nconst MAX_QUESTIONS = 200;\n\nfunction parseFields(parts: string[]): Omit<ParsedQuestion, \"question\" | \"answer\" | \"option2\" | \"option3\" | \"option4\" | \"explanation\" | \"tags\" | \"type\"> {\n let extra = \"\";\n let difficulty: Difficulty | undefined;\n let subjectId = \"\";\n let chapterId = \"\";\n let topicId = \"\";\n let markingInstructions = \"\";\n let id = \"\";\n let imageUrl = \"\";\n let answerImageUrl = \"\";\n let option2ImageUrl = \"\";\n let option3ImageUrl = \"\";\n let option4ImageUrl = \"\";\n\n if (parts.length >= 13) {\n extra = parts[7]?.trim() ?? \"\";\n const rawDiff = parts[8]?.trim() ?? \"\";\n if (rawDiff) {\n const cap = rawDiff.charAt(0).toUpperCase() + rawDiff.slice(1).toLowerCase();\n if (cap === \"Easy\" || cap === \"Medium\" || cap === \"Hard\") difficulty = cap as Difficulty;\n }\n subjectId = parts[9]?.trim() ?? \"\";\n chapterId = parts[10]?.trim() ?? \"\";\n topicId = parts[11]?.trim() ?? \"\";\n markingInstructions = parts[12]?.trim() ?? \"\";\n if (parts.length > 13) id = parts[13]?.trim() ?? \"\";\n if (parts.length > 14) imageUrl = parts[14]?.trim() ?? \"\";\n if (parts.length > 15) answerImageUrl = parts[15]?.trim() ?? \"\";\n if (parts.length > 16) option2ImageUrl = parts[16]?.trim() ?? \"\";\n if (parts.length > 17) option3ImageUrl = parts[17]?.trim() ?? \"\";\n if (parts.length > 18) option4ImageUrl = parts[18]?.trim() ?? \"\";\n } else if (parts.length === 8) {\n extra = parts[7]?.trim() ?? \"\";\n } else if (parts.length === 9) {\n extra = parts[7]?.trim() ?? \"\";\n const rawDiff = parts[8]?.trim() ?? \"\";\n if (rawDiff) {\n const cap = rawDiff.charAt(0).toUpperCase() + rawDiff.slice(1).toLowerCase();\n if (cap === \"Easy\" || cap === \"Medium\" || cap === \"Hard\") difficulty = cap as Difficulty;\n }\n } else if (parts.length === 10) {\n subjectId = parts[7]?.trim() ?? \"\";\n chapterId = parts[8]?.trim() ?? \"\";\n topicId = parts[9]?.trim() ?? \"\";\n } else if (parts.length === 11) {\n extra = parts[7]?.trim() ?? \"\";\n subjectId = parts[8]?.trim() ?? \"\";\n chapterId = parts[9]?.trim() ?? \"\";\n topicId = parts[10]?.trim() ?? \"\";\n } else if (parts.length === 12) {\n extra = parts[7]?.trim() ?? \"\";\n const rawDiff = parts[8]?.trim() ?? \"\";\n if (rawDiff) {\n const cap = rawDiff.charAt(0).toUpperCase() + rawDiff.slice(1).toLowerCase();\n if (cap === \"Easy\" || cap === \"Medium\" || cap === \"Hard\") difficulty = cap as Difficulty;\n }\n subjectId = parts[9]?.trim() ?? \"\";\n chapterId = parts[10]?.trim() ?? \"\";\n topicId = parts[11]?.trim() ?? \"\";\n }\n\n return {\n id: id || undefined,\n imageUrl: imageUrl || undefined,\n extra: extra || undefined,\n difficulty,\n subjectId: subjectId || undefined,\n chapterId: chapterId || undefined,\n topicId: topicId || undefined,\n markingInstructions: markingInstructions || undefined,\n answerImageUrl: answerImageUrl || undefined,\n option2ImageUrl: option2ImageUrl || undefined,\n option3ImageUrl: option3ImageUrl || undefined,\n option4ImageUrl: option4ImageUrl || undefined,\n };\n}\n\nfunction buildQuestion(parts: string[]): ParsedQuestion {\n const tags =\n parts.length >= 7 && parts[6]\n ? parts[6].split(\",\").map((t) => t.trim()).filter(Boolean)\n : [];\n\n const opt2 = parts[2]?.trim() ?? \"\";\n const opt3 = parts[3]?.trim() ?? \"\";\n const opt4 = parts[4]?.trim() ?? \"\";\n const isWritten = parts.length < 6 || (!opt2 && !opt3 && !opt4);\n\n const extra = parseFields(parts);\n\n return {\n question: parts[0]?.trim() ?? \"\",\n answer: parts[1]?.trim() ?? \"\",\n option2: opt2,\n option3: opt3,\n option4: opt4,\n explanation: parts[5]?.trim() ?? \"\",\n tags,\n type: isWritten ? \"WRITTEN\" : \"MCQ\",\n ...extra,\n };\n}\n\nexport function parseTextQuestions(text: string, keyword: string, minParts: number = 2): ParseResult {\n const lines = text.split(\"\\n\");\n const questions: ParsedQuestion[] = [];\n const errors: string[] = [];\n\n if (lines.length > MAX_QUESTIONS) {\n errors.push(`Can't have more than ${MAX_QUESTIONS} questions/lines`);\n return { questions, errors };\n }\n\n for (let idx = 0; idx < lines.length; idx++) {\n const line = lines[idx]!;\n const parts = line.split(keyword);\n if (parts.length === 1 && (parts[0] === \"\\r\" || parts[0] === \"\\n\" || parts[0] === \"\")) {\n continue;\n }\n if (parts.length < minParts || parts.length > 19) {\n errors.push(`Line ${idx + 1} doesn't have between ${minParts} and 19 parts`);\n continue;\n }\n questions.push(buildQuestion(parts));\n }\n\n return { questions, errors };\n}\n\nexport function parseCsvQuestions(rows: string[][]): ParseResult {\n const questions: ParsedQuestion[] = [];\n const errors: string[] = [];\n\n // Skip header row\n const dataRows = rows.slice(1);\n if (dataRows.length > MAX_QUESTIONS) {\n errors.push(`Can't have more than ${MAX_QUESTIONS} questions/lines`);\n return { questions, errors };\n }\n\n for (let idx = 0; idx < dataRows.length; idx++) {\n const line = dataRows[idx]!;\n if (line.length < 2 || line.length > 19) {\n errors.push(`Line ${idx + 2} doesn't have between 2 and 19 parts`);\n continue;\n }\n questions.push(buildQuestion(line));\n }\n\n return { questions, errors };\n}\n\nexport function formatQuestionsToText(\n questions: ParsedQuestion[],\n keyword: string,\n packSubjectId?: string,\n packChapterId?: string,\n packTopicId?: string\n): string {\n const lines = questions.map((q) => {\n const activeSubject = q.subjectId || packSubjectId;\n const activeChapter = q.chapterId || packChapterId;\n const activeTopic = q.topicId || packTopicId;\n\n const subjectSlug = activeSubject ? activeSubject.split(\"::\")[0]?.trim() ?? \"\" : \"\";\n const chapterSlug = activeChapter ? activeChapter.split(\"::\")[0]?.trim() ?? \"\" : \"\";\n const topicSlug = activeTopic ? activeTopic.split(\"::\")[0]?.trim() ?? \"\" : \"\";\n\n const fields = [\n q.question,\n q.answer,\n q.type === \"WRITTEN\" ? \"\" : q.option2,\n q.type === \"WRITTEN\" ? \"\" : q.option3,\n q.type === \"WRITTEN\" ? \"\" : q.option4,\n q.explanation,\n q.tags.join(\", \"),\n q.extra ?? \"\",\n q.difficulty ?? \"\",\n subjectSlug,\n chapterSlug,\n topicSlug,\n q.markingInstructions ?? \"\",\n q.id ?? \"\",\n q.imageUrl ?? \"\",\n q.answerImageUrl ?? \"\",\n q.option2ImageUrl ?? \"\",\n q.option3ImageUrl ?? \"\",\n q.option4ImageUrl ?? \"\",\n ];\n return fields.map((f) => (f || \"\").trim()).join(keyword);\n });\n\n return lines.join(\"\\n\");\n}\n\nexport function formatQuestionsToCsv(\n questions: ParsedQuestion[],\n packSubjectId?: string,\n packChapterId?: string,\n packTopicId?: string\n): string {\n const escapeCSV = (value: string | undefined) =>\n `\"${(value || \"\").replace(/\"/g, '\"\"')}\"`;\n\n const header =\n \"Question,Answer,Option2,Option3,Option4,Explanation,Tags,Extra,Difficulty,Subject,Chapter,Topic,MarkingInstructions,Id,ImageUrl,AnswerImageUrl,Option2ImageUrl,Option3ImageUrl,Option4ImageUrl\";\n\n const rows = questions.map((q) => {\n const activeSubject = q.subjectId || packSubjectId;\n const activeChapter = q.chapterId || packChapterId;\n const activeTopic = q.topicId || packTopicId;\n\n const subjectSlug = activeSubject ? activeSubject.split(\"::\")[0]?.trim() ?? \"\" : \"\";\n const chapterSlug = activeChapter ? activeChapter.split(\"::\")[0]?.trim() ?? \"\" : \"\";\n const topicSlug = activeTopic ? activeTopic.split(\"::\")[0]?.trim() ?? \"\" : \"\";\n\n return [\n escapeCSV(q.question),\n escapeCSV(q.answer),\n escapeCSV(q.type === \"WRITTEN\" ? \"\" : q.option2),\n escapeCSV(q.type === \"WRITTEN\" ? \"\" : q.option3),\n escapeCSV(q.type === \"WRITTEN\" ? \"\" : q.option4),\n escapeCSV(q.explanation),\n escapeCSV(q.tags.join(\", \")),\n escapeCSV(q.extra),\n escapeCSV(q.difficulty),\n escapeCSV(subjectSlug),\n escapeCSV(chapterSlug),\n escapeCSV(topicSlug),\n escapeCSV(q.markingInstructions),\n escapeCSV(q.id),\n escapeCSV(q.imageUrl),\n escapeCSV(q.answerImageUrl),\n escapeCSV(q.option2ImageUrl),\n escapeCSV(q.option3ImageUrl),\n escapeCSV(q.option4ImageUrl),\n ].join(\",\");\n });\n\n return `${header}\\n${rows.join(\"\\n\")}`;\n}\n","export type BillingCycle = \"monthly\" | \"quarterly\" | \"biannually\" | \"annually\";\nexport type SubscriptionTier = \"FREE\" | \"PRO\" | \"PRO_AI\";\n\nexport interface PlanCyclePricing {\n monthly: number;\n quarterly: number;\n biannually: number;\n annually: number;\n}\n\nexport interface PlanAiQuotaConfig {\n monthly: { aiEvals: number; aiQuestions: number };\n quarterly: { aiEvals: number; aiQuestions: number };\n biannually: { aiEvals: number; aiQuestions: number };\n annually: { aiEvals: number; aiQuestions: number };\n}\n\nexport interface SubscriptionPlan {\n id: SubscriptionTier;\n tier: SubscriptionTier;\n titleKey: string;\n descKey: string;\n pricing: PlanCyclePricing;\n aiQuotas: PlanAiQuotaConfig;\n}\n\nexport interface TopUpBoosterPack {\n id: string;\n type: \"AI_EVAL\" | \"AI_QUESTION\";\n amount: number;\n price: number;\n titleKey: string;\n descKey: string;\n badgeKey?: string;\n}\n\nexport const MANUAL_PAYMENT_CONFIG = {\n phoneNumber: \"01323567688\",\n accountType: \"Personal\",\n methods: [\"BKASH\", \"ROCKET\"] as const,\n};\n\nexport const SUBSCRIPTION_PLANS: Record<SubscriptionTier, SubscriptionPlan> = {\n FREE: {\n id: \"FREE\",\n tier: \"FREE\",\n titleKey: \"upgrade.freeTitle\",\n descKey: \"upgrade.freeDesc\",\n pricing: {\n monthly: 0,\n quarterly: 0,\n biannually: 0,\n annually: 0,\n },\n aiQuotas: {\n monthly: { aiEvals: 0, aiQuestions: 0 },\n quarterly: { aiEvals: 0, aiQuestions: 0 },\n biannually: { aiEvals: 0, aiQuestions: 0 },\n annually: { aiEvals: 0, aiQuestions: 0 },\n },\n },\n PRO: {\n id: \"PRO\",\n tier: \"PRO\",\n titleKey: \"upgrade.proTitle\",\n descKey: \"upgrade.proDesc\",\n pricing: {\n monthly: 149,\n quarterly: 399,\n biannually: 699,\n annually: 999,\n },\n aiQuotas: {\n monthly: { aiEvals: 0, aiQuestions: 0 },\n quarterly: { aiEvals: 0, aiQuestions: 0 },\n biannually: { aiEvals: 0, aiQuestions: 0 },\n annually: { aiEvals: 0, aiQuestions: 0 },\n },\n },\n PRO_AI: {\n id: \"PRO_AI\",\n tier: \"PRO_AI\",\n titleKey: \"upgrade.aiTitle\",\n descKey: \"upgrade.aiDesc\",\n pricing: {\n monthly: 349,\n quarterly: 899,\n biannually: 1599,\n annually: 2499,\n },\n aiQuotas: {\n monthly: { aiEvals: 100, aiQuestions: 450 },\n quarterly: { aiEvals: 280, aiQuestions: 1100 },\n biannually: { aiEvals: 500, aiQuestions: 2000 },\n annually: { aiEvals: 800, aiQuestions: 3000 },\n },\n },\n};\n\nexport const AI_EVAL_BOOSTER_PACKS: TopUpBoosterPack[] = [\n {\n id: \"eval_booster_starter\",\n type: \"AI_EVAL\",\n amount: 40,\n price: 69,\n titleKey: \"upgrade.boosters.evalStarterTitle\",\n descKey: \"upgrade.boosters.evalStarterDesc\",\n },\n {\n id: \"eval_booster_standard\",\n type: \"AI_EVAL\",\n amount: 100,\n price: 159,\n titleKey: \"upgrade.boosters.evalStandardTitle\",\n descKey: \"upgrade.boosters.evalStandardDesc\",\n badgeKey: \"upgrade.popular\",\n },\n {\n id: \"eval_booster_mega\",\n type: \"AI_EVAL\",\n amount: 220,\n price: 349,\n titleKey: \"upgrade.boosters.evalMegaTitle\",\n descKey: \"upgrade.boosters.evalMegaDesc\",\n badgeKey: \"upgrade.bestValue\",\n },\n];\n\nexport const AI_QUESTION_BOOSTER_PACKS: TopUpBoosterPack[] = [\n {\n id: \"question_booster_drill\",\n type: \"AI_QUESTION\",\n amount: 100,\n price: 39,\n titleKey: \"upgrade.boosters.questionDrillTitle\",\n descKey: \"upgrade.boosters.questionDrillDesc\",\n },\n {\n id: \"question_booster_chapter\",\n type: \"AI_QUESTION\",\n amount: 250,\n price: 99,\n titleKey: \"upgrade.boosters.questionChapterTitle\",\n descKey: \"upgrade.boosters.questionChapterDesc\",\n badgeKey: \"upgrade.popular\",\n },\n {\n id: \"question_booster_exam\",\n type: \"AI_QUESTION\",\n amount: 700,\n price: 249,\n titleKey: \"upgrade.boosters.questionExamTitle\",\n descKey: \"upgrade.boosters.questionExamDesc\",\n badgeKey: \"upgrade.bestValue\",\n },\n];\n\nexport function getCycleMultiplier(cycle: BillingCycle): number {\n switch (cycle) {\n case \"quarterly\":\n return 3;\n case \"biannually\":\n return 6;\n case \"annually\":\n return 12;\n case \"monthly\":\n default:\n return 1;\n }\n}\n\nexport function getPlanOriginalPrice(tier: SubscriptionTier | string, cycle: BillingCycle): number {\n const normalizedTier = (tier === \"AI_ANNUAL\" || tier === \"AI_MONTHLY\" ? \"PRO_AI\" : tier) as SubscriptionTier;\n const plan = SUBSCRIPTION_PLANS[normalizedTier] || SUBSCRIPTION_PLANS.FREE;\n const monthlyPrice = plan.pricing.monthly ?? 0;\n return monthlyPrice * getCycleMultiplier(cycle);\n}\n\nexport function getPlanPrice(tier: SubscriptionTier | string, cycle: BillingCycle): number {\n const normalizedTier = (tier === \"AI_ANNUAL\" || tier === \"AI_MONTHLY\" ? \"PRO_AI\" : tier) as SubscriptionTier;\n const plan = SUBSCRIPTION_PLANS[normalizedTier] || SUBSCRIPTION_PLANS.FREE;\n return plan.pricing[cycle] ?? 0;\n}\n\nexport function getPlanMonthlyEquivalentPrice(tier: SubscriptionTier | string, cycle: BillingCycle): number {\n const normalizedTier = (tier === \"AI_ANNUAL\" || tier === \"AI_MONTHLY\" ? \"PRO_AI\" : tier) as SubscriptionTier;\n const plan = SUBSCRIPTION_PLANS[normalizedTier] || SUBSCRIPTION_PLANS.FREE;\n const cyclePrice = plan.pricing[cycle] ?? 0;\n const multiplier = getCycleMultiplier(cycle);\n return Math.round(cyclePrice / multiplier);\n}\n\nexport function getPlanDiscountPercent(cycle: BillingCycle): number {\n switch (cycle) {\n case \"quarterly\":\n return 10;\n case \"biannually\":\n return 22;\n case \"annually\":\n return 44;\n case \"monthly\":\n default:\n return 0;\n }\n}\n\nexport function getPlanAiQuotas(\n tier: SubscriptionTier | string,\n cycle: BillingCycle\n): { aiEvals: number; aiQuestions: number } {\n const normalizedTier = (tier === \"AI_ANNUAL\" || tier === \"AI_MONTHLY\" ? \"PRO_AI\" : tier) as SubscriptionTier;\n const plan = SUBSCRIPTION_PLANS[normalizedTier] || SUBSCRIPTION_PLANS.FREE;\n return plan.aiQuotas[cycle] ?? { aiEvals: 0, aiQuestions: 0 };\n}\n\nexport function isValidBangladeshiPhone(phone: string): boolean {\n if (!phone) return false;\n // Strip spaces, dashes, parentheses\n const cleaned = phone.replace(/[\\s\\-()]/g, \"\");\n // Matches +8801[3-9]\\d{8} or 8801[3-9]\\d{8} or 01[3-9]\\d{8}\n const regex = /^(?:\\+?88)?01[3-9]\\d{8}$/;\n return regex.test(cleaned);\n}\n\nexport function isValidTrxId(trxId: string): boolean {\n if (!trxId) return false;\n const cleaned = trxId.trim();\n return cleaned.length >= 4 && cleaned.length <= 40;\n}\n\nexport function hasProAccess(tier?: string | null, expiresAt?: string | null): boolean {\n if (!tier) return false;\n const upper = tier.toUpperCase();\n const isTierPro = upper === \"PRO\" || upper === \"PRO_AI\";\n if (!isTierPro) return false;\n if (!expiresAt) return true;\n const expTime = new Date(expiresAt).getTime();\n if (isNaN(expTime)) return true;\n return expTime > Date.now();\n}\n\nexport function hasAiTierAccess(tier?: string | null, expiresAt?: string | null): boolean {\n if (!tier) return false;\n const upper = tier.toUpperCase();\n const isTierAi = upper === \"PRO_AI\";\n if (!isTierAi) return false;\n if (!expiresAt) return true;\n const expTime = new Date(expiresAt).getTime();\n if (isNaN(expTime)) return true;\n return expTime > Date.now();\n}\n\nexport const FREE_DAILY_PRACTICE_LIMIT = 30;\n\nexport function canExportPack(tier?: string | null, expiresAt?: string | null, isCreator?: boolean): boolean {\n return Boolean(isCreator || hasProAccess(tier, expiresAt));\n}\n\nexport function canAccessMistakeVault(tier?: string | null, expiresAt?: string | null, isCreator?: boolean): boolean {\n return Boolean(isCreator || hasProAccess(tier, expiresAt));\n}\n\nexport function canWatchContestSolution(tier?: string | null, expiresAt?: string | null, isCreator?: boolean): boolean {\n return Boolean(isCreator || hasProAccess(tier, expiresAt));\n}\n\nexport function shouldTrackPracticeProgress(tier?: string | null, expiresAt?: string | null, isCreator?: boolean): boolean {\n return Boolean(isCreator || hasProAccess(tier, expiresAt));\n}\n\nexport function getDailyPracticeLimit(tier?: string | null, expiresAt?: string | null): number | null {\n if (hasProAccess(tier, expiresAt)) return null;\n return FREE_DAILY_PRACTICE_LIMIT;\n}\n\nexport function checkDailyPracticeLimit(\n dailyCount: number,\n lastPracticeDate: string | null | undefined,\n tier?: string | null,\n expiresAt?: string | null,\n todayStr?: string\n): { allowed: boolean; remaining: number; currentCount: number; isUnlimited: boolean } {\n if (hasProAccess(tier, expiresAt)) {\n return {\n allowed: true,\n remaining: Infinity,\n currentCount: dailyCount,\n isUnlimited: true,\n };\n }\n\n const today: string = todayStr || (new Date().toISOString().split(\"T\")[0] as string);\n const isSameDay = lastPracticeDate ? lastPracticeDate.startsWith(today) : false;\n const currentCount = isSameDay ? (Number.isFinite(dailyCount) ? dailyCount : 0) : 0;\n const remaining = Math.max(0, FREE_DAILY_PRACTICE_LIMIT - currentCount);\n const allowed = currentCount < FREE_DAILY_PRACTICE_LIMIT;\n\n return {\n allowed,\n remaining,\n currentCount,\n isUnlimited: false,\n };\n}\n\nexport function hasSufficientAiQuestionQuota(quota: number | null | undefined, requested: number): boolean {\n if (requested <= 0) return false;\n return (quota ?? 0) >= requested;\n}\n\nexport function hasSufficientAiEvalQuota(quota: number | null | undefined): boolean {\n return (quota ?? 0) > 0;\n}\n\n","export type PrimaryTab = \"home\" | \"learn\" | \"contests\" | \"courses\" | \"profile\";\n\nexport function getActiveTab(pathname: string): PrimaryTab {\n const normalized = pathname.toLowerCase();\n\n if (\n normalized.startsWith(\"/contests\") ||\n normalized.startsWith(\"/contest\")\n ) {\n return \"contests\";\n }\n\n if (\n normalized.startsWith(\"/courses\") ||\n normalized.startsWith(\"/course\") ||\n normalized.startsWith(\"/exams\")\n ) {\n return \"courses\";\n }\n\n if (\n normalized.startsWith(\"/profile\") ||\n normalized.startsWith(\"/dashboard\") ||\n normalized.startsWith(\"/edit-profile\")\n ) {\n return \"profile\";\n }\n\n if (\n normalized.startsWith(\"/learn\") ||\n normalized.startsWith(\"/practice\") ||\n normalized.startsWith(\"/questions\") ||\n normalized.startsWith(\"/question\") ||\n normalized.startsWith(\"/bank\") ||\n normalized.startsWith(\"/question-bank\") ||\n normalized.startsWith(\"/packs\") ||\n normalized.startsWith(\"/pack\") ||\n normalized.startsWith(\"/taxonomy\") ||\n normalized.startsWith(\"/explore\")\n ) {\n return \"learn\";\n }\n\n return \"home\";\n}\n\nexport function getCanonicalPath(pathname: string): string {\n const normalized = pathname.toLowerCase();\n\n if (normalized === \"/explore/questions\") return \"/learn/questions\";\n if (normalized === \"/explore/packs\") return \"/learn/packs\";\n if (normalized === \"/question-bank\") return \"/learn/bank\";\n if (normalized === \"/contest/all\") return \"/contests\";\n if (normalized === \"/dashboard\") return \"/profile\";\n if (normalized === \"/edit-profile\") return \"/profile/edit\";\n\n return pathname;\n}\n\nexport function isAuthGatedRoute(pathname: string): boolean {\n const normalized = pathname.toLowerCase();\n\n const isProtectedPath =\n normalized.includes(\"/exam\") ||\n normalized.includes(\"/enroll\") ||\n normalized.startsWith(\"/profile\") ||\n normalized.startsWith(\"/dashboard\") ||\n normalized.startsWith(\"/edit-profile\") ||\n normalized.startsWith(\"/avatar-creator\") ||\n normalized.startsWith(\"/practice\");\n\n return isProtectedPath;\n}\n","const BANGLA_DIGITS = [\"০\", \"১\", \"২\", \"৩\", \"৪\", \"৫\", \"৬\", \"৭\", \"৮\", \"৯\"] as const;\n\nexport function engToBanglaNumber(num: number | string): string {\n return String(num)\n .split(\"\")\n .map((digit) => {\n const n = parseInt(digit, 10);\n return !isNaN(n) ? BANGLA_DIGITS[n]! : digit;\n })\n .join(\"\");\n}\n","/**\n * Splits a bilingual string formatted as \"Bangla Name (English Name)\"\n * into the requested language (\"bn\" or \"en\").\n * If the string does not follow the bilingual pattern, returns the clean string.\n */\nexport function extractBilingualName(name: string, language?: \"bn\" | \"en\" | string): string {\n if (!name) return \"\";\n const trimmed = name.trim();\n\n // Match \"Part1 (Part2)\" or \"Part1 [Part2]\" or full-width \"Part1 (Part2)\"\n const match = trimmed.match(/^(.*?)\\s*[\\(\\[(]\\s*([^\\)\\])]+)\\s*[\\)\\])]\\s*$/);\n if (!match) {\n return trimmed;\n }\n\n const part1 = match[1]?.trim() || \"\";\n const part2 = match[2]?.trim() || \"\";\n\n if (!part1 && !part2) return trimmed;\n if (!part1) return part2;\n if (!part2) return part1;\n\n const hasBengali = (text: string) => /[\\u0980-\\u09FF]/.test(text);\n\n const isPart1Bengali = hasBengali(part1);\n const isPart2Bengali = hasBengali(part2);\n\n let banglaPart = part1;\n let englishPart = part2;\n\n if (!isPart1Bengali && isPart2Bengali) {\n banglaPart = part2;\n englishPart = part1;\n } else {\n banglaPart = part1;\n englishPart = part2;\n }\n\n const lang = language?.toLowerCase();\n if (lang === \"en\") {\n return englishPart || banglaPart || trimmed;\n }\n if (lang === \"bn\") {\n return banglaPart || englishPart || trimmed;\n }\n\n return trimmed;\n}\n\nexport function formatCurriculumName(\n idString: string,\n dbName?: string | null,\n language?: \"bn\" | \"en\" | string\n): string {\n if (dbName) {\n return extractBilingualName(dbName, language);\n }\n if (!idString) return \"\";\n const namePart = idString.split(\"::\")[0] ?? \"\";\n const clean = namePart.replace(/[-_]/g, \" \");\n const formatted = clean\n .split(/\\s+/)\n .map((word) => {\n if (!word) return \"\";\n return word.charAt(0).toUpperCase() + word.slice(1);\n })\n .join(\" \");\n\n return extractBilingualName(formatted, language);\n}\n\n","export function getRelativeTime(startTime: string | Date, endTime?: string | Date): string {\n const now = new Date();\n const start = new Date(startTime);\n const end = endTime ? new Date(endTime) : null;\n\n if (end && now >= start && now <= end) {\n return \"\";\n }\n\n const diffInSeconds = Math.floor((start.getTime() - now.getTime()) / 1000);\n const absDiff = Math.abs(diffInSeconds);\n\n const minute = 60;\n const hour = minute * 60;\n const day = hour * 24;\n const week = day * 7;\n const month = day * 30;\n const year = day * 365;\n\n if (diffInSeconds < 0) {\n if (absDiff < minute) return \"just now\";\n if (absDiff < hour) return `${Math.floor(absDiff / minute)} minutes ago`;\n if (absDiff < day) return `${Math.floor(absDiff / hour)} hours ago`;\n if (absDiff < week) return `${Math.floor(absDiff / day)} days ago`;\n if (absDiff < month) return `${Math.floor(absDiff / week)} weeks ago`;\n if (absDiff < year) return `${Math.floor(absDiff / month)} months ago`;\n return `${Math.floor(absDiff / year)} years ago`;\n } else {\n if (absDiff < minute) return \"in a moment\";\n if (absDiff < hour) return `in ${Math.floor(absDiff / minute)} minutes`;\n if (absDiff < day) return `in ${Math.floor(absDiff / hour)} hours`;\n if (absDiff < week) return `in ${Math.floor(absDiff / day)} days`;\n if (absDiff < month) return `in ${Math.floor(absDiff / week)} weeks`;\n if (absDiff < year) return `in ${Math.floor(absDiff / month)} months`;\n return `in ${Math.floor(absDiff / year)} years`;\n }\n}\n","export function processLatexText(str: string): string {\n if (!str) return str;\n\n const regex = /(\\$\\$(.*?)\\$\\$)|(\\$(.*?)\\$)/gs;\n\n let lastIndex = 0;\n let result = \"\";\n let match;\n\n while ((match = regex.exec(str)) !== null) {\n const textBefore = str.substring(lastIndex, match.index);\n result += textBefore.replace(/\\\\n/g, \"\\n\");\n\n const isDisplay = match[1] !== undefined;\n const mathContent = isDisplay ? match[2]! : match[4]!;\n const delimiter = isDisplay ? \"$$\" : \"$\";\n\n const processedMath = mathContent.replace(\n /(\\\\text\\s*\\{[^{}]*\\})|([\\u0980-\\u09FF]+(?:\\s+[\\u0980-\\u09FF]+)*)/g,\n (_: string, p1: string | undefined, p2: string | undefined) => {\n if (p1) return p1;\n return `\\\\text{${p2}}`;\n }\n );\n\n result += delimiter + processedMath + delimiter;\n lastIndex = regex.lastIndex;\n }\n\n const remainingText = str.substring(lastIndex);\n result += remainingText.replace(/\\\\n/g, \"\\n\");\n\n return result;\n}\n","export interface TimeLeft {\n days: number;\n hours: number;\n minutes: number;\n seconds: number;\n isEnded: boolean;\n}\n\nexport function convertToBangladeshTime(isoString: string, locale: string = \"en-GB\"): string {\n const options: Intl.DateTimeFormatOptions = {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"numeric\",\n hour12: true,\n };\n\n const date = new Date(isoString);\n\n // Convert to Bangladesh Standard Time (BST), which is UTC +6\n const resolvedLocale = locale === \"bn\" ? \"bn-BD\" : locale;\n return date.toLocaleString(resolvedLocale, {\n ...options,\n timeZone: \"Asia/Dhaka\",\n });\n}\n\nexport function getDhakaNow(): Date {\n const now = new Date();\n const parts = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: \"Asia/Dhaka\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false,\n }).formatToParts(now);\n\n const get = (type: string) => parts.find((p) => p.type === type)?.value ?? \"00\";\n return new Date(`${get(\"year\")}-${get(\"month\")}-${get(\"day\")}T${get(\"hour\")}:${get(\"minute\")}:${get(\"second\")}`);\n}\n\nexport function calculateTimeLeft(endTime: string | Date): TimeLeft {\n const target = new Date(endTime).getTime();\n const now = new Date().getTime();\n const difference = target - now;\n\n if (difference <= 0 || isNaN(difference)) {\n return {\n days: 0,\n hours: 0,\n minutes: 0,\n seconds: 0,\n isEnded: true,\n };\n }\n\n return {\n days: Math.floor(difference / (1000 * 60 * 60 * 24)),\n hours: Math.floor((difference / (1000 * 60 * 60)) % 24),\n minutes: Math.floor((difference / 1000 / 60) % 60),\n seconds: Math.floor((difference / 1000) % 60),\n isEnded: false,\n };\n}\n\nexport function formatDate(date: Date | string, locale: string = \"en-GB\"): string {\n const d = typeof date === \"string\" ? new Date(date) : date;\n return d.toLocaleDateString(locale, {\n day: \"2-digit\",\n month: \"short\",\n year: \"numeric\",\n });\n}\n","export function formatBDT(amount: number): string {\n return amount.toLocaleString(\"en-US\");\n}\n","export function getYouTubeId(url: string | null | undefined): string | null {\n if (!url) return null;\n const regExp = /^.*(youtu.be\\/|v\\/|u\\/\\w\\/|embed\\/|watch\\?v=|[?&]v=)([^#&?]*).*/;\n const match = url.match(regExp);\n return match && match[2]?.length === 11 ? match[2] : null;\n}\n","export function sanitizeFileName(name: string): string {\n return name.replace(/[\\s,]+/g, \"_\");\n}\n","import type { Level, Language, Difficulty } from \"../types/enums\";\n\nexport function isQuestionLevel(value: unknown): value is Level {\n return [\n \"One\",\n \"Two\",\n \"Three\",\n \"Four\",\n \"Five\",\n \"Six\",\n \"Seven\",\n \"Eight\",\n \"SSC\",\n \"HSC\",\n \"Admission\",\n \"HSC+Admission\",\n \"BCS\",\n ].includes(value as string);\n}\n\nexport function isLanguage(value: unknown): value is Language {\n return [\"Bangla\", \"English\", \"Any\"].includes(value as string);\n}\n\nexport function isDifficulty(value: unknown): value is Difficulty {\n return [\"Easy\", \"Medium\", \"Hard\"].includes(value as string);\n}\n\nexport function normalizeLevel(value?: string | null): string {\n if (!value) return \"\";\n const clean = value.trim().replace(/\\s+/g, \"+\");\n const upper = clean.toUpperCase();\n if (upper === \"SSC\" || upper.includes(\"CLASS_9\") || upper.includes(\"CLASS_10\")) return \"SSC\";\n if (upper === \"HSC\" || upper === \"ADMISSION\" || upper === \"HSC+ADMISSION\" || upper.includes(\"CLASS_11\") || upper.includes(\"CLASS_12\")) return \"HSC+Admission\";\n if (upper === \"BCS\") return \"BCS\";\n return clean;\n}\n\n"]}