@studious-lms/server 1.0.6 → 1.0.7

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 (101) hide show
  1. package/API_SPECIFICATION.md +1117 -0
  2. package/dist/exportType.js +1 -2
  3. package/dist/index.js +25 -30
  4. package/dist/lib/fileUpload.d.ts.map +1 -1
  5. package/dist/lib/fileUpload.js +31 -29
  6. package/dist/lib/googleCloudStorage.js +9 -14
  7. package/dist/lib/prisma.js +4 -7
  8. package/dist/lib/thumbnailGenerator.js +12 -20
  9. package/dist/middleware/auth.d.ts.map +1 -1
  10. package/dist/middleware/auth.js +17 -22
  11. package/dist/middleware/logging.js +5 -9
  12. package/dist/routers/_app.d.ts +3483 -1801
  13. package/dist/routers/_app.d.ts.map +1 -1
  14. package/dist/routers/_app.js +28 -27
  15. package/dist/routers/agenda.d.ts +13 -8
  16. package/dist/routers/agenda.d.ts.map +1 -1
  17. package/dist/routers/agenda.js +14 -17
  18. package/dist/routers/announcement.d.ts +4 -3
  19. package/dist/routers/announcement.d.ts.map +1 -1
  20. package/dist/routers/announcement.js +28 -31
  21. package/dist/routers/assignment.d.ts +282 -196
  22. package/dist/routers/assignment.d.ts.map +1 -1
  23. package/dist/routers/assignment.js +256 -202
  24. package/dist/routers/attendance.d.ts +5 -4
  25. package/dist/routers/attendance.d.ts.map +1 -1
  26. package/dist/routers/attendance.js +31 -34
  27. package/dist/routers/auth.d.ts +1 -0
  28. package/dist/routers/auth.d.ts.map +1 -1
  29. package/dist/routers/auth.js +80 -75
  30. package/dist/routers/class.d.ts +284 -14
  31. package/dist/routers/class.d.ts.map +1 -1
  32. package/dist/routers/class.js +435 -164
  33. package/dist/routers/event.d.ts +47 -38
  34. package/dist/routers/event.d.ts.map +1 -1
  35. package/dist/routers/event.js +76 -79
  36. package/dist/routers/file.d.ts +71 -1
  37. package/dist/routers/file.d.ts.map +1 -1
  38. package/dist/routers/file.js +267 -32
  39. package/dist/routers/folder.d.ts +296 -0
  40. package/dist/routers/folder.d.ts.map +1 -0
  41. package/dist/routers/folder.js +693 -0
  42. package/dist/routers/notifications.d.ts +103 -0
  43. package/dist/routers/notifications.d.ts.map +1 -0
  44. package/dist/routers/notifications.js +91 -0
  45. package/dist/routers/school.d.ts +208 -0
  46. package/dist/routers/school.d.ts.map +1 -0
  47. package/dist/routers/school.js +481 -0
  48. package/dist/routers/section.d.ts +1 -0
  49. package/dist/routers/section.d.ts.map +1 -1
  50. package/dist/routers/section.js +30 -33
  51. package/dist/routers/user.d.ts +2 -1
  52. package/dist/routers/user.d.ts.map +1 -1
  53. package/dist/routers/user.js +21 -24
  54. package/dist/seedDatabase.d.ts +22 -0
  55. package/dist/seedDatabase.d.ts.map +1 -0
  56. package/dist/seedDatabase.js +57 -0
  57. package/dist/socket/handlers.js +26 -30
  58. package/dist/trpc.d.ts +5 -0
  59. package/dist/trpc.d.ts.map +1 -1
  60. package/dist/trpc.js +35 -26
  61. package/dist/types/trpc.js +1 -2
  62. package/dist/utils/email.js +2 -8
  63. package/dist/utils/generateInviteCode.js +1 -5
  64. package/dist/utils/logger.d.ts.map +1 -1
  65. package/dist/utils/logger.js +13 -9
  66. package/dist/utils/prismaErrorHandler.d.ts +9 -0
  67. package/dist/utils/prismaErrorHandler.d.ts.map +1 -0
  68. package/dist/utils/prismaErrorHandler.js +234 -0
  69. package/dist/utils/prismaWrapper.d.ts +14 -0
  70. package/dist/utils/prismaWrapper.d.ts.map +1 -0
  71. package/dist/utils/prismaWrapper.js +64 -0
  72. package/package.json +11 -4
  73. package/prisma/migrations/20250807062924_init/migration.sql +436 -0
  74. package/prisma/migrations/migration_lock.toml +3 -0
  75. package/prisma/schema.prisma +67 -0
  76. package/src/index.ts +2 -2
  77. package/src/lib/fileUpload.ts +16 -7
  78. package/src/middleware/auth.ts +0 -2
  79. package/src/routers/_app.ts +5 -1
  80. package/src/routers/assignment.ts +82 -22
  81. package/src/routers/auth.ts +80 -54
  82. package/src/routers/class.ts +330 -36
  83. package/src/routers/file.ts +283 -20
  84. package/src/routers/folder.ts +755 -0
  85. package/src/routers/notifications.ts +93 -0
  86. package/src/seedDatabase.ts +66 -0
  87. package/src/socket/handlers.ts +4 -4
  88. package/src/trpc.ts +13 -0
  89. package/src/utils/logger.ts +14 -4
  90. package/src/utils/prismaErrorHandler.ts +275 -0
  91. package/src/utils/prismaWrapper.ts +91 -0
  92. package/tests/auth.test.ts +25 -0
  93. package/tests/class.test.ts +281 -0
  94. package/tests/setup.ts +98 -0
  95. package/tests/startup.test.ts +5 -0
  96. package/tsconfig.json +2 -1
  97. package/vitest.config.ts +11 -0
  98. package/dist/logger.d.ts +0 -26
  99. package/dist/logger.d.ts.map +0 -1
  100. package/dist/logger.js +0 -135
  101. package/src/logger.ts +0 -163
@@ -1,90 +1,93 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.assignmentRouter = void 0;
4
- const zod_1 = require("zod");
5
- const trpc_1 = require("../trpc");
6
- const server_1 = require("@trpc/server");
7
- const prisma_1 = require("../lib/prisma");
8
- const fileUpload_1 = require("../lib/fileUpload");
9
- const googleCloudStorage_1 = require("../lib/googleCloudStorage");
10
- const fileSchema = zod_1.z.object({
11
- name: zod_1.z.string(),
12
- type: zod_1.z.string(),
13
- size: zod_1.z.number(),
14
- data: zod_1.z.string(), // base64 encoded file data
1
+ import { z } from "zod";
2
+ import { createTRPCRouter, protectedProcedure, protectedClassMemberProcedure, protectedTeacherProcedure } from "../trpc";
3
+ import { TRPCError } from "@trpc/server";
4
+ import { prisma } from "../lib/prisma";
5
+ import { uploadFiles } from "../lib/fileUpload";
6
+ import { deleteFile } from "../lib/googleCloudStorage";
7
+ const fileSchema = z.object({
8
+ name: z.string(),
9
+ type: z.string(),
10
+ size: z.number(),
11
+ data: z.string(), // base64 encoded file data
15
12
  });
16
- const createAssignmentSchema = zod_1.z.object({
17
- classId: zod_1.z.string(),
18
- title: zod_1.z.string(),
19
- instructions: zod_1.z.string(),
20
- dueDate: zod_1.z.string(),
21
- files: zod_1.z.array(fileSchema).optional(),
22
- maxGrade: zod_1.z.number().optional(),
23
- graded: zod_1.z.boolean().optional(),
24
- weight: zod_1.z.number().optional(),
25
- sectionId: zod_1.z.string().optional(),
26
- type: zod_1.z.enum(['HOMEWORK', 'QUIZ', 'TEST', 'PROJECT', 'ESSAY', 'DISCUSSION', 'PRESENTATION', 'LAB', 'OTHER']).optional(),
27
- markSchemeId: zod_1.z.string().optional(),
28
- gradingBoundaryId: zod_1.z.string().optional(),
13
+ const createAssignmentSchema = z.object({
14
+ classId: z.string(),
15
+ title: z.string(),
16
+ instructions: z.string(),
17
+ dueDate: z.string(),
18
+ files: z.array(fileSchema).optional(),
19
+ existingFileIds: z.array(z.string()).optional(),
20
+ maxGrade: z.number().optional(),
21
+ graded: z.boolean().optional(),
22
+ weight: z.number().optional(),
23
+ sectionId: z.string().optional(),
24
+ type: z.enum(['HOMEWORK', 'QUIZ', 'TEST', 'PROJECT', 'ESSAY', 'DISCUSSION', 'PRESENTATION', 'LAB', 'OTHER']).optional(),
25
+ markSchemeId: z.string().optional(),
26
+ gradingBoundaryId: z.string().optional(),
27
+ inProgress: z.boolean().optional(),
29
28
  });
30
- const updateAssignmentSchema = zod_1.z.object({
31
- classId: zod_1.z.string(),
32
- id: zod_1.z.string(),
33
- title: zod_1.z.string().optional(),
34
- instructions: zod_1.z.string().optional(),
35
- dueDate: zod_1.z.string().optional(),
36
- files: zod_1.z.array(fileSchema).optional(),
37
- removedAttachments: zod_1.z.array(zod_1.z.string()).optional(),
38
- maxGrade: zod_1.z.number().optional(),
39
- graded: zod_1.z.boolean().optional(),
40
- weight: zod_1.z.number().optional(),
41
- sectionId: zod_1.z.string().nullable().optional(),
42
- type: zod_1.z.enum(['HOMEWORK', 'QUIZ', 'TEST', 'PROJECT', 'ESSAY', 'DISCUSSION', 'PRESENTATION', 'LAB', 'OTHER']).optional(),
29
+ const updateAssignmentSchema = z.object({
30
+ classId: z.string(),
31
+ id: z.string(),
32
+ title: z.string().optional(),
33
+ instructions: z.string().optional(),
34
+ dueDate: z.string().optional(),
35
+ files: z.array(fileSchema).optional(),
36
+ existingFileIds: z.array(z.string()).optional(),
37
+ removedAttachments: z.array(z.string()).optional(),
38
+ maxGrade: z.number().optional(),
39
+ graded: z.boolean().optional(),
40
+ weight: z.number().optional(),
41
+ sectionId: z.string().nullable().optional(),
42
+ type: z.enum(['HOMEWORK', 'QUIZ', 'TEST', 'PROJECT', 'ESSAY', 'DISCUSSION', 'PRESENTATION', 'LAB', 'OTHER']).optional(),
43
+ inProgress: z.boolean().optional(),
43
44
  });
44
- const deleteAssignmentSchema = zod_1.z.object({
45
- id: zod_1.z.string(),
46
- classId: zod_1.z.string(),
45
+ const deleteAssignmentSchema = z.object({
46
+ id: z.string(),
47
+ classId: z.string(),
47
48
  });
48
- const getAssignmentSchema = zod_1.z.object({
49
- id: zod_1.z.string(),
50
- classId: zod_1.z.string(),
49
+ const getAssignmentSchema = z.object({
50
+ id: z.string(),
51
+ classId: z.string(),
51
52
  });
52
- const submissionSchema = zod_1.z.object({
53
- assignmentId: zod_1.z.string(),
54
- classId: zod_1.z.string(),
55
- submissionId: zod_1.z.string(),
56
- submit: zod_1.z.boolean().optional(),
57
- newAttachments: zod_1.z.array(fileSchema).optional(),
58
- removedAttachments: zod_1.z.array(zod_1.z.string()).optional(),
53
+ const submissionSchema = z.object({
54
+ assignmentId: z.string(),
55
+ classId: z.string(),
56
+ submissionId: z.string(),
57
+ submit: z.boolean().optional(),
58
+ newAttachments: z.array(fileSchema).optional(),
59
+ existingFileIds: z.array(z.string()).optional(),
60
+ removedAttachments: z.array(z.string()).optional(),
59
61
  });
60
- const updateSubmissionSchema = zod_1.z.object({
61
- assignmentId: zod_1.z.string(),
62
- classId: zod_1.z.string(),
63
- submissionId: zod_1.z.string(),
64
- return: zod_1.z.boolean().optional(),
65
- gradeReceived: zod_1.z.number().nullable().optional(),
66
- newAttachments: zod_1.z.array(fileSchema).optional(),
67
- removedAttachments: zod_1.z.array(zod_1.z.string()).optional(),
68
- rubricGrades: zod_1.z.array(zod_1.z.object({
69
- criteriaId: zod_1.z.string(),
70
- selectedLevelId: zod_1.z.string(),
71
- points: zod_1.z.number(),
72
- comments: zod_1.z.string(),
62
+ const updateSubmissionSchema = z.object({
63
+ assignmentId: z.string(),
64
+ classId: z.string(),
65
+ submissionId: z.string(),
66
+ return: z.boolean().optional(),
67
+ gradeReceived: z.number().nullable().optional(),
68
+ newAttachments: z.array(fileSchema).optional(),
69
+ existingFileIds: z.array(z.string()).optional(),
70
+ removedAttachments: z.array(z.string()).optional(),
71
+ rubricGrades: z.array(z.object({
72
+ criteriaId: z.string(),
73
+ selectedLevelId: z.string(),
74
+ points: z.number(),
75
+ comments: z.string(),
73
76
  })).optional(),
74
77
  });
75
- exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
76
- create: trpc_1.protectedProcedure
78
+ export const assignmentRouter = createTRPCRouter({
79
+ create: protectedProcedure
77
80
  .input(createAssignmentSchema)
78
81
  .mutation(async ({ ctx, input }) => {
79
- const { classId, title, instructions, dueDate, files, maxGrade, graded, weight, sectionId, type, markSchemeId, gradingBoundaryId } = input;
82
+ const { classId, title, instructions, dueDate, files, existingFileIds, maxGrade, graded, weight, sectionId, type, markSchemeId, gradingBoundaryId, inProgress } = input;
80
83
  if (!ctx.user) {
81
- throw new server_1.TRPCError({
84
+ throw new TRPCError({
82
85
  code: "UNAUTHORIZED",
83
86
  message: "User must be authenticated",
84
87
  });
85
88
  }
86
89
  // Get all students in the class
87
- const classData = await prisma_1.prisma.class.findUnique({
90
+ const classData = await prisma.class.findUnique({
88
91
  where: { id: classId },
89
92
  include: {
90
93
  students: {
@@ -93,25 +96,29 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
93
96
  }
94
97
  });
95
98
  if (!classData) {
96
- throw new server_1.TRPCError({
99
+ throw new TRPCError({
97
100
  code: "NOT_FOUND",
98
101
  message: "Class not found",
99
102
  });
100
103
  }
101
- const rubric = await prisma_1.prisma.markScheme.findUnique({
102
- where: { id: markSchemeId },
103
- select: {
104
- structured: true,
105
- }
106
- });
107
- const parsedRubric = JSON.parse(rubric?.structured || "{}");
108
- // Calculate max grade from rubric criteria levels
109
- const computedMaxGrade = parsedRubric.criteria.reduce((acc, criterion) => {
110
- const maxPoints = Math.max(...criterion.levels.map((level) => level.points));
111
- return acc + maxPoints;
112
- }, 0);
104
+ let computedMaxGrade = maxGrade;
105
+ if (markSchemeId) {
106
+ const rubric = await prisma.markScheme.findUnique({
107
+ where: { id: markSchemeId },
108
+ select: {
109
+ structured: true,
110
+ }
111
+ });
112
+ const parsedRubric = JSON.parse(rubric?.structured || "{}");
113
+ // Calculate max grade from rubric criteria levels
114
+ computedMaxGrade = parsedRubric.criteria.reduce((acc, criterion) => {
115
+ const maxPoints = Math.max(...criterion.levels.map((level) => level.points));
116
+ return acc + maxPoints;
117
+ }, 0);
118
+ }
119
+ console.log(markSchemeId, gradingBoundaryId);
113
120
  // Create assignment with submissions for all students
114
- const assignment = await prisma_1.prisma.assignment.create({
121
+ const assignment = await prisma.assignment.create({
115
122
  data: {
116
123
  title,
117
124
  instructions,
@@ -120,6 +127,7 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
120
127
  graded,
121
128
  weight,
122
129
  type,
130
+ inProgress: inProgress || false,
123
131
  class: {
124
132
  connect: { id: classId }
125
133
  },
@@ -189,11 +197,11 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
189
197
  let uploadedFiles = [];
190
198
  if (files && files.length > 0) {
191
199
  // Store files in a class and assignment specific directory
192
- uploadedFiles = await (0, fileUpload_1.uploadFiles)(files, ctx.user.id, `class/${classId}/assignment/${assignment.id}`);
200
+ uploadedFiles = await uploadFiles(files, ctx.user.id);
193
201
  }
194
202
  // Update assignment with new file attachments
195
203
  if (uploadedFiles.length > 0) {
196
- await prisma_1.prisma.assignment.update({
204
+ await prisma.assignment.update({
197
205
  where: { id: assignment.id },
198
206
  data: {
199
207
  attachments: {
@@ -212,20 +220,31 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
212
220
  }
213
221
  });
214
222
  }
223
+ // Connect existing files if provided
224
+ if (existingFileIds && existingFileIds.length > 0) {
225
+ await prisma.assignment.update({
226
+ where: { id: assignment.id },
227
+ data: {
228
+ attachments: {
229
+ connect: existingFileIds.map(fileId => ({ id: fileId }))
230
+ }
231
+ }
232
+ });
233
+ }
215
234
  return assignment;
216
235
  }),
217
- update: trpc_1.protectedProcedure
236
+ update: protectedProcedure
218
237
  .input(updateAssignmentSchema)
219
238
  .mutation(async ({ ctx, input }) => {
220
- const { id, title, instructions, dueDate, files, maxGrade, graded, weight, sectionId, type } = input;
239
+ const { id, title, instructions, dueDate, files, existingFileIds, maxGrade, graded, weight, sectionId, type, inProgress } = input;
221
240
  if (!ctx.user) {
222
- throw new server_1.TRPCError({
241
+ throw new TRPCError({
223
242
  code: "UNAUTHORIZED",
224
243
  message: "User must be authenticated",
225
244
  });
226
245
  }
227
246
  // Get the assignment with current attachments
228
- const assignment = await prisma_1.prisma.assignment.findFirst({
247
+ const assignment = await prisma.assignment.findFirst({
229
248
  where: {
230
249
  id,
231
250
  teacherId: ctx.user.id,
@@ -236,6 +255,8 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
236
255
  id: true,
237
256
  name: true,
238
257
  type: true,
258
+ path: true,
259
+ size: true,
239
260
  thumbnail: {
240
261
  select: {
241
262
  path: true
@@ -252,7 +273,7 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
252
273
  },
253
274
  });
254
275
  if (!assignment) {
255
- throw new server_1.TRPCError({
276
+ throw new TRPCError({
256
277
  code: "NOT_FOUND",
257
278
  message: "Assignment not found",
258
279
  });
@@ -261,10 +282,10 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
261
282
  let uploadedFiles = [];
262
283
  if (files && files.length > 0) {
263
284
  // Store files in a class and assignment specific directory
264
- uploadedFiles = await (0, fileUpload_1.uploadFiles)(files, ctx.user.id, `class/${assignment.classId}/assignment/${id}`);
285
+ uploadedFiles = await uploadFiles(files, ctx.user.id);
265
286
  }
266
287
  // Update assignment
267
- const updatedAssignment = await prisma_1.prisma.assignment.update({
288
+ const updatedAssignment = await prisma.assignment.update({
268
289
  where: { id },
269
290
  data: {
270
291
  ...(title && { title }),
@@ -274,6 +295,7 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
274
295
  ...(graded !== undefined && { graded }),
275
296
  ...(weight && { weight }),
276
297
  ...(type && { type }),
298
+ ...(inProgress !== undefined && { inProgress }),
277
299
  ...(sectionId !== undefined && {
278
300
  section: sectionId ? {
279
301
  connect: { id: sectionId }
@@ -296,6 +318,11 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
296
318
  }))
297
319
  }
298
320
  }),
321
+ ...(existingFileIds && existingFileIds.length > 0 && {
322
+ attachments: {
323
+ connect: existingFileIds.map(fileId => ({ id: fileId }))
324
+ }
325
+ }),
299
326
  ...(input.removedAttachments && input.removedAttachments.length > 0 && {
300
327
  attachments: {
301
328
  deleteMany: {
@@ -329,7 +356,11 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
329
356
  id: true,
330
357
  name: true,
331
358
  type: true,
332
- thumbnail: true
359
+ thumbnail: true,
360
+ size: true,
361
+ path: true,
362
+ uploadedAt: true,
363
+ thumbnailId: true,
333
364
  }
334
365
  },
335
366
  section: true,
@@ -338,7 +369,7 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
338
369
  }
339
370
  });
340
371
  if (assignment.markSchemeId) {
341
- const rubric = await prisma_1.prisma.markScheme.findUnique({
372
+ const rubric = await prisma.markScheme.findUnique({
342
373
  where: { id: assignment.markSchemeId },
343
374
  select: {
344
375
  structured: true,
@@ -349,7 +380,7 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
349
380
  const maxPoints = Math.max(...criterion.levels.map((level) => level.points));
350
381
  return acc + maxPoints;
351
382
  }, 0);
352
- await prisma_1.prisma.assignment.update({
383
+ await prisma.assignment.update({
353
384
  where: { id },
354
385
  data: {
355
386
  maxGrade: computedMaxGrade,
@@ -358,18 +389,18 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
358
389
  }
359
390
  return updatedAssignment;
360
391
  }),
361
- delete: trpc_1.protectedProcedure
392
+ delete: protectedProcedure
362
393
  .input(deleteAssignmentSchema)
363
394
  .mutation(async ({ ctx, input }) => {
364
395
  const { id, classId } = input;
365
396
  if (!ctx.user) {
366
- throw new server_1.TRPCError({
397
+ throw new TRPCError({
367
398
  code: "UNAUTHORIZED",
368
399
  message: "User must be authenticated",
369
400
  });
370
401
  }
371
402
  // Get the assignment with all related files
372
- const assignment = await prisma_1.prisma.assignment.findFirst({
403
+ const assignment = await prisma.assignment.findFirst({
373
404
  where: {
374
405
  id,
375
406
  teacherId: ctx.user.id,
@@ -397,7 +428,7 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
397
428
  }
398
429
  });
399
430
  if (!assignment) {
400
- throw new server_1.TRPCError({
431
+ throw new TRPCError({
401
432
  code: "NOT_FOUND",
402
433
  message: "Assignment not found",
403
434
  });
@@ -411,10 +442,10 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
411
442
  await Promise.all(filesToDelete.map(async (file) => {
412
443
  try {
413
444
  // Delete the main file
414
- await (0, googleCloudStorage_1.deleteFile)(file.path);
445
+ await deleteFile(file.path);
415
446
  // Delete thumbnail if it exists
416
447
  if (file.thumbnail) {
417
- await (0, googleCloudStorage_1.deleteFile)(file.thumbnail.path);
448
+ await deleteFile(file.thumbnail.path);
418
449
  }
419
450
  }
420
451
  catch (error) {
@@ -422,24 +453,24 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
422
453
  }
423
454
  }));
424
455
  // Delete the assignment (this will cascade delete all related records)
425
- await prisma_1.prisma.assignment.delete({
456
+ await prisma.assignment.delete({
426
457
  where: { id },
427
458
  });
428
459
  return {
429
460
  id,
430
461
  };
431
462
  }),
432
- get: trpc_1.protectedProcedure
463
+ get: protectedProcedure
433
464
  .input(getAssignmentSchema)
434
465
  .query(async ({ ctx, input }) => {
435
466
  const { id, classId } = input;
436
467
  if (!ctx.user) {
437
- throw new server_1.TRPCError({
468
+ throw new TRPCError({
438
469
  code: "UNAUTHORIZED",
439
470
  message: "User must be authenticated",
440
471
  });
441
472
  }
442
- const assignment = await prisma_1.prisma.assignment.findUnique({
473
+ const assignment = await prisma.assignment.findUnique({
443
474
  where: {
444
475
  id,
445
476
  // classId,
@@ -462,6 +493,7 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
462
493
  type: true,
463
494
  size: true,
464
495
  path: true,
496
+ uploadedAt: true,
465
497
  thumbnailId: true,
466
498
  }
467
499
  },
@@ -508,12 +540,12 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
508
540
  }
509
541
  });
510
542
  if (!assignment) {
511
- throw new server_1.TRPCError({
543
+ throw new TRPCError({
512
544
  code: "NOT_FOUND",
513
545
  message: "Assignment not found",
514
546
  });
515
547
  }
516
- const sections = await prisma_1.prisma.section.findMany({
548
+ const sections = await prisma.section.findMany({
517
549
  where: {
518
550
  classId: assignment.classId,
519
551
  },
@@ -524,20 +556,20 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
524
556
  });
525
557
  return { ...assignment, sections };
526
558
  }),
527
- getSubmission: trpc_1.protectedClassMemberProcedure
528
- .input(zod_1.z.object({
529
- assignmentId: zod_1.z.string(),
530
- classId: zod_1.z.string(),
559
+ getSubmission: protectedClassMemberProcedure
560
+ .input(z.object({
561
+ assignmentId: z.string(),
562
+ classId: z.string(),
531
563
  }))
532
564
  .query(async ({ ctx, input }) => {
533
565
  if (!ctx.user) {
534
- throw new server_1.TRPCError({
566
+ throw new TRPCError({
535
567
  code: "UNAUTHORIZED",
536
568
  message: "User must be authenticated",
537
569
  });
538
570
  }
539
571
  const { assignmentId } = input;
540
- const submission = await prisma_1.prisma.submission.findFirst({
572
+ const submission = await prisma.submission.findFirst({
541
573
  where: {
542
574
  assignmentId,
543
575
  studentId: ctx.user.id,
@@ -572,7 +604,7 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
572
604
  });
573
605
  if (!submission) {
574
606
  // Create a new submission if it doesn't exist
575
- return await prisma_1.prisma.submission.create({
607
+ return await prisma.submission.create({
576
608
  data: {
577
609
  assignment: {
578
610
  connect: { id: assignmentId },
@@ -615,20 +647,20 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
615
647
  late: submission.assignment.dueDate < new Date(),
616
648
  };
617
649
  }),
618
- getSubmissionById: trpc_1.protectedTeacherProcedure
619
- .input(zod_1.z.object({
620
- submissionId: zod_1.z.string(),
621
- classId: zod_1.z.string(),
650
+ getSubmissionById: protectedTeacherProcedure
651
+ .input(z.object({
652
+ submissionId: z.string(),
653
+ classId: z.string(),
622
654
  }))
623
655
  .query(async ({ ctx, input }) => {
624
656
  if (!ctx.user) {
625
- throw new server_1.TRPCError({
657
+ throw new TRPCError({
626
658
  code: "UNAUTHORIZED",
627
659
  message: "User must be authenticated",
628
660
  });
629
661
  }
630
662
  const { submissionId, classId } = input;
631
- const submission = await prisma_1.prisma.submission.findFirst({
663
+ const submission = await prisma.submission.findFirst({
632
664
  where: {
633
665
  id: submissionId,
634
666
  assignment: {
@@ -671,7 +703,7 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
671
703
  },
672
704
  });
673
705
  if (!submission) {
674
- throw new server_1.TRPCError({
706
+ throw new TRPCError({
675
707
  code: "NOT_FOUND",
676
708
  message: "Submission not found",
677
709
  });
@@ -681,17 +713,17 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
681
713
  late: submission.assignment.dueDate < new Date(),
682
714
  };
683
715
  }),
684
- updateSubmission: trpc_1.protectedClassMemberProcedure
716
+ updateSubmission: protectedClassMemberProcedure
685
717
  .input(submissionSchema)
686
718
  .mutation(async ({ ctx, input }) => {
687
719
  if (!ctx.user) {
688
- throw new server_1.TRPCError({
720
+ throw new TRPCError({
689
721
  code: "UNAUTHORIZED",
690
722
  message: "User must be authenticated",
691
723
  });
692
724
  }
693
- const { submissionId, submit, newAttachments, removedAttachments } = input;
694
- const submission = await prisma_1.prisma.submission.findFirst({
725
+ const { submissionId, submit, newAttachments, existingFileIds, removedAttachments } = input;
726
+ const submission = await prisma.submission.findFirst({
695
727
  where: {
696
728
  id: submissionId,
697
729
  OR: [
@@ -739,14 +771,14 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
739
771
  },
740
772
  });
741
773
  if (!submission) {
742
- throw new server_1.TRPCError({
774
+ throw new TRPCError({
743
775
  code: "NOT_FOUND",
744
776
  message: "Submission not found",
745
777
  });
746
778
  }
747
779
  if (submit !== undefined) {
748
780
  // Toggle submission status
749
- return await prisma_1.prisma.submission.update({
781
+ return await prisma.submission.update({
750
782
  where: { id: submission.id },
751
783
  data: {
752
784
  submitted: !submission.submitted,
@@ -783,11 +815,11 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
783
815
  let uploadedFiles = [];
784
816
  if (newAttachments && newAttachments.length > 0) {
785
817
  // Store files in a class and assignment specific directory
786
- uploadedFiles = await (0, fileUpload_1.uploadFiles)(newAttachments, ctx.user.id, `class/${submission.assignment.classId}/assignment/${submission.assignmentId}/submission/${submission.id}`);
818
+ uploadedFiles = await uploadFiles(newAttachments, ctx.user.id);
787
819
  }
788
820
  // Update submission with new file attachments
789
821
  if (uploadedFiles.length > 0) {
790
- await prisma_1.prisma.submission.update({
822
+ await prisma.submission.update({
791
823
  where: { id: submission.id },
792
824
  data: {
793
825
  attachments: {
@@ -806,6 +838,17 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
806
838
  }
807
839
  });
808
840
  }
841
+ // Connect existing files if provided
842
+ if (existingFileIds && existingFileIds.length > 0) {
843
+ await prisma.submission.update({
844
+ where: { id: submission.id },
845
+ data: {
846
+ attachments: {
847
+ connect: existingFileIds.map(fileId => ({ id: fileId }))
848
+ }
849
+ }
850
+ });
851
+ }
809
852
  // Delete removed attachments if any
810
853
  if (removedAttachments && removedAttachments.length > 0) {
811
854
  const filesToDelete = submission.attachments.filter((file) => removedAttachments.includes(file.id));
@@ -813,10 +856,10 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
813
856
  await Promise.all(filesToDelete.map(async (file) => {
814
857
  try {
815
858
  // Delete the main file
816
- await (0, googleCloudStorage_1.deleteFile)(file.path);
859
+ await deleteFile(file.path);
817
860
  // Delete thumbnail if it exists
818
861
  if (file.thumbnail?.path) {
819
- await (0, googleCloudStorage_1.deleteFile)(file.thumbnail.path);
862
+ await deleteFile(file.thumbnail.path);
820
863
  }
821
864
  }
822
865
  catch (error) {
@@ -825,7 +868,7 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
825
868
  }));
826
869
  }
827
870
  // Update submission with attachments
828
- return await prisma_1.prisma.submission.update({
871
+ return await prisma.submission.update({
829
872
  where: { id: submission.id },
830
873
  data: {
831
874
  ...(removedAttachments && removedAttachments.length > 0 && {
@@ -868,20 +911,20 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
868
911
  },
869
912
  });
870
913
  }),
871
- getSubmissions: trpc_1.protectedTeacherProcedure
872
- .input(zod_1.z.object({
873
- assignmentId: zod_1.z.string(),
874
- classId: zod_1.z.string(),
914
+ getSubmissions: protectedTeacherProcedure
915
+ .input(z.object({
916
+ assignmentId: z.string(),
917
+ classId: z.string(),
875
918
  }))
876
919
  .query(async ({ ctx, input }) => {
877
920
  if (!ctx.user) {
878
- throw new server_1.TRPCError({
921
+ throw new TRPCError({
879
922
  code: "UNAUTHORIZED",
880
923
  message: "User must be authenticated",
881
924
  });
882
925
  }
883
926
  const { assignmentId } = input;
884
- const submissions = await prisma_1.prisma.submission.findMany({
927
+ const submissions = await prisma.submission.findMany({
885
928
  where: {
886
929
  assignment: {
887
930
  id: assignmentId,
@@ -928,17 +971,17 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
928
971
  late: submission.assignment.dueDate < new Date(),
929
972
  }));
930
973
  }),
931
- updateSubmissionAsTeacher: trpc_1.protectedTeacherProcedure
974
+ updateSubmissionAsTeacher: protectedTeacherProcedure
932
975
  .input(updateSubmissionSchema)
933
976
  .mutation(async ({ ctx, input }) => {
934
977
  if (!ctx.user) {
935
- throw new server_1.TRPCError({
978
+ throw new TRPCError({
936
979
  code: "UNAUTHORIZED",
937
980
  message: "User must be authenticated",
938
981
  });
939
982
  }
940
- const { submissionId, return: returnSubmission, gradeReceived, newAttachments, removedAttachments, rubricGrades } = input;
941
- const submission = await prisma_1.prisma.submission.findFirst({
983
+ const { submissionId, return: returnSubmission, gradeReceived, newAttachments, existingFileIds, removedAttachments, rubricGrades } = input;
984
+ const submission = await prisma.submission.findFirst({
942
985
  where: {
943
986
  id: submissionId,
944
987
  assignment: {
@@ -980,14 +1023,14 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
980
1023
  },
981
1024
  });
982
1025
  if (!submission) {
983
- throw new server_1.TRPCError({
1026
+ throw new TRPCError({
984
1027
  code: "NOT_FOUND",
985
1028
  message: "Submission not found",
986
1029
  });
987
1030
  }
988
1031
  if (returnSubmission !== undefined) {
989
1032
  // Toggle return status
990
- return await prisma_1.prisma.submission.update({
1033
+ return await prisma.submission.update({
991
1034
  where: { id: submissionId },
992
1035
  data: {
993
1036
  returned: !submission.returned,
@@ -1023,11 +1066,11 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1023
1066
  let uploadedFiles = [];
1024
1067
  if (newAttachments && newAttachments.length > 0) {
1025
1068
  // Store files in a class and assignment specific directory
1026
- uploadedFiles = await (0, fileUpload_1.uploadFiles)(newAttachments, ctx.user.id, `class/${submission.assignment.classId}/assignment/${submission.assignmentId}/submission/${submission.id}/annotations`);
1069
+ uploadedFiles = await uploadFiles(newAttachments, ctx.user.id);
1027
1070
  }
1028
1071
  // Update submission with new file attachments
1029
1072
  if (uploadedFiles.length > 0) {
1030
- await prisma_1.prisma.submission.update({
1073
+ await prisma.submission.update({
1031
1074
  where: { id: submission.id },
1032
1075
  data: {
1033
1076
  annotations: {
@@ -1046,6 +1089,17 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1046
1089
  }
1047
1090
  });
1048
1091
  }
1092
+ // Connect existing files if provided
1093
+ if (existingFileIds && existingFileIds.length > 0) {
1094
+ await prisma.submission.update({
1095
+ where: { id: submission.id },
1096
+ data: {
1097
+ annotations: {
1098
+ connect: existingFileIds.map(fileId => ({ id: fileId }))
1099
+ }
1100
+ }
1101
+ });
1102
+ }
1049
1103
  // Delete removed attachments if any
1050
1104
  if (removedAttachments && removedAttachments.length > 0) {
1051
1105
  const filesToDelete = submission.annotations.filter((file) => removedAttachments.includes(file.id));
@@ -1053,10 +1107,10 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1053
1107
  await Promise.all(filesToDelete.map(async (file) => {
1054
1108
  try {
1055
1109
  // Delete the main file
1056
- await (0, googleCloudStorage_1.deleteFile)(file.path);
1110
+ await deleteFile(file.path);
1057
1111
  // Delete thumbnail if it exists
1058
1112
  if (file.thumbnail?.path) {
1059
- await (0, googleCloudStorage_1.deleteFile)(file.thumbnail.path);
1113
+ await deleteFile(file.thumbnail.path);
1060
1114
  }
1061
1115
  }
1062
1116
  catch (error) {
@@ -1065,7 +1119,7 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1065
1119
  }));
1066
1120
  }
1067
1121
  // Update submission with grade and attachments
1068
- return await prisma_1.prisma.submission.update({
1122
+ return await prisma.submission.update({
1069
1123
  where: { id: submissionId },
1070
1124
  data: {
1071
1125
  ...(gradeReceived !== undefined && { gradeReceived }),
@@ -1115,21 +1169,21 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1115
1169
  },
1116
1170
  });
1117
1171
  }),
1118
- attachToEvent: trpc_1.protectedTeacherProcedure
1119
- .input(zod_1.z.object({
1120
- assignmentId: zod_1.z.string(),
1121
- eventId: zod_1.z.string(),
1172
+ attachToEvent: protectedTeacherProcedure
1173
+ .input(z.object({
1174
+ assignmentId: z.string(),
1175
+ eventId: z.string(),
1122
1176
  }))
1123
1177
  .mutation(async ({ ctx, input }) => {
1124
1178
  if (!ctx.user) {
1125
- throw new server_1.TRPCError({
1179
+ throw new TRPCError({
1126
1180
  code: "UNAUTHORIZED",
1127
1181
  message: "User must be authenticated",
1128
1182
  });
1129
1183
  }
1130
1184
  const { assignmentId, eventId } = input;
1131
1185
  // Check if assignment exists and user is a teacher of the class
1132
- const assignment = await prisma_1.prisma.assignment.findFirst({
1186
+ const assignment = await prisma.assignment.findFirst({
1133
1187
  where: {
1134
1188
  id: assignmentId,
1135
1189
  class: {
@@ -1143,26 +1197,26 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1143
1197
  },
1144
1198
  });
1145
1199
  if (!assignment) {
1146
- throw new server_1.TRPCError({
1200
+ throw new TRPCError({
1147
1201
  code: "NOT_FOUND",
1148
1202
  message: "Assignment not found or you are not authorized",
1149
1203
  });
1150
1204
  }
1151
1205
  // Check if event exists and belongs to the same class
1152
- const event = await prisma_1.prisma.event.findFirst({
1206
+ const event = await prisma.event.findFirst({
1153
1207
  where: {
1154
1208
  id: eventId,
1155
1209
  classId: assignment.classId,
1156
1210
  },
1157
1211
  });
1158
1212
  if (!event) {
1159
- throw new server_1.TRPCError({
1213
+ throw new TRPCError({
1160
1214
  code: "NOT_FOUND",
1161
1215
  message: "Event not found or does not belong to the same class",
1162
1216
  });
1163
1217
  }
1164
1218
  // Attach assignment to event
1165
- const updatedAssignment = await prisma_1.prisma.assignment.update({
1219
+ const updatedAssignment = await prisma.assignment.update({
1166
1220
  where: { id: assignmentId },
1167
1221
  data: {
1168
1222
  eventAttached: {
@@ -1201,20 +1255,20 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1201
1255
  });
1202
1256
  return { assignment: updatedAssignment };
1203
1257
  }),
1204
- detachEvent: trpc_1.protectedTeacherProcedure
1205
- .input(zod_1.z.object({
1206
- assignmentId: zod_1.z.string(),
1258
+ detachEvent: protectedTeacherProcedure
1259
+ .input(z.object({
1260
+ assignmentId: z.string(),
1207
1261
  }))
1208
1262
  .mutation(async ({ ctx, input }) => {
1209
1263
  if (!ctx.user) {
1210
- throw new server_1.TRPCError({
1264
+ throw new TRPCError({
1211
1265
  code: "UNAUTHORIZED",
1212
1266
  message: "User must be authenticated",
1213
1267
  });
1214
1268
  }
1215
1269
  const { assignmentId } = input;
1216
1270
  // Check if assignment exists and user is a teacher of the class
1217
- const assignment = await prisma_1.prisma.assignment.findFirst({
1271
+ const assignment = await prisma.assignment.findFirst({
1218
1272
  where: {
1219
1273
  id: assignmentId,
1220
1274
  class: {
@@ -1225,13 +1279,13 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1225
1279
  },
1226
1280
  });
1227
1281
  if (!assignment) {
1228
- throw new server_1.TRPCError({
1282
+ throw new TRPCError({
1229
1283
  code: "NOT_FOUND",
1230
1284
  message: "Assignment not found or you are not authorized",
1231
1285
  });
1232
1286
  }
1233
1287
  // Detach assignment from event
1234
- const updatedAssignment = await prisma_1.prisma.assignment.update({
1288
+ const updatedAssignment = await prisma.assignment.update({
1235
1289
  where: { id: assignmentId },
1236
1290
  data: {
1237
1291
  eventAttached: {
@@ -1270,20 +1324,20 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1270
1324
  });
1271
1325
  return { assignment: updatedAssignment };
1272
1326
  }),
1273
- getAvailableEvents: trpc_1.protectedTeacherProcedure
1274
- .input(zod_1.z.object({
1275
- assignmentId: zod_1.z.string(),
1327
+ getAvailableEvents: protectedTeacherProcedure
1328
+ .input(z.object({
1329
+ assignmentId: z.string(),
1276
1330
  }))
1277
1331
  .query(async ({ ctx, input }) => {
1278
1332
  if (!ctx.user) {
1279
- throw new server_1.TRPCError({
1333
+ throw new TRPCError({
1280
1334
  code: "UNAUTHORIZED",
1281
1335
  message: "User must be authenticated",
1282
1336
  });
1283
1337
  }
1284
1338
  const { assignmentId } = input;
1285
1339
  // Get the assignment to find the class
1286
- const assignment = await prisma_1.prisma.assignment.findFirst({
1340
+ const assignment = await prisma.assignment.findFirst({
1287
1341
  where: {
1288
1342
  id: assignmentId,
1289
1343
  class: {
@@ -1295,13 +1349,13 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1295
1349
  select: { classId: true }
1296
1350
  });
1297
1351
  if (!assignment) {
1298
- throw new server_1.TRPCError({
1352
+ throw new TRPCError({
1299
1353
  code: "NOT_FOUND",
1300
1354
  message: "Assignment not found or you are not authorized",
1301
1355
  });
1302
1356
  }
1303
1357
  // Get all events for the class that don't already have this assignment attached
1304
- const events = await prisma_1.prisma.event.findMany({
1358
+ const events = await prisma.event.findMany({
1305
1359
  where: {
1306
1360
  classId: assignment.classId,
1307
1361
  assignmentsAttached: {
@@ -1324,15 +1378,15 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1324
1378
  });
1325
1379
  return { events };
1326
1380
  }),
1327
- dueToday: trpc_1.protectedProcedure
1381
+ dueToday: protectedProcedure
1328
1382
  .query(async ({ ctx }) => {
1329
1383
  if (!ctx.user) {
1330
- throw new server_1.TRPCError({
1384
+ throw new TRPCError({
1331
1385
  code: "UNAUTHORIZED",
1332
1386
  message: "User must be authenticated",
1333
1387
  });
1334
1388
  }
1335
- const assignments = await prisma_1.prisma.assignment.findMany({
1389
+ const assignments = await prisma.assignment.findMany({
1336
1390
  where: {
1337
1391
  dueDate: {
1338
1392
  equals: new Date(),
@@ -1358,39 +1412,39 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1358
1412
  dueDate: assignment.dueDate.toISOString(),
1359
1413
  }));
1360
1414
  }),
1361
- attachMarkScheme: trpc_1.protectedTeacherProcedure
1362
- .input(zod_1.z.object({
1363
- assignmentId: zod_1.z.string(),
1364
- markSchemeId: zod_1.z.string().nullable(),
1415
+ attachMarkScheme: protectedTeacherProcedure
1416
+ .input(z.object({
1417
+ assignmentId: z.string(),
1418
+ markSchemeId: z.string().nullable(),
1365
1419
  }))
1366
1420
  .mutation(async ({ ctx, input }) => {
1367
1421
  const { assignmentId, markSchemeId } = input;
1368
- const assignment = await prisma_1.prisma.assignment.findFirst({
1422
+ const assignment = await prisma.assignment.findFirst({
1369
1423
  where: {
1370
1424
  id: assignmentId,
1371
1425
  },
1372
1426
  });
1373
1427
  if (!assignment) {
1374
- throw new server_1.TRPCError({
1428
+ throw new TRPCError({
1375
1429
  code: "NOT_FOUND",
1376
1430
  message: "Assignment not found",
1377
1431
  });
1378
1432
  }
1379
1433
  // If markSchemeId is provided, verify it exists
1380
1434
  if (markSchemeId) {
1381
- const markScheme = await prisma_1.prisma.markScheme.findFirst({
1435
+ const markScheme = await prisma.markScheme.findFirst({
1382
1436
  where: {
1383
1437
  id: markSchemeId,
1384
1438
  },
1385
1439
  });
1386
1440
  if (!markScheme) {
1387
- throw new server_1.TRPCError({
1441
+ throw new TRPCError({
1388
1442
  code: "NOT_FOUND",
1389
1443
  message: "Mark scheme not found",
1390
1444
  });
1391
1445
  }
1392
1446
  }
1393
- const updatedAssignment = await prisma_1.prisma.assignment.update({
1447
+ const updatedAssignment = await prisma.assignment.update({
1394
1448
  where: { id: assignmentId },
1395
1449
  data: {
1396
1450
  markScheme: markSchemeId ? {
@@ -1409,24 +1463,24 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1409
1463
  });
1410
1464
  return updatedAssignment;
1411
1465
  }),
1412
- detachMarkScheme: trpc_1.protectedTeacherProcedure
1413
- .input(zod_1.z.object({
1414
- assignmentId: zod_1.z.string(),
1466
+ detachMarkScheme: protectedTeacherProcedure
1467
+ .input(z.object({
1468
+ assignmentId: z.string(),
1415
1469
  }))
1416
1470
  .mutation(async ({ ctx, input }) => {
1417
1471
  const { assignmentId } = input;
1418
- const assignment = await prisma_1.prisma.assignment.findFirst({
1472
+ const assignment = await prisma.assignment.findFirst({
1419
1473
  where: {
1420
1474
  id: assignmentId,
1421
1475
  },
1422
1476
  });
1423
1477
  if (!assignment) {
1424
- throw new server_1.TRPCError({
1478
+ throw new TRPCError({
1425
1479
  code: "NOT_FOUND",
1426
1480
  message: "Assignment not found",
1427
1481
  });
1428
1482
  }
1429
- const updatedAssignment = await prisma_1.prisma.assignment.update({
1483
+ const updatedAssignment = await prisma.assignment.update({
1430
1484
  where: { id: assignmentId },
1431
1485
  data: {
1432
1486
  markScheme: {
@@ -1443,39 +1497,39 @@ exports.assignmentRouter = (0, trpc_1.createTRPCRouter)({
1443
1497
  });
1444
1498
  return updatedAssignment;
1445
1499
  }),
1446
- attachGradingBoundary: trpc_1.protectedTeacherProcedure
1447
- .input(zod_1.z.object({
1448
- assignmentId: zod_1.z.string(),
1449
- gradingBoundaryId: zod_1.z.string().nullable(),
1500
+ attachGradingBoundary: protectedTeacherProcedure
1501
+ .input(z.object({
1502
+ assignmentId: z.string(),
1503
+ gradingBoundaryId: z.string().nullable(),
1450
1504
  }))
1451
1505
  .mutation(async ({ ctx, input }) => {
1452
1506
  const { assignmentId, gradingBoundaryId } = input;
1453
- const assignment = await prisma_1.prisma.assignment.findFirst({
1507
+ const assignment = await prisma.assignment.findFirst({
1454
1508
  where: {
1455
1509
  id: assignmentId,
1456
1510
  },
1457
1511
  });
1458
1512
  if (!assignment) {
1459
- throw new server_1.TRPCError({
1513
+ throw new TRPCError({
1460
1514
  code: "NOT_FOUND",
1461
1515
  message: "Assignment not found",
1462
1516
  });
1463
1517
  }
1464
1518
  // If gradingBoundaryId is provided, verify it exists
1465
1519
  if (gradingBoundaryId) {
1466
- const gradingBoundary = await prisma_1.prisma.gradingBoundary.findFirst({
1520
+ const gradingBoundary = await prisma.gradingBoundary.findFirst({
1467
1521
  where: {
1468
1522
  id: gradingBoundaryId,
1469
1523
  },
1470
1524
  });
1471
1525
  if (!gradingBoundary) {
1472
- throw new server_1.TRPCError({
1526
+ throw new TRPCError({
1473
1527
  code: "NOT_FOUND",
1474
1528
  message: "Grading boundary not found",
1475
1529
  });
1476
1530
  }
1477
1531
  }
1478
- const updatedAssignment = await prisma_1.prisma.assignment.update({
1532
+ const updatedAssignment = await prisma.assignment.update({
1479
1533
  where: { id: assignmentId },
1480
1534
  data: {
1481
1535
  gradingBoundary: gradingBoundaryId ? {