Skip to content

Commit

Permalink
feat(user/profile): update user location on profile
Browse files Browse the repository at this point in the history
- infer type from zod's user schema
- use `GeoPoint` when handling location
  • Loading branch information
Veirt committed Nov 13, 2024
1 parent 637321c commit 9ddcfb0
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 34 deletions.
20 changes: 17 additions & 3 deletions src/controllers/user.controller.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
import { firestore } from "@/config";
import { Controller } from "@/types";
import { logger } from "@middleware/logging.middleware";
import { userSchema } from "@schemas/user.schema";
import { updateProfileSchema } from "@schemas/user.schema";
import { uploadProfilePicture } from "@services/upload.service";
import type { RequestHandler } from "express";
import firebase from "firebase-admin";
import { z } from "zod";

type UpdateProfileSchema = z.infer<typeof userSchema>;
type UpdateProfileSchema = z.infer<typeof updateProfileSchema>;
export const updateProfile: Controller<UpdateProfileSchema> = async (
req,
res,
) => {
try {
const { location, ...restOfData } = req.body;

// Handle location separately if present
const newData = location
? {
...restOfData,
location: new firebase.firestore.GeoPoint(
location.latitude,
location.longitude,
),
}
: restOfData;

firestore
.collection("users")
.doc(req.user.id)
.update({
...req.body,
...newData,
updatedAt: new Date(),
});
} catch (error) {
Expand Down
10 changes: 5 additions & 5 deletions src/routes/v1/user.route.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Router } from "express";
import * as userController from "@controllers/user.controller";
import {
firebaseAuthMiddleware,
verifyUser,
} from "../../middleware/auth.middleware";
} from "@middleware/auth.middleware";
import {
validateProfilePictureUpload,
validator,
} from "../../middleware/validation.middleware";
import { userSchema } from "../../schemas/user.schema";
} from "@middleware/validation.middleware";
import { updateProfileSchema } from "@schemas/user.schema";
import { Router } from "express";

// /api/v1/users
const userRouter = Router();
Expand All @@ -17,7 +17,7 @@ userRouter.use(verifyUser);

userRouter.patch(
"/profile",
validator(userSchema),
validator(updateProfileSchema),
userController.updateProfile,
);

Expand Down
39 changes: 29 additions & 10 deletions src/schemas/user.schema.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
import { z } from "zod";

export const userSchema = z.object({
body: z.object({
name: z.string().min(3, "Name must be at least 3 characters").optional(),
phoneNum: z
.string()
.min(10, "Phone number must be at least 10 characters")
.optional(),
// TODO:
// city
// interests
// learningStyle
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(),
createdAt: z.date(),
updatedAt: z.date(),
lastSeen: z.date().optional(),
interests: z.array(z.string()).optional(),
learningStyle: z
.enum(["visual", "auditory", "reading/writing", "kinesthetic"])
.optional(),
});

export const updateProfileSchema = z.object({
body: userSchema.omit({
id: true,
createdAt: true,
updatedAt: true,
lastSeen: true,
interests: true,
learningStyle: true,
}),
});
19 changes: 3 additions & 16 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { userSchema } from "@schemas/user.schema";
import type { RequestHandler } from "express";
import { z } from "zod";

interface RequestData {
body?: Record<string, any>;
Expand All @@ -9,22 +11,7 @@ interface RequestData {
export interface Controller<T extends RequestData = RequestData>
extends RequestHandler<T["params"], {}, T["body"], T["query"]> {}

enum LearningStyle {
VISUAL = "VISUAL",
AUDITORY = "AUDITORY",
KINESTHETIC = "KINESTHETIC",
}

export interface User {
id: string;
phoneNum?: string;
city?: string;
interests?: string[];
learningStyle?: LearningStyle;
createdAt: Date;
updatedAt: Date;
lastSeen?: Date;
}
export type User = z.infer<typeof userSchema>;

export interface Tutor {
id: string;
Expand Down

0 comments on commit 9ddcfb0

Please sign in to comment.