├── .firebaserc ├── client ├── .firebaserc ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── apple-touch-icon.png │ ├── mstile-150x150.png │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── browserconfig.xml │ ├── site.webmanifest │ ├── manifest.json │ ├── index.html │ └── safari-pinned-tab.svg ├── src │ ├── media │ │ ├── logo.png │ │ ├── spinner.gif │ │ ├── homepage_ambulance.webp │ │ ├── main_homepage_blob.svg │ │ └── main_homepage__blob.svg │ ├── components │ │ ├── ServerUrl.js │ │ ├── verifyLocation.js │ │ ├── MakeMessage.js │ │ ├── Home.js │ │ ├── MainDashboard.js │ │ ├── Tagline.js │ │ ├── Header.js │ │ ├── About.js │ │ ├── UserDashboard │ │ │ ├── History.js │ │ │ ├── Donate.js │ │ │ ├── UserDashboard.js │ │ │ ├── Profile.js │ │ │ └── RequestAmbulance.js │ │ ├── SudoDashboard │ │ │ ├── Donations.js │ │ │ ├── SudoDashboard.js │ │ │ ├── Dashboard.js │ │ │ ├── Drivers.js │ │ │ ├── Ambulances.js │ │ │ └── Paramedics.js │ │ ├── Contact.js │ │ ├── DriverDashboard │ │ │ ├── DriverDashboard.js │ │ │ └── IncomingRequests.js │ │ ├── Login.js │ │ └── Signup.js │ ├── index.js │ ├── App.css │ ├── App.js │ └── index.css ├── README.md ├── firebase.json └── package.json ├── firebase.json ├── functions ├── jwtGen.js ├── connect.js ├── authorize.js ├── package.json └── index.js ├── package.json ├── LICENSE ├── .gitignore └── README.md /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "ambulancekaro" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /client/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "ambulancekaro" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4lf0rm3d/Ambulance-Karo/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/src/media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4lf0rm3d/Ambulance-Karo/HEAD/client/src/media/logo.png -------------------------------------------------------------------------------- /client/src/media/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4lf0rm3d/Ambulance-Karo/HEAD/client/src/media/spinner.gif -------------------------------------------------------------------------------- /client/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4lf0rm3d/Ambulance-Karo/HEAD/client/public/favicon-16x16.png -------------------------------------------------------------------------------- /client/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4lf0rm3d/Ambulance-Karo/HEAD/client/public/favicon-32x32.png -------------------------------------------------------------------------------- /client/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4lf0rm3d/Ambulance-Karo/HEAD/client/public/apple-touch-icon.png -------------------------------------------------------------------------------- /client/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4lf0rm3d/Ambulance-Karo/HEAD/client/public/mstile-150x150.png -------------------------------------------------------------------------------- /client/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4lf0rm3d/Ambulance-Karo/HEAD/client/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /client/public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4lf0rm3d/Ambulance-Karo/HEAD/client/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /client/src/media/homepage_ambulance.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4lf0rm3d/Ambulance-Karo/HEAD/client/src/media/homepage_ambulance.webp -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # Rescue++ — Online Ambulance Booking App 2 | Rescue++ is Pakistan's first online ambulance booking app. Call an ambulance with a click only!
3 | This project is currently in development mode. 4 | -------------------------------------------------------------------------------- /client/src/components/ServerUrl.js: -------------------------------------------------------------------------------- 1 | // GLOBAL API 2 | const url = 'https://us-central1-ambulancekaro.cloudfunctions.net/api' 3 | 4 | // UNCOMMENT Line:4 when running on local environment 5 | // const url = `http://${window.location.hostname}:5000` 6 | 7 | export default url -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": [ 3 | { 4 | "source": "functions", 5 | "codebase": "default", 6 | "ignore": [ 7 | "node_modules", 8 | ".git", 9 | "firebase-debug.log", 10 | "firebase-debug.*.log" 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /client/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /functions/jwtGen.js: -------------------------------------------------------------------------------- 1 | // jwt module 2 | const jwt = require("jsonwebtoken"); 3 | // secret key 4 | require("dotenv").config(); 5 | 6 | const jwtGen = (email, userid) => { 7 | const payload = { email, userid } 8 | return jwt.sign(payload, process.env.KEY, {expiresIn : '1h'}); 9 | } 10 | module.exports = jwtGen; -------------------------------------------------------------------------------- /client/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "build", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import { BrowserRouter } from 'react-router-dom'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /client/src/components/verifyLocation.js: -------------------------------------------------------------------------------- 1 | const verifyLocation = async()=>{ 2 | return new Promise((resolve, reject) => { 3 | navigator.geolocation.getCurrentPosition( 4 | (position) => { 5 | resolve([position.coords.latitude, position.coords.longitude]); 6 | }, 7 | (error) => { 8 | reject(false); 9 | } 10 | ); 11 | }); 12 | } 13 | export default verifyLocation; -------------------------------------------------------------------------------- /functions/connect.js: -------------------------------------------------------------------------------- 1 | // postgres module pool for connection 2 | const Pool = require("pg").Pool; 3 | 4 | // .env file variables 5 | require("dotenv").config(); 6 | 7 | // database configuration 8 | const db = new Pool({ 9 | "user": process.env.USER_DB, 10 | "password": process.env.PASSWORD, 11 | "host": process.env.HOST, 12 | "port": Number(process.env.PORT_DB), 13 | "database": process.env.DATABASE, 14 | }); 15 | 16 | module.exports = db; 17 | -------------------------------------------------------------------------------- /client/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Ambulance Karo", 3 | "short_name": "AK", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "AK", 3 | "name": "Ambulance Karo", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#ff5049", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /functions/authorize.js: -------------------------------------------------------------------------------- 1 | // Authorize user request 2 | const jwt = require("jsonwebtoken") 3 | require("dotenv").config() 4 | 5 | module.exports = (req, res, next) => { 6 | // token must present in each request header 7 | const token = req.header('token'); 8 | 9 | // if there is not token, throw error 10 | if(!token) return res.status(401).send({ message: 'Unauthorized!' }); 11 | 12 | try{ 13 | const verify = jwt.verify(token,process.env.KEY); 14 | next(); 15 | }catch(e){ 16 | 17 | // if there is malformed token, throw error 18 | return res.status(401).send({ message: 'Unauthorized!' }); 19 | } 20 | } -------------------------------------------------------------------------------- /client/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "serve": "firebase emulators:start --only functions", 6 | "shell": "firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log" 10 | }, 11 | "engines": { 12 | "node": "16" 13 | }, 14 | "main": "index.js", 15 | "dependencies": { 16 | "cors": "^2.8.5", 17 | "dotenv": "^16.0.3", 18 | "express": "^4.18.2", 19 | "firebase-admin": "^10.0.2", 20 | "firebase-functions": "^3.18.0", 21 | "pg": "^8.8.0" 22 | }, 23 | "devDependencies": { 24 | "firebase-functions-test": "^0.2.0" 25 | }, 26 | "private": true 27 | } 28 | -------------------------------------------------------------------------------- /client/src/media/main_homepage_blob.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "serve": "firebase emulators:start --only functions", 6 | "shell": "firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log" 10 | }, 11 | "engines": { 12 | "node": "16" 13 | }, 14 | "main": "index.js", 15 | "dependencies": { 16 | "bcrypt": "^5.1.0", 17 | "cors": "^2.8.5", 18 | "dotenv": "^16.0.3", 19 | "express": "^4.18.2", 20 | "firebase-admin": "^10.0.2", 21 | "firebase-functions": "^3.18.0", 22 | "jsonwebtoken": "^9.0.0", 23 | "pg": "^8.8.0" 24 | }, 25 | "devDependencies": { 26 | "firebase-functions-test": "^0.2.0" 27 | }, 28 | "private": true 29 | } 30 | -------------------------------------------------------------------------------- /client/src/components/MakeMessage.js: -------------------------------------------------------------------------------- 1 | const makeMessage = (messageIconStyle, messageIconColor, messageText, messageProgressColor,id) => { 2 | // message to user 3 | const messageContainer = document.createElement("div") 4 | messageContainer.className = "message_container" 5 | messageContainer.id = id 6 | const messageIcon = document.createElement("i") 7 | const message = document.createElement("p") 8 | const progressBar = document.createElement("div") 9 | progressBar.className = "message_progress" 10 | messageIcon.className = messageIconStyle 11 | message.innerText = messageText 12 | messageIcon.style.color = messageIconColor 13 | progressBar.style.background = messageProgressColor 14 | messageContainer.append(messageIcon) 15 | messageContainer.append(message) 16 | messageContainer.append(progressBar) 17 | document.body.append(messageContainer) 18 | } 19 | 20 | export default makeMessage; -------------------------------------------------------------------------------- /client/src/media/main_homepage__blob.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/Home.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useNavigate } from "react-router-dom"; 3 | import Header from "./Header"; 4 | import Tagline from "./Tagline"; 5 | import Helmet from "react-helmet"; 6 | 7 | function Home(){ 8 | 9 | const redirect = useNavigate() 10 | 11 | useEffect(()=>{ 12 | 13 | if(localStorage.getItem("token")){ 14 | redirect("/dashboard") 15 | } 16 | },[]) 17 | 18 | return( 19 | <> 20 | 21 | Ambulance Karo — Pakistan's first 22 | E-Ambulance app 23 | 28 | 29 |
30 |
31 |
32 | 33 | 34 |
35 |
36 | 37 | ) 38 | } 39 | 40 | export default Home; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ambulance Karo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rescue", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "chart.js": "^4.0.1", 10 | "framer-motion": "^7.6.5", 11 | "react": "^18.2.0", 12 | "react-chartjs-2": "^5.0.1", 13 | "react-dom": "^18.2.0", 14 | "react-helmet": "^6.1.0", 15 | "react-router-dom": "^6.4.3", 16 | "react-scripts": "5.0.1", 17 | "web-vitals": "^2.1.4" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject" 24 | }, 25 | "eslintConfig": { 26 | "extends": [ 27 | "react-app", 28 | "react-app/jest" 29 | ] 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /client/src/components/MainDashboard.js: -------------------------------------------------------------------------------- 1 | 2 | import url from "./ServerUrl" 3 | import { useNavigate } from "react-router-dom" 4 | import { useState,useEffect } from "react" 5 | import UserDashboard from "./UserDashboard/UserDashboard" 6 | 7 | const MainDashboard = () => { 8 | 9 | const redirect = useNavigate() 10 | const [data, setData] = useState({}) 11 | const verifyJWT = async() => { 12 | const headersList = { 13 | "Accept": "*/*", 14 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 15 | "token": localStorage.token 16 | } 17 | 18 | let response = await fetch(`${url}/self`, { 19 | method: "POST", 20 | headers: headersList 21 | }); 22 | if(response.status !== 200){ 23 | localStorage.clear() 24 | return redirect("/") 25 | } 26 | const temp = await response.json() 27 | setData(temp.rule) 28 | } 29 | 30 | useEffect(()=>{ 31 | setTimeout(() => { 32 | verifyJWT() 33 | }, 2000); 34 | },[]) 35 | 36 | return <> 37 | 38 | {/* {!data ?

Loading...

: } */} 39 | {!data ?

Loading...

:

Welcome

} 40 | 41 | 42 | 43 | } 44 | export default MainDashboard; -------------------------------------------------------------------------------- /client/src/components/Tagline.js: -------------------------------------------------------------------------------- 1 | import Ambulance from "./../media/homepage_ambulance.webp" 2 | import { useNavigate } from "react-router-dom"; 3 | import { motion } from "framer-motion" 4 | 5 | function Tagline(){ 6 | const n = useNavigate() 7 | 8 | 9 | 10 | return( 11 |
12 |
13 |

Pakistan's first
E-Ambulance app

14 |

Emergency care, just a tap away

15 | { 16 | n("/signup") 17 | 18 | }} >Signup for free! 19 |
20 |
21 | 38 |
39 |
40 | ) 41 | } 42 | 43 | export default Tagline; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | firebase-debug.log* 8 | firebase-debug.*.log* 9 | 10 | # Firebase cache 11 | .firebase/ 12 | 13 | # Firebase config 14 | 15 | # Uncomment this if you'd like others to create their own Firebase project. 16 | # For a team working on the same Firebase project(s), it is recommended to leave 17 | # it commented so all members can deploy to the same project(s) in .firebaserc. 18 | # .firebaserc 19 | 20 | # Runtime data 21 | pids 22 | *.pid 23 | *.seed 24 | *.pid.lock 25 | 26 | # Directory for instrumented libs generated by jscoverage/JSCover 27 | lib-cov 28 | 29 | # Coverage directory used by tools like istanbul 30 | coverage 31 | 32 | # nyc test coverage 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 36 | .grunt 37 | 38 | # Bower dependency directory (https://bower.io/) 39 | bower_components 40 | 41 | # node-waf configuration 42 | .lock-wscript 43 | 44 | # Compiled binary addons (http://nodejs.org/api/addons.html) 45 | build/ 46 | 47 | # Dependency directories 48 | node_modules/ 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | -------------------------------------------------------------------------------- /client/src/components/Header.js: -------------------------------------------------------------------------------- 1 | 2 | import { useNavigate } from "react-router-dom"; 3 | import { motion } from "framer-motion"; 4 | import logo from "../media/logo.png" 5 | 6 | function Header(){ 7 | const redirect = useNavigate() 8 | 9 | return( 10 |
11 |
12 | { 13 | document.getElementById("header_links").style.left = "0" 14 | }} className="fa-solid fa-bars open_nav"> 15 | 16 | { 17 | window.location.replace("/") 18 | }} className="logo" src={logo} alt="Ambulance Karo Logo" /> 19 | 20 | 33 |
34 |
35 | ) 36 | } 37 | 38 | export default Header; -------------------------------------------------------------------------------- /client/src/components/About.js: -------------------------------------------------------------------------------- 1 | import Header from "./Header"; 2 | 3 | 4 | export default function About(){ 5 | return( 6 | <> 7 |
8 |
9 |
10 |
11 |

About

12 |

Welcome to Ambulance Karo, Pakistan's first e-ambulance service. Our mission is to provide fast and reliable medical transportation to those in need, using the latest technology to ensure the safety and comfort of our patients.

13 |

Ambulance Karo was founded by Ahsan Azeemi, a dedicated and innovative developer who saw the need for an e-ambulance service in Pakistan. Ahsan has worked tirelessly to bring Ambulance Karo to life, and he is committed to continuing to improve and expand our services.

14 |

We are proud to be the first app of our kind in Pakistan, and we are grateful for the opportunity to serve our community. However, we rely on donations to keep our services running. If you would like to support our mission and help us to continue providing e-ambulance services to those in need, please consider making a donation.

15 |

Thank you for choosing Ambulance Karo for your medical transportation needs. We are committed to providing the highest level of care to our patients and we look forward to serving you.

16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |


Regards,
Pioneer of online ambulance service in Pakistan

24 |

AHSAN AZEEMI (xorahsan@gmail.com)

27 |
28 | 29 |
30 | 31 | ) 32 | } -------------------------------------------------------------------------------- /client/src/components/UserDashboard/History.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import url from "../ServerUrl"; 3 | 4 | const History = () => { 5 | 6 | const [history, setHistory] = useState([]) 7 | 8 | const getHistory = async() => { 9 | let headersList = { 10 | "Accept": "*/*", 11 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 12 | "Content-Type": "application/json", 13 | "token": localStorage.token 14 | } 15 | 16 | let response = await fetch(`${url}/history`, { 17 | method: "POST", 18 | headers: headersList 19 | }); 20 | let data = await response.json(); 21 | data = data.reverse() 22 | setHistory(data) 23 | } 24 | useEffect(()=>{ 25 | getHistory() 26 | },[]) 27 | 28 | 29 | return( 30 |
31 |

History

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | {history.map((r)=>{ 41 | return 42 | 43 | 44 | 45 | {r.status === "Active" ? : ""} 46 | {r.status === "Completed" ? : ""} 47 | {r.status === "Canceled" ? : ""} 48 | 49 | })} 50 | 51 |
IDLocationTimeStatus
{r.requestid}Open Maps{r.time}{r.status}{r.status}{r.status}
52 |
53 | ) 54 | } 55 | export default History; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ambulance Karo — Pakistan's first E-Ambulance app 2 | 3 | Ambulance Karo is a web application that provides a convenient and efficient way to request an ambulance. The app is built with the Pern stack (PostgreSQL, Express.js, React, and Node.js) and uses JWT authentication for secure user login. 4 | 5 | ## Live Preview 6 | 7 | [Visit Ambulance Karo](https://ambulancekaro.web.app/) 8 | 9 | ## Features 10 | - Request an ambulance with just a few clicks 11 | - Track the ambulance's location 12 | - Update profile information 13 | - View ride history 14 | - User-friendly interface 15 | 16 | ## Technology Stack 17 | - Pern Stack (PostgreSQL, Express.js, React, and Node.js) 18 | - JWT Authentication 19 | - Google Cloud Functions for API endpoint 20 | - Firebase for hosting 21 | 22 | ## Installation 23 | 24 | 1. Clone the repository `git clone https://github.com/xorahsan/Ambulance-Karo.git` 25 | 2. Navigate to the project directory `cd Ambulance-Karo` 26 | 3. Install the dependencies `npm install` 27 | 4. Start the development server `npm start` 28 | 29 | ## Deployment 30 | 31 | This project is deployed on Firebase, you can use the Firebase CLI to deploy your own version of the app. 32 | 33 | 1. Install Firebase CLI `npm install -g firebase-tools` 34 | 2. Login to your Firebase account `firebase login` 35 | 3. Initialize the project `firebase init` 36 | 4. Select the options as per your need 37 | 5. Deploy the project `firebase deploy` 38 | 39 | ## Contributing 40 | 41 | We welcome contributions to this project. If you want to contribute, please follow these guidelines: 42 | 43 | 1. Fork the repository 44 | 2. Create a new branch for your feature 45 | 3. Make your changes and commit them 46 | 4. Push your changes to your fork 47 | 5. Open a pull request and describe your changes 48 | 49 | ## License 50 | 51 | This project is licensed under the [MIT License](https://opensource.org/licenses/MIT). 52 | 53 | ## Contact 54 | 55 | For any queries or suggestions, please open an issue or contact me at [email](mailto:xorahsan@gmail.com) 56 | 57 | ## Acknowledgements 58 | 59 | I would like to thank the open-source community for providing the tools and resources that made this project possible. 60 | -------------------------------------------------------------------------------- /client/src/components/UserDashboard/Donate.js: -------------------------------------------------------------------------------- 1 | 2 | import { motion } from "framer-motion"; 3 | import { useState } from "react"; 4 | import makeMessage from "../MakeMessage"; 5 | import url from "../ServerUrl" 6 | 7 | const Donate = () => { 8 | 9 | const [amount, setAmount] = useState(1000) 10 | 11 | const userDonate = async() => { 12 | 13 | let headersList = { 14 | "Accept": "*/*", 15 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 16 | "Content-Type": "application/json", 17 | "token": localStorage.token 18 | } 19 | 20 | let bodyContent = JSON.stringify({ 21 | "donatedOn": String(new Date()), 22 | "donatedAmount": amount 23 | }); 24 | 25 | let response = await fetch(`${url}/donate`, { 26 | method: "POST", 27 | body: bodyContent, 28 | headers: headersList 29 | }); 30 | 31 | 32 | makeMessage("fa-solid fa-circle-check"," #198754","Thanks for donation! Our team will contact you soon!","#198754","donate_by_user") 33 | setTimeout(() => { 34 | document.getElementById("donate_by_user").remove() 35 | 36 | }, 3000); 37 | } 38 | 39 | return( 40 |
41 |

Donate

42 | 43 |

Please consider making a donation to support the development and maintenance of our E-Ambulance app.

44 |

Select amount (in PKR):

45 | 51 | Donate 52 |
53 | ) 54 | } 55 | export default Donate; -------------------------------------------------------------------------------- /client/src/components/SudoDashboard/Donations.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import url from "../ServerUrl"; 3 | 4 | const Donations = () => { 5 | 6 | const [donations, setDonations] = useState([]) 7 | 8 | const loadDonations = async() => { 9 | let headersList = { 10 | "Accept": "*/*", 11 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 12 | "Content-Type": "application/json", 13 | "token": localStorage.token 14 | } 15 | let bodyContent = JSON.stringify({ 16 | "query": `getDonationsData` 17 | }); 18 | 19 | let response = await fetch(`${url}/resolveQuery`, { 20 | method: "POST", 21 | body: bodyContent, 22 | headers: headersList 23 | }); 24 | let data = await response.json(); 25 | setDonations(data) 26 | 27 | } 28 | useEffect(()=>{ 29 | loadDonations() 30 | },[]) 31 | 32 | 33 | return( 34 |
35 |

Donations

36 |

Current Donations ({donations.length})

37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | {donations.map(item => { 50 | return 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | })} 59 | 60 |
Donation IDDonor IDDonor EmailDonor ContactTimeDonor Amount
{item.donationid}{item.donorid}{item.email}{item.contact}{item.donatedon}{item.donatedamount}
61 |
62 | ) 63 | } 64 | export default Donations; -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import { Routes,Route, useNavigate } from 'react-router-dom'; 3 | import { Suspense, useEffect, useState } from 'react'; 4 | 5 | import Home from './components/Home'; 6 | import Signup from './components/Signup'; 7 | import Login from './components/Login'; 8 | import About from './components/About'; 9 | import Contact from './components/Contact'; 10 | import UserDashboard from './components/UserDashboard/UserDashboard'; 11 | import DriverDashboard from './components/DriverDashboard/DriverDashboard'; 12 | import SudoDashboard from './components/SudoDashboard/SudoDashboard'; 13 | import url from './components/ServerUrl'; 14 | 15 | function App () { 16 | const redirect = useNavigate() 17 | const [data, setData] = useState({}) 18 | const verifyJWT = async() => { 19 | const headersList = { 20 | "Accept": "*/*", 21 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 22 | "token": localStorage.token 23 | } 24 | 25 | let response = await fetch(`${url}/self`, { 26 | method: "POST", 27 | headers: headersList 28 | }); 29 | if(response.status !== 200 && window.location.pathname.search('/dashboard') !== -1){ 30 | localStorage.clear() 31 | return redirect("/") 32 | } 33 | const temp = await response.json() 34 | setData(temp.rule) 35 | } 36 | 37 | useEffect(()=>{ 38 | verifyJWT() 39 | },[]) 40 | 41 | return ( 42 | Redirecting...} > 43 | 44 | 45 | } /> 46 | } /> 47 | } /> 48 | {/* } /> */} 49 | { 50 | data === 'user' ? } /> 51 | : <> 52 | } 53 | { 54 | data === 'driver' ? } /> 55 | : <> 56 | } 57 | { 58 | data === 'sudo' ? } /> 59 | : <> 60 | } 61 | 62 | } /> 63 | } /> 64 | 65 | 66 | 67 | ); 68 | } 69 | 70 | export default App; 71 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 33 | Ambulance Karo — Pakistan's first 34 | E-Ambulance app 35 | 36 | 37 | 38 | 39 | 40 |
41 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /client/src/components/Contact.js: -------------------------------------------------------------------------------- 1 | import { motion } from "framer-motion" 2 | import { useState } from "react"; 3 | import Header from "./Header"; 4 | import makeMessage from "./MakeMessage"; 5 | import Helmet from "react-helmet"; 6 | 7 | function Contact(){ 8 | 9 | const [email,setEmail] = useState("") 10 | const [name, setName] = useState("") 11 | const [contact, setContact] = useState("") 12 | const [text, setText] = useState("") 13 | 14 | const sendMail = async() =>{ 15 | if(email == "" || name == "" || contact == "" || text == ""){ 16 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Field(s) are empty!","#ff0000","a0289u3") 17 | setTimeout(() => { 18 | document.getElementById("a0289u3").remove() 19 | }, 3000); 20 | return 21 | } 22 | let headersList = { 23 | "Accept": "*/*", 24 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 25 | "Content-Type": "application/json" 26 | } 27 | 28 | let bodyContent = JSON.stringify({ 29 | "access_key": "70662f1c-5629-4e46-9159-03cc3abeb537", 30 | "name":name, 31 | "email": email, 32 | "message": "Contact No: "+contact + ` Message: `+text 33 | }); 34 | 35 | let response = await fetch("https://api.web3forms.com/submit", { 36 | method: "POST", 37 | body: bodyContent, 38 | headers: headersList 39 | }); 40 | if(response.status === 200){ 41 | makeMessage("fa-solid fa-circle-check"," #198754","Message Sent!","#198754","temp_61B") 42 | setTimeout(() => { 43 | document.getElementById("temp_61B").remove() 44 | }, 3000); 45 | return 46 | } 47 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Something went wrong!","#ff0000","a0289u3") 48 | setTimeout(() => { 49 | document.getElementById("a0289u3").remove() 50 | }, 3000); 51 | return 52 | 53 | } 54 | 55 | 56 | return( 57 | <> 58 | 59 | 60 | Contact 61 | 66 | 67 |
68 |
69 |
70 |
71 |

Contact us

72 |

Email:

73 | {setEmail(e.target.value)}}/> 74 |

Full Name:

75 | {setName(e.target.value)}} type="text" placeholder="Enter your full name" /> 76 |

Contact no:

77 | {setContact(e.target.value)}} type="tel" placeholder="Enter your contact number" /> 78 |

Message:

79 | 80 | Send 81 | 82 |
83 |
84 |
85 | 86 | ) 87 | } 88 | 89 | export default Contact; -------------------------------------------------------------------------------- /client/src/components/DriverDashboard/DriverDashboard.js: -------------------------------------------------------------------------------- 1 | import { useState,useEffect } from "react"; 2 | import { useNavigate } from "react-router-dom"; 3 | import Donate from "../UserDashboard/Donate"; 4 | import History from "../UserDashboard/History"; 5 | import IncomingRequests from "./IncomingRequests"; 6 | import Profile from "../UserDashboard/Profile" 7 | import url from "../ServerUrl"; 8 | import Helmet from "react-helmet"; 9 | 10 | const DriverDashboard = () =>{ 11 | 12 | // sidebar state 13 | const [sidebarName, setSidebarName] = useState("IncomingRequests") 14 | const [firstname, setFirstName] = useState("") 15 | const redirect = useNavigate() 16 | 17 | const fetchProfile = async() => { 18 | let headersList = { 19 | "Accept": "*/*", 20 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 21 | "Content-Type": "application/json", 22 | "token": localStorage.token 23 | } 24 | 25 | let response = await fetch(`${url}/self`, { 26 | method: "POST", 27 | headers: headersList 28 | }); 29 | 30 | let data = await response.json(); 31 | setFirstName(data.firstname) 32 | } 33 | 34 | useEffect(()=>{ 35 | fetchProfile() 36 | },[]) 37 | 38 | return ( 39 | <> 40 | 41 | Dashboard 42 | 43 |
44 | { 45 | document.querySelector(".dashboard_sidebar").style.left = 0 46 | }} > 47 |

Hello, {firstname}!

48 |
49 | 50 |
51 |
52 |

 Home

53 |
    54 |
  • { 55 | setSidebarName("IncomingRequests") 56 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 57 | 58 | }} className={sidebarName == "IncomingRequests" ? "active_sidebar" : ""} >   Incoming Requests
  • 59 |
  • { 60 | setSidebarName("History") 61 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 62 | 63 | }} >   History
  • 64 |
  • { 65 | setSidebarName("Donate") 66 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 67 | 68 | }} >   Donate
  • 69 |
  • { 70 | setSidebarName("Profile") 71 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 72 | 73 | }} >   Edit Profile
  • 74 | 75 |
  • { 76 | localStorage.clear() 77 | redirect("/") 78 | }} >   Logout
  • 79 |
80 |
81 |
{ 82 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 83 | }} > 84 | {sidebarName == "IncomingRequests" ? : <>} 85 | {sidebarName == "History" ? : <>} 86 | {sidebarName == "Donate" ? : <>} 87 | {sidebarName == "Profile" ? : <>} 88 |
89 |
90 | 91 | ) 92 | } 93 | export default DriverDashboard; -------------------------------------------------------------------------------- /client/src/components/UserDashboard/UserDashboard.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import Donate from "./Donate"; 3 | import History from "./History"; 4 | import RequestAmbulance from "./RequestAmbulance"; 5 | import Profile from "./Profile" 6 | import { useNavigate } from "react-router-dom"; 7 | import url from "../ServerUrl"; 8 | import Helmet from "react-helmet"; 9 | 10 | 11 | const UserDashboard = () =>{ 12 | 13 | // sidebar state 14 | const [sidebarName, setSidebarName] = useState("RequestAmbulance") 15 | const [firstname, setFirstname] = useState('') 16 | const redirect = useNavigate() 17 | 18 | if(!localStorage.token) redirect("/"); 19 | 20 | 21 | const fetchSelf = async() => { 22 | let headersList = { 23 | "Accept": "*/*", 24 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 25 | "token": localStorage.token 26 | } 27 | 28 | let response = await fetch(`${url}/self`, { 29 | method: "POST", 30 | headers: headersList 31 | }); 32 | 33 | if(response.status !== 200) redirect("/"); 34 | 35 | let data = await response.json(); 36 | setFirstname(data.firstname) 37 | } 38 | 39 | useEffect(()=>{ 40 | fetchSelf() 41 | },[]) 42 | 43 | return ( 44 | <> 45 | 46 | Dashboard 47 | 48 |
49 | { 50 | document.querySelector(".dashboard_sidebar").style.left = 0 51 | }} > 52 |

Hello, {firstname}!

53 |
54 | 55 |
56 |
57 |

 Home

58 |
    59 |
  • { 60 | setSidebarName("RequestAmbulance") 61 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 62 | 63 | }} className={sidebarName == "RequestAmbulance" ? "active_sidebar" : ""} >   Request Ambulance
  • 64 |
  • { 65 | setSidebarName("History") 66 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 67 | 68 | }} >   History
  • 69 |
  • { 70 | setSidebarName("Donate") 71 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 72 | 73 | }} >   Donate
  • 74 |
  • { 75 | setSidebarName("Profile") 76 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 77 | 78 | }} >   Edit Profile
  • 79 | 80 |
  • { 81 | localStorage.clear() 82 | redirect("/") 83 | }} >   Logout
  • 84 |
85 |
86 |
{ 87 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 88 | }} > 89 | {sidebarName == "RequestAmbulance" ? : <>} 90 | {sidebarName == "History" ? : <>} 91 | {sidebarName == "Donate" ? : <>} 92 | {sidebarName == "Profile" ? : <>} 93 |
94 |
95 | 96 | ) 97 | } 98 | export default UserDashboard; -------------------------------------------------------------------------------- /client/src/components/SudoDashboard/SudoDashboard.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import Profile from "../UserDashboard/Profile" 3 | import Donations from "./Donations"; 4 | import Dashboard from "./Dashboard"; 5 | import Ambulances from "./Ambulances"; 6 | import Drivers from "./Drivers"; 7 | import Paramedics from "./Paramedics"; 8 | import { useNavigate } from "react-router-dom"; 9 | import Helmet from "react-helmet"; 10 | 11 | 12 | const SudoDashboard = () =>{ 13 | 14 | // sidebar state 15 | const [sidebarName, setSidebarName] = useState("Dashboard") 16 | const n = useNavigate() 17 | 18 | return ( 19 | <> 20 | 21 | Dashboard 22 | 23 |
24 | { 25 | document.querySelector(".dashboard_sidebar").style.left = 0 26 | }} > 27 |

Hello, Sudo!

28 |
29 | 30 |
31 |
32 |

 Home

33 |
    34 |
  • { 35 | setSidebarName("Dashboard") 36 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 37 | 38 | }} className={sidebarName == "Dashboard" ? "active_sidebar" : ""} >   Dashboard
  • 39 |
  • { 40 | setSidebarName("Ambulances") 41 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 42 | 43 | }} >   Ambulances
  • 44 |
  • { 45 | setSidebarName("Paramedics") 46 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 47 | 48 | }} >   Paramedics
  • 49 | 50 |
  • { 51 | setSidebarName("Drivers") 52 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 53 | 54 | }} >   Drivers
  • 55 | 56 |
  • { 57 | setSidebarName("Donations") 58 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 59 | 60 | }} >   Donations
  • 61 | 62 |
  • { 63 | setSidebarName("Profile") 64 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 65 | 66 | }} >   Edit Profile
  • 67 | 68 |
  • { 69 | localStorage.clear() 70 | n("/") 71 | }} >   Logout
  • 72 |
73 |
74 |
{ 75 | document.querySelector(".dashboard_sidebar").style.left = "-100%" 76 | }} > 77 | {sidebarName == "Dashboard" ? : <>} 78 | {sidebarName == "Donations" ? : <>} 79 | {sidebarName == "Drivers" ? : <>} 80 | {sidebarName == "Paramedics" ? : <>} 81 | {sidebarName == "Profile" ? : <>} 82 | {sidebarName == "Ambulances" ? : <>} 83 |
84 |
85 | 86 | ) 87 | } 88 | export default SudoDashboard; -------------------------------------------------------------------------------- /client/src/components/SudoDashboard/Dashboard.js: -------------------------------------------------------------------------------- 1 | import {Line} from "react-chartjs-2" 2 | import {Chart} from "chart.js/auto" 3 | import { motion } from "framer-motion" 4 | import { useState, useEffect } from "react" 5 | import url from "../ServerUrl" 6 | 7 | const Dashboard = () => { 8 | 9 | const [ambulanceCount, setAmbulanceCount] = useState(0) 10 | const [paramedicCount, setParamedicCount] = useState(0) 11 | const [driverCount, setDriverCount] = useState(0) 12 | const [donationCount, setDonationCount] = useState(0) 13 | 14 | 15 | const getCount = async() =>{ 16 | 17 | let headersList = { 18 | "Accept": "*/*", 19 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 20 | "Content-Type": "application/json", 21 | "token": localStorage.token 22 | } 23 | 24 | let bodyContent = JSON.stringify({ 25 | "query": "sumDonation" 26 | }); 27 | 28 | let response = await fetch(`${url}/resolveQuery`, { 29 | method: "POST", 30 | body: bodyContent, 31 | headers: headersList 32 | }); 33 | 34 | let data1 = await response.json(); 35 | 36 | bodyContent = JSON.stringify({ 37 | "query": "sumDriver" 38 | }); 39 | response = await fetch(`${url}/resolveQuery`, { 40 | method: "POST", 41 | body: bodyContent, 42 | headers: headersList 43 | }); 44 | 45 | let data2 = await response.json(); 46 | 47 | bodyContent = JSON.stringify({ 48 | "query": "sumParamedic" 49 | }); 50 | response = await fetch(`${url}/resolveQuery`, { 51 | method: "POST", 52 | body: bodyContent, 53 | headers: headersList 54 | }); 55 | 56 | let data3 = await response.json(); 57 | 58 | bodyContent = JSON.stringify({ 59 | "query": "sumAmbulance" 60 | }); 61 | response = await fetch(`${url}/resolveQuery`, { 62 | method: "POST", 63 | body: bodyContent, 64 | headers: headersList 65 | }); 66 | 67 | let data4 = await response.json(); 68 | 69 | setDonationCount(data1.sum) 70 | setDriverCount(data2.sum) 71 | setParamedicCount(data3.sum) 72 | setAmbulanceCount(data4.sum) 73 | 74 | 75 | 76 | } 77 | 78 | useEffect(()=>{ 79 | getCount() 80 | },[]) 81 | 82 | const test = { 83 | labels: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23], 84 | datasets: [{ 85 | label: "no of users", 86 | data: [12,14,15,15,16,18,16,15,17,20,23,21,25,27,25,28,30,28,32,32,32,28,35], 87 | backgroundColor: ["white"], 88 | borderColor: ["#ff5049"] 89 | }] 90 | 91 | } 92 | 93 | return( 94 |
95 |

Dashboard

96 |
97 | 98 |

Active Ambulances

99 |

{ambulanceCount}

100 | 101 |
102 | 103 |

Active Paramedics

104 |

{paramedicCount}

105 | 106 | 107 |
108 | 109 |

Active Drivers

110 |

{driverCount}

111 | 112 | 113 |
114 | 115 |

Donations

116 |

{donationCount}

117 | 118 | 119 |
120 |
121 |

Daily Incoming Requests

122 | 123 |
124 | ) 125 | } 126 | export default Dashboard; -------------------------------------------------------------------------------- /client/src/components/UserDashboard/Profile.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import makeMessage from "../MakeMessage"; 3 | import {motion} from "framer-motion" 4 | import url from "../ServerUrl" 5 | import { redirect } from "react-router-dom"; 6 | import { useEffect } from "react"; 7 | 8 | const Profile = () => { 9 | 10 | const [edit, setEdit] = useState(false) 11 | const [cancel, setCancel] = useState(false) 12 | 13 | // profile state 14 | const [firstName, setFirstName] = useState("") 15 | const [lastName, setLastName] = useState("") 16 | const [genderInp, setGender] = useState("") 17 | const [ageInp, setAge] = useState("") 18 | // const [emailInp, setEmail] = useState(email) 19 | const [phone, setPhone] = useState("") 20 | 21 | const fetchProfile = async() => { 22 | let headersList = { 23 | "Accept": "*/*", 24 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 25 | "Content-Type": "application/json", 26 | "token": localStorage.token 27 | } 28 | 29 | let bodyContent = "" 30 | 31 | let response = await fetch(`${url}/self`, { 32 | method: "POST", 33 | body: bodyContent, 34 | headers: headersList 35 | }); 36 | 37 | let data = await response.json(); 38 | setFirstName(data.firstname) 39 | setLastName(data.lastname) 40 | setAge(data.age) 41 | setGender(data.gender) 42 | setPhone(data.contact) 43 | } 44 | 45 | useEffect(()=>{ 46 | fetchProfile() 47 | },[]) 48 | 49 | 50 | // update user profile information 51 | const updateProfile = async() => { 52 | 53 | 54 | let headersList = { 55 | "Accept": "*/*", 56 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 57 | "Content-Type": "application/json", 58 | "token": localStorage.token 59 | } 60 | 61 | let bodyContent = JSON.stringify({ 62 | "firstname":firstName, 63 | "lastname":lastName, 64 | "gender":genderInp, 65 | "age":ageInp, 66 | "phone":phone 67 | }); 68 | 69 | let response = await fetch(`${url}/update`, { 70 | method: "POST", 71 | body: bodyContent, 72 | headers: headersList 73 | }); 74 | 75 | if(response.status !== 200){ 76 | localStorage.clear() 77 | redirect("/") 78 | return <> 79 | } 80 | 81 | 82 | setEdit(!edit) 83 | 84 | makeMessage("fa-solid fa-circle-check"," #198754","Profile Update Success!","#198754","update_message_profile") 85 | setTimeout(() => { 86 | document.getElementById("update_message_profile").remove() 87 | window.location.reload() 88 | }, 3000); 89 | } 90 | 91 | return( 92 |
93 | 94 |

Edit Profile

95 |

First Name:

96 | setFirstName(e.target.value)} defaultValue={firstName} disabled={!edit} /> 97 |

Last Name:

98 | setLastName(e.target.value)} defaultValue={lastName} disabled={!edit} /> 99 |

Gender:

100 | 104 |

Age:

105 | setAge(e.target.value)} defaultValue={ageInp} disabled={!edit} /> 106 | {/*

Email:

107 | setEmail(e.target.value)} defaultValue={emailInp} disabled={!edit} /> */} 108 |

Phone No:

109 | setPhone(e.target.value)} defaultValue={phone} disabled={!edit} /> 110 | {!edit ? { 111 | setEdit(true) 112 | 113 | }} >Edit : <>} 114 |
115 | Update 116 | 117 | { 118 | setCancel(!cancel) 119 | setEdit(!edit) 120 | }} >Cancel 121 | 122 |
123 |
124 | ) 125 | } 126 | export default Profile; -------------------------------------------------------------------------------- /client/src/components/Login.js: -------------------------------------------------------------------------------- 1 | 2 | import { Link, redirect, useNavigate } from "react-router-dom"; 3 | import { motion } from "framer-motion" 4 | import { useEffect, useState } from "react"; 5 | import makeMessage from "./MakeMessage"; 6 | import Header from "./Header"; 7 | import url from "./ServerUrl"; 8 | import Helmet from "react-helmet"; 9 | 10 | function Login(){ 11 | 12 | // user defined email and password states 13 | const [inputEmail,setInputEmail] = useState("") 14 | const [inputPassword,setInputPassword] = useState("") 15 | 16 | const redirect = useNavigate() 17 | 18 | useEffect(()=>{ 19 | if(localStorage.token){ 20 | redirect("/dashboard") 21 | } 22 | },[]) 23 | 24 | // when user clicks on Login Button 25 | const loginUser = async() => { 26 | 27 | // input validation 28 | if(inputEmail === "" && inputPassword !== ""){ 29 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Please enter your email","#ff0000","temp_1x") 30 | setTimeout(() => { 31 | document.getElementById("temp_1x").remove() 32 | }, 3000); 33 | return 34 | 35 | } 36 | if(inputEmail !== "" && inputPassword === ""){ 37 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Please enter your password","#ff0000","temp_2x") 38 | setTimeout(() => { 39 | document.getElementById("temp_2x").remove() 40 | }, 3000); 41 | return 42 | } 43 | if(inputEmail === "" && inputPassword === ""){ 44 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Please enter your email and password","#ff0000","temp_3x") 45 | setTimeout(() => { 46 | document.getElementById("temp_3x").remove() 47 | }, 3000); 48 | return 49 | } 50 | let flag = true 51 | if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(inputEmail)) flag = false; 52 | 53 | 54 | if(flag){ 55 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Invalid email address","#ff0000","temp_4x") 56 | setTimeout(() => { 57 | document.getElementById("temp_4x").remove() 58 | }, 3000); 59 | return 60 | } 61 | 62 | let headersList = { 63 | "Accept": "*/*", 64 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 65 | "Content-Type": "application/json" 66 | } 67 | 68 | let bodyContent = JSON.stringify({ 69 | "email":inputEmail, 70 | "passwd":inputPassword 71 | }); 72 | 73 | try { 74 | let response = await fetch(`${url}/login`, { 75 | method: "POST", 76 | body: bodyContent, 77 | headers: headersList 78 | }); 79 | if(response.status === 200){ 80 | const output = await response.json() 81 | localStorage.setItem('token',output.token) 82 | makeMessage("fa-solid fa-circle-check"," #198754","Login Success!","#198754","temp_6B") 83 | setTimeout(() => { 84 | document.getElementById("temp_6B").remove() 85 | window.location.replace("/dashboard") 86 | }, 3000); 87 | return 88 | } 89 | 90 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Incorrect email or password!","#ff0000","temp_1A") 91 | setTimeout(() => { 92 | document.getElementById("temp_1A").remove() 93 | }, 3000); 94 | 95 | } catch (error) { 96 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Something went wrong!","#ff0000","temp_1A") 97 | setTimeout(() => { 98 | document.getElementById("temp_1A").remove() 99 | }, 3000); 100 | return 101 | } 102 | 103 | } 104 | 105 | 106 | return( 107 | <> 108 | 109 | Login 110 | 115 | 116 |
117 |
118 |
119 |
120 |

Login

121 |

Email:

122 | {setInputEmail(e.target.value)}}/> 123 |

Password:

124 | {setInputPassword(e.target.value)}} /> 125 | 126 | Login 127 | Don't have an account? Signup using this link 128 |
129 |
130 |
131 | 132 | ) 133 | } 134 | 135 | export default Login; -------------------------------------------------------------------------------- /client/public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.14, written by Peter Selinger 2001-2017 9 | 10 | 12 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /client/src/components/DriverDashboard/IncomingRequests.js: -------------------------------------------------------------------------------- 1 | import { motion } from "framer-motion"; 2 | import { useEffect, useState } from "react"; 3 | import url from "../ServerUrl" 4 | import verifyLocation from "../verifyLocation"; 5 | import makeMessage from "../MakeMessage"; 6 | 7 | const IncomingRequests = () => { 8 | const [driverStatus, setDriverStatus] = useState("requests") 9 | const [driverAcceptReqId, setDriverAcceptReqId] = useState(0) 10 | const [allRequests, setRequests] = useState([]) 11 | const [resState, setReqState] = useState(true) 12 | const headersList = { 13 | "Accept": "*/*", 14 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 15 | "Content-Type": "application/json", 16 | 'token': localStorage.token 17 | } 18 | 19 | 20 | const [userLocation, setUserLocation] = useState({}) 21 | 22 | const locationStatus = async() =>{ 23 | await verifyLocation().then(async l =>{ 24 | 25 | setUserLocation(`${l[0]},${l[1]}`) 26 | 27 | }).catch(async e=>{ 28 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Please allow location","#ff0000","temp_1y") 29 | setTimeout(async() => { 30 | document.getElementById("temp_1y").remove() 31 | await verifyLocation().then(l =>{ 32 | setUserLocation(`${l[0]},${l[1]}`) 33 | }).catch(e=>{ 34 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Location is required to run the app. Please allow location!","#ff0000","temp_1y") 35 | setTimeout(() => { 36 | document.getElementById("temp_1y").remove() 37 | window.location.replace("/") 38 | 39 | }, 3000); 40 | }) 41 | }, 3000); 42 | }) 43 | } 44 | 45 | useEffect(()=>{ 46 | locationStatus() 47 | },[]) 48 | 49 | 50 | const fetchRequests = async() =>{ 51 | let response = await fetch(`${url}/fetchRequests`, { 52 | method: "POST", 53 | headers: headersList 54 | }); 55 | let data = await response.json(); 56 | setRequests(data) 57 | } 58 | 59 | const accpetRequest = async(req_id) => { 60 | setReqState(false) 61 | setDriverStatus("riding") 62 | setDriverAcceptReqId(req_id) 63 | 64 | 65 | let bodyContent = JSON.stringify({ 66 | "requestid": req_id.requestid, 67 | "loc": userLocation 68 | }); 69 | 70 | let response = await fetch(`${url}/accept`, { 71 | method: "POST", 72 | body: bodyContent, 73 | headers: headersList 74 | }); 75 | 76 | } 77 | 78 | const completeRequest = async(req_id) => { 79 | setReqState(false) 80 | setTimeout(() => { 81 | window.location.reload() 82 | }, 1000); 83 | 84 | let bodyContent = JSON.stringify({ 85 | "requestid": req_id 86 | }); 87 | console.log(req_id); 88 | let response = await fetch(`${url}/complete`, { 89 | method: "POST", 90 | body: bodyContent, 91 | headers: headersList 92 | }); 93 | 94 | 95 | } 96 | 97 | 98 | useEffect(()=>{ 99 | if(resState){ 100 | setInterval(() => { 101 | fetchRequests() 102 | }, 1000); 103 | } 104 | 105 | },[]) 106 | 107 | return( 108 |
109 | {driverStatus == "requests" ? 110 | <> 111 |

Requests ({allRequests.length})

112 | 113 | {allRequests.map((item,i) => { 114 | 115 | return
116 |

Request ID: {item.requestid}

117 |

{item.firstname} {item.lastname}

118 |

{item.contact}

119 | {item.ambulancetype == "basic" ?

Ambulance type: Basic Ambulance

: <>} 120 | {item.ambulancetype == "advance" ?

Ambulance type: Advance Ambulance

: <>} 121 | {item.ambulancetype == "mortuary" ?

Ambulance type: Mortuary Ambulance

: <>} 122 | {item.ambulancetype == "patient" ?

Ambulance type: Patient Transport Ambulance

: <>} 123 |

Location: Open Maps

124 | accpetRequest(item)} className="accept_request" >Accept 125 | {Math.floor((new Date().getTime() - new Date(item.time).getTime())/(1000*60))} mins ago 126 |
127 | 128 | })} 129 | 130 | : <> } 131 | 132 | 133 | {driverStatus == "riding" ? 134 | <> 135 |

Riding

136 |
137 |

Request ID: {driverAcceptReqId.requestid}

138 |

{driverAcceptReqId.firstname} {driverAcceptReqId.lastname}

139 |

{driverAcceptReqId.contact}

140 | {driverAcceptReqId.ambulancetype == "basic" ?

Ambulance type: Basic Ambulance

: <>} 141 | {driverAcceptReqId.ambulancetype == "advance" ?

Ambulance type: Advance Ambulance

: <>} 142 | {driverAcceptReqId.ambulancetype == "mortuary" ?

Ambulance type: Mortuary Ambulance

: <>} 143 | {driverAcceptReqId.ambulancetype == "patient" ?

Ambulance type: Patient Transport Ambulance

: <>} 144 |

Location: Open Maps

145 | 146 | 147 | completeRequest(driverAcceptReqId.requestid)} className="reached_location" >Complete Request 148 | {Math.floor((new Date().getTime() - new Date(driverAcceptReqId.time).getTime())/(1000*60))} mins ago 149 | 150 |
: <> } 151 | 152 |
153 | ) 154 | } 155 | export default IncomingRequests; -------------------------------------------------------------------------------- /client/src/components/UserDashboard/RequestAmbulance.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react" 2 | import { useState } from "react" 3 | import spinner from "../../media/spinner.gif" 4 | import makeMessage from "../MakeMessage" 5 | import url from "../ServerUrl" 6 | import verifyLocation from "../verifyLocation" 7 | import { motion } from "framer-motion" 8 | import { useNavigate } from "react-router-dom" 9 | 10 | const RequestAmbulance = () => { 11 | 12 | const redirect = useNavigate() 13 | const [ambType, setAmbType] = useState("basic") 14 | const [oxy, setOxy] = useState(false) 15 | const [bp, setBp] = useState(false) 16 | const [wh, setWh] = useState(false) 17 | const [pd, setPd] = useState(false) 18 | const [driver, setDriver] = useState(null) 19 | const [userLocation, setUserLocation] = useState({}) 20 | 21 | 22 | const locationStatus = async() =>{ 23 | await verifyLocation().then(async l =>{ 24 | 25 | setUserLocation(`${l[0]},${l[1]}`) 26 | 27 | }).catch(async e=>{ 28 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Please allow location","#ff0000","temp_1y") 29 | setTimeout(async() => { 30 | document.getElementById("temp_1y").remove() 31 | await verifyLocation().then(l =>{ 32 | setUserLocation(`${l[0]},${l[1]}`) 33 | }).catch(e=>{ 34 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Location is required to run the app. Please allow location!","#ff0000","temp_1y") 35 | setTimeout(() => { 36 | document.getElementById("temp_1y").remove() 37 | window.location.replace("/") 38 | 39 | }, 3000); 40 | }) 41 | }, 3000); 42 | }) 43 | } 44 | 45 | useEffect(()=>{ 46 | locationStatus() 47 | },[]) 48 | 49 | 50 | 51 | const callAmbulance = async() =>{ 52 | 53 | 54 | let facilityStr = oxy ? "Oxyegn Cylinder, " : "" 55 | facilityStr += bp ? "Bloodpressure, " : "" 56 | facilityStr += wh ? "Wheelchair, " : "" 57 | facilityStr += pd ? "Paramedic" :"" 58 | 59 | let d = new Date() 60 | d = String(d) 61 | let i1 = d.indexOf(" GMT") 62 | const date = d.substring(0,i1) 63 | let headersList = { 64 | "Accept": "*/*", 65 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 66 | "Content-Type": "application/json", 67 | "token": localStorage.token 68 | } 69 | 70 | let bodyContent = JSON.stringify({ 71 | "time": date, 72 | "location": userLocation, 73 | "ambType": ambType, 74 | "facility": facilityStr 75 | }); 76 | 77 | let response = await fetch(`${url}/call`, { 78 | method: "POST", 79 | body: bodyContent, 80 | headers: headersList 81 | }); 82 | 83 | if(response.status !== 200) { 84 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Server error","#ff0000","temp_6B") 85 | setTimeout(() => { 86 | document.getElementById("temp_6B").remove() 87 | },3000) 88 | return 89 | } 90 | 91 | localStorage.setItem("ambulanceLoading",true) 92 | document.getElementById('ambulance_call').style.display = "none" 93 | document.getElementById('ambulance_loading').style.display = "block" 94 | 95 | 96 | const fetchDriver = async() =>{ 97 | let response = await fetch(`${url}/fetchDriver`, { 98 | method: "POST", 99 | headers: headersList 100 | }); 101 | 102 | if(response.status === 200){ 103 | let data = await response.json(); 104 | setDriver(data) 105 | localStorage.setItem("ambulanceLoading",false) 106 | document.getElementById('ambulance_loading').style.display = "none" 107 | clearInterval(interval_id_driver) 108 | 109 | } 110 | } 111 | 112 | const interval_id_driver = setInterval(fetchDriver,1000); 113 | 114 | } 115 | 116 | const drivingStatus = async() => { 117 | document.getElementById('ambulance_loading').style.display = "none" 118 | localStorage.setItem("ambulanceLoading",false) 119 | 120 | let headersList = { 121 | "Accept": "*/*", 122 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 123 | "Content-Type": "application/json", 124 | "token": localStorage.token 125 | } 126 | 127 | let response = await fetch(`${url}/requestStatus`, { 128 | method: "POST", 129 | headers: headersList 130 | }); 131 | 132 | let data = await response.json(); 133 | 134 | if(data.message === 'done'){ 135 | clearInterval(interval_id_driving) 136 | makeMessage("fa-solid fa-circle-check"," #198754","Ride Completed","#198754","temp_6B") 137 | 138 | setTimeout(() => { 139 | document.getElementById("temp_6B").remove() 140 | window.location.reload() 141 | document.getElementById('ambulance_call').style.display = "block" 142 | document.getElementById('wxyz').style.display = "none" 143 | 144 | }, 3000); 145 | } 146 | } 147 | let interval_id_driving; 148 | 149 | if(driver){ 150 | interval_id_driving = setInterval(drivingStatus,4000); 151 | } 152 | 153 | return( 154 | <> 155 | {driver ?
156 |

Your Ambulance is on its way!

157 | 158 | {/*

Driver Information

*/} 159 |

{driver?.firstname} {driver?.lastname}

160 |

{driver?.contact}

161 |

Location: Open Maps

162 | {/* */} 163 | 10 mins away 164 | 165 |
166 |
: <>} 167 | 168 | {localStorage.getItem("ambulanceLoading") === 'true' ? 169 |
170 | 171 |

Searching for Ambulance

172 |

(Expected waiting time: 1-2 minutes)

173 |
:
174 | 175 |

Searching for Ambulance

176 |

(Expected waiting time: 1-2 minutes)

177 |
} 178 | {localStorage.getItem("ambulanceLoading") !== 'true' ?
179 |

Call Ambulance Now!

180 | 181 |

Select Ambulance Type:

182 | 188 | 189 |

Ambulance must have:

190 |
191 | setOxy(!oxy)} /> 192 | 193 |
194 |
195 | setBp(!bp)} /> 196 | 197 |
198 |
199 | setWh(!wh)} /> 200 | 201 |
202 |
203 | setPd(!pd)} /> 204 | 205 |
206 | 207 | Call Now! 208 | 209 |
210 | : <> } 211 | 212 | ) 213 | } 214 | 215 | export default RequestAmbulance; -------------------------------------------------------------------------------- /client/src/components/Signup.js: -------------------------------------------------------------------------------- 1 | import { Link,useNavigate } from "react-router-dom"; 2 | import { motion } from "framer-motion" 3 | import Header from "./Header" 4 | import { useState,useEffect } from "react"; 5 | import makeMessage from "./MakeMessage"; 6 | import url from "./ServerUrl"; 7 | import verifyLocation from "./verifyLocation"; 8 | import Helmet from "react-helmet"; 9 | 10 | 11 | function Signup(){ 12 | 13 | 14 | const redirect = useNavigate() 15 | 16 | 17 | 18 | useEffect(()=>{ 19 | if(localStorage.token){ 20 | redirect("/dashboard") 21 | } 22 | },[]) 23 | 24 | // fileds states 25 | const [firstName, setFirstName] = useState("") 26 | const [lastName, setLastName] = useState("") 27 | const [gender, setGender] = useState("male") 28 | const [dob, setDob] = useState("") 29 | const [inputEmail, setInputEmail] = useState("") 30 | const [phone, setPhone] = useState(0) 31 | const [password, setPassword] = useState("") 32 | const [repeatPassword, setRepeatPassword] = useState("") 33 | const [loc, setLoc] = useState() 34 | 35 | const locationStatus = async() =>{ 36 | await verifyLocation().then(l =>{ 37 | setLoc(l) 38 | }).catch(async e=>{ 39 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Please allow location","#ff0000","temp_1y") 40 | setTimeout(async() => { 41 | document.getElementById("temp_1y").remove() 42 | await verifyLocation().then(l =>{ 43 | setLoc(l) 44 | }).catch(e=>{ 45 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Location is required to run the app. Please allow location!","#ff0000","temp_1y") 46 | setTimeout(() => { 47 | document.getElementById("temp_1y").remove() 48 | window.location.replace("/") 49 | 50 | }, 3000); 51 | }) 52 | }, 3000); 53 | }) 54 | } 55 | 56 | // verify basic details container 57 | const verifyBasicDetails = async(verify) =>{ 58 | await locationStatus() 59 | 60 | if(firstName == ""){ 61 | makeMessage("fa-solid fa-circle-xmark","#ff0000","First name is empty","#ff0000","temp_1y") 62 | setTimeout(() => { 63 | document.getElementById("temp_1y").remove() 64 | }, 3000); 65 | return false 66 | } 67 | if(lastName == ""){ 68 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Last name is empty","#ff0000","temp_2y") 69 | setTimeout(() => { 70 | document.getElementById("temp_2y").remove() 71 | }, 3000); 72 | return false 73 | } 74 | if(dob === "" || Number(dob[0] + dob[1] + dob[2] + dob[3]) > 2007){ 75 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Age should be at least 15 years!","#ff0000","temp_3y") 76 | setTimeout(() => { 77 | document.getElementById("temp_3y").remove() 78 | }, 3000); 79 | return false 80 | } 81 | 82 | if(!verify){ 83 | document.getElementById("signup_container_1").style.display = "none" 84 | document.getElementById("signup_container_3").style.display = "block" 85 | } 86 | return true 87 | } 88 | 89 | // main sign up 90 | const singupUser = async() =>{ 91 | 92 | if(!verifyBasicDetails(true)){ 93 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Something went wrong!","#ff0000","temp_1A") 94 | setTimeout(() => { 95 | document.getElementById("temp_1A").remove() 96 | }, 3000); 97 | return false 98 | } 99 | 100 | let flag = true 101 | if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(inputEmail)) flag = false; 102 | 103 | if(flag){ 104 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Invalid email address","#ff0000","temp_4x") 105 | setTimeout(() => { 106 | document.getElementById("temp_4x").remove() 107 | }, 3000); 108 | return false 109 | } 110 | 111 | if(phone < 3000000000 || phone > 3999999999){ 112 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Invalid phone number","#ff0000","temp_2B") 113 | setTimeout(() => { 114 | document.getElementById("temp_2B").remove() 115 | }, 3000); 116 | return false 117 | } 118 | if(password === "" || repeatPassword === ""){ 119 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Password is empty","#ff0000","temp_3B") 120 | setTimeout(() => { 121 | document.getElementById("temp_3B").remove() 122 | }, 3000); 123 | return false 124 | } 125 | if(password !== repeatPassword){ 126 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Repeat password does not match password","#ff0000","temp_4B") 127 | setTimeout(() => { 128 | document.getElementById("temp_4B").remove() 129 | }, 3000); 130 | return false 131 | } 132 | if(password.length < 8){ 133 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Password length must be greater than 7","#ff0000","temp_5B") 134 | setTimeout(() => { 135 | document.getElementById("temp_5B").remove() 136 | }, 3000); 137 | return false 138 | } 139 | 140 | 141 | const location = `${loc[0]},${loc[1]}` 142 | const ageDate = new Date(dob) 143 | const age = (2023-ageDate.getFullYear()); 144 | 145 | let headersList = { 146 | "Accept": "*/*", 147 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 148 | "Content-Type": "application/json" 149 | } 150 | 151 | let bodyContent = JSON.stringify({ 152 | "firstName": firstName, 153 | "lastName": lastName, 154 | "location": location, 155 | "contact": phone, 156 | "rule": "user", 157 | "gender": gender, 158 | "age": age, 159 | "email": inputEmail, 160 | "passwd": password 161 | }); 162 | let response = await fetch(`${url}/signup`, { 163 | method: "POST", 164 | body: bodyContent, 165 | headers: headersList 166 | }); 167 | if(response.status == 200){ 168 | const { token } = await response.json(); 169 | localStorage.setItem('token',token) 170 | 171 | makeMessage("fa-solid fa-circle-check"," #198754","Signup Success!","#198754","temp_6B") 172 | setTimeout(() => { 173 | document.getElementById("temp_6B").remove() 174 | window.location.replace("/dashboard") 175 | }, 3000); 176 | return 177 | } 178 | console.log(response); 179 | 180 | makeMessage("fa-solid fa-circle-xmark","#ff0000","Something Went Wrong!","#ff0000","temp_1A") 181 | setTimeout(() => { 182 | document.getElementById("temp_1A").remove() 183 | }, 3000); 184 | 185 | return 186 | 187 | } 188 | 189 | 190 | return( 191 | <> 192 | 193 | Singup 194 | 199 | 200 |
201 | 202 |
203 |
204 | 205 |
206 | 207 |

Signup

208 | 209 |
210 |

First Name:

211 | setFirstName(e.target.value)} /> 212 |

Last Name:

213 | setLastName(e.target.value)} /> 214 |

Gender:

215 | 219 | 220 |

Birthday:

221 | setDob(e.target.value)} /> 222 | 223 | verifyBasicDetails(false)} whileTap={{scale: 0.9}} >Next 224 |
225 | 226 | 227 |
228 |

Email:

229 | setInputEmail(e.target.value)} /> 230 |

Phone Number:

231 | setPhone(e.target.value)} /> 232 | 233 |

Password:

234 | setPassword(e.target.value)} /> 235 |

Repeat Password:

236 | setRepeatPassword(e.target.value)} /> 237 | 238 | { 239 | document.getElementById("signup_container_3").style.display = "none" 240 | document.getElementById("signup_container_1").style.display = "block" 241 | }} whileTap={{scale: 0.9}} id="previous" > Previous 242 | 243 | Signup 244 |
245 | 246 | 247 | 248 | 249 | Already have an account? Signin using this link 250 | 251 |
252 |
253 |
254 | 255 | ) 256 | } 257 | 258 | export default Signup; -------------------------------------------------------------------------------- /client/src/components/SudoDashboard/Drivers.js: -------------------------------------------------------------------------------- 1 | 2 | import { motion } from "framer-motion"; 3 | 4 | const Drivers = () => { 5 | 6 | // add ambulance 7 | const addDriver = () => { 8 | document.getElementById("blur_overlay").style.display = "block" 9 | document.getElementById("custom_model_add_ambulance").style.top = "50%" 10 | 11 | } 12 | 13 | return( 14 |
15 | 16 |
17 | 18 |
19 |

Update Driver

20 |

First name:

21 | 22 |

Last name:

23 | 24 |

Email:

25 | 26 |

Enter phone no:

27 | 28 |

Ambulance ID:

29 | 30 |

License no:

31 | 32 |
33 | { 34 | document.getElementById("blur_overlay").style.display = "none" 35 | document.getElementById("custom_model_update_ambulance").style.top= "-100%" 36 | }} >Cancel 37 | Update 38 | 39 |
40 | { 41 | document.getElementById("blur_overlay").style.display = "none" 42 | document.getElementById("custom_model_update_ambulance").style.top= "-100%" 43 | 44 | }} > 45 |
46 | 47 | 48 |
49 |

Add Driver

50 |

First name:

51 | 52 |

Last name:

53 | 54 |

Email:

55 | 56 |

Enter phone no:

57 | 58 |

Ambulance ID:

59 | 60 |

License no:

61 | 62 | 63 | 64 |
65 | { 66 | document.getElementById("blur_overlay").style.display = "none" 67 | document.getElementById("custom_model_add_ambulance").style.top= "-100%" 68 | }} >Cancel 69 | Add 70 | 71 |
72 | { 73 | document.getElementById("blur_overlay").style.display = "none" 74 | document.getElementById("custom_model_add_ambulance").style.top= "-100%" 75 | 76 | }} > 77 |
78 | 79 | 80 | 81 |
82 | { 83 | document.getElementById("blur_overlay").style.display = "none" 84 | document.getElementById("custom_model_delete").style.top= "-100%" 85 | 86 | }} > 87 |

Are you sure you want to delete driver with ID 10092?

88 | Yes 89 | { 90 | document.getElementById("blur_overlay").style.display = "none" 91 | document.getElementById("custom_model_delete").style.top= "-100%" 92 | 93 | }} >No 94 |
95 | 96 | 97 | 98 |

Drivers

99 |

Available Drivers (6) Add

100 | 101 | 102 |
103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 131 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 150 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 169 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 188 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 207 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 226 | 230 | 231 | 232 | 233 | 234 | 235 | 236 |
IDFirst NameLast NameEmailContact NoAmbulance IDLicense NoStatusEditDelete
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Busy
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Busy
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Busy
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Free
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Busy
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Free
237 | 238 | 239 |
240 | ) 241 | } 242 | export default Drivers; -------------------------------------------------------------------------------- /client/src/components/SudoDashboard/Ambulances.js: -------------------------------------------------------------------------------- 1 | 2 | import { motion } from "framer-motion"; 3 | import { useEffect, useState } from "react"; 4 | import url from "../ServerUrl" 5 | import makeMessage from "../MakeMessage" 6 | 7 | const Ambulances = () => { 8 | 9 | const [ambulances, setAmbulances] = useState([]) 10 | 11 | const [driverIdAdd, setDriverIdAdd ] = useState("") 12 | const [paramedicIdAdd,setParamedicIdAdd ] = useState("") 13 | const [ambNoPlateAdd,setAmbNoPlateAdd ] = useState("") 14 | const [ambTypeAdd, setAmbTypeAdd ] = useState("") 15 | const [oxyAdd, setOxyAdd ] = useState(false) 16 | const [bpAdd, setBpAdd] = useState(false) 17 | const [wcAdd, setWcAdd] = useState(false) 18 | const [pdAdd, setPdAdd] = useState(false) 19 | const [delId, setDelId] = useState(0) 20 | 21 | const getAllAmbulances = async() => { 22 | let headersList = { 23 | "Accept": "*/*", 24 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 25 | "Content-Type": "application/json" 26 | } 27 | 28 | let bodyContent = JSON.stringify({ 29 | "query": "select sum(donatedAmount) as sum from donations;" 30 | }); 31 | 32 | let response = await fetch(`http://${url}:5000/getAllAmbulances`, { 33 | method: "POST", 34 | body: bodyContent, 35 | headers: headersList 36 | }); 37 | 38 | let data = await response.json(); 39 | data = data.reverse() 40 | setAmbulances(data) 41 | 42 | } 43 | 44 | useEffect(()=>{ 45 | getAllAmbulances() 46 | },[]) 47 | 48 | const delOnCLick = async(ambid) => { 49 | document.getElementById("blur_overlay").style.display = "block" 50 | document.getElementById("custom_model_delete").style.top= "50%" 51 | setDelId(ambid) 52 | } 53 | 54 | const deleteAmbulance = async() => { 55 | document.getElementById("blur_overlay").style.display = "none" 56 | document.getElementById("custom_model_delete").style.top= "-100%" 57 | let headersList = { 58 | "Accept": "*/*", 59 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 60 | "Content-Type": "application/json" 61 | } 62 | let bodyContent = JSON.stringify({ 63 | "query": `delete from ambulance where ambulanceid=${delId};` 64 | }); 65 | 66 | let response = await fetch(`http://${url}:5000/resolveQuery`, { 67 | method: "POST", 68 | body: bodyContent, 69 | headers: headersList 70 | }); 71 | 72 | makeMessage("fa-solid fa-circle-check"," #198754","Ambulance has been deleted!","#198754","donate_by_user") 73 | setTimeout(() => { 74 | document.getElementById("donate_by_user").remove() 75 | 76 | }, 3000); 77 | } 78 | 79 | // add ambulance 80 | const addAmbulance = () => { 81 | document.getElementById("blur_overlay").style.display = "block" 82 | document.getElementById("custom_model_add_ambulance").style.top = "50%" 83 | } 84 | 85 | const addAmbServer = async() => { 86 | 87 | let facStr = document.getElementById("oxygen_cylinder").checked ? "Oxygen Cylinder, " : "" 88 | facStr += document.getElementById("blood_pressure").checked ? "Blood Pressure, " : "" 89 | facStr += document.getElementById("Wheelchair").checked ? "Wheelchair, " : "" 90 | facStr += document.getElementById("Paramedic").checked ? "Paramedic, " : "" 91 | 92 | let headersList = { 93 | "Accept": "*/*", 94 | "User-Agent": "Thunder Client (https://www.thunderclient.com)", 95 | "Content-Type": "application/json" 96 | } 97 | 98 | let bodyContent = JSON.stringify({ 99 | "driverid":driverIdAdd, 100 | "paramedicid": paramedicIdAdd, 101 | "ambulancetype": ambTypeAdd, 102 | "ambfacilities": facStr, 103 | "noplate": ambNoPlateAdd 104 | }); 105 | 106 | let response = await fetch(`http://${url}:5000/addAmbulance`, { 107 | method: "POST", 108 | body: bodyContent, 109 | headers: headersList 110 | }); 111 | document.getElementById("blur_overlay").style.display = "none" 112 | document.getElementById("custom_model_add_ambulance").style.top = "-100%" 113 | makeMessage("fa-solid fa-circle-check"," #198754","Ambulance has been added!","#198754","donate_by_user") 114 | setTimeout(() => { 115 | document.getElementById("donate_by_user").remove() 116 | 117 | }, 3000); 118 | 119 | } 120 | 121 | return( 122 |
123 | 124 |
125 | 126 |
127 |

Update Ambulance

128 |

Driver ID:

129 | 130 |

Paramedic ID:

131 | 132 |

Ambulance No Plate:

133 | 134 |

Ambulance Type:

135 | 141 | 142 |

Facilities:

143 |
144 | 145 | 146 |
147 |
148 | 149 | 150 |
151 |
152 | 153 | 154 |
155 |
156 | 157 |
158 |
159 | { 160 | document.getElementById("blur_overlay").style.display = "none" 161 | document.getElementById("custom_model_update_ambulance").style.top= "-100%" 162 | }} >Cancel 163 | Update 164 | 165 |
166 | { 167 | document.getElementById("blur_overlay").style.display = "none" 168 | document.getElementById("custom_model_update_ambulance").style.top= "-100%" 169 | 170 | }} > 171 |
172 | 173 | 174 |
175 |

Add Ambulance

176 |

Driver ID:

177 | setDriverIdAdd(e.target.value)} placeholder="Enter ambulance driver id" /> 178 |

Paramedic ID:

179 | setParamedicIdAdd(e.target.value)} placeholder="Enter ambulance paramedic id" /> 180 |

Ambulance No Plate:

181 | setAmbNoPlateAdd(e.target.value)} placeholder="Enter ambulance no plate" /> 182 |

Ambulance Type:

183 | 189 | 190 |

Facilities:

191 |
192 | 193 | 194 |
195 |
196 | 197 | 198 |
199 |
200 | 201 | 202 |
203 |
204 | 205 |
206 |
207 | { 208 | document.getElementById("blur_overlay").style.display = "none" 209 | document.getElementById("custom_model_add_ambulance").style.top= "-100%" 210 | }} >Cancel 211 | Add 212 | 213 |
214 | { 215 | document.getElementById("blur_overlay").style.display = "none" 216 | document.getElementById("custom_model_add_ambulance").style.top= "-100%" 217 | 218 | }} > 219 |
220 | 221 | 222 | 223 |
224 | { 225 | document.getElementById("blur_overlay").style.display = "none" 226 | document.getElementById("custom_model_delete").style.top= "-100%" 227 | 228 | }} > 229 |

Are you sure you want to delete ambulance with ID {delId}?

230 | Yes 231 | { 232 | document.getElementById("blur_overlay").style.display = "none" 233 | document.getElementById("custom_model_delete").style.top= "-100%" 234 | 235 | }} >No 236 |
237 | 238 | 239 | 240 |

Ambulances

241 |

Available Ambulances ({ambulances.length}) Add

242 | 243 | 244 |
245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | {ambulances.map(item => { 260 | return 261 | 262 | {item.ambulancetype == "advance" ? : <>} 263 | {item.ambulancetype == "basic" ? : <>} 264 | {item.ambulancetype == "mortuary" ? : <>} 265 | {item.ambulancetype == "patient" ? : <>} 266 | 267 | 268 | 269 | {item.status == "Busy" ? : } 270 | 271 | 275 | 276 | 277 | })} 278 | 279 | 280 | 281 | 282 |
IDTypeDriverFacilityNo PlateStatusEditDelete
{item.ambulanceid}Advance AmbulanceBasic AmbulanceMortuary AmbulancePatient Transport Ambulance{item.driverid}{item.ambulancefacilities}{item.noplate}BusyFree
283 | 284 | 285 |
286 | ) 287 | } 288 | export default Ambulances; -------------------------------------------------------------------------------- /functions/index.js: -------------------------------------------------------------------------------- 1 | // firebase functions module 2 | const functions = require("firebase-functions"); 3 | 4 | // express js server 5 | const express = require("express"); 6 | const app = express(); 7 | 8 | // postgres db configuration 9 | const db = require("./connect"); 10 | 11 | // middleware to avoid cors errors 12 | const cors = require("cors"); 13 | app.use(cors()); 14 | 15 | // easily communicate with json format user request body 16 | app.use(express.json()); 17 | 18 | // encrypt password 19 | const bcrypt = require("bcrypt"); 20 | 21 | // generate jwt 22 | const jwtGen = require("./jwtGen"); 23 | 24 | // authorization middleware 25 | const auth = require("./authorize"); 26 | 27 | // api routes 28 | 29 | // -> create a new user 30 | 31 | app.post("/signup",async (req,res)=>{ 32 | 33 | // destructuring request body object 34 | const { firstName, lastName, location, contact, rule, gender, age,email,passwd } = req.body; 35 | console.log(firstName, lastName, location, contact, rule, gender, age,email,passwd); 36 | // checking if custom invalid request was sent 37 | if(!firstName || !lastName || !location || !contact || !rule || !gender || !age || !email || !passwd) return res.status(401).send({ message: 'Invalid Request' }); 38 | 39 | // check if user already exist 40 | 41 | try{ 42 | const checkUser = await db.query('select * from userauthentication where email=$1',[email]); 43 | if (checkUser.rows.length !== 0){ 44 | 45 | return res.status(401).send({ message: 'User already exists' }); 46 | } 47 | 48 | // if user not exist, then encrypt password 49 | const salt = await bcrypt.genSalt(10); 50 | const encryptedPwd = await bcrypt.hash(passwd, salt); 51 | 52 | // insert user data into database 53 | const newUserInfo = await db.query(`insert into users (firstName, lastName, location, contact, rule, gender, age) values($1,$2,$3,$4,$5,$6,$7) returning *`,[firstName, lastName, location, contact, 'user', gender, age]); 54 | const id = newUserInfo.rows[0].userid; 55 | const newUserAuth = await db.query("insert into userAuthentication (userid, email, passwd) values($1,$2,$3) returning *",[id, email, encryptedPwd]); 56 | 57 | // send jwt back to user 58 | const token = jwtGen(email,id); 59 | res.send({ token }); 60 | 61 | }catch(e){ 62 | console.log(e.message); 63 | return res.status(500).send({ message: 'Internal server error'}); 64 | } 65 | 66 | }); 67 | 68 | // -> login user 69 | 70 | app.post("/login",async (req,res)=>{ 71 | 72 | try { 73 | // destructuring user request body 74 | const { email,passwd } = req.body; 75 | 76 | // checking if email and password both are present 77 | if(!email || !passwd){ 78 | return res.status(401).send({ message: 'Invalid Request' }); 79 | } 80 | 81 | // checking if user even exist 82 | const user = await db.query("select * from userauthentication where email=$1",[email]); 83 | 84 | // if not exist send error 85 | if(user.rows.length === 0){ 86 | return res.status(401).send({ message: 'Incorrect email or password' }); 87 | } 88 | 89 | // if exist, then match the password with stored hash 90 | const isValidPwd = await bcrypt.compare(passwd, user.rows[0].passwd); 91 | 92 | // if incorrect password, send error 93 | if(!isValidPwd) return res.status(401).send({ message: 'Incorrect email or password' }); 94 | 95 | // if everything remain successful, send jwt token 96 | const token = jwtGen(email,user.rows[0].userid); 97 | return res.send({ token }); 98 | 99 | } catch (err) { 100 | return res.status(500).send({ message: 'Internal server error'}); 101 | } 102 | }) 103 | 104 | // -> let the user get their own data : requires authorization 105 | 106 | app.post("/self",auth,async (req,res)=>{ 107 | 108 | // firstly, the 'auth' will work as middleware and check whether user is real or not! 109 | 110 | try { 111 | // then, we will get user id from token and send its data 112 | const token = req.header('token'); 113 | const { userid } = JSON.parse(Buffer.from(token.split(".")[1],'base64')) 114 | 115 | if(!userid) return res.status(401).send({ message: 'Invalid Request' }); 116 | 117 | const temp = await db.query("select * from users where userid=$1",[userid]); 118 | return res.send(temp.rows[0]); 119 | } catch (err) { 120 | return res.status(500).send({ message: 'Internal server error'}); 121 | } 122 | 123 | }); 124 | 125 | // -> when user calls for an ambulance : requires authorization 126 | 127 | app.post("/call",auth, async (req,res)=>{ 128 | 129 | // firstly, the 'auth' will work as middleware and check whether user is real or not! 130 | 131 | try { 132 | 133 | const token = req.header('token'); 134 | const { userid } = JSON.parse(Buffer.from(token.split(".")[1],'base64')) 135 | const { time, location, ambType, facility } = req.body; 136 | if( !time || !location || !ambType ) return res.status(401).send({ message: 'Invalid Request' }); 137 | 138 | const verifyCall = await db.query("select * from userRequestsAmbulance where userid=$1 and status='Active'",[userid]) 139 | if(verifyCall.rows[0]) return res.status(403).send({ message: 'Not allowed!' }); 140 | 141 | const temp = await db.query("insert into userRequestsAmbulance (userid, time, location, status, ambulanceType, facilities) values($1,$2,$3,'Active',$4,$5) returning *",[userid, time, location, ambType, facility]); 142 | return res.send({message: "Success"}); 143 | 144 | } catch (err) { 145 | console.log(err); 146 | return res.status(500).send({ message: 'Internal server error'}); 147 | } 148 | 149 | }); 150 | 151 | // -> when user clicks history componenet : requires authorization 152 | 153 | app.post("/history",auth,async (req,res)=>{ 154 | 155 | // firstly, the 'auth' will work as middleware and check whether user is real or not! 156 | 157 | try { 158 | const token = req.header('token'); 159 | const { userid } = JSON.parse(Buffer.from(token.split(".")[1],'base64')) 160 | const _dj20j = await db.query("select rule from users where userid=$1",[userid]); 161 | 162 | if(_dj20j.rows[0].rule === 'user'){ 163 | const temp = await db.query("select * from userrequestsambulance where userid=$1",[userid]); 164 | return res.send(temp.rows); 165 | } 166 | 167 | const temp = await db.query("select a.requestid,b.location,b.time,b.status from driveracceptsrequest as a,userrequestsambulance as b where a.requestid=b.requestid and a.driverid=$1",[userid]); 168 | 169 | return res.send(temp.rows); 170 | 171 | } catch (err) { 172 | console.log(err.message); 173 | return res.status(500).send({ message: 'Internal server error'}); 174 | } 175 | }); 176 | 177 | // -> when user donates any money : requires authorization 178 | 179 | app.post("/donate",auth, async (req,res)=>{ 180 | try { 181 | const token = req.header('token'); 182 | const { userid } = JSON.parse(Buffer.from(token.split(".")[1],'base64')); 183 | 184 | const { donatedOn, donatedAmount } = req.body; 185 | 186 | if(!donatedOn || !donatedAmount) return res.status(401).send({ message: 'Invalid Request' }); 187 | 188 | const temp = await db.query("insert into donations (donorId, donatedOn, donatedAmount) values($1,$2,$3) returning *",[userid, donatedOn, donatedAmount ]); 189 | return res.send({message: "Success"}); 190 | } catch (err) { 191 | console.log(err.message); 192 | return res.status(500).send({ message: 'Internal server error'}); 193 | } 194 | }); 195 | 196 | // -> when user click on update button on Profile page : requires authorization 197 | 198 | app.post("/update",auth, async (req,res)=>{ 199 | try { 200 | const token = req.header('token'); 201 | const { userid } = JSON.parse(Buffer.from(token.split(".")[1],'base64')); 202 | 203 | const { firstname, lastname, gender, age, phone } = req.body; 204 | 205 | if(!firstname || !lastname || !gender || !age || !phone) return res.status(401).send({ message: 'Invalid Request' }); 206 | 207 | const temp = await db.query("update users set firstname=$1,lastname=$2,contact=$3, gender=$4, age=$5 where userid = $6 returning *",[firstname, lastname, phone, gender, age,userid ]); 208 | return res.send({message: "Donation Success"}); 209 | } catch (err) { 210 | return res.status(500).send({ message: 'Internal server error'}); 211 | } 212 | }); 213 | 214 | // -> when driver fetch all requests : requires driver authorization 215 | 216 | app.post("/fetchRequests",auth, async (req,res)=>{ 217 | try { 218 | const temp = await db.query("select b.*,a.* from userrequestsambulance as a, users as b where a.userid=b.userid and a.status='Active'",[]); 219 | return res.send(temp.rows); 220 | } catch (err) { 221 | return res.status(500).send({ message: 'Internal server error'}); 222 | } 223 | }); 224 | 225 | // -> when driver accept any request : requires driver authorization 226 | 227 | app.post("/accept",auth,async (req,res)=>{ 228 | try { 229 | 230 | const token = req.header('token'); 231 | const { userid } = JSON.parse(Buffer.from(token.split(".")[1],'base64')); 232 | 233 | const role = await db.query("select rule from users where userid=$1",[userid]) 234 | 235 | if(role.rows[0].rule !== 'driver') return res.status(401).send({ message: "Not allowed!"}) 236 | 237 | const { requestid, loc } = req.body; 238 | if(!requestid || !loc) return res.status(401).send({ message: 'Invalid Request' }); 239 | 240 | const updateDriverLoc = await db.query("update users set location=$1 where userid=$2",[loc,userid]) 241 | 242 | const isDriverFree = await db.query("select status from driver where driverid=$1",[userid]) 243 | if(isDriverFree.rows[0].status === "Busy") return res.status(401).send({ message: 'Invalid Request' }); 244 | 245 | const temp = await db.query("insert into driveracceptsrequest (driverid,requestid) values($1,$2);",[userid, requestid]); 246 | await db.query("update driver set status='Busy' where driverid=$1",[userid]); 247 | return res.send({ message: 'Success' }); 248 | } catch (err) { 249 | console.log(err.message); 250 | return res.status(500).send({ message: 'Internal server error'}); 251 | } 252 | }); 253 | 254 | // -> when driver click on complete request (ride): requires driver authorization 255 | 256 | app.post("/complete",auth,async (req,res)=>{ 257 | try { 258 | 259 | const token = req.header('token'); 260 | const { userid } = JSON.parse(Buffer.from(token.split(".")[1],'base64')); 261 | 262 | const role = await db.query("select rule from users where userid=$1",[userid]) 263 | 264 | if(role.rows[0].rule !== 'driver') return res.status(401).send({ message: "Not allowed!"}) 265 | 266 | const { requestid } = req.body; 267 | if(!requestid) return res.status(401).send({ message: 'Invalid Request' }); 268 | 269 | await db.query("update userrequestsambulance set status='Completed' where requestid=$1",[requestid]); 270 | await db.query("update driver set status='Free' where driverid=$1",[userid]) 271 | 272 | return res.send({message: "Success"}); 273 | 274 | } catch (err) { 275 | return res.status(500).send({ message: 'Internal server error'}); 276 | } 277 | }) 278 | 279 | // -> when user wait for ambulance after calling: requires user authorization 280 | 281 | app.post("/getAmbulance",auth,async (req,res)=>{ 282 | try { 283 | const token = req.header('token'); 284 | const { userid } = JSON.parse(Buffer.from(token.split(".")[1],'base64')); 285 | 286 | const requestid = await db.query("select requestid from userrequestsambulance where userid = $1 and status = 'Active'",[userid]); 287 | if(!requestid.rows[0]) return res.status(403).send({ message: 'Not allowed'}); 288 | 289 | const temp = await db.query("select * from driveracceptsrequest where requestid=$1;",[requestid.rows[0].requestid]); 290 | 291 | if(temp.rows[0]?.driverid){ 292 | return res.send(temp.rows[0]); 293 | } 294 | return res.status(404).send(); 295 | 296 | } catch (err) { 297 | return res.status(500).send({ message: 'Internal server error'}); 298 | } 299 | }) 300 | 301 | // -> when user found an ambulance after calling and getting its details: requires user authorization 302 | 303 | app.post("/fetchDriver",auth,async (req,res)=>{ 304 | try { 305 | 306 | const token = req.header('token'); 307 | const { userid } = JSON.parse(Buffer.from(token.split(".")[1],'base64')); 308 | 309 | const requestid = await db.query("select requestid from userrequestsambulance where userid = $1 and status = 'Active'",[userid]); 310 | if(!requestid.rows[0]) return res.status(403).send({ message: 'Not allowed'}); 311 | 312 | const driverData = await db.query("select * from users where userid = (select driverid from driveracceptsrequest where requestid=$1)",[requestid.rows[0].requestid]); 313 | if(!driverData.rows[0]) return res.status(204).send({ message: 'waiting'}); 314 | 315 | return res.send(driverData.rows[0]) 316 | 317 | } catch (err) { 318 | console.log(err.message); 319 | return res.status(500).send({ message: 'Internal server error'}); 320 | } 321 | }) 322 | 323 | // -> when user found an ambulance after calling and getting its details: requires authorization 324 | 325 | app.post("/getAllAmbulances",auth,async (req,res)=>{ 326 | try { 327 | const temp = await db.query("select * from ambulance,driver where ambulance.driverid = driver.driverid;",[]); 328 | return res.send(temp.rows); 329 | } catch (err) { 330 | return res.status(500).send({ message: 'Internal server error'}); 331 | } 332 | }) 333 | 334 | // -> when admin add an ambulance: requires admin authorization 335 | 336 | app.post("/addAmbulance",auth,async (req,res)=>{ 337 | try { 338 | const { driverid, paramedicid, noplate, ambulancetype, ambfacilities } = req.body; 339 | if(!driverid || !paramedicid || !noplate || !ambulancetype || !ambfacilities) return res.status(401).send({ message: 'Invalid Request' }); 340 | 341 | const temp = await db.query("insert into ambulance (driverid,paramedicid, noplate, ambulancetype, ambulancefacilities) values($1,$2,$3,$4,$5)",[driverid, paramedicid, noplate, ambulancetype, ambfacilities]); 342 | 343 | return res.send({message: "Ambulance Added!"}); 344 | 345 | } catch (err) { 346 | return res.status(500).send({ message: 'Internal server error'}); 347 | } 348 | }) 349 | 350 | // -> track ambulance request: requires authorization 351 | 352 | app.post("/requestStatus",auth, async(req, res)=>{ 353 | const token = req.header('token'); 354 | const { userid } = JSON.parse(Buffer.from(token.split(".")[1],'base64')); 355 | 356 | const requestid = await db.query("select requestid from userrequestsambulance where userid = $1 and status = 'Active'",[userid]); 357 | if(requestid.rows[0]) return res.send({ message: 'busy'}); 358 | 359 | return res.send({ message: 'done'}); 360 | 361 | }) 362 | 363 | // -> resolve custom queries: requires admin authorization 364 | 365 | app.post("/resolveQuery",auth, async(req, res)=>{ 366 | const token = req.header('token'); 367 | const { userid } = JSON.parse(Buffer.from(token.split(".")[1],'base64')) 368 | const role = await db.query("select rule from users where userid=$1",[userid]) 369 | 370 | if(role.rows[0].rule !== 'sudo') return res.status(401).send({ message: "Not allowed!"}) 371 | const queryType = req.body.query 372 | 373 | if(!queryType) return res.status(401).send({ message: 'Invalid Request' }); 374 | 375 | if(queryType === "getDonationsData"){ 376 | const temp = await db.query('select * from donations,users,userauthentication where donations.donorid = users.userid and users.userid = userauthentication.userid') 377 | return res.send(temp.rows) 378 | }else if(queryType === "sumDonation" || queryType === "sumDriver" || queryType === "sumParamedic" || queryType === "sumAmbulance" ){ 379 | let temp; 380 | if(queryType === "sumDonation"){ 381 | temp = await db.query('select sum(donatedAmount) as sum from donations') 382 | } 383 | if(queryType === "sumDriver"){ 384 | temp = await db.query('select count(driverid) as sum from driver') 385 | } 386 | if(queryType === "sumParamedic"){ 387 | temp = await db.query('select count(paramedicid) as sum from paramedic') 388 | } 389 | if(queryType === "sumAmbulance"){ 390 | temp = await db.query('select count(ambulanceid) as sum from ambulance') 391 | } 392 | return res.send(temp.rows[0]) 393 | 394 | } 395 | res.send({ message: queryType }) 396 | 397 | }) 398 | 399 | // for google cloud functions 400 | exports.api = functions.https.onRequest(app); 401 | 402 | // -> UNCOMMENT when running on local environment and COMMENT Line: 400 403 | 404 | // app.listen(5000, () => { 405 | // console.log('Server listening on port 5000'); 406 | // }); -------------------------------------------------------------------------------- /client/src/components/SudoDashboard/Paramedics.js: -------------------------------------------------------------------------------- 1 | 2 | import { motion } from "framer-motion"; 3 | 4 | const Paramedics = () => { 5 | 6 | // add ambulance 7 | const addParamedic = () => { 8 | document.getElementById("blur_overlay").style.display = "block" 9 | document.getElementById("custom_model_add_ambulance").style.top = "50%" 10 | 11 | } 12 | 13 | return( 14 |
15 | 16 |
17 | 18 |
19 |

Update Paramedic

20 |

First name:

21 | 22 |

Last name:

23 | 24 |

Email:

25 | 26 |

Enter phone no:

27 | 28 |

Ambulance ID:

29 | 30 |

Medical License no:

31 | 32 |
33 | { 34 | document.getElementById("blur_overlay").style.display = "none" 35 | document.getElementById("custom_model_update_ambulance").style.top= "-100%" 36 | }} >Cancel 37 | Update 38 | 39 |
40 | { 41 | document.getElementById("blur_overlay").style.display = "none" 42 | document.getElementById("custom_model_update_ambulance").style.top= "-100%" 43 | 44 | }} > 45 |
46 | 47 | 48 |
49 |

Add Paramedic

50 |

First name:

51 | 52 |

Last name:

53 | 54 |

Email:

55 | 56 |

Enter phone no:

57 | 58 |

Ambulance ID:

59 | 60 |

Medical License no:

61 | 62 | 63 | 64 |
65 | { 66 | document.getElementById("blur_overlay").style.display = "none" 67 | document.getElementById("custom_model_add_ambulance").style.top= "-100%" 68 | }} >Cancel 69 | Add 70 | 71 |
72 | { 73 | document.getElementById("blur_overlay").style.display = "none" 74 | document.getElementById("custom_model_add_ambulance").style.top= "-100%" 75 | 76 | }} > 77 |
78 | 79 | 80 | 81 |
82 | { 83 | document.getElementById("blur_overlay").style.display = "none" 84 | document.getElementById("custom_model_delete").style.top= "-100%" 85 | 86 | }} > 87 |

Are you sure you want to delete paramedic with ID 10092?

88 | Yes 89 | { 90 | document.getElementById("blur_overlay").style.display = "none" 91 | document.getElementById("custom_model_delete").style.top= "-100%" 92 | 93 | }} >No 94 |
95 | 96 | 97 | 98 |

Paramedics

99 |

Available Paramedics (198) Add

100 | 101 | 102 |
103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 131 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 150 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 170 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 189 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 208 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 227 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 246 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 265 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 285 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 304 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 323 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 342 | 346 | 347 | 348 | 349 | 350 | 351 | 352 |
IDFirst NameLast NameEmailContact NoAmbulance IDMedical License NoStatusEditDelete
10004BabarAzambabar.azam@gmail.com0315407918110982143920563487Busy
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Free
10004BabarAzambabar.azam@gmail.com0315407918110982143920563487Busy
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Free
10004BabarAzambabar.azam@gmail.com0315407918110982143920563487Busy
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Free
10004BabarAzambabar.azam@gmail.com0315407918110982143920563487Busy
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Free
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Busy
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Free
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Busy
10004ShoaibMailkshoaib@gmail.com0315178908110982143920563487Free
353 | 354 | 355 |
356 | ) 357 | } 358 | export default Paramedics; -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap'); 2 | 3 | /* general style for all elements */ 4 | 5 | 6 | *{ 7 | font-family: 'Poppins', sans-serif; 8 | box-sizing: border-box; 9 | padding: 0; 10 | margin: 0; 11 | } 12 | 13 | /* Hide the scrollbar */ 14 | ::-webkit-scrollbar { 15 | width: 0; 16 | height: 0; 17 | } 18 | 19 | 20 | /* Blob for Homepage */ 21 | 22 | .main_homepage_blob_image{ 23 | width: 100%; 24 | height: 100vh; 25 | background-image: url("./media/main_homepage_blob.svg"); 26 | background-repeat: no-repeat; 27 | background-size: cover; 28 | background-position: center; 29 | } 30 | .main_content{ 31 | width: 100%; 32 | height: 100vh; 33 | background: transparent; 34 | backdrop-filter: blur(20px); 35 | -webkit-backdrop-filter: blur(20px); 36 | background-attachment: fixed; 37 | position: fixed; 38 | overflow-y: scroll; 39 | } 40 | 41 | 42 | /* Header */ 43 | 44 | .header_item{ 45 | width: 100%; 46 | height: 65px; 47 | } 48 | .logo{ 49 | width: 300px; 50 | display: inline-block; 51 | margin: 12px 0px 0px 30px; 52 | } 53 | .logo:hover{ 54 | cursor: pointer; 55 | } 56 | .header_links{ 57 | width: 330px; 58 | height: 60px; 59 | text-align: center; 60 | float: right; 61 | font-size: 18px; 62 | margin-right: 20px; 63 | padding: 10px 0px; 64 | font-weight: 500; 65 | display: flex; 66 | justify-content: center; 67 | align-items: center; 68 | } 69 | .header_links li{ 70 | list-style: none; 71 | width: 100px; 72 | height: 40px; 73 | line-height: 40px; 74 | margin-right: 10px; 75 | justify-content: center; 76 | align-items: center; 77 | transition: 50ms linear all; 78 | border: 1px solid transparent; 79 | } 80 | .header_links li:hover{ 81 | background: transparent; 82 | color: #ff5049; 83 | border: 1px solid #ff5049; 84 | border-radius: 20px; 85 | cursor: pointer; 86 | } 87 | 88 | /* style for login button in header links */ 89 | 90 | .header_links li:nth-child(3){ 91 | background: #ff5049; 92 | color: white; 93 | border-radius: 20px; 94 | cursor: pointer; 95 | } 96 | .cancel_nav,.open_nav{ 97 | display: none; 98 | } 99 | 100 | 101 | /* tagline css */ 102 | 103 | .homepage_tagline{ 104 | width: 100%; 105 | display: flex; 106 | justify-content: center; 107 | align-items: center; 108 | } 109 | 110 | .tagline_text{ 111 | width: 40%; 112 | margin: 0px; 113 | } 114 | .tagline_text h1{ 115 | font-size: 3vw; 116 | font-weight: bolder; 117 | margin: 10px 0px; 118 | margin-top: 40px; 119 | } 120 | .tagline_text h1 b{ 121 | color: #ff5049; 122 | } 123 | .tagline_text p{ 124 | font-size: 20px; 125 | margin: 10px 0px; 126 | } 127 | .tagline_text button{ 128 | width: 200px; 129 | font-size: 20px; 130 | height: 50px; 131 | background: #ff5049; 132 | color: white; 133 | border-radius: 50px; 134 | border: none; 135 | margin: 10px; 136 | } 137 | .tagline_text button:hover{ 138 | cursor: pointer; 139 | } 140 | .tagline_ambulance{ 141 | width: 30%; 142 | } 143 | .tagline_ambulance img{ 144 | width: 500px; 145 | max-width: 100%; 146 | } 147 | 148 | /* Singup Page */ 149 | 150 | .signup_container{ 151 | width: 450px; 152 | background: rgba(255, 255, 255, 0.3); 153 | margin: 0px auto; 154 | border: 2px solid #ff5049; 155 | border-radius: 15px; 156 | padding: 10px 20px; 157 | margin: 20px auto; 158 | 159 | 160 | background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0)); 161 | backdrop-filter: blur(10px); 162 | -webkit-backdrop-filter: blur(10px); 163 | border-radius: 20px; 164 | border:1px solid rgba(255, 255, 255, 0.18); 165 | box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; 166 | 167 | } 168 | .signup_container h1{ 169 | text-align: center; 170 | color: #ff5049; 171 | width: 70%; 172 | border-bottom: 2px solid #ff5049; 173 | padding-bottom: 10px; 174 | margin: 5px auto; 175 | font-size: 25px; 176 | padding: 0; 177 | } 178 | .signup_container p{ 179 | font-size: 16px; 180 | margin: 4px 0px 4px 10px; 181 | 182 | } 183 | .signup_container input, .signup_container textarea{ 184 | width: 100%; 185 | border-radius: 20px; 186 | outline: none; 187 | background: transparent; 188 | border: 1px solid #ff5049; 189 | height: 40px; 190 | padding: 0px 20px; 191 | font-size: 16px; 192 | } 193 | .signup_container textarea{ 194 | height: 100px; 195 | padding: 10px; 196 | border-radius: 5px; 197 | } 198 | .signup_container button{ 199 | background: #ff5049; 200 | color: white; 201 | width: 150px; 202 | display: block; 203 | border: none; 204 | border-radius: 40px; 205 | height: 40px; 206 | margin: 10px auto; 207 | font-size: 18px; 208 | } 209 | .signup_container button:hover{ 210 | cursor: pointer; 211 | } 212 | .signup_container a{ 213 | font-size: 14px; 214 | display: block; 215 | margin: 10px auto; 216 | width: 100%; 217 | text-align: center; 218 | color: #ff5049; 219 | } 220 | .signup_container select{ 221 | width: 100%; 222 | height: 40px; 223 | font-size: 18px; 224 | outline: none; 225 | background: transparent; 226 | border: 1px solid #ff5049; 227 | border-radius: 10px; 228 | padding: 0px 10px; 229 | } 230 | .signup_container option{ 231 | color: #ff5049; 232 | 233 | } 234 | .signup_container input[type='checkbox']{ 235 | font-size: 20px; 236 | width: 15px; 237 | background: #ff5049; 238 | margin: 0px 10px; 239 | } 240 | .custom_checkbox{ 241 | display: flex; 242 | align-items: center; 243 | } 244 | 245 | #previous,#next{ 246 | width: 130px; 247 | float: left; 248 | margin: 10px 30px; 249 | } 250 | #next{ 251 | float: right; 252 | } 253 | 254 | /* Dashboard */ 255 | 256 | .dashboard_main_container{ 257 | width: 100%; 258 | height: auto; 259 | display: flex; 260 | } 261 | .dashboard_sidebar{ 262 | width: 400px !important; 263 | background: #ff5049; 264 | height: 100vh; 265 | z-index: 2; 266 | background: #FFA6A3; 267 | background: -webkit-linear-gradient(top, #FFA6A3, #FF5149); 268 | background: -moz-linear-gradient(top, #FFA6A3, #FF5149); 269 | background: linear-gradient(to bottom, #FFA6A3, #FF5149); 270 | color: white; 271 | display: inline-block; 272 | 273 | } 274 | .dashboard_sidebar li{ 275 | width: 95%; 276 | margin: 5px auto; 277 | list-style: none; 278 | height: 50px; 279 | font-size: 18px; 280 | font-weight: 500; 281 | padding: 10px 20px; 282 | border-radius: 15px; 283 | } 284 | .dashboard_sidebar li:hover{ 285 | background: rgba(255, 255, 255, 0.2); 286 | cursor: pointer; 287 | } 288 | .dashboard_sidebar p{ 289 | text-align: center; 290 | margin: 10px auto; 291 | border-bottom: 1px solid white; 292 | padding-bottom: 12px; 293 | font-weight: bolder; 294 | font-size: 20px 295 | } 296 | .dashboard_content{ 297 | 298 | width: 100%; 299 | display: inline-block; 300 | padding: 20px; 301 | height: 100vh; 302 | overflow-y: scroll; 303 | padding-top: 60px; 304 | } 305 | 306 | .active_sidebar{ 307 | background: rgba(255, 255, 255, 0.2); 308 | } 309 | 310 | /* ambulance */ 311 | 312 | #call_ambulance_button{ 313 | width: 250px; 314 | margin: 20px auto; 315 | font-size: 18px; 316 | height: 40px; 317 | border-radius: 20px; 318 | border: none; 319 | background: #ff5049; 320 | color: white; 321 | } 322 | #call_ambulance_button{ 323 | cursor: pointer; 324 | } 325 | .user_profile_details_label{ 326 | margin: 5px 15px; 327 | display: inline-block; 328 | } 329 | .user_profile_details{ 330 | font-size: 21px; 331 | color: #ff5049; 332 | font-weight: 500; 333 | margin: 10px 0px 0px 20px; 334 | display: inline-block; 335 | } 336 | 337 | /* blog styles */ 338 | 339 | .blog_container{ 340 | width: 80%; 341 | margin: 10px auto; 342 | font-size: 20px; 343 | } 344 | .blog_container h1{ 345 | font-size: 60px; 346 | } 347 | 348 | /* message container */ 349 | 350 | .message_container{ 351 | min-height: 50px; 352 | background: white; 353 | position: absolute; 354 | z-index: 3; 355 | font-size: 18px; 356 | border-radius: 10px; 357 | box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; 358 | border: 1px solid lightgray; 359 | padding: 5px 10px; 360 | font-weight: bolder; 361 | overflow: hidden; 362 | bottom: 20px; 363 | right: 0; 364 | left: 40%; 365 | right: 40%; 366 | /* left: 0; */ 367 | animation: fadeOut 3s linear forwards; 368 | display: flex; 369 | align-items: center; 370 | } 371 | .message_container p{ 372 | padding-left: 10px; 373 | } 374 | .message_container i{ 375 | font-size: 25px; 376 | } 377 | .message_progress{ 378 | width: 0; 379 | height: 5px; 380 | position: absolute; 381 | bottom: 0px; 382 | left: 0%; 383 | border-radius: 10px; 384 | animation: runProgress 2s linear forwards 0.5s; 385 | } 386 | @keyframes runProgress { 387 | 0% { width: 0%; } 388 | 100% { width: 100%; } 389 | } 390 | @keyframes fadeOut { 391 | 0% { opacity: 0; } 392 | 10% { opacity: 1; } 393 | 90% { opacity: 1; transform: translateY(0px);} 394 | 99% { opacity: 0; transform: translateY(-30px);} 395 | 100% { opacity: 0; } 396 | } 397 | 398 | /* welcome header */ 399 | .welcome_header{ 400 | width: 100%; 401 | height: 50px; 402 | background: #ffffff; 403 | color: #ff5049; 404 | box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; 405 | position: fixed; 406 | z-index: 2; 407 | } 408 | .welcome_header i{ 409 | position: absolute; 410 | display: inline; 411 | font-size: 35px; 412 | line-height: 50px; 413 | margin-left: 10px; 414 | padding: 0px 10px; 415 | } 416 | .welcome_header i:hover{ 417 | cursor: pointer; 418 | box-shadow: rgba(0, 0, 0, 0.15) 0px 5px 15px; 419 | border-radius: 10px; 420 | } 421 | .welcome_header h3{ 422 | text-align: center; 423 | margin: 10px auto; 424 | margin-left: 300px; 425 | } 426 | 427 | /* ambulance waiting state */ 428 | 429 | .ambulance_waiting_state{ 430 | width: 300px; 431 | margin: 0 auto; 432 | text-align: center; 433 | display: none; 434 | } 435 | .ambulance_waiting_state img{ 436 | width: 150px; 437 | } 438 | .ambulance_waiting_state p{ 439 | font-size: 20px; 440 | color: #ff5049; 441 | } 442 | .ambulance_waiting_state p:nth-child(3){ 443 | font-size: 13px; 444 | } 445 | 446 | /* driver details when ambulance found */ 447 | 448 | .driver_details_ambulance_found{ 449 | width: 300px; 450 | margin: 0 auto; 451 | display: flex; 452 | flex-direction: column; 453 | align-items: center; 454 | justify-content: center; 455 | } 456 | .driver_details_ambulance_found h1{ 457 | font-size: 30px; 458 | color: #ff5049; 459 | text-align: center; 460 | } 461 | .ambulance_driver_details_card{ 462 | box-shadow: rgba(50, 50, 93, 0.15) 0px 6px 12px 5px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px; 463 | border-radius: 10px; 464 | padding: 10px 10px 20px 10px; 465 | margin: 20px auto; 466 | background: linear-gradient(to bottom, #FFA6A3, #FF5149); 467 | cursor: pointer; 468 | color: white; 469 | position: relative; 470 | } 471 | .custom_card{ 472 | width: 300px !important; 473 | } 474 | .ambulance_driver_details_card h2{ 475 | font-size: 20px; 476 | } 477 | .ambulance_driver_details_card p{ 478 | font-size: 15px; 479 | font-weight: bolder; 480 | margin: 5px 20px; 481 | } 482 | .ambulance_driver_details_card a{ 483 | color: white; 484 | } 485 | .ambulance_driver_details_card button{ 486 | color: white; 487 | outline: none; 488 | border: none; 489 | padding: 5px 10px; 490 | background: transparent; 491 | border-radius: 50px; 492 | width: 80px; 493 | margin-right: 20px; 494 | font-size: 12px; 495 | float: right; 496 | position: relative; 497 | z-index: 2; 498 | } 499 | .accept_request{ 500 | margin-top: 0px !important; 501 | height: 30px !important; 502 | color: white important; 503 | background: transparent; 504 | border: 1px solid white !important; 505 | } 506 | .accept_request:hover{ 507 | background: white !important; 508 | color: #ff5049 !important; 509 | } 510 | .reached_location{ 511 | width: 150px !important; 512 | margin-top: 0px !important; 513 | height: 30px !important; 514 | color: white !important; 515 | background: transparent; 516 | border: 1px solid white !important; 517 | } 518 | .reached_location:hover{ 519 | background: white!important; 520 | color: #ff5049 !important; 521 | } 522 | .ambulance_driver_details_card button:hover{ 523 | cursor: pointer; 524 | background: #ff5049; 525 | color: white; 526 | } 527 | .ambulance_driver_details_card span{ 528 | font-size: 10px; 529 | margin-left: 10px; 530 | font-weight: bolder; 531 | } 532 | 533 | 534 | /* history table */ 535 | 536 | .history_table{ 537 | width: 100%; 538 | font-size: 14px; 539 | text-align: center; 540 | border-collapse: collapse; 541 | margin-top: 20px; 542 | } 543 | .history_table th{ 544 | border: 2px solid #ff5049; 545 | padding: 10px; 546 | font-weight: bolder; 547 | /* color: #ff5049; */ 548 | background: #ff5049; 549 | color: white; 550 | /* border-right: 2px solid white; */ 551 | font-size: 18px; 552 | } 553 | .history_table td{ 554 | border: 2px solid #ff5049; 555 | padding: 5px; 556 | } 557 | .active{ 558 | color: #ff5049; 559 | font-weight: bolder; 560 | } 561 | .fulfilled{ 562 | color: #0a0; 563 | font-weight: bold; 564 | } 565 | .canceled{ 566 | color: #ff0000; 567 | font-weight: bolder; 568 | } 569 | 570 | /* donation */ 571 | 572 | .donation_message{ 573 | width: 100%; 574 | padding: 20px 0px; 575 | font-size: 20px !important; 576 | } 577 | .donation_message span{ 578 | color: #ff5049; 579 | font-weight: bolder; 580 | } 581 | 582 | /* admin ambulances */ 583 | 584 | .admin_ambulances{ 585 | width: 100%; 586 | height: auto; 587 | } 588 | .admin_ambulances h1{ 589 | margin: 20px 0px 20px 20px; 590 | color: #ff5049; 591 | } 592 | 593 | .custom_model_delete{ 594 | width: 350px; 595 | height: 130px; 596 | background: white; 597 | border-radius: 20px; 598 | position: absolute; 599 | margin: 0; 600 | top: -100%; 601 | left: 50%; 602 | -ms-transform: translate(-50%, -50%); 603 | transform: translate(-50%, -50%); 604 | z-index: 5; 605 | padding: 20px; 606 | box-shadow: rgba(0, 0, 0, 0.56) 0px 22px 70px 4px; 607 | transition: .5s linear all; 608 | } 609 | .custom_model_delete h2{ 610 | font-size: 15px; 611 | color: #ff5049; 612 | } 613 | .custom_model_delete button{ 614 | width: 70px; 615 | padding: 5px 20px; 616 | border-radius: 40px; 617 | border: none; 618 | float: right; 619 | margin: 10px; 620 | } 621 | .custom_model_delete button:hover{ 622 | cursor: pointer; 623 | } 624 | .yes{ 625 | background: #ff5049; 626 | color: white; 627 | border: 1px solid #ff5049 !important; 628 | 629 | margin-left: 0 !important; 630 | } 631 | .no{ 632 | background: transparent; 633 | border: 1px solid #ff5049 !important; 634 | color: #ff5049; 635 | } 636 | .close_button{ 637 | position: absolute; 638 | right: 10px; 639 | top: 10px; 640 | display: block; 641 | font-size: 25px; 642 | color: gray; 643 | } 644 | .close_button:hover{ 645 | cursor: pointer; 646 | } 647 | 648 | #blur_overlay{ 649 | top: 0; 650 | left: 0; 651 | position: absolute; 652 | background: rgba(0, 0, 0, 0.6); 653 | width: 100%; 654 | height: 100vh; 655 | z-index: 4; 656 | backdrop-filter: blur(2px); 657 | -webkit-backdrop-filter: blur(2px); 658 | display: none; 659 | } 660 | 661 | .history_table button{ 662 | width: 30px; 663 | height: 30px; 664 | font-size: 15px; 665 | background: #ff5049; 666 | border: none; 667 | color: white; 668 | border-radius: 5px; 669 | } 670 | .history_table button:hover{ 671 | cursor: pointer; 672 | } 673 | .add_ambulance_button{ 674 | float: right; 675 | width: 90px; 676 | border-radius: 20px; 677 | height: 40px; 678 | margin: -5px 20px 0px 0px; 679 | border: none; 680 | background: #ff5049; 681 | color: white; 682 | font-size: 17px; 683 | 684 | } 685 | .add_ambulance_button:hover{ 686 | cursor: pointer; 687 | } 688 | .ambulance_table{ 689 | width: 100%; 690 | padding: 0px 10px; 691 | } 692 | .custom_model_add_ambulance,.custom_model_update_ambulance{ 693 | width: 380px; 694 | height: auto; 695 | background: white; 696 | border-radius: 20px; 697 | position: absolute; 698 | margin: 0; 699 | left: 50%; 700 | -ms-transform: translate(-50%, -50%); 701 | transform: translate(-50%, -50%); 702 | z-index: 5; 703 | padding: 20px; 704 | box-shadow: rgba(0, 0, 0, 0.56) 0px 22px 70px 4px; 705 | transition: .5s linear all; 706 | top: -100%; 707 | } 708 | .custom_model_add_ambulance h2,.custom_model_update_ambulance h2{ 709 | color: #ff5049; 710 | margin: 10px auto; 711 | margin-top: 0; 712 | text-align: center; 713 | 714 | } 715 | .custom_model_add_ambulance p,.custom_model_update_ambulance p{ 716 | color: #ff5049; 717 | font-weight: bolder; 718 | margin: 10px 0px 10px 10px; 719 | } 720 | .custom_model_add_ambulance input,.custom_model_update_ambulance input{ 721 | width: 100%; 722 | border: 1px solid #ff5049; 723 | height: 30px; 724 | border-radius: 40px; 725 | padding: 3px 20px; 726 | font-size: 14px; 727 | outline: none; 728 | } 729 | .custom_model_add_ambulance input[type="checkbox"],.custom_model_update_ambulance input[type="checkbox"]{ 730 | width: 40px; 731 | height: 20px; 732 | } 733 | .custom_model_add_ambulance select,.custom_model_update_ambulance select{ 734 | width: 100%; 735 | height: 30px; 736 | outline: none; 737 | border: 1px solid #ff5049; 738 | font-size: 14px; 739 | color: #ff5049; 740 | } 741 | .custom_model_add_ambulance button,.custom_model_update_ambulance button{ 742 | margin: 20px 10px 0px 0px; 743 | width: 90px; 744 | height: 30px; 745 | font-size: 15px; 746 | border-radius: 20px; 747 | background: #ff5049; 748 | color: white; 749 | border: 1px solid #ff5049; 750 | } 751 | .custom_model_add_ambulance button:hover,.custom_model_update_ambulance button:hover{ 752 | cursor: pointer; 753 | } 754 | 755 | /* dashboard of sudo admin */ 756 | 757 | .active_staffs{ 758 | width: 100%; 759 | } 760 | .ambulances_active_count{ 761 | width: 220px; 762 | color: white; 763 | background: -webkit-linear-gradient(top, #FFA6A3, #FF5149); 764 | background: -moz-linear-gradient(top, #FFA6A3, #FF5149); 765 | background: linear-gradient(to bottom, #ff8480, #FF5149); 766 | text-align: center; 767 | font-size: 20px; 768 | font-weight: bolder; 769 | display: inline-block; 770 | margin: 10px; 771 | padding: 10px; 772 | border-radius: 10px; 773 | height: 140px; 774 | padding-top: 30px; 775 | position: relative; 776 | box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px; 777 | z-index: 0; 778 | overflow: hidden; 779 | } 780 | .ambulances_active_count h1{ 781 | color: white; 782 | width: 100%; 783 | margin: 0; 784 | padding: 0; 785 | } 786 | .absolute_icon{ 787 | position: absolute; 788 | font-size: 80px; 789 | bottom: -10px; 790 | right: -10px; 791 | color: rgba(255, 255, 255, 0.2); 792 | z-index: 0; 793 | } 794 | .ambulances_active_count:hover{ 795 | cursor: pointer; 796 | } 797 | .dashboard_chart_heading{ 798 | color: #ff5049; 799 | font-weight: bolder; 800 | margin: 30px 0px 20px 20px; 801 | } 802 | 803 | 804 | /* media queries */ 805 | 806 | @media screen and (max-width: 700px ) { 807 | .main_content{ 808 | /* background: rgb(255,191,188); 809 | background: linear-gradient(45deg, rgba(255,191,188,1) 0%, rgba(255,139,135,.8) 49%, rgba(255,198,195,1) 100%); */ 810 | } 811 | .header_item{ 812 | height: 60px; 813 | background: transparent; 814 | box-shadow: rgba(0, 0, 0, 0.19) 0px 10px 20px, rgba(0, 0, 0, 0.23) 0px 6px 6px; 815 | } 816 | .cancel_nav,.open_nav{ 817 | display: initial; 818 | } 819 | .logo{ 820 | width: 250px; 821 | margin: 0 auto; 822 | padding: 15px; 823 | display: block; 824 | 825 | } 826 | .header_links{ 827 | position: fixed; 828 | top: 0; 829 | left: -100%; 830 | width: 100vw; 831 | height: 100vh; 832 | background: #ff4f49f5; 833 | color: white; 834 | display: flex; 835 | justify-content: center; 836 | align-items: center; 837 | flex-direction: column; 838 | transition: 200ms linear all; 839 | z-index: 100; 840 | } 841 | .header_links li{ 842 | display: block; 843 | text-decoration: underline; 844 | } 845 | .cancel_nav{ 846 | margin: 20px; 847 | background: rgba(255, 255, 255, 0.226); 848 | display: block; 849 | width: 60px; 850 | height: 60px; 851 | border-radius: 50%; 852 | font-size: 40px; 853 | line-height: 60px; 854 | } 855 | .open_nav{ 856 | position: fixed; 857 | top: 8px; 858 | left: 12px; 859 | font-size: 25px; 860 | color: #ff5049; 861 | border: 1px solid rgba(0, 0, 0, 0.129); 862 | padding: 7px; 863 | background: rgba(119, 119, 119, 0.116); 864 | border-radius: 5px; 865 | } 866 | 867 | .homepage_tagline{ 868 | display: flex; 869 | flex-flow: column; 870 | margin-top: 30px; 871 | } 872 | .tagline_text{ 873 | width: 100%; 874 | padding: 0px 15px; 875 | order: 2; 876 | margin: 0; 877 | } 878 | .tagline_text h1{ 879 | font-size: 30px; 880 | margin-top: 0; 881 | } 882 | .tagline_ambulance{ 883 | order: 1; 884 | width: 60%; 885 | margin-top: 10px; 886 | } 887 | .signup_container{ 888 | width: 90%; 889 | /* border: 1px solid lightgray; */ 890 | border: none; 891 | padding: 10px; 892 | margin-top: 30px; 893 | } 894 | .message_container{ 895 | left: 20px; 896 | right: 20px; 897 | bottom: 50px; 898 | } 899 | #previous,#next{ 900 | width: 130px; 901 | float: left; 902 | margin: 10px 0px; 903 | 904 | } 905 | #next{ 906 | float: right; 907 | } 908 | .dashboard_sidebar{ 909 | position: absolute; 910 | left: -100%; 911 | width: 80% !important; 912 | transition: 0.5s all linear; 913 | } 914 | .dashboard_sidebar li{ 915 | font-size: 17px; 916 | height: 50px; 917 | } 918 | .dashboard_content{ 919 | padding: 0; 920 | padding-top: 60px; 921 | } 922 | .welcome_header h3{ 923 | margin: 10px auto; 924 | } 925 | .history_table{ 926 | font-size: 11px; 927 | } 928 | .history_table th{ 929 | font-size: 13px; 930 | } 931 | .ambulances_active_count{ 932 | display: block; 933 | margin: 20px auto; 934 | } 935 | } --------------------------------------------------------------------------------