-
+ {radioGroupSubjectsLoading ? (
+
+ ) : (
+
+ )}
- {showSkeleton ? (
+ {isFetching ? (
) : data?.items?.length ? (
@@ -101,7 +130,7 @@ export const TeachersPage = () => {
theme="primary"
shape="round"
activeIndex={pageNumber}
- onIndexChange={setPage}
+ onIndexChange={handlePageChange}
totalPages={data?.pagesCount ?? 1}
/>
diff --git a/client/src/util/createTestIdFilePath.js b/client/src/util/createTestIdFilePath.js
deleted file mode 100644
index e4aed8c..0000000
--- a/client/src/util/createTestIdFilePath.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * This file is used to create the TEST_ID file paths
- */
-const createTestIdFilePath = (...args) => {
- return args.join("/");
-};
-
-export default createTestIdFilePath;
diff --git a/client/src/util/data.util.ts b/client/src/util/date.util.ts
similarity index 100%
rename from client/src/util/data.util.ts
rename to client/src/util/date.util.ts
diff --git a/client/src/util/mapSubjectToOptions.util.ts b/client/src/util/mapSubjectToOptions.util.ts
new file mode 100644
index 0000000..5d3bcee
--- /dev/null
+++ b/client/src/util/mapSubjectToOptions.util.ts
@@ -0,0 +1,13 @@
+import { SubjectsType } from "../api/subjects/subjects.type.ts";
+
+export type Option = {
+ label: string;
+ value: string;
+};
+
+export const mapSubjectsToOptions = (subjects: SubjectsType[]): Option[] => {
+ return subjects.map(({ id, name }) => ({
+ label: name,
+ value: id,
+ }));
+};
diff --git a/server/src/app.ts b/server/src/app.ts
index 477c18c..c5cce65 100644
--- a/server/src/app.ts
+++ b/server/src/app.ts
@@ -10,6 +10,7 @@ import { chatRouter } from "./routes/chatRoute.js";
import { streamRouter } from "./routes/streamRoute.js";
import { videoCallRouter } from "./routes/videoCallRoute.js";
+import { subjectRouter } from "./routes/subjectRoute.js";
// Create an express server
const app = express();
@@ -23,6 +24,7 @@ app.use(express.json());
* As we also host our client code on heroku we want to separate the API endpoints.
*/
app.use("/api/auth", authRouter);
+app.use("/api/subjects", subjectRouter);
app.use("/api/appointments", appointmentRouter);
app.use("/api/teachers", teacherRouter);
app.use("/api/reviews", reviewRouter);
diff --git a/server/src/composition/composition.types.ts b/server/src/composition/composition.types.ts
index 45ae974..a3020cb 100644
--- a/server/src/composition/composition.types.ts
+++ b/server/src/composition/composition.types.ts
@@ -43,4 +43,7 @@ export const TYPES = {
VideoCallQuery: Symbol.for("VideoCallQuery"),
VideoCallService: Symbol.for("VideoCallService"),
VideoCallController: Symbol.for("VideoCallController"),
+ //subjects
+ SubjectsController: Symbol.for("SubjectsController"),
+ SubjectsQuery: Symbol.for("SubjectsQuery"),
};
diff --git a/server/src/composition/compositionRoot.ts b/server/src/composition/compositionRoot.ts
index 5a7b7e2..e04824e 100644
--- a/server/src/composition/compositionRoot.ts
+++ b/server/src/composition/compositionRoot.ts
@@ -33,6 +33,8 @@ import { VideoCallCommand } from "../repositories/commandRepositories/videoCall.
import { VideoCallController } from "../controllers/videoCall.controller.js";
import { VideoCallQuery } from "../repositories/queryRepositories/videoCall.query.js";
import { VideoCallService } from "../services/video/videoCall.service.js";
+import { SubjectsController } from "../controllers/subjects.controller.js";
+import { SubjectsQuery } from "../repositories/queryRepositories/subjects.query.js";
export const container = new Container();
@@ -90,6 +92,9 @@ container.bind(TYPES.ChatQuery).to(ChatQuery);
container.bind(TYPES.ChatCommand).to(ChatCommand);
container.bind(TYPES.ChatController).to(ChatController);
container.bind(TYPES.ConversationCommand).to(ConversationCommand);
+//subjects
+container.bind(TYPES.SubjectsController).to(SubjectsController);
+container.bind(TYPES.SubjectsQuery).to(SubjectsQuery);
//video call
container.bind
(TYPES.StreamController).to(StreamController);
diff --git a/server/src/controllers/subjects.controller.ts b/server/src/controllers/subjects.controller.ts
new file mode 100644
index 0000000..c0820aa
--- /dev/null
+++ b/server/src/controllers/subjects.controller.ts
@@ -0,0 +1,21 @@
+import { TYPES } from "../composition/composition.types.js";
+import { inject, injectable } from "inversify";
+import { NextFunction, Response, Request } from "express";
+import { SubjectsQuery } from "../repositories/queryRepositories/subjects.query.js";
+
+@injectable()
+export class SubjectsController {
+ constructor(
+ @inject(TYPES.SubjectsQuery) private subjectsQuery: SubjectsQuery,
+ ) {}
+
+ async getSubjects(req: Request, res: Response, next: NextFunction) {
+ try {
+ const subjects = await this.subjectsQuery.getAllSubjects();
+
+ return res.status(200).json(subjects);
+ } catch (err) {
+ return next(err);
+ }
+ }
+}
diff --git a/server/src/db/schemes/subjects.schema.ts b/server/src/db/schemes/subjects.schema.ts
new file mode 100644
index 0000000..0c8a4ca
--- /dev/null
+++ b/server/src/db/schemes/subjects.schema.ts
@@ -0,0 +1,15 @@
+import mongoose from "mongoose";
+import { WithId } from "mongodb";
+import { SubjectsTypeDB } from "./types/subjects.types.js";
+
+const SubjectsSchema = new mongoose.Schema(
+ {
+ id: { type: String, required: true, unique: true, index: true },
+ name: { type: String, required: true, index: true },
+ },
+ { versionKey: false },
+);
+export const SubjectsModel = mongoose.model>(
+ "subjects",
+ SubjectsSchema,
+);
diff --git a/server/src/db/schemes/types/subjects.types.ts b/server/src/db/schemes/types/subjects.types.ts
new file mode 100644
index 0000000..e6fb1bf
--- /dev/null
+++ b/server/src/db/schemes/types/subjects.types.ts
@@ -0,0 +1,4 @@
+export type SubjectsTypeDB = {
+ id: string;
+ name: string;
+};
diff --git a/server/src/repositories/queryRepositories/subjects.query.ts b/server/src/repositories/queryRepositories/subjects.query.ts
new file mode 100644
index 0000000..1201ed6
--- /dev/null
+++ b/server/src/repositories/queryRepositories/subjects.query.ts
@@ -0,0 +1,17 @@
+import { injectable } from "inversify";
+import { SubjectsModel } from "../../db/schemes/subjects.schema.js";
+import { subjectsMapper } from "../../utils/mappers/subject.mapper.js";
+
+@injectable()
+export class SubjectsQuery {
+ async getAllSubjects(): Promise[]> {
+ try {
+ const subjects = await SubjectsModel.find().lean();
+ return subjects.map(subjectsMapper);
+ } catch (err: unknown) {
+ throw new Error("Something went wrong with getting all subjects", {
+ cause: err,
+ });
+ }
+ }
+}
diff --git a/server/src/routes/subjectRoute.ts b/server/src/routes/subjectRoute.ts
new file mode 100644
index 0000000..bc14e38
--- /dev/null
+++ b/server/src/routes/subjectRoute.ts
@@ -0,0 +1,11 @@
+import { Router } from "express";
+import { container } from "../composition/compositionRoot.js";
+import { SubjectsController } from "../controllers/subjects.controller.js";
+import { TYPES } from "../composition/composition.types.js";
+
+export const subjectRouter = Router();
+const subjectsController = container.get(
+ TYPES.SubjectsController,
+);
+
+subjectRouter.get("/", subjectsController.getSubjects.bind(subjectsController));
diff --git a/server/src/types/subjects/subjects.type.ts b/server/src/types/subjects/subjects.type.ts
new file mode 100644
index 0000000..d347861
--- /dev/null
+++ b/server/src/types/subjects/subjects.type.ts
@@ -0,0 +1,4 @@
+export type SubjectsViewType = {
+ id: string;
+ name: string;
+};
diff --git a/server/src/utils/mappers/subject.mapper.ts b/server/src/utils/mappers/subject.mapper.ts
new file mode 100644
index 0000000..652e24b
--- /dev/null
+++ b/server/src/utils/mappers/subject.mapper.ts
@@ -0,0 +1,12 @@
+import { WithId } from "mongodb";
+import { SubjectsTypeDB } from "../../db/schemes/types/subjects.types.js";
+import { SubjectsViewType } from "../../types/subjects/subjects.type.js";
+
+export const subjectsMapper = (
+ subject: WithId,
+): SubjectsViewType => {
+ return {
+ id: subject.id,
+ name: subject.name,
+ };
+};