@studious-lms/server 1.17.0 → 1.18.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 (79) hide show
  1. package/dist/models/agenda.d.ts +2 -0
  2. package/dist/models/agenda.d.ts.map +1 -1
  3. package/dist/models/announcement.d.ts +1 -0
  4. package/dist/models/announcement.d.ts.map +1 -1
  5. package/dist/models/assignment.d.ts +7 -0
  6. package/dist/models/assignment.d.ts.map +1 -1
  7. package/dist/models/auth.d.ts +2 -0
  8. package/dist/models/auth.d.ts.map +1 -1
  9. package/dist/models/class.d.ts +6 -0
  10. package/dist/models/class.d.ts.map +1 -1
  11. package/dist/models/class.js +3 -2
  12. package/dist/models/class.js.map +1 -1
  13. package/dist/models/event.d.ts +4 -0
  14. package/dist/models/event.d.ts.map +1 -1
  15. package/dist/models/file.d.ts +8 -0
  16. package/dist/models/file.d.ts.map +1 -1
  17. package/dist/models/folder.d.ts +2 -0
  18. package/dist/models/folder.d.ts.map +1 -1
  19. package/dist/models/labChat.d.ts +2 -0
  20. package/dist/models/labChat.d.ts.map +1 -1
  21. package/dist/models/newtonChat.d.ts +1 -0
  22. package/dist/models/newtonChat.d.ts.map +1 -1
  23. package/dist/models/school.d.ts +1 -0
  24. package/dist/models/school.d.ts.map +1 -1
  25. package/dist/models/section.d.ts +1 -0
  26. package/dist/models/section.d.ts.map +1 -1
  27. package/dist/models/tag.d.ts +34 -0
  28. package/dist/models/tag.d.ts.map +1 -1
  29. package/dist/models/tag.js +18 -2
  30. package/dist/models/tag.js.map +1 -1
  31. package/dist/models/worksheet.d.ts +3 -0
  32. package/dist/models/worksheet.d.ts.map +1 -1
  33. package/dist/routers/_app.d.ts +48 -0
  34. package/dist/routers/_app.d.ts.map +1 -1
  35. package/dist/routers/agenda.d.ts +2 -0
  36. package/dist/routers/agenda.d.ts.map +1 -1
  37. package/dist/routers/assignment.d.ts +6 -0
  38. package/dist/routers/assignment.d.ts.map +1 -1
  39. package/dist/routers/class.d.ts +11 -0
  40. package/dist/routers/class.d.ts.map +1 -1
  41. package/dist/routers/class.js +4 -2
  42. package/dist/routers/class.js.map +1 -1
  43. package/dist/routers/event.d.ts +1 -0
  44. package/dist/routers/event.d.ts.map +1 -1
  45. package/dist/routers/school.d.ts +3 -0
  46. package/dist/routers/school.d.ts.map +1 -1
  47. package/dist/routers/school.js +3 -2
  48. package/dist/routers/school.js.map +1 -1
  49. package/dist/routers/worksheet.d.ts +1 -0
  50. package/dist/routers/worksheet.d.ts.map +1 -1
  51. package/dist/services/agenda.d.ts +2 -0
  52. package/dist/services/agenda.d.ts.map +1 -1
  53. package/dist/services/assignment.d.ts +6 -0
  54. package/dist/services/assignment.d.ts.map +1 -1
  55. package/dist/services/class.d.ts +11 -0
  56. package/dist/services/class.d.ts.map +1 -1
  57. package/dist/services/class.js +7 -3
  58. package/dist/services/class.js.map +1 -1
  59. package/dist/services/event.d.ts +1 -0
  60. package/dist/services/event.d.ts.map +1 -1
  61. package/dist/services/school.d.ts +3 -0
  62. package/dist/services/school.d.ts.map +1 -1
  63. package/dist/services/school.js +3 -2
  64. package/dist/services/school.js.map +1 -1
  65. package/dist/services/tag.d.ts.map +1 -1
  66. package/dist/services/tag.js +40 -5
  67. package/dist/services/tag.js.map +1 -1
  68. package/dist/services/worksheet.d.ts +1 -0
  69. package/dist/services/worksheet.d.ts.map +1 -1
  70. package/package.json +1 -1
  71. package/prisma/migrations/20260616071500_add_class_symbol/migration.sql +2 -0
  72. package/prisma/schema.prisma +1 -0
  73. package/src/models/class.ts +1 -0
  74. package/src/models/tag.ts +21 -0
  75. package/src/routers/class.ts +2 -0
  76. package/src/routers/school.ts +1 -0
  77. package/src/services/class.ts +7 -0
  78. package/src/services/school.ts +2 -0
  79. package/src/services/tag.ts +39 -2
@@ -9,6 +9,7 @@ import {
9
9
  createTag,
10
10
  deleteTag,
11
11
  detachTagFromTarget,
12
+ findTagByName,
12
13
  getTag,
13
14
  listTagsAttachedTo,
14
15
  listTagsForSchool,
@@ -20,6 +21,23 @@ import {
20
21
  findSchoolMembership,
21
22
  } from "../models/school.js";
22
23
 
24
+ /**
25
+ * Normalises a tag name to a canonical display form: trimmed with internal
26
+ * whitespace collapsed to single spaces. Casing is preserved so labels like
27
+ * "AP" or "Year 12" read naturally; uniqueness is enforced case-insensitively
28
+ * separately via {@link findTagByName}.
29
+ */
30
+ function normalizeTagName(name: string): string {
31
+ const normalized = name.trim().replace(/\s+/g, " ");
32
+ if (!normalized) {
33
+ throw new TRPCError({
34
+ code: "BAD_REQUEST",
35
+ message: "Tag name can't be empty",
36
+ });
37
+ }
38
+ return normalized;
39
+ }
40
+
23
41
  async function requireAdmin(schoolId: string, userId: string) {
24
42
  const membership = await findActiveSchoolAdminMembership(schoolId, userId);
25
43
  if (!membership) {
@@ -73,7 +91,15 @@ export async function createTagRecord(
73
91
  });
74
92
  }
75
93
  }
76
- return createTag(input);
94
+ const name = normalizeTagName(input.name);
95
+ const existing = await findTagByName(input.schoolId, name);
96
+ if (existing) {
97
+ throw new TRPCError({
98
+ code: "CONFLICT",
99
+ message: `A tag named "${existing.name}" already exists in this institution`,
100
+ });
101
+ }
102
+ return createTag({ ...input, name });
77
103
  }
78
104
 
79
105
  export async function updateTagRecord(
@@ -102,8 +128,19 @@ export async function updateTagRecord(
102
128
  });
103
129
  }
104
130
  }
131
+ let name = input.name;
132
+ if (name !== undefined) {
133
+ name = normalizeTagName(name);
134
+ const existing = await findTagByName(tag.schoolId, name, input.tagId);
135
+ if (existing) {
136
+ throw new TRPCError({
137
+ code: "CONFLICT",
138
+ message: `A tag named "${existing.name}" already exists in this institution`,
139
+ });
140
+ }
141
+ }
105
142
  return updateTag(input.tagId, {
106
- name: input.name,
143
+ name,
107
144
  color: input.color,
108
145
  departmentId: input.departmentId,
109
146
  });