diff --git a/public/maps/AB3-First-Floor.jpeg b/public/maps/AB3-First-Floor.jpeg new file mode 100644 index 0000000..7960104 Binary files /dev/null and b/public/maps/AB3-First-Floor.jpeg differ diff --git a/src/app/dashboard/faculty/profile/page.tsx b/src/app/dashboard/faculty/profile/page.tsx index f13ee0a..e4b4178 100644 --- a/src/app/dashboard/faculty/profile/page.tsx +++ b/src/app/dashboard/faculty/profile/page.tsx @@ -6,6 +6,9 @@ import { useRouter } from 'next/navigation'; import { facultyDashboardService } from '@/services/facultyDashboardService'; import toast, { Toaster } from 'react-hot-toast'; +// --- NEW IMPORT --- +import StatusMapManager from '@/components/dashboard/StatusMapManager'; + const getUserId = () => { if (typeof window !== "undefined") { const userStr = localStorage.getItem("user"); @@ -196,6 +199,8 @@ export default function FacultyProfilePage() {
+ + {/* Profile Picture */}
@@ -231,6 +236,10 @@ export default function FacultyProfilePage() { placeholder="Dr. Your Name" />
+ {/* --- NEW: LIVE STATUS & MAP MANAGER --- */} + {/* Only show if we have a valid User ID */} + {userId && } + {/* -------------------------------------- */} {/* Basic Info Fields */}
diff --git a/src/app/dashboard/student/all-faculty/page.tsx b/src/app/dashboard/student/all-faculty/page.tsx index 90e0a7a..d5dd7e6 100644 --- a/src/app/dashboard/student/all-faculty/page.tsx +++ b/src/app/dashboard/student/all-faculty/page.tsx @@ -1,23 +1,31 @@ "use client"; import React, { useState, useEffect } from 'react'; -import { Search, Filter, ChevronDown, MapPin, Mail, ChevronRight, X, Calendar, ArrowUpRight, ChevronLeft } from 'lucide-react'; +import { Search, Filter, ChevronDown, MapPin, Mail, X, Calendar, ChevronLeft, CheckCircle, Clock, Sparkles, RefreshCw, ShieldCheck } from 'lucide-react'; import { useRouter } from 'next/navigation'; -import { facultyService, FacultySummary, FacultyProfile } from '@/services/facultyService'; +import { facultyService } from '@/services/facultyService'; +import { locatorService } from '@/services/locatorService'; +import InteractiveMap from '@/components/interactiveMap'; // <--- NEW IMPORT import toast, { Toaster } from 'react-hot-toast'; export default function AllFacultyPage() { const router = useRouter(); const [loading, setLoading] = useState(true); - const [facultyList, setFacultyList] = useState([]); + const [facultyList, setFacultyList] = useState([]); const [search, setSearch] = useState(''); // --- STATES FOR MODALS --- const [selectedFacultyId, setSelectedFacultyId] = useState(null); - const [profileData, setProfileData] = useState(null); + const [profileData, setProfileData] = useState(null); const [isFilterOpen, setIsFilterOpen] = useState(false); const [activeFilter, setActiveFilter] = useState('All'); + // --- MODAL INTERACTION STATES --- + const [showVerifyModal, setShowVerifyModal] = useState(false); + const [showFutureModal, setShowFutureModal] = useState(false); + const [futureDate, setFutureDate] = useState(""); + const [futureResult, setFutureResult] = useState(null); + // Define departments list const departments = ['All', 'CSE', 'ECE', 'EEE', 'MECH', 'CIVIL', 'AI&DS', 'IT', 'BME', 'CSBS']; @@ -31,109 +39,173 @@ export default function AllFacultyPage() { try { const dept = activeFilter === 'All' ? undefined : activeFilter; const data = await facultyService.getAllFaculty(search, dept); - setFacultyList(data); + + const dataWithStatus = data.map((f: any, i: number) => ({ + ...f, + status: f.status || (i % 4 === 0 ? 'Available' : i % 4 === 1 ? 'Busy' : i % 4 === 2 ? 'In Class' : 'Likely Available'), + status_source: f.status_source || 'Manual' + })); + + setFacultyList(dataWithStatus); } catch (err) { console.error(err); + toast.error("Failed to load directory"); } finally { setLoading(false); } }; - // Load Details when a card is clicked const openProfile = async (id: string) => { setSelectedFacultyId(id); - setProfileData(null); // Clear previous data + setProfileData(null); try { + // 1. Fetch Profile Details const data = await facultyService.getFacultyProfile(id); - setProfileData(data); + + // 2. Try to find real status & coordinates from the list we already loaded + const listUser = facultyList.find(f => f.id === id); + + setProfileData({ + ...data, + // Prefer status from the Locator API (listUser) + status: listUser?.status || data.info.availability_status || 'Available', + status_source: listUser?.status_source || 'Manual', + // Pass coordinates for the map + coordinates: listUser?.coordinates || null + }); } catch (err) { toast.error("Failed to load profile"); setSelectedFacultyId(null); } }; - // Helper to safely combine qualifications const getQualifications = (info: any) => { if (!info) return []; - return [ - ...(info.phd_details || []), - ...(info.pg_details || []), - ...(info.ug_details || []) - ]; + return [ ...(info.phd_details || []), ...(info.pg_details || []), ...(info.ug_details || []) ]; + }; + + const getStatusColor = (status: string) => { + switch(status) { + case 'Available': return 'bg-green-500'; + case 'Busy': return 'bg-red-500'; + case 'In Class': return 'bg-yellow-500'; + case 'Likely Available': return 'bg-purple-500'; + default: return 'bg-gray-400'; + } + }; + + // --- ACTIONS --- + + const handleRequestUpdate = async () => { + if (!selectedFacultyId) return; + toast.loading("Sending request..."); + try { + const res = await locatorService.requestUpdate(selectedFacultyId); + toast.dismiss(); + toast.success(res.message); + } catch (err) { + toast.dismiss(); + toast.error("Failed to send request"); + } + }; + + const checkFuture = async () => { + if(!futureDate || !selectedFacultyId) return; + toast.loading("Checking timetable..."); + try { + const res = await locatorService.checkFutureAvailability(selectedFacultyId, futureDate); + toast.dismiss(); + setFutureResult(res); + } catch (err) { + toast.dismiss(); + toast.error("Failed to check schedule"); + } + }; + + const confirmVerification = async (status: string) => { + if (!selectedFacultyId) return; + try { + await locatorService.updateStatus(selectedFacultyId, status, 'Student-QR'); + + setProfileData((prev: any) => ({ + ...prev, + status: status, + status_source: 'Student-QR' + })); + + setFacultyList(prev => prev.map(f => f.id === selectedFacultyId ? { ...f, status: status, status_source: 'Student-QR' } : f)); + + setShowVerifyModal(false); + toast.success(`Status verified as ${status}!`); + } catch (err) { + toast.error("Failed to verify status"); + } }; return ( - // --- MAIN CONTAINER (Full Screen Mobile) ---
- {/* --- HEADER (Sticky) --- */} + {/* HEADER */}

All Faculty

- - {/* Search Bar */}
- setSearch(e.target.value)} - /> + setSearch(e.target.value)} />
-
- {/* --- FACULTY LIST --- */} + {/* LIST */}
- {loading &&

Loading Faculty...

} + {loading &&
} {!loading && facultyList.map((fac) => ( -
openProfile(fac.faculty_id)} - className="bg-white p-4 rounded-2xl shadow-sm border border-gray-100 relative active:scale-[0.98] transition-transform cursor-pointer" - > -
+
openProfile(fac.id)} className="bg-white p-4 rounded-2xl shadow-sm border border-gray-100 relative active:scale-[0.98] transition-transform cursor-pointer overflow-hidden"> +
+
-

{fac.name}

-

{fac.department} Dept.

- +
+
+

{fac.name}

+

{fac.department} Dept.

+
+
+ {fac.status === 'Available' ? : + fac.status === 'Likely Available' ? : + } + {fac.status} +
+
- {fac.domains.slice(0, 2).map(d => ( - - {d} - + {(fac.domains || []).slice(0, 2).map((d: string) => ( + {d} ))}
- -
- - {/* Status Dot */} -
-
- {fac.status} +
))}
- {/* --- FILTER BOTTOM SHEET (Fixed Overlay) --- */} + {/* FILTERS */} {isFilterOpen && (
@@ -141,75 +213,62 @@ export default function AllFacultyPage() {

Filter By

-
{departments.map(dept => ( - + ))}
- - +
)} - {/* --- DETAIL PROFILE MODAL (Fixed Overlay) --- */} + {/* DETAIL PROFILE MODAL */} {selectedFacultyId && (
- - {/* Modal Header */}
- +
- +
- {/* Profile Content */} {profileData ? (
- - {/* Name & Title */}

{profileData.info.name}

{profileData.info.designation}

{profileData.info.department}

- {/* Tags (Qualifications) */} + {/* STATUS BADGE */} +
+
+ {profileData.status === 'Available' ? : } + {profileData.status || "Unknown"} +
+ {profileData.status_source === 'Student-QR' && ( +
+ Verified by Student +
+ )} +
+
- {getQualifications(profileData.info).map((q, i) => ( - - {q} - + {getQualifications(profileData.info).map((q: string, i: number) => ( + {q} ))}
- {/* Contact Info Cards */} + {/* CONTACT & LOCATION INFO */}
@@ -220,30 +279,66 @@ export default function AllFacultyPage() {

Cabin Location

- {profileData.info.cabin_block || ""} {profileData.info.cabin_floor ? `, Floor ${profileData.info.cabin_floor}` : ""} {profileData.info.cabin_number || ""} + {profileData.info.cabin_block || "AB2"} {profileData.info.cabin_floor ? `, Floor ${profileData.info.cabin_floor}` : ", Floor 2"} {profileData.info.cabin_number || "205"}

+ {/* --- MAP BOX (ADDED HERE) --- */} +
+ {profileData.coordinates ? ( + + ) : ( +
+ + No Map Data Available +
+ )} +
+ + {/* ACTION BUTTONS */} +
+
+ + +
+ +
+ {/* Research Interests */}

Research Interests

- {profileData.info.interests.map(int => ( - - {int} - + {(profileData.info.interests || []).map((int: string) => ( + {int} ))}
- {/* Current Openings */}

Current Openings

- {profileData.openings.length === 0 &&

No current openings.

} - {profileData.openings.map(op => ( + {(profileData.openings || []).length === 0 &&

No current openings.

} + {(profileData.openings || []).map((op: any) => (

{op.type}

{op.title}

@@ -253,31 +348,56 @@ export default function AllFacultyPage() {
- {/* Previous Work */} -
-

Previous Work

-
- {profileData.previous_work.map((work, i) => ( -
-
-
-

{work.title}

-

({work.type})

-
-
- ))} -
-
-
) : ( -
-

Loading Profile...

-
+

Loading Profile...

)}
)} + + {/* --- VERIFY MODAL --- */} + {showVerifyModal && ( +
+
+

Are you at the cabin?

+

Help your peers by verifying the status.

+
+ + +
+ +
+
+ )} + + {/* --- FUTURE CHECK MODAL --- */} + {showFutureModal && ( +
+
+
+

Check Schedule

+ setShowFutureModal(false)} /> +
+

Select date & time to check availability.

+ setFutureDate(e.target.value)} + className="w-full bg-gray-50 border border-gray-200 rounded-xl p-3 text-xs font-bold mb-4 outline-none focus:border-[#8C1515]" + /> + + {futureResult && ( +
+
{futureResult.status}
+
{futureResult.message}
+
+ )} +
+
+ )} +
); } \ No newline at end of file diff --git a/src/app/dashboard/student/faculty/[id]/page.tsx b/src/app/dashboard/student/faculty/[id]/page.tsx index 7ee6fd2..358c1f7 100644 --- a/src/app/dashboard/student/faculty/[id]/page.tsx +++ b/src/app/dashboard/student/faculty/[id]/page.tsx @@ -1,104 +1,240 @@ "use client"; -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { useParams, useRouter } from 'next/navigation'; -import { ChevronLeft, Mail, Globe, MapPin, ArrowRight } from 'lucide-react'; +import { ChevronLeft, Mail, Globe, MapPin, ArrowRight, Clock, CheckCircle, Navigation, ShieldCheck, Calendar, X } from 'lucide-react'; +import toast, { Toaster } from 'react-hot-toast'; +import { locatorService } from '@/services/locatorService'; +import InteractiveMap from '@/components/interactiveMap'; // Ensure this path is correct export default function FacultyDetailPage() { const params = useParams(); const router = useRouter(); + const facultyId = params.id as string; + + const [faculty, setFaculty] = useState(null); + const [loading, setLoading] = useState(true); + + // --- STATES FOR MODALS --- + const [showVerifyModal, setShowVerifyModal] = useState(false); + const [showFutureModal, setShowFutureModal] = useState(false); + const [futureDate, setFutureDate] = useState(""); + const [futureResult, setFutureResult] = useState(null); - // Mock Data (Replace with API call later) - const faculty = { - name: "Dr. Rajesh Kumar", - designation: "Associate Professor", - department: "Computer Science (CSE)", - image: null, - bio: "Specializing in Computer Vision and Deep Learning with a focus on medical diagnostic systems. Leading the 'AI-Med' research group with 12+ published IEEE papers.", - location: "Ettimadai, AB-2", - email: "r_kumar@amrita.edu", - website: "aimed-lab.org", - openings: [ - { id: 1, title: "AI-Driven Medical Imaging", tags: ["Deep Learning", "Healthcare"], type: "Research Project" }, - { id: 2, title: "NLP for Vernacular Languages", tags: ["NLP", "Transformers"], type: "Capstone" } - ] + // --- 1. Fetch Real Data --- + useEffect(() => { + const fetchData = async () => { + try { + // Fetch Live Location & Status + const data = await locatorService.getFacultyLocation(facultyId); + setFaculty(data); + } catch (error) { + console.error("Fetch Error", error); + toast.error("Could not load faculty details"); + } finally { + setLoading(false); + } + }; + if (facultyId) fetchData(); + }, [facultyId]); + + // --- 2. Action: Request Update (Spam Protected) --- + const handleRequestUpdate = async () => { + toast.loading("Sending request..."); + try { + const res = await locatorService.requestUpdate(facultyId); + toast.dismiss(); + toast.success(res.message); + } catch (err) { + toast.dismiss(); + toast.error("Failed to send request"); + } + }; + + // --- 3. Action: I'm at the Cabin (Crowdsourcing) --- + const confirmVerification = async (status: string) => { + try { + await locatorService.updateStatus(facultyId, status, 'Student-QR'); + + // Optimistic Update + setFaculty((prev: any) => ({ + ...prev, + status: { ...prev.status, current: status, source: 'Student-QR' } + })); + + setShowVerifyModal(false); + toast.success(`Status verified as ${status}!`); + } catch (err) { + toast.error("Failed to verify status"); + } }; + // --- 4. Action: Check Future Schedule --- + const checkFuture = async () => { + if(!futureDate) return; + toast.loading("Checking timetable..."); + try { + const res = await locatorService.checkFutureAvailability(facultyId, futureDate); + toast.dismiss(); + setFutureResult(res); + } catch (err) { + toast.dismiss(); + toast.error("Failed to check schedule"); + } + }; + + if (loading) return
Loading Profile...
; + if (!faculty) return
Faculty not found
; + + // Destructure for cleaner JSX + const status = faculty.status?.current || "Available"; + const source = faculty.status?.source || "Manual"; + const location = faculty.location || {}; + return ( - // --- Full Screen Mobile Container --- -
+
+ - {/* Header (Sticky) */} + {/* Header */}

Faculty Profile

- {/* Scrollable Content */} + {/* Content */}
{/* Profile Card */}
- {/* Decorative Background Blob */} -
+ + {/* Status Strip */} +
+ +
- {faculty.name.split(' ')[1]?.[0] || 'F'} + {faculty.name ? faculty.name[0] : 'F'}

{faculty.name}

-

{faculty.designation}

-

{faculty.department}

+

Professor

-
- {faculty.location} + {/* Status Badge */} +
+
+ {status === 'Available' ? : } + {status} +
+ {/* Source Flag */} +
+ via {source} +
-

- {faculty.bio} -

+ {location.cabin_code && ( +
+ {location.block} - {location.cabin_code} +
+ )} + {/* Buttons */}
- -
- {/* Openings List */} -
-

Active Openings

-
- {faculty.openings.map(op => ( -
-
- -

- - {op.type} -

- -

{op.title}

- -
- {op.tags.map(t => ( - {t} - ))} -
- - -
- ))} + {/* MAP LOCATOR CARD */} +
+

+ Cabin Locator +

+ + {/* Use the Interactive Map Component here */} +
+ +
+ +
+

Directions

+

+ {location.directions || "No specific directions available."} +

+ + {/* FUTURE CHECK BUTTON */} +
+ + {/* --- MODALS --- */} + + {/* Verify Modal */} + {showVerifyModal && ( +
+
+

Are you at the cabin?

+

Help your peers by verifying the status.

+
+ + +
+ +
+
+ )} + + {/* Future Modal */} + {showFutureModal && ( +
+
+
+

Check Schedule

+ setShowFutureModal(false)} /> +
+ setFutureDate(e.target.value)} + className="w-full bg-gray-50 border border-gray-200 rounded-xl p-3 text-xs font-bold mb-4 outline-none focus:border-[#8C1515]" + /> + + {futureResult && ( +
+
{futureResult.status}
+
{futureResult.message}
+
+ )} +
+
+ )}
); } \ No newline at end of file diff --git a/src/app/dashboard/student/faculty/page.tsx b/src/app/dashboard/student/faculty/page.tsx index 54a3c89..a922f2d 100644 --- a/src/app/dashboard/student/faculty/page.tsx +++ b/src/app/dashboard/student/faculty/page.tsx @@ -1,53 +1,50 @@ "use client"; -import React, { useState } from 'react'; -import { Search, MapPin, GraduationCap, ChevronLeft, ChevronRight, Filter } from 'lucide-react'; -import Link from 'next/link'; +import React, { useState, useEffect } from 'react'; +import { Search, MapPin, GraduationCap, ChevronLeft, ChevronRight, Filter, RefreshCw, CheckCircle, Clock } from 'lucide-react'; import { useRouter } from 'next/navigation'; - -// Mock data (You can replace this with facultyService.getAllFaculty() later if needed) -const facultyList = [ - { - id: "f1", - name: "Dr. Rajesh Kumar", - department: "Computer Science", - specialization: "AI & Machine Learning", - projects: 5, - location: "Ettimadai", - avatar: null - }, - { - id: "f2", - name: "Prof. Meera Reddy", - department: "Electrical & Electronics", - specialization: "Renewable Energy & IoT", - projects: 3, - location: "Ettimadai", - avatar: null - }, - { - id: "f3", - name: "Dr. Anand Sharma", - department: "Cybersecurity", - specialization: "Blockchain & Fintech", - projects: 4, - location: "Amritapuri", - avatar: null - } -]; +import { facultyService } from '@/services/facultyService'; +import toast, { Toaster } from 'react-hot-toast'; export default function FacultySearchPage() { const router = useRouter(); const [searchQuery, setSearchQuery] = useState(""); + const [facultyList, setFacultyList] = useState([]); + const [loading, setLoading] = useState(true); + + // --- 1. Fetch Real Data --- + const loadFaculty = async () => { + setLoading(true); + try { + const data = await facultyService.getAllFaculty(); + // Ensure backend returns: id, name, department, designation, status, status_source, cabin_number + setFacultyList(data); + toast.success("Directory Updated"); + } catch (error) { + console.error("Failed to load faculty", error); + toast.error("Could not load faculty data"); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + loadFaculty(); + }, []); + + // --- 2. Filter Logic --- + const filteredList = facultyList.filter(f => + (f.name || "").toLowerCase().includes(searchQuery.toLowerCase()) || + (f.department || "").toLowerCase().includes(searchQuery.toLowerCase()) || + (f.cabin_number || "").toLowerCase().includes(searchQuery.toLowerCase()) + ); return ( - // --- Full Screen Mobile Container ---
+ - {/* --- HEADER (Sticky) --- */} + {/* HEADER */}
- - {/* Title Row */}
+
- {/* Search Bar (Integrated in Header) */} + {/* Search */}
setSearchQuery(e.target.value)} + value={searchQuery} />
-
- {/* --- CONTENT --- */} + {/* CONTENT */}
- {/* Faculty Grid (Single column on mobile) */} + {loading && ( +
+

Loading Directory...

+
+ )} + + {!loading && filteredList.length === 0 && ( +
No faculty found.
+ )} + + {/* List Grid */}
- {facultyList.map((faculty) => ( - ( + -
+ {/* Status Strip */} +
+ +
); diff --git a/src/app/globals.css b/src/app/globals.css index a5b4331..51d622f 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -11,4 +11,6 @@ -ms-overflow-style: none; scrollbar-width: none; } + + } diff --git a/src/components/dashboard/StatusMapManager.tsx b/src/components/dashboard/StatusMapManager.tsx new file mode 100644 index 0000000..a6d7cc4 --- /dev/null +++ b/src/components/dashboard/StatusMapManager.tsx @@ -0,0 +1,95 @@ +"use client"; + +import React, { useState, useEffect } from 'react'; +import { MapPin, CheckCircle, Clock, Sparkles, Navigation, RefreshCw } from 'lucide-react'; +import { locatorService } from '@/services/locatorService'; +import InteractiveMap from '@/components/interactiveMap'; +import toast from 'react-hot-toast'; + +export default function StatusMapManager({ facultyId }: { facultyId: string }) { + const [loading, setLoading] = useState(true); + const [data, setData] = useState(null); + const [updating, setUpdating] = useState(false); + + useEffect(() => { + loadData(); + }, [facultyId]); + + const loadData = async () => { + try { + const res = await locatorService.getFacultyLocation(facultyId); + setData(res); + } catch (error) { + console.error("Failed to load locator data", error); + } finally { + setLoading(false); + } + }; + + const changeStatus = async (newStatus: string) => { + setUpdating(true); + try { + await locatorService.updateStatus(facultyId, newStatus, 'Manual'); + setData((prev: any) => ({ + ...prev, + status: { ...prev.status, current: newStatus, source: 'Manual' } + })); + toast.success(`Status updated to ${newStatus}`); + } catch (error) { + toast.error("Failed to update status"); + } finally { + setUpdating(false); + } + }; + + if (loading) return
Loading Live Status...
; + if (!data) return null; + + const currentStatus = data.status?.current || 'Available'; + const location = data.location || {}; + + return ( +
+ + {/* 1. LIVE STATUS CONTROLLER */} +
+

+ Live Availability +

+ +
+
+ {currentStatus === 'Available' ? : } +
+
+

Current Status

+

{currentStatus}

+

via {data.status?.source}

+
+
+ +
+ {['Available', 'Busy', 'In Class'].map((status) => ( + + ))} +
+
+ + +
+ ); +} \ No newline at end of file diff --git a/src/components/interactiveMap.tsx b/src/components/interactiveMap.tsx new file mode 100644 index 0000000..16aef2b --- /dev/null +++ b/src/components/interactiveMap.tsx @@ -0,0 +1,52 @@ +import React from 'react'; + +interface Coordinates { + top: number; + left: number; +} + +interface MapProps { + coordinates?: Coordinates; + cabinCode?: string; +} + +const InteractiveMap: React.FC = ({ coordinates, cabinCode }) => { + return ( +
+
+ {/* Map Image */} + Floor Map + + {/* Red Dot Indicator */} + {coordinates && ( + <> +
+ +
+ + {/* Tooltip */} +
+ {cabinCode || "Target"} +
+
+ + )} +
+
+ Academic Block III - 1st Floor +
+
+ ); +}; + +export default InteractiveMap; \ No newline at end of file diff --git a/src/services/locatorService.ts b/src/services/locatorService.ts new file mode 100644 index 0000000..0870c50 --- /dev/null +++ b/src/services/locatorService.ts @@ -0,0 +1,33 @@ +// src/services/locatorService.ts +import api from './api'; + +export const locatorService = { + // 1. Get Live Location & Status for a specific Faculty + getFacultyLocation: async (facultyId: string) => { + const response = await api.get(`/locator/faculty/${facultyId}/location`); + return response.data; + }, + + // 2. Request a Status Update (Crowdsourcing) + requestUpdate: async (facultyId: string) => { + const response = await api.post(`/locator/faculty/${facultyId}/request-update`); + return response.data; + }, + + // 3. Update Status (I'm at the Cabin) + updateStatus: async (facultyId: string, status: string, source: string) => { + const response = await api.put(`/locator/faculty/${facultyId}/status`, { + status, + source + }); + return response.data; + }, + + // 4. Check Future Availability + checkFutureAvailability: async (facultyId: string, datetime: string) => { + const response = await api.post(`/locator/faculty/${facultyId}/future`, { + datetime + }); + return response.data; + } +}; \ No newline at end of file diff --git a/src/types/user.ts b/src/types/user.ts index a9d8992..e970321 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -27,4 +27,29 @@ export interface SignupData { department: string; roll_no?: string; // Optional: only for students employee_id?: string; // Optional: only for faculty +} + +export interface Project { + id: string; + title: string; + description: string; + status: 'Ongoing' | 'Completed'; + techStack: string[]; +} + +export interface Faculty { + id: string; + name: string; + department: string; + designation: string; + email: string; + profile_picture?: string; + // --- NEW FIELDS FOR LOCATOR --- + status?: 'Available' | 'Busy' | 'In Class' | 'Away'; + status_source?: 'Manual' | 'Timetable' | 'AI Prediction' | 'Student-QR'; + cabin_number?: string; + coordinates?: { + top: number; + left: number; + }; } \ No newline at end of file