better map

This commit is contained in:
2025-07-22 10:00:35 -05:00
parent c353a037d6
commit 2537609aea
5 changed files with 445 additions and 138 deletions
+1 -1
View File
@@ -48,4 +48,4 @@ export var currentServers: Server[] = servers.data.map(
} as Server)
);
servers.data.map(useServer);
servers.data.forEach(useServer);
+27 -129
View File
@@ -1,132 +1,30 @@
import React, { useRef, useEffect, useState } from "react";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import Server from "@/types/server";
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";
import ServerComponent, { ByteUnit, UsageBar } from "../dashboard/server";
import {
Cpu,
HardDrive,
MapPin,
MemoryStick,
Network,
ServerIcon,
} from "lucide-react";
export default function Map({ servers }: { servers: Server[] }) {
const mapContainer = useRef<HTMLDivElement | null>(null);
const map = useRef<maplibregl.Map | null>(null);
const markers = useRef<maplibregl.Marker[]>([]);
const [sheet, setSheet] = useState<number | undefined>();
// Initialize map only once
useEffect(() => {
if (map.current || !mapContainer.current) return;
map.current = new maplibregl.Map({
container: mapContainer.current,
style: "/map_style.json",
center: [0, 0],
zoom: 2,
});
return () => {
// Clean up markers
markers.current.forEach((m) => m.remove());
markers.current = [];
// Clean up map
map.current?.remove();
map.current = null;
};
}, []);
// Update markers when servers change
useEffect(() => {
if (!map.current) return;
// Remove old markers
markers.current.forEach((marker) => marker.remove());
markers.current = [];
// Add new markers
servers.forEach((server, i) => {
if (server.coordinates) {
const marker = new maplibregl.Marker({ color: "#ff3e00" })
.setLngLat(server.coordinates)
.addTo(map.current!);
marker.getElement().addEventListener("click", () => {
setSheet(i);
});
markers.current.push(marker);
}
});
}, [servers]);
import Map, { Marker } from 'react-map-gl/maplibre';
import 'maplibre-gl/dist/maplibre-gl.css';
import Server from '@/types/server';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { MapPin } from 'lucide-react';
import ServerComponent from '../dashboard/server';
export default function ServerMap({ servers }: { servers: Server[] }) {
return (
<div ref={mapContainer} className="w-full h-full">
<Sheet open={sheet != undefined} onOpenChange={() => setSheet(undefined)}>
{sheet != undefined && (
<SheetContent>
<SheetHeader>
<div className="flex items-center gap-2">
<div className="bg-muted text-muted-foreground p-1.5 rounded-md">
<ServerIcon className="w-4.5 h-4.5 text-current my-auto" />
</div>
<div>
<SheetTitle>{servers[sheet].name}</SheetTitle>
<SheetDescription className="flex gap-2">
{servers[sheet].status[0].toUpperCase() + servers[sheet].status.slice(1)}{" "}
{" · "}
{servers[sheet].ip}
{servers[sheet].location && (
<span className="flex items-center gap-0.5">
<MapPin size={14} />
{servers[sheet].location}
</span>
)}
</SheetDescription>
</div>
</div>
</SheetHeader>
<div className="px-4 gap-4 flex flex-col">
<UsageBar
value={servers[sheet].cpu}
Icon={Cpu}
text="CPU"
color="var(--chart-2)"
/>
<UsageBar
value={servers[sheet].memory}
Icon={MemoryStick}
text="Memory"
color="var(--chart-1)"
/>
<UsageBar
value={servers[sheet].storage}
Icon={HardDrive}
text="Storage"
color="var(--chart-5)"
/>
<ByteUnit
value={servers[sheet].network}
Icon={Network}
text="Network"
color="var(--chart-3)"
/>
</div>
</SheetContent>
)}
</Sheet>
</div>
<Map
style={{width: "100%", height: "100%"}}
mapStyle="/map_style.json"
>
{
servers.filter(s => s.coordinates).map((s, i) => (
<Marker longitude={s.coordinates?.[0] || 0} latitude={s.coordinates?.[1] || 0} color="red" key={i}>
<Popover>
<PopoverTrigger>
<MapPin />
</PopoverTrigger>
<PopoverContent className='w-96'>
<ServerComponent server={s} index={i} session={null} />
</PopoverContent>
</Popover>
</Marker>
))
}
</Map>
);
}
}
+48
View File
@@ -0,0 +1,48 @@
"use client"
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/lib/utils"
function Popover({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
return <PopoverPrimitive.Root data-slot="popover" {...props} />
}
function PopoverTrigger({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
}
function PopoverContent({
className,
align = "center",
sideOffset = 4,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
return (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
data-slot="popover-content"
align={align}
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
)
}
function PopoverAnchor({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
}
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }