@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
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Branded transcript / progress-report PDF builder (pdf-lib).
3
+ *
4
+ * Variants:
5
+ * - `letter` — percentage + letter-grade style transcript
6
+ * - `level` — CDC Annex-4 style progress report (Below Basic / Basic /
7
+ * Proficient / Advanced) when grading boundaries are level-based
8
+ *
9
+ * School branding uses name + optional logo. Letterhead is not in the School
10
+ * schema yet — when added, callers can pass `letterheadBytes` without blocking.
11
+ */
12
+
13
+ !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]="1384d4a1-636c-51e0-8dbd-e4271dae1aac")}catch(e){}}();
14
+ import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
15
+ import fontkit from "@pdf-lib/fontkit";
16
+ import { readFileSync } from "fs";
17
+ import { join } from "path";
18
+ const PAGE_WIDTH = 595.28; // A4
19
+ const PAGE_HEIGHT = 841.89;
20
+ const MARGIN = 48;
21
+ const CONTENT_WIDTH = PAGE_WIDTH - MARGIN * 2;
22
+ const LEVEL_ORDER = [
23
+ "Below Basic",
24
+ "Basic",
25
+ "Proficient",
26
+ "Advanced",
27
+ ];
28
+ const LEVEL_COLORS = {
29
+ "Below Basic": rgb(0.75, 0.22, 0.17),
30
+ Basic: rgb(0.85, 0.55, 0.13),
31
+ Proficient: rgb(0.2, 0.45, 0.7),
32
+ Advanced: rgb(0.13, 0.55, 0.35),
33
+ };
34
+ async function embedFonts(pdfDoc) {
35
+ pdfDoc.registerFontkit(fontkit);
36
+ const fontDir = join(process.cwd(), "src", "lib");
37
+ try {
38
+ const regularBytes = readFileSync(join(fontDir, "NotoSans-Regular.ttf"));
39
+ const boldBytes = readFileSync(join(fontDir, "NotoSans-Bold.ttf"));
40
+ return {
41
+ regular: await pdfDoc.embedFont(regularBytes),
42
+ bold: await pdfDoc.embedFont(boldBytes),
43
+ };
44
+ }
45
+ catch {
46
+ return {
47
+ regular: await pdfDoc.embedFont(StandardFonts.Helvetica),
48
+ bold: await pdfDoc.embedFont(StandardFonts.HelveticaBold),
49
+ };
50
+ }
51
+ }
52
+ async function embedImage(pdfDoc, bytes, contentType) {
53
+ if (!bytes || bytes.length === 0)
54
+ return null;
55
+ const type = (contentType ?? "").toLowerCase();
56
+ try {
57
+ if (type.includes("png") || (!type && looksLikePng(bytes))) {
58
+ return await pdfDoc.embedPng(bytes);
59
+ }
60
+ return await pdfDoc.embedJpg(bytes);
61
+ }
62
+ catch {
63
+ try {
64
+ return await pdfDoc.embedPng(bytes);
65
+ }
66
+ catch {
67
+ try {
68
+ return await pdfDoc.embedJpg(bytes);
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ }
74
+ }
75
+ }
76
+ function looksLikePng(bytes) {
77
+ return (bytes.length >= 8 &&
78
+ bytes[0] === 0x89 &&
79
+ bytes[1] === 0x50 &&
80
+ bytes[2] === 0x4e &&
81
+ bytes[3] === 0x47);
82
+ }
83
+ function formatPercent(value) {
84
+ if (value === null || Number.isNaN(value))
85
+ return "—";
86
+ return `${value.toFixed(1)}%`;
87
+ }
88
+ function drawText(page, text, x, y, font, size, color = rgb(0.12, 0.12, 0.14)) {
89
+ page.drawText(text, { x, y, size, font, color });
90
+ }
91
+ function truncateToWidth(text, font, size, maxWidth) {
92
+ if (font.widthOfTextAtSize(text, size) <= maxWidth)
93
+ return text;
94
+ let truncated = text;
95
+ while (truncated.length > 1) {
96
+ truncated = truncated.slice(0, -1);
97
+ if (font.widthOfTextAtSize(`${truncated}…`, size) <= maxWidth) {
98
+ return `${truncated}…`;
99
+ }
100
+ }
101
+ return "…";
102
+ }
103
+ /**
104
+ * Detects CDC Annex-4 style level boundaries from a GradingBoundary
105
+ * `structured` JSON payload.
106
+ */
107
+ export function isLevelBasedBoundary(structured) {
108
+ if (!structured)
109
+ return false;
110
+ try {
111
+ const parsed = JSON.parse(structured);
112
+ if (!Array.isArray(parsed.boundaries) || parsed.boundaries.length === 0) {
113
+ return false;
114
+ }
115
+ const grades = parsed.boundaries
116
+ .map((b) => (b.grade ?? "").trim().toLowerCase())
117
+ .filter(Boolean);
118
+ if (grades.length === 0)
119
+ return false;
120
+ const levelSet = new Set(LEVEL_ORDER.map((g) => g.toLowerCase()));
121
+ const matches = grades.filter((g) => levelSet.has(g));
122
+ return matches.length >= Math.ceil(grades.length * 0.5);
123
+ }
124
+ catch {
125
+ return false;
126
+ }
127
+ }
128
+ export async function buildTranscriptPdf(input) {
129
+ const pdfDoc = await PDFDocument.create();
130
+ const { regular, bold } = await embedFonts(pdfDoc);
131
+ const logo = await embedImage(pdfDoc, input.logoBytes, input.logoContentType);
132
+ const letterhead = await embedImage(pdfDoc, input.letterheadBytes, input.letterheadContentType);
133
+ let page = pdfDoc.addPage([PAGE_WIDTH, PAGE_HEIGHT]);
134
+ let y = PAGE_HEIGHT - MARGIN;
135
+ if (letterhead) {
136
+ const lhHeight = 72;
137
+ const scale = Math.min(CONTENT_WIDTH / letterhead.width, lhHeight / letterhead.height);
138
+ const w = letterhead.width * scale;
139
+ const h = letterhead.height * scale;
140
+ page.drawImage(letterhead, {
141
+ x: MARGIN + (CONTENT_WIDTH - w) / 2,
142
+ y: y - h,
143
+ width: w,
144
+ height: h,
145
+ });
146
+ y -= h + 16;
147
+ }
148
+ const logoSize = 48;
149
+ let textLeft = MARGIN;
150
+ if (logo) {
151
+ const scale = Math.min(logoSize / logo.width, logoSize / logo.height);
152
+ const w = logo.width * scale;
153
+ const h = logo.height * scale;
154
+ page.drawImage(logo, {
155
+ x: MARGIN,
156
+ y: y - h,
157
+ width: w,
158
+ height: h,
159
+ });
160
+ textLeft = MARGIN + w + 14;
161
+ }
162
+ const title = input.variant === "level"
163
+ ? "Student Progress Report"
164
+ : "Academic Transcript";
165
+ drawText(page, input.schoolName, textLeft, y - 16, bold, 16);
166
+ drawText(page, title, textLeft, y - 34, regular, 11, rgb(0.35, 0.35, 0.4));
167
+ y -= logo || letterhead ? 64 : 48;
168
+ page.drawLine({
169
+ start: { x: MARGIN, y },
170
+ end: { x: PAGE_WIDTH - MARGIN, y },
171
+ thickness: 1,
172
+ color: rgb(0.82, 0.82, 0.85),
173
+ });
174
+ y -= 22;
175
+ drawText(page, "Student", MARGIN, y, regular, 9, rgb(0.45, 0.45, 0.5));
176
+ y -= 14;
177
+ drawText(page, input.studentName, MARGIN, y, bold, 13);
178
+ y -= 16;
179
+ if (input.studentUsername) {
180
+ drawText(page, `@${input.studentUsername}`, MARGIN, y, regular, 9, rgb(0.4, 0.4, 0.45));
181
+ y -= 14;
182
+ }
183
+ const metaParts = [];
184
+ if (input.schoolYear?.trim()) {
185
+ metaParts.push(`School year: ${input.schoolYear.trim()}`);
186
+ }
187
+ if (input.periodLabel?.trim()) {
188
+ metaParts.push(`Period: ${input.periodLabel.trim()}`);
189
+ }
190
+ if (metaParts.length === 0) {
191
+ metaParts.push("Period: All recorded grades & attendance");
192
+ }
193
+ drawText(page, metaParts.join(" · "), MARGIN, y, regular, 9, rgb(0.35, 0.35, 0.4));
194
+ y -= 24;
195
+ y = drawAttendanceBlock(page, y, input.attendance, regular, bold);
196
+ if (input.variant === "level") {
197
+ y = drawLevelLegend(page, y, regular, bold);
198
+ }
199
+ const reportTitle = input.variant === "level" ? "Subject progress" : "Course grades";
200
+ drawText(page, reportTitle, MARGIN, y, bold, 11);
201
+ y -= 16;
202
+ y = drawTableHeader(page, y, input.variant, bold);
203
+ if (input.classes.length === 0) {
204
+ drawText(page, "No enrolled classes with grade data for this period.", MARGIN, y, regular, 9, rgb(0.45, 0.45, 0.5));
205
+ y -= 18;
206
+ }
207
+ else {
208
+ for (const row of input.classes) {
209
+ if (y < MARGIN + 80) {
210
+ page = pdfDoc.addPage([PAGE_WIDTH, PAGE_HEIGHT]);
211
+ y = PAGE_HEIGHT - MARGIN;
212
+ y = drawTableHeader(page, y, input.variant, bold);
213
+ }
214
+ y = drawClassRow(page, y, row, input.variant, regular, bold);
215
+ }
216
+ }
217
+ const generated = input.generatedAt ?? new Date();
218
+ const footer = `Generated ${generated.toISOString().slice(0, 10)} · Studious LMS`;
219
+ drawText(page, footer, MARGIN, MARGIN - 8, regular, 8, rgb(0.55, 0.55, 0.58));
220
+ drawText(page, "Note: Academic terms (C1) are not modelled yet; period filters use dates or a school-year label.", MARGIN, MARGIN - 20, regular, 7, rgb(0.6, 0.6, 0.62));
221
+ return pdfDoc.save();
222
+ }
223
+ function drawAttendanceBlock(page, y, attendance, regular, bold) {
224
+ drawText(page, "Attendance", MARGIN, y, bold, 11);
225
+ y -= 14;
226
+ const line = [
227
+ `Present: ${attendance.present}`,
228
+ `Late: ${attendance.late}`,
229
+ `Absent: ${attendance.absent}`,
230
+ `Rate: ${formatPercent(attendance.percent)}`,
231
+ ].join(" · ");
232
+ drawText(page, line, MARGIN, y, regular, 9, rgb(0.25, 0.25, 0.3));
233
+ y -= 8;
234
+ drawText(page, "Rate = (present + late) / (present + late + absent). Excused status is not modelled in this report path.", MARGIN, y, regular, 7, rgb(0.55, 0.55, 0.58));
235
+ return y - 20;
236
+ }
237
+ function drawLevelLegend(page, y, regular, bold) {
238
+ drawText(page, "Achievement levels (CDC Annex-4 style)", MARGIN, y, bold, 10);
239
+ y -= 14;
240
+ let x = MARGIN;
241
+ for (const level of LEVEL_ORDER) {
242
+ const color = LEVEL_COLORS[level];
243
+ page.drawRectangle({
244
+ x,
245
+ y: y - 2,
246
+ width: 8,
247
+ height: 8,
248
+ color,
249
+ });
250
+ drawText(page, level, x + 12, y - 1, regular, 8, color);
251
+ x += level.length * 8 * 0.52 + 28;
252
+ }
253
+ return y - 22;
254
+ }
255
+ function drawTableHeader(page, y, variant, bold) {
256
+ const gradeCol = variant === "level" ? "Level" : "Grade";
257
+ page.drawRectangle({
258
+ x: MARGIN,
259
+ y: y - 4,
260
+ width: CONTENT_WIDTH,
261
+ height: 18,
262
+ color: rgb(0.94, 0.95, 0.97),
263
+ });
264
+ drawText(page, "Subject / Class", MARGIN + 6, y, bold, 8);
265
+ drawText(page, "Section", MARGIN + 250, y, bold, 8);
266
+ drawText(page, "Score", MARGIN + 320, y, bold, 8);
267
+ drawText(page, gradeCol, MARGIN + 390, y, bold, 8);
268
+ return y - 20;
269
+ }
270
+ function drawClassRow(page, y, row, variant, regular, bold) {
271
+ const label = truncateToWidth(row.subject?.trim()
272
+ ? `${row.subject} — ${row.className}`
273
+ : row.className, regular, 9, 230);
274
+ drawText(page, label, MARGIN + 6, y, regular, 9);
275
+ drawText(page, truncateToWidth(row.section || "—", regular, 9, 60), MARGIN + 250, y, regular, 9);
276
+ drawText(page, formatPercent(row.percentage), MARGIN + 320, y, regular, 9);
277
+ const grade = row.gradeLabel?.trim() || "—";
278
+ const levelKey = LEVEL_ORDER.find((l) => l.toLowerCase() === grade.toLowerCase());
279
+ const gradeColor = variant === "level" && levelKey
280
+ ? LEVEL_COLORS[levelKey]
281
+ : rgb(0.12, 0.12, 0.14);
282
+ drawText(page, truncateToWidth(grade, bold, 9, 120), MARGIN + 390, y, bold, 9, gradeColor);
283
+ y -= 14;
284
+ if (row.categorySummaries.length > 0) {
285
+ const cats = row.categorySummaries
286
+ .filter((c) => c.average !== null)
287
+ .map((c) => `${c.name}: ${formatPercent(c.average)}`)
288
+ .join(" · ");
289
+ if (cats) {
290
+ drawText(page, truncateToWidth(cats, regular, 7, CONTENT_WIDTH - 12), MARGIN + 6, y, regular, 7, rgb(0.45, 0.45, 0.5));
291
+ y -= 12;
292
+ }
293
+ }
294
+ page.drawLine({
295
+ start: { x: MARGIN, y: y + 4 },
296
+ end: { x: PAGE_WIDTH - MARGIN, y: y + 4 },
297
+ thickness: 0.5,
298
+ color: rgb(0.9, 0.9, 0.92),
299
+ });
300
+ return y - 6;
301
+ }
302
+ //# sourceMappingURL=transcriptPdf.js.map
303
+ //# debugId=1384d4a1-636c-51e0-8dbd-e4271dae1aac
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcriptPdf.js","sources":["lib/transcriptPdf.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Branded transcript / progress-report PDF builder (pdf-lib).\n *\n * Variants:\n * - `letter` — percentage + letter-grade style transcript\n * - `level` — CDC Annex-4 style progress report (Below Basic / Basic /\n * Proficient / Advanced) when grading boundaries are level-based\n *\n * School branding uses name + optional logo. Letterhead is not in the School\n * schema yet — when added, callers can pass `letterheadBytes` without blocking.\n */\n\nimport { PDFDocument, PDFFont, PDFPage, PDFImage, rgb, StandardFonts } from \"pdf-lib\";\nimport fontkit from \"@pdf-lib/fontkit\";\nimport { readFileSync } from \"fs\";\nimport { join } from \"path\";\n\nexport type TranscriptReportVariant = \"letter\" | \"level\";\n\nexport interface TranscriptClassRow {\n className: string;\n subject: string;\n section: string;\n percentage: number | null;\n gradeLabel: string | null;\n categorySummaries: { name: string; average: number | null }[];\n}\n\nexport interface TranscriptAttendanceSummary {\n present: number;\n late: number;\n absent: number;\n total: number;\n /** (present + late) / total * 100, or null when total is 0. */\n percent: number | null;\n}\n\nexport interface TranscriptPdfInput {\n schoolName: string;\n studentName: string;\n studentUsername?: string | null;\n /** Free-text school year (no AcademicTerm model yet — see C1). */\n schoolYear?: string | null;\n periodLabel?: string | null;\n variant: TranscriptReportVariant;\n classes: TranscriptClassRow[];\n attendance: TranscriptAttendanceSummary;\n logoBytes?: Uint8Array | null;\n logoContentType?: string | null;\n /** Optional future letterhead image; ignored when null/undefined. */\n letterheadBytes?: Uint8Array | null;\n letterheadContentType?: string | null;\n generatedAt?: Date;\n}\n\nconst PAGE_WIDTH = 595.28; // A4\nconst PAGE_HEIGHT = 841.89;\nconst MARGIN = 48;\nconst CONTENT_WIDTH = PAGE_WIDTH - MARGIN * 2;\n\nconst LEVEL_ORDER = [\n \"Below Basic\",\n \"Basic\",\n \"Proficient\",\n \"Advanced\",\n] as const;\n\nconst LEVEL_COLORS: Record<(typeof LEVEL_ORDER)[number], ReturnType<typeof rgb>> = {\n \"Below Basic\": rgb(0.75, 0.22, 0.17),\n Basic: rgb(0.85, 0.55, 0.13),\n Proficient: rgb(0.2, 0.45, 0.7),\n Advanced: rgb(0.13, 0.55, 0.35),\n};\n\nasync function embedFonts(pdfDoc: PDFDocument): Promise<{\n regular: PDFFont;\n bold: PDFFont;\n}> {\n pdfDoc.registerFontkit(fontkit);\n const fontDir = join(process.cwd(), \"src\", \"lib\");\n try {\n const regularBytes = readFileSync(join(fontDir, \"NotoSans-Regular.ttf\"));\n const boldBytes = readFileSync(join(fontDir, \"NotoSans-Bold.ttf\"));\n return {\n regular: await pdfDoc.embedFont(regularBytes),\n bold: await pdfDoc.embedFont(boldBytes),\n };\n } catch {\n return {\n regular: await pdfDoc.embedFont(StandardFonts.Helvetica),\n bold: await pdfDoc.embedFont(StandardFonts.HelveticaBold),\n };\n }\n}\n\nasync function embedImage(\n pdfDoc: PDFDocument,\n bytes: Uint8Array | null | undefined,\n contentType?: string | null\n): Promise<PDFImage | null> {\n if (!bytes || bytes.length === 0) return null;\n const type = (contentType ?? \"\").toLowerCase();\n try {\n if (type.includes(\"png\") || (!type && looksLikePng(bytes))) {\n return await pdfDoc.embedPng(bytes);\n }\n return await pdfDoc.embedJpg(bytes);\n } catch {\n try {\n return await pdfDoc.embedPng(bytes);\n } catch {\n try {\n return await pdfDoc.embedJpg(bytes);\n } catch {\n return null;\n }\n }\n }\n}\n\nfunction looksLikePng(bytes: Uint8Array): boolean {\n return (\n bytes.length >= 8 &&\n bytes[0] === 0x89 &&\n bytes[1] === 0x50 &&\n bytes[2] === 0x4e &&\n bytes[3] === 0x47\n );\n}\n\nfunction formatPercent(value: number | null): string {\n if (value === null || Number.isNaN(value)) return \"—\";\n return `${value.toFixed(1)}%`;\n}\n\nfunction drawText(\n page: PDFPage,\n text: string,\n x: number,\n y: number,\n font: PDFFont,\n size: number,\n color = rgb(0.12, 0.12, 0.14)\n) {\n page.drawText(text, { x, y, size, font, color });\n}\n\nfunction truncateToWidth(\n text: string,\n font: PDFFont,\n size: number,\n maxWidth: number\n): string {\n if (font.widthOfTextAtSize(text, size) <= maxWidth) return text;\n let truncated = text;\n while (truncated.length > 1) {\n truncated = truncated.slice(0, -1);\n if (font.widthOfTextAtSize(`${truncated}…`, size) <= maxWidth) {\n return `${truncated}…`;\n }\n }\n return \"…\";\n}\n\n/**\n * Detects CDC Annex-4 style level boundaries from a GradingBoundary\n * `structured` JSON payload.\n */\nexport function isLevelBasedBoundary(structured: string | null | undefined): boolean {\n if (!structured) return false;\n try {\n const parsed = JSON.parse(structured) as {\n boundaries?: { grade?: string }[];\n };\n if (!Array.isArray(parsed.boundaries) || parsed.boundaries.length === 0) {\n return false;\n }\n const grades = parsed.boundaries\n .map((b) => (b.grade ?? \"\").trim().toLowerCase())\n .filter(Boolean);\n if (grades.length === 0) return false;\n\n const levelSet = new Set(LEVEL_ORDER.map((g) => g.toLowerCase()));\n const matches = grades.filter((g) => levelSet.has(g));\n return matches.length >= Math.ceil(grades.length * 0.5);\n } catch {\n return false;\n }\n}\n\nexport async function buildTranscriptPdf(\n input: TranscriptPdfInput\n): Promise<Uint8Array> {\n const pdfDoc = await PDFDocument.create();\n const { regular, bold } = await embedFonts(pdfDoc);\n const logo = await embedImage(pdfDoc, input.logoBytes, input.logoContentType);\n const letterhead = await embedImage(\n pdfDoc,\n input.letterheadBytes,\n input.letterheadContentType\n );\n\n let page = pdfDoc.addPage([PAGE_WIDTH, PAGE_HEIGHT]);\n let y = PAGE_HEIGHT - MARGIN;\n\n if (letterhead) {\n const lhHeight = 72;\n const scale = Math.min(\n CONTENT_WIDTH / letterhead.width,\n lhHeight / letterhead.height\n );\n const w = letterhead.width * scale;\n const h = letterhead.height * scale;\n page.drawImage(letterhead, {\n x: MARGIN + (CONTENT_WIDTH - w) / 2,\n y: y - h,\n width: w,\n height: h,\n });\n y -= h + 16;\n }\n\n const logoSize = 48;\n let textLeft = MARGIN;\n if (logo) {\n const scale = Math.min(logoSize / logo.width, logoSize / logo.height);\n const w = logo.width * scale;\n const h = logo.height * scale;\n page.drawImage(logo, {\n x: MARGIN,\n y: y - h,\n width: w,\n height: h,\n });\n textLeft = MARGIN + w + 14;\n }\n\n const title =\n input.variant === \"level\"\n ? \"Student Progress Report\"\n : \"Academic Transcript\";\n\n drawText(page, input.schoolName, textLeft, y - 16, bold, 16);\n drawText(page, title, textLeft, y - 34, regular, 11, rgb(0.35, 0.35, 0.4));\n y -= logo || letterhead ? 64 : 48;\n\n page.drawLine({\n start: { x: MARGIN, y },\n end: { x: PAGE_WIDTH - MARGIN, y },\n thickness: 1,\n color: rgb(0.82, 0.82, 0.85),\n });\n y -= 22;\n\n drawText(page, \"Student\", MARGIN, y, regular, 9, rgb(0.45, 0.45, 0.5));\n y -= 14;\n drawText(page, input.studentName, MARGIN, y, bold, 13);\n y -= 16;\n if (input.studentUsername) {\n drawText(\n page,\n `@${input.studentUsername}`,\n MARGIN,\n y,\n regular,\n 9,\n rgb(0.4, 0.4, 0.45)\n );\n y -= 14;\n }\n\n const metaParts: string[] = [];\n if (input.schoolYear?.trim()) {\n metaParts.push(`School year: ${input.schoolYear.trim()}`);\n }\n if (input.periodLabel?.trim()) {\n metaParts.push(`Period: ${input.periodLabel.trim()}`);\n }\n if (metaParts.length === 0) {\n metaParts.push(\"Period: All recorded grades & attendance\");\n }\n drawText(\n page,\n metaParts.join(\" · \"),\n MARGIN,\n y,\n regular,\n 9,\n rgb(0.35, 0.35, 0.4)\n );\n y -= 24;\n\n y = drawAttendanceBlock(page, y, input.attendance, regular, bold);\n\n if (input.variant === \"level\") {\n y = drawLevelLegend(page, y, regular, bold);\n }\n\n const reportTitle =\n input.variant === \"level\" ? \"Subject progress\" : \"Course grades\";\n drawText(page, reportTitle, MARGIN, y, bold, 11);\n y -= 16;\n\n y = drawTableHeader(page, y, input.variant, bold);\n\n if (input.classes.length === 0) {\n drawText(\n page,\n \"No enrolled classes with grade data for this period.\",\n MARGIN,\n y,\n regular,\n 9,\n rgb(0.45, 0.45, 0.5)\n );\n y -= 18;\n } else {\n for (const row of input.classes) {\n if (y < MARGIN + 80) {\n page = pdfDoc.addPage([PAGE_WIDTH, PAGE_HEIGHT]);\n y = PAGE_HEIGHT - MARGIN;\n y = drawTableHeader(page, y, input.variant, bold);\n }\n y = drawClassRow(page, y, row, input.variant, regular, bold);\n }\n }\n\n const generated = input.generatedAt ?? new Date();\n const footer = `Generated ${generated.toISOString().slice(0, 10)} · Studious LMS`;\n drawText(\n page,\n footer,\n MARGIN,\n MARGIN - 8,\n regular,\n 8,\n rgb(0.55, 0.55, 0.58)\n );\n\n drawText(\n page,\n \"Note: Academic terms (C1) are not modelled yet; period filters use dates or a school-year label.\",\n MARGIN,\n MARGIN - 20,\n regular,\n 7,\n rgb(0.6, 0.6, 0.62)\n );\n\n return pdfDoc.save();\n}\n\nfunction drawAttendanceBlock(\n page: PDFPage,\n y: number,\n attendance: TranscriptAttendanceSummary,\n regular: PDFFont,\n bold: PDFFont\n): number {\n drawText(page, \"Attendance\", MARGIN, y, bold, 11);\n y -= 14;\n\n const line = [\n `Present: ${attendance.present}`,\n `Late: ${attendance.late}`,\n `Absent: ${attendance.absent}`,\n `Rate: ${formatPercent(attendance.percent)}`,\n ].join(\" · \");\n\n drawText(page, line, MARGIN, y, regular, 9, rgb(0.25, 0.25, 0.3));\n y -= 8;\n drawText(\n page,\n \"Rate = (present + late) / (present + late + absent). Excused status is not modelled in this report path.\",\n MARGIN,\n y,\n regular,\n 7,\n rgb(0.55, 0.55, 0.58)\n );\n return y - 20;\n}\n\nfunction drawLevelLegend(\n page: PDFPage,\n y: number,\n regular: PDFFont,\n bold: PDFFont\n): number {\n drawText(page, \"Achievement levels (CDC Annex-4 style)\", MARGIN, y, bold, 10);\n y -= 14;\n let x = MARGIN;\n for (const level of LEVEL_ORDER) {\n const color = LEVEL_COLORS[level];\n page.drawRectangle({\n x,\n y: y - 2,\n width: 8,\n height: 8,\n color,\n });\n drawText(page, level, x + 12, y - 1, regular, 8, color);\n x += level.length * 8 * 0.52 + 28;\n }\n return y - 22;\n}\n\nfunction drawTableHeader(\n page: PDFPage,\n y: number,\n variant: TranscriptReportVariant,\n bold: PDFFont\n): number {\n const gradeCol = variant === \"level\" ? \"Level\" : \"Grade\";\n page.drawRectangle({\n x: MARGIN,\n y: y - 4,\n width: CONTENT_WIDTH,\n height: 18,\n color: rgb(0.94, 0.95, 0.97),\n });\n drawText(page, \"Subject / Class\", MARGIN + 6, y, bold, 8);\n drawText(page, \"Section\", MARGIN + 250, y, bold, 8);\n drawText(page, \"Score\", MARGIN + 320, y, bold, 8);\n drawText(page, gradeCol, MARGIN + 390, y, bold, 8);\n return y - 20;\n}\n\nfunction drawClassRow(\n page: PDFPage,\n y: number,\n row: TranscriptClassRow,\n variant: TranscriptReportVariant,\n regular: PDFFont,\n bold: PDFFont\n): number {\n const label = truncateToWidth(\n row.subject?.trim()\n ? `${row.subject} — ${row.className}`\n : row.className,\n regular,\n 9,\n 230\n );\n drawText(page, label, MARGIN + 6, y, regular, 9);\n drawText(\n page,\n truncateToWidth(row.section || \"—\", regular, 9, 60),\n MARGIN + 250,\n y,\n regular,\n 9\n );\n drawText(page, formatPercent(row.percentage), MARGIN + 320, y, regular, 9);\n\n const grade = row.gradeLabel?.trim() || \"—\";\n const levelKey = LEVEL_ORDER.find(\n (l) => l.toLowerCase() === grade.toLowerCase()\n );\n const gradeColor =\n variant === \"level\" && levelKey\n ? LEVEL_COLORS[levelKey]\n : rgb(0.12, 0.12, 0.14);\n drawText(\n page,\n truncateToWidth(grade, bold, 9, 120),\n MARGIN + 390,\n y,\n bold,\n 9,\n gradeColor\n );\n\n y -= 14;\n\n if (row.categorySummaries.length > 0) {\n const cats = row.categorySummaries\n .filter((c) => c.average !== null)\n .map((c) => `${c.name}: ${formatPercent(c.average)}`)\n .join(\" · \");\n if (cats) {\n drawText(\n page,\n truncateToWidth(cats, regular, 7, CONTENT_WIDTH - 12),\n MARGIN + 6,\n y,\n regular,\n 7,\n rgb(0.45, 0.45, 0.5)\n );\n y -= 12;\n }\n }\n\n page.drawLine({\n start: { x: MARGIN, y: y + 4 },\n end: { x: PAGE_WIDTH - MARGIN, y: y + 4 },\n thickness: 0.5,\n color: rgb(0.9, 0.9, 0.92),\n });\n return y - 6;\n}\n"],"names":[],"mappings":"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","debug_id":"1384d4a1-636c-51e0-8dbd-e4271dae1aac"}
@@ -0,0 +1,198 @@
1
+ export declare function listAcademicYears(schoolId: string): import("@prisma/client/runtime/library.js").PrismaPromise<({
2
+ terms: {
3
+ id: string;
4
+ createdAt: Date;
5
+ updatedAt: Date;
6
+ name: string;
7
+ startDate: Date;
8
+ endDate: Date;
9
+ isCurrent: boolean;
10
+ academicYearId: string;
11
+ }[];
12
+ } & {
13
+ id: string;
14
+ createdAt: Date;
15
+ updatedAt: Date;
16
+ name: string;
17
+ schoolId: string;
18
+ startDate: Date;
19
+ endDate: Date;
20
+ isCurrent: boolean;
21
+ })[]>;
22
+ export declare function getAcademicYearById(id: string, schoolId: string): import("@prisma/client/runtime/library.js").DynamicModelExtensionFluentApi<import(".prisma/client").Prisma.TypeMap<import("@prisma/client/runtime/library.js").InternalArgs & {
23
+ result: {
24
+ userProfile: {
25
+ profilePicture: () => {
26
+ needs: {
27
+ profilePicture: true;
28
+ };
29
+ compute(profile: {
30
+ profilePicture: string | null;
31
+ }): string | null;
32
+ };
33
+ profilePictureThumbnail: () => {
34
+ needs: {
35
+ profilePictureThumbnail: true;
36
+ };
37
+ compute(profile: {
38
+ profilePictureThumbnail: string | null;
39
+ }): string | null;
40
+ };
41
+ };
42
+ };
43
+ model: {};
44
+ query: {};
45
+ client: {};
46
+ }, {}>, "AcademicYear", "findFirst", null> & import("@prisma/client/runtime/library.js").PrismaPromise<({
47
+ terms: {
48
+ id: string;
49
+ createdAt: Date;
50
+ updatedAt: Date;
51
+ name: string;
52
+ startDate: Date;
53
+ endDate: Date;
54
+ isCurrent: boolean;
55
+ academicYearId: string;
56
+ }[];
57
+ } & {
58
+ id: string;
59
+ createdAt: Date;
60
+ updatedAt: Date;
61
+ name: string;
62
+ schoolId: string;
63
+ startDate: Date;
64
+ endDate: Date;
65
+ isCurrent: boolean;
66
+ }) | null>;
67
+ export declare function createAcademicYear(data: {
68
+ schoolId: string;
69
+ name: string;
70
+ startDate: Date;
71
+ endDate: Date;
72
+ isCurrent?: boolean;
73
+ }): Promise<{
74
+ terms: {
75
+ id: string;
76
+ createdAt: Date;
77
+ updatedAt: Date;
78
+ name: string;
79
+ startDate: Date;
80
+ endDate: Date;
81
+ isCurrent: boolean;
82
+ academicYearId: string;
83
+ }[];
84
+ } & {
85
+ id: string;
86
+ createdAt: Date;
87
+ updatedAt: Date;
88
+ name: string;
89
+ schoolId: string;
90
+ startDate: Date;
91
+ endDate: Date;
92
+ isCurrent: boolean;
93
+ }>;
94
+ export declare function updateAcademicYear(id: string, schoolId: string, data: {
95
+ name?: string;
96
+ startDate?: Date;
97
+ endDate?: Date;
98
+ isCurrent?: boolean;
99
+ }): Promise<({
100
+ terms: {
101
+ id: string;
102
+ createdAt: Date;
103
+ updatedAt: Date;
104
+ name: string;
105
+ startDate: Date;
106
+ endDate: Date;
107
+ isCurrent: boolean;
108
+ academicYearId: string;
109
+ }[];
110
+ } & {
111
+ id: string;
112
+ createdAt: Date;
113
+ updatedAt: Date;
114
+ name: string;
115
+ schoolId: string;
116
+ startDate: Date;
117
+ endDate: Date;
118
+ isCurrent: boolean;
119
+ }) | null>;
120
+ export declare function deleteAcademicYear(id: string, schoolId: string): import("@prisma/client/runtime/library.js").PrismaPromise<import("@prisma/client/runtime/library.js").GetBatchResult>;
121
+ export declare function createAcademicTerm(data: {
122
+ academicYearId: string;
123
+ name: string;
124
+ startDate: Date;
125
+ endDate: Date;
126
+ isCurrent?: boolean;
127
+ }): Promise<{
128
+ id: string;
129
+ createdAt: Date;
130
+ updatedAt: Date;
131
+ name: string;
132
+ startDate: Date;
133
+ endDate: Date;
134
+ isCurrent: boolean;
135
+ academicYearId: string;
136
+ }>;
137
+ export declare function updateAcademicTerm(id: string, academicYearId: string, data: {
138
+ name?: string;
139
+ startDate?: Date;
140
+ endDate?: Date;
141
+ isCurrent?: boolean;
142
+ }): Promise<{
143
+ id: string;
144
+ createdAt: Date;
145
+ updatedAt: Date;
146
+ name: string;
147
+ startDate: Date;
148
+ endDate: Date;
149
+ isCurrent: boolean;
150
+ academicYearId: string;
151
+ } | null>;
152
+ export declare function deleteAcademicTerm(id: string, academicYearId: string): import("@prisma/client/runtime/library.js").PrismaPromise<import("@prisma/client/runtime/library.js").GetBatchResult>;
153
+ export declare function getCurrentAcademicYear(schoolId: string): import("@prisma/client/runtime/library.js").DynamicModelExtensionFluentApi<import(".prisma/client").Prisma.TypeMap<import("@prisma/client/runtime/library.js").InternalArgs & {
154
+ result: {
155
+ userProfile: {
156
+ profilePicture: () => {
157
+ needs: {
158
+ profilePicture: true;
159
+ };
160
+ compute(profile: {
161
+ profilePicture: string | null;
162
+ }): string | null;
163
+ };
164
+ profilePictureThumbnail: () => {
165
+ needs: {
166
+ profilePictureThumbnail: true;
167
+ };
168
+ compute(profile: {
169
+ profilePictureThumbnail: string | null;
170
+ }): string | null;
171
+ };
172
+ };
173
+ };
174
+ model: {};
175
+ query: {};
176
+ client: {};
177
+ }, {}>, "AcademicYear", "findFirst", null> & import("@prisma/client/runtime/library.js").PrismaPromise<({
178
+ terms: {
179
+ id: string;
180
+ createdAt: Date;
181
+ updatedAt: Date;
182
+ name: string;
183
+ startDate: Date;
184
+ endDate: Date;
185
+ isCurrent: boolean;
186
+ academicYearId: string;
187
+ }[];
188
+ } & {
189
+ id: string;
190
+ createdAt: Date;
191
+ updatedAt: Date;
192
+ name: string;
193
+ schoolId: string;
194
+ startDate: Date;
195
+ endDate: Date;
196
+ isCurrent: boolean;
197
+ }) | null>;
198
+ //# sourceMappingURL=academicCalendar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"academicCalendar.d.ts","sourceRoot":"/","sources":["models/academicCalendar.ts"],"names":[],"mappings":"AAKA,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;MAQjD;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAO/D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;;;;;;;;;;;;;;;;;;;;GAmBA;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IACJ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;;;;;;;;;;;;;;;;;;;;WAmBF;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,yHAE9D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;;;;;;;;;GAUA;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,MAAM,EACV,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE;IACJ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;;;;;;;;;UAeF;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,yHAEpE;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAOtD"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Academic year / term model helpers (C1).
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]="e5645cac-bf80-59ad-8fd6-24a0b0f87bf7")}catch(e){}}();
6
+ import { prisma } from "../lib/prisma.js";
7
+ export function listAcademicYears(schoolId) {
8
+ return prisma.academicYear.findMany({
9
+ where: { schoolId },
10
+ orderBy: [{ isCurrent: "desc" }, { startDate: "desc" }],
11
+ include: {
12
+ terms: { orderBy: [{ isCurrent: "desc" }, { startDate: "asc" }] },
13
+ },
14
+ });
15
+ }
16
+ export function getAcademicYearById(id, schoolId) {
17
+ return prisma.academicYear.findFirst({
18
+ where: { id, schoolId },
19
+ include: {
20
+ terms: { orderBy: [{ isCurrent: "desc" }, { startDate: "asc" }] },
21
+ },
22
+ });
23
+ }
24
+ export function createAcademicYear(data) {
25
+ return prisma.$transaction(async (tx) => {
26
+ if (data.isCurrent) {
27
+ await tx.academicYear.updateMany({
28
+ where: { schoolId: data.schoolId, isCurrent: true },
29
+ data: { isCurrent: false },
30
+ });
31
+ }
32
+ return tx.academicYear.create({
33
+ data: {
34
+ schoolId: data.schoolId,
35
+ name: data.name,
36
+ startDate: data.startDate,
37
+ endDate: data.endDate,
38
+ isCurrent: data.isCurrent ?? false,
39
+ },
40
+ include: { terms: true },
41
+ });
42
+ });
43
+ }
44
+ export function updateAcademicYear(id, schoolId, data) {
45
+ return prisma.$transaction(async (tx) => {
46
+ const existing = await tx.academicYear.findFirst({ where: { id, schoolId } });
47
+ if (!existing)
48
+ return null;
49
+ if (data.isCurrent) {
50
+ await tx.academicYear.updateMany({
51
+ where: { schoolId, isCurrent: true, NOT: { id } },
52
+ data: { isCurrent: false },
53
+ });
54
+ }
55
+ return tx.academicYear.update({
56
+ where: { id },
57
+ data,
58
+ include: {
59
+ terms: { orderBy: [{ isCurrent: "desc" }, { startDate: "asc" }] },
60
+ },
61
+ });
62
+ });
63
+ }
64
+ export function deleteAcademicYear(id, schoolId) {
65
+ return prisma.academicYear.deleteMany({ where: { id, schoolId } });
66
+ }
67
+ export function createAcademicTerm(data) {
68
+ return prisma.$transaction(async (tx) => {
69
+ if (data.isCurrent) {
70
+ await tx.academicTerm.updateMany({
71
+ where: { academicYearId: data.academicYearId, isCurrent: true },
72
+ data: { isCurrent: false },
73
+ });
74
+ }
75
+ return tx.academicTerm.create({ data: { ...data, isCurrent: data.isCurrent ?? false } });
76
+ });
77
+ }
78
+ export function updateAcademicTerm(id, academicYearId, data) {
79
+ return prisma.$transaction(async (tx) => {
80
+ const existing = await tx.academicTerm.findFirst({
81
+ where: { id, academicYearId },
82
+ });
83
+ if (!existing)
84
+ return null;
85
+ if (data.isCurrent) {
86
+ await tx.academicTerm.updateMany({
87
+ where: { academicYearId, isCurrent: true, NOT: { id } },
88
+ data: { isCurrent: false },
89
+ });
90
+ }
91
+ return tx.academicTerm.update({ where: { id }, data });
92
+ });
93
+ }
94
+ export function deleteAcademicTerm(id, academicYearId) {
95
+ return prisma.academicTerm.deleteMany({ where: { id, academicYearId } });
96
+ }
97
+ export function getCurrentAcademicYear(schoolId) {
98
+ return prisma.academicYear.findFirst({
99
+ where: { schoolId, isCurrent: true },
100
+ include: {
101
+ terms: { orderBy: [{ isCurrent: "desc" }, { startDate: "asc" }] },
102
+ },
103
+ });
104
+ }
105
+ //# sourceMappingURL=academicCalendar.js.map
106
+ //# debugId=e5645cac-bf80-59ad-8fd6-24a0b0f87bf7