Updated UI
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,6 +9,7 @@ node_modules
|
||||
target/
|
||||
dist/
|
||||
Cargo.lock
|
||||
ssl/
|
||||
|
||||
.DS_Store
|
||||
|
||||
|
||||
4
Makefile
4
Makefile
@@ -109,3 +109,7 @@ build: ## Build a specific docker image (`make build f=httpd`)
|
||||
${folder}
|
||||
|
||||
docker-build: build
|
||||
|
||||
cert: domain=$(if $(d),$(d),aviation.bensherriff.com)
|
||||
cert: ## Generate a cert for the given domain
|
||||
@./scripts/generate_cert.sh ${domain}
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
ProxyPreserveHost On
|
||||
LogLevel warn
|
||||
|
||||
#SSLEngine on
|
||||
#SSLCertificateFile /path/to/your/cert.pem
|
||||
#SSLCertificateKeyFile /path/to/your/privkey.pem
|
||||
|
||||
#Protocols h2 http/1.1
|
||||
|
||||
ProxyPass "/api" "${API_PROTOCOL}://${HTTPD_API_HOST}:${API_PORT}/api"
|
||||
ProxyPassReverse "/api" "${API_PROTOCOL}://${HTTPD_API_HOST}:${API_PORT}/api"
|
||||
|
||||
|
||||
@@ -158,14 +158,14 @@ LoadModule proxy_http_module modules/mod_proxy_http.so
|
||||
#LoadModule session_dbd_module modules/mod_session_dbd.so
|
||||
#LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
|
||||
#LoadModule slotmem_plain_module modules/mod_slotmem_plain.so
|
||||
#LoadModule ssl_module modules/mod_ssl.so
|
||||
LoadModule ssl_module modules/mod_ssl.so
|
||||
#LoadModule optional_hook_export_module modules/mod_optional_hook_export.so
|
||||
#LoadModule optional_hook_import_module modules/mod_optional_hook_import.so
|
||||
#LoadModule optional_fn_import_module modules/mod_optional_fn_import.so
|
||||
#LoadModule optional_fn_export_module modules/mod_optional_fn_export.so
|
||||
#LoadModule dialup_module modules/mod_dialup.so
|
||||
#LoadModule http2_module modules/mod_http2.so
|
||||
#LoadModule proxy_http2_module modules/mod_proxy_http2.so
|
||||
LoadModule http2_module modules/mod_http2.so
|
||||
LoadModule proxy_http2_module modules/mod_proxy_http2.so
|
||||
#LoadModule md_module modules/mod_md.so
|
||||
#LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
|
||||
#LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so
|
||||
|
||||
32
scripts/generate_cert.sh
Executable file
32
scripts/generate_cert.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: ./generate_cert.sh example.com
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "Usage: $0 <domain>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DOMAIN=$1
|
||||
DAYS=365
|
||||
SSL_DIR="./ssl"
|
||||
|
||||
# Create directory if it doesn't exist
|
||||
mkdir -p "$SSL_DIR"
|
||||
|
||||
KEY_PATH="${SSL_DIR}/${DOMAIN}.key"
|
||||
CRT_PATH="${SSL_DIR}/${DOMAIN}.crt"
|
||||
|
||||
echo "Generating self-signed certificate for ${DOMAIN}..."
|
||||
|
||||
openssl req -x509 -nodes -days ${DAYS} -newkey rsa:2048 \
|
||||
-keyout "${KEY_PATH}" \
|
||||
-out "${CRT_PATH}" \
|
||||
-subj "/CN=${DOMAIN}"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Successfully generated certificate:"
|
||||
echo "Private Key: ${KEY_PATH}"
|
||||
echo "Certificate: ${CRT_PATH}"
|
||||
else
|
||||
echo "Certificate generation failed."
|
||||
fi
|
||||
@@ -29,3 +29,39 @@ body,
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.map-button {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
z-index: 1000;
|
||||
|
||||
color: #000;
|
||||
background: #fff;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #ccc;
|
||||
border-bottom-width: 2px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
|
||||
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
line-height: 30px; /* Vertically center text */
|
||||
font-weight: bold;
|
||||
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background-color 0.2s, color 0.2s;
|
||||
}
|
||||
|
||||
.map-button.active {
|
||||
background-color: #228be6;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.map-button.active:hover {
|
||||
background-color: #187ed7;
|
||||
}
|
||||
|
||||
.map-button:hover {
|
||||
background: #e6e6e6;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MapContainer, TileLayer, ZoomControl } from 'react-leaflet';
|
||||
import { LayersControl, MapContainer, TileLayer, useMapEvents, ZoomControl } from 'react-leaflet';
|
||||
import '@mantine/core/styles.css';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import './App.css';
|
||||
@@ -12,8 +12,8 @@ import { useEffect, useState } from 'react';
|
||||
import { Airport } from '@lib/airport.types.ts';
|
||||
import AirportDrawer from '@components/AirportDrawer.tsx';
|
||||
import { getWeatherMapUrl } from '@lib/rainViewer.ts';
|
||||
import { Switch } from '@mantine/core';
|
||||
|
||||
import { IconRadar } from '@tabler/icons-react';
|
||||
import Cookies from 'js-cookie';
|
||||
// Fix Leaflet's default icon path issues with Webpack
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
@@ -26,13 +26,19 @@ L.Icon.Default.mergeOptions({
|
||||
});
|
||||
|
||||
const openStreetMapUrl = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
|
||||
const lightLayerUrl = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png';
|
||||
const darkLayerUrl = 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png';
|
||||
// const dark1Url = 'https://maps.rainviewer.com/data/v3/5/10/11.pbf';
|
||||
// const dark2Url = 'https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer/tile/2/0/3.pbf';
|
||||
const defaultZoom = 6;
|
||||
const defaultCenter: L.LatLngExpression = [38.944444, -77.455833];
|
||||
|
||||
function App() {
|
||||
const [airport, setAirport] = useState<Airport | null>(null);
|
||||
const [rainViewerUrl, setRainViewerUrl] = useState<string | null>(null);
|
||||
const [showRadar, setShowRadar] = useState<boolean>(false);
|
||||
const initialRadarValue = Cookies.get('showRadar') === 'true';
|
||||
const [showRadar, setShowRadar] = useState<boolean>(initialRadarValue);
|
||||
const [baseLayer, setBaseLayer] = useState<string>(Cookies.get('selectedBaseLayer') || 'Open Street Map');
|
||||
|
||||
useEffect(() => {
|
||||
if (showRadar) {
|
||||
@@ -40,7 +46,25 @@ function App() {
|
||||
setRainViewerUrl(url);
|
||||
});
|
||||
}
|
||||
}, [showRadar])
|
||||
}, [showRadar]);
|
||||
|
||||
function toggleRadar() {
|
||||
setShowRadar(prev => {
|
||||
const newValue = !prev;
|
||||
Cookies.set('showRadar', newValue.toString(), { expires: 7 });
|
||||
return newValue;
|
||||
});
|
||||
}
|
||||
|
||||
function BaseLayerChangeHandler() {
|
||||
useMapEvents({
|
||||
baselayerchange: (e) => {
|
||||
setBaseLayer(e.name);
|
||||
Cookies.set('selectedBaseLayer', e.name, { expires: 7 });
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='App'>
|
||||
@@ -61,25 +85,29 @@ function App() {
|
||||
scrollWheelZoom={true}
|
||||
zoomControl={false}
|
||||
>
|
||||
<ZoomControl position={'bottomright'} />
|
||||
<LayersControl>
|
||||
<LayersControl.BaseLayer checked={baseLayer === 'Open Street Map'} name={'Open Street Map'}>
|
||||
<TileLayer url={openStreetMapUrl} />
|
||||
{ rainViewerUrl && showRadar && (
|
||||
<TileLayer url={rainViewerUrl} opacity={0.5} zIndex={5} />
|
||||
)}
|
||||
</LayersControl.BaseLayer>
|
||||
<LayersControl.BaseLayer checked={baseLayer === 'Carto Light'} name={'Carto Light'}>
|
||||
<TileLayer url={lightLayerUrl} />
|
||||
</LayersControl.BaseLayer>
|
||||
<LayersControl.BaseLayer checked={baseLayer === 'Carto Dark'} name={'Carto Dark'}>
|
||||
<TileLayer url={darkLayerUrl} />
|
||||
</LayersControl.BaseLayer>
|
||||
</LayersControl>
|
||||
{rainViewerUrl && showRadar && <TileLayer url={rainViewerUrl} opacity={0.5} zIndex={5} />}
|
||||
<ZoomControl position={'bottomright'} />
|
||||
<AirportLayer setAirport={setAirport} />
|
||||
<BaseLayerChangeHandler />
|
||||
</MapContainer>
|
||||
<div style={{
|
||||
position: 'absolute', top: '100px', right: '10px', zIndex: 1000, backgroundColor: '#fff',
|
||||
borderRadius: '8px', boxShadow: '0 2px 6px rgba(0,0,0,0.15)', padding: '10px'
|
||||
}}>
|
||||
<Switch
|
||||
label="Radar"
|
||||
checked={showRadar}
|
||||
onChange={(event) => setShowRadar(event.currentTarget.checked)}
|
||||
<IconRadar
|
||||
onClick={toggleRadar}
|
||||
style={{ bottom: '80px' }}
|
||||
className={`map-button ${showRadar ? 'active' : ''}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -84,9 +84,9 @@ export default function AirportLayer({ setAirport }: { setAirport: (airport: Air
|
||||
|
||||
return (
|
||||
<>
|
||||
{sortedAirports.map((airport, index) => {
|
||||
return <AirportMarker airport={airport} index={index} setAirport={setAirport} />;
|
||||
})}
|
||||
{sortedAirports.map((airport, index) => (
|
||||
<AirportMarker key={index} airport={airport} index={index} setAirport={setAirport} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -126,9 +126,6 @@ export function Header() {
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(Cookies.get('logged_in'));
|
||||
console.log(Cookies.get('session'));
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box>
|
||||
|
||||
@@ -13,22 +13,19 @@ async function getWeatherMaps(): Promise<WeatherMaps | undefined> {
|
||||
}
|
||||
}
|
||||
|
||||
// const rainViewerUrl = 'https://tilecache.rainviewer.com/v2/radar/1744386000/256/{z}/{x}/{y}/2/1_1.png';
|
||||
// const rainViewerUrl = 'https://tilecache.rainviewer.com/v2/radar/1744386000/256/10/290/391/2/1_1.png'
|
||||
// https://api.rainviewer.com/public/weather-maps.json
|
||||
export async function getWeatherMapUrl(): Promise<string | null> {
|
||||
const weatherMaps = await getWeatherMaps();
|
||||
if (weatherMaps != undefined) {
|
||||
let url = weatherMaps.host;
|
||||
// url = 'https://cdn.rainviewer.com';
|
||||
let latest = "";
|
||||
if (weatherMaps.radar.nowcast.length > 0) {
|
||||
latest = weatherMaps.radar.nowcast[weatherMaps.radar.nowcast.length - 1].path;
|
||||
} else if (weatherMaps.radar.past.length > 0) {
|
||||
if (weatherMaps.radar.past.length > 0) {
|
||||
latest = weatherMaps.radar.past[weatherMaps.radar.past.length - 1].path;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
url += latest + "/256/{z}/{x}/{y}/2/1_1.png";
|
||||
// url += latest + "/256/{z}/{x}/{y}/255/1_1_1_0.webp";
|
||||
return url;
|
||||
} else {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user