├── .gitignore ├── admin-backend ├── app.js ├── controllers │ ├── adminController.js │ ├── authController.js │ ├── common.js │ ├── otpController.js │ ├── placeRequestController.js │ ├── requestsController.js │ ├── riderController.js │ └── sms.js ├── endpointsList.txt ├── models │ ├── admin.js │ ├── otpSchema.js │ ├── readme.md │ ├── registrations.js │ ├── request.js │ ├── requesters.js │ └── riders.js ├── package-lock.json ├── package.json └── routes │ ├── adminRouter.js │ ├── requests │ ├── general.js │ ├── pd.js │ └── requestRouter.js │ ├── riderRouter.js │ └── verify_token │ └── verify_token.js ├── admin-frontend ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.js │ ├── Components │ │ ├── AdminHome │ │ │ ├── AdminHome.js │ │ │ ├── fetchOrder.js │ │ │ └── home.module.css │ │ ├── AssignRequest │ │ │ ├── assignRequest.js │ │ │ └── assignrequest.module.css │ │ ├── Auth │ │ │ ├── Login.js │ │ │ └── OTP.js │ │ ├── CreateAdmin │ │ │ ├── CreateAdmin.js │ │ │ ├── createAdminHelper.js │ │ │ ├── createadmin.module.css │ │ │ ├── deleteAdmin.js │ │ │ └── fetchAdmins.js │ │ ├── CreateRequest │ │ │ ├── GeneralRequest │ │ │ │ ├── createRequestGeneral.js │ │ │ │ └── createRequestGeneral.module.css │ │ │ ├── P&DRequest │ │ │ │ ├── pndrequest.js │ │ │ │ └── pndrequest.module.css │ │ │ └── RequestType │ │ │ │ ├── RequestType.js │ │ │ │ └── requesttype.module.css │ │ ├── GlobalComponents │ │ │ ├── NavigationBar.js │ │ │ ├── Overlay │ │ │ │ ├── overlay.js │ │ │ │ └── overlayStyles.css │ │ │ ├── carousel │ │ │ │ ├── carousel.js │ │ │ │ └── carousel.module.css │ │ │ ├── input.js │ │ │ ├── input.module.css │ │ │ └── readme.txt │ │ ├── Home.js │ │ └── home.module.css │ ├── Context │ │ ├── authOperations.js │ │ └── authProvider.js │ ├── Images │ │ └── logo.png │ ├── Routes │ │ └── index.js │ ├── Utils │ │ └── useLocalStorageState.js │ ├── index.css │ ├── index.js │ ├── reportWebVitals.js │ └── setupTests.js └── yarn.lock ├── backend ├── .gitignore ├── app.js ├── controllers │ ├── OTP_Controller.js │ ├── authController.js │ ├── common.js │ ├── feedbackController.js │ ├── imageController.js │ ├── leaderboardController.js │ ├── registrationController.js │ ├── requesterController.js │ ├── requestsController.js │ ├── riderController.js │ └── sms.js ├── models │ ├── admin.js │ ├── feedback.js │ ├── otpSchema.js │ ├── question.js │ ├── readme.md │ ├── registrations.js │ ├── request.js │ ├── requesters.js │ └── riders.js ├── package-lock.json ├── package.json ├── readme.md └── routes │ ├── authentication │ ├── authRouter.js │ └── readme.md │ ├── common │ └── tokenAuth.js │ ├── feedback │ └── router.js │ ├── leaderboard │ ├── leaderboardRouter.js │ └── readme.md │ ├── questions │ └── router.js │ ├── readme.md │ ├── requester │ ├── readme.md │ └── requesterRouter.js │ ├── requests │ ├── generalRequest.js │ ├── newRequests.js │ ├── readme.md │ └── requestsRouter.js │ └── rider │ ├── readme.md │ └── riderRouter.js ├── frontend ├── .eslintrc.json ├── .gitignore ├── package-lock.json ├── package.json ├── public │ ├── assets │ │ └── logo.webp │ ├── favicon.ico │ ├── index.html │ ├── manifest.json │ └── robots.txt ├── readme.md └── src │ ├── App.css │ ├── App.js │ ├── components │ ├── about │ │ ├── about.js │ │ └── aboutStyles.css │ ├── authentication │ │ ├── readme.md │ │ └── ui │ │ │ ├── login │ │ │ ├── login.js │ │ │ └── loginStyles.css │ │ │ ├── otp │ │ │ ├── verify_otp.css │ │ │ └── verify_otp.js │ │ │ └── register │ │ │ ├── register_form.css │ │ │ └── register_form.js │ ├── context │ │ ├── auth │ │ │ ├── authOperations.js │ │ │ └── authProvider.js │ │ └── new_request │ │ │ ├── newRequestProvider.js │ │ │ └── place_request.js │ ├── global_ui │ │ ├── Imginput │ │ │ ├── ImgInput.js │ │ │ └── ImgInput.module.css │ │ ├── buttons │ │ │ └── button.js │ │ ├── carousel │ │ │ ├── carousel.js │ │ │ └── carousel.module.css │ │ ├── dialog │ │ │ ├── dialog.css │ │ │ └── dialog.js │ │ ├── fade_transition.css │ │ ├── image_viewer │ │ │ ├── imageViewer.module.css │ │ │ └── image_viewer.js │ │ ├── input.js │ │ ├── input.module.css │ │ ├── logo.css │ │ ├── logo.js │ │ ├── nav.css │ │ ├── nav.js │ │ ├── overlay.css │ │ ├── overlay.js │ │ ├── remarks │ │ │ ├── remarks.js │ │ │ └── remarks.module.css │ │ ├── spinner.js │ │ ├── spinner.module.css │ │ └── textarea │ │ │ ├── textArea.js │ │ │ └── textArea.module.css │ ├── home │ │ ├── initial_home │ │ │ ├── fetchLeaderBoard.js │ │ │ ├── initial-home.css │ │ │ ├── initialHomeRouting.js │ │ │ ├── initial_home.js │ │ │ ├── leaderboard.css │ │ │ ├── leaderboard.js │ │ │ ├── top-banner.js │ │ │ └── topbanner.css │ │ ├── readme.md │ │ ├── requester │ │ │ ├── RequesterHomeScreen.js │ │ │ ├── routes.js │ │ │ └── style.css │ │ └── rider │ │ │ ├── RiderHome.js │ │ │ └── routes.js │ ├── requester │ │ ├── confirm_request │ │ │ ├── confirmRequest.module.css │ │ │ ├── generalRequestConfirm.js │ │ │ └── pdRequestConfirm.js │ │ ├── feedback │ │ │ ├── feedBackForm.css │ │ │ └── feedbackForm.js │ │ ├── my_requests │ │ │ ├── MyRequests.js │ │ │ ├── MyRequests.module.css │ │ │ ├── MyRequestsListItem.js │ │ │ ├── my_requests_routes.js │ │ │ └── placed_request │ │ │ │ ├── cancel_confirm_request.js │ │ │ │ ├── items_requested_list.js │ │ │ │ ├── placed_request.js │ │ │ │ ├── placed_request.module.css │ │ │ │ ├── request_images.js │ │ │ │ └── request_images.module.css │ │ ├── new_request │ │ │ ├── ItemListType.js │ │ │ ├── ItemListType.module.css │ │ │ ├── NewRequestType.js │ │ │ ├── NewRequestType.module.css │ │ │ ├── Upload_images.module.css │ │ │ ├── choose_address.js │ │ │ ├── choose_address.module.css │ │ │ ├── enter_items_form │ │ │ │ ├── enterItemsForm.js │ │ │ │ └── requestItem.module.css │ │ │ ├── maps │ │ │ │ └── map.js │ │ │ ├── new_request_routes.js │ │ │ └── upload_images.js │ │ ├── profile │ │ │ ├── RequesterProfile.js │ │ │ ├── RequesterProfile.module.css │ │ │ ├── editRequesterProfile.js │ │ │ ├── editRequesterProfile.module.css │ │ │ └── profileRouting.js │ │ └── readme.md │ └── rider │ │ ├── common │ │ ├── viewRequest.js │ │ └── viewRequest.module.css │ │ ├── current_request │ │ ├── address.js │ │ ├── check_list.js │ │ ├── checklist.module.css │ │ ├── current_request.js │ │ ├── current_request.module.css │ │ ├── fetch_current_request.js │ │ ├── finish_request.js │ │ ├── preview_images.js │ │ ├── user_details.js │ │ └── user_details.module.css │ │ ├── make_delivery │ │ ├── ChooseRequest.module.css │ │ ├── ChooseRequestRoutes.js │ │ ├── chooseRequest.js │ │ └── chooseRequestItem.js │ │ ├── my_deliveries │ │ ├── .ukw │ │ ├── MyDeliveries.js │ │ ├── MyDeliveries.module.css │ │ ├── MyDeliveriesListItem.js │ │ └── my_delivery_routes.js │ │ ├── profile │ │ ├── RiderProfile.js │ │ ├── RiderProfile.module.css │ │ ├── editRiderProfile.js │ │ ├── editRiderProfile.module.css │ │ └── profileRouting.js │ │ └── readme.md │ ├── index.css │ ├── index.js │ ├── models │ ├── requester.js │ ├── rider.js │ └── user.js │ ├── reportWebVitals.js │ ├── service-worker.js │ ├── serviceWorkerRegistration.js │ ├── setupTests.js │ └── utils │ └── useLocalStorageState.js ├── mockups ├── AdminModuleFinal.xd └── RidersForReliefFinal.xd └── uml_diagrams ├── component.jpg ├── sequence.jpg ├── sequence.svg └── usecase.png /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | data/images 3 | .env 4 | .vscodeignore -------------------------------------------------------------------------------- /admin-backend/app.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const express = require("express"); 3 | const app = express(); 4 | const mongoose = require("mongoose"); 5 | const port = process.env.PORT || 8002; 6 | const cors = require("cors"); 7 | const requestsRouter = require("./routes/requests/requestRouter"); 8 | 9 | //express middleware usage. 10 | app.use(express.json()); 11 | app.use(cors()); 12 | var path = require("path"); 13 | 14 | const adminRouter = require("./routes/adminRouter"); 15 | const riderRouter = require("./routes/riderRouter"); 16 | const verify_token = require("./routes/verify_token/verify_token"); 17 | 18 | mongoose.set('useFindAndModify', false); 19 | mongoose.connect(`mongodb+srv://admin:${process.env.MONGO_PASSWORD}@cluster0.xgkw0.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`, { useNewUrlParser: true, useUnifiedTopology: true }); 20 | 21 | mongoose.connection.once('open', function() { 22 | console.log('You are connected to the database'); 23 | }).on('error', function(error) { 24 | console.log('error :', error); 25 | }) 26 | 27 | app.use( 28 | express.static(path.join(__dirname + "/" + process.env.IMAGE_DIR_PATH)) 29 | ); 30 | 31 | 32 | 33 | app.get("/", (req, res) => { 34 | res.send("Hey I am alive!") 35 | }); 36 | 37 | app.use("/admin", adminRouter) 38 | app.use("/rider",verify_token, riderRouter) 39 | app.use('/requests',verify_token,requestsRouter) 40 | 41 | app.listen(port, () => { 42 | console.log(`Listening at port ${port}`); 43 | }); -------------------------------------------------------------------------------- /admin-backend/controllers/adminController.js: -------------------------------------------------------------------------------- 1 | const admins = require("../models/admin"); 2 | const otpController = require("./otpController.js"); 3 | const sms = require("./sms.js"); 4 | const { sendError, sendResponse } = require("../controllers/common"); 5 | 6 | async function createAdmin(phoneNumber, name,otp) { 7 | try { 8 | 9 | const admin = await admins.findOne({ phoneNumber: phoneNumber }) 10 | if(admin.OTP.currentOTP === otp){ 11 | admin.name = name 12 | await admin.save() 13 | return sendResponse("Admin created") 14 | }else{ 15 | 16 | return sendError("Incorrect OTP") 17 | } 18 | 19 | 20 | } catch (error) { 21 | console.log(error) 22 | return sendError('Internal Server Error') 23 | } 24 | } 25 | 26 | async function deleteAdmin(phoneNumber) { 27 | console.log(phoneNumber); 28 | try { 29 | const admin = await admins.findOne({ phoneNumber: phoneNumber }) 30 | if (!admin) { 31 | return sendError("Admin not found") 32 | } 33 | admin.deleteOne() 34 | return sendResponse("Admin Deleted") 35 | } catch (error) { 36 | return sendError('Internal Server Error') 37 | } 38 | } 39 | 40 | async function fetchAdmins() { 41 | try { 42 | const admin = await admins.find() 43 | console.log(admin); 44 | return sendResponse(admin) 45 | } catch (error) { 46 | return sendError('Internal Server Error') 47 | } 48 | } 49 | 50 | module.exports = { 51 | createAdmin, 52 | deleteAdmin, 53 | fetchAdmins 54 | } -------------------------------------------------------------------------------- /admin-backend/controllers/common.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | function sendError(message) 8 | { 9 | return { 10 | status: "failure", 11 | message: message 12 | } 13 | } 14 | 15 | function sendResponse(message) 16 | { 17 | return { 18 | status: "success", 19 | message: message 20 | } 21 | } 22 | module.exports = { 23 | sendResponse, 24 | sendError 25 | } 26 | -------------------------------------------------------------------------------- /admin-backend/controllers/otpController.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | async function generateOTP() 3 | { 4 | return new Promise((resolve, reject)=>{ 5 | let res = ""; 6 | for(let a = 6; a>0; a--) 7 | res = res + Math.floor(Math.random()*10); 8 | resolve(res); 9 | }) 10 | } 11 | 12 | async function newOTP(doc) 13 | { 14 | return new Promise((resolve, reject)=>{ 15 | 16 | generateOTP() 17 | .then((res)=>{ 18 | 19 | doc.OTP = { 20 | currentOTP: res, 21 | otpSetTime: Date.now(), 22 | resendsLeft: process.env.MAX_OTP_RESENDS, 23 | guessesLeft: process.env.MAX_OTP_GUESSES, 24 | } 25 | 26 | resolve(); 27 | }) 28 | .catch(error=>{ 29 | console.log(error); 30 | }) 31 | }) 32 | } 33 | 34 | async function resendOTP(OTP) 35 | { 36 | return new Promise((resolve, reject)=>{ 37 | generateOTP() 38 | .then(res=>{ 39 | OTP.otpSetTime = Date.now(), 40 | OTP.currentOTP = res; 41 | OTP.resendsLeft--; 42 | resolve(); 43 | }) 44 | }) 45 | } 46 | 47 | async function verifyOTP(userDoc, otpGuess) 48 | { 49 | return new Promise((resolve, reject)=>{ 50 | 51 | if(!userDoc.OTP) 52 | reject("No OTP object found!") 53 | else if(userDoc.OTP.guessesLeft <= 0) 54 | { 55 | const timeDiff = (Date.now() - userDoc.OTP.otpSetTime)/(1000 * 60); 56 | if(timeDiff > OTP_PUNISHMENT_INTERVAL) 57 | userDoc.OTP.guessesLeft = 10; 58 | else 59 | reject("You have exceeded the maximum number of OTP tries, please try again after some time."); 60 | } 61 | if(userDoc.OTP.guessesLeft > 0) 62 | { 63 | const timeDiffMins = (Date.now() - userDoc.OTP.otpSetTime)/(1000 * 60); 64 | if(timeDiffMins > process.env.OTP_LIFE){ 65 | reject("OTP has expired. Try resend OTP."); 66 | } 67 | else 68 | { 69 | if(userDoc.OTP.currentOTP == otpGuess) 70 | { 71 | token = jwt.sign({ 72 | phoneNumber: userDoc.phoneNumber, 73 | 74 | }, process.env.TOKEN_SECRET); 75 | 76 | resolve(token); 77 | } 78 | else 79 | { 80 | userDoc.OTP.guessesLeft--; 81 | reject(`Wrong OTP, ${userDoc.OTP.guessesLeft} guesses left.`) 82 | } 83 | } 84 | } 85 | }) 86 | } 87 | 88 | module.exports = { 89 | newOTP, 90 | resendOTP, 91 | verifyOTP 92 | } 93 | -------------------------------------------------------------------------------- /admin-backend/controllers/placeRequestController.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /admin-backend/controllers/requestsController.js: -------------------------------------------------------------------------------- 1 | const requests = require("../models/request") 2 | const requesters = require("../models/requesters") 3 | const { sendError, sendResponse } = require("./common") 4 | 5 | const searchByRequestNumber = async(requestNumber)=> { 6 | try { 7 | 8 | const request = await requests.findOne({ requestNumber: requestNumber }).populate('requesterID') 9 | return sendResponse(request) 10 | 11 | } catch (error) { 12 | console.log(error) 13 | return sendError('Internal Server Error') 14 | } 15 | 16 | } 17 | module.exports = { 18 | searchByRequestNumber 19 | } -------------------------------------------------------------------------------- /admin-backend/controllers/riderController.js: -------------------------------------------------------------------------------- 1 | const riders = require("../models/riders"); 2 | const requests = require("../models/request") 3 | const { sendError, sendResponse } = require("./common"); 4 | 5 | async function riderByName(name) { 6 | try { 7 | 8 | const riderAvailable = await riders.find({ name: { $regex: name }, currentStatus: "AVAILABLE" }, { name: 1 }).exec() 9 | return sendResponse(riderAvailable) 10 | 11 | } catch (error) { 12 | console.log(error) 13 | return sendError('Internal Server Error') 14 | } 15 | } 16 | 17 | async function assignRider(phoneNumber, requestNumber) { 18 | try { 19 | 20 | const rider = await riders.findOne({ phoneNumber: phoneNumber }) 21 | const request = await requests.findOne({ requestNumber: requestNumber }) 22 | rider.currentStatus = "BUSY" 23 | rider.currentRequest = request._id 24 | rider.currentRequestType = request.requestType 25 | request.requestStatus = "UNDER DELIVERY" 26 | request.riderID = rider._id 27 | await rider.save(); 28 | await request.save(); 29 | return sendResponse(`Request assigned to ${rider.name} successfully`) 30 | 31 | } catch (error) { 32 | console.log(error) 33 | return sendError('Internal Server Error') 34 | } 35 | } 36 | 37 | async function getDeliveries() { 38 | try { 39 | 40 | const getDeliveries = await requests.find().exec() 41 | return sendResponse(getDeliveries) 42 | 43 | } catch (error) { 44 | console.log(error) 45 | return sendError('Internal Server Error') 46 | } 47 | } 48 | 49 | async function getDeliveriesByRequestStatus() { 50 | try { 51 | 52 | const getDeliveries = await requests.find({ requestStatus: requestStatus }).exec() 53 | return sendResponse(getDeliveries) 54 | 55 | } catch (error) { 56 | console.log(error) 57 | return sendError('Internal Server Error') 58 | } 59 | } 60 | 61 | 62 | 63 | 64 | async function searchDeliveryByRiderName(name) { 65 | try { 66 | 67 | const deliveriesByName = await riders.find({ name: { $regex: name } }).exec() 68 | return sendResponse(deliveriesByName) 69 | 70 | } catch (error) { 71 | console.log(error) 72 | return sendError('Internal Server Error') 73 | } 74 | } 75 | 76 | module.exports = { 77 | riderByName, 78 | assignRider, 79 | getDeliveries, 80 | searchDeliveryByRiderName, 81 | getDeliveriesByRequestStatus 82 | 83 | } -------------------------------------------------------------------------------- /admin-backend/controllers/sms.js: -------------------------------------------------------------------------------- 1 | async function sendOTP(phoneNumber, OTP) { 2 | //SMS SENDING CODE GOES HERE. 3 | //until then we only have this. 4 | return new Promise((resolve, reject) => { 5 | console.log("Relief Riders OTP: ", OTP); 6 | resolve(); 7 | }) 8 | } 9 | 10 | module.exports = { 11 | sendOTP 12 | }; -------------------------------------------------------------------------------- /admin-backend/endpointsList.txt: -------------------------------------------------------------------------------- 1 | /auth/requestOTP -post 2 | /auth/verifyOTP -post 3 | /admin/deliveries -get 4 | /admin/request/search/:requestNumber -get 5 | /admin/rider/search/:riderNamePrefix -get 6 | /admin/assign/:riderID/:requestNumber -get 7 | /admin/create -post 8 | /admin/delete -delete 9 | /admin/request/newgeneral -post 10 | /admin/request/newpd -post -------------------------------------------------------------------------------- /admin-backend/models/admin.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const OTPSchema = require("./otpSchema"); 3 | const user = new mongoose.Schema({ 4 | 5 | phoneNumber: { 6 | type: Number, 7 | required: [true, "Phone Number is required"], 8 | min: 1000000000, 9 | max: 9999999999, 10 | validate: { 11 | validator: (phone) => { 12 | var patt = /^[6789]\d{9}$/; 13 | return patt.test(phone) 14 | } 15 | } 16 | }, 17 | name: { 18 | type: String, 19 | required: [true, "Name is required"], 20 | minLength: 3, 21 | maxLength: [40, "Exceeded Characters"] 22 | }, 23 | OTP: OTPSchema 24 | }); 25 | 26 | 27 | const admin = mongoose.model("admin", user); 28 | 29 | module.exports = admin; -------------------------------------------------------------------------------- /admin-backend/models/otpSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | module.exports = new mongoose.Schema({ 4 | currentOTP: {type: String, required: true}, 5 | otpSetTime: {type: String, required: true}, 6 | resendsLeft:{type: Number, required: true}, 7 | guessesLeft:{type:Number, required: true} 8 | }) 9 | -------------------------------------------------------------------------------- /admin-backend/models/readme.md: -------------------------------------------------------------------------------- 1 | Models contain all neccessary models. 2 | 3 | 1. Requester 4 | 2. Rider 5 | 3. Request Model (contains request / order form data) 6 | 4. WIP -------------------------------------------------------------------------------- /admin-backend/models/registrations.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const otpSchema = require("./otpSchema"); 3 | 4 | const schema = new mongoose.Schema({ 5 | type: String, 6 | phoneNumber: Number, 7 | OTP: otpSchema 8 | }); 9 | 10 | module.exports = mongoose.model("registrations", schema) 11 | -------------------------------------------------------------------------------- /admin-backend/models/request.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const schema = new mongoose.Schema({ 4 | 5 | date:{ 6 | type: String, 7 | default: ()=>{ 8 | var now = new Date(); 9 | return (now.getDate() + '/' + (now.getMonth()+1) + '/' + now.getFullYear()); 10 | } 11 | }, 12 | 13 | requestNumber: {type: Number, required: [true, 'request number is required.']}, 14 | 15 | requesterID:{ 16 | type: mongoose.Schema.Types.ObjectId, 17 | required: true, 18 | ref: 'requesters' 19 | }, 20 | riderID:{ 21 | type: mongoose.Schema.Types.ObjectId, 22 | ref:'riders', 23 | default: null 24 | }, 25 | 26 | requesterCovidStatus: Boolean, 27 | 28 | noContactDelivery: Boolean, // Added No Contact Delivery 29 | 30 | requestStatus: { 31 | type: String, 32 | default: 'PENDING', 33 | uppercase:true, 34 | enum: ['PENDING', 'UNDER DELIVERY', 'DELIVERED', 'CANCELLED', 'RIDER CONFIRMED'] 35 | }, 36 | 37 | requestType:{ 38 | required: [true, 'request type is required.'], 39 | type: String, 40 | uppercase:true, 41 | enum: ['GENERAL', 'P&D'] 42 | }, 43 | 44 | itemsListImages: [String], 45 | 46 | itemsListList:[{ 47 | itemName: {type: String}, 48 | quantity: {type: String} //Yeah String only. Thank revanth. cuz units are different for things. The world is weird and the units are weirder. 49 | }], 50 | 51 | itemCategories: [ 52 | { 53 | type: String, 54 | enum: ['GROCERIES', 'MEDICINES', 'MISC'], 55 | uppercase:true, 56 | }], 57 | 58 | remarks: {type: String, maxLength: 240}, 59 | 60 | billsImageList: [String], 61 | 62 | rideImages: [String], 63 | 64 | 65 | // [ longitude, latitude ] 66 | roughLocationCoordinates:{ 67 | type: {type: String, default: "Point"}, 68 | coordinates: [Number] 69 | }, 70 | 71 | pickupLocationCoordinates:{ 72 | type: {type: String, default: "Point"}, 73 | coordinates: [Number] 74 | }, 75 | 76 | //Pickup location address MUST be there if the request is P&D and pickup coordinates have not been specified. 77 | pickupLocationAddress:{ 78 | address: { type: String, maxLength: 240}, 79 | area: String, 80 | city: String, 81 | }, 82 | 83 | dropLocationCoordinates:{ 84 | type: {type: String, default: "Point"}, 85 | coordinates: [Number] 86 | }, 87 | 88 | //drop location address MUST be there if the drop coordinates have not been specified. 89 | dropLocationAddress:{ 90 | address: { type: String, maxLength: 240}, 91 | area: String, 92 | city: String, 93 | } 94 | }) 95 | 96 | 97 | const requests = mongoose.model("requests", schema); 98 | module.exports= requests; 99 | -------------------------------------------------------------------------------- /admin-backend/models/requesters.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const OTPSchema = require("./otpSchema"); 3 | 4 | const user = new mongoose.Schema({ 5 | 6 | phoneNumber: { 7 | type: Number, 8 | required: [true, "Phone Number is required"], 9 | min: 1000000000, 10 | max: 9999999999, 11 | validate: { 12 | validator: (phone) => { 13 | var patt = /^[6789]\d{9}$/; return patt.test(phone) 14 | } 15 | } 16 | }, 17 | lastRequestTime: { type: Number, default: Date.now() }, 18 | name: { 19 | type: String, 20 | required: [true, "Name is required"], 21 | minLength: 3, 22 | maxLength: [40, "Exceeded Characters"] 23 | }, 24 | yearOfBirth: { 25 | type: Number, 26 | required: true, 27 | minLength: 4, 28 | maxLength: 4, 29 | validate: { 30 | validator: (yearOfBirth) => { 31 | var date = new Date(); 32 | var year = date.getFullYear(); 33 | return (year >= year - 100 && year <= year + 15) 34 | } 35 | } 36 | }, 37 | defaultAddress: { 38 | address: { 39 | type: String 40 | }, 41 | city: { 42 | type: String 43 | }, 44 | area: { 45 | type: String 46 | } 47 | }, 48 | location: { 49 | type: { type: String, default: "Point" }, 50 | coordinates: [Number] 51 | }, 52 | OTP: OTPSchema 53 | }); 54 | 55 | 56 | const requesters = mongoose.model("requesters", user); 57 | 58 | module.exports = requesters; 59 | -------------------------------------------------------------------------------- /admin-backend/models/riders.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const validator = require('validator'); 3 | const OTPSchema = require("./otpSchema"); 4 | 5 | const user = new mongoose.Schema({ 6 | 7 | phoneNumber: { 8 | type: Number, 9 | required: [true, "Phone Number is required"], 10 | min: 1000000000, 11 | max: 9999999999, 12 | validate: { 13 | validator: (phone) => { 14 | var patt = /^[6789]\d{9}$/; 15 | return patt.test(phone) 16 | } 17 | } 18 | 19 | }, 20 | name: { 21 | type: String, 22 | required: [true, "Name is required"], 23 | minLength: 3, 24 | maxLength: 40 25 | }, 26 | lastLocation: { 27 | type: { type: String, default: "Point" }, 28 | coordinates: [Number] 29 | }, 30 | currentStatus: { 31 | type: String, 32 | enum: ["AVAILABLE", "UNAVAILABLE", "BUSY"] 33 | }, 34 | currentRequestType: { 35 | type: String, 36 | enum: ["P&D", "GENERAL"] 37 | }, 38 | currentRequest: { 39 | type: mongoose.Schema.Types.ObjectId, 40 | ref: 'requests', 41 | default: null 42 | }, 43 | OTP: OTPSchema 44 | }); 45 | 46 | 47 | const riders = mongoose.model("riders", user); 48 | 49 | module.exports = riders; -------------------------------------------------------------------------------- /admin-backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin-backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "axios": "^0.21.4", 14 | "cors": "^2.8.5", 15 | "dotenv": "^10.0.0", 16 | "express": "^4.17.1", 17 | "jsonwebtoken": "^8.5.1", 18 | "md5": "^2.3.0", 19 | "mongoose": "^5.13.3", 20 | "multer": "^1.4.3", 21 | "path": "^0.12.7", 22 | "validator": "^13.6.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /admin-backend/routes/adminRouter.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const authController = require("../controllers/authController"); 4 | const adminController = require("../controllers/adminController") 5 | const { sendError, sendResponse } = require("../controllers/common"); 6 | const verify_token = require("./verify_token/verify_token"); 7 | 8 | 9 | router.post("/requestOTP", (req, res) => { 10 | console.log(req.body); 11 | authController.requestOTP(req.body.phoneNumber,req.body.operation) 12 | .then(response => { 13 | res.json(response); 14 | }) 15 | .catch(error => { 16 | console.log(error); 17 | res.json(error); 18 | }) 19 | }); 20 | 21 | router.post("/verifyOTP", (req, res) => { 22 | authController.verifyOTP(req.body.phoneNumber, req.body.OTP) 23 | .then(response => { 24 | res.json(response); 25 | }) 26 | .catch(error => { 27 | console.log(error); 28 | res.json(sendError("Internal Server Response")); 29 | }) 30 | }); 31 | 32 | router.post("/createAdmin", async(req, res) => { 33 | console.log(req.body); 34 | const response = await adminController.createAdmin(req.body.phoneNumber, req.body.name,req.body.OTP) 35 | console.log(response); 36 | res.json(response); 37 | }) 38 | 39 | router.post("/deleteAdmin", async(req, res) => { 40 | 41 | const response = await adminController.deleteAdmin(req.body.phoneNumber) 42 | //send data back 43 | res.json(response); 44 | }) 45 | router.get("/fetchAdmins", async(req, res) => { 46 | 47 | const response = await adminController.fetchAdmins() 48 | //send data back 49 | res.json(response); 50 | }) 51 | module.exports = router; -------------------------------------------------------------------------------- /admin-backend/routes/requests/requestRouter.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { searchByRequestNumber } = require("../../controllers/requestsController"); 3 | const router = express.Router(); 4 | const generalRequestRouter = require("./general"); 5 | const pdRequestRouter = require("./pd"); 6 | 7 | router.use("/general", generalRequestRouter); 8 | router.use("/pd", pdRequestRouter); 9 | router.post("/byRequestNumber", async(req, res) => { 10 | 11 | const response = await searchByRequestNumber(req.body.requestNumber) 12 | //send data back 13 | 14 | res.json(response); 15 | }) 16 | 17 | module.exports = router; 18 | -------------------------------------------------------------------------------- /admin-backend/routes/riderRouter.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const riderController = require("../controllers/riderController"); 4 | 5 | 6 | 7 | router.post("/riderByName", async(req, res) => { 8 | const response = await riderController.riderByName(req.body.name) 9 | //send data back 10 | 11 | res.json(response); 12 | }) 13 | 14 | router.post("/assignRider", async(req, res) => { 15 | const response = await riderController.assignRider(req.body.phoneNumber, req.body.requestNumber) 16 | //send data back 17 | 18 | res.json(response); 19 | }) 20 | 21 | router.get("/getDeliveries", async(req, res) => { 22 | const response = await riderController.getDeliveries() 23 | //send data back 24 | 25 | res.json(response); 26 | }) 27 | 28 | router.get("/getDeliveriesByRequestStatus", async(req, res) => { 29 | const response = await riderController.getDeliveriesByRequestStatus(req.body.requestStatus) 30 | //send data back 31 | 32 | res.json(response); 33 | }) 34 | 35 | 36 | router.get("/searchByRequestNumber", async(req, res) => { 37 | const response = await riderController.searchByRequestNumber(req.body.requestNumber) 38 | //send data back 39 | 40 | res.json(response); 41 | }) 42 | 43 | router.get("/searchByName", async(req, res) => { 44 | const response = await riderController.searchDeliveryByRiderName(req.body.name) 45 | //send data back 46 | 47 | res.json(response); 48 | }) 49 | 50 | 51 | 52 | 53 | module.exports = router; -------------------------------------------------------------------------------- /admin-backend/routes/verify_token/verify_token.js: -------------------------------------------------------------------------------- 1 | //Author: Shivani 2 | const JWT = require('jsonwebtoken') 3 | 4 | module.exports = 5 | (req, res, next) => { 6 | if (!req.headers['authorization']) { return res.json({ status: 'failure', message: "No authorization header found." }) } 7 | const authHeader = req.headers['authorization'] 8 | const bearerToken = authHeader.split(' ') 9 | const token = bearerToken[1] 10 | JWT.verify(token, process.env.TOKEN_SECRET, (err, data) => { 11 | if (err) { 12 | return res.json({ status: 'failure', message: "Token verification Failed." }) 13 | } 14 | req.user = data 15 | next() 16 | }) 17 | } -------------------------------------------------------------------------------- /admin-frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /admin-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin-frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "bootstrap": "^5.0.2", 10 | "react": "^17.0.2", 11 | "react-bootstrap": "^1.6.1", 12 | "react-date-picker": "^8.3.2", 13 | "react-dom": "^17.0.2", 14 | "react-router-dom": "^5.2.0", 15 | "react-scripts": "^4.0.3", 16 | "web-vitals": "^1.0.1" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /admin-frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding-Studio-vbit/ReliefRiders/3227eb361d66a3eda3d8794c54c72db31dbc6e8f/admin-frontend/public/favicon.ico -------------------------------------------------------------------------------- /admin-frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 | 32 |
33 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /admin-frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding-Studio-vbit/ReliefRiders/3227eb361d66a3eda3d8794c54c72db31dbc6e8f/admin-frontend/public/logo192.png -------------------------------------------------------------------------------- /admin-frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding-Studio-vbit/ReliefRiders/3227eb361d66a3eda3d8794c54c72db31dbc6e8f/admin-frontend/public/logo512.png -------------------------------------------------------------------------------- /admin-frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 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": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /admin-frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /admin-frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import { Switch, BrowserRouter as Router } from "react-router-dom"; 2 | import { AuthProvider } from "./Context/authProvider"; 3 | import Routes from "./Routes"; 4 | 5 | function App() { 6 | return ( 7 |
8 | 9 |
10 | 11 |
12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |
20 | ); 21 | } 22 | 23 | export default App; 24 | -------------------------------------------------------------------------------- /admin-frontend/src/Components/AdminHome/fetchOrder.js: -------------------------------------------------------------------------------- 1 | export const fetchOrder = async(requestNumber,token)=>{ 2 | const url = process.env.REACT_APP_URL 3 | 4 | try { 5 | const res = await fetch( 6 | `${url}/requests/byRequestNumber`, 7 | { 8 | method: `POST`, 9 | headers: { 10 | 'Accept': 'application/json', 11 | 'Content-Type': 'application/json', 12 | 'authorization' : `Bearer ${token}` 13 | }, 14 | body: JSON.stringify({ 15 | requestNumber: requestNumber, 16 | }) 17 | } 18 | ) 19 | const data = await res.json() 20 | console.log(data); 21 | if(data.status==='success'){ 22 | return {error:null,data:data.message} 23 | }else{ 24 | return {error:data.message} 25 | } 26 | } catch (error) { 27 | console.log(error); 28 | alert("Unable to access server, please try again later") 29 | } 30 | } -------------------------------------------------------------------------------- /admin-frontend/src/Components/AdminHome/home.module.css: -------------------------------------------------------------------------------- 1 | input[type="date"]{ 2 | border-radius: 10px; 3 | padding: 3px 6px; 4 | max-width: 168px; 5 | outline: 0px; 6 | color: #95a5a6; 7 | font-size: 18px; 8 | border:1px solid #ecf0f1; 9 | background:#ecf0f1; 10 | font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 11 | } 12 | 13 | .field{ 14 | width: 150px; 15 | } 16 | 17 | .inputField{ 18 | width: 150px; 19 | border-radius: 10px; 20 | padding: 4px 6px; 21 | outline: 0px; 22 | color: darkslategrey; 23 | font-size: 16px; 24 | border:1px solid #ecf0f1; 25 | background:#ecf0f1; 26 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 27 | } 28 | 29 | option{ 30 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 31 | border: none; 32 | outline: none; 33 | } 34 | 35 | 36 | .btn{ 37 | outline:0; 38 | border:0; 39 | background-color: #263238; 40 | color:white; 41 | border-radius:5px; 42 | padding: 4px 16px; 43 | text-align: center; 44 | font-size:17px 45 | } 46 | 47 | .requests { 48 | font-family: Arial, Helvetica, sans-serif; 49 | border-collapse: collapse; 50 | margin: 7px; 51 | 52 | width:75%; 53 | } 54 | 55 | .requests td, .requests th { 56 | border: 1px solid #ddd; 57 | padding: 4px; 58 | } 59 | 60 | /* .requests tr:nth-child(even){background-color: #f2f2f2;} */ 61 | 62 | .requests tr:hover {background-color: #ddd;} 63 | 64 | .requests th { 65 | padding: 4px 8px; 66 | text-align: left; 67 | background-color:darkslategrey; 68 | color: white; 69 | } -------------------------------------------------------------------------------- /admin-frontend/src/Components/AssignRequest/assignrequest.module.css: -------------------------------------------------------------------------------- 1 | .border { 2 | padding: 20px; 3 | margin-left: 5%; 4 | width: 90%; 5 | margin-right: 5%; 6 | margin-top:25px; 7 | margin-bottom: 25px; 8 | } 9 | 10 | .border2 { 11 | border: 1px solid #000000; 12 | border-radius: 10px; 13 | padding: 20px; 14 | margin-left: 5%; 15 | margin-right: 5%; 16 | margin-top: 3%; 17 | } 18 | 19 | .border3 { 20 | border: 1px solid grey; 21 | background:white; 22 | color: #000000; 23 | border-radius: 20px; 24 | padding: 20px; 25 | margin-left: 5%; 26 | margin-right: 5%; 27 | margin-top: 1%; 28 | } 29 | 30 | .border4 { 31 | padding: 20px; 32 | margin-left: 5%; 33 | margin-right: 5%; 34 | margin-top: 1%; 35 | } 36 | 37 | .header{ 38 | color: #AE1818; 39 | text-align: center; 40 | } 41 | 42 | .pow{ 43 | color:#AE1818; 44 | text-align: center; 45 | margin-top: 10px; 46 | } 47 | 48 | .ordersearch{ 49 | display: flex; 50 | align-items: center; 51 | flex-direction: column; 52 | justify-content: center; 53 | } 54 | 55 | .details{ 56 | display: inline; 57 | } 58 | 59 | .field{ 60 | display: flex; 61 | justify-content: space-around; 62 | align-items: center; 63 | flex-direction: row; 64 | width: 60%; 65 | } 66 | 67 | .inputField{ 68 | margin-right: 15px; 69 | width: 150px; 70 | border-radius: 10px; 71 | padding: 4px 10px; 72 | outline: 0px; 73 | color: darkslategrey; 74 | font-size: 16px; 75 | border:1px solid #ecf0f1; 76 | background:#ecf0f1; 77 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 78 | } -------------------------------------------------------------------------------- /admin-frontend/src/Components/Auth/Login.js: -------------------------------------------------------------------------------- 1 | import Logo from "../../Images/logo.png"; 2 | import { Button } from "react-bootstrap"; 3 | import { useHistory } from "react-router-dom"; 4 | import { useState } from "react"; 5 | import { requestOTP } from "../../Context/authOperations"; 6 | 7 | export default function Login() { 8 | const [mobile, setMobile] = useState() 9 | const [error, setError] = useState(null) 10 | let history = useHistory(); 11 | 12 | const validateMobile=()=>{ 13 | setError(null); 14 | const pattern = new RegExp(/^[6-9]\d{9}$/); 15 | if (mobile === "") { 16 | setError("Mobile number cannot be empty"); 17 | return false; 18 | } 19 | if (!pattern.test(mobile)) { 20 | setError("Please enter a valid number"); 21 | return false; 22 | } 23 | setError(null); 24 | return true; 25 | } 26 | 27 | const handleOTP = async () => { 28 | if(validateMobile()){ 29 | const res = await requestOTP(mobile) 30 | if(res.error){ 31 | setError(res.error) 32 | }else{ 33 | history.push("/otp",{ 34 | number:mobile 35 | }); 36 | } 37 | } 38 | }; 39 | 40 | return ( 41 |
42 |
43 | 44 | logo 45 | 46 |
Admin Login
47 | 48 | setMobile(e.target.value)} 54 | > 55 | { 56 | error &&

{error}

57 | } 58 | 61 |
62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /admin-frontend/src/Components/Auth/OTP.js: -------------------------------------------------------------------------------- 1 | import Logo from "../../Images/logo.png"; 2 | import { Button } from "react-bootstrap"; 3 | import { useState } from "react"; 4 | import { useHistory } from "react-router-dom"; 5 | import { verifyOTP } from "../../Context/authOperations"; 6 | import { useContext } from "react"; 7 | import { AuthContext } from "../../Context/authProvider"; 8 | 9 | export default function OTP() { 10 | const [error, setError] = useState(null) 11 | const [otp, setotp] = useState() 12 | const history = useHistory(); 13 | const {setToken} = useContext(AuthContext) 14 | function validateOTP() { 15 | setError(null) 16 | const pattern = new RegExp(/^[0-9]{6,6}$/); 17 | 18 | if (otp === "") { 19 | setError("OTP cannot be empty"); 20 | return false; 21 | } 22 | if (!pattern.test(otp)) { 23 | setError("Enter a valid OTP"); 24 | return false; 25 | } 26 | setError(null) 27 | return true 28 | } 29 | 30 | const handleLogin=async()=>{ 31 | if(validateOTP()){ 32 | const res = await verifyOTP(history.location.state.number,otp) 33 | if(res.error) setError(res.error) 34 | else{ 35 | setToken(res.token) 36 | history.push('/') 37 | } 38 | } 39 | } 40 | 41 | return ( 42 |
43 |
44 | logo 45 |
Enter OTP
46 | setotp(e.target.value)} 52 | > 53 | { 54 | error &&

{error}

55 | } 56 | 59 |
60 |
61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /admin-frontend/src/Components/CreateAdmin/createAdminHelper.js: -------------------------------------------------------------------------------- 1 | export const createAdminHelper = async (phoneNumber,name,otp,token)=>{ 2 | const url = process.env.REACT_APP_URL 3 | console.log(url); 4 | try { 5 | const res = await fetch( 6 | `${url}/admin/createAdmin`, 7 | { 8 | method: `POST`, 9 | headers: { 10 | 'Accept': 'application/json', 11 | 'Content-Type': 'application/json', 12 | 'authorization' : `Bearer ${token}` 13 | }, 14 | body: JSON.stringify({ 15 | phoneNumber: phoneNumber, 16 | name:name, 17 | OTP:otp 18 | }) 19 | } 20 | ) 21 | const data = await res.json() 22 | console.log(res); 23 | if(data.status==='success'){ 24 | return {error:null} 25 | }else{ 26 | return {error:data.message} 27 | } 28 | } catch (error) { 29 | console.log(error); 30 | return error 31 | } 32 | } -------------------------------------------------------------------------------- /admin-frontend/src/Components/CreateAdmin/deleteAdmin.js: -------------------------------------------------------------------------------- 1 | export const deleteAdminByID = async(number,token)=>{ 2 | const url = process.env.REACT_APP_URL 3 | 4 | try { 5 | const res = await fetch( 6 | `${url}/admin/deleteAdmin`, 7 | { 8 | method: `POST`, 9 | headers: { 10 | 'Accept': 'application/json', 11 | 'Content-Type': 'application/json', 12 | 'authorization' : `Bearer ${token}` 13 | }, 14 | body: JSON.stringify({ 15 | phoneNumber:number 16 | }) 17 | } 18 | ) 19 | const data = await res.json() 20 | console.log(data); 21 | if(data.status==='success'){ 22 | return {error:null,data:data.message} 23 | }else{ 24 | return {error:data.message} 25 | } 26 | } catch (error) { 27 | return {error:"Unable to access server"} 28 | } 29 | } -------------------------------------------------------------------------------- /admin-frontend/src/Components/CreateAdmin/fetchAdmins.js: -------------------------------------------------------------------------------- 1 | export const fetchAdmins = async(token)=>{ 2 | const url = process.env.REACT_APP_URL 3 | 4 | try { 5 | const res = await fetch( 6 | `${url}/admin/fetchAdmins`, 7 | { 8 | method: `GET`, 9 | headers: { 10 | 'Accept': 'application/json', 11 | 'Content-Type': 'application/json', 12 | 'authorization' : `Bearer ${token}` 13 | }, 14 | 15 | } 16 | ) 17 | const data = await res.json() 18 | console.log(res); 19 | if(data.status==='success'){ 20 | return {error:null,data:data.message} 21 | }else{ 22 | return {error:data.message} 23 | } 24 | } catch (error) { 25 | return {error:"Unable to access server"} 26 | } 27 | } -------------------------------------------------------------------------------- /admin-frontend/src/Components/CreateRequest/P&DRequest/pndrequest.module.css: -------------------------------------------------------------------------------- 1 | .checkBox{ 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | align-items: center; 6 | justify-items: center; 7 | } 8 | 9 | input[type="date"]{ 10 | border-radius: 10px; 11 | padding: 3px 6px; 12 | max-width: 168px; 13 | outline: 0px; 14 | color: #95a5a6; 15 | font-size: 18px; 16 | border:1px solid #ecf0f1; 17 | background:#ecf0f1; 18 | font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 19 | } 20 | 21 | .border { 22 | border-radius: 25px; 23 | border: 1px solid #000000; 24 | padding: 20px; 25 | padding-bottom: 30px; 26 | margin-left: 5%; 27 | margin-right: 5%; 28 | margin-top: 3%; 29 | margin-bottom: 3%; 30 | } 31 | 32 | .border2 { 33 | border-radius: 25px; 34 | border: 1px solid #000000; 35 | padding: 20px; 36 | padding-bottom: 25%; 37 | margin-left: 5%; 38 | margin-right: 5%; 39 | margin-top: 3%; 40 | } 41 | .header{ 42 | color: red; 43 | text-align: center; 44 | } 45 | 46 | .contactdetails{ 47 | justify-content: center; 48 | } 49 | 50 | .buttons{ 51 | display: inline; 52 | text-align: center; 53 | margin-left: 200px; 54 | width: 300px; 55 | height: 200px; 56 | align-items: center; 57 | 58 | } 59 | 60 | .card { 61 | display: flex; 62 | flex-direction: row; 63 | font-size: clamp(1rem, 2.2vw, 1.2rem); 64 | justify-content: space-between; 65 | width: 500px; 66 | padding: top 30%; 67 | background-color: rgba(151, 209, 247, 0.356); 68 | box-shadow: 0px 0px 10px 2px rgba(37, 6, 6, 0.15), 69 | 0px 0px 5px 1px rgba(31, 26, 26, 0.08), 0px 0px 3px rgba(0, 0, 0, 0.05); 70 | padding: 0.9em; 71 | padding-top: 2%; 72 | } 73 | .card i { 74 | cursor: pointer; 75 | } 76 | 77 | .btn { 78 | display: flex; 79 | gap: 10vw; 80 | justify-content: center; 81 | margin-top: 2%; 82 | } 83 | 84 | .btnArea { 85 | width: 15ch; 86 | padding: 10px 10px; 87 | font-weight: 800; 88 | } 89 | 90 | .checkBoxArea { 91 | display:flex; 92 | padding-top: 2%; 93 | flex-direction: column; 94 | justify-content: center; 95 | gap: min(50px, 10%); 96 | margin-bottom: 2.5rem; 97 | width: 100%; 98 | } 99 | .checkBoxArea input[type="checkbox"] { 100 | width: 16px; 101 | 102 | height: 16px; 103 | } 104 | .inputArea { 105 | margin-top: 1rem; 106 | display: grid; 107 | margin-bottom: 2rem; 108 | gap: 16px; 109 | justify-items: center; 110 | } 111 | 112 | .inputArea button { 113 | width: 10ch; 114 | padding: 4px 12px; 115 | } -------------------------------------------------------------------------------- /admin-frontend/src/Components/CreateRequest/RequestType/RequestType.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './requesttype.module.css' 3 | import { useHistory } from "react-router-dom"; 4 | import { 5 | Switch, 6 | Route 7 | } from "react-router-dom"; 8 | 9 | import CreateRequestGeneral from '../GeneralRequest/createRequestGeneral'; 10 | import PndRequest from '../P&DRequest/pndrequest'; 11 | 12 | function NewRequest() { 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ) 28 | } 29 | 30 | export default NewRequest; 31 | 32 | function RequestType() { 33 | let history=useHistory(); 34 | 35 | function route(p) { 36 | //p=true routes to general request page 37 | //p=false routes to p&d request page 38 | if(p){ 39 | history.push('/createrequest/general'); 40 | } 41 | else{ 42 | history.push('/createrequest/p&d') 43 | } 44 | } 45 | return ( 46 |
47 |
route(true)}> 48 |

General Request

49 |
50 |
route(false)}> 51 |

P&D Request

52 |
53 |
54 | ) 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /admin-frontend/src/Components/CreateRequest/RequestType/requesttype.module.css: -------------------------------------------------------------------------------- 1 | .page{ 2 | margin: 0; 3 | position: absolute; 4 | top: 50%; 5 | width: 100%; 6 | left: 50%; 7 | -ms-transform: translate(-50%, -50%); 8 | transform: translate(-50%, -50%); 9 | display: flex; 10 | flex-direction: row; 11 | justify-content: space-evenly; 12 | } 13 | 14 | .card{ 15 | box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; 16 | border-radius: 10px; 17 | width: 300px; 18 | height: 300px; 19 | background-color: #b0bec5; 20 | color: #263238; 21 | display: flex; 22 | justify-content: center; 23 | align-items: center; 24 | 25 | } 26 | 27 | .card:hover{ 28 | background-color: #eceff1; 29 | color: #263238; 30 | cursor: pointer; 31 | } 32 | 33 | .name{ 34 | font-size: 22px; 35 | font-weight: 400; 36 | } 37 | 38 | 39 | @media only screen and (max-width: 720px) { 40 | .card{ 41 | width: 260px; 42 | height: 260px; 43 | 44 | } 45 | } 46 | 47 | @media only screen and (max-width: 625px) { 48 | .card{ 49 | width: 250px; 50 | height: 200px; 51 | margin-bottom: 40px; 52 | } 53 | .page{ 54 | flex-direction: column; 55 | justify-content: center; 56 | align-items: center; 57 | } 58 | } 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /admin-frontend/src/Components/GlobalComponents/NavigationBar.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { Navbar, Nav } from "react-bootstrap"; 3 | import { useHistory } from "react-router"; 4 | import { AuthContext } from "../../Context/authProvider"; 5 | 6 | import logo from '../../Images/logo.png' 7 | 8 | export default function Navigationbar() { 9 | const {token ,setToken} = useContext(AuthContext) 10 | const history = useHistory() 11 | return ( 12 |
13 | 20 | 21 | 22 | Relief Riders 23 | 24 | 25 | 26 | 27 | 47 | 48 | 49 |
50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /admin-frontend/src/Components/GlobalComponents/Overlay/overlay.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './overlayStyles.css' 3 | 4 | function Dialog({ 5 | message,// message on dialog 6 | closeOverlay, 7 | display,//css prop 8 | operation//function to be performed on pressing yes 9 | }) { 10 | return ( 11 |
12 |
13 |

14 | {message} 15 |

16 | 17 | 18 | 19 | 20 |
21 |
22 | ) 23 | } 24 | 25 | export default Dialog; 26 | -------------------------------------------------------------------------------- /admin-frontend/src/Components/GlobalComponents/Overlay/overlayStyles.css: -------------------------------------------------------------------------------- 1 | .overlay{ 2 | display: none; 3 | position: fixed; 4 | top:0; 5 | left:0; 6 | right:0; 7 | bottom:0; 8 | width: 100%; 9 | height: 100%; 10 | display: grid; 11 | justify-content: center; 12 | overflow:hidden; 13 | background-color: rgba(0,0,0,0.3); /* Black w/ opacity */ 14 | } 15 | 16 | .overlay-content{ 17 | font-size: medium; 18 | font-weight: 500; 19 | background-color: #fefefe; 20 | border: 1px solid orchid; 21 | border-radius: 7px; 22 | 23 | padding:20px; 24 | width:330px; 25 | height: 30%; 26 | 27 | text-align: center; 28 | 29 | position: absolute; 30 | top: 50%; 31 | left: 50%; 32 | transform: translate(-50%, -50%); 33 | 34 | display: grid; 35 | justify-content: center; 36 | align-content:space-around; 37 | 38 | box-shadow: 15px rgba(0,0,0,0.2); 39 | transition: 0.3s; 40 | } 41 | 42 | .message{ 43 | font-size: medium; 44 | font-weight: 700; 45 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 46 | } 47 | 48 | .container{ 49 | height: 100%; 50 | width: 100%; 51 | display: flex; 52 | justify-content: center; 53 | align-items: center; 54 | } 55 | 56 | .btn-container{ 57 | display: flex; 58 | justify-content:space-evenly; 59 | align-content: center; 60 | padding: 0px; 61 | margin: 0px; 62 | } 63 | 64 | .yes{ 65 | border:none; 66 | outline: none; 67 | background-color: green; 68 | color: white; 69 | padding: 8px 30px; 70 | border-radius: 8px; 71 | } 72 | 73 | .no{ 74 | border:0.1px solid black ; 75 | outline:none; 76 | background-color:white; 77 | color: black; 78 | padding: 8px 30px; 79 | border-radius: 8px; 80 | } 81 | 82 | 83 | @media screen and (max-width: 450px) { 84 | .overlay-content{ 85 | height: 25%; 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /admin-frontend/src/Components/GlobalComponents/carousel/carousel.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-useless-concat */ 2 | import React,{useState} from 'react' 3 | import styles from './carousel.module.css' 4 | 5 | function Carousel({list,title}) { 6 | const [current, setcurrent] = useState(0); 7 | 8 | function forward(){ 9 | if(current!==list.length-1){ 10 | setTimeout(() => { 11 | setcurrent(current+1) 12 | }, 100); 13 | 14 | } 15 | } 16 | function backward(){ 17 | if(current!==0){ 18 | setTimeout(() => { 19 | setcurrent(current-1) 20 | }, 100); 21 | } 22 | } 23 | 24 | return ( 25 |
26 |

{title}

27 | 28 |
29 | backward()} 32 | style={{color:current===0?'lightgray':'black'}} 33 | > 34 |
35 | { 36 | list.map((item)=>{ 37 | return( 38 | list[current]===item && 39 | 40 | ) 41 | }) 42 | } 43 |
44 | 45 | forward()} 48 | style={{color:current===list.length-1?'lightgray':'black'}} 49 | > 50 |
51 |
52 | 53 | ) 54 | } 55 | 56 | 57 | export default Carousel; 58 | -------------------------------------------------------------------------------- /admin-frontend/src/Components/GlobalComponents/carousel/carousel.module.css: -------------------------------------------------------------------------------- 1 | .container{ 2 | display: flex; 3 | flex-direction: row; 4 | justify-items: center; 5 | justify-content: center; 6 | align-items: center; 7 | padding: 5px; 8 | margin: 0px; 9 | } 10 | 11 | .carouselImage{ 12 | max-width: 300px; 13 | border-radius: 3px; 14 | } 15 | 16 | .carouselImage::before{ 17 | -webkit-transition: opacity 1s ease-in-out; 18 | -moz-transition: opacity 1s ease-in-out; 19 | -o-transition: opacity 1s ease-in-out; 20 | transition: opacity 1s ease-in-out; 21 | } 22 | 23 | .move{ 24 | height: 30px; 25 | padding:10px 20px; 26 | } -------------------------------------------------------------------------------- /admin-frontend/src/Components/GlobalComponents/input.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './input.module.css' 3 | 4 | /** 5 | * 6 | * @param {*} props textAlign,size,minLength,value,placeholder,name,type,onChange,error,maxLength are the supported props 7 | * You can pass an error msg to show an error below input field 8 | * @returns a custom styled input component. 9 | */ 10 | const InputField = ({ukey,textAlign,size,minLength,value,placeholder,name,type,onChange,error,maxLength}) => { 11 | 12 | return ( 13 | 14 |
15 | 16 | 17 |
18 | {error && {error}} 19 | {error &&
} 20 |
21 | ); 22 | } 23 | 24 | export default InputField; -------------------------------------------------------------------------------- /admin-frontend/src/Components/GlobalComponents/input.module.css: -------------------------------------------------------------------------------- 1 | .inputField { 2 | position : relative; 3 | font-size: 1rem; 4 | height: fit-content; 5 | width: 100%; 6 | --label-top:calc(23%); 7 | } 8 | .label { 9 | position : absolute; 10 | left : 0.6em; 11 | padding-left : 0.2em; 12 | padding-right : 0.2em; 13 | color : #5a5858; 14 | font-size : 1em; 15 | top : var(--label-top); 16 | pointer-events: none; 17 | transition : top 0.2s ease, 18 | font-size 0.2s ease, 19 | color 0.2s ease; 20 | } 21 | 22 | .input { 23 | box-sizing:border-box; 24 | border : 2px solid #ddd; 25 | padding : 0.6em; 26 | font-size : 1.1em; 27 | border-radius: 4px; 28 | width: 100%; 29 | outline : none; 30 | transition : border-color 0.3s ease, 31 | border-color 0.2s ease 32 | ; 33 | 34 | } 35 | 36 | .inputField span{ 37 | position: absolute; 38 | left: 0.4%; 39 | color: red; 40 | font-size: 0.8em; 41 | padding-top: 0.2em; 42 | padding-left: 0.3em; 43 | } 44 | .inputField .normalField{ 45 | --label-top:calc(50% - 10px); 46 | } 47 | .errorField{ 48 | border-color: red; 49 | } 50 | 51 | .errorField +.label{ 52 | color: red; 53 | --label-top:17%; 54 | } 55 | .input:valid+.label, 56 | .input:focus+.label { 57 | font-size : 0.7em; 58 | top : -8px; 59 | color : black; 60 | border-color: black; 61 | background : white; 62 | } 63 | 64 | .input:focus { 65 | border-color: black; 66 | } 67 | .errorField:focus{ 68 | border-color: red; 69 | } 70 | .errorField:valid+.label, 71 | .errorField:focus +.label{ 72 | color: red; 73 | } 74 | .input[type=number]::-webkit-inner-spin-button, 75 | .input[type=number]::-webkit-outer-spin-button { 76 | -webkit-appearance: none; 77 | -moz-appearance: none; 78 | appearance: none; 79 | margin: 0; 80 | } 81 | 82 | textarea { 83 | box-sizing:border-box; 84 | border : 2px solid #ddd; 85 | font-size : 1.1em; 86 | border-radius: 4px; 87 | width: 100%; 88 | outline : none; 89 | transition : border-color 0.3s ease, 90 | border-color 0.2s ease 91 | ; 92 | resize: none; 93 | padding: 10px; 94 | } 95 | textarea:focus{ 96 | border-color: black; 97 | 98 | } 99 | 100 | textarea+label{ 101 | top: 0; 102 | left:1.2em; 103 | text-align: center; 104 | margin-top: 10px; 105 | font-size:0.9em; 106 | padding: 0px; 107 | transition : top 0.2s ease, 108 | font-size 0.2s ease, 109 | color 0.2s ease; 110 | } 111 | 112 | textarea:valid+label,textarea:focus+label{ 113 | position:absolute; 114 | top:-20px; 115 | left: 0.7em; 116 | padding: 0px 2px; 117 | color: black; 118 | height: 15px; 119 | font-size : 0.7em; 120 | 121 | background-color: white; 122 | } 123 | -------------------------------------------------------------------------------- /admin-frontend/src/Components/GlobalComponents/readme.txt: -------------------------------------------------------------------------------- 1 | Hi, create your module files here :D -------------------------------------------------------------------------------- /admin-frontend/src/Components/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useHistory } from 'react-router' 3 | import styles from './home.module.css' 4 | 5 | function AdminHome() { 6 | let history = useHistory(); 7 | 8 | return ( 9 |
10 |

Welcome Admin

11 | 12 |
13 | ) 14 | } 15 | 16 | export default AdminHome 17 | -------------------------------------------------------------------------------- /admin-frontend/src/Components/home.module.css: -------------------------------------------------------------------------------- 1 | .homeTitle{ 2 | font-size: xx-large; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 4 | } 5 | 6 | .home{ 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: center; 10 | align-items: center; 11 | height: 400px; 12 | } 13 | 14 | .loginBtn{ 15 | outline: none; 16 | border: none; 17 | background-color: #37474f; 18 | color:white; 19 | border-radius: 11px; 20 | padding: 4px; 21 | width: 10em; 22 | 23 | } -------------------------------------------------------------------------------- /admin-frontend/src/Context/authOperations.js: -------------------------------------------------------------------------------- 1 | 2 | const url = process.env.REACT_APP_URL 3 | //Operation == 1 login else register 4 | export const requestOTP = async (number,operation=1)=>{ 5 | try { 6 | const res = await fetch( 7 | `${url}/admin/requestOTP`, 8 | { 9 | method: `POST`, 10 | headers: { 11 | 'Accept': 'application/json', 12 | 'Content-Type': 'application/json' 13 | }, 14 | body: JSON.stringify({ 15 | phoneNumber: number, 16 | operation:operation 17 | }) 18 | } 19 | ) 20 | const data = await res.json() 21 | if(data.status==='success'){ 22 | return {error:null} 23 | }else{ 24 | return {error:data.message} 25 | } 26 | } catch (error) { 27 | console.log(error); 28 | alert("Unable to access server") 29 | } 30 | } 31 | 32 | export const verifyOTP = async (number,otp)=>{ 33 | try { 34 | const res = await fetch( 35 | `${url}/admin/verifyOTP`, 36 | { 37 | method: `POST`, 38 | headers: { 39 | 'Accept': 'application/json', 40 | 'Content-Type': 'application/json' 41 | }, 42 | body: JSON.stringify({ 43 | phoneNumber: number, 44 | OTP:otp 45 | }) 46 | } 47 | ) 48 | const data = await res.json() 49 | if(data.status==='success'){ 50 | return {error:null,token:data.message} 51 | }else{ 52 | return {error:data.message} 53 | } 54 | } catch (error) { 55 | console.log(error); 56 | alert("Unable to access server") 57 | } 58 | } -------------------------------------------------------------------------------- /admin-frontend/src/Context/authProvider.js: -------------------------------------------------------------------------------- 1 | import { createContext } from "react"; 2 | import { useLocalStorageState } from "../Utils/useLocalStorageState"; 3 | 4 | 5 | export const AuthContext = createContext(); 6 | 7 | export const AuthProvider = (prop) => { 8 | const [token, setToken] = useLocalStorageState("token",null) 9 | 10 | const setUpToken = (t)=>setToken(t) 11 | 12 | return ( 13 | 19 | {prop.children} 20 | 21 | ); 22 | }; -------------------------------------------------------------------------------- /admin-frontend/src/Images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding-Studio-vbit/ReliefRiders/3227eb361d66a3eda3d8794c54c72db31dbc6e8f/admin-frontend/src/Images/logo.png -------------------------------------------------------------------------------- /admin-frontend/src/Routes/index.js: -------------------------------------------------------------------------------- 1 | import { Route,Switch } from "react-router-dom"; 2 | import NavigationBar from "../Components/GlobalComponents/NavigationBar"; 3 | import Login from './../Components/Auth/Login' 4 | import OTP from "../Components/Auth/OTP"; 5 | import AdminHome from "../Components/AdminHome/AdminHome"; 6 | import CreateAdmin from "../Components/CreateAdmin/CreateAdmin"; 7 | import NewRequest from "../Components/CreateRequest/RequestType/RequestType"; 8 | import AssignRequest from "../Components/AssignRequest/assignRequest"; 9 | import { useContext } from "react"; 10 | import { AuthContext } from "../Context/authProvider"; 11 | import { Redirect } from "react-router"; 12 | 13 | const Routes = () => { 14 | const {token} = useContext(AuthContext) 15 | 16 | 17 | return ( 18 |
19 | 20 | { 21 | !token && 22 | } 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | ); 39 | }; 40 | 41 | 42 | 43 | export default Routes; 44 | -------------------------------------------------------------------------------- /admin-frontend/src/Utils/useLocalStorageState.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | /** 3 | * 4 | * @param {string} key Any unqiue key 5 | * @param {any} initialValue 6 | * 7 | * * Data will be stored in local storage. 8 | 9 | usage 10 | 11 | const [data,setData] = useLocalStorageState("data","") 12 | */ 13 | export const useLocalStorageState = (key,initialValue) => { 14 | 15 | const [data,setData] = useState(()=>{ 16 | const value = localStorage.getItem(key) 17 | if(value) 18 | return JSON.parse(value) 19 | return initialValue 20 | }) 21 | useEffect(()=>{ 22 | localStorage.setItem(key,JSON.stringify(data)) 23 | },[key,data]) 24 | 25 | return [data,setData]; 26 | } 27 | /** 28 | * 29 | * @param {string} key Any unqiue key 30 | * @param {any} initialValue 31 | * 32 | * Data will be deleted when browser or app is closed. Best for storing temporary session data. 33 | * 34 | usage 35 | 36 | const [data,setData] = useSessionStorageState("data","") 37 | */ 38 | export const useSessionStorageState = (key,initialValue) => { 39 | 40 | const [data,setData] = useState(()=>{ 41 | const value = sessionStorage.getItem(key) 42 | if(value) 43 | return JSON.parse(value) 44 | return initialValue 45 | }) 46 | useEffect(()=>{ 47 | sessionStorage.setItem(key,JSON.stringify(data)) 48 | },[key,data]) 49 | 50 | return [data,setData]; 51 | } 52 | -------------------------------------------------------------------------------- /admin-frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /admin-frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | import "bootstrap/dist/css/bootstrap.min.css"; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById("root") 13 | ); 14 | 15 | // If you want to start measuring performance in your app, pass a function 16 | // to log results (for example: reportWebVitals(console.log)) 17 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 18 | reportWebVitals(); 19 | -------------------------------------------------------------------------------- /admin-frontend/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /admin-frontend/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | 4 | -------------------------------------------------------------------------------- /backend/app.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const express = require('express'); 3 | const app = express(); 4 | const mongoose = require("mongoose"); 5 | const port = process.env.PORT || 8000; 6 | const cors = require('cors') 7 | var path = require('path'); 8 | 9 | //express middleware usage. 10 | app.use(express.json()); 11 | app.use(cors()) 12 | app.use( 13 | express.static(path.join(__dirname + '/' + process.env.IMAGE_DIR_PATH))); 14 | 15 | //mongoose connection. 16 | mongoose.set('useFindAndModify', false); 17 | mongoose.connect(`mongodb+srv://admin:${process.env.MONGO_PASSWORD}@cluster0.xgkw0.mongodb.net/myFirstDatabase?retryWrites=true&w=majority`, {useNewUrlParser: true, useUnifiedTopology: true}); 18 | 19 | mongoose.connection.once('open',function(){ 20 | console.log('You are connected to the database'); 21 | }).on('error', function(error){ 22 | console.log('error :', error); 23 | }) 24 | 25 | 26 | //Import routers here. 27 | const authRouter = require("./routes/authentication/authRouter"); 28 | const requesterRouter = require("./routes/requester/requesterRouter"); 29 | const riderRouter = require("./routes/rider/riderRouter"); 30 | const requestsRouter = require("./routes/requests/requestsRouter"); 31 | const leaderboardRouter = require("./routes/leaderboard/leaderboardRouter"); 32 | const feedbackRouter = require("./routes/feedback/router") 33 | const questionsRouter = require("./routes/questions/router") 34 | 35 | app.get("/", (req, res)=>{ 36 | res.send("Hey I am alive!"); 37 | }); 38 | 39 | 40 | //Use routers here. 41 | 42 | app.use("/auth", authRouter); 43 | app.use("/requester", requesterRouter); 44 | app.use("/rider", riderRouter); 45 | app.use("/requests",requestsRouter); 46 | app.use("/leaderboard",leaderboardRouter); 47 | app.use('/feedback',feedbackRouter) 48 | app.use('/questionsList',questionsRouter) 49 | //This call starts the periodic cleanup of the TEMP_OTP.json file, which is handled by the clearAllExpiredOTPs.js script. 50 | 51 | app.listen(port, () => { 52 | console.log(`Listening at http://localhost:${port}`) 53 | //Expired OTPs are cleared periodically at an interval defined in the .env file as OTP_FILE_CLEAR_INTERVAL 54 | }) 55 | -------------------------------------------------------------------------------- /backend/controllers/OTP_Controller.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | async function generateOTP() 3 | { 4 | return new Promise((resolve, reject)=>{ 5 | let res = ""; 6 | for(let a = 6; a>0; a--) 7 | res = res + Math.floor(Math.random()*10); 8 | resolve(res); 9 | }) 10 | } 11 | 12 | async function newOTP(doc) 13 | { 14 | return new Promise((resolve, reject)=>{ 15 | 16 | generateOTP() 17 | .then(res=>{ 18 | doc.OTP = ({ 19 | currentOTP: res, 20 | otpSetTime: Date.now(), 21 | resendsLeft: process.env.MAX_OTP_RESENDS, 22 | guessesLeft: process.env.MAX_OTP_GUESSES, 23 | }) 24 | resolve(); 25 | }) 26 | .catch(error=>{ 27 | console.log(error); 28 | }) 29 | }) 30 | } 31 | 32 | async function resendOTP(OTP) 33 | { 34 | return new Promise((resolve, reject)=>{ 35 | generateOTP() 36 | .then(res=>{ 37 | OTP.otpSetTime = Date.now(), 38 | OTP.currentOTP = res; 39 | OTP.resendsLeft--; 40 | resolve(); 41 | }) 42 | }) 43 | } 44 | 45 | async function verifyOTP(userDoc, type, otpGuess) 46 | { 47 | return new Promise((resolve, reject)=>{ 48 | 49 | if(!userDoc.OTP) 50 | reject("No OTP object found!") 51 | else if(userDoc.OTP.guessesLeft <= 0) 52 | { 53 | const timeDiff = (Date.now() - userDoc.OTP.otpSetTime)/(1000 * 60); 54 | if(timeDiff > OTP_PUNISHMENT_INTERVAL) 55 | userDoc.OTP.guessesLeft = 10; 56 | else 57 | reject("You have exceeded the maximum number of OTP tries, please try again after some time."); 58 | } 59 | if(userDoc.OTP.guessesLeft > 0) 60 | { 61 | const timeDiffMins = (Date.now() - userDoc.OTP.otpSetTime)/(1000 * 60); 62 | if(timeDiffMins > process.env.OTP_LIFE){ 63 | reject("OTP has expired. Try resend OTP."); 64 | } 65 | else 66 | { 67 | if(userDoc.OTP.currentOTP == otpGuess) 68 | { 69 | token = jwt.sign({ 70 | phoneNumber: userDoc.phoneNumber, 71 | type: type 72 | }, process.env.TOKEN_SECRET); 73 | 74 | resolve(token); 75 | } 76 | else 77 | { 78 | userDoc.OTP.guessesLeft--; 79 | reject(`Wrong OTP, ${userDoc.OTP.guessesLeft} guesses left.`) 80 | } 81 | } 82 | } 83 | }) 84 | } 85 | 86 | module.exports = { 87 | newOTP, 88 | resendOTP, 89 | verifyOTP 90 | } 91 | -------------------------------------------------------------------------------- /backend/controllers/common.js: -------------------------------------------------------------------------------- 1 | function sendError(message) 2 | { 3 | return { 4 | status: "failure", 5 | message: message 6 | } 7 | } 8 | 9 | function sendResponse(message) 10 | { 11 | return { 12 | status: "success", 13 | message: message 14 | } 15 | } 16 | module.exports = { 17 | sendResponse, 18 | sendError 19 | } 20 | -------------------------------------------------------------------------------- /backend/controllers/feedbackController.js: -------------------------------------------------------------------------------- 1 | const Feedback = require("../models/feedback"); 2 | const QuestionsList = require("../models/question"); 3 | const Question = require("../models/question"); 4 | const requesters = require("../models/requesters"); 5 | const { sendResponse, sendError } = require("./common"); 6 | 7 | async function saveFeedback(req,res){ 8 | try { 9 | const {questionsID, answers,optionalFeedback} = req.body 10 | const requester = await requesters.findOne({phoneNumber:req.user.phoneNumber}); 11 | const questions = await QuestionsList.findById(questionsID) 12 | const feed = new Feedback({ 13 | userID:requester, 14 | response:(answers), 15 | questionsID:questions, 16 | optionalFeedback:optionalFeedback 17 | }) 18 | console.log(feed); 19 | await feed.save() 20 | res.json(sendResponse("Feedback Submitted Successfully")) 21 | } catch (error) { 22 | console.log(error); 23 | res.json(sendError("Failed to submit feedback")) 24 | 25 | } 26 | } 27 | 28 | module.exports = { 29 | saveFeedback 30 | }; 31 | -------------------------------------------------------------------------------- /backend/controllers/imageController.js: -------------------------------------------------------------------------------- 1 | const multer = require('multer') 2 | const md5 = require('md5') 3 | const path = require('path'); 4 | const fs = require('fs') 5 | 6 | const storage = multer.diskStorage({ 7 | destination: function (req, file, cb) { 8 | let hashValue = md5(file.originalname+Date.now()); 9 | pathValue = path.join(__dirname, '../../../data/images', hashValue.slice(0,1),hashValue.slice(0,2)) 10 | var stat = null; 11 | try { 12 | stat = fs.statSync(pathValue); 13 | } catch (err) { 14 | fs.mkdirSync(pathValue, {recursive: true}); 15 | } 16 | if (stat && !stat.isDirectory()) { 17 | throw new Error('Directory cannot be created because an inode of a different type exists at "' + pathValue + '"'); 18 | } 19 | cb(null, pathValue); 20 | 21 | }, 22 | filename: function (req, file, cb) { 23 | console.log(file); 24 | fileName = md5(file.fieldname + String(Date.now()) + file.originalname )+ "." + file.mimetype.slice(6); 25 | return cb(null, fileName) 26 | } 27 | }); 28 | 29 | const upload = multer({ 30 | storage: storage 31 | }) 32 | 33 | 34 | module.exports = { 35 | upload 36 | } -------------------------------------------------------------------------------- /backend/controllers/leaderboardController.js: -------------------------------------------------------------------------------- 1 | const riders = require("../models/riders"); 2 | const { sendError, sendResponse } = require("./common"); 3 | 4 | 5 | async function displayLeaderboard() { 6 | return new Promise((resolve, reject) => { 7 | riders.find({ numberOfDeliveriesCompleted:{ $gt : 0 }},{ name: 1, numberOfDeliveriesCompleted: 1}).sort({numberOfDeliveriesCompleted:-1}).limit(4) 8 | .then( result => { 9 | resolve(sendResponse(result)); 10 | }) 11 | .catch(error => { 12 | console.log(error); 13 | resolve(sendError("Internal Server Error")); 14 | }) 15 | }) 16 | } 17 | module.exports = { 18 | displayLeaderboard 19 | }; 20 | -------------------------------------------------------------------------------- /backend/controllers/requestsController.js: -------------------------------------------------------------------------------- 1 | const requests = require("../models/request"); 2 | 3 | 4 | 5 | module.exports = { 6 | 7 | }; 8 | -------------------------------------------------------------------------------- /backend/controllers/sms.js: -------------------------------------------------------------------------------- 1 | 2 | async function sendOTP(phoneNumber, OTP) 3 | { 4 | //SMS SENDING CODE GOES HERE. 5 | //until then we only have this. 6 | return new Promise((resolve, reject)=>{ 7 | console.log("Relief Riders OTP: ", OTP); 8 | resolve(); 9 | }) 10 | } 11 | 12 | module.exports = { 13 | sendOTP 14 | }; 15 | -------------------------------------------------------------------------------- /backend/models/admin.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const user = new mongoose.Schema({ 4 | 5 | phoneNumber: { 6 | type: Number, 7 | required: [true, "Phone Number is required"], 8 | min: 1000000000, 9 | max: 9999999999, 10 | validate:{ 11 | validator: (phone)=> { 12 | var patt = /^[6789]\d{9}$/; return patt.test(phone) 13 | } 14 | } 15 | }, 16 | name: { 17 | type: String, 18 | required: [true, "Name is required"], 19 | minLength: 3, 20 | maxLength: [40, "Exceeded Characters"] 21 | } 22 | }); 23 | 24 | 25 | const admin = mongoose.model("admin", user); 26 | 27 | module.exports = admin; 28 | -------------------------------------------------------------------------------- /backend/models/feedback.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const feedback = new mongoose.Schema({ 4 | 5 | userID:{ 6 | type: mongoose.Schema.Types.ObjectId, 7 | ref: 'requesters', 8 | }, 9 | questionsID:{ 10 | type: mongoose.Schema.Types.ObjectId, 11 | ref: 'questions', 12 | }, 13 | optionalFeedback:{ 14 | type:String, 15 | }, 16 | response: [ 17 | { 18 | type:Boolean 19 | } 20 | ] 21 | }); 22 | 23 | 24 | const Feedback = mongoose.model("feedbacks", feedback); 25 | 26 | module.exports = Feedback; 27 | -------------------------------------------------------------------------------- /backend/models/otpSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | module.exports = new mongoose.Schema({ 4 | currentOTP: {type: String, required: true}, 5 | otpSetTime: {type: String, required: true}, 6 | resendsLeft:{type: Number, required: true}, 7 | guessesLeft:{type:Number, required: true} 8 | }) 9 | -------------------------------------------------------------------------------- /backend/models/question.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const Question = new mongoose.Schema({ 4 | questions: [ 5 | { 6 | type: String, 7 | }, 8 | ], 9 | latest:{ 10 | type:Boolean 11 | } 12 | }); 13 | 14 | const QuestionsList = mongoose.model("questions", Question); 15 | 16 | module.exports = QuestionsList; 17 | -------------------------------------------------------------------------------- /backend/models/readme.md: -------------------------------------------------------------------------------- 1 | Models contain all neccessary models. 2 | 3 | 1. Requester 4 | 2. Rider 5 | 3. Request Model (contains request / order form data) 6 | 4. WIP -------------------------------------------------------------------------------- /backend/models/registrations.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const otpSchema = require("./otpSchema"); 3 | 4 | const schema = new mongoose.Schema({ 5 | type: String, 6 | phoneNumber: Number, 7 | OTP: otpSchema 8 | }); 9 | 10 | module.exports = mongoose.model("registrations", schema) 11 | -------------------------------------------------------------------------------- /backend/models/requesters.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const OTPSchema = require("./otpSchema"); 3 | 4 | const user = new mongoose.Schema({ 5 | 6 | phoneNumber: { 7 | type: Number, 8 | required: [true, "Phone Number is required"], 9 | min: 1000000000, 10 | max: 9999999999, 11 | validate: { 12 | validator: (phone) => { 13 | var patt = /^[6789]\d{9}$/; return patt.test(phone) 14 | } 15 | } 16 | }, 17 | lastRequestTime: { type: Number, default: Date.now() }, 18 | name: { 19 | type: String, 20 | required: [true, "Name is required"], 21 | minLength: 3, 22 | maxLength: [40, "Exceeded Characters"] 23 | }, 24 | yearOfBirth: { 25 | type: Number, 26 | required: true, 27 | minLength: 4, 28 | maxLength: 4, 29 | validate: { 30 | validator: (yearOfBirth) => { 31 | var date = new Date(); 32 | var year = date.getFullYear(); 33 | return (year >= year - 100 && year <= year + 15) 34 | } 35 | } 36 | }, 37 | defaultAddress: { 38 | address: { 39 | type: String 40 | }, 41 | city: { 42 | type: String 43 | }, 44 | area: { 45 | type: String 46 | } 47 | }, 48 | location: { 49 | type: { type: String, default: "Point" }, 50 | coordinates: [Number] 51 | }, 52 | OTP: OTPSchema 53 | }); 54 | 55 | 56 | 57 | const requesters = mongoose.model("requesters", user); 58 | 59 | module.exports = requesters; 60 | -------------------------------------------------------------------------------- /backend/models/riders.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const validator = require('validator'); 3 | const OTPSchema = require("./otpSchema"); 4 | 5 | const user = new mongoose.Schema({ 6 | 7 | phoneNumber: { 8 | type: Number, 9 | required: [true, "Phone Number is required"], 10 | min: 1000000000, 11 | max: 9999999999, 12 | validate:{ 13 | validator: (phone)=> { 14 | var patt = /^[6789]\d{9}$/; return patt.test(phone) 15 | } 16 | } 17 | 18 | }, 19 | name: { 20 | type: String, 21 | required: [true, "Name is required"], 22 | minLength: 3, 23 | maxLength: 40 24 | }, 25 | lastLocation: { 26 | type: {type: String, default: "Point"}, 27 | coordinates: [Number] 28 | }, 29 | currentStatus: { 30 | type: String, 31 | enum: [ "AVAILABLE", "BUSY" ], 32 | default: "AVAILABLE" 33 | }, 34 | currentRequestType: { 35 | type: String, 36 | enum: ["P&D","GENERAL"] 37 | }, 38 | numberOfDeliveriesCompleted: { 39 | type: Number, 40 | default : 0 41 | }, 42 | currentRequest: { 43 | type: mongoose.Schema.Types.ObjectId, 44 | ref: 'requests', 45 | default : null 46 | }, 47 | OTP: OTPSchema 48 | }); 49 | 50 | const riders = mongoose.model("riders", user); 51 | module.exports = riders; 52 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "riders_relief", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "axios": "^0.21.1", 13 | "cors": "^2.8.5", 14 | "express": "^4.17.1", 15 | "jsonwebtoken": "^8.5.1", 16 | "md5": "^2.3.0", 17 | "mkdirp": "^0.5.5", 18 | "mongoose": "^5.12.12", 19 | "multer": "^1.4.2", 20 | "nodemon": "^2.0.15", 21 | "validator": "^13.6.0" 22 | }, 23 | "devDependencies": { 24 | "dotenv": "^10.0.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/readme.md: -------------------------------------------------------------------------------- 1 | # Auth 2 | ------------------- 3 | 4 | ## Login Endpoints: 5 | 6 | ### /auth/login/requestOTP
7 | request an OTP for registered riders or requesters. also works as resend OTP. 8 | 9 | #### request body: 10 | ```json 11 | { 12 | "phone":"", 13 | "type":"" 14 | } 15 | ``` 16 | #### response: 17 | ```json 18 | { 19 | "status":"failure/success", 20 | "message":"" 21 | } 22 | note: The generated OTP will be printed by the server on the console. 23 | On success, the message will contain the token. 24 | On failure, the message will contain the error message. 25 | ``` 26 | 27 | 28 | ### /auth/login/verifyOTP
29 | get OTP verified for registered riders/requesters. 30 | 31 | #### request body: 32 | ```json 33 | { 34 | "phone":"<10 digit phone number>", 35 | "OTP":"<6 digit OTP here>" 36 | } 37 | ``` 38 | #### response: 39 | ```json 40 | { 41 | "status":"failure/success", 42 | "message":"" 43 | } 44 | ``` 45 | ## Registration Endpoints: 46 | 47 | ### /auth/register/requestOTP
48 | request OTP for registration for a new rider/requester. 49 | 50 | #### request body: 51 | ```json 52 | { 53 | "type":"", 54 | "phone":"<10 digit phone number>" 55 | } 56 | ``` 57 | #### response: 58 | ```json 59 | { 60 | "status":"failure/success", 61 | "message":"" 62 | } 63 | ``` 64 | -------------------------------------------------------------------------------- /backend/routes/authentication/readme.md: -------------------------------------------------------------------------------- 1 | # Auth 2 | ------------------- 3 | 4 | ## Login Endpoints: 5 | 6 | ### /auth/login/requestOTP
7 | request an OTP for registered riders or requesters. also works as resend OTP. 8 | 9 | #### request body: 10 | ```json 11 | { 12 | "phone":"", 13 | "type":"" 14 | } 15 | ``` 16 | #### response: 17 | ```json 18 | { 19 | "status":"failure/success", 20 | "message":"" 21 | } 22 | note: The generated OTP will be printed by the server on the console. 23 | On success, the message will contain the token. 24 | On failure, the message will contain the error message. 25 | ``` 26 | 27 | 28 | ### /auth/login/verifyOTP
29 | get OTP verified for registered riders/requesters. 30 | 31 | #### request body: 32 | ```json 33 | { 34 | "phone":"<10 digit phone number>", 35 | "OTP":"<6 digit OTP here>", 36 | "type":"" 37 | } 38 | ``` 39 | #### response: 40 | ```json 41 | { 42 | "status":"failure/success", 43 | "message":"" 44 | } 45 | ``` 46 | ## Registration Endpoints: 47 | 48 | ### /auth/register/requestOTP
49 | request OTP for registration for a new rider/requester. 50 | 51 | #### request body: 52 | ```json 53 | { 54 | "type":"", 55 | "phone":"<10 digit phone number>" 56 | } 57 | ``` 58 | #### response: 59 | ```json 60 | { 61 | "status":"failure/success", 62 | "message":"" 63 | } 64 | ``` 65 | -------------------------------------------------------------------------------- /backend/routes/common/tokenAuth.js: -------------------------------------------------------------------------------- 1 | //Author: Shivani 2 | const JWT = require('jsonwebtoken') 3 | 4 | module.exports = 5 | (req, res, next) => { 6 | if (!req.headers['authorization']) { return res.json({ status: 'failure', message: "No authorization header found." }) } 7 | const authHeader = req.headers['authorization'] 8 | const bearerToken = authHeader.split(' ') 9 | const token = bearerToken[1] 10 | JWT.verify(token, process.env.TOKEN_SECRET, (err, data) => { 11 | if (err) { 12 | return res.json({ status: 'failure', message: "Token verification Failed." }) 13 | } 14 | req.user = data 15 | next() 16 | }) 17 | } -------------------------------------------------------------------------------- /backend/routes/feedback/router.js: -------------------------------------------------------------------------------- 1 | 2 | const express = require("express"); 3 | const { saveFeedback } = require("../../controllers/feedbackController"); 4 | const tokenAuth = require("../common/tokenAuth"); 5 | const router = express.Router(); 6 | router.use(tokenAuth) 7 | router.post("/", saveFeedback) 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /backend/routes/leaderboard/leaderboardRouter.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const tokenAuth = require("../common/tokenAuth"); 4 | const {sendError, sendResponse} = require("../../controllers/common"); 5 | const leaderboardController = require("../../controllers/leaderboardController"); 6 | 7 | router.get("/showLeaderboard", (req, res)=>{ 8 | leaderboardController.displayLeaderboard() 9 | .then(response=>{ 10 | res.json(response); 11 | }) 12 | .catch(error=>{ 13 | console.log(error); 14 | res.json(senderror("internal server error")); 15 | }) 16 | }) 17 | 18 | module.exports = router; 19 | -------------------------------------------------------------------------------- /backend/routes/leaderboard/readme.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding-Studio-vbit/ReliefRiders/3227eb361d66a3eda3d8794c54c72db31dbc6e8f/backend/routes/leaderboard/readme.md -------------------------------------------------------------------------------- /backend/routes/questions/router.js: -------------------------------------------------------------------------------- 1 | 2 | const express = require("express"); 3 | const { sendResponse, sendError } = require("../../controllers/common"); 4 | const QuestionsList = require("../../models/question"); 5 | const router = express.Router(); 6 | 7 | router.get("/",async (req,res)=>{ 8 | try { 9 | const questions = await QuestionsList.findOne({latest:true}) 10 | res.json(sendResponse(questions)) 11 | } catch (error) { 12 | res.json(sendError("Could not fetch questions")) 13 | 14 | } 15 | }) 16 | router.post("/set", async (req,res)=>{ 17 | try { 18 | const questionsLatest = await QuestionsList.findOne({latest:true}) 19 | if(questionsLatest){ 20 | questionsLatest.latest = false 21 | await questionsLatest.save() 22 | } 23 | const {questions} = req.body 24 | const questionsList = new QuestionsList({ 25 | questions:(questions), 26 | latest:true 27 | }) 28 | await questionsList.save() 29 | 30 | res.json(sendResponse("Questions saved ")) 31 | } catch (error) { 32 | console.log(error); 33 | res.json(sendError("Could not set questions")) 34 | 35 | } 36 | }) 37 | module.exports = router; 38 | -------------------------------------------------------------------------------- /backend/routes/readme.md: -------------------------------------------------------------------------------- 1 | Contains all required endpoints 2 | 3 | WIP -------------------------------------------------------------------------------- /backend/routes/requester/readme.md: -------------------------------------------------------------------------------- 1 | # Requester 2 | ------------------- 3 | 4 | Note: All these endpoints are protected and need the Auth header to be set. 5 | 6 | ## Profile: 7 | 8 | ### GET /requester/profile
9 | Get the profile details of a requester. 10 | 11 | #### request body: 12 | ```json 13 | No request Body. Just set the auth header. 14 | ``` 15 | #### response: 16 | ```json 17 | { 18 | "status":"failure/success", 19 | "message":"", 20 | "result": {} 21 | } 22 | ``` 23 | 24 | 25 | ### PUT /requester/profile
26 | Update the requester profile details. 27 | #### request body: 28 | ```json 29 | { 30 | "name":"newName", 31 | "yearOfBirth":"newYOB", 32 | "defaultAddress": 33 | { 34 | "addressLine":"", 35 | "city":"", 36 | "pincode":"" 37 | } 38 | } 39 | ``` 40 | #### response: 41 | ```json 42 | { 43 | "status":"failure/success", 44 | "message":"" 45 | } 46 | ``` 47 | ## My Requests: 48 | 49 | ### GET /requester/myRequests
50 | Get a list of all the requester's past and current requests. 51 | 52 | #### request body: 53 | ```json 54 | No request body 55 | ``` 56 | #### response: 57 | ```json 58 | { 59 | "status":"failure/success", 60 | "message":[] 61 | } 62 | ``` 63 | ## Cancel and Confirm Request: 64 | 65 | ### GET /requester/cancelRequest/:requestID
66 | cancel request specified by the requestID which is the request number of the request 67 | #### request body: 68 | ```json 69 | No request body 70 | ``` 71 | #### response: 72 | ```json 73 | { 74 | "status":"failure/success", 75 | "message":"" 76 | } 77 | ``` 78 | ### GET /requester/confirmRequest/:requestID
79 | confirm request specified by the requestID which is the request number of the request 80 | #### request body: 81 | ```json 82 | No request body 83 | ``` 84 | #### response: 85 | ```json 86 | { 87 | "status":"failure/success", 88 | "message":"" 89 | } 90 | ``` 91 | -------------------------------------------------------------------------------- /backend/routes/requests/readme.md: -------------------------------------------------------------------------------- 1 | Must provide CRUD operations on requests DB as well as Handle requests, which are pending (not yet taken by any riders). 2 | -------------------------------------------------------------------------------- /backend/routes/requests/requestsRouter.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const generalRequestRouter = require("./generalRequest"); 4 | const pdRequestRouter = require("./newRequests"); 5 | 6 | router.use("/general", generalRequestRouter); 7 | router.use("/pd", pdRequestRouter); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /backend/routes/rider/readme.md: -------------------------------------------------------------------------------- 1 | Must provide CRUD on riders DB -------------------------------------------------------------------------------- /frontend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "browser": true, 5 | "es2021": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:react/recommended" 10 | ], 11 | "parserOptions": { 12 | "ecmaFeatures": { 13 | "jsx": true 14 | }, 15 | "ecmaVersion": 12, 16 | "sourceType": "module" 17 | }, 18 | "plugins": [ 19 | "react" 20 | ], 21 | "rules": { 22 | "react/prop-types": "off" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@fortawesome/fontawesome-svg-core": "^1.2.35", 7 | "@fortawesome/free-solid-svg-icons": "^5.15.3", 8 | "@fortawesome/react-fontawesome": "^0.1.14", 9 | "@googlemaps/js-api-loader": "^1.12.2", 10 | "@react-google-maps/api": "^2.2.0", 11 | "@testing-library/jest-dom": "^5.12.0", 12 | "@testing-library/react": "^11.2.7", 13 | "@testing-library/user-event": "^12.8.3", 14 | "axios": "^0.21.1", 15 | "browser-image-compression": "^1.0.14", 16 | "chokidar": "^3.5.2", 17 | "google-distance": "^1.0.1", 18 | "google-distance-matrix": "^1.1.1", 19 | "google-maps-react": "^2.0.6", 20 | "node-distance-matrix": "^1.2.0", 21 | "react": "^17.0.2", 22 | "react-dom": "^17.0.2", 23 | "react-router-dom": "^5.2.0", 24 | "react-scripts": "4.0.3", 25 | "react-select": "^5.2.2", 26 | "react-transition-group": "^4.4.2", 27 | "web-vitals": "^0.2.4", 28 | "workbox-background-sync": "^5.1.4", 29 | "workbox-broadcast-update": "^5.1.4", 30 | "workbox-cacheable-response": "^5.1.4", 31 | "workbox-core": "^5.1.4", 32 | "workbox-expiration": "^5.1.4", 33 | "workbox-google-analytics": "^5.1.4", 34 | "workbox-navigation-preload": "^5.1.4", 35 | "workbox-precaching": "^5.1.4", 36 | "workbox-range-requests": "^5.1.4", 37 | "workbox-routing": "^5.1.4", 38 | "workbox-strategies": "^5.1.4", 39 | "workbox-streams": "^5.1.4" 40 | }, 41 | "scripts": { 42 | "start": "react-scripts start", 43 | "build": "react-scripts build", 44 | "test": "react-scripts test", 45 | "eject": "react-scripts eject" 46 | }, 47 | "eslintConfig": { 48 | "extends": [ 49 | "react-app", 50 | "react-app/jest" 51 | ] 52 | }, 53 | "browserslist": { 54 | "production": [ 55 | ">0.2%", 56 | "not dead", 57 | "not op_mini all" 58 | ], 59 | "development": [ 60 | "last 1 chrome version", 61 | "last 1 firefox version", 62 | "last 1 safari version" 63 | ] 64 | }, 65 | "devDependencies": { 66 | "eslint": "^7.27.0", 67 | "eslint-plugin-react": "^7.24.0" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /frontend/public/assets/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding-Studio-vbit/ReliefRiders/3227eb361d66a3eda3d8794c54c72db31dbc6e8f/frontend/public/assets/logo.webp -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding-Studio-vbit/ReliefRiders/3227eb361d66a3eda3d8794c54c72db31dbc6e8f/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 27 | Relief Riders 28 | 29 | 30 | 31 |
32 | 33 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon", 9 | "purpose":"any maskable" 10 | } 11 | ], 12 | "start_url": ".", 13 | "display": "standalone", 14 | "theme_color": "#000000", 15 | "background_color": "#ffffff" 16 | } 17 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/readme.md: -------------------------------------------------------------------------------- 1 |

RidersRelief

2 | 3 | Description : WIP 4 | 5 | WIP 6 | 7 | WIP 8 | 9 | *** 10 | 11 | UI Components : 12 | Below are available in src/components/global_ui/ 13 | 14 | 1.html button is globally styled 15 | 16 | 2.InputField - custom input field 17 | 18 | 3.Spinner - Loading component 19 | 20 | 4.Logo - RR logo component 21 | 22 | 23 | *** 24 | TODO : 25 | Loading component 26 | fetch API -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Poppins&display=swap"); 2 | 3 | @media (min-resolution: 120dpi) { 4 | body { 5 | zoom:0.82; 6 | background-color:var(--tertiary); 7 | } 8 | } 9 | 10 | :root{ 11 | --primary: #64778d; 12 | --secondary:#e25b4b; 13 | --tertiary:#ecfdff48; 14 | --textColor:white; 15 | --max-width:400px; 16 | --almost-transparent:rgba(0, 0, 0, 0.200); 17 | } 18 | 19 | body { 20 | font-family: "Poppins", sans-serif; 21 | margin: 0px; 22 | padding: 0px; 23 | } 24 | 25 | * { 26 | box-sizing: border-box; 27 | } 28 | button { 29 | align-self: center; 30 | background: var(--secondary); 31 | color: white; 32 | font-size: large; 33 | border-radius: 4px; 34 | font-weight: bold; 35 | padding: 0.7rem 1.25rem; 36 | cursor: pointer; 37 | border: none; 38 | transition: color 0.2s ease; 39 | } 40 | button:hover , button:focus{ 41 | outline:none !important; 42 | box-shadow: rgba(100, 100, 111, 0.3) 0.5px 5px 6px 0px; 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./App.css"; 3 | import { AuthProvider } from "./components/context/auth/authProvider"; 4 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 5 | import InitialHomeRouting from "./components/home/initial_home/initialHomeRouting"; 6 | import About from "./components/about/about"; 7 | 8 | function App() { 9 | return ( 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | ); 25 | } 26 | 27 | export default App; -------------------------------------------------------------------------------- /frontend/src/components/about/aboutStyles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Merienda&display=swap'); 2 | 3 | .about{ 4 | height: 100%; 5 | } 6 | 7 | .aboutContent{ 8 | height: 100%; 9 | display: grid; 10 | justify-content: center; 11 | justify-items: center; 12 | } 13 | 14 | .quoted-text{ 15 | text-align: center; 16 | font-family: 'Merienda', cursive; 17 | border-radius: 15px; 18 | font-size: 20px; 19 | margin: 10px; 20 | } 21 | 22 | .founder{ 23 | height:400px; 24 | margin-bottom: 40px; 25 | /* background-color: #8360c3; */ 26 | } 27 | 28 | .founderNote{ 29 | display: flex; 30 | justify-content: space-around; 31 | flex-direction: row; 32 | /* align-items: center; */ 33 | height: 300px; 34 | padding: 30px; 35 | 36 | background: #06beb6; /* fallback for old browsers */ 37 | background: -webkit-linear-gradient(to right, #48b1bf, #06beb6); /* Chrome 10-25, Safari 5.1-6 */ 38 | background: linear-gradient(to right, #48b1bf, #06beb6); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ 39 | 40 | } 41 | 42 | .founderContent{ 43 | max-width: 350px; 44 | font-size: 16px; 45 | align-self: center; 46 | color: #e8f5e9; 47 | margin: 10px; 48 | 49 | } 50 | 51 | .founderImg{ 52 | height: 330px; 53 | width: 225px; 54 | } 55 | 56 | footer{ 57 | background-color:#320032; 58 | color:beige; 59 | border-top: 1px solid saddlebrown; 60 | height: 100%; 61 | display: grid; 62 | justify-content: center; 63 | border-top: 0.1em solid green; 64 | } 65 | 66 | .title{ 67 | text-align: center; 68 | font-weight: 400; 69 | } 70 | 71 | .icons{ 72 | width: 200px; 73 | padding: 0px; 74 | margin: 4px; 75 | display: flex; 76 | justify-content: center; 77 | flex-direction: row; 78 | justify-content: space-around; 79 | } 80 | 81 | .finalText{ 82 | text-align: center; 83 | color: gray; 84 | font-weight: 300; 85 | } 86 | 87 | .icon{ 88 | font-size:x-large; 89 | } 90 | 91 | @media screen and (max-width: 485px) { 92 | .founder{ 93 | height: fit-content; 94 | margin-top: 50px; 95 | padding:0px; 96 | } 97 | .founderNote{ 98 | display: flex; 99 | flex-direction: column-reverse; 100 | align-items: center; 101 | height:450px; 102 | } 103 | .founderContent{ 104 | margin: 5px; 105 | } 106 | .founderImg{ 107 | margin-top: -100px; 108 | text-align: center; 109 | height: 300px; 110 | width: 250px; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /frontend/src/components/authentication/readme.md: -------------------------------------------------------------------------------- 1 | Must provide 2 | 3 | 1. UI screens : Login , Register. 4 | 2. login(credentials),register(credentials),logout(ID) operations/functions -------------------------------------------------------------------------------- /frontend/src/components/authentication/ui/login/loginStyles.css: -------------------------------------------------------------------------------- 1 | .login { 2 | position: relative; 3 | text-align: center; 4 | display: grid; 5 | padding: 1em; 6 | } 7 | .btnStyle{ 8 | font-size: large; 9 | } 10 | 11 | .btnStyle-register{ 12 | font-size: large; 13 | } 14 | 15 | 16 | .btnStyle-register > a { 17 | font-size: large; 18 | text-decoration: none; 19 | color: white; 20 | } 21 | .reg-cont { 22 | margin-top: 5rem; 23 | text-align: center; 24 | } 25 | .spinner { 26 | justify-self: center; 27 | } 28 | .routetext { 29 | color: slategray; 30 | align-self: end; 31 | margin-bottom: 5px; 32 | } 33 | .almostFooter a{ 34 | font-weight: bold; 35 | color:rgb(62, 101, 185) 36 | } -------------------------------------------------------------------------------- /frontend/src/components/authentication/ui/otp/verify_otp.css: -------------------------------------------------------------------------------- 1 | 2 | .otp-container{ 3 | display: grid; 4 | padding:1rem; 5 | justify-content: center; 6 | justify-items: center; 7 | gap: 0.5em; 8 | } 9 | 10 | .logo-container{ 11 | margin-bottom: 1em; 12 | } 13 | .send-otp-btn, .go-back-reg{ 14 | cursor:pointer; 15 | padding: 0px; 16 | color: red; 17 | margin: 0px; 18 | width: fit-content; 19 | background: transparent; 20 | } 21 | 22 | 23 | 24 | .verify-btn{ 25 | width: fit-content; 26 | margin: 0 auto; 27 | padding: 0.5em 1.2em; 28 | letter-spacing: 1pt; 29 | } -------------------------------------------------------------------------------- /frontend/src/components/authentication/ui/register/register_form.css: -------------------------------------------------------------------------------- 1 | .form { 2 | display: grid; 3 | justify-items: center; 4 | padding: 1em; 5 | position: relative; 6 | grid-auto-rows: min-content; 7 | 8 | } 9 | .inputContainer{ 10 | display: grid; 11 | grid-auto-rows: min-content; 12 | justify-items: center; 13 | justify-content: center; 14 | } 15 | 16 | .otp-btn { 17 | font-weight: bold; 18 | width: fit-content; 19 | align-self: flex-start; 20 | } 21 | .sec-row { 22 | margin-bottom: 75px; 23 | margin-top: 1rem; 24 | display: grid; 25 | justify-self: center; 26 | grid-auto-flow: column; 27 | gap: 16px; 28 | 29 | 30 | } 31 | 32 | .almostFooter a{ 33 | font-size: clamp(0.8rem,1.9vw,1rem); 34 | text-align: center; 35 | } -------------------------------------------------------------------------------- /frontend/src/components/context/auth/authProvider.js: -------------------------------------------------------------------------------- 1 | import { createContext, useReducer, React } from "react"; 2 | 3 | const AuthReducer = (state, action) => { 4 | switch (action.type) { 5 | case "SETERROR": 6 | return { ...state, loading: false, error: action.payload }; 7 | 8 | case "ISRIDER": 9 | return { ...state, isRequester: false }; 10 | case "AUTHENTICATED": 11 | return { 12 | ...state, 13 | token: action.payload.token, 14 | user: action.payload.user, 15 | isAuthenticated: true, 16 | isRequester: action.payload.user.isRequester, 17 | }; 18 | case "SETUSER": 19 | return { ...state, loading: true, user: action.payload }; 20 | case "SETLOADING": 21 | return { ...state, loading: !state.loading }; 22 | case "UNAUTHENTICATED": 23 | return {...state,isAuthenticated:false} 24 | case "LOGOUT": 25 | return {...initState,isAuthenticated:false}; 26 | } 27 | }; 28 | 29 | const initState = { 30 | isRequester: true, 31 | user: null, 32 | loading: false, 33 | isAuthenticated: null, 34 | token: null, 35 | error: "", 36 | }; 37 | 38 | export const AuthContext = createContext(); 39 | 40 | export const AuthProvider = (prop) => { 41 | const [state, dispatch] = useReducer(AuthReducer, initState); 42 | 43 | return ( 44 | 55 | {prop.children} 56 | 57 | ); 58 | }; 59 | -------------------------------------------------------------------------------- /frontend/src/components/context/new_request/newRequestProvider.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useReducer } from "react"; 3 | import { React, createContext } from "react"; 4 | import { useLocation } from "react-router-dom"; 5 | 6 | const newRequestReducer = (state, action) => { 7 | switch (action.type) { 8 | case "REQUEST_TYPE": 9 | return { ...state, requestType: action.payload }; 10 | case "ADD_CATEGORIES_IMAGES": 11 | return { ...state, uploadItemsList: true, categories: action.payload, }; 12 | case "ENTER_ITEMS": 13 | return { 14 | ...state, 15 | uploadItemsList: false, 16 | categories: action.categories, 17 | itemsList: action.itemsList, 18 | }; 19 | case "ADD_PICKUP_LOCATION_COORDINATES": 20 | return { ...state,pickupLocation:{ 21 | address: "", 22 | city: "", 23 | area: "", 24 | }, pickupLocationCoordinates: action.payload }; 25 | 26 | case "ADD_DROP_LOCATION_COORDINATES": 27 | return { ...state,dropLocation:{ 28 | address: "", 29 | city: "", 30 | area: "", 31 | }, dropLocationCoordinates: action.payload }; 32 | case "ADD_PICKUP_ADDRESS": 33 | return { ...state,pickupLocationCoordinates:[], pickupLocation: action.payload }; 34 | case "ADD_DROP_ADDRESS": 35 | return { ...state,dropLocationCoordinates:[], dropLocation: action.payload}; 36 | 37 | } 38 | }; 39 | const initState = { 40 | requestType: "", 41 | categories: [], 42 | uploadItemsList: false, 43 | itemsList: [], 44 | pickupLocation: { 45 | address: "", 46 | city: "", 47 | area: "", 48 | }, 49 | dropLocation: { 50 | address: "", 51 | city: "", 52 | area: "", 53 | }, 54 | dropLocationCoordinates: [], 55 | pickupLocationCoordinates: [], 56 | }; 57 | 58 | export const NewRequestContext = createContext(); 59 | 60 | export const NewRequestProvider = (prop) => { 61 | const [state, dispatch] = useReducer(newRequestReducer, initState, () => { 62 | const data = localStorage.getItem("new_request"); 63 | if (data) return JSON.parse(data); 64 | else return initState; 65 | }); 66 | const location = useLocation() 67 | useEffect(()=>{ 68 | 69 | localStorage.setItem('draft',location.pathname) 70 | },[location.pathname]) 71 | useEffect(() => { 72 | 73 | localStorage.setItem("new_request", JSON.stringify(state)); 74 | }, [state]); 75 | return ( 76 | 82 | {prop.children} 83 | 84 | ); 85 | }; 86 | -------------------------------------------------------------------------------- /frontend/src/components/context/new_request/place_request.js: -------------------------------------------------------------------------------- 1 | export const placeRequest = async (formData,token,type)=>{ 2 | 3 | try { 4 | const imgsParsed = sessionStorage.getItem('uploaded_images') 5 | const imgs = JSON.parse(imgsParsed) 6 | console.log(formData); 7 | for(const src in imgs){ 8 | console.log(imgs[src]); 9 | const res = await fetch(imgs[src]) 10 | const blob = await res.blob() 11 | const file = new File([blob],"image",{type:'image/jpg'}) 12 | formData.append('images',file) 13 | } 14 | const res = await fetch(process.env.REACT_APP_URL+`/requests/${type==='p&d'?"pd":"general"}/new`,{ 15 | method:'POST', 16 | headers: { 17 | authorization: "Bearer " + token, 18 | }, 19 | body:formData 20 | }) 21 | const data = await res.json() 22 | console.log(data); 23 | if(data.status === 'success'){ 24 | sessionStorage.clear() 25 | localStorage.removeItem('new_request') 26 | localStorage.setItem('draft','/new_request') 27 | return 1; 28 | }else{ 29 | return data.message 30 | } 31 | 32 | } catch (error) { 33 | 34 | return "Unable to access the server right now! Please try after some time" 35 | 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /frontend/src/components/global_ui/Imginput/ImgInput.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import imageCompression from "browser-image-compression"; 3 | import styles from "./ImgInput.module.css"; 4 | 5 | const ImgInput = ({ imgHeader, imgText, setImages}) => { 6 | 7 | async function onInputChange(e) { 8 | var imageFile = e.target.files[0]; 9 | var options = { 10 | maxSizeMB: 0.1, 11 | useWebWorker: true, 12 | }; 13 | const compressed = await imageCompression(imageFile, options); 14 | let reader = new FileReader(); 15 | reader.onload = function () { 16 | setImages((Image) => [...Image, reader.result]); 17 | }; 18 | reader.readAsDataURL(compressed); 19 | document.getElementById(imgHeader).value = null; 20 | } 21 | 22 | return ( 23 |
24 | 37 |
38 | ); 39 | }; 40 | 41 | export default ImgInput; 42 | -------------------------------------------------------------------------------- /frontend/src/components/global_ui/Imginput/ImgInput.module.css: -------------------------------------------------------------------------------- 1 | .container{ 2 | 3 | display : grid; 4 | align-content: flex-start; 5 | justify-items: center; 6 | gap: 0.2em; 7 | width: min(90%,400px); 8 | 9 | } 10 | 11 | 12 | .labels{ 13 | width:100%; 14 | } 15 | 16 | .form_group{ 17 | border: 2px dashed #92b0b3; 18 | height:100px; 19 | border-radius: 8px; 20 | /* text-align: center; */ 21 | display: flex; 22 | justify-content:center; 23 | align-items:center; 24 | flex-direction: column; 25 | cursor: pointer; 26 | padding:0.2em; 27 | } 28 | 29 | .form_group input[type="file"]{ 30 | display:none; 31 | } 32 | 33 | .form_group p{ 34 | font-size:14px; 35 | color:#778899; 36 | } 37 | 38 | .up_msg{ 39 | 40 | font-size:15px; 41 | font-weight: bold; 42 | margin-top:12px; 43 | 44 | } -------------------------------------------------------------------------------- /frontend/src/components/global_ui/buttons/button.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | import React from 'react' 3 | 4 | function Button({ 5 | paddingTB,paddingLR,marginTB,marginLR, 6 | isBlock,isElevated,isRounded, 7 | color,bgColor, 8 | text,fontSize, 9 | customClass, 10 | borderColor,borderWidth, 11 | onClick, 12 | borderRadius 13 | }) { 14 | return ( 15 | 37 | ) 38 | } 39 | 40 | export default Button 41 | -------------------------------------------------------------------------------- /frontend/src/components/global_ui/carousel/carousel.js: -------------------------------------------------------------------------------- 1 | import React,{useState} from 'react' 2 | import styles from './carousel.module.css' 3 | 4 | function Carousel({list,title}) { 5 | const [current, setcurrent] = useState(0); 6 | function forward(){ 7 | if(current!=list.length-1){ 8 | setTimeout(() => { 9 | setcurrent(current+1) 10 | }, 100); 11 | 12 | } 13 | } 14 | function backward(){ 15 | if(current!=0){ 16 | setTimeout(() => { 17 | setcurrent(current-1) 18 | }, 100); 19 | } 20 | } 21 | 22 | return ( 23 |
24 |

{title}

25 | 26 |
27 | backward()} 30 | style={{color:current==0?'lightgray':'black'}} 31 | > 32 |
33 | { 34 | list.map((item)=>{ 35 | return( 36 | list[current]==item && 37 | 38 | ) 39 | }) 40 | } 41 |
42 | 43 | forward()} 46 | style={{color:current==list.length-1?'lightgray':'black'}} 47 | > 48 |
49 |
50 | 51 | ) 52 | } 53 | 54 | 55 | export default Carousel; 56 | -------------------------------------------------------------------------------- /frontend/src/components/global_ui/carousel/carousel.module.css: -------------------------------------------------------------------------------- 1 | .container{ 2 | display: flex; 3 | flex-direction: row; 4 | justify-items: center; 5 | justify-content: center; 6 | align-items: center; 7 | padding: 5px; 8 | margin: 0px; 9 | } 10 | 11 | .carouselImage{ 12 | max-width: 300px; 13 | border-radius: 3px; 14 | } 15 | 16 | .carouselImage::before{ 17 | -webkit-transition: opacity 1s ease-in-out; 18 | -moz-transition: opacity 1s ease-in-out; 19 | -o-transition: opacity 1s ease-in-out; 20 | transition: opacity 1s ease-in-out; 21 | } 22 | 23 | .move{ 24 | height: 30px; 25 | padding:10px 20px; 26 | } -------------------------------------------------------------------------------- /frontend/src/components/global_ui/dialog/dialog.css: -------------------------------------------------------------------------------- 1 | .modal-wrapper { 2 | display: grid; 3 | place-content: center; 4 | background: rgba(0, 0, 0, 0.15); 5 | position: fixed; 6 | top: 0; 7 | left: 0; 8 | z-index: 10; 9 | opacity: 0; 10 | width: 100%; 11 | height: 100%; 12 | animation: fade forwards 200ms; 13 | } 14 | 15 | .modal { 16 | display: grid; 17 | grid-template-columns: 1fr 1fr; 18 | justify-self: center; 19 | margin: auto 0; 20 | width: min(85vw, 400px); 21 | box-shadow: 0 10px 10px rgba(0, 0, 0, 0.185), 0 10px 10px rgba(0, 0, 0, 0.225); 22 | gap: 1em; 23 | align-content: center; 24 | justify-content: center; 25 | margin: 1rem; 26 | padding:2.5em 2.2em; 27 | border-radius: 8px; 28 | background: white; 29 | 30 | } 31 | 32 | 33 | 34 | 35 | @keyframes fadeOut { 36 | to { 37 | opacity: 0; 38 | } 39 | } 40 | 41 | @keyframes fade { 42 | to { 43 | opacity: 1; 44 | } 45 | } 46 | 47 | .modal-close-button { 48 | width: 10ch; 49 | align-self: center; 50 | cursor: pointer; 51 | background-color: var(--secondary); 52 | color: white; 53 | /* box-shadow: 0 0 5px rgba(0, 0, 0, 0.397); */ 54 | box-shadow: 2px 2px 2px grey; 55 | 56 | justify-self: center; 57 | } 58 | .modal-close-button:is(:hover, :focus) { 59 | outline: none; 60 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.397); 61 | } 62 | .modal-wrapper h3 { 63 | color: rgb(211, 61, 111); 64 | margin: 0; 65 | grid-column: 1/-1; 66 | text-align: center; 67 | } 68 | 69 | .modal p { 70 | grid-column: 1/-1; 71 | 72 | text-align: center; 73 | font-size: 16px; 74 | } 75 | .modal button:first-of-type { 76 | background:#e5e5e5; 77 | color: darkslategrey; 78 | box-shadow: 2px 2px 2px grey; 79 | } 80 | .spinnerModal{ 81 | grid-column: 1/-1; 82 | text-align: center; 83 | } 84 | .scale-transition-appear { 85 | transform: scale(0.1); 86 | 87 | } 88 | .scale-transition-appear-active { 89 | transform: scale(1); 90 | transition: transform 200ms; 91 | } 92 | .scale-transition-enter { 93 | transform: scale(0.1); 94 | } 95 | .scale-transition-enter-active { 96 | transform: scale(1); 97 | transition: transform 200ms; 98 | } 99 | .scale-transition-exit { 100 | transform: scale(1); 101 | 102 | } 103 | .scale-transition-exit-active { 104 | transform: scale(0); 105 | transition: transform 150ms; 106 | } -------------------------------------------------------------------------------- /frontend/src/components/global_ui/fade_transition.css: -------------------------------------------------------------------------------- 1 | 2 | .fade-appear { 3 | opacity: 0.2; 4 | z-index: 1; 5 | } 6 | 7 | .fade-appear.fade-appear-active { 8 | opacity: 1; 9 | transition: opacity 200ms ease-in; 10 | 11 | } 12 | .fade-enter { 13 | opacity: 0.2; 14 | z-index: 1; 15 | } 16 | 17 | .fade-enter.fade-enter-active { 18 | opacity: 1; 19 | transition: opacity 200ms ease-in; 20 | 21 | } 22 | 23 | .fade-exit { 24 | display: none; 25 | 26 | } 27 | 28 | .fade-exit-active { 29 | display: none; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /frontend/src/components/global_ui/image_viewer/imageViewer.module.css: -------------------------------------------------------------------------------- 1 | .viewer { 2 | background: rgba(0, 0, 0, 0.15); 3 | position: fixed; 4 | top: 0; 5 | z-index: 10; 6 | left: 0; 7 | display: grid; 8 | opacity: 0; 9 | animation: fade forwards 300ms; 10 | 11 | width: 100%; 12 | height: 100%; 13 | } 14 | .modal { 15 | display: grid; 16 | place-content: center; 17 | gap: 20px; 18 | 19 | transform: scale(0.1); 20 | animation: zoomin forwards 250ms; 21 | } 22 | .viewer img { 23 | width: min(90%, 800px); 24 | 25 | justify-self: center; 26 | } 27 | .viewer i { 28 | font-size: 1.2rem; 29 | justify-self: center; 30 | cursor: pointer; 31 | color: rgb(204, 200, 200); 32 | } 33 | .viewer i:hover{ 34 | transform: scale(1.1); 35 | } 36 | .actions{ 37 | background: rgba(0, 0, 0, 0.845); 38 | padding: 8px 16px; 39 | border-radius: 16px; 40 | justify-self: center; 41 | } 42 | .actions i:first-child{ 43 | margin-right: 16px; 44 | 45 | } 46 | @keyframes zoomin { 47 | to { 48 | transform: scale(1); 49 | } 50 | } 51 | @keyframes fade { 52 | to { 53 | opacity: 1; 54 | } 55 | } -------------------------------------------------------------------------------- /frontend/src/components/global_ui/image_viewer/image_viewer.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | import React from "react"; 3 | import ReactDOM from "react-dom"; 4 | import styles from "./imageViewer.module.css"; 5 | /** 6 | * 7 | * @param viewerData | {show:boolean,src:img url} (from useState hook) 8 | * @param setViewerData | setter for viewerData (basically setState from useState hook) 9 | * @param alt | img alt 10 | * 11 | * @returns Custom img viewer 12 | */ 13 | const ImageViewer = ({ viewerData, setViewerData, alt }) => { 14 | console.log(viewerData); 15 | return viewerData.show 16 | ? ReactDOM.createPortal( 17 |
18 |
19 |
20 | 21 | { 23 | setViewerData({ show: false, src: "" }); 24 | }} 25 | className="fas fa-download" 26 | > 27 | 28 | { 30 | setViewerData({ show: false, src: "" }); 31 | }} 32 | className="fas fa-times-circle" 33 | > 34 |
35 | {alt} 36 |
37 |
, 38 | document.body 39 | ) 40 | : null; 41 | }; 42 | export default ImageViewer; 43 | -------------------------------------------------------------------------------- /frontend/src/components/global_ui/input.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './input.module.css' 3 | 4 | /** 5 | * 6 | * @param {*} props textAlign,size,minLength,value,placeholder,name,type,onChange,error,maxLength are the supported props 7 | * You can pass an error msg to show an error below input field 8 | * @returns a custom styled input component. 9 | */ 10 | const InputField = ({ukey,textAlign,size,minLength,value,placeholder,name,type,onChange,error,maxLength}) => { 11 | 12 | return ( 13 | 14 |
15 | 16 | 17 |
18 | {error && {error}} 19 | {error &&
} 20 |
21 | ); 22 | } 23 | 24 | export default InputField; -------------------------------------------------------------------------------- /frontend/src/components/global_ui/input.module.css: -------------------------------------------------------------------------------- 1 | .inputField { 2 | position : relative; 3 | font-size: 1rem; 4 | height: fit-content; 5 | width: 100%; 6 | --label-top:calc(23%); 7 | } 8 | .label { 9 | position : absolute; 10 | left : 0.6em; 11 | padding-left : 0.2em; 12 | padding-right : 0.2em; 13 | color : #5a5858; 14 | font-size : 1em; 15 | top : var(--label-top); 16 | pointer-events: none; 17 | transition : top 0.2s ease, 18 | font-size 0.2s ease, 19 | color 0.2s ease; 20 | } 21 | 22 | .input { 23 | box-sizing:border-box; 24 | border : 2px solid #ddd; 25 | padding : 0.6em; 26 | font-size : 1.1em; 27 | border-radius: 4px; 28 | width: 100%; 29 | outline : none; 30 | transition : border-color 0.3s ease, 31 | border-color 0.2s ease 32 | ; 33 | 34 | } 35 | 36 | .inputField span{ 37 | position: absolute; 38 | left: 0.4%; 39 | color: red; 40 | font-size: 0.8em; 41 | padding-top: 0.2em; 42 | padding-left: 0.3em; 43 | } 44 | .inputField .normalField{ 45 | --label-top:calc(50% - 10px); 46 | } 47 | .errorField{ 48 | border-color: red; 49 | } 50 | 51 | .errorField +.label{ 52 | color: red; 53 | --label-top:17%; 54 | } 55 | .input:valid+.label, 56 | .input:focus+.label { 57 | font-size : 0.7em; 58 | top : -8px; 59 | color : black; 60 | border-color: black; 61 | background : white; 62 | } 63 | 64 | .input:focus { 65 | border-color: black; 66 | } 67 | .errorField:focus{ 68 | border-color: red; 69 | } 70 | .errorField:valid+.label, 71 | .errorField:focus +.label{ 72 | color: red; 73 | } 74 | .input[type=number]::-webkit-inner-spin-button, 75 | .input[type=number]::-webkit-outer-spin-button { 76 | -webkit-appearance: none; 77 | -moz-appearance: none; 78 | appearance: none; 79 | margin: 0; 80 | } 81 | 82 | textarea { 83 | box-sizing:border-box; 84 | border : 2px solid #ddd; 85 | font-size : 1.1em; 86 | border-radius: 4px; 87 | width: 100%; 88 | outline : none; 89 | transition : border-color 0.3s ease, 90 | border-color 0.2s ease 91 | ; 92 | resize: none; 93 | padding: 10px; 94 | } 95 | textarea:focus{ 96 | border-color: black; 97 | 98 | } 99 | 100 | textarea+label{ 101 | top: 0; 102 | left:1.2em; 103 | text-align: center; 104 | margin-top: 10px; 105 | font-size:0.9em; 106 | padding: 0px; 107 | transition : top 0.2s ease, 108 | font-size 0.2s ease, 109 | color 0.2s ease; 110 | } 111 | 112 | textarea:valid+label,textarea:focus+label{ 113 | position:absolute; 114 | top:-20px; 115 | left: 0.7em; 116 | padding: 0px 2px; 117 | color: black; 118 | height: 15px; 119 | font-size : 0.7em; 120 | 121 | background-color: white; 122 | } 123 | -------------------------------------------------------------------------------- /frontend/src/components/global_ui/logo.css: -------------------------------------------------------------------------------- 1 | 2 | .logo-container{ 3 | display: flex; 4 | justify-content: center; 5 | } 6 | .logo-container img{ 7 | 8 | height: auto; 9 | width: min(50%,250px) 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/components/global_ui/logo.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './logo.css' 3 | /** 4 | * 5 | * @returns ReliefRider's Logo 6 | */ 7 | const Logo = () => { 8 | return ( 9 |
10 | logo 11 |
12 | ); 13 | } 14 | 15 | export default Logo; -------------------------------------------------------------------------------- /frontend/src/components/global_ui/nav.css: -------------------------------------------------------------------------------- 1 | .nav-bar { 2 | width: 100%; 3 | padding: 0.6em; 4 | display: flex; 5 | align-items: center; 6 | background: var(--primary); 7 | justify-content: center; 8 | height: fit-content; 9 | margin-bottom: 0.75rem; 10 | text-align: center; 11 | 12 | letter-spacing: 2px; 13 | font-size: clamp(0.8rem, 4.5vw, 1.2rem); 14 | } 15 | .nav-bar h3 { 16 | margin: 0px; 17 | color: white; 18 | } 19 | .nav-bar i { 20 | padding: 0.5em; 21 | color: white; 22 | position: absolute; 23 | left: 1rem; 24 | font-size: clamp(0.8rem, 4.5vw, 1.2rem); 25 | 26 | margin: 0px; 27 | } 28 | 29 | .backStyle{ 30 | cursor: pointer; 31 | } 32 | -------------------------------------------------------------------------------- /frontend/src/components/global_ui/nav.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | import "./nav.css"; 4 | /** 5 | * 6 | * style : style object : style for nav 7 | * 8 | * back : boolean : displays back button 9 | * 10 | * backStyle : style object : style for back button 11 | * 12 | * title : string : Title for navbar (default:UNSET) 13 | * 14 | * onBackClick : function : Custom function which executes when back is clicked 15 | * @returns Custom Navbar 16 | */ 17 | const Navbar = ({ 18 | style, 19 | titleStyle, 20 | back, 21 | title = "UNSET", 22 | //backStyle = {}, 23 | onBackClick = null, 24 | }) => { 25 | const route = useHistory(); 26 | return ( 27 | 41 | ); 42 | }; 43 | 44 | export default Navbar; 45 | -------------------------------------------------------------------------------- /frontend/src/components/global_ui/overlay.css: -------------------------------------------------------------------------------- 1 | .overlay{ 2 | display: none; 3 | position: fixed; 4 | 5 | top:0; 6 | left:0; 7 | right:0; 8 | bottom:0; 9 | 10 | width: 100%; 11 | height: 100%; 12 | display: grid; 13 | justify-content: center; 14 | overflow:hidden; 15 | background-color: rgba(0,0,0,0.3); /* Black w/ opacity */ 16 | } 17 | 18 | .overlay-content{ 19 | font-size: medium; 20 | font-weight: 500; 21 | background-color: #fefefe; 22 | border: 1px solid grey; 23 | border-radius: 7px; 24 | 25 | padding: 10px 20px 30px; 26 | width:330px; 27 | height: 35%; 28 | 29 | text-align: center; 30 | 31 | position: absolute; 32 | top: 50%; 33 | left: 50%; 34 | transform: translate(-50%, -50%); 35 | 36 | display: grid; 37 | justify-content: center; 38 | align-content:space-around; 39 | 40 | box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); 41 | transition: 0.3s; 42 | } 43 | 44 | /* .btn-container{ 45 | display: flex; 46 | justify-content:flex-end; 47 | align-content: center; 48 | padding: 0px; 49 | margin: 0px; 50 | } */ 51 | 52 | .btn{ 53 | border:none; 54 | outline: none; 55 | background-color: green; 56 | color: white; 57 | padding: 8px 30px; 58 | } 59 | 60 | @media screen and (max-width: 450px) { 61 | .overlay-content{ 62 | height: 25%; 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /frontend/src/components/global_ui/overlay.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function Overlay({message,display,z_index,showOverlay}) { 4 | return ( 5 |
6 |
7 | {/* Shows Dynamic Content for cancelling and confirmation */} 8 |

9 | { 10 | message 11 | } 12 |

13 | 14 | {/* Buttons Performing Operations */} 15 | 16 | 17 | 18 |
19 |
20 | ) 21 | } 22 | 23 | export default Overlay 24 | -------------------------------------------------------------------------------- /frontend/src/components/global_ui/remarks/remarks.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './remarks.module.css' 3 | 4 | const Remarks = ({remarks}) => { 5 | return remarks? ( 6 | <> 7 |

Remarks

8 |
9 | 10 | {remarks} 11 | 12 |
13 | 14 | ):null; 15 | } 16 | 17 | export default Remarks; -------------------------------------------------------------------------------- /frontend/src/components/global_ui/remarks/remarks.module.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght@1,400&display=swap'); 2 | .remarksContainer{ 3 | width: min(90%,400px); 4 | min-height: 70px; 5 | overflow:auto; 6 | border-radius: 8px; 7 | padding: 8px; 8 | height: auto; 9 | font-style: italic; 10 | font-family: 'Lato', sans-serif; 11 | border: 2px solid rgba(128, 128, 128, 0.281); 12 | 13 | } 14 | .remarksText{ 15 | text-align: center; 16 | } -------------------------------------------------------------------------------- /frontend/src/components/global_ui/spinner.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './spinner.module.css' 3 | /** 4 | * 5 | * @param {string} radius radius of spinner 6 | * @returns Spinner component 7 | */ 8 | const Spinner = ({radius}) => { 9 | return ( 10 |
11 | 12 |
13 | ); 14 | } 15 | 16 | const LoadingScreen = () => { 17 | return ( 18 |
19 | 20 |
21 | ); 22 | } 23 | export {Spinner,LoadingScreen}; -------------------------------------------------------------------------------- /frontend/src/components/global_ui/spinner.module.css: -------------------------------------------------------------------------------- 1 | .spinner{ 2 | 3 | display: inline-block; 4 | border: 4px solid rgba(128, 128, 128, 0.459); 5 | border-radius: 50%; 6 | border-top-color: green; 7 | animation: spin 0.8s infinite cubic-bezier(.35,1.34,.87,.47) ; 8 | } 9 | 10 | @keyframes spin { 11 | to { 12 | transform: rotate(360deg); 13 | } 14 | } 15 | 16 | .loadingScreen{ 17 | display: grid; 18 | justify-items: center; 19 | align-items: center; 20 | height: 100vh; 21 | } -------------------------------------------------------------------------------- /frontend/src/components/global_ui/textarea/textArea.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './textArea.module.css' 3 | 4 | function textArea({ 5 | textAlign,size,minLength,value,placeholder,name,type,onChange,error,maxLength, 6 | rows,cols, 7 | width 8 | }) { 9 | return ( 10 |
11 |