fixed wrong env var, added base maps
This commit is contained in:
@@ -20,6 +20,10 @@ NEXT_PUBLIC_HUB_SERVER_URL=https://api.hub.premiumag.de
|
|||||||
NEXT_PUBLIC_DISPATCH_URL=https://dispatch.premiumag.de
|
NEXT_PUBLIC_DISPATCH_URL=https://dispatch.premiumag.de
|
||||||
NEXT_PUBLIC_DISPATCH_SERVER_URL=https://api.dispatch.premiumag.de
|
NEXT_PUBLIC_DISPATCH_SERVER_URL=https://api.dispatch.premiumag.de
|
||||||
|
|
||||||
|
|
||||||
|
NEXT_PUBLIC_ESRI_ACCESS_TOKEN=
|
||||||
|
NEXT_PUBLIC_OPENAIP_ACCESS=6e85069940543ef02f8615b737059d98
|
||||||
|
|
||||||
# ───────────────────────────────────────────────
|
# ───────────────────────────────────────────────
|
||||||
# 🗄️ Datenbank
|
# 🗄️ Datenbank
|
||||||
# ───────────────────────────────────────────────
|
# ───────────────────────────────────────────────
|
||||||
|
|||||||
@@ -9,3 +9,5 @@ DATABASE_URL=postgresql://persistant-data:persistant-data-pw@localhost:5432/var
|
|||||||
NEXT_PUBLIC_LIVEKIT_URL=ws://localhost:7880
|
NEXT_PUBLIC_LIVEKIT_URL=ws://localhost:7880
|
||||||
LIVEKIT_API_KEY=APIAnsGdtdYp2Ho
|
LIVEKIT_API_KEY=APIAnsGdtdYp2Ho
|
||||||
LIVEKIT_API_SECRET=tdPjVsYUx8ddC7K9NvdmVAeLRF9GeADD6Fedm1x63fWC
|
LIVEKIT_API_SECRET=tdPjVsYUx8ddC7K9NvdmVAeLRF9GeADD6Fedm1x63fWC
|
||||||
|
NEXT_PUBLIC_ESRI_ACCESS_TOKEN=
|
||||||
|
NEXT_PUBLIC_OPENAIP_ACCESS=
|
||||||
@@ -1,7 +1,272 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { usePannelStore } from "_store/pannelStore";
|
import { usePannelStore } from "_store/pannelStore";
|
||||||
import { useEffect } from "react";
|
import { Control, Icon, LatLngExpression } from "leaflet";
|
||||||
import { TileLayer, useMap } from "react-leaflet";
|
import { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
LayerGroup,
|
||||||
|
LayersControl,
|
||||||
|
TileLayer,
|
||||||
|
useMap,
|
||||||
|
WMSTileLayer,
|
||||||
|
GeoJSON,
|
||||||
|
Circle,
|
||||||
|
useMapEvent,
|
||||||
|
FeatureGroup,
|
||||||
|
Marker,
|
||||||
|
Tooltip,
|
||||||
|
} from "react-leaflet";
|
||||||
|
// @ts-ignore
|
||||||
|
import type { FeatureCollection, Geometry } from "geojson";
|
||||||
|
import L from "leaflet";
|
||||||
|
import LEITSTELLENBERECHE from "./_geojson/Leitstellen_VAR.json";
|
||||||
|
import { createCustomMarker } from "_components/map/_components/createCustomMarker";
|
||||||
|
import { Station } from "@repo/db";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { getStationsAPI } from "_querys/stations";
|
||||||
|
|
||||||
|
const RadioAreaLayer = () => {
|
||||||
|
const getColor = (randint: number) => {
|
||||||
|
switch (randint) {
|
||||||
|
case 1:
|
||||||
|
return "#2b6eff";
|
||||||
|
case 2:
|
||||||
|
return "#233ee5";
|
||||||
|
case 3:
|
||||||
|
return "#7BA5FF";
|
||||||
|
case 4:
|
||||||
|
return "#5087FF";
|
||||||
|
default:
|
||||||
|
return "#7f7f7f";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GeoJSON
|
||||||
|
data={LEITSTELLENBERECHE as FeatureCollection<Geometry>}
|
||||||
|
style={(feature) => {
|
||||||
|
if (!feature || !feature.properties) return {}; // Early return if feature or its properties are undefined
|
||||||
|
return {
|
||||||
|
color: getColor(feature.properties.randint),
|
||||||
|
weight: 1.5,
|
||||||
|
className: "no-pointer",
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
onEachFeature={(feature, layer) => {
|
||||||
|
if (feature && feature.properties && feature.properties.name) {
|
||||||
|
layer.bindTooltip(
|
||||||
|
new L.Tooltip({
|
||||||
|
content: feature.properties.name,
|
||||||
|
direction: "top",
|
||||||
|
sticky: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const StationsLayer = ({ attribution }: { attribution: Control.Attribution }) => {
|
||||||
|
const { data: stations } = useQuery({
|
||||||
|
queryKey: ["stations"],
|
||||||
|
queryFn: () => getStationsAPI(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const [selectedStations, setSelectedStations] = useState<Station["id"][]>([]);
|
||||||
|
const [stationsWithIcon, setStationsWithIcon] = useState<(Station & { icon?: string })[]>([]); // Zustand für die Stationen mit Icon
|
||||||
|
const attributionText = "";
|
||||||
|
|
||||||
|
const resetSelection = () => {
|
||||||
|
setSelectedStations([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
useMapEvent("click", () => {
|
||||||
|
resetSelection();
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleMarkerClick = (stationId: number) => {
|
||||||
|
if (selectedStations.includes(stationId)) {
|
||||||
|
setSelectedStations((prevStations) => prevStations.filter((s) => s !== stationId));
|
||||||
|
} else {
|
||||||
|
setSelectedStations((prevStations) => [...prevStations, stationId]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Erstelle die Icons für alle Stationen
|
||||||
|
|
||||||
|
const fetchIcons = async () => {
|
||||||
|
if (!stations) return;
|
||||||
|
const urls = await Promise.all(
|
||||||
|
stations.map(async (station) => {
|
||||||
|
return createCustomMarker(station.operator);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
setStationsWithIcon(stations.map((station, index) => ({ ...station, icon: urls[index] })));
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchIcons();
|
||||||
|
}, [stations]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FeatureGroup>
|
||||||
|
{stationsWithIcon.map((station) => {
|
||||||
|
const coordinates: LatLngExpression = [station.latitude, station.longitude];
|
||||||
|
const typeLabel = station.bosUse.charAt(0).toUpperCase();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Marker
|
||||||
|
key={`marker-${station.id}`}
|
||||||
|
position={coordinates}
|
||||||
|
icon={
|
||||||
|
new Icon({
|
||||||
|
iconUrl: station.icon,
|
||||||
|
iconSize: [30, 30],
|
||||||
|
iconAnchor: [15, 15],
|
||||||
|
tooltipAnchor: [0, 20],
|
||||||
|
className: station.hideRangeRings ? "no-pointer" : "pointer",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
eventHandlers={{
|
||||||
|
click: () => {
|
||||||
|
if (!station.hideRangeRings) handleMarkerClick(station.id);
|
||||||
|
},
|
||||||
|
add: () => attribution.addAttribution(attributionText),
|
||||||
|
remove: () => attribution.removeAttribution(attributionText),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tooltip direction="top" sticky>
|
||||||
|
<div style={{ textAlign: "center" }}>
|
||||||
|
<strong>{station.bosCallsign}</strong>
|
||||||
|
<small style={{ fontWeight: "bold", fontSize: "0.7em" }}>{` (${typeLabel})`}</small>
|
||||||
|
<br />
|
||||||
|
<small>
|
||||||
|
{[
|
||||||
|
station.hasWinch ? "W" : null,
|
||||||
|
station.is24h ? "24h" : null,
|
||||||
|
station.hasNvg ? "N" : null,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(", ")}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</Marker>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{selectedStations.map((stationId) => {
|
||||||
|
const station = stations?.find((s) => s.id === stationId);
|
||||||
|
|
||||||
|
if (!station) return null;
|
||||||
|
|
||||||
|
const center: LatLngExpression = [station.latitude, station.longitude];
|
||||||
|
return (
|
||||||
|
<div key={`marker-${stationId}`}>
|
||||||
|
<Circle
|
||||||
|
center={center}
|
||||||
|
radius={(station.aircraftSpeed * 1000) / 6}
|
||||||
|
color="#0e0ecf"
|
||||||
|
fillOpacity={0}
|
||||||
|
weight={2}
|
||||||
|
/>
|
||||||
|
<Circle
|
||||||
|
center={center}
|
||||||
|
radius={(station.aircraftSpeed * 1000) / 3}
|
||||||
|
color="navy"
|
||||||
|
fillOpacity={0}
|
||||||
|
weight={2}
|
||||||
|
/>
|
||||||
|
{(station.bosUse === "SECONDARY" || station.bosUse === "DUAL_USE") && (
|
||||||
|
<Circle
|
||||||
|
center={center}
|
||||||
|
radius={station.aircraftSpeed * 1000}
|
||||||
|
color="maroon"
|
||||||
|
fillOpacity={0}
|
||||||
|
weight={1}
|
||||||
|
dashArray="40,30"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</FeatureGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const EsriSatellite = ({ attribution }: { attribution: Control.Attribution }) => {
|
||||||
|
const accessToken = process.env.NEXT_PUBLIC_ESRI_ACCESS_TOKEN || "";
|
||||||
|
|
||||||
|
const attributionText = "Sources: Esri, TomTom, Garmin, FAO, NOAA, USGS";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Satellite Imagery Layer; API KEY PROVIDED BY VAR0002 */}
|
||||||
|
<TileLayer
|
||||||
|
eventHandlers={{
|
||||||
|
add: () => attribution.addAttribution(attributionText),
|
||||||
|
remove: () => attribution.removeAttribution(attributionText),
|
||||||
|
}}
|
||||||
|
url={`https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}?token=${accessToken}`}
|
||||||
|
tileSize={256}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const StrassentexteEsri = () => {
|
||||||
|
return (
|
||||||
|
<WMSTileLayer
|
||||||
|
url="https://tiledbasemaps.arcgis.com/arcgis/rest/services/Reference/World_Transportation/MapServer/tile/{z}/{y}/{x}"
|
||||||
|
format="image/png"
|
||||||
|
transparent
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const OpenAIP = ({ attribution }: { attribution: Control.Attribution }) => {
|
||||||
|
const accessToken = process.env.NEXT_PUBLIC_OPENAIP_ACCESS;
|
||||||
|
const attributionText = '© <a href="https://www.openaip.net" target="_blank">OpenAIP</a>';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TileLayer
|
||||||
|
eventHandlers={{
|
||||||
|
add: () => attribution.addAttribution(attributionText),
|
||||||
|
remove: () => attribution.removeAttribution(attributionText),
|
||||||
|
}}
|
||||||
|
url={`https://api.tiles.openaip.net/api/data/openaip/{z}/{x}/{y}.png?apiKey=${accessToken}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const NiederschlagOverlay = ({ attribution }: { attribution: Control.Attribution }) => {
|
||||||
|
let tileLayer: L.TileLayer | null = null;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (tileLayer) {
|
||||||
|
tileLayer.bringToFront();
|
||||||
|
}
|
||||||
|
}, [tileLayer]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WMSTileLayer
|
||||||
|
ref={(layer) => {
|
||||||
|
if (layer) {
|
||||||
|
tileLayer = layer;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
eventHandlers={{
|
||||||
|
add: () => attribution.addAttribution("Quelle: Deutscher Wetterdienst"),
|
||||||
|
remove: () => attribution.removeAttribution("Quelle: Deutscher Wetterdienst"),
|
||||||
|
}}
|
||||||
|
url="https://maps.dwd.de/geoserver/wms?"
|
||||||
|
format="image/png"
|
||||||
|
layers="dwd:Niederschlagsradar"
|
||||||
|
transparent
|
||||||
|
opacity={0.7}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const BaseMaps = () => {
|
export const BaseMaps = () => {
|
||||||
const map = useMap();
|
const map = useMap();
|
||||||
@@ -14,16 +279,39 @@ export const BaseMaps = () => {
|
|||||||
}, [isPannelOpen]);
|
}, [isPannelOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<LayersControl position="topleft">
|
||||||
<TileLayer
|
<LayersControl.Overlay name={"Funknetzbereiche"}>
|
||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
<RadioAreaLayer />
|
||||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
</LayersControl.Overlay>
|
||||||
/>
|
<LayersControl.Overlay name={"Niederschlag"}>
|
||||||
<TileLayer
|
<NiederschlagOverlay attribution={map.attributionControl} />
|
||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
</LayersControl.Overlay>
|
||||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
||||||
className="invert-100 grayscale"
|
<LayersControl.Overlay name={"LRZs"}>
|
||||||
/>
|
<StationsLayer attribution={map.attributionControl} />
|
||||||
</>
|
</LayersControl.Overlay>
|
||||||
|
<LayersControl.Overlay name={"OpenAIP"}>
|
||||||
|
<OpenAIP attribution={map.attributionControl} />
|
||||||
|
</LayersControl.Overlay>
|
||||||
|
<LayersControl.BaseLayer name="OpenStreetMap" checked>
|
||||||
|
<TileLayer
|
||||||
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
|
/>
|
||||||
|
</LayersControl.BaseLayer>
|
||||||
|
<LayersControl.BaseLayer name="OpenStreetMap Dark">
|
||||||
|
<TileLayer
|
||||||
|
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
|
className="invert-100 grayscale"
|
||||||
|
/>
|
||||||
|
</LayersControl.BaseLayer>
|
||||||
|
<LayersControl.BaseLayer name="ESRI Satellite">
|
||||||
|
<LayerGroup>
|
||||||
|
<EsriSatellite attribution={map.attributionControl} />
|
||||||
|
<StrassentexteEsri />
|
||||||
|
</LayerGroup>
|
||||||
|
</LayersControl.BaseLayer>
|
||||||
|
</LayersControl>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
const getOperatorColor = (operator: string) => {
|
||||||
|
switch (operator) {
|
||||||
|
case "ADAC":
|
||||||
|
case "ÖAMTC":
|
||||||
|
case "ANWB":
|
||||||
|
return "#d5b500";
|
||||||
|
case "BMI":
|
||||||
|
return "#FF5900";
|
||||||
|
case "DRF":
|
||||||
|
case "ARA":
|
||||||
|
case "NHC":
|
||||||
|
return "#eb0000";
|
||||||
|
case "JLR":
|
||||||
|
return "#050073";
|
||||||
|
case "BUND":
|
||||||
|
return "#334811";
|
||||||
|
case "Rega":
|
||||||
|
return "#ED0000";
|
||||||
|
case "AAA":
|
||||||
|
return "#164070";
|
||||||
|
case "Air Zermatt":
|
||||||
|
return "#0094d4";
|
||||||
|
case "INAER":
|
||||||
|
return "#e96b22";
|
||||||
|
case "Heli Austria":
|
||||||
|
return "#009de2";
|
||||||
|
case "Air Glaciers":
|
||||||
|
return "#004680";
|
||||||
|
case "Wucher":
|
||||||
|
return "#F97514";
|
||||||
|
case "SHS":
|
||||||
|
return "#c50014";
|
||||||
|
case "Schenk Air":
|
||||||
|
return "black";
|
||||||
|
case "LAR":
|
||||||
|
return "#ED174F";
|
||||||
|
case "AAD":
|
||||||
|
return "#7F1B24";
|
||||||
|
default:
|
||||||
|
return "gray";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createCustomMarker = async (operator: string) => {
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
canvas.width = 30; // Breite des Markers
|
||||||
|
canvas.height = 30; // Höhe des Markers
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
const color = getOperatorColor(operator);
|
||||||
|
if (ctx) {
|
||||||
|
// Erstelle den farbigen Kreis
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(15, 15, 10, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Füge den weißen Rand hinzu
|
||||||
|
ctx.strokeStyle = ["ADAC", "ÖAMTC", "ANWB"].includes(operator) ? "black" : "white"; // Randfarbe
|
||||||
|
ctx.lineWidth = ["ADAC", "ÖAMTC", "ANWB"].includes(operator) ? 1 : 2; // Dicke des Randes
|
||||||
|
ctx.stroke(); // Zeichne den Rand
|
||||||
|
|
||||||
|
const img = new Image();
|
||||||
|
img.src = ["ADAC", "ÖAMTC", "ANWB"].includes(operator)
|
||||||
|
? "/icons/Station_Icon_black.svg"
|
||||||
|
: "/icons/Station_Icon.svg";
|
||||||
|
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
img.onload = () => {
|
||||||
|
ctx.drawImage(img, 8, 8, 15, 15); // Draw icon on the canvas
|
||||||
|
resolve(); // Resolve the promise when the image loads
|
||||||
|
};
|
||||||
|
|
||||||
|
img.onerror = () => {
|
||||||
|
reject(new Error("Image failed to load.")); // Handle image load failure
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error("Canvas context is not initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas.toDataURL(); // Return the image data URL after the marker is created
|
||||||
|
};
|
||||||
113241
apps/dispatch/app/_components/map/_geojson/Leitstellen_VAR.json
Normal file
113241
apps/dispatch/app/_components/map/_geojson/Leitstellen_VAR.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,11 +5,10 @@ import { verify } from "jsonwebtoken";
|
|||||||
export const PUT = async (req: Request) => {
|
export const PUT = async (req: Request) => {
|
||||||
const session = await getServerSession();
|
const session = await getServerSession();
|
||||||
const token = req.headers.get("authorization")?.split(" ")[1];
|
const token = req.headers.get("authorization")?.split(" ")[1];
|
||||||
if (!token)
|
if (!token) return Response.json({ message: "Missing token" }, { status: 401 });
|
||||||
return Response.json({ message: "Missing token" }, { status: 401 });
|
|
||||||
|
|
||||||
const payload = await new Promise<User>((resolve, reject) => {
|
const payload = await new Promise<User>((resolve, reject) => {
|
||||||
verify(token, process.env.NEXTAUTH_HUB_SECRET as string, (err, decoded) => {
|
verify(token, process.env.AUTH_HUB_SECRET as string, (err, decoded) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@@ -18,8 +17,7 @@ export const PUT = async (req: Request) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!session && !payload)
|
if (!session && !payload) return Response.json({ message: "Unauthorized" }, { status: 401 });
|
||||||
return Response.json({ message: "Unauthorized" }, { status: 401 });
|
|
||||||
|
|
||||||
const userId = session?.user.id || payload.id;
|
const userId = session?.user.id || payload.id;
|
||||||
const { position, h145 } = (await req.json()) as {
|
const { position, h145 } = (await req.json()) as {
|
||||||
@@ -47,10 +45,7 @@ export const PUT = async (req: Request) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!activeAircraft) {
|
if (!activeAircraft) {
|
||||||
return Response.json(
|
return Response.json({ message: "No active aircraft found" }, { status: 400 });
|
||||||
{ message: "No active aircraft found" },
|
|
||||||
{ status: 400 },
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const positionLog = await prisma.positionLog.create({
|
const positionLog = await prisma.positionLog.create({
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
"@types/jsonwebtoken": "^9.0.9",
|
"@types/jsonwebtoken": "^9.0.9",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"geojson": "^0.5.0",
|
||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
|
|||||||
1
apps/dispatch/public/icons/Station_Icon.svg
Normal file
1
apps/dispatch/public/icons/Station_Icon.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M360-440v-240q-100 0-170 70t-70 170h240Zm160 160v-400h-80v320H120v80h400Zm80-128 240-24v-48H600v72ZM520-80H120v-80h400v80Zm80-120H120q-33 0-56.5-23.5T40-280v-160q0-134 93-227t227-93h240v200h200l40-80h80v280l-320 32v128Zm160-600H120v-80h640v80ZM600-408v-72 72Zm-80 128Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 393 B |
3
apps/dispatch/public/icons/Station_Icon_black.svg
Normal file
3
apps/dispatch/public/icons/Station_Icon_black.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000" stroke="#000000" stroke-width="10">
|
||||||
|
<path d="M360-440v-240q-100 0-170 70t-70 170h240Zm160 160v-400h-80v320H120v80h400Zm80-128 240-24v-48H600v72ZM520-80H120v-80h400v80Zm80-120H120q-33 0-56.5-23.5T40-280v-160q0-134 93-227t227-93h240v200h200l40-80h80v280l-320 32v128Zm160-600H120v-80h640v80ZM600-408v-72 72Zm-80 128Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 435 B |
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -56,6 +56,9 @@ importers:
|
|||||||
clsx:
|
clsx:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
|
geojson:
|
||||||
|
specifier: ^0.5.0
|
||||||
|
version: 0.5.0
|
||||||
i:
|
i:
|
||||||
specifier: ^0.3.7
|
specifier: ^0.3.7
|
||||||
version: 0.3.7
|
version: 0.3.7
|
||||||
@@ -2648,6 +2651,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
geojson@0.5.0:
|
||||||
|
resolution: {integrity: sha512-/Bx5lEn+qRF4TfQ5aLu6NH+UKtvIv7Lhc487y/c8BdludrCTpiWf9wyI0RTyqg49MFefIAvFDuEi5Dfd/zgNxQ==}
|
||||||
|
engines: {node: '>= 0.10'}
|
||||||
|
|
||||||
get-caller-file@2.0.5:
|
get-caller-file@2.0.5:
|
||||||
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||||
engines: {node: 6.* || 8.* || >= 10.*}
|
engines: {node: 6.* || 8.* || >= 10.*}
|
||||||
@@ -6953,6 +6960,8 @@ snapshots:
|
|||||||
|
|
||||||
gensync@1.0.0-beta.2: {}
|
gensync@1.0.0-beta.2: {}
|
||||||
|
|
||||||
|
geojson@0.5.0: {}
|
||||||
|
|
||||||
get-caller-file@2.0.5: {}
|
get-caller-file@2.0.5: {}
|
||||||
|
|
||||||
get-intrinsic@1.3.0:
|
get-intrinsic@1.3.0:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"AUTH_DISPATCH_SECRET",
|
"AUTH_DISPATCH_SECRET",
|
||||||
"LIVEKIT_API_KEY",
|
"LIVEKIT_API_KEY",
|
||||||
"LIVEKIT_API_SECRET",
|
"LIVEKIT_API_SECRET",
|
||||||
"NEXTAUTH_HUB_SECRET",
|
"AUTH_HUB_SECRET",
|
||||||
"AUTH_DISPATCH_COOKIE_PREFIX"
|
"AUTH_DISPATCH_COOKIE_PREFIX"
|
||||||
],
|
],
|
||||||
"ui": "tui",
|
"ui": "tui",
|
||||||
|
|||||||
Reference in New Issue
Block a user