@studious-lms/server 1.23.0 → 1.25.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 +547 -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 +180 -0
  72. package/dist/models/marketplace.d.ts.map +1 -0
  73. package/dist/models/marketplace.js +116 -0
  74. package/dist/models/marketplace.js.map +1 -0
  75. package/dist/models/moduleItem.d.ts +308 -0
  76. package/dist/models/moduleItem.d.ts.map +1 -0
  77. package/dist/models/moduleItem.js +93 -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 +12024 -7778
  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 +61 -0
  132. package/dist/routers/moduleItem.js.map +1 -0
  133. package/dist/routers/parent.d.ts +170 -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 +110 -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 +242 -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 +327 -10
  158. package/dist/services/attendance.d.ts.map +1 -1
  159. package/dist/services/attendance.js +389 -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 +34 -2
  174. package/dist/services/class.d.ts.map +1 -1
  175. package/dist/services/class.js +27 -4
  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 +330 -0
  182. package/dist/services/fees.js.map +1 -0
  183. package/dist/services/feesAuthz.d.ts +46 -0
  184. package/dist/services/feesAuthz.d.ts.map +1 -0
  185. package/dist/services/feesAuthz.js +49 -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 +631 -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 +204 -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 +131 -0
  210. package/dist/services/parentPortal.d.ts.map +1 -0
  211. package/dist/services/parentPortal.js +314 -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 +98 -54
  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 +249 -0
  220. package/dist/services/studentImport.js.map +1 -0
  221. package/dist/services/transcript.d.ts +60 -0
  222. package/dist/services/transcript.d.ts.map +1 -0
  223. package/dist/services/transcript.js +231 -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 +402 -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 +702 -73
  254. package/src/models/class.ts +1 -0
  255. package/src/models/fees.ts +349 -0
  256. package/src/models/marketplace.ts +161 -0
  257. package/src/models/moduleItem.ts +125 -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 +91 -0
  268. package/src/routers/parent.ts +68 -0
  269. package/src/routers/school.ts +149 -1
  270. package/src/routers/worksheet.ts +11 -0
  271. package/src/services/academicCalendar.ts +327 -0
  272. package/src/services/assignment.ts +122 -12
  273. package/src/services/attendance.ts +552 -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 +26 -2
  278. package/src/services/fees.ts +447 -0
  279. package/src/services/feesAuthz.ts +73 -0
  280. package/src/services/gradebook.ts +70 -0
  281. package/src/services/idCard.ts +147 -0
  282. package/src/services/marketplace.ts +790 -0
  283. package/src/services/moduleItem.ts +298 -0
  284. package/src/services/notification.ts +43 -0
  285. package/src/services/parentPortal.ts +367 -0
  286. package/src/services/school.ts +100 -54
  287. package/src/services/studentImport.ts +285 -0
  288. package/src/services/transcript.ts +301 -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,41 @@
1
+ import type { AssignmentType } from "@prisma/client";
2
+ export type AssignmentTypeBehavior = {
3
+ acceptFiles: boolean;
4
+ acceptExtendedResponse: boolean;
5
+ acceptWorksheet: boolean;
6
+ singleAttempt: boolean;
7
+ allowLateSubmissions: boolean;
8
+ timeLimitMinutes: number | null;
9
+ };
10
+ /**
11
+ * Sensible defaults by assignment type. QUIZ/TEST tighten attempts and
12
+ * discourage late file spam; ESSAY/DISCUSSION prefer written response.
13
+ */
14
+ export declare function getAssignmentTypeDefaults(type: AssignmentType | string | undefined | null): AssignmentTypeBehavior;
15
+ export type AssignmentBehaviorInput = {
16
+ type?: AssignmentType | string | null;
17
+ acceptFiles?: boolean;
18
+ acceptExtendedResponse?: boolean;
19
+ acceptWorksheet?: boolean;
20
+ singleAttempt?: boolean;
21
+ allowLateSubmissions?: boolean;
22
+ timeLimitMinutes?: number | null;
23
+ /** When true, fill unspecified deliverable/behavior flags from type defaults. */
24
+ applyTypeDefaults?: boolean;
25
+ };
26
+ /**
27
+ * Merge explicit fields with type defaults and validate incompatible combos.
28
+ */
29
+ export declare function resolveAssignmentBehavior(input: AssignmentBehaviorInput): {
30
+ acceptFiles: boolean;
31
+ acceptExtendedResponse: boolean;
32
+ acceptWorksheet: boolean;
33
+ singleAttempt: boolean;
34
+ allowLateSubmissions: boolean;
35
+ timeLimitMinutes: number | null | undefined;
36
+ };
37
+ export declare const SIDEBAR_FEATURE_KEYS: readonly ["overview", "assignments", "quizzes", "worksheets", "grades", "files", "modules", "discussions", "attendance", "aiLabs", "members", "syllabus", "settings"];
38
+ export type SidebarFeatureKey = (typeof SIDEBAR_FEATURE_KEYS)[number];
39
+ export type SidebarFeatures = Partial<Record<SidebarFeatureKey, boolean>>;
40
+ export declare function normalizeSidebarFeatures(value: unknown): SidebarFeatures | null;
41
+ //# sourceMappingURL=assignmentTypeDefaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assignmentTypeDefaults.d.ts","sourceRoot":"/","sources":["lib/assignmentTypeDefaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,MAAM,MAAM,sBAAsB,GAAG;IACnC,WAAW,EAAE,OAAO,CAAC;IACrB,sBAAsB,EAAE,OAAO,CAAC;IAChC,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,GAC/C,sBAAsB,CA6DxB;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,CAAC,EAAE,cAAc,GAAG,MAAM,GAAG,IAAI,CAAC;IACtC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iFAAiF;IACjF,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,uBAAuB,GAAG;IACzE,WAAW,EAAE,OAAO,CAAC;IACrB,sBAAsB,EAAE,OAAO,CAAC;IAChC,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CAC7C,CAmFA;AAED,eAAO,MAAM,oBAAoB,uKAcvB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC,CAAC;AAE1E,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,GACb,eAAe,GAAG,IAAI,CAWxB"}
@@ -0,0 +1,167 @@
1
+
2
+ !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]="f18d9467-4e3c-5922-8f66-5eb168354131")}catch(e){}}();
3
+ import { TRPCError } from "@trpc/server";
4
+ /**
5
+ * Sensible defaults by assignment type. QUIZ/TEST tighten attempts and
6
+ * discourage late file spam; ESSAY/DISCUSSION prefer written response.
7
+ */
8
+ export function getAssignmentTypeDefaults(type) {
9
+ switch (type) {
10
+ case "QUIZ":
11
+ return {
12
+ acceptFiles: false,
13
+ acceptExtendedResponse: false,
14
+ acceptWorksheet: true,
15
+ singleAttempt: true,
16
+ allowLateSubmissions: false,
17
+ timeLimitMinutes: null,
18
+ };
19
+ case "TEST":
20
+ return {
21
+ acceptFiles: false,
22
+ acceptExtendedResponse: false,
23
+ acceptWorksheet: true,
24
+ singleAttempt: true,
25
+ allowLateSubmissions: false,
26
+ timeLimitMinutes: null,
27
+ };
28
+ case "ESSAY":
29
+ return {
30
+ acceptFiles: false,
31
+ acceptExtendedResponse: true,
32
+ acceptWorksheet: false,
33
+ singleAttempt: false,
34
+ allowLateSubmissions: true,
35
+ timeLimitMinutes: null,
36
+ };
37
+ case "DISCUSSION":
38
+ return {
39
+ acceptFiles: false,
40
+ acceptExtendedResponse: true,
41
+ acceptWorksheet: false,
42
+ singleAttempt: false,
43
+ allowLateSubmissions: true,
44
+ timeLimitMinutes: null,
45
+ };
46
+ case "PROJECT":
47
+ case "PRESENTATION":
48
+ case "LAB":
49
+ return {
50
+ acceptFiles: true,
51
+ acceptExtendedResponse: true,
52
+ acceptWorksheet: false,
53
+ singleAttempt: false,
54
+ allowLateSubmissions: true,
55
+ timeLimitMinutes: null,
56
+ };
57
+ case "HOMEWORK":
58
+ case "OTHER":
59
+ default:
60
+ return {
61
+ acceptFiles: true,
62
+ acceptExtendedResponse: false,
63
+ acceptWorksheet: false,
64
+ singleAttempt: false,
65
+ allowLateSubmissions: true,
66
+ timeLimitMinutes: null,
67
+ };
68
+ }
69
+ }
70
+ /**
71
+ * Merge explicit fields with type defaults and validate incompatible combos.
72
+ */
73
+ export function resolveAssignmentBehavior(input) {
74
+ const defaults = getAssignmentTypeDefaults(input.type);
75
+ const apply = input.applyTypeDefaults !== false;
76
+ const acceptFiles = input.acceptFiles !== undefined
77
+ ? input.acceptFiles
78
+ : apply
79
+ ? defaults.acceptFiles
80
+ : false;
81
+ const acceptExtendedResponse = input.acceptExtendedResponse !== undefined
82
+ ? input.acceptExtendedResponse
83
+ : apply
84
+ ? defaults.acceptExtendedResponse
85
+ : false;
86
+ const acceptWorksheet = input.acceptWorksheet !== undefined
87
+ ? input.acceptWorksheet
88
+ : apply
89
+ ? defaults.acceptWorksheet
90
+ : false;
91
+ const singleAttempt = input.singleAttempt !== undefined
92
+ ? input.singleAttempt
93
+ : apply
94
+ ? defaults.singleAttempt
95
+ : false;
96
+ const allowLateSubmissions = input.allowLateSubmissions !== undefined
97
+ ? input.allowLateSubmissions
98
+ : apply
99
+ ? defaults.allowLateSubmissions
100
+ : true;
101
+ const timeLimitMinutes = input.timeLimitMinutes !== undefined
102
+ ? input.timeLimitMinutes
103
+ : apply
104
+ ? defaults.timeLimitMinutes
105
+ : undefined;
106
+ if (timeLimitMinutes !== undefined &&
107
+ timeLimitMinutes !== null &&
108
+ (timeLimitMinutes < 1 || timeLimitMinutes > 24 * 60)) {
109
+ throw new TRPCError({
110
+ code: "BAD_REQUEST",
111
+ message: "timeLimitMinutes must be between 1 and 1440",
112
+ });
113
+ }
114
+ // Quizzes/tests: disallow file deliverables when late submissions are off
115
+ // (prevents "late file spam" after the window closes).
116
+ if ((input.type === "QUIZ" || input.type === "TEST") &&
117
+ acceptFiles &&
118
+ !allowLateSubmissions &&
119
+ input.acceptFiles === true) {
120
+ // Allowed only when teacher explicitly opts into files; keep allowLate false.
121
+ }
122
+ if (!acceptFiles &&
123
+ !acceptExtendedResponse &&
124
+ !acceptWorksheet) {
125
+ throw new TRPCError({
126
+ code: "BAD_REQUEST",
127
+ message: "Assignment must accept at least one deliverable (files, extended response, or worksheet)",
128
+ });
129
+ }
130
+ return {
131
+ acceptFiles,
132
+ acceptExtendedResponse,
133
+ acceptWorksheet,
134
+ singleAttempt,
135
+ allowLateSubmissions,
136
+ timeLimitMinutes,
137
+ };
138
+ }
139
+ export const SIDEBAR_FEATURE_KEYS = [
140
+ "overview",
141
+ "assignments",
142
+ "quizzes",
143
+ "worksheets",
144
+ "grades",
145
+ "files",
146
+ "modules",
147
+ "discussions",
148
+ "attendance",
149
+ "aiLabs",
150
+ "members",
151
+ "syllabus",
152
+ "settings",
153
+ ];
154
+ export function normalizeSidebarFeatures(value) {
155
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
156
+ return null;
157
+ }
158
+ const out = {};
159
+ for (const key of SIDEBAR_FEATURE_KEYS) {
160
+ if (key in value) {
161
+ out[key] = Boolean(value[key]);
162
+ }
163
+ }
164
+ return out;
165
+ }
166
+ //# sourceMappingURL=assignmentTypeDefaults.js.map
167
+ //# debugId=f18d9467-4e3c-5922-8f66-5eb168354131
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assignmentTypeDefaults.js","sources":["lib/assignmentTypeDefaults.ts"],"sourceRoot":"/","sourcesContent":["import type { AssignmentType } from \"@prisma/client\";\nimport { TRPCError } from \"@trpc/server\";\n\nexport type AssignmentTypeBehavior = {\n acceptFiles: boolean;\n acceptExtendedResponse: boolean;\n acceptWorksheet: boolean;\n singleAttempt: boolean;\n allowLateSubmissions: boolean;\n timeLimitMinutes: number | null;\n};\n\n/**\n * Sensible defaults by assignment type. QUIZ/TEST tighten attempts and\n * discourage late file spam; ESSAY/DISCUSSION prefer written response.\n */\nexport function getAssignmentTypeDefaults(\n type: AssignmentType | string | undefined | null\n): AssignmentTypeBehavior {\n switch (type) {\n case \"QUIZ\":\n return {\n acceptFiles: false,\n acceptExtendedResponse: false,\n acceptWorksheet: true,\n singleAttempt: true,\n allowLateSubmissions: false,\n timeLimitMinutes: null,\n };\n case \"TEST\":\n return {\n acceptFiles: false,\n acceptExtendedResponse: false,\n acceptWorksheet: true,\n singleAttempt: true,\n allowLateSubmissions: false,\n timeLimitMinutes: null,\n };\n case \"ESSAY\":\n return {\n acceptFiles: false,\n acceptExtendedResponse: true,\n acceptWorksheet: false,\n singleAttempt: false,\n allowLateSubmissions: true,\n timeLimitMinutes: null,\n };\n case \"DISCUSSION\":\n return {\n acceptFiles: false,\n acceptExtendedResponse: true,\n acceptWorksheet: false,\n singleAttempt: false,\n allowLateSubmissions: true,\n timeLimitMinutes: null,\n };\n case \"PROJECT\":\n case \"PRESENTATION\":\n case \"LAB\":\n return {\n acceptFiles: true,\n acceptExtendedResponse: true,\n acceptWorksheet: false,\n singleAttempt: false,\n allowLateSubmissions: true,\n timeLimitMinutes: null,\n };\n case \"HOMEWORK\":\n case \"OTHER\":\n default:\n return {\n acceptFiles: true,\n acceptExtendedResponse: false,\n acceptWorksheet: false,\n singleAttempt: false,\n allowLateSubmissions: true,\n timeLimitMinutes: null,\n };\n }\n}\n\nexport type AssignmentBehaviorInput = {\n type?: AssignmentType | string | null;\n acceptFiles?: boolean;\n acceptExtendedResponse?: boolean;\n acceptWorksheet?: boolean;\n singleAttempt?: boolean;\n allowLateSubmissions?: boolean;\n timeLimitMinutes?: number | null;\n /** When true, fill unspecified deliverable/behavior flags from type defaults. */\n applyTypeDefaults?: boolean;\n};\n\n/**\n * Merge explicit fields with type defaults and validate incompatible combos.\n */\nexport function resolveAssignmentBehavior(input: AssignmentBehaviorInput): {\n acceptFiles: boolean;\n acceptExtendedResponse: boolean;\n acceptWorksheet: boolean;\n singleAttempt: boolean;\n allowLateSubmissions: boolean;\n timeLimitMinutes: number | null | undefined;\n} {\n const defaults = getAssignmentTypeDefaults(input.type);\n const apply = input.applyTypeDefaults !== false;\n\n const acceptFiles =\n input.acceptFiles !== undefined\n ? input.acceptFiles\n : apply\n ? defaults.acceptFiles\n : false;\n const acceptExtendedResponse =\n input.acceptExtendedResponse !== undefined\n ? input.acceptExtendedResponse\n : apply\n ? defaults.acceptExtendedResponse\n : false;\n const acceptWorksheet =\n input.acceptWorksheet !== undefined\n ? input.acceptWorksheet\n : apply\n ? defaults.acceptWorksheet\n : false;\n const singleAttempt =\n input.singleAttempt !== undefined\n ? input.singleAttempt\n : apply\n ? defaults.singleAttempt\n : false;\n const allowLateSubmissions =\n input.allowLateSubmissions !== undefined\n ? input.allowLateSubmissions\n : apply\n ? defaults.allowLateSubmissions\n : true;\n const timeLimitMinutes =\n input.timeLimitMinutes !== undefined\n ? input.timeLimitMinutes\n : apply\n ? defaults.timeLimitMinutes\n : undefined;\n\n if (\n timeLimitMinutes !== undefined &&\n timeLimitMinutes !== null &&\n (timeLimitMinutes < 1 || timeLimitMinutes > 24 * 60)\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"timeLimitMinutes must be between 1 and 1440\",\n });\n }\n\n // Quizzes/tests: disallow file deliverables when late submissions are off\n // (prevents \"late file spam\" after the window closes).\n if (\n (input.type === \"QUIZ\" || input.type === \"TEST\") &&\n acceptFiles &&\n !allowLateSubmissions &&\n input.acceptFiles === true\n ) {\n // Allowed only when teacher explicitly opts into files; keep allowLate false.\n }\n\n if (\n !acceptFiles &&\n !acceptExtendedResponse &&\n !acceptWorksheet\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message:\n \"Assignment must accept at least one deliverable (files, extended response, or worksheet)\",\n });\n }\n\n return {\n acceptFiles,\n acceptExtendedResponse,\n acceptWorksheet,\n singleAttempt,\n allowLateSubmissions,\n timeLimitMinutes,\n };\n}\n\nexport const SIDEBAR_FEATURE_KEYS = [\n \"overview\",\n \"assignments\",\n \"quizzes\",\n \"worksheets\",\n \"grades\",\n \"files\",\n \"modules\",\n \"discussions\",\n \"attendance\",\n \"aiLabs\",\n \"members\",\n \"syllabus\",\n \"settings\",\n] as const;\n\nexport type SidebarFeatureKey = (typeof SIDEBAR_FEATURE_KEYS)[number];\n\nexport type SidebarFeatures = Partial<Record<SidebarFeatureKey, boolean>>;\n\nexport function normalizeSidebarFeatures(\n value: unknown\n): SidebarFeatures | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return null;\n }\n const out: SidebarFeatures = {};\n for (const key of SIDEBAR_FEATURE_KEYS) {\n if (key in (value as object)) {\n out[key] = Boolean((value as Record<string, unknown>)[key]);\n }\n }\n return out;\n}\n"],"names":[],"mappings":"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","debug_id":"f18d9467-4e3c-5922-8f66-5eb168354131"}
@@ -34,8 +34,6 @@ declare const envSchema: z.ZodObject<{
34
34
  INFERENCE_API_BASE_URL: z.ZodOptional<z.ZodString>;
35
35
  OPENAI_API_KEY: z.ZodOptional<z.ZodString>;
36
36
  LOG_MODE: z.ZodDefault<z.ZodEnum<["normal", "verbose", "quiet"]>>;
37
- /** Comma/whitespace-separated user UUIDs allowed to call `admin.*` tRPC (required in production). */
38
- ADMIN_PANEL_USER_IDS: z.ZodOptional<z.ZodString>;
39
37
  }, "strip", z.ZodTypeAny, {
40
38
  NODE_ENV: "test" | "development" | "production";
41
39
  LOG_MODE: "normal" | "verbose" | "quiet";
@@ -65,7 +63,6 @@ declare const envSchema: z.ZodObject<{
65
63
  INFERENCE_API_KEY?: string | undefined;
66
64
  INFERENCE_API_BASE_URL?: string | undefined;
67
65
  OPENAI_API_KEY?: string | undefined;
68
- ADMIN_PANEL_USER_IDS?: string | undefined;
69
66
  }, {
70
67
  DATABASE_URL: string;
71
68
  EMAIL_HOST: string;
@@ -95,7 +92,6 @@ declare const envSchema: z.ZodObject<{
95
92
  INFERENCE_API_KEY?: string | undefined;
96
93
  INFERENCE_API_BASE_URL?: string | undefined;
97
94
  OPENAI_API_KEY?: string | undefined;
98
- ADMIN_PANEL_USER_IDS?: string | undefined;
99
95
  }> | z.ZodObject<{
100
96
  NODE_ENV: z.ZodDefault<z.ZodEnum<["development", "production", "test"]>>;
101
97
  PORT: z.ZodDefault<z.ZodEffects<z.ZodString, number, string>>;
@@ -131,7 +127,6 @@ declare const envSchema: z.ZodObject<{
131
127
  OPENAI_API_KEY: z.ZodOptional<z.ZodString>;
132
128
  INFERENCE_API_BASE_URL: z.ZodOptional<z.ZodString>;
133
129
  LOG_MODE: z.ZodDefault<z.ZodEnum<["normal", "verbose", "quiet"]>>;
134
- ADMIN_PANEL_USER_IDS: z.ZodOptional<z.ZodString>;
135
130
  }, "strip", z.ZodTypeAny, {
136
131
  NODE_ENV: "test" | "development" | "production";
137
132
  LOG_MODE: "normal" | "verbose" | "quiet";
@@ -161,7 +156,6 @@ declare const envSchema: z.ZodObject<{
161
156
  INFERENCE_API_KEY?: string | undefined;
162
157
  INFERENCE_API_BASE_URL?: string | undefined;
163
158
  OPENAI_API_KEY?: string | undefined;
164
- ADMIN_PANEL_USER_IDS?: string | undefined;
165
159
  }, {
166
160
  DATABASE_URL: string;
167
161
  NODE_ENV?: "test" | "development" | "production" | undefined;
@@ -191,7 +185,6 @@ declare const envSchema: z.ZodObject<{
191
185
  INFERENCE_API_KEY?: string | undefined;
192
186
  INFERENCE_API_BASE_URL?: string | undefined;
193
187
  OPENAI_API_KEY?: string | undefined;
194
- ADMIN_PANEL_USER_IDS?: string | undefined;
195
188
  }>;
196
189
  export declare const env: {
197
190
  NODE_ENV: "test" | "development" | "production";
@@ -222,7 +215,6 @@ export declare const env: {
222
215
  INFERENCE_API_KEY?: string | undefined;
223
216
  INFERENCE_API_BASE_URL?: string | undefined;
224
217
  OPENAI_API_KEY?: string | undefined;
225
- ADMIN_PANEL_USER_IDS?: string | undefined;
226
218
  };
227
219
  export type Env = z.infer<typeof envSchema>;
228
220
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"/","sources":["lib/config/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAyGxB,QAAA,MAAM,SAAS;;;;IApEb;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8BH,qGAAqG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAlCrG;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgE6C,CAAC;AA2CnD,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAgB,CAAC;AAGjC,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"/","sources":["lib/config/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsGxB,QAAA,MAAM,SAAS;;;;IAjEb;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAJH;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6D6C,CAAC;AA2CnD,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAgB,CAAC;AAGjC,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC"}
@@ -1,5 +1,5 @@
1
1
 
2
- !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]="8e7c7480-b0bc-5778-9502-7fd6d8f4dae8")}catch(e){}}();
2
+ !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]="da28616b-78b5-5754-bd3a-0b2022498054")}catch(e){}}();
3
3
  import { z } from 'zod';
4
4
  import dotenv from 'dotenv';
5
5
  import { resolve } from 'path';
@@ -61,8 +61,6 @@ const fullSchema = baseSchema.extend({
61
61
  INFERENCE_API_BASE_URL: z.string().url().optional(),
62
62
  OPENAI_API_KEY: z.string().optional(),
63
63
  LOG_MODE: z.enum(['normal', 'verbose', 'quiet']).default('normal'),
64
- /** Comma/whitespace-separated user UUIDs allowed to call `admin.*` tRPC (required in production). */
65
- ADMIN_PANEL_USER_IDS: z.string().optional(),
66
64
  });
67
65
  // Test schema - only require what's needed for tests
68
66
  const testSchema = baseSchema.extend({
@@ -90,7 +88,6 @@ const testSchema = baseSchema.extend({
90
88
  OPENAI_API_KEY: z.string().optional(),
91
89
  INFERENCE_API_BASE_URL: z.string().url().optional(),
92
90
  LOG_MODE: z.enum(['normal', 'verbose', 'quiet']).default('quiet'),
93
- ADMIN_PANEL_USER_IDS: z.string().optional(),
94
91
  });
95
92
  // Use test schema in test mode, full schema otherwise
96
93
  const envSchema = isTest ? testSchema : fullSchema;
@@ -134,4 +131,4 @@ function validateEnv() {
134
131
  // Export validated environment variables
135
132
  export const env = validateEnv();
136
133
  //# sourceMappingURL=env.js.map
137
- //# debugId=8e7c7480-b0bc-5778-9502-7fd6d8f4dae8
134
+ //# debugId=da28616b-78b5-5754-bd3a-0b2022498054
@@ -1 +1 @@
1
- {"version":3,"file":"env.js","sources":["lib/config/env.ts"],"sourceRoot":"/","sourcesContent":["import { z } from 'zod';\nimport dotenv from 'dotenv';\nimport { resolve } from 'path';\nimport { logger } from '../../utils/logger.js';\nimport { isValidCorsOriginPatternList } from './cors.js';\n\n// Determine which env file to load based on NODE_ENV\nconst nodeEnv = process.env.NODE_ENV || 'development';\nconst envFileMap: Record<string, string> = {\n test: '.env.test',\n development: '.env.development',\n production: '.env.production',\n};\n\n// Load the appropriate env file\nconst envFile = envFileMap[nodeEnv] || '.env';\nconst envPath = resolve(process.cwd(), envFile);\n\n// Load environment variables from the correct file\n// First load .env (base), then override with environment-specific file\ndotenv.config(); // Load .env first (base config)\ndotenv.config({ path: envPath, override: true }); // Override with env-specific\n\nconst isTest = nodeEnv === 'test';\nconst isProduction = nodeEnv === 'production';\n\nconst corsAllowedOriginsSchema = z.string().optional();\nconst corsAllowedOriginPatternsSchema = z.string().optional().refine(\n isValidCorsOriginPatternList,\n { message: 'CORS_ALLOWED_ORIGIN_PATTERNS must contain valid regex patterns' }\n);\n\n// Base schema with required vars for all environments\nconst baseSchema = z.object({\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n PORT: z.string().transform(Number).default('3001'),\n DATABASE_URL: z.string().url('DATABASE_URL must be a valid URL'),\n /**\n * Optional BullMQ key prefix. Defaults to `bull:<NODE_ENV>` (see\n * `getBullPrefix`). Set explicitly to isolate queues when multiple\n * databases share one Redis instance (e.g. several devs on one Upstash).\n */\n BULL_PREFIX: z.string().optional(),\n});\n\n// Production/development schema with all required vars\nconst fullSchema = baseSchema.extend({\n NEXT_PUBLIC_APP_URL: z.string().url().default('http://localhost:3000'),\n CORS_ALLOWED_ORIGINS: corsAllowedOriginsSchema,\n CORS_ALLOWED_ORIGIN_PATTERNS: corsAllowedOriginPatternsSchema,\n BACKEND_URL: z.string().url().default('http://localhost:3001'),\n SENTRY_DSN: z.string().url().optional(),\n EMAIL_HOST: z.string().min(1, 'EMAIL_HOST is required'),\n EMAIL_PORT: z.string().transform(Number).default('587'),\n EMAIL_USER: z.string().email('EMAIL_USER must be a valid email'),\n EMAIL_PASS: z.string().min(1, 'EMAIL_PASS is required'),\n EMAIL_DRY_RUN: z.string().optional().default('false'),\n GOOGLE_CLOUD_PROJECT_ID: z.string().min(1, 'GOOGLE_CLOUD_PROJECT_ID is required'),\n GOOGLE_CLOUD_CLIENT_EMAIL: z.string().email('GOOGLE_CLOUD_CLIENT_EMAIL must be a valid email'),\n GOOGLE_CLOUD_PRIVATE_KEY: z.string().min(1, 'GOOGLE_CLOUD_PRIVATE_KEY is required'),\n GOOGLE_CLOUD_BUCKET_NAME: z.string().min(1, 'GOOGLE_CLOUD_BUCKET_NAME is required'),\n PUSHER_APP_ID: z.string().min(1, 'PUSHER_APP_ID is required'),\n PUSHER_KEY: z.string().min(1, 'PUSHER_KEY is required'),\n PUSHER_SECRET: z.string().min(1, 'PUSHER_SECRET is required'),\n PUSHER_CLUSTER: z.string().min(1, 'PUSHER_CLUSTER is required'),\n PUSHER_AUTH_COOKIE_NAME: z.string().optional(), // Cookie name for session token (default: 'token')\n REDIS_URL: z.string().url().optional(), // Redis connection URL (e.g. redis://localhost:6379)\n INFERENCE_API_KEY: z.string().optional(),\n INFERENCE_API_BASE_URL: z.string().url().optional(),\n OPENAI_API_KEY: z.string().optional(),\n LOG_MODE: z.enum(['normal', 'verbose', 'quiet']).default('normal'),\n /** Comma/whitespace-separated user UUIDs allowed to call `admin.*` tRPC (required in production). */\n ADMIN_PANEL_USER_IDS: z.string().optional(),\n});\n\n// Test schema - only require what's needed for tests\nconst testSchema = baseSchema.extend({\n NEXT_PUBLIC_APP_URL: z.string().url().optional().default('http://localhost:3000'),\n CORS_ALLOWED_ORIGINS: corsAllowedOriginsSchema,\n CORS_ALLOWED_ORIGIN_PATTERNS: corsAllowedOriginPatternsSchema,\n BACKEND_URL: z.string().url().optional().default('http://localhost:3001'),\n SENTRY_DSN: z.string().url().optional(),\n EMAIL_HOST: z.string().optional().default('smtp.test.com'),\n EMAIL_PORT: z.string().transform(Number).default('587'),\n EMAIL_USER: z.string().email().optional().default('test@test.com'),\n EMAIL_PASS: z.string().optional().default('test'),\n EMAIL_DRY_RUN: z.string().optional().default('false'),\n GOOGLE_CLOUD_PROJECT_ID: z.string().optional().default('test-project'),\n GOOGLE_CLOUD_CLIENT_EMAIL: z.string().email().optional().default('test@test.iam.gserviceaccount.com'),\n GOOGLE_CLOUD_PRIVATE_KEY: z.string().optional().default('test-key'),\n GOOGLE_CLOUD_BUCKET_NAME: z.string().optional().default('test-bucket'),\n PUSHER_APP_ID: z.string().optional().default('test-app-id'),\n PUSHER_KEY: z.string().optional().default('test-key'),\n PUSHER_SECRET: z.string().optional().default('test-secret'),\n PUSHER_CLUSTER: z.string().optional().default('us2'),\n PUSHER_AUTH_COOKIE_NAME: z.string().optional(),\n REDIS_URL: z.string().url().optional(),\n INFERENCE_API_KEY: z.string().optional(),\n OPENAI_API_KEY: z.string().optional(),\n INFERENCE_API_BASE_URL: z.string().url().optional(),\n LOG_MODE: z.enum(['normal', 'verbose', 'quiet']).default('quiet'),\n ADMIN_PANEL_USER_IDS: z.string().optional(),\n});\n\n// Use test schema in test mode, full schema otherwise\nconst envSchema = isTest ? testSchema : fullSchema;\n\n// Validate environment variables\nfunction validateEnv() {\n try {\n const parsed = envSchema.parse(process.env);\n \n // Only exit on validation failure in production\n if (isProduction && !parsed.DATABASE_URL) {\n logger.error('DATABASE_URL is required in production');\n process.exit(1);\n }\n \n return parsed;\n } catch (error) {\n if (error instanceof z.ZodError) {\n const missingVars = error.errors.map(err => ({\n path: err.path.join('.'),\n message: err.message,\n }));\n \n logger.error('Environment variable validation failed', {\n envFile,\n missingVars,\n });\n \n // Only exit in production - in test/dev, log warning but continue\n if (isProduction) {\n logger.error(`Please check your ${envFile} file and ensure all required variables are set.`);\n process.exit(1);\n } else {\n logger.warn('Continuing with defaults - some features may not work correctly', {\n envFile,\n });\n // Return parsed with defaults for non-production\n return envSchema.parse({ ...process.env });\n }\n }\n throw error;\n }\n}\n\n// Export validated environment variables\nexport const env = validateEnv();\n\n// Type-safe environment access\nexport type Env = z.infer<typeof envSchema>;\n"],"names":[],"mappings":"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","debug_id":"8e7c7480-b0bc-5778-9502-7fd6d8f4dae8"}
1
+ {"version":3,"file":"env.js","sources":["lib/config/env.ts"],"sourceRoot":"/","sourcesContent":["import { z } from 'zod';\nimport dotenv from 'dotenv';\nimport { resolve } from 'path';\nimport { logger } from '../../utils/logger.js';\nimport { isValidCorsOriginPatternList } from './cors.js';\n\n// Determine which env file to load based on NODE_ENV\nconst nodeEnv = process.env.NODE_ENV || 'development';\nconst envFileMap: Record<string, string> = {\n test: '.env.test',\n development: '.env.development',\n production: '.env.production',\n};\n\n// Load the appropriate env file\nconst envFile = envFileMap[nodeEnv] || '.env';\nconst envPath = resolve(process.cwd(), envFile);\n\n// Load environment variables from the correct file\n// First load .env (base), then override with environment-specific file\ndotenv.config(); // Load .env first (base config)\ndotenv.config({ path: envPath, override: true }); // Override with env-specific\n\nconst isTest = nodeEnv === 'test';\nconst isProduction = nodeEnv === 'production';\n\nconst corsAllowedOriginsSchema = z.string().optional();\nconst corsAllowedOriginPatternsSchema = z.string().optional().refine(\n isValidCorsOriginPatternList,\n { message: 'CORS_ALLOWED_ORIGIN_PATTERNS must contain valid regex patterns' }\n);\n\n// Base schema with required vars for all environments\nconst baseSchema = z.object({\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n PORT: z.string().transform(Number).default('3001'),\n DATABASE_URL: z.string().url('DATABASE_URL must be a valid URL'),\n /**\n * Optional BullMQ key prefix. Defaults to `bull:<NODE_ENV>` (see\n * `getBullPrefix`). Set explicitly to isolate queues when multiple\n * databases share one Redis instance (e.g. several devs on one Upstash).\n */\n BULL_PREFIX: z.string().optional(),\n});\n\n// Production/development schema with all required vars\nconst fullSchema = baseSchema.extend({\n NEXT_PUBLIC_APP_URL: z.string().url().default('http://localhost:3000'),\n CORS_ALLOWED_ORIGINS: corsAllowedOriginsSchema,\n CORS_ALLOWED_ORIGIN_PATTERNS: corsAllowedOriginPatternsSchema,\n BACKEND_URL: z.string().url().default('http://localhost:3001'),\n SENTRY_DSN: z.string().url().optional(),\n EMAIL_HOST: z.string().min(1, 'EMAIL_HOST is required'),\n EMAIL_PORT: z.string().transform(Number).default('587'),\n EMAIL_USER: z.string().email('EMAIL_USER must be a valid email'),\n EMAIL_PASS: z.string().min(1, 'EMAIL_PASS is required'),\n EMAIL_DRY_RUN: z.string().optional().default('false'),\n GOOGLE_CLOUD_PROJECT_ID: z.string().min(1, 'GOOGLE_CLOUD_PROJECT_ID is required'),\n GOOGLE_CLOUD_CLIENT_EMAIL: z.string().email('GOOGLE_CLOUD_CLIENT_EMAIL must be a valid email'),\n GOOGLE_CLOUD_PRIVATE_KEY: z.string().min(1, 'GOOGLE_CLOUD_PRIVATE_KEY is required'),\n GOOGLE_CLOUD_BUCKET_NAME: z.string().min(1, 'GOOGLE_CLOUD_BUCKET_NAME is required'),\n PUSHER_APP_ID: z.string().min(1, 'PUSHER_APP_ID is required'),\n PUSHER_KEY: z.string().min(1, 'PUSHER_KEY is required'),\n PUSHER_SECRET: z.string().min(1, 'PUSHER_SECRET is required'),\n PUSHER_CLUSTER: z.string().min(1, 'PUSHER_CLUSTER is required'),\n PUSHER_AUTH_COOKIE_NAME: z.string().optional(), // Cookie name for session token (default: 'token')\n REDIS_URL: z.string().url().optional(), // Redis connection URL (e.g. redis://localhost:6379)\n INFERENCE_API_KEY: z.string().optional(),\n INFERENCE_API_BASE_URL: z.string().url().optional(),\n OPENAI_API_KEY: z.string().optional(),\n LOG_MODE: z.enum(['normal', 'verbose', 'quiet']).default('normal'),\n});\n\n// Test schema - only require what's needed for tests\nconst testSchema = baseSchema.extend({\n NEXT_PUBLIC_APP_URL: z.string().url().optional().default('http://localhost:3000'),\n CORS_ALLOWED_ORIGINS: corsAllowedOriginsSchema,\n CORS_ALLOWED_ORIGIN_PATTERNS: corsAllowedOriginPatternsSchema,\n BACKEND_URL: z.string().url().optional().default('http://localhost:3001'),\n SENTRY_DSN: z.string().url().optional(),\n EMAIL_HOST: z.string().optional().default('smtp.test.com'),\n EMAIL_PORT: z.string().transform(Number).default('587'),\n EMAIL_USER: z.string().email().optional().default('test@test.com'),\n EMAIL_PASS: z.string().optional().default('test'),\n EMAIL_DRY_RUN: z.string().optional().default('false'),\n GOOGLE_CLOUD_PROJECT_ID: z.string().optional().default('test-project'),\n GOOGLE_CLOUD_CLIENT_EMAIL: z.string().email().optional().default('test@test.iam.gserviceaccount.com'),\n GOOGLE_CLOUD_PRIVATE_KEY: z.string().optional().default('test-key'),\n GOOGLE_CLOUD_BUCKET_NAME: z.string().optional().default('test-bucket'),\n PUSHER_APP_ID: z.string().optional().default('test-app-id'),\n PUSHER_KEY: z.string().optional().default('test-key'),\n PUSHER_SECRET: z.string().optional().default('test-secret'),\n PUSHER_CLUSTER: z.string().optional().default('us2'),\n PUSHER_AUTH_COOKIE_NAME: z.string().optional(),\n REDIS_URL: z.string().url().optional(),\n INFERENCE_API_KEY: z.string().optional(),\n OPENAI_API_KEY: z.string().optional(),\n INFERENCE_API_BASE_URL: z.string().url().optional(),\n LOG_MODE: z.enum(['normal', 'verbose', 'quiet']).default('quiet'),\n});\n\n// Use test schema in test mode, full schema otherwise\nconst envSchema = isTest ? testSchema : fullSchema;\n\n// Validate environment variables\nfunction validateEnv() {\n try {\n const parsed = envSchema.parse(process.env);\n \n // Only exit on validation failure in production\n if (isProduction && !parsed.DATABASE_URL) {\n logger.error('DATABASE_URL is required in production');\n process.exit(1);\n }\n \n return parsed;\n } catch (error) {\n if (error instanceof z.ZodError) {\n const missingVars = error.errors.map(err => ({\n path: err.path.join('.'),\n message: err.message,\n }));\n \n logger.error('Environment variable validation failed', {\n envFile,\n missingVars,\n });\n \n // Only exit in production - in test/dev, log warning but continue\n if (isProduction) {\n logger.error(`Please check your ${envFile} file and ensure all required variables are set.`);\n process.exit(1);\n } else {\n logger.warn('Continuing with defaults - some features may not work correctly', {\n envFile,\n });\n // Return parsed with defaults for non-production\n return envSchema.parse({ ...process.env });\n }\n }\n throw error;\n }\n}\n\n// Export validated environment variables\nexport const env = validateEnv();\n\n// Type-safe environment access\nexport type Env = z.infer<typeof envSchema>;\n"],"names":[],"mappings":"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","debug_id":"da28616b-78b5-5754-bd3a-0b2022498054"}
@@ -0,0 +1,19 @@
1
+ import { formatNpr, monthLabel } from "./feePdfFormat.js";
2
+ export { formatNpr, monthLabel };
3
+ export type FeePdfLine = {
4
+ label: string;
5
+ value: string;
6
+ };
7
+ export interface FeePdfDocumentInput {
8
+ schoolName: string;
9
+ title: string;
10
+ subtitle?: string;
11
+ lines: FeePdfLine[];
12
+ footerNote?: string;
13
+ letterhead?: {
14
+ path: string;
15
+ type: string;
16
+ } | null;
17
+ }
18
+ export declare function buildFeePdf(input: FeePdfDocumentInput): Promise<Uint8Array>;
19
+ //# sourceMappingURL=feePdf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feePdf.d.ts","sourceRoot":"/","sources":["lib/feePdf.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;AAEjC,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,IAAI,CAAC;CACV;AAoED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,UAAU,CAAC,CA6FrB"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Fee invoice / receipt PDF generation.
3
+ *
4
+ * Stamps content onto the school's uploaded letterhead when present:
5
+ * - PDF letterhead → reuse first page as background
6
+ * - Image letterhead (png/jpeg) → draw full-page background then text
7
+ * - No letterhead → plain A4 page with school name header
8
+ */
9
+
10
+ !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]="c5f88a88-dde6-5eeb-89ee-f9b02e9e22a1")}catch(e){}}();
11
+ import { PDFDocument, StandardFonts, rgb, degrees, } from "pdf-lib";
12
+ import fontkit from "@pdf-lib/fontkit";
13
+ import { readFileSync } from "fs";
14
+ import { join } from "path";
15
+ import { bucket } from "./googleCloudStorage.js";
16
+ import { logger } from "../utils/logger.js";
17
+ import { formatNpr, monthLabel } from "./feePdfFormat.js";
18
+ export { formatNpr, monthLabel };
19
+ async function loadUnicodeFonts(pdfDoc) {
20
+ pdfDoc.registerFontkit(fontkit);
21
+ const fontDir = join(process.cwd(), "src", "lib");
22
+ try {
23
+ const regular = readFileSync(join(fontDir, "NotoSans-Regular.ttf"));
24
+ const bold = readFileSync(join(fontDir, "NotoSans-Bold.ttf"));
25
+ return {
26
+ regular: await pdfDoc.embedFont(regular),
27
+ bold: await pdfDoc.embedFont(bold),
28
+ };
29
+ }
30
+ catch (err) {
31
+ logger.warn(`feePdf: falling back to Helvetica fonts: ${err}`);
32
+ return {
33
+ regular: await pdfDoc.embedFont(StandardFonts.Helvetica),
34
+ bold: await pdfDoc.embedFont(StandardFonts.HelveticaBold),
35
+ };
36
+ }
37
+ }
38
+ async function loadLetterheadBytes(path) {
39
+ try {
40
+ const [bytes] = await bucket.file(path).download();
41
+ return bytes;
42
+ }
43
+ catch (err) {
44
+ logger.warn(`feePdf: failed to download letterhead ${path}: ${err}`);
45
+ return null;
46
+ }
47
+ }
48
+ async function preparePage(pdfDoc, letterhead) {
49
+ if (letterhead?.path) {
50
+ const bytes = await loadLetterheadBytes(letterhead.path);
51
+ if (bytes) {
52
+ const type = (letterhead.type || "").toLowerCase();
53
+ if (type.includes("pdf") || letterhead.path.toLowerCase().endsWith(".pdf")) {
54
+ const src = await PDFDocument.load(bytes);
55
+ const [page] = await pdfDoc.copyPages(src, [0]);
56
+ pdfDoc.addPage(page);
57
+ return pdfDoc.getPage(0);
58
+ }
59
+ if (type.includes("png") ||
60
+ type.includes("jpeg") ||
61
+ type.includes("jpg")) {
62
+ const page = pdfDoc.addPage();
63
+ const { width, height } = page.getSize();
64
+ try {
65
+ const image = type.includes("png")
66
+ ? await pdfDoc.embedPng(bytes)
67
+ : await pdfDoc.embedJpg(bytes);
68
+ page.drawImage(image, { x: 0, y: 0, width, height });
69
+ return page;
70
+ }
71
+ catch (err) {
72
+ logger.warn(`feePdf: could not embed letterhead image: ${err}`);
73
+ }
74
+ }
75
+ }
76
+ }
77
+ return pdfDoc.addPage();
78
+ }
79
+ export async function buildFeePdf(input) {
80
+ const pdfDoc = await PDFDocument.create();
81
+ const fonts = await loadUnicodeFonts(pdfDoc);
82
+ const page = await preparePage(pdfDoc, input.letterhead);
83
+ const { width, height } = page.getSize();
84
+ const left = 56;
85
+ const right = width - 56;
86
+ let y = height - 120;
87
+ // When no letterhead, print school name as a simple header.
88
+ if (!input.letterhead?.path) {
89
+ page.drawText(input.schoolName, {
90
+ x: left,
91
+ y: height - 56,
92
+ size: 16,
93
+ font: fonts.bold,
94
+ color: rgb(0.1, 0.1, 0.1),
95
+ });
96
+ y = height - 96;
97
+ }
98
+ page.drawText(input.title, {
99
+ x: left,
100
+ y,
101
+ size: 18,
102
+ font: fonts.bold,
103
+ color: rgb(0.08, 0.08, 0.08),
104
+ });
105
+ y -= 22;
106
+ if (input.subtitle) {
107
+ page.drawText(input.subtitle, {
108
+ x: left,
109
+ y,
110
+ size: 11,
111
+ font: fonts.regular,
112
+ color: rgb(0.3, 0.3, 0.3),
113
+ });
114
+ y -= 28;
115
+ }
116
+ else {
117
+ y -= 12;
118
+ }
119
+ page.drawLine({
120
+ start: { x: left, y },
121
+ end: { x: right, y },
122
+ thickness: 0.75,
123
+ color: rgb(0.75, 0.75, 0.75),
124
+ });
125
+ y -= 28;
126
+ for (const line of input.lines) {
127
+ page.drawText(line.label, {
128
+ x: left,
129
+ y,
130
+ size: 10,
131
+ font: fonts.regular,
132
+ color: rgb(0.35, 0.35, 0.35),
133
+ });
134
+ const valueWidth = fonts.bold.widthOfTextAtSize(line.value, 11);
135
+ page.drawText(line.value, {
136
+ x: Math.max(left + 160, right - valueWidth),
137
+ y,
138
+ size: 11,
139
+ font: fonts.bold,
140
+ color: rgb(0.1, 0.1, 0.1),
141
+ });
142
+ y -= 22;
143
+ if (y < 80)
144
+ break;
145
+ }
146
+ if (input.footerNote) {
147
+ page.drawText(input.footerNote, {
148
+ x: left,
149
+ y: 48,
150
+ size: 9,
151
+ font: fonts.regular,
152
+ color: rgb(0.45, 0.45, 0.45),
153
+ });
154
+ }
155
+ // Light watermark so blank letterheads still look intentional.
156
+ page.drawText("Studious", {
157
+ x: width / 2 - 40,
158
+ y: 28,
159
+ size: 8,
160
+ font: fonts.regular,
161
+ color: rgb(0.7, 0.7, 0.7),
162
+ rotate: degrees(0),
163
+ });
164
+ return pdfDoc.save();
165
+ }
166
+ //# sourceMappingURL=feePdf.js.map
167
+ //# debugId=c5f88a88-dde6-5eeb-89ee-f9b02e9e22a1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feePdf.js","sources":["lib/feePdf.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Fee invoice / receipt PDF generation.\n *\n * Stamps content onto the school's uploaded letterhead when present:\n * - PDF letterhead → reuse first page as background\n * - Image letterhead (png/jpeg) → draw full-page background then text\n * - No letterhead → plain A4 page with school name header\n */\nimport {\n PDFDocument,\n StandardFonts,\n rgb,\n degrees,\n} from \"pdf-lib\";\nimport fontkit from \"@pdf-lib/fontkit\";\nimport { readFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { bucket } from \"./googleCloudStorage.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { formatNpr, monthLabel } from \"./feePdfFormat.js\";\n\nexport { formatNpr, monthLabel };\n\nexport type FeePdfLine = { label: string; value: string };\n\nexport interface FeePdfDocumentInput {\n schoolName: string;\n title: string;\n subtitle?: string;\n lines: FeePdfLine[];\n footerNote?: string;\n letterhead?: {\n path: string;\n type: string;\n } | null;\n}\n\nasync function loadUnicodeFonts(pdfDoc: PDFDocument) {\n pdfDoc.registerFontkit(fontkit);\n const fontDir = join(process.cwd(), \"src\", \"lib\");\n try {\n const regular = readFileSync(join(fontDir, \"NotoSans-Regular.ttf\"));\n const bold = readFileSync(join(fontDir, \"NotoSans-Bold.ttf\"));\n return {\n regular: await pdfDoc.embedFont(regular),\n bold: await pdfDoc.embedFont(bold),\n };\n } catch (err) {\n logger.warn(`feePdf: falling back to Helvetica fonts: ${err}`);\n return {\n regular: await pdfDoc.embedFont(StandardFonts.Helvetica),\n bold: await pdfDoc.embedFont(StandardFonts.HelveticaBold),\n };\n }\n}\n\nasync function loadLetterheadBytes(path: string): Promise<Uint8Array | null> {\n try {\n const [bytes] = await bucket.file(path).download();\n return bytes;\n } catch (err) {\n logger.warn(`feePdf: failed to download letterhead ${path}: ${err}`);\n return null;\n }\n}\n\nasync function preparePage(\n pdfDoc: PDFDocument,\n letterhead?: FeePdfDocumentInput[\"letterhead\"]\n) {\n if (letterhead?.path) {\n const bytes = await loadLetterheadBytes(letterhead.path);\n if (bytes) {\n const type = (letterhead.type || \"\").toLowerCase();\n if (type.includes(\"pdf\") || letterhead.path.toLowerCase().endsWith(\".pdf\")) {\n const src = await PDFDocument.load(bytes);\n const [page] = await pdfDoc.copyPages(src, [0]);\n pdfDoc.addPage(page);\n return pdfDoc.getPage(0);\n }\n if (\n type.includes(\"png\") ||\n type.includes(\"jpeg\") ||\n type.includes(\"jpg\")\n ) {\n const page = pdfDoc.addPage();\n const { width, height } = page.getSize();\n try {\n const image = type.includes(\"png\")\n ? await pdfDoc.embedPng(bytes)\n : await pdfDoc.embedJpg(bytes);\n page.drawImage(image, { x: 0, y: 0, width, height });\n return page;\n } catch (err) {\n logger.warn(`feePdf: could not embed letterhead image: ${err}`);\n }\n }\n }\n }\n\n return pdfDoc.addPage();\n}\n\nexport async function buildFeePdf(\n input: FeePdfDocumentInput\n): Promise<Uint8Array> {\n const pdfDoc = await PDFDocument.create();\n const fonts = await loadUnicodeFonts(pdfDoc);\n const page = await preparePage(pdfDoc, input.letterhead);\n const { width, height } = page.getSize();\n\n const left = 56;\n const right = width - 56;\n let y = height - 120;\n\n // When no letterhead, print school name as a simple header.\n if (!input.letterhead?.path) {\n page.drawText(input.schoolName, {\n x: left,\n y: height - 56,\n size: 16,\n font: fonts.bold,\n color: rgb(0.1, 0.1, 0.1),\n });\n y = height - 96;\n }\n\n page.drawText(input.title, {\n x: left,\n y,\n size: 18,\n font: fonts.bold,\n color: rgb(0.08, 0.08, 0.08),\n });\n y -= 22;\n\n if (input.subtitle) {\n page.drawText(input.subtitle, {\n x: left,\n y,\n size: 11,\n font: fonts.regular,\n color: rgb(0.3, 0.3, 0.3),\n });\n y -= 28;\n } else {\n y -= 12;\n }\n\n page.drawLine({\n start: { x: left, y },\n end: { x: right, y },\n thickness: 0.75,\n color: rgb(0.75, 0.75, 0.75),\n });\n y -= 28;\n\n for (const line of input.lines) {\n page.drawText(line.label, {\n x: left,\n y,\n size: 10,\n font: fonts.regular,\n color: rgb(0.35, 0.35, 0.35),\n });\n const valueWidth = fonts.bold.widthOfTextAtSize(line.value, 11);\n page.drawText(line.value, {\n x: Math.max(left + 160, right - valueWidth),\n y,\n size: 11,\n font: fonts.bold,\n color: rgb(0.1, 0.1, 0.1),\n });\n y -= 22;\n if (y < 80) break;\n }\n\n if (input.footerNote) {\n page.drawText(input.footerNote, {\n x: left,\n y: 48,\n size: 9,\n font: fonts.regular,\n color: rgb(0.45, 0.45, 0.45),\n });\n }\n\n // Light watermark so blank letterheads still look intentional.\n page.drawText(\"Studious\", {\n x: width / 2 - 40,\n y: 28,\n size: 8,\n font: fonts.regular,\n color: rgb(0.7, 0.7, 0.7),\n rotate: degrees(0),\n });\n\n return pdfDoc.save();\n}\n"],"names":[],"mappings":"AAAA;;;;;;;GAOG;;;AACH,OAAO,EACL,WAAW,EACX,aAAa,EACb,GAAG,EACH,OAAO,GACR,MAAM,SAAS,CAAC;AACjB,OAAO,OAAO,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;AAgBjC,KAAK,UAAU,gBAAgB,CAAC,MAAmB;IACjD,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAChC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAClD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAC9D,OAAO;YACL,OAAO,EAAE,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC;YACxC,IAAI,EAAE,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;SACnC,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,4CAA4C,GAAG,EAAE,CAAC,CAAC;QAC/D,OAAO;YACL,OAAO,EAAE,MAAM,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,SAAS,CAAC;YACxD,IAAI,EAAE,MAAM,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,aAAa,CAAC;SAC1D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,IAAY;IAC7C,IAAI,CAAC;QACH,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;QACnD,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,yCAAyC,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC;QACrE,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,MAAmB,EACnB,UAA8C;IAE9C,IAAI,UAAU,EAAE,IAAI,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACzD,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;YACnD,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3E,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC1C,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChD,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBACrB,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC;YACD,IACE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBACpB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;gBACrB,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EACpB,CAAC;gBACD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9B,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;gBACzC,IAAI,CAAC;oBACH,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;wBAChC,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;wBAC9B,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;oBACjC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;oBACrD,OAAO,IAAI,CAAC;gBACd,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,IAAI,CAAC,6CAA6C,GAAG,EAAE,CAAC,CAAC;gBAClE,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAA0B;IAE1B,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC;IAC1C,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACzD,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAEzC,MAAM,IAAI,GAAG,EAAE,CAAC;IAChB,MAAM,KAAK,GAAG,KAAK,GAAG,EAAE,CAAC;IACzB,IAAI,CAAC,GAAG,MAAM,GAAG,GAAG,CAAC;IAErB,4DAA4D;IAC5D,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE;YAC9B,CAAC,EAAE,IAAI;YACP,CAAC,EAAE,MAAM,GAAG,EAAE;YACd,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;SAC1B,CAAC,CAAC;QACH,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE;QACzB,CAAC,EAAE,IAAI;QACP,CAAC;QACD,IAAI,EAAE,EAAE;QACR,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;KAC7B,CAAC,CAAC;IACH,CAAC,IAAI,EAAE,CAAC;IAER,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACnB,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE;YAC5B,CAAC,EAAE,IAAI;YACP,CAAC;YACD,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,KAAK,CAAC,OAAO;YACnB,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;SAC1B,CAAC,CAAC;QACH,CAAC,IAAI,EAAE,CAAC;IACV,CAAC;SAAM,CAAC;QACN,CAAC,IAAI,EAAE,CAAC;IACV,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC;QACZ,KAAK,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE;QACrB,GAAG,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;QACpB,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;KAC7B,CAAC,CAAC;IACH,CAAC,IAAI,EAAE,CAAC;IAER,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE;YACxB,CAAC,EAAE,IAAI;YACP,CAAC;YACD,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,KAAK,CAAC,OAAO;YACnB,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;SAC7B,CAAC,CAAC;QACH,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE;YACxB,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,EAAE,KAAK,GAAG,UAAU,CAAC;YAC3C,CAAC;YACD,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;SAC1B,CAAC,CAAC;QACH,CAAC,IAAI,EAAE,CAAC;QACR,IAAI,CAAC,GAAG,EAAE;YAAE,MAAM;IACpB,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,UAAU,EAAE;YAC9B,CAAC,EAAE,IAAI;YACP,CAAC,EAAE,EAAE;YACL,IAAI,EAAE,CAAC;YACP,IAAI,EAAE,KAAK,CAAC,OAAO;YACnB,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;SAC7B,CAAC,CAAC;IACL,CAAC;IAED,+DAA+D;IAC/D,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE;QACxB,CAAC,EAAE,KAAK,GAAG,CAAC,GAAG,EAAE;QACjB,CAAC,EAAE,EAAE;QACL,IAAI,EAAE,CAAC;QACP,IAAI,EAAE,KAAK,CAAC,OAAO;QACnB,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;QACzB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;KACnB,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC","debug_id":"c5f88a88-dde6-5eeb-89ee-f9b02e9e22a1"}
@@ -0,0 +1,4 @@
1
+ /** Pure formatting helpers for fee invoices / receipts (no I/O). */
2
+ export declare function formatNpr(amount: number): string;
3
+ export declare function monthLabel(year: number, month: number): string;
4
+ //# sourceMappingURL=feePdfFormat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feePdfFormat.d.ts","sourceRoot":"/","sources":["lib/feePdfFormat.ts"],"names":[],"mappings":"AAAA,oEAAoE;AAEpE,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,UAEvC;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,UAOrD"}
@@ -0,0 +1,16 @@
1
+ /** Pure formatting helpers for fee invoices / receipts (no I/O). */
2
+
3
+ !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]="2db5c56e-f0c1-5234-906c-b7f44d03e976")}catch(e){}}();
4
+ export function formatNpr(amount) {
5
+ return `NPR ${amount.toLocaleString("en-NP")}`;
6
+ }
7
+ export function monthLabel(year, month) {
8
+ const d = new Date(Date.UTC(year, month - 1, 1));
9
+ return d.toLocaleString("en-GB", {
10
+ month: "long",
11
+ year: "numeric",
12
+ timeZone: "UTC",
13
+ });
14
+ }
15
+ //# sourceMappingURL=feePdfFormat.js.map
16
+ //# debugId=2db5c56e-f0c1-5234-906c-b7f44d03e976
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feePdfFormat.js","sources":["lib/feePdfFormat.ts"],"sourceRoot":"/","sourcesContent":["/** Pure formatting helpers for fee invoices / receipts (no I/O). */\n\nexport function formatNpr(amount: number) {\n return `NPR ${amount.toLocaleString(\"en-NP\")}`;\n}\n\nexport function monthLabel(year: number, month: number) {\n const d = new Date(Date.UTC(year, month - 1, 1));\n return d.toLocaleString(\"en-GB\", {\n month: \"long\",\n year: \"numeric\",\n timeZone: \"UTC\",\n });\n}\n"],"names":[],"mappings":"AAAA,oEAAoE;;;AAEpE,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,OAAO,OAAO,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAa;IACpD,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjD,OAAO,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE;QAC/B,KAAK,EAAE,MAAM;QACb,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;AACL,CAAC","debug_id":"2db5c56e-f0c1-5234-906c-b7f44d03e976"}