├── .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 | You need to enable JavaScript to run this app.
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 |
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 |
45 |
46 |
Admin Login
47 |
48 |
setMobile(e.target.value)}
54 | >
55 | {
56 | error &&
{error}
57 | }
58 |
handleOTP()}>
59 | Request OTP
60 |
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 |
45 |
Enter OTP
46 |
setotp(e.target.value)}
52 | >
53 | {
54 | error &&
{error}
55 | }
56 |
handleLogin()}>
57 | LOGIN
58 |
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 |
28 | Home
29 | Create Request
30 | Assign Request
31 | Create Admin
32 | {
33 | token && (
34 | {
41 | setToken(null)
42 | history.replace('/')
43 | }} >SIGN OUT
44 | )
45 | }
46 |
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 | operation()}>YES
18 | closeOverlay()}>NO
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 |
{placeholder}
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 |
history.push('/login')}>LOGIN
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 | You need to enable JavaScript to run this app.
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 |
25 | {imgHeader}
26 |
36 |
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 |
35 | { text}
36 |
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 |
35 |
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 |
{placeholder}
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 |
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 |
28 | {back && (
29 | {
32 | if (onBackClick !== null) {
33 | onBackClick();
34 | } else route.replace(back);
35 | }}
36 | className="fas fa-chevron-left"
37 | >
38 | )}
39 | {title}
40 |
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 | showOverlay}>OK
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 |
14 |
{placeholder}
15 |
16 | {error &&
{error} }
17 | {error &&
}
18 |
19 | )
20 | }
21 |
22 | export default textArea
23 |
--------------------------------------------------------------------------------
/frontend/src/components/home/initial_home/fetchLeaderBoard.js:
--------------------------------------------------------------------------------
1 | export const fetchLeaderBoard = async () => {
2 | try {
3 | const res = await fetch(
4 | process.env.REACT_APP_URL + "/leaderboard/showLeaderboard",
5 | {
6 | method: "GET",
7 | }
8 | );
9 | const data = await res.json();
10 | console.log(data);
11 | return { status: data.status === "success" ? 1 : -1, data: data.message };
12 | } catch (error) {
13 | return {error:"Unable to access server"}
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/frontend/src/components/home/initial_home/initial-home.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@600&display=swap');.ih-container{
2 | display: grid;
3 | /* background: chocolate; */
4 |
5 | justify-items: center;
6 |
7 | }
8 |
9 | .contents-ih {
10 | display: flex;
11 | gap: 12px;
12 | flex-direction: column;
13 | justify-items: center;
14 | }
15 |
16 |
17 | .loginasrider-btn {
18 | padding: min(4%,1em);
19 | width: 25ch;
20 | margin-bottom: min(4%,0.5em);;
21 | box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.24);
22 |
23 | margin-left: auto;
24 | margin-right: auto;
25 | background: var(--secondary);
26 | display: block;
27 | color:var(--textColor);
28 | text-shadow: 0px 2px rgba(0, 0, 0, 0.116);
29 | outline: none;
30 |
31 | }
32 | .loginasrider-btn:hover{
33 | transition: 500ms;
34 | background: var(--tertiary);
35 | color:var(--secondary);
36 | box-shadow: none;
37 | outline: none;
38 | border: 1px solid grey;
39 | }
40 |
41 | .ih-container p{
42 | text-align: center;
43 | font-size: min(2.1em,3.2vh);
44 | margin: 0px;
45 | padding-bottom: 0.4em;
46 | }
47 | .aboutUs{
48 | width: min(50ch,90%);
49 | font-size: 1.2rem;
50 | background: rgba(224, 224, 224, 0.479);
51 | text-align: center;
52 | border-radius: 10px;
53 | padding: 16px;
54 | font-family: 'Quicksand', sans-serif;
55 | align-self: center;
56 | margin-bottom: 16px;
57 | }
58 |
--------------------------------------------------------------------------------
/frontend/src/components/home/initial_home/initial_home.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import Logo from "../../global_ui/logo";
3 | import TopBanner from "./top-banner";
4 | import { LeaderBoard } from "./leaderboard";
5 |
6 | import "./initial-home.css";
7 | import { Link, useHistory } from "react-router-dom";
8 | import { AuthContext } from "../../context/auth/authProvider";
9 | const InitialHome = () => {
10 | const routes = useHistory();
11 | const { dispatch } = useContext(AuthContext);
12 | const goToLoginRider = () => {
13 | dispatch({
14 | type: "ISRIDER",
15 | payload: null,
16 | });
17 | routes.push("/login/rider");
18 | };
19 | const goToLoginRequester = () => {
20 | routes.push("/login/requester");
21 | };
22 |
23 | return (
24 |
25 |
26 |
27 |
28 |
29 | The Relief Riders initiative is a voluntary effort by a group of
30 | cyclists to support the needs of the elderly and the needy in their
31 | local communities. Know more
32 |
33 |
34 |
How can we help you?
35 |
36 |
37 | Login as Requester
38 |
39 |
40 | Login as Rider
41 |
42 |
43 |
44 |
45 |
46 | );
47 | };
48 |
49 | export default InitialHome;
50 |
--------------------------------------------------------------------------------
/frontend/src/components/home/initial_home/leaderboard.css:
--------------------------------------------------------------------------------
1 | .leaderboard-container {
2 | background: var(--primary);
3 | width: min(90%,30rem);
4 | margin-left: auto;
5 | height:auto;
6 | margin-bottom: 25px;
7 | margin-right: auto;
8 | border-radius: 4px;
9 | }
10 |
11 | .leaderboard-title {
12 | display : block;
13 |
14 | padding-top: 1em;
15 | padding-bottom: 0.5em;
16 | font-size: min(2em,3vh);
17 |
18 | text-align: center;
19 | }
20 | .pn-space{
21 | display: flex;
22 | gap: 0.5em;
23 | align-items: center;
24 | }
25 | .pn-space svg{
26 | padding-bottom: 0.1em;
27 | }
28 | .leaderboard-user {
29 | padding : 0.6em;
30 | margin : 0.5em;
31 | border-radius: 4px;
32 | display : grid;
33 | align-items : center;
34 | box-shadow : 0px 8px 5px rgba(0, 0, 0, 0.281);
35 | background : white;
36 | border : 1px solid black;
37 | grid-auto-flow: column;
38 | }
39 |
40 | .dc {
41 | display : flex;
42 | text-align : center;
43 | justify-self: end;
44 | color : darkslategrey;
45 | flex-flow : column;
46 | }
47 |
--------------------------------------------------------------------------------
/frontend/src/components/home/initial_home/leaderboard.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable react/no-unescaped-entities */
2 | import React, { Fragment, useEffect, useState } from 'react';
3 | import {Spinner} from '../../global_ui/spinner';
4 | import { fetchLeaderBoard } from './fetchLeaderBoard';
5 | import './leaderboard.css'
6 | export const LeaderBoard = () => {
7 | const [data,setData] = useState([])
8 | const [error,setError] = useState(null)
9 | useEffect(async()=>{
10 | const res = await fetchLeaderBoard()
11 | if(res.error){
12 | setError(res.error)
13 | return
14 | }
15 | if(res.status === 1 ){
16 | if(res.data.length === 0){
17 | setError("No top riders this week")
18 | }
19 | setData(res.data)
20 | }else{
21 | setError(res.data)
22 | }
23 |
24 |
25 | },[])
26 |
27 | if(data){
28 | return (
29 | <>
30 | This Week's Top Riders
31 | {
32 | error ? {error}
:
33 |
34 | {
35 | data.map((item,i)=> )
36 | }
37 |
38 | }
39 | >
40 | );
41 | }else{
42 | return ( );
43 | }
44 |
45 | }
46 |
47 |
48 | const LeaderBoardUser = ({position,name,deliveryCount}) => {
49 | return (
50 |
51 |
52 | #{position}
53 |
54 |
55 |
56 |
57 |
58 | {name}
59 |
60 |
61 | {deliveryCount}
62 | deliveries
63 |
64 |
65 | );
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/frontend/src/components/home/initial_home/top-banner.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './topbanner.css'
3 | const TopBanner = () => {
4 | return (
5 |
6 |
Difficulty in using the app?
7 |
11 |
12 | );
13 | }
14 |
15 | export default TopBanner;
--------------------------------------------------------------------------------
/frontend/src/components/home/initial_home/topbanner.css:
--------------------------------------------------------------------------------
1 | .top-banner {
2 | display : flex;
3 | padding : 0.5em 1em;
4 | margin-bottom : 1rem;
5 | width: 100%;
6 | height : max-content;
7 | font-size: 1.2rem;
8 | align-items : center;
9 | gap : 1em;
10 | justify-content: center;
11 | font-weight: bold;
12 | background : var(--primary);
13 | color: var(--textColor)
14 | ;
15 | }
16 |
17 | .call-btn {
18 | cursor : pointer;
19 | padding : 0.2em;
20 | border: none;
21 | border-radius: 8px;
22 | /* box-shadow: 0px 2px 7px rgba(0, 0, 0, 0.192); */
23 | display : flex;
24 | width : fit-content;
25 |
26 | }
27 | .call-btn > a {
28 | text-decoration: none;
29 | color: var(--textColor);
30 | }
31 | .call-btn > i {
32 | padding-left: 4px;
33 | align-self: center;
34 | }
35 | .nnn {
36 | padding-left: 0.5em;
37 | align-self : center;
38 | }
--------------------------------------------------------------------------------
/frontend/src/components/home/readme.md:
--------------------------------------------------------------------------------
1 | Must Provide
2 |
3 | 1. Homescreen for Rider.
4 | 2. HomeScreen for requester.
5 | (Basically we insert all other components here)
--------------------------------------------------------------------------------
/frontend/src/components/home/requester/RequesterHomeScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 | import { useHistory } from "react-router-dom";
3 | import "./style.css";
4 | import Logo from "../../global_ui/logo";
5 | import Navbar from "../../global_ui/nav";
6 | import { AuthContext } from "../../context/auth/authProvider";
7 | import { logout } from "../../context/auth/authOperations";
8 |
9 | function RequesterHomeScreen() {
10 | const history = useHistory();
11 | const { dispatch } = useContext(AuthContext);
12 |
13 | const routehandler = (route) => {
14 | history.push(route);
15 | };
16 |
17 | return (
18 |
19 |
20 |
21 |
22 | routehandler("new_request")} className="rider-home-btn">
23 |
24 | Place new Request
25 |
26 | routehandler("my_requests")} className="rider-home-btn">
27 |
28 | My Requests
29 |
30 |
31 | routehandler("my_profile")} className="rider-home-btn">
32 |
33 | My Profile
34 |
35 | {
37 | logout(dispatch);
38 | history.replace("/");
39 | }}
40 | className="rider-home-btn"
41 | >
42 |
43 | Logout
44 |
45 |
46 |
47 | );
48 | }
49 |
50 | export default RequesterHomeScreen;
51 |
--------------------------------------------------------------------------------
/frontend/src/components/home/requester/routes.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Route, Switch } from "react-router";
3 | import RequesterHomeScreen from "./RequesterHomeScreen";
4 | import { BrowserRouter } from "react-router-dom";
5 | import MyRequestsRoutes from "../../requester/my_requests/my_requests_routes";
6 | import NewRequestRoutes from "../../requester/new_request/new_request_routes";
7 | import RequesterProfileRoutes from "../../requester/profile/profileRouting";
8 | import { NewRequestProvider } from "../../context/new_request/newRequestProvider";
9 |
10 |
11 | const RequesterHomeRoutes = () => {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | );
35 | };
36 |
37 | export default RequesterHomeRoutes;
38 |
--------------------------------------------------------------------------------
/frontend/src/components/home/requester/style.css:
--------------------------------------------------------------------------------
1 | .riderhome{
2 | display: flex;
3 | flex-flow: column;
4 | gap: min(2vh,20px);
5 | align-self: flex-start;
6 |
7 | font-size: min(4.5vw,1.25rem);
8 | }
9 | .rider-home-container{
10 | height: 100vh;
11 | display: grid;
12 | align-content: flex-start;
13 | }
14 |
15 | .rider-home-btn{
16 | width: min(80%,400px);
17 | position: relative;
18 | text-transform: uppercase;
19 |
20 | padding: min(3vh,1.2em);
21 | font-weight: bold;
22 | }
23 | .rider-home-btn:focus , .rider-home-btn:hover{
24 | box-shadow: rgba(100, 100, 111, 0.3) 0px 12px 29px 0px;
25 |
26 | }
27 |
28 | .rider-home-btn i{
29 | position: absolute;
30 | left: 9%;
31 |
32 | width: max-content;
33 | }
34 |
--------------------------------------------------------------------------------
/frontend/src/components/home/rider/RiderHome.js:
--------------------------------------------------------------------------------
1 | import React, { useContext } from "react";
2 |
3 | import { AuthContext } from "../../context/auth/authProvider";
4 | import { logout } from "../../context/auth/authOperations";
5 |
6 | // import "../requester/style.css";
7 | import { useHistory } from "react-router-dom";
8 | import Logo from "../../global_ui/logo";
9 | import Navbar from "../../global_ui/nav";
10 |
11 | function RiderHome() {
12 | const history = useHistory();
13 | const { dispatch } = useContext(AuthContext);
14 |
15 | const routehandler = (route) => {
16 | history.push(route);
17 | };
18 |
19 | return (
20 |
21 |
22 |
23 |
24 | routehandler("/new_delivery")} className="rider-home-btn">
25 |
26 | Make New Delivery
27 |
28 | routehandler("/current_request")} className="rider-home-btn">
29 |
30 | Current Request
31 |
32 | routehandler("/my_deliveries")} className="rider-home-btn">
33 |
34 | My Deliveries
35 |
36 | routehandler("/my_profile")} className="rider-home-btn">
37 |
38 | My Profile
39 |
40 | {
42 | logout(dispatch);
43 | history.replace("/");
44 | }}
45 | className="rider-home-btn"
46 | >
47 |
48 | Logout
49 |
50 |
51 |
52 | );
53 | }
54 |
55 | export default RiderHome;
56 |
--------------------------------------------------------------------------------
/frontend/src/components/home/rider/routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Route, Switch } from 'react-router-dom';
3 | import { BrowserRouter } from 'react-router-dom';
4 | import RiderHome from './RiderHome';
5 | import RiderProfileRoutes from "../../rider/profile/profileRouting";
6 | import { CurrentRequest } from '../../rider/current_request/current_request';
7 | import MyDeliveryRoutes from '../../rider/my_deliveries/my_delivery_routes';
8 |
9 | import ChooseRequestRoutes from '../../rider/make_delivery/ChooseRequestRoutes';
10 |
11 | const RiderHomeRoutes = () => {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | );
38 | }
39 |
40 | export default RiderHomeRoutes;
--------------------------------------------------------------------------------
/frontend/src/components/requester/feedback/feedBackForm.css:
--------------------------------------------------------------------------------
1 | .feedBackForm{
2 | width: min(500px,90%);
3 | margin: 40px auto;
4 | }
5 |
6 | .radioButton{
7 | display: flex;
8 | justify-content: space-between;
9 | align-items: flex-start;
10 | font-size: large;
11 | margin-bottom: 14px;
12 | }
13 |
14 |
15 | .radioButton>span{
16 | white-space: nowrap;
17 | }
--------------------------------------------------------------------------------
/frontend/src/components/requester/my_requests/MyRequests.module.css:
--------------------------------------------------------------------------------
1 | .myRequestsList
2 | {
3 | margin-top: 30px;
4 | min-height: 100vh;
5 | text-align: center;
6 | margin-left: auto;
7 | margin-right: auto;
8 | justify-items: center;
9 | }
10 |
11 | .myRequestsListItem{
12 | padding: 15px 20px;
13 | min-height: 200px;
14 | margin-bottom: 20px;
15 | margin-left: auto;
16 | margin-right: auto;
17 | row-gap: 3px;
18 | display: flex;
19 | flex-direction: column;
20 | align-content: center;
21 | justify-content:space-evenly;
22 | font-size: clamp(0.7rem, 1vw, 1.2rem);
23 | width: min(90%, 550px);
24 | border-radius: 4px;
25 | box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
26 | }
27 |
28 | .rowOne{
29 | display: flex;
30 | flex-direction: row;
31 | justify-content:space-between;
32 | align-content: center;
33 | }
34 |
35 |
36 | .rowTwo{
37 | display: flex;
38 | flex-direction: row;
39 | justify-items: start;
40 | align-items: center;
41 | }
42 |
43 |
44 | .myRequestsListItem:hover{
45 | box-shadow: rgba(0, 0, 0, 0.25) 0px 14px 28px, rgba(0, 0, 0, 0.22) 0px 10px 10px;
46 | cursor: pointer;
47 | }
48 | .date
49 | {
50 | color: black;
51 | font-size: 15px;
52 | width: 50%;
53 | text-align: start;
54 | }
55 | .requestType
56 | {
57 | font-weight: bold;
58 | background-color: rgb(167, 255, 255);
59 | border-radius: 20px;
60 | color: black;
61 | padding: 5px 10px;
62 | outline: none;
63 | }
64 | .covidStatus
65 | {
66 | font-weight: bold;
67 | color: black;
68 | text-align: center;
69 | border-radius: 20px;
70 | background-color: rgb(167, 255, 255);
71 | padding: 5px 10px;
72 | }
73 | .medicines
74 | {
75 | margin-right:4px;
76 | color: white;
77 | border-radius: 20px;
78 | background-color: darkmagenta;
79 | padding: 5px 10px;
80 |
81 | }
82 | .groceries
83 | {
84 | margin-right: 4px;
85 | color: white;
86 | border-radius: 20px;
87 | background-color: darkmagenta;
88 | padding: 5px 10px;
89 |
90 | }
91 | .misc
92 | {
93 | margin-right: 4px;
94 | color: white;
95 | background-color: darkmagenta;
96 | border-radius: 20px;
97 | padding: 5px 10px;
98 |
99 | }
100 |
101 | .viewButton{
102 | display: none;
103 | margin: auto;
104 | padding: 3px;
105 | border-radius: 10px;
106 | border: 1px solid black;
107 | box-shadow: 0 0 2px black;
108 | }
109 | .ok
110 | {
111 | color: green;
112 | font-weight: bold;
113 | }
114 | .notOk
115 | {
116 | color: red;
117 | font-weight: bold;
118 | }
119 |
120 | .noRequests{
121 | text-align: center;
122 | margin-top: 45vh;
123 | font-weight: 600;
124 | color: #242323;
125 |
126 |
127 | }
--------------------------------------------------------------------------------
/frontend/src/components/requester/my_requests/MyRequestsListItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useHistory } from 'react-router-dom';
3 | import styles from './MyRequests.module.css';
4 |
5 | const MyRequestsListItem = (props) => {
6 |
7 | const routes = useHistory()
8 | return (
9 | {
10 | routes.push("my_requests/" + props.data.requestNumber, {
11 | request: props.data
12 | })
13 | }}
14 | className={styles.myRequestsListItem}>
15 |
16 | Order Date : {props.data.date}
17 | Status
18 | {
19 | (props.data.requestStatus == "DELIVERED" || props.data.requestStatus == "UNDER DELIVERY") ?
20 | ({" "+props.data.requestStatus} ) :
21 | ({" "+props.data.requestStatus} )
22 | }
23 |
24 |
25 |
26 |
27 | Request Type
28 | {props.data.requestType}
29 |
30 |
31 |
32 | Requester Status
33 | {props.data.requesterCovidStatus ?"COVID+":"COVID-FREE"}
34 |
35 |
36 |
37 | Ordered Items
38 |
39 | {
40 | props.data.itemCategories.map((category, index) => {
41 | switch (category) {
42 | case 'MEDICINES':
43 | return Medcines ;
44 | case 'GROCERIES':
45 | return Groceries ;
46 | case 'MISC':
47 | return Misc. ;
48 | }
49 | })
50 | }
51 |
52 |
53 |
54 |
55 | )
56 | }
57 |
58 | export default MyRequestsListItem
--------------------------------------------------------------------------------
/frontend/src/components/requester/my_requests/my_requests_routes.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Redirect, Route, Switch } from "react-router";
3 | import FeedbackForm from "../feedback/feedbackForm";
4 | import MyRequests from "./MyRequests";
5 | import PlacedRequest from "./placed_request/placed_request";
6 | const MyRequestsRoutes = () => {
7 | return (
8 |
9 |
10 |
11 |
12 | {
15 | if(!match.location.state) return
16 | return ;
17 | }}
18 | />
19 |
20 |
21 |
22 |
23 |
24 | );
25 | };
26 |
27 | export default MyRequestsRoutes;
28 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/my_requests/placed_request/cancel_confirm_request.js:
--------------------------------------------------------------------------------
1 | const cancelConfirmRequest = async (token, requestID,cancel=false) => {
2 | try {
3 | let url
4 | if(cancel)
5 | url = `${process.env.REACT_APP_URL}/requester/cancelRequest/${requestID}`;
6 | else
7 | url = `${process.env.REACT_APP_URL}/requester/confirmRequest/${requestID}`;
8 | console.log(url);
9 | const res = await fetch(url, {
10 | method: "GET",
11 | headers: {
12 | authorization: "Bearer " + token,
13 | },
14 | });
15 |
16 | const data = await res.json();
17 | console.log(data);
18 | if (data.status[0] === "s") {
19 | return 1;
20 | } else {
21 | return data.message;
22 | }
23 | } catch (error) {
24 | return "Unable to access server, please try again later";
25 | }
26 | };
27 | export default cancelConfirmRequest;
28 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/my_requests/placed_request/items_requested_list.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | const ItemsRequestedList = ({ list, category,styles }) => {
3 | return (
4 |
5 |
Items Requested
6 |
7 |
8 | {category.map((cat) => (
9 |
13 | {cat}
14 |
15 | ))}
16 |
17 |
18 | {list.map((item) => (
19 |
20 |
26 | {item.itemName}
27 |
28 | {item.quantity}
29 |
30 | ))}
31 |
32 |
33 | );
34 | };
35 | export default ItemsRequestedList
--------------------------------------------------------------------------------
/frontend/src/components/requester/my_requests/placed_request/placed_request.module.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css2?family=Lato:wght@700&display=swap');
2 |
3 | .container {
4 | display: grid;
5 | align-content: flex-start;
6 | justify-items: center;
7 | gap: 0.2em;
8 | }
9 |
10 | .container p:first-child {
11 | font-size: clamp(1.2rem, 3.5vw, 2.5rem);
12 | }
13 |
14 | p {
15 | margin: 0px;
16 | }
17 | .address {
18 | background: rgba(141, 139, 139, 0.048);
19 | display: flex;
20 | flex-flow: column;
21 | color: rgb(62, 62, 63);
22 | border-radius: 12px;
23 | padding: 0.5em;
24 | margin-top: 0.2em;
25 | box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.137);
26 | margin-bottom: 0.5em;
27 | }
28 |
29 | .address a{
30 | text-align: center;
31 | color: blue;
32 | }
33 |
34 | .address span:first-child {
35 | font-weight: bold;
36 | }
37 | .addressContainer {
38 | margin-top: 0.5em;
39 | width: min(90%, 400px);
40 | }
41 | .category span {
42 | width: fit-content;
43 | font-size: clamp(0.8rem, 1.2vw, 1rem);
44 | margin-right: 0.6em;
45 | padding: 0.3em 0.6em;
46 | font-family: 'Lato', sans-serif;
47 | border-radius: 1rem;
48 | }
49 | .catGreen {
50 | background:#266A63 ;
51 | color:white ;
52 |
53 | }
54 | .catGray {
55 | background: #6f823a;
56 | color: white;
57 | }
58 | .miscColor{
59 | background:#e9e9e9;
60 | color: black;
61 | }
62 |
63 | .requested {
64 | display: grid;
65 | justify-items: center;
66 | margin: 0.5em 0em;
67 | gap: 0.5em;
68 | }
69 | .items {
70 | flex-direction: column;
71 | }
72 | .items > div{
73 |
74 | display: flex;
75 | gap: 24px;
76 | justify-content: space-between;
77 | }
78 |
79 |
80 | .buttonsContainer {
81 | display: grid;
82 | gap: 16px;
83 | padding: 32px;
84 | grid-template-columns: repeat(auto-fit,minmax(150px,1fr));
85 | justify-content: space-around;
86 | margin: 1rem;
87 | width: min(100%, 400px);
88 | }
89 | .buttonsContainer button {
90 | color: white;
91 | padding: 0.6em;
92 | font-family: 'Lato', sans-serif;
93 | cursor: pointer;
94 | box-shadow: 0 1px 12px rgba(0, 0, 0, 0.263);
95 | transition: transform 70ms, outline 70ms;
96 | }
97 | .buttonsContainer button:hover,
98 | button:focus {
99 | transform: scale(1.02);
100 | outline: 2px solid black;
101 | border-radius: 4px;
102 | box-shadow: 0 10px 10px rgba(0, 0, 0, 0.263);
103 | }
104 | /* .buttonsContainer button:first-child {
105 | background:var(--primary);
106 | }
107 | .buttonsContainer button:last-child {
108 | background:var(--primary)
109 | } */
110 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/my_requests/placed_request/request_images.js:
--------------------------------------------------------------------------------
1 | import React,{useState} from "react";
2 | import ImageViewer from "../../../global_ui/image_viewer/image_viewer";
3 |
4 | import styles from "./request_images.module.css";
5 | const RequestImages = ({ bills, images, items = [] }) => {
6 |
7 | const [imageViewerData, setImageViewerData] = useState({
8 | show: false,
9 | src: "",
10 | });
11 |
12 | const onImageClicked = (src) => {
13 | console.log(src);
14 | setImageViewerData({ show: true, src: src });
15 | };
16 |
17 | return (
18 |
19 |
24 |
25 | {
26 | items.map((link) => {
27 | console.log(10,link);
28 | return (
29 |
31 | onImageClicked(link)
32 | }
33 | className={styles.singleImage}
34 | key={link}
35 | >
36 |
37 |
Items
38 |
39 | );
40 | })
41 | }
42 | {
43 | bills.map((link, index) => (
44 |
46 | onImageClicked(process.env.REACT_APP_URL + link)
47 | }
48 | className={styles.singleImage}
49 | key={link}
50 | >
51 |
52 |
Bill #{index + 1}
53 |
54 | ))
55 | }
56 | {
57 | images.map((link) => (
58 |
60 | onImageClicked(process.env.REACT_APP_URL + link)
61 | }
62 | className={styles.singleImage}
63 | key={link}
64 | >
65 |
66 |
Rider-Selfie
67 |
68 | ))
69 | }
70 |
71 | );
72 | };
73 | export default RequestImages;
74 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/my_requests/placed_request/request_images.module.css:
--------------------------------------------------------------------------------
1 | .imagesContainer {
2 | display: grid;
3 | justify-items: center;
4 | margin-top: 1em;
5 | width: min(90vw, 550px);
6 | gap: 0.5em;
7 | grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
8 | }
9 | .singleImage {
10 | width: fit-content;
11 | display: flex;
12 | flex-direction: column;
13 | align-items: center;
14 | gap: 0.3em;
15 | }
16 |
17 | .singleImage img {
18 | height: auto;
19 | box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.137);
20 |
21 | border-radius: 8px;
22 | width: clamp(160px, 50%, 200px);
23 | }
24 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/new_request/ItemListType.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useHistory } from "react-router-dom";
3 | import ItemListTypeCSS from "./ItemListType.module.css";
4 | import Navbar from "../../global_ui/nav";
5 |
6 | function ListType() {
7 | const history = useHistory();
8 | const routehandler = (route) => {
9 | history.push(route);
10 | };
11 |
12 | return (
13 |
14 |
15 |
16 |
17 |
18 | Please choose the items you want to request
19 |
20 | routehandler("add_image")}
22 | >
23 | Upload Image
24 |
25 | or
26 | routehandler("enter_items")}
28 | >
29 | Enter Items
30 |
31 |
32 |
33 | );
34 | }
35 | export default ListType;
36 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/new_request/ItemListType.module.css:
--------------------------------------------------------------------------------
1 | .container{
2 | display: grid;
3 |
4 | padding: 1rem;
5 | justify-items: center;
6 | align-items: center;
7 | grid-template-rows: repeat(4,100px);
8 | }
9 |
10 |
11 | .container span{
12 | text-align: center;
13 | font-size: 1.2rem;
14 | }
15 | .container button{
16 |
17 | width: 20ch;
18 | font-size: clamp(1.2rem,3.5vw,1.5rem);
19 | background-color: var(--secondary);
20 | border-radius: 20px;
21 | padding: 10px 20px;
22 | cursor: pointer;
23 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.350);
24 | }
25 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/new_request/NewRequestType.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useHistory } from "react-router-dom";
3 | import { useContext } from "react/cjs/react.development";
4 | import { NewRequestContext } from "../../context/new_request/newRequestProvider";
5 | import Logo from "../../global_ui/logo";
6 | import Navbar from "../../global_ui/nav";
7 | import rtstyles from "./NewRequestType.module.css";
8 |
9 | function RequestType() {
10 | const history = useHistory();
11 | const { dispatch } = useContext(NewRequestContext);
12 | const routehandler = (route) => {
13 | history.push("new_request/" + route);
14 | };
15 |
16 | return (
17 |
18 |
19 |
20 |
21 |
22 | {
25 | dispatch({ type: "REQUEST_TYPE", payload: "general" });
26 | routehandler("list_type");
27 | }}
28 | >
29 | General Request
30 |
31 | {
34 | const route = "list_type";
35 | dispatch({ type: "REQUEST_TYPE", payload: "p&d" });
36 |
37 | routehandler(route);
38 | }}
39 | >
40 | Pickup / Drop
41 |
42 |
43 |
44 | );
45 | }
46 |
47 | export default RequestType;
48 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/new_request/NewRequestType.module.css:
--------------------------------------------------------------------------------
1 | .rcontainer{
2 | display: grid;
3 | justify-items:center;
4 | margin:40px auto;
5 |
6 | gap: 1rem;
7 | width: min(90%, 500px);
8 | }
9 | .btn{
10 | margin-top: 10px;
11 | width: min(30ch,90%);
12 | font-weight: 600;
13 | background:var(--secondary);
14 | padding: 1em 2em;
15 | font-size: clamp(1.3rem,3vw,1.4rem);
16 | }
17 | .btn:hover{
18 | box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
19 | }
--------------------------------------------------------------------------------
/frontend/src/components/requester/new_request/choose_address.module.css:
--------------------------------------------------------------------------------
1 | .form {
2 | display: grid;
3 | justify-items: center;
4 |
5 | }
6 |
7 | .inputs {
8 | display: grid;
9 | justify-items: center;
10 | width: min(90%,var(--max-width));
11 | gap: 16px;
12 | grid-template-areas: 'address address'
13 | 'city area'
14 | ;
15 | margin-bottom: 50px;
16 | }
17 | .inputs > div:nth-child(1){
18 | grid-area: address;
19 | }
20 |
21 | .locationBtn {
22 | padding: 0.5em 1em;
23 | font-weight: bold;
24 | border: 0.8px solid lightpink;
25 | color: black;
26 | background: white;
27 | }
28 |
29 | .btnProceed {
30 | padding: 0.65em 1em;
31 | width: min(250px, 30%);
32 | background-color: var(--secondary);
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/new_request/enter_items_form/requestItem.module.css:
--------------------------------------------------------------------------------
1 | .buttonArea {
2 | display: flex;
3 | gap: 10vw;
4 | justify-content: center;
5 | }
6 |
7 | .buttonArea button {
8 | width: 15ch;
9 | padding: 10px 10px;
10 | font-weight: 800;
11 | }
12 |
13 | .btn{
14 | padding:10px 30px;
15 | }
16 |
17 | .checkBoxArea {
18 | display: flex;
19 | justify-content: center;
20 | gap: min(50px, 10%);
21 | margin-bottom: 2.5rem;
22 | width: 100%;
23 | }
24 |
25 | .checkBoxArea input[type="checkbox"] {
26 | width: 16px;
27 | height: 16px;
28 | }
29 |
30 | .inputArea {
31 | margin-top: 1rem;
32 | display: grid;
33 | margin-bottom: 2rem;
34 | gap: 16px;
35 | grid-auto-columns: min(90%,var(--max-width));
36 | justify-items: center;
37 | justify-content: center;
38 | }
39 |
40 | .inputArea button {
41 | width: 10ch;
42 | padding: 4px 12px;
43 | }
44 |
45 | .card {
46 | display: grid;
47 | grid-template-columns: 5fr 1fr;
48 | font-size: clamp(1rem, 2.2vw, 1.2rem);
49 | justify-content: space-between;
50 | width: 100%;
51 | background-color:rgba(224, 224, 224, 0.479);
52 | border-radius: 20px;
53 | box-shadow: 0px 0px 10px 2px rgba(37, 6, 6, 0.15),
54 | 0px 0px 5px 1px rgba(31, 26, 26, 0.08), 0px 0px 3px rgba(0, 0, 0, 0.05);
55 | padding: 0.9em;
56 | }
57 |
58 | .card i {
59 | cursor: pointer;
60 | }
--------------------------------------------------------------------------------
/frontend/src/components/requester/new_request/new_request_routes.js:
--------------------------------------------------------------------------------
1 | import React, { useRef } from "react";
2 | import { Switch, Route } from "react-router";
3 | import ListType from "./ItemListType";
4 | import { CSSTransition } from "react-transition-group";
5 |
6 | import UploadImages from "../../requester/new_request/upload_images";
7 | import EnterItemsForm from "../new_request/enter_items_form/enterItemsForm";
8 | import ChooseAddress from "./choose_address";
9 | import ConfirmRequestGeneral from "../../requester/confirm_request/generalRequestConfirm";
10 | import ConfirmRequestPD from "../../requester/confirm_request/pdRequestConfirm";
11 | import Map from "../../requester/new_request/maps/map";
12 | import RequestType from "../../requester/new_request/NewRequestType";
13 | import { useEffect } from "react";
14 | import { useHistory, useLocation } from "react-router-dom";
15 | import "../../global_ui/fade_transition.css";
16 |
17 | const NewRequestRoutes = () => {
18 | const history = useHistory();
19 | const nodeRef = useRef(null)
20 | const location = useLocation();
21 | useEffect(() => {
22 | const draft = history.location.pathname;
23 | const lastPath = localStorage.getItem("draft");
24 | if (draft === "/new_request") history.replace("/new_request");
25 | else if (lastPath) history.replace(lastPath, history.location.state);
26 | }, []);
27 | return (
28 |
29 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | );
72 | };
73 |
74 | export default NewRequestRoutes;
75 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/profile/RequesterProfile.module.css:
--------------------------------------------------------------------------------
1 | .requesterProfileContainer {
2 | display: grid;
3 | grid-gap: 8px;
4 | justify-items: center;
5 | margin-bottom: 10px;
6 | }
7 |
8 | .requesterProfileContainer label {
9 | font-weight: bold;
10 | align-self: start;
11 | position: relative;
12 | color: darkslategray;
13 | }
14 |
15 | .profileImage {
16 | border: 1px solid black;
17 | background-color: pink;
18 | margin: 3px;
19 |
20 | justify-self: center;
21 | border-radius: 50%;
22 | width: min(50%, 150px);
23 | }
24 | .profileDummy {
25 | position: relative;
26 | width: min(35vw, 150px);
27 | height: min(35vw, 150px);
28 | background: var(--secondary);
29 | border-radius: 50%;
30 | margin: 0 0 8px 0;
31 | }
32 | .profileDummy::before {
33 | content: "Profile";
34 | position: absolute;
35 | top: 50%;
36 | left: 50%;
37 | transform: translate(-50%, -50%);
38 | }
39 |
40 | .requesterProfileContainer span {
41 | padding: 8px;
42 | width: min(85%, var(--max-width));
43 | box-sizing: border-box;
44 | border: 2px solid #ddd;
45 | font-size: 16px;
46 | border-radius: 6px;
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/profile/editRequesterProfile.module.css:
--------------------------------------------------------------------------------
1 | .requesterProfileContainer {
2 | display: grid;
3 | gap: 10px;
4 | justify-items: center;
5 | margin-bottom: 10px;
6 | }
7 |
8 | .profileImage {
9 | border: 1px solid black;
10 | background-color: pink;
11 | margin: 3px;
12 |
13 | justify-self: center;
14 | border-radius: 50%;
15 | width: min(50%, 150px);
16 | }
17 | .profileDummy {
18 | position: relative;
19 | width: min(35vw, 150px);
20 | height: min(35vw, 150px);
21 | background: var(--secondary);
22 | border-radius: 50%;
23 | margin: 0 0 8px 0;
24 | }
25 | .profileDummy::before {
26 | content: "Profile";
27 |
28 | position: absolute;
29 | top: 50%;
30 | left: 50%;
31 | transform: translate(-50%, -50%);
32 | }
33 |
34 | .editProfileForm {
35 | display: grid;
36 | grid-gap: 10px;
37 | width: min(90%, var(--max-width));
38 | }
39 | .editProfileForm > div {
40 | width: min(100%, var(--max-width));
41 | }
42 |
43 | .address {
44 | display: grid;
45 | gap: 8px;
46 |
47 | margin-bottom: 1rem;
48 | grid-template-areas:
49 | "completeAddress completeAddress"
50 | "area city";
51 | }
52 | .address div:first-of-type {
53 | padding: 0;
54 | margin: 0;
55 | }
56 | .city {
57 | grid-area: city;
58 | }
59 | .city > div {
60 | width: 100%;
61 | }
62 | .area {
63 | grid-area: area;
64 | }
65 | .completeAddress {
66 | grid-area: completeAddress;
67 | }
68 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/profile/profileRouting.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Route, Switch } from 'react-router-dom';
3 | import RequesterProfile from './RequesterProfile'
4 | import EditRequesterProfile from './editRequesterProfile'
5 |
6 | function ProfileRouting() {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | )
18 | }
19 |
20 | export default ProfileRouting
21 |
--------------------------------------------------------------------------------
/frontend/src/components/requester/readme.md:
--------------------------------------------------------------------------------
1 | Must provide
2 |
3 | 1. My requests screen
4 | 2. New_request screen (order form) and it's functions
5 | 3. Profile screen
6 | 4. Respective functions of above screens. (like in profile screen, ability to change name....etc)
--------------------------------------------------------------------------------
/frontend/src/components/rider/common/viewRequest.module.css:
--------------------------------------------------------------------------------
1 | .currentRequestContent{
2 | display: grid;
3 | align-content: flex-start;
4 | justify-items: center;
5 | /* width: min(90%,var(--maxselect-width)); */
6 | }
7 |
8 | .select {
9 | width: min(300px,90%);
10 |
11 | }
12 | .categories{
13 | display: flex;
14 | margin-top: 8px;
15 | gap: 8px;
16 | justify-content: start;
17 | align-items: center;
18 | }
19 |
20 | .currentRequestContent>*{
21 | margin-bottom: 8px;
22 | }
23 |
24 | .request{
25 | font-size: large;
26 | font-weight: 600;
27 | text-align: center;
28 | }
29 | .name{
30 | font-size: large;
31 | font-weight: 600;
32 | }
33 |
34 | .mobile{
35 | display: flex;
36 | flex-direction: row;
37 | justify-content: flex-start;
38 | align-items: center;
39 | gap: 15px;
40 | }
41 |
42 | .address{
43 | padding: 10px;
44 | border: 1px solid darkslategray;
45 | border-radius: 6px;
46 | display: flex;
47 | flex-direction: column;
48 | gap: 0px;
49 | }
50 |
51 | .addressPlaceHolder{
52 | margin-top: -25px;
53 | background-color: white;
54 | width: fit-content;
55 | font-size: large;
56 | font-weight:bold;
57 | margin-bottom: 0px;
58 | }
59 |
60 | .inputField{
61 | margin: 5px;
62 | padding: 0px;
63 | display: flex;
64 | flex-direction: row;
65 | justify-content: space-between;
66 | }
67 |
68 | .fieldName{
69 | margin: 0px;
70 | }
71 |
72 | .field{
73 | margin: 0px;
74 | border: 1px solid darkslategrey;
75 | height: 30px;
76 | padding-left: 10px;
77 | padding-right: 5px;
78 | min-width: 150px;
79 | border-radius: 4px;
80 | margin-left: 4px;
81 | }
82 |
83 |
84 | .orderType{
85 | display: flex;
86 | justify-content: space-between;
87 | gap: 10px;
88 |
89 |
90 | }
91 | .orderType Button{
92 | border-radius: 16px;
93 | }
94 |
95 | .itemsListList{
96 | margin-top: 15px;
97 | width: min(90%,var(--max-width));
98 | }
99 |
100 | table {
101 | border-collapse: collapse;
102 | width: 100%;
103 | }
104 |
105 | td,th {
106 | border: 1px solid #ddd;
107 | padding: 7px;
108 | text-align: left;
109 | }
110 |
111 |
112 | tr:hover {background-color: #ddd;}
113 |
114 | th {
115 | background-color:var(--secondary);
116 | color: black;
117 | }
118 |
119 |
120 | .noRequests{
121 | display: grid;
122 | flex-direction: column;
123 | justify-items:center;
124 | width: 100%;
125 | height: 100vh;
126 | }
127 |
--------------------------------------------------------------------------------
/frontend/src/components/rider/current_request/check_list.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-vars */
2 | import React from "react";
3 | import styles from "./checklist.module.css";
4 | function CheckList({ items ,category,dispatch}) {
5 |
6 | const _check = (e,item,index)=>{
7 | if(e.target.checked){
8 | dispatch({type:'SETITEMS',payload:item})
9 | }else{
10 |
11 | dispatch({type:'REMOVEITEMS',payload:index})
12 |
13 | }
14 | }
15 |
16 | return (
17 | <>
18 | Items requested
19 |
20 | {category.map((cat) => (
21 |
25 | {cat}
26 |
27 | ))}
28 |
29 | Please tick the items you purchased
30 |
31 |
32 | {items.map((req,index) => {
33 | return (
34 |
35 |
36 | {req.itemName} - {req.quantity}
37 |
38 |
39 | _check(e,req,index)} type="checkbox" className={styles.checklist1} />
40 |
41 |
42 | );
43 | })}
44 |
45 | >
46 | );
47 | }
48 |
49 | export default CheckList;
50 |
--------------------------------------------------------------------------------
/frontend/src/components/rider/current_request/checklist.module.css:
--------------------------------------------------------------------------------
1 | .checklist_container {
2 | display: grid;
3 | width: min(90%, 400px);
4 | padding-left: 4px;
5 | }
6 | .checkbox {
7 | padding: 0;
8 | display: flex;
9 | justify-content: space-between;
10 | }
11 |
12 | .checklist1 {
13 | appearance: none;
14 | -webkit-appearance: none;
15 | height: 24px;
16 | width: 24px;
17 | background: #b6b6b67e;
18 | border-radius: 8px;
19 | cursor: pointer;
20 | display: flex;
21 | align-items: center;
22 | justify-content: center;
23 | outline: none;
24 | }
25 |
26 | .checklist1:after {
27 | content: "\2713";
28 | font-size: 20px;
29 | color: white;
30 | display: none;
31 | }
32 | .checklist1:hover {
33 | background-color: #a5a5a5;
34 | }
35 | .checklist1:checked {
36 | background-color: #5bcd3e;
37 | }
38 | .checklist1:checked:after {
39 | display: block;
40 | }
41 |
42 | .category span {
43 | width: fit-content;
44 | font-size: clamp(0.8rem, 1.2vw, 1rem);
45 | margin-right: 0.6em;
46 | padding: 0.3em 0.6em;
47 | font-family: 'Lato', sans-serif;
48 | border-radius: 1rem;
49 | }
50 | .catGreen {
51 | color: honeydew;
52 | background: rgb(250, 93, 93) ;
53 | }
54 | .catGray {
55 | color: white;
56 | background: rgb(75, 131, 216);
57 | }
--------------------------------------------------------------------------------
/frontend/src/components/rider/current_request/fetch_current_request.js:
--------------------------------------------------------------------------------
1 | export const fetchCurrentRequest = async (dispatch, token) => {
2 |
3 | try {
4 | const res = await fetch(process.env.REACT_APP_URL + "/rider/currentRequest", {
5 | method: "GET",
6 | headers: {
7 | authorization: "Bearer " + token,
8 | },
9 | });
10 | const data = await res.json()
11 | console.log(data)
12 | if (data.status === 'success') {
13 | dispatch({ type: "SETREQUEST", payload: data.message });
14 | } else {
15 | dispatch({ type: "SHOWMSG", payload: data.message });
16 |
17 | }
18 | } catch (error) {
19 | dispatch({ type: "SHOWMSG", payload: "Unable to access server, Please try again " });
20 |
21 | }
22 |
23 |
24 | };
25 |
26 | // eslint-disable-next-line no-unused-vars
27 | const request = {
28 | requestNumber: "8628290",
29 | requesterID: "8628290",
30 | requestStatus: "PENDING",
31 | requesterCovidStatus: true,
32 | requestType: "P&D",
33 | name: "Mark Zucc",
34 | phoneNumber: "9999999999",
35 | itemsListImages: [
36 | // "https://images.unsplash.com/photo-1586281380117-5a60ae2050cc?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80",
37 | ],
38 | riderID: {
39 | name: "Someone",
40 | },
41 | itemsListList: [
42 | {
43 | itemName: "Tomato",
44 | quantity: "2kg",
45 | },
46 | {
47 | itemName: "Zomato",
48 | quantity: "2kg",
49 | },
50 | ],
51 | itemCategories: ["MEDICINES", "MISC"],
52 | remarks: "Please delivery ASAP here",
53 | billsImageList: [
54 | // "https://images.unsplash.com/photo-1586281380117-5a60ae2050cc?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80",
55 | ],
56 | rideImages: [
57 | // "https://images.unsplash.com/photo-1586281380117-5a60ae2050cc?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80",
58 | ],
59 | pickupLocationAddress: {
60 | addressLine: "Some place far away",
61 | area: "",
62 | city: "Unknown",
63 | pincode: "XXXXXX",
64 | },
65 | dropLocationAddress: null,
66 | // {
67 | // addressLine: "Some place far away",
68 | // area: "",
69 | // city: "Unknown",
70 | // pincode: "XXXXXX",
71 | // }
72 | pickupLocationCoordinates: {
73 | coordinates: [17.9, 78.6],
74 | },
75 | dropLocationCoordinates: {
76 | coordinates: [17.9, 78.6],
77 | },
78 | };
79 |
--------------------------------------------------------------------------------
/frontend/src/components/rider/current_request/finish_request.js:
--------------------------------------------------------------------------------
1 |
2 | export const finishRequest = async (token, cancel = false) => {
3 | try {
4 | if (cancel) {
5 | const url = process.env.REACT_APP_URL + `/rider/cancelDelivery`;
6 | return await cancelDelivery(url, token)
7 | }
8 | else {
9 |
10 |
11 | const formData = new FormData()
12 | const imgsParsed = sessionStorage.getItem('i-images')
13 | const imgs = JSON.parse(imgsParsed)
14 | const imgsParsedBills = sessionStorage.getItem('b-images')
15 | const bills = JSON.parse(imgsParsedBills)
16 |
17 | for (const src in bills) {
18 | const res = await fetch(imgs[src])
19 | const blob = await res.blob()
20 | const file = new File([blob], "bill", { type: 'image/jpg' })
21 | formData.append('images', file)
22 | }
23 |
24 | for (const src in imgs) {
25 | const res = await fetch(imgs[src])
26 | const blob = await res.blob()
27 | const file = new File([blob], "image", { type: 'image/jpg' })
28 | formData.append('images', file)
29 | }
30 |
31 |
32 |
33 | const url = process.env.REACT_APP_URL + `/rider/finishDelivery`;
34 |
35 | const res = await fetch(url, {
36 | method: "POST",
37 | headers: {
38 | authorization: "Bearer " + token,
39 | },
40 | body: formData
41 | });
42 | const data = await res.json();
43 | console.log(data);
44 | if (data.status === "success") {
45 | sessionStorage.clear();
46 | return 1;
47 | } else {
48 | return data.message;
49 | }
50 | }
51 | } catch (error) {
52 | console.log(error);
53 | return "Unable to access server, Please try again later";
54 | }
55 | };
56 |
57 | const cancelDelivery = async (url, token) => {
58 | const res = await fetch(url, {
59 | method: "GET",
60 | headers: {
61 | authorization: "Bearer " + token,
62 | },
63 |
64 | });
65 | const data = await res.json();
66 | console.log(data);
67 | if (data.status === "success") {
68 | sessionStorage.clear();
69 | return 1;
70 | } else {
71 |
72 | return data.message;
73 | }
74 | }
--------------------------------------------------------------------------------
/frontend/src/components/rider/current_request/preview_images.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import ImageViewer from "../../global_ui/image_viewer/image_viewer";
3 |
4 | import styles from "./current_request.module.css";
5 | const PreviewImages = ({ setImages, title, images, imgWidth = "150px",canDelete = false }) => {
6 | const [imageViewerData, setImageViewerData] = useState({
7 | show: false,
8 | src: "",
9 | });
10 | const deleteImage = (index) => {
11 | console.log(index);
12 | setImages((imgs) => {
13 | const imgList = [...imgs]
14 | imgList.splice(index, 1)
15 |
16 | return [...imgList]
17 | })
18 | }
19 | const onImageClicked = (src) => {
20 | setImageViewerData({ show: true, src: src });
21 | };
22 | return (
23 | <>
24 | {images.length > 0 && {title} }
25 |
31 |
36 |
37 |
38 | {images.map((link, index) => {
39 | console.log(link);
40 | return (
41 |
42 |
47 | { canDelete &&
deleteImage(index)} className="fas fa-times-circle" > }
48 |
onImageClicked(link)} src={ process.env.REACT_APP_URL+ link} alt="img" />
52 |
53 | )
54 | })}
55 |
56 | >
57 | );
58 | };
59 | export default PreviewImages;
60 |
--------------------------------------------------------------------------------
/frontend/src/components/rider/current_request/user_details.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styles from './user_details.module.css'
3 | const UserDetails = ({ covid, name, phone }) => {
4 | const btnStyle = {
5 | background: "var(--secondary)",
6 | fontWeight: "bold",
7 | padding: "0.2em 0.90em",
8 | textDecoration: "none",
9 | cursor: "pointer",
10 | color: "hsl(0 0% 90%)",
11 | borderRadius: "16px",
12 | boxShadow: "0 0 5px var(--almost-transparent)",
13 | fontSize: "0.87rem",
14 | };
15 | return (
16 |
25 | {covid && (
26 |
39 | )}
40 |
47 | Requester Details
48 |
49 | {covid && (
50 |
57 | Requester is covid positive
58 |
59 | )}
60 |
Name : {name}
61 |
62 | {" "}
63 | Phone : {phone}
64 |
65 |
84 |
85 | );
86 | };
87 |
88 | export default UserDetails;
89 |
--------------------------------------------------------------------------------
/frontend/src/components/rider/current_request/user_details.module.css:
--------------------------------------------------------------------------------
1 |
2 | .covidDot{
3 | opacity: 0;
4 | animation: fade 8000ms infinite;
5 | }
6 | @keyframes fade {
7 | 50%{
8 | opacity: 1;
9 | }
10 | 100%{
11 |
12 | opacity: 0;
13 | }
14 |
15 | }
--------------------------------------------------------------------------------
/frontend/src/components/rider/make_delivery/ChooseRequestRoutes.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | Switch,
4 | Route
5 | } from "react-router-dom";
6 | import ViewRequest from '../common/viewRequest';
7 | import ChooseRequest from './chooseRequest'
8 |
9 | function ChooseRequestRoutes() {
10 | return (
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | )
24 | }
25 |
26 | export default ChooseRequestRoutes
27 |
--------------------------------------------------------------------------------
/frontend/src/components/rider/my_deliveries/.ukw:
--------------------------------------------------------------------------------
1 | u know what
--------------------------------------------------------------------------------
/frontend/src/components/rider/my_deliveries/MyDeliveries.module.css:
--------------------------------------------------------------------------------
1 | .myDeliveriesList
2 | {
3 | height: 100vh;
4 | text-align: center;
5 | margin-left: auto;
6 | margin-right: auto;
7 | justify-items: center;
8 | }
9 |
10 | .myDeliveriesListItem{
11 | padding: 15px;
12 | min-height: 150px;
13 | margin-bottom: 20px;
14 | margin-left: auto;
15 | margin-right: auto;
16 | row-gap: 3px;
17 | font-size: clamp(0.7rem, 1vw, 1.2rem);
18 | display: grid;
19 | width: min(90%, 500px);
20 | justify-content: center;
21 | grid-template-columns: 30% 30% 15% 25%;
22 | grid-template-rows: auto;
23 | grid-template-areas:
24 | "date requestType . requestStatus"
25 | "category1 category2 category3 .";
26 | border-radius: 4px;
27 | box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
28 | padding: 15px;
29 | }
30 |
31 | .myDeliveriesListItem:hover{
32 | box-shadow: rgba(0, 0, 0, 0.25) 0px 7px 14px, rgba(0, 0, 0, 0.22) 0px 5px 10px;
33 | cursor: pointer;
34 | }
35 |
36 | .date
37 | {
38 | margin: auto;
39 | grid-area: date;
40 | color: black;
41 | }
42 | .requestType
43 | {
44 | font-weight: bold;
45 | background-color: rgb(167, 255, 255);
46 | grid-area: requestType;
47 | border-radius: 20px;
48 | color: black;
49 | text-align: center;
50 | margin: auto;
51 | padding: 5px 10px;
52 | }
53 |
54 | .medicines
55 | {
56 | color: white;
57 | margin: auto;
58 | grid-area: category1;
59 | border-radius: 20px;
60 | background-color: rgb(216, 0, 0);
61 | padding: 5px 15px;
62 |
63 | }
64 | .groceries
65 | {
66 | color: white;
67 | margin: auto;
68 | grid-area: category2;
69 | border-radius: 20px;
70 | background-color: rgb(3, 209, 3);
71 | padding: 5px 15px;
72 |
73 | }
74 | .misc
75 | {
76 | color: white;
77 | margin: auto;
78 | grid-area: category3;
79 | border-radius: 10px;
80 | border: 2px solid rgb(7, 75, 144);
81 | background-color: blue;
82 | padding: 0px 3px 0px 3px;
83 |
84 | }
85 |
86 | .viewButton{
87 | display: none;
88 | margin: auto;
89 | grid-area: viewButton;
90 | padding: 3px;
91 | border-radius: 10px;
92 | border: 1px solid black;
93 | box-shadow: 0 0 2px black;
94 | }
95 | .ok
96 | {
97 | grid-area: requestStatus;
98 | color: green;
99 | font-weight: bold;
100 | }
101 | .notOk
102 | {
103 | grid-area: requestStatus;
104 | color: red;
105 | font-weight: bold;
106 | }
107 |
108 | .noRequests{
109 | text-align: center;
110 | margin-top: 45vh;
111 |
112 |
113 | }
--------------------------------------------------------------------------------
/frontend/src/components/rider/my_deliveries/MyDeliveriesListItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useHistory } from 'react-router-dom';
3 | import styles from './MyDeliveries.module.css';
4 |
5 | const MyDeliveriesListItem = (props) => {
6 |
7 | const routes = useHistory()
8 | return (
9 | {
10 | console.log("props:", props.data);
11 | routes.push("my_deliveries/" + props.data.requestNumber, {
12 | reqObj: props.data
13 | })
14 | }} className={styles.myDeliveriesListItem}>
15 | Date: {props.data.date}
16 | {props.data.requestType}
17 | {
18 | (props.data.requestStatus == "DELIVERED" || props.data.requestStatus == "UNDER DELIVERY") ? ({props.data.requestStatus} ) : ({props.data.requestStatus} )
19 | }
20 | {
21 | props.data.itemCategories.map((category, index) => {
22 | switch (category) {
23 | case 'MEDICINES': return Medcines ;
24 | case 'GROCERIES': return Groceries ;
25 | case 'MISC.': return Misc. ;
26 | }
27 | })
28 | }
29 |
30 |
31 |
32 | View
33 |
34 |
)
35 | }
36 |
37 | export default MyDeliveriesListItem
--------------------------------------------------------------------------------
/frontend/src/components/rider/my_deliveries/my_delivery_routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Route, Switch } from "react-router-dom";
3 | import ViewRequest from '../common/viewRequest';
4 | import MyDeliveries from './MyDeliveries';
5 | const MyDeliveryRoutes = () => {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | );
13 | }
14 |
15 | export default MyDeliveryRoutes;
--------------------------------------------------------------------------------
/frontend/src/components/rider/profile/RiderProfile.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Navbar from "../../global_ui/nav";
3 | import styles from "./RiderProfile.module.css";
4 | import axios from "axios";
5 | import { useHistory } from "react-router-dom";
6 | import { useEffect, useState } from "react";
7 | import { Dialog } from "../../global_ui/dialog/dialog";
8 | import { LoadingScreen } from "../../global_ui/spinner";
9 | import { useContext } from "react";
10 | import { AuthContext } from "../../context/auth/authProvider";
11 |
12 | const RiderProfile = () => {
13 | const history = useHistory();
14 | const [data, setData] = useState({
15 | name: "",
16 | phoneNumber: "",
17 | profileUrl: "",
18 | });
19 | const [error, setError] = useState(null);
20 | const {token} = useContext(AuthContext)
21 | const [isLoaded, setisLoaded] = useState(false);
22 |
23 | useEffect(async () => {
24 | const options = {
25 | headers: {
26 | authorization: "Bearer " + token,
27 | },
28 | };
29 | axios.get(process.env.REACT_APP_URL+"/rider/profile", options).then(
30 | (response) => {
31 | console.log(response);
32 | if (response.data.status === "success") {
33 | setData({
34 | name: response.data.message.name,
35 | phoneNumber: response.data.message.phoneNumber,
36 | profileUrl: response.data.message.profileUrl,
37 | });
38 | setError(null);
39 | } else {
40 | setError(response.data.message);
41 | }
42 | setisLoaded(true);
43 | },
44 | (error) => {
45 | console.log("An error occured", error);
46 | setError(error.message);
47 | setisLoaded(true);
48 | }
49 | );
50 | }, []);
51 |
52 | return isLoaded ? (
53 | error ? (
54 | {
57 | history.push("/my_profile");
58 | setError(false);
59 | }}
60 | msg={error}
61 | />
62 | ) : (
63 |
64 |
65 |
66 | {data.profileURL ? (
67 |
68 | ) : (
69 |
70 | )}
71 |
Full Name:
72 |
{data.name}
73 |
74 |
Phone Number:
75 |
{data.phoneNumber}
76 |
77 |
history.push("/my_profile/edit_profile",{userData:data})}
79 | style={{
80 | marginTop: "5rem",
81 | padding: "0.55em 2em",
82 | }}
83 | >
84 | Edit
85 |
86 |
87 | )
88 | ) : (
89 |
90 | );
91 | };
92 |
93 | export default RiderProfile;
94 |
--------------------------------------------------------------------------------
/frontend/src/components/rider/profile/RiderProfile.module.css:
--------------------------------------------------------------------------------
1 | .riderProfileContainer{
2 | display:grid;
3 | flex-direction: column;
4 | grid-gap: 13px;
5 | justify-items: center;
6 | }
7 |
8 | .labelHead{
9 | font-weight: bold;
10 | align-self: start;
11 | position: relative;
12 | font-size: large;
13 | color: darkslategrey;
14 | }
15 |
16 | .profileImage {
17 | border: 1px solid black;
18 | background-color: pink;
19 | margin: 3px;
20 |
21 | justify-self: center;
22 | border-radius: 50%;
23 | width: min(50%, 150px);
24 | }
25 | .profileDummy {
26 | position: relative;
27 | width: min(35vw, 150px);
28 | height: min(35vw, 150px);
29 | background: var(--secondary);
30 | border-radius: 50%;
31 | margin: 0 0 8px 0;
32 | }
33 | .profileDummy::before {
34 | content: "Profile";
35 |
36 | position: absolute;
37 | top: 50%;
38 | left: 50%;
39 | transform: translate(-50%, -50%);
40 | }
41 | .phoneNumber,.name{
42 | display: flex;
43 | align-items: center;
44 | align-content: center;
45 | padding-left: 15px;
46 | height: 45px;
47 | width: min(80%,35rem);
48 | box-sizing:border-box;
49 | border : 2px solid #ddd;
50 | font-size : 16px;
51 | border-radius: 6px;
52 | }
53 |
54 | .edit{
55 | font-size: large;
56 | padding: auto 20px;
57 | align-self: center;
58 | }
--------------------------------------------------------------------------------
/frontend/src/components/rider/profile/editRiderProfile.module.css:
--------------------------------------------------------------------------------
1 | .riderProfileContainer {
2 | display:grid;
3 | gap: 13px;
4 | justify-items: center;
5 | }
6 |
7 | .profileImage {
8 | border: 1px solid black;
9 | background-color: pink;
10 | margin: 3px;
11 |
12 | justify-self: center;
13 | border-radius: 50%;
14 | width: min(50%, 150px);
15 | }
16 | .profileDummy {
17 | position: relative;
18 | width: min(35vw, 150px);
19 | height: min(35vw, 150px);
20 | background: var(--secondary);
21 | border-radius: 50%;
22 | margin: 0 0 8px 0;
23 | }
24 | .profileDummy::before {
25 | content: "Profile";
26 |
27 | position: absolute;
28 | top: 50%;
29 | left: 50%;
30 | transform: translate(-50%, -50%);
31 | }
32 |
33 |
34 |
35 | .editRiderProfileForm {
36 | display: flex;
37 | gap: 23px;
38 | flex-direction: column;
39 | justify-content: space-between;
40 | width: min(90%, var(--max-width));
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/frontend/src/components/rider/profile/profileRouting.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Route, Switch } from 'react-router-dom';
3 | import RiderProfile from './RiderProfile'
4 | import EditRiderProfile from './editRiderProfile'
5 |
6 | function profileRouting() {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | )
18 | }
19 |
20 | export default profileRouting
21 |
--------------------------------------------------------------------------------
/frontend/src/components/rider/readme.md:
--------------------------------------------------------------------------------
1 | Must provide
2 |
3 | 1. Respective screens (refer folder names)
4 | 2. And their respective functions. (like current_request comp must have confirm delivery btn, which ends the delivery.....etc)
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 * as serviceWorkerRegistration from './serviceWorkerRegistration';
6 | import reportWebVitals from './reportWebVitals';
7 |
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | document.getElementById('root')
13 | );
14 |
15 | // If you want your app to work offline and load faster, you can change
16 | // unregister() to register() below. Note this comes with some pitfalls.
17 | // Learn more about service workers: https://cra.link/PWA
18 | serviceWorkerRegistration.register();
19 |
20 | // If you want to start measuring performance in your app, pass a function
21 | // to log results (for example: reportWebVitals(console.log))
22 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
23 | reportWebVitals();
24 |
--------------------------------------------------------------------------------
/frontend/src/models/requester.js:
--------------------------------------------------------------------------------
1 | import User from "./user"
2 | export default class Requester extends User {
3 | /**
4 | *
5 | * @param {string} number User's phone number
6 | * @param {string } name User's name
7 | * @param {number} yearOfBirth Year of Birth
8 | */
9 | constructor(number,name,yearOfBirth){
10 | super(name,number)
11 | this.yearOfBirth = yearOfBirth
12 | }
13 |
14 | }
--------------------------------------------------------------------------------
/frontend/src/models/rider.js:
--------------------------------------------------------------------------------
1 | import User from "./user"
2 | export default class Rider extends User {
3 | /**
4 | *
5 | * @param {string} number User's phone number
6 | * @param {string } name User's name
7 | */
8 | constructor(number,name){
9 | super(name,number)
10 |
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/frontend/src/models/user.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Base user class for both rider and requester
3 | */
4 | export default class User {
5 | /**
6 | *
7 | * @param {string} name
8 | */
9 | constructor(name,mobile){
10 | this.name = name;
11 | this.mobile = mobile;
12 | }
13 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | },[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 | },[data])
49 |
50 | return [data,setData];
51 | }
52 |
--------------------------------------------------------------------------------
/mockups/AdminModuleFinal.xd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coding-Studio-vbit/ReliefRiders/3227eb361d66a3eda3d8794c54c72db31dbc6e8f/mockups/AdminModuleFinal.xd
--------------------------------------------------------------------------------
/mockups/RidersForReliefFinal.xd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coding-Studio-vbit/ReliefRiders/3227eb361d66a3eda3d8794c54c72db31dbc6e8f/mockups/RidersForReliefFinal.xd
--------------------------------------------------------------------------------
/uml_diagrams/component.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coding-Studio-vbit/ReliefRiders/3227eb361d66a3eda3d8794c54c72db31dbc6e8f/uml_diagrams/component.jpg
--------------------------------------------------------------------------------
/uml_diagrams/sequence.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coding-Studio-vbit/ReliefRiders/3227eb361d66a3eda3d8794c54c72db31dbc6e8f/uml_diagrams/sequence.jpg
--------------------------------------------------------------------------------
/uml_diagrams/usecase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coding-Studio-vbit/ReliefRiders/3227eb361d66a3eda3d8794c54c72db31dbc6e8f/uml_diagrams/usecase.png
--------------------------------------------------------------------------------