Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import React, { useEffect, useState } from 'react';
- import { User, MapPin, Phone, Clock, AlertTriangle, Calendar, Ambulance, Bell } from 'lucide-react';
- import { useAuth } from './authContext';
- import SimpleMap from './map';
- import { useNavigate } from 'react-router-dom';
- import axios from 'axios';
- import WebSocketDemo from './websocket';
- // EmergencyModal moved outside
- const EmergencyModal = ({
- hospital,
- setHospital,
- emergencyDetail,
- setEmergencyDetail,
- setShowEmergencyModal,
- handleEmergencyRequest,
- }) => {
- const [hospitals, setHospitals] = useState([])
- const [selectedHospital, setSelectedHospital] = useState("");
- const [location, setLocation] = useState("");
- const [address, setAddress] = useState("");
- // Fetch all hospitals from API
- useEffect(() => {
- const getHospitals = async () => {
- try {
- const response = await axios.get(
- "http://localhost:8000/ambulance/hospital/"
- );
- const data = response.data;
- setHospitals(Array.isArray(data.data) ? data.data : []);
- } catch (error) {
- setHospitals([]);
- }
- };
- getHospitals();
- }, []);
- // Get user location and set location state on mount
- useEffect(() => {
- if (navigator.geolocation) {
- navigator.geolocation.getCurrentPosition(
- (position) => {
- const coords = `${position.coords.latitude},${position.coords.longitude}`;
- setLocation(coords);
- },
- (error) => {
- setLocation("");
- }
- );
- } else {
- setLocation("");
- }
- }, []);
- // When location changes and is not empty, reverse geocode automatically
- useEffect(() => {
- const getUserLocation = async () => {
- try {
- const [lat, lng] = location.split(',').map(coord => coord.trim());
- if (!lat || !lng) return;
- const response = await axios.get(
- `https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lng}`
- );
- setAddress(response.data.display_name || "");
- } catch (error) {
- setAddress("");
- }
- };
- if (location) {
- getUserLocation();
- } else {
- setAddress("");
- }
- }, [location]);
- // Determine if the form is valid
- const isFormValid = selectedHospital && emergencyDetail.trim();
- return (
- <div className="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50">
- <div className="bg-gray-800 rounded-2xl p-8 max-w-md w-full mx-4 shadow-2xl border border-gray-700">
- <div className="flex items-center gap-3 mb-6">
- <AlertTriangle className="text-red-400" size={32} />
- <h2 className="text-2xl font-bold text-red-400">Emergency Request</h2>
- </div>
- <div className="space-y-4">
- <div className="flex items-center gap-2">
- {address && (
- <div className="text-xs text-gray-400 mt-1">Address: {address}</div>
- )}
- <MapPin className="w-10 h-10 text-blue-400" />
- </div>
- <select
- value={selectedHospital}
- onChange={e => setSelectedHospital(e.target.value)}
- className="w-full p-3 bg-gray-700 text-white border border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400"
- >
- <option value="">Select a hospital</option>
- {Array.isArray(hospitals) && hospitals.map((hospital) => (
- <option key={hospital.id} value={hospital.id}>
- {hospital.name}
- </option>
- ))}
- </select>
- <textarea
- value={emergencyDetail}
- onChange={e => setEmergencyDetail(e.target.value)}
- placeholder="Emergency Details"
- className="w-full p-3 bg-gray-700 text-white border border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-400 h-20"
- />
- <div className="flex gap-3">
- <button
- onClick={() => setShowEmergencyModal(false)}
- className="flex-1 p-3 border border-gray-600 rounded-lg hover:bg-gray-700 text-gray-300"
- >
- Cancel
- </button>
- <button
- className="flex-1 p-3 bg-red-500 text-white rounded-lg hover:bg-red-600 disabled:opacity-50 disabled:cursor-not-allowed"
- onClick={() =>{ handleEmergencyRequest({
- location,
- emergencyDetail,
- selectedHospital,
- address
- }); setShowEmergencyModal(false)}}
- disabled={!isFormValid}
- >
- Request Now
- </button>
- </div>
- </div>
- </div>
- </div>
- );
- };
- // CasualModal moved outside
- const CasualModal = ({ setShowCasualModal }) => (
- <div className="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50">
- <div className="bg-gray-800 rounded-2xl p-8 max-w-md w-full mx-4 shadow-2xl border border-gray-700">
- <div className="flex items-center gap-3 mb-6">
- <Calendar className="text-blue-400" size={32} />
- <h2 className="text-2xl font-bold text-blue-400">Schedule Ride</h2>
- </div>
- <div className="space-y-4">
- <input
- type="text"
- placeholder="Pickup Location"
- className="w-full p-3 bg-gray-700 text-white border border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400"
- />
- <input
- type="text"
- placeholder="Destination"
- className="w-full p-3 bg-gray-700 text-white border border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400"
- />
- <input
- type="datetime-local"
- className="w-full p-3 bg-gray-700 text-white border border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400"
- />
- <select className="w-full p-3 bg-gray-700 text-white border border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400">
- <option>Select Service Type</option>
- <option>Medical Transport</option>
- <option>Routine Checkup</option>
- <option>Discharge Transport</option>
- </select>
- <div className="flex gap-3">
- <button
- onClick={() => setShowCasualModal(false)}
- className="flex-1 p-3 border border-gray-600 rounded-lg hover:bg-gray-700 text-gray-300"
- >
- Cancel
- </button>
- <button className="flex-1 p-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
- Schedule
- </button>
- </div>
- </div>
- </div>
- </div>
- );
- function IndividualPortal() {
- const [user, setUser] = useState(null);
- const [showEmergencyModal, setShowEmergencyModal] = useState(false);
- const [showCasualModal, setShowCasualModal] = useState(false);
- const { logout } = useAuth();
- const navigate = useNavigate();
- const [location, setLocation] = useState(null);
- const [hospital, setHospital] = useState('');
- const [emergencyDetail, setEmergencyDetail] = useState('');
- const [requestInfo, setRequestInfo] = useState('');
- const [showRequestInfo, setShowRequestInfo] = useState(false);
- const [requestInfoTimestamp, setRequestInfoTimestamp] = useState(null);
- const [requestInfoTitle, setRequestInfoTitle] = useState('');
- // Show popup and set timer when requestInfo changes
- useEffect(() => {
- if (requestInfo) {
- setShowRequestInfo(true);
- setRequestInfoTimestamp(new Date());
- setRequestInfoTitle('Notification'); // You can customize or set dynamically
- const timer = setTimeout(() => {
- setShowRequestInfo(false);
- }, 5000); // 5 seconds
- return () => clearTimeout(timer);
- }
- }, [requestInfo]);
- useEffect(() => {
- const userString = localStorage.getItem('user');
- if (userString) {
- try {
- const u = JSON.parse(userString);
- setUser(u);
- console.log('User found:', u.email);
- } catch (error) {
- console.error('Error parsing user data:', error);
- localStorage.removeItem('user');
- }
- }
- }, []);
- const handleLogout = () => {
- logout();
- navigate('/login', { replace: true });
- };
- const handleEmergencyRequest = async ({ location, emergencyDetail, selectedHospital, address }) => {
- try {
- console.log(selectedHospital)
- // Parse latitude and longitude from location string
- const [latitude, longitude] = location.split(',').map(coord => parseFloat(coord.trim()));
- const roundedLat = latitude ? Number(latitude.toFixed(6)) : null;
- const roundedLng = longitude ? Number(longitude.toFixed(6)) : null;
- // Use address as detected_address
- const detected_address = address;
- // Use selectedHospital as hospital id
- // Use emergencyDetail as emergency_details
- // Use 'Emergency' as priority for now
- const payload = {
- priority: 'Emergency',
- hospital: selectedHospital,
- emergency_details: emergencyDetail,
- detected_address,
- latitude: roundedLat,
- longitude: roundedLng,
- };
- console.log(payload)
- // Get token from localStorage if available
- const token = localStorage.getItem('token');
- await axios.post(
- 'http://127.0.0.1:8000/ambulance/request-ride/',
- payload,
- {
- headers: {
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
- 'Content-Type': 'application/json',
- },
- }
- );
- // Optionally show a success message or reset form here
- } catch (error) {
- // Optionally show an error message
- console.log(error);
- }
- };
- useEffect(() => {
- if (navigator.geolocation) {
- navigator.geolocation.getCurrentPosition(
- (position) => {
- setLocation(position.coords);
- },
- (error) => {
- console.log(error);
- }
- );
- } else {
- console.log('Geolocation is not supported by this browser.');
- }
- }, []);
- const getRequestNotification = (data) => {
- console.log(data)
- setRequestInfo(data)
- }
- // Popup modal for requestInfo
- // const showRequestInfo = Boolean(requestInfo); // This line is no longer needed
- return (
- <div className="min-h-screen bg-gradient-to-br from-gray-900 via-gray-800 to-gray-700">
- {/* Header */}
- <WebSocketDemo token={localStorage.getItem("token")} getNotifications={getRequestNotification} />
- <header className="bg-gray-900 shadow-sm border-b border-gray-800">
- <div className="max-w-6xl mx-auto px-6 py-4 flex items-center justify-between">
- <div className="flex items-center gap-3">
- <Ambulance className="w-10 h-10 text-red-500" />
- <h1 className="text-2xl font-bold text-white tracking-wide">AmbuCare</h1>
- </div>
- <div className="flex items-center gap-3">
- <div className="w-10 h-10 bg-blue-600 rounded-full flex items-center justify-center">
- <User className="text-white" size={20} />
- </div>
- <div>
- <p className="font-medium text-white">
- {user ? user.first_name : ''} {user ? user.last_name : ''}
- </p>
- <p className="text-sm text-gray-400">{user ? user.role : ''}</p>
- </div>
- <button
- className="bg-red-500 text-white px-4 py-2 rounded-md hover:bg-red-600"
- onClick={handleLogout}
- >
- Logout
- </button>
- </div>
- </div>
- </header>
- <div className="max-w-6xl mx-auto px-6 py-8">
- <div className="grid lg:grid-cols-3 gap-8">
- {/* Map Section */}
- <div className="lg:col-span-2">
- <div className="bg-gray-800 rounded-2xl shadow-lg p-6 border border-gray-700">
- <div className="flex items-center gap-2 mb-4">
- <MapPin className="text-blue-400" size={24} />
- <h2 className="text-xl font-semibold text-white">Your Location</h2>
- </div>
- <div className="h-96 bg-gray-900 rounded-xl flex items-center justify-center relative z-0 border border-gray-700">
- {/* send location to map */}
- <SimpleMap ambulance_location={[requestInfo.lat, requestInfo.lng]} />
- </div>
- </div>
- </div>
- {/* Service Panel */}
- <div className="space-y-6">
- {/* Emergency Button */}
- <div className="bg-gray-800 rounded-2xl shadow-lg p-6 border border-gray-700">
- <button
- onClick={() => setShowEmergencyModal(true)}
- className="w-full bg-red-500 hover:bg-red-600 text-white p-6 rounded-xl transition-colors font-semibold text-lg shadow-md"
- >
- <AlertTriangle className="mx-auto mb-3" size={32} />
- <h3 className="text-xl font-bold">Emergency</h3>
- <p className="text-sm mt-2 opacity-90">Immediate medical assistance</p>
- </button>
- </div>
- {/* Casual Service Button */}
- <div className="bg-gray-800 rounded-2xl shadow-lg p-6 border border-gray-700">
- <button
- onClick={() => setShowCasualModal(true)}
- className="w-full bg-blue-500 hover:bg-blue-600 text-white p-6 rounded-xl transition-colors font-semibold text-lg shadow-md"
- >
- <Calendar className="mx-auto mb-3" size={32} />
- <h3 className="text-xl font-bold">Schedule Ride</h3>
- <p className="text-sm mt-2 opacity-90">Plan your medical transport</p>
- </button>
- </div>
- {/* Quick Stats */}
- <div className="bg-gray-800 rounded-2xl shadow-lg p-6 border border-gray-700">
- <h3 className="font-semibold mb-4 text-white">Quick Stats</h3>
- <div className="space-y-3">
- <div className="flex items-center gap-3">
- <Clock className="text-green-400" size={16} />
- <span className="text-sm text-gray-300">Avg. Response: 8 min</span>
- </div>
- <div className="flex items-center gap-3">
- <Phone className="text-blue-400" size={16} />
- <span className="text-sm text-gray-300">24/7 Support</span>
- </div>
- <div className="flex items-center gap-3">
- <MapPin className="text-purple-400" size={16} />
- <span className="text-sm text-gray-300">12 Nearby Units</span>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- {/* Modals */}
- {showEmergencyModal && (
- <EmergencyModal
- hospital={hospital}
- setHospital={setHospital}
- emergencyDetail={emergencyDetail}
- setEmergencyDetail={setEmergencyDetail}
- setShowEmergencyModal={setShowEmergencyModal}
- handleEmergencyRequest={handleEmergencyRequest}
- />
- )}
- {showCasualModal && <CasualModal setShowCasualModal={setShowCasualModal} />}
- {/* ================================== > */}
- {/* Popup for requestInfo */}
- {showRequestInfo && (
- <div className="fixed inset-0 flex items-center justify-center z-50">
- <div className="bg-gray-900 rounded-xl shadow-2xl p-6 max-w-sm w-full border border-gray-700 flex flex-col items-center">
- <div className="mb-3">
- <Bell className="w-10 h-10 text-yellow-400" />
- </div>
- <div className="text-lg font-semibold text-white mb-1">{requestInfoTitle}</div>
- {requestInfoTimestamp && (
- <div className="text-xs text-gray-400 mb-2">
- {requestInfoTimestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })}
- </div>
- )}
- {/* display the state of the request */}
- <div className="mb-4 text-gray-200 text-center">{requestInfo.info}</div>
- <button
- className="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700 transition-colors"
- onClick={() => { setShowRequestInfo(false); }}
- >
- Close
- </button>
- </div>
- </div>
- )}
- </div>
- );
- }
- export default IndividualPortal;
Advertisement
Add Comment
Please, Sign In to add comment