├── .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 |
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 | ID
35 | Location
36 | Time
37 | Status
38 |
39 |
40 | {history.map((r)=>{
41 | return
42 | {r.requestid}
43 | Open Maps
44 | {r.time}
45 | {r.status === "Active" ? {r.status} : ""}
46 | {r.status === "Completed" ? {r.status} : ""}
47 | {r.status === "Canceled" ? {r.status} : ""}
48 |
49 | })}
50 |
51 |
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 |
setAmount(e.target.value)} >
46 | PKR 1000
47 | PKR 5000
48 | PKR 10000
49 | PKR 50000
50 |
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 | Donation ID
42 | Donor ID
43 | Donor Email
44 | Donor Contact
45 | Time
46 | Donor Amount
47 |
48 |
49 | {donations.map(item => {
50 | return
51 | {item.donationid}
52 | {item.donorid}
53 | {item.email}
54 | {item.contact}
55 | {item.donatedon}
56 | {item.donatedamount}
57 |
58 | })}
59 |
60 |
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 | You need to enable JavaScript to run this app.
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 |
setGender(e.target.value)} value={genderInp} disabled={!edit} >
101 | Male
102 | Female
103 |
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 | {/* Cancel */}
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 |
setAmbType(e.target.value)} >
183 | Basic Ambulance
184 | Advance Ambulance
185 | Mortuary Ambulance
186 | Patient Transport Ambulance
187 |
188 |
189 |
Ambulance must have:
190 |
191 | setOxy(!oxy)} />
192 | Oxygen Cylinder
193 |
194 |
195 | setBp(!bp)} />
196 | Blood Pressure Machine
197 |
198 |
199 | setWh(!wh)} />
200 | Wheelchair
201 |
202 |
203 | setPd(!pd)} />
204 | Paramedic
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 |
setGender(e.target.value)} >
216 | Male
217 | Female
218 |
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 | ID
107 | First Name
108 | Last Name
109 | Email
110 | Contact No
111 | Ambulance ID
112 | License No
113 | Status
114 | Edit
115 | Delete
116 |
117 |
118 |
119 | 10004
120 | Shoaib
121 | Mailk
122 | shoaib@gmail.com
123 | 03151789081
124 | 109821
125 | 43920563487
126 | Busy
127 | {
128 | document.getElementById("blur_overlay").style.display = "block"
129 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
130 | }} >
131 | {
132 | document.getElementById("blur_overlay").style.display = "block"
133 | document.getElementById("custom_model_delete").style.top= "50%"
134 | }} >
135 |
136 |
137 |
138 | 10004
139 | Shoaib
140 | Mailk
141 | shoaib@gmail.com
142 | 03151789081
143 | 109821
144 | 43920563487
145 | Busy
146 | {
147 | document.getElementById("blur_overlay").style.display = "block"
148 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
149 | }} >
150 | {
151 | document.getElementById("blur_overlay").style.display = "block"
152 | document.getElementById("custom_model_delete").style.top= "50%"
153 | }} >
154 |
155 |
156 |
157 | 10004
158 | Shoaib
159 | Mailk
160 | shoaib@gmail.com
161 | 03151789081
162 | 109821
163 | 43920563487
164 | Busy
165 | {
166 | document.getElementById("blur_overlay").style.display = "block"
167 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
168 | }} >
169 | {
170 | document.getElementById("blur_overlay").style.display = "block"
171 | document.getElementById("custom_model_delete").style.top= "50%"
172 | }} >
173 |
174 |
175 |
176 | 10004
177 | Shoaib
178 | Mailk
179 | shoaib@gmail.com
180 | 03151789081
181 | 109821
182 | 43920563487
183 | Free
184 | {
185 | document.getElementById("blur_overlay").style.display = "block"
186 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
187 | }} >
188 | {
189 | document.getElementById("blur_overlay").style.display = "block"
190 | document.getElementById("custom_model_delete").style.top= "50%"
191 | }} >
192 |
193 |
194 |
195 | 10004
196 | Shoaib
197 | Mailk
198 | shoaib@gmail.com
199 | 03151789081
200 | 109821
201 | 43920563487
202 | Busy
203 | {
204 | document.getElementById("blur_overlay").style.display = "block"
205 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
206 | }} >
207 | {
208 | document.getElementById("blur_overlay").style.display = "block"
209 | document.getElementById("custom_model_delete").style.top= "50%"
210 | }} >
211 |
212 |
213 |
214 | 10004
215 | Shoaib
216 | Mailk
217 | shoaib@gmail.com
218 | 03151789081
219 | 109821
220 | 43920563487
221 | Free
222 | {
223 | document.getElementById("blur_overlay").style.display = "block"
224 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
225 | }} >
226 | {
227 | document.getElementById("blur_overlay").style.display = "block"
228 | document.getElementById("custom_model_delete").style.top= "50%"
229 | }} >
230 |
231 |
232 |
233 |
234 |
235 |
236 |
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 |
136 | Basic Ambulance
137 | Advance Ambulance
138 | Mortuary Ambulance
139 | Patient Transport Ambulance
140 |
141 |
142 |
Facilities:
143 |
144 |
145 | Oxygen Cylinder
146 |
147 |
148 |
149 | Blood Pressure Machine
150 |
151 |
152 |
153 | Wheelchair
154 |
155 |
156 |
157 | Paramedic
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 |
setAmbTypeAdd(e.target.value)} >
184 | Basic Ambulance
185 | Advance Ambulance
186 | Mortuary Ambulance
187 | Patient Transport Ambulance
188 |
189 |
190 |
Facilities:
191 |
192 |
193 | Oxygen Cylinder
194 |
195 |
196 |
197 | Blood Pressure Machine
198 |
199 |
200 |
201 | Wheelchair
202 |
203 |
204 |
205 | Paramedic
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 | ID
249 | Type
250 | Driver
251 | Facility
252 | No Plate
253 | Status
254 | Edit
255 | Delete
256 |
257 |
258 |
259 | {ambulances.map(item => {
260 | return
261 | {item.ambulanceid}
262 | {item.ambulancetype == "advance" ? Advance Ambulance : <>>}
263 | {item.ambulancetype == "basic" ? Basic Ambulance : <>>}
264 | {item.ambulancetype == "mortuary" ? Mortuary Ambulance : <>>}
265 | {item.ambulancetype == "patient" ? Patient Transport Ambulance : <>>}
266 | {item.driverid}
267 | {item.ambulancefacilities}
268 | {item.noplate}
269 | {item.status == "Busy" ? Busy : Free }
270 |
271 | {
272 | document.getElementById("blur_overlay").style.display = "block"
273 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
274 | }} >
275 | delOnCLick(item.ambulanceid)} >
276 |
277 | })}
278 |
279 |
280 |
281 |
282 |
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 | ID
107 | First Name
108 | Last Name
109 | Email
110 | Contact No
111 | Ambulance ID
112 | Medical License No
113 | Status
114 | Edit
115 | Delete
116 |
117 |
118 |
119 | 10004
120 | Babar
121 | Azam
122 | babar.azam@gmail.com
123 | 03154079181
124 | 109821
125 | 43920563487
126 | Busy
127 | {
128 | document.getElementById("blur_overlay").style.display = "block"
129 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
130 | }} >
131 | {
132 | document.getElementById("blur_overlay").style.display = "block"
133 | document.getElementById("custom_model_delete").style.top= "50%"
134 | }} >
135 |
136 |
137 |
138 | 10004
139 | Shoaib
140 | Mailk
141 | shoaib@gmail.com
142 | 03151789081
143 | 109821
144 | 43920563487
145 | Free
146 | {
147 | document.getElementById("blur_overlay").style.display = "block"
148 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
149 | }} >
150 | {
151 | document.getElementById("blur_overlay").style.display = "block"
152 | document.getElementById("custom_model_delete").style.top= "50%"
153 | }} >
154 |
155 |
156 |
157 |
158 | 10004
159 | Babar
160 | Azam
161 | babar.azam@gmail.com
162 | 03154079181
163 | 109821
164 | 43920563487
165 | Busy
166 | {
167 | document.getElementById("blur_overlay").style.display = "block"
168 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
169 | }} >
170 | {
171 | document.getElementById("blur_overlay").style.display = "block"
172 | document.getElementById("custom_model_delete").style.top= "50%"
173 | }} >
174 |
175 |
176 |
177 | 10004
178 | Shoaib
179 | Mailk
180 | shoaib@gmail.com
181 | 03151789081
182 | 109821
183 | 43920563487
184 | Free
185 | {
186 | document.getElementById("blur_overlay").style.display = "block"
187 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
188 | }} >
189 | {
190 | document.getElementById("blur_overlay").style.display = "block"
191 | document.getElementById("custom_model_delete").style.top= "50%"
192 | }} >
193 |
194 |
195 |
196 | 10004
197 | Babar
198 | Azam
199 | babar.azam@gmail.com
200 | 03154079181
201 | 109821
202 | 43920563487
203 | Busy
204 | {
205 | document.getElementById("blur_overlay").style.display = "block"
206 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
207 | }} >
208 | {
209 | document.getElementById("blur_overlay").style.display = "block"
210 | document.getElementById("custom_model_delete").style.top= "50%"
211 | }} >
212 |
213 |
214 |
215 | 10004
216 | Shoaib
217 | Mailk
218 | shoaib@gmail.com
219 | 03151789081
220 | 109821
221 | 43920563487
222 | Free
223 | {
224 | document.getElementById("blur_overlay").style.display = "block"
225 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
226 | }} >
227 | {
228 | document.getElementById("blur_overlay").style.display = "block"
229 | document.getElementById("custom_model_delete").style.top= "50%"
230 | }} >
231 |
232 |
233 |
234 | 10004
235 | Babar
236 | Azam
237 | babar.azam@gmail.com
238 | 03154079181
239 | 109821
240 | 43920563487
241 | Busy
242 | {
243 | document.getElementById("blur_overlay").style.display = "block"
244 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
245 | }} >
246 | {
247 | document.getElementById("blur_overlay").style.display = "block"
248 | document.getElementById("custom_model_delete").style.top= "50%"
249 | }} >
250 |
251 |
252 |
253 | 10004
254 | Shoaib
255 | Mailk
256 | shoaib@gmail.com
257 | 03151789081
258 | 109821
259 | 43920563487
260 | Free
261 | {
262 | document.getElementById("blur_overlay").style.display = "block"
263 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
264 | }} >
265 | {
266 | document.getElementById("blur_overlay").style.display = "block"
267 | document.getElementById("custom_model_delete").style.top= "50%"
268 | }} >
269 |
270 |
271 |
272 |
273 | 10004
274 | Shoaib
275 | Mailk
276 | shoaib@gmail.com
277 | 03151789081
278 | 109821
279 | 43920563487
280 | Busy
281 | {
282 | document.getElementById("blur_overlay").style.display = "block"
283 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
284 | }} >
285 | {
286 | document.getElementById("blur_overlay").style.display = "block"
287 | document.getElementById("custom_model_delete").style.top= "50%"
288 | }} >
289 |
290 |
291 |
292 | 10004
293 | Shoaib
294 | Mailk
295 | shoaib@gmail.com
296 | 03151789081
297 | 109821
298 | 43920563487
299 | Free
300 | {
301 | document.getElementById("blur_overlay").style.display = "block"
302 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
303 | }} >
304 | {
305 | document.getElementById("blur_overlay").style.display = "block"
306 | document.getElementById("custom_model_delete").style.top= "50%"
307 | }} >
308 |
309 |
310 |
311 | 10004
312 | Shoaib
313 | Mailk
314 | shoaib@gmail.com
315 | 03151789081
316 | 109821
317 | 43920563487
318 | Busy
319 | {
320 | document.getElementById("blur_overlay").style.display = "block"
321 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
322 | }} >
323 | {
324 | document.getElementById("blur_overlay").style.display = "block"
325 | document.getElementById("custom_model_delete").style.top= "50%"
326 | }} >
327 |
328 |
329 |
330 | 10004
331 | Shoaib
332 | Mailk
333 | shoaib@gmail.com
334 | 03151789081
335 | 109821
336 | 43920563487
337 | Free
338 | {
339 | document.getElementById("blur_overlay").style.display = "block"
340 | document.getElementById("custom_model_update_ambulance").style.top= "50%"
341 | }} >
342 | {
343 | document.getElementById("blur_overlay").style.display = "block"
344 | document.getElementById("custom_model_delete").style.top= "50%"
345 | }} >
346 |
347 |
348 |
349 |
350 |
351 |
352 |
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 | }
--------------------------------------------------------------------------------