@studious-lms/server 1.22.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 +193 -0
  82. package/dist/models/school.d.ts.map +1 -1
  83. package/dist/models/school.js +80 -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 +11605 -7133
  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 +476 -6
  138. package/dist/routers/school.d.ts.map +1 -1
  139. package/dist/routers/school.js +130 -4
  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 +122 -3
  214. package/dist/services/school.d.ts.map +1 -1
  215. package/dist/services/school.js +179 -47
  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 +95 -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 +184 -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 +243 -45
  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
@@ -11,6 +11,9 @@ export declare function getCurrentSchool(userId: string, schoolId?: string): Pro
11
11
  logoId: string | null;
12
12
  logoPath: string | null;
13
13
  logoType: string | null;
14
+ letterheadFileId: string | null;
15
+ letterheadPath: string | null;
16
+ letterheadType: string | null;
14
17
  } | null;
15
18
  activeRole: import(".prisma/client").$Enums.SchoolMembershipRole | null;
16
19
  memberships: ({
@@ -21,12 +24,19 @@ export declare function getCurrentSchool(userId: string, schoolId?: string): Pro
21
24
  name: string;
22
25
  description: string | null;
23
26
  logoId: string | null;
27
+ letterheadFileId: string | null;
24
28
  subdomain: string | null;
25
29
  logo: {
26
30
  path: string;
27
31
  type: string;
28
32
  id: string;
29
33
  } | null;
34
+ letterhead: {
35
+ path: string;
36
+ type: string;
37
+ id: string;
38
+ name: string;
39
+ } | null;
30
40
  };
31
41
  } & {
32
42
  status: import(".prisma/client").$Enums.SchoolMembershipStatus;
@@ -61,6 +71,7 @@ export declare function createSchoolRecord(userId: string, input: {
61
71
  name: string;
62
72
  description: string | null;
63
73
  logoId: string | null;
74
+ letterheadFileId: string | null;
64
75
  subdomain: string | null;
65
76
  };
66
77
  }>;
@@ -70,12 +81,14 @@ export declare function updateSchoolDetails(userId: string, input: {
70
81
  description?: string | null;
71
82
  subdomain?: string | null;
72
83
  logoId?: string | null;
84
+ letterheadFileId?: string | null;
73
85
  }): Promise<{
74
86
  school: {
75
87
  id: string;
76
88
  name: string;
77
89
  description: string | null;
78
90
  logoId: string | null;
91
+ letterheadFileId: string | null;
79
92
  subdomain: string | null;
80
93
  };
81
94
  }>;
@@ -88,6 +101,8 @@ export declare function getAdminOverview(userId: string, schoolId: string): Prom
88
101
  ADMIN: number;
89
102
  TEACHER: number;
90
103
  STUDENT: number;
104
+ ACCOUNTANT: number;
105
+ PARENT: number;
91
106
  classes: number;
92
107
  members: number;
93
108
  pendingInvites: number;
@@ -110,6 +125,23 @@ export declare function getAdminOverview(userId: string, schoolId: string): Prom
110
125
  expiresAt: Date;
111
126
  role: import(".prisma/client").$Enums.SchoolMembershipRole;
112
127
  }[];
128
+ recentMembers: {
129
+ status: import(".prisma/client").$Enums.SchoolMembershipStatus;
130
+ user: {
131
+ id: string;
132
+ username: string;
133
+ email: string;
134
+ profile: {
135
+ profilePicture: string | null;
136
+ displayName: string | null;
137
+ profilePictureThumbnail: string | null;
138
+ } | null;
139
+ };
140
+ id: string;
141
+ createdAt: Date;
142
+ updatedAt: Date;
143
+ role: import(".prisma/client").$Enums.SchoolMembershipRole;
144
+ }[];
113
145
  };
114
146
  }>;
115
147
  export declare function listClasses(userId: string, schoolId: string): Promise<{
@@ -144,6 +176,7 @@ export declare function listClasses(userId: string, schoolId: string): Promise<{
144
176
  discussionsEnabled: boolean;
145
177
  studentsCanCreateDiscussions: boolean;
146
178
  studentsCanReply: boolean;
179
+ sidebarFeatures: import("@prisma/client/runtime/library.js").JsonValue | null;
147
180
  treatMissingAsZero: boolean;
148
181
  gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
149
182
  })[];
@@ -193,6 +226,7 @@ export declare function createSchoolClass(userId: string, input: {
193
226
  discussionsEnabled: boolean;
194
227
  studentsCanCreateDiscussions: boolean;
195
228
  studentsCanReply: boolean;
229
+ sidebarFeatures: import("@prisma/client/runtime/library.js").JsonValue | null;
196
230
  treatMissingAsZero: boolean;
197
231
  gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
198
232
  };
@@ -246,6 +280,83 @@ export declare function updateMemberRole(userId: string, input: {
246
280
  role: import(".prisma/client").$Enums.SchoolMembershipRole;
247
281
  };
248
282
  }>;
283
+ export declare function getMemberDetail(userId: string, input: {
284
+ schoolId: string;
285
+ memberUserId: string;
286
+ }): Promise<{
287
+ membership: {
288
+ status: import(".prisma/client").$Enums.SchoolMembershipStatus;
289
+ user: {
290
+ id: string;
291
+ username: string;
292
+ email: string;
293
+ profile: {
294
+ profilePicture: string | null;
295
+ displayName: string | null;
296
+ profilePictureThumbnail: string | null;
297
+ } | null;
298
+ };
299
+ id: string;
300
+ createdAt: Date;
301
+ updatedAt: Date;
302
+ role: import(".prisma/client").$Enums.SchoolMembershipRole;
303
+ };
304
+ teachingClasses: {
305
+ id: string;
306
+ name: string;
307
+ section: string;
308
+ subject: string;
309
+ color: string | null;
310
+ teacherCount: number;
311
+ studentCount: number;
312
+ }[];
313
+ enrolledClasses: {
314
+ id: string;
315
+ name: string;
316
+ section: string;
317
+ subject: string;
318
+ color: string | null;
319
+ teacherCount: number;
320
+ studentCount: number;
321
+ }[];
322
+ }>;
323
+ export declare function bulkUpdateMemberRoles(userId: string, input: {
324
+ schoolId: string;
325
+ memberUserIds: string[];
326
+ role: SchoolMembershipRole;
327
+ }): Promise<{
328
+ updatedCount: number;
329
+ }>;
330
+ export declare function bulkRemoveMembers(userId: string, input: {
331
+ schoolId: string;
332
+ memberUserIds: string[];
333
+ }): Promise<{
334
+ removedCount: number;
335
+ }>;
336
+ export declare function deleteSchoolClass(userId: string, input: {
337
+ schoolId: string;
338
+ classId: string;
339
+ }): Promise<{
340
+ deletedClass: {
341
+ id: string;
342
+ };
343
+ }>;
344
+ export declare function updateClassMembers(userId: string, input: {
345
+ schoolId: string;
346
+ classId: string;
347
+ addTeacherIds?: string[];
348
+ removeTeacherIds?: string[];
349
+ addStudentIds?: string[];
350
+ removeStudentIds?: string[];
351
+ }): Promise<{
352
+ class: {
353
+ id: string;
354
+ _count: {
355
+ teachers: number;
356
+ students: number;
357
+ };
358
+ };
359
+ }>;
249
360
  export declare function removeMember(userId: string, input: {
250
361
  schoolId: string;
251
362
  memberUserId: string;
@@ -290,7 +401,7 @@ export declare function confirmSchoolAssetUpload(userId: string, fileId: string,
290
401
  */
291
402
  export declare function listMemberIds(userId: string, schoolId: string): Promise<string[]>;
292
403
  /** Search active institution members by username, displayName, or email. */
293
- export declare function searchMembers(userId: string, schoolId: string, query: string, limit?: number, role?: "ADMIN" | "TEACHER" | "STUDENT"): Promise<{
404
+ export declare function searchMembers(userId: string, schoolId: string, query: string, limit?: number, role?: SchoolMembershipRole): Promise<{
294
405
  id: string;
295
406
  username: string;
296
407
  profile: {
@@ -360,13 +471,17 @@ export declare function getStudentTimeline(userId: string, input: {
360
471
  }[];
361
472
  attendance: {
362
473
  id: string;
474
+ recordId: string;
363
475
  date: Date;
364
476
  class: {
365
477
  id: string;
366
478
  name: string;
367
479
  color: string | null;
368
480
  };
369
- status: "PRESENT" | "LATE" | "ABSENT";
481
+ status: import(".prisma/client").$Enums.AttendanceStatus;
482
+ note: string | null;
483
+ lockedFromExcusal: boolean;
484
+ reason: string | null;
370
485
  }[];
371
486
  }>;
372
487
  /**
@@ -437,13 +552,17 @@ export declare function getMemberTimeline(userId: string, input: {
437
552
  }[];
438
553
  attendance: {
439
554
  id: string;
555
+ recordId: string;
440
556
  date: Date;
441
557
  class: {
442
558
  id: string;
443
559
  name: string;
444
560
  color: string | null;
445
561
  };
446
- status: "PRESENT" | "LATE" | "ABSENT";
562
+ status: import(".prisma/client").$Enums.AttendanceStatus;
563
+ note: string | null;
564
+ lockedFromExcusal: boolean;
565
+ reason: string | null;
447
566
  }[];
448
567
  classes?: undefined;
449
568
  } | {
@@ -1 +1 @@
1
- {"version":3,"file":"school.d.ts","sourceRoot":"/","sources":["services/school.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"school.d.ts","sourceRoot":"/","sources":["services/school.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  * School service - school admin workflow, memberships, and invites.
3
3
  */
4
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]="e6792872-7695-5c7d-85ab-253749211d95")}catch(e){}}();
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]="63d2144c-911d-56ab-8693-f7615b26334e")}catch(e){}}();
6
6
  import { SessionType } from "@prisma/client";
7
7
  import { TRPCError } from "@trpc/server";
8
8
  import { hash } from "bcryptjs";
@@ -14,7 +14,7 @@ import { generateUniqueUsername } from "../utils/generateUsername.js";
14
14
  import { logger } from "../utils/logger.js";
15
15
  import { createSession, createUser, findUserByEmail, findUserById, findUserByUsername, } from "../models/auth.js";
16
16
  import { upsertUserProfile } from "../models/user.js";
17
- import { acceptSchoolInvite, countActiveAdmins, createSchoolInvite, createSchoolWithAdmin, findActiveSchoolAdminMembership, findPendingInviteByToken, findSchoolMembership, getSchoolOverview, listSchoolClasses, listSchoolInvites, listSchoolMembers, listUserSchoolMemberships, removeSchoolMembershipPreservingContent, revokeSchoolInvite, updateSchoolMembershipRole, updateSchoolRecord, upsertSchoolMembership, } from "../models/school.js";
17
+ import { acceptSchoolInvite, countActiveAdmins, createSchoolInvite, createSchoolWithAdmin, deleteSchoolClassRecord, findActiveSchoolAdminMembership, findPendingInviteByToken, findSchoolClass, findSchoolMembership, getSchoolMemberDetail, getSchoolOverview, listActiveSchoolMemberships, listMemberClasses, listRecentSchoolMembers, listSchoolClasses, listSchoolInvites, listSchoolMembers, listUserSchoolMemberships, removeSchoolMembershipPreservingContent, revokeSchoolInvite, updateSchoolClassMembers, updateSchoolMembershipRole, updateSchoolRecord, upsertSchoolMembership, } from "../models/school.js";
18
18
  async function requireSchoolAdmin(schoolId, userId) {
19
19
  const membership = await findActiveSchoolAdminMembership(schoolId, userId);
20
20
  if (!membership) {
@@ -51,6 +51,9 @@ export async function getCurrentSchool(userId, schoolId) {
51
51
  // signed URL through trpc.file.getSignedUrl when needed.
52
52
  logoPath: activeMembership.school.logo?.path ?? null,
53
53
  logoType: activeMembership.school.logo?.type ?? null,
54
+ letterheadFileId: activeMembership.school.letterheadFileId ?? null,
55
+ letterheadPath: activeMembership.school.letterhead?.path ?? null,
56
+ letterheadType: activeMembership.school.letterhead?.type ?? null,
54
57
  }
55
58
  : null;
56
59
  return {
@@ -78,19 +81,29 @@ export async function updateSchoolDetails(userId, input) {
78
81
  description: input.description,
79
82
  subdomain: input.subdomain,
80
83
  logoId: input.logoId,
84
+ letterheadFileId: input.letterheadFileId,
81
85
  });
82
86
  return { school: updated };
83
87
  }
84
88
  export async function getAdminOverview(userId, schoolId) {
85
89
  await requireSchoolAdmin(schoolId, userId);
86
- const school = await getSchoolOverview(schoolId);
90
+ const [school, recentMembers] = await Promise.all([
91
+ getSchoolOverview(schoolId),
92
+ listRecentSchoolMembers(schoolId),
93
+ ]);
87
94
  if (!school) {
88
95
  throw new TRPCError({ code: "NOT_FOUND", message: "School not found" });
89
96
  }
90
97
  const roleCounts = school.memberships.reduce((counts, membership) => {
91
98
  counts[membership.role] += 1;
92
99
  return counts;
93
- }, { ADMIN: 0, TEACHER: 0, STUDENT: 0 });
100
+ }, {
101
+ ADMIN: 0,
102
+ TEACHER: 0,
103
+ STUDENT: 0,
104
+ ACCOUNTANT: 0,
105
+ PARENT: 0,
106
+ });
94
107
  return {
95
108
  school: {
96
109
  id: school.id,
@@ -104,6 +117,7 @@ export async function getAdminOverview(userId, schoolId) {
104
117
  },
105
118
  recentClasses: school.classes,
106
119
  recentInvites: school.invites,
120
+ recentMembers,
107
121
  },
108
122
  };
109
123
  }
@@ -176,6 +190,114 @@ export async function updateMemberRole(userId, input) {
176
190
  const membership = await updateSchoolMembershipRole(input.schoolId, input.memberUserId, input.role);
177
191
  return { membership };
178
192
  }
193
+ export async function getMemberDetail(userId, input) {
194
+ await requireSchoolAdmin(input.schoolId, userId);
195
+ const membership = await getSchoolMemberDetail(input.schoolId, input.memberUserId);
196
+ if (!membership) {
197
+ throw new TRPCError({ code: "NOT_FOUND", message: "Member not found" });
198
+ }
199
+ const [teachingClasses, enrolledClasses] = await listMemberClasses(input.schoolId, input.memberUserId);
200
+ const toClassSummary = (cls) => ({
201
+ id: cls.id,
202
+ name: cls.name,
203
+ section: cls.section,
204
+ subject: cls.subject,
205
+ color: cls.color,
206
+ teacherCount: cls._count.teachers,
207
+ studentCount: cls._count.students,
208
+ });
209
+ return {
210
+ membership,
211
+ teachingClasses: teachingClasses.map(toClassSummary),
212
+ enrolledClasses: enrolledClasses.map(toClassSummary),
213
+ };
214
+ }
215
+ export async function bulkUpdateMemberRoles(userId, input) {
216
+ await requireSchoolAdmin(input.schoolId, userId);
217
+ const memberships = await listActiveSchoolMemberships(input.schoolId, input.memberUserIds);
218
+ if (memberships.length === 0) {
219
+ throw new TRPCError({ code: "NOT_FOUND", message: "No members found" });
220
+ }
221
+ if (input.role !== "ADMIN") {
222
+ const demotedAdmins = memberships.filter((m) => m.role === "ADMIN").length;
223
+ if (demotedAdmins > 0) {
224
+ const adminCount = await countActiveAdmins(input.schoolId);
225
+ if (adminCount - demotedAdmins < 1) {
226
+ throw new TRPCError({
227
+ code: "BAD_REQUEST",
228
+ message: "A school must have at least one active admin",
229
+ });
230
+ }
231
+ }
232
+ }
233
+ await prisma.schoolMembership.updateMany({
234
+ where: {
235
+ schoolId: input.schoolId,
236
+ userId: { in: memberships.map((m) => m.userId) },
237
+ status: "ACTIVE",
238
+ },
239
+ data: { role: input.role },
240
+ });
241
+ return { updatedCount: memberships.length };
242
+ }
243
+ export async function bulkRemoveMembers(userId, input) {
244
+ await requireSchoolAdmin(input.schoolId, userId);
245
+ const targetIds = input.memberUserIds.filter((id) => id !== userId);
246
+ const memberships = await listActiveSchoolMemberships(input.schoolId, targetIds);
247
+ if (memberships.length === 0) {
248
+ throw new TRPCError({ code: "NOT_FOUND", message: "No members found" });
249
+ }
250
+ const removedAdmins = memberships.filter((m) => m.role === "ADMIN").length;
251
+ if (removedAdmins > 0) {
252
+ const adminCount = await countActiveAdmins(input.schoolId);
253
+ if (adminCount - removedAdmins < 1) {
254
+ throw new TRPCError({
255
+ code: "BAD_REQUEST",
256
+ message: "A school must have at least one active admin",
257
+ });
258
+ }
259
+ }
260
+ for (const membership of memberships) {
261
+ await removeSchoolMembershipPreservingContent(input.schoolId, membership.userId);
262
+ }
263
+ return { removedCount: memberships.length };
264
+ }
265
+ export async function deleteSchoolClass(userId, input) {
266
+ await requireSchoolAdmin(input.schoolId, userId);
267
+ const cls = await findSchoolClass(input.schoolId, input.classId);
268
+ if (!cls) {
269
+ throw new TRPCError({ code: "NOT_FOUND", message: "Class not found" });
270
+ }
271
+ await deleteSchoolClassRecord(input.schoolId, input.classId);
272
+ return { deletedClass: { id: input.classId } };
273
+ }
274
+ export async function updateClassMembers(userId, input) {
275
+ await requireSchoolAdmin(input.schoolId, userId);
276
+ const cls = await findSchoolClass(input.schoolId, input.classId);
277
+ if (!cls) {
278
+ throw new TRPCError({ code: "NOT_FOUND", message: "Class not found" });
279
+ }
280
+ const addTeacherIds = input.addTeacherIds ?? [];
281
+ const addStudentIds = input.addStudentIds ?? [];
282
+ const addedIds = [...addTeacherIds, ...addStudentIds];
283
+ if (addedIds.length > 0) {
284
+ const memberships = await listActiveSchoolMemberships(input.schoolId, addedIds);
285
+ const activeIds = new Set(memberships.map((m) => m.userId));
286
+ if (addedIds.some((id) => !activeIds.has(id))) {
287
+ throw new TRPCError({
288
+ code: "BAD_REQUEST",
289
+ message: "All added users must be active members of this school",
290
+ });
291
+ }
292
+ }
293
+ const updated = await updateSchoolClassMembers(input.classId, {
294
+ addTeacherIds,
295
+ removeTeacherIds: input.removeTeacherIds ?? [],
296
+ addStudentIds,
297
+ removeStudentIds: input.removeStudentIds ?? [],
298
+ });
299
+ return { class: updated };
300
+ }
179
301
  export async function removeMember(userId, input) {
180
302
  await requireSchoolAdmin(input.schoolId, userId);
181
303
  const current = await findSchoolMembership(input.schoolId, input.memberUserId);
@@ -372,35 +494,40 @@ export async function getStudentTimeline(userId, input) {
372
494
  },
373
495
  orderBy: { dueDate: "asc" },
374
496
  }),
375
- prisma.attendance.findMany({
497
+ prisma.attendanceRecord.findMany({
376
498
  where: {
377
- OR: [
378
- { present: { some: { id: input.studentId } } },
379
- { late: { some: { id: input.studentId } } },
380
- { absent: { some: { id: input.studentId } } },
381
- ],
382
- ...(hasRange ? { date: range } : {}),
499
+ studentId: input.studentId,
500
+ attendance: {
501
+ class: { schoolId: input.schoolId },
502
+ ...(hasRange ? { date: range } : {}),
503
+ },
383
504
  },
384
505
  select: {
385
506
  id: true,
386
- date: true,
387
- class: { select: { id: true, name: true, color: true } },
388
- present: { where: { id: input.studentId }, select: { id: true } },
389
- late: { where: { id: input.studentId }, select: { id: true } },
390
- absent: { where: { id: input.studentId }, select: { id: true } },
507
+ status: true,
508
+ note: true,
509
+ excusedByExcusalId: true,
510
+ attendance: {
511
+ select: {
512
+ id: true,
513
+ date: true,
514
+ class: { select: { id: true, name: true, color: true } },
515
+ },
516
+ },
517
+ excusedByExcusal: { select: { id: true, reason: true } },
391
518
  },
392
- orderBy: { date: "asc" },
519
+ orderBy: { attendance: { date: "asc" } },
393
520
  }),
394
521
  ]);
395
522
  const attendanceShaped = attendance.map((record) => ({
396
- id: record.id,
397
- date: record.date,
398
- class: record.class,
399
- status: record.present.length
400
- ? "PRESENT"
401
- : record.late.length
402
- ? "LATE"
403
- : "ABSENT",
523
+ id: record.attendance.id,
524
+ recordId: record.id,
525
+ date: record.attendance.date,
526
+ class: record.attendance.class,
527
+ status: record.status,
528
+ note: record.note,
529
+ lockedFromExcusal: Boolean(record.excusedByExcusalId),
530
+ reason: record.excusedByExcusal?.reason ?? null,
404
531
  }));
405
532
  return {
406
533
  student: studentMembership.user,
@@ -524,35 +651,40 @@ export async function getMemberTimeline(userId, input) {
524
651
  },
525
652
  orderBy: { dueDate: "asc" },
526
653
  }),
527
- prisma.attendance.findMany({
654
+ prisma.attendanceRecord.findMany({
528
655
  where: {
529
- OR: [
530
- { present: { some: { id: input.memberId } } },
531
- { late: { some: { id: input.memberId } } },
532
- { absent: { some: { id: input.memberId } } },
533
- ],
534
- ...(hasRange ? { date: range } : {}),
656
+ studentId: input.memberId,
657
+ attendance: {
658
+ class: { schoolId: input.schoolId },
659
+ ...(hasRange ? { date: range } : {}),
660
+ },
535
661
  },
536
662
  select: {
537
663
  id: true,
538
- date: true,
539
- class: { select: { id: true, name: true, color: true } },
540
- present: { where: { id: input.memberId }, select: { id: true } },
541
- late: { where: { id: input.memberId }, select: { id: true } },
542
- absent: { where: { id: input.memberId }, select: { id: true } },
664
+ status: true,
665
+ note: true,
666
+ excusedByExcusalId: true,
667
+ attendance: {
668
+ select: {
669
+ id: true,
670
+ date: true,
671
+ class: { select: { id: true, name: true, color: true } },
672
+ },
673
+ },
674
+ excusedByExcusal: { select: { id: true, reason: true } },
543
675
  },
544
- orderBy: { date: "asc" },
676
+ orderBy: { attendance: { date: "asc" } },
545
677
  }),
546
678
  ]);
547
679
  const attendanceShaped = attendance.map((record) => ({
548
- id: record.id,
549
- date: record.date,
550
- class: record.class,
551
- status: record.present.length
552
- ? "PRESENT"
553
- : record.late.length
554
- ? "LATE"
555
- : "ABSENT",
680
+ id: record.attendance.id,
681
+ recordId: record.id,
682
+ date: record.attendance.date,
683
+ class: record.attendance.class,
684
+ status: record.status,
685
+ note: record.note,
686
+ lockedFromExcusal: Boolean(record.excusedByExcusalId),
687
+ reason: record.excusedByExcusal?.reason ?? null,
556
688
  }));
557
689
  return {
558
690
  role: "STUDENT",
@@ -753,4 +885,4 @@ export async function acceptInvite(userId, token) {
753
885
  return { membership };
754
886
  }
755
887
  //# sourceMappingURL=school.js.map
756
- //# debugId=e6792872-7695-5c7d-85ab-253749211d95
888
+ //# debugId=63d2144c-911d-56ab-8693-f7615b26334e