Skip to content

Commit ed07e6f

Browse files
authored
Merge branch 'codeaashu:main' into update-x-icon-and-tooltips
2 parents e4f8a47 + bd58548 commit ed07e6f

File tree

162 files changed

+4620
-1580
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

162 files changed

+4620
-1580
lines changed

backend/app.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import express from 'express';
22
import { devRouter } from './routes/dev.routes.js';
33
import { githubRouter } from './routes/github.routes.js';
44
import { subscribersRouter } from './routes/subscribers.routes.js';
5+
import { ideasRouter } from './routes/ideas.routes.js';
56
import { getSubscribers } from './controllers/subscribers.controllers.js';
67
import Subscribers from './models/subscribers.models.js';
78
const app = express();
@@ -15,6 +16,9 @@ app.use('/devdisplay/v1/trending/dev', devRouter);
1516
// Use the githubRouter for the "/api/v1/trending/github" route
1617
app.use('/devdisplay/v1/trending/github', githubRouter);
1718

19+
// Use the ideasRouter for the "/devdisplay/v1/ideas" route
20+
app.use('/devdisplay/v1/ideas', ideasRouter);
21+
1822
app.use('/devdisplay/v1/subscribers', getSubscribers);
1923

2024
app.get('/', (req, res) => {
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
import Ideas from '../models/ideas.models.js';
2+
import { apiResponse } from '../utils/response.utils.js';
3+
4+
// Submit a new idea
5+
export const submitIdea = async (req, res) => {
6+
try {
7+
const { title, description, submittedBy, submitterEmail, tags, resourcesNeeded, mediaUrls } = req.body;
8+
9+
// Check if submission is open
10+
if (!Ideas.isSubmissionOpen()) {
11+
return res
12+
.status(400)
13+
.json(apiResponse(400, null, 'Idea submission is only allowed during the first week of each month'));
14+
}
15+
16+
// Get current submission period
17+
const { month, year } = Ideas.getCurrentSubmissionPeriod();
18+
19+
// Check if user already submitted an idea this month
20+
const existingIdea = await Ideas.findOne({
21+
submitterEmail,
22+
submissionMonth: month,
23+
submissionYear: year,
24+
});
25+
26+
if (existingIdea) {
27+
return res.status(400).json(apiResponse(400, null, 'You can only submit one idea per month'));
28+
}
29+
30+
// Create new idea
31+
const newIdea = new Ideas({
32+
title,
33+
description,
34+
submittedBy,
35+
submitterEmail,
36+
tags: tags || [],
37+
resourcesNeeded,
38+
mediaUrls: mediaUrls || [],
39+
submissionMonth: month,
40+
submissionYear: year,
41+
});
42+
43+
await newIdea.save();
44+
45+
res.status(201).json(apiResponse(201, newIdea, 'Idea submitted successfully'));
46+
} catch (error) {
47+
console.error('Error submitting idea:', error);
48+
res.status(500).json(apiResponse(500, null, 'Internal server error'));
49+
}
50+
};
51+
52+
// Get all ideas for current month
53+
export const getCurrentMonthIdeas = async (req, res) => {
54+
try {
55+
const { month, year } = Ideas.getCurrentSubmissionPeriod();
56+
const { page = 1, limit = 10, sortBy = 'votes', order = 'desc' } = req.query;
57+
58+
const skip = (page - 1) * limit;
59+
const sortOrder = order === 'desc' ? -1 : 1;
60+
61+
const ideas = await Ideas.find({
62+
submissionMonth: month,
63+
submissionYear: year,
64+
isArchived: false,
65+
})
66+
.sort({ [sortBy]: sortOrder, createdAt: -1 })
67+
.skip(skip)
68+
.limit(parseInt(limit))
69+
.select('-voters'); // Don't expose voter details
70+
71+
const totalIdeas = await Ideas.countDocuments({
72+
submissionMonth: month,
73+
submissionYear: year,
74+
isArchived: false,
75+
});
76+
77+
res.status(200).json(
78+
apiResponse(
79+
200,
80+
{
81+
ideas,
82+
currentPage: parseInt(page),
83+
totalPages: Math.ceil(totalIdeas / limit),
84+
totalIdeas,
85+
submissionPeriod: `${month} ${year}`,
86+
isSubmissionOpen: Ideas.isSubmissionOpen(),
87+
},
88+
'Ideas retrieved successfully',
89+
),
90+
);
91+
} catch (error) {
92+
console.error('Error getting ideas:', error);
93+
res.status(500).json(apiResponse(500, null, 'Internal server error'));
94+
}
95+
};
96+
97+
// Get trending ideas
98+
export const getTrendingIdeas = async (req, res) => {
99+
try {
100+
const { limit = 5 } = req.query;
101+
const trendingIdeas = await Ideas.getTrendingIdeas(parseInt(limit));
102+
103+
res.status(200).json(apiResponse(200, trendingIdeas, 'Trending ideas retrieved successfully'));
104+
} catch (error) {
105+
console.error('Error getting trending ideas:', error);
106+
res.status(500).json(apiResponse(500, null, 'Internal server error'));
107+
}
108+
};
109+
110+
// Vote for an idea
111+
export const voteForIdea = async (req, res) => {
112+
try {
113+
const { ideaId } = req.params;
114+
const { userEmail } = req.body;
115+
116+
if (!userEmail) {
117+
return res.status(400).json(apiResponse(400, null, 'User email is required'));
118+
}
119+
120+
const idea = await Ideas.findById(ideaId);
121+
if (!idea) {
122+
return res.status(404).json(apiResponse(404, null, 'Idea not found'));
123+
}
124+
125+
// Check if user can vote (hasn't voted already)
126+
if (!idea.canUserVote(userEmail)) {
127+
return res.status(400).json(apiResponse(400, null, 'You have already voted for this idea'));
128+
}
129+
130+
// Add vote
131+
idea.addVote(userEmail);
132+
await idea.save();
133+
134+
res.status(200).json(apiResponse(200, { votes: idea.votes }, 'Vote added successfully'));
135+
} catch (error) {
136+
console.error('Error voting for idea:', error);
137+
res.status(500).json(apiResponse(500, null, 'Internal server error'));
138+
}
139+
};
140+
141+
// Remove vote from an idea
142+
export const removeVoteFromIdea = async (req, res) => {
143+
try {
144+
const { ideaId } = req.params;
145+
const { userEmail } = req.body;
146+
147+
if (!userEmail) {
148+
return res.status(400).json(apiResponse(400, null, 'User email is required'));
149+
}
150+
151+
const idea = await Ideas.findById(ideaId);
152+
if (!idea) {
153+
return res.status(404).json(apiResponse(404, null, 'Idea not found'));
154+
}
155+
156+
// Remove vote
157+
const voteRemoved = idea.removeVote(userEmail);
158+
if (!voteRemoved) {
159+
return res.status(400).json(apiResponse(400, null, 'You have not voted for this idea'));
160+
}
161+
162+
await idea.save();
163+
164+
res.status(200).json(apiResponse(200, { votes: idea.votes }, 'Vote removed successfully'));
165+
} catch (error) {
166+
console.error('Error removing vote:', error);
167+
res.status(500).json(apiResponse(500, null, 'Internal server error'));
168+
}
169+
};
170+
171+
// Get idea by ID
172+
export const getIdeaById = async (req, res) => {
173+
try {
174+
const { ideaId } = req.params;
175+
176+
const idea = await Ideas.findById(ideaId).populate('collaborators', 'name email role').select('-voters'); // Don't expose voter details
177+
178+
if (!idea) {
179+
return res.status(404).json(apiResponse(404, null, 'Idea not found'));
180+
}
181+
182+
res.status(200).json(apiResponse(200, idea, 'Idea retrieved successfully'));
183+
} catch (error) {
184+
console.error('Error getting idea:', error);
185+
res.status(500).json(apiResponse(500, null, 'Internal server error'));
186+
}
187+
};
188+
189+
// Select idea for development (admin function)
190+
export const selectIdeaForDevelopment = async (req, res) => {
191+
try {
192+
const { ideaId } = req.params;
193+
194+
const idea = await Ideas.findById(ideaId);
195+
if (!idea) {
196+
return res.status(404).json(apiResponse(404, null, 'Idea not found'));
197+
}
198+
199+
// Mark as selected for development
200+
idea.status = 'selected';
201+
idea.selectedForDevelopment = true;
202+
idea.developmentStartDate = new Date();
203+
204+
await idea.save();
205+
206+
res.status(200).json(apiResponse(200, idea, 'Idea selected for development'));
207+
} catch (error) {
208+
console.error('Error selecting idea:', error);
209+
res.status(500).json(apiResponse(500, null, 'Internal server error'));
210+
}
211+
};
212+
213+
// Join idea collaboration
214+
export const joinIdeaCollaboration = async (req, res) => {
215+
try {
216+
const { ideaId } = req.params;
217+
const { name, email, role } = req.body;
218+
219+
const idea = await Ideas.findById(ideaId);
220+
if (!idea) {
221+
return res.status(404).json(apiResponse(404, null, 'Idea not found'));
222+
}
223+
224+
if (idea.status !== 'selected' && idea.status !== 'in_development') {
225+
return res.status(400).json(apiResponse(400, null, 'This idea is not open for collaboration yet'));
226+
}
227+
228+
// Check if user already joined
229+
const existingCollaborator = idea.collaborators.find((collaborator) => collaborator.email === email);
230+
231+
if (existingCollaborator) {
232+
return res.status(400).json(apiResponse(400, null, 'You have already joined this project'));
233+
}
234+
235+
// Add collaborator
236+
idea.collaborators.push({ name, email, role });
237+
if (idea.status === 'selected') {
238+
idea.status = 'in_development';
239+
}
240+
241+
await idea.save();
242+
243+
res.status(200).json(apiResponse(200, idea.collaborators, 'Successfully joined the collaboration'));
244+
} catch (error) {
245+
console.error('Error joining collaboration:', error);
246+
res.status(500).json(apiResponse(500, null, 'Internal server error'));
247+
}
248+
};
249+
250+
// Get submission status
251+
export const getSubmissionStatus = async (req, res) => {
252+
try {
253+
const { userEmail } = req.query;
254+
const { month, year } = Ideas.getCurrentSubmissionPeriod();
255+
const isSubmissionOpen = Ideas.isSubmissionOpen();
256+
257+
let hasSubmittedThisMonth = false;
258+
if (userEmail) {
259+
const existingIdea = await Ideas.findOne({
260+
submitterEmail: userEmail,
261+
submissionMonth: month,
262+
submissionYear: year,
263+
});
264+
hasSubmittedThisMonth = !!existingIdea;
265+
}
266+
267+
// Calculate days left for submission
268+
const now = new Date();
269+
const currentDay = now.getDate();
270+
const daysLeftForSubmission = isSubmissionOpen ? 7 - currentDay + 1 : 0;
271+
272+
res.status(200).json(
273+
apiResponse(
274+
200,
275+
{
276+
isSubmissionOpen,
277+
submissionPeriod: `${month} ${year}`,
278+
hasSubmittedThisMonth,
279+
daysLeftForSubmission,
280+
submissionEndsAt: isSubmissionOpen ? new Date(now.getFullYear(), now.getMonth(), 7, 23, 59, 59) : null,
281+
},
282+
'Submission status retrieved successfully',
283+
),
284+
);
285+
} catch (error) {
286+
console.error('Error getting submission status:', error);
287+
res.status(500).json(apiResponse(500, null, 'Internal server error'));
288+
}
289+
};

0 commit comments

Comments
 (0)