moahmedwafy commited on
Commit
ac2e12b
·
2 Parent(s): b69d7c7 469fdee

Merge branch 'main' into models-server

Browse files
src/common/enums/activity-type.enum.ts ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ export enum ActivityType {
2
+ EAT_CUSTOM_MEAL = "eat-custom-meal",
3
+ }
src/common/models/activity.model.ts ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import mongoose, { Types } from "mongoose";
2
+ import { MealDocument } from "./meal.model";
3
+ import { MealPlanDocument } from "./meal-plan.model";
4
+ import { WorkoutDocument } from "./workout.model";
5
+ import { ExerciseDocument } from "./exercise.model";
6
+ import { ActivityType } from "@common/enums/activity-type.enum";
7
+ const { Schema } = mongoose;
8
+
9
+ export type RelatedItem = | MealDocument
10
+ | MealPlanDocument
11
+ | WorkoutDocument
12
+ | ExerciseDocument
13
+ | {
14
+ ingredients: {
15
+ id: string,
16
+ noServings: number
17
+ }[];
18
+ }
19
+ export type AMetaData = {};
20
+
21
+ export interface IActivity {
22
+ related_item?: RelatedItem;
23
+ meta_data?: AMetaData;
24
+ activity_type: ActivityType;
25
+ related_id?: string | Types.ObjectId;
26
+ user_id: string | Types.ObjectId;
27
+ created_at?: Date;
28
+ }
29
+
30
+ const activitySchema = new Schema({
31
+ related_item: {
32
+ type: Schema.Types.Mixed,
33
+ required: false,
34
+ },
35
+ meta_data: {
36
+ type: Schema.Types.Mixed,
37
+ default: {},
38
+ },
39
+ activity_type: {
40
+ type: String,
41
+ enum: Object.values(ActivityType),
42
+ },
43
+ related_id: {
44
+ type: Schema.Types.ObjectId,
45
+ required: false,
46
+ },
47
+ user_id: {
48
+ type: Schema.Types.ObjectId,
49
+ required: true,
50
+ },
51
+ created_at: {
52
+ type: Date,
53
+ default: Date.now,
54
+ },
55
+ });
56
+
57
+ export type ActivityDocument = IActivity & mongoose.Document;
58
+
59
+ export const Activity = mongoose.model<ActivityDocument>(
60
+ "activities",
61
+ activitySchema
62
+ );
src/common/models/meal.model.ts CHANGED
@@ -4,6 +4,7 @@ import { MealType } from "@common/enums/meal-type.enum";
4
  export interface IMeal {
5
  name: string;
6
  created_at: Date;
 
7
  ingredients: string[];
8
  calories: number;
9
  carbs: number;
@@ -15,6 +16,7 @@ export interface IMeal {
15
  const mealSchema = new Schema({
16
  name: { type: String, required: true, unique: true, dropDups: true },
17
  created_at: { type: Date, default: Date.now() },
 
18
  type: {
19
  type: String,
20
  enum: MealType,
 
4
  export interface IMeal {
5
  name: string;
6
  created_at: Date;
7
+ image: string;
8
  ingredients: string[];
9
  calories: number;
10
  carbs: number;
 
16
  const mealSchema = new Schema({
17
  name: { type: String, required: true, unique: true, dropDups: true },
18
  created_at: { type: Date, default: Date.now() },
19
+ image: { type: String, required: true },
20
  type: {
21
  type: String,
22
  enum: MealType,
src/common/serializers/meal-planPopulate.serialization.ts CHANGED
@@ -97,4 +97,15 @@ export class ListMealPlanSerialization {
97
  )
98
  key_features: any;
99
 
 
 
 
 
 
 
 
 
 
 
 
100
  }
 
97
  )
98
  key_features: any;
99
 
100
+ }
101
+
102
+
103
+ export class MSerialization {
104
+ @Expose({ name: "_id" })
105
+ @SwaggerResponseProperty({ type: "string" })
106
+ id: string;
107
+
108
+ @Expose()
109
+ @SwaggerResponseProperty({ type: "string" })
110
+ image: string;
111
  }
src/common/serializers/meal.serialization.ts CHANGED
@@ -15,6 +15,10 @@ export class MealSerialization {
15
  @SwaggerResponseProperty({ type: "string" })
16
  created_at: Date;
17
 
 
 
 
 
18
  @Expose()
19
  @SwaggerResponseProperty({ type: ["string"] })
20
  ingredients: any;
 
15
  @SwaggerResponseProperty({ type: "string" })
16
  created_at: Date;
17
 
18
+ @Expose()
19
+ @SwaggerResponseProperty({ type: "string" })
20
+ image: string;
21
+
22
  @Expose()
23
  @SwaggerResponseProperty({ type: ["string"] })
24
  ingredients: any;
src/common/serializers/mealPopulate.serialization.ts CHANGED
@@ -17,6 +17,10 @@ export class MealPopulateSerialization {
17
  @SwaggerResponseProperty({ type: "string" })
18
  created_at: Date;
19
 
 
 
 
 
20
  @Expose()
21
  @SwaggerResponseProperty({ type: [IngredientSerialization] })
22
  @Transform(
 
17
  @SwaggerResponseProperty({ type: "string" })
18
  created_at: Date;
19
 
20
+ @Expose()
21
+ @SwaggerResponseProperty({ type: "string" })
22
+ image: string;
23
+
24
  @Expose()
25
  @SwaggerResponseProperty({ type: [IngredientSerialization] })
26
  @Transform(
src/common/serializers/user-registered-meal-planPopulate.serialization.ts CHANGED
@@ -1,8 +1,8 @@
1
  import { Expose, Transform } from "class-transformer";
2
  import { serialize } from "@helpers/serialize";
3
  import { SwaggerResponseProperty } from "@lib/decorators/swagger-response-property.decorator";
4
- import { ListMealPlanSerialization } from "./meal-planPopulate.serialization";
5
- import { MealSerialization } from "./meal.serialization";
6
  import { MealPlanSerialization } from "./meal-plan.serialization";
7
 
8
 
@@ -13,9 +13,9 @@ class MealDaysPopulate {
13
  day_number: number;
14
 
15
  @Expose({ name: "meals" })
16
- @SwaggerResponseProperty({ type: [MealSerialization] })
17
  @Transform(
18
- ({ value }) => serialize(value, MealSerialization)
19
  )
20
  meals: any;
21
 
@@ -65,7 +65,7 @@ export class GetMyMealPlanSerialization {
65
 
66
  @Expose()
67
  @SwaggerResponseProperty({ type: ListMealPlanSerialization })
68
- meal_plan: string;
69
 
70
  @Expose()
71
  @SwaggerResponseProperty({ type: "boolean" })
 
1
  import { Expose, Transform } from "class-transformer";
2
  import { serialize } from "@helpers/serialize";
3
  import { SwaggerResponseProperty } from "@lib/decorators/swagger-response-property.decorator";
4
+ import { ListMealPlanSerialization } from "@common/serializers/meal-planPopulate.serialization";
5
+ import { MealPopulateSerialization } from "./mealPopulate.serialization";
6
  import { MealPlanSerialization } from "./meal-plan.serialization";
7
 
8
 
 
13
  day_number: number;
14
 
15
  @Expose({ name: "meals" })
16
+ @SwaggerResponseProperty({ type: [MealPopulateSerialization] })
17
  @Transform(
18
+ ({ value }) => serialize(value, MealPopulateSerialization)
19
  )
20
  meals: any;
21
 
 
65
 
66
  @Expose()
67
  @SwaggerResponseProperty({ type: ListMealPlanSerialization })
68
+ meal_plan: any;
69
 
70
  @Expose()
71
  @SwaggerResponseProperty({ type: "boolean" })
src/lib/events/events-manager.ts ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { asyncHandler } from '@helpers/async-handler';
2
+ import EventEmitter from 'node:events';
3
+
4
+ /**
5
+ * EventsManager is a singleton class that manages events.
6
+ * It uses the EventEmitter class from Node.js to handle events.
7
+ */
8
+ export class EventsManager {
9
+ private static instance: EventsManager;
10
+ private emitter: EventEmitter;
11
+
12
+ private constructor() {
13
+ this.emitter = new EventEmitter();
14
+ }
15
+
16
+ public static getInstance(): EventsManager {
17
+ if (!EventsManager.instance) {
18
+ EventsManager.instance = new EventsManager();
19
+ }
20
+
21
+ return EventsManager.instance;
22
+ }
23
+
24
+ /**
25
+ * Register an event listener.
26
+ * @param event The event name.
27
+ * @param listener The listener function.
28
+ */
29
+ public static on(event: string, listener: (...args: any[]) => void | Promise<void>): void {
30
+ try {
31
+ EventsManager.getInstance().emitter.on(event, asyncHandler(listener));
32
+ } catch (error) {
33
+ console.error(error);
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Remove an event listener.
39
+ * @param event The event name.
40
+ * @param listener The listener function.
41
+ */
42
+ public static emit(event: string, ...args: any[]): void {
43
+ EventsManager.getInstance().emitter.emit(event, ...args);
44
+ }
45
+
46
+ /**
47
+ * Create a queue to store events.
48
+ * @returns An EventsQueue object.
49
+ * @see EventsQueue
50
+ */
51
+ public createQueue() {
52
+ return new class EventsQueue {
53
+ private queue: any[] = [];
54
+
55
+ public add(event: string, ...args: any[]): void {
56
+ this.queue.push({ event, args });
57
+ }
58
+
59
+ public clear() {
60
+ this.queue = [];
61
+ }
62
+
63
+ public process() {
64
+ this.queue.forEach((item) => {
65
+ EventsManager.emit(item.event, ...item.args);
66
+ });
67
+
68
+ this.clear();
69
+ }
70
+ }
71
+ }
72
+ }
src/modules/console/modules/meals/validations/create-meals.validation.ts CHANGED
@@ -5,6 +5,7 @@ import { MealType } from "@common/enums/meal-type.enum";
5
  export interface ICreateMeal {
6
  name: string;
7
  created_at?: Date;
 
8
  ingredients: string[];
9
  calories: number;
10
  carbs: number;
@@ -24,6 +25,12 @@ export const createMealSchema = createSchema<ICreateMeal>({
24
  "date.empty": "created_at can not be empty",
25
  }),
26
 
 
 
 
 
 
 
27
  calories: joi.number().empty().required().messages({
28
  "number.base": "please enter a valid calories",
29
  "any.required": "calories is required",
 
5
  export interface ICreateMeal {
6
  name: string;
7
  created_at?: Date;
8
+ image: string;
9
  ingredients: string[];
10
  calories: number;
11
  carbs: number;
 
25
  "date.empty": "created_at can not be empty",
26
  }),
27
 
28
+ image: joi.string().empty().required().messages({
29
+ "string.base": "please enter a valid image",
30
+ "any.required": "image is required",
31
+ "string.empty": "image can not be empty",
32
+ }),
33
+
34
  calories: joi.number().empty().required().messages({
35
  "number.base": "please enter a valid calories",
36
  "any.required": "calories is required",
src/modules/console/modules/meals/validations/update-meals.validation.ts CHANGED
@@ -5,6 +5,7 @@ import { MealType } from "@common/enums/meal-type.enum";
5
  export interface IUpdateMeal {
6
  name?: string;
7
  created_at?: Date;
 
8
  ingredients?: [string];
9
  calories?: number;
10
  carbs?: number;
@@ -22,6 +23,10 @@ export const updateMealSchema = createSchema<IUpdateMeal>({
22
  "date.base": "please enter a valid created_at",
23
  "date.empty": "created_at can not be empty",
24
  }),
 
 
 
 
25
  ingredients: joi.array().items(joi.string()).optional().messages({
26
  "array.base": "please enter a valid ingredients",
27
  }),
 
5
  export interface IUpdateMeal {
6
  name?: string;
7
  created_at?: Date;
8
+ image?: string;
9
  ingredients?: [string];
10
  calories?: number;
11
  carbs?: number;
 
23
  "date.base": "please enter a valid created_at",
24
  "date.empty": "created_at can not be empty",
25
  }),
26
+ image: joi.string().empty().optional().messages({
27
+ "string.base": "please enter a valid image",
28
+ "string.empty": "image can not be empty",
29
+ }),
30
  ingredients: joi.array().items(joi.string()).optional().messages({
31
  "array.base": "please enter a valid ingredients",
32
  }),
src/modules/users/modules/home/controllers/home-nutriguide.controller.ts CHANGED
@@ -15,7 +15,7 @@ import { UserHomeService } from "../services/user-home.service";
15
  import { IUserRequest } from "@common/interfaces/user-request.interface";
16
  import { UserHomeYourDailyIntakeSerialization } from "../responses/user-home-your-daily-intake.serialization";
17
  import { UserNutriHomeDailyGoalsSerialization } from "../responses/user-nutri-home-daily-goals.serialization";
18
- import { UserRegisteredMealPlansPopulateSerialization } from "@common/serializers/user-registered-meal-planPopulate.serialization";
19
  import { UserRegisteredMealPlansService } from "../../user-registered-meal-plans/services/user-registered-meal-plans.service";
20
  import { SwaggerRequest } from "@lib/decorators/swagger-request.decorator";
21
 
@@ -37,7 +37,7 @@ export class homeNutriGuideController extends BaseController {
37
  }
38
 
39
  @SwaggerGet("/today-meals")
40
- @SwaggerResponse(UserRegisteredMealPlansPopulateSerialization)
41
  @SwaggerSummary("Get today's meals")
42
  @SwaggerDescription("Get today's meals for the user.")
43
  getTodayMeals = async (req: IUserRequest, res: Response): Promise<Response> => {
@@ -48,11 +48,13 @@ export class homeNutriGuideController extends BaseController {
48
  {
49
  populateArray: [
50
  { path: "meal_plan", select: "-days" },
51
- { path: "days.meals" }
 
 
 
52
  ],
53
  }
54
  );
55
- console.log(data);
56
 
57
 
58
  const dayToEat = data.days.find(day => day.is_eaten === false);
@@ -61,7 +63,7 @@ export class homeNutriGuideController extends BaseController {
61
  data.days = [dayToEat];
62
  return JsonResponse.success(
63
  {
64
- data: serialize(data, UserRegisteredMealPlansPopulateSerialization),
65
  },
66
  res
67
  );
 
15
  import { IUserRequest } from "@common/interfaces/user-request.interface";
16
  import { UserHomeYourDailyIntakeSerialization } from "../responses/user-home-your-daily-intake.serialization";
17
  import { UserNutriHomeDailyGoalsSerialization } from "../responses/user-nutri-home-daily-goals.serialization";
18
+ import { GetMyMealPlanSerialization } from "@common/serializers/user-registered-meal-planPopulate.serialization";
19
  import { UserRegisteredMealPlansService } from "../../user-registered-meal-plans/services/user-registered-meal-plans.service";
20
  import { SwaggerRequest } from "@lib/decorators/swagger-request.decorator";
21
 
 
37
  }
38
 
39
  @SwaggerGet("/today-meals")
40
+ @SwaggerResponse(GetMyMealPlanSerialization)
41
  @SwaggerSummary("Get today's meals")
42
  @SwaggerDescription("Get today's meals for the user.")
43
  getTodayMeals = async (req: IUserRequest, res: Response): Promise<Response> => {
 
48
  {
49
  populateArray: [
50
  { path: "meal_plan", select: "-days" },
51
+ {
52
+ path: "days.meals",
53
+ populate: { path: "ingredients" }
54
+ }
55
  ],
56
  }
57
  );
 
58
 
59
 
60
  const dayToEat = data.days.find(day => day.is_eaten === false);
 
63
  data.days = [dayToEat];
64
  return JsonResponse.success(
65
  {
66
+ data: serialize(data, GetMyMealPlanSerialization),
67
  },
68
  res
69
  );
src/modules/users/modules/meals/controller/meals.controller.ts CHANGED
@@ -9,11 +9,15 @@ import { Controller } from "@lib/decorators/controller.decorator";
9
  import { serialize } from "@helpers/serialize";
10
  import { ControllerMiddleware } from "@lib/decorators/controller-middleware.decorator";
11
  import { UsersGuardMiddleware } from "modules/users/common/guards/users.guard";
12
- import { SwaggerGet } from "@lib/decorators/swagger-routes.decorator";
13
  import { SwaggerResponse } from "@lib/decorators/swagger-response.decorator";
14
  import { SwaggerSummary } from "@lib/decorators/swagger-summary.decorator";
15
  import { SwaggerDescription } from "@lib/decorators/swagger-description.decorator";
16
  import { SwaggerQuery } from "@lib/decorators/swagger-query.decorator";
 
 
 
 
17
 
18
 
19
  @Controller("/user/meals")
@@ -23,6 +27,7 @@ export class UsersMealsController extends BaseController {
23
 
24
  setRoutes(): void {
25
  this.router.get("/", asyncHandler(this.list));
 
26
  }
27
 
28
  @SwaggerGet()
@@ -52,4 +57,21 @@ export class UsersMealsController extends BaseController {
52
  res
53
  );
54
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
 
 
9
  import { serialize } from "@helpers/serialize";
10
  import { ControllerMiddleware } from "@lib/decorators/controller-middleware.decorator";
11
  import { UsersGuardMiddleware } from "modules/users/common/guards/users.guard";
12
+ import { SwaggerGet, SwaggerPost } from "@lib/decorators/swagger-routes.decorator";
13
  import { SwaggerResponse } from "@lib/decorators/swagger-response.decorator";
14
  import { SwaggerSummary } from "@lib/decorators/swagger-summary.decorator";
15
  import { SwaggerDescription } from "@lib/decorators/swagger-description.decorator";
16
  import { SwaggerQuery } from "@lib/decorators/swagger-query.decorator";
17
+ import { IUserRequest } from "@common/interfaces/user-request.interface";
18
+ import { IEatCustomMeal, eatCustomMealSchema } from "../validations/eat-custom-meal.validation";
19
+ import { bodyValidator } from "@helpers/validation.helper";
20
+ import { SwaggerRequest } from "@lib/decorators/swagger-request.decorator";
21
 
22
 
23
  @Controller("/user/meals")
 
27
 
28
  setRoutes(): void {
29
  this.router.get("/", asyncHandler(this.list));
30
+ this.router.post("/eat-custom-meal", bodyValidator(eatCustomMealSchema), asyncHandler(this.eatCustomMeal));
31
  }
32
 
33
  @SwaggerGet()
 
57
  res
58
  );
59
  };
60
+
61
+ @SwaggerPost('/eat-custom-meal')
62
+ @SwaggerResponse({})
63
+ @SwaggerRequest(eatCustomMealSchema)
64
+ @SwaggerSummary("Eat custom meal")
65
+ @SwaggerDescription("Eat custom meal")
66
+ eatCustomMeal = async (req: IUserRequest, res: Response) => {
67
+ await this.mealsService.eatCustomMeal(req.jwtPayload.id, req.body as IEatCustomMeal);
68
+
69
+ return JsonResponse.success(
70
+ {
71
+ message: "Meal created successfully",
72
+ },
73
+ res
74
+ );
75
+ };
76
  }
77
+
src/modules/users/modules/meals/events/eat-custom-meal.event.ts ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ActivityType } from "@common/enums/activity-type.enum";
2
+ import { Activity, IActivity } from "@common/models/activity.model";
3
+ import { EventsManager } from "@lib/events/events-manager";
4
+ import { IEatCustomMeal } from "../validations/eat-custom-meal.validation";
5
+
6
+ export class EatCustomMealEvent {
7
+ constructor(public userId: string, public ingredients: IEatCustomMeal['ingredients']) {}
8
+ }
9
+
10
+ EventsManager.on(ActivityType.EAT_CUSTOM_MEAL, (event: EatCustomMealEvent) => {
11
+ console.log(`User with id ${event.userId} ate a custom meal with ingredients: ${event.ingredients.map(i => i.id).join(", ")}`);
12
+
13
+ Activity.create({
14
+ user_id: event.userId,
15
+ activity_type: ActivityType.EAT_CUSTOM_MEAL,
16
+ related_item: { ingredients: event.ingredients },
17
+ } satisfies IActivity).catch(console.error);
18
+ });
src/modules/users/modules/meals/services/meals.service.ts CHANGED
@@ -1,4 +1,27 @@
1
  import { Meal } from "@common/models/meal.model";
2
  import { CrudService } from "@lib/services/crud.service";
 
 
 
 
 
 
 
3
 
4
- export class MealsService extends CrudService(Meal) {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import { Meal } from "@common/models/meal.model";
2
  import { CrudService } from "@lib/services/crud.service";
3
+ import { IEatCustomMeal } from "../validations/eat-custom-meal.validation";
4
+ import { EventsManager } from "@lib/events/events-manager";
5
+ import { Ingredient } from "@common/models/ingredient.model";
6
+ import { HttpError } from "@lib/error-handling/http-error";
7
+ import { ActivityType } from "@common/enums/activity-type.enum";
8
+ import { EatCustomMealEvent } from "../events/eat-custom-meal.event";
9
+ import { Types } from "mongoose";
10
 
11
+ export class MealsService extends CrudService(Meal) {
12
+ async eatCustomMeal(id: string, body: IEatCustomMeal) {
13
+ // Validate ingredients
14
+ await Promise.all(
15
+ body.ingredients.map(async i => {
16
+ const ing = await Ingredient.findOne({ _id: new Types.ObjectId(i.id) });
17
+ if(!ing) throw new HttpError(404, `Ingredient with id ${i} not found`);
18
+ return {
19
+ ingredient: ing,
20
+ noServings: i.noServings,
21
+ };
22
+ })
23
+ )
24
+
25
+ EventsManager.emit(ActivityType.EAT_CUSTOM_MEAL, new EatCustomMealEvent(id, body.ingredients));
26
+ }
27
+ }
src/modules/users/modules/meals/validations/eat-custom-meal.validation.ts ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as joi from "joi";
2
+ import { createSchema } from "@helpers/create-schema";
3
+
4
+ export interface IEatCustomMeal {
5
+ ingredients: {
6
+ id: string;
7
+ noServings: number;
8
+ }[];
9
+ }
10
+
11
+ export const eatCustomMealKeys = {
12
+ ingredients: joi.array().items(joi.object({
13
+ id: joi.string().required(),
14
+ noServings: joi.number().required(),
15
+ })).required(),
16
+ };
17
+
18
+ export const eatCustomMealSchema = createSchema<IEatCustomMeal>(eatCustomMealKeys);
src/modules/users/modules/user-registered-meal-plans/controller/user-registered-meal-plans.controller.ts CHANGED
@@ -9,6 +9,8 @@ import { GetMyMealPlanSerialization } from "@common/serializers/user-registered-
9
  import { ControllerMiddleware } from "@lib/decorators/controller-middleware.decorator";
10
  import { UsersGuardMiddleware } from "modules/users/common/guards/users.guard";
11
  import { SwaggerGet } from "@lib/decorators/swagger-routes.decorator";
 
 
12
  import { SwaggerResponse } from "@lib/decorators/swagger-response.decorator";
13
  import { SwaggerSummary } from "@lib/decorators/swagger-summary.decorator";
14
  import { SwaggerDescription } from "@lib/decorators/swagger-description.decorator";
@@ -23,6 +25,10 @@ export class UsersRegisteredMealPlansController extends BaseController {
23
 
24
  setRoutes(): void {
25
  this.router.get("/", asyncHandler(this.get));
 
 
 
 
26
  }
27
 
28
  @SwaggerGet()
@@ -34,8 +40,8 @@ export class UsersRegisteredMealPlansController extends BaseController {
34
  { user: req.jwtPayload.id, isActive: true },
35
  {
36
  populateArray: [
37
- { path: "meal_plan", select: "-days"},
38
- { path: "days.meals"}
39
  ],
40
  });
41
 
@@ -46,4 +52,21 @@ export class UsersRegisteredMealPlansController extends BaseController {
46
  res
47
  );
48
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  }
 
9
  import { ControllerMiddleware } from "@lib/decorators/controller-middleware.decorator";
10
  import { UsersGuardMiddleware } from "modules/users/common/guards/users.guard";
11
  import { SwaggerGet } from "@lib/decorators/swagger-routes.decorator";
12
+ import { SwaggerPost } from "@lib/decorators/swagger-routes.decorator";
13
+ import { SwaggerRequest } from "@lib/decorators/swagger-request.decorator";
14
  import { SwaggerResponse } from "@lib/decorators/swagger-response.decorator";
15
  import { SwaggerSummary } from "@lib/decorators/swagger-summary.decorator";
16
  import { SwaggerDescription } from "@lib/decorators/swagger-description.decorator";
 
25
 
26
  setRoutes(): void {
27
  this.router.get("/", asyncHandler(this.get));
28
+ this.router.post(
29
+ "/",
30
+ asyncHandler(this.create)
31
+ );
32
  }
33
 
34
  @SwaggerGet()
 
40
  { user: req.jwtPayload.id, isActive: true },
41
  {
42
  populateArray: [
43
+ { path: "meal_plan", select: "-days" },
44
+ { path: "days.meals", populate: { path: "ingredients" } }
45
  ],
46
  });
47
 
 
52
  res
53
  );
54
  };
55
+
56
+
57
+ @SwaggerPost()
58
+ @SwaggerResponse(GetMyMealPlanSerialization)
59
+ @SwaggerRequest({ mealPlan: "string" })
60
+ @SwaggerSummary("create my meal plan")
61
+ @SwaggerDescription("Create a new meal plan for the user")
62
+ create = async (req: userRequest, res: Response) => {
63
+ const data = await this.userRegisteredMealPlansService.createForUser(req.body, req.jwtPayload.id);
64
+ return JsonResponse.success(
65
+ {
66
+ status: 201,
67
+ data: serialize(data.toJSON(), GetMyMealPlanSerialization),
68
+ },
69
+ res
70
+ );
71
+ };
72
  }
src/modules/users/modules/user-registered-meal-plans/services/user-registered-meal-plans.service.ts CHANGED
@@ -1,10 +1,35 @@
1
  import { UserRegisteredMealPlan } from "@common/models/user-registered-meal-plan.model";
2
  import { CrudService } from "@lib/services/crud.service";
3
- import { IUpdateUserRegisteredMealPlan } from "../validations/update-user-registered-meal-plan.validation";
4
  import { HttpError } from "@lib/error-handling/http-error";
5
 
6
 
7
  export class UserRegisteredMealPlansService extends CrudService(UserRegisteredMealPlan) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  async updateForUser(mealPlanProps: {urwId: string; dayNumber: number}, data: any, userId: string) {
9
  // find workout
10
  const mealPlan = await this.findOneOrFail({
 
1
  import { UserRegisteredMealPlan } from "@common/models/user-registered-meal-plan.model";
2
  import { CrudService } from "@lib/services/crud.service";
3
+ import { MealPlansService } from "../../meal-plans/services/meal-plans.service";
4
  import { HttpError } from "@lib/error-handling/http-error";
5
 
6
 
7
  export class UserRegisteredMealPlansService extends CrudService(UserRegisteredMealPlan) {
8
+ private mealPlansService: MealPlansService = new MealPlansService();
9
+
10
+ async unregisterCurrentMealPlan(userId: string) {
11
+ return await this.updateMany({
12
+ user: userId,
13
+ isActive: true,
14
+ }, {
15
+ isActive: false,
16
+ });
17
+ }
18
+
19
+ async createForUser(data: any, userId: string) {
20
+ const mealPlan = await this.mealPlansService.findOneOrFail({
21
+ _id: data.mealPlan,
22
+ });
23
+
24
+ await this.unregisterCurrentMealPlan(userId);
25
+
26
+ return await this.create({
27
+ ...data,
28
+ user: userId,
29
+ days: mealPlan.days,
30
+ isActive: true,
31
+ });
32
+ }
33
  async updateForUser(mealPlanProps: {urwId: string; dayNumber: number}, data: any, userId: string) {
34
  // find workout
35
  const mealPlan = await this.findOneOrFail({
src/resources/meals1.json DELETED
The diff for this file is too large to render. See raw diff
 
src/resources/meals2.json DELETED
The diff for this file is too large to render. See raw diff
 
src/seeder/seeders/9-meals.seeder.ts CHANGED
@@ -21,6 +21,8 @@ export default seederWrapper(Meal, async () => {
21
  console.log('preping meals data...')
22
  const data = await Promise.all(dbStore.mealsDataset.map(async (mealJson) => ({
23
  name: mealJson.Name,
 
 
24
  ingredients: mealJson.RecipeIngredientParts.map(name => ingredientsIds.find(i => i.name === name)._id),
25
  calories: mealJson.Calories,
26
  carbs: mealJson.CarbohydrateContent,
 
21
  console.log('preping meals data...')
22
  const data = await Promise.all(dbStore.mealsDataset.map(async (mealJson) => ({
23
  name: mealJson.Name,
24
+ created_at: new Date(),
25
+ image: `https://placehold.co/300x400`,
26
  ingredients: mealJson.RecipeIngredientParts.map(name => ingredientsIds.find(i => i.name === name)._id),
27
  calories: mealJson.Calories,
28
  carbs: mealJson.CarbohydrateContent,