Compare commits

...

11 Commits

55 changed files with 3536 additions and 176 deletions

5
.firebaserc Normal file
View File

@@ -0,0 +1,5 @@
{
"projects": {
"default": "riteventhub"
}
}

3
.gitignore vendored
View File

@@ -25,3 +25,6 @@ Thumbs.db
**/*.njsproj
**/*.sln
**/*.sw?
# Firebase cache
.firebase/

61
backend/pom.xml Normal file
View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.rit</groupId>
<artifactId>ems</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ems</name>
<description>Event Management System Backend</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,11 @@
package com.rit.ems;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class EmsApplication {
public static void main(String[] args) {
SpringApplication.run(EmsApplication.class, args);
}
}

View File

@@ -0,0 +1,23 @@
package com.rit.ems.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")
.allowedHeaders("*")
.allowCredentials(true);
}
};
}
}

View File

@@ -0,0 +1,110 @@
package com.rit.ems.config;
import com.rit.ems.model.*;
import com.rit.ems.repository.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@Component
public class DataSeeder implements CommandLineRunner {
@Autowired
private UserRepository userRepository;
@Autowired
private ClassMappingRepository classMappingRepository;
@Autowired
private VenueRepository venueRepository;
@Autowired
private InstitutionalEventRepository institutionalEventRepository;
@Override
public void run(String... args) throws Exception {
seedUsers();
seedClasses();
seedVenues();
seedInstitutionalEvents();
}
private void seedUsers() {
if (userRepository.count() == 0) {
System.out.println("[DataSeeder] Seeding default users...");
List<User> defaultUsers = Arrays.asList(
new User(1L, "admin@rit.edu", "admin123", "System Administrator", "ADMIN", "ADMIN", false, false, false, 0, null, null, null, null, null, null, "", "Male", "Rajalakshmi Institute of Technology", new ArrayList<>()),
new User(2L, "faculty@rit.edu", "faculty123", "Dr. Faculty Member", "FACULTY", "CSE", false, false, true, 0, "CSE", "3rd Year", "A", "3rd Year", "A", null, "", "Male", "Rajalakshmi Institute of Technology", new ArrayList<>()),
new User(3L, "hod@rit.edu", "hod123", "Prof. Head of Dept", "HOD", "AI&ML", false, false, false, 0, null, null, null, null, null, null, "", "Male", "Rajalakshmi Institute of Technology", new ArrayList<>()),
new User(4L, "principal@rit.edu", "principal123", "Dr. College Principal", "PRINCIPAL", "ADMIN", false, false, false, 0, null, null, null, null, null, null, "", "Male", "Rajalakshmi Institute of Technology", new ArrayList<>()),
new User(5L, "placement@rit.edu", "placement123", "Placement Coordinator", "PLACEMENT", "Placement Department", false, true, false, 0, null, null, null, null, null, null, "", "Male", "Rajalakshmi Institute of Technology", new ArrayList<>()),
new User(6L, "admin2@rit.edu", "admin123", "Secondary Admin", "ADMIN", "ADMIN", false, false, false, 0, null, null, null, null, null, null, "", "Male", "Rajalakshmi Institute of Technology", new ArrayList<>()),
new User(7L, "principal2@rit.edu", "principal123", "Vice Principal", "PRINCIPAL", "ADMIN", false, false, false, 0, null, null, null, null, null, null, "", "Male", "Rajalakshmi Institute of Technology", new ArrayList<>()),
new User(8L, "hod_cse@rit.edu", "hod123", "CSE HOD", "HOD", "CSE", false, false, false, 0, null, null, null, null, null, null, "", "Male", "Rajalakshmi Institute of Technology", new ArrayList<>()),
new User(9L, "faculty2@rit.edu", "faculty123", "Assistant Professor CSE", "FACULTY", "CSE", false, false, false, 0, null, null, null, null, null, null, "", "Male", "Rajalakshmi Institute of Technology", new ArrayList<>())
);
userRepository.saveAll(defaultUsers);
}
}
private void seedClasses() {
if (classMappingRepository.count() == 0) {
System.out.println("[DataSeeder] Seeding default classes...");
List<ClassMapping> defaultClasses = Arrays.asList(
new ClassMapping(1L, "RIT", "CSE", "3rd Year", Arrays.asList("A", "B"), "Ready"),
new ClassMapping(2L, "RIT", "AI&ML", "3rd Year", Collections.singletonList("A"), "Ready"),
new ClassMapping(3L, "RIT", "ECE", "2nd Year", Arrays.asList("A", "B", "C"), "Ready")
);
classMappingRepository.saveAll(defaultClasses);
}
}
private void seedVenues() {
if (venueRepository.count() == 0) {
System.out.println("[DataSeeder] Seeding default venues...");
List<Venue> defaultVenues = Arrays.asList(
new Venue("v-1", "GB 4th floor auditorium", 500),
new Venue("v-2", "Wozniak Auditorium", 180),
new Venue("v-3", "C6-02 Indoor Theatre", 180),
new Venue("v-4", "H Block Guest Lecture Theatre", 60),
new Venue("v-5", "Steve Jobs Computer Centre 1", 120),
new Venue("v-6", "Steve Jobs Computer Centre 2", 120)
);
venueRepository.saveAll(defaultVenues);
}
}
private void seedInstitutionalEvents() {
if (institutionalEventRepository.count() == 0) {
System.out.println("[DataSeeder] Seeding institutional events...");
List<InstitutionalEvent> defaultEvents = Arrays.asList(
new InstitutionalEvent("ie-1", "New Year & Christmas Celebration", "January", "EVEN"),
new InstitutionalEvent("ie-2", "Republic Day Celebration", "January", "EVEN"),
new InstitutionalEvent("ie-3", "Pongal Celebration", "January", "EVEN"),
new InstitutionalEvent("ie-4", "Yatra Banner Release", "January", "EVEN"),
new InstitutionalEvent("ie-5", "Yatra Ethnic Day", "February", "EVEN"),
new InstitutionalEvent("ie-6", "Yatra", "February", "EVEN"),
new InstitutionalEvent("ie-7", "International Womens Day", "March", "EVEN"),
new InstitutionalEvent("ie-8", "Ugathi", "March", "EVEN"),
new InstitutionalEvent("ie-9", "Tamil New Year", "April", "EVEN"),
new InstitutionalEvent("ie-10", "Techritz", "July", "ODD"),
new InstitutionalEvent("ie-11", "Independence Day", "August", "ODD"),
new InstitutionalEvent("ie-12", "AI Horizon Week", "August", "ODD"),
new InstitutionalEvent("ie-13", "Initiation Day", "September", "ODD"),
new InstitutionalEvent("ie-14", "Onam Celebration", "September", "ODD"),
new InstitutionalEvent("ie-15", "Teachers Day", "September", "ODD"),
new InstitutionalEvent("ie-16", "Engineers Day", "September", "ODD"),
new InstitutionalEvent("ie-17", "SDG Golu", "September", "ODD"),
new InstitutionalEvent("ie-18", "Ayutha Puja", "October", "ODD"),
new InstitutionalEvent("ie-19", "Tech Fest IIT Bombay", "October", "ODD"),
new InstitutionalEvent("ie-20", "EDI Conclave - FICCI Flow", "November", "ODD")
);
institutionalEventRepository.saveAll(defaultEvents);
}
}
}

View File

@@ -0,0 +1,102 @@
package com.rit.ems.controller;
import com.rit.ems.model.User;
import com.rit.ems.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
@RestController
@RequestMapping("/api/admin/users")
public class AdminController {
@Autowired
private UserRepository userRepository;
@GetMapping
public List<User> getAllUsers() {
return userRepository.findAll();
}
@PostMapping
public ResponseEntity<?> createUser(@RequestBody User userPayload) {
Optional<User> existing = userRepository.findByEmailIgnoreCase(userPayload.getEmail());
String role = userPayload.getRole() != null ? userPayload.getRole() : "FACULTY";
String department = userPayload.getDepartment() != null ? userPayload.getDepartment() :
((role.equals("HOD") || role.equals("FACULTY")) ? "H&S Dept" : "");
if (existing.isPresent()) {
User existingUser = existing.get();
if ("STUDENT".equals(existingUser.getRole())) {
// Promote existing student to faculty/staff
existingUser.setRole(role);
existingUser.setDepartment(department);
existingUser.setFullName(userPayload.getFullName());
if (userPayload.getAssignedClubs() != null) {
existingUser.setAssignedClubs(userPayload.getAssignedClubs());
}
userRepository.save(existingUser);
return ResponseEntity.ok(existingUser);
} else {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("message", "User with this email already exists"));
}
}
userPayload.setId(System.currentTimeMillis() + new Random().nextInt(1000));
userPayload.setEmail(userPayload.getEmail().trim().toLowerCase());
userPayload.setFullName(userPayload.getFullName().trim());
userPayload.setRole(role);
userPayload.setDepartment(department);
if (userPayload.getAssignedClubs() == null) {
userPayload.setAssignedClubs(new java.util.ArrayList<>());
}
userRepository.save(userPayload);
return ResponseEntity.ok(userPayload);
}
@PutMapping("/{id}")
public ResponseEntity<?> updateUser(@PathVariable Long id, @RequestBody Map<String, Object> payload) {
Optional<User> userOpt = userRepository.findById(id);
if (userOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("message", "User not found"));
}
User user = userOpt.get();
if (payload.containsKey("fullName")) user.setFullName((String) payload.get("fullName"));
if (payload.containsKey("email")) user.setEmail(((String) payload.get("email")).trim().toLowerCase());
if (payload.containsKey("role")) user.setRole((String) payload.get("role"));
if (payload.containsKey("department")) user.setDepartment((String) payload.get("department"));
if (payload.containsKey("isClubCoordinator")) user.setIsClubCoordinator((Boolean) payload.get("isClubCoordinator"));
if (payload.containsKey("isPlacementStaff")) user.setIsPlacementStaff((Boolean) payload.get("isPlacementStaff"));
if (payload.containsKey("isClassIncharge")) user.setIsClassIncharge((Boolean) payload.get("isClassIncharge"));
if (payload.containsKey("classStrength")) user.setClassStrength(Integer.parseInt(payload.get("classStrength").toString()));
if (payload.containsKey("inchargeClass")) user.setInchargeClass((String) payload.get("inchargeClass"));
if (payload.containsKey("inchargeBatch")) user.setInchargeBatch((String) payload.get("inchargeBatch"));
if (payload.containsKey("inchargeSection")) user.setInchargeSection((String) payload.get("inchargeSection"));
if (payload.containsKey("year")) user.setYear((String) payload.get("year"));
if (payload.containsKey("section")) user.setSection((String) payload.get("section"));
if (payload.containsKey("regNo")) user.setRegNo((String) payload.get("regNo"));
if (payload.containsKey("phone")) user.setPhone((String) payload.get("phone"));
if (payload.containsKey("gender")) user.setGender((String) payload.get("gender"));
if (payload.containsKey("collegeName")) user.setCollegeName((String) payload.get("collegeName"));
if (payload.containsKey("assignedClubs")) {
user.setAssignedClubs((List<String>) payload.get("assignedClubs"));
}
userRepository.save(user);
return ResponseEntity.ok(user);
}
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteUser(@PathVariable Long id) {
userRepository.deleteById(id);
return ResponseEntity.ok(Map.of("message", "User deleted successfully"));
}
}

View File

@@ -0,0 +1,47 @@
package com.rit.ems.controller;
import com.rit.ems.model.Announcement;
import com.rit.ems.repository.AnnouncementRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Random;
@RestController
@RequestMapping("/api/announcements")
public class AnnouncementController {
@Autowired
private AnnouncementRepository announcementRepository;
@GetMapping
public List<Announcement> getAllAnnouncements() {
List<Announcement> list = announcementRepository.findAll();
list.sort((a, b) -> {
String ta = a.getTimestamp() != null ? a.getTimestamp() : "";
String tb = b.getTimestamp() != null ? b.getTimestamp() : "";
return tb.compareTo(ta); // Descending order
});
return list;
}
@PostMapping
public ResponseEntity<?> createAnnouncement(@RequestBody Announcement ann) {
if (ann.getId() == null) {
ann.setId(String.valueOf(System.currentTimeMillis() + new Random().nextInt(1000)));
}
ann.setTimestamp(Instant.now().toString());
announcementRepository.save(ann);
return ResponseEntity.ok(ann);
}
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteAnnouncement(@PathVariable String id) {
announcementRepository.deleteById(id);
return ResponseEntity.ok(Map.of("message", "Announcement deleted successfully"));
}
}

View File

@@ -0,0 +1,72 @@
package com.rit.ems.controller;
import com.rit.ems.model.Attendance;
import com.rit.ems.repository.AttendanceRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/attendance")
public class AttendanceController {
@Autowired
private AttendanceRepository attendanceRepository;
@GetMapping
public List<Attendance> getAttendance(@RequestParam(required = false) String registrationId,
@RequestParam(required = false) Long eventId) {
if (registrationId != null) {
return attendanceRepository.findByRegistrationId(registrationId);
}
if (eventId != null) {
return attendanceRepository.findByEventId(eventId);
}
return attendanceRepository.findAll();
}
@PostMapping
public ResponseEntity<?> saveAttendance(@RequestBody Object payload) {
List<Map<String, Object>> records = new ArrayList<>();
if (payload instanceof List) {
records = (List<Map<String, Object>>) payload;
} else if (payload instanceof Map) {
records.add((Map<String, Object>) payload);
}
for (Map<String, Object> rec : records) {
String regId = (String) (rec.containsKey("registrationId") ? rec.get("registrationId") : rec.get("registration_id"));
String day = (String) (rec.containsKey("dayLabel") ? rec.get("dayLabel") :
(rec.containsKey("day_idx") ? rec.get("day_idx") : "Day 1"));
String slot = (String) (rec.containsKey("batchLabel") ? rec.get("batchLabel") :
(rec.containsKey("batch_idx") ? rec.get("batch_idx") : "Batch 1"));
Object eventIdObj = rec.containsKey("eventId") ? rec.get("eventId") : rec.get("event_id");
Long eventId = eventIdObj != null ? Long.parseLong(eventIdObj.toString()) : null;
String id = regId + "_" + day + "_" + slot;
Boolean isPresent = (Boolean) (rec.containsKey("isPresent") ? rec.get("isPresent") : rec.get("is_present"));
String date = (String) rec.get("date");
if (date == null) {
date = LocalDate.now().toString();
}
Attendance att = new Attendance();
att.setId(id);
att.setRegistrationId(regId);
att.setEventId(eventId);
att.setDayLabel(day);
att.setBatchLabel(slot);
att.setIsPresent(Boolean.TRUE.equals(isPresent));
att.setDate(date);
attendanceRepository.save(att);
}
return ResponseEntity.ok(Map.of("message", "Attendance updated successfully"));
}
}

View File

@@ -0,0 +1,220 @@
package com.rit.ems.controller;
import com.rit.ems.model.User;
import com.rit.ems.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private UserRepository userRepository;
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody Map<String, String> payload) {
String email = payload.get("email");
String password = payload.get("password");
if (email == null || password == null) {
return ResponseEntity.badRequest().body(Map.of("message", "Email and password are required"));
}
String emailLower = email.trim().toLowerCase();
Optional<User> userOpt = userRepository.findByEmailIgnoreCase(emailLower);
if (userOpt.isPresent()) {
User user = userOpt.get();
if (password.equals(user.getPassword())) {
// Exclude password from return payload
Map<String, Object> userData = new HashMap<>();
userData.put("id", user.getId());
userData.put("email", user.getEmail());
userData.put("fullName", user.getFullName());
userData.put("role", user.getRole());
userData.put("department", user.getDepartment() != null ? user.getDepartment() : "N/A");
userData.put("isClubCoordinator", user.getIsClubCoordinator());
userData.put("isPlacementStaff", user.getIsPlacementStaff());
userData.put("isClassIncharge", user.getIsClassIncharge());
userData.put("classStrength", user.getClassStrength());
userData.put("inchargeClass", user.getInchargeClass());
userData.put("inchargeBatch", user.getInchargeBatch());
userData.put("inchargeSection", user.getInchargeSection());
userData.put("year", user.getYear());
userData.put("section", user.getSection());
userData.put("regNo", user.getRegNo());
userData.put("phone", user.getPhone());
userData.put("gender", user.getGender());
userData.put("collegeName", user.getCollegeName());
userData.put("assignedClubs", user.getAssignedClubs() != null ? user.getAssignedClubs() : new ArrayList<>());
return ResponseEntity.ok(userData);
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid password for this registered email"));
}
}
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(Map.of("message", "Invalid email or passcode. Please check your credentials or register a new account."));
}
@PostMapping("/google-login")
public ResponseEntity<?> googleLogin(@RequestBody Map<String, String> payload) {
String email = payload.get("email");
String fullName = payload.get("fullName");
if (email == null) {
return ResponseEntity.badRequest().body(Map.of("message", "Email is required"));
}
String emailLower = email.trim().toLowerCase();
Optional<User> userOpt = userRepository.findByEmailIgnoreCase(emailLower);
if (userOpt.isPresent()) {
User user = userOpt.get();
Map<String, Object> userData = convertUserToMap(user);
return ResponseEntity.ok(userData);
}
// Auto-registration logic for RIT students
boolean isRitEmail = emailLower.contains("@ritchennai.edu.in") || emailLower.contains(".ritchennai.edu.in");
if (isRitEmail) {
String rollNo = "";
String rawDept = "CSE";
// Try to match student patterns: student.240007@cse.ritchennai.edu.in
if (emailLower.startsWith("student.")) {
try {
String[] parts = emailLower.split("@");
rollNo = parts[0].substring(8); // extracts "240007"
rawDept = parts[1].split("\\.")[0]; // cse
} catch (Exception e) {
rollNo = String.valueOf(100000 + new Random().nextInt(900000));
}
} else {
rollNo = String.valueOf(100000 + new Random().nextInt(900000));
}
// Calculate joining year and academic year (current local time is 2026-06-30)
int joinYear = 2000 + Integer.parseInt(rollNo.substring(0, Math.min(rollNo.length(), 2)));
int currentYear = 2026;
int academicYearOffset = 1; // Since month is June (June is >= 5)
int yearIndex = currentYear - joinYear + academicYearOffset;
String[] years = {"1st Year", "2nd Year", "3rd Year", "4th Year"};
String calculatedYear = (yearIndex >= 1 && yearIndex <= 4) ? years[yearIndex - 1] : "3rd Year";
// Map department
String dept = rawDept.toUpperCase();
if (dept.equals("AIDS")) dept = "AI&DS";
else if (dept.equals("AIML")) dept = "AI&ML";
else if (dept.equals("VLSI")) dept = "EE(VLSI)";
else if (dept.equals("BIOTECH") || dept.equals("BIO-TECH")) dept = "BIOTECH";
else if (dept.equals("H&S")) dept = "H&S Dept";
User newUser = new User();
newUser.setId(System.currentTimeMillis() + new Random().nextInt(1000));
newUser.setEmail(emailLower);
newUser.setPassword("google-oauth-placeholder");
newUser.setFullName(fullName != null ? fullName : "Student " + rollNo);
newUser.setRole("STUDENT");
newUser.setDepartment(dept);
newUser.setYear(calculatedYear);
newUser.setSection("A");
newUser.setRegNo(rollNo);
newUser.setPhone("");
newUser.setGender("Male");
newUser.setCollegeName("Rajalakshmi Institute of Technology");
newUser.setIsClubCoordinator(false);
newUser.setIsPlacementStaff(false);
newUser.setIsClassIncharge(false);
newUser.setAssignedClubs(new java.util.ArrayList<>());
userRepository.save(newUser);
return ResponseEntity.ok(convertUserToMap(newUser));
}
// Register external students automatically
User newUser = new User();
newUser.setId(System.currentTimeMillis() + new Random().nextInt(1000));
newUser.setEmail(emailLower);
newUser.setPassword("google-oauth-placeholder");
newUser.setFullName(fullName != null ? fullName : emailLower.split("@")[0]);
newUser.setRole("STUDENT");
newUser.setDepartment("Others");
newUser.setYear("N/A");
newUser.setSection("N/A");
newUser.setRegNo("EXT-" + (10000 + new Random().nextInt(90000)));
newUser.setPhone("");
newUser.setGender("Male");
newUser.setCollegeName("External Institution");
newUser.setIsClubCoordinator(false);
newUser.setIsPlacementStaff(false);
newUser.setIsClassIncharge(false);
newUser.setAssignedClubs(new java.util.ArrayList<>());
userRepository.save(newUser);
return ResponseEntity.ok(convertUserToMap(newUser));
}
@PostMapping("/signup")
public ResponseEntity<?> signup(@RequestBody Map<String, Object> payload) {
String email = (String) payload.get("email");
if (email == null) {
return ResponseEntity.badRequest().body(Map.of("message", "Email is required"));
}
String emailLower = email.trim().toLowerCase();
if (userRepository.findByEmailIgnoreCase(emailLower).isPresent()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("message", "A user with this email already exists"));
}
User newUser = new User();
newUser.setId(System.currentTimeMillis() + new Random().nextInt(1000));
newUser.setEmail(emailLower);
newUser.setPassword((String) payload.get("password"));
newUser.setFullName((String) payload.get("fullName"));
newUser.setRole("STUDENT");
newUser.setDepartment(payload.get("department") != null ? (String) payload.get("department") : "CSE");
newUser.setYear(payload.get("year") != null ? (String) payload.get("year") : "1st Year");
newUser.setSection(payload.get("section") != null ? (String) payload.get("section") : "A");
newUser.setRegNo((String) payload.get("regNo"));
newUser.setPhone((String) payload.get("phone"));
newUser.setGender(payload.get("gender") != null ? (String) payload.get("gender") : "Male");
boolean isRitEmail = emailLower.contains("@ritchennai.edu.in") || emailLower.contains(".ritchennai.edu.in");
newUser.setCollegeName(isRitEmail ? "Rajalakshmi Institute of Technology" : (String) payload.get("collegeName"));
userRepository.save(newUser);
return ResponseEntity.ok(convertUserToMap(newUser));
}
private Map<String, Object> convertUserToMap(User user) {
Map<String, Object> userData = new HashMap<>();
userData.put("id", user.getId());
userData.put("email", user.getEmail());
userData.put("fullName", user.getFullName());
userData.put("role", user.getRole());
userData.put("department", user.getDepartment() != null ? user.getDepartment() : "N/A");
userData.put("isClubCoordinator", user.getIsClubCoordinator());
userData.put("isPlacementStaff", user.getIsPlacementStaff());
userData.put("isClassIncharge", user.getIsClassIncharge());
userData.put("classStrength", user.getClassStrength());
userData.put("inchargeClass", user.getInchargeClass());
userData.put("inchargeBatch", user.getInchargeBatch());
userData.put("inchargeSection", user.getInchargeSection());
userData.put("year", user.getYear());
userData.put("section", user.getSection());
userData.put("regNo", user.getRegNo());
userData.put("phone", user.getPhone());
userData.put("gender", user.getGender());
userData.put("collegeName", user.getCollegeName());
userData.put("assignedClubs", user.getAssignedClubs() != null ? user.getAssignedClubs() : new ArrayList<>());
return userData;
}
}

View File

@@ -0,0 +1,42 @@
package com.rit.ems.controller;
import com.rit.ems.model.Batch;
import com.rit.ems.repository.BatchRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.Random;
@RestController
@RequestMapping("/api/batches")
public class BatchController {
@Autowired
private BatchRepository batchRepository;
@GetMapping
public List<Batch> getAllBatches() {
return batchRepository.findAll();
}
@PostMapping
public ResponseEntity<?> createBatch(@RequestBody Batch batch) {
if (batch.getId() == null) {
batch.setId(System.currentTimeMillis() + new Random().nextInt(1000));
}
if (batch.getClasses() == null) {
batch.setClasses(new java.util.ArrayList<>());
}
batchRepository.save(batch);
return ResponseEntity.ok(batch);
}
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteBatch(@PathVariable Long id) {
batchRepository.deleteById(id);
return ResponseEntity.ok(Map.of("message", "Batch deleted successfully"));
}
}

View File

@@ -0,0 +1,69 @@
package com.rit.ems.controller;
import com.rit.ems.model.ClassMapping;
import com.rit.ems.repository.ClassMappingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.Random;
@RestController
@RequestMapping("/api/classes")
public class ClassController {
@Autowired
private ClassMappingRepository classMappingRepository;
@GetMapping
public List<ClassMapping> getAllClasses() {
return classMappingRepository.findAll();
}
@PostMapping
public ResponseEntity<?> createClass(@RequestBody ClassMapping classMapping) {
if (classMapping.getId() == null) {
classMapping.setId(System.currentTimeMillis() + new Random().nextInt(1000));
}
if (classMapping.getStatus() == null) {
classMapping.setStatus("Ready");
}
classMappingRepository.save(classMapping);
return ResponseEntity.ok(classMapping);
}
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteClass(@PathVariable Long id) {
classMappingRepository.deleteById(id);
return ResponseEntity.ok(Map.of("message", "Class mapping deleted successfully"));
}
@PostMapping("/promote")
public ResponseEntity<?> promoteClasses(@RequestParam String institution) {
List<ClassMapping> classes = classMappingRepository.findAll();
for (ClassMapping cm : classes) {
if (cm.getInstitution() != null && cm.getInstitution().trim().equalsIgnoreCase(institution.trim())) {
switch (cm.getAcademicYear()) {
case "1st Year":
cm.setAcademicYear("2nd Year");
classMappingRepository.save(cm);
break;
case "2nd Year":
cm.setAcademicYear("3rd Year");
classMappingRepository.save(cm);
break;
case "3rd Year":
cm.setAcademicYear("4th Year");
classMappingRepository.save(cm);
break;
case "4th Year":
classMappingRepository.delete(cm);
break;
}
}
}
return ResponseEntity.ok(Map.of("message", "Academic year promotion completed for " + institution));
}
}

View File

@@ -0,0 +1,555 @@
package com.rit.ems.controller;
import com.rit.ems.model.Event;
import com.rit.ems.model.Registration;
import com.rit.ems.model.User;
import com.rit.ems.repository.EventRepository;
import com.rit.ems.repository.RegistrationRepository;
import com.rit.ems.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.*;
@RestController
@RequestMapping("/api/events")
public class EventController {
@Autowired
private EventRepository eventRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private RegistrationRepository registrationRepository;
@GetMapping
public List<Event> getAllEvents() {
List<Event> events = eventRepository.findAll();
List<Registration> regs = registrationRepository.findAll();
for (Event event : events) {
if (event.getStartDate() != null) {
String sd = event.getStartDate().replace(" ", "T");
if (!sd.endsWith("Z")) sd += "Z";
event.setStartDate(sd);
}
if (event.getEndDate() != null) {
String ed = event.getEndDate().replace(" ", "T");
if (!ed.endsWith("Z")) ed += "Z";
event.setEndDate(ed);
}
int count = (int) regs.stream()
.filter(r -> r.getEventId() != null && r.getEventId().equals(event.getId()))
.count();
event.setCurrentParticipants(count);
// Re-populate conflict messages if needed
if (!Arrays.asList("APPROVED", "COMPLETED", "CANCELLED").contains(event.getStatus())) {
String conflictMsg = getConflictMessage(event, event.getId());
if (conflictMsg != null) {
event.setConflictMessage(conflictMsg);
}
}
}
return events;
}
@PostMapping("/propose")
public ResponseEntity<?> proposeEvent(@RequestBody Map<String, Object> payload) {
Object userIdObj = payload.get("userId");
if (userIdObj == null) {
return ResponseEntity.badRequest().body(Map.of("message", "userId is required"));
}
Long userId = Long.parseLong(userIdObj.toString());
Optional<User> userOpt = userRepository.findById(userId);
if (userOpt.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("message", "Proposer user not found"));
}
User proposer = userOpt.get();
String startDateStr = (String) payload.get("startDate");
String endDateStr = (String) payload.get("endDate");
Instant start = parseDate(startDateStr);
Instant end = parseDate(endDateStr);
if (start.isBefore(Instant.now())) {
return ResponseEntity.badRequest().body(Map.of("message", "Cannot schedule events in the past"));
}
if (end.isBefore(start) || end.equals(start)) {
return ResponseEntity.badRequest().body(Map.of("message", "End date must be after start date"));
}
// Temporarily build target Event object to check conflicts
Event tempEvent = new Event();
tempEvent.setStartDate(startDateStr);
tempEvent.setEndDate(endDateStr);
tempEvent.setLocation((String) payload.get("venue"));
tempEvent.setInstitution((String) payload.get("institution"));
tempEvent.setGroupRequestId((String) payload.get("groupRequestId"));
String conflictMsg = getConflictMessage(tempEvent, null);
if (conflictMsg != null) {
String category = (String) payload.get("category");
Boolean cancelConflicting = (Boolean) payload.get("cancelConflicting");
if ("PLACEMENT".equals(category) && Boolean.TRUE.equals(cancelConflicting)) {
// Cancel conflicting events
List<Event> allEvents = eventRepository.findAll();
String location = tempEvent.getLocation().trim();
String inst = tempEvent.getInstitution() != null ? tempEvent.getInstitution().trim() : "RIT";
for (Event other : allEvents) {
if ("CANCELLED".equals(other.getStatus())) continue;
if (tempEvent.getGroupRequestId() != null && tempEvent.getGroupRequestId().equals(other.getGroupRequestId())) continue;
Instant oStart = parseDate(other.getStartDate());
Instant oEnd = parseDate(other.getEndDate());
String oLoc = other.getLocation() != null ? other.getLocation().trim() : "";
String oInst = other.getInstitution() != null ? other.getInstitution().trim() : "RIT";
if (oInst.equalsIgnoreCase(inst) && oLoc.equalsIgnoreCase(location) && start.isBefore(oEnd) && end.isAfter(oStart)) {
other.setStatus("CANCELLED");
other.setRejectionReason("This event is cancelled due to the placement activity happening at the venue at this timing.");
eventRepository.save(other);
}
}
} else {
// Return conflicts list
List<Event> conflicts = new ArrayList<>();
List<Event> allEvents = eventRepository.findAll();
String location = tempEvent.getLocation().trim();
String inst = tempEvent.getInstitution() != null ? tempEvent.getInstitution().trim() : "RIT";
for (Event other : allEvents) {
if ("CANCELLED".equals(other.getStatus())) continue;
Instant oStart = parseDate(other.getStartDate());
Instant oEnd = parseDate(other.getEndDate());
String oLoc = other.getLocation() != null ? other.getLocation().trim() : "";
String oInst = other.getInstitution() != null ? other.getInstitution().trim() : "RIT";
if (oInst.equalsIgnoreCase(inst) && oLoc.equalsIgnoreCase(location) && start.isBefore(oEnd) && end.isAfter(oStart)) {
conflicts.add(other);
}
}
return ResponseEntity.status(HttpStatus.CONFLICT).body(Map.of(
"message", conflictMsg,
"conflicts", conflicts,
"canOverride", "PLACEMENT".equals(category)
));
}
}
// Propose logic status
String status = "REQUESTED";
if ("PRINCIPAL".equals(proposer.getRole())) {
status = "APPROVED";
} else if ("HOD".equals(proposer.getRole()) ||
"CLUB".equals(payload.get("category")) ||
"PLACEMENT".equals(payload.get("category")) ||
"INSTITUTIONAL".equals(payload.get("category")) ||
"PLACEMENT".equals(proposer.getRole()) ||
Boolean.TRUE.equals(proposer.getIsPlacementStaff()) ||
"Placement Department".equals(proposer.getDepartment())) {
status = "PENDING_PR";
}
Event event = new Event();
event.setId(System.currentTimeMillis() + new Random().nextInt(1000));
event.setTitle((String) payload.get("eventName"));
event.setDescription((String) payload.get("description"));
event.setStartDate(start.toString());
event.setEndDate(end.toString());
event.setLocation((String) payload.get("venue"));
event.setCategory((String) payload.get("category"));
event.setType((String) payload.get("eventType"));
event.setInstitution((String) payload.get("institution"));
event.setDepartment((String) payload.get("department"));
event.setAcademicYears((List<String>) payload.get("academicYears"));
event.setStatus(status);
event.setGuestName((String) payload.get("guestName"));
event.setGuestSocialProfile((String) payload.get("socialProfile"));
event.setRequirements((List<String>) payload.get("requirements"));
event.setTargetedSections((List<String>) payload.get("targetedSections"));
event.setTargetDepartments((List<String>) payload.get("targetDepartments"));
event.setGroupRequestId((String) payload.get("groupRequestId"));
event.setSponsors((List<String>) payload.get("sponsors"));
event.setBudget(payload.get("budget") != null ? Double.parseDouble(payload.get("budget").toString()) : 0.0);
event.setHasRegistrationFee(Boolean.TRUE.equals(payload.get("hasRegistrationFee")));
event.setRegistrationFee(payload.get("registrationFee") != null ? Double.parseDouble(payload.get("registrationFee").toString()) : 0.0);
event.setPaymentLink((String) payload.get("paymentLink"));
event.setCentreName((String) payload.get("centreName"));
event.setIsPublicEvent(Boolean.TRUE.equals(payload.get("isPublicEvent")));
event.setImage((String) payload.get("image"));
event.setTargetedBatch((String) payload.get("targetedBatch"));
event.setOpenToAll(Boolean.TRUE.equals(payload.get("openToAll")));
Integer maxParticipants = payload.get("maxParticipants") != null && !payload.get("maxParticipants").toString().isEmpty()
? Integer.parseInt(payload.get("maxParticipants").toString()) : null;
event.setMaxParticipants(maxParticipants);
Event.ProposerInfo proposerInfo = new Event.ProposerInfo(
proposer.getId(),
proposer.getFullName(),
proposer.getEmail(),
proposer.getRole(),
proposer.getDepartment()
);
event.setProposer(proposerInfo);
event.setDayConfigs((List<Object>) payload.get("dayConfigs"));
event.setDeptLimits((Map<String, Object>) payload.get("deptLimits"));
event.setDeptSectionLimits((Map<String, Object>) payload.get("deptSectionLimits"));
event.setRefreshmentExpense(payload.get("refreshment_expense") != null ? Double.parseDouble(payload.get("refreshment_expense").toString()) : 0.0);
event.setTransportationExpense(payload.get("transportation_expense") != null ? Double.parseDouble(payload.get("transportation_expense").toString()) : 0.0);
event.setSessionCoverageFee(payload.get("session_coverage_fee") != null ? Double.parseDouble(payload.get("session_coverage_fee").toString()) : 0.0);
event.setTotalExpense(payload.get("total_expense") != null ? Double.parseDouble(payload.get("total_expense").toString()) : 0.0);
event.setDurationDays(payload.get("durationDays") != null ? Integer.parseInt(payload.get("durationDays").toString()) : 1);
eventRepository.save(event);
return ResponseEntity.ok(Map.of("message", "Event proposed successfully", "id", event.getId()));
}
@PostMapping("/batch-create")
public ResponseEntity<?> batchCreateEvents(@RequestBody List<Map<String, Object>> batchList) {
List<Event> eventsToSave = new ArrayList<>();
for (Map<String, Object> payload : batchList) {
String title = (String) payload.get("title");
String dateObj = payload.containsKey("startDate") ? (String) payload.get("startDate") : (String) payload.get("finalDate");
if (dateObj == null) {
return ResponseEntity.badRequest().body(Map.of("message", "Missing date for event: " + title));
}
Instant start = parseDate(dateObj);
Instant end;
if (payload.containsKey("startDate") && payload.containsKey("endDate")) {
try {
Instant origStart = parseDate((String) payload.get("startDate"));
Instant origEnd = parseDate((String) payload.get("endDate"));
long durationMs = origEnd.toEpochMilli() - origStart.toEpochMilli();
end = Instant.ofEpochMilli(start.toEpochMilli() + durationMs);
} catch (Exception e) {
end = Instant.ofEpochMilli(start.toEpochMilli() + 2 * 60 * 60 * 1000);
}
} else {
end = Instant.ofEpochMilli(start.toEpochMilli() + 2 * 60 * 60 * 1000);
}
if (end.isBefore(start) || end.equals(start)) {
return ResponseEntity.badRequest().body(Map.of("message", "End date must be after start date for event: " + title));
}
String dept = payload.containsKey("targetDepartment") ? (String) payload.get("targetDepartment") :
(payload.containsKey("department") ? (String) payload.get("department") : "General");
List<String> ay = payload.containsKey("targetBatch") ? Collections.singletonList((String) payload.get("targetBatch")) :
(List<String>) payload.get("academicYears");
String venue = payload.containsKey("venue") ? (String) payload.get("venue") :
(payload.containsKey("location") ? (String) payload.get("location") : "TBD");
Event.ProposerInfo proposer = new Event.ProposerInfo(1L, "Admin", "admin@rit.edu", "ADMIN", "ADMIN");
Event event = new Event();
event.setId(System.currentTimeMillis() + new Random().nextInt(1000));
event.setTitle(title);
event.setDescription((String) payload.get("description"));
event.setStartDate(start.toString());
event.setEndDate(end.toString());
event.setLocation(venue.trim());
event.setCategory((String) payload.get("category"));
event.setType((String) payload.get("type"));
event.setInstitution((String) payload.get("institution"));
event.setDepartment(dept);
event.setAcademicYears(ay);
event.setStatus(payload.containsKey("status") ? (String) payload.get("status") : "APPROVED");
event.setRequirements((List<String>) payload.get("requirements"));
event.setImage((String) payload.get("image"));
event.setProposer(proposer);
event.setOpenToAll(Boolean.TRUE.equals(payload.get("openToAll")));
String conflictMsg = getConflictMessage(event, null);
if (conflictMsg != null) {
return ResponseEntity.badRequest().body(Map.of("message", String.format("Conflict in batch item '%s': %s", title, conflictMsg)));
}
eventsToSave.add(event);
}
// Check intra-batch conflicts
for (int i = 0; i < eventsToSave.size(); i++) {
Event current = eventsToSave.get(i);
Instant currentStart = parseDate(current.getStartDate());
Instant currentEnd = parseDate(current.getEndDate());
String currentLoc = current.getLocation().toLowerCase();
for (int j = i + 1; j < eventsToSave.size(); j++) {
Event other = eventsToSave.get(j);
Instant otherStart = parseDate(other.getStartDate());
Instant otherEnd = parseDate(other.getEndDate());
String otherLoc = other.getLocation().toLowerCase();
if (current.getInstitution().equalsIgnoreCase(other.getInstitution()) &&
currentLoc.equals(otherLoc) &&
currentStart.isBefore(otherEnd) &&
currentEnd.isAfter(otherStart)) {
return ResponseEntity.badRequest().body(Map.of("message", String.format("Conflict between imported events '%s' and '%s' at %s",
current.getTitle(), other.getTitle(), current.getLocation())));
}
}
}
eventRepository.saveAll(eventsToSave);
return ResponseEntity.ok(Map.of("message", "Batch events created successfully", "count", eventsToSave.size()));
}
@PostMapping("/{id}/approve")
public ResponseEntity<?> approveEvent(@PathVariable Long id,
@RequestParam Long userId,
@RequestParam(required = false) String newVenue) {
Optional<User> userOpt = userRepository.findById(userId);
Optional<Event> eventOpt = eventRepository.findById(id);
if (userOpt.isEmpty() || eventOpt.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("message", "User or Event not found"));
}
User user = userOpt.get();
Event event = eventOpt.get();
if (newVenue != null) {
event.setLocation(newVenue);
}
String conflictMsg = getConflictMessage(event, id);
if (conflictMsg != null) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(Map.of("message", conflictMsg));
}
String newStatus = event.getStatus();
if ("HOD".equals(user.getRole())) {
newStatus = "PENDING_PR";
} else if ("PRINCIPAL".equals(user.getRole())) {
newStatus = "APPROVED";
} else {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of("message", "Only HoD or Principal can approve events"));
}
event.setStatus(newStatus);
eventRepository.save(event);
return ResponseEntity.ok(Map.of("message", "Event action completed successfully", "status", newStatus));
}
@PostMapping("/{id}/reject")
public ResponseEntity<?> rejectEvent(@PathVariable Long id,
@RequestParam Long userId,
@RequestBody Map<String, String> body) {
String reason = body.get("reason");
if (reason == null || reason.trim().isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("message", "Rejection reason is mandatory"));
}
Optional<User> userOpt = userRepository.findById(userId);
Optional<Event> eventOpt = eventRepository.findById(id);
if (userOpt.isEmpty() || eventOpt.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("message", "User or Event not found"));
}
User user = userOpt.get();
Event event = eventOpt.get();
String newStatus = event.getStatus();
if ("HOD".equals(user.getRole())) {
newStatus = "HOD_REJECTED";
} else if ("PRINCIPAL".equals(user.getRole())) {
newStatus = "PRINCIPAL_REJECTED";
} else {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of("message", "Only HoD or Principal can reject events"));
}
event.setStatus(newStatus);
event.setRejectionReason(reason);
eventRepository.save(event);
return ResponseEntity.ok(Map.of("message", "Event rejected successfully", "status", newStatus));
}
@PutMapping("/{id}")
public ResponseEntity<?> updateEvent(@PathVariable Long id, @RequestBody Map<String, Object> payload) {
Optional<Event> eventOpt = eventRepository.findById(id);
if (eventOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("message", "Event not found"));
}
Event event = eventOpt.get();
Object userIdObj = payload.get("userId");
if (userIdObj == null) {
return ResponseEntity.badRequest().body(Map.of("message", "User ID is required to update event"));
}
Long userId = Long.parseLong(userIdObj.toString());
Optional<User> userOpt = userRepository.findById(userId);
if (userOpt.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("message", "User not found"));
}
User user = userOpt.get();
boolean isAdmin = "ADMIN".equals(user.getRole());
if (!isAdmin) {
if (event.getProposer() == null || !event.getProposer().getId().equals(userId)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of("message", "Only the proposer can edit this event"));
}
if (!"REQUESTED".equals(event.getStatus())) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(Map.of("message", "Event cannot be edited once it moves past the initial request stage"));
}
}
String startStr = payload.containsKey("startDate") ? (String) payload.get("startDate") : event.getStartDate();
String endStr = payload.containsKey("endDate") ? (String) payload.get("endDate") : event.getEndDate();
Instant start = parseDate(startStr);
Instant end = parseDate(endStr);
if (end.isBefore(start) || end.equals(start)) {
return ResponseEntity.badRequest().body(Map.of("message", "End date must be after start date"));
}
// Apply fields selectively
if (payload.containsKey("title")) event.setTitle((String) payload.get("title"));
event.setStartDate(start.toString());
event.setEndDate(end.toString());
if (payload.containsKey("eventType")) event.setType((String) payload.get("eventType"));
if (payload.containsKey("institution")) event.setInstitution((String) payload.get("institution"));
if (payload.containsKey("department")) event.setDepartment((String) payload.get("department"));
if (payload.containsKey("venue")) event.setLocation((String) payload.get("venue"));
if (payload.containsKey("category")) event.setCategory((String) payload.get("category"));
if (payload.containsKey("maxParticipants")) {
String val = payload.get("maxParticipants").toString();
event.setMaxParticipants(val.isEmpty() ? null : Integer.parseInt(val));
}
if (payload.containsKey("guestName")) event.setGuestName((String) payload.get("guestName"));
if (payload.containsKey("socialProfile")) event.setGuestSocialProfile((String) payload.get("socialProfile"));
if (payload.containsKey("academicYears")) event.setAcademicYears((List<String>) payload.get("academicYears"));
if (payload.containsKey("targetedSections")) event.setTargetedSections((List<String>) payload.get("targetedSections"));
if (payload.containsKey("requirements")) event.setRequirements((List<String>) payload.get("requirements"));
if (payload.containsKey("sponsors")) event.setSponsors((List<String>) payload.get("sponsors"));
if (payload.containsKey("description")) event.setDescription((String) payload.get("description"));
if (payload.containsKey("budget")) {
String b = payload.get("budget").toString();
event.setBudget(b.isEmpty() ? 0.0 : Double.parseDouble(b));
}
if (payload.containsKey("hasRegistrationFee")) {
event.setHasRegistrationFee((Boolean) payload.get("hasRegistrationFee"));
}
if (payload.containsKey("registrationFee")) {
String f = payload.get("registrationFee").toString();
event.setRegistrationFee(f.isEmpty() ? 0.0 : Double.parseDouble(f));
}
if (payload.containsKey("centreName")) event.setCentreName((String) payload.get("centreName"));
if (payload.containsKey("isPublicEvent")) event.setIsPublicEvent((Boolean) payload.get("isPublicEvent"));
if (payload.containsKey("image")) event.setImage((String) payload.get("image"));
if (payload.containsKey("openToAll")) event.setOpenToAll((Boolean) payload.get("openToAll"));
if (payload.containsKey("targetedBatch")) event.setTargetedBatch((String) payload.get("targetedBatch"));
if (payload.containsKey("dayConfigs")) event.setDayConfigs((List<Object>) payload.get("dayConfigs"));
if (payload.containsKey("deptLimits")) event.setDeptLimits((Map<String, Object>) payload.get("deptLimits"));
if (payload.containsKey("deptSectionLimits")) event.setDeptSectionLimits((Map<String, Object>) payload.get("deptSectionLimits"));
if (payload.containsKey("refreshment_expense")) event.setRefreshmentExpense(Double.parseDouble(payload.get("refreshment_expense").toString()));
if (payload.containsKey("transportation_expense")) event.setTransportationExpense(Double.parseDouble(payload.get("transportation_expense").toString()));
if (payload.containsKey("session_coverage_fee")) event.setSessionCoverageFee(Double.parseDouble(payload.get("session_coverage_fee").toString()));
if (payload.containsKey("total_expense")) event.setTotalExpense(Double.parseDouble(payload.get("total_expense").toString()));
if (payload.containsKey("durationDays")) event.setDurationDays(Integer.parseInt(payload.get("durationDays").toString()));
if (payload.containsKey("status") && isAdmin) {
event.setStatus((String) payload.get("status"));
}
String conflictMsg = getConflictMessage(event, id);
if (conflictMsg != null) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(Map.of("message", conflictMsg));
}
eventRepository.save(event);
return ResponseEntity.ok(Map.of("message", "Event updated successfully"));
}
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteEvent(@PathVariable Long id) {
eventRepository.deleteById(id);
return ResponseEntity.ok(Map.of("message", "Event deleted successfully"));
}
private Instant parseDate(String val) {
if (val == null || val.trim().isEmpty()) {
return Instant.now();
}
String clean = val.trim().replace(" ", "T");
if (clean.length() == 10) {
clean += "T00:00:00";
}
if (clean.length() == 16) {
clean += ":00";
}
if (!clean.contains("Z") && !clean.contains("+") && !clean.contains("-")) {
clean += "Z";
}
try {
return Instant.parse(clean);
} catch (Exception e) {
try {
return java.time.LocalDateTime.parse(clean.substring(0, 19))
.toInstant(java.time.ZoneOffset.UTC);
} catch (Exception ex) {
return Instant.now();
}
}
}
private String getConflictMessage(Event event, Long ignoreId) {
if (event.getLocation() == null || event.getStartDate() == null || event.getEndDate() == null) {
return null;
}
String location = event.getLocation().trim();
String institution = event.getInstitution() != null ? event.getInstitution().trim() : "RIT";
Instant start = parseDate(event.getStartDate());
Instant end = parseDate(event.getEndDate());
List<Event> existingEvents = eventRepository.findAll();
for (Event other : existingEvents) {
if ("CANCELLED".equals(other.getStatus())) continue;
if (ignoreId != null && other.getId().equals(ignoreId)) continue;
if (event.getId() != null && other.getId().equals(event.getId())) continue;
if (event.getGroupRequestId() != null && event.getGroupRequestId().equals(other.getGroupRequestId())) continue;
Instant oStart = parseDate(other.getStartDate());
Instant oEnd = parseDate(other.getEndDate());
String oLoc = other.getLocation() != null ? other.getLocation().trim() : "";
String oInst = other.getInstitution() != null ? other.getInstitution().trim() : "RIT";
if (oInst.equalsIgnoreCase(institution) &&
oLoc.equalsIgnoreCase(location) &&
start.isBefore(oEnd) &&
end.isAfter(oStart)) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm")
.withZone(java.time.ZoneOffset.UTC);
return String.format("Venue Conflict: '%s' is already booked for '%s' from %s to %s",
location, other.getTitle(), formatter.format(oStart), formatter.format(oEnd));
}
}
return null;
}
}

View File

@@ -0,0 +1,38 @@
package com.rit.ems.controller;
import com.rit.ems.model.InstitutionalEvent;
import com.rit.ems.repository.InstitutionalEventRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/institutional-events")
public class InstitutionalEventController {
@Autowired
private InstitutionalEventRepository institutionalEventRepository;
@GetMapping
public List<InstitutionalEvent> getAllEvents() {
return institutionalEventRepository.findAll();
}
@PostMapping
public ResponseEntity<?> createEvent(@RequestBody InstitutionalEvent event) {
if (event.getId() == null) {
event.setId("ie-" + System.currentTimeMillis());
}
institutionalEventRepository.save(event);
return ResponseEntity.ok(event);
}
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteEvent(@PathVariable String id) {
institutionalEventRepository.deleteById(id);
return ResponseEntity.ok(Map.of("message", "Institutional event deleted successfully"));
}
}

View File

@@ -0,0 +1,40 @@
package com.rit.ems.controller;
import com.rit.ems.model.Note;
import com.rit.ems.repository.NoteRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.Instant;
import java.util.List;
import java.util.Random;
@RestController
@RequestMapping("/api/notes")
public class NoteController {
@Autowired
private NoteRepository noteRepository;
@GetMapping
public List<Note> getAllNotes() {
List<Note> list = noteRepository.findAll();
list.sort((a, b) -> {
String ca = a.getCreatedAt() != null ? a.getCreatedAt() : "";
String cb = b.getCreatedAt() != null ? b.getCreatedAt() : "";
return cb.compareTo(ca); // Descending order
});
return list;
}
@PostMapping
public ResponseEntity<?> createNote(@RequestBody Note note) {
if (note.getId() == null) {
note.setId(System.currentTimeMillis() + new Random().nextInt(1000));
}
note.setCreatedAt(Instant.now().toString());
noteRepository.save(note);
return ResponseEntity.ok(note);
}
}

View File

@@ -0,0 +1,220 @@
package com.rit.ems.controller;
import com.rit.ems.model.Event;
import com.rit.ems.model.Registration;
import com.rit.ems.repository.EventRepository;
import com.rit.ems.repository.RegistrationRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@RestController
@RequestMapping("/api/registrations")
public class RegistrationController {
@Autowired
private RegistrationRepository registrationRepository;
@Autowired
private EventRepository eventRepository;
@GetMapping
public List<Registration> getRegistrations(@RequestParam(required = false) Long userId,
@RequestParam(required = false) Long eventId,
@RequestParam(required = false) String teamCode) {
List<Registration> list;
if (userId != null) {
list = registrationRepository.findByUserId(userId);
} else if (eventId != null) {
list = registrationRepository.findByEventId(eventId);
} else if (teamCode != null) {
list = registrationRepository.findByTeamCode(teamCode);
} else {
list = registrationRepository.findAll();
}
for (Registration reg : list) {
if (reg.getRegisteredAt() != null) {
String ra = reg.getRegisteredAt().replace(" ", "T");
if (!ra.endsWith("Z")) ra += "Z";
reg.setRegisteredAt(ra);
}
}
return list;
}
@PostMapping
public ResponseEntity<?> createRegistration(@RequestBody Registration payload) {
Long userId = payload.getUserId();
Long eventId = payload.getEventId();
if (userId == null || eventId == null) {
return ResponseEntity.badRequest().body(Map.of("message", "userId and eventId are required"));
}
String regId = userId + "_" + eventId;
Optional<Registration> existing = registrationRepository.findById(regId);
if (existing.isPresent()) {
return ResponseEntity.badRequest().body(Map.of("message", "You are already registered for this event."));
}
Optional<Event> eventOpt = eventRepository.findById(eventId);
if (eventOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("message", "Event not found"));
}
Event event = eventOpt.get();
if (event.getEndDate() != null) {
// Check deadline
Instant deadline = parseDate(event.getEndDate());
if (Instant.now().isAfter(deadline)) {
return ResponseEntity.badRequest().body(Map.of("message", "Registration Blocked: The deadline for this event has passed."));
}
}
List<Registration> eventRegs = registrationRepository.findByEventId(eventId);
int currentParticipants = eventRegs.size();
if (event.getMaxParticipants() != null && currentParticipants >= event.getMaxParticipants()) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(Map.of("message", "Registration Blocked: The event has reached its capacity."));
}
String studentDept = payload.getDept() != null ? payload.getDept() : (payload.getDepartment() != null ? payload.getDepartment() : "CSE");
String studentSec = payload.getSection() != null ? payload.getSection() : "A";
if (event.getDeptLimits() != null && !event.getDeptLimits().isEmpty()) {
if (!event.getDeptLimits().containsKey(studentDept)) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(Map.of("message", "Registration Blocked: Department " + studentDept + " is not permitted to register."));
}
int deptLimit = Integer.parseInt(event.getDeptLimits().get(studentDept).toString());
long deptRegsCount = eventRegs.stream().filter(r -> studentDept.equals(r.getDept()) || studentDept.equals(r.getDepartment())).count();
if (event.getDeptSectionLimits() != null && event.getDeptSectionLimits().containsKey(studentDept)) {
Map<String, Object> sectionLimits = (Map<String, Object>) event.getDeptSectionLimits().get(studentDept);
if (!sectionLimits.containsKey(studentSec)) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(Map.of("message", "Registration Blocked: Section " + studentSec + " of " + studentDept + " is not permitted to register."));
}
int sectionLimit = Integer.parseInt(sectionLimits.get(studentSec).toString());
if (sectionLimit <= 0) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(Map.of("message", "Registration Blocked: Section " + studentSec + " of " + studentDept + " is not permitted to register."));
}
long secRegsCount = eventRegs.stream().filter(r -> (studentDept.equals(r.getDept()) || studentDept.equals(r.getDepartment())) && studentSec.equals(r.getSection())).count();
if (secRegsCount >= sectionLimit) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(Map.of("message", "Registration Blocked: Quota for Section " + studentSec + " of " + studentDept + " is full."));
}
} else {
if (deptRegsCount >= deptLimit) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(Map.of("message", "Registration Blocked: Quota for department " + studentDept + " is full."));
}
}
}
boolean isFree = event.getHasRegistrationFee() == null || !event.getHasRegistrationFee() || event.getRegistrationFee() == 0;
payload.setId(regId);
payload.setPaymentStatus(isFree ? "COMPLETED" : "PENDING");
payload.setCertificationStatus("NOT_SUBMITTED");
payload.setRegisteredAt(Instant.now().toString());
registrationRepository.save(payload);
event.setCurrentParticipants(currentParticipants + 1);
eventRepository.save(event);
return ResponseEntity.ok(payload);
}
@PutMapping("/{id}")
public ResponseEntity<?> updateRegistration(@PathVariable String id, @RequestBody Map<String, Object> payload) {
Optional<Registration> regOpt = registrationRepository.findById(id);
if (regOpt.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(Map.of("message", "Registration not found"));
}
Registration reg = regOpt.get();
if (payload.containsKey("payment_status")) reg.setPaymentStatus((String) payload.get("payment_status"));
if (payload.containsKey("paymentStatus")) reg.setPaymentStatus((String) payload.get("paymentStatus"));
if (payload.containsKey("team_code")) reg.setTeamCode((String) payload.get("team_code"));
if (payload.containsKey("teamCode")) reg.setTeamCode((String) payload.get("teamCode"));
if (payload.containsKey("team_name")) reg.setTeamName((String) payload.get("team_name"));
if (payload.containsKey("teamName")) reg.setTeamName((String) payload.get("teamName"));
if (payload.containsKey("is_team_leader")) reg.setIsTeamLeader((Boolean) payload.get("is_team_leader"));
if (payload.containsKey("isTeamLeader")) reg.setIsTeamLeader((Boolean) payload.get("isTeamLeader"));
if (payload.containsKey("certification_url")) reg.setCertificationUrl((String) payload.get("certification_url"));
if (payload.containsKey("certificationUrl")) reg.setCertificationUrl((String) payload.get("certificationUrl"));
if (payload.containsKey("certification_status")) reg.setCertificationStatus((String) payload.get("certification_status"));
if (payload.containsKey("certificationStatus")) reg.setCertificationStatus((String) payload.get("certificationStatus"));
if (payload.containsKey("od_url")) reg.setOdUrl((String) payload.get("od_url"));
if (payload.containsKey("odUrl")) reg.setOdUrl((String) payload.get("odUrl"));
if (payload.containsKey("ticket_id")) reg.setTicketId((String) payload.get("ticket_id"));
if (payload.containsKey("ticketId")) reg.setTicketId((String) payload.get("ticketId"));
if (payload.containsKey("ticket_qrcode")) reg.setTicketQrcode((String) payload.get("ticket_qrcode"));
if (payload.containsKey("ticketQrcode")) reg.setTicketQrcode((String) payload.get("ticketQrcode"));
registrationRepository.save(reg);
return ResponseEntity.ok(Map.of("message", "Registration updated successfully", "data", reg));
}
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteRegistration(@PathVariable String id) {
Optional<Registration> regOpt = registrationRepository.findById(id);
if (regOpt.isPresent()) {
Registration reg = regOpt.get();
Long eventId = reg.getEventId();
registrationRepository.delete(reg);
if (eventId != null) {
Optional<Event> eventOpt = eventRepository.findById(eventId);
if (eventOpt.isPresent()) {
Event event = eventOpt.get();
int count = registrationRepository.findByEventId(eventId).size();
event.setCurrentParticipants(count);
eventRepository.save(event);
}
}
}
return ResponseEntity.ok(Map.of("message", "Registration deleted successfully"));
}
private Instant parseDate(String val) {
if (val == null || val.trim().isEmpty()) {
return Instant.now();
}
String clean = val.trim().replace(" ", "T");
if (clean.length() == 10) {
clean += "T00:00:00";
}
if (clean.length() == 16) {
clean += ":00";
}
if (!clean.contains("Z") && !clean.contains("+") && !clean.contains("-")) {
clean += "Z";
}
try {
return Instant.parse(clean);
} catch (Exception e) {
try {
return java.time.LocalDateTime.parse(clean.substring(0, 19))
.toInstant(java.time.ZoneOffset.UTC);
} catch (Exception ex) {
return Instant.now();
}
}
}
}

View File

@@ -0,0 +1,40 @@
package com.rit.ems.controller;
import com.rit.ems.model.Setting;
import com.rit.ems.repository.SettingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/settings")
public class SettingController {
@Autowired
private SettingRepository settingRepository;
@GetMapping
public Map<String, Object> getSettings() {
List<Setting> list = settingRepository.findAll();
Map<String, Object> settings = new HashMap<>();
for (Setting s : list) {
settings.put(s.getId(), s.getValue());
}
return settings;
}
@PostMapping
public ResponseEntity<?> saveSetting(@RequestBody Map<String, Object> payload) {
String key = (String) payload.get("key");
Object value = payload.get("value");
if (key != null) {
Setting setting = new Setting(key, value);
settingRepository.save(setting);
}
return ResponseEntity.ok(Map.of("message", "Settings saved successfully"));
}
}

View File

@@ -0,0 +1,72 @@
package com.rit.ems.controller;
import com.rit.ems.model.SpecialEvent;
import com.rit.ems.repository.SpecialEventRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Random;
@RestController
@RequestMapping("/api/special-events")
public class SpecialEventController {
@Autowired
private SpecialEventRepository specialEventRepository;
@GetMapping
public List<SpecialEvent> getAllSpecialEvents() {
List<SpecialEvent> list = specialEventRepository.findAll();
for (SpecialEvent se : list) {
if (se.getStartDate() != null) {
String sd = se.getStartDate().replace(" ", "T");
if (!sd.endsWith("Z")) sd += "Z";
se.setStartDate(sd);
}
if (se.getEndDate() != null) {
String ed = se.getEndDate().replace(" ", "T");
if (!ed.endsWith("Z")) ed += "Z";
se.setEndDate(ed);
}
if (se.getCreatedAt() != null) {
String ca = se.getCreatedAt().replace(" ", "T");
if (!ca.endsWith("Z")) ca += "Z";
se.setCreatedAt(ca);
}
}
list.sort((a, b) -> {
String ca = a.getCreatedAt() != null ? a.getCreatedAt() : "";
String cb = b.getCreatedAt() != null ? b.getCreatedAt() : "";
return cb.compareTo(ca); // Descending order
});
return list;
}
@PostMapping
public ResponseEntity<?> createSpecialEvent(@RequestBody SpecialEvent se) {
if (se.getId() == null) {
se.setId(String.valueOf(System.currentTimeMillis() + new Random().nextInt(1000)));
}
if (se.getCreatedAt() == null) {
se.setCreatedAt(Instant.now().toString());
}
if (se.getIsActive() == null) {
se.setIsActive(true);
}
if (se.getVerificationStatus() == null) {
se.setVerificationStatus("APPROVED");
}
specialEventRepository.save(se);
return ResponseEntity.ok(se);
}
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteSpecialEvent(@PathVariable String id) {
specialEventRepository.deleteById(id);
return ResponseEntity.ok(Map.of("message", "Special event deleted successfully"));
}
}

View File

@@ -0,0 +1,39 @@
package com.rit.ems.controller;
import com.rit.ems.model.Venue;
import com.rit.ems.repository.VenueRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.Random;
@RestController
@RequestMapping("/api/venues")
public class VenueController {
@Autowired
private VenueRepository venueRepository;
@GetMapping
public List<Venue> getAllVenues() {
return venueRepository.findAll();
}
@PostMapping
public ResponseEntity<?> createVenue(@RequestBody Venue venue) {
if (venue.getId() == null) {
venue.setId("v-" + (System.currentTimeMillis() + new Random().nextInt(1000)));
}
venueRepository.save(venue);
return ResponseEntity.ok(venue);
}
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteVenue(@PathVariable String id) {
venueRepository.deleteById(id);
return ResponseEntity.ok(Map.of("message", "Venue deleted successfully"));
}
}

View File

@@ -0,0 +1,48 @@
package com.rit.ems.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "ems_announcements")
public class Announcement {
@Id
private String id;
private String title;
@Column(columnDefinition = "TEXT")
private String content;
private String timestamp;
@Column(name = "sender_role")
private String senderRole;
public Announcement() {}
public Announcement(String id, String title, String content, String timestamp, String senderRole) {
this.id = id;
this.title = title;
this.content = content;
this.timestamp = timestamp;
this.senderRole = senderRole;
}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public String getTimestamp() { return timestamp; }
public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
public String getSenderRole() { return senderRole; }
public void setSenderRole(String senderRole) { this.senderRole = senderRole; }
}

View File

@@ -0,0 +1,100 @@
package com.rit.ems.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "ems_attendance")
public class Attendance {
@Id
private String id; // format: regId_day_slot
@Column(name = "registration_id")
private String registrationId;
@Column(name = "event_id")
private Long eventId;
@Column(name = "day_label")
private String dayLabel;
@Column(name = "batch_label")
private String batchLabel;
@Column(name = "is_present")
private Boolean isPresent = false;
private String date;
public Attendance() {}
public Attendance(String id, String registrationId, Long eventId, String dayLabel, String batchLabel, Boolean isPresent, String date) {
this.id = id;
this.registrationId = registrationId;
this.eventId = eventId;
this.dayLabel = dayLabel;
this.batchLabel = batchLabel;
this.isPresent = isPresent;
this.date = date;
}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getRegistrationId() { return registrationId; }
public void setRegistrationId(String registrationId) { this.registrationId = registrationId; }
public Long getEventId() { return eventId; }
public void setEventId(Long eventId) { this.eventId = eventId; }
public String getDayLabel() { return dayLabel; }
public void setDayLabel(String dayLabel) { this.dayLabel = dayLabel; }
public String getBatchLabel() { return batchLabel; }
public void setBatchLabel(String batchLabel) { this.batchLabel = batchLabel; }
public Boolean getIsPresent() { return isPresent; }
public void setIsPresent(Boolean isPresent) { this.isPresent = isPresent; }
public String getDate() { return date; }
public void setDate(String date) { this.date = date; }
// Getters and setters to bridge snake_case from frontend
@JsonProperty("registration_id")
public String getRegistrationIdSnake() { return registrationId; }
@JsonProperty("registration_id")
public void setRegistrationIdSnake(String registrationId) { this.registrationId = registrationId; }
@JsonProperty("event_id")
public Long getEventIdSnake() { return eventId; }
@JsonProperty("event_id")
public void setEventIdSnake(Long eventId) { this.eventId = eventId; }
@JsonProperty("day_label")
public String getDayLabelSnake() { return dayLabel; }
@JsonProperty("day_label")
public void setDayLabelSnake(String dayLabel) { this.dayLabel = dayLabel; }
@JsonProperty("day_idx")
public String getDayIdx() { return dayLabel; }
@JsonProperty("day_idx")
public void setDayIdx(String dayLabel) { this.dayLabel = dayLabel; }
@JsonProperty("batch_label")
public String getBatchLabelSnake() { return batchLabel; }
@JsonProperty("batch_label")
public void setBatchLabelSnake(String batchLabel) { this.batchLabel = batchLabel; }
@JsonProperty("batch_idx")
public String getBatchIdx() { return batchLabel; }
@JsonProperty("batch_idx")
public void setBatchIdx(String batchLabel) { this.batchLabel = batchLabel; }
@JsonProperty("is_present")
public Boolean getIsPresentSnake() { return isPresent; }
@JsonProperty("is_present")
public void setIsPresentSnake(Boolean isPresent) { this.isPresent = isPresent; }
}

View File

@@ -0,0 +1,35 @@
package com.rit.ems.model;
import com.rit.ems.util.JsonConverter;
import jakarta.persistence.*;
import java.util.List;
@Entity
@Table(name = "ems_batches")
public class Batch {
@Id
private Long id;
private String name;
@Convert(converter = JsonConverter.class)
@Column(columnDefinition = "TEXT")
private List<Object> classes;
public Batch() {}
public Batch(Long id, String name, List<Object> classes) {
this.id = id;
this.name = name;
this.classes = classes;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List<Object> getClasses() { return classes; }
public void setClasses(List<Object> classes) { this.classes = classes; }
}

View File

@@ -0,0 +1,53 @@
package com.rit.ems.model;
import com.rit.ems.util.JsonConverter;
import jakarta.persistence.*;
import java.util.List;
@Entity
@Table(name = "ems_classes")
public class ClassMapping {
@Id
private Long id;
private String institution;
private String department;
@Column(name = "academic_year")
private String academicYear;
@Convert(converter = JsonConverter.class)
@Column(columnDefinition = "TEXT")
private List<String> sections;
private String status;
public ClassMapping() {}
public ClassMapping(Long id, String institution, String department, String academicYear, List<String> sections, String status) {
this.id = id;
this.institution = institution;
this.department = department;
this.academicYear = academicYear;
this.sections = sections;
this.status = status;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getInstitution() { return institution; }
public void setInstitution(String institution) { this.institution = institution; }
public String getDepartment() { return department; }
public void setDepartment(String department) { this.department = department; }
public String getAcademicYear() { return academicYear; }
public void setAcademicYear(String academicYear) { this.academicYear = academicYear; }
public List<String> getSections() { return sections; }
public void setSections(List<String> sections) { this.sections = sections; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
}

View File

@@ -0,0 +1,341 @@
package com.rit.ems.model;
import com.rit.ems.util.JsonConverter;
import jakarta.persistence.*;
import java.util.List;
import java.util.Map;
@Entity
@Table(name = "ems_events")
public class Event {
@Id
private Long id;
private String title;
@Column(columnDefinition = "TEXT")
private String description;
@Column(name = "start_date")
private String startDate;
@Column(name = "end_date")
private String endDate;
private String location;
private String category;
private String type;
private String institution;
private String department;
@Convert(converter = JsonConverter.class)
@Column(name = "academic_years", columnDefinition = "TEXT")
private List<String> academicYears;
private String status;
@Column(name = "guest_name")
private String guestName;
@Column(name = "guest_social_profile")
private String guestSocialProfile;
@Convert(converter = JsonConverter.class)
@Column(columnDefinition = "TEXT")
private List<String> requirements;
@Convert(converter = JsonConverter.class)
@Column(name = "targeted_sections", columnDefinition = "TEXT")
private List<String> targetedSections;
@Convert(converter = JsonConverter.class)
@Column(name = "target_departments", columnDefinition = "TEXT")
private List<String> targetDepartments;
@Column(name = "group_request_id")
private String groupRequestId;
@Convert(converter = JsonConverter.class)
@Column(columnDefinition = "TEXT")
private List<String> sponsors;
private Double budget = 0.0;
@Column(name = "has_registration_fee")
private Boolean hasRegistrationFee = false;
@Column(name = "registration_fee")
private Double registrationFee = 0.0;
@Column(name = "payment_link")
private String paymentLink;
@Column(name = "centre_name")
private String centreName;
@Column(name = "is_public_event")
private Boolean isPublicEvent = false;
@Column(columnDefinition = "TEXT")
private String image;
@Column(name = "targeted_batch")
private String targetedBatch;
@Column(name = "open_to_all")
private Boolean openToAll = false;
@Column(name = "max_participants")
private Integer maxParticipants;
@Column(name = "current_participants")
private Integer currentParticipants = 0;
@Embedded
private ProposerInfo proposer;
@Convert(converter = JsonConverter.class)
@Column(name = "day_configs", columnDefinition = "TEXT")
private List<Object> dayConfigs;
@Convert(converter = JsonConverter.class)
@Column(name = "dept_limits", columnDefinition = "TEXT")
private Map<String, Object> deptLimits;
@Convert(converter = JsonConverter.class)
@Column(name = "dept_section_limits", columnDefinition = "TEXT")
private Map<String, Object> deptSectionLimits;
@Column(name = "refreshment_expense")
private Double refreshmentExpense = 0.0;
@Column(name = "transportation_expense")
private Double transportationExpense = 0.0;
@Column(name = "session_coverage_fee")
private Double sessionCoverageFee = 0.0;
@Column(name = "total_expense")
private Double totalExpense = 0.0;
@Column(name = "duration_days")
private Integer durationDays = 1;
@Column(name = "rejection_reason")
private String rejectionReason;
@Column(name = "conflict_message")
private String conflictMessage;
public Event() {}
public Event(Long id, String title, String description, String startDate, String endDate, String location, String category, String type, String institution, String department, List<String> academicYears, String status, String guestName, String guestSocialProfile, List<String> requirements, List<String> targetedSections, List<String> targetDepartments, String groupRequestId, List<String> sponsors, Double budget, Boolean hasRegistrationFee, Double registrationFee, String paymentLink, String centreName, Boolean isPublicEvent, String image, String targetedBatch, Boolean openToAll, Integer maxParticipants, Integer currentParticipants, ProposerInfo proposer, List<Object> dayConfigs, Map<String, Object> deptLimits, Map<String, Object> deptSectionLimits, Double refreshmentExpense, Double transportationExpense, Double sessionCoverageFee, Double totalExpense, Integer durationDays, String rejectionReason, String conflictMessage) {
this.id = id;
this.title = title;
this.description = description;
this.startDate = startDate;
this.endDate = endDate;
this.location = location;
this.category = category;
this.type = type;
this.institution = institution;
this.department = department;
this.academicYears = academicYears;
this.status = status;
this.guestName = guestName;
this.guestSocialProfile = guestSocialProfile;
this.requirements = requirements;
this.targetedSections = targetedSections;
this.targetDepartments = targetDepartments;
this.groupRequestId = groupRequestId;
this.sponsors = sponsors;
this.budget = budget;
this.hasRegistrationFee = hasRegistrationFee;
this.registrationFee = registrationFee;
this.paymentLink = paymentLink;
this.centreName = centreName;
this.isPublicEvent = isPublicEvent;
this.image = image;
this.targetedBatch = targetedBatch;
this.openToAll = openToAll;
this.maxParticipants = maxParticipants;
this.currentParticipants = currentParticipants;
this.proposer = proposer;
this.dayConfigs = dayConfigs;
this.deptLimits = deptLimits;
this.deptSectionLimits = deptSectionLimits;
this.refreshmentExpense = refreshmentExpense;
this.transportationExpense = transportationExpense;
this.sessionCoverageFee = sessionCoverageFee;
this.totalExpense = totalExpense;
this.durationDays = durationDays;
this.rejectionReason = rejectionReason;
this.conflictMessage = conflictMessage;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getStartDate() { return startDate; }
public void setStartDate(String startDate) { this.startDate = startDate; }
public String getEndDate() { return endDate; }
public void setEndDate(String endDate) { this.endDate = endDate; }
public String getLocation() { return location; }
public void setLocation(String location) { this.location = location; }
public String getCategory() { return category; }
public void setCategory(String category) { this.category = category; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getInstitution() { return institution; }
public void setInstitution(String institution) { this.institution = institution; }
public String getDepartment() { return department; }
public void setDepartment(String department) { this.department = department; }
public List<String> getAcademicYears() { return academicYears; }
public void setAcademicYears(List<String> academicYears) { this.academicYears = academicYears; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getGuestName() { return guestName; }
public void setGuestName(String guestName) { this.guestName = guestName; }
public String getGuestSocialProfile() { return guestSocialProfile; }
public void setGuestSocialProfile(String guestSocialProfile) { this.guestSocialProfile = guestSocialProfile; }
public List<String> getRequirements() { return requirements; }
public void setRequirements(List<String> requirements) { this.requirements = requirements; }
public List<String> getTargetedSections() { return targetedSections; }
public void setTargetedSections(List<String> targetedSections) { this.targetedSections = targetedSections; }
public List<String> getTargetDepartments() { return targetDepartments; }
public void setTargetDepartments(List<String> targetDepartments) { this.targetDepartments = targetDepartments; }
public String getGroupRequestId() { return groupRequestId; }
public void setGroupRequestId(String groupRequestId) { this.groupRequestId = groupRequestId; }
public List<String> getSponsors() { return sponsors; }
public void setSponsors(List<String> sponsors) { this.sponsors = sponsors; }
public Double getBudget() { return budget; }
public void setBudget(Double budget) { this.budget = budget; }
public Boolean getHasRegistrationFee() { return hasRegistrationFee; }
public void setHasRegistrationFee(Boolean hasRegistrationFee) { this.hasRegistrationFee = hasRegistrationFee; }
public Double getRegistrationFee() { return registrationFee; }
public void setRegistrationFee(Double registrationFee) { this.registrationFee = registrationFee; }
public String getPaymentLink() { return paymentLink; }
public void setPaymentLink(String paymentLink) { this.paymentLink = paymentLink; }
public String getCentreName() { return centreName; }
public void setCentreName(String centreName) { this.centreName = centreName; }
public Boolean getIsPublicEvent() { return isPublicEvent; }
public void setIsPublicEvent(Boolean isPublicEvent) { this.isPublicEvent = isPublicEvent; }
public String getImage() { return image; }
public void setImage(String image) { this.image = image; }
public String getTargetedBatch() { return targetedBatch; }
public void setTargetedBatch(String targetedBatch) { this.targetedBatch = targetedBatch; }
public Boolean getOpenToAll() { return openToAll; }
public void setOpenToAll(Boolean openToAll) { this.openToAll = openToAll; }
public Integer getMaxParticipants() { return maxParticipants; }
public void setMaxParticipants(Integer maxParticipants) { this.maxParticipants = maxParticipants; }
public Integer getCurrentParticipants() { return currentParticipants; }
public void setCurrentParticipants(Integer currentParticipants) { this.currentParticipants = currentParticipants; }
public ProposerInfo getProposer() { return proposer; }
public void setProposer(ProposerInfo proposer) { this.proposer = proposer; }
public List<Object> getDayConfigs() { return dayConfigs; }
public void setDayConfigs(List<Object> dayConfigs) { this.dayConfigs = dayConfigs; }
public Map<String, Object> getDeptLimits() { return deptLimits; }
public void setDeptLimits(Map<String, Object> deptLimits) { this.deptLimits = deptLimits; }
public Map<String, Object> getDeptSectionLimits() { return deptSectionLimits; }
public void setDeptSectionLimits(Map<String, Object> deptSectionLimits) { this.deptSectionLimits = deptSectionLimits; }
public Double getRefreshmentExpense() { return refreshmentExpense; }
public void setRefreshmentExpense(Double refreshmentExpense) { this.refreshmentExpense = refreshmentExpense; }
public Double getTransportationExpense() { return transportationExpense; }
public void setTransportationExpense(Double transportationExpense) { this.transportationExpense = transportationExpense; }
public Double getSessionCoverageFee() { return sessionCoverageFee; }
public void setSessionCoverageFee(Double sessionCoverageFee) { this.sessionCoverageFee = sessionCoverageFee; }
public Double getTotalExpense() { return totalExpense; }
public void setTotalExpense(Double totalExpense) { this.totalExpense = totalExpense; }
public Integer getDurationDays() { return durationDays; }
public void setDurationDays(Integer durationDays) { this.durationDays = durationDays; }
public String getRejectionReason() { return rejectionReason; }
public void setRejectionReason(String rejectionReason) { this.rejectionReason = rejectionReason; }
public String getConflictMessage() { return conflictMessage; }
public void setConflictMessage(String conflictMessage) { this.conflictMessage = conflictMessage; }
@Embeddable
public static class ProposerInfo {
@Column(name = "proposer_id")
private Long id;
@Column(name = "proposer_full_name")
private String fullName;
@Column(name = "proposer_email")
private String email;
@Column(name = "proposer_role")
private String role;
@Column(name = "proposer_department")
private String department;
public ProposerInfo() {}
public ProposerInfo(Long id, String fullName, String email, String role, String department) {
this.id = id;
this.fullName = fullName;
this.email = email;
this.role = role;
this.department = department;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getFullName() { return fullName; }
public void setFullName(String fullName) { this.fullName = fullName; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public String getDepartment() { return department; }
public void setDepartment(String department) { this.department = department; }
}
}

View File

@@ -0,0 +1,37 @@
package com.rit.ems.model;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "ems_institutional_events")
public class InstitutionalEvent {
@Id
private String id;
private String name;
private String month;
private String semester;
public InstitutionalEvent() {}
public InstitutionalEvent(String id, String name, String month, String semester) {
this.id = id;
this.name = name;
this.month = month;
this.semester = semester;
}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getMonth() { return month; }
public void setMonth(String month) { this.month = month; }
public String getSemester() { return semester; }
public void setSemester(String semester) { this.semester = semester; }
}

View File

@@ -0,0 +1,48 @@
package com.rit.ems.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "ems_notes")
public class Note {
@Id
private Long id;
private String title;
@Column(columnDefinition = "TEXT")
private String content;
@Column(name = "created_at")
private String createdAt;
public Note() {}
public Note(Long id, String title, String content, String createdAt) {
this.id = id;
this.title = title;
this.content = content;
this.createdAt = createdAt;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public String getCreatedAt() { return createdAt; }
public void setCreatedAt(String createdAt) { this.createdAt = createdAt; }
@JsonProperty("created_at")
public String getCreatedAtSnake() { return createdAt; }
@JsonProperty("created_at")
public void setCreatedAtSnake(String createdAt) { this.createdAt = createdAt; }
}

View File

@@ -0,0 +1,222 @@
package com.rit.ems.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.*;
@Entity
@Table(name = "ems_registrations")
public class Registration {
@Id
private String id; // format: userId_eventId
@Column(name = "user_id")
private Long userId;
@Column(name = "event_id")
private Long eventId;
@Column(name = "payment_status")
private String paymentStatus;
@Column(name = "certification_status")
private String certificationStatus = "NOT_SUBMITTED";
@Column(name = "registered_at")
private String registeredAt;
@Column(name = "team_code")
private String teamCode;
@Column(name = "team_name")
private String teamName;
@Column(name = "is_team_leader")
private Boolean isTeamLeader = false;
@Column(name = "certification_url", columnDefinition = "TEXT")
private String certificationUrl;
@Column(name = "od_url", columnDefinition = "TEXT")
private String odUrl;
@Column(name = "ticket_id")
private String ticketId;
@Column(name = "ticket_qrcode", columnDefinition = "TEXT")
private String ticketQrcode;
// Student profile details captured at registration time
private String name;
private String email;
private String dept;
private String department;
private String section;
private String year;
@Column(name = "reg_no")
private String regNo;
private String phone;
private String gender;
@Column(name = "college_name")
private String collegeName;
public Registration() {}
public Registration(String id, Long userId, Long eventId, String paymentStatus, String certificationStatus, String registeredAt, String teamCode, String teamName, Boolean isTeamLeader, String certificationUrl, String odUrl, String ticketId, String ticketQrcode, String name, String email, String dept, String department, String section, String year, String regNo, String phone, String gender, String collegeName) {
this.id = id;
this.userId = userId;
this.eventId = eventId;
this.paymentStatus = paymentStatus;
this.certificationStatus = certificationStatus;
this.registeredAt = registeredAt;
this.teamCode = teamCode;
this.teamName = teamName;
this.isTeamLeader = isTeamLeader;
this.certificationUrl = certificationUrl;
this.odUrl = odUrl;
this.ticketId = ticketId;
this.ticketQrcode = ticketQrcode;
this.name = name;
this.email = email;
this.dept = dept;
this.department = department;
this.section = section;
this.year = year;
this.regNo = regNo;
this.phone = phone;
this.gender = gender;
this.collegeName = collegeName;
}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public Long getEventId() { return eventId; }
public void setEventId(Long eventId) { this.eventId = eventId; }
public String getPaymentStatus() { return paymentStatus; }
public void setPaymentStatus(String paymentStatus) { this.paymentStatus = paymentStatus; }
public String getCertificationStatus() { return certificationStatus; }
public void setCertificationStatus(String certificationStatus) { this.certificationStatus = certificationStatus; }
public String getRegisteredAt() { return registeredAt; }
public void setRegisteredAt(String registeredAt) { this.registeredAt = registeredAt; }
public String getTeamCode() { return teamCode; }
public void setTeamCode(String teamCode) { this.teamCode = teamCode; }
public String getTeamName() { return teamName; }
public void setTeamName(String teamName) { this.teamName = teamName; }
public Boolean getIsTeamLeader() { return isTeamLeader; }
public void setIsTeamLeader(Boolean isTeamLeader) { this.isTeamLeader = isTeamLeader; }
public String getCertificationUrl() { return certificationUrl; }
public void setCertificationUrl(String certificationUrl) { this.certificationUrl = certificationUrl; }
public String getOdUrl() { return odUrl; }
public void setOdUrl(String odUrl) { this.odUrl = odUrl; }
public String getTicketId() { return ticketId; }
public void setTicketId(String ticketId) { this.ticketId = ticketId; }
public String getTicketQrcode() { return ticketQrcode; }
public void setTicketQrcode(String ticketQrcode) { this.ticketQrcode = ticketQrcode; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getDept() { return dept; }
public void setDept(String dept) { this.dept = dept; }
public String getDepartment() { return department; }
public void setDepartment(String department) { this.department = department; }
public String getSection() { return section; }
public void setSection(String section) { this.section = section; }
public String getYear() { return year; }
public void setYear(String year) { this.year = year; }
public String getRegNo() { return regNo; }
public void setRegNo(String regNo) { this.regNo = regNo; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getGender() { return gender; }
public void setGender(String gender) { this.gender = gender; }
public String getCollegeName() { return collegeName; }
public void setCollegeName(String collegeName) { this.collegeName = collegeName; }
// Getters and setters to bridge snake_case from frontend
@JsonProperty("user_id")
public Long getUserIdSnake() { return userId; }
@JsonProperty("user_id")
public void setUserIdSnake(Long userId) { this.userId = userId; }
@JsonProperty("event_id")
public Long getEventIdSnake() { return eventId; }
@JsonProperty("event_id")
public void setEventIdSnake(Long eventId) { this.eventId = eventId; }
@JsonProperty("payment_status")
public String getPaymentStatusSnake() { return paymentStatus; }
@JsonProperty("payment_status")
public void setPaymentStatusSnake(String paymentStatus) { this.paymentStatus = paymentStatus; }
@JsonProperty("certification_status")
public String getCertificationStatusSnake() { return certificationStatus; }
@JsonProperty("certification_status")
public void setCertificationStatusSnake(String certificationStatus) { this.certificationStatus = certificationStatus; }
@JsonProperty("registered_at")
public String getRegisteredAtSnake() { return registeredAt; }
@JsonProperty("registered_at")
public void setRegisteredAtSnake(String registeredAt) { this.registeredAt = registeredAt; }
@JsonProperty("team_code")
public String getTeamCodeSnake() { return teamCode; }
@JsonProperty("team_code")
public void setTeamCodeSnake(String teamCode) { this.teamCode = teamCode; }
@JsonProperty("team_name")
public String getTeamNameSnake() { return teamName; }
@JsonProperty("team_name")
public void setTeamNameSnake(String teamName) { this.teamName = teamName; }
@JsonProperty("is_team_leader")
public Boolean getIsTeamLeaderSnake() { return isTeamLeader; }
@JsonProperty("is_team_leader")
public void setIsTeamLeaderSnake(Boolean isTeamLeader) { this.isTeamLeader = isTeamLeader; }
@JsonProperty("certification_url")
public String getCertificationUrlSnake() { return certificationUrl; }
@JsonProperty("certification_url")
public void setCertificationUrlSnake(String certificationUrl) { this.certificationUrl = certificationUrl; }
@JsonProperty("od_url")
public String getOdUrlSnake() { return odUrl; }
@JsonProperty("od_url")
public void setOdUrlSnake(String odUrl) { this.odUrl = odUrl; }
@JsonProperty("ticket_id")
public String getTicketIdSnake() { return ticketId; }
@JsonProperty("ticket_id")
public void setTicketIdSnake(String ticketId) { this.ticketId = ticketId; }
@JsonProperty("ticket_qrcode")
public String getTicketQrcodeSnake() { return ticketQrcode; }
@JsonProperty("ticket_qrcode")
public void setTicketQrcodeSnake(String ticketQrcode) { this.ticketQrcode = ticketQrcode; }
}

View File

@@ -0,0 +1,28 @@
package com.rit.ems.model;
import com.rit.ems.util.JsonConverter;
import jakarta.persistence.*;
@Entity
@Table(name = "ems_settings")
public class Setting {
@Id
private String id; // key
@Convert(converter = JsonConverter.class)
@Column(name = "setting_value", columnDefinition = "TEXT")
private Object value;
public Setting() {}
public Setting(String id, Object value) {
this.id = id;
this.value = value;
}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public Object getValue() { return value; }
public void setValue(Object value) { this.value = value; }
}

View File

@@ -0,0 +1,92 @@
package com.rit.ems.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "ems_special_events")
public class SpecialEvent {
@Id
private String id;
private String name;
@Column(columnDefinition = "TEXT")
private String description;
private String location;
@Column(name = "start_date")
private String startDate;
@Column(name = "end_date")
private String endDate;
@Column(name = "created_at")
private String createdAt;
@Column(name = "is_active")
private Boolean isActive = true;
@Column(name = "verification_status")
private String verificationStatus = "APPROVED";
public SpecialEvent() {}
public SpecialEvent(String id, String name, String description, String location, String startDate, String endDate, String createdAt, Boolean isActive, String verificationStatus) {
this.id = id;
this.name = name;
this.description = description;
this.location = location;
this.startDate = startDate;
this.endDate = endDate;
this.createdAt = createdAt;
this.isActive = isActive;
this.verificationStatus = verificationStatus;
}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getLocation() { return location; }
public void setLocation(String location) { this.location = location; }
public String getStartDate() { return startDate; }
public void setStartDate(String startDate) { this.startDate = startDate; }
public String getEndDate() { return endDate; }
public void setEndDate(String endDate) { this.endDate = endDate; }
public String getCreatedAt() { return createdAt; }
public void setCreatedAt(String createdAt) { this.createdAt = createdAt; }
public Boolean getIsActive() { return isActive; }
public void setIsActive(Boolean isActive) { this.isActive = isActive; }
public String getVerificationStatus() { return verificationStatus; }
public void setVerificationStatus(String verificationStatus) { this.verificationStatus = verificationStatus; }
@JsonProperty("created_at")
public String getCreatedAtSnake() { return createdAt; }
@JsonProperty("created_at")
public void setCreatedAtSnake(String createdAt) { this.createdAt = createdAt; }
@JsonProperty("is_active")
public Boolean getIsActiveSnake() { return isActive; }
@JsonProperty("is_active")
public void setIsActiveSnake(Boolean isActive) { this.isActive = isActive; }
@JsonProperty("verification_status")
public String getVerificationStatusSnake() { return verificationStatus; }
@JsonProperty("verification_status")
public void setVerificationStatusSnake(String verificationStatus) { this.verificationStatus = verificationStatus; }
}

View File

@@ -0,0 +1,147 @@
package com.rit.ems.model;
import jakarta.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "ems_users")
public class User {
@Id
private Long id;
@Column(unique = true, nullable = false)
private String email;
private String password;
@Column(name = "full_name")
private String fullName;
private String role;
private String department;
@Column(name = "is_club_coordinator")
private Boolean isClubCoordinator = false;
@Column(name = "is_placement_staff")
private Boolean isPlacementStaff = false;
@Column(name = "is_class_incharge")
private Boolean isClassIncharge = false;
@Column(name = "class_strength")
private Integer classStrength = 0;
@Column(name = "incharge_class")
private String inchargeClass;
@Column(name = "incharge_batch")
private String inchargeBatch;
@Column(name = "incharge_section")
private String inchargeSection;
private String year;
private String section;
@Column(name = "reg_no")
private String regNo;
private String phone;
private String gender;
@Column(name = "college_name")
private String collegeName;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "ems_user_assigned_clubs", joinColumns = @JoinColumn(name = "user_id"))
@Column(name = "club_name")
private List<String> assignedClubs = new ArrayList<>();
public User() {}
public User(Long id, String email, String password, String fullName, String role, String department, Boolean isClubCoordinator, Boolean isPlacementStaff, Boolean isClassIncharge, Integer classStrength, String inchargeClass, String inchargeBatch, String inchargeSection, String year, String section, String regNo, String phone, String gender, String collegeName, List<String> assignedClubs) {
this.id = id;
this.email = email;
this.password = password;
this.fullName = fullName;
this.role = role;
this.department = department;
this.isClubCoordinator = isClubCoordinator;
this.isPlacementStaff = isPlacementStaff;
this.isClassIncharge = isClassIncharge;
this.classStrength = classStrength;
this.inchargeClass = inchargeClass;
this.inchargeBatch = inchargeBatch;
this.inchargeSection = inchargeSection;
this.year = year;
this.section = section;
this.regNo = regNo;
this.phone = phone;
this.gender = gender;
this.collegeName = collegeName;
this.assignedClubs = assignedClubs;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getFullName() { return fullName; }
public void setFullName(String fullName) { this.fullName = fullName; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
public String getDepartment() { return department; }
public void setDepartment(String department) { this.department = department; }
public Boolean getIsClubCoordinator() { return isClubCoordinator; }
public void setIsClubCoordinator(Boolean isClubCoordinator) { this.isClubCoordinator = isClubCoordinator; }
public Boolean getIsPlacementStaff() { return isPlacementStaff; }
public void setIsPlacementStaff(Boolean isPlacementStaff) { this.isPlacementStaff = isPlacementStaff; }
public Boolean getIsClassIncharge() { return isClassIncharge; }
public void setIsClassIncharge(Boolean isClassIncharge) { this.isClassIncharge = isClassIncharge; }
public Integer getClassStrength() { return classStrength; }
public void setClassStrength(Integer classStrength) { this.classStrength = classStrength; }
public String getInchargeClass() { return inchargeClass; }
public void setInchargeClass(String inchargeClass) { this.inchargeClass = inchargeClass; }
public String getInchargeBatch() { return inchargeBatch; }
public void setInchargeBatch(String inchargeBatch) { this.inchargeBatch = inchargeBatch; }
public String getInchargeSection() { return inchargeSection; }
public void setInchargeSection(String inchargeSection) { this.inchargeSection = inchargeSection; }
public String getYear() { return year; }
public void setYear(String year) { this.year = year; }
public String getSection() { return section; }
public void setSection(String section) { this.section = section; }
public String getRegNo() { return regNo; }
public void setRegNo(String regNo) { this.regNo = regNo; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
public String getGender() { return gender; }
public void setGender(String gender) { this.gender = gender; }
public String getCollegeName() { return collegeName; }
public void setCollegeName(String collegeName) { this.collegeName = collegeName; }
public List<String> getAssignedClubs() { return assignedClubs; }
public void setAssignedClubs(List<String> assignedClubs) { this.assignedClubs = assignedClubs; }
}

View File

@@ -0,0 +1,32 @@
package com.rit.ems.model;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "ems_venues")
public class Venue {
@Id
private String id;
private String name;
private Integer capacity;
public Venue() {}
public Venue(String id, String name, Integer capacity) {
this.id = id;
this.name = name;
this.capacity = capacity;
}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getCapacity() { return capacity; }
public void setCapacity(Integer capacity) { this.capacity = capacity; }
}

View File

@@ -0,0 +1,7 @@
package com.rit.ems.repository;
import com.rit.ems.model.Announcement;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AnnouncementRepository extends JpaRepository<Announcement, String> {
}

View File

@@ -0,0 +1,10 @@
package com.rit.ems.repository;
import com.rit.ems.model.Attendance;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface AttendanceRepository extends JpaRepository<Attendance, String> {
List<Attendance> findByRegistrationId(String registrationId);
List<Attendance> findByEventId(Long eventId);
}

View File

@@ -0,0 +1,7 @@
package com.rit.ems.repository;
import com.rit.ems.model.Batch;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BatchRepository extends JpaRepository<Batch, Long> {
}

View File

@@ -0,0 +1,7 @@
package com.rit.ems.repository;
import com.rit.ems.model.ClassMapping;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ClassMappingRepository extends JpaRepository<ClassMapping, Long> {
}

View File

@@ -0,0 +1,7 @@
package com.rit.ems.repository;
import com.rit.ems.model.Event;
import org.springframework.data.jpa.repository.JpaRepository;
public interface EventRepository extends JpaRepository<Event, Long> {
}

View File

@@ -0,0 +1,7 @@
package com.rit.ems.repository;
import com.rit.ems.model.InstitutionalEvent;
import org.springframework.data.jpa.repository.JpaRepository;
public interface InstitutionalEventRepository extends JpaRepository<InstitutionalEvent, String> {
}

View File

@@ -0,0 +1,7 @@
package com.rit.ems.repository;
import com.rit.ems.model.Note;
import org.springframework.data.jpa.repository.JpaRepository;
public interface NoteRepository extends JpaRepository<Note, Long> {
}

View File

@@ -0,0 +1,13 @@
package com.rit.ems.repository;
import com.rit.ems.model.Registration;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface RegistrationRepository extends JpaRepository<Registration, String> {
List<Registration> findByUserId(Long userId);
List<Registration> findByEventId(Long eventId);
List<Registration> findByTeamCode(String teamCode);
Optional<Registration> findByUserIdAndEventId(Long userId, Long eventId);
}

View File

@@ -0,0 +1,7 @@
package com.rit.ems.repository;
import com.rit.ems.model.Setting;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SettingRepository extends JpaRepository<Setting, String> {
}

View File

@@ -0,0 +1,7 @@
package com.rit.ems.repository;
import com.rit.ems.model.SpecialEvent;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SpecialEventRepository extends JpaRepository<SpecialEvent, String> {
}

View File

@@ -0,0 +1,9 @@
package com.rit.ems.repository;
import com.rit.ems.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmailIgnoreCase(String email);
}

View File

@@ -0,0 +1,7 @@
package com.rit.ems.repository;
import com.rit.ems.model.Venue;
import org.springframework.data.jpa.repository.JpaRepository;
public interface VenueRepository extends JpaRepository<Venue, String> {
}

View File

@@ -0,0 +1,35 @@
package com.rit.ems.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
@Converter
public class JsonConverter implements AttributeConverter<Object, String> {
private static final ObjectMapper objectMapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(Object attribute) {
if (attribute == null) {
return null;
}
try {
return objectMapper.writeValueAsString(attribute);
} catch (JsonProcessingException e) {
throw new RuntimeException("Error converting object to JSON string", e);
}
}
@Override
public Object convertToEntityAttribute(String dbData) {
if (dbData == null || dbData.isEmpty()) {
return null;
}
try {
return objectMapper.readValue(dbData, Object.class);
} catch (JsonProcessingException e) {
throw new RuntimeException("Error converting JSON string to object", e);
}
}
}

View File

@@ -0,0 +1,17 @@
spring.application.name=ems-backend
server.port=8081
# Database Configuration
spring.datasource.url=jdbc:postgresql://localhost:5432/rit_ems
spring.datasource.username=postgres
spring.datasource.password=RITHosting123
spring.datasource.driver-class-name=org.postgresql.Driver
# JPA/Hibernate Config
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
# Max upload sizes for files/images (if any)
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

19
firebase.json Normal file
View File

@@ -0,0 +1,19 @@
{
"firestore": {
"rules": "firestore.rules"
},
"hosting": {
"public": "frontend/dist",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}

8
firestore.rules Normal file
View File

@@ -0,0 +1,8 @@
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}

View File

@@ -29,7 +29,7 @@
"@types/node": "^24.12.2",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"@vitejs/plugin-react": "^6.0.5",
"autoprefixer": "^10.5.0",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.1.1",
@@ -1598,9 +1598,9 @@
}
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-rc.7",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz",
"integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
"dev": true,
"license": "MIT"
},
@@ -2154,16 +2154,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"version": "5.0.9",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
"node": "20 || >=22"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
@@ -2251,13 +2251,13 @@
}
},
"node_modules/@vitejs/plugin-react": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz",
"integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==",
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.5.tgz",
"integrity": "sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@rolldown/pluginutils": "1.0.0-rc.7"
"@rolldown/pluginutils": "^1.0.1"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
@@ -2423,9 +2423,9 @@
}
},
"node_modules/brace-expansion": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
"integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
"version": "1.1.18",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2713,9 +2713,9 @@
}
},
"node_modules/dompurify": {
"version": "3.4.11",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz",
"integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==",
"version": "3.4.13",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.13.tgz",
"integrity": "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optional": true,
"optionalDependencies": {
@@ -3385,9 +3385,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"dev": true,
"funding": [
{
@@ -3856,9 +3856,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.15",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
"integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
"version": "3.3.17",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz",
"integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==",
"dev": true,
"funding": [
{
@@ -4005,9 +4005,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.15",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
"version": "8.5.25",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz",
"integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==",
"dev": true,
"funding": [
{
@@ -4025,7 +4025,7 @@
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.12",
"nanoid": "^3.3.16",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
@@ -4051,9 +4051,9 @@
}
},
"node_modules/protobufjs": {
"version": "7.6.4",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz",
"integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==",
"version": "7.6.5",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz",
"integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==",
"hasInstallScript": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -4190,13 +4190,6 @@
"@rolldown/binding-win32-x64-msvc": "1.0.3"
}
},
"node_modules/rolldown/node_modules/@rolldown/pluginutils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
"dev": true,
"license": "MIT"
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",

View File

@@ -31,7 +31,7 @@
"@types/node": "^24.12.2",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"@vitejs/plugin-react": "^6.0.5",
"autoprefixer": "^10.5.0",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.1.1",

View File

@@ -13,7 +13,6 @@ import { DialogProvider } from './context/DialogContext';
import { motion, AnimatePresence } from 'framer-motion';
import { StatusTimeline, type Event } from './components/dashboard/EventStatusTimeline';
import { useEffect } from 'react';
import ritLogo from './assets/images/college-logo.png';
import { InstitutionalCalendar } from './components/dashboard/InstitutionalCalendar';
import { VenueTimeline } from './components/dashboard/VenueTimeline';
@@ -28,56 +27,6 @@ import { ManageNoticesView } from './components/dashboard/ManageNoticesView';
import { ManageStudentsView } from './components/dashboard/ManageStudentsView';
import { VenueManagement } from './components/dashboard/VenueManagement';
const LoginTransitionScreen: React.FC<{ user: any }> = ({ user }) => {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.6, ease: "easeInOut" }}
className="fixed inset-0 z-[300] bg-gradient-to-br from-[#003B5C] via-[#004a99] to-[#001D3D] flex flex-col items-center justify-center text-white"
>
<div className="flex flex-col items-center gap-6 max-w-sm text-center px-6">
<motion.div
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ delay: 0.2, duration: 0.6, type: "spring" }}
className="bg-white/95 p-4 rounded-3xl shadow-2xl shadow-blue-900/30"
>
<img src={ritLogo} alt="RIT Logo" className="h-16 w-auto object-contain" />
</motion.div>
<div className="space-y-2 mt-4 animate-pulse">
<motion.h2
initial={{ y: 15, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ delay: 0.4, duration: 0.5 }}
className="text-2xl font-black uppercase tracking-tight"
>
Welcome Back
</motion.h2>
<motion.p
initial={{ y: 10, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ delay: 0.6, duration: 0.5 }}
className="text-sm font-bold text-slate-300 uppercase tracking-widest"
>
{user?.fullName || "User"}
</motion.p>
</div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: [0, 1, 0] }}
transition={{ delay: 0.8, duration: 1.5, repeat: Infinity }}
className="text-[9px] font-black uppercase tracking-widest text-[#f97316] mt-8"
>
Connecting to Event Hub...
</motion.div>
</div>
</motion.div>
);
};
const AppContent: React.FC = () => {
const { isAuthenticated, user } = useAuth();
@@ -85,22 +34,6 @@ const AppContent: React.FC = () => {
const [activeItem, setActiveItem] = useState('dashboard');
const [userEvents, setUserEvents] = useState<Event[]>([]);
const [preFillData, setPreFillData] = useState<any>(null);
const [showTransition, setShowTransition] = useState(false);
const [cachedUser, setCachedUser] = useState<any>(null);
useEffect(() => {
if (isAuthenticated && user) {
setCachedUser(user);
setShowTransition(true);
const timer = setTimeout(() => {
setShowTransition(false);
}, 2000);
return () => clearTimeout(timer);
} else {
setShowTransition(false);
setCachedUser(null);
}
}, [isAuthenticated, user]);
const handleIncompleteClick = (data: any) => {
setPreFillData(data);
@@ -168,6 +101,38 @@ const AppContent: React.FC = () => {
}
}, [isAuthenticated, activeItem, user?.email, user?.role, user?.department]);
if (!isAuthenticated) {
return (
<AnimatePresence mode="wait">
<motion.div
key="login"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.5 }}
>
<LoginPage />
</motion.div>
</AnimatePresence>
);
}
if (user?.role === 'STUDENT') {
return (
<AnimatePresence mode="wait">
<motion.div
key="student-dashboard"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.5 }}
>
<StudentDashboard />
</motion.div>
</AnimatePresence>
);
}
const renderContent = () => {
const isDashboard = (
<Overview
@@ -217,16 +182,6 @@ const AppContent: React.FC = () => {
};
return (
<>
<AnimatePresence>
{showTransition && <LoginTransitionScreen user={cachedUser} />}
</AnimatePresence>
{!isAuthenticated ? (
<LoginPage />
) : user?.role === 'STUDENT' ? (
<StudentDashboard />
) : (
<DashboardLayout
activeItem={activeItem}
onItemClick={handleSidebarClick}
@@ -243,8 +198,6 @@ const AppContent: React.FC = () => {
</motion.div>
</AnimatePresence>
</DashboardLayout>
)}
</>
);
};

View File

@@ -1,6 +1,6 @@
import { API_BASE_URL } from '../../lib/config';
import React, { useEffect, useState } from 'react';
import { motion } from 'framer-motion';
import { motion, AnimatePresence } from 'framer-motion';
import {
Calendar,
MapPin,
@@ -8,22 +8,46 @@ import {
Filter,
Search,
Trash2,
FileSpreadsheet
FileSpreadsheet,
FileText,
Users,
Ticket,
Wallet,
Heart,
XCircle,
AlertTriangle,
Split,
Layers,
CheckCircle
} from 'lucide-react';
import { cn } from '../../lib/utils';
import { useAuth } from '../../context/AuthContext';
import { StatusTimeline, type Event } from './EventStatusTimeline';
import { Pagination } from './Pagination';
interface ExtendedEvent extends Event {
description?: string;
sponsors?: string[];
academicYears?: string[];
dayConfigs?: any[];
deptLimits?: Record<string, any>;
deptSectionLimits?: Record<string, any>;
budget?: number;
hasRegistrationFee?: boolean;
registrationFee?: number;
conflictMessage?: string;
}
interface AllEventsProps {
onEditEvent?: (event: any) => void;
}
export const AllEvents: React.FC<AllEventsProps> = ({ onEditEvent }) => {
const { user } = useAuth();
const [events, setEvents] = useState<Event[]>([]);
const [events, setEvents] = useState<ExtendedEvent[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [searchTerm, setSearchTerm] = useState('');
const [selectedEvent, setSelectedEvent] = useState<ExtendedEvent | null>(null);
useEffect(() => {
fetchEvents();
@@ -78,7 +102,6 @@ export const AllEvents: React.FC<AllEventsProps> = ({ onEditEvent }) => {
}
};
const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(10);
@@ -209,6 +232,12 @@ export const AllEvents: React.FC<AllEventsProps> = ({ onEditEvent }) => {
</td>
<td className="px-8 py-6 text-right">
<div className="flex items-center justify-end gap-2">
<button
onClick={() => setSelectedEvent(event)}
className="px-4 py-2 bg-slate-50 text-brand-indigo hover:bg-brand-indigo hover:text-white rounded-xl text-[9px] font-black uppercase tracking-widest transition-all border border-brand-indigo/10"
>
View
</button>
{(user?.role === 'ADMIN' || (event.status === 'REQUESTED' && event.proposer?.email === user?.email)) && onEditEvent && (
<button
onClick={() => onEditEvent(event)}
@@ -248,6 +277,262 @@ export const AllEvents: React.FC<AllEventsProps> = ({ onEditEvent }) => {
</>
)}
</div>
{/* Details Modal */}
<AnimatePresence>
{selectedEvent && (
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setSelectedEvent(null)}
className="absolute inset-0 bg-slate-900/40 backdrop-blur-sm"
/>
<motion.div
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
className="relative w-full max-w-2xl bg-white rounded-[2.5rem] premium-shadow overflow-hidden max-h-[90vh] overflow-y-auto"
>
<div className={cn(
"p-8 text-white flex justify-between items-start",
selectedEvent.status === 'CANCELLED' ? "bg-red-600" : "bg-brand-navy"
)}>
<div>
<div className="flex items-center gap-2 mb-2">
<span className="text-[10px] font-black uppercase tracking-[0.2em] text-white/60">Event Details</span>
<span className="w-1 h-1 bg-white/30 rounded-full" />
<span className="text-[10px] font-black uppercase tracking-[0.2em] text-white/60">{selectedEvent.institution}</span>
{selectedEvent.category === 'CLUB' && (
<>
<span className="w-1 h-1 bg-white/30 rounded-full" />
<span className="text-[10px] font-black uppercase tracking-[0.2em] text-emerald-400">Institutional Club Event</span>
</>
)}
</div>
<h3 className="text-2xl font-black tracking-tight">{selectedEvent.title}</h3>
</div>
<button
onClick={() => setSelectedEvent(null)}
className="p-2 hover:bg-white/10 rounded-xl transition-all"
>
<XCircle className="w-6 h-6" />
</button>
</div>
<div className="p-8 space-y-8">
{/* Description Section */}
{selectedEvent.description && (
<div className="space-y-2">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<FileText className="w-3.5 h-3.5" />
Event Description
</div>
<p className="text-sm font-medium text-text-dark leading-relaxed bg-slate-50 p-4 rounded-2xl border border-slate-100">
{selectedEvent.description}
</p>
</div>
)}
<div className="grid grid-cols-2 gap-8">
<div className="space-y-6">
<div className="space-y-1">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<Calendar className="w-3.5 h-3.5" />
Schedule
</div>
<p className="text-sm font-bold text-text-dark">
{parseDate(selectedEvent.startDate) ? parseDate(selectedEvent.startDate)!.toLocaleString('en-IN') : 'N/A'}
</p>
</div>
<div className="space-y-1">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<MapPin className="w-3.5 h-3.5" />
Venue
</div>
<p className="text-sm font-bold text-text-dark">{selectedEvent.location}</p>
</div>
</div>
<div className="space-y-6">
<div className="space-y-1">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<Users className="w-3.5 h-3.5" />
Proposed By
</div>
<p className="text-sm font-bold text-text-dark">{selectedEvent.proposer?.fullName || 'Faculty'}</p>
<p className="text-[10px] font-black text-brand-indigo uppercase">{selectedEvent.department}</p>
</div>
{selectedEvent.academicYears && selectedEvent.academicYears.length > 0 && (
<div className="space-y-1">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<Users className="w-3.5 h-3.5" />
Target Audience
</div>
<p className="text-sm font-bold text-text-dark">{selectedEvent.academicYears.join(', ')} Batches</p>
</div>
)}
</div>
</div>
{/* Sponsors Section */}
{selectedEvent.sponsors && selectedEvent.sponsors.length > 0 && (
<div className="space-y-3">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<Heart className="w-3.5 h-3.5 text-brand-indigo" />
Event Sponsors
</div>
<div className="flex flex-wrap gap-2">
{selectedEvent.sponsors.map((sponsor, idx) => (
<span key={idx} className="px-3 py-1 bg-brand-glow text-brand-indigo rounded-lg text-[10px] font-black uppercase tracking-widest border border-brand-indigo/10">
{sponsor}
</span>
))}
</div>
</div>
)}
{/* Itinerary & Day Configs */}
{selectedEvent.dayConfigs && selectedEvent.dayConfigs.length > 0 && (
<div className="space-y-3">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<Calendar className="w-3.5 h-3.5 text-brand-indigo" />
Detailed Itinerary & Schedule
</div>
<div className="space-y-4">
{selectedEvent.dayConfigs.map((day: any, dIdx: number) => {
const dayDate = day.date ? new Date(day.date) : null;
return (
<div key={dIdx} className="p-4 rounded-2xl bg-slate-50 border border-slate-100 space-y-3">
<div className="flex justify-between items-center border-b border-slate-200 pb-1.5">
<span className="text-[11px] font-black text-brand-navy uppercase tracking-wider">
Day {dIdx + 1}: {dayDate && !isNaN(dayDate.getTime()) ? dayDate.toLocaleDateString() : day.date || 'TBD'}
</span>
<span className="px-2 py-0.5 bg-brand-glow text-brand-indigo text-[8px] font-black uppercase tracking-widest rounded">
{day.batches?.length || 0} {(day.batches?.length || 0) === 1 ? 'Batch' : 'Batches'}
</span>
</div>
<div className="space-y-3 divide-y divide-slate-200/50">
{day.batches?.map((batch: any, bIdx: number) => (
<div key={bIdx} className="pt-3 first:pt-0 space-y-2">
<div className="flex justify-between items-center">
<span className="text-[9px] font-black text-slate-400 uppercase tracking-widest">
Batch {batch.id || bIdx + 1}
</span>
<span className="text-[10px] font-bold text-brand-indigo bg-white px-2 py-0.5 rounded-lg border border-slate-200 shadow-sm flex items-center gap-1">
<Clock className="w-3.5 h-3.5" />
{batch.startTime || 'TBD'} - {batch.endTime || 'TBD'}
</span>
</div>
{batch.resourcePerson && batch.resourcePerson.name && (
<div className="p-3 bg-white rounded-xl border border-slate-100 space-y-1.5">
<div className="flex justify-between items-start">
<div>
<p className="text-[8px] font-black text-slate-400 uppercase tracking-widest">Resource Person</p>
<p className="text-xs font-bold text-text-dark">{batch.resourcePerson.name}</p>
</div>
<span className={cn(
"px-2 py-0.5 rounded text-[8px] font-black uppercase tracking-wider",
batch.resourcePerson.type === 'EXTERNAL' ? "bg-amber-50 text-amber-600 border border-amber-100" : "bg-brand-glow text-brand-indigo border border-brand-indigo/10"
)}>
{batch.resourcePerson.type || 'INTERNAL'}
</span>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-1.5 text-[9px] text-text-muted font-semibold">
{batch.resourcePerson.dept && (
<div>
<span className="font-bold text-slate-400 uppercase tracking-wider">Dept:</span> {batch.resourcePerson.dept}
</div>
)}
{batch.resourcePerson.college_name && (
<div className="col-span-2">
<span className="font-bold text-slate-400 uppercase tracking-wider">Inst:</span> {batch.resourcePerson.college_name}
</div>
)}
{batch.resourcePerson.phone && (
<div>
<span className="font-bold text-slate-400 tracking-wider uppercase">Phone:</span> {batch.resourcePerson.phone}
</div>
)}
{batch.resourcePerson.email && (
<div>
<span className="font-bold text-slate-400 tracking-wider uppercase">Email:</span> {batch.resourcePerson.email}
</div>
)}
</div>
</div>
)}
</div>
))}
</div>
</div>
);
})}
</div>
</div>
)}
{/* Department limits and quotas */}
{selectedEvent.deptLimits && Object.keys(selectedEvent.deptLimits).length > 0 && (
<div className="space-y-3">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted">
<Users className="w-3.5 h-3.5 text-brand-indigo" />
Department Limits & Quotas
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{Object.entries(selectedEvent.deptLimits).map(([dept, maxSeats]) => {
const sectionLimits = selectedEvent.deptSectionLimits?.[dept];
const hasSectionLimits = sectionLimits && Object.keys(sectionLimits).length > 0;
return (
<div key={dept} className="bg-slate-50 border border-slate-100 rounded-2xl p-4 space-y-3">
<div className="flex justify-between items-center">
<span className="text-xs font-black text-brand-navy uppercase tracking-wider">{dept}</span>
<span className="px-2 py-0.5 bg-brand-indigo text-white text-[9px] font-black uppercase rounded-md">
Quota: {maxSeats} Seats
</span>
</div>
{hasSectionLimits && (
<div className="pt-2 border-t border-slate-200/60 grid grid-cols-3 gap-2">
{Object.entries(sectionLimits).map(([sec, limit]) => (
<div key={sec} className="bg-white px-2 py-1 rounded-xl border border-slate-100 text-center">
<span className="block text-[8px] font-black text-slate-400 uppercase mb-0.5">Sec {sec}</span>
<span className="text-[10px] font-black text-brand-navy">{String(limit)}</span>
</div>
))}
</div>
)}
</div>
);
})}
</div>
</div>
)}
<div className="grid grid-cols-2 gap-8 pt-4 border-t border-slate-50">
<div className="p-4 bg-slate-50 rounded-2xl">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted mb-1">
<Wallet className="w-3.5 h-3.5 text-brand-indigo" />
Estimated Budget
</div>
<p className="text-lg font-black text-text-dark">{selectedEvent.budget?.toLocaleString() || '0'}</p>
</div>
<div className="p-4 bg-slate-50 rounded-2xl">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-widest text-text-muted mb-1">
<Ticket className="w-3.5 h-3.5 text-brand-indigo" />
Registration
</div>
<p className="text-lg font-black text-text-dark">
{selectedEvent.hasRegistrationFee ? `${selectedEvent.registrationFee}` : 'FREE'}
</p>
</div>
</div>
</div>
</motion.div>
</div>
)}
</AnimatePresence>
</div>
);
};

View File

@@ -12,13 +12,13 @@ import {
import { getAuth, GoogleAuthProvider } from 'firebase/auth';
const firebaseConfig = {
apiKey: "AIzaSyBdRUyA7LDtDReUA3TXDys71dSHgD2tOEA",
authDomain: "ems-ritchennai1.firebaseapp.com",
projectId: "ems-ritchennai1",
storageBucket: "ems-ritchennai1.firebasestorage.app",
messagingSenderId: "825363154108",
appId: "1:825363154108:web:7b6d3430aa3b696fef3cb9",
measurementId: "G-3W7ECQ52G1"
apiKey: "AIzaSyBVt4qPOJWS3Sv4bxTOdVxyC6oDxAfCEaI",
authDomain: "riteventhub.firebaseapp.com",
projectId: "riteventhub",
storageBucket: "riteventhub.firebasestorage.app",
messagingSenderId: "779973148361",
appId: "1:779973148361:web:fc4d6536ee5d2456ef9718",
measurementId: "G-7B8PP8CCZ9"
};
// Initialize Firebase App

View File

@@ -1,4 +1,4 @@
import './lib/firebaseBackend'
// import './lib/firebaseBackend'
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'

View File

@@ -49,21 +49,16 @@ export const LoginPage: React.FC = () => {
useEffect(() => {
const timer1 = setTimeout(() => {
setSplashState('text');
}, 1500);
setSplashState('fade-to-white');
}, 3200);
const timer2 = setTimeout(() => {
setSplashState('fade-to-white');
}, 3000);
const timer3 = setTimeout(() => {
setSplashState('done');
}, 4500);
}, 4700);
return () => {
clearTimeout(timer1);
clearTimeout(timer2);
clearTimeout(timer3);
};
}, []);
@@ -269,7 +264,7 @@ export const LoginPage: React.FC = () => {
{/* Top: Brand Header */}
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
animate={splashState === 'fade-to-white' || splashState === 'done' ? { opacity: 1, y: 0 } : { opacity: 0, y: -20 }}
transition={{ duration: 0.8, ease: "easeOut" }}
className="flex items-center z-10"
>
@@ -279,7 +274,7 @@ export const LoginPage: React.FC = () => {
{/* Middle: Feature list / value proposition */}
<motion.div
initial="hidden"
animate="visible"
animate={splashState === 'fade-to-white' || splashState === 'done' ? "visible" : "hidden"}
variants={{
hidden: { opacity: 0 },
visible: {
@@ -643,32 +638,33 @@ export const LoginPage: React.FC = () => {
>
{/* Centered content overlay for Logo/Text */}
<div className="absolute inset-0 flex items-center justify-center z-10 pointer-events-none">
<AnimatePresence mode="wait">
{splashState === 'logo' && (
<AnimatePresence>
{splashState !== 'fade-to-white' && (
<motion.div
key="logo"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 1.1 }}
key="splash-content"
initial={{ opacity: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.6, ease: "easeInOut" }}
className="flex flex-col items-center gap-4"
className="flex flex-col items-center gap-6 text-center"
>
{/* College Logo */}
<motion.div
initial={{ opacity: 0, scale: 0.8, y: 10 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
transition={{ duration: 0.8, ease: "easeOut" }}
>
<img src={ritLogo} alt="RIT Logo" className="h-28 w-auto object-contain drop-shadow-[0_10px_20px_rgba(255,255,255,0.15)]" />
</motion.div>
)}
{splashState === 'text' && (
<motion.div
key="text"
initial={{ opacity: 0, y: 10 }}
{/* Text RIT EVENT HUB appearing below the logo */}
<motion.h1
initial={{ opacity: 0, y: 15 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.6, ease: "easeInOut" }}
className="text-center"
transition={{ delay: 0.9, duration: 0.8, ease: "easeOut" }}
className="text-4xl md:text-5xl font-black text-white tracking-widest uppercase mt-2"
>
<h1 className="text-4xl md:text-5xl font-black text-white tracking-widest uppercase">
RIT EVENT HUB
</h1>
</motion.h1>
</motion.div>
)}
</AnimatePresence>

View File

@@ -4,4 +4,11 @@ import react from '@vitejs/plugin-react'
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
server: {
host: true,
port: 5176,
allowedHosts: true,
},
})
//This is a sample test