diff --git a/public/campus-map.jpg b/public/campus-map.jpg new file mode 100644 index 0000000..b0339af Binary files /dev/null and b/public/campus-map.jpg differ diff --git a/src/components/FeatureCard/FeatureCard.tsx b/src/components/FeatureCard/FeatureCard.tsx index 0867686..088c5b3 100644 --- a/src/components/FeatureCard/FeatureCard.tsx +++ b/src/components/FeatureCard/FeatureCard.tsx @@ -21,9 +21,9 @@ export default function FeatureCard({ feature }: FeatureCardProps) { {/* Icon */}
- {IconComponent && } + {IconComponent && }
{/* Content */} diff --git a/src/constants/index.ts b/src/constants/index.ts index 47291c6..3b21d5b 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -902,7 +902,6 @@ export const CAMPUS_LOCATIONS: CampusLocation[] = [ { id: 'labs', name: 'Labs', icon: 'FlaskConical' }, { id: 'hostel', name: 'Hostels', icon: 'Home' }, { id: 'canteen', name: 'Canteen', icon: 'UtensilsCrossed' }, - { id: 'busstop', name: 'Bus Stop', icon: 'Bus' }, { id: 'auditorium', name: 'Auditorium', icon: 'Theater' }, { id: 'sports', name: 'Sports Ground', icon: 'Trophy' }, ]; diff --git a/src/pages/Campus/Campus.tsx b/src/pages/Campus/Campus.tsx index 53a60a1..a1a56a2 100644 --- a/src/pages/Campus/Campus.tsx +++ b/src/pages/Campus/Campus.tsx @@ -1,6 +1,27 @@ import { useState, useMemo, useEffect } from 'react'; -import { motion } from 'framer-motion'; -import { Search, ChevronRight, MapPin, Users, BookOpen, Filter, ArrowUpDown, Loader2 } from 'lucide-react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { + ChevronRight, + MapPin, + ZoomIn, + ZoomOut, + Maximize2, + Minimize2, + Download, + Compass, + Layers, + Building2, + CheckCircle2, + Sparkles, + X, + Target, + Search, + Users, + BookOpen, + Filter, + ArrowUpDown, + Loader2, +} from 'lucide-react'; import SectionTitle from '@/components/SectionTitle/SectionTitle'; import BusCard from '@/components/BusCard/BusCard'; import FacultyCard from '@/components/FacultyCard/FacultyCard'; @@ -10,6 +31,60 @@ import * as LucideIcons from 'lucide-react'; import { useLocation } from 'react-router-dom'; import type { BusRoute } from '@/types'; +interface LocationSpot { + name: string; + x: number; // percentage + y: number; // percentage + category: string; + desc: string; + area: string; +} + +const BLUEPRINT_LEGEND = [ + { id: 'all', label: 'All Locations', color: '#F97316', bg: 'bg-orange-50/80', text: 'text-orange-950', border: 'border-orange-300' }, + { id: 'a-block', label: 'A Block', color: '#60A5FA', bg: 'bg-blue-50/80', text: 'text-blue-900', border: 'border-blue-200' }, + { id: 'b-block', label: 'B Block', color: '#93C5FD', bg: 'bg-blue-50/60', text: 'text-blue-900', border: 'border-blue-200' }, + { id: 'c-block', label: 'C Block', color: '#BFDBFE', bg: 'bg-blue-50/40', text: 'text-blue-900', border: 'border-blue-200' }, + { id: 'admin', label: 'Admin / Facility', color: '#FDBA74', bg: 'bg-orange-50/80', text: 'text-orange-950', border: 'border-orange-200' }, + { id: 'hostel', label: 'Hostel / Mess', color: '#C084FC', bg: 'bg-purple-50/80', text: 'text-purple-950', border: 'border-purple-200' }, + { id: 'lab', label: 'Lab / Academic', color: '#FDE047', bg: 'bg-yellow-50/80', text: 'text-yellow-950', border: 'border-yellow-200' }, + { id: 'amenity', label: 'Amenity', color: '#BEF264', bg: 'bg-lime-50/80', text: 'text-lime-950', border: 'border-lime-200' }, + { id: 'parking', label: 'Parking Area', color: '#94A3B8', bg: 'bg-slate-100/80', text: 'text-slate-900', border: 'border-slate-300' }, + { id: 'green', label: 'Green Area', color: '#4ADE80', bg: 'bg-emerald-50/80', text: 'text-emerald-950', border: 'border-emerald-200' }, +]; + +const LOCATION_SPOTS: LocationSpot[] = [ + { name: 'Girls Hostel', x: 8.5, y: 12.0, category: 'Hostel / Mess', desc: 'Residential Quarter for Women', area: 'Top Left' }, + { name: 'Playground', x: 32.0, y: 24.0, category: 'Amenity', desc: 'Main Sports & Athletic Field', area: 'Top Left' }, + { name: 'Canteen', x: 64.0, y: 14.0, category: 'Amenity', desc: 'Food Court & Refreshments', area: 'North Center' }, + { name: 'Statue', x: 63.0, y: 25.5, category: 'Amenity', desc: 'Landmark Monument', area: 'North Center' }, + { name: 'Boys Mess', x: 87.0, y: 9.5, category: 'Amenity', desc: 'Ground Floor Dining', area: 'Top Right' }, + { name: 'Staff Mess', x: 87.0, y: 18.5, category: 'Amenity', desc: 'First Floor Dining', area: 'Top Right' }, + { name: 'C Block', x: 87.0, y: 42.0, category: 'C Block', desc: 'Labs, Classrooms, Auditorium & Staff Rooms', area: 'East Block' }, + { name: 'Steve Jobs Block', x: 8.5, y: 39.0, category: 'Admin / Facility', desc: 'Auditorium, Labs & Staff Rooms', area: 'West Wing' }, + { name: 'Girls Mess', x: 8.5, y: 53.0, category: 'Amenity', desc: 'Dedicated Dining Hall for Women', area: 'West Wing' }, + { name: 'EPL Lab', x: 8.5, y: 67.0, category: 'Lab / Academic', desc: 'Engineering Practices Laboratory', area: 'South West' }, + { name: 'B Block', x: 33.0, y: 51.0, category: 'B Block', desc: 'Labs, Classrooms & Faculty Rooms', area: 'Central West' }, + { name: 'Hut', x: 63.0, y: 41.0, category: 'Amenity', desc: 'Student Discussion & Rest Area', area: 'Central Green' }, + { name: 'A Block', x: 70.0, y: 65.0, category: 'A Block', desc: 'Labs, Classrooms, Faculty Rooms, Principal Room & Exam Cell', area: 'Central East' }, + { name: 'RSB Block', x: 24.0, y: 80.0, category: 'Amenity', desc: 'Library, Auditorium & Accounts Room', area: 'South West' }, + { name: 'Side Parking', x: 6.5, y: 83.0, category: 'Parking Area', desc: 'Visitor & Staff Parking Zone', area: 'South West' }, + { name: 'Arcade', x: 43.5, y: 72.0, category: 'Amenity', desc: 'Student Hub & Stores', area: 'South Central' }, + { name: 'Security Booth', x: 45.8, y: 88.5, category: 'Amenity', desc: 'Main Gate Security Checkpoint', area: 'South Entrance' }, + { name: 'Parking Area', x: 73.5, y: 85.5, category: 'Parking Area', desc: 'Two-Wheeler & Four-Wheeler Parking', area: 'South East' }, + { name: 'Main Entrance', x: 50.5, y: 95.5, category: 'Amenity', desc: 'Primary Campus Security Gate', area: 'South Entrance' }, +]; + +const CATEGORY_MAP_SPOTS: Record = { + dept: ['A Block', 'B Block', 'C Block'], + library: ['RSB Block'], + labs: ['Steve Jobs Block', 'A Block', 'B Block', 'C Block', 'EPL Lab'], + hostel: ['Girls Hostel'], + canteen: ['Canteen', 'Staff Mess', 'Girls Mess', 'Boys Mess'], + auditorium: ['Steve Jobs Block', 'C Block', 'RSB Block'], + sports: ['Playground'], +}; + type Tab = 'map' | 'bus' | 'faculty'; export default function Campus() { @@ -25,6 +100,73 @@ export default function Campus() { const [busRoutes, setBusRoutes] = useState([]); const [loadingBus, setLoadingBus] = useState(true); + // Interactive Map State + const [zoomLevel, setZoomLevel] = useState(1); + const [isFullscreen, setIsFullscreen] = useState(false); + const [activeLegend, setActiveLegend] = useState('all'); + const [activeCategory, setActiveCategory] = useState(null); + const [selectedFacility, setSelectedFacility] = useState(null); + + const currentMapSrc = '/campus-map.jpg'; + + const handleZoomIn = () => setZoomLevel((prev) => Math.min(prev + 0.25, 2.5)); + const handleZoomOut = () => setZoomLevel((prev) => Math.max(prev - 0.25, 0.75)); + const handleResetZoom = () => setZoomLevel(1); + + const handleDownload = () => { + const link = document.createElement('a'); + link.href = currentMapSrc; + link.download = 'RIT_Campus_Blueprint.jpg'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }; + + const handleCategoryClick = (catId: string) => { + if (activeCategory === catId) { + setActiveCategory(null); + } else { + setActiveCategory(catId); + setSelectedFacility(null); + // Auto focus zoom slightly for visual feedback + setZoomLevel(1.1); + } + }; + + // Determine active highlighted spots on the map + const highlightedSpotNames = new Set(); + if (selectedFacility) { + highlightedSpotNames.add(selectedFacility.name); + } else if (activeCategory && CATEGORY_MAP_SPOTS[activeCategory]) { + CATEGORY_MAP_SPOTS[activeCategory].forEach((name) => highlightedSpotNames.add(name)); + } else if (activeLegend !== 'all') { + LOCATION_SPOTS.forEach((spot) => { + if (activeLegend === 'a-block' && spot.category === 'A Block') highlightedSpotNames.add(spot.name); + if (activeLegend === 'b-block' && spot.category === 'B Block') highlightedSpotNames.add(spot.name); + if (activeLegend === 'c-block' && spot.category === 'C Block') highlightedSpotNames.add(spot.name); + if (activeLegend === 'admin' && spot.category === 'Admin / Facility') highlightedSpotNames.add(spot.name); + if (activeLegend === 'hostel' && spot.category === 'Hostel / Mess') highlightedSpotNames.add(spot.name); + if (activeLegend === 'lab' && spot.category === 'Lab / Academic') highlightedSpotNames.add(spot.name); + if (activeLegend === 'amenity' && spot.category === 'Amenity') highlightedSpotNames.add(spot.name); + if (activeLegend === 'parking' && spot.category === 'Parking Area') highlightedSpotNames.add(spot.name); + if (activeLegend === 'green' && spot.area.includes('Green')) highlightedSpotNames.add(spot.name); + }); + } + + const filteredFacilities = LOCATION_SPOTS.filter((fac) => { + if (activeLegend === 'all') return true; + if (activeLegend === 'a-block') return fac.category === 'A Block'; + if (activeLegend === 'b-block') return fac.category === 'B Block'; + if (activeLegend === 'c-block') return fac.category === 'C Block'; + if (activeLegend === 'admin') return fac.category === 'Admin / Facility'; + if (activeLegend === 'hostel') return fac.category === 'Hostel / Mess'; + if (activeLegend === 'lab') return fac.category === 'Lab / Academic'; + if (activeLegend === 'amenity') return fac.category === 'Amenity'; + if (activeLegend === 'parking') return fac.category === 'Parking Area'; + if (activeLegend === 'green') return fac.area.includes('Green'); + return true; + }); + // Sync tab state when URL query parameter changes useEffect(() => { const qTab = new URLSearchParams(location.search).get('tab') as Tab; @@ -103,16 +245,16 @@ export default function Campus() {
Home - Campus + Campus Map

Explore{' '} - Campus + Campus Map

- Navigate campus, find bus routes, and connect with faculty. + Navigate campus layout, click location categories or blocks to spot exact places on the blueprint.

@@ -149,80 +291,441 @@ export default function Campus() { })} - {/* Map Tab */} {activeTab === 'map' && ( - -
-
-
- {/* Mock campus blocks */} - {[ - { label: 'CSE Block', x: '20%', y: '25%', w: '14%', h: '18%' }, - { label: 'ECE Block', x: '38%', y: '25%', w: '12%', h: '18%' }, - { label: 'Library', x: '58%', y: '30%', w: '16%', h: '14%' }, - { label: 'Canteen', x: '65%', y: '58%', w: '12%', h: '12%' }, - { label: 'Hostel', x: '18%', y: '58%', w: '14%', h: '20%' }, - { label: 'Auditorium', x: '40%', y: '55%', w: '18%', h: '14%' }, - ].map((block, i) => ( - - - {block.label} - - - ))} + + + {/* Map Viewer Header Bar */} +
+
+
+ + Official Blueprint + + + Interactive Location Spotter + +
+

+ Rajalakshmi Institute of Technology +

+

+ + Kuthambakkam, Chennai • Interactive Map Spotting Enabled +

+
- {/* Pin */} -
-
- - RIT Campus, Kuthambakkam, Chennai + {/* Toolbar Controls */} +
+ {/* Zoom Controls */} +
+ + + +
+ + {/* Fullscreen Button */} + + + {/* Download Button */} + +
+
+ + {/* Blueprint Main Canvas Container with Spotting Markers */} +
+
+ + {/* Main Campus Map Image Container */} +
+ Rajalakshmi Institute of Technology Campus Blueprint + + {/* Interactive Spot Markers overlayed on map image */} + {LOCATION_SPOTS.map((spot) => { + const isHighlighted = highlightedSpotNames.has(spot.name); + const isSelected = selectedFacility?.name === spot.name; + + return ( +
setSelectedFacility(spot)} + > + {/* Glowing Pulsing Ring if Highlighted */} + {isHighlighted && ( + + )} + + {/* Spot Pin Button */} + + + + {/* Hover / Highlight Tooltip Label */} +
+ {spot.name} +
+
+
+ ); + })} +
+
+
+ + {/* Active Spotting Info Banner Overlay at Bottom */} +
+ {activeCategory ? ( +
+ + Spotting Category: {CAMPUS_LOCATIONS.find((c) => c.id === activeCategory)?.name} ({highlightedSpotNames.size} Locations) + +
+ ) : selectedFacility ? ( +
+ + Spotted: {selectedFacility.name} ({selectedFacility.area}) + +
+ ) : ( +
+ + Click any category below to spot on map +
+ )} +
+
+ + {/* Quick Location Categories Bar */} +
+
+

+ Quick Location Categories +

+ {activeCategory && ( + + )} +
+
+ {CAMPUS_LOCATIONS.map((loc) => { + const Icon = (LucideIcons as unknown as Record>)[loc.icon]; + const isCatActive = activeCategory === loc.id; + return ( + handleCategoryClick(loc.id)} + className={`flex flex-col items-center gap-2.5 p-3 rounded-2xl transition-all border cursor-pointer group ${ + isCatActive + ? 'border-orange-500 bg-orange-500 text-white shadow-lg ring-2 ring-orange-400/30' + : 'border-transparent hover:border-[#FED7AA] hover:bg-orange-50/50' + }`} + > +
+ {Icon && } +
+ + {loc.name} + +
+ ); + })} +
+
+ + {/* Blueprint Legend & Color Key */} +
+
+ +

+ Blueprint Map Legend +

+
+
+ {BLUEPRINT_LEGEND.map((item) => { + const isActive = activeLegend === item.id; + return ( + + ); + })} +
+
+ + {/* Blueprint Facilities List & Quick Selector */} +
+
+
+
+ +

+ Campus Blocks & Facilities Index +

+
+

+ Click any facility card below to spot its exact location on the campus map image. +

+
+ + {filteredFacilities.length} Locations Mapped + +
+ +
+ {filteredFacilities.map((facility) => { + const isSelected = selectedFacility?.name === facility.name; + const isHighlighted = highlightedSpotNames.has(facility.name); + return ( + { + if (isSelected) { + setSelectedFacility(null); + } else { + setSelectedFacility(facility); + setActiveCategory(null); + } + }} + className={`text-left p-4 rounded-2xl border transition-all cursor-pointer ${ + isSelected + ? 'border-[#F97316] bg-orange-500 text-white shadow-lg ring-2 ring-orange-400/40' + : isHighlighted + ? 'border-[#F97316] bg-orange-50/90 shadow-md' + : 'border-[#E5E7EB] bg-[#F8FAFC] hover:bg-white hover:border-orange-200 hover:shadow-xs' + }`} + > +
+ + {facility.name} + + + {facility.area} + +
+

+ {facility.desc} +

+
+ ); + })} +
+
+ + {/* Fullscreen Lightbox Modal */} + + {isFullscreen && ( + +
+
+ +

RIT Campus Blueprint — Fullscreen Spotter

+
+
+
-
- {/* Quick Nav */} -
- {CAMPUS_LOCATIONS.map((loc) => { - const Icon = (LucideIcons as unknown as Record>)[loc.icon]; - return ( - -
- {Icon && } -
- - {loc.name} - -
- ); - })} -
-
-
+
+
+ RIT Campus Blueprint Fullscreen + {LOCATION_SPOTS.map((spot) => { + const isHighlighted = highlightedSpotNames.has(spot.name); + const isSelected = selectedFacility?.name === spot.name; + return ( +
setSelectedFacility(spot)} + > +
+ +
+ {spot.name} +
+
+
+ ); + })} +
+
+ + )} + + + {/* Quick Nav */} +
+ {CAMPUS_LOCATIONS.map((loc) => { + const Icon = (LucideIcons as unknown as Record>)[loc.icon]; + return ( + +
+ {Icon && } +
+ + {loc.name} + +
+ ); + })} +
+ )} {/* Bus Routes Tab */} @@ -388,7 +891,7 @@ export default function Campus() { )}
-
- ); + +); } diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 519ca43..8f97d46 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -359,7 +359,7 @@ export default function Home() { {/* Quick nav buttons */} -
+
{CAMPUS_LOCATIONS.map((loc) => { const Icon = (LucideIcons as unknown as Record>)[loc.icon]; return (