@studious-lms/server 1.23.0 → 1.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +0 -1
- package/data/curriculum/nepal-cdc-g4-g5/README.md +90 -0
- package/data/curriculum/nepal-cdc-g4-g5/manifest.json +97 -0
- package/data/curriculum/nepal-cdc-g4-g5/proficiency-scale.json +76 -0
- package/data/curriculum/nepal-cdc-g4-g5/subjects/english.json +6936 -0
- package/data/curriculum/nepal-cdc-g4-g5/subjects/health-physical-creative-arts.json +3401 -0
- package/data/curriculum/nepal-cdc-g4-g5/subjects/mathematics.json +1511 -0
- package/data/curriculum/nepal-cdc-g4-g5/subjects/nepali.json +7928 -0
- package/data/curriculum/nepal-cdc-g4-g5/subjects/science-technology.json +2420 -0
- package/data/curriculum/nepal-cdc-g4-g5/subjects/social-studies.json +2177 -0
- package/dist/lib/assignmentTypeDefaults.d.ts +41 -0
- package/dist/lib/assignmentTypeDefaults.d.ts.map +1 -0
- package/dist/lib/assignmentTypeDefaults.js +167 -0
- package/dist/lib/assignmentTypeDefaults.js.map +1 -0
- package/dist/lib/config/env.d.ts +0 -8
- package/dist/lib/config/env.d.ts.map +1 -1
- package/dist/lib/config/env.js +2 -5
- package/dist/lib/config/env.js.map +1 -1
- package/dist/lib/feePdf.d.ts +19 -0
- package/dist/lib/feePdf.d.ts.map +1 -0
- package/dist/lib/feePdf.js +167 -0
- package/dist/lib/feePdf.js.map +1 -0
- package/dist/lib/feePdfFormat.d.ts +4 -0
- package/dist/lib/feePdfFormat.d.ts.map +1 -0
- package/dist/lib/feePdfFormat.js +16 -0
- package/dist/lib/feePdfFormat.js.map +1 -0
- package/dist/lib/idCardPdf.d.ts +26 -0
- package/dist/lib/idCardPdf.d.ts.map +1 -0
- package/dist/lib/idCardPdf.js +272 -0
- package/dist/lib/idCardPdf.js.map +1 -0
- package/dist/lib/pdfLetterhead.d.ts +41 -0
- package/dist/lib/pdfLetterhead.d.ts.map +1 -0
- package/dist/lib/pdfLetterhead.js +86 -0
- package/dist/lib/pdfLetterhead.js.map +1 -0
- package/dist/lib/transcriptPdf.d.ts +55 -0
- package/dist/lib/transcriptPdf.d.ts.map +1 -0
- package/dist/lib/transcriptPdf.js +303 -0
- package/dist/lib/transcriptPdf.js.map +1 -0
- package/dist/models/academicCalendar.d.ts +198 -0
- package/dist/models/academicCalendar.d.ts.map +1 -0
- package/dist/models/academicCalendar.js +106 -0
- package/dist/models/academicCalendar.js.map +1 -0
- package/dist/models/agenda.d.ts +8 -0
- package/dist/models/agenda.d.ts.map +1 -1
- package/dist/models/announcement.d.ts +2 -0
- package/dist/models/announcement.d.ts.map +1 -1
- package/dist/models/assignment.d.ts +66 -0
- package/dist/models/assignment.d.ts.map +1 -1
- package/dist/models/attendance.d.ts +659 -21
- package/dist/models/attendance.d.ts.map +1 -1
- package/dist/models/attendance.js +544 -75
- package/dist/models/attendance.js.map +1 -1
- package/dist/models/auth.d.ts +2 -0
- package/dist/models/auth.d.ts.map +1 -1
- package/dist/models/class.d.ts +12 -0
- package/dist/models/class.d.ts.map +1 -1
- package/dist/models/class.js +3 -2
- package/dist/models/class.js.map +1 -1
- package/dist/models/event.d.ts +16 -0
- package/dist/models/event.d.ts.map +1 -1
- package/dist/models/fees.d.ts +554 -0
- package/dist/models/fees.d.ts.map +1 -0
- package/dist/models/fees.js +278 -0
- package/dist/models/fees.js.map +1 -0
- package/dist/models/file.d.ts +29 -0
- package/dist/models/file.d.ts.map +1 -1
- package/dist/models/folder.d.ts +2 -0
- package/dist/models/folder.d.ts.map +1 -1
- package/dist/models/labChat.d.ts +2 -0
- package/dist/models/labChat.d.ts.map +1 -1
- package/dist/models/marketplace.d.ts +178 -0
- package/dist/models/marketplace.d.ts.map +1 -0
- package/dist/models/marketplace.js +115 -0
- package/dist/models/marketplace.js.map +1 -0
- package/dist/models/moduleItem.d.ts +304 -0
- package/dist/models/moduleItem.d.ts.map +1 -0
- package/dist/models/moduleItem.js +78 -0
- package/dist/models/moduleItem.js.map +1 -0
- package/dist/models/newtonChat.d.ts +1 -0
- package/dist/models/newtonChat.d.ts.map +1 -1
- package/dist/models/school.d.ts +18 -0
- package/dist/models/school.d.ts.map +1 -1
- package/dist/models/school.js +7 -2
- package/dist/models/school.js.map +1 -1
- package/dist/models/section.d.ts +4 -0
- package/dist/models/section.d.ts.map +1 -1
- package/dist/models/worksheet.d.ts +19 -0
- package/dist/models/worksheet.d.ts.map +1 -1
- package/dist/models/worksheet.js +2 -2
- package/dist/models/worksheet.js.map +1 -1
- package/dist/routers/_app.d.ts +11341 -7097
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +12 -2
- package/dist/routers/_app.js.map +1 -1
- package/dist/routers/academicCalendar.d.ts +229 -0
- package/dist/routers/academicCalendar.d.ts.map +1 -0
- package/dist/routers/academicCalendar.js +75 -0
- package/dist/routers/academicCalendar.js.map +1 -0
- package/dist/routers/admin.d.ts +2 -0
- package/dist/routers/admin.d.ts.map +1 -1
- package/dist/routers/agenda.d.ts +8 -0
- package/dist/routers/agenda.d.ts.map +1 -1
- package/dist/routers/assignment.d.ts +75 -0
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/assignment.js +10 -2
- package/dist/routers/assignment.js.map +1 -1
- package/dist/routers/attendance.d.ts +103 -0
- package/dist/routers/attendance.d.ts.map +1 -1
- package/dist/routers/attendance.js +8 -5
- package/dist/routers/attendance.js.map +1 -1
- package/dist/routers/class.d.ts +27 -0
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +6 -2
- package/dist/routers/class.js.map +1 -1
- package/dist/routers/event.d.ts +8 -0
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/fees.d.ts +513 -0
- package/dist/routers/fees.d.ts.map +1 -0
- package/dist/routers/fees.js +88 -0
- package/dist/routers/fees.js.map +1 -0
- package/dist/routers/file.d.ts +2 -0
- package/dist/routers/file.d.ts.map +1 -1
- package/dist/routers/gradebook.d.ts +4 -1
- package/dist/routers/gradebook.d.ts.map +1 -1
- package/dist/routers/marketplace.d.ts +371 -0
- package/dist/routers/marketplace.d.ts.map +1 -0
- package/dist/routers/marketplace.js +87 -0
- package/dist/routers/marketplace.js.map +1 -0
- package/dist/routers/moduleItem.d.ts +263 -0
- package/dist/routers/moduleItem.d.ts.map +1 -0
- package/dist/routers/moduleItem.js +60 -0
- package/dist/routers/moduleItem.js.map +1 -0
- package/dist/routers/parent.d.ts +169 -0
- package/dist/routers/parent.d.ts.map +1 -0
- package/dist/routers/parent.js +49 -0
- package/dist/routers/parent.js.map +1 -0
- package/dist/routers/school.d.ts +363 -7
- package/dist/routers/school.d.ts.map +1 -1
- package/dist/routers/school.js +100 -3
- package/dist/routers/school.js.map +1 -1
- package/dist/routers/worksheet.d.ts +27 -0
- package/dist/routers/worksheet.d.ts.map +1 -1
- package/dist/routers/worksheet.js +10 -3
- package/dist/routers/worksheet.js.map +1 -1
- package/dist/services/academicCalendar.d.ts +173 -0
- package/dist/services/academicCalendar.d.ts.map +1 -0
- package/dist/services/academicCalendar.js +214 -0
- package/dist/services/academicCalendar.js.map +1 -0
- package/dist/services/adminInstitution.d.ts +2 -0
- package/dist/services/adminInstitution.d.ts.map +1 -1
- package/dist/services/agenda.d.ts +8 -0
- package/dist/services/agenda.d.ts.map +1 -1
- package/dist/services/assignment.d.ts +75 -0
- package/dist/services/assignment.d.ts.map +1 -1
- package/dist/services/assignment.js +91 -12
- package/dist/services/assignment.js.map +1 -1
- package/dist/services/attendance.d.ts +323 -10
- package/dist/services/attendance.d.ts.map +1 -1
- package/dist/services/attendance.js +385 -25
- package/dist/services/attendance.js.map +1 -1
- package/dist/services/attendanceMerge.d.ts +53 -0
- package/dist/services/attendanceMerge.d.ts.map +1 -0
- package/dist/services/attendanceMerge.js +87 -0
- package/dist/services/attendanceMerge.js.map +1 -0
- package/dist/services/attendanceRate.d.ts +24 -0
- package/dist/services/attendanceRate.d.ts.map +1 -0
- package/dist/services/attendanceRate.js +31 -0
- package/dist/services/attendanceRate.js.map +1 -0
- package/dist/services/attendanceStats.d.ts +22 -0
- package/dist/services/attendanceStats.d.ts.map +1 -0
- package/dist/services/attendanceStats.js +45 -0
- package/dist/services/attendanceStats.js.map +1 -0
- package/dist/services/class.d.ts +27 -0
- package/dist/services/class.d.ts.map +1 -1
- package/dist/services/class.js +26 -3
- package/dist/services/class.js.map +1 -1
- package/dist/services/event.d.ts +8 -0
- package/dist/services/event.d.ts.map +1 -1
- package/dist/services/fees.d.ts +422 -0
- package/dist/services/fees.d.ts.map +1 -0
- package/dist/services/fees.js +310 -0
- package/dist/services/fees.js.map +1 -0
- package/dist/services/feesAuthz.d.ts +49 -0
- package/dist/services/feesAuthz.d.ts.map +1 -0
- package/dist/services/feesAuthz.js +52 -0
- package/dist/services/feesAuthz.js.map +1 -0
- package/dist/services/file.d.ts +4 -0
- package/dist/services/file.d.ts.map +1 -1
- package/dist/services/gradebook.d.ts +18 -1
- package/dist/services/gradebook.d.ts.map +1 -1
- package/dist/services/gradebook.js +57 -2
- package/dist/services/gradebook.js.map +1 -1
- package/dist/services/idCard.d.ts +11 -0
- package/dist/services/idCard.d.ts.map +1 -0
- package/dist/services/idCard.js +122 -0
- package/dist/services/idCard.js.map +1 -0
- package/dist/services/marketplace.d.ts +301 -0
- package/dist/services/marketplace.d.ts.map +1 -0
- package/dist/services/marketplace.js +628 -0
- package/dist/services/marketplace.js.map +1 -0
- package/dist/services/moduleItem.d.ts +196 -0
- package/dist/services/moduleItem.d.ts.map +1 -0
- package/dist/services/moduleItem.js +188 -0
- package/dist/services/moduleItem.js.map +1 -0
- package/dist/services/notification.d.ts +58 -0
- package/dist/services/notification.d.ts.map +1 -1
- package/dist/services/notification.js +30 -2
- package/dist/services/notification.js.map +1 -1
- package/dist/services/parentPortal.d.ts +133 -0
- package/dist/services/parentPortal.d.ts.map +1 -0
- package/dist/services/parentPortal.js +328 -0
- package/dist/services/parentPortal.js.map +1 -0
- package/dist/services/school.d.ts +28 -3
- package/dist/services/school.d.ts.map +1 -1
- package/dist/services/school.js +65 -45
- package/dist/services/school.js.map +1 -1
- package/dist/services/studentImport.d.ts +34 -0
- package/dist/services/studentImport.d.ts.map +1 -0
- package/dist/services/studentImport.js +236 -0
- package/dist/services/studentImport.js.map +1 -0
- package/dist/services/transcript.d.ts +61 -0
- package/dist/services/transcript.d.ts.map +1 -0
- package/dist/services/transcript.js +243 -0
- package/dist/services/transcript.js.map +1 -0
- package/dist/services/worksheet.d.ts +22 -0
- package/dist/services/worksheet.d.ts.map +1 -1
- package/dist/services/worksheet.js +94 -2
- package/dist/services/worksheet.js.map +1 -1
- package/dist/trpc.d.ts +1 -1
- package/dist/trpc.d.ts.map +1 -1
- package/dist/trpc.js +5 -5
- package/dist/trpc.js.map +1 -1
- package/dist/utils/adminPanelAccess.d.ts +4 -5
- package/dist/utils/adminPanelAccess.d.ts.map +1 -1
- package/dist/utils/adminPanelAccess.js +12 -34
- package/dist/utils/adminPanelAccess.js.map +1 -1
- package/package.json +2 -1
- package/prisma/migrations/20260804120000_add_attendance_records_and_excusals/migration.sql +114 -0
- package/prisma/migrations/20260804120100_add_fee_plans_invoices_payments/migration.sql +101 -0
- package/prisma/migrations/20260804120200_add_marketplace_listings/migration.sql +54 -0
- package/prisma/migrations/20260804120300_content_platform_b3_b9/migration.sql +63 -0
- package/prisma/migrations/20260804120400_c_academic_ops/migration.sql +77 -0
- package/prisma/schema.prisma +371 -11
- package/scripts/import-nepal-cdc-curriculum.ts +379 -0
- package/src/lib/assignmentTypeDefaults.ts +222 -0
- package/src/lib/config/env.ts +0 -3
- package/src/lib/feePdf.ts +199 -0
- package/src/lib/feePdfFormat.ts +14 -0
- package/src/lib/idCardPdf.ts +336 -0
- package/src/lib/pdfLetterhead.ts +124 -0
- package/src/lib/transcriptPdf.ts +502 -0
- package/src/models/academicCalendar.ts +139 -0
- package/src/models/attendance.ts +704 -73
- package/src/models/class.ts +1 -0
- package/src/models/fees.ts +349 -0
- package/src/models/marketplace.ts +158 -0
- package/src/models/moduleItem.ts +107 -0
- package/src/models/school.ts +6 -0
- package/src/models/worksheet.ts +2 -0
- package/src/routers/_app.ts +10 -0
- package/src/routers/academicCalendar.ts +103 -0
- package/src/routers/assignment.ts +8 -0
- package/src/routers/attendance.ts +7 -3
- package/src/routers/class.ts +4 -0
- package/src/routers/fees.ts +126 -0
- package/src/routers/marketplace.ts +134 -0
- package/src/routers/moduleItem.ts +89 -0
- package/src/routers/parent.ts +68 -0
- package/src/routers/school.ts +139 -1
- package/src/routers/worksheet.ts +11 -0
- package/src/services/academicCalendar.ts +299 -0
- package/src/services/assignment.ts +122 -12
- package/src/services/attendance.ts +548 -26
- package/src/services/attendanceMerge.ts +139 -0
- package/src/services/attendanceRate.ts +41 -0
- package/src/services/attendanceStats.ts +63 -0
- package/src/services/class.ts +25 -1
- package/src/services/fees.ts +424 -0
- package/src/services/feesAuthz.ts +77 -0
- package/src/services/gradebook.ts +70 -0
- package/src/services/idCard.ts +147 -0
- package/src/services/marketplace.ts +785 -0
- package/src/services/moduleItem.ts +274 -0
- package/src/services/notification.ts +43 -0
- package/src/services/parentPortal.ts +380 -0
- package/src/services/school.ts +65 -44
- package/src/services/studentImport.ts +273 -0
- package/src/services/transcript.ts +313 -0
- package/src/services/worksheet.ts +111 -0
- package/src/trpc.ts +3 -3
- package/src/utils/adminPanelAccess.ts +12 -35
- package/tests/unit/assignmentTypeDefaults.test.ts +52 -0
- package/tests/unit/lib/feePdf.test.ts +15 -0
- package/tests/unit/lib/idCardPdf.test.ts +40 -0
- package/tests/unit/lib/transcriptPdf.test.ts +111 -0
- package/tests/unit/services/attendanceMerge.test.ts +90 -0
- package/tests/unit/services/attendanceRate.test.ts +38 -0
- package/tests/unit/services/attendanceStats.test.ts +54 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification.js","sources":["services/notification.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Notification service – list, emit, and mark notifications as read.\n *\n * Notifications are only emitted server-side by domain services (assignments,\n * announcements, discussions, attendance, …); there is no public \"send\"\n * endpoint. New notifications are also pushed in realtime over Pusher on the\n * receiver's private per-user channel.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport type { NotificationType } from \"@prisma/client\";\nimport {\n findNotificationsByReceiverIdPaginated,\n findNotificationByIdForReceiver,\n countUnreadNotifications,\n createNotification,\n createManyNotifications,\n markNotificationAsReadForReceiver,\n markAllNotificationsAsReadForReceiver,\n type NotificationCreateData,\n} from \"../models/notification.js\";\nimport { pusher, userNotificationChannel } from \"../lib/pusher.js\";\nimport { logger } from \"../utils/logger.js\";\n\nexport const NOTIFICATION_CREATED_EVENT = \"notification-created\";\n\ntype CreatedNotification = Awaited<ReturnType<typeof createNotification>>;\n\n/** Pusher batch triggers accept at most 10 events per call. */\nconst PUSHER_BATCH_SIZE = 10;\n\nasync function pushNotifications(notifications: CreatedNotification[]) {\n for (let i = 0; i < notifications.length; i += PUSHER_BATCH_SIZE) {\n const batch = notifications.slice(i, i + PUSHER_BATCH_SIZE).map((notification) => ({\n channel: userNotificationChannel(notification.receiverId),\n name: NOTIFICATION_CREATED_EVENT,\n data: notification,\n }));\n try {\n await pusher.triggerBatch(batch);\n } catch (error) {\n logger.error(\"Failed to push notifications over Pusher\", { error });\n }\n }\n}\n\n/** List notifications for a user, newest first, cursor-paginated. */\nexport async function listNotifications(\n userId: string,\n input?: { limit?: number; cursor?: string | null }\n) {\n const limit = input?.limit ?? 20;\n const rows = await findNotificationsByReceiverIdPaginated(userId, limit, input?.cursor);\n\n let nextCursor: string | null = null;\n if (rows.length > limit) {\n const nextItem = rows.pop();\n nextCursor = nextItem!.id;\n }\n\n return { items: rows, nextCursor };\n}\n\n/** Get a single notification by ID for its receiver. */\nexport async function getNotification(userId: string, id: string) {\n const notification = await findNotificationByIdForReceiver(id, userId);\n\n if (!notification) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Notification not found\",\n });\n }\n\n return notification;\n}\n\n/** Number of unread notifications for a user. */\nexport function getUnreadCount(userId: string) {\n return countUnreadNotifications(userId);\n}\n\n/**\n * Emit a notification to one user (server-side only) and push it over Pusher.\n */\nexport async function sendNotification(input: NotificationCreateData) {\n const notification = await createNotification(input);\n void pushNotifications([notification]);\n return notification;\n}\n\n/**\n * Emit the same notification to multiple users (server-side only) and push\n * each over Pusher.\n */\nexport async function sendToMultiple(input: {\n receiverIds: string[];\n title: string;\n content: string;\n senderId?: string | null;\n type?: NotificationType;\n entityType?: string | null;\n entityId?: string | null;\n classId?: string | null;\n}) {\n const { receiverIds, ...rest } = input;\n if (receiverIds.length === 0) return [];\n const notifications = await createManyNotifications(\n receiverIds.map((receiverId) => ({ receiverId, ...rest }))\n );\n void pushNotifications(notifications);\n return notifications;\n}\n\n/** Mark a notification as read for its receiver. */\nexport async function markAsRead(userId: string, id: string) {\n const notification = await markNotificationAsReadForReceiver(id, userId);\n\n if (!notification) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Notification not found\",\n });\n }\n\n return notification;\n}\n\n/** Mark all notifications as read for a user. */\nexport async function markAllAsRead(userId: string) {\n const result = await markAllNotificationsAsReadForReceiver(userId);\n return { count: result.count };\n}\n"],"names":[],"mappings":"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","debug_id":"
|
|
1
|
+
{"version":3,"file":"notification.js","sources":["services/notification.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Notification service – list, emit, and mark notifications as read.\n *\n * Notifications are only emitted server-side by domain services (assignments,\n * announcements, discussions, attendance, …); there is no public \"send\"\n * endpoint. New notifications are also pushed in realtime over Pusher on the\n * receiver's private per-user channel.\n */\nimport { TRPCError } from \"@trpc/server\";\nimport type { NotificationType } from \"@prisma/client\";\nimport {\n findNotificationsByReceiverIdPaginated,\n findNotificationByIdForReceiver,\n countUnreadNotifications,\n createNotification,\n createManyNotifications,\n markNotificationAsReadForReceiver,\n markAllNotificationsAsReadForReceiver,\n type NotificationCreateData,\n} from \"../models/notification.js\";\nimport { pusher, userNotificationChannel } from \"../lib/pusher.js\";\nimport { logger } from \"../utils/logger.js\";\n\nexport const NOTIFICATION_CREATED_EVENT = \"notification-created\";\n\ntype CreatedNotification = Awaited<ReturnType<typeof createNotification>>;\n\n/** Pusher batch triggers accept at most 10 events per call. */\nconst PUSHER_BATCH_SIZE = 10;\n\nasync function pushNotifications(notifications: CreatedNotification[]) {\n for (let i = 0; i < notifications.length; i += PUSHER_BATCH_SIZE) {\n const batch = notifications.slice(i, i + PUSHER_BATCH_SIZE).map((notification) => ({\n channel: userNotificationChannel(notification.receiverId),\n name: NOTIFICATION_CREATED_EVENT,\n data: notification,\n }));\n try {\n await pusher.triggerBatch(batch);\n } catch (error) {\n logger.error(\"Failed to push notifications over Pusher\", { error });\n }\n }\n}\n\n/** List notifications for a user, newest first, cursor-paginated. */\nexport async function listNotifications(\n userId: string,\n input?: { limit?: number; cursor?: string | null }\n) {\n const limit = input?.limit ?? 20;\n const rows = await findNotificationsByReceiverIdPaginated(userId, limit, input?.cursor);\n\n let nextCursor: string | null = null;\n if (rows.length > limit) {\n const nextItem = rows.pop();\n nextCursor = nextItem!.id;\n }\n\n return { items: rows, nextCursor };\n}\n\n/** Get a single notification by ID for its receiver. */\nexport async function getNotification(userId: string, id: string) {\n const notification = await findNotificationByIdForReceiver(id, userId);\n\n if (!notification) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Notification not found\",\n });\n }\n\n return notification;\n}\n\n/** Number of unread notifications for a user. */\nexport function getUnreadCount(userId: string) {\n return countUnreadNotifications(userId);\n}\n\n/**\n * Emit a notification to one user (server-side only) and push it over Pusher.\n */\nexport async function sendNotification(input: NotificationCreateData) {\n const notification = await createNotification(input);\n void pushNotifications([notification]);\n return notification;\n}\n\n/**\n * Emit the same notification to multiple users (server-side only) and push\n * each over Pusher.\n */\nexport async function sendToMultiple(input: {\n receiverIds: string[];\n title: string;\n content: string;\n senderId?: string | null;\n type?: NotificationType;\n entityType?: string | null;\n entityId?: string | null;\n classId?: string | null;\n}) {\n const { receiverIds, ...rest } = input;\n if (receiverIds.length === 0) return [];\n const notifications = await createManyNotifications(\n receiverIds.map((receiverId) => ({ receiverId, ...rest }))\n );\n void pushNotifications(notifications);\n return notifications;\n}\n\n/** Mark a notification as read for its receiver. */\nexport async function markAsRead(userId: string, id: string) {\n const notification = await markNotificationAsReadForReceiver(id, userId);\n\n if (!notification) {\n throw new TRPCError({\n code: \"NOT_FOUND\",\n message: \"Notification not found\",\n });\n }\n\n return notification;\n}\n\n/** Mark all notifications as read for a user. */\nexport async function markAllAsRead(userId: string) {\n const result = await markAllNotificationsAsReadForReceiver(userId);\n return { count: result.count };\n}\n\n/** C6: Emit a FEES notification (due / paid / reminder). Used by A2 fee flows. */\nexport async function notifyFeeEvent(input: {\n receiverIds: string[];\n title: string;\n content: string;\n entityId?: string | null;\n senderId?: string | null;\n}) {\n return sendToMultiple({\n receiverIds: input.receiverIds,\n title: input.title,\n content: input.content,\n senderId: input.senderId,\n type: \"FEES\",\n entityType: \"FEE\",\n entityId: input.entityId ?? null,\n });\n}\n\n/**\n * C6: Excusal notification hook (trivial).\n * A1 owns StudentExcusal; call this when an excusal is created/decided.\n */\nexport async function notifyExcusalEvent(input: {\n receiverIds: string[];\n title: string;\n content: string;\n entityId?: string | null;\n classId?: string | null;\n senderId?: string | null;\n}) {\n return sendToMultiple({\n receiverIds: input.receiverIds,\n title: input.title,\n content: input.content,\n senderId: input.senderId,\n type: \"EXCUSAL\",\n entityType: \"EXCUSAL\",\n entityId: input.entityId ?? null,\n classId: input.classId ?? null,\n });\n}\n"],"names":[],"mappings":"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","debug_id":"1a9e09c3-7424-5b2f-8b38-4719aa064f5b"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export declare function linkParentToStudent(userId: string, input: {
|
|
2
|
+
schoolId: string;
|
|
3
|
+
parentUserId: string;
|
|
4
|
+
studentUserId: string;
|
|
5
|
+
}): Promise<{
|
|
6
|
+
link: {
|
|
7
|
+
id: string;
|
|
8
|
+
createdAt: Date;
|
|
9
|
+
updatedAt: Date;
|
|
10
|
+
schoolId: string;
|
|
11
|
+
parentUserId: string;
|
|
12
|
+
studentUserId: string;
|
|
13
|
+
};
|
|
14
|
+
}>;
|
|
15
|
+
export declare function unlinkParentFromStudent(userId: string, input: {
|
|
16
|
+
schoolId: string;
|
|
17
|
+
parentUserId: string;
|
|
18
|
+
studentUserId: string;
|
|
19
|
+
}): Promise<{
|
|
20
|
+
ok: true;
|
|
21
|
+
}>;
|
|
22
|
+
export declare function listParentLinks(userId: string, input: {
|
|
23
|
+
schoolId: string;
|
|
24
|
+
parentUserId?: string;
|
|
25
|
+
studentUserId?: string;
|
|
26
|
+
}): Promise<{
|
|
27
|
+
links: ({
|
|
28
|
+
parent: {
|
|
29
|
+
id: string;
|
|
30
|
+
username: string;
|
|
31
|
+
email: string;
|
|
32
|
+
profile: {
|
|
33
|
+
displayName: string | null;
|
|
34
|
+
} | null;
|
|
35
|
+
};
|
|
36
|
+
student: {
|
|
37
|
+
id: string;
|
|
38
|
+
username: string;
|
|
39
|
+
email: string;
|
|
40
|
+
profile: {
|
|
41
|
+
displayName: string | null;
|
|
42
|
+
} | null;
|
|
43
|
+
};
|
|
44
|
+
} & {
|
|
45
|
+
id: string;
|
|
46
|
+
createdAt: Date;
|
|
47
|
+
updatedAt: Date;
|
|
48
|
+
schoolId: string;
|
|
49
|
+
parentUserId: string;
|
|
50
|
+
studentUserId: string;
|
|
51
|
+
})[];
|
|
52
|
+
}>;
|
|
53
|
+
export declare function listMyChildren(userId: string, schoolId: string): Promise<{
|
|
54
|
+
children: {
|
|
55
|
+
linkId: string;
|
|
56
|
+
student: {
|
|
57
|
+
id: string;
|
|
58
|
+
username: string;
|
|
59
|
+
email: string;
|
|
60
|
+
displayName: string | null;
|
|
61
|
+
profilePicture: string | null;
|
|
62
|
+
profilePictureThumbnail: string | null;
|
|
63
|
+
classes: {
|
|
64
|
+
section: string;
|
|
65
|
+
id: string;
|
|
66
|
+
name: string;
|
|
67
|
+
color: string | null;
|
|
68
|
+
subject: string;
|
|
69
|
+
}[];
|
|
70
|
+
};
|
|
71
|
+
}[];
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Read-only attendance stub for a linked child (aggregates class attendance M2M).
|
|
75
|
+
* Does not touch A1 AttendanceRecord refactor.
|
|
76
|
+
*/
|
|
77
|
+
export declare function getChildAttendance(userId: string, input: {
|
|
78
|
+
schoolId: string;
|
|
79
|
+
studentUserId: string;
|
|
80
|
+
classId?: string;
|
|
81
|
+
from?: Date;
|
|
82
|
+
to?: Date;
|
|
83
|
+
}): Promise<{
|
|
84
|
+
summary: {
|
|
85
|
+
present: number;
|
|
86
|
+
late: number;
|
|
87
|
+
absent: number;
|
|
88
|
+
total: number;
|
|
89
|
+
};
|
|
90
|
+
records: {
|
|
91
|
+
attendanceId: string;
|
|
92
|
+
date: Date;
|
|
93
|
+
classId: string;
|
|
94
|
+
class: {
|
|
95
|
+
section: string;
|
|
96
|
+
id: string;
|
|
97
|
+
name: string;
|
|
98
|
+
subject: string;
|
|
99
|
+
} | null;
|
|
100
|
+
status: "PRESENT" | "LATE" | "ABSENT";
|
|
101
|
+
}[];
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* Read-only grades stub — returns assignment score rows for the linked child.
|
|
105
|
+
*/
|
|
106
|
+
export declare function getChildGrades(userId: string, input: {
|
|
107
|
+
schoolId: string;
|
|
108
|
+
studentUserId: string;
|
|
109
|
+
classId?: string;
|
|
110
|
+
}): Promise<{
|
|
111
|
+
classes: {
|
|
112
|
+
id: string;
|
|
113
|
+
name: string;
|
|
114
|
+
subject: string;
|
|
115
|
+
section: string;
|
|
116
|
+
assignments: {
|
|
117
|
+
id: string;
|
|
118
|
+
title: string;
|
|
119
|
+
type: import(".prisma/client").$Enums.AssignmentType;
|
|
120
|
+
dueDate: Date;
|
|
121
|
+
maxGrade: number | null;
|
|
122
|
+
submission: {
|
|
123
|
+
id: string;
|
|
124
|
+
gradeReceived: number | null;
|
|
125
|
+
returned: boolean | null;
|
|
126
|
+
submittedAt: Date | null;
|
|
127
|
+
} | null;
|
|
128
|
+
}[];
|
|
129
|
+
}[];
|
|
130
|
+
}>;
|
|
131
|
+
/** Helper for other services: is this user a parent of the student? */
|
|
132
|
+
export declare function isParentOfStudent(schoolId: string, parentUserId: string, studentUserId: string): Promise<boolean>;
|
|
133
|
+
//# sourceMappingURL=parentPortal.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C4: Minimal parent portal — link parents to students and expose
|
|
3
|
+
* read-only attendance / grades stubs for linked children.
|
|
4
|
+
*/
|
|
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){}}();
|
|
7
|
+
import { TRPCError } from "@trpc/server";
|
|
8
|
+
import { prisma } from "../lib/prisma.js";
|
|
9
|
+
import { findActiveSchoolAdminMembership, findActiveSchoolMembership, findSchoolMembership, } from "../models/school.js";
|
|
10
|
+
async function requireSchoolAdmin(schoolId, userId) {
|
|
11
|
+
const membership = await findActiveSchoolAdminMembership(schoolId, userId);
|
|
12
|
+
if (!membership) {
|
|
13
|
+
throw new TRPCError({
|
|
14
|
+
code: "FORBIDDEN",
|
|
15
|
+
message: "Only school admins can perform this action",
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return membership;
|
|
19
|
+
}
|
|
20
|
+
async function requireParentMembership(schoolId, userId) {
|
|
21
|
+
const membership = await findActiveSchoolMembership(schoolId, userId);
|
|
22
|
+
if (!membership || membership.role !== "PARENT") {
|
|
23
|
+
throw new TRPCError({
|
|
24
|
+
code: "FORBIDDEN",
|
|
25
|
+
message: "Only parents can access this resource",
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return membership;
|
|
29
|
+
}
|
|
30
|
+
export async function linkParentToStudent(userId, input) {
|
|
31
|
+
await requireSchoolAdmin(input.schoolId, userId);
|
|
32
|
+
const [parentMembership, studentMembership] = await Promise.all([
|
|
33
|
+
findSchoolMembership(input.schoolId, input.parentUserId),
|
|
34
|
+
findSchoolMembership(input.schoolId, input.studentUserId),
|
|
35
|
+
]);
|
|
36
|
+
if (!parentMembership || parentMembership.status !== "ACTIVE") {
|
|
37
|
+
throw new TRPCError({
|
|
38
|
+
code: "BAD_REQUEST",
|
|
39
|
+
message: "Parent must be an active school member",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (parentMembership.role !== "PARENT") {
|
|
43
|
+
throw new TRPCError({
|
|
44
|
+
code: "BAD_REQUEST",
|
|
45
|
+
message: "parentUserId must have the PARENT role",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (!studentMembership ||
|
|
49
|
+
studentMembership.status !== "ACTIVE" ||
|
|
50
|
+
studentMembership.role !== "STUDENT") {
|
|
51
|
+
throw new TRPCError({
|
|
52
|
+
code: "BAD_REQUEST",
|
|
53
|
+
message: "studentUserId must be an active STUDENT member",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const link = await prisma.parentStudentLink.upsert({
|
|
57
|
+
where: {
|
|
58
|
+
schoolId_parentUserId_studentUserId: {
|
|
59
|
+
schoolId: input.schoolId,
|
|
60
|
+
parentUserId: input.parentUserId,
|
|
61
|
+
studentUserId: input.studentUserId,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
update: {},
|
|
65
|
+
create: {
|
|
66
|
+
schoolId: input.schoolId,
|
|
67
|
+
parentUserId: input.parentUserId,
|
|
68
|
+
studentUserId: input.studentUserId,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
return { link };
|
|
72
|
+
}
|
|
73
|
+
export async function unlinkParentFromStudent(userId, input) {
|
|
74
|
+
await requireSchoolAdmin(input.schoolId, userId);
|
|
75
|
+
await prisma.parentStudentLink.deleteMany({
|
|
76
|
+
where: {
|
|
77
|
+
schoolId: input.schoolId,
|
|
78
|
+
parentUserId: input.parentUserId,
|
|
79
|
+
studentUserId: input.studentUserId,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
return { ok: true };
|
|
83
|
+
}
|
|
84
|
+
export async function listParentLinks(userId, input) {
|
|
85
|
+
await requireSchoolAdmin(input.schoolId, userId);
|
|
86
|
+
const links = await prisma.parentStudentLink.findMany({
|
|
87
|
+
where: {
|
|
88
|
+
schoolId: input.schoolId,
|
|
89
|
+
...(input.parentUserId ? { parentUserId: input.parentUserId } : {}),
|
|
90
|
+
...(input.studentUserId ? { studentUserId: input.studentUserId } : {}),
|
|
91
|
+
},
|
|
92
|
+
include: {
|
|
93
|
+
parent: {
|
|
94
|
+
select: {
|
|
95
|
+
id: true,
|
|
96
|
+
username: true,
|
|
97
|
+
email: true,
|
|
98
|
+
profile: { select: { displayName: true } },
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
student: {
|
|
102
|
+
select: {
|
|
103
|
+
id: true,
|
|
104
|
+
username: true,
|
|
105
|
+
email: true,
|
|
106
|
+
profile: { select: { displayName: true } },
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
orderBy: { createdAt: "desc" },
|
|
111
|
+
});
|
|
112
|
+
return { links };
|
|
113
|
+
}
|
|
114
|
+
export async function listMyChildren(userId, schoolId) {
|
|
115
|
+
await requireParentMembership(schoolId, userId);
|
|
116
|
+
const links = await prisma.parentStudentLink.findMany({
|
|
117
|
+
where: { schoolId, parentUserId: userId },
|
|
118
|
+
include: {
|
|
119
|
+
student: {
|
|
120
|
+
select: {
|
|
121
|
+
id: true,
|
|
122
|
+
username: true,
|
|
123
|
+
email: true,
|
|
124
|
+
profile: {
|
|
125
|
+
select: {
|
|
126
|
+
displayName: true,
|
|
127
|
+
profilePicture: true,
|
|
128
|
+
profilePictureThumbnail: true,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
studentIn: {
|
|
132
|
+
where: { schoolId },
|
|
133
|
+
select: {
|
|
134
|
+
id: true,
|
|
135
|
+
name: true,
|
|
136
|
+
subject: true,
|
|
137
|
+
section: true,
|
|
138
|
+
color: true,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
orderBy: { createdAt: "asc" },
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
children: links.map((link) => ({
|
|
148
|
+
linkId: link.id,
|
|
149
|
+
student: {
|
|
150
|
+
id: link.student.id,
|
|
151
|
+
username: link.student.username,
|
|
152
|
+
email: link.student.email,
|
|
153
|
+
displayName: link.student.profile?.displayName ?? null,
|
|
154
|
+
profilePicture: link.student.profile?.profilePicture ?? null,
|
|
155
|
+
profilePictureThumbnail: link.student.profile?.profilePictureThumbnail ?? null,
|
|
156
|
+
classes: link.student.studentIn,
|
|
157
|
+
},
|
|
158
|
+
})),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
async function assertParentOfStudent(schoolId, parentUserId, studentUserId) {
|
|
162
|
+
await requireParentMembership(schoolId, parentUserId);
|
|
163
|
+
const link = await prisma.parentStudentLink.findUnique({
|
|
164
|
+
where: {
|
|
165
|
+
schoolId_parentUserId_studentUserId: {
|
|
166
|
+
schoolId,
|
|
167
|
+
parentUserId,
|
|
168
|
+
studentUserId,
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
if (!link) {
|
|
173
|
+
throw new TRPCError({
|
|
174
|
+
code: "FORBIDDEN",
|
|
175
|
+
message: "You are not linked to this student",
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return link;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Read-only attendance stub for a linked child (aggregates class attendance M2M).
|
|
182
|
+
* Does not touch A1 AttendanceRecord refactor.
|
|
183
|
+
*/
|
|
184
|
+
export async function getChildAttendance(userId, input) {
|
|
185
|
+
await assertParentOfStudent(input.schoolId, userId, input.studentUserId);
|
|
186
|
+
const classes = await prisma.class.findMany({
|
|
187
|
+
where: {
|
|
188
|
+
schoolId: input.schoolId,
|
|
189
|
+
students: { some: { id: input.studentUserId } },
|
|
190
|
+
...(input.classId ? { id: input.classId } : {}),
|
|
191
|
+
},
|
|
192
|
+
select: { id: true, name: true, subject: true, section: true },
|
|
193
|
+
});
|
|
194
|
+
const classIds = classes.map((c) => c.id);
|
|
195
|
+
const attendance = await prisma.attendance.findMany({
|
|
196
|
+
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
|
+
],
|
|
211
|
+
},
|
|
212
|
+
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 } },
|
|
219
|
+
},
|
|
220
|
+
orderBy: { date: "desc" },
|
|
221
|
+
take: 200,
|
|
222
|
+
});
|
|
223
|
+
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
|
+
});
|
|
238
|
+
const summary = records.reduce((acc, r) => {
|
|
239
|
+
acc.total += 1;
|
|
240
|
+
if (r.status === "PRESENT")
|
|
241
|
+
acc.present += 1;
|
|
242
|
+
else if (r.status === "LATE")
|
|
243
|
+
acc.late += 1;
|
|
244
|
+
else
|
|
245
|
+
acc.absent += 1;
|
|
246
|
+
return acc;
|
|
247
|
+
}, { present: 0, late: 0, absent: 0, total: 0 });
|
|
248
|
+
return { summary, records };
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Read-only grades stub — returns assignment score rows for the linked child.
|
|
252
|
+
*/
|
|
253
|
+
export async function getChildGrades(userId, input) {
|
|
254
|
+
await assertParentOfStudent(input.schoolId, userId, input.studentUserId);
|
|
255
|
+
const classes = await prisma.class.findMany({
|
|
256
|
+
where: {
|
|
257
|
+
schoolId: input.schoolId,
|
|
258
|
+
students: { some: { id: input.studentUserId } },
|
|
259
|
+
...(input.classId ? { id: input.classId } : {}),
|
|
260
|
+
},
|
|
261
|
+
select: {
|
|
262
|
+
id: true,
|
|
263
|
+
name: true,
|
|
264
|
+
subject: true,
|
|
265
|
+
section: true,
|
|
266
|
+
assignments: {
|
|
267
|
+
select: {
|
|
268
|
+
id: true,
|
|
269
|
+
title: true,
|
|
270
|
+
type: true,
|
|
271
|
+
dueDate: true,
|
|
272
|
+
maxGrade: true,
|
|
273
|
+
submissions: {
|
|
274
|
+
where: { studentId: input.studentUserId },
|
|
275
|
+
select: {
|
|
276
|
+
id: true,
|
|
277
|
+
gradeReceived: true,
|
|
278
|
+
returned: true,
|
|
279
|
+
submittedAt: true,
|
|
280
|
+
},
|
|
281
|
+
take: 1,
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
orderBy: { dueDate: "desc" },
|
|
285
|
+
take: 100,
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
return {
|
|
290
|
+
classes: classes.map((cls) => ({
|
|
291
|
+
id: cls.id,
|
|
292
|
+
name: cls.name,
|
|
293
|
+
subject: cls.subject,
|
|
294
|
+
section: cls.section,
|
|
295
|
+
assignments: cls.assignments.map((a) => ({
|
|
296
|
+
id: a.id,
|
|
297
|
+
title: a.title,
|
|
298
|
+
type: a.type,
|
|
299
|
+
dueDate: a.dueDate,
|
|
300
|
+
maxGrade: a.maxGrade,
|
|
301
|
+
submission: a.submissions[0]
|
|
302
|
+
? {
|
|
303
|
+
id: a.submissions[0].id,
|
|
304
|
+
gradeReceived: a.submissions[0].gradeReceived,
|
|
305
|
+
returned: a.submissions[0].returned,
|
|
306
|
+
submittedAt: a.submissions[0].submittedAt,
|
|
307
|
+
}
|
|
308
|
+
: null,
|
|
309
|
+
})),
|
|
310
|
+
})),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/** Helper for other services: is this user a parent of the student? */
|
|
314
|
+
export async function isParentOfStudent(schoolId, parentUserId, studentUserId) {
|
|
315
|
+
const link = await prisma.parentStudentLink.findUnique({
|
|
316
|
+
where: {
|
|
317
|
+
schoolId_parentUserId_studentUserId: {
|
|
318
|
+
schoolId,
|
|
319
|
+
parentUserId,
|
|
320
|
+
studentUserId,
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
select: { id: true },
|
|
324
|
+
});
|
|
325
|
+
return Boolean(link);
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=parentPortal.js.map
|
|
328
|
+
//# debugId=3d43f9df-fb4b-55f0-948a-80ffd38d00d9
|
|
@@ -0,0 +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"}
|
|
@@ -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;
|
|
@@ -161,6 +176,7 @@ export declare function listClasses(userId: string, schoolId: string): Promise<{
|
|
|
161
176
|
discussionsEnabled: boolean;
|
|
162
177
|
studentsCanCreateDiscussions: boolean;
|
|
163
178
|
studentsCanReply: boolean;
|
|
179
|
+
sidebarFeatures: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
164
180
|
treatMissingAsZero: boolean;
|
|
165
181
|
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
166
182
|
})[];
|
|
@@ -210,6 +226,7 @@ export declare function createSchoolClass(userId: string, input: {
|
|
|
210
226
|
discussionsEnabled: boolean;
|
|
211
227
|
studentsCanCreateDiscussions: boolean;
|
|
212
228
|
studentsCanReply: boolean;
|
|
229
|
+
sidebarFeatures: import("@prisma/client/runtime/library.js").JsonValue | null;
|
|
213
230
|
treatMissingAsZero: boolean;
|
|
214
231
|
gradeRoundingMode: import(".prisma/client").$Enums.GradeRoundingMode;
|
|
215
232
|
};
|
|
@@ -384,7 +401,7 @@ export declare function confirmSchoolAssetUpload(userId: string, fileId: string,
|
|
|
384
401
|
*/
|
|
385
402
|
export declare function listMemberIds(userId: string, schoolId: string): Promise<string[]>;
|
|
386
403
|
/** Search active institution members by username, displayName, or email. */
|
|
387
|
-
export declare function searchMembers(userId: string, schoolId: string, query: string, limit?: number, role?:
|
|
404
|
+
export declare function searchMembers(userId: string, schoolId: string, query: string, limit?: number, role?: SchoolMembershipRole): Promise<{
|
|
388
405
|
id: string;
|
|
389
406
|
username: string;
|
|
390
407
|
profile: {
|
|
@@ -454,13 +471,17 @@ export declare function getStudentTimeline(userId: string, input: {
|
|
|
454
471
|
}[];
|
|
455
472
|
attendance: {
|
|
456
473
|
id: string;
|
|
474
|
+
recordId: string;
|
|
457
475
|
date: Date;
|
|
458
476
|
class: {
|
|
459
477
|
id: string;
|
|
460
478
|
name: string;
|
|
461
479
|
color: string | null;
|
|
462
480
|
};
|
|
463
|
-
status: "
|
|
481
|
+
status: import(".prisma/client").$Enums.AttendanceStatus;
|
|
482
|
+
note: string | null;
|
|
483
|
+
lockedFromExcusal: boolean;
|
|
484
|
+
reason: string | null;
|
|
464
485
|
}[];
|
|
465
486
|
}>;
|
|
466
487
|
/**
|
|
@@ -531,13 +552,17 @@ export declare function getMemberTimeline(userId: string, input: {
|
|
|
531
552
|
}[];
|
|
532
553
|
attendance: {
|
|
533
554
|
id: string;
|
|
555
|
+
recordId: string;
|
|
534
556
|
date: Date;
|
|
535
557
|
class: {
|
|
536
558
|
id: string;
|
|
537
559
|
name: string;
|
|
538
560
|
color: string | null;
|
|
539
561
|
};
|
|
540
|
-
status: "
|
|
562
|
+
status: import(".prisma/client").$Enums.AttendanceStatus;
|
|
563
|
+
note: string | null;
|
|
564
|
+
lockedFromExcusal: boolean;
|
|
565
|
+
reason: string | null;
|
|
541
566
|
}[];
|
|
542
567
|
classes?: undefined;
|
|
543
568
|
} | {
|