Update campus map layout, refine navbar, remove discord integration
Some checks failed
Deploy RIT Freshers Hub to VPS / Deploy to Live VPS (push) Has been cancelled
Some checks failed
Deploy RIT Freshers Hub to VPS / Deploy to Live VPS (push) Has been cancelled
This commit is contained in:
@@ -101,7 +101,7 @@ public class CollabController {
|
|||||||
request.setApplicationsCount(request.getApplicationsCount() + 1);
|
request.setApplicationsCount(request.getApplicationsCount() + 1);
|
||||||
collabRequestRepository.save(request);
|
collabRequestRepository.save(request);
|
||||||
|
|
||||||
// Notify Telegram/Discord intermediary bot asynchronously
|
// Notify Telegram intermediary bot asynchronously
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
Map<String, Object> payload = new HashMap<>();
|
Map<String, Object> payload = new HashMap<>();
|
||||||
@@ -112,7 +112,6 @@ public class CollabController {
|
|||||||
payload.put("author_name", request.getAuthorName());
|
payload.put("author_name", request.getAuthorName());
|
||||||
payload.put("contact_info", request.getContactInfo());
|
payload.put("contact_info", request.getContactInfo());
|
||||||
payload.put("telegram_chat_id", request.getTelegramChatId());
|
payload.put("telegram_chat_id", request.getTelegramChatId());
|
||||||
payload.put("discord_user_id", request.getDiscordUserId());
|
|
||||||
payload.put("applicant_name", savedApp.getApplicantName());
|
payload.put("applicant_name", savedApp.getApplicantName());
|
||||||
payload.put("applicant_email", savedApp.getApplicantEmail());
|
payload.put("applicant_email", savedApp.getApplicantEmail());
|
||||||
payload.put("applicant_dept", savedApp.getApplicantDept());
|
payload.put("applicant_dept", savedApp.getApplicantDept());
|
||||||
@@ -172,11 +171,6 @@ public class CollabController {
|
|||||||
return collabRequestRepository.findByTelegramChatIdAndStatusOrderByCreatedAtDesc(chatId, "OPEN");
|
return collabRequestRepository.findByTelegramChatIdAndStatusOrderByCreatedAtDesc(chatId, "OPEN");
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/active/discord/{userId}")
|
|
||||||
public List<CollabRequest> getActiveRequestsByDiscord(@PathVariable String userId) {
|
|
||||||
return collabRequestRepository.findByDiscordUserIdAndStatusOrderByCreatedAtDesc(userId, "OPEN");
|
|
||||||
}
|
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public ResponseEntity<Void> deleteOrCancelCollabRequest(@PathVariable Integer id) {
|
public ResponseEntity<Void> deleteOrCancelCollabRequest(@PathVariable Integer id) {
|
||||||
return collabRequestRepository.findById(id).map(req -> {
|
return collabRequestRepository.findById(id).map(req -> {
|
||||||
|
|||||||
@@ -46,9 +46,6 @@ public class CollabRequest {
|
|||||||
@Column(name = "telegram_chat_id")
|
@Column(name = "telegram_chat_id")
|
||||||
private Long telegramChatId;
|
private Long telegramChatId;
|
||||||
|
|
||||||
@Column(name = "discord_user_id")
|
|
||||||
private String discordUserId;
|
|
||||||
|
|
||||||
@Column(name = "status")
|
@Column(name = "status")
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private String status = "OPEN";
|
private String status = "OPEN";
|
||||||
|
|||||||
@@ -10,5 +10,4 @@ public interface CollabRequestRepository extends JpaRepository<CollabRequest, In
|
|||||||
List<CollabRequest> findAllByOrderByCreatedAtDesc();
|
List<CollabRequest> findAllByOrderByCreatedAtDesc();
|
||||||
List<CollabRequest> findByAuthorEmailOrderByCreatedAtDesc(String authorEmail);
|
List<CollabRequest> findByAuthorEmailOrderByCreatedAtDesc(String authorEmail);
|
||||||
List<CollabRequest> findByTelegramChatIdAndStatusOrderByCreatedAtDesc(Long telegramChatId, String status);
|
List<CollabRequest> findByTelegramChatIdAndStatusOrderByCreatedAtDesc(Long telegramChatId, String status);
|
||||||
List<CollabRequest> findByDiscordUserIdAndStatusOrderByCreatedAtDesc(String discordUserId, String status);
|
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
public/map.png
BIN
public/map.png
Binary file not shown.
|
Before Width: | Height: | Size: 282 KiB After Width: | Height: | Size: 394 KiB |
@@ -1,34 +1,77 @@
|
|||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { Link, useLocation } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { Menu, X, LogIn, LogOut, ShieldCheck, ChevronDown, Rocket, Sparkles } from 'lucide-react';
|
import {
|
||||||
import { NAV_LINKS } from '@/constants';
|
Menu,
|
||||||
|
X,
|
||||||
|
LogIn,
|
||||||
|
LogOut,
|
||||||
|
ShieldCheck,
|
||||||
|
ChevronDown,
|
||||||
|
Map,
|
||||||
|
Bus,
|
||||||
|
UserCheck,
|
||||||
|
MessageCircle,
|
||||||
|
Rocket,
|
||||||
|
BookOpen,
|
||||||
|
Wrench,
|
||||||
|
Bot,
|
||||||
|
Users,
|
||||||
|
Code2,
|
||||||
|
Trophy,
|
||||||
|
Home as HomeIcon,
|
||||||
|
} from 'lucide-react';
|
||||||
import { useAuth } from '@/context/AuthContext';
|
import { useAuth } from '@/context/AuthContext';
|
||||||
|
|
||||||
|
interface NavItem {
|
||||||
|
label: string;
|
||||||
|
path: string;
|
||||||
|
icon?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PRIMARY_LINKS: NavItem[] = [
|
||||||
|
{ label: 'Home', path: '/', icon: HomeIcon },
|
||||||
|
{ label: 'Notes', path: '/notes', icon: BookOpen },
|
||||||
|
{ label: 'Toolkit', path: '/toolkit', icon: Wrench },
|
||||||
|
{ label: 'Chatbot', path: '/ai-assistant', icon: Bot },
|
||||||
|
{ label: 'Clubs & Centers', path: '/events', icon: Users },
|
||||||
|
{ label: 'Dev Collab', path: '/collab', icon: Code2 },
|
||||||
|
{ label: 'LeetCode', path: '/leetcode', icon: Trophy },
|
||||||
|
];
|
||||||
|
|
||||||
|
const MORE_LINKS: NavItem[] = [
|
||||||
|
{ label: 'Campus Map', path: '/campus', icon: Map },
|
||||||
|
{ label: 'Bus Routes', path: '/bus-routes', icon: Bus },
|
||||||
|
{ label: 'Faculty Directory', path: '/faculty', icon: UserCheck },
|
||||||
|
{ label: 'Community', path: '/community', icon: MessageCircle },
|
||||||
|
{ label: 'RAISE Incubator', path: '/raise', icon: Rocket },
|
||||||
|
];
|
||||||
|
|
||||||
|
const ALL_MOBILE_LINKS: NavItem[] = [...PRIMARY_LINKS, ...MORE_LINKS];
|
||||||
|
|
||||||
export default function Navbar() {
|
export default function Navbar() {
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
|
||||||
const [isMobileOpen, setIsMobileOpen] = useState(false);
|
const [isMobileOpen, setIsMobileOpen] = useState(false);
|
||||||
const [isProfileOpen, setIsProfileOpen] = useState(false);
|
const [isProfileOpen, setIsProfileOpen] = useState(false);
|
||||||
|
const [isMoreOpen, setIsMoreOpen] = useState(false);
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { user, isAuthenticated, isVerifiedStudent, loginWithGoogle, logout } = useAuth();
|
const { user, isAuthenticated, isVerifiedStudent, loginWithGoogle, logout } = useAuth();
|
||||||
const profileRef = useRef<HTMLDivElement>(null);
|
const profileRef = useRef<HTMLDivElement>(null);
|
||||||
|
const moreRef = useRef<HTMLDivElement>(null);
|
||||||
useEffect(() => {
|
|
||||||
const handleScroll = () => setIsScrolled(window.scrollY > 20);
|
|
||||||
window.addEventListener('scroll', handleScroll);
|
|
||||||
return () => window.removeEventListener('scroll', handleScroll);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsMobileOpen(false);
|
setIsMobileOpen(false);
|
||||||
|
setIsMoreOpen(false);
|
||||||
}, [location.pathname]);
|
}, [location.pathname]);
|
||||||
|
|
||||||
// Close profile dropdown when clicking outside
|
// Close dropdowns when clicking outside
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (e: MouseEvent) => {
|
const handleClickOutside = (e: MouseEvent) => {
|
||||||
if (profileRef.current && !profileRef.current.contains(e.target as Node)) {
|
if (profileRef.current && !profileRef.current.contains(e.target as Node)) {
|
||||||
setIsProfileOpen(false);
|
setIsProfileOpen(false);
|
||||||
}
|
}
|
||||||
|
if (moreRef.current && !moreRef.current.contains(e.target as Node)) {
|
||||||
|
setIsMoreOpen(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
document.addEventListener('mousedown', handleClickOutside);
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||||
@@ -36,84 +79,240 @@ export default function Navbar() {
|
|||||||
|
|
||||||
const checkIsActive = (linkPath: string) => {
|
const checkIsActive = (linkPath: string) => {
|
||||||
const [pathName] = linkPath.split('?');
|
const [pathName] = linkPath.split('?');
|
||||||
return location.pathname === pathName;
|
if (pathName === '/') return location.pathname === '/';
|
||||||
|
return location.pathname.startsWith(pathName);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isMoreActive = MORE_LINKS.some((link) => checkIsActive(link.path));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Fixed Header Layout Wrapper */}
|
{/* Unified Floating Navbar Container */}
|
||||||
<div className="fixed top-3 left-1/2 -translate-x-1/2 z-50 w-[96%] xl:w-[97%] max-w-[1560px] flex items-start justify-between gap-2.5 lg:gap-3.5 pointer-events-none px-1 lg:px-2">
|
<header className="fixed top-3.5 left-1/2 -translate-x-1/2 z-50 w-[95%] max-w-[1440px] pointer-events-none">
|
||||||
{/* Grey Rectangular Capsule Container (Logo + Links ending with LeetCode) */}
|
|
||||||
<motion.nav
|
<motion.nav
|
||||||
initial={{ y: -100, opacity: 0 }}
|
initial={{ y: -60, opacity: 0 }}
|
||||||
animate={{ y: 0, opacity: 1 }}
|
animate={{ y: 0, opacity: 1 }}
|
||||||
transition={{ duration: 0.6, ease: 'easeOut' }}
|
transition={{ duration: 0.45, ease: 'easeOut' }}
|
||||||
className="pointer-events-auto flex items-center justify-between flex-1 min-w-0 pl-2.5 pr-4 lg:pl-3.5 lg:pr-5 py-2 transition-all duration-300 shadow-[0_8px_32px_rgba(0,0,0,0.25)]"
|
className="pointer-events-auto flex items-center justify-between gap-2 lg:gap-4 px-3 sm:px-4 py-2 rounded-2xl bg-slate-950/90 backdrop-blur-xl border border-white/10 shadow-[0_8px_30px_rgba(0,0,0,0.35)] transition-all"
|
||||||
style={{
|
|
||||||
background: 'linear-gradient(to right, rgba(19, 9, 36, 0.88) 0%, rgba(30, 12, 54, 0.82) 50%, rgba(19, 9, 36, 0.88) 100%)',
|
|
||||||
backdropFilter: 'blur(16px)',
|
|
||||||
WebkitBackdropFilter: 'blur(16px)',
|
|
||||||
borderRadius: '20px',
|
|
||||||
border: '1px solid rgba(255, 255, 255, 0.12)',
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{/* Logo - RIT Event Hub Style */}
|
{/* Left: Logo & College Brand */}
|
||||||
<a href="/" className="flex items-center gap-1.5 lg:gap-2 group shrink-0">
|
<Link to="/" className="flex items-center gap-2.5 group shrink-0">
|
||||||
{/* Logo Image */}
|
|
||||||
<motion.img
|
<motion.img
|
||||||
whileHover={{ scale: 1.05 }}
|
whileHover={{ scale: 1.06 }}
|
||||||
src="/logo.png"
|
src="/logo.png"
|
||||||
alt="RIT Logo"
|
alt="RIT Logo"
|
||||||
className="w-8 h-8 lg:w-9 lg:h-9 object-contain rounded-full shrink-0"
|
className="w-8 h-8 lg:w-9 lg:h-9 object-contain rounded-full shrink-0"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<span className="font-extrabold text-white text-lg lg:text-xl xl:text-2xl tracking-tighter" style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}>
|
<span
|
||||||
|
className="font-extrabold text-white text-lg lg:text-xl tracking-tight group-hover:text-[#FF6B00] transition-colors"
|
||||||
|
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
|
||||||
|
>
|
||||||
rit
|
rit
|
||||||
</span>
|
</span>
|
||||||
<div className="h-5 lg:h-6 w-[1px] bg-white/20 mx-1 lg:mx-1.5" />
|
<div className="h-4 lg:h-5 w-[1px] bg-white/20 mx-2" />
|
||||||
<div className="hidden sm:flex flex-col text-[7.5px] lg:text-[8px] xl:text-[8.5px] font-bold text-slate-200 leading-tight tracking-wider" style={{ fontFamily: 'Inter, sans-serif' }}>
|
<div
|
||||||
<span>RAJALAKSHMI</span>
|
className="hidden xl:flex flex-col text-[7.5px] lg:text-[8px] font-bold text-slate-300 leading-[1.15] tracking-wider uppercase"
|
||||||
<span>INSTITUTE OF</span>
|
style={{ fontFamily: 'Inter, sans-serif' }}
|
||||||
<span>TECHNOLOGY</span>
|
>
|
||||||
|
<span>Rajalakshmi</span>
|
||||||
|
<span>Institute of</span>
|
||||||
|
<span>Technology</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</Link>
|
||||||
|
|
||||||
{/* Desktop Nav - Ends with LeetCode */}
|
{/* Center: Desktop Navigation Links */}
|
||||||
<div className="hidden md:flex items-center gap-0.5 lg:gap-0.5 xl:gap-1">
|
<div className="hidden lg:flex items-center gap-1 xl:gap-1.5 min-w-0">
|
||||||
{NAV_LINKS.map((link) => {
|
{PRIMARY_LINKS.map((link) => {
|
||||||
const isActive = checkIsActive(link.path);
|
const isActive = checkIsActive(link.path);
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
key={link.path}
|
key={link.path}
|
||||||
to={link.path}
|
to={link.path}
|
||||||
className="relative px-1 lg:px-1.5 xl:px-2 2xl:px-2.5 py-1.5 rounded-lg text-[8.5px] lg:text-[9px] xl:text-[10px] 2xl:text-[11px] font-semibold uppercase tracking-wider transition-all duration-200 whitespace-nowrap shrink-0"
|
className={`relative px-2.5 xl:px-3 py-1.5 rounded-xl text-xs font-semibold tracking-wide transition-all duration-200 whitespace-nowrap ${
|
||||||
style={{
|
isActive ? 'text-white' : 'text-slate-300 hover:text-white hover:bg-white/10'
|
||||||
fontFamily: 'Plus Jakarta Sans, sans-serif',
|
}`}
|
||||||
color: isActive ? '#FFFFFF' : '#CBD5E1',
|
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{isActive && (
|
{isActive && (
|
||||||
<motion.div
|
<motion.div
|
||||||
layoutId="nav-active"
|
layoutId="nav-pill-active"
|
||||||
className="absolute inset-0 rounded-xl"
|
className="absolute inset-0 rounded-xl bg-gradient-to-r from-[#FF6B00] to-[#F97316] shadow-sm"
|
||||||
style={{ background: 'linear-gradient(135deg, #FF6B00, #F97316)' }}
|
transition={{ type: 'spring', bounce: 0.15, duration: 0.35 }}
|
||||||
transition={{ type: 'spring', bounce: 0.2, duration: 0.4 }}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<span className="relative z-10">{link.label}</span>
|
<span className="relative z-10">{link.label}</span>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{/* "More" Dropdown Menu */}
|
||||||
|
<div className="relative" ref={moreRef}>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsMoreOpen(!isMoreOpen)}
|
||||||
|
className={`relative flex items-center gap-1 px-2.5 xl:px-3 py-1.5 rounded-xl text-xs font-semibold tracking-wide transition-all cursor-pointer ${
|
||||||
|
isMoreActive ? 'text-white' : 'text-slate-300 hover:text-white hover:bg-white/10'
|
||||||
|
}`}
|
||||||
|
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
|
||||||
|
>
|
||||||
|
{isMoreActive && (
|
||||||
|
<motion.div
|
||||||
|
layoutId="nav-pill-active"
|
||||||
|
className="absolute inset-0 rounded-xl bg-gradient-to-r from-[#FF6B00] to-[#F97316] shadow-sm"
|
||||||
|
transition={{ type: 'spring', bounce: 0.15, duration: 0.35 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span className="relative z-10">More</span>
|
||||||
|
<ChevronDown
|
||||||
|
className={`w-3.5 h-3.5 relative z-10 transition-transform duration-200 ${
|
||||||
|
isMoreOpen ? 'rotate-180 text-white' : 'text-slate-400'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<AnimatePresence>
|
||||||
|
{isMoreOpen && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 8, scale: 0.96 }}
|
||||||
|
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, y: 8, scale: 0.96 }}
|
||||||
|
transition={{ duration: 0.15 }}
|
||||||
|
className="absolute right-0 top-full mt-2 w-52 bg-slate-900/95 backdrop-blur-xl border border-white/10 rounded-2xl shadow-2xl p-1.5 z-50 overflow-hidden"
|
||||||
|
>
|
||||||
|
{MORE_LINKS.map((item) => {
|
||||||
|
const IconComponent = item.icon;
|
||||||
|
const active = checkIsActive(item.path);
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.path}
|
||||||
|
to={item.path}
|
||||||
|
onClick={() => setIsMoreOpen(false)}
|
||||||
|
className={`flex items-center gap-2.5 px-3 py-2 rounded-xl text-xs font-medium transition-all ${
|
||||||
|
active
|
||||||
|
? 'bg-gradient-to-r from-[#FF6B00] to-[#F97316] text-white font-bold'
|
||||||
|
: 'text-slate-300 hover:text-white hover:bg-white/10'
|
||||||
|
}`}
|
||||||
|
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
|
||||||
|
>
|
||||||
|
{IconComponent && <IconComponent className="w-4 h-4 shrink-0 text-slate-300" />}
|
||||||
|
<span>{item.label}</span>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Mobile Toggle */}
|
{/* Right: Auth Profile / Sign In */}
|
||||||
<div className="md:hidden flex items-center">
|
<div className="hidden lg:flex items-center gap-2 shrink-0">
|
||||||
|
{isAuthenticated && user ? (
|
||||||
|
<div className="relative" ref={profileRef}>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsProfileOpen(!isProfileOpen)}
|
||||||
|
className="flex items-center gap-2 px-3 py-1.5 rounded-xl bg-slate-800/80 hover:bg-slate-800 border border-white/10 transition-all cursor-pointer"
|
||||||
|
>
|
||||||
|
{user.pictureUrl ? (
|
||||||
|
<img
|
||||||
|
src={user.pictureUrl}
|
||||||
|
alt={user.name}
|
||||||
|
className="w-6 h-6 rounded-full border border-white/30 object-cover"
|
||||||
|
referrerPolicy="no-referrer"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="w-6 h-6 rounded-full bg-gradient-to-br from-[#FF6B00] to-[#F97316] flex items-center justify-center text-white font-bold text-xs">
|
||||||
|
{user.name.charAt(0).toUpperCase()}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className="text-white text-xs font-semibold max-w-[100px] truncate"
|
||||||
|
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
|
||||||
|
>
|
||||||
|
{user.name.split(' ')[0]}
|
||||||
|
</span>
|
||||||
|
<ChevronDown className="w-3.5 h-3.5 text-slate-400" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Profile Dropdown */}
|
||||||
|
<AnimatePresence>
|
||||||
|
{isProfileOpen && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 8, scale: 0.95 }}
|
||||||
|
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, y: 8, scale: 0.95 }}
|
||||||
|
transition={{ duration: 0.15 }}
|
||||||
|
className="absolute right-0 top-full mt-2 w-64 bg-slate-900 border border-white/10 rounded-2xl shadow-2xl overflow-hidden z-50"
|
||||||
|
>
|
||||||
|
<div className="p-4 border-b border-white/10">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
{user.pictureUrl ? (
|
||||||
|
<img
|
||||||
|
src={user.pictureUrl}
|
||||||
|
alt={user.name}
|
||||||
|
className="w-10 h-10 rounded-full border-2 border-orange-500/40 object-cover"
|
||||||
|
referrerPolicy="no-referrer"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-[#FF6B00] to-[#F97316] flex items-center justify-center text-white font-bold text-lg">
|
||||||
|
{user.name.charAt(0).toUpperCase()}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<p className="text-white text-sm font-bold truncate">{user.name}</p>
|
||||||
|
<p className="text-slate-400 text-[11px] truncate">{user.email}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{isVerifiedStudent ? (
|
||||||
|
<div className="mt-3 flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg bg-emerald-500/15 border border-emerald-500/25">
|
||||||
|
<ShieldCheck className="w-3.5 h-3.5 text-emerald-400" />
|
||||||
|
<span className="text-[11px] font-bold text-emerald-400">Verified RIT Student</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="mt-3 flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg bg-amber-500/15 border border-amber-500/25">
|
||||||
|
<span className="text-[11px] font-semibold text-amber-400">
|
||||||
|
Sign in with @ritchennai.edu.in
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="p-2">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
logout();
|
||||||
|
setIsProfileOpen(false);
|
||||||
|
}}
|
||||||
|
className="w-full flex items-center gap-2.5 px-3 py-2 rounded-xl text-slate-300 hover:text-white hover:bg-red-500/15 text-xs font-semibold transition-all cursor-pointer"
|
||||||
|
>
|
||||||
|
<LogOut className="w-4 h-4 text-red-400" />
|
||||||
|
Sign Out
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<motion.button
|
||||||
|
whileHover={{ scale: 1.03 }}
|
||||||
|
whileTap={{ scale: 0.97 }}
|
||||||
|
onClick={loginWithGoogle}
|
||||||
|
className="flex items-center gap-1.5 px-3.5 py-1.5 rounded-xl text-white text-xs font-bold bg-white/10 hover:bg-white/15 border border-white/15 transition-all cursor-pointer"
|
||||||
|
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
|
||||||
|
>
|
||||||
|
<LogIn className="w-3.5 h-3.5 text-[#FF6B00]" />
|
||||||
|
<span>Sign In</span>
|
||||||
|
</motion.button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Hamburger Toggle */}
|
||||||
|
<div className="lg:hidden flex items-center">
|
||||||
<motion.button
|
<motion.button
|
||||||
whileTap={{ scale: 0.9 }}
|
whileTap={{ scale: 0.9 }}
|
||||||
onClick={() => setIsMobileOpen(!isMobileOpen)}
|
onClick={() => setIsMobileOpen(!isMobileOpen)}
|
||||||
className="w-9 h-9 rounded-xl flex items-center justify-center text-slate-200 hover:text-white bg-slate-800/60 hover:bg-slate-700/60 border border-slate-700/50 transition-all active:scale-95"
|
className="w-9 h-9 rounded-xl flex items-center justify-center text-slate-200 hover:text-white bg-slate-800/80 border border-white/10 transition-all active:scale-95"
|
||||||
aria-label="Toggle menu"
|
aria-label="Toggle menu"
|
||||||
>
|
>
|
||||||
{isMobileOpen ? <X className="w-5 h-5 stroke-[2.5]" /> : <Menu className="w-5 h-5 stroke-[2.5]" />}
|
{isMobileOpen ? <X className="w-5 h-5 stroke-[2.5]" /> : <Menu className="w-5 h-5 stroke-[2.5]" />}
|
||||||
@@ -128,189 +327,69 @@ export default function Navbar() {
|
|||||||
animate={{ height: 'auto', opacity: 1 }}
|
animate={{ height: 'auto', opacity: 1 }}
|
||||||
exit={{ height: 0, opacity: 0 }}
|
exit={{ height: 0, opacity: 0 }}
|
||||||
transition={{ duration: 0.25 }}
|
transition={{ duration: 0.25 }}
|
||||||
className="absolute left-0 top-full mt-2 w-full overflow-hidden md:hidden max-h-[80vh] overflow-y-auto z-50"
|
className="absolute left-0 top-full mt-2 w-full overflow-hidden lg:hidden max-h-[82vh] overflow-y-auto z-50 rounded-2xl border border-white/10 bg-slate-950/95 backdrop-blur-2xl shadow-2xl"
|
||||||
>
|
>
|
||||||
<div className="px-4 pb-5 pt-3 grid grid-cols-2 gap-2 border border-white/10 bg-slate-950/95 backdrop-blur-2xl rounded-2xl shadow-2xl">
|
<div className="p-4 space-y-3">
|
||||||
{NAV_LINKS.map((link) => {
|
<div className="grid grid-cols-2 gap-2">
|
||||||
const isActive = checkIsActive(link.path);
|
{ALL_MOBILE_LINKS.map((link) => {
|
||||||
return (
|
const isActive = checkIsActive(link.path);
|
||||||
<Link
|
const IconComp = link.icon;
|
||||||
key={link.path}
|
return (
|
||||||
to={link.path}
|
<Link
|
||||||
onClick={() => setIsMobileOpen(false)}
|
key={link.path}
|
||||||
className="px-3.5 py-3 rounded-xl text-xs font-semibold uppercase tracking-wider flex items-center justify-center text-center transition-all min-h-[44px]"
|
to={link.path}
|
||||||
style={{
|
onClick={() => setIsMobileOpen(false)}
|
||||||
fontFamily: 'Poppins, sans-serif',
|
className={`px-3 py-2.5 rounded-xl text-xs font-semibold flex items-center gap-2 transition-all ${
|
||||||
color: isActive ? '#FFFFFF' : '#CBD5E1',
|
isActive
|
||||||
backgroundColor: isActive ? '#F97316' : 'rgba(255, 255, 255, 0.05)',
|
? 'bg-gradient-to-r from-[#FF6B00] to-[#F97316] text-white shadow-sm'
|
||||||
border: isActive ? '1px solid #FB923C' : '1px solid rgba(255, 255, 255, 0.08)',
|
: 'bg-white/5 text-slate-200 hover:bg-white/10 border border-white/5'
|
||||||
}}
|
}`}
|
||||||
>
|
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
|
||||||
{link.label}
|
>
|
||||||
</Link>
|
{IconComp && <IconComp className="w-3.5 h-3.5 opacity-80" />}
|
||||||
);
|
<span className="truncate">{link.label}</span>
|
||||||
})}
|
</Link>
|
||||||
|
);
|
||||||
{/* Mobile RAISE Button */}
|
})}
|
||||||
<Link
|
</div>
|
||||||
to="/raise"
|
|
||||||
onClick={() => setIsMobileOpen(false)}
|
|
||||||
className="col-span-2 mt-1 flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl text-white text-xs font-bold bg-gradient-to-r from-rose-500 to-pink-600 border border-rose-400/40 shadow-md shadow-rose-500/20 transition-all active:scale-95"
|
|
||||||
style={{ fontFamily: 'Poppins, sans-serif' }}
|
|
||||||
>
|
|
||||||
<Sparkles className="w-4 h-4 text-amber-200" />
|
|
||||||
Pitch your Idea - RAISE Incubator
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{/* Mobile Auth Button */}
|
{/* Mobile Auth Button */}
|
||||||
{!isAuthenticated ? (
|
<div className="pt-2 border-t border-white/10">
|
||||||
<button
|
{!isAuthenticated ? (
|
||||||
onClick={() => {
|
<button
|
||||||
loginWithGoogle();
|
onClick={() => {
|
||||||
setIsMobileOpen(false);
|
loginWithGoogle();
|
||||||
}}
|
setIsMobileOpen(false);
|
||||||
className="col-span-2 mt-2 flex items-center justify-center gap-2 px-4 py-3 rounded-xl text-white text-xs font-semibold border border-orange-500/40 bg-orange-500/10 hover:bg-orange-500/20 transition-all cursor-pointer"
|
}}
|
||||||
style={{ fontFamily: 'Poppins, sans-serif' }}
|
className="w-full flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl text-white text-xs font-semibold border border-orange-500/40 bg-orange-500/15 hover:bg-orange-500/25 transition-all cursor-pointer"
|
||||||
>
|
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
|
||||||
<LogIn className="w-4 h-4" />
|
>
|
||||||
Sign In with Google
|
<LogIn className="w-4 h-4 text-orange-400" />
|
||||||
</button>
|
Sign In with Google
|
||||||
) : (
|
</button>
|
||||||
<button
|
) : (
|
||||||
onClick={() => {
|
<button
|
||||||
logout();
|
onClick={() => {
|
||||||
setIsMobileOpen(false);
|
logout();
|
||||||
}}
|
setIsMobileOpen(false);
|
||||||
className="col-span-2 mt-2 flex items-center justify-center gap-2 px-4 py-3 rounded-xl text-red-400 text-xs font-semibold border border-red-500/30 bg-red-500/10 hover:bg-red-500/20 transition-all cursor-pointer"
|
}}
|
||||||
style={{ fontFamily: 'Poppins, sans-serif' }}
|
className="w-full flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl text-red-400 text-xs font-semibold border border-red-500/30 bg-red-500/10 hover:bg-red-500/20 transition-all cursor-pointer"
|
||||||
>
|
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
|
||||||
<LogOut className="w-4 h-4" />
|
>
|
||||||
Sign Out ({user?.name.split(' ')[0]})
|
<LogOut className="w-4 h-4" />
|
||||||
</button>
|
Sign Out ({user?.name.split(' ')[0]})
|
||||||
)}
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</motion.nav>
|
</motion.nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
{/* Separate Top Right Section: Sign In on Top, RAISE Incubator Below */}
|
{/* Spacer to prevent content overlap */}
|
||||||
<motion.div
|
<div className="h-20" />
|
||||||
initial={{ y: -100, opacity: 0 }}
|
|
||||||
animate={{ y: 0, opacity: 1 }}
|
|
||||||
transition={{ duration: 0.6, ease: 'easeOut', delay: 0.05 }}
|
|
||||||
className="hidden md:flex flex-col items-center gap-2 pointer-events-auto shrink-0"
|
|
||||||
>
|
|
||||||
{/* Sign Option (Top Right, Centered) */}
|
|
||||||
{isAuthenticated && user ? (
|
|
||||||
<div className="relative" ref={profileRef}>
|
|
||||||
<button
|
|
||||||
onClick={() => setIsProfileOpen(!isProfileOpen)}
|
|
||||||
className="flex items-center justify-center gap-2 px-4 py-2 rounded-xl bg-slate-900/90 backdrop-blur-md border border-slate-700/80 hover:border-orange-500/50 hover:bg-slate-800/95 shadow-lg transition-all cursor-pointer min-w-[120px]"
|
|
||||||
>
|
|
||||||
{user.pictureUrl ? (
|
|
||||||
<img
|
|
||||||
src={user.pictureUrl}
|
|
||||||
alt={user.name}
|
|
||||||
className="w-6 h-6 rounded-full border border-white/30 object-cover"
|
|
||||||
referrerPolicy="no-referrer"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="w-6 h-6 rounded-full bg-gradient-to-br from-orange-400 to-orange-600 flex items-center justify-center text-white font-bold text-xs">
|
|
||||||
{user.name.charAt(0).toUpperCase()}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<span className="text-white text-xs lg:text-sm font-semibold max-w-[100px] truncate" style={{ fontFamily: 'Poppins, sans-serif' }}>
|
|
||||||
{user.name.split(' ')[0]}
|
|
||||||
</span>
|
|
||||||
<ChevronDown className="w-4 h-4 text-slate-400" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* Profile Dropdown */}
|
|
||||||
<AnimatePresence>
|
|
||||||
{isProfileOpen && (
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0, y: 8, scale: 0.95 }}
|
|
||||||
animate={{ opacity: 1, y: 0, scale: 1 }}
|
|
||||||
exit={{ opacity: 0, y: 8, scale: 0.95 }}
|
|
||||||
transition={{ duration: 0.15 }}
|
|
||||||
className="absolute right-0 top-full mt-2 w-64 bg-slate-900 border border-slate-700 rounded-2xl shadow-2xl overflow-hidden z-50"
|
|
||||||
>
|
|
||||||
<div className="p-4 border-b border-slate-700/60">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
{user.pictureUrl ? (
|
|
||||||
<img
|
|
||||||
src={user.pictureUrl}
|
|
||||||
alt={user.name}
|
|
||||||
className="w-10 h-10 rounded-full border-2 border-orange-500/40 object-cover"
|
|
||||||
referrerPolicy="no-referrer"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-orange-400 to-orange-600 flex items-center justify-center text-white font-bold text-lg">
|
|
||||||
{user.name.charAt(0).toUpperCase()}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<p className="text-white text-sm font-bold truncate">{user.name}</p>
|
|
||||||
<p className="text-slate-400 text-[11px] truncate">{user.email}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isVerifiedStudent ? (
|
|
||||||
<div className="mt-3 flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg bg-emerald-500/15 border border-emerald-500/25">
|
|
||||||
<ShieldCheck className="w-3.5 h-3.5 text-emerald-400" />
|
|
||||||
<span className="text-[11px] font-bold text-emerald-400">Verified RIT Student</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="mt-3 flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg bg-amber-500/15 border border-amber-500/25">
|
|
||||||
<span className="text-[11px] font-semibold text-amber-400">
|
|
||||||
Sign in with @ritchennai.edu.in for verified badge
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="p-2">
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
logout();
|
|
||||||
setIsProfileOpen(false);
|
|
||||||
}}
|
|
||||||
className="w-full flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-slate-300 hover:text-white hover:bg-red-500/15 text-xs font-semibold transition-all cursor-pointer"
|
|
||||||
>
|
|
||||||
<LogOut className="w-4 h-4" />
|
|
||||||
Sign Out
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
)}
|
|
||||||
</AnimatePresence>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.03 }}
|
|
||||||
whileTap={{ scale: 0.97 }}
|
|
||||||
onClick={loginWithGoogle}
|
|
||||||
className="flex items-center justify-center gap-2 px-5 py-2 rounded-xl text-white text-xs lg:text-sm font-bold bg-slate-900/90 backdrop-blur-md border border-slate-700/80 hover:border-orange-500/60 hover:bg-slate-800/95 shadow-lg shadow-black/30 transition-all cursor-pointer min-w-[120px]"
|
|
||||||
style={{ fontFamily: 'Poppins, sans-serif' }}
|
|
||||||
>
|
|
||||||
<LogIn className="w-4 h-4 text-orange-400" />
|
|
||||||
<span className="text-white">Sign In</span>
|
|
||||||
</motion.button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* RAISE Incubator Module (Below Sign Option) */}
|
|
||||||
<Link
|
|
||||||
to="/raise"
|
|
||||||
className="flex items-center justify-center gap-1.5 px-3.5 py-1.5 lg:px-4 lg:py-2 rounded-xl bg-gradient-to-r from-rose-500 via-pink-500 to-rose-600 hover:from-rose-600 hover:to-pink-600 text-white text-[10.5px] lg:text-[11.5px] font-bold shadow-lg shadow-rose-500/25 border border-white/20 transition-all duration-200 hover:scale-[1.03] active:scale-95 whitespace-nowrap"
|
|
||||||
style={{ fontFamily: 'Poppins, sans-serif' }}
|
|
||||||
>
|
|
||||||
<Sparkles className="w-3.5 h-3.5 text-amber-200 shrink-0" />
|
|
||||||
<span>Pitch your Idea - RAISE Incubator</span>
|
|
||||||
</Link>
|
|
||||||
</motion.div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Spacer */}
|
|
||||||
<div className="h-24" />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,6 +116,15 @@ export const FEATURES: Feature[] = [
|
|||||||
color: '#F59E0B',
|
color: '#F59E0B',
|
||||||
bgColor: '#FFFBEB',
|
bgColor: '#FFFBEB',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'raise',
|
||||||
|
icon: 'Rocket',
|
||||||
|
title: 'RAISE Incubator',
|
||||||
|
description: 'Pitch startup ideas, get incubation support, seed funding opportunities, and entrepreneurial mentoring.',
|
||||||
|
path: '/raise',
|
||||||
|
color: '#EC4899',
|
||||||
|
bgColor: '#FDF2F8',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// ─── Bus Routes ──────────────────────────────────────────────────────────────
|
// ─── Bus Routes ──────────────────────────────────────────────────────────────
|
||||||
@@ -3106,7 +3115,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Harish A (CSE)',
|
vicePresidentName: 'Harish A (CSE)',
|
||||||
coordinatorName: 'Pradakshina Rajesh Bhavani (AI&DS)',
|
coordinatorName: 'Pradakshina Rajesh Bhavani (AI&DS)',
|
||||||
year: 'Senior Year',
|
year: 'Senior Year',
|
||||||
contactEmail: '',
|
contactEmail: 'nationalservicescheme@ritchennai.edu.in',
|
||||||
contactPhone: '+91 8668130804',
|
contactPhone: '+91 8668130804',
|
||||||
icon: 'HeartHandshake',
|
icon: 'HeartHandshake',
|
||||||
logoUrl: '/clubs/nss.png',
|
logoUrl: '/clubs/nss.png',
|
||||||
@@ -3125,7 +3134,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Supeckshana M (CSE)',
|
vicePresidentName: 'Supeckshana M (CSE)',
|
||||||
coordinatorName: 'Dr. S. Niranjana (VLSI)',
|
coordinatorName: 'Dr. S. Niranjana (VLSI)',
|
||||||
year: 'Senior Year',
|
year: 'Senior Year',
|
||||||
contactEmail: '',
|
contactEmail: 'ubarit@ritchennai.edu.in',
|
||||||
contactPhone: '+91 7305642992',
|
contactPhone: '+91 7305642992',
|
||||||
icon: 'Users',
|
icon: 'Users',
|
||||||
logoUrl: '/clubs/unnat_bharat.png',
|
logoUrl: '/clubs/unnat_bharat.png',
|
||||||
@@ -3144,7 +3153,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Danish (VLSI)',
|
vicePresidentName: 'Danish (VLSI)',
|
||||||
coordinatorName: 'Rotaract Faculty Lead',
|
coordinatorName: 'Rotaract Faculty Lead',
|
||||||
year: 'Senior Year',
|
year: 'Senior Year',
|
||||||
contactEmail: '',
|
contactEmail: 'rotaract@ritchennai.edu.in',
|
||||||
contactPhone: '+91 7200696226',
|
contactPhone: '+91 7200696226',
|
||||||
icon: 'HeartHandshake',
|
icon: 'HeartHandshake',
|
||||||
logoUrl: '/clubs/rotaract.png',
|
logoUrl: '/clubs/rotaract.png',
|
||||||
@@ -3163,7 +3172,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Julinsa (CSE)',
|
vicePresidentName: 'Julinsa (CSE)',
|
||||||
coordinatorName: 'Niraimathy Marthandan (Biotech)',
|
coordinatorName: 'Niraimathy Marthandan (Biotech)',
|
||||||
year: 'Senior Year',
|
year: 'Senior Year',
|
||||||
contactEmail: '',
|
contactEmail: 'youthredcross@ritchennai.edu.in',
|
||||||
contactPhone: '+91 9344061221',
|
contactPhone: '+91 9344061221',
|
||||||
icon: 'Heart',
|
icon: 'Heart',
|
||||||
logoUrl: '/clubs/nss.png',
|
logoUrl: '/clubs/nss.png',
|
||||||
@@ -3182,7 +3191,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Indhuvadhani M (ECE)',
|
vicePresidentName: 'Indhuvadhani M (ECE)',
|
||||||
coordinatorName: 'Ms. N. Suganthi (ECE)',
|
coordinatorName: 'Ms. N. Suganthi (ECE)',
|
||||||
year: 'Third Year',
|
year: 'Third Year',
|
||||||
contactEmail: '',
|
contactEmail: 'womenempowermentcell@ritchennai.edu.in',
|
||||||
contactPhone: '+91 6385227710',
|
contactPhone: '+91 6385227710',
|
||||||
icon: 'Sparkles',
|
icon: 'Sparkles',
|
||||||
logoUrl: '/clubs/wec.png',
|
logoUrl: '/clubs/wec.png',
|
||||||
@@ -3201,7 +3210,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Pradakshina RB',
|
vicePresidentName: 'Pradakshina RB',
|
||||||
coordinatorName: 'YUVA Faculty Lead',
|
coordinatorName: 'YUVA Faculty Lead',
|
||||||
year: 'Senior Year',
|
year: 'Senior Year',
|
||||||
contactEmail: '',
|
contactEmail: 'ciiyiclub@ritchennai.edu.in',
|
||||||
contactPhone: '+91 9677043565',
|
contactPhone: '+91 9677043565',
|
||||||
icon: 'Rocket',
|
icon: 'Rocket',
|
||||||
logoUrl: '/clubs/yuva.png',
|
logoUrl: '/clubs/yuva.png',
|
||||||
@@ -3220,7 +3229,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Keerthana S (III CSE)',
|
vicePresidentName: 'Keerthana S (III CSE)',
|
||||||
coordinatorName: 'Dr. J. Anitha (H&S)',
|
coordinatorName: 'Dr. J. Anitha (H&S)',
|
||||||
year: 'III Year, CSE',
|
year: 'III Year, CSE',
|
||||||
contactEmail: '',
|
contactEmail: 'wistemclub@ritchennai.edu.in',
|
||||||
contactPhone: '+91 9600110150',
|
contactPhone: '+91 9600110150',
|
||||||
icon: 'Atom',
|
icon: 'Atom',
|
||||||
logoUrl: '/clubs/wistem.png',
|
logoUrl: '/clubs/wistem.png',
|
||||||
@@ -3240,7 +3249,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Barath S (AIML)',
|
vicePresidentName: 'Barath S (AIML)',
|
||||||
coordinatorName: 'Prof. Avudai Selvi (CSE)',
|
coordinatorName: 'Prof. Avudai Selvi (CSE)',
|
||||||
year: 'Senior Year',
|
year: 'Senior Year',
|
||||||
contactEmail: '',
|
contactEmail: 'techspark@ritchennai.edu.in',
|
||||||
contactPhone: '+91 8667466390',
|
contactPhone: '+91 8667466390',
|
||||||
icon: 'Zap',
|
icon: 'Zap',
|
||||||
logoUrl: '/clubs/techspark.png',
|
logoUrl: '/clubs/techspark.png',
|
||||||
@@ -3259,7 +3268,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Sanjay Y (III ECE)',
|
vicePresidentName: 'Sanjay Y (III ECE)',
|
||||||
coordinatorName: 'Dr. A. Thilagavathy Vinoth (H&S)',
|
coordinatorName: 'Dr. A. Thilagavathy Vinoth (H&S)',
|
||||||
year: 'Third Year',
|
year: 'Third Year',
|
||||||
contactEmail: '',
|
contactEmail: 'infinitus@ritchennai.edu.in',
|
||||||
contactPhone: '+91 8838552068',
|
contactPhone: '+91 8838552068',
|
||||||
icon: 'Calculator',
|
icon: 'Calculator',
|
||||||
logoUrl: '/clubs/infinitus.png',
|
logoUrl: '/clubs/infinitus.png',
|
||||||
@@ -3278,7 +3287,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Abhi Harish (III VLSI)',
|
vicePresidentName: 'Abhi Harish (III VLSI)',
|
||||||
coordinatorName: 'Mr. Mohan Ram (AP/ECE)',
|
coordinatorName: 'Mr. Mohan Ram (AP/ECE)',
|
||||||
year: 'III Year, VLSI',
|
year: 'III Year, VLSI',
|
||||||
contactEmail: '',
|
contactEmail: 'steam@ritchennai.edu.in',
|
||||||
contactPhone: '+91 7550271227',
|
contactPhone: '+91 7550271227',
|
||||||
icon: 'Printer',
|
icon: 'Printer',
|
||||||
logoUrl: '/clubs/steam.png',
|
logoUrl: '/clubs/steam.png',
|
||||||
@@ -3297,7 +3306,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Jonathi (III AIDS)',
|
vicePresidentName: 'Jonathi (III AIDS)',
|
||||||
coordinatorName: 'Prof. Shri Janani P (CSE)',
|
coordinatorName: 'Prof. Shri Janani P (CSE)',
|
||||||
year: 'III Year, CSE',
|
year: 'III Year, CSE',
|
||||||
contactEmail: '',
|
contactEmail: 'culturalcell@ritchennai.edu.in',
|
||||||
contactPhone: '+91 9025726185',
|
contactPhone: '+91 9025726185',
|
||||||
icon: 'Sparkles',
|
icon: 'Sparkles',
|
||||||
logoUrl: '/clubs/artist_league.png',
|
logoUrl: '/clubs/artist_league.png',
|
||||||
@@ -3316,7 +3325,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Sarvesh V (III VLSI)',
|
vicePresidentName: 'Sarvesh V (III VLSI)',
|
||||||
coordinatorName: 'Mr. Keerthana Pandiyan (Biotech)',
|
coordinatorName: 'Mr. Keerthana Pandiyan (Biotech)',
|
||||||
year: 'III Year, AIML',
|
year: 'III Year, AIML',
|
||||||
contactEmail: '',
|
contactEmail: 'vaarithitamilclub@ritchennai.edu.in',
|
||||||
contactPhone: '+91 8248701027',
|
contactPhone: '+91 8248701027',
|
||||||
icon: 'BookOpen',
|
icon: 'BookOpen',
|
||||||
logoUrl: '/clubs/vaarithi.png',
|
logoUrl: '/clubs/vaarithi.png',
|
||||||
@@ -3335,7 +3344,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Farren Jeanvil',
|
vicePresidentName: 'Farren Jeanvil',
|
||||||
coordinatorName: 'Ms. Roopavathi',
|
coordinatorName: 'Ms. Roopavathi',
|
||||||
year: 'Third Year',
|
year: 'Third Year',
|
||||||
contactEmail: '',
|
contactEmail: 'nippon@ritchennai.edu.in',
|
||||||
contactPhone: '+91 9042294493',
|
contactPhone: '+91 9042294493',
|
||||||
icon: 'Globe',
|
icon: 'Globe',
|
||||||
logoUrl: '/clubs/nippon.png',
|
logoUrl: '/clubs/nippon.png',
|
||||||
@@ -3354,7 +3363,7 @@ export const CLUBS_DATA: Club[] = [
|
|||||||
vicePresidentName: 'Thirumalai (III VLSI)',
|
vicePresidentName: 'Thirumalai (III VLSI)',
|
||||||
coordinatorName: 'Ms. Fouzia Sulthana (AIDS)',
|
coordinatorName: 'Ms. Fouzia Sulthana (AIDS)',
|
||||||
year: 'III Year, ECE',
|
year: 'III Year, ECE',
|
||||||
contactEmail: '',
|
contactEmail: 'podxrit@ritchennai.edu.in',
|
||||||
contactPhone: '+91 9962840247',
|
contactPhone: '+91 9962840247',
|
||||||
icon: 'Mic',
|
icon: 'Mic',
|
||||||
logoUrl: '/clubs/podx.png',
|
logoUrl: '/clubs/podx.png',
|
||||||
|
|||||||
@@ -139,19 +139,6 @@ export default function AIAssistant() {
|
|||||||
<Send className="w-3.5 h-3.5" />
|
<Send className="w-3.5 h-3.5" />
|
||||||
<span className="hidden sm:inline">Telegram Bot</span>
|
<span className="hidden sm:inline">Telegram Bot</span>
|
||||||
</a>
|
</a>
|
||||||
<a
|
|
||||||
href="https://discord.com/api/oauth2/authorize?client_id=1476942599167414506&permissions=8&scope=bot"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="flex items-center gap-1.5 px-3 py-2 rounded-xl text-xs font-medium text-white transition-all shadow-xs hover:brightness-105 active:scale-95"
|
|
||||||
style={{
|
|
||||||
background: 'linear-gradient(135deg, #5865F2, #7289DA)',
|
|
||||||
fontFamily: 'Inter, sans-serif'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Bot className="w-3.5 h-3.5" />
|
|
||||||
<span className="hidden sm:inline">Discord Bot</span>
|
|
||||||
</a>
|
|
||||||
<motion.button
|
<motion.button
|
||||||
whileHover={{ scale: 1.03 }}
|
whileHover={{ scale: 1.03 }}
|
||||||
whileTap={{ scale: 0.97 }}
|
whileTap={{ scale: 0.97 }}
|
||||||
|
|||||||
@@ -32,38 +32,37 @@ const BLUEPRINT_LEGEND = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const LOCATION_SPOTS: LocationSpot[] = [
|
const LOCATION_SPOTS: LocationSpot[] = [
|
||||||
{ name: 'Girls Hostel', x: 16.0, y: 8.5, category: 'Hostel / Mess', desc: 'Residential Quarter for Women', area: 'Top Left' },
|
{ name: 'Girls Hostel', x: 29.4, y: 7.3, category: 'Hostel / Mess', desc: 'Residential Quarter for Women', area: 'Top Left' },
|
||||||
{ name: 'Playground', x: 40.0, y: 11.0, category: 'Amenity', desc: 'Main Sports & Athletic Field', area: 'Top Center' },
|
{ name: 'Playground', x: 48.5, y: 8.8, category: 'Amenity', desc: 'Main Sports & Athletic Field', area: 'Top Center' },
|
||||||
{ name: 'Canteen', x: 67.0, y: 8.5, category: 'Amenity', desc: 'Food Court & Refreshments', area: 'Top Right' },
|
{ name: 'Canteen', x: 68.3, y: 8.3, category: 'Amenity', desc: 'Food Court & Refreshments', area: 'Top Right' },
|
||||||
{ name: 'Steve Jobs Block', x: 16.0, y: 21.0, category: 'Admin / Facility', desc: 'Auditorium, Computer Labs & Staff Rooms', area: 'West Wing' },
|
{ name: 'Steve Jobs Block', x: 27.2, y: 17.1, category: 'Admin / Facility', desc: 'Auditorium, Computer Labs & Staff Rooms', area: 'West Wing' },
|
||||||
{ name: 'Girls Mess', x: 14.5, y: 31.0, category: 'Hostel / Mess', desc: 'Dedicated Dining Hall for Women', area: 'West Wing' },
|
{ name: 'Girls Mess', x: 25.7, y: 25.9, category: 'Hostel / Mess', desc: 'Dedicated Dining Hall for Women', area: 'West Wing' },
|
||||||
{ name: 'MEC Lab', x: 14.0, y: 39.5, category: 'Lab / Academic', desc: 'Mechanical Engineering Laboratory', area: 'West Wing' },
|
{ name: 'MEC Lab', x: 23.5, y: 34.7, category: 'Lab / Academic', desc: 'Mechanical Engineering Laboratory', area: 'West Wing' },
|
||||||
{ name: 'EPL Lab', x: 13.0, y: 47.5, category: 'Lab / Academic', desc: 'Engineering Practices Laboratory', area: 'West Wing' },
|
{ name: 'EPL Lab', x: 21.3, y: 43.5, category: 'Lab / Academic', desc: 'Engineering Practices Laboratory', area: 'West Wing' },
|
||||||
{ name: 'Nikola Tesla Block', x: 40.5, y: 36.5, category: 'B Block', desc: 'Main Electrical & Engineering Research Block', area: 'Central Block' },
|
{ name: 'Astronaut Statue', x: 51.4, y: 16.1, category: 'Amenity', desc: 'Iconic Landmark Monument', area: 'Central Park' },
|
||||||
{ name: 'Astronaut Statue', x: 51.0, y: 23.0, category: 'Amenity', desc: 'Iconic Landmark Monument', area: 'Central Park' },
|
{ name: 'Campus Centre', x: 65.3, y: 20.5, category: 'Admin / Facility', desc: 'Student Activity & Administrative Center', area: 'North East' },
|
||||||
{ name: 'Campus Centre', x: 67.0, y: 23.0, category: 'Admin / Facility', desc: 'Student Activity & Administrative Center', area: 'North East' },
|
{ name: 'Nikola Tesla Block', x: 43.3, y: 34.7, category: 'B Block', desc: 'Main Electrical & Engineering Research Block', area: 'Central Block' },
|
||||||
{ name: 'Hut', x: 70.0, y: 37.0, category: 'Amenity', desc: 'Student Discussion & Rest Area', area: 'Central East' },
|
{ name: 'Hut', x: 66.8, y: 31.3, category: 'Amenity', desc: 'Student Discussion & Rest Area', area: 'Central East' },
|
||||||
{ name: 'Homi Jehangir Bhabha Block', x: 87.0, y: 24.0, category: 'A Block', desc: 'Science & Physics Research Block', area: 'East Wing' },
|
{ name: 'Homi Jehangir Bhabha Block', x: 79.3, y: 22.9, category: 'A Block', desc: 'Science & Physics Research Block', area: 'East Wing' },
|
||||||
{ name: 'Boys Mess', x: 87.0, y: 17.5, category: 'Hostel / Mess', desc: 'Ground Floor Dining Hall for Men (Homi Bhabha Block)', area: 'East Wing' },
|
{ name: 'C Block', x: 78.6, y: 37.6, category: 'C Block', desc: 'Labs, Classrooms & Staff Rooms', area: 'East Wing' },
|
||||||
{ name: 'Staff Mess', x: 87.0, y: 30.5, category: 'Amenity', desc: 'First Floor Dining Hall for Faculty & Staff (Homi Bhabha Block)', area: 'East Wing' },
|
{ name: 'Alan Turing Block', x: 66.8, y: 50.8, category: 'Lab / Academic', desc: 'Computer Science & AI Labs', area: 'East Wing' },
|
||||||
{ name: 'C Block', x: 87.0, y: 38.0, category: 'C Block', desc: 'Labs, Classrooms & Staff Rooms', area: 'East Wing' },
|
{ name: 'Arcade', x: 36.7, y: 53.2, category: 'Amenity', desc: 'Student Hub & Stores', area: 'Central South' },
|
||||||
{ name: 'Alan Turing Block', x: 77.0, y: 53.0, category: 'Lab / Academic', desc: 'Computer Science & AI Labs', area: 'East Wing' },
|
{ name: 'Green Building', x: 27.2, y: 63.5, category: 'Admin / Facility', desc: 'Eco-Friendly Academic Complex & Library', area: 'South West' },
|
||||||
{ name: 'Arcade', x: 35.0, y: 57.0, category: 'Amenity', desc: 'Student Hub & Stores', area: 'Central South' },
|
{ name: 'Security Booth', x: 44.8, y: 65.9, category: 'Amenity', desc: 'Main Gate Security Checkpoint', area: 'South Entrance' },
|
||||||
{ name: 'Green Building', x: 23.0, y: 71.0, category: 'Admin / Facility', desc: 'Eco-Friendly Academic Complex & Library', area: 'South West' },
|
{ name: 'ATM', x: 44.3, y: 73.0, category: 'Amenity', desc: '24/7 Campus Banking ATM', area: 'South Entrance' },
|
||||||
{ name: 'Security Booth', x: 49.0, y: 77.0, category: 'Amenity', desc: 'Main Gate Security Checkpoint', area: 'South Entrance' },
|
{ name: 'Bike Parking', x: 65.3, y: 61.5, category: 'Parking Area', desc: 'Two-Wheeler Vehicle Parking', area: 'South East' },
|
||||||
{ name: 'ATM', x: 49.0, y: 86.0, category: 'Amenity', desc: '24/7 Campus Banking ATM', area: 'South Entrance' },
|
{ name: 'Generator Room', x: 63.1, y: 71.8, category: 'Admin / Facility', desc: 'Campus Power & Utility Station', area: 'South East' },
|
||||||
{ name: 'Bike Parking', x: 75.0, y: 67.0, category: 'Parking Area', desc: 'Two-Wheeler Vehicle Parking', area: 'South East' },
|
{ name: 'Side Gate', x: 22.8, y: 75.0, category: 'Amenity', desc: 'Secondary Campus Gate', area: 'South West' },
|
||||||
{ name: 'Generator Room', x: 76.0, y: 81.0, category: 'Admin / Facility', desc: 'Campus Power & Utility Station', area: 'South East' },
|
{ name: 'Main Gate', x: 49.2, y: 77.9, category: 'Amenity', desc: 'Primary Campus Entrance', area: 'South Entrance' },
|
||||||
{ name: 'Side Gate', x: 23.0, y: 93.0, category: 'Amenity', desc: 'Secondary Campus Gate', area: 'South West' },
|
{ name: 'Bus Stop', x: 49.6, y: 92.8, category: 'Amenity', desc: 'Public & College Bus Boarding Point', area: 'Highway' },
|
||||||
{ name: 'Main Gate', x: 59.0, y: 93.0, category: 'Amenity', desc: 'Primary Campus Entrance', area: 'South Entrance' },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const CATEGORY_MAP_SPOTS: Record<string, string[]> = {
|
const CATEGORY_MAP_SPOTS: Record<string, string[]> = {
|
||||||
dept: ['Homi Jehangir Bhabha Block', 'Nikola Tesla Block', 'C Block', 'Steve Jobs Block', 'Alan Turing Block', 'Green Building'],
|
dept: ['Homi Jehangir Bhabha Block', 'Nikola Tesla Block', 'C Block', 'Steve Jobs Block', 'Alan Turing Block', 'Green Building'],
|
||||||
library: ['Green Building'],
|
library: ['Green Building'],
|
||||||
labs: ['MEC Lab', 'EPL Lab', 'Steve Jobs Block', 'Alan Turing Block', 'Nikola Tesla Block', 'C Block'],
|
labs: ['MEC Lab', 'EPL Lab', 'Steve Jobs Block', 'Alan Turing Block', 'Nikola Tesla Block', 'C Block'],
|
||||||
hostel: ['Girls Hostel', 'Boys Mess'],
|
hostel: ['Girls Hostel', 'Girls Mess'],
|
||||||
canteen: ['Canteen', 'Girls Mess', 'Boys Mess', 'Staff Mess', 'Arcade'],
|
canteen: ['Canteen', 'Girls Mess', 'Arcade'],
|
||||||
auditorium: ['Steve Jobs Block', 'Campus Centre'],
|
auditorium: ['Steve Jobs Block', 'Campus Centre'],
|
||||||
sports: ['Playground'],
|
sports: ['Playground'],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ export interface CollabRequestItem {
|
|||||||
acceptedCount?: number;
|
acceptedCount?: number;
|
||||||
contactInfo?: string;
|
contactInfo?: string;
|
||||||
telegramChatId?: number;
|
telegramChatId?: number;
|
||||||
discordUserId?: string;
|
|
||||||
status: string;
|
status: string;
|
||||||
applicationsCount: number;
|
applicationsCount: number;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
|||||||
@@ -90,19 +90,6 @@ export default function Home() {
|
|||||||
Ask Assistant
|
Ask Assistant
|
||||||
</motion.button>
|
</motion.button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link to="/raise">
|
|
||||||
<motion.button
|
|
||||||
whileHover={{ scale: 1.04, boxShadow: '0 10px 30px -5px rgba(233,30,99,0.5)' }}
|
|
||||||
whileTap={{ scale: 0.96 }}
|
|
||||||
className="flex items-center gap-2 px-8 py-3.5 rounded-xl font-bold text-sm text-white bg-gradient-to-r from-rose-500 to-pink-600 border border-white/30 backdrop-blur-md transition-all cursor-pointer shadow-lg shadow-pink-500/20"
|
|
||||||
style={{ fontFamily: 'Plus Jakarta Sans, sans-serif' }}
|
|
||||||
>
|
|
||||||
<Sparkles className="w-4 h-4 text-amber-200" />
|
|
||||||
Pitch your Idea - RAISE
|
|
||||||
<ArrowRight className="w-4 h-4" />
|
|
||||||
</motion.button>
|
|
||||||
</Link>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ const CS_TOOLS_CATEGORIES = [
|
|||||||
bg: '#FEFCE8',
|
bg: '#FEFCE8',
|
||||||
tools: [
|
tools: [
|
||||||
{ name: 'Documentation', desc: 'Confluence, Notion, MkDocs, and Docusaurus.', url: 'https://docusaurus.io/' },
|
{ name: 'Documentation', desc: 'Confluence, Notion, MkDocs, and Docusaurus.', url: 'https://docusaurus.io/' },
|
||||||
{ name: 'Team Communication', desc: 'Slack, Microsoft Teams, Discord, and Mattermost.', url: 'https://slack.com/' },
|
{ name: 'Team Communication', desc: 'Slack, Microsoft Teams, and Mattermost.', url: 'https://slack.com/' },
|
||||||
{ name: 'Project Management', desc: 'Jira, Trello, Asana, and Linear ticket tracking.', url: 'https://linear.app/' },
|
{ name: 'Project Management', desc: 'Jira, Trello, Asana, and Linear ticket tracking.', url: 'https://linear.app/' },
|
||||||
{ name: 'Whiteboarding', desc: 'Miro, FigJam, Excalidraw, and Lucidchart.', url: 'https://excalidraw.com/' },
|
{ name: 'Whiteboarding', desc: 'Miro, FigJam, Excalidraw, and Lucidchart.', url: 'https://excalidraw.com/' },
|
||||||
{ name: 'Knowledge Management', desc: 'Obsidian, Notion, and Slite workspace wiki.', url: 'https://obsidian.md/' }
|
{ name: 'Knowledge Management', desc: 'Obsidian, Notion, and Slite workspace wiki.', url: 'https://obsidian.md/' }
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ const CS_TOOLS_CATEGORIES = [
|
|||||||
bg: '#FEFCE8',
|
bg: '#FEFCE8',
|
||||||
tools: [
|
tools: [
|
||||||
{ name: 'Documentation', desc: 'Confluence, Notion, MkDocs, and Docusaurus.', url: 'https://docusaurus.io/' },
|
{ name: 'Documentation', desc: 'Confluence, Notion, MkDocs, and Docusaurus.', url: 'https://docusaurus.io/' },
|
||||||
{ name: 'Team Communication', desc: 'Slack, Microsoft Teams, Discord, and Mattermost.', url: 'https://slack.com/' },
|
{ name: 'Team Communication', desc: 'Slack, Microsoft Teams, and Mattermost.', url: 'https://slack.com/' },
|
||||||
{ name: 'Project Management', desc: 'Jira, Trello, Asana, and Linear ticket tracking.', url: 'https://linear.app/' },
|
{ name: 'Project Management', desc: 'Jira, Trello, Asana, and Linear ticket tracking.', url: 'https://linear.app/' },
|
||||||
{ name: 'Whiteboarding', desc: 'Miro, FigJam, Excalidraw, and Lucidchart.', url: 'https://excalidraw.com/' },
|
{ name: 'Whiteboarding', desc: 'Miro, FigJam, Excalidraw, and Lucidchart.', url: 'https://excalidraw.com/' },
|
||||||
{ name: 'Knowledge Management', desc: 'Obsidian, Notion, and Slite workspace wiki.', url: 'https://obsidian.md/' }
|
{ name: 'Knowledge Management', desc: 'Obsidian, Notion, and Slite workspace wiki.', url: 'https://obsidian.md/' }
|
||||||
@@ -766,7 +766,20 @@ export default function Toolkit() {
|
|||||||
const [calculationType, setCalculationType] = useState<'gpa' | 'internal' | null>(null);
|
const [calculationType, setCalculationType] = useState<'gpa' | 'internal' | null>(null);
|
||||||
const [gpaStep, setGpaStep] = useState<1 | 2>(1);
|
const [gpaStep, setGpaStep] = useState<1 | 2>(1);
|
||||||
const [calculatedScore, setCalculatedScore] = useState<string | null>(null);
|
const [calculatedScore, setCalculatedScore] = useState<string | null>(null);
|
||||||
const [subjects, setSubjects] = useState([
|
interface SubjectItem {
|
||||||
|
name: string;
|
||||||
|
credits: number;
|
||||||
|
grade: string;
|
||||||
|
cat1: string;
|
||||||
|
cat2: string;
|
||||||
|
assignment: string;
|
||||||
|
attendance: string;
|
||||||
|
isElective?: boolean;
|
||||||
|
electiveSlot?: number;
|
||||||
|
electiveCode?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [subjects, setSubjects] = useState<SubjectItem[]>([
|
||||||
{ name: 'Subject 1', credits: 4, grade: 'O', cat1: '45', cat2: '48', assignment: '10', attendance: '95' },
|
{ name: 'Subject 1', credits: 4, grade: 'O', cat1: '45', cat2: '48', assignment: '10', attendance: '95' },
|
||||||
{ name: 'Subject 2', credits: 3, grade: 'A+', cat1: '42', cat2: '44', assignment: '9', attendance: '92' },
|
{ name: 'Subject 2', credits: 3, grade: 'A+', cat1: '42', cat2: '44', assignment: '9', attendance: '92' },
|
||||||
{ name: 'Subject 3', credits: 3, grade: 'A', cat1: '38', cat2: '40', assignment: '8', attendance: '88' },
|
{ name: 'Subject 3', credits: 3, grade: 'A', cat1: '38', cat2: '40', assignment: '8', attendance: '88' },
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
{
|
{
|
||||||
"community_bot_token": "",
|
"community_bot_token": "",
|
||||||
"telegram_bot_token": "",
|
"telegram_bot_token": "",
|
||||||
"helper_chat_ids": [971749136, 5567776672,1873240361],
|
"helper_chat_ids": [971749136, 5567776672, 1873240361],
|
||||||
"discord_bot_token": "",
|
|
||||||
"discord_helper_user_ids": [789393727641878568],
|
|
||||||
"spring_backend_url": "http://localhost:8085"
|
"spring_backend_url": "http://localhost:8085"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,3 @@ requests>=2.28.0
|
|||||||
fastapi>=0.95.0
|
fastapi>=0.95.0
|
||||||
uvicorn>=0.20.0
|
uvicorn>=0.20.0
|
||||||
pydantic>=2.0
|
pydantic>=2.0
|
||||||
discord.py>=2.3.0
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import time
|
|||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
import asyncio
|
import asyncio
|
||||||
import discord
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
@@ -53,7 +52,6 @@ def load_config():
|
|||||||
config = load_config()
|
config = load_config()
|
||||||
COMMUNITY_BOT_TOKEN = os.environ.get("COMMUNITY_BOT_TOKEN") or config.get("community_bot_token", "8859374355:AAH0dhwstkTBhRerRTjzmb2RG2fjPbigzvo")
|
COMMUNITY_BOT_TOKEN = os.environ.get("COMMUNITY_BOT_TOKEN") or config.get("community_bot_token", "8859374355:AAH0dhwstkTBhRerRTjzmb2RG2fjPbigzvo")
|
||||||
BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN") or config.get("telegram_bot_token")
|
BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN") or config.get("telegram_bot_token")
|
||||||
DISCORD_TOKEN = os.environ.get("DISCORD_BOT_TOKEN") or config.get("discord_bot_token")
|
|
||||||
BACKEND_URL = os.environ.get("SPRING_BACKEND_URL") or config.get("spring_backend_url", "http://localhost:8085")
|
BACKEND_URL = os.environ.get("SPRING_BACKEND_URL") or config.get("spring_backend_url", "http://localhost:8085")
|
||||||
|
|
||||||
# Database Setup
|
# Database Setup
|
||||||
@@ -611,292 +609,8 @@ def telegram_polling_thread():
|
|||||||
logging.error(f"Error in 24/7 chatbot polling loop: {e}")
|
logging.error(f"Error in 24/7 chatbot polling loop: {e}")
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|
||||||
# Discord Action View for Collaboration Applications
|
|
||||||
class DiscordCollabActionView(discord.ui.View):
|
|
||||||
def __init__(self, application_id: int, applicant_contact: str):
|
|
||||||
super().__init__(timeout=86400)
|
|
||||||
self.application_id = application_id
|
|
||||||
self.applicant_contact = applicant_contact
|
|
||||||
|
|
||||||
@discord.ui.button(label="Accept Collaboration", style=discord.ButtonStyle.success, emoji="✅")
|
|
||||||
async def accept_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
||||||
try:
|
|
||||||
requests.put(f"{BACKEND_URL}/api/collab/applications/{self.application_id}/status?status=ACCEPTED", timeout=5)
|
|
||||||
await interaction.response.send_message(
|
|
||||||
f"✅ **Collaboration Request Accepted!**\nDirect contact details: **{self.applicant_contact}**",
|
|
||||||
ephemeral=False
|
|
||||||
)
|
|
||||||
for item in self.children:
|
|
||||||
item.disabled = True
|
|
||||||
await interaction.message.edit(view=self)
|
|
||||||
except Exception as e:
|
|
||||||
await interaction.response.send_message(f"❌ Error updating status: {e}", ephemeral=True)
|
|
||||||
|
|
||||||
@discord.ui.button(label="Decline Request", style=discord.ButtonStyle.danger, emoji="❌")
|
|
||||||
async def decline_button(self, interaction: discord.Interaction, button: discord.ui.Button):
|
|
||||||
try:
|
|
||||||
requests.put(f"{BACKEND_URL}/api/collab/applications/{self.application_id}/status?status=REJECTED", timeout=5)
|
|
||||||
await interaction.response.send_message("❌ **Collaboration Request Declined.**", ephemeral=False)
|
|
||||||
for item in self.children:
|
|
||||||
item.disabled = True
|
|
||||||
await interaction.message.edit(view=self)
|
|
||||||
except Exception as e:
|
|
||||||
await interaction.response.send_message(f"❌ Error updating status: {e}", ephemeral=True)
|
|
||||||
|
|
||||||
async def broadcast_discord_collab_application(application_id: int, project_idea: str, tag: str, applicant_name: str, applicant_dept: str, applicant_year: str, applicant_contact: str, message: str, user_ids: list):
|
|
||||||
formatted_msg = (
|
|
||||||
f"🤝 **New Collaboration Request for your Project!**\n\n"
|
|
||||||
f"📌 **Project Idea:** {project_idea}\n"
|
|
||||||
f"🏷️ **Tag:** `{tag}`\n\n"
|
|
||||||
f"👤 **Applicant:** {applicant_name} ({applicant_dept}, {applicant_year})\n"
|
|
||||||
f"💬 **Message:** {message or 'No message provided'}\n"
|
|
||||||
f"📱 **Contact Info:** {applicant_contact}\n\n"
|
|
||||||
f"Click a button below to respond:"
|
|
||||||
)
|
|
||||||
view = DiscordCollabActionView(application_id, applicant_contact)
|
|
||||||
for user_id_val in user_ids:
|
|
||||||
try:
|
|
||||||
user_id = int(user_id_val)
|
|
||||||
user = await discord_client.fetch_user(user_id)
|
|
||||||
if user:
|
|
||||||
await user.send(content=formatted_msg, view=view)
|
|
||||||
logging.info(f"Sent Discord collab notification DM to user {user_id}")
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Failed to send Discord collab DM to {user_id_val}: {e}")
|
|
||||||
|
|
||||||
# Discord Remove Select View
|
|
||||||
class DiscordCollabRemoveSelect(discord.ui.Select):
|
|
||||||
def __init__(self, active_requests: list):
|
|
||||||
options = []
|
|
||||||
for req in active_requests:
|
|
||||||
req_id = str(req["id"])
|
|
||||||
snippet = req.get("projectIdea", "Project")[:45]
|
|
||||||
options.append(discord.SelectOption(
|
|
||||||
label=f"Cancel: {snippet}",
|
|
||||||
value=req_id,
|
|
||||||
description=f"Tag: {req.get('tag')} | Dept: {req.get('department')}",
|
|
||||||
emoji="❌"
|
|
||||||
))
|
|
||||||
super().__init__(placeholder="Select a request to remove...", min_values=1, max_values=1, options=options)
|
|
||||||
|
|
||||||
async def callback(self, interaction: discord.Interaction):
|
|
||||||
req_id = self.values[0]
|
|
||||||
try:
|
|
||||||
res = requests.delete(f"{BACKEND_URL}/api/collab/{req_id}", timeout=5)
|
|
||||||
if res.status_code in [200, 204]:
|
|
||||||
await interaction.response.send_message("✅ **Collaboration Request cancelled and removed from RIT Dev Hub.**", ephemeral=False)
|
|
||||||
else:
|
|
||||||
await interaction.response.send_message("❌ Failed to cancel request.", ephemeral=True)
|
|
||||||
except Exception as e:
|
|
||||||
await interaction.response.send_message(f"❌ Error: {e}", ephemeral=True)
|
|
||||||
|
|
||||||
class DiscordCollabRemoveView(discord.ui.View):
|
|
||||||
def __init__(self, active_requests: list):
|
|
||||||
super().__init__(timeout=180)
|
|
||||||
self.add_item(DiscordCollabRemoveSelect(active_requests))
|
|
||||||
|
|
||||||
# Discord Native Interactive UI Components (Modal & Dropdown View)
|
|
||||||
class CollabModal(discord.ui.Modal, title="Post Collaboration Request"):
|
|
||||||
author_name = discord.ui.TextInput(label="1) Your Name", placeholder="e.g. Priyan Sharma", required=True)
|
|
||||||
dept_and_year = discord.ui.TextInput(label="2) Dept & Year", placeholder="e.g. CSE, 1st Year", required=True, default="CSE, 1st Year")
|
|
||||||
collaborators_needed = discord.ui.TextInput(label="3) Collaborators Needed", placeholder="e.g. 2", required=True, default="1")
|
|
||||||
project_idea = discord.ui.TextInput(label="4) Project Idea & Details", style=discord.TextStyle.paragraph, placeholder="Describe your project idea...", required=True)
|
|
||||||
github_link = discord.ui.TextInput(label="5) GitHub Link (Optional)", placeholder="https://github.com/...", required=False)
|
|
||||||
|
|
||||||
def __init__(self, tag: str):
|
|
||||||
super().__init__()
|
|
||||||
self.selected_tag = tag
|
|
||||||
|
|
||||||
async def on_submit(self, interaction: discord.Interaction):
|
|
||||||
try:
|
|
||||||
num_needed = int(self.collaborators_needed.value)
|
|
||||||
except ValueError:
|
|
||||||
num_needed = 1
|
|
||||||
|
|
||||||
dept_val = "CSE"
|
|
||||||
year_val = "1st Year"
|
|
||||||
if "," in self.dept_and_year.value:
|
|
||||||
parts = [p.strip() for p in self.dept_and_year.value.split(",", 1)]
|
|
||||||
dept_val = parts[0]
|
|
||||||
year_val = parts[1]
|
|
||||||
else:
|
|
||||||
dept_val = self.dept_and_year.value.strip()
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
"authorName": self.author_name.value,
|
|
||||||
"department": dept_val,
|
|
||||||
"year": year_val,
|
|
||||||
"collaboratorsNeeded": num_needed,
|
|
||||||
"tag": self.selected_tag,
|
|
||||||
"projectIdea": self.project_idea.value,
|
|
||||||
"githubLink": self.github_link.value or None,
|
|
||||||
"contactInfo": f"Discord: {interaction.user.name}",
|
|
||||||
"discordUserId": str(interaction.user.id)
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
res = requests.post(f"{BACKEND_URL}/api/collab", json=payload, timeout=10)
|
|
||||||
if res.status_code in [200, 201]:
|
|
||||||
await interaction.response.send_message(
|
|
||||||
f"🎉 **Collaboration Request posted live to RIT Dev Hub!**\n"
|
|
||||||
f"👤 **Author:** {self.author_name.value} ({dept_val}, {year_val})\n"
|
|
||||||
f"📌 **Project:** {self.project_idea.value}\n"
|
|
||||||
f"🏷️ **Tag:** `{self.selected_tag}`\n"
|
|
||||||
f"👥 **Collaborators Needed:** `{num_needed}`",
|
|
||||||
ephemeral=False
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
await interaction.response.send_message(f"❌ Failed to save request (Status: {res.status_code})", ephemeral=True)
|
|
||||||
except Exception as e:
|
|
||||||
await interaction.response.send_message(f"❌ Error connecting to backend: {e}", ephemeral=True)
|
|
||||||
|
|
||||||
class CollabTagSelect(discord.ui.Select):
|
|
||||||
def __init__(self):
|
|
||||||
options = [
|
|
||||||
discord.SelectOption(
|
|
||||||
label="Co-developing from scratch",
|
|
||||||
value="looking for co-developing a project from scratch",
|
|
||||||
description="Build a brand new project from scratch together",
|
|
||||||
emoji="🚀"
|
|
||||||
),
|
|
||||||
discord.SelectOption(
|
|
||||||
label="Beta Testers needed",
|
|
||||||
value="looking for beta testers",
|
|
||||||
description="Test early builds and provide user feedback",
|
|
||||||
emoji="🧪"
|
|
||||||
),
|
|
||||||
discord.SelectOption(
|
|
||||||
label="Open-Source Contributors",
|
|
||||||
value="looking for Open-source Collaborators/Contributers",
|
|
||||||
description="Open repository seeking PRs and contributors",
|
|
||||||
emoji="🌐"
|
|
||||||
),
|
|
||||||
]
|
|
||||||
super().__init__(placeholder="Scroll down to select a tag for your request...", min_values=1, max_values=1, options=options)
|
|
||||||
|
|
||||||
async def callback(self, interaction: discord.Interaction):
|
|
||||||
selected_tag = self.values[0]
|
|
||||||
modal = CollabModal(tag=selected_tag)
|
|
||||||
await interaction.response.send_modal(modal)
|
|
||||||
|
|
||||||
class CollabView(discord.ui.View):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__(timeout=180)
|
|
||||||
self.add_item(CollabTagSelect())
|
|
||||||
|
|
||||||
# Discord Bot Client Setup
|
|
||||||
intents = discord.Intents.default()
|
|
||||||
intents.messages = True
|
|
||||||
intents.message_content = True
|
|
||||||
|
|
||||||
discord_client = discord.Client(intents=intents)
|
|
||||||
discord_loop = None
|
|
||||||
|
|
||||||
@discord_client.event
|
|
||||||
async def on_ready():
|
|
||||||
logging.info(f"Discord Bot logged in as {discord_client.user}!")
|
|
||||||
|
|
||||||
@discord_client.event
|
|
||||||
async def on_message(message):
|
|
||||||
if message.author == discord_client.user:
|
|
||||||
return
|
|
||||||
|
|
||||||
is_dm = isinstance(message.channel, discord.DMChannel)
|
|
||||||
is_mention = discord_client.user in message.mentions
|
|
||||||
|
|
||||||
if not is_dm and not is_mention:
|
|
||||||
return
|
|
||||||
|
|
||||||
content = message.content
|
|
||||||
if is_mention:
|
|
||||||
mention_str = f"<@{discord_client.user.id}>"
|
|
||||||
mention_nick_str = f"<@!{discord_client.user.id}>"
|
|
||||||
content = content.replace(mention_str, "").replace(mention_nick_str, "").strip()
|
|
||||||
|
|
||||||
# Discord /collab & /remove commands disabled as requested
|
|
||||||
if content.lower().startswith("/collab") or content.lower().startswith("/remove") or content.lower().startswith("/cancel"):
|
|
||||||
await message.reply("ℹ️ **Dev Hub collaboration requests via Discord are currently disabled.** Please use the RIT Dev Hub website (https://rit-services.in/dev-collab) or the Telegram bot (@Ritchatbot_bot).")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check if this message is a reply to a question DM sent to a helper
|
|
||||||
if message.reference and message.reference.message_id:
|
|
||||||
question_id = get_question_id(message.author.id, message.reference.message_id)
|
|
||||||
if question_id:
|
|
||||||
author_name = message.author.display_name or message.author.name or "Senior Helper"
|
|
||||||
logging.info(f"Submitting answer for question {question_id} by Discord helper '{author_name}'")
|
|
||||||
backend_endpoint = f"{BACKEND_URL}/api/questions/{question_id}/answers"
|
|
||||||
answer_payload = {
|
|
||||||
"body": content,
|
|
||||||
"author": author_name
|
|
||||||
}
|
|
||||||
try:
|
|
||||||
def call_backend():
|
|
||||||
return requests.post(backend_endpoint, json=answer_payload, timeout=10)
|
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
res = await loop.run_in_executor(None, call_backend)
|
|
||||||
if res.status_code in [200, 201]:
|
|
||||||
await message.reply("✅ **Answer posted successfully to the Q&A board!**")
|
|
||||||
else:
|
|
||||||
await message.reply(f"❌ **Failed to post answer to backend.** (Status: {res.status_code})")
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Error calling backend endpoint {backend_endpoint}: {e}")
|
|
||||||
await message.reply(f"❌ **Connection error to backend.** ({e})")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Direct query fallback to Go chatbot service
|
|
||||||
if not content.strip():
|
|
||||||
return
|
|
||||||
|
|
||||||
logging.info(f"Querying Go chatbot service for Discord user {message.author.id}: '{content}'")
|
|
||||||
chatbot_service_url = "http://localhost:8081/api/chat"
|
|
||||||
try:
|
|
||||||
def call_chatbot():
|
|
||||||
return requests.post(chatbot_service_url, json={"message": content}, timeout=10)
|
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
res = await loop.run_in_executor(None, call_chatbot)
|
|
||||||
|
|
||||||
if res.status_code == 200:
|
|
||||||
ans_data = res.json()
|
|
||||||
bot_response = ans_data.get("answer", "I am having trouble processing that question.")
|
|
||||||
await message.reply(bot_response)
|
|
||||||
else:
|
|
||||||
logging.error(f"Go chatbot API returned status code {res.status_code}")
|
|
||||||
await message.reply("⚠️ The RIT Chatbot service is currently experiencing issues. Please try again later.")
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Failed to connect to Go chatbot service: {e}")
|
|
||||||
await message.reply("⚠️ I cannot connect to the RIT Chatbot database right now. Please make sure the service is online.")
|
|
||||||
|
|
||||||
async def broadcast_discord_question(question_id: int, title: str, body: str, author: str, user_ids: list):
|
|
||||||
formatted_msg = (
|
|
||||||
f"❓ **New Student Question!**\n\n"
|
|
||||||
f"👤 **Author:** {author}\n"
|
|
||||||
f"📌 **Topic:** {title}\n"
|
|
||||||
f"📝 **Details:** {body}\n\n"
|
|
||||||
f"💬 **Reply directly to this message to submit your answer.**"
|
|
||||||
)
|
|
||||||
for user_id_val in user_ids:
|
|
||||||
try:
|
|
||||||
user_id = int(user_id_val)
|
|
||||||
user = await discord_client.fetch_user(user_id)
|
|
||||||
if user:
|
|
||||||
msg = await user.send(formatted_msg)
|
|
||||||
save_mapping(user_id, msg.id, question_id)
|
|
||||||
logging.info(f"Sent Discord DM to helper {user_id}")
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Failed to send Discord DM to helper {user_id_val}: {e}")
|
|
||||||
|
|
||||||
async def run_discord_bot():
|
|
||||||
global discord_loop
|
|
||||||
logging.info("Starting Discord bot...")
|
|
||||||
discord_loop = asyncio.get_running_loop()
|
|
||||||
try:
|
|
||||||
await discord_client.start(DISCORD_TOKEN)
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Discord Bot failed to run: {e}")
|
|
||||||
|
|
||||||
# FastAPI Web Server Setup
|
# FastAPI Web Server Setup
|
||||||
app = FastAPI(title="RIT Telegram & Discord Intermediary Bot HTTP Server")
|
app = FastAPI(title="RIT Telegram Intermediary Bot HTTP Server")
|
||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
@@ -920,7 +634,6 @@ class CollabApplicationPayload(BaseModel):
|
|||||||
author_name: str
|
author_name: str
|
||||||
contact_info: Optional[str] = None
|
contact_info: Optional[str] = None
|
||||||
telegram_chat_id: Optional[int] = None
|
telegram_chat_id: Optional[int] = None
|
||||||
discord_user_id: Optional[str] = None
|
|
||||||
applicant_name: str
|
applicant_name: str
|
||||||
applicant_dept: str
|
applicant_dept: str
|
||||||
applicant_year: str
|
applicant_year: str
|
||||||
@@ -932,7 +645,7 @@ def send_question(payload: QuestionPayload):
|
|||||||
current_config = load_config()
|
current_config = load_config()
|
||||||
comm_token = os.environ.get("COMMUNITY_BOT_TOKEN") or current_config.get("community_bot_token", COMMUNITY_BOT_TOKEN)
|
comm_token = os.environ.get("COMMUNITY_BOT_TOKEN") or current_config.get("community_bot_token", COMMUNITY_BOT_TOKEN)
|
||||||
|
|
||||||
# 1. Telegram Broadcast via Senior Helper Community Bot
|
# Telegram Broadcast via Senior Helper Community Bot
|
||||||
telegram_helpers = current_config.get("helper_chat_ids", [])
|
telegram_helpers = current_config.get("helper_chat_ids", [])
|
||||||
telegram_sent = 0
|
telegram_sent = 0
|
||||||
if telegram_helpers and comm_token:
|
if telegram_helpers and comm_token:
|
||||||
@@ -951,27 +664,9 @@ def send_question(payload: QuestionPayload):
|
|||||||
save_mapping(chat_id, message_id, payload.question_id)
|
save_mapping(chat_id, message_id, payload.question_id)
|
||||||
telegram_sent += 1
|
telegram_sent += 1
|
||||||
|
|
||||||
# 2. Discord Broadcast
|
|
||||||
discord_helpers = current_config.get("discord_helper_user_ids", [])
|
|
||||||
discord_sent = 0
|
|
||||||
if discord_helpers and DISCORD_TOKEN:
|
|
||||||
logging.info(f"Broadcasting question {payload.question_id} to {len(discord_helpers)} Discord helpers.")
|
|
||||||
if discord_loop:
|
|
||||||
try:
|
|
||||||
asyncio.run_coroutine_threadsafe(
|
|
||||||
broadcast_discord_question(payload.question_id, payload.title, payload.body, payload.author, discord_helpers),
|
|
||||||
discord_loop
|
|
||||||
)
|
|
||||||
discord_sent = len(discord_helpers)
|
|
||||||
except Exception as e:
|
|
||||||
logging.error(f"Error scheduling Discord broadcast: {e}")
|
|
||||||
else:
|
|
||||||
logging.warning("Discord loop not running. Skipping Discord broadcast.")
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"telegram_delivered_to": telegram_sent,
|
"telegram_delivered_to": telegram_sent
|
||||||
"discord_queued_for": discord_sent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.post("/send_collab_application")
|
@app.post("/send_collab_application")
|
||||||
@@ -980,9 +675,8 @@ def send_collab_application(payload: CollabApplicationPayload):
|
|||||||
bot_token = os.environ.get("TELEGRAM_BOT_TOKEN") or current_config.get("telegram_bot_token")
|
bot_token = os.environ.get("TELEGRAM_BOT_TOKEN") or current_config.get("telegram_bot_token")
|
||||||
|
|
||||||
telegram_sent = False
|
telegram_sent = False
|
||||||
discord_sent = False
|
|
||||||
|
|
||||||
# 1. Telegram Notification strictly to Person A (the project author)
|
# Telegram Notification strictly to Person A (the project author)
|
||||||
chat_id = payload.telegram_chat_id
|
chat_id = payload.telegram_chat_id
|
||||||
|
|
||||||
if chat_id:
|
if chat_id:
|
||||||
@@ -1009,13 +703,9 @@ def send_collab_application(payload: CollabApplicationPayload):
|
|||||||
else:
|
else:
|
||||||
logging.info(f"Skipping Telegram notification for collab application {payload.application_id}: No telegram_chat_id provided for author.")
|
logging.info(f"Skipping Telegram notification for collab application {payload.application_id}: No telegram_chat_id provided for author.")
|
||||||
|
|
||||||
# 2. Discord Broadcast - Disabled as requested
|
|
||||||
discord_sent = False
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"telegram_sent": telegram_sent,
|
"telegram_sent": telegram_sent
|
||||||
"discord_sent": discord_sent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async def run_uvicorn():
|
async def run_uvicorn():
|
||||||
@@ -1030,14 +720,7 @@ async def main():
|
|||||||
chat_thread = threading.Thread(target=telegram_polling_thread, daemon=True)
|
chat_thread = threading.Thread(target=telegram_polling_thread, daemon=True)
|
||||||
chat_thread.start()
|
chat_thread.start()
|
||||||
|
|
||||||
tasks = []
|
await run_uvicorn()
|
||||||
if DISCORD_TOKEN:
|
|
||||||
tasks.append(run_discord_bot())
|
|
||||||
else:
|
|
||||||
logging.warning("Discord Bot Token is empty. Skipping Discord bot startup.")
|
|
||||||
|
|
||||||
tasks.append(run_uvicorn())
|
|
||||||
await asyncio.gather(*tasks)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user