@studious-lms/server 1.2.26 → 1.2.28
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/dist/routers/_app.d.ts +774 -0
- package/dist/routers/_app.d.ts.map +1 -1
- package/dist/routers/_app.js +2 -0
- package/dist/routers/assignment.d.ts +16 -0
- package/dist/routers/assignment.d.ts.map +1 -1
- package/dist/routers/auth.js +1 -1
- package/dist/routers/class.d.ts +9 -0
- package/dist/routers/class.d.ts.map +1 -1
- package/dist/routers/class.js +4 -0
- package/dist/routers/event.d.ts +2 -0
- package/dist/routers/event.d.ts.map +1 -1
- package/dist/routers/worksheet.d.ts +362 -0
- package/dist/routers/worksheet.d.ts.map +1 -0
- package/dist/routers/worksheet.js +365 -0
- package/package.json +1 -1
- package/prisma/schema.prisma +70 -1
- package/src/routers/_app.ts +2 -0
- package/src/routers/auth.ts +1 -1
- package/src/routers/class.ts +4 -0
- package/src/routers/worksheet.ts +419 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { TRPCError } from "@trpc/server";
|
|
2
|
+
import { createTRPCRouter, protectedProcedure } from "src/trpc";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { prisma } from "src/lib/prisma";
|
|
5
|
+
export const worksheetRouter = createTRPCRouter({
|
|
6
|
+
// Get a single worksheet with all questions
|
|
7
|
+
getWorksheet: protectedProcedure
|
|
8
|
+
.input(z.object({
|
|
9
|
+
worksheetId: z.string(),
|
|
10
|
+
}))
|
|
11
|
+
.query(async ({ ctx, input }) => {
|
|
12
|
+
const { worksheetId } = input;
|
|
13
|
+
const worksheet = await prisma.worksheet.findUnique({
|
|
14
|
+
where: { id: worksheetId },
|
|
15
|
+
include: {
|
|
16
|
+
questions: {
|
|
17
|
+
orderBy: { createdAt: 'asc' },
|
|
18
|
+
},
|
|
19
|
+
class: true,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
if (!worksheet) {
|
|
23
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: 'Worksheet not found' });
|
|
24
|
+
}
|
|
25
|
+
return worksheet;
|
|
26
|
+
}),
|
|
27
|
+
// List all worksheets for a class
|
|
28
|
+
listWorksheets: protectedProcedure
|
|
29
|
+
.input(z.object({
|
|
30
|
+
classId: z.string(),
|
|
31
|
+
}))
|
|
32
|
+
.query(async ({ ctx, input }) => {
|
|
33
|
+
const { classId } = input;
|
|
34
|
+
const worksheets = await prisma.worksheet.findMany({
|
|
35
|
+
where: { classId },
|
|
36
|
+
include: {
|
|
37
|
+
questions: {
|
|
38
|
+
select: { id: true },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
orderBy: { createdAt: 'desc' },
|
|
42
|
+
});
|
|
43
|
+
return worksheets.map(worksheet => ({
|
|
44
|
+
...worksheet,
|
|
45
|
+
questionCount: worksheet.questions.length,
|
|
46
|
+
}));
|
|
47
|
+
}),
|
|
48
|
+
// Update worksheet metadata
|
|
49
|
+
updateWorksheet: protectedProcedure
|
|
50
|
+
.input(z.object({
|
|
51
|
+
worksheetId: z.string(),
|
|
52
|
+
name: z.string().optional(),
|
|
53
|
+
}))
|
|
54
|
+
.mutation(async ({ ctx, input }) => {
|
|
55
|
+
const { worksheetId, name } = input;
|
|
56
|
+
const worksheet = await prisma.worksheet.update({
|
|
57
|
+
where: { id: worksheetId },
|
|
58
|
+
data: {
|
|
59
|
+
...(name !== undefined && { name }),
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
return worksheet;
|
|
63
|
+
}),
|
|
64
|
+
// Delete a worksheet
|
|
65
|
+
deleteWorksheet: protectedProcedure
|
|
66
|
+
.input(z.object({
|
|
67
|
+
worksheetId: z.string(),
|
|
68
|
+
}))
|
|
69
|
+
.mutation(async ({ ctx, input }) => {
|
|
70
|
+
const { worksheetId } = input;
|
|
71
|
+
// This will cascade delete all questions and responses
|
|
72
|
+
const deletedWorksheet = await prisma.worksheet.delete({
|
|
73
|
+
where: { id: worksheetId },
|
|
74
|
+
});
|
|
75
|
+
return deletedWorksheet;
|
|
76
|
+
}),
|
|
77
|
+
create: protectedProcedure
|
|
78
|
+
.input(z.object({
|
|
79
|
+
classId: z.string(),
|
|
80
|
+
name: z.string(),
|
|
81
|
+
}))
|
|
82
|
+
.mutation(async ({ ctx, input }) => {
|
|
83
|
+
const { classId, name } = input;
|
|
84
|
+
// Create the worksheet
|
|
85
|
+
const worksheet = await prisma.worksheet.create({
|
|
86
|
+
data: {
|
|
87
|
+
name,
|
|
88
|
+
classId,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
return worksheet;
|
|
92
|
+
}),
|
|
93
|
+
addQuestion: protectedProcedure
|
|
94
|
+
.input(z.object({
|
|
95
|
+
worksheetId: z.string(),
|
|
96
|
+
question: z.string(),
|
|
97
|
+
answer: z.string(),
|
|
98
|
+
markScheme: z.any().optional(), // JSON field
|
|
99
|
+
type: z.enum(['MULTIPLE_CHOICE', 'TRUE_FALSE', 'SHORT_ANSWER', 'LONG_ANSWER', 'MATH_EXPRESSION', 'ESSAY']),
|
|
100
|
+
}))
|
|
101
|
+
.mutation(async ({ ctx, input }) => {
|
|
102
|
+
const { worksheetId, question, answer, markScheme, type } = input;
|
|
103
|
+
const worksheet = await prisma.worksheet.findUnique({
|
|
104
|
+
where: { id: worksheetId },
|
|
105
|
+
});
|
|
106
|
+
if (!worksheet) {
|
|
107
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: 'Worksheet not found' });
|
|
108
|
+
}
|
|
109
|
+
const newQuestion = await prisma.worksheetQuestion.create({
|
|
110
|
+
data: {
|
|
111
|
+
worksheetId,
|
|
112
|
+
type,
|
|
113
|
+
question,
|
|
114
|
+
answer,
|
|
115
|
+
markScheme,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
return newQuestion;
|
|
119
|
+
}),
|
|
120
|
+
updateQuestion: protectedProcedure
|
|
121
|
+
.input(z.object({
|
|
122
|
+
worksheetId: z.string(),
|
|
123
|
+
questionId: z.string(),
|
|
124
|
+
question: z.string().optional(),
|
|
125
|
+
answer: z.string().optional(),
|
|
126
|
+
markScheme: z.any().optional(), // JSON field
|
|
127
|
+
type: z.enum(['MULTIPLE_CHOICE', 'TRUE_FALSE', 'SHORT_ANSWER', 'LONG_ANSWER', 'MATH_EXPRESSION', 'ESSAY']).optional(),
|
|
128
|
+
}))
|
|
129
|
+
.mutation(async ({ ctx, input }) => {
|
|
130
|
+
const { worksheetId, questionId, question, answer, markScheme, type } = input;
|
|
131
|
+
const worksheet = await prisma.worksheet.findUnique({
|
|
132
|
+
where: { id: worksheetId },
|
|
133
|
+
});
|
|
134
|
+
if (!worksheet) {
|
|
135
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: 'Worksheet not found' });
|
|
136
|
+
}
|
|
137
|
+
const updatedQuestion = await prisma.worksheetQuestion.update({
|
|
138
|
+
where: { id: questionId },
|
|
139
|
+
data: {
|
|
140
|
+
...(question !== undefined && { question }),
|
|
141
|
+
...(answer !== undefined && { answer }),
|
|
142
|
+
...(markScheme !== undefined && { markScheme }),
|
|
143
|
+
...(type !== undefined && { type }),
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
return updatedQuestion;
|
|
147
|
+
}),
|
|
148
|
+
deleteQuestion: protectedProcedure
|
|
149
|
+
.input(z.object({
|
|
150
|
+
worksheetId: z.string(),
|
|
151
|
+
questionId: z.string(),
|
|
152
|
+
}))
|
|
153
|
+
.mutation(async ({ ctx, input }) => {
|
|
154
|
+
const { worksheetId, questionId } = input;
|
|
155
|
+
const worksheet = await prisma.worksheet.findUnique({
|
|
156
|
+
where: { id: worksheetId },
|
|
157
|
+
});
|
|
158
|
+
if (!worksheet) {
|
|
159
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: 'Worksheet not found' });
|
|
160
|
+
}
|
|
161
|
+
const deletedQuestion = await prisma.worksheetQuestion.delete({
|
|
162
|
+
where: { id: questionId },
|
|
163
|
+
});
|
|
164
|
+
return deletedQuestion;
|
|
165
|
+
}),
|
|
166
|
+
getWorksheetSubmission: protectedProcedure
|
|
167
|
+
.input(z.object({
|
|
168
|
+
worksheetId: z.string(),
|
|
169
|
+
submissionId: z.string(),
|
|
170
|
+
}))
|
|
171
|
+
.query(async ({ ctx, input }) => {
|
|
172
|
+
const { worksheetId, submissionId } = input;
|
|
173
|
+
const submission = await prisma.submission.findUnique({
|
|
174
|
+
where: { id: submissionId },
|
|
175
|
+
});
|
|
176
|
+
if (!submission) {
|
|
177
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: 'Submission not found' });
|
|
178
|
+
}
|
|
179
|
+
const worksheetResponses = await prisma.studentWorksheetResponse.findMany({
|
|
180
|
+
where: { submissionId },
|
|
181
|
+
include: {
|
|
182
|
+
responses: true,
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
return worksheetResponses;
|
|
186
|
+
}),
|
|
187
|
+
answerQuestion: protectedProcedure
|
|
188
|
+
.input(z.object({
|
|
189
|
+
worksheetResponseId: z.string(),
|
|
190
|
+
questionId: z.string(),
|
|
191
|
+
response: z.string(),
|
|
192
|
+
}))
|
|
193
|
+
.mutation(async ({ ctx, input }) => {
|
|
194
|
+
const { worksheetResponseId, questionId, response } = input;
|
|
195
|
+
const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
|
|
196
|
+
where: { id: worksheetResponseId },
|
|
197
|
+
include: {
|
|
198
|
+
responses: {
|
|
199
|
+
where: { questionId },
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
if (!worksheetResponse) {
|
|
204
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: 'Worksheet response not found' });
|
|
205
|
+
}
|
|
206
|
+
// Check if a response already exists for this question
|
|
207
|
+
const existingResponse = worksheetResponse.responses[0];
|
|
208
|
+
if (existingResponse) {
|
|
209
|
+
// Update existing response
|
|
210
|
+
await prisma.studentQuestionProgress.update({
|
|
211
|
+
where: { id: existingResponse.id },
|
|
212
|
+
data: { response },
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
// Create new response
|
|
217
|
+
await prisma.studentQuestionProgress.create({
|
|
218
|
+
data: {
|
|
219
|
+
studentId: worksheetResponse.studentId,
|
|
220
|
+
questionId,
|
|
221
|
+
response,
|
|
222
|
+
studentWorksheetResponseId: worksheetResponseId,
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
// Return the updated worksheet response with all responses
|
|
227
|
+
const updatedWorksheetResponse = await prisma.studentWorksheetResponse.findUnique({
|
|
228
|
+
where: { id: worksheetResponseId },
|
|
229
|
+
include: {
|
|
230
|
+
responses: true,
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
return updatedWorksheetResponse;
|
|
234
|
+
}),
|
|
235
|
+
submitWorksheet: protectedProcedure
|
|
236
|
+
.input(z.object({
|
|
237
|
+
worksheetResponseId: z.string(),
|
|
238
|
+
}))
|
|
239
|
+
.mutation(async ({ ctx, input }) => {
|
|
240
|
+
const { worksheetResponseId } = input;
|
|
241
|
+
const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
|
|
242
|
+
where: { id: worksheetResponseId },
|
|
243
|
+
include: {
|
|
244
|
+
worksheet: {
|
|
245
|
+
include: {
|
|
246
|
+
questions: true,
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
responses: true,
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
if (!worksheetResponse) {
|
|
253
|
+
throw new TRPCError({ code: 'NOT_FOUND', message: 'Worksheet response not found' });
|
|
254
|
+
}
|
|
255
|
+
if (worksheetResponse.submitted) {
|
|
256
|
+
throw new TRPCError({ code: 'BAD_REQUEST', message: 'Worksheet already submitted' });
|
|
257
|
+
}
|
|
258
|
+
// Mark worksheet as submitted
|
|
259
|
+
const submittedWorksheet = await prisma.studentWorksheetResponse.update({
|
|
260
|
+
where: { id: worksheetResponseId },
|
|
261
|
+
data: {
|
|
262
|
+
submitted: true,
|
|
263
|
+
submittedAt: new Date(),
|
|
264
|
+
},
|
|
265
|
+
include: {
|
|
266
|
+
responses: true,
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
// TODO: Implement AI grading here
|
|
270
|
+
// For now, we'll just mark all answers as pending review
|
|
271
|
+
// You could integrate with an AI service to auto-grade certain question types
|
|
272
|
+
return submittedWorksheet;
|
|
273
|
+
}),
|
|
274
|
+
// Get or create a student's worksheet response
|
|
275
|
+
getOrCreateWorksheetResponse: protectedProcedure
|
|
276
|
+
.input(z.object({
|
|
277
|
+
worksheetId: z.string(),
|
|
278
|
+
studentId: z.string(),
|
|
279
|
+
}))
|
|
280
|
+
.mutation(async ({ ctx, input }) => {
|
|
281
|
+
const { worksheetId, studentId } = input;
|
|
282
|
+
// Try to find existing response
|
|
283
|
+
let worksheetResponse = await prisma.studentWorksheetResponse.findFirst({
|
|
284
|
+
where: {
|
|
285
|
+
worksheetId,
|
|
286
|
+
studentId,
|
|
287
|
+
submitted: false, // Only get unsubmitted responses
|
|
288
|
+
},
|
|
289
|
+
include: {
|
|
290
|
+
responses: {
|
|
291
|
+
include: {
|
|
292
|
+
question: true,
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
// Create new response if none exists
|
|
298
|
+
if (!worksheetResponse) {
|
|
299
|
+
worksheetResponse = await prisma.studentWorksheetResponse.create({
|
|
300
|
+
data: {
|
|
301
|
+
worksheetId,
|
|
302
|
+
studentId,
|
|
303
|
+
},
|
|
304
|
+
include: {
|
|
305
|
+
responses: {
|
|
306
|
+
include: {
|
|
307
|
+
question: true,
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
return worksheetResponse;
|
|
314
|
+
}),
|
|
315
|
+
// Get all student responses for a worksheet (teacher view)
|
|
316
|
+
getWorksheetResponses: protectedProcedure
|
|
317
|
+
.input(z.object({
|
|
318
|
+
worksheetId: z.string(),
|
|
319
|
+
}))
|
|
320
|
+
.query(async ({ ctx, input }) => {
|
|
321
|
+
const { worksheetId } = input;
|
|
322
|
+
const responses = await prisma.studentWorksheetResponse.findMany({
|
|
323
|
+
where: { worksheetId },
|
|
324
|
+
include: {
|
|
325
|
+
student: {
|
|
326
|
+
select: {
|
|
327
|
+
id: true,
|
|
328
|
+
username: true,
|
|
329
|
+
profile: {
|
|
330
|
+
select: {
|
|
331
|
+
displayName: true,
|
|
332
|
+
profilePicture: true,
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
responses: {
|
|
338
|
+
include: {
|
|
339
|
+
question: true,
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
orderBy: { submittedAt: 'desc' },
|
|
344
|
+
});
|
|
345
|
+
return responses;
|
|
346
|
+
}),
|
|
347
|
+
// Grade a student's answer
|
|
348
|
+
gradeAnswer: protectedProcedure
|
|
349
|
+
.input(z.object({
|
|
350
|
+
responseId: z.string(), // StudentQuestionProgress ID
|
|
351
|
+
isCorrect: z.boolean(),
|
|
352
|
+
feedback: z.string().optional(),
|
|
353
|
+
}))
|
|
354
|
+
.mutation(async ({ ctx, input }) => {
|
|
355
|
+
const { responseId, isCorrect, feedback } = input;
|
|
356
|
+
const gradedResponse = await prisma.studentQuestionProgress.update({
|
|
357
|
+
where: { id: responseId },
|
|
358
|
+
data: {
|
|
359
|
+
isCorrect,
|
|
360
|
+
...(feedback !== undefined && { feedback }),
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
return gradedResponse;
|
|
364
|
+
}),
|
|
365
|
+
});
|
package/package.json
CHANGED
package/prisma/schema.prisma
CHANGED
|
@@ -42,6 +42,15 @@ enum UploadStatus {
|
|
|
42
42
|
CANCELLED
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
enum WorksheetQuestionType {
|
|
46
|
+
MULTIPLE_CHOICE
|
|
47
|
+
TRUE_FALSE
|
|
48
|
+
SHORT_ANSWER
|
|
49
|
+
LONG_ANSWER
|
|
50
|
+
MATH_EXPRESSION
|
|
51
|
+
ESSAY
|
|
52
|
+
}
|
|
53
|
+
|
|
45
54
|
enum ReactionType {
|
|
46
55
|
THUMBSUP
|
|
47
56
|
CELEBRATE
|
|
@@ -85,6 +94,8 @@ model User {
|
|
|
85
94
|
announcements Announcement[]
|
|
86
95
|
notificationsSent Notification[] @relation("SentNotifications")
|
|
87
96
|
notificationsReceived Notification[] @relation("ReceivedNotifications")
|
|
97
|
+
studentQuestionProgress StudentQuestionProgress[]
|
|
98
|
+
studentWorksheetResponses StudentWorksheetResponse[]
|
|
88
99
|
|
|
89
100
|
presentAttendance Attendance[] @relation("PresentAttendance")
|
|
90
101
|
lateAttendance Attendance[] @relation("LateAttendance")
|
|
@@ -132,6 +143,7 @@ model Class {
|
|
|
132
143
|
events Event[]
|
|
133
144
|
sections Section[]
|
|
134
145
|
sessions Session[]
|
|
146
|
+
worksheets Worksheet[]
|
|
135
147
|
students User[] @relation("UserStudentToClass")
|
|
136
148
|
teachers User[] @relation("UserTeacherToClass")
|
|
137
149
|
markSchemes MarkScheme[] @relation("ClassToMarkScheme")
|
|
@@ -255,6 +267,8 @@ model Assignment {
|
|
|
255
267
|
order Int?
|
|
256
268
|
gradingBoundary GradingBoundary? @relation(fields: [gradingBoundaryId], references: [id], onDelete: Cascade)
|
|
257
269
|
gradingBoundaryId String?
|
|
270
|
+
worksheet Worksheet? @relation(fields: [worksheetId], references: [id], onDelete: Cascade)
|
|
271
|
+
worksheetId String?
|
|
258
272
|
}
|
|
259
273
|
|
|
260
274
|
|
|
@@ -318,7 +332,8 @@ model Submission {
|
|
|
318
332
|
|
|
319
333
|
attachments File[] @relation("SubmissionFile")
|
|
320
334
|
annotations File[] @relation("SubmissionAnnotations")
|
|
321
|
-
|
|
335
|
+
worksheetResponses StudentWorksheetResponse[]
|
|
336
|
+
|
|
322
337
|
gradeReceived Int?
|
|
323
338
|
|
|
324
339
|
rubricState String?
|
|
@@ -471,6 +486,60 @@ model Mention {
|
|
|
471
486
|
@@unique([messageId, userId])
|
|
472
487
|
}
|
|
473
488
|
|
|
489
|
+
model Worksheet {
|
|
490
|
+
id String @id @default(uuid())
|
|
491
|
+
name String
|
|
492
|
+
createdAt DateTime @default(now())
|
|
493
|
+
updatedAt DateTime @updatedAt
|
|
494
|
+
classId String
|
|
495
|
+
class Class? @relation(fields: [classId], references: [id], onDelete: Cascade)
|
|
496
|
+
assignments Assignment[]
|
|
497
|
+
questions WorksheetQuestion[]
|
|
498
|
+
studentWorksheetResponses StudentWorksheetResponse[]
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
model WorksheetQuestion {
|
|
502
|
+
id String @id @default(uuid())
|
|
503
|
+
worksheetId String
|
|
504
|
+
worksheet Worksheet @relation(fields: [worksheetId], references: [id], onDelete: Cascade)
|
|
505
|
+
type WorksheetQuestionType
|
|
506
|
+
question String
|
|
507
|
+
answer String
|
|
508
|
+
markScheme Json? @default("{}")
|
|
509
|
+
createdAt DateTime @default(now())
|
|
510
|
+
updatedAt DateTime @updatedAt
|
|
511
|
+
studentResponses StudentQuestionProgress[]
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
model StudentWorksheetResponse {
|
|
515
|
+
id String @id @default(uuid())
|
|
516
|
+
studentId String
|
|
517
|
+
student User @relation(fields: [studentId], references: [id], onDelete: Cascade)
|
|
518
|
+
worksheetId String
|
|
519
|
+
worksheet Worksheet @relation(fields: [worksheetId], references: [id], onDelete: Cascade)
|
|
520
|
+
responses StudentQuestionProgress[]
|
|
521
|
+
submission Submission? @relation(fields: [submissionId], references: [id], onDelete: Cascade)
|
|
522
|
+
submissionId String?
|
|
523
|
+
createdAt DateTime @default(now())
|
|
524
|
+
updatedAt DateTime @updatedAt
|
|
525
|
+
submittedAt DateTime?
|
|
526
|
+
submitted Boolean @default(false)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
model StudentQuestionProgress {
|
|
530
|
+
id String @id @default(uuid())
|
|
531
|
+
studentId String
|
|
532
|
+
student User @relation(fields: [studentId], references: [id], onDelete: Cascade)
|
|
533
|
+
questionId String
|
|
534
|
+
question WorksheetQuestion @relation(fields: [questionId], references: [id], onDelete: Cascade)
|
|
535
|
+
response String
|
|
536
|
+
isCorrect Boolean @default(false)
|
|
537
|
+
feedback String?
|
|
538
|
+
createdAt DateTime @default(now())
|
|
539
|
+
updatedAt DateTime @updatedAt
|
|
540
|
+
studentWorksheetResponseId String?
|
|
541
|
+
studentWorksheetResponse StudentWorksheetResponse? @relation(fields: [studentWorksheetResponseId], references: [id], onDelete: Cascade)
|
|
542
|
+
}
|
|
474
543
|
|
|
475
544
|
model SchoolDevelopementProgram {
|
|
476
545
|
id String @id
|
package/src/routers/_app.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { conversationRouter } from "./conversation.js";
|
|
|
17
17
|
import { messageRouter } from "./message.js";
|
|
18
18
|
import { labChatRouter } from "./labChat.js";
|
|
19
19
|
import { marketingRouter } from "./marketing.js";
|
|
20
|
+
import { worksheetRouter } from "./worksheet.js";
|
|
20
21
|
|
|
21
22
|
export const appRouter = createTRPCRouter({
|
|
22
23
|
class: classRouter,
|
|
@@ -35,6 +36,7 @@ export const appRouter = createTRPCRouter({
|
|
|
35
36
|
message: messageRouter,
|
|
36
37
|
labChat: labChatRouter,
|
|
37
38
|
marketing: marketingRouter,
|
|
39
|
+
worksheet: worksheetRouter,
|
|
38
40
|
});
|
|
39
41
|
|
|
40
42
|
// Export type router type definition
|
package/src/routers/auth.ts
CHANGED
package/src/routers/class.ts
CHANGED
|
@@ -137,6 +137,7 @@ export const classRouter = createTRPCRouter({
|
|
|
137
137
|
id: true,
|
|
138
138
|
remarks: true,
|
|
139
139
|
createdAt: true,
|
|
140
|
+
modifiedAt: true,
|
|
140
141
|
teacher: {
|
|
141
142
|
select: {
|
|
142
143
|
id: true,
|
|
@@ -194,6 +195,9 @@ export const classRouter = createTRPCRouter({
|
|
|
194
195
|
studentId: true,
|
|
195
196
|
id: true,
|
|
196
197
|
submitted: true,
|
|
198
|
+
gradeReceived: true,
|
|
199
|
+
rubricState: true,
|
|
200
|
+
teacherComments: true,
|
|
197
201
|
returned: true,
|
|
198
202
|
submittedAt: true,
|
|
199
203
|
},
|