diff --git a/backend/package.json b/backend/package.json index 7943d61d..1d97dfc0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -37,6 +37,7 @@ "jwks-rsa": "^3.1.0", "loki-logger-ts": "^1.0.2", "marked": "^15.0.7", + "nanoid": "^5.1.5", "response-time": "^2.3.3", "swagger-ui-express": "^5.0.1", "uuid": "^11.1.0", diff --git a/backend/src/entities/classes/class.entity.ts b/backend/src/entities/classes/class.entity.ts index b2c59ade..5bedf560 100644 --- a/backend/src/entities/classes/class.entity.ts +++ b/backend/src/entities/classes/class.entity.ts @@ -1,15 +1,17 @@ import { Collection, Entity, ManyToMany, PrimaryKey, Property } from '@mikro-orm/core'; -import { v4 } from 'uuid'; import { Teacher } from '../users/teacher.entity.js'; import { Student } from '../users/student.entity.js'; import { ClassRepository } from '../../data/classes/class-repository.js'; +import { customAlphabet } from 'nanoid'; + +const generateClassId = customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 6); @Entity({ repository: () => ClassRepository, }) export class Class { @PrimaryKey() - classId? = v4(); + classId? = generateClassId(); @Property({ type: 'string' }) displayName!: string; diff --git a/backend/tests/controllers/classes.test.ts b/backend/tests/controllers/classes.test.ts new file mode 100644 index 00000000..ab941773 --- /dev/null +++ b/backend/tests/controllers/classes.test.ts @@ -0,0 +1,47 @@ +import { setupTestApp } from '../setup-tests.js'; +import { describe, it, expect, beforeAll, beforeEach, vi, Mock } from 'vitest'; +import { Request, Response } from 'express'; +import { createClassHandler, deleteClassHandler } from '../../src/controllers/classes'; + +describe('Class controllers', () => { + let req: Partial; + let res: Partial; + + let jsonMock: Mock; + let statusMock: Mock; + + beforeAll(async () => { + await setupTestApp(); + }); + + beforeEach(async () => { + jsonMock = vi.fn(); + statusMock = vi.fn().mockReturnThis(); + + res = { + json: jsonMock, + status: statusMock, + }; + }); + + it('create and delete class', async () => { + req = { + body: { displayName: 'coole_nieuwe_klas' }, + }; + + await createClassHandler(req as Request, res as Response); + + const result = jsonMock.mock.lastCall?.[0]; + // Console.log('class', result.class); + + expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ class: expect.anything() })); + + req = { + params: { id: result.class.id }, + }; + + await deleteClassHandler(req as Request, res as Response); + + expect(jsonMock).toHaveBeenCalledWith(expect.objectContaining({ class: expect.anything() })); + }); +}); diff --git a/backend/tests/data/classes/classes.test.ts b/backend/tests/data/classes/classes.test.ts index 99ef1b7f..2f3683d8 100644 --- a/backend/tests/data/classes/classes.test.ts +++ b/backend/tests/data/classes/classes.test.ts @@ -3,6 +3,7 @@ import { ClassRepository } from '../../../src/data/classes/class-repository'; import { setupTestApp } from '../../setup-tests'; import { getClassRepository } from '../../../src/data/repositories'; import { getClass01, getClass04 } from '../../test_assets/classes/classes.testdata'; +import { getClass01, getClass04 } from '../../test_assets/classes/classes.testdata'; describe('ClassRepository', () => { let classRepository: ClassRepository; diff --git a/backend/tests/test_assets/classes/classes.testdata.ts b/backend/tests/test_assets/classes/classes.testdata.ts index 883d2a03..2cfa8603 100644 --- a/backend/tests/test_assets/classes/classes.testdata.ts +++ b/backend/tests/test_assets/classes/classes.testdata.ts @@ -10,7 +10,7 @@ export function makeTestClasses(em: EntityManager): Class[] { const teacherClass01: Teacher[] = [getTestleerkracht1()]; class01 = em.create(Class, { - classId: '8764b861-90a6-42e5-9732-c0d9eb2f55f9', + classId: 'X2J9QT', // 8764b861-90a6-42e5-9732-c0d9eb2f55f9 displayName: 'class01', teachers: teacherClass01, students: studentsClass01, @@ -20,7 +20,7 @@ export function makeTestClasses(em: EntityManager): Class[] { const teacherClass02: Teacher[] = [getLimpBizkit()]; class02 = em.create(Class, { - classId: '34d484a1-295f-4e9f-bfdc-3e7a23d86a89', + classId: '7KLPMA', // 34d484a1-295f-4e9f-bfdc-3e7a23d86a89 displayName: 'class02', teachers: teacherClass02, students: studentsClass02, @@ -30,7 +30,7 @@ export function makeTestClasses(em: EntityManager): Class[] { const teacherClass03: Teacher[] = [getStaind()]; class03 = em.create(Class, { - classId: '80dcc3e0-1811-4091-9361-42c0eee91cfa', + classId: 'R0D3UZ', // 80dcc3e0-1811-4091-9361-42c0eee91cfa displayName: 'class03', teachers: teacherClass03, students: studentsClass03, @@ -41,14 +41,14 @@ export function makeTestClasses(em: EntityManager): Class[] { // gets deleted in test class04 = em.create(Class, { - classId: '33d03536-83b8-4880-9982-9bbf2f908ddf', + classId: 'Q8N5YC', // 33d03536-83b8-4880-9982-9bbf2f908ddf displayName: 'class04', teachers: teacherClass04, students: studentsClass04, }); classWithTestleerlingAndTestleerkracht = em.create(Class, { - classId: 'a75298b5-18aa-471d-8eeb-5d77eb989393', + classId: 'ZAV71B', // Was a75298b5-18aa-471d-8eeb-5d77eb989393 displayName: 'Testklasse', teachers: [getTestleerkracht1()], students: [getTestleerling1()], diff --git a/frontend/src/assets/common.css b/frontend/src/assets/common.css new file mode 100644 index 00000000..bcc5d39f --- /dev/null +++ b/frontend/src/assets/common.css @@ -0,0 +1,54 @@ +.h1 { + color: #0e6942; + text-transform: uppercase; + font-weight: bolder; + font-size: 50px; + padding-left: 1%; +} + +.empty-message { + text-align: center; + font-size: 18px; +} + +.header { + font-weight: bold !important; + background-color: #0e6942; + color: white; + padding: 10px; +} + +.table thead th:first-child { + border-top-left-radius: 10px; +} + +.table thead th:last-child { + border-top-right-radius: 10px; +} + +.table tbody tr:nth-child(odd) { + background-color: white; +} + +.table tbody tr:nth-child(even) { + background-color: #f6faf2; +} + +.table td, +.table th { + border-bottom: 1px solid #0e6942; + border-top: 1px solid #0e6942; +} + +.table { + width: 90%; + padding-top: 10px; + border-collapse: collapse; +} + +@media screen and (max-width: 850px) { + .h1 { + text-align: center; + padding-left: 0; + } +} diff --git a/frontend/src/components/BrowseThemes.vue b/frontend/src/components/BrowseThemes.vue index b65c4e26..7b1971d4 100644 --- a/frontend/src/components/BrowseThemes.vue +++ b/frontend/src/components/BrowseThemes.vue @@ -57,6 +57,22 @@ + + + - - - - + diff --git a/frontend/src/components/MenuBar.vue b/frontend/src/components/MenuBar.vue index e3734976..a58be2f8 100644 --- a/frontend/src/components/MenuBar.vue +++ b/frontend/src/components/MenuBar.vue @@ -14,6 +14,7 @@ const _router = useRouter(); // Zonder '_' gaf dit een linter error voor unused variable const name: string = auth.authState.user!.profile.name!; + const email = auth.authState.user!.profile.email; const initials: string = name .split(" ") .map((n) => n[0]) @@ -90,31 +91,34 @@ - - - - - {{ language.name }} - - - + + + + + {{ language.name }} + + + - {{ initials }} + + + + +
+ + {{ initials }} + +

{{ name }}

+

{{ email }}

+ + {{ t("logout") }} + +
+
+
+
-

{{ t("homeTitle") }}

+

{{ t("homeTitle") }}

{{ t("homeIntroduction1") }}

@@ -84,7 +84,10 @@
- +