= ({ events = [], announcement
{ann.title}
+
+ {(ann as any).image && (
+
+ )}
{ann.message}
diff --git a/RIT-EMS-main/frontend/src/components/student/ScrollNotification.tsx b/RIT-EMS-main/frontend/src/components/student/ScrollNotification.tsx
new file mode 100644
index 0000000..39c7447
--- /dev/null
+++ b/RIT-EMS-main/frontend/src/components/student/ScrollNotification.tsx
@@ -0,0 +1,128 @@
+import React, { useState } from 'react';
+import { motion } from 'framer-motion';
+
+interface ScrollNotificationProps {
+ settings?: Record;
+}
+
+export const ScrollNotification: React.FC = ({ settings = {} }) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const scrollData = settings.scroll_notification;
+
+ if (!scrollData || !scrollData.isActive) return null;
+
+ const title = scrollData.title || "The Grand Decrees of RIT";
+ const message = scrollData.message || "No decrees active at this time.";
+
+ return (
+
+
+
+ {/* Top Roller - Interactive */}
+ setIsOpen(!isOpen)}
+ className="relative z-30 w-full h-16 rounded-full shadow-[0_8px_32px_rgba(62,39,35,0.25)] border-[3px] border-[#3e2723]/60 overflow-hidden cursor-pointer active:scale-95 transition-transform"
+ style={{
+ backgroundImage: 'url(/wood_roller_texture.png)',
+ backgroundSize: '100% 100%',
+ backgroundPosition: 'center'
+ }}
+ >
+
+
+
+
+
+
+
+
+ {isOpen ? 'Tap to Close Decree' : 'Tap to Open Decree'}
+
+
+
+
+
+ {title}
+
+
+
+
+ {/* Parchment Body */}
+
+
+
+
+
+
+ {title}
+
+
+
+ {message}
+
+
+
+
+
+ {/* Bottom Roller - Interactive */}
+ setIsOpen(!isOpen)}
+ variants={{
+ closed: { y: -64 },
+ open: { y: 0 }
+ }}
+ transition={{ duration: 0.8, ease: "easeOut" }}
+ className="relative z-30 w-full h-16 rounded-full shadow-[0_-8px_32px_rgba(62,39,35,0.25)] border-[3px] border-[#3e2723]/60 overflow-hidden cursor-pointer active:scale-95 transition-transform"
+ style={{
+ backgroundImage: 'url(/wood_roller_texture.png)',
+ backgroundSize: '100% 100%',
+ backgroundPosition: 'center'
+ }}
+ >
+
+
+
+
+
+
+
+
+
+ );
+};
+export default ScrollNotification;
diff --git a/RIT-EMS-main/frontend/src/components/student/SpecialEventsBanner.tsx b/RIT-EMS-main/frontend/src/components/student/SpecialEventsBanner.tsx
index ac41548..667cc97 100644
--- a/RIT-EMS-main/frontend/src/components/student/SpecialEventsBanner.tsx
+++ b/RIT-EMS-main/frontend/src/components/student/SpecialEventsBanner.tsx
@@ -1,183 +1,255 @@
-import React, { useState } from 'react';
-import { motion, AnimatePresence } from 'framer-motion';
+import React from 'react';
+import { motion } from 'framer-motion';
import type { SpecialEvent } from '../../types';
+import { Sparkles, ArrowUpRight, Zap } from 'lucide-react';
interface SpecialEventsBannerProps {
specialEvents: SpecialEvent[];
}
const SpecialEventsBanner: React.FC = ({ specialEvents }) => {
- const [isOpen, setIsOpen] = useState(false);
const hasEvents = specialEvents && specialEvents.length > 0;
const handleRedirect = (link: string) => {
- window.location.href = link;
+ if (link) {
+ window.open(link, '_blank', 'noopener,noreferrer');
+ }
};
- const defaultTitle = "The Grand Chronicles of RIT";
- const defaultDescription = "Hark! The tides have brought forth prestigious gatherings and legendary challenges. Seek thy destiny in the links below.";
+ if (!hasEvents) return null;
+
+ const marqueeText = Array(8).fill("⚡ SPECIAL EVENT ALERT ⚡ REGISTRATION LIVE ⚡").join(" /// ") + " /// ";
+
+ // Animation variants for container
+ const containerVariants: any = {
+ hidden: { opacity: 0 },
+ visible: {
+ opacity: 1,
+ transition: {
+ staggerChildren: 0.15,
+ delayChildren: 0.2
+ }
+ }
+ };
+
+ // Animation variants for cards
+ const cardVariants: any = {
+ hidden: { opacity: 0, y: 40, scale: 0.95 },
+ visible: {
+ opacity: 1,
+ y: 0,
+ scale: 1,
+ transition: {
+ type: 'spring',
+ stiffness: 100,
+ damping: 15
+ }
+ }
+ };
+
+ // Header animation variants
+ const headerVariants: any = {
+ hidden: { opacity: 0, y: -20 },
+ visible: {
+ opacity: 1,
+ y: 0,
+ transition: { duration: 0.6, ease: 'easeOut' }
+ }
+ };
return (
-
- {/* Background glow (Softer and centered) */}
-
-
-
- {/* New Header: Descriptive text above the scroll */}
-
-
- Special Events Registry
-
-
- Tap the ancient scroll to reveal the current decrees of RIT
-
-
+
+ {/* Full-width Skewed Light Multi-Gradient Background (Blue, Red/Rose, Pink) */}
+
+ {/* Caution Tape Top */}
+
+
+
+
+ {marqueeText}
+ {marqueeText}
+
+
+
+ {/* Caution Tape Bottom */}
+
+
+
+
+ {marqueeText}
+ {marqueeText}
+
+
+
+
+
+
+ {/* Floating Pastel Glow Spheres matching the theme */}
+
+
+
+
+ {/* Un-skewed Content Container */}
+
+ {/* Animated Header */}
- {/* Top Roller - Interactive */}
setIsOpen(!isOpen)}
- className="relative z-30 w-full h-16 rounded-full shadow-[0_8px_32px_rgba(62,39,35,0.25)] border-[3px] border-[#3e2723]/60 overflow-hidden cursor-pointer active:scale-95 transition-transform"
- style={{
- backgroundImage: 'url(/wood_roller_texture.png)',
- backgroundSize: '100% 100%',
- backgroundPosition: 'center'
- }}
+ whileHover={{ scale: 1.05 }}
+ className="inline-flex items-center gap-2 px-4 py-1.5 bg-gradient-to-r from-blue-500/10 via-rose-500/10 to-pink-500/10 text-indigo-700 border border-indigo-500/20 text-[10px] font-black uppercase tracking-widest rounded-full mb-6 cursor-default shadow-lg shadow-indigo-500/5 hover:border-indigo-400/40 transition-colors"
>
- {/* End Caps: Fixes the 'broken corner' look */}
-
-
-
-
-
-
-
-
- {isOpen ? 'Tap to Close' : 'Tap to Open'}
-
-
+
+ Registry Live
+
+ Special Events Registry
+
+
+
+ Explore premium workshops, hackathons, and institutional programs selected for Rajalakshmi Institute of Technology.
+
+
- {/* Parchment Body */}
-
- {/* Inner Shadow Shadow */}
-
-
-
-
-
- {hasEvents ? defaultTitle : "The Void Chronicles"}
-
-
-
- {hasEvents ? defaultDescription : "The magic portal remains dormant. Check back when the moons align."}
+ {/* Animated Cards Grid */}
+
+ {specialEvents.map((event) => (
+ handleRedirect(event.link)}
+ className="bg-white/85 backdrop-blur-md border border-slate-200 rounded-[2.2rem] p-8 transition-all duration-300 cursor-pointer group flex flex-col justify-between min-h-[290px] relative overflow-hidden shadow-xl shadow-slate-900/5"
+ >
+ {/* Animated inner glowing border gradient on hover */}
+
+
+
+
+
+
+ {/* Glowing Icon Container */}
+
+
+
+
+ {/* Status chip */}
+
+ Featured
+
+
+
+
+ {event.title}
+
+
+ {event.description}
-
-
-
- {specialEvents.map((event, index) => (
-
handleRedirect(event.link)}
- >
-
-
- {event.title}
-
-
- {event.description}
-
-
-
- Behold
-
-
-
-
- ))}
- {hasEvents && (
-
- End of Scroll
-
- )}
-
-
-
- {/* Bottom Roller - Interactive */}
- setIsOpen(!isOpen)}
- variants={{
- closed: { y: -64 }, // Perfect overlap (h-16 = 64px)
- open: { y: 0 }
- }}
- transition={{ duration: 0.8, ease: "easeOut" }}
- className="relative z-30 w-full h-16 rounded-full shadow-[0_-8px_32px_rgba(62,39,35,0.25)] border-[3px] border-[#3e2723]/60 overflow-hidden cursor-pointer active:scale-95 transition-transform"
- style={{
- backgroundImage: 'url(/wood_roller_texture.png)',
- backgroundSize: '100% 100%',
- backgroundPosition: 'center'
- }}
- >
- {/* End Caps: Fixes the 'broken corner' look */}
-
-
-
-
-
+ {/* Bottom bar with action */}
+
+
+ Launch Registry
+
+
+
+ {/* Visual accent dot */}
+
+
+
+ ))}
);
};
export default SpecialEventsBanner;
-
-
diff --git a/RIT-EMS-main/frontend/src/components/student/StatusTrackerView.tsx b/RIT-EMS-main/frontend/src/components/student/StatusTrackerView.tsx
index f9fc723..d704ee8 100644
--- a/RIT-EMS-main/frontend/src/components/student/StatusTrackerView.tsx
+++ b/RIT-EMS-main/frontend/src/components/student/StatusTrackerView.tsx
@@ -221,7 +221,7 @@ const StatusTrackerView: React.FC = ({ event, registrati
};
return (
-
+
{showSuccess &&
{ setShowSuccess(false); window.location.reload(); }} />}
diff --git a/RIT-EMS-main/frontend/src/context/DialogContext.tsx b/RIT-EMS-main/frontend/src/context/DialogContext.tsx
new file mode 100644
index 0000000..2a64071
--- /dev/null
+++ b/RIT-EMS-main/frontend/src/context/DialogContext.tsx
@@ -0,0 +1,127 @@
+import React, { createContext, useContext, useState } from 'react';
+import { createPortal } from 'react-dom';
+import { CheckSquare, XCircle, CheckCircle, Eye } from 'lucide-react';
+
+interface DialogOptions {
+ type?: 'info' | 'confirm' | 'error' | 'success';
+ title: string;
+ message: string;
+ onConfirm?: () => void;
+ onCancel?: () => void;
+}
+
+interface DialogContextType {
+ showAlert: (title: string, message: string, type?: 'info' | 'error' | 'success') => void;
+ showConfirm: (title: string, message: string, onConfirm: () => void, onCancel?: () => void) => void;
+}
+
+const DialogContext = createContext
(undefined);
+
+export const useDialog = () => {
+ const context = useContext(DialogContext);
+ if (!context) {
+ throw new Error('useDialog must be used within a DialogProvider');
+ }
+ return context;
+};
+
+export const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [options, setOptions] = useState(null);
+
+ const showAlert = (title: string, message: string, type: 'info' | 'error' | 'success' = 'info') => {
+ setOptions({
+ title,
+ message,
+ type,
+ });
+ setIsOpen(true);
+ };
+
+ const showConfirm = (title: string, message: string, onConfirm: () => void, onCancel?: () => void) => {
+ setOptions({
+ title,
+ message,
+ type: 'confirm',
+ onConfirm,
+ onCancel,
+ });
+ setIsOpen(true);
+ };
+
+ const handleClose = () => {
+ if (options?.onCancel) {
+ options.onCancel();
+ }
+ setIsOpen(false);
+ };
+
+ const handleConfirm = () => {
+ if (options?.onConfirm) {
+ options.onConfirm();
+ }
+ setIsOpen(false);
+ };
+
+ return (
+
+ {children}
+ {isOpen && options && createPortal(
+
+
+
+
+ {options.type === 'confirm' && }
+ {options.type === 'error' && }
+ {options.type === 'success' && }
+ {(options.type === 'info' || !options.type) && }
+
+
+ {options.title}
+
+
+ {options.message}
+
+
+
+
+ {options.type === 'confirm' ? (
+ <>
+
+
+ >
+ ) : (
+
+ )}
+
+
+
,
+ document.body
+ )}
+
+ );
+};
diff --git a/RIT-EMS-main/frontend/src/lib/firebaseBackend.ts b/RIT-EMS-main/frontend/src/lib/firebaseBackend.ts
index 8d45b10..443ba00 100644
--- a/RIT-EMS-main/frontend/src/lib/firebaseBackend.ts
+++ b/RIT-EMS-main/frontend/src/lib/firebaseBackend.ts
@@ -199,20 +199,32 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
}
}
- // Auto-register student if matches roll number format
- const studentMatch = emailLower.match(/^student\.(\d{6})@([a-zA-Z0-9&-_]+)\.ritchennai\.edu\.in$/);
- if (studentMatch) {
- const rollNo = studentMatch[1];
- const rawDept = studentMatch[2].toLowerCase();
+ // Auto-register student if email contains @ritchennai.edu.in
+ const isRitEmail = emailLower.includes('@ritchennai.edu.in') || emailLower.includes('.ritchennai.edu.in');
+ if (isRitEmail) {
+ const studentMatch = emailLower.match(/^student\.(\d{6})@([a-zA-Z0-9&-_]+)\.ritchennai\.edu\.in$/);
+ let rollNo = studentMatch ? studentMatch[1] : '';
+ let rawDept = studentMatch ? studentMatch[2] : 'CSE';
+
+ // Extract department from the email subdomain if possible
+ const deptMatch = emailLower.match(/@([a-zA-Z0-9&-_]+)\.ritchennai\.edu\.in$/);
+ if (!studentMatch && deptMatch) {
+ rawDept = deptMatch[1];
+ }
+
+ if (!rollNo) {
+ const digitMatch = emailLower.match(/\d+/);
+ rollNo = digitMatch ? digitMatch[0] : String(Math.floor(100000 + Math.random() * 900000));
+ }
// Calculate joining year and academic year (current local time is 2026-06-19)
- const joinYear = 2000 + parseInt(rollNo.substring(0, 2));
+ const joinYear = 2000 + parseInt(rollNo.substring(0, 2) || "23");
const currentYear = new Date().getFullYear();
const currentMonth = new Date().getMonth();
const academicYearOffset = currentMonth >= 5 ? 1 : 0;
const yearIndex = currentYear - joinYear + academicYearOffset;
const years = ["1st Year", "2nd Year", "3rd Year", "4th Year"];
- const calculatedYear = years[yearIndex - 1] || "N/A";
+ const calculatedYear = years[yearIndex - 1] || "3rd Year";
// Map department
let dept = rawDept.toUpperCase();
@@ -271,19 +283,31 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
}
// Check if matches student RIT format
- const studentMatch = emailLower.match(/^student\.(\d{6})@([a-zA-Z0-9&-_]+)\.ritchennai\.edu\.in$/);
- if (studentMatch) {
- const rollNo = studentMatch[1];
- const rawDept = studentMatch[2].toLowerCase();
+ const isRitEmail = emailLower.includes('@ritchennai.edu.in') || emailLower.includes('.ritchennai.edu.in');
+ if (isRitEmail) {
+ const studentMatch = emailLower.match(/^student\.(\d{6})@([a-zA-Z0-9&-_]+)\.ritchennai\.edu\.in$/);
+ let rollNo = studentMatch ? studentMatch[1] : '';
+ let rawDept = studentMatch ? studentMatch[2] : 'CSE';
+
+ // Extract department from the email subdomain if possible
+ const deptMatch = emailLower.match(/@([a-zA-Z0-9&-_]+)\.ritchennai\.edu\.in$/);
+ if (!studentMatch && deptMatch) {
+ rawDept = deptMatch[1];
+ }
+
+ if (!rollNo) {
+ const digitMatch = emailLower.match(/\d+/);
+ rollNo = digitMatch ? digitMatch[0] : String(Math.floor(100000 + Math.random() * 900000));
+ }
// Calculate joining year and academic year (current local time is 2026-06-19)
- const joinYear = 2000 + parseInt(rollNo.substring(0, 2));
+ const joinYear = 2000 + parseInt(rollNo.substring(0, 2) || "23");
const currentYear = new Date().getFullYear();
const currentMonth = new Date().getMonth();
const academicYearOffset = currentMonth >= 5 ? 1 : 0;
const yearIndex = currentYear - joinYear + academicYearOffset;
const years = ["1st Year", "2nd Year", "3rd Year", "4th Year"];
- const calculatedYear = years[yearIndex - 1] || "N/A";
+ const calculatedYear = years[yearIndex - 1] || "3rd Year";
// Map department
let dept = rawDept.toUpperCase();
@@ -477,6 +501,33 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
return jsonResponse({ message: `Academic year promotion completed for ${inst}` });
}
+ // ----------------------------------------------------
+ // BATCHES CONTROLLER
+ // ----------------------------------------------------
+ if (path === '/api/batches' && method === 'GET') {
+ const snap = await getDocs(collection(db, 'ems_batches'));
+ return jsonResponse(snap.docs.map(d => d.data()));
+ }
+
+ if (path === '/api/batches' && method === 'POST') {
+ const payload = parseBody(init?.body);
+ const id = payload.id || generateNumericId();
+ const newBatch = {
+ ...payload,
+ id,
+ classes: payload.classes || []
+ };
+ await setDoc(doc(db, 'ems_batches', String(id)), newBatch);
+ return jsonResponse(newBatch);
+ }
+
+ if (path.startsWith('/api/batches/') && method === 'DELETE') {
+ const parts = path.split('/');
+ const id = parts[parts.length - 1];
+ await deleteDoc(doc(db, 'ems_batches', String(id)));
+ return jsonResponse({ message: "Batch deleted successfully" });
+ }
+
// ----------------------------------------------------
// NOTES CONTROLLER
// ----------------------------------------------------
@@ -642,9 +693,11 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
budget: budgetStr === "" ? 0.0 : parseFloat(budgetStr),
hasRegistrationFee: !!payload.hasRegistrationFee,
registrationFee: feeStr === "" ? 0.0 : parseFloat(feeStr),
+ paymentLink: payload.paymentLink || null,
centreName: payload.centreName || null,
isPublicEvent: !!payload.isPublicEvent,
image: payload.image || null,
+ targetedBatch: payload.targetedBatch || null,
proposer: {
id: proposer.id,
fullName: proposer.fullName,
@@ -894,6 +947,7 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
if (payload.centreName !== undefined) updatedFields.centreName = payload.centreName;
if (payload.isPublicEvent !== undefined) updatedFields.isPublicEvent = !!payload.isPublicEvent;
if (payload.image !== undefined) updatedFields.image = payload.image;
+ if (payload.targetedBatch !== undefined) updatedFields.targetedBatch = payload.targetedBatch;
if (payload.status !== undefined && isAdmin) {
updatedFields.status = payload.status;
}
@@ -922,6 +976,7 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
}
const id = generateNumericId();
+ const isRitEmail = email.includes('@ritchennai.edu.in') || email.includes('.ritchennai.edu.in');
const newUser = {
...payload,
id,
@@ -933,7 +988,7 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
regNo: payload.regNo || '',
phone: payload.phone || '',
gender: payload.gender || 'Male',
- collegeName: payload.collegeName || 'Rajalakshmi Institute of Technology'
+ collegeName: isRitEmail ? 'Rajalakshmi Institute of Technology' : (payload.collegeName || 'External Institution')
};
await setDoc(doc(db, 'ems_users', String(id)), newUser);
@@ -984,14 +1039,12 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
return jsonResponse({ message: "You are already registered for this event." }, 400);
}
- // Fetch event details
const eventSnap = await getDoc(doc(db, 'ems_events', String(eventId)));
if (!eventSnap.exists()) {
return jsonResponse({ message: "Event not found" }, 404);
}
const event = eventSnap.data();
- // Check deadline
if (event.endDate) {
const deadline = new Date(event.registrationFee ? (event.registrationDeadline || event.endDate) : event.endDate);
if (new Date() > deadline) {
@@ -999,17 +1052,14 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
}
}
- // Count current registrations for this event
const allRegsSnap = await getDocs(collection(db, 'ems_registrations'));
const eventRegs = allRegsSnap.docs.map(d => d.data()).filter(r => String(r.eventId || r.event_id) === String(eventId));
const currentParticipants = eventRegs.length;
- // Validate overall capacity
if (event.maxParticipants && currentParticipants >= event.maxParticipants) {
return jsonResponse({ message: "Registration Blocked: The event has reached its capacity." }, 409);
}
- // Quota validation
const studentDept = payload.dept || payload.department || 'CSE';
const studentSec = payload.section || 'A';
@@ -1017,7 +1067,6 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
const deptLimit = Number(event.deptLimits[studentDept]);
const deptRegs = eventRegs.filter(r => (r.dept || r.department) === studentDept);
- // Section limit check if present
if (event.deptSectionLimits && event.deptSectionLimits[studentDept]) {
const sectionLimits = event.deptSectionLimits[studentDept];
const sectionLimit = Number(sectionLimits[studentSec] || 0);
@@ -1031,17 +1080,16 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
return jsonResponse({ message: `Registration Blocked: Quota for Section ${studentSec} of ${studentDept} is full.` }, 409);
}
} else {
- // Standard department limit check
if (deptRegs.length >= deptLimit) {
return jsonResponse({ message: `Registration Blocked: Quota for department ${studentDept} is full.` }, 409);
}
}
}
- // Safe to create registration
+ const { image, ...registrationData } = payload;
const isFree = event.hasRegistrationFee === false || event.registrationFee === 0 || event.pricingType === 'FREE';
const newReg = {
- ...payload,
+ ...registrationData,
id: regId,
userId,
user_id: userId,
@@ -1056,8 +1104,6 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
};
await setDoc(regDocRef, newReg);
-
- // Update event's currentParticipants
const eventDocRef = doc(db, 'ems_events', String(eventId));
await updateDoc(eventDocRef, { currentParticipants: currentParticipants + 1 });
@@ -1184,6 +1230,58 @@ window.fetch = async function(input: RequestInfo | URL, init?: RequestInit): Pro
return jsonResponse({ message: "Attendance updated successfully" });
}
+ // ----------------------------------------------------
+ // SETTINGS CONTROLLER
+ // ----------------------------------------------------
+ if (path === '/api/settings' && method === 'GET') {
+ const snap = await getDocs(collection(db, 'ems_settings'));
+ const settings: Record = {};
+ snap.docs.forEach(d => {
+ settings[d.id] = d.data();
+ });
+ return jsonResponse(settings);
+ }
+
+ if (path === '/api/settings' && method === 'POST') {
+ const payload = parseBody(init?.body);
+ const { key, value } = payload;
+ if (key) {
+ await setDoc(doc(db, 'ems_settings', key), value);
+ }
+ return jsonResponse({ message: "Settings saved successfully" });
+ }
+
+ // ----------------------------------------------------
+ // SPECIAL EVENTS CONTROLLER
+ // ----------------------------------------------------
+ if (path === '/api/special-events' && method === 'GET') {
+ const snap = await getDocs(collection(db, 'ems_special_events'));
+ const list = snap.docs.map(d => d.data());
+ list.sort((a, b) => new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime());
+ return jsonResponse(list);
+ }
+
+ if (path === '/api/special-events' && method === 'POST') {
+ const payload = parseBody(init?.body);
+ const id = payload.id || String(generateNumericId());
+ const newSE = {
+ ...payload,
+ id,
+ created_at: payload.created_at || new Date().toISOString(),
+ is_active: payload.is_active !== undefined ? !!payload.is_active : true,
+ verificationStatus: payload.verificationStatus || 'APPROVED'
+ };
+ await setDoc(doc(db, 'ems_special_events', id), newSE);
+ return jsonResponse(newSE);
+ }
+
+ if (path.startsWith('/api/special-events/') && method === 'DELETE') {
+ const parts = path.split('/');
+ const id = parts[parts.length - 1];
+ await deleteDoc(doc(db, 'ems_special_events', String(id)));
+ return jsonResponse({ message: "Special event deleted successfully" });
+ }
+
// ----------------------------------------------------
// ANNOUNCEMENTS CONTROLLER
// ----------------------------------------------------
diff --git a/RIT-EMS-main/frontend/src/types.ts b/RIT-EMS-main/frontend/src/types.ts
index 5fd0f99..c0c823f 100644
--- a/RIT-EMS-main/frontend/src/types.ts
+++ b/RIT-EMS-main/frontend/src/types.ts
@@ -55,6 +55,7 @@ export interface Event {
status?: 'Registered' | 'Event Ongoing' | 'Completed' | 'Scheduled' | 'APPROVED' | 'COMPLETED' | 'ONGOING' | 'REJECTED';
hasRegistrationFee?: boolean;
registrationFee?: number;
+ paymentLink?: string;
registrationDeadline?: string;
maxParticipants?: number;
durationDays?: number;
@@ -78,6 +79,7 @@ export interface Event {
conducting_dept?: string;
request_by_faculty?: string;
request_by_HOD?: string;
+ targetedBatch?: string;
}
export interface Announcement {
@@ -150,4 +152,12 @@ export interface SpecialEvent {
created_by: string;
is_active: boolean;
verificationStatus?: 'PENDING' | 'PENDING_HOD' | 'PENDING_ADMIN' | 'APPROVED' | 'REJECTED';
+ image?: string;
+}
+
+export interface DeptBatch {
+ id: number;
+ name: string;
+ department: string;
+ classes: string[]; // List of Year - Section strings, e.g. ["1st Year - A", "2nd Year - B"]
}
diff --git a/RIT-EVENT-MANAGEMENT--main/components/SpecialEventsBanner.tsx b/RIT-EVENT-MANAGEMENT--main/components/SpecialEventsBanner.tsx
index a149686..6bb1daa 100644
--- a/RIT-EVENT-MANAGEMENT--main/components/SpecialEventsBanner.tsx
+++ b/RIT-EVENT-MANAGEMENT--main/components/SpecialEventsBanner.tsx
@@ -108,34 +108,38 @@ const SpecialEventsBanner: React.FC = ({ specialEvents
-
- {specialEvents.map((event, index) => (
-
handleRedirect(event.link)}
- >
-
-
+
+ {/* Slideshow container */}
+
+
+ {specialEvents.map((event) => (
+
handleRedirect(event.link)}
+ style={{
+ backgroundImage: event.coverImage ? `url(${event.coverImage})` : "url('/parchment_texture.png')",
+ backgroundSize: 'cover',
+ backgroundPosition: 'center',
+ backgroundColor: event.coverImage ? undefined : '#6b5b95',
+ }}
+ >
+
{event.title}
-
-
- {event.description}
-
-
-
- Behold
-
-
- ))}
+ ))}
+
{hasEvents && (
diff --git a/image copy.png b/image copy.png
new file mode 100644
index 0000000..1567a34
Binary files /dev/null and b/image copy.png differ
diff --git a/image.png b/image.png
index d4615ea..2c3d0a8 100644
Binary files a/image.png and b/image.png differ