@studious-lms/server 1.7.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/dist/middleware/security.d.ts.map +1 -1
  2. package/dist/middleware/security.js +3 -3
  3. package/dist/middleware/security.js.map +1 -1
  4. package/dist/models/access.d.ts +21 -66
  5. package/dist/models/access.d.ts.map +1 -1
  6. package/dist/models/access.js +10 -26
  7. package/dist/models/access.js.map +1 -1
  8. package/dist/models/agenda.d.ts +4 -2
  9. package/dist/models/agenda.d.ts.map +1 -1
  10. package/dist/models/announcement.d.ts +4 -3
  11. package/dist/models/announcement.d.ts.map +1 -1
  12. package/dist/models/announcement.js +2 -3
  13. package/dist/models/announcement.js.map +1 -1
  14. package/dist/models/assignment.d.ts +40 -33
  15. package/dist/models/assignment.d.ts.map +1 -1
  16. package/dist/models/auth.d.ts +2 -4
  17. package/dist/models/auth.d.ts.map +1 -1
  18. package/dist/models/auth.js +2 -3
  19. package/dist/models/auth.js.map +1 -1
  20. package/dist/models/class.d.ts +34 -6
  21. package/dist/models/class.d.ts.map +1 -1
  22. package/dist/models/class.js +15 -2
  23. package/dist/models/class.js.map +1 -1
  24. package/dist/models/cloudStorage.d.ts +153 -0
  25. package/dist/models/cloudStorage.d.ts.map +1 -0
  26. package/dist/models/cloudStorage.js +295 -0
  27. package/dist/models/cloudStorage.js.map +1 -0
  28. package/dist/models/comment.d.ts +0 -24
  29. package/dist/models/comment.d.ts.map +1 -1
  30. package/dist/models/comment.js +2 -6
  31. package/dist/models/comment.js.map +1 -1
  32. package/dist/models/conversation.d.ts +22 -22
  33. package/dist/models/department.d.ts +154 -0
  34. package/dist/models/department.d.ts.map +1 -0
  35. package/dist/models/department.js +135 -0
  36. package/dist/models/department.js.map +1 -0
  37. package/dist/models/event.d.ts +10 -4
  38. package/dist/models/event.d.ts.map +1 -1
  39. package/dist/models/event.js +2 -5
  40. package/dist/models/event.js.map +1 -1
  41. package/dist/models/file.d.ts +52 -36
  42. package/dist/models/file.d.ts.map +1 -1
  43. package/dist/models/file.js +4 -42
  44. package/dist/models/file.js.map +1 -1
  45. package/dist/models/folder.d.ts +47 -0
  46. package/dist/models/folder.d.ts.map +1 -1
  47. package/dist/models/labChat.d.ts +11 -9
  48. package/dist/models/labChat.d.ts.map +1 -1
  49. package/dist/models/message.d.ts +46 -12
  50. package/dist/models/message.d.ts.map +1 -1
  51. package/dist/models/message.js +19 -10
  52. package/dist/models/message.js.map +1 -1
  53. package/dist/models/newtonChat.d.ts +2 -1
  54. package/dist/models/newtonChat.d.ts.map +1 -1
  55. package/dist/models/school.d.ts +45 -12
  56. package/dist/models/school.d.ts.map +1 -1
  57. package/dist/models/school.js +19 -2
  58. package/dist/models/school.js.map +1 -1
  59. package/dist/models/section.d.ts +2 -1
  60. package/dist/models/section.d.ts.map +1 -1
  61. package/dist/models/tag.d.ts +117 -0
  62. package/dist/models/tag.d.ts.map +1 -0
  63. package/dist/models/tag.js +113 -0
  64. package/dist/models/tag.js.map +1 -0
  65. package/dist/models/worksheet.d.ts +3 -0
  66. package/dist/models/worksheet.d.ts.map +1 -1
  67. package/dist/pipelines/aiLabChatContract.d.ts +8 -8
  68. package/dist/routers/_app.d.ts +6704 -5222
  69. package/dist/routers/_app.d.ts.map +1 -1
  70. package/dist/routers/_app.js +8 -2
  71. package/dist/routers/_app.js.map +1 -1
  72. package/dist/routers/agenda.d.ts +4 -2
  73. package/dist/routers/agenda.d.ts.map +1 -1
  74. package/dist/routers/announcement.d.ts.map +1 -1
  75. package/dist/routers/announcement.js +3 -5
  76. package/dist/routers/announcement.js.map +1 -1
  77. package/dist/routers/assignment.d.ts +49 -40
  78. package/dist/routers/assignment.d.ts.map +1 -1
  79. package/dist/routers/assignment.js +2 -3
  80. package/dist/routers/assignment.js.map +1 -1
  81. package/dist/routers/class.d.ts +92 -19
  82. package/dist/routers/class.d.ts.map +1 -1
  83. package/dist/routers/class.js +5 -2
  84. package/dist/routers/class.js.map +1 -1
  85. package/dist/routers/conversation.d.ts +5 -5
  86. package/dist/routers/department.d.ts +293 -0
  87. package/dist/routers/department.d.ts.map +1 -0
  88. package/dist/routers/department.js +93 -0
  89. package/dist/routers/department.js.map +1 -0
  90. package/dist/routers/event.d.ts +4 -3
  91. package/dist/routers/event.d.ts.map +1 -1
  92. package/dist/routers/file.d.ts +10 -8
  93. package/dist/routers/file.d.ts.map +1 -1
  94. package/dist/routers/file.js +26 -17
  95. package/dist/routers/file.js.map +1 -1
  96. package/dist/routers/folder.d.ts +163 -186
  97. package/dist/routers/folder.d.ts.map +1 -1
  98. package/dist/routers/folder.js +102 -88
  99. package/dist/routers/folder.js.map +1 -1
  100. package/dist/routers/labChat.d.ts +13 -4
  101. package/dist/routers/labChat.d.ts.map +1 -1
  102. package/dist/routers/message.d.ts +20 -2
  103. package/dist/routers/message.d.ts.map +1 -1
  104. package/dist/routers/newtonChat.d.ts +10 -1
  105. package/dist/routers/newtonChat.d.ts.map +1 -1
  106. package/dist/routers/permissions.d.ts +120 -0
  107. package/dist/routers/permissions.d.ts.map +1 -0
  108. package/dist/routers/permissions.js +50 -0
  109. package/dist/routers/permissions.js.map +1 -0
  110. package/dist/routers/school.d.ts +160 -17
  111. package/dist/routers/school.d.ts.map +1 -1
  112. package/dist/routers/school.js +46 -3
  113. package/dist/routers/school.js.map +1 -1
  114. package/dist/routers/tag.d.ts +119 -0
  115. package/dist/routers/tag.d.ts.map +1 -0
  116. package/dist/routers/tag.js +52 -0
  117. package/dist/routers/tag.js.map +1 -0
  118. package/dist/routers/worksheet.d.ts +1 -0
  119. package/dist/routers/worksheet.d.ts.map +1 -1
  120. package/dist/seedDatabase.d.ts +0 -1
  121. package/dist/seedDatabase.d.ts.map +1 -1
  122. package/dist/services/agenda.d.ts +4 -2
  123. package/dist/services/agenda.d.ts.map +1 -1
  124. package/dist/services/assignment.d.ts +49 -40
  125. package/dist/services/assignment.d.ts.map +1 -1
  126. package/dist/services/class.d.ts +92 -19
  127. package/dist/services/class.d.ts.map +1 -1
  128. package/dist/services/class.js +92 -5
  129. package/dist/services/class.js.map +1 -1
  130. package/dist/services/conversation.d.ts +5 -5
  131. package/dist/services/department.d.ts +200 -0
  132. package/dist/services/department.d.ts.map +1 -0
  133. package/dist/services/department.js +368 -0
  134. package/dist/services/department.js.map +1 -0
  135. package/dist/services/event.d.ts +4 -3
  136. package/dist/services/event.d.ts.map +1 -1
  137. package/dist/services/file.d.ts +105 -3
  138. package/dist/services/file.d.ts.map +1 -1
  139. package/dist/services/file.js +167 -41
  140. package/dist/services/file.js.map +1 -1
  141. package/dist/services/folder.d.ts +220 -149
  142. package/dist/services/folder.d.ts.map +1 -1
  143. package/dist/services/folder.js +326 -196
  144. package/dist/services/folder.js.map +1 -1
  145. package/dist/services/labChat.d.ts +13 -4
  146. package/dist/services/labChat.d.ts.map +1 -1
  147. package/dist/services/labChat.js +4 -3
  148. package/dist/services/labChat.js.map +1 -1
  149. package/dist/services/message.d.ts +20 -2
  150. package/dist/services/message.d.ts.map +1 -1
  151. package/dist/services/message.js +7 -23
  152. package/dist/services/message.js.map +1 -1
  153. package/dist/services/newtonChat.d.ts +9 -0
  154. package/dist/services/newtonChat.d.ts.map +1 -1
  155. package/dist/services/newtonChat.js +4 -3
  156. package/dist/services/newtonChat.js.map +1 -1
  157. package/dist/services/permissions.d.ts +188 -0
  158. package/dist/services/permissions.d.ts.map +1 -0
  159. package/dist/services/permissions.js +661 -0
  160. package/dist/services/permissions.js.map +1 -0
  161. package/dist/services/school.d.ts +142 -15
  162. package/dist/services/school.d.ts.map +1 -1
  163. package/dist/services/school.js +308 -54
  164. package/dist/services/school.js.map +1 -1
  165. package/dist/services/tag.d.ts +71 -0
  166. package/dist/services/tag.d.ts.map +1 -0
  167. package/dist/services/tag.js +178 -0
  168. package/dist/services/tag.js.map +1 -0
  169. package/dist/services/worksheet.d.ts +1 -0
  170. package/dist/services/worksheet.d.ts.map +1 -1
  171. package/dist/utils/aiUser.d.ts.map +1 -1
  172. package/dist/utils/aiUser.js +2 -3
  173. package/dist/utils/aiUser.js.map +1 -1
  174. package/package.json +1 -1
  175. package/prisma/migrations/20260509220000_institution_overhaul/migration.sql +184 -0
  176. package/prisma/migrations/20260510170000_add_folder_permissions/migration.sql +93 -0
  177. package/prisma/schema.prisma +193 -35
  178. package/src/middleware/security.ts +1 -1
  179. package/src/models/access.ts +8 -24
  180. package/src/models/announcement.ts +0 -1
  181. package/src/models/auth.ts +0 -1
  182. package/src/models/class.ts +13 -0
  183. package/src/models/cloudStorage.ts +327 -0
  184. package/src/models/comment.ts +0 -4
  185. package/src/models/department.ts +159 -0
  186. package/src/models/event.ts +0 -3
  187. package/src/models/file.ts +2 -40
  188. package/src/models/message.ts +19 -5
  189. package/src/models/school.ts +26 -0
  190. package/src/models/tag.ts +140 -0
  191. package/src/routers/_app.ts +6 -0
  192. package/src/routers/announcement.ts +1 -3
  193. package/src/routers/assignment.ts +0 -1
  194. package/src/routers/class.ts +3 -0
  195. package/src/routers/department.ts +174 -0
  196. package/src/routers/file.ts +29 -26
  197. package/src/routers/folder.ts +127 -118
  198. package/src/routers/permissions.ts +71 -0
  199. package/src/routers/school.ts +83 -0
  200. package/src/routers/tag.ts +78 -0
  201. package/src/services/class.ts +109 -4
  202. package/src/services/department.ts +478 -0
  203. package/src/services/file.ts +172 -43
  204. package/src/services/folder.ts +399 -237
  205. package/src/services/labChat.ts +2 -1
  206. package/src/services/message.ts +5 -21
  207. package/src/services/newtonChat.ts +2 -1
  208. package/src/services/permissions.ts +778 -0
  209. package/src/services/school.ts +369 -57
  210. package/src/services/tag.ts +229 -0
  211. package/src/utils/aiUser.ts +0 -1
  212. package/tests/unit/services/permissions.test.ts +463 -0
@@ -52,10 +52,6 @@ const commentClassAuthSelect = {
52
52
  students: { select: { id: true } },
53
53
  school: {
54
54
  select: {
55
- users: {
56
- where: { role: "ADMIN" as const },
57
- select: { id: true },
58
- },
59
55
  memberships: {
60
56
  where: {
61
57
  role: "ADMIN" as const,
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Department model — department CRUD, members, and template artefacts.
3
+ *
4
+ * Departments scope rubric/boundary templates and tags inside an institution.
5
+ * They are an admin-managed taxonomy; teachers/students can be members of
6
+ * many departments.
7
+ */
8
+ import { prisma } from "../lib/prisma.js";
9
+
10
+ const departmentSelect = {
11
+ id: true,
12
+ schoolId: true,
13
+ name: true,
14
+ description: true,
15
+ color: true,
16
+ createdAt: true,
17
+ updatedAt: true,
18
+ } as const;
19
+
20
+ const memberSelect = {
21
+ id: true,
22
+ username: true,
23
+ email: true,
24
+ profile: {
25
+ select: {
26
+ displayName: true,
27
+ profilePicture: true,
28
+ profilePictureThumbnail: true,
29
+ },
30
+ },
31
+ } as const;
32
+
33
+ export function listDepartmentsForSchool(schoolId: string) {
34
+ return prisma.department.findMany({
35
+ where: { schoolId },
36
+ orderBy: { name: "asc" },
37
+ select: {
38
+ ...departmentSelect,
39
+ _count: {
40
+ select: {
41
+ members: true,
42
+ classes: true,
43
+ tags: true,
44
+ rubricTemplates: true,
45
+ boundaryTemplates: true,
46
+ },
47
+ },
48
+ },
49
+ });
50
+ }
51
+
52
+ export function listDepartmentsForUser(userId: string, schoolId: string) {
53
+ return prisma.department.findMany({
54
+ where: {
55
+ schoolId,
56
+ members: { some: { id: userId } },
57
+ },
58
+ orderBy: { name: "asc" },
59
+ select: departmentSelect,
60
+ });
61
+ }
62
+
63
+ export function getDepartment(departmentId: string) {
64
+ return prisma.department.findUnique({
65
+ where: { id: departmentId },
66
+ select: {
67
+ ...departmentSelect,
68
+ members: { select: memberSelect },
69
+ tags: {
70
+ select: { id: true, name: true, color: true },
71
+ },
72
+ rubricTemplates: {
73
+ select: { id: true, name: true, structured: true },
74
+ },
75
+ boundaryTemplates: {
76
+ select: { id: true, name: true, structured: true },
77
+ },
78
+ classes: {
79
+ select: {
80
+ id: true,
81
+ name: true,
82
+ subject: true,
83
+ section: true,
84
+ color: true,
85
+ },
86
+ },
87
+ },
88
+ });
89
+ }
90
+
91
+ export function createDepartment(data: {
92
+ schoolId: string;
93
+ name: string;
94
+ description?: string;
95
+ color?: string;
96
+ }) {
97
+ return prisma.department.create({
98
+ data: {
99
+ schoolId: data.schoolId,
100
+ name: data.name,
101
+ description: data.description,
102
+ color: data.color,
103
+ },
104
+ select: departmentSelect,
105
+ });
106
+ }
107
+
108
+ export function updateDepartment(
109
+ departmentId: string,
110
+ data: { name?: string; description?: string | null; color?: string }
111
+ ) {
112
+ return prisma.department.update({
113
+ where: { id: departmentId },
114
+ data,
115
+ select: departmentSelect,
116
+ });
117
+ }
118
+
119
+ export function deleteDepartment(departmentId: string) {
120
+ return prisma.department.delete({
121
+ where: { id: departmentId },
122
+ });
123
+ }
124
+
125
+ export function addDepartmentMembers(
126
+ departmentId: string,
127
+ userIds: string[]
128
+ ) {
129
+ return prisma.department.update({
130
+ where: { id: departmentId },
131
+ data: {
132
+ members: { connect: userIds.map((id) => ({ id })) },
133
+ },
134
+ select: { id: true },
135
+ });
136
+ }
137
+
138
+ export function removeDepartmentMember(
139
+ departmentId: string,
140
+ userId: string
141
+ ) {
142
+ return prisma.department.update({
143
+ where: { id: departmentId },
144
+ data: {
145
+ members: { disconnect: { id: userId } },
146
+ },
147
+ select: { id: true },
148
+ });
149
+ }
150
+
151
+ export function findDepartmentMembership(
152
+ departmentId: string,
153
+ userId: string
154
+ ) {
155
+ return prisma.department.findFirst({
156
+ where: { id: departmentId, members: { some: { id: userId } } },
157
+ select: { id: true },
158
+ });
159
+ }
@@ -11,7 +11,6 @@ const eventGetInclude = {
11
11
  school: {
12
12
  include: {
13
13
  users: {
14
- where: { role: "ADMIN" as const },
15
14
  select: { id: true },
16
15
  },
17
16
  memberships: {
@@ -134,7 +133,6 @@ export function findEventWithClass(eventId: string) {
134
133
  school: {
135
134
  include: {
136
135
  users: {
137
- where: { role: "ADMIN" as const },
138
136
  select: { id: true },
139
137
  },
140
138
  memberships: {
@@ -163,7 +161,6 @@ export function findAssignmentWithClass(assignmentId: string) {
163
161
  school: {
164
162
  include: {
165
163
  users: {
166
- where: { role: "ADMIN" as const },
167
164
  select: { id: true },
168
165
  },
169
166
  memberships: {
@@ -8,10 +8,6 @@ const classFileAccessInclude = {
8
8
  teachers: true,
9
9
  school: {
10
10
  include: {
11
- users: {
12
- where: { role: "ADMIN" as const },
13
- select: { id: true },
14
- },
15
11
  memberships: {
16
12
  where: {
17
13
  role: "ADMIN" as const,
@@ -41,24 +37,7 @@ export function findFileById(fileId: string) {
41
37
  assignment: {
42
38
  include: {
43
39
  class: {
44
- include: {
45
- teachers: true,
46
- school: {
47
- include: {
48
- users: {
49
- where: { role: "ADMIN" as const },
50
- select: { id: true },
51
- },
52
- memberships: {
53
- where: {
54
- role: "ADMIN" as const,
55
- status: "ACTIVE" as const,
56
- },
57
- select: { userId: true },
58
- },
59
- },
60
- },
61
- },
40
+ include: classFileAccessInclude,
62
41
  },
63
42
  },
64
43
  },
@@ -70,24 +49,7 @@ export function findFileById(fileId: string) {
70
49
  assignment: {
71
50
  include: {
72
51
  class: {
73
- include: {
74
- teachers: true,
75
- school: {
76
- include: {
77
- users: {
78
- where: { role: "ADMIN" as const },
79
- select: { id: true },
80
- },
81
- memberships: {
82
- where: {
83
- role: "ADMIN" as const,
84
- status: "ACTIVE" as const,
85
- },
86
- select: { userId: true },
87
- },
88
- },
89
- },
90
- },
52
+ include: classFileAccessInclude,
91
53
  },
92
54
  },
93
55
  },
@@ -1,15 +1,29 @@
1
1
  /**
2
2
  * Message model – messages, attachments, mentions, unread counts.
3
3
  */
4
+ import type { Prisma } from "@prisma/client";
4
5
  import { prisma } from "../lib/prisma.js";
5
6
 
7
+ /**
8
+ * Canonical shape for File rows surfaced as message attachments.
9
+ * Use this everywhere a message (list, send, update, suggestion broadcast)
10
+ * exposes attached files so the wire shape stays in sync.
11
+ */
12
+ export const messageFileSelect = {
13
+ id: true,
14
+ name: true,
15
+ type: true,
16
+ size: true,
17
+ path: true,
18
+ uploadedAt: true,
19
+ uploadStatus: true,
20
+ thumbnailId: true,
21
+ thumbnail: { select: { id: true, path: true } },
22
+ } as const satisfies Prisma.FileSelect;
23
+
6
24
  const messageInclude = {
7
25
  attachments: {
8
- select: {
9
- id: true,
10
- name: true,
11
- type: true,
12
- },
26
+ select: messageFileSelect,
13
27
  },
14
28
  sender: {
15
29
  select: {
@@ -52,8 +52,10 @@ export function listUserSchoolMemberships(userId: string) {
52
52
  select: {
53
53
  id: true,
54
54
  name: true,
55
+ description: true,
55
56
  subdomain: true,
56
57
  logoId: true,
58
+ logo: { select: { id: true, path: true, type: true } },
57
59
  createdAt: true,
58
60
  updatedAt: true,
59
61
  },
@@ -96,14 +98,38 @@ export function getSchoolById(schoolId: string) {
96
98
  select: {
97
99
  id: true,
98
100
  name: true,
101
+ description: true,
99
102
  subdomain: true,
100
103
  logoId: true,
104
+ logo: { select: { id: true, name: true, type: true, path: true } },
101
105
  createdAt: true,
102
106
  updatedAt: true,
103
107
  },
104
108
  });
105
109
  }
106
110
 
111
+ export function updateSchoolRecord(
112
+ schoolId: string,
113
+ data: {
114
+ name?: string;
115
+ description?: string | null;
116
+ subdomain?: string | null;
117
+ logoId?: string | null;
118
+ }
119
+ ) {
120
+ return prisma.school.update({
121
+ where: { id: schoolId },
122
+ data,
123
+ select: {
124
+ id: true,
125
+ name: true,
126
+ description: true,
127
+ subdomain: true,
128
+ logoId: true,
129
+ },
130
+ });
131
+ }
132
+
107
133
  export function getSchoolOverview(schoolId: string) {
108
134
  return prisma.school.findUnique({
109
135
  where: { id: schoolId },
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Tag model — institution-scoped tags applicable to users, classes, rubrics,
3
+ * and grading boundaries. Optionally associated with a department.
4
+ */
5
+ import { prisma } from "../lib/prisma.js";
6
+
7
+ const tagSelect = {
8
+ id: true,
9
+ schoolId: true,
10
+ departmentId: true,
11
+ name: true,
12
+ color: true,
13
+ createdAt: true,
14
+ updatedAt: true,
15
+ } as const;
16
+
17
+ export type TagTarget = "USER" | "CLASS" | "RUBRIC" | "BOUNDARY";
18
+
19
+ export function listTagsForSchool(
20
+ schoolId: string,
21
+ options?: { departmentId?: string | null }
22
+ ) {
23
+ return prisma.tag.findMany({
24
+ where: {
25
+ schoolId,
26
+ ...(options?.departmentId === null
27
+ ? { departmentId: null }
28
+ : options?.departmentId
29
+ ? { departmentId: options.departmentId }
30
+ : {}),
31
+ },
32
+ orderBy: [{ name: "asc" }],
33
+ select: tagSelect,
34
+ });
35
+ }
36
+
37
+ export function getTag(tagId: string) {
38
+ return prisma.tag.findUnique({
39
+ where: { id: tagId },
40
+ select: tagSelect,
41
+ });
42
+ }
43
+
44
+ export function createTag(data: {
45
+ schoolId: string;
46
+ departmentId?: string | null;
47
+ name: string;
48
+ color?: string;
49
+ }) {
50
+ return prisma.tag.create({
51
+ data: {
52
+ schoolId: data.schoolId,
53
+ departmentId: data.departmentId ?? null,
54
+ name: data.name,
55
+ color: data.color ?? "#3B82F6",
56
+ },
57
+ select: tagSelect,
58
+ });
59
+ }
60
+
61
+ export function updateTag(
62
+ tagId: string,
63
+ data: { name?: string; color?: string; departmentId?: string | null }
64
+ ) {
65
+ return prisma.tag.update({
66
+ where: { id: tagId },
67
+ data,
68
+ select: tagSelect,
69
+ });
70
+ }
71
+
72
+ export function deleteTag(tagId: string) {
73
+ return prisma.tag.delete({ where: { id: tagId } });
74
+ }
75
+
76
+ const targetRelation: Record<TagTarget, "users" | "classes" | "rubrics" | "boundaries"> = {
77
+ USER: "users",
78
+ CLASS: "classes",
79
+ RUBRIC: "rubrics",
80
+ BOUNDARY: "boundaries",
81
+ };
82
+
83
+ export function attachTagToTarget(params: {
84
+ tagId: string;
85
+ target: TagTarget;
86
+ targetId: string;
87
+ }) {
88
+ const relation = targetRelation[params.target];
89
+ return prisma.tag.update({
90
+ where: { id: params.tagId },
91
+ data: {
92
+ [relation]: { connect: { id: params.targetId } },
93
+ },
94
+ select: { id: true },
95
+ });
96
+ }
97
+
98
+ export function detachTagFromTarget(params: {
99
+ tagId: string;
100
+ target: TagTarget;
101
+ targetId: string;
102
+ }) {
103
+ const relation = targetRelation[params.target];
104
+ return prisma.tag.update({
105
+ where: { id: params.tagId },
106
+ data: {
107
+ [relation]: { disconnect: { id: params.targetId } },
108
+ },
109
+ select: { id: true },
110
+ });
111
+ }
112
+
113
+ export function listTagsAttachedTo(target: TagTarget, targetId: string) {
114
+ switch (target) {
115
+ case "USER":
116
+ return prisma.tag.findMany({
117
+ where: { users: { some: { id: targetId } } },
118
+ select: tagSelect,
119
+ orderBy: { name: "asc" },
120
+ });
121
+ case "CLASS":
122
+ return prisma.tag.findMany({
123
+ where: { classes: { some: { id: targetId } } },
124
+ select: tagSelect,
125
+ orderBy: { name: "asc" },
126
+ });
127
+ case "RUBRIC":
128
+ return prisma.tag.findMany({
129
+ where: { rubrics: { some: { id: targetId } } },
130
+ select: tagSelect,
131
+ orderBy: { name: "asc" },
132
+ });
133
+ case "BOUNDARY":
134
+ return prisma.tag.findMany({
135
+ where: { boundaries: { some: { id: targetId } } },
136
+ select: tagSelect,
137
+ orderBy: { name: "asc" },
138
+ });
139
+ }
140
+ }
@@ -23,6 +23,9 @@ import { commentRouter } from "./comment.js";
23
23
  import { studentProgressRouter } from "./studentProgress.js";
24
24
  import { searchRouter } from "./search.js";
25
25
  import { schoolRouter } from "./school.js";
26
+ import { departmentRouter } from "./department.js";
27
+ import { tagRouter } from "./tag.js";
28
+ import { permissionsRouter } from "./permissions.js";
26
29
 
27
30
  export const appRouter = createTRPCRouter({
28
31
  class: classRouter,
@@ -47,6 +50,9 @@ export const appRouter = createTRPCRouter({
47
50
  studentProgress: studentProgressRouter,
48
51
  search: searchRouter,
49
52
  school: schoolRouter,
53
+ department: departmentRouter,
54
+ tag: tagRouter,
55
+ permissions: permissionsRouter,
50
56
  });
51
57
 
52
58
  // Export type router type definition
@@ -342,9 +342,7 @@ export const announcementRouter = createTRPCRouter({
342
342
  school: {
343
343
  include: {
344
344
  users: {
345
- where: {
346
- role: "ADMIN",
347
- },
345
+ select: { id: true },
348
346
  },
349
347
  memberships: {
350
348
  where: {
@@ -722,7 +722,6 @@ export const assignmentRouter = createTRPCRouter({
722
722
  school: {
723
723
  include: {
724
724
  users: {
725
- where: { role: "ADMIN" },
726
725
  select: { id: true },
727
726
  },
728
727
  memberships: {
@@ -58,6 +58,8 @@ export const classRouter = createTRPCRouter({
58
58
  name: z.string().optional(),
59
59
  section: z.string().optional(),
60
60
  subject: z.string().optional(),
61
+ color: z.string().optional(),
62
+ departmentId: z.string().nullable().optional(),
61
63
  })
62
64
  )
63
65
  .mutation(({ input }) => {
@@ -75,6 +77,7 @@ export const classRouter = createTRPCRouter({
75
77
  subject: z.string(),
76
78
  color: z.string().optional(),
77
79
  schoolId: z.string().optional(),
80
+ departmentId: z.string().optional(),
78
81
  })
79
82
  )
80
83
  .mutation(({ ctx, input }) =>