better map
This commit is contained in:
@@ -48,4 +48,4 @@ export var currentServers: Server[] = servers.data.map(
|
||||
} as Server)
|
||||
);
|
||||
|
||||
servers.data.map(useServer);
|
||||
servers.data.forEach(useServer);
|
||||
|
||||
+27
-129
@@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
Reference in New Issue
Block a user