From 493e79273f535aacf7631a70ce835d319d3fa5bb Mon Sep 17 00:00:00 2001 From: Deep Koluguri Date: Thu, 6 Nov 2025 13:43:51 -0500 Subject: [PATCH] added admin features --- backend/src/controllers/authController.js | 102 ++++++++++- .../src/controllers/monthlyDrawController.js | 159 +++++++++++++++++- backend/src/routes/auth.js | 1 + backend/src/routes/monthlyDraws.js | 10 +- 4 files changed, 269 insertions(+), 3 deletions(-) diff --git a/backend/src/controllers/authController.js b/backend/src/controllers/authController.js index ce90dab..a4cdec9 100644 --- a/backend/src/controllers/authController.js +++ b/backend/src/controllers/authController.js @@ -393,11 +393,111 @@ const updateProfile = async (req, res) => { } }; +// Update member details (Manager only) +const updateMemberDetails = async (req, res) => { + try { + const { memberId } = req.params; + const { full_name, mobile_number, email, address, emergency_contact } = req.body; + const managerId = req.user.id; + + // Find the member + const member = await User.findByPk(memberId); + if (!member) { + return res.status(404).json({ + success: false, + message: 'Member not found' + }); + } + + // Check if the logged-in user is a manager + const manager = await User.findByPk(managerId); + if (manager.role !== 'manager') { + return res.status(403).json({ + success: false, + message: 'Only managers can update member details' + }); + } + + // Prepare updates + const updates = {}; + + if (full_name) { + updates.full_name = full_name; + } + + if (mobile_number) { + // Validate mobile number format + if (!/^[0-9]{10}$/.test(mobile_number)) { + return res.status(400).json({ + success: false, + message: 'Mobile number must be exactly 10 digits' + }); + } + + // Check if mobile number is already taken by another user + const existingUser = await User.findOne({ + where: { mobile_number } + }); + + if (existingUser && existingUser.id !== memberId) { + return res.status(400).json({ + success: false, + message: 'Mobile number is already in use' + }); + } + + updates.mobile_number = mobile_number; + } + + if (email !== undefined) { + if (email && !/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/.test(email)) { + return res.status(400).json({ + success: false, + message: 'Please provide a valid email address' + }); + } + updates.email = email; + } + + if (address !== undefined) { + updates.address = address; + } + + if (emergency_contact !== undefined) { + if (emergency_contact && !/^[0-9]{10}$/.test(emergency_contact)) { + return res.status(400).json({ + success: false, + message: 'Emergency contact must be exactly 10 digits' + }); + } + updates.emergency_contact = emergency_contact; + } + + // Update member + await member.update(updates); + + res.json({ + success: true, + message: 'Member details updated successfully', + data: { + user: member.toJSON() + } + }); + } catch (error) { + console.error('Update member details error:', error); + res.status(500).json({ + success: false, + message: 'Internal server error' + }); + } +}; + module.exports = { signup, login, createMember, changePassword, getProfile, - updateProfile + updateProfile, + updateMemberDetails }; diff --git a/backend/src/controllers/monthlyDrawController.js b/backend/src/controllers/monthlyDrawController.js index 598ee14..40c4ffb 100644 --- a/backend/src/controllers/monthlyDrawController.js +++ b/backend/src/controllers/monthlyDrawController.js @@ -510,10 +510,167 @@ const getDrawStatistics = async (req, res) => { } }; +// Delete monthly draw (manager only, for mistakes) +const deleteMonthlyDraw = async (req, res) => { + try { + const { draw_id } = req.params; + const managerId = req.user.id; + + // Find the draw + const monthlyDraw = await MonthlyDraw.findOne({ + where: { id: draw_id }, + include: [ + { + model: ChitGroup, + attributes: ['id', 'manager_id', 'name'] + } + ] + }); + + if (!monthlyDraw) { + return res.status(404).json({ + success: false, + message: 'Monthly draw not found' + }); + } + + // Check if user is the manager + if (monthlyDraw.ChitGroup.manager_id !== managerId) { + return res.status(403).json({ + success: false, + message: 'Only the group manager can delete draws' + }); + } + + // Store draw info for response + const drawInfo = { + month: monthlyDraw.month, + year: monthlyDraw.year, + groupName: monthlyDraw.ChitGroup.name + }; + + // Delete the draw + await monthlyDraw.destroy(); + + res.json({ + success: true, + message: `Draw for ${drawInfo.month}/${drawInfo.year} deleted successfully`, + data: drawInfo + }); + } catch (error) { + console.error('Delete monthly draw error:', error); + res.status(500).json({ + success: false, + message: 'Internal server error' + }); + } +}; + +// Update monthly draw (manager only, for corrections) +const updateMonthlyDraw = async (req, res) => { + try { + const { draw_id } = req.params; + const { winner_id, prize_amount, notes } = req.body; + const managerId = req.user.id; + + // Find the draw + const monthlyDraw = await MonthlyDraw.findOne({ + where: { id: draw_id }, + include: [ + { + model: ChitGroup, + attributes: ['id', 'manager_id', 'name'] + } + ] + }); + + if (!monthlyDraw) { + return res.status(404).json({ + success: false, + message: 'Monthly draw not found' + }); + } + + // Check if user is the manager + if (monthlyDraw.ChitGroup.manager_id !== managerId) { + return res.status(403).json({ + success: false, + message: 'Only the group manager can update draws' + }); + } + + const updates = {}; + + // Update winner if provided + if (winner_id) { + // Verify the winner is a member of the group + const member = await GroupMember.findOne({ + where: { + group_id: monthlyDraw.ChitGroup.id, + user_id: winner_id, + status: 'active' + }, + include: [{ model: User, attributes: ['id', 'full_name'] }] + }); + + if (!member) { + return res.status(400).json({ + success: false, + message: 'Winner must be an active member of this group' + }); + } + + updates.winner_id = winner_id; + if (!notes) { + updates.notes = `Winner updated to: ${member.User.full_name}`; + } + } + + // Update prize amount if provided + if (prize_amount) { + updates.prize_amount = prize_amount; + } + + // Update notes if provided + if (notes) { + updates.notes = notes; + } + + // Perform update + await monthlyDraw.update(updates); + + // Fetch updated draw with winner details + const updatedDraw = await MonthlyDraw.findOne({ + where: { id: draw_id }, + include: [ + { + model: User, + as: 'winner', + attributes: ['id', 'full_name', 'mobile_number'] + } + ] + }); + + res.json({ + success: true, + message: 'Monthly draw updated successfully', + data: updatedDraw + }); + } catch (error) { + console.error('Update monthly draw error:', error); + res.status(500).json({ + success: false, + message: 'Internal server error' + }); + } +}; + module.exports = { createMonthlyDraw, getGroupMonthlyDraws, getMonthlyDrawDetails, verifyDrawResult, - getDrawStatistics + getDrawStatistics, + deleteMonthlyDraw, + updateMonthlyDraw }; diff --git a/backend/src/routes/auth.js b/backend/src/routes/auth.js index e6a7f38..3a9bee4 100644 --- a/backend/src/routes/auth.js +++ b/backend/src/routes/auth.js @@ -17,5 +17,6 @@ router.put('/change-password', authController.changePassword); // Manager only routes router.post('/create-member', requireManager, authController.createMember); +router.put('/member/:memberId', requireManager, authController.updateMemberDetails); module.exports = router; diff --git a/backend/src/routes/monthlyDraws.js b/backend/src/routes/monthlyDraws.js index 9cc8790..aa3fbfe 100644 --- a/backend/src/routes/monthlyDraws.js +++ b/backend/src/routes/monthlyDraws.js @@ -6,7 +6,9 @@ const { getGroupMonthlyDraws, getMonthlyDrawDetails, verifyDrawResult, - getDrawStatistics + getDrawStatistics, + deleteMonthlyDraw, + updateMonthlyDraw } = require('../controllers/monthlyDrawController'); // All routes require authentication @@ -21,6 +23,12 @@ router.get('/group/:group_id', getGroupMonthlyDraws); // Get monthly draw details router.get('/:draw_id', getMonthlyDrawDetails); +// Update monthly draw (manager only) +router.put('/:draw_id', updateMonthlyDraw); + +// Delete monthly draw (manager only) +router.delete('/:draw_id', deleteMonthlyDraw); + // Verify provably fair result router.post('/:draw_id/verify', verifyDrawResult);