feat: Add native Android driver tracking app and real-time backend/frontend bus tracking system

This commit is contained in:
Shanmuga Krishnan S M
2026-07-24 21:22:37 +05:30
parent 8e87bd72cd
commit 85b128bef5
71 changed files with 206796 additions and 1368 deletions

View File

@@ -3,6 +3,7 @@ import { motion, AnimatePresence } from 'framer-motion';
import { Bot, Send, Sparkles, RefreshCw, Mic, X, ChevronRight } from 'lucide-react';
import { AI_SUGGESTED_PROMPTS } from '@/constants';
import type { ChatMessage } from '@/types';
import { getChatbotUrl } from '@/lib/utils';
const INITIAL_MESSAGES: ChatMessage[] = [
{
@@ -44,7 +45,7 @@ export default function AIAssistant() {
setIsTyping(true); // Call the Go chatbot microservice
let responseText = '';
try {
const res = await fetch('http://localhost:8081/api/chat', {
const res = await fetch(getChatbotUrl('/api/chat'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message: text }),
@@ -72,7 +73,7 @@ export default function AIAssistant() {
} else if (lower.includes('library')) {
responseText = responses.library;
} else {
responseText = `Thank you for your question about **"${text}"**!\n\nI couldn't reach the chatbot API server. Please make sure the Go service is running on http://localhost:8081.`;
responseText = `Thank you for your question about **"${text}"**!\n\nI couldn't reach the chatbot API server. Please make sure the Go service is running on ${getChatbotUrl()}.`;
}
}

View File

@@ -31,6 +31,7 @@ import { FACULTY_DATA, CAMPUS_LOCATIONS, DEPARTMENTS } from '@/constants';
import * as LucideIcons from 'lucide-react';
import { useLocation } from 'react-router-dom';
import type { BusRoute } from '@/types';
import { getBackendUrl } from '@/lib/utils';
interface LocationSpot {
name: string;
@@ -101,6 +102,7 @@ export default function Campus() {
const [busRoutes, setBusRoutes] = useState<BusRoute[]>([]);
const [loadingBus, setLoadingBus] = useState(true);
const [selectedBusRoute, setSelectedBusRoute] = useState<BusRoute | null>(null);
const [hasFetchedBus, setHasFetchedBus] = useState(false);
// Interactive Map State
const [zoomLevel, setZoomLevel] = useState(1);
@@ -178,23 +180,38 @@ export default function Campus() {
}, [location.search]);
useEffect(() => {
if (activeTab === 'bus' && busRoutes.length === 0) {
if (activeTab === 'bus' && !hasFetchedBus) {
setLoadingBus(true);
fetch('http://localhost:8080/api/bus-routes')
fetch(getBackendUrl('/api/bus-routes'))
.then((res) => {
if (!res.ok) throw new Error('Failed to fetch');
if (!res.ok) throw new Error('Backend offline');
return res.json();
})
.then((data) => {
setBusRoutes(data);
if (data && data.length > 0) {
setBusRoutes(data);
setSelectedBusRoute(prev => prev || data[0]);
} else {
throw new Error('Empty data');
}
setLoadingBus(false);
setHasFetchedBus(true);
})
.catch((err) => {
console.error('Error fetching bus routes:', err);
setLoadingBus(false);
.catch(() => {
fetch('/bus_routes.json')
.then((res) => res.json())
.then((data) => {
setBusRoutes(data);
setSelectedBusRoute(prev => prev || data[0]);
})
.catch((err) => console.error('Fallback fetch error:', err))
.finally(() => {
setLoadingBus(false);
setHasFetchedBus(true);
});
});
}
}, [activeTab, busRoutes.length]);
}, [activeTab, hasFetchedBus]);
const filteredAndSortedFaculty = useMemo(() => {
const filtered = FACULTY_DATA.filter((f) => {

View File

@@ -5,6 +5,7 @@ import {
Shield, Lock, Smile, ChevronRight, Search, Plus, User
} from 'lucide-react';
import SectionTitle from '@/components/SectionTitle/SectionTitle';
import { getBackendUrl } from '@/lib/utils';
import { StaggerContainer, StaggerItem } from '@/components/AnimatedContainer/AnimatedContainer';
import AnimatedContainer from '@/components/AnimatedContainer/AnimatedContainer';
import { CONFESSIONS_DATA } from '@/constants';
@@ -91,7 +92,7 @@ export default function Community() {
const fetchQuestions = async () => {
setLoading(true);
try {
const response = await fetch(`http://localhost:8080/api/questions`);
const response = await fetch(getBackendUrl('/api/questions'));
if (response.ok) {
const data = await response.json();
// Backend returns oldest-first, reverse to show newest first
@@ -193,7 +194,7 @@ export default function Community() {
try {
if (isLikedNow) {
await fetch(`http://localhost:8080/api/questions/${id}/upvote`, {
await fetch(getBackendUrl(`/api/questions/${id}/upvote`), {
method: 'POST'
});
fetchQuestions();
@@ -240,7 +241,7 @@ export default function Community() {
};
try {
const response = await fetch('http://localhost:8080/api/questions', {
const response = await fetch(getBackendUrl('/api/questions'), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({

View File

@@ -3,6 +3,7 @@ import { useSearchParams } from 'react-router-dom';
import { motion } from 'framer-motion';
import { Search, Download, Filter, BookOpen, FileText, ScrollText, BookMarked, ChevronRight, ArrowLeft, ExternalLink, Code, Globe, GitBranch, Cloud, Palette, Shield, DollarSign, BarChart3, Users, Megaphone, TrendingUp, UserCheck, ClipboardList, ShoppingCart, Cpu, Activity, LayoutGrid, Layers, Zap, Settings, Gauge, Radio, Wifi, Bot, Sun, Wrench, Binary, BatteryCharging, CircuitBoard, ShieldCheck, BrainCircuit, FlaskConical, Lock, Terminal, Dna, Microscope, Sparkles, Smartphone, Monitor, Server, CheckCircle2, Database, Briefcase } from 'lucide-react';
import SectionTitle from '@/components/SectionTitle/SectionTitle';
import { getBackendUrl } from '@/lib/utils';
import { StaggerContainer, StaggerItem } from '@/components/AnimatedContainer/AnimatedContainer';
import { TOOLKIT_ITEMS, DEPARTMENTS } from '@/constants';
@@ -1332,7 +1333,7 @@ export default function Notes() {
useEffect(() => {
const fetchNotes = async () => {
try {
const response = await fetch('http://localhost:8080/api/notes');
const response = await fetch(getBackendUrl('/api/notes'));
if (response.ok) {
const data = await response.json();
const mappedData = data.map((n: any) => ({
@@ -1361,7 +1362,7 @@ export default function Notes() {
const handleDownload = async (noteId: string, downloadUrl: string) => {
try {
await fetch(`http://localhost:8080/api/notes/${noteId}/download`, { method: 'POST' });
await fetch(getBackendUrl(`/api/notes/${noteId}/download`), { method: 'POST' });
setNotes(prev => prev.map(n => n.id === noteId ? { ...n, downloads: n.downloads + 1 } : n));
} catch (err) {
console.error('Error incrementing download count:', err);