From 31d94b1b3f75c4f3ed0ad6ab18291e015620f438 Mon Sep 17 00:00:00 2001 From: jackiettran <41605212+jackiettran@users.noreply.github.com> Date: Tue, 25 Nov 2025 17:22:57 -0500 Subject: [PATCH] simplified message model --- .../20241124000007-create-messages.js | 61 ++++ backend/models/Message.js | 12 - backend/models/index.js | 5 - backend/routes/messages.js | 20 +- .../email/domain/MessagingEmailService.js | 2 - .../templates/emails/newMessageToUser.html | 8 - backend/tests/unit/routes/messages.test.js | 144 +--------- frontend/src/App.tsx | 9 - frontend/src/components/ChatWindow.tsx | 1 - frontend/src/components/MessageModal.tsx | 22 +- frontend/src/pages/MessageDetail.tsx | 260 ------------------ frontend/src/types/index.ts | 3 - 12 files changed, 74 insertions(+), 473 deletions(-) create mode 100644 backend/migrations/20241124000007-create-messages.js delete mode 100644 frontend/src/pages/MessageDetail.tsx diff --git a/backend/migrations/20241124000007-create-messages.js b/backend/migrations/20241124000007-create-messages.js new file mode 100644 index 0000000..2bc81b3 --- /dev/null +++ b/backend/migrations/20241124000007-create-messages.js @@ -0,0 +1,61 @@ +"use strict"; + +module.exports = { + up: async (queryInterface, Sequelize) => { + await queryInterface.createTable("Messages", { + id: { + type: Sequelize.UUID, + defaultValue: Sequelize.UUIDV4, + primaryKey: true, + }, + senderId: { + type: Sequelize.UUID, + allowNull: false, + references: { + model: "Users", + key: "id", + }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + }, + receiverId: { + type: Sequelize.UUID, + allowNull: false, + references: { + model: "Users", + key: "id", + }, + onUpdate: "CASCADE", + onDelete: "CASCADE", + }, + content: { + type: Sequelize.TEXT, + allowNull: false, + }, + isRead: { + type: Sequelize.BOOLEAN, + defaultValue: false, + }, + imagePath: { + type: Sequelize.STRING, + allowNull: true, + }, + createdAt: { + type: Sequelize.DATE, + allowNull: false, + }, + updatedAt: { + type: Sequelize.DATE, + allowNull: false, + }, + }); + + // Add indexes + await queryInterface.addIndex("Messages", ["senderId"]); + await queryInterface.addIndex("Messages", ["receiverId"]); + }, + + down: async (queryInterface, Sequelize) => { + await queryInterface.dropTable("Messages"); + }, +}; diff --git a/backend/models/Message.js b/backend/models/Message.js index 7269131..724f569 100644 --- a/backend/models/Message.js +++ b/backend/models/Message.js @@ -23,10 +23,6 @@ const Message = sequelize.define('Message', { key: 'id' } }, - subject: { - type: DataTypes.STRING, - allowNull: false - }, content: { type: DataTypes.TEXT, allowNull: false @@ -35,14 +31,6 @@ const Message = sequelize.define('Message', { type: DataTypes.BOOLEAN, defaultValue: false }, - parentMessageId: { - type: DataTypes.UUID, - allowNull: true, - references: { - model: 'Messages', - key: 'id' - } - }, imagePath: { type: DataTypes.STRING, allowNull: true diff --git a/backend/models/index.js b/backend/models/index.js index de20370..a794e96 100644 --- a/backend/models/index.js +++ b/backend/models/index.js @@ -27,11 +27,6 @@ User.hasMany(Message, { as: "sentMessages", foreignKey: "senderId" }); User.hasMany(Message, { as: "receivedMessages", foreignKey: "receiverId" }); Message.belongsTo(User, { as: "sender", foreignKey: "senderId" }); Message.belongsTo(User, { as: "receiver", foreignKey: "receiverId" }); -Message.hasMany(Message, { as: "replies", foreignKey: "parentMessageId" }); -Message.belongsTo(Message, { - as: "parentMessage", - foreignKey: "parentMessageId", -}); // Forum associations User.hasMany(ForumPost, { as: "forumPosts", foreignKey: "authorId" }); diff --git a/backend/routes/messages.js b/backend/routes/messages.js index c1fc02d..f7f2d33 100644 --- a/backend/routes/messages.js +++ b/backend/routes/messages.js @@ -171,11 +171,11 @@ router.get('/sent', authenticateToken, async (req, res) => { } }); -// Get a single message with replies +// Get a single message router.get('/:id', authenticateToken, async (req, res) => { try { const message = await Message.findOne({ - where: { + where: { id: req.params.id, [require('sequelize').Op.or]: [ { senderId: req.user.id }, @@ -192,15 +192,6 @@ router.get('/:id', authenticateToken, async (req, res) => { model: User, as: 'receiver', attributes: ['id', 'firstName', 'lastName', 'profileImage'] - }, - { - model: Message, - as: 'replies', - include: [{ - model: User, - as: 'sender', - attributes: ['id', 'firstName', 'lastName', 'profileImage'] - }] } ] }); @@ -248,7 +239,7 @@ router.get('/:id', authenticateToken, async (req, res) => { // Send a new message router.post('/', authenticateToken, uploadMessageImage, async (req, res) => { try { - const { receiverId, subject, content, parentMessageId } = req.body; + const { receiverId, content } = req.body; // Check if receiver exists const receiver = await User.findByPk(receiverId); @@ -267,9 +258,7 @@ router.post('/', authenticateToken, uploadMessageImage, async (req, res) => { const message = await Message.create({ senderId: req.user.id, receiverId, - subject, content, - parentMessageId, imagePath }); @@ -308,8 +297,7 @@ router.post('/', authenticateToken, uploadMessageImage, async (req, res) => { reqLogger.info("Message sent", { senderId: req.user.id, receiverId: receiverId, - messageId: message.id, - isReply: !!parentMessageId + messageId: message.id }); res.status(201).json(messageWithSender); diff --git a/backend/services/email/domain/MessagingEmailService.js b/backend/services/email/domain/MessagingEmailService.js index 6ebd3c2..8f7c5bd 100644 --- a/backend/services/email/domain/MessagingEmailService.js +++ b/backend/services/email/domain/MessagingEmailService.js @@ -39,7 +39,6 @@ class MessagingEmailService { * @param {string} sender.firstName - Sender's first name * @param {string} sender.lastName - Sender's last name * @param {Object} message - Message object - * @param {string} message.subject - Message subject * @param {string} message.content - Message content * @param {Date} message.createdAt - Message creation timestamp * @returns {Promise<{success: boolean, messageId?: string, error?: string}>} @@ -61,7 +60,6 @@ class MessagingEmailService { const variables = { recipientName: receiver.firstName || "there", senderName: `${sender.firstName} ${sender.lastName}`.trim() || "A user", - subject: message.subject, messageContent: message.content, conversationUrl: conversationUrl, timestamp: timestamp, diff --git a/backend/templates/emails/newMessageToUser.html b/backend/templates/emails/newMessageToUser.html index 8a1b092..633b5c4 100644 --- a/backend/templates/emails/newMessageToUser.html +++ b/backend/templates/emails/newMessageToUser.html @@ -134,13 +134,6 @@ border-radius: 6px; } - .message-box .subject { - font-size: 16px; - font-weight: 600; - color: #495057; - margin: 0 0 15px 0; - } - .message-box .content-text { color: #212529; line-height: 1.6; @@ -226,7 +219,6 @@
{{senderName}} sent you a message on RentAll.
diff --git a/backend/tests/unit/routes/messages.test.js b/backend/tests/unit/routes/messages.test.js index 7db4203..1d85ec3 100644 --- a/backend/tests/unit/routes/messages.test.js +++ b/backend/tests/unit/routes/messages.test.js @@ -56,7 +56,6 @@ describe('Messages Routes', () => { id: 1, senderId: 2, receiverId: 1, - subject: 'Test Message', content: 'Hello there!', isRead: false, createdAt: '2024-01-15T10:00:00.000Z', @@ -71,7 +70,6 @@ describe('Messages Routes', () => { id: 2, senderId: 3, receiverId: 1, - subject: 'Another Message', content: 'Hi!', isRead: true, createdAt: '2024-01-14T10:00:00.000Z', @@ -122,7 +120,6 @@ describe('Messages Routes', () => { id: 3, senderId: 1, receiverId: 2, - subject: 'My Message', content: 'Hello Jane!', isRead: false, createdAt: '2024-01-15T12:00:00.000Z', @@ -171,7 +168,6 @@ describe('Messages Routes', () => { id: 1, senderId: 2, receiverId: 1, - subject: 'Test Message', content: 'Hello there!', isRead: false, createdAt: '2024-01-15T10:00:00.000Z', @@ -187,19 +183,6 @@ describe('Messages Routes', () => { lastName: 'Doe', profileImage: 'john.jpg' }, - replies: [ - { - id: 4, - senderId: 1, - content: 'Reply message', - sender: { - id: 1, - firstName: 'John', - lastName: 'Doe', - profileImage: 'john.jpg' - } - } - ], update: jest.fn() }; @@ -207,7 +190,7 @@ describe('Messages Routes', () => { mockMessageFindOne.mockResolvedValue(mockMessage); }); - it('should get message with replies for receiver', async () => { + it('should get message for receiver and mark as read', async () => { mockMessage.update.mockResolvedValue(); const response = await request(app) @@ -218,7 +201,6 @@ describe('Messages Routes', () => { id: 1, senderId: 2, receiverId: 1, - subject: 'Test Message', content: 'Hello there!', isRead: false, createdAt: '2024-01-15T10:00:00.000Z', @@ -233,20 +215,7 @@ describe('Messages Routes', () => { firstName: 'John', lastName: 'Doe', profileImage: 'john.jpg' - }, - replies: [ - { - id: 4, - senderId: 1, - content: 'Reply message', - sender: { - id: 1, - firstName: 'John', - lastName: 'Doe', - profileImage: 'john.jpg' - } - } - ] + } }); expect(mockMessage.update).toHaveBeenCalledWith({ isRead: true }); }); @@ -263,7 +232,6 @@ describe('Messages Routes', () => { id: 1, senderId: 1, receiverId: 2, - subject: 'Test Message', content: 'Hello there!', isRead: false, createdAt: '2024-01-15T10:00:00.000Z', @@ -278,20 +246,7 @@ describe('Messages Routes', () => { firstName: 'John', lastName: 'Doe', profileImage: 'john.jpg' - }, - replies: [ - { - id: 4, - senderId: 1, - content: 'Reply message', - sender: { - id: 1, - firstName: 'John', - lastName: 'Doe', - profileImage: 'john.jpg' - } - } - ] + } }); expect(mockMessage.update).not.toHaveBeenCalled(); }); @@ -340,9 +295,7 @@ describe('Messages Routes', () => { id: 5, senderId: 1, receiverId: 2, - subject: 'New Message', - content: 'Hello Jane!', - parentMessageId: null + content: 'Hello Jane!' }; const mockMessageWithSender = { @@ -364,9 +317,7 @@ describe('Messages Routes', () => { it('should create a new message', async () => { const messageData = { receiverId: 2, - subject: 'New Message', - content: 'Hello Jane!', - parentMessageId: null + content: 'Hello Jane!' }; const response = await request(app) @@ -378,31 +329,8 @@ describe('Messages Routes', () => { expect(mockMessageCreate).toHaveBeenCalledWith({ senderId: 1, receiverId: 2, - subject: 'New Message', content: 'Hello Jane!', - parentMessageId: null - }); - }); - - it('should create a reply message with parentMessageId', async () => { - const replyData = { - receiverId: 2, - subject: 'Re: Original Message', - content: 'This is a reply', - parentMessageId: 1 - }; - - const response = await request(app) - .post('/messages') - .send(replyData); - - expect(response.status).toBe(201); - expect(mockMessageCreate).toHaveBeenCalledWith({ - senderId: 1, - receiverId: 2, - subject: 'Re: Original Message', - content: 'This is a reply', - parentMessageId: 1 + imagePath: null }); }); @@ -413,7 +341,6 @@ describe('Messages Routes', () => { .post('/messages') .send({ receiverId: 999, - subject: 'Test', content: 'Test message' }); @@ -426,7 +353,6 @@ describe('Messages Routes', () => { .post('/messages') .send({ receiverId: 1, // Same as sender ID - subject: 'Self Message', content: 'Hello self!' }); @@ -441,7 +367,6 @@ describe('Messages Routes', () => { .post('/messages') .send({ receiverId: 2, - subject: 'Test', content: 'Test message' }); @@ -596,62 +521,5 @@ describe('Messages Routes', () => { expect(response.status).toBe(200); expect(response.body).toEqual([]); }); - - it('should handle message with no replies', async () => { - const messageWithoutReplies = { - id: 1, - senderId: 2, - receiverId: 1, - subject: 'Test Message', - content: 'Hello there!', - isRead: false, - replies: [], - update: jest.fn() - }; - mockMessageFindOne.mockResolvedValue(messageWithoutReplies); - - const response = await request(app) - .get('/messages/1'); - - expect(response.status).toBe(200); - expect(response.body.replies).toEqual([]); - }); - - it('should handle missing optional fields in message creation', async () => { - const mockReceiver = { id: 2, firstName: 'Jane', lastName: 'Smith' }; - const mockCreatedMessage = { - id: 6, - senderId: 1, - receiverId: 2, - subject: undefined, - content: 'Just content', - parentMessageId: undefined - }; - const mockMessageWithSender = { - ...mockCreatedMessage, - sender: { id: 1, firstName: 'John', lastName: 'Doe' } - }; - - mockUserFindByPk.mockResolvedValue(mockReceiver); - mockMessageCreate.mockResolvedValue(mockCreatedMessage); - mockMessageFindByPk.mockResolvedValue(mockMessageWithSender); - - const response = await request(app) - .post('/messages') - .send({ - receiverId: 2, - content: 'Just content' - // subject and parentMessageId omitted - }); - - expect(response.status).toBe(201); - expect(mockMessageCreate).toHaveBeenCalledWith({ - senderId: 1, - receiverId: 2, - subject: undefined, - content: 'Just content', - parentMessageId: undefined - }); - }); }); }); \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 96c3660..8eaf995 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -21,7 +21,6 @@ import Owning from './pages/Owning'; import Profile from './pages/Profile'; import PublicProfile from './pages/PublicProfile'; import Messages from './pages/Messages'; -import MessageDetail from './pages/MessageDetail'; import ForumPosts from './pages/ForumPosts'; import ForumPostDetail from './pages/ForumPostDetail'; import CreateForumPost from './pages/CreateForumPost'; @@ -150,14 +149,6 @@ const AppContent: React.FC = () => { } /> -