@studious-lms/server 1.23.0 → 1.24.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.
Files changed (298) hide show
  1. package/.env.example +0 -1
  2. package/data/curriculum/nepal-cdc-g4-g5/README.md +90 -0
  3. package/data/curriculum/nepal-cdc-g4-g5/manifest.json +97 -0
  4. package/data/curriculum/nepal-cdc-g4-g5/proficiency-scale.json +76 -0
  5. package/data/curriculum/nepal-cdc-g4-g5/subjects/english.json +6936 -0
  6. package/data/curriculum/nepal-cdc-g4-g5/subjects/health-physical-creative-arts.json +3401 -0
  7. package/data/curriculum/nepal-cdc-g4-g5/subjects/mathematics.json +1511 -0
  8. package/data/curriculum/nepal-cdc-g4-g5/subjects/nepali.json +7928 -0
  9. package/data/curriculum/nepal-cdc-g4-g5/subjects/science-technology.json +2420 -0
  10. package/data/curriculum/nepal-cdc-g4-g5/subjects/social-studies.json +2177 -0
  11. package/dist/lib/assignmentTypeDefaults.d.ts +41 -0
  12. package/dist/lib/assignmentTypeDefaults.d.ts.map +1 -0
  13. package/dist/lib/assignmentTypeDefaults.js +167 -0
  14. package/dist/lib/assignmentTypeDefaults.js.map +1 -0
  15. package/dist/lib/config/env.d.ts +0 -8
  16. package/dist/lib/config/env.d.ts.map +1 -1
  17. package/dist/lib/config/env.js +2 -5
  18. package/dist/lib/config/env.js.map +1 -1
  19. package/dist/lib/feePdf.d.ts +19 -0
  20. package/dist/lib/feePdf.d.ts.map +1 -0
  21. package/dist/lib/feePdf.js +167 -0
  22. package/dist/lib/feePdf.js.map +1 -0
  23. package/dist/lib/feePdfFormat.d.ts +4 -0
  24. package/dist/lib/feePdfFormat.d.ts.map +1 -0
  25. package/dist/lib/feePdfFormat.js +16 -0
  26. package/dist/lib/feePdfFormat.js.map +1 -0
  27. package/dist/lib/idCardPdf.d.ts +26 -0
  28. package/dist/lib/idCardPdf.d.ts.map +1 -0
  29. package/dist/lib/idCardPdf.js +272 -0
  30. package/dist/lib/idCardPdf.js.map +1 -0
  31. package/dist/lib/pdfLetterhead.d.ts +41 -0
  32. package/dist/lib/pdfLetterhead.d.ts.map +1 -0
  33. package/dist/lib/pdfLetterhead.js +86 -0
  34. package/dist/lib/pdfLetterhead.js.map +1 -0
  35. package/dist/lib/transcriptPdf.d.ts +55 -0
  36. package/dist/lib/transcriptPdf.d.ts.map +1 -0
  37. package/dist/lib/transcriptPdf.js +303 -0
  38. package/dist/lib/transcriptPdf.js.map +1 -0
  39. package/dist/models/academicCalendar.d.ts +198 -0
  40. package/dist/models/academicCalendar.d.ts.map +1 -0
  41. package/dist/models/academicCalendar.js +106 -0
  42. package/dist/models/academicCalendar.js.map +1 -0
  43. package/dist/models/agenda.d.ts +8 -0
  44. package/dist/models/agenda.d.ts.map +1 -1
  45. package/dist/models/announcement.d.ts +2 -0
  46. package/dist/models/announcement.d.ts.map +1 -1
  47. package/dist/models/assignment.d.ts +66 -0
  48. package/dist/models/assignment.d.ts.map +1 -1
  49. package/dist/models/attendance.d.ts +659 -21
  50. package/dist/models/attendance.d.ts.map +1 -1
  51. package/dist/models/attendance.js +544 -75
  52. package/dist/models/attendance.js.map +1 -1
  53. package/dist/models/auth.d.ts +2 -0
  54. package/dist/models/auth.d.ts.map +1 -1
  55. package/dist/models/class.d.ts +12 -0
  56. package/dist/models/class.d.ts.map +1 -1
  57. package/dist/models/class.js +3 -2
  58. package/dist/models/class.js.map +1 -1
  59. package/dist/models/event.d.ts +16 -0
  60. package/dist/models/event.d.ts.map +1 -1
  61. package/dist/models/fees.d.ts +554 -0
  62. package/dist/models/fees.d.ts.map +1 -0
  63. package/dist/models/fees.js +278 -0
  64. package/dist/models/fees.js.map +1 -0
  65. package/dist/models/file.d.ts +29 -0
  66. package/dist/models/file.d.ts.map +1 -1
  67. package/dist/models/folder.d.ts +2 -0
  68. package/dist/models/folder.d.ts.map +1 -1
  69. package/dist/models/labChat.d.ts +2 -0
  70. package/dist/models/labChat.d.ts.map +1 -1
  71. package/dist/models/marketplace.d.ts +178 -0
  72. package/dist/models/marketplace.d.ts.map +1 -0
  73. package/dist/models/marketplace.js +115 -0
  74. package/dist/models/marketplace.js.map +1 -0
  75. package/dist/models/moduleItem.d.ts +304 -0
  76. package/dist/models/moduleItem.d.ts.map +1 -0
  77. package/dist/models/moduleItem.js +78 -0
  78. package/dist/models/moduleItem.js.map +1 -0
  79. package/dist/models/newtonChat.d.ts +1 -0
  80. package/dist/models/newtonChat.d.ts.map +1 -1
  81. package/dist/models/school.d.ts +18 -0
  82. package/dist/models/school.d.ts.map +1 -1
  83. package/dist/models/school.js +7 -2
  84. package/dist/models/school.js.map +1 -1
  85. package/dist/models/section.d.ts +4 -0
  86. package/dist/models/section.d.ts.map +1 -1
  87. package/dist/models/worksheet.d.ts +19 -0
  88. package/dist/models/worksheet.d.ts.map +1 -1
  89. package/dist/models/worksheet.js +2 -2
  90. package/dist/models/worksheet.js.map +1 -1
  91. package/dist/routers/_app.d.ts +11341 -7097
  92. package/dist/routers/_app.d.ts.map +1 -1
  93. package/dist/routers/_app.js +12 -2
  94. package/dist/routers/_app.js.map +1 -1
  95. package/dist/routers/academicCalendar.d.ts +229 -0
  96. package/dist/routers/academicCalendar.d.ts.map +1 -0
  97. package/dist/routers/academicCalendar.js +75 -0
  98. package/dist/routers/academicCalendar.js.map +1 -0
  99. package/dist/routers/admin.d.ts +2 -0
  100. package/dist/routers/admin.d.ts.map +1 -1
  101. package/dist/routers/agenda.d.ts +8 -0
  102. package/dist/routers/agenda.d.ts.map +1 -1
  103. package/dist/routers/assignment.d.ts +75 -0
  104. package/dist/routers/assignment.d.ts.map +1 -1
  105. package/dist/routers/assignment.js +10 -2
  106. package/dist/routers/assignment.js.map +1 -1
  107. package/dist/routers/attendance.d.ts +103 -0
  108. package/dist/routers/attendance.d.ts.map +1 -1
  109. package/dist/routers/attendance.js +8 -5
  110. package/dist/routers/attendance.js.map +1 -1
  111. package/dist/routers/class.d.ts +27 -0
  112. package/dist/routers/class.d.ts.map +1 -1
  113. package/dist/routers/class.js +6 -2
  114. package/dist/routers/class.js.map +1 -1
  115. package/dist/routers/event.d.ts +8 -0
  116. package/dist/routers/event.d.ts.map +1 -1
  117. package/dist/routers/fees.d.ts +513 -0
  118. package/dist/routers/fees.d.ts.map +1 -0
  119. package/dist/routers/fees.js +88 -0
  120. package/dist/routers/fees.js.map +1 -0
  121. package/dist/routers/file.d.ts +2 -0
  122. package/dist/routers/file.d.ts.map +1 -1
  123. package/dist/routers/gradebook.d.ts +4 -1
  124. package/dist/routers/gradebook.d.ts.map +1 -1
  125. package/dist/routers/marketplace.d.ts +371 -0
  126. package/dist/routers/marketplace.d.ts.map +1 -0
  127. package/dist/routers/marketplace.js +87 -0
  128. package/dist/routers/marketplace.js.map +1 -0
  129. package/dist/routers/moduleItem.d.ts +263 -0
  130. package/dist/routers/moduleItem.d.ts.map +1 -0
  131. package/dist/routers/moduleItem.js +60 -0
  132. package/dist/routers/moduleItem.js.map +1 -0
  133. package/dist/routers/parent.d.ts +169 -0
  134. package/dist/routers/parent.d.ts.map +1 -0
  135. package/dist/routers/parent.js +49 -0
  136. package/dist/routers/parent.js.map +1 -0
  137. package/dist/routers/school.d.ts +363 -7
  138. package/dist/routers/school.d.ts.map +1 -1
  139. package/dist/routers/school.js +100 -3
  140. package/dist/routers/school.js.map +1 -1
  141. package/dist/routers/worksheet.d.ts +27 -0
  142. package/dist/routers/worksheet.d.ts.map +1 -1
  143. package/dist/routers/worksheet.js +10 -3
  144. package/dist/routers/worksheet.js.map +1 -1
  145. package/dist/services/academicCalendar.d.ts +173 -0
  146. package/dist/services/academicCalendar.d.ts.map +1 -0
  147. package/dist/services/academicCalendar.js +214 -0
  148. package/dist/services/academicCalendar.js.map +1 -0
  149. package/dist/services/adminInstitution.d.ts +2 -0
  150. package/dist/services/adminInstitution.d.ts.map +1 -1
  151. package/dist/services/agenda.d.ts +8 -0
  152. package/dist/services/agenda.d.ts.map +1 -1
  153. package/dist/services/assignment.d.ts +75 -0
  154. package/dist/services/assignment.d.ts.map +1 -1
  155. package/dist/services/assignment.js +91 -12
  156. package/dist/services/assignment.js.map +1 -1
  157. package/dist/services/attendance.d.ts +323 -10
  158. package/dist/services/attendance.d.ts.map +1 -1
  159. package/dist/services/attendance.js +385 -25
  160. package/dist/services/attendance.js.map +1 -1
  161. package/dist/services/attendanceMerge.d.ts +53 -0
  162. package/dist/services/attendanceMerge.d.ts.map +1 -0
  163. package/dist/services/attendanceMerge.js +87 -0
  164. package/dist/services/attendanceMerge.js.map +1 -0
  165. package/dist/services/attendanceRate.d.ts +24 -0
  166. package/dist/services/attendanceRate.d.ts.map +1 -0
  167. package/dist/services/attendanceRate.js +31 -0
  168. package/dist/services/attendanceRate.js.map +1 -0
  169. package/dist/services/attendanceStats.d.ts +22 -0
  170. package/dist/services/attendanceStats.d.ts.map +1 -0
  171. package/dist/services/attendanceStats.js +45 -0
  172. package/dist/services/attendanceStats.js.map +1 -0
  173. package/dist/services/class.d.ts +27 -0
  174. package/dist/services/class.d.ts.map +1 -1
  175. package/dist/services/class.js +26 -3
  176. package/dist/services/class.js.map +1 -1
  177. package/dist/services/event.d.ts +8 -0
  178. package/dist/services/event.d.ts.map +1 -1
  179. package/dist/services/fees.d.ts +422 -0
  180. package/dist/services/fees.d.ts.map +1 -0
  181. package/dist/services/fees.js +310 -0
  182. package/dist/services/fees.js.map +1 -0
  183. package/dist/services/feesAuthz.d.ts +49 -0
  184. package/dist/services/feesAuthz.d.ts.map +1 -0
  185. package/dist/services/feesAuthz.js +52 -0
  186. package/dist/services/feesAuthz.js.map +1 -0
  187. package/dist/services/file.d.ts +4 -0
  188. package/dist/services/file.d.ts.map +1 -1
  189. package/dist/services/gradebook.d.ts +18 -1
  190. package/dist/services/gradebook.d.ts.map +1 -1
  191. package/dist/services/gradebook.js +57 -2
  192. package/dist/services/gradebook.js.map +1 -1
  193. package/dist/services/idCard.d.ts +11 -0
  194. package/dist/services/idCard.d.ts.map +1 -0
  195. package/dist/services/idCard.js +122 -0
  196. package/dist/services/idCard.js.map +1 -0
  197. package/dist/services/marketplace.d.ts +301 -0
  198. package/dist/services/marketplace.d.ts.map +1 -0
  199. package/dist/services/marketplace.js +628 -0
  200. package/dist/services/marketplace.js.map +1 -0
  201. package/dist/services/moduleItem.d.ts +196 -0
  202. package/dist/services/moduleItem.d.ts.map +1 -0
  203. package/dist/services/moduleItem.js +188 -0
  204. package/dist/services/moduleItem.js.map +1 -0
  205. package/dist/services/notification.d.ts +58 -0
  206. package/dist/services/notification.d.ts.map +1 -1
  207. package/dist/services/notification.js +30 -2
  208. package/dist/services/notification.js.map +1 -1
  209. package/dist/services/parentPortal.d.ts +133 -0
  210. package/dist/services/parentPortal.d.ts.map +1 -0
  211. package/dist/services/parentPortal.js +328 -0
  212. package/dist/services/parentPortal.js.map +1 -0
  213. package/dist/services/school.d.ts +28 -3
  214. package/dist/services/school.d.ts.map +1 -1
  215. package/dist/services/school.js +65 -45
  216. package/dist/services/school.js.map +1 -1
  217. package/dist/services/studentImport.d.ts +34 -0
  218. package/dist/services/studentImport.d.ts.map +1 -0
  219. package/dist/services/studentImport.js +236 -0
  220. package/dist/services/studentImport.js.map +1 -0
  221. package/dist/services/transcript.d.ts +61 -0
  222. package/dist/services/transcript.d.ts.map +1 -0
  223. package/dist/services/transcript.js +243 -0
  224. package/dist/services/transcript.js.map +1 -0
  225. package/dist/services/worksheet.d.ts +22 -0
  226. package/dist/services/worksheet.d.ts.map +1 -1
  227. package/dist/services/worksheet.js +94 -2
  228. package/dist/services/worksheet.js.map +1 -1
  229. package/dist/trpc.d.ts +1 -1
  230. package/dist/trpc.d.ts.map +1 -1
  231. package/dist/trpc.js +5 -5
  232. package/dist/trpc.js.map +1 -1
  233. package/dist/utils/adminPanelAccess.d.ts +4 -5
  234. package/dist/utils/adminPanelAccess.d.ts.map +1 -1
  235. package/dist/utils/adminPanelAccess.js +12 -34
  236. package/dist/utils/adminPanelAccess.js.map +1 -1
  237. package/package.json +2 -1
  238. package/prisma/migrations/20260804120000_add_attendance_records_and_excusals/migration.sql +114 -0
  239. package/prisma/migrations/20260804120100_add_fee_plans_invoices_payments/migration.sql +101 -0
  240. package/prisma/migrations/20260804120200_add_marketplace_listings/migration.sql +54 -0
  241. package/prisma/migrations/20260804120300_content_platform_b3_b9/migration.sql +63 -0
  242. package/prisma/migrations/20260804120400_c_academic_ops/migration.sql +77 -0
  243. package/prisma/schema.prisma +371 -11
  244. package/scripts/import-nepal-cdc-curriculum.ts +379 -0
  245. package/src/lib/assignmentTypeDefaults.ts +222 -0
  246. package/src/lib/config/env.ts +0 -3
  247. package/src/lib/feePdf.ts +199 -0
  248. package/src/lib/feePdfFormat.ts +14 -0
  249. package/src/lib/idCardPdf.ts +336 -0
  250. package/src/lib/pdfLetterhead.ts +124 -0
  251. package/src/lib/transcriptPdf.ts +502 -0
  252. package/src/models/academicCalendar.ts +139 -0
  253. package/src/models/attendance.ts +704 -73
  254. package/src/models/class.ts +1 -0
  255. package/src/models/fees.ts +349 -0
  256. package/src/models/marketplace.ts +158 -0
  257. package/src/models/moduleItem.ts +107 -0
  258. package/src/models/school.ts +6 -0
  259. package/src/models/worksheet.ts +2 -0
  260. package/src/routers/_app.ts +10 -0
  261. package/src/routers/academicCalendar.ts +103 -0
  262. package/src/routers/assignment.ts +8 -0
  263. package/src/routers/attendance.ts +7 -3
  264. package/src/routers/class.ts +4 -0
  265. package/src/routers/fees.ts +126 -0
  266. package/src/routers/marketplace.ts +134 -0
  267. package/src/routers/moduleItem.ts +89 -0
  268. package/src/routers/parent.ts +68 -0
  269. package/src/routers/school.ts +139 -1
  270. package/src/routers/worksheet.ts +11 -0
  271. package/src/services/academicCalendar.ts +299 -0
  272. package/src/services/assignment.ts +122 -12
  273. package/src/services/attendance.ts +548 -26
  274. package/src/services/attendanceMerge.ts +139 -0
  275. package/src/services/attendanceRate.ts +41 -0
  276. package/src/services/attendanceStats.ts +63 -0
  277. package/src/services/class.ts +25 -1
  278. package/src/services/fees.ts +424 -0
  279. package/src/services/feesAuthz.ts +77 -0
  280. package/src/services/gradebook.ts +70 -0
  281. package/src/services/idCard.ts +147 -0
  282. package/src/services/marketplace.ts +785 -0
  283. package/src/services/moduleItem.ts +274 -0
  284. package/src/services/notification.ts +43 -0
  285. package/src/services/parentPortal.ts +380 -0
  286. package/src/services/school.ts +65 -44
  287. package/src/services/studentImport.ts +273 -0
  288. package/src/services/transcript.ts +313 -0
  289. package/src/services/worksheet.ts +111 -0
  290. package/src/trpc.ts +3 -3
  291. package/src/utils/adminPanelAccess.ts +12 -35
  292. package/tests/unit/assignmentTypeDefaults.test.ts +52 -0
  293. package/tests/unit/lib/feePdf.test.ts +15 -0
  294. package/tests/unit/lib/idCardPdf.test.ts +40 -0
  295. package/tests/unit/lib/transcriptPdf.test.ts +111 -0
  296. package/tests/unit/services/attendanceMerge.test.ts +90 -0
  297. package/tests/unit/services/attendanceRate.test.ts +38 -0
  298. package/tests/unit/services/attendanceStats.test.ts +54 -0
@@ -1 +1 @@
1
- {"version":3,"file":"gradebook.js","sources":["services/gradebook.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Gradebook service – grade categories, custom columns/entries, class\n * grade-calculation policies, the computed gradebook, and CSV export.\n *\n * All grade math lives in `gradeCalculation.ts` (pure, unit-tested); this\n * service only assembles inputs and persists configuration.\n */\nimport { GradeRoundingMode as PrismaGradeRoundingMode } from \"@prisma/client\";\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { isTeacherInClass } from \"../models/class.js\";\nimport {\n findGradeCategoriesByClassId,\n findGradeCategory,\n findCustomGradeColumnsByClassId,\n findCustomGradeColumn,\n findClassForGradebook,\n findClassGradePolicies,\n} from \"../models/gradebook.js\";\nimport {\n computeFinalGrade,\n resolveLetterGrade,\n type FinalGradeResult,\n type GradeCategoryInput,\n type ScoreInput,\n} from \"./gradeCalculation.js\";\n\n// ===== Categories =====\n\nexport async function listGradeCategories(classId: string) {\n return findGradeCategoriesByClassId(classId);\n}\n\nexport async function createGradeCategory(\n classId: string,\n data: { name: string; weight: number; dropLowest?: number; position?: number }\n) {\n return prisma.gradeCategory.create({\n data: {\n classId,\n name: data.name,\n weight: data.weight,\n dropLowest: data.dropLowest ?? 0,\n position: data.position ?? (await findGradeCategoriesByClassId(classId)).length,\n },\n });\n}\n\nexport async function updateGradeCategory(\n categoryId: string,\n classId: string,\n data: {\n name?: string;\n weight?: number;\n dropLowest?: number;\n position?: number;\n }\n) {\n const category = await findGradeCategory(categoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n return prisma.gradeCategory.update({\n where: { id: categoryId },\n data,\n });\n}\n\nexport async function deleteGradeCategory(categoryId: string, classId: string) {\n const category = await findGradeCategory(categoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n return prisma.gradeCategory.delete({ where: { id: categoryId } });\n}\n\nexport async function assignAssignmentCategory(\n classId: string,\n assignmentId: string,\n gradeCategoryId: string | null\n) {\n if (gradeCategoryId) {\n const category = await findGradeCategory(gradeCategoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n }\n return prisma.assignment.update({\n where: { id: assignmentId, classId },\n data: { gradeCategoryId },\n });\n}\n\n// ===== Custom columns =====\n\nexport async function listCustomGradeColumns(classId: string) {\n return findCustomGradeColumnsByClassId(classId);\n}\n\nexport async function createCustomGradeColumn(\n classId: string,\n data: {\n name: string;\n maxPoints: number;\n gradeCategoryId?: string | null;\n position?: number;\n }\n) {\n if (data.gradeCategoryId) {\n const category = await findGradeCategory(data.gradeCategoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n }\n return prisma.customGradeColumn.create({\n data: {\n classId,\n name: data.name,\n maxPoints: data.maxPoints,\n gradeCategoryId: data.gradeCategoryId ?? null,\n position:\n data.position ?? (await findCustomGradeColumnsByClassId(classId)).length,\n },\n });\n}\n\nexport async function updateCustomGradeColumn(\n columnId: string,\n classId: string,\n data: {\n name?: string;\n maxPoints?: number;\n gradeCategoryId?: string | null;\n position?: number;\n }\n) {\n const column = await findCustomGradeColumn(columnId, classId);\n if (!column) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Column not found\" });\n }\n if (data.gradeCategoryId) {\n const category = await findGradeCategory(data.gradeCategoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n }\n return prisma.customGradeColumn.update({\n where: { id: columnId },\n data,\n });\n}\n\nexport async function deleteCustomGradeColumn(\n columnId: string,\n classId: string\n) {\n const column = await findCustomGradeColumn(columnId, classId);\n if (!column) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Column not found\" });\n }\n return prisma.customGradeColumn.delete({ where: { id: columnId } });\n}\n\nexport async function setCustomGradeEntry(\n classId: string,\n columnId: string,\n studentId: string,\n points: number | null,\n note?: string | null\n) {\n const column = await findCustomGradeColumn(columnId, classId);\n if (!column) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Column not found\" });\n }\n if (points === null) {\n await prisma.customGradeEntry.deleteMany({\n where: { columnId, studentId },\n });\n return null;\n }\n return prisma.customGradeEntry.upsert({\n where: { columnId_studentId: { columnId, studentId } },\n create: { columnId, studentId, points, note: note ?? null },\n update: { points, ...(note !== undefined ? { note } : {}) },\n });\n}\n\n// ===== Policies =====\n\nexport async function getGradePolicies(classId: string) {\n const policies = await findClassGradePolicies(classId);\n if (!policies) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Class not found\" });\n }\n return policies;\n}\n\nexport async function updateGradePolicies(\n classId: string,\n data: {\n treatMissingAsZero?: boolean;\n gradeRoundingMode?: PrismaGradeRoundingMode;\n }\n) {\n return prisma.class.update({\n where: { id: classId },\n data,\n select: {\n id: true,\n treatMissingAsZero: true,\n gradeRoundingMode: true,\n },\n });\n}\n\n// ===== Computed gradebook =====\n\nexport interface GradebookStudentRow {\n studentId: string;\n studentName: string;\n assignmentScores: Record<string, number | null>;\n customScores: Record<string, { points: number | null; note: string | null }>;\n result: FinalGradeResult;\n letterGrade: string | null;\n}\n\nexport async function getComputedGradebook(classId: string, userId: string) {\n const [classData, categories, columns, isTeacher] = await Promise.all([\n findClassForGradebook(classId),\n findGradeCategoriesByClassId(classId),\n findCustomGradeColumnsByClassId(classId),\n isTeacherInClass(classId, userId),\n ]);\n if (!classData) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Class not found\" });\n }\n\n // Students only ever see their own row.\n const students = isTeacher\n ? classData.students\n : classData.students.filter((student) => student.id === userId);\n\n const policies = {\n treatMissingAsZero: classData.treatMissingAsZero,\n roundingMode: classData.gradeRoundingMode,\n };\n const categoryInputs: GradeCategoryInput[] = categories.map((category) => ({\n id: category.id,\n name: category.name,\n weight: category.weight,\n dropLowest: category.dropLowest,\n }));\n const boundary = classData.gradingBoundaries[0] ?? null;\n\n const rows: GradebookStudentRow[] = students.map((student) => {\n const assignmentScores: Record<string, number | null> = {};\n const scores: ScoreInput[] = [];\n\n for (const assignment of classData.assignments) {\n const submission = assignment.submissions.find(\n (item) => item.studentId === student.id\n );\n const pointsEarned = submission?.gradeReceived ?? null;\n assignmentScores[assignment.id] = pointsEarned;\n scores.push({\n categoryId: assignment.gradeCategoryId,\n pointsEarned,\n maxPoints: assignment.maxGrade ?? 0,\n weight: assignment.weight,\n });\n }\n\n const customScores: GradebookStudentRow[\"customScores\"] = {};\n for (const column of columns) {\n const entry = column.entries.find(\n (item) => item.studentId === student.id\n );\n customScores[column.id] = {\n points: entry?.points ?? null,\n note: entry?.note ?? null,\n };\n scores.push({\n categoryId: column.gradeCategoryId,\n pointsEarned: entry?.points ?? null,\n maxPoints: column.maxPoints,\n });\n }\n\n const result = computeFinalGrade(policies, categoryInputs, scores);\n const letterGrade =\n boundary && result.percentage !== null\n ? resolveLetterGrade(boundary.structured, result.percentage)\n : null;\n\n return {\n studentId: student.id,\n studentName: student.profile?.displayName || student.username,\n assignmentScores,\n customScores,\n result,\n letterGrade,\n };\n });\n\n return {\n policies,\n categories,\n assignments: classData.assignments.map((assignment) => ({\n id: assignment.id,\n title: assignment.title,\n maxGrade: assignment.maxGrade,\n weight: assignment.weight,\n gradeCategoryId: assignment.gradeCategoryId,\n dueDate: assignment.dueDate,\n })),\n customColumns: columns.map((column) => ({\n id: column.id,\n name: column.name,\n maxPoints: column.maxPoints,\n gradeCategoryId: column.gradeCategoryId,\n position: column.position,\n })),\n gradingBoundary: boundary,\n students: rows,\n };\n}\n\n// ===== CSV export =====\n\nfunction escapeCsvCell(value: string): string {\n if (/[\",\\n\\r]/.test(value)) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\nexport async function exportGradebookCsv(classId: string, userId: string) {\n const gradebook = await getComputedGradebook(classId, userId);\n\n const header = [\n \"Student\",\n ...gradebook.assignments.map(\n (assignment) => `${assignment.title} (/${assignment.maxGrade ?? 0})`\n ),\n ...gradebook.customColumns.map(\n (column) => `${column.name} (/${column.maxPoints})`\n ),\n ...gradebook.categories.map((category) => `${category.name} Avg (%)`),\n \"Final (%)\",\n \"Letter\",\n ];\n\n const lines = [header.map(escapeCsvCell).join(\",\")];\n\n for (const row of gradebook.students) {\n const categoryCells = gradebook.categories.map((category) => {\n const average = row.result.categories.find(\n (item) => item.categoryId === category.id\n )?.average;\n return average === null || average === undefined\n ? \"\"\n : average.toFixed(2);\n });\n\n const cells = [\n row.studentName,\n ...gradebook.assignments.map((assignment) => {\n const points = row.assignmentScores[assignment.id];\n return points === null || points === undefined ? \"\" : String(points);\n }),\n ...gradebook.customColumns.map((column) => {\n const points = row.customScores[column.id]?.points;\n return points === null || points === undefined ? \"\" : String(points);\n }),\n ...categoryCells,\n row.result.percentage === null ? \"\" : row.result.percentage.toFixed(2),\n row.letterGrade ?? \"\",\n ];\n lines.push(cells.map(escapeCsvCell).join(\",\"));\n }\n\n return { csv: lines.join(\"\\n\") };\n}\n"],"names":[],"mappings":"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","debug_id":"2860d786-c6b1-5d9a-b1bd-880c5bef0f51"}
1
+ {"version":3,"file":"gradebook.js","sources":["services/gradebook.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Gradebook service – grade categories, custom columns/entries, class\n * grade-calculation policies, the computed gradebook, and CSV export.\n *\n * All grade math lives in `gradeCalculation.ts` (pure, unit-tested); this\n * service only assembles inputs and persists configuration.\n */\nimport { GradeRoundingMode as PrismaGradeRoundingMode } from \"@prisma/client\";\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport { isTeacherInClass } from \"../models/class.js\";\nimport {\n findGradeCategoriesByClassId,\n findGradeCategory,\n findCustomGradeColumnsByClassId,\n findCustomGradeColumn,\n findClassForGradebook,\n findClassGradePolicies,\n} from \"../models/gradebook.js\";\nimport {\n computeFinalGrade,\n resolveLetterGrade,\n type FinalGradeResult,\n type GradeCategoryInput,\n type ScoreInput,\n} from \"./gradeCalculation.js\";\n\n// ===== Categories =====\n\nexport async function listGradeCategories(classId: string) {\n return findGradeCategoriesByClassId(classId);\n}\n\nexport async function createGradeCategory(\n classId: string,\n data: { name: string; weight: number; dropLowest?: number; position?: number }\n) {\n return prisma.gradeCategory.create({\n data: {\n classId,\n name: data.name,\n weight: data.weight,\n dropLowest: data.dropLowest ?? 0,\n position: data.position ?? (await findGradeCategoriesByClassId(classId)).length,\n },\n });\n}\n\nexport async function updateGradeCategory(\n categoryId: string,\n classId: string,\n data: {\n name?: string;\n weight?: number;\n dropLowest?: number;\n position?: number;\n }\n) {\n const category = await findGradeCategory(categoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n return prisma.gradeCategory.update({\n where: { id: categoryId },\n data,\n });\n}\n\nexport async function deleteGradeCategory(categoryId: string, classId: string) {\n const category = await findGradeCategory(categoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n return prisma.gradeCategory.delete({ where: { id: categoryId } });\n}\n\nexport async function assignAssignmentCategory(\n classId: string,\n assignmentId: string,\n gradeCategoryId: string | null\n) {\n if (gradeCategoryId) {\n const category = await findGradeCategory(gradeCategoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n }\n return prisma.assignment.update({\n where: { id: assignmentId, classId },\n data: { gradeCategoryId },\n });\n}\n\n// ===== Custom columns =====\n\nexport async function listCustomGradeColumns(classId: string) {\n return findCustomGradeColumnsByClassId(classId);\n}\n\nexport async function createCustomGradeColumn(\n classId: string,\n data: {\n name: string;\n maxPoints: number;\n gradeCategoryId?: string | null;\n position?: number;\n }\n) {\n if (data.gradeCategoryId) {\n const category = await findGradeCategory(data.gradeCategoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n }\n return prisma.customGradeColumn.create({\n data: {\n classId,\n name: data.name,\n maxPoints: data.maxPoints,\n gradeCategoryId: data.gradeCategoryId ?? null,\n position:\n data.position ?? (await findCustomGradeColumnsByClassId(classId)).length,\n },\n });\n}\n\nexport async function updateCustomGradeColumn(\n columnId: string,\n classId: string,\n data: {\n name?: string;\n maxPoints?: number;\n gradeCategoryId?: string | null;\n position?: number;\n }\n) {\n const column = await findCustomGradeColumn(columnId, classId);\n if (!column) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Column not found\" });\n }\n if (data.gradeCategoryId) {\n const category = await findGradeCategory(data.gradeCategoryId, classId);\n if (!category) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Category not found\" });\n }\n }\n return prisma.customGradeColumn.update({\n where: { id: columnId },\n data,\n });\n}\n\nexport async function deleteCustomGradeColumn(\n columnId: string,\n classId: string\n) {\n const column = await findCustomGradeColumn(columnId, classId);\n if (!column) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Column not found\" });\n }\n return prisma.customGradeColumn.delete({ where: { id: columnId } });\n}\n\nexport async function setCustomGradeEntry(\n classId: string,\n columnId: string,\n studentId: string,\n points: number | null,\n note?: string | null\n) {\n const column = await findCustomGradeColumn(columnId, classId);\n if (!column) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Column not found\" });\n }\n if (points === null) {\n await prisma.customGradeEntry.deleteMany({\n where: { columnId, studentId },\n });\n return null;\n }\n return prisma.customGradeEntry.upsert({\n where: { columnId_studentId: { columnId, studentId } },\n create: { columnId, studentId, points, note: note ?? null },\n update: { points, ...(note !== undefined ? { note } : {}) },\n });\n}\n\n// ===== Policies =====\n\nexport async function getGradePolicies(classId: string) {\n const policies = await findClassGradePolicies(classId);\n if (!policies) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Class not found\" });\n }\n return policies;\n}\n\nexport async function updateGradePolicies(\n classId: string,\n data: {\n treatMissingAsZero?: boolean;\n gradeRoundingMode?: PrismaGradeRoundingMode;\n }\n) {\n return prisma.class.update({\n where: { id: classId },\n data,\n select: {\n id: true,\n treatMissingAsZero: true,\n gradeRoundingMode: true,\n },\n });\n}\n\n// ===== Computed gradebook =====\n\nexport interface GradebookStudentRow {\n studentId: string;\n studentName: string;\n assignmentScores: Record<string, number | null>;\n customScores: Record<string, { points: number | null; note: string | null }>;\n result: FinalGradeResult;\n letterGrade: string | null;\n}\n\nexport async function getComputedGradebook(classId: string, userId: string) {\n const [classData, categories, columns, isTeacher] = await Promise.all([\n findClassForGradebook(classId),\n findGradeCategoriesByClassId(classId),\n findCustomGradeColumnsByClassId(classId),\n isTeacherInClass(classId, userId),\n ]);\n if (!classData) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Class not found\" });\n }\n\n // Students only ever see their own row.\n const students = isTeacher\n ? classData.students\n : classData.students.filter((student) => student.id === userId);\n\n const policies = {\n treatMissingAsZero: classData.treatMissingAsZero,\n roundingMode: classData.gradeRoundingMode,\n };\n const categoryInputs: GradeCategoryInput[] = categories.map((category) => ({\n id: category.id,\n name: category.name,\n weight: category.weight,\n dropLowest: category.dropLowest,\n }));\n const boundary = classData.gradingBoundaries[0] ?? null;\n\n const rows: GradebookStudentRow[] = students.map((student) => {\n const assignmentScores: Record<string, number | null> = {};\n const scores: ScoreInput[] = [];\n\n for (const assignment of classData.assignments) {\n const submission = assignment.submissions.find(\n (item) => item.studentId === student.id\n );\n const pointsEarned = submission?.gradeReceived ?? null;\n assignmentScores[assignment.id] = pointsEarned;\n scores.push({\n categoryId: assignment.gradeCategoryId,\n pointsEarned,\n maxPoints: assignment.maxGrade ?? 0,\n weight: assignment.weight,\n });\n }\n\n const customScores: GradebookStudentRow[\"customScores\"] = {};\n for (const column of columns) {\n const entry = column.entries.find(\n (item) => item.studentId === student.id\n );\n customScores[column.id] = {\n points: entry?.points ?? null,\n note: entry?.note ?? null,\n };\n scores.push({\n categoryId: column.gradeCategoryId,\n pointsEarned: entry?.points ?? null,\n maxPoints: column.maxPoints,\n });\n }\n\n const result = computeFinalGrade(policies, categoryInputs, scores);\n const letterGrade =\n boundary && result.percentage !== null\n ? resolveLetterGrade(boundary.structured, result.percentage)\n : null;\n\n return {\n studentId: student.id,\n studentName: student.profile?.displayName || student.username,\n assignmentScores,\n customScores,\n result,\n letterGrade,\n };\n });\n\n return {\n policies,\n categories,\n assignments: classData.assignments.map((assignment) => ({\n id: assignment.id,\n title: assignment.title,\n maxGrade: assignment.maxGrade,\n weight: assignment.weight,\n gradeCategoryId: assignment.gradeCategoryId,\n dueDate: assignment.dueDate,\n })),\n customColumns: columns.map((column) => ({\n id: column.id,\n name: column.name,\n maxPoints: column.maxPoints,\n gradeCategoryId: column.gradeCategoryId,\n position: column.position,\n })),\n gradingBoundary: boundary,\n students: rows,\n };\n}\n\n/**\n * Compute one student's final grade in a class without teacher-auth filtering.\n * Used by school-admin transcripts (caller must enforce school admin separately).\n */\nexport async function computeStudentGradeInClass(\n classId: string,\n studentId: string\n): Promise<{\n result: FinalGradeResult;\n letterGrade: string | null;\n gradingBoundary: { id: string; name: string | null; structured: string } | null;\n studentName: string | null;\n} | null> {\n const [classData, categories, columns] = await Promise.all([\n findClassForGradebook(classId),\n findGradeCategoriesByClassId(classId),\n findCustomGradeColumnsByClassId(classId),\n ]);\n if (!classData) return null;\n\n const student = classData.students.find((s) => s.id === studentId);\n if (!student) return null;\n\n const policies = {\n treatMissingAsZero: classData.treatMissingAsZero,\n roundingMode: classData.gradeRoundingMode,\n };\n const categoryInputs: GradeCategoryInput[] = categories.map((category) => ({\n id: category.id,\n name: category.name,\n weight: category.weight,\n dropLowest: category.dropLowest,\n }));\n const boundary = classData.gradingBoundaries[0] ?? null;\n\n const scores: ScoreInput[] = [];\n for (const assignment of classData.assignments) {\n const submission = assignment.submissions.find(\n (item) => item.studentId === studentId\n );\n scores.push({\n categoryId: assignment.gradeCategoryId,\n pointsEarned: submission?.gradeReceived ?? null,\n maxPoints: assignment.maxGrade ?? 0,\n weight: assignment.weight,\n });\n }\n for (const column of columns) {\n const entry = column.entries.find((item) => item.studentId === studentId);\n scores.push({\n categoryId: column.gradeCategoryId,\n pointsEarned: entry?.points ?? null,\n maxPoints: column.maxPoints,\n });\n }\n\n const result = computeFinalGrade(policies, categoryInputs, scores);\n const letterGrade =\n boundary && result.percentage !== null\n ? resolveLetterGrade(boundary.structured, result.percentage)\n : null;\n\n return {\n result,\n letterGrade,\n gradingBoundary: boundary,\n studentName: student.profile?.displayName || student.username,\n };\n}\n\n// ===== CSV export =====\n\nfunction escapeCsvCell(value: string): string {\n if (/[\",\\n\\r]/.test(value)) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\nexport async function exportGradebookCsv(classId: string, userId: string) {\n const gradebook = await getComputedGradebook(classId, userId);\n\n const header = [\n \"Student\",\n ...gradebook.assignments.map(\n (assignment) => `${assignment.title} (/${assignment.maxGrade ?? 0})`\n ),\n ...gradebook.customColumns.map(\n (column) => `${column.name} (/${column.maxPoints})`\n ),\n ...gradebook.categories.map((category) => `${category.name} Avg (%)`),\n \"Final (%)\",\n \"Letter\",\n ];\n\n const lines = [header.map(escapeCsvCell).join(\",\")];\n\n for (const row of gradebook.students) {\n const categoryCells = gradebook.categories.map((category) => {\n const average = row.result.categories.find(\n (item) => item.categoryId === category.id\n )?.average;\n return average === null || average === undefined\n ? \"\"\n : average.toFixed(2);\n });\n\n const cells = [\n row.studentName,\n ...gradebook.assignments.map((assignment) => {\n const points = row.assignmentScores[assignment.id];\n return points === null || points === undefined ? \"\" : String(points);\n }),\n ...gradebook.customColumns.map((column) => {\n const points = row.customScores[column.id]?.points;\n return points === null || points === undefined ? \"\" : String(points);\n }),\n ...categoryCells,\n row.result.percentage === null ? \"\" : row.result.percentage.toFixed(2),\n row.letterGrade ?? \"\",\n ];\n lines.push(cells.map(escapeCsvCell).join(\",\"));\n }\n\n return { csv: lines.join(\"\\n\") };\n}\n"],"names":[],"mappings":"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","debug_id":"8cc94c17-86dc-5a58-864f-1590c5dbf050"}
@@ -0,0 +1,11 @@
1
+ export declare function generateStudentIdCards(userId: string, input: {
2
+ schoolId: string;
3
+ studentUserIds: string[];
4
+ }): Promise<{
5
+ pdfBase64: string;
6
+ fileName: string;
7
+ mimeType: "application/pdf";
8
+ studentCount: number;
9
+ skippedCount: number;
10
+ }>;
11
+ //# sourceMappingURL=idCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idCard.d.ts","sourceRoot":"/","sources":["services/idCard.ts"],"names":[],"mappings":"AAqCA,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,EAAE,CAAA;CAAE;;;;;;GA2GtD"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * School admin ID card generation — bulk PDF for selected student members.
3
+ */
4
+
5
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="0db3caa4-3ada-59ef-9735-ff139c1e275c")}catch(e){}}();
6
+ import { TRPCError } from "@trpc/server";
7
+ import { prisma } from "../lib/prisma.js";
8
+ import { buildIdCardsPdf, DEFAULT_BRAND_COLOR, } from "../lib/idCardPdf.js";
9
+ import { findActiveSchoolAdminMembership } from "../models/school.js";
10
+ const MAX_STUDENTS = 100;
11
+ async function requireSchoolAdmin(schoolId, userId) {
12
+ const membership = await findActiveSchoolAdminMembership(schoolId, userId);
13
+ if (!membership) {
14
+ throw new TRPCError({
15
+ code: "FORBIDDEN",
16
+ message: "Only school admins can generate ID cards",
17
+ });
18
+ }
19
+ return membership;
20
+ }
21
+ function formatClassLabel(cls) {
22
+ const parts = [cls.name, cls.section].filter((p) => p && p.trim().length > 0);
23
+ if (parts.length === 0)
24
+ return cls.subject || "—";
25
+ return parts.join(" · ");
26
+ }
27
+ export async function generateStudentIdCards(userId, input) {
28
+ await requireSchoolAdmin(input.schoolId, userId);
29
+ const uniqueIds = [...new Set(input.studentUserIds)].slice(0, MAX_STUDENTS);
30
+ if (uniqueIds.length === 0) {
31
+ throw new TRPCError({
32
+ code: "BAD_REQUEST",
33
+ message: "Select at least one student",
34
+ });
35
+ }
36
+ const school = await prisma.school.findUnique({
37
+ where: { id: input.schoolId },
38
+ select: {
39
+ id: true,
40
+ name: true,
41
+ logo: { select: { path: true } },
42
+ },
43
+ });
44
+ if (!school) {
45
+ throw new TRPCError({ code: "NOT_FOUND", message: "School not found" });
46
+ }
47
+ const memberships = await prisma.schoolMembership.findMany({
48
+ where: {
49
+ schoolId: input.schoolId,
50
+ userId: { in: uniqueIds },
51
+ role: "STUDENT",
52
+ status: "ACTIVE",
53
+ },
54
+ select: {
55
+ user: {
56
+ select: {
57
+ id: true,
58
+ username: true,
59
+ profile: {
60
+ select: {
61
+ displayName: true,
62
+ profilePicture: true,
63
+ },
64
+ },
65
+ studentIn: {
66
+ where: { schoolId: input.schoolId },
67
+ select: {
68
+ name: true,
69
+ section: true,
70
+ subject: true,
71
+ color: true,
72
+ department: { select: { color: true } },
73
+ },
74
+ orderBy: { name: "asc" },
75
+ take: 1,
76
+ },
77
+ },
78
+ },
79
+ },
80
+ });
81
+ if (memberships.length === 0) {
82
+ throw new TRPCError({
83
+ code: "NOT_FOUND",
84
+ message: "No active student members found for the given IDs",
85
+ });
86
+ }
87
+ const byId = new Map(memberships.map((m) => [m.user.id, m.user]));
88
+ const students = [];
89
+ for (const id of uniqueIds) {
90
+ const user = byId.get(id);
91
+ if (!user)
92
+ continue;
93
+ const primaryClass = user.studentIn[0] ?? null;
94
+ const brandColor = primaryClass?.color ??
95
+ primaryClass?.department?.color ??
96
+ DEFAULT_BRAND_COLOR;
97
+ students.push({
98
+ displayName: user.profile?.displayName?.trim() || user.username,
99
+ username: user.username,
100
+ classLabel: primaryClass ? formatClassLabel(primaryClass) : null,
101
+ brandColor,
102
+ profilePictureUrl: user.profile?.profilePicture ?? null,
103
+ });
104
+ }
105
+ const pdfBytes = await buildIdCardsPdf({
106
+ name: school.name,
107
+ logoPath: school.logo?.path ?? null,
108
+ }, students);
109
+ const dateStamp = new Date().toISOString().slice(0, 10);
110
+ const fileName = students.length === 1
111
+ ? `id-card-${students[0].username}-${dateStamp}.pdf`
112
+ : `id-cards-${school.name.replace(/[^\w\-]+/g, "_").slice(0, 40)}-${dateStamp}.pdf`;
113
+ return {
114
+ pdfBase64: Buffer.from(pdfBytes).toString("base64"),
115
+ fileName,
116
+ mimeType: "application/pdf",
117
+ studentCount: students.length,
118
+ skippedCount: uniqueIds.length - students.length,
119
+ };
120
+ }
121
+ //# sourceMappingURL=idCard.js.map
122
+ //# debugId=0db3caa4-3ada-59ef-9735-ff139c1e275c
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idCard.js","sources":["services/idCard.ts"],"sourceRoot":"/","sourcesContent":["/**\n * School admin ID card generation — bulk PDF for selected student members.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n buildIdCardsPdf,\n DEFAULT_BRAND_COLOR,\n type IdCardStudent,\n} from \"../lib/idCardPdf.js\";\nimport { findActiveSchoolAdminMembership } from \"../models/school.js\";\n\nconst MAX_STUDENTS = 100;\n\nasync function requireSchoolAdmin(schoolId: string, userId: string) {\n const membership = await findActiveSchoolAdminMembership(schoolId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only school admins can generate ID cards\",\n });\n }\n return membership;\n}\n\nfunction formatClassLabel(cls: {\n name: string;\n section: string;\n subject: string;\n}): string {\n const parts = [cls.name, cls.section].filter(\n (p) => p && p.trim().length > 0\n );\n if (parts.length === 0) return cls.subject || \"—\";\n return parts.join(\" · \");\n}\n\nexport async function generateStudentIdCards(\n userId: string,\n input: { schoolId: string; studentUserIds: string[] }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const uniqueIds = [...new Set(input.studentUserIds)].slice(0, MAX_STUDENTS);\n if (uniqueIds.length === 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Select at least one student\",\n });\n }\n\n const school = await prisma.school.findUnique({\n where: { id: input.schoolId },\n select: {\n id: true,\n name: true,\n logo: { select: { path: true } },\n },\n });\n if (!school) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"School not found\" });\n }\n\n const memberships = await prisma.schoolMembership.findMany({\n where: {\n schoolId: input.schoolId,\n userId: { in: uniqueIds },\n role: \"STUDENT\",\n status: \"ACTIVE\",\n },\n select: {\n user: {\n select: {\n id: true,\n username: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n },\n },\n studentIn: {\n where: { schoolId: input.schoolId },\n select: {\n name: true,\n section: true,\n subject: true,\n color: true,\n department: { select: { color: true } },\n },\n orderBy: { name: \"asc\" },\n take: 1,\n },\n },\n },\n },\n });\n\n if (memberships.length === 0) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"No active student members found for the given IDs\",\n });\n }\n\n const byId = new Map(memberships.map((m) => [m.user.id, m.user]));\n const students: IdCardStudent[] = [];\n for (const id of uniqueIds) {\n const user = byId.get(id);\n if (!user) continue;\n const primaryClass = user.studentIn[0] ?? null;\n const brandColor =\n primaryClass?.color ??\n primaryClass?.department?.color ??\n DEFAULT_BRAND_COLOR;\n\n students.push({\n displayName: user.profile?.displayName?.trim() || user.username,\n username: user.username,\n classLabel: primaryClass ? formatClassLabel(primaryClass) : null,\n brandColor,\n profilePictureUrl: user.profile?.profilePicture ?? null,\n });\n }\n\n const pdfBytes = await buildIdCardsPdf(\n {\n name: school.name,\n logoPath: school.logo?.path ?? null,\n },\n students\n );\n\n const dateStamp = new Date().toISOString().slice(0, 10);\n const fileName =\n students.length === 1\n ? `id-card-${students[0].username}-${dateStamp}.pdf`\n : `id-cards-${school.name.replace(/[^\\w\\-]+/g, \"_\").slice(0, 40)}-${dateStamp}.pdf`;\n\n return {\n pdfBase64: Buffer.from(pdfBytes).toString(\"base64\"),\n fileName,\n mimeType: \"application/pdf\" as const,\n studentCount: students.length,\n skippedCount: uniqueIds.length - students.length,\n };\n}\n"],"names":[],"mappings":"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","debug_id":"0db3caa4-3ada-59ef-9735-ff139c1e275c"}
@@ -0,0 +1,301 @@
1
+ import type { MarketplaceListingType, MarketplaceVisibility, Prisma } from "@prisma/client";
2
+ export declare function listMarketplace(userId: string, input: {
3
+ type?: MarketplaceListingType;
4
+ search?: string;
5
+ tags?: string[];
6
+ schoolId?: string;
7
+ cursor?: string;
8
+ limit?: number;
9
+ }): Promise<{
10
+ items: ({
11
+ school: {
12
+ id: string;
13
+ name: string;
14
+ } | null;
15
+ author: {
16
+ id: string;
17
+ username: string;
18
+ profile: {
19
+ displayName: string | null;
20
+ } | null;
21
+ };
22
+ } & {
23
+ type: import(".prisma/client").$Enums.MarketplaceListingType;
24
+ payload: Prisma.JsonValue;
25
+ id: string;
26
+ createdAt: Date;
27
+ updatedAt: Date;
28
+ schoolId: string | null;
29
+ tags: string[];
30
+ description: string | null;
31
+ title: string;
32
+ worksheetId: string | null;
33
+ markSchemeId: string | null;
34
+ authorId: string;
35
+ fileId: string | null;
36
+ visibility: import(".prisma/client").$Enums.MarketplaceVisibility;
37
+ reportCount: number;
38
+ isHidden: boolean;
39
+ })[];
40
+ nextCursor: string | undefined;
41
+ }>;
42
+ export declare function getMarketplaceListing(userId: string, listingId: string): Promise<{
43
+ school: {
44
+ id: string;
45
+ name: string;
46
+ } | null;
47
+ author: {
48
+ id: string;
49
+ username: string;
50
+ profile: {
51
+ displayName: string | null;
52
+ } | null;
53
+ };
54
+ } & {
55
+ type: import(".prisma/client").$Enums.MarketplaceListingType;
56
+ payload: Prisma.JsonValue;
57
+ id: string;
58
+ createdAt: Date;
59
+ updatedAt: Date;
60
+ schoolId: string | null;
61
+ tags: string[];
62
+ description: string | null;
63
+ title: string;
64
+ worksheetId: string | null;
65
+ markSchemeId: string | null;
66
+ authorId: string;
67
+ fileId: string | null;
68
+ visibility: import(".prisma/client").$Enums.MarketplaceVisibility;
69
+ reportCount: number;
70
+ isHidden: boolean;
71
+ }>;
72
+ export declare function publishMarkSchemeListing(userId: string, input: {
73
+ markSchemeId: string;
74
+ title: string;
75
+ description?: string;
76
+ visibility: MarketplaceVisibility;
77
+ schoolId?: string;
78
+ tags?: string[];
79
+ }): Promise<{
80
+ school: {
81
+ id: string;
82
+ name: string;
83
+ } | null;
84
+ author: {
85
+ id: string;
86
+ username: string;
87
+ profile: {
88
+ displayName: string | null;
89
+ } | null;
90
+ };
91
+ } & {
92
+ type: import(".prisma/client").$Enums.MarketplaceListingType;
93
+ payload: Prisma.JsonValue;
94
+ id: string;
95
+ createdAt: Date;
96
+ updatedAt: Date;
97
+ schoolId: string | null;
98
+ tags: string[];
99
+ description: string | null;
100
+ title: string;
101
+ worksheetId: string | null;
102
+ markSchemeId: string | null;
103
+ authorId: string;
104
+ fileId: string | null;
105
+ visibility: import(".prisma/client").$Enums.MarketplaceVisibility;
106
+ reportCount: number;
107
+ isHidden: boolean;
108
+ }>;
109
+ export declare function publishWorksheetListing(userId: string, input: {
110
+ worksheetId: string;
111
+ title: string;
112
+ description?: string;
113
+ visibility: MarketplaceVisibility;
114
+ schoolId?: string;
115
+ tags?: string[];
116
+ }): Promise<{
117
+ school: {
118
+ id: string;
119
+ name: string;
120
+ } | null;
121
+ author: {
122
+ id: string;
123
+ username: string;
124
+ profile: {
125
+ displayName: string | null;
126
+ } | null;
127
+ };
128
+ } & {
129
+ type: import(".prisma/client").$Enums.MarketplaceListingType;
130
+ payload: Prisma.JsonValue;
131
+ id: string;
132
+ createdAt: Date;
133
+ updatedAt: Date;
134
+ schoolId: string | null;
135
+ tags: string[];
136
+ description: string | null;
137
+ title: string;
138
+ worksheetId: string | null;
139
+ markSchemeId: string | null;
140
+ authorId: string;
141
+ fileId: string | null;
142
+ visibility: import(".prisma/client").$Enums.MarketplaceVisibility;
143
+ reportCount: number;
144
+ isHidden: boolean;
145
+ }>;
146
+ export declare function publishFileListing(userId: string, input: {
147
+ fileId: string;
148
+ title: string;
149
+ description?: string;
150
+ visibility: MarketplaceVisibility;
151
+ schoolId?: string;
152
+ tags?: string[];
153
+ }): Promise<{
154
+ school: {
155
+ id: string;
156
+ name: string;
157
+ } | null;
158
+ author: {
159
+ id: string;
160
+ username: string;
161
+ profile: {
162
+ displayName: string | null;
163
+ } | null;
164
+ };
165
+ } & {
166
+ type: import(".prisma/client").$Enums.MarketplaceListingType;
167
+ payload: Prisma.JsonValue;
168
+ id: string;
169
+ createdAt: Date;
170
+ updatedAt: Date;
171
+ schoolId: string | null;
172
+ tags: string[];
173
+ description: string | null;
174
+ title: string;
175
+ worksheetId: string | null;
176
+ markSchemeId: string | null;
177
+ authorId: string;
178
+ fileId: string | null;
179
+ visibility: import(".prisma/client").$Enums.MarketplaceVisibility;
180
+ reportCount: number;
181
+ isHidden: boolean;
182
+ }>;
183
+ /**
184
+ * Import a listing into the caller's workspace:
185
+ * - MARKSCHEME → class mark scheme (or department template when departmentId given)
186
+ * - WORKSHEET → class worksheet copy
187
+ * - FILE → personal / class / school folder copy (GCS object copied)
188
+ */
189
+ export declare function importMarketplaceListing(userId: string, input: {
190
+ listingId: string;
191
+ classId?: string;
192
+ departmentId?: string;
193
+ folderId?: string;
194
+ name?: string;
195
+ }): Promise<{
196
+ type: "MARKSCHEME";
197
+ markScheme: {
198
+ id: string;
199
+ name: string | null;
200
+ classId: string | null;
201
+ departmentId: string | null;
202
+ };
203
+ worksheet?: undefined;
204
+ file?: undefined;
205
+ } | {
206
+ type: "WORKSHEET";
207
+ worksheet: {
208
+ id: string;
209
+ name: string;
210
+ classId: string;
211
+ };
212
+ markScheme?: undefined;
213
+ file?: undefined;
214
+ } | {
215
+ type: "FILE";
216
+ file: {
217
+ path: string;
218
+ type: string;
219
+ id: string;
220
+ name: string;
221
+ folderId: string | null;
222
+ };
223
+ markScheme?: undefined;
224
+ worksheet?: undefined;
225
+ }>;
226
+ export declare function reportMarketplaceListing(userId: string, listingId: string): Promise<{
227
+ id: string;
228
+ reportCount: number;
229
+ isHidden: boolean;
230
+ }>;
231
+ export declare function hideMarketplaceListing(userId: string, listingId: string, isHidden: boolean): Promise<{
232
+ school: {
233
+ id: string;
234
+ name: string;
235
+ } | null;
236
+ author: {
237
+ id: string;
238
+ username: string;
239
+ profile: {
240
+ displayName: string | null;
241
+ } | null;
242
+ };
243
+ } & {
244
+ type: import(".prisma/client").$Enums.MarketplaceListingType;
245
+ payload: Prisma.JsonValue;
246
+ id: string;
247
+ createdAt: Date;
248
+ updatedAt: Date;
249
+ schoolId: string | null;
250
+ tags: string[];
251
+ description: string | null;
252
+ title: string;
253
+ worksheetId: string | null;
254
+ markSchemeId: string | null;
255
+ authorId: string;
256
+ fileId: string | null;
257
+ visibility: import(".prisma/client").$Enums.MarketplaceVisibility;
258
+ reportCount: number;
259
+ isHidden: boolean;
260
+ }>;
261
+ export declare function unpublishMarketplaceListing(userId: string, listingId: string): Promise<{
262
+ success: true;
263
+ deleted: boolean;
264
+ }>;
265
+ /** Publishable sources for the current user (for the publish dialog). */
266
+ export declare function listPublishableSources(userId: string, input: {
267
+ classId?: string;
268
+ schoolId?: string;
269
+ }): Promise<{
270
+ markSchemes: {
271
+ department: {
272
+ name: string;
273
+ } | null;
274
+ class: {
275
+ name: string;
276
+ } | null;
277
+ id: string;
278
+ name: string | null;
279
+ classId: string | null;
280
+ departmentId: string | null;
281
+ }[];
282
+ worksheets: {
283
+ class: {
284
+ name: string;
285
+ } | null;
286
+ id: string;
287
+ name: string;
288
+ _count: {
289
+ questions: number;
290
+ };
291
+ classId: string;
292
+ }[];
293
+ files: {
294
+ type: string;
295
+ id: string;
296
+ name: string;
297
+ size: number | null;
298
+ folderId: string | null;
299
+ }[];
300
+ }>;
301
+ //# sourceMappingURL=marketplace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marketplace.d.ts","sourceRoot":"/","sources":["services/marketplace.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACrB,MAAM,EAEP,MAAM,gBAAgB,CAAC;AA8HxB,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,IAAI,CAAC,EAAE,sBAAsB,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkBF;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAO5E;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,qBAAqB,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEF;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,qBAAqB,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDF;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,qBAAqB,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFF;AAED;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6MF;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM;;;;GASlB;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiBlB;AAED,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM;;;GAqBlB;AAED,yEAAyE;AACzE,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8E/C"}