Skip to content

Feature/back#32

Merged
elhalj merged 2 commits intomainfrom
feature/back
Sep 7, 2025
Merged

Feature/back#32
elhalj merged 2 commits intomainfrom
feature/back

Conversation

@elhalj
Copy link
Owner

@elhalj elhalj commented Sep 7, 2025

###Feature Back
#Add notification an comment route

@elhalj elhalj added the enhancement New feature or request label Sep 7, 2025
@elhalj
Copy link
Owner Author

elhalj commented Sep 7, 2025

@sourcery-ai title

@elhalj
Copy link
Owner Author

elhalj commented Sep 7, 2025

@sourcery-ai review

@elhalj elhalj merged commit 8aab5e0 into main Sep 7, 2025
4 of 6 checks passed
@SourceryAI
Copy link

Reviewer's Guide

This PR refactors route definitions to leverage dedicated controller classes, consolidates business logic into a new controllers directory, introduces a full notification subsystem (model, controller, routes), enhances the comment module with pagination, replies, and likes, and extends the frontend Auth hook/context to support user profile and preference updates.

Sequence diagram for comment creation with reply and like

sequenceDiagram
  actor User
  participant Frontend
  participant Fastify
  participant Comments
  participant CommentModel
  participant TaskModel
  participant RoomModel
  participant UserModel

  User->>Frontend: Submit new comment (with reply/like)
  Frontend->>Fastify: POST /add/comments
  Fastify->>Comments: addComment(request)
  Comments->>CommentModel: Save comment
  Comments->>UserModel: Update user stats
  Comments->>TaskModel: Update task (if taskId)
  Comments->>RoomModel: Update room (if roomId)
  Comments-->>Fastify: Return populated comment
  Fastify-->>Frontend: Response
  User->>Frontend: Like comment
  Frontend->>Fastify: POST /comments/:commentId/like
  Fastify->>Comments: addLike(request)
  Comments->>CommentModel: Update likes
  Comments-->>Fastify: Return updated like count
  Fastify-->>Frontend: Response
Loading

ER diagram for updated User, Task, and Comment models

erDiagram
  USER {
    string userName
    string email
    string password
    object profile
    object stats
    object notification
    object preference
    object rooms
    object myTasks
    object collaborators
    object comment
  }
  TASK {
    string title
    string description
    string status
    string priority
    date dueDate
    number estimatedHours
    object author
    object assignees
    object room
    object comments
  }
  COMMENT {
    string content
    object author
    object task
    object room
    object replies
    object likes
  }
  USER ||--o{ TASK : "author"
  USER ||--o{ COMMENT : "comment"
  TASK ||--o{ COMMENT : "comments"
  COMMENT }o--|| USER : "author"
  COMMENT }o--|| TASK : "task"
  COMMENT }o--|| ROOM : "room"
  COMMENT ||--o{ COMMENT : "replies"
  COMMENT ||--o{ USER : "likes"
  USER ||--o{ ROOM : "rooms"
  ROOM ||--o{ USER : "members"
  ROOM ||--o{ TASK : "tasks"
  ROOM ||--o{ COMMENT : "comments"
Loading

File-Level Changes

Change Details Files
Refactored routes to delegate logic to controller methods
  • Imported and instantiated controllers in each route file
  • Replaced inline async route handlers with controller.method references
  • Updated server setup to register notification routes
src/routes/room.route.js
src/routes/task.route.js
src/routes/comment.route.js
src/routes/user.route.js
src/routes/notification.route.js
index.js
Added notification subsystem
  • Created NotificationController with CRUD and read/unread endpoints
  • Defined notification routes for create, fetch, mark read, delete, unread count
  • Registered notificationRoutes in main server index
src/controllers/notification.controller.js
src/routes/notification.route.js
index.js
Consolidated backend logic into controllers directory
  • Moved business logic out of route files into controllers for room, task, comment, user
  • Adjusted models index export to include all schemas
  • Introduced centralized route index exporting all route modules
src/controllers/room.controller.js
src/controllers/task.controller.js
src/controllers/comment.controller.js
src/controllers/user.controller.js
src/models/index.js
src/routes/index.js
Enhanced comment functionality
  • Implemented pagination for task/room comment retrieval
  • Added reply and like/unlike handling in comment controller
  • Updated comment routes to point to new controller methods
src/controllers/comment.controller.js
src/routes/comment.route.js
src/models/comment.model.js
Extended frontend Auth hook/context
  • Added updatePreferences and updateProfile methods to useAuth hook
  • Integrated new API calls in AuthProvider with loading/error handling and toast notifications
  • Updated AuthContext definition to include profile and preference update functions
frontend/src/hook/useAuth.ts
frontend/src/context/AuthProvider.tsx
frontend/src/context/AuthContext.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@SourceryAI SourceryAI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • src/controllers/room.controller.js is missing the import mongoose statement before using mongoose.startSession, which will cause runtime errors.
  • RoomController defines deleteRoom twice—remove the duplicate method at the end to avoid unintentionally overriding the intended logic.
  • TaskController methods reference fastify.io and call emitTaskNotification directly—consider injecting the Fastify instance into the controller (e.g. via constructor) and using this.emitTaskNotification so socket emissions work correctly.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- src/controllers/room.controller.js is missing the `import mongoose` statement before using `mongoose.startSession`, which will cause runtime errors.
- RoomController defines `deleteRoom` twice—remove the duplicate method at the end to avoid unintentionally overriding the intended logic.
- TaskController methods reference `fastify.io` and call `emitTaskNotification` directly—consider injecting the Fastify instance into the controller (e.g. via constructor) and using `this.emitTaskNotification` so socket emissions work correctly.

## Individual Comments

### Comment 1
<location> `src/controllers/user.controller.js:157` </location>
<code_context>
+          .send({ success: false, message: "Erreur au niveau de l'objet" });
+      }
+
+      const updatePrefer = await User.findByIdAndUpdate(
+        profileId,
+        { $set: { preferences } },
+        { $new: true }
+      );
</code_context>

<issue_to_address>
Incorrect option '$new' in Mongoose update may prevent returning updated document.

Use 'new: true' instead of '$new: true' to ensure the updated document is returned.
</issue_to_address>

### Comment 2
<location> `src/controllers/user.controller.js:126` </location>
<code_context>
+        $push: { replies: newReply._id },
+      });
+
+      return reply.code(201).send({
+        success: true,
+        message: "Effectué avec success",
</code_context>

<issue_to_address>
updatePreference response incorrectly sets 'success' to false on success.

Please change 'success' to true in the response to correctly indicate a successful update.
</issue_to_address>

### Comment 3
<location> `src/controllers/task.controller.js:9` </location>
<code_context>
+import User from "../models/user.model.js";
+
+export class TaskController {
+  emitTaskNotification = (events, data, userId = null) => {
+    try {
+      const notification = {
</code_context>

<issue_to_address>
emitTaskNotification references 'fastify' which is not defined in the controller context.

Please pass 'fastify' as a parameter to this function or refactor to use an alternative notification approach.
</issue_to_address>

### Comment 4
<location> `src/controllers/task.controller.js:204` </location>
<code_context>
+        session.endSession();
+
+        // Notifications
+        emitTaskNotification("taskCreated", {
+          task: savedTask,
+          message: `Nouvelle tâche créée: ${savedTask.title}`,
</code_context>

<issue_to_address>
emitTaskNotification is called without 'this.', which may cause a ReferenceError.

Call 'emitTaskNotification' with 'this.' to avoid context issues and potential ReferenceErrors.
</issue_to_address>

### Comment 5
<location> `src/controllers/task.controller.js:366` </location>
<code_context>
+      });
+
+      //Notification spécifique à la tache (si d'autre utilisateur collabore)
+      fastify.io.to(`task_${id}`).emit("taskRoomUpdate", {
+        taskId: id,
+        task: updatedTask,
</code_context>

<issue_to_address>
Direct use of 'fastify.io' in controller may break separation of concerns.

Consider moving notification logic out of the controller or injecting dependencies to maintain separation of concerns.

Suggested implementation:

```javascript
      //Notification spécifique à la tache (si d'autre utilisateur collabore)
+      await taskNotificationService.notifyTaskRoomUpdate({
+        taskId: id,
+        task: updatedTask,

```

1. Create a new file `src/services/taskNotification.service.js` with a class or object that exposes a `notifyTaskRoomUpdate` method, which uses `fastify.io` to emit the event.
2. In your controller, import and instantiate (or inject) `taskNotificationService`.
3. Ensure that `fastify.io` is available to the service, either by passing it in the constructor or as a parameter to the method.
4. Update any tests or usages to use the new service.
</issue_to_address>

### Comment 6
<location> `src/controllers/task.controller.js:464` </location>
<code_context>
+      });
+
+      // Informer les utilisateurs de la tâche qu'elle a été supprimée
+      fastify.io.to(`task_${id}`).emit("taskRoomDeleted", {
+        taskId: id,
+        message: "Cette tache a été suprimé",
</code_context>

<issue_to_address>
Same issue as above: 'fastify.io' is not defined in controller context.
</issue_to_address>

### Comment 7
<location> `src/controllers/comment.controller.js:361` </location>
<code_context>
+        });
+      }
+
+      // Créer l'objet de réponse
+      const replyData = {
+        content: content.trim(),
+        author: userId,
+        likes: [],
+      };
+
+      // Ajouter la réponse au tableau replies du commentaire parent
+      parentComment.replies.push(replyData);
+      await parentComment.save();
</code_context>

<issue_to_address>
Variable 'newReply' is used before assignment in replyComment.

Referencing 'newReply' before it is defined will result in a ReferenceError.
</issue_to_address>

### Comment 8
<location> `src/controllers/comment.controller.js:373` </location>
<code_context>
+      await parentComment.save();
+
+      // Mettre à jour les statistiques de l'utilisateur
+      await User.findByIdAndUpdate(userId, {
+        $push: { comment: newReply._id },
+      });
</code_context>

<issue_to_address>
User comment statistics update in replyComment may not work as intended.

'newReply' is undefined here; use the correct reply object or its ID when updating the user's comment array.
</issue_to_address>

### Comment 9
<location> `src/controllers/comment.controller.js:378` </location>
<code_context>
+      });
+
+      // Mettre à jour les données du commentaire parent
+      await Comment.findByIdAndUpdate(commentId, {
+        $push: { replies: newReply._id },
+      });
</code_context>

<issue_to_address>
Redundant update to parent comment's replies array in replyComment.

Since the reply is already added and saved, this extra update could cause duplicates in 'parentComment.replies'.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
      // Mettre à jour les données du commentaire parent
=======
      // Mettre à jour les données du commentaire parent
      // (Suppression de la mise à jour redondante pour éviter les doublons)
>>>>>>> REPLACE

</suggested_fix>

### Comment 10
<location> `src/controllers/comment.controller.js:382` </location>
<code_context>
+        $push: { replies: newReply._id },
+      });
+
+      return reply.code(201).send({
+        success: true,
+        message: "Effectué avec success",
</code_context>

<issue_to_address>
Response in replyComment includes 'newReply', which is not defined.

'newReply' is used but never assigned, which will cause runtime errors due to its undefined value.
</issue_to_address>

### Comment 11
<location> `src/models/user.model.js:128` </location>
<code_context>
         },
       ],
     },
+    comment: {
+      type: mongoose.Schema.Types.ObjectId,
+      ref: "Comment",
</code_context>

<issue_to_address>
User model 'comment' field is defined as ObjectId but default is an array.

Ensure the field type matches its intended use. If multiple comments are allowed, define the field as an array of ObjectIds; otherwise, update the default value to match the type.
</issue_to_address>

Hi @elhalj! 👋

Thanks for trying out Sourcery by commenting with @sourcery-ai review! 🚀

Install the sourcery-ai bot to get automatic code reviews on every pull request ✨

Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +157 to +159
const updatePrefer = await User.findByIdAndUpdate(
profileId,
{ $set: { preferences } },

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Incorrect option '$new' in Mongoose update may prevent returning updated document.

Use 'new: true' instead of '$new: true' to ensure the updated document is returned.

{ new: true }
);

return reply.code(201).send({

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): updatePreference response incorrectly sets 'success' to false on success.

Please change 'success' to true in the response to correctly indicate a successful update.

import User from "../models/user.model.js";

export class TaskController {
emitTaskNotification = (events, data, userId = null) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): emitTaskNotification references 'fastify' which is not defined in the controller context.

Please pass 'fastify' as a parameter to this function or refactor to use an alternative notification approach.

session.endSession();

// Notifications
emitTaskNotification("taskCreated", {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): emitTaskNotification is called without 'this.', which may cause a ReferenceError.

Call 'emitTaskNotification' with 'this.' to avoid context issues and potential ReferenceErrors.

});

//Notification spécifique à la tache (si d'autre utilisateur collabore)
fastify.io.to(`task_${id}`).emit("taskRoomUpdate", {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Direct use of 'fastify.io' in controller may break separation of concerns.

Consider moving notification logic out of the controller or injecting dependencies to maintain separation of concerns.

Suggested implementation:

      //Notification spécifique à la tache (si d'autre utilisateur collabore)
+      await taskNotificationService.notifyTaskRoomUpdate({
+        taskId: id,
+        task: updatedTask,
  1. Create a new file src/services/taskNotification.service.js with a class or object that exposes a notifyTaskRoomUpdate method, which uses fastify.io to emit the event.
  2. In your controller, import and instantiate (or inject) taskNotificationService.
  3. Ensure that fastify.io is available to the service, either by passing it in the constructor or as a parameter to the method.
  4. Update any tests or usages to use the new service.

async updateTask(request, reply) {
try {
const { id } = request.params;
const userId = request.user.userId;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring)

Suggested change
const userId = request.user.userId;
const {userId} = request.user;


ExplanationObject destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.

From the Airbnb Javascript Style Guide

async delete(request, reply) {
try {
const { id } = request.params;
const userId = request.user.userId;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring)

Suggested change
const userId = request.user.userId;
const {userId} = request.user;


ExplanationObject destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.

From the Airbnb Javascript Style Guide

Comment on lines +6 to +18
const users = await User.find({})
.populate("userName email stats")
.populate({
path: "notification",
select: "message type",
populate: {
path: "user",
select: "userName email",
},
})
.sort({ updatedAt: -1 });

return users;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
const users = await User.find({})
.populate("userName email stats")
.populate({
path: "notification",
select: "message type",
populate: {
path: "user",
select: "userName email",
},
})
.sort({ updatedAt: -1 });
return users;
return await User.find({})
.populate("userName email stats")
.populate({
path: "notification",
select: "message type",
populate: {
path: "user",
select: "userName email",
},
})
.sort({ updatedAt: -1 });


ExplanationSomething that we often see in people's code is assigning to a result variable
and then immediately returning it.

Returning the result directly shortens the code and removes an unnecessary
variable, reducing the mental load of reading the function.

Where intermediate variables can be useful is if they then get used as a
parameter or a condition, and the name can act like a comment on what the
variable represents. In the case where you're returning it from a function, the
function name is there to tell you what the result is, so the variable name
is unnecessary.


async update(request, reply) {
const { profileId } = request.params;
const userId = request.user.userId;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring)

Suggested change
const userId = request.user.userId;
const {userId} = request.user;


ExplanationObject destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.

From the Airbnb Javascript Style Guide


async updatePreference(request, reply) {
const { profileId } = request.params;
const userId = request.user.userId;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring)

Suggested change
const userId = request.user.userId;
const {userId} = request.user;


ExplanationObject destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.

From the Airbnb Javascript Style Guide

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants