-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add Map page with some filters and simple stats
- Loading branch information
Showing
19 changed files
with
399 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import dayjs from "dayjs"; | ||
// MUI UI elements | ||
import Grid from "@mui/material/Grid2"; | ||
import FormControlLabel from "@mui/material/FormControlLabel"; | ||
import Switch from "@mui/material/Switch"; | ||
// Custom | ||
import DatePicker from "../UIElements/DatePicker"; | ||
import AircraftReg from "../UIElements/AircraftReg"; | ||
import AircraftType from "../UIElements/AircraftType"; | ||
import AircraftCategories from "../UIElements/AircraftCategories"; | ||
import TextField from "../UIElements/TextField"; | ||
import Select from "../UIElements/Select"; | ||
|
||
const dateRanges = [ | ||
{ | ||
label: 'Last 7 days', | ||
fn: () => ({ start: dayjs().subtract(7, 'day'), end: dayjs() }) | ||
}, | ||
{ | ||
label: 'Last 30 days', | ||
fn: () => ({ start: dayjs().subtract(30, 'day'), end: dayjs() }) | ||
}, | ||
{ | ||
label: 'Last 90 days', | ||
fn: () => ({ start: dayjs().subtract(90, 'day'), end: dayjs() }) | ||
}, | ||
{ | ||
label: 'This Month', | ||
fn: () => ({ start: dayjs().startOf('month'), end: dayjs().endOf('month') }) | ||
}, | ||
{ | ||
label: 'Last Month', | ||
fn: () => ({ start: dayjs().subtract(1, 'month').startOf('month'), end: dayjs().subtract(1, 'month').endOf('month') }) | ||
}, | ||
{ | ||
label: 'Last 3 Months', | ||
fn: () => ({ start: dayjs().subtract(3, 'month').startOf('month'), end: dayjs().endOf('month') }) | ||
}, | ||
{ | ||
label: 'This Year', | ||
fn: () => ({ start: dayjs().startOf('year'), end: dayjs().endOf('year') }) | ||
}, | ||
{ | ||
label: 'Last Year', | ||
fn: () => ({ start: dayjs().subtract(1, 'year').startOf('year'), end: dayjs().subtract(1, 'year').endOf('year') }) | ||
}, | ||
]; | ||
|
||
export const Filters = ({ filter, handleChange }) => { | ||
const handleQuickSelect = (_, value) => { | ||
const range = dateRanges.find(r => r.label === value); | ||
if (range) { | ||
const { start, end } = range.fn(); | ||
handleChange('start_date', start); | ||
handleChange('end_date', end); | ||
} | ||
}; | ||
|
||
return ( | ||
<> | ||
<Grid container spacing={1}> | ||
<Select gsize={{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12 }} | ||
label="Quick Date Range" | ||
onChange={handleQuickSelect} | ||
defaultValue="This Year" | ||
options={dateRanges.map((range) => (range.label))} | ||
/> | ||
<DatePicker | ||
gsize={{ xs: 12, sm: 6, md: 6, lg: 6, xl: 6 }} | ||
id="start_date" | ||
label="Start Date" | ||
handleChange={handleChange} | ||
value={filter?.start_date ? dayjs(filter?.start_date, "DD/MM/YYYY") : null} | ||
tooltip="Start Date" | ||
/> | ||
<DatePicker | ||
gsize={{ xs: 12, sm: 6, md: 6, lg: 6, xl: 6 }} | ||
id="end_date" | ||
label="End Date" | ||
handleChange={handleChange} | ||
value={filter?.end_date ? dayjs(filter?.end_date, "DD/MM/YYYY") : null} | ||
tooltip="End Date" | ||
/> | ||
<AircraftReg | ||
gsize={{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12 }} | ||
id="aircraft_reg" | ||
handleChange={handleChange} | ||
value={filter?.aircraft_reg} | ||
last={false} disableClearable={false} | ||
/> | ||
<AircraftType | ||
gsize={{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12 }} | ||
id="aircraft_model" | ||
handleChange={handleChange} | ||
value={filter?.aircraft_model} | ||
disableClearable={false} | ||
/> | ||
<AircraftCategories | ||
gsize={{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12 }} | ||
id="aircraft_category" | ||
multiple={false} | ||
handleChange={handleChange} | ||
value={filter.aircraft_category} | ||
disableClearable={false} | ||
/> | ||
<TextField | ||
gsize={{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12 }} | ||
id="place" | ||
label="Departure/Arrival" | ||
handleChange={handleChange} | ||
tooltip="Departure/Arrival" | ||
value={filter?.place} | ||
/> | ||
<FormControlLabel | ||
control={ | ||
<Switch | ||
checked={filter?.no_routes ?? false} | ||
onChange={(event) => handleChange("no_routes", event.target.checked)} | ||
/> | ||
} | ||
label="No Route Lines" | ||
/> | ||
</Grid> | ||
</> | ||
); | ||
} | ||
|
||
export default Filters; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import { useEffect, useState } from "react"; | ||
import { useNavigate } from "react-router-dom"; | ||
import { useQuery } from "@tanstack/react-query"; | ||
import dayjs from "dayjs"; | ||
// MUI | ||
import Grid from "@mui/material/Grid2"; | ||
import Card from '@mui/material/Card'; | ||
import CardContent from '@mui/material/CardContent'; | ||
import LinearProgress from "@mui/material/LinearProgress"; | ||
// Custom | ||
import CardHeader from "../UIElements/CardHeader"; | ||
import { MAP_FILTER_INITIAL_STATE } from "../../constants/constants"; | ||
import Filters from "./Filters"; | ||
import FlightMap from "../FlightMap/FlightMap"; | ||
import { useErrorNotification } from "../../hooks/useAppNotifications"; | ||
import { fetchLogbookData } from "../../util/http/logbook"; | ||
import { fetchAircraftModelsCategories } from "../../util/http/aircraft"; | ||
import SummaryStats from "./SummaryStats"; | ||
|
||
const getModelsByCategory = (modelsData, category) => { | ||
if (!category || !modelsData) return []; | ||
return modelsData | ||
.filter(item => item.category.split(',').map(c => c.trim()).includes(category)) | ||
.map(item => item.model); | ||
}; | ||
|
||
const filterData = (data, filter, modelsData) => { | ||
// Filter data | ||
const filteredData = data.filter((flight) => { | ||
// filter by date | ||
const flightDate = dayjs(flight.date, 'DD/MM/YYYY'); | ||
const matchesDate = flightDate.isBetween(filter.start_date, filter.end_date, null, '[]'); | ||
|
||
// filter registration | ||
const matchesReg = filter.aircraft_reg ? flight.aircraft.reg_name === filter.aircraft_reg : true; | ||
|
||
// filter type | ||
const matchesType = filter.aircraft_model ? flight.aircraft.model === filter.aircraft_model : true; | ||
|
||
// filter category | ||
const matchesCategory = filter.aircraft_category ? | ||
getModelsByCategory(modelsData, filter.aircraft_category).includes(flight.aircraft.model) : true; | ||
|
||
// filter arrival and departure place | ||
const matchesArrival = filter.place ? flight.arrival.place.toUpperCase().includes(filter.place.toUpperCase()) : true; | ||
const matchesDeparture = filter.place ? flight.departure.place.toUpperCase().includes(filter.place.toUpperCase()) : true; | ||
|
||
return matchesDate & matchesReg && matchesType && matchesCategory && (matchesArrival || matchesDeparture); | ||
}); | ||
|
||
return filteredData; | ||
} | ||
|
||
export const SummaryFlightMap = () => { | ||
const [filter, setFilter] = useState(MAP_FILTER_INITIAL_STATE); | ||
const [mapData, setMapData] = useState([]); | ||
const navigate = useNavigate(); | ||
|
||
const { data, isLoading, isError, error } = useQuery({ | ||
queryKey: ['logbook'], | ||
queryFn: ({ signal }) => fetchLogbookData({ signal, navigate }), | ||
}); | ||
useErrorNotification({ isError, error, fallbackMessage: 'Failed to load logbook' }); | ||
|
||
const { data: modelsData } = useQuery({ | ||
queryKey: ['models-categories'], | ||
queryFn: ({ signal }) => fetchAircraftModelsCategories({ signal, navigate }), | ||
}); | ||
|
||
const handleFilterChange = (key, value) => { | ||
setFilter(prev => ({ ...prev, [key]: value })); | ||
}; | ||
|
||
useEffect(() => { | ||
if (!data) return; | ||
const filteredData = filterData(data, filter, modelsData); | ||
setMapData(filteredData); | ||
}, [data, filter, modelsData]); | ||
|
||
return ( | ||
<> | ||
{isLoading && <LinearProgress />} | ||
<Grid container spacing={1} > | ||
<Grid size={{ xs: 12, sm: 12, md: 3, lg: 3, xl: 3 }}> | ||
<Card variant="outlined" sx={{ mb: 1 }}> | ||
<CardContent> | ||
<CardHeader title="Filters" /> | ||
<Filters filter={filter} handleChange={handleFilterChange} /> | ||
</CardContent> | ||
</Card > | ||
<Card variant="outlined" sx={{ mb: 1 }}> | ||
<CardContent> | ||
<CardHeader title="Stats" /> | ||
<SummaryStats data={mapData} /> | ||
</CardContent> | ||
</Card > | ||
</Grid> | ||
|
||
<Grid size={{ xs: 12, sm: 12, md: 9, lg: 9, xl: 9 }}> | ||
<FlightMap data={mapData} routes={!filter.no_routes} /> | ||
</Grid> | ||
</Grid> | ||
</> | ||
); | ||
} | ||
|
||
export default SummaryFlightMap; |
Oops, something went wrong.