diff --git a/backend/package.json b/backend/package.json index 5ff353d..08b1fb8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "loa-viewer-backend", - "version": "2.1.0", + "version": "2.2.0", "description": "", "main": "app.js", "scripts": { diff --git a/backend/src/models/sector.model.ts b/backend/src/models/sector.model.ts index bed8212..2939673 100644 --- a/backend/src/models/sector.model.ts +++ b/backend/src/models/sector.model.ts @@ -10,6 +10,7 @@ export const sectorSchema = new mongoose.Schema({ }); export const airspaceSchema = new mongoose.Schema({ + country: { type: String, required: true }, id: { type: String, required: true }, group: { type: String, required: true }, owner: { type: [String], required: true }, diff --git a/backend/src/services/sector.service.ts b/backend/src/services/sector.service.ts index fce46a6..c57c741 100644 --- a/backend/src/services/sector.service.ts +++ b/backend/src/services/sector.service.ts @@ -23,6 +23,7 @@ async function retrieveAirspacesFromCountries(countries: string[]): Promise([]); - const [drawnConditions, setDrawnConditions] = useState([]); - const [searchInput, setSearchInput] = useState(''); const [loading, setLoading] = useState(true); + // persistent data + const [conditions, setConditions] = useState([]); const [airspaces, setAirspaces] = useState([]); - const [drawnAirspaces, setDrawnAirspaces] = useState([]); + const selectableGroups = ['EDMM', 'EDWW', 'EDGG', 'EDYY', 'EDUU', 'APP'].sort((a, b) => a.localeCompare(b)); - const selectableGroups = ['EDMM', 'EDWW', 'EDGG', 'EDYY', 'EDUU', 'APP']; - const sortedSelectableGroups = selectableGroups.sort((a, b) => a.localeCompare(b)); - const [allStations, setAllStations] = useState([]); + // Toolbar: + // Dropdown menu const [selectedSector, setSelectedSector] = useState('GIN'); const [selectedFir, setSelectedFir] = useState('EDGG'); + // Button + const [showVerticalLimits, setShowVerticalLimits] = useState(false); + const [conditionSearchRange, setConditionSearchRange] = useState<'ofSelectedSector' | 'all'>('ofSelectedSector'); + const [filterFromToSector, setFilterFromToSector] = useState(false); + const [filterFromToSectorSelection, setFilterFromToSectorSelection] = useState<'from sector' | 'to sector'>('to sector'); + // search bar + const [searchInput, setSearchInput] = useState(''); + + // filtered data - based on input in toolbar + const [drawnConditions, setDrawnConditions] = useState([]); + const [drawnAirspaces, setDrawnAirspaces] = useState([]); + const [allStations, setAllStations] = useState([]); useEffect(() => { conditionService.getConditions().then((data: FrontendCondition[]) => { - const convertedData: FrontendCondition[] = data.map((element: FrontendCondition) => { + const convertedConditionData: FrontendCondition[] = data.map((element: FrontendCondition) => { return { _id: element._id, aerodrome: element.aerodrome, @@ -47,18 +60,19 @@ export default function LoaViewerMap() { to_fir: element.to_fir, }; }); - setConditions(convertedData); + setConditions(convertedConditionData); }); - sectorService.getWaypoints().then((data: Airspace[]) => { - const convertedData: Airspace[] = data.map((element: Airspace) => { + sectorService.getSectors().then((data: Airspace[]) => { + const convertedAirspaceData: Airspace[] = data.map((element: Airspace) => { return { + country: element.country, id: element.id, group: element.group, owner: element.owner, sectors: element.sectors, }; }); - setAirspaces(convertedData); + setAirspaces(convertedAirspaceData); }); setLoading(false); @@ -67,53 +81,93 @@ export default function LoaViewerMap() { const debounceSearch = useDebounce(searchInput, 500); useEffect(() => { if (!loading) { - const searchConditions = filterConditionsService(conditions, searchInput, selectedSector); + const searchConditions = filterConditionsService(conditions, searchInput, conditionSearchRange === 'ofSelectedSector' ? selectedSector : undefined, filterFromToSector === true ? filterFromToSectorSelection : false); groupConditionsByCop(searchConditions).then(groupedConditions => { setDrawnConditions(groupedConditions); }); } - }, [debounceSearch, loading, searchInput, conditions, selectedSector]); + }, [debounceSearch, loading, searchInput, conditions, selectedSector, conditionSearchRange, filterFromToSector, filterFromToSectorSelection]); useEffect(() => { const stationsSet: Set = new Set(); for (const airspace of airspaces) { - if (airspace.group === selectedFir) { - const owner = airspace.owner[0]; - // filter approach stations not belonging to vACC Germany - all approach sectors are not using Vatsim callsigns i.e. no _ in their name - const isNonGermanApproachStation = owner.includes('_'); - // filter Maastricht Sectors belonging to vACC Germany - sectors belonging to vACC Germany have 3 or more characters - const isGermanMaastrichtSector = owner.length > 2; - if (!isNonGermanApproachStation && isGermanMaastrichtSector) { - stationsSet.add(owner); - } + if (airspace.country === 'germany' && airspace.group === selectedFir) { + stationsSet.add(airspace.owner[0]); } } const stations: string[] = Array.from(stationsSet); const sortedStations = stations.sort((a, b) => a.localeCompare(b)); setAllStations(sortedStations); - }, [selectedFir, loading, airspaces]); + + // If the FIR changes, select the first station from the FIR + if (!sortedStations.includes(selectedSector as string) && sortedStations.length !== 0) { + setSelectedSector(sortedStations[0]); + } + }, [selectedSector, selectedFir, loading, airspaces]); useEffect(() => { const filtered = airspaces.filter(airspace => airspace.owner[0] === selectedSector); setDrawnAirspaces(filtered); }, [selectedFir, loading, airspaces, selectedSector]); + const startContent = [ + setSearchInput(e.target.value)} />, + , +