From 785fe05902f458938e8a006b2d3e13f7e95318e5 Mon Sep 17 00:00:00 2001 From: Abiram070207 Date: Fri, 19 Jun 2026 09:37:29 +0530 Subject: [PATCH] feat: implement cover image upload with canvas compression and db storage in RIT-EMS-main --- .../backend/controller/AdminController.java | 21 +- .../backend/controller/EventController.java | 9 + .../java/com/ems/backend/model/Event.java | 12 + RIT-EMS-main/frontend/src/App.tsx | 3 +- .../components/dashboard/ApprovalsView.tsx | 3 +- .../dashboard/EventProposalForm.tsx | 87 +- .../dashboard/InstitutionalChecklist.tsx | 5 +- .../src/components/dashboard/Overview.tsx | 6 +- .../dashboard/StudentRegistrationsView.tsx | 3 +- .../components/dashboard/UserManagement.tsx | 94 +- .../frontend/src/docs/GOOGLE_AUTH_SETUP.md | 169 ++++ .../frontend/src/lib/firebaseBackend.ts | 182 +++- RIT-EMS-main/frontend/src/pages/LoginPage.tsx | 859 ++++++++++-------- RIT-EVENT-MANAGEMENT--main/App.tsx | 99 +- .../components/CreateEventForm.tsx | 33 +- .../components/LoginForm.tsx | 60 +- RIT-EVENT-MANAGEMENT--main/supabase.ts | 25 +- 17 files changed, 1217 insertions(+), 453 deletions(-) create mode 100644 RIT-EMS-main/frontend/src/docs/GOOGLE_AUTH_SETUP.md diff --git a/RIT-EMS-main/backend/src/main/java/com/ems/backend/controller/AdminController.java b/RIT-EMS-main/backend/src/main/java/com/ems/backend/controller/AdminController.java index f6dc757..fa8d5bd 100644 --- a/RIT-EMS-main/backend/src/main/java/com/ems/backend/controller/AdminController.java +++ b/RIT-EMS-main/backend/src/main/java/com/ems/backend/controller/AdminController.java @@ -43,8 +43,16 @@ public class AdminController { if (user.getRole() == null || user.getRole().trim().isEmpty()) { user.setRole("FACULTY"); } - if (user.getDepartment() == null || user.getDepartment().trim().isEmpty()) { - user.setDepartment("H&S Dept"); + + String role = user.getRole(); + if ("HOD".equals(role) || "FACULTY".equals(role)) { + if (user.getDepartment() == null || user.getDepartment().trim().isEmpty()) { + user.setDepartment("H&S Dept"); + } else { + user.setDepartment(user.getDepartment().trim()); + } + } else { + user.setDepartment(""); } if (userRepository.findByEmail(user.getEmail()).isPresent()) { @@ -61,7 +69,14 @@ public class AdminController { user.setFullName(userDetails.getFullName()); user.setEmail(userDetails.getEmail()); user.setRole(userDetails.getRole()); - user.setDepartment(userDetails.getDepartment()); + + String role = user.getRole(); + if ("HOD".equals(role) || "FACULTY".equals(role)) { + user.setDepartment(userDetails.getDepartment() != null ? userDetails.getDepartment().trim() : ""); + } else { + user.setDepartment(""); + } + user.setClubCoordinator(userDetails.isClubCoordinator()); user.setPlacementStaff(userDetails.isPlacementStaff()); user.setClassIncharge(userDetails.isClassIncharge()); diff --git a/RIT-EMS-main/backend/src/main/java/com/ems/backend/controller/EventController.java b/RIT-EMS-main/backend/src/main/java/com/ems/backend/controller/EventController.java index b25898b..f677772 100644 --- a/RIT-EMS-main/backend/src/main/java/com/ems/backend/controller/EventController.java +++ b/RIT-EMS-main/backend/src/main/java/com/ems/backend/controller/EventController.java @@ -76,6 +76,9 @@ public class EventController { if (payload.containsKey("isPublicEvent")) { event.setPublicEvent((boolean) payload.get("isPublicEvent")); } + if (payload.containsKey("image")) { + event.setImage(payload.get("image") != null ? payload.get("image").toString() : null); + } // Workflow Logic if ("PRINCIPAL".equals(proposer.getRole())) { @@ -202,6 +205,9 @@ public class EventController { if (payload.containsKey("requirements")) { event.setRequirements((List) payload.get("requirements")); } + if (payload.containsKey("image")) { + event.setImage(payload.get("image") != null ? payload.get("image").toString() : null); + } if (payload.get("proposer") != null) { try { @@ -350,6 +356,9 @@ public class EventController { if (payload.containsKey("centreName")) event.setCentreName(payload.get("centreName").toString()); if (payload.containsKey("isPublicEvent")) event.setPublicEvent((boolean) payload.get("isPublicEvent")); + if (payload.containsKey("image")) { + event.setImage(payload.get("image") != null ? payload.get("image").toString() : null); + } if (payload.containsKey("status") && isAdmin) { event.setStatus(payload.get("status").toString()); } diff --git a/RIT-EMS-main/backend/src/main/java/com/ems/backend/model/Event.java b/RIT-EMS-main/backend/src/main/java/com/ems/backend/model/Event.java index 0e40986..c5ef37d 100644 --- a/RIT-EMS-main/backend/src/main/java/com/ems/backend/model/Event.java +++ b/RIT-EMS-main/backend/src/main/java/com/ems/backend/model/Event.java @@ -73,6 +73,9 @@ public class Event extends BaseEntity { @Column(columnDefinition = "boolean default false") private boolean isPublicEvent; + @Column(columnDefinition = "LONGTEXT") + private String image; + @jakarta.persistence.Transient private String conflictMessage; @@ -169,6 +172,14 @@ public class Event extends BaseEntity { isPublicEvent = publicEvent; } + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + // Manual Builder public static EventBuilder builder() { return new EventBuilder(); @@ -198,6 +209,7 @@ public class Event extends BaseEntity { public EventBuilder sponsors(List sponsors) { event.setSponsors(sponsors); return this; } public EventBuilder proposer(User proposer) { event.setProposer(proposer); return this; } public EventBuilder groupRequestId(String groupRequestId) { event.setGroupRequestId(groupRequestId); return this; } + public EventBuilder image(String image) { event.setImage(image); return this; } public Event build() { return event; diff --git a/RIT-EMS-main/frontend/src/App.tsx b/RIT-EMS-main/frontend/src/App.tsx index a7e14a3..8177832 100644 --- a/RIT-EMS-main/frontend/src/App.tsx +++ b/RIT-EMS-main/frontend/src/App.tsx @@ -79,7 +79,8 @@ const AppContent: React.FC = () => { if (Array.isArray(data)) { let filtered = data; if (user?.role === 'HOD') { - filtered = data.filter((e: Event) => e.department === user?.department); + const userDepts = user?.department ? user.department.split(',').map(d => d.trim().toLowerCase()) : []; + filtered = data.filter((e: Event) => userDepts.includes(e.department?.trim().toLowerCase())); } else if (user?.role === 'FACULTY') { filtered = data.filter((e: Event) => e.proposer?.email === user?.email); } diff --git a/RIT-EMS-main/frontend/src/components/dashboard/ApprovalsView.tsx b/RIT-EMS-main/frontend/src/components/dashboard/ApprovalsView.tsx index 20f126b..5d2ecb9 100644 --- a/RIT-EMS-main/frontend/src/components/dashboard/ApprovalsView.tsx +++ b/RIT-EMS-main/frontend/src/components/dashboard/ApprovalsView.tsx @@ -70,7 +70,8 @@ export const ApprovalsView: React.FC = () => { const data = await response.json(); // Filter based on role if (user?.role === 'HOD') { - setEvents(data.filter((e: Event) => e.status === 'REQUESTED' && e.department === user.department)); + const userDepts = user.department ? user.department.split(',').map(d => d.trim().toLowerCase()) : []; + setEvents(data.filter((e: Event) => e.status === 'REQUESTED' && userDepts.includes(e.department?.trim().toLowerCase()))); } else if (user?.role === 'PRINCIPAL') { setEvents(data.filter((e: Event) => e.status === 'PENDING_PR')); } diff --git a/RIT-EMS-main/frontend/src/components/dashboard/EventProposalForm.tsx b/RIT-EMS-main/frontend/src/components/dashboard/EventProposalForm.tsx index 6b8cfa8..fa21eb8 100644 --- a/RIT-EMS-main/frontend/src/components/dashboard/EventProposalForm.tsx +++ b/RIT-EMS-main/frontend/src/components/dashboard/EventProposalForm.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Calendar, @@ -105,6 +105,7 @@ interface EventProposalFormProps { sponsors?: string[]; centreName?: string; isPublicEvent?: boolean; + image?: string; status?: string; } | null; } @@ -122,6 +123,42 @@ export const EventProposalForm: React.FC = ({ initialDat const [existingEvents, setExistingEvents] = useState([]); const [classStrengths, setClassStrengths] = useState([]); + const fileInputRef = useRef(null); + + const handleImageChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onloadend = () => { + const img = new window.Image(); + img.onload = () => { + const canvas = document.createElement('canvas'); + const max_width = 800; + let width = img.width; + let height = img.height; + + if (width > max_width) { + height = Math.round((height * max_width) / width); + width = max_width; + } + + canvas.width = width; + canvas.height = height; + const ctx = canvas.getContext('2d'); + if (ctx) { + ctx.drawImage(img, 0, 0, width, height); + const compressedBase64 = canvas.toDataURL('image/jpeg', 0.7); + setFormData(prev => ({ ...prev, image: compressedBase64 })); + } else { + setFormData(prev => ({ ...prev, image: reader.result as string })); + } + }; + img.src = reader.result as string; + }; + reader.readAsDataURL(file); + } + }; + const [eventScope, setEventScope] = useState<'INSTITUTIONAL' | 'DEPARTMENT' | 'CLUB' | 'PLACEMENT' | 'CENTRE'>( initialData?.isClubEvent ? 'INSTITUTIONAL' : (isPlacementCell ? 'PLACEMENT' : 'DEPARTMENT') ); @@ -165,6 +202,7 @@ export const EventProposalForm: React.FC = ({ initialDat sponsors: initialData?.sponsors || [] as string[], centreName: initialData?.centreName || (eventScope === 'CENTRE' ? ((CENTRE_MAPPING[user?.department || ''] || CENTRE_MAPPING['Placement Department'] || [])[0] || '') : ''), isPublicEvent: initialData?.isPublicEvent || false, + image: initialData?.image || '', status: initialData?.status || 'REQUESTED' }); @@ -422,7 +460,8 @@ export const EventProposalForm: React.FC = ({ initialDat category: eventScope === 'CLUB' ? 'CLUB' : (eventScope === 'INSTITUTIONAL' ? 'INSTITUTIONAL' : (eventScope === 'CENTRE' ? 'CENTRE' : 'ACADEMIC')), eventType: formData.eventType, centreName: eventScope === 'CENTRE' ? formData.centreName : undefined, - isPublicEvent: eventScope === 'CENTRE' ? formData.isPublicEvent : undefined + isPublicEvent: eventScope === 'CENTRE' ? formData.isPublicEvent : undefined, + image: formData.image }; const res = await fetch(API_BASE_URL + '/api/events/propose', { @@ -1191,6 +1230,50 @@ export const EventProposalForm: React.FC = ({ initialDat )} + {/* Cover Image Upload Area */} +
+ +
+ + +
+
+
{error &&
{error}
}
-
-
- - -
-
- - { + const newRole = e.target.value; + setFormData({ + ...formData, + role: newRole, + department: (newRole === 'HOD' || newRole === 'FACULTY') ? formData.department : '' + }); + }} + className="w-full bg-slate-50 border-transparent rounded-xl py-3 px-4 text-sm focus:bg-white focus:border-brand-indigo transition-all appearance-none" + > + + + + + + +
+ + {(formData.role === 'HOD' || formData.role === 'FACULTY') && ( + + +
{[ "AI&DS", "AI&ML", "CSE", "CCE", "CSBS", "ECE", "MECH", "EE(VLSI)", "BIOTECH", "Placement Department", "H&S Dept", "Club", "Centre" - ].map(dept => ( - - ))} - -
-
+ ].map(dept => { + const selectedDepts = formData.department ? formData.department.split(',').map(d => d.trim()).filter(Boolean) : []; + const isSelected = selectedDepts.includes(dept); + return ( + + ); + })} + + + )}