@studious-lms/server 1.24.0 → 1.26.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 (117) hide show
  1. package/data/curriculum/nepal-cdc-g4-g5/subjects/english.json +4 -4
  2. package/data/curriculum/nepal-cdc-g4-g5/subjects/mathematics.json +1 -1
  3. package/data/curriculum/nepal-cdc-g4-g5/subjects/science-technology.json +4 -4
  4. package/data/curriculum/nepal-cdc-g4-g5/subjects/social-studies.json +4 -4
  5. package/dist/models/attendance.d.ts.map +1 -1
  6. package/dist/models/attendance.js +17 -14
  7. package/dist/models/attendance.js.map +1 -1
  8. package/dist/models/marketplace.d.ts +37 -0
  9. package/dist/models/marketplace.d.ts.map +1 -1
  10. package/dist/models/marketplace.js +10 -2
  11. package/dist/models/marketplace.js.map +1 -1
  12. package/dist/models/module.d.ts +228 -0
  13. package/dist/models/module.d.ts.map +1 -0
  14. package/dist/models/module.js +69 -0
  15. package/dist/models/module.js.map +1 -0
  16. package/dist/models/moduleItem.d.ts +5 -1
  17. package/dist/models/moduleItem.d.ts.map +1 -1
  18. package/dist/models/moduleItem.js +19 -4
  19. package/dist/models/moduleItem.js.map +1 -1
  20. package/dist/pipelines/gradeWorksheet.d.ts.map +1 -1
  21. package/dist/pipelines/gradeWorksheet.js +4 -2
  22. package/dist/pipelines/gradeWorksheet.js.map +1 -1
  23. package/dist/routers/_app.d.ts +768 -6
  24. package/dist/routers/_app.d.ts.map +1 -1
  25. package/dist/routers/_app.js +4 -2
  26. package/dist/routers/_app.js.map +1 -1
  27. package/dist/routers/fees.d.ts +1 -1
  28. package/dist/routers/marketplace.d.ts +41 -0
  29. package/dist/routers/marketplace.d.ts.map +1 -1
  30. package/dist/routers/marketplace.js +13 -3
  31. package/dist/routers/marketplace.js.map +1 -1
  32. package/dist/routers/module.d.ts +345 -0
  33. package/dist/routers/module.d.ts.map +1 -0
  34. package/dist/routers/module.js +82 -0
  35. package/dist/routers/module.js.map +1 -0
  36. package/dist/routers/moduleItem.d.ts.map +1 -1
  37. package/dist/routers/moduleItem.js +7 -6
  38. package/dist/routers/moduleItem.js.map +1 -1
  39. package/dist/routers/parent.d.ts +2 -1
  40. package/dist/routers/parent.d.ts.map +1 -1
  41. package/dist/routers/school.d.ts +1 -1
  42. package/dist/routers/school.d.ts.map +1 -1
  43. package/dist/routers/school.js +14 -4
  44. package/dist/routers/school.js.map +1 -1
  45. package/dist/services/academicCalendar.d.ts.map +1 -1
  46. package/dist/services/academicCalendar.js +30 -2
  47. package/dist/services/academicCalendar.js.map +1 -1
  48. package/dist/services/assignment.d.ts.map +1 -1
  49. package/dist/services/assignment.js +8 -3
  50. package/dist/services/assignment.js.map +1 -1
  51. package/dist/services/attendance.d.ts +6 -2
  52. package/dist/services/attendance.d.ts.map +1 -1
  53. package/dist/services/attendance.js +8 -4
  54. package/dist/services/attendance.js.map +1 -1
  55. package/dist/services/class.d.ts +14 -9
  56. package/dist/services/class.d.ts.map +1 -1
  57. package/dist/services/class.js +4 -4
  58. package/dist/services/class.js.map +1 -1
  59. package/dist/services/fees.d.ts.map +1 -1
  60. package/dist/services/fees.js +24 -4
  61. package/dist/services/fees.js.map +1 -1
  62. package/dist/services/feesAuthz.d.ts +2 -5
  63. package/dist/services/feesAuthz.d.ts.map +1 -1
  64. package/dist/services/feesAuthz.js +5 -8
  65. package/dist/services/feesAuthz.js.map +1 -1
  66. package/dist/services/marketplace.d.ts +36 -0
  67. package/dist/services/marketplace.d.ts.map +1 -1
  68. package/dist/services/marketplace.js +36 -6
  69. package/dist/services/marketplace.js.map +1 -1
  70. package/dist/services/module.d.ts +288 -0
  71. package/dist/services/module.d.ts.map +1 -0
  72. package/dist/services/module.js +518 -0
  73. package/dist/services/module.js.map +1 -0
  74. package/dist/services/moduleItem.d.ts.map +1 -1
  75. package/dist/services/moduleItem.js +23 -7
  76. package/dist/services/moduleItem.js.map +1 -1
  77. package/dist/services/parentPortal.d.ts +3 -5
  78. package/dist/services/parentPortal.d.ts.map +1 -1
  79. package/dist/services/parentPortal.js +29 -43
  80. package/dist/services/parentPortal.js.map +1 -1
  81. package/dist/services/school.d.ts.map +1 -1
  82. package/dist/services/school.js +52 -16
  83. package/dist/services/school.js.map +1 -1
  84. package/dist/services/studentImport.d.ts.map +1 -1
  85. package/dist/services/studentImport.js +38 -25
  86. package/dist/services/studentImport.js.map +1 -1
  87. package/dist/services/transcript.d.ts +2 -3
  88. package/dist/services/transcript.d.ts.map +1 -1
  89. package/dist/services/transcript.js +13 -25
  90. package/dist/services/transcript.js.map +1 -1
  91. package/package.json +1 -1
  92. package/prisma/migrations/20260708013000_add_modules/migration.sql +100 -0
  93. package/prisma/schema.prisma +93 -0
  94. package/scripts/import-nepal-cdc-curriculum.ts +29 -6
  95. package/src/models/attendance.ts +10 -12
  96. package/src/models/marketplace.ts +19 -0
  97. package/src/models/module.ts +77 -0
  98. package/src/models/moduleItem.ts +20 -2
  99. package/src/pipelines/gradeWorksheet.ts +6 -0
  100. package/src/routers/_app.ts +2 -0
  101. package/src/routers/marketplace.ts +16 -0
  102. package/src/routers/module.ts +118 -0
  103. package/src/routers/moduleItem.ts +6 -4
  104. package/src/routers/school.ts +16 -6
  105. package/src/services/academicCalendar.ts +28 -0
  106. package/src/services/assignment.ts +12 -1
  107. package/src/services/attendance.ts +7 -3
  108. package/src/services/class.ts +2 -2
  109. package/src/services/fees.ts +25 -2
  110. package/src/services/feesAuthz.ts +3 -7
  111. package/src/services/marketplace.ts +47 -3
  112. package/src/services/module.ts +683 -0
  113. package/src/services/moduleItem.ts +28 -4
  114. package/src/services/parentPortal.ts +26 -39
  115. package/src/services/school.ts +52 -15
  116. package/src/services/studentImport.ts +37 -25
  117. package/src/services/transcript.ts +11 -23
@@ -2,10 +2,10 @@
2
2
  * ModuleItem service – Canvas-style class content (pages, file packs, videos).
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]="59adfd77-e2d9-5f75-9467-06605b2ddc77")}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]="12a2a128-6cfd-5abf-8cc1-da7dcbaf76f8")}catch(e){}}();
6
6
  import { TRPCError } from "@trpc/server";
7
7
  import { prisma } from "../lib/prisma.js";
8
- import { attachFilesToModuleItem, createModuleItem, deleteModuleItem, findModuleItemByIdForMember, findModuleItemByIdForTeacher, findModuleItemsByClassId, findModuleItemsBySectionId, updateModuleItem, } from "../models/moduleItem.js";
8
+ import { attachFilesToModuleItem, createModuleItem, findAccessibleFilesByIds, deleteModuleItem, findModuleItemByIdForMember, findModuleItemByIdForTeacher, findModuleItemsByClassId, findModuleItemsBySectionId, updateModuleItem, } from "../models/moduleItem.js";
9
9
  import { classTeacherOrSchoolAdminWhere } from "../models/access.js";
10
10
  import { createDirectUploadFiles } from "../lib/fileUpload.js";
11
11
  const VIDEO_MIME_PREFIX = "video/";
@@ -20,6 +20,16 @@ function assertVideoFiles(type, files) {
20
20
  });
21
21
  }
22
22
  }
23
+ async function resolveExistingFiles(type, existingFileIds, userId) {
24
+ const accessible = await findAccessibleFilesByIds(existingFileIds, userId);
25
+ if (accessible.length !== new Set(existingFileIds).size) {
26
+ throw new TRPCError({
27
+ code: "FORBIDDEN",
28
+ message: "One or more files are not accessible",
29
+ });
30
+ }
31
+ assertVideoFiles(type, accessible);
32
+ }
23
33
  async function assertTeacherOwnsSection(sectionId, userId) {
24
34
  const section = await prisma.section.findFirst({
25
35
  where: {
@@ -109,6 +119,9 @@ export async function createModuleItemRecord(userId, input) {
109
119
  // Allow empty collection; files attach later via upload URLs.
110
120
  }
111
121
  assertVideoFiles(input.type, input.files);
122
+ if (input.existingFileIds?.length) {
123
+ await resolveExistingFiles(input.type, input.existingFileIds, userId);
124
+ }
112
125
  const maxOrder = await prisma.moduleItem.aggregate({
113
126
  where: { sectionId: input.sectionId },
114
127
  _max: { order: true },
@@ -123,12 +136,12 @@ export async function createModuleItemRecord(userId, input) {
123
136
  published: input.published ?? true,
124
137
  });
125
138
  if (input.existingFileIds?.length) {
126
- await attachFilesToModuleItem(created.id, input.existingFileIds);
139
+ await attachFilesToModuleItem(created.id, input.existingFileIds, userId);
127
140
  }
128
141
  let uploadFiles = [];
129
142
  if (input.files?.length) {
130
143
  uploadFiles = await createDirectUploadFiles(input.files, userId, undefined, undefined, undefined);
131
- await attachFilesToModuleItem(created.id, uploadFiles.map((f) => f.id));
144
+ await attachFilesToModuleItem(created.id, uploadFiles.map((f) => f.id), userId);
132
145
  }
133
146
  const refreshed = await findModuleItemByIdForTeacher(created.id, userId);
134
147
  return { item: refreshed ?? created, uploadFiles };
@@ -143,6 +156,9 @@ export async function updateModuleItemRecord(userId, input) {
143
156
  }
144
157
  const nextType = input.type ?? existing.type;
145
158
  assertVideoFiles(nextType, input.files);
159
+ if (input.existingFileIds?.length) {
160
+ await resolveExistingFiles(nextType, input.existingFileIds, userId);
161
+ }
146
162
  if (input.sectionId && input.sectionId !== existing.sectionId) {
147
163
  await assertTeacherOwnsSection(input.sectionId, userId);
148
164
  }
@@ -155,12 +171,12 @@ export async function updateModuleItemRecord(userId, input) {
155
171
  order: input.order,
156
172
  });
157
173
  if (input.existingFileIds?.length) {
158
- await attachFilesToModuleItem(input.id, input.existingFileIds);
174
+ await attachFilesToModuleItem(input.id, input.existingFileIds, userId);
159
175
  }
160
176
  let uploadFiles = [];
161
177
  if (input.files?.length) {
162
178
  uploadFiles = await createDirectUploadFiles(input.files, userId, undefined, undefined, undefined);
163
- await attachFilesToModuleItem(input.id, uploadFiles.map((f) => f.id));
179
+ await attachFilesToModuleItem(input.id, uploadFiles.map((f) => f.id), userId);
164
180
  }
165
181
  const refreshed = await findModuleItemByIdForTeacher(input.id, userId);
166
182
  return { item: refreshed ?? updated, uploadFiles };
@@ -185,4 +201,4 @@ export async function reorderModuleItems(userId, input) {
185
201
  return findModuleItemsBySectionId(input.sectionId);
186
202
  }
187
203
  //# sourceMappingURL=moduleItem.js.map
188
- //# debugId=59adfd77-e2d9-5f75-9467-06605b2ddc77
204
+ //# debugId=12a2a128-6cfd-5abf-8cc1-da7dcbaf76f8
@@ -1 +1 @@
1
- {"version":3,"file":"moduleItem.js","sources":["services/moduleItem.ts"],"sourceRoot":"/","sourcesContent":["/**\n * ModuleItem service – Canvas-style class content (pages, file packs, videos).\n */\nimport { TRPCError } from \"@trpc/server\";\nimport type { ModuleItemType } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n attachFilesToModuleItem,\n createModuleItem,\n deleteModuleItem,\n findModuleItemByIdForMember,\n findModuleItemByIdForTeacher,\n findModuleItemsByClassId,\n findModuleItemsBySectionId,\n updateModuleItem,\n} from \"../models/moduleItem.js\";\nimport { classTeacherOrSchoolAdminWhere } from \"../models/access.js\";\nimport { createDirectUploadFiles } from \"../lib/fileUpload.js\";\n\nconst VIDEO_MIME_PREFIX = \"video/\";\n\nfunction assertVideoFiles(\n type: ModuleItemType,\n files?: { type: string }[]\n) {\n if (type !== \"VIDEO\" || !files?.length) return;\n const invalid = files.filter((f) => !f.type.startsWith(VIDEO_MIME_PREFIX));\n if (invalid.length > 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"VIDEO module items only accept video MIME types\",\n });\n }\n}\n\nasync function assertTeacherOwnsSection(sectionId: string, userId: string) {\n const section = await prisma.section.findFirst({\n where: {\n id: sectionId,\n class: classTeacherOrSchoolAdminWhere(userId),\n },\n select: { id: true, classId: true },\n });\n if (!section) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can manage module items\",\n });\n }\n return section;\n}\n\nexport async function listModuleItemsForClass(classId: string, userId: string) {\n const membership = await prisma.class.findFirst({\n where: {\n id: classId,\n OR: [\n { teachers: { some: { id: userId } } },\n { students: { some: { id: userId } } },\n ],\n },\n select: { id: true },\n });\n if (!membership) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Not a class member\" });\n }\n const items = await findModuleItemsByClassId(classId);\n const isTeacher = await prisma.class.findFirst({\n where: { id: classId, ...classTeacherOrSchoolAdminWhere(userId) },\n select: { id: true },\n });\n if (isTeacher) return items;\n return items.filter((item) => item.published);\n}\n\nexport async function listModuleItemsForSection(\n sectionId: string,\n userId: string\n) {\n const section = await prisma.section.findFirst({\n where: {\n id: sectionId,\n class: {\n OR: [\n { teachers: { some: { id: userId } } },\n { students: { some: { id: userId } } },\n ],\n },\n },\n select: { id: true, classId: true },\n });\n if (!section) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Not a class member\" });\n }\n const items = await findModuleItemsBySectionId(sectionId);\n const isTeacher = await prisma.class.findFirst({\n where: {\n id: section.classId,\n ...classTeacherOrSchoolAdminWhere(userId),\n },\n select: { id: true },\n });\n if (isTeacher) return items;\n return items.filter((item) => item.published);\n}\n\nexport async function getModuleItem(id: string, userId: string) {\n const item = await findModuleItemByIdForMember(id, userId);\n if (!item) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module item not found\" });\n }\n const isTeacher = await findModuleItemByIdForTeacher(id, userId);\n if (!item.published && !isTeacher) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module item not found\" });\n }\n return item;\n}\n\nexport async function createModuleItemRecord(\n userId: string,\n input: {\n sectionId: string;\n title: string;\n type: ModuleItemType;\n body?: string | null;\n published?: boolean;\n files?: { name: string; type: string; size: number }[];\n existingFileIds?: string[];\n }\n) {\n await assertTeacherOwnsSection(input.sectionId, userId);\n\n if (input.type === \"PAGE\" && !input.body && !input.files?.length) {\n // Allow empty page draft; body can be filled later.\n }\n if (\n (input.type === \"FILES\" || input.type === \"VIDEO\") &&\n !input.files?.length &&\n !input.existingFileIds?.length\n ) {\n // Allow empty collection; files attach later via upload URLs.\n }\n\n assertVideoFiles(input.type, input.files);\n\n const maxOrder = await prisma.moduleItem.aggregate({\n where: { sectionId: input.sectionId },\n _max: { order: true },\n });\n const order = (maxOrder._max.order ?? 0) + 1;\n\n const created = await createModuleItem({\n sectionId: input.sectionId,\n title: input.title,\n type: input.type,\n body: input.type === \"PAGE\" ? input.body ?? \"\" : input.body,\n order,\n published: input.published ?? true,\n });\n\n if (input.existingFileIds?.length) {\n await attachFilesToModuleItem(created.id, input.existingFileIds);\n }\n\n let uploadFiles: Awaited<ReturnType<typeof createDirectUploadFiles>> = [];\n if (input.files?.length) {\n uploadFiles = await createDirectUploadFiles(\n input.files,\n userId,\n undefined,\n undefined,\n undefined\n );\n await attachFilesToModuleItem(\n created.id,\n uploadFiles.map((f) => f.id)\n );\n }\n\n const refreshed = await findModuleItemByIdForTeacher(created.id, userId);\n return { item: refreshed ?? created, uploadFiles };\n}\n\nexport async function updateModuleItemRecord(\n userId: string,\n input: {\n id: string;\n title?: string;\n body?: string | null;\n published?: boolean;\n type?: ModuleItemType;\n sectionId?: string;\n order?: number | null;\n existingFileIds?: string[];\n files?: { name: string; type: string; size: number }[];\n }\n) {\n const existing = await findModuleItemByIdForTeacher(input.id, userId);\n if (!existing) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can update module items\",\n });\n }\n\n const nextType = input.type ?? existing.type;\n assertVideoFiles(nextType, input.files);\n\n if (input.sectionId && input.sectionId !== existing.sectionId) {\n await assertTeacherOwnsSection(input.sectionId, userId);\n }\n\n const updated = await updateModuleItem(input.id, {\n title: input.title,\n body: input.body,\n published: input.published,\n type: input.type,\n sectionId: input.sectionId,\n order: input.order,\n });\n\n if (input.existingFileIds?.length) {\n await attachFilesToModuleItem(input.id, input.existingFileIds);\n }\n\n let uploadFiles: Awaited<ReturnType<typeof createDirectUploadFiles>> = [];\n if (input.files?.length) {\n uploadFiles = await createDirectUploadFiles(\n input.files,\n userId,\n undefined,\n undefined,\n undefined\n );\n await attachFilesToModuleItem(\n input.id,\n uploadFiles.map((f) => f.id)\n );\n }\n\n const refreshed = await findModuleItemByIdForTeacher(input.id, userId);\n return { item: refreshed ?? updated, uploadFiles };\n}\n\nexport async function deleteModuleItemRecord(userId: string, id: string) {\n const existing = await findModuleItemByIdForTeacher(id, userId);\n if (!existing) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can delete module items\",\n });\n }\n await deleteModuleItem(id);\n return { success: true };\n}\n\nexport async function reorderModuleItems(\n userId: string,\n input: {\n sectionId: string;\n orderedIds: string[];\n }\n) {\n await assertTeacherOwnsSection(input.sectionId, userId);\n await prisma.$transaction(\n input.orderedIds.map((id, index) =>\n prisma.moduleItem.updateMany({\n where: { id, sectionId: input.sectionId },\n data: { order: index + 1 },\n })\n )\n );\n return findModuleItemsBySectionId(input.sectionId);\n}\n"],"names":[],"mappings":"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","debug_id":"59adfd77-e2d9-5f75-9467-06605b2ddc77"}
1
+ {"version":3,"file":"moduleItem.js","sources":["services/moduleItem.ts"],"sourceRoot":"/","sourcesContent":["/**\n * ModuleItem service – Canvas-style class content (pages, file packs, videos).\n */\nimport { TRPCError } from \"@trpc/server\";\nimport type { ModuleItemType } from \"@prisma/client\";\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n attachFilesToModuleItem,\n createModuleItem,\n findAccessibleFilesByIds,\n deleteModuleItem,\n findModuleItemByIdForMember,\n findModuleItemByIdForTeacher,\n findModuleItemsByClassId,\n findModuleItemsBySectionId,\n updateModuleItem,\n} from \"../models/moduleItem.js\";\nimport { classTeacherOrSchoolAdminWhere } from \"../models/access.js\";\nimport { createDirectUploadFiles } from \"../lib/fileUpload.js\";\n\nconst VIDEO_MIME_PREFIX = \"video/\";\n\nfunction assertVideoFiles(\n type: ModuleItemType,\n files?: { type: string }[]\n) {\n if (type !== \"VIDEO\" || !files?.length) return;\n const invalid = files.filter((f) => !f.type.startsWith(VIDEO_MIME_PREFIX));\n if (invalid.length > 0) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"VIDEO module items only accept video MIME types\",\n });\n }\n}\n\nasync function resolveExistingFiles(\n type: ModuleItemType,\n existingFileIds: string[],\n userId: string\n) {\n const accessible = await findAccessibleFilesByIds(existingFileIds, userId);\n if (accessible.length !== new Set(existingFileIds).size) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"One or more files are not accessible\",\n });\n }\n assertVideoFiles(type, accessible);\n}\n\nasync function assertTeacherOwnsSection(sectionId: string, userId: string) {\n const section = await prisma.section.findFirst({\n where: {\n id: sectionId,\n class: classTeacherOrSchoolAdminWhere(userId),\n },\n select: { id: true, classId: true },\n });\n if (!section) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can manage module items\",\n });\n }\n return section;\n}\n\nexport async function listModuleItemsForClass(classId: string, userId: string) {\n const membership = await prisma.class.findFirst({\n where: {\n id: classId,\n OR: [\n { teachers: { some: { id: userId } } },\n { students: { some: { id: userId } } },\n ],\n },\n select: { id: true },\n });\n if (!membership) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Not a class member\" });\n }\n const items = await findModuleItemsByClassId(classId);\n const isTeacher = await prisma.class.findFirst({\n where: { id: classId, ...classTeacherOrSchoolAdminWhere(userId) },\n select: { id: true },\n });\n if (isTeacher) return items;\n return items.filter((item) => item.published);\n}\n\nexport async function listModuleItemsForSection(\n sectionId: string,\n userId: string\n) {\n const section = await prisma.section.findFirst({\n where: {\n id: sectionId,\n class: {\n OR: [\n { teachers: { some: { id: userId } } },\n { students: { some: { id: userId } } },\n ],\n },\n },\n select: { id: true, classId: true },\n });\n if (!section) {\n throw new TRPCError({ code: \"FORBIDDEN\", message: \"Not a class member\" });\n }\n const items = await findModuleItemsBySectionId(sectionId);\n const isTeacher = await prisma.class.findFirst({\n where: {\n id: section.classId,\n ...classTeacherOrSchoolAdminWhere(userId),\n },\n select: { id: true },\n });\n if (isTeacher) return items;\n return items.filter((item) => item.published);\n}\n\nexport async function getModuleItem(id: string, userId: string) {\n const item = await findModuleItemByIdForMember(id, userId);\n if (!item) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module item not found\" });\n }\n const isTeacher = await findModuleItemByIdForTeacher(id, userId);\n if (!item.published && !isTeacher) {\n throw new TRPCError({ code: \"NOT_FOUND\", message: \"Module item not found\" });\n }\n return item;\n}\n\nexport async function createModuleItemRecord(\n userId: string,\n input: {\n sectionId: string;\n title: string;\n type: ModuleItemType;\n body?: string | null;\n published?: boolean;\n files?: { name: string; type: string; size: number }[];\n existingFileIds?: string[];\n }\n) {\n await assertTeacherOwnsSection(input.sectionId, userId);\n\n if (input.type === \"PAGE\" && !input.body && !input.files?.length) {\n // Allow empty page draft; body can be filled later.\n }\n if (\n (input.type === \"FILES\" || input.type === \"VIDEO\") &&\n !input.files?.length &&\n !input.existingFileIds?.length\n ) {\n // Allow empty collection; files attach later via upload URLs.\n }\n\n assertVideoFiles(input.type, input.files);\n if (input.existingFileIds?.length) {\n await resolveExistingFiles(input.type, input.existingFileIds, userId);\n }\n\n const maxOrder = await prisma.moduleItem.aggregate({\n where: { sectionId: input.sectionId },\n _max: { order: true },\n });\n const order = (maxOrder._max.order ?? 0) + 1;\n\n const created = await createModuleItem({\n sectionId: input.sectionId,\n title: input.title,\n type: input.type,\n body: input.type === \"PAGE\" ? input.body ?? \"\" : input.body,\n order,\n published: input.published ?? true,\n });\n\n if (input.existingFileIds?.length) {\n await attachFilesToModuleItem(created.id, input.existingFileIds, userId);\n }\n\n let uploadFiles: Awaited<ReturnType<typeof createDirectUploadFiles>> = [];\n if (input.files?.length) {\n uploadFiles = await createDirectUploadFiles(\n input.files,\n userId,\n undefined,\n undefined,\n undefined\n );\n await attachFilesToModuleItem(\n created.id,\n uploadFiles.map((f) => f.id),\n userId\n );\n }\n\n const refreshed = await findModuleItemByIdForTeacher(created.id, userId);\n return { item: refreshed ?? created, uploadFiles };\n}\n\nexport async function updateModuleItemRecord(\n userId: string,\n input: {\n id: string;\n title?: string;\n body?: string | null;\n published?: boolean;\n type?: ModuleItemType;\n sectionId?: string;\n order?: number | null;\n existingFileIds?: string[];\n files?: { name: string; type: string; size: number }[];\n }\n) {\n const existing = await findModuleItemByIdForTeacher(input.id, userId);\n if (!existing) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can update module items\",\n });\n }\n\n const nextType = input.type ?? existing.type;\n assertVideoFiles(nextType, input.files);\n if (input.existingFileIds?.length) {\n await resolveExistingFiles(nextType, input.existingFileIds, userId);\n }\n\n if (input.sectionId && input.sectionId !== existing.sectionId) {\n await assertTeacherOwnsSection(input.sectionId, userId);\n }\n\n const updated = await updateModuleItem(input.id, {\n title: input.title,\n body: input.body,\n published: input.published,\n type: input.type,\n sectionId: input.sectionId,\n order: input.order,\n });\n\n if (input.existingFileIds?.length) {\n await attachFilesToModuleItem(input.id, input.existingFileIds, userId);\n }\n\n let uploadFiles: Awaited<ReturnType<typeof createDirectUploadFiles>> = [];\n if (input.files?.length) {\n uploadFiles = await createDirectUploadFiles(\n input.files,\n userId,\n undefined,\n undefined,\n undefined\n );\n await attachFilesToModuleItem(\n input.id,\n uploadFiles.map((f) => f.id),\n userId\n );\n }\n\n const refreshed = await findModuleItemByIdForTeacher(input.id, userId);\n return { item: refreshed ?? updated, uploadFiles };\n}\n\nexport async function deleteModuleItemRecord(userId: string, id: string) {\n const existing = await findModuleItemByIdForTeacher(id, userId);\n if (!existing) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only class teachers can delete module items\",\n });\n }\n await deleteModuleItem(id);\n return { success: true };\n}\n\nexport async function reorderModuleItems(\n userId: string,\n input: {\n sectionId: string;\n orderedIds: string[];\n }\n) {\n await assertTeacherOwnsSection(input.sectionId, userId);\n await prisma.$transaction(\n input.orderedIds.map((id, index) =>\n prisma.moduleItem.updateMany({\n where: { id, sectionId: input.sectionId },\n data: { order: index + 1 },\n })\n )\n );\n return findModuleItemsBySectionId(input.sectionId);\n}\n"],"names":[],"mappings":"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","debug_id":"12a2a128-6cfd-5abf-8cc1-da7dcbaf76f8"}
@@ -70,10 +70,7 @@ export declare function listMyChildren(userId: string, schoolId: string): Promis
70
70
  };
71
71
  }[];
72
72
  }>;
73
- /**
74
- * Read-only attendance stub for a linked child (aggregates class attendance M2M).
75
- * Does not touch A1 AttendanceRecord refactor.
76
- */
73
+ /** Read-only attendance view for a linked child (reads AttendanceRecord). */
77
74
  export declare function getChildAttendance(userId: string, input: {
78
75
  schoolId: string;
79
76
  studentUserId: string;
@@ -85,6 +82,7 @@ export declare function getChildAttendance(userId: string, input: {
85
82
  present: number;
86
83
  late: number;
87
84
  absent: number;
85
+ excused: number;
88
86
  total: number;
89
87
  };
90
88
  records: {
@@ -97,7 +95,7 @@ export declare function getChildAttendance(userId: string, input: {
97
95
  name: string;
98
96
  subject: string;
99
97
  } | null;
100
- status: "PRESENT" | "LATE" | "ABSENT";
98
+ status: import(".prisma/client").$Enums.AttendanceStatus;
101
99
  }[];
102
100
  }>;
103
101
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"parentPortal.d.ts","sourceRoot":"/","sources":["services/parentPortal.ts"],"names":[],"mappings":"AAkCA,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;;;;;;;;GAiDzE;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;GAWzE;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;GA8B3E;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;GAgDpE;AA0BD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,EAAE,CAAC,EAAE,IAAI,CAAC;CACX;;;;;;;;;;;;;;;;;;;GAqEF;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;GA8DrE;AAED,uEAAuE;AACvE,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,oBAatB"}
1
+ {"version":3,"file":"parentPortal.d.ts","sourceRoot":"/","sources":["services/parentPortal.ts"],"names":[],"mappings":"AAkCA,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;;;;;;;;GAiDzE;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE;;GAWzE;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;GA8B3E;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;GAgDpE;AA0BD,6EAA6E;AAC7E,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IACL,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,EAAE,CAAC,EAAE,IAAI,CAAC;CACX;;;;;;;;;;;;;;;;;;;;GA2DF;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;GA8DrE;AAED,uEAAuE;AACvE,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,oBAatB"}
@@ -3,7 +3,7 @@
3
3
  * read-only attendance / grades stubs for linked children.
4
4
  */
5
5
 
6
- !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]="3d43f9df-fb4b-55f0-948a-80ffd38d00d9")}catch(e){}}();
6
+ !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]="e0a84844-f117-5700-89b0-1d460b45e236")}catch(e){}}();
7
7
  import { TRPCError } from "@trpc/server";
8
8
  import { prisma } from "../lib/prisma.js";
9
9
  import { findActiveSchoolAdminMembership, findActiveSchoolMembership, findSchoolMembership, } from "../models/school.js";
@@ -177,10 +177,7 @@ async function assertParentOfStudent(schoolId, parentUserId, studentUserId) {
177
177
  }
178
178
  return link;
179
179
  }
180
- /**
181
- * Read-only attendance stub for a linked child (aggregates class attendance M2M).
182
- * Does not touch A1 AttendanceRecord refactor.
183
- */
180
+ /** Read-only attendance view for a linked child (reads AttendanceRecord). */
184
181
  export async function getChildAttendance(userId, input) {
185
182
  await assertParentOfStudent(input.schoolId, userId, input.studentUserId);
186
183
  const classes = await prisma.class.findMany({
@@ -192,59 +189,48 @@ export async function getChildAttendance(userId, input) {
192
189
  select: { id: true, name: true, subject: true, section: true },
193
190
  });
194
191
  const classIds = classes.map((c) => c.id);
195
- const attendance = await prisma.attendance.findMany({
192
+ const attendanceRecords = await prisma.attendanceRecord.findMany({
196
193
  where: {
197
- classId: { in: classIds },
198
- ...(input.from || input.to
199
- ? {
200
- date: {
201
- ...(input.from ? { gte: input.from } : {}),
202
- ...(input.to ? { lte: input.to } : {}),
203
- },
204
- }
205
- : {}),
206
- OR: [
207
- { present: { some: { id: input.studentUserId } } },
208
- { late: { some: { id: input.studentUserId } } },
209
- { absent: { some: { id: input.studentUserId } } },
210
- ],
194
+ studentId: input.studentUserId,
195
+ attendance: {
196
+ classId: { in: classIds },
197
+ ...(input.from || input.to
198
+ ? {
199
+ date: {
200
+ ...(input.from ? { gte: input.from } : {}),
201
+ ...(input.to ? { lte: input.to } : {}),
202
+ },
203
+ }
204
+ : {}),
205
+ },
211
206
  },
212
207
  select: {
213
- id: true,
214
- date: true,
215
- classId: true,
216
- present: { where: { id: input.studentUserId }, select: { id: true } },
217
- late: { where: { id: input.studentUserId }, select: { id: true } },
218
- absent: { where: { id: input.studentUserId }, select: { id: true } },
208
+ status: true,
209
+ attendance: { select: { id: true, date: true, classId: true } },
219
210
  },
220
- orderBy: { date: "desc" },
211
+ orderBy: { attendance: { date: "desc" } },
221
212
  take: 200,
222
213
  });
223
214
  const classMap = new Map(classes.map((c) => [c.id, c]));
224
- const records = attendance.map((row) => {
225
- let status = "PRESENT";
226
- if (row.absent.length > 0)
227
- status = "ABSENT";
228
- else if (row.late.length > 0)
229
- status = "LATE";
230
- return {
231
- attendanceId: row.id,
232
- date: row.date,
233
- classId: row.classId,
234
- class: classMap.get(row.classId) ?? null,
235
- status,
236
- };
237
- });
215
+ const records = attendanceRecords.map((row) => ({
216
+ attendanceId: row.attendance.id,
217
+ date: row.attendance.date,
218
+ classId: row.attendance.classId,
219
+ class: classMap.get(row.attendance.classId) ?? null,
220
+ status: row.status,
221
+ }));
238
222
  const summary = records.reduce((acc, r) => {
239
223
  acc.total += 1;
240
224
  if (r.status === "PRESENT")
241
225
  acc.present += 1;
242
226
  else if (r.status === "LATE")
243
227
  acc.late += 1;
228
+ else if (r.status === "EXCUSED")
229
+ acc.excused += 1;
244
230
  else
245
231
  acc.absent += 1;
246
232
  return acc;
247
- }, { present: 0, late: 0, absent: 0, total: 0 });
233
+ }, { present: 0, late: 0, absent: 0, excused: 0, total: 0 });
248
234
  return { summary, records };
249
235
  }
250
236
  /**
@@ -325,4 +311,4 @@ export async function isParentOfStudent(schoolId, parentUserId, studentUserId) {
325
311
  return Boolean(link);
326
312
  }
327
313
  //# sourceMappingURL=parentPortal.js.map
328
- //# debugId=3d43f9df-fb4b-55f0-948a-80ffd38d00d9
314
+ //# debugId=e0a84844-f117-5700-89b0-1d460b45e236
@@ -1 +1 @@
1
- {"version":3,"file":"parentPortal.js","sources":["services/parentPortal.ts"],"sourceRoot":"/","sourcesContent":["/**\n * C4: Minimal parent portal — link parents to students and expose\n * read-only attendance / grades stubs for linked children.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n findActiveSchoolAdminMembership,\n findActiveSchoolMembership,\n findSchoolMembership,\n} from \"../models/school.js\";\n\nasync function requireSchoolAdmin(schoolId: string, userId: string) {\n const membership = await findActiveSchoolAdminMembership(schoolId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only school admins can perform this action\",\n });\n }\n return membership;\n}\n\nasync function requireParentMembership(schoolId: string, userId: string) {\n const membership = await findActiveSchoolMembership(schoolId, userId);\n if (!membership || membership.role !== \"PARENT\") {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only parents can access this resource\",\n });\n }\n return membership;\n}\n\nexport async function linkParentToStudent(\n userId: string,\n input: { schoolId: string; parentUserId: string; studentUserId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const [parentMembership, studentMembership] = await Promise.all([\n findSchoolMembership(input.schoolId, input.parentUserId),\n findSchoolMembership(input.schoolId, input.studentUserId),\n ]);\n\n if (!parentMembership || parentMembership.status !== \"ACTIVE\") {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Parent must be an active school member\",\n });\n }\n if (parentMembership.role !== \"PARENT\") {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"parentUserId must have the PARENT role\",\n });\n }\n if (\n !studentMembership ||\n studentMembership.status !== \"ACTIVE\" ||\n studentMembership.role !== \"STUDENT\"\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"studentUserId must be an active STUDENT member\",\n });\n }\n\n const link = await prisma.parentStudentLink.upsert({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId: input.schoolId,\n parentUserId: input.parentUserId,\n studentUserId: input.studentUserId,\n },\n },\n update: {},\n create: {\n schoolId: input.schoolId,\n parentUserId: input.parentUserId,\n studentUserId: input.studentUserId,\n },\n });\n\n return { link };\n}\n\nexport async function unlinkParentFromStudent(\n userId: string,\n input: { schoolId: string; parentUserId: string; studentUserId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n await prisma.parentStudentLink.deleteMany({\n where: {\n schoolId: input.schoolId,\n parentUserId: input.parentUserId,\n studentUserId: input.studentUserId,\n },\n });\n return { ok: true as const };\n}\n\nexport async function listParentLinks(\n userId: string,\n input: { schoolId: string; parentUserId?: string; studentUserId?: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const links = await prisma.parentStudentLink.findMany({\n where: {\n schoolId: input.schoolId,\n ...(input.parentUserId ? { parentUserId: input.parentUserId } : {}),\n ...(input.studentUserId ? { studentUserId: input.studentUserId } : {}),\n },\n include: {\n parent: {\n select: {\n id: true,\n username: true,\n email: true,\n profile: { select: { displayName: true } },\n },\n },\n student: {\n select: {\n id: true,\n username: true,\n email: true,\n profile: { select: { displayName: true } },\n },\n },\n },\n orderBy: { createdAt: \"desc\" },\n });\n return { links };\n}\n\nexport async function listMyChildren(userId: string, schoolId: string) {\n await requireParentMembership(schoolId, userId);\n const links = await prisma.parentStudentLink.findMany({\n where: { schoolId, parentUserId: userId },\n include: {\n student: {\n select: {\n id: true,\n username: true,\n email: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n studentIn: {\n where: { schoolId },\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n color: true,\n },\n },\n },\n },\n },\n orderBy: { createdAt: \"asc\" },\n });\n\n return {\n children: links.map((link) => ({\n linkId: link.id,\n student: {\n id: link.student.id,\n username: link.student.username,\n email: link.student.email,\n displayName: link.student.profile?.displayName ?? null,\n profilePicture: link.student.profile?.profilePicture ?? null,\n profilePictureThumbnail:\n link.student.profile?.profilePictureThumbnail ?? null,\n classes: link.student.studentIn,\n },\n })),\n };\n}\n\nasync function assertParentOfStudent(\n schoolId: string,\n parentUserId: string,\n studentUserId: string\n) {\n await requireParentMembership(schoolId, parentUserId);\n const link = await prisma.parentStudentLink.findUnique({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId,\n parentUserId,\n studentUserId,\n },\n },\n });\n if (!link) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"You are not linked to this student\",\n });\n }\n return link;\n}\n\n/**\n * Read-only attendance stub for a linked child (aggregates class attendance M2M).\n * Does not touch A1 AttendanceRecord refactor.\n */\nexport async function getChildAttendance(\n userId: string,\n input: {\n schoolId: string;\n studentUserId: string;\n classId?: string;\n from?: Date;\n to?: Date;\n }\n) {\n await assertParentOfStudent(input.schoolId, userId, input.studentUserId);\n\n const classes = await prisma.class.findMany({\n where: {\n schoolId: input.schoolId,\n students: { some: { id: input.studentUserId } },\n ...(input.classId ? { id: input.classId } : {}),\n },\n select: { id: true, name: true, subject: true, section: true },\n });\n const classIds = classes.map((c) => c.id);\n\n const attendance = await prisma.attendance.findMany({\n where: {\n classId: { in: classIds },\n ...(input.from || input.to\n ? {\n date: {\n ...(input.from ? { gte: input.from } : {}),\n ...(input.to ? { lte: input.to } : {}),\n },\n }\n : {}),\n OR: [\n { present: { some: { id: input.studentUserId } } },\n { late: { some: { id: input.studentUserId } } },\n { absent: { some: { id: input.studentUserId } } },\n ],\n },\n select: {\n id: true,\n date: true,\n classId: true,\n present: { where: { id: input.studentUserId }, select: { id: true } },\n late: { where: { id: input.studentUserId }, select: { id: true } },\n absent: { where: { id: input.studentUserId }, select: { id: true } },\n },\n orderBy: { date: \"desc\" },\n take: 200,\n });\n\n const classMap = new Map(classes.map((c) => [c.id, c]));\n const records = attendance.map((row) => {\n let status: \"PRESENT\" | \"LATE\" | \"ABSENT\" = \"PRESENT\";\n if (row.absent.length > 0) status = \"ABSENT\";\n else if (row.late.length > 0) status = \"LATE\";\n return {\n attendanceId: row.id,\n date: row.date,\n classId: row.classId,\n class: classMap.get(row.classId) ?? null,\n status,\n };\n });\n\n const summary = records.reduce(\n (acc, r) => {\n acc.total += 1;\n if (r.status === \"PRESENT\") acc.present += 1;\n else if (r.status === \"LATE\") acc.late += 1;\n else acc.absent += 1;\n return acc;\n },\n { present: 0, late: 0, absent: 0, total: 0 }\n );\n\n return { summary, records };\n}\n\n/**\n * Read-only grades stub — returns assignment score rows for the linked child.\n */\nexport async function getChildGrades(\n userId: string,\n input: { schoolId: string; studentUserId: string; classId?: string }\n) {\n await assertParentOfStudent(input.schoolId, userId, input.studentUserId);\n\n const classes = await prisma.class.findMany({\n where: {\n schoolId: input.schoolId,\n students: { some: { id: input.studentUserId } },\n ...(input.classId ? { id: input.classId } : {}),\n },\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n assignments: {\n select: {\n id: true,\n title: true,\n type: true,\n dueDate: true,\n maxGrade: true,\n submissions: {\n where: { studentId: input.studentUserId },\n select: {\n id: true,\n gradeReceived: true,\n returned: true,\n submittedAt: true,\n },\n take: 1,\n },\n },\n orderBy: { dueDate: \"desc\" },\n take: 100,\n },\n },\n });\n\n return {\n classes: classes.map((cls) => ({\n id: cls.id,\n name: cls.name,\n subject: cls.subject,\n section: cls.section,\n assignments: cls.assignments.map((a) => ({\n id: a.id,\n title: a.title,\n type: a.type,\n dueDate: a.dueDate,\n maxGrade: a.maxGrade,\n submission: a.submissions[0]\n ? {\n id: a.submissions[0].id,\n gradeReceived: a.submissions[0].gradeReceived,\n returned: a.submissions[0].returned,\n submittedAt: a.submissions[0].submittedAt,\n }\n : null,\n })),\n })),\n };\n}\n\n/** Helper for other services: is this user a parent of the student? */\nexport async function isParentOfStudent(\n schoolId: string,\n parentUserId: string,\n studentUserId: string\n) {\n const link = await prisma.parentStudentLink.findUnique({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId,\n parentUserId,\n studentUserId,\n },\n },\n select: { id: true },\n });\n return Boolean(link);\n}\n"],"names":[],"mappings":"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","debug_id":"3d43f9df-fb4b-55f0-948a-80ffd38d00d9"}
1
+ {"version":3,"file":"parentPortal.js","sources":["services/parentPortal.ts"],"sourceRoot":"/","sourcesContent":["/**\n * C4: Minimal parent portal — link parents to students and expose\n * read-only attendance / grades stubs for linked children.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport { prisma } from \"../lib/prisma.js\";\nimport {\n findActiveSchoolAdminMembership,\n findActiveSchoolMembership,\n findSchoolMembership,\n} from \"../models/school.js\";\n\nasync function requireSchoolAdmin(schoolId: string, userId: string) {\n const membership = await findActiveSchoolAdminMembership(schoolId, userId);\n if (!membership) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only school admins can perform this action\",\n });\n }\n return membership;\n}\n\nasync function requireParentMembership(schoolId: string, userId: string) {\n const membership = await findActiveSchoolMembership(schoolId, userId);\n if (!membership || membership.role !== \"PARENT\") {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"Only parents can access this resource\",\n });\n }\n return membership;\n}\n\nexport async function linkParentToStudent(\n userId: string,\n input: { schoolId: string; parentUserId: string; studentUserId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n\n const [parentMembership, studentMembership] = await Promise.all([\n findSchoolMembership(input.schoolId, input.parentUserId),\n findSchoolMembership(input.schoolId, input.studentUserId),\n ]);\n\n if (!parentMembership || parentMembership.status !== \"ACTIVE\") {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"Parent must be an active school member\",\n });\n }\n if (parentMembership.role !== \"PARENT\") {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"parentUserId must have the PARENT role\",\n });\n }\n if (\n !studentMembership ||\n studentMembership.status !== \"ACTIVE\" ||\n studentMembership.role !== \"STUDENT\"\n ) {\n throw new TRPCError({\n code: \"BAD_REQUEST\",\n message: \"studentUserId must be an active STUDENT member\",\n });\n }\n\n const link = await prisma.parentStudentLink.upsert({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId: input.schoolId,\n parentUserId: input.parentUserId,\n studentUserId: input.studentUserId,\n },\n },\n update: {},\n create: {\n schoolId: input.schoolId,\n parentUserId: input.parentUserId,\n studentUserId: input.studentUserId,\n },\n });\n\n return { link };\n}\n\nexport async function unlinkParentFromStudent(\n userId: string,\n input: { schoolId: string; parentUserId: string; studentUserId: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n await prisma.parentStudentLink.deleteMany({\n where: {\n schoolId: input.schoolId,\n parentUserId: input.parentUserId,\n studentUserId: input.studentUserId,\n },\n });\n return { ok: true as const };\n}\n\nexport async function listParentLinks(\n userId: string,\n input: { schoolId: string; parentUserId?: string; studentUserId?: string }\n) {\n await requireSchoolAdmin(input.schoolId, userId);\n const links = await prisma.parentStudentLink.findMany({\n where: {\n schoolId: input.schoolId,\n ...(input.parentUserId ? { parentUserId: input.parentUserId } : {}),\n ...(input.studentUserId ? { studentUserId: input.studentUserId } : {}),\n },\n include: {\n parent: {\n select: {\n id: true,\n username: true,\n email: true,\n profile: { select: { displayName: true } },\n },\n },\n student: {\n select: {\n id: true,\n username: true,\n email: true,\n profile: { select: { displayName: true } },\n },\n },\n },\n orderBy: { createdAt: \"desc\" },\n });\n return { links };\n}\n\nexport async function listMyChildren(userId: string, schoolId: string) {\n await requireParentMembership(schoolId, userId);\n const links = await prisma.parentStudentLink.findMany({\n where: { schoolId, parentUserId: userId },\n include: {\n student: {\n select: {\n id: true,\n username: true,\n email: true,\n profile: {\n select: {\n displayName: true,\n profilePicture: true,\n profilePictureThumbnail: true,\n },\n },\n studentIn: {\n where: { schoolId },\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n color: true,\n },\n },\n },\n },\n },\n orderBy: { createdAt: \"asc\" },\n });\n\n return {\n children: links.map((link) => ({\n linkId: link.id,\n student: {\n id: link.student.id,\n username: link.student.username,\n email: link.student.email,\n displayName: link.student.profile?.displayName ?? null,\n profilePicture: link.student.profile?.profilePicture ?? null,\n profilePictureThumbnail:\n link.student.profile?.profilePictureThumbnail ?? null,\n classes: link.student.studentIn,\n },\n })),\n };\n}\n\nasync function assertParentOfStudent(\n schoolId: string,\n parentUserId: string,\n studentUserId: string\n) {\n await requireParentMembership(schoolId, parentUserId);\n const link = await prisma.parentStudentLink.findUnique({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId,\n parentUserId,\n studentUserId,\n },\n },\n });\n if (!link) {\n throw new TRPCError({\n code: \"FORBIDDEN\",\n message: \"You are not linked to this student\",\n });\n }\n return link;\n}\n\n/** Read-only attendance view for a linked child (reads AttendanceRecord). */\nexport async function getChildAttendance(\n userId: string,\n input: {\n schoolId: string;\n studentUserId: string;\n classId?: string;\n from?: Date;\n to?: Date;\n }\n) {\n await assertParentOfStudent(input.schoolId, userId, input.studentUserId);\n\n const classes = await prisma.class.findMany({\n where: {\n schoolId: input.schoolId,\n students: { some: { id: input.studentUserId } },\n ...(input.classId ? { id: input.classId } : {}),\n },\n select: { id: true, name: true, subject: true, section: true },\n });\n const classIds = classes.map((c) => c.id);\n\n const attendanceRecords = await prisma.attendanceRecord.findMany({\n where: {\n studentId: input.studentUserId,\n attendance: {\n classId: { in: classIds },\n ...(input.from || input.to\n ? {\n date: {\n ...(input.from ? { gte: input.from } : {}),\n ...(input.to ? { lte: input.to } : {}),\n },\n }\n : {}),\n },\n },\n select: {\n status: true,\n attendance: { select: { id: true, date: true, classId: true } },\n },\n orderBy: { attendance: { date: \"desc\" } },\n take: 200,\n });\n\n const classMap = new Map(classes.map((c) => [c.id, c]));\n const records = attendanceRecords.map((row) => ({\n attendanceId: row.attendance.id,\n date: row.attendance.date,\n classId: row.attendance.classId,\n class: classMap.get(row.attendance.classId) ?? null,\n status: row.status,\n }));\n\n const summary = records.reduce(\n (acc, r) => {\n acc.total += 1;\n if (r.status === \"PRESENT\") acc.present += 1;\n else if (r.status === \"LATE\") acc.late += 1;\n else if (r.status === \"EXCUSED\") acc.excused += 1;\n else acc.absent += 1;\n return acc;\n },\n { present: 0, late: 0, absent: 0, excused: 0, total: 0 }\n );\n\n return { summary, records };\n}\n\n/**\n * Read-only grades stub — returns assignment score rows for the linked child.\n */\nexport async function getChildGrades(\n userId: string,\n input: { schoolId: string; studentUserId: string; classId?: string }\n) {\n await assertParentOfStudent(input.schoolId, userId, input.studentUserId);\n\n const classes = await prisma.class.findMany({\n where: {\n schoolId: input.schoolId,\n students: { some: { id: input.studentUserId } },\n ...(input.classId ? { id: input.classId } : {}),\n },\n select: {\n id: true,\n name: true,\n subject: true,\n section: true,\n assignments: {\n select: {\n id: true,\n title: true,\n type: true,\n dueDate: true,\n maxGrade: true,\n submissions: {\n where: { studentId: input.studentUserId },\n select: {\n id: true,\n gradeReceived: true,\n returned: true,\n submittedAt: true,\n },\n take: 1,\n },\n },\n orderBy: { dueDate: \"desc\" },\n take: 100,\n },\n },\n });\n\n return {\n classes: classes.map((cls) => ({\n id: cls.id,\n name: cls.name,\n subject: cls.subject,\n section: cls.section,\n assignments: cls.assignments.map((a) => ({\n id: a.id,\n title: a.title,\n type: a.type,\n dueDate: a.dueDate,\n maxGrade: a.maxGrade,\n submission: a.submissions[0]\n ? {\n id: a.submissions[0].id,\n gradeReceived: a.submissions[0].gradeReceived,\n returned: a.submissions[0].returned,\n submittedAt: a.submissions[0].submittedAt,\n }\n : null,\n })),\n })),\n };\n}\n\n/** Helper for other services: is this user a parent of the student? */\nexport async function isParentOfStudent(\n schoolId: string,\n parentUserId: string,\n studentUserId: string\n) {\n const link = await prisma.parentStudentLink.findUnique({\n where: {\n schoolId_parentUserId_studentUserId: {\n schoolId,\n parentUserId,\n studentUserId,\n },\n },\n select: { id: true },\n });\n return Boolean(link);\n}\n"],"names":[],"mappings":"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","debug_id":"e0a84844-f117-5700-89b0-1d460b45e236"}
@@ -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]="63d2144c-911d-56ab-8693-f7615b26334e")}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]="c3d38743-6297-594a-9bde-bf049584e6b4")}catch(e){}}();
6
6
  import { SessionType } from "@prisma/client";
7
7
  import { TRPCError } from "@trpc/server";
8
8
  import { hash } from "bcryptjs";
@@ -76,6 +76,26 @@ export async function createSchoolRecord(userId, input) {
76
76
  }
77
77
  export async function updateSchoolDetails(userId, input) {
78
78
  await requireSchoolAdmin(input.schoolId, userId);
79
+ const brandingFileIds = [input.logoId, input.letterheadFileId].filter((id) => typeof id === "string");
80
+ if (brandingFileIds.length) {
81
+ const accessible = await prisma.file.findMany({
82
+ where: {
83
+ id: { in: brandingFileIds },
84
+ OR: [
85
+ { userId },
86
+ { folder: { schoolStorageId: input.schoolId } },
87
+ ],
88
+ },
89
+ select: { id: true },
90
+ });
91
+ const accessibleIds = new Set(accessible.map((f) => f.id));
92
+ if (brandingFileIds.some((id) => !accessibleIds.has(id))) {
93
+ throw new TRPCError({
94
+ code: "FORBIDDEN",
95
+ message: "File does not belong to this school",
96
+ });
97
+ }
98
+ }
79
99
  const updated = await updateSchoolRecord(input.schoolId, {
80
100
  name: input.name,
81
101
  description: input.description,
@@ -433,16 +453,19 @@ export async function getStudentTimeline(userId, input) {
433
453
  message: "Student is not in this institution",
434
454
  });
435
455
  }
456
+ // Default to the last 12 months when no range is given, to bound reads.
457
+ const defaultFrom = new Date();
458
+ defaultFrom.setFullYear(defaultFrom.getFullYear() - 1);
436
459
  const range = {
437
- ...(input.from ? { gte: input.from } : {}),
460
+ gte: input.from ?? defaultFrom,
438
461
  ...(input.to ? { lte: input.to } : {}),
439
462
  };
440
- const hasRange = Object.keys(range).length > 0;
441
463
  const [events, assignments, attendance] = await Promise.all([
442
464
  prisma.event.findMany({
443
465
  where: {
444
466
  userId: input.studentId,
445
- ...(hasRange ? { startTime: range } : {}),
467
+ startTime: range,
468
+ OR: [{ classId: null }, { class: { schoolId: input.schoolId } }],
446
469
  },
447
470
  select: {
448
471
  id: true,
@@ -459,10 +482,11 @@ export async function getStudentTimeline(userId, input) {
459
482
  prisma.assignment.findMany({
460
483
  where: {
461
484
  class: {
485
+ schoolId: input.schoolId,
462
486
  students: { some: { id: input.studentId } },
463
487
  },
464
488
  template: false,
465
- ...(hasRange ? { dueDate: range } : {}),
489
+ dueDate: range,
466
490
  },
467
491
  select: {
468
492
  id: true,
@@ -499,7 +523,7 @@ export async function getStudentTimeline(userId, input) {
499
523
  studentId: input.studentId,
500
524
  attendance: {
501
525
  class: { schoolId: input.schoolId },
502
- ...(hasRange ? { date: range } : {}),
526
+ date: range,
503
527
  },
504
528
  },
505
529
  select: {
@@ -575,11 +599,13 @@ export async function getMemberTimeline(userId, input) {
575
599
  message: "This person is not in this institution",
576
600
  });
577
601
  }
602
+ // Default to the last 12 months when no range is given, to bound reads.
603
+ const defaultFrom = new Date();
604
+ defaultFrom.setFullYear(defaultFrom.getFullYear() - 1);
578
605
  const range = {
579
- ...(input.from ? { gte: input.from } : {}),
606
+ gte: input.from ?? defaultFrom,
580
607
  ...(input.to ? { lte: input.to } : {}),
581
608
  };
582
- const hasRange = Object.keys(range).length > 0;
583
609
  if (membership.role === "STUDENT") {
584
610
  if (!callerIsAdmin) {
585
611
  const sharedClass = await prisma.class.findFirst({
@@ -601,7 +627,8 @@ export async function getMemberTimeline(userId, input) {
601
627
  prisma.event.findMany({
602
628
  where: {
603
629
  userId: input.memberId,
604
- ...(hasRange ? { startTime: range } : {}),
630
+ startTime: range,
631
+ OR: [{ classId: null }, { class: { schoolId: input.schoolId } }],
605
632
  },
606
633
  select: {
607
634
  id: true,
@@ -617,9 +644,12 @@ export async function getMemberTimeline(userId, input) {
617
644
  }),
618
645
  prisma.assignment.findMany({
619
646
  where: {
620
- class: { students: { some: { id: input.memberId } } },
647
+ class: {
648
+ schoolId: input.schoolId,
649
+ students: { some: { id: input.memberId } },
650
+ },
621
651
  template: false,
622
- ...(hasRange ? { dueDate: range } : {}),
652
+ dueDate: range,
623
653
  },
624
654
  select: {
625
655
  id: true,
@@ -656,7 +686,7 @@ export async function getMemberTimeline(userId, input) {
656
686
  studentId: input.memberId,
657
687
  attendance: {
658
688
  class: { schoolId: input.schoolId },
659
- ...(hasRange ? { date: range } : {}),
689
+ date: range,
660
690
  },
661
691
  },
662
692
  select: {
@@ -705,10 +735,16 @@ export async function getMemberTimeline(userId, input) {
705
735
  prisma.event.findMany({
706
736
  where: {
707
737
  OR: [
708
- { userId: input.memberId },
709
- { class: { teachers: { some: { id: input.memberId } } } },
738
+ { userId: input.memberId, classId: null },
739
+ { userId: input.memberId, class: { schoolId: input.schoolId } },
740
+ {
741
+ class: {
742
+ schoolId: input.schoolId,
743
+ teachers: { some: { id: input.memberId } },
744
+ },
745
+ },
710
746
  ],
711
- ...(hasRange ? { startTime: range } : {}),
747
+ startTime: range,
712
748
  },
713
749
  select: {
714
750
  id: true,
@@ -885,4 +921,4 @@ export async function acceptInvite(userId, token) {
885
921
  return { membership };
886
922
  }
887
923
  //# sourceMappingURL=school.js.map
888
- //# debugId=63d2144c-911d-56ab-8693-f7615b26334e
924
+ //# debugId=c3d38743-6297-594a-9bde-bf049584e6b4