From 582d2ac34cac799dce643457819f0959de3ad8d4 Mon Sep 17 00:00:00 2001 From: Tharindu Jayasanka Date: Wed, 3 May 2023 22:39:01 +0530 Subject: [PATCH] search function fixed --- client/package-lock.json | 9 + client/package.json | 1 + .../FlightSearch/FilterSiderbar.jsx | 178 ++++++++++-------- .../components/FlightSearch/FlightCard.jsx | 124 ++++++++---- client/src/components/GoogleMap/Map.jsx | 4 +- client/src/components/Search/Search.jsx | 90 ++++++--- client/src/context/SearchContext.jsx | 41 ++++ client/src/main.jsx | 5 +- client/src/pages/agent/FlightCheckout.jsx | 9 +- client/src/pages/agent/FlightReservation.jsx | 25 ++- server/controllers/flightController.js | 32 ++++ server/routes/flights.js | 10 +- 12 files changed, 365 insertions(+), 163 deletions(-) create mode 100644 client/src/context/SearchContext.jsx diff --git a/client/package-lock.json b/client/package-lock.json index f5e1b7a..84772e6 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -15,6 +15,7 @@ "react-date-range": "^1.4.0", "react-dom": "^18.2.0", "react-icons": "^4.8.0", + "react-loading-skeleton": "^3.3.0", "react-numeric-input": "^2.2.3", "react-router-dom": "^6.11.0", "react-select": "^5.7.2" @@ -3819,6 +3820,14 @@ "react": "0.14 || 15 - 18" } }, + "node_modules/react-loading-skeleton": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/react-loading-skeleton/-/react-loading-skeleton-3.3.0.tgz", + "integrity": "sha512-+jwXoz7hA3kyNHzRyz/vjZx8lA/Vt91f6zB8gaygFZY8eLvbZyQiJC4ckYPWxqvfp2OiiwSs/fXTbZ7NyL3IrQ==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/react-numeric-input": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/react-numeric-input/-/react-numeric-input-2.2.3.tgz", diff --git a/client/package.json b/client/package.json index 571cb0c..589e46b 100644 --- a/client/package.json +++ b/client/package.json @@ -17,6 +17,7 @@ "react-date-range": "^1.4.0", "react-dom": "^18.2.0", "react-icons": "^4.8.0", + "react-loading-skeleton": "^3.3.0", "react-numeric-input": "^2.2.3", "react-router-dom": "^6.11.0", "react-select": "^5.7.2" diff --git a/client/src/components/FlightSearch/FilterSiderbar.jsx b/client/src/components/FlightSearch/FilterSiderbar.jsx index 15a988c..d3d0e31 100644 --- a/client/src/components/FlightSearch/FilterSiderbar.jsx +++ b/client/src/components/FlightSearch/FilterSiderbar.jsx @@ -1,8 +1,18 @@ import React, { useEffect, useState } from 'react'; import Select from 'react-select'; import useFetch from '../../hooks/useFetch'; +import Skeleton from 'react-loading-skeleton'; +import 'react-loading-skeleton/dist/skeleton.css'; const FilterSiderbar = () => { + const [loading, setLoading] = useState(true); + + useEffect(() => { + setTimeout(() => { + setLoading(false); + }, 1500); + }); + const [airlines, setAirlines] = useState([]); useEffect(() => { @@ -23,90 +33,96 @@ const FilterSiderbar = () => { ]; return ( -
-
Filters
-
-
-
Airlines
- -
-
Price
- + <> + {loading ? ( + + ) : ( +
+
Filters
+
+
+
Airlines
+ +
+
Price
+ -
-
Stops
-
-
- - -
-
- - -
-
- - -
-
-
-
Ticket Type
-
-
- - -
-
- - -
-
-
-
Cabin Class
-
-
- - -
-
- - -
-
- - +
+
Stops
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
Ticket Type
+
+
+ + +
+
+ + +
+
+
+
Cabin Class
+
+
+ + +
+
+ + +
+
+ + +
+
+
+
Duration
+ +
-
-
Duration
- - -
-
+ )} + ); }; diff --git a/client/src/components/FlightSearch/FlightCard.jsx b/client/src/components/FlightSearch/FlightCard.jsx index 84a7b40..210392c 100644 --- a/client/src/components/FlightSearch/FlightCard.jsx +++ b/client/src/components/FlightSearch/FlightCard.jsx @@ -1,50 +1,92 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; +import Skeleton from 'react-loading-skeleton'; +import 'react-loading-skeleton/dist/skeleton.css'; + +const FlightCard = (flight) => { + const [loading, setLoading] = useState(true); + + useEffect(() => { + setTimeout(() => { + setLoading(false); + }, 2000); + }); + + function convertMinutesToHrsMins(minutes) { + if (minutes < 60) { + return `${minutes}m`; + } + + const hours = Math.floor(minutes / 60); + const mins = minutes % 60; + + if (mins === 0) { + return `${hours}h`; + } + + return `${hours}h ${mins}m`; + } -const FlightCard = () => { return ( -
-
-
-
- Return -
-
-
- - Sri Lankan Airlines -
-
-
18:00
-
CMB
-
-
-
-
Direct
-
-
-
18:00
-
CMB
+ <> + {loading ? ( + + ) : ( +
+
+
+
+ {flight.flight.isReturn ? 'Return' : 'One Way'} +
+ +
+
+ {flight.flight.airline.name} + {flight.flight.airline.name} +
+
+
18:00
+
+ {flight.flight.departure_destination.code} +
+
+
+
+
+ {flight.flight.isDirect ? 'Direct' : flight.flight.stops} +
+
+
+
18:00
+
+ {flight.flight.arrival_destination.code} +
+
+
+
+ {convertMinutesToHrsMins(flight.flight.duration)} +
+
+
-
-
6h 25m
+
+
+
+ {flight.flight.price} LKR +
+
Tax included
+ +
-
-
-
58,000
-
Tax included
- -
-
-
-
+ )} + ); }; diff --git a/client/src/components/GoogleMap/Map.jsx b/client/src/components/GoogleMap/Map.jsx index 5e74e23..03aaf64 100644 --- a/client/src/components/GoogleMap/Map.jsx +++ b/client/src/components/GoogleMap/Map.jsx @@ -13,8 +13,8 @@ const containerStyle = { }; const loactions = [ - { lat: 6.927079, lng: 79.861244 }, - { lat: 19.07609, lng: 72.877426 }, + { id: 1, lat: 6.927079, lng: 79.861244 }, + { id: 2, lat: 19.07609, lng: 72.877426 }, ]; const options = { diff --git a/client/src/components/Search/Search.jsx b/client/src/components/Search/Search.jsx index 51df9c2..513f5e6 100644 --- a/client/src/components/Search/Search.jsx +++ b/client/src/components/Search/Search.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import Select from 'react-select'; import { MdLocationOn, @@ -11,9 +11,11 @@ import { DateRange } from 'react-date-range'; import NumericInput from 'react-numeric-input'; import 'react-date-range/dist/styles.css'; // main css file import 'react-date-range/dist/theme/default.css'; // theme css file +import useFetch from '../../hooks/useFetch'; -const Search = () => { +const Search = ({ setFlights }) => { const [selectedOption, setSelectedOption] = useState(null); + const [openDate, setOpenDate] = useState(false); const [dates, setDates] = useState([ { @@ -33,10 +35,44 @@ const Search = () => { }, []); const options = airports.map((airport) => ({ - value: airport.id, + value: airport._id, label: airport.name + ' (' + airport.code + ')', })); + const [from, setFrom] = useState(''); + const [to, setTo] = useState(''); + const [passengers, setPassengers] = useState(1); + + const departure_date = format(dates[0].startDate, 'yyyy-MM-dd'); + const arrival_date = format(dates[0].endDate, 'yyyy-MM-dd'); + + const handleClick = async () => { + if (!from || !to || !departure_date || !arrival_date || !passengers) + return alert('Please fill all fields'); + await fetch( + `/api/flights/search?from=${from.value}&to=${to.value}&departure_date=${departure_date}&arrival_date=${arrival_date}&pax=${passengers}` + ) + .then((response) => response.json()) + .then((data) => setFlights(data)) + .catch((err) => console.log(err)); + }; + + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + const dropdownRef2 = useRef(null); + + const handleOutsideClick = (event) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { + setIsOpen(false); + } + }; + + useEffect(() => { + document.addEventListener('click', handleOutsideClick, true); + return () => { + document.removeEventListener('click', handleOutsideClick, true); + }; + }); return (
@@ -44,7 +80,7 @@ const Search = () => {
{ }} />
-
+
- setOpenDate(!openDate)} + + {isOpen && ( +
+ ref={dropdownRef} + > + setDates([item.selection])} + moveRangeOnFirstSelection={false} + ranges={dates} + className='w-full' + minDate={new Date()} + /> +
)}
@@ -107,8 +151,12 @@ const Search = () => { />
+
-
diff --git a/client/src/context/SearchContext.jsx b/client/src/context/SearchContext.jsx new file mode 100644 index 0000000..c3a42c9 --- /dev/null +++ b/client/src/context/SearchContext.jsx @@ -0,0 +1,41 @@ +import { createContext, useReducer } from 'react'; + +const INITIAL_STATE = { + from: undefined, + to: undefined, + departure_date: undefined, + arrival_date: undefined, + pax: undefined, +}; + +export const SearchContext = createContext(INITIAL_STATE); + +const SearchReducer = (state, action) => { + switch (action.type) { + case 'NEW_SEARCH': + return action.payload; + case 'RESET_SEARCH': + return INITIAL_STATE; + default: + return state; + } +}; + +export const SearchContextProvider = ({ children }) => { + const [state, dispatch] = useReducer(SearchReducer, INITIAL_STATE); + + return ( + + {children} + + ); +}; diff --git a/client/src/main.jsx b/client/src/main.jsx index 7497ae8..fb20285 100644 --- a/client/src/main.jsx +++ b/client/src/main.jsx @@ -2,9 +2,12 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App.jsx'; import './index.css'; +import { SearchContextProvider } from './context/SearchContext'; ReactDOM.createRoot(document.getElementById('root')).render( - + + + ); diff --git a/client/src/pages/agent/FlightCheckout.jsx b/client/src/pages/agent/FlightCheckout.jsx index 2dcf4e1..b793986 100644 --- a/client/src/pages/agent/FlightCheckout.jsx +++ b/client/src/pages/agent/FlightCheckout.jsx @@ -1,8 +1,13 @@ -import React from 'react'; +import React, { useContext } from 'react'; import { MdOutlineArrowBack, MdArrowDropDown } from 'react-icons/md'; import Map from '../../components/GoogleMap/Map'; +import { SearchContext } from '../../context/SearchContext'; const FlightCheckout = () => { + const { departure_date } = useContext(SearchContext); + + console.log(departure_date); + return (
-
+
Direct
diff --git a/client/src/pages/agent/FlightReservation.jsx b/client/src/pages/agent/FlightReservation.jsx index b290efe..f0520a4 100644 --- a/client/src/pages/agent/FlightReservation.jsx +++ b/client/src/pages/agent/FlightReservation.jsx @@ -1,28 +1,27 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import Search from '../../components/Search/Search'; import FlightCard from '../../components/FlightSearch/FlightCard'; import FilterSiderbar from './../../components/FlightSearch/FilterSiderbar'; const FlightReservation = () => { + const [flights, setFlights] = useState([]); + return (
- +
- - - - - - - - -
-
- + {flights.length > 0 ? ( + flights.map((flight, index) => ( + + )) + ) : ( +

No Flights Available

+ )}
+ {flights.length > 0 ? : ''}
); diff --git a/server/controllers/flightController.js b/server/controllers/flightController.js index a14fc8f..0cbf7d0 100644 --- a/server/controllers/flightController.js +++ b/server/controllers/flightController.js @@ -20,3 +20,35 @@ export const getFlights = async (req, res, next) => { next(err); } }; + +export const getFlight = async (req, res, next) => { + try { + const flights = await Flight.findById(req.params.id); + res.status(200).json(flights); + } catch (err) { + next(err); + } +}; + +export const searchFlights = async (req, res, next) => { + try { + const departureDate = req.query.departure_date; + const arrivalDate = req.query.arrival_date; + const seats = req.query.pax; + const flights = await Flight.find({ + departure_destination: req.query.from, + arrival_destination: req.query.to, + $and: [ + { departure_date: { $gte: new Date(departureDate).toISOString() } }, + { arrival_date: { $lte: new Date(arrivalDate).toISOString() } }, + ], + available_seats: { $gte: parseInt(seats) }, + }) + .populate('airline') + .populate('departure_destination') + .populate('arrival_destination'); + res.status(200).json(flights); + } catch (err) { + next(err); + } +}; diff --git a/server/routes/flights.js b/server/routes/flights.js index 16b4d10..86b0cb5 100644 --- a/server/routes/flights.js +++ b/server/routes/flights.js @@ -1,7 +1,11 @@ import express from 'express'; -import { addFlight } from '../controllers/flightController.js'; import { verifyToken } from '../utils/verifyToken.js'; -import { getFlights } from '../controllers/flightController.js'; +import { + addFlight, + getFlights, + getFlight, + searchFlights, +} from '../controllers/flightController.js'; const router = express.Router(); // router.get('/checkauthentication', verifyToken, (req, res, next) => { @@ -10,5 +14,7 @@ const router = express.Router(); router.post('/', addFlight); router.get('/', getFlights); +router.get('/search', searchFlights); +router.get('/find/:id', getFlight); export default router;