diff --git a/src/schemas/tutor.schema.ts b/src/schemas/tutor.schema.ts new file mode 100644 index 0000000..7ead1c9 --- /dev/null +++ b/src/schemas/tutor.schema.ts @@ -0,0 +1,99 @@ +import { firestore } from "@/config"; +import { z } from "zod"; + +export const tutorSchema = z.object({ + id: z.string(), + name: z.string().min(3, "Name must be at least 3 characters"), + phoneNum: z + .string() + .min(10, "Phone number must be at least 10 characters") + .optional(), + location: z + .object({ + latitude: z.number({ message: "Latitude must be a number" }), + longitude: z.number({ message: "Longitude must be a number" }), + }) + .optional(), + services: z + .array(z.string()) + .superRefine(async (services, ctx) => { + // Validate that the services are valid by checking the tutor_services collection + try { + const servicesSnapshot = await firestore + .collection("tutor_services") + .get(); + const validServices = servicesSnapshot.docs.map((doc) => doc.id); + const invalidServices = services.filter( + (service) => !validServices.includes(service), + ); + + if (invalidServices.length > 0) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: `Invalid services found: ${invalidServices.join(", ")}`, + }); + } + } catch (error) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Failed to validate services due to an internal error", + }); + } + }) + .optional(), + coverageRange: z.number().optional(), + createdAt: z.date(), + updatedAt: z.date().optional(), + lastSeen: z.date().optional(), +}); + +export const tutorServiceSchema = z.object({ + id: z.string(), + tutorId: z.string().superRefine(async (tutorId, ctx) => { + // Validate that the tutor exists by checking the tutors collection + try { + const tutorSnapshot = await firestore + .collection("tutors") + .doc(tutorId) + .get(); + if (!tutorSnapshot.exists) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Tutor does not exist", + }); + } + } catch (error) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Failed to validate tutor due to an internal error", + }); + } + }), + subjectId: z.string().superRefine(async (subjectId, ctx) => { + // Validate that the subject exists by checking the subjects collection + try { + const subjectSnapshot = await firestore + .collection("subjects") + .doc(subjectId) + .get(); + if (!subjectSnapshot.exists) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Subject does not exist", + }); + } + } catch (error) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Failed to validate subject due to an internal error", + }); + } + }), + // About the tutor's experience with the subject + aboutYou: z.string().optional(), + // Tutor's teaching methodology + teachingMethodology: z.string().optional(), + hourlyRate: z.number().optional(), + createdAt: z.date(), + updatedAt: z.date().optional(), +}); diff --git a/src/schemas/user.schema.ts b/src/schemas/user.schema.ts index 597d5d4..9b7dc5f 100644 --- a/src/schemas/user.schema.ts +++ b/src/schemas/user.schema.ts @@ -15,7 +15,7 @@ export const userSchema = z.object({ }) .optional(), createdAt: z.date(), - updatedAt: z.date(), + updatedAt: z.date().optional(), lastSeen: z.date().optional(), interests: z .array(z.string()) diff --git a/src/types.ts b/src/types.ts index dda0413..70b1772 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,4 @@ +import { tutorSchema, tutorServiceSchema } from "@schemas/tutor.schema"; import { userSchema } from "@schemas/user.schema"; import type { RequestHandler } from "express"; import { z } from "zod"; @@ -12,16 +13,8 @@ export interface Controller extends RequestHandler {} export type User = z.infer; - -export interface Tutor { - id: string; - phoneNum?: string; - location?: unknown; // not sure now - coverageRange: number; - createdAt: Date; - updatedAt: Date; - lastSeen?: Date; -} +export type Tutor = z.infer; +export type Service = z.infer; export interface Subject { name: string;