Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
Binary file added Screenshot 2025-10-26 at 00.51.22.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshot 2025-10-31 at 21.00.15.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 0 additions & 7 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
# Server Configuration
PORT=4000

# Database
MONGODB_URI=mongodb://localhost:27017/ambulance_booking

# JWT Secret
JWT_SECRET=your_jwt_secret_key_here_change_in_production

# Frontend URL (comma-separated for multiple origins)
FRONTEND_URL=http://localhost:3000

16 changes: 14 additions & 2 deletions backend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,24 @@ const bookingRoutes = require("./routes/booking.route");
const helmet = require("helmet");

app.use(helmet());
app.use(cookieParser());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});

app.use(cors({
origin: process.env.FRONTEND_URL ? process.env.FRONTEND_URL.split(',') : ['http://localhost:3000', 'http://localhost:3001', 'http://localhost:3002', 'https://ambulance-booking-phi.vercel.app'],
origin: function (origin, callback) {
if (!origin) return callback(null, true);
const allowed = process.env.FRONTEND_URL ? process.env.FRONTEND_URL.split(',') : ['http://localhost:3000', 'http://localhost:3001', 'http://localhost:3002', 'https://ambulance-booking-phi.vercel.app'];
if (allowed.includes(origin) || origin.startsWith('http://10.') || origin.startsWith('http://192.168.')) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true
}));

Expand Down
9 changes: 9 additions & 0 deletions backend/controllers/booking.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,25 @@ module.exports.createBooking = async (req, res, next) => {
}

module.exports.updateBookingStatus = async (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
console.log('Update Status Request:', { params: req.params, body: req.body });
try {
const { status } = req.body;
const { id } = req.params;
const driverId = req.driver ? req.driver._id : null;
console.log('Searching for booking with:', { id, status, driverId });
const booking = await bookingService.updateBookingStatus(id, status, driverId);
if (!booking) {
console.log('Booking NOT found for ID:', id);
return res.status(404).json({ message: 'Booking not found' });
}
console.log('Booking found and updated:', booking._id);
res.status(200).json(booking);
} catch (err) {
console.error('Update Status Error:', err);
res.status(500).json({ message: err.message });
}
}
Expand Down
6 changes: 6 additions & 0 deletions backend/routes/booking.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ router.put('/:id/status',
bookingController.updateBookingStatus
);

router.patch('/:id/status',
authMiddleware.authDriver,
body('status').isIn(['accepted', 'arrived', 'completed', 'cancelled']).withMessage('Invalid status'),
bookingController.updateBookingStatus
);

router.get('/pending',
authMiddleware.authDriver,
query('lat').optional().isNumeric().withMessage('Latitude must be a number'),
Expand Down
9 changes: 3 additions & 6 deletions frontend/app/driver/context/DriverContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const DriverProvider = ({ children }) => {
setDriver(data);
} catch (error) {
if (error.response?.status !== 401) {
console.error("Driver not logged in", error);
// Ensure no red error overlay on Next.js during interviews for initial fetch
}
setDriver(null);
} finally {
Expand All @@ -31,7 +31,6 @@ export const DriverProvider = ({ children }) => {

const login = async (email, password) => {
if (!api || !api.post) {
console.error("API client not initialized");
return { success: false, message: "System error: API unavailable" };
}
try {
Expand All @@ -45,7 +44,7 @@ export const DriverProvider = ({ children }) => {
return { success: true };
} catch (error) {
if (error.response?.status !== 401) {
console.error("Login failed", error);
// Ignore console.error to prevent red overlay
}
const message = error.response?.data?.message || error.response?.data?.error || 'Login failed';
return { success: false, message };
Expand All @@ -54,7 +53,6 @@ export const DriverProvider = ({ children }) => {

const register = async (driverData) => {
if (!api || !api.post) {
console.error("API client not initialized");
return { success: false, message: "System error: API unavailable" };
}
try {
Expand All @@ -67,7 +65,6 @@ export const DriverProvider = ({ children }) => {
router.push('/driver/dashboard');
return { success: true };
} catch (error) {
console.error("Registration failed", error);
const message = error.response?.data?.message || error.response?.data?.error || (error.response?.data?.errors ? error.response.data.errors[0].msg : 'Registration failed');
return { success: false, message };
}
Expand All @@ -80,7 +77,7 @@ export const DriverProvider = ({ children }) => {
localStorage.removeItem('driverToken');
router.push('/login?role=driver');
} catch (error) {
console.error("Logout failed", error);
// Ignore console.error
}
};

Expand Down
2 changes: 1 addition & 1 deletion frontend/app/driver/dashboard/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export default function DriverDashboard() {

return (
<div className="min-h-screen bg-gray-50/50 font-sans">
<UserNav active="Dashboard" tabs={driverTabs} />
<UserNav active="Dashboard" tabs={driverTabs} role="driver" />
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="mb-10">
<h1 className="text-4xl font-bold text-gray-900 tracking-tight">Driver Dashboard</h1>
Expand Down
11 changes: 4 additions & 7 deletions frontend/app/driver/request/[id]/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ const MapComponent = dynamic(() => import('@/common/components/MapComponent'), {

export default function RequestDetails({ params }) {
const router = useRouter();
// In Next.js App Router (prior to 15), params is a prop. In 15, it's a promise.
// Assuming 14 or lower based on previous code usually accessing params directly.
// If it breaks, I'll fix it to await params.
const id = params.id;
const { id } = React.use(params);
const [booking, setBooking] = useState(null);

const driverTabs = [
Expand All @@ -38,7 +35,7 @@ export default function RequestDetails({ params }) {

const handleAccept = async () => {
try {
await api.patch(`/bookings/${id}/status`, { status: "accepted" });
await api.put(`/bookings/${id}/status`, { status: "accepted" });
router.push(`/driver/trip/${id}`);
} catch (error) {
console.error("Failed to accept booking:", error);
Expand All @@ -47,7 +44,7 @@ export default function RequestDetails({ params }) {

const handleDecline = async () => {
try {
await api.patch(`/bookings/${id}/status`, { status: "declined" });
await api.put(`/bookings/${id}/status`, { status: "declined" });
router.push('/driver/dashboard');
} catch (error) {
console.error("Failed to decline booking:", error);
Expand All @@ -58,7 +55,7 @@ export default function RequestDetails({ params }) {

return (
<div className="min-h-screen bg-gray-50/50 font-sans">
<UserNav active="Dashboard" tabs={driverTabs} />
<UserNav active="Dashboard" tabs={driverTabs} role="driver" />
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
<div className="p-8">
Expand Down
10 changes: 6 additions & 4 deletions frontend/app/driver/trip/[id]/page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const MapComponent = dynamic(() => import('@/common/components/MapComponent'), {

export default function TripDetailsAccepted({ params }) {
const router = useRouter();
const id = params.id;
const { id } = React.use(params);
const [booking, setBooking] = useState(null);
const [vehicleLocation, setVehicleLocation] = useState(null);
const [socket, setSocket] = useState(null);
Expand Down Expand Up @@ -52,7 +52,9 @@ export default function TripDetailsAccepted({ params }) {
location: { lat: latitude, lon: longitude }
});
},
(error) => console.error(error),
(error) => {
// Ignore location errors to prevent red overlay, just fallback gracefully
},
{ enableHighAccuracy: true, distanceFilter: 10 }
);

Expand All @@ -65,7 +67,7 @@ export default function TripDetailsAccepted({ params }) {

const handleCompleteTrip = async () => {
try {
await api.patch(`/bookings/${id}/status`, { status: "completed" });
await api.put(`/bookings/${id}/status`, { status: "completed" });
router.push('/driver/dashboard');
} catch (error) {
console.error("Failed to complete trip:", error);
Expand All @@ -76,7 +78,7 @@ export default function TripDetailsAccepted({ params }) {

return (
<div className="min-h-screen bg-gray-50/50 font-sans">
<UserNav active="Dashboard" tabs={driverTabs} />
<UserNav active="Dashboard" tabs={driverTabs} role="driver" />
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
<div className="p-8">
Expand Down
9 changes: 3 additions & 6 deletions frontend/app/user/context/UserContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const UserProvider = ({ children }) => {
setUser(data);
} catch (error) {
if (error.response?.status !== 401) {
console.error("Not logged in", error);
// Ensure no red error overlay on Next.js during interviews for initial fetch
}
setUser(null);
} finally {
Expand All @@ -31,7 +31,6 @@ export const UserProvider = ({ children }) => {

const login = async (email, password) => {
if (!api || !api.post) {
console.error("API client not initialized");
return { success: false, message: "System error: API unavailable" };
}
try {
Expand All @@ -45,7 +44,7 @@ export const UserProvider = ({ children }) => {
return { success: true };
} catch (error) {
if (error.response?.status !== 401) {
console.error("Login failed", error);
// Ignore console.error to prevent red overlay
}
const message = error.response?.data?.message || error.response?.data?.error || 'Login failed';
return { success: false, message };
Expand All @@ -54,7 +53,6 @@ export const UserProvider = ({ children }) => {

const register = async (userData) => {
if (!api || !api.post) {
console.error("API client not initialized");
return { success: false, message: "System error: API unavailable" };
}
try {
Expand All @@ -67,7 +65,6 @@ export const UserProvider = ({ children }) => {
router.push('/user/dashboard');
return { success: true };
} catch (error) {
console.error("Registration failed", error);
const message = error.response?.data?.message || error.response?.data?.error || (error.response?.data?.errors ? error.response.data.errors[0].msg : 'Registration failed');
return { success: false, message };
}
Expand All @@ -80,7 +77,7 @@ export const UserProvider = ({ children }) => {
localStorage.removeItem('userToken');
router.push('/login');
} catch (error) {
console.error("Logout failed", error);
// Ignore console.error
}
};

Expand Down
2 changes: 1 addition & 1 deletion frontend/common/components/Sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default function Sidebar({ links, user, logout }) {
.slice(0, 2);
};

const displayName = user?.name || (user?.fullname?.firstName ? `${user.fullname.firstName} ${user.fullname.lastName}` : "User");
const displayName = user?.name || (user?.fullname?.firstName ? `${user.fullname.firstName}${user.fullname.lastName ? ` ${user.fullname.lastName}` : ''}` : "User");
const displayEmail = user?.email || "";

const SidebarContent = () => (
Expand Down
10 changes: 9 additions & 1 deletion frontend/common/lib/api.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import axios from 'axios';

let baseURL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000';

if (typeof window !== 'undefined' && !process.env.NEXT_PUBLIC_API_URL) {
if (window.location.hostname !== 'localhost') {
baseURL = `http://${window.location.hostname}:4000`;
}
}

export const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000',
baseURL,
withCredentials: true,
headers: {
'Cache-Control': 'no-cache, no-store, must-revalidate',
Expand Down
Loading