-
Notifications
You must be signed in to change notification settings - Fork 56
PR: Implement Request Page Components (Card, Requests, Reject Modal) #232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
codewkaushik404
wants to merge
53
commits into
OpenLake:main
Choose a base branch
from
codewkaushik404:feature/certificate
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 26 commits
Commits
Show all changes
53 commits
Select commit
Hold shift + click to select a range
cbefec9
chore(config): move db connection and passport strategy to config folder
codewkaushik404 78dbc24
fix(schema): update user schema to support correct login and registra…
codewkaushik404 f2e5883
refactor(auth): replace passport-local-mongoose with manual auth impl…
codewkaushik404 ae0295f
feat(validation): add zod validation for auth routes with IIT Bhilai …
codewkaushik404 57c4189
feat(auth): add manual JWT authentication middleware
codewkaushik404 61dfd89
refactor(schema): update certificate schema
codewkaushik404 29bc583
feat(certificates): implement controller logic to create certificate …
codewkaushik404 a8b4d8e
feat(certificates): implement controller logic to create certificate …
codewkaushik404 82d3b70
feat(validation): add Zod schema to validate certificate batch creati…
codewkaushik404 bde7d5e
Fix crashes and ensure intended behavior
codewkaushik404 ecc1ebd
refactor(auth): split schemas into separate files and fix local auth …
codewkaushik404 8126097
refactor(auth, models, middleware): refactor code to ensure robust l…
codewkaushik404 4e96a8e
Refactored authentication logic and fixed related bugs.
codewkaushik404 d3c0261
Refactored authentication logic and fixed related bugs. Switched to s…
codewkaushik404 2a31781
refactor few segments
codewkaushik404 53d7216
fix: api responses to handle frontend requirements
codewkaushik404 cdf07e2
refactor
codewkaushik404 c342d2b
fix: imports for models in controllers according to the updated struc…
codewkaushik404 649fb09
fix: imports for models in controllers according to the updated struc…
codewkaushik404 3fe6ed8
refactor
codewkaushik404 f521062
refactor: streamline authentication and registration processes, enhan…
codewkaushik404 2ef2e05
fix: incorrect imports for models in routes.
codewkaushik404 0f47b6a
refactor: improve auth flow
codewkaushik404 0bd1220
feat: add certificate page and update navbar config for role-based ac…
codewkaushik404 ec6b13b
feat: add certificate page and update navbar config for role-based ac…
codewkaushik404 b726fc8
feat: implement request page components including Card, Requests, and…
codewkaushik404 56e25bf
feat: enhance authentication flow by handling fetch errors and updati…
codewkaushik404 7263443
chore(ui): revamp sidebar layout and component structure
codewkaushik404 7045720
fix: resolve CodeRabbit review comments and improve requests section UI
codewkaushik404 a41729a
feat: implement certificate batch creation and retrieval functionality
codewkaushik404 9eae2d6
feat: add template schema
codewkaushik404 a573f2c
feat: add full batch management UI with cards, modals, grids, and lists
codewkaushik404 6827589
feat: add batch and events service functions for API interactions
codewkaushik404 e142387
feat: remove certificates from navbar configuration
codewkaushik404 7691452
feat: implement templates management UI with filtering, creation, and…
codewkaushik404 0fd33ec
feat: implement certificate retrieval and add event and organization …
codewkaushik404 e774f14
chore: update backend seeding, validation, and error handling
codewkaushik404 47887db
feat: add BatchesPage, templatesPage and CertificatePage
codewkaushik404 e6cbe84
feat: add full batch management endpoints
codewkaushik404 e220ef9
feat: refactored batch-related UI components and create student panel
codewkaushik404 4283ada
feat(frontend): implement API service methods for create, edit, fetch…
codewkaushik404 1d9c799
feat: add approve,reject and approver edit batch APIs with frontend i…
codewkaushik404 f1a5147
refactor: update routes, APIs, hooks, and UI improvements
codewkaushik404 ba5cd5e
feat: add approve/reject and approver edit batch API services with up…
codewkaushik404 a9d0206
refactored codebase to resolve code rabbit raised issues
codewkaushik404 136facf
Merge remote-tracking branch 'upstream/main' into feature/certificate
codewkaushik404 1ccfc77
feat: implement email service with nodemailer for notifications
codewkaushik404 32bf8ac
feat: implement email notifications for batch approve/reject actions …
codewkaushik404 7da1465
fix: list-view of batches in users dashboard
codewkaushik404 b906669
fix: sync API response with UI to fix visibility issues
codewkaushik404 6e44c7f
fix: correct batch page field mapping and improve email service error…
codewkaushik404 e7569f6
feat: generate PDF certificates, upload to Cloudinary, and save to DB
codewkaushik404 11b82ab
resolve merge conflicts with main branch
codewkaushik404 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| #!/usr/bin/env sh | ||
| . "$(dirname -- "$0")/_/husky.sh" | ||
|
|
||
| cd frontend && npx lint-staged && cd ../backend && npx lint-staged | ||
| npx lint-staged |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| const passport = require("passport"); | ||
| const GoogleStrategy = require("passport-google-oauth20").Strategy; | ||
| const LocalStrategy = require("passport-local").Strategy; | ||
| const isIITBhilaiEmail = require("../utils/isIITBhilaiEmail"); | ||
| const User = require("../models/userSchema"); | ||
| const { loginValidate } = require("../utils/authValidate"); | ||
| const bcrypt = require("bcrypt"); | ||
| // Google OAuth Strategy | ||
| passport.use( | ||
| new GoogleStrategy( | ||
| { | ||
| clientID: process.env.GOOGLE_CLIENT_ID, | ||
| clientSecret: process.env.GOOGLE_CLIENT_SECRET, | ||
| callbackURL: `${process.env.BACKEND_URL}/auth/google/verify`, // Update with your callback URL | ||
| }, | ||
| async (accessToken, refreshToken, profile, done) => { | ||
| // Check if the user already exists in your database | ||
| const email = profile.emails?.[0]?.value; | ||
| if (!email) { | ||
| //console.log("No email found in Google profile"); | ||
| return done(null, false, { message: "Email not available from Google." }); | ||
| } | ||
|
|
||
| if (!isIITBhilaiEmail(profile.emails[0].value)) { | ||
| console.log("Google OAuth blocked for: ", profile.emails[0].value); | ||
| return done(null, false, { | ||
| message: "Only @iitbhilai.ac.in emails are allowed.", | ||
| }); | ||
| } | ||
| try { | ||
| const user = await User.findOne({ username: email }); | ||
| //console.log("Looking for existing user with email:", email, "Found:", !!user); | ||
|
|
||
| if (user) { | ||
| // If user exists, return the user | ||
| //console.log("Returning existing user:", user.username); | ||
| return done(null, user); | ||
| } | ||
| // If user doesn't exist, create a new user in your database | ||
| const newUser = await User.create({ | ||
| username: email, | ||
| role: "STUDENT", | ||
| strategy: "google", | ||
| personal_info: { | ||
| name: profile.displayName || "No Name", | ||
| email: email, | ||
| profilePic: | ||
| profile.photos && profile.photos.length > 0 | ||
| ? profile.photos[0].value | ||
| : "https://www.gravatar.com/avatar/?d=mp", | ||
| }, | ||
| onboardingComplete: false, | ||
| }); | ||
| //console.log("User is",newUser); | ||
| return done(null, newUser); | ||
| } catch (error) { | ||
| console.error("Error in Google strategy:", error); | ||
| return done(error); | ||
| } | ||
| }, | ||
| ), | ||
| ); | ||
|
|
||
| //Local Strategy | ||
| passport.use(new LocalStrategy(async (username, password, done) => { | ||
|
|
||
| const result = loginValidate.safeParse({ username, password }); | ||
|
|
||
| if (!result.success) { | ||
| let errors = result.error.issues.map((issue) => issue.message); | ||
| return done(null, false, {message: errors}); | ||
| } | ||
|
|
||
| try{ | ||
|
|
||
| const user = await User.findOne({ username }); | ||
| if (!user) { | ||
| return done(null, false, {message: "Invalid user credentials"}); | ||
| } | ||
|
|
||
|
|
||
| if (user.strategy !== "local" || !user.password) { | ||
| return done(null, false, { message: "Invalid login method" }); | ||
| } | ||
|
|
||
| const isValid = await bcrypt.compare(password, user.password); | ||
| if (!isValid) { | ||
| return done(null, false, { message: "Invalid user credentials" }); | ||
| } | ||
| return done(null, user); | ||
| }catch(err){ | ||
| return done(err); | ||
| } | ||
|
|
||
| })); | ||
|
|
||
|
|
||
| //When login succeeds this will run | ||
| // serialize basically converts user obj into a format that can be transmitted(like a string, etc...) | ||
| // here take user obj and done callback and store only userId in session | ||
| passport.serializeUser((user, done) => { | ||
| done(null, user._id.toString()); | ||
| }); | ||
|
|
||
| //When a request comes in, take the stored id, fetch full user from DB, and attach it to req.user. | ||
| passport.deserializeUser(async (id, done) => { | ||
| try { | ||
| let user = await User.findById(id); | ||
| if(!user) return done(null, false); | ||
| done(null, user); | ||
| } catch (err) { | ||
| done(err, null); | ||
| } | ||
| }); | ||
|
|
||
| module.exports = passport; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
|
|
||
| const User = require("../models/userSchema"); | ||
| const Position = require("../models/positionSchema"); | ||
| const PositionHolder = require("../models/positionHolderSchema"); | ||
| const OrganizationalUnit = require("../models/organizationSchema"); | ||
| const { CertificateBatch } = require("../models/certificateSchema"); | ||
| const { validateBatchSchema, zodObjectId } = require("../utils/batchValidate"); | ||
|
|
||
| async function createBatch(req, res) { | ||
| //console.log(req.user); | ||
| try{ | ||
| const id = req.user.id; | ||
| const user = await User.findById(id); | ||
| if (!user) { | ||
| return res.status(404).json({ messge: "Invalid data (User not found)" }); | ||
| } | ||
|
|
||
| if (user.role !== "CLUB_COORDINATOR") { | ||
| return res.status(403).json({ message: "Not authorized to perform the task" }); | ||
| } | ||
|
|
||
| //to get user club | ||
| // positionHolders({user_id: id}) -> positions({_id: position_id}) -> organizationalUnit({_id: unit_id}) -> unit_id = "Club name" | ||
| const { title, unit_id, commonData, template_id, users } = req.body; | ||
| const validation = validateBatchSchema.safeParse({ | ||
| title, | ||
| unit_id, | ||
| commonData, | ||
| template_id, | ||
| users, | ||
| }); | ||
|
|
||
| if (!validation.success) { | ||
| let errors = validation.error.issues.map(issue => issue.message); | ||
| return res.status(400).json({ message: errors }); | ||
| } | ||
|
|
||
| // Get coordinator's position and unit | ||
| const positionHolder = await PositionHolder.findOne({ user_id: id }); | ||
| if (!positionHolder) { | ||
| return res.status(403).json({ message: "You are not part of any position in a unit" }); | ||
| } | ||
|
|
||
| const position = await Position.findById(positionHolder.position_id); | ||
| console.log(position._id); | ||
| if (!position) { | ||
| return res.status(403).json({ message: "Your position is invalid" }); | ||
| } | ||
|
|
||
| const userUnitId = position.unit_id.toString(); | ||
| if (userUnitId !== unit_id) { | ||
| return res | ||
| .status(403) | ||
| .json({ | ||
| message: | ||
| "You are not authorized to initiate batches outside of your club", | ||
| }); | ||
| } | ||
|
|
||
| //const clubId = unit_id; | ||
| // Ensure unit_id is a Club | ||
| const unitObj = await OrganizationalUnit.findById(unit_id); | ||
| if (!unitObj || unitObj.type !== "Club") { | ||
| return res | ||
| .status(403) | ||
| .json({ message: "Invalid Data: unit is not a Club" }); | ||
| } | ||
| //console.log(unitObj._id); | ||
|
|
||
| // Get council (parent unit) and ensure it's a Council | ||
| if (!unitObj.parent_unit_id) { | ||
| return res | ||
| .status(403) | ||
| .json({ message: "Invalid Data: club does not belong to a council" }); | ||
| } | ||
| //console.log(unitObj.parent_unit_id); | ||
|
|
||
| const councilObj = await OrganizationalUnit.findById(unitObj.parent_unit_id); | ||
| if (!councilObj || councilObj.type !== "Council") { | ||
| return res.status(403).json({ message: "Invalid Data: council not found" }); | ||
| } | ||
|
|
||
| //const councilId = councilObj._id.toString(); | ||
| const presidentOrgUnitId = councilObj.parent_unit_id; | ||
| const category = councilObj.category.toUpperCase(); | ||
|
|
||
| // Resolve General Secretary and President for the council (server-side, tamper-proof) | ||
| const gensecObj = await User.findOne({ role: `GENSEC_${category}` }); | ||
| if(!gensecObj){ | ||
| return res.status(500).json({ message: "General Secretary not found" }); | ||
| } | ||
| //console.log(gensecObj._id); | ||
|
|
||
| const presidentPosition = await Position.findOne({ | ||
| unit_id: presidentOrgUnitId, | ||
| title: /president/i, | ||
| }); | ||
| if (!presidentPosition) { | ||
| return res | ||
| .status(500) | ||
| .json({ message: "President position not found for council" }); | ||
| } | ||
| //console.log(presidentPosition._id); | ||
|
|
||
| const presidentHolder = await PositionHolder.findOne({ | ||
| position_id: presidentPosition._id, | ||
| }); | ||
| const presidentId = presidentHolder.user_id.toString(); | ||
| //console.log(presidentId); | ||
| const presidentObj = await User.findById(presidentId); | ||
|
|
||
| console.log(presidentObj._id); | ||
| if (!presidentObj) { | ||
| return res.status(500).json({ message: "President not found" }); | ||
|
codewkaushik404 marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| const approverIds = [gensecObj._id.toString(), presidentId]; | ||
|
|
||
| const userChecks = await Promise.all( | ||
| users.map(async (uid) => { | ||
| const validation = zodObjectId.safeParse(uid); | ||
| if (!validation) { | ||
| return { uid, ok: false, reason: "Invalid ID" }; | ||
| } | ||
|
|
||
| const userObj = await User.findById(uid); | ||
| if (!userObj) return { uid, ok: false, reason: "User not found" }; | ||
|
|
||
| return { uid, ok: true }; | ||
| }), | ||
| ); | ||
|
codewkaushik404 marked this conversation as resolved.
Outdated
|
||
|
|
||
| const invalidData = userChecks.filter((c) => !c.ok); | ||
| if (invalidData.length > 0) { | ||
| return res | ||
| .status(400) | ||
| .json({ message: "Invalid user data sent", details: invalidData }); | ||
| } | ||
|
|
||
| const newBatch = await CertificateBatch.create({ | ||
| title, | ||
| unit_id, | ||
| commonData, | ||
| templateId: template_id, | ||
| initiatedBy: id, | ||
| approverIds, | ||
| users, | ||
| }); | ||
|
|
||
| res.json({ message: "New Batch created successfully", details: newBatch }); | ||
| }catch(err){ | ||
| res.status(500).json({message: err.message || "Internal server error"}); | ||
| } | ||
| } | ||
|
|
||
| module.exports = { | ||
| createBatch, | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.