Compare commits
2 Commits
e2f804bac7
...
90b190b8d5
Author | SHA1 | Date | |
---|---|---|---|
90b190b8d5 | |||
c10f4b43cb |
@@ -2,12 +2,12 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Breadcrumb,
|
Breadcrumb,
|
||||||
BreadcrumbItem,
|
BreadcrumbItem,
|
||||||
BreadcrumbLink,
|
BreadcrumbLink,
|
||||||
BreadcrumbList,
|
BreadcrumbList,
|
||||||
BreadcrumbPage,
|
BreadcrumbPage,
|
||||||
BreadcrumbSeparator,
|
BreadcrumbSeparator,
|
||||||
} from '@/components/ui/breadcrumb';
|
} from '@/components/ui/breadcrumb';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
@@ -31,7 +31,7 @@ export const Header = ({ title, breadcrumbs = [] }: HeaderProps) => {
|
|||||||
try {
|
try {
|
||||||
const list = await ListCharacters();
|
const list = await ListCharacters();
|
||||||
setChars((list as any[]).map((c: any) => ({ character_id: c.character_id, character_name: c.character_name })));
|
setChars((list as any[]).map((c: any) => ({ character_id: c.character_id, character_name: c.character_name })));
|
||||||
} catch {}
|
} catch { }
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -84,7 +84,7 @@ export const Header = ({ title, breadcrumbs = [] }: HeaderProps) => {
|
|||||||
</Breadcrumb>
|
</Breadcrumb>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h1 className="text-2xl font-bold text-white">{title}</h1>
|
<h1 className="text-2xl font-bold text-white">{title}</h1>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
|
@@ -96,7 +96,7 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
|
|||||||
|
|
||||||
type OffIndicator = { from: string; toRegion: string; count: number; color: string; angle: number; sampleTo?: string };
|
type OffIndicator = { from: string; toRegion: string; count: number; color: string; angle: number; sampleTo?: string };
|
||||||
const [offRegionIndicators, setOffRegionIndicators] = useState<OffIndicator[]>([]);
|
const [offRegionIndicators, setOffRegionIndicators] = useState<OffIndicator[]>([]);
|
||||||
const [meanInboundAngle, setMeanInboundAngle] = useState<Record<string, number>>({});
|
const [meanNeighborAngle, setMeanNeighborAngle] = useState<Record<string, number>>({});
|
||||||
const [charLocs, setCharLocs] = useState<Array<{ character_id: number; character_name: string; solar_system_name: string }>>([]);
|
const [charLocs, setCharLocs] = useState<Array<{ character_id: number; character_name: string; solar_system_name: string }>>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -133,7 +133,7 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
|
|||||||
setPositions(positions);
|
setPositions(positions);
|
||||||
const connections = computeNodeConnections(systems);
|
const connections = computeNodeConnections(systems);
|
||||||
setConnections(connections);
|
setConnections(connections);
|
||||||
// Compute per-system mean outbound BEARING (0=north, clockwise positive) to in-region neighbors
|
// Compute per-system mean outbound angle in screen coords (atan2(dy,dx)) to in-region neighbors
|
||||||
const angleMap: Record<string, number> = {};
|
const angleMap: Record<string, number> = {};
|
||||||
systems.forEach((sys, name) => {
|
systems.forEach((sys, name) => {
|
||||||
const neighbors = (sys.connectedSystems || '').split(',').map(s => s.trim()).filter(Boolean);
|
const neighbors = (sys.connectedSystems || '').split(',').map(s => s.trim()).filter(Boolean);
|
||||||
@@ -142,17 +142,17 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
|
|||||||
const neighbor = systems.get(n);
|
const neighbor = systems.get(n);
|
||||||
if (!neighbor) continue;
|
if (!neighbor) continue;
|
||||||
const dx = neighbor.x - sys.x;
|
const dx = neighbor.x - sys.x;
|
||||||
const dy = neighbor.y - sys.y; // screen coords (y down)
|
const dy = neighbor.y - sys.y; // y-down screen
|
||||||
const bearing = Math.atan2(dx, -dy); // bearing relative to north
|
const a = Math.atan2(dy, dx);
|
||||||
sumSin += Math.sin(bearing);
|
sumSin += Math.sin(a);
|
||||||
sumCos += Math.cos(bearing);
|
sumCos += Math.cos(a);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
angleMap[name] = Math.atan2(sumSin, sumCos); // average bearing
|
angleMap[name] = Math.atan2(sumSin, sumCos); // average angle
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setMeanInboundAngle(angleMap);
|
setMeanNeighborAngle(angleMap);
|
||||||
}, [systems]);
|
}, [systems]);
|
||||||
|
|
||||||
// Poll character locations every 7s and store those in this region
|
// Poll character locations every 7s and store those in this region
|
||||||
@@ -208,58 +208,49 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
|
|||||||
}
|
}
|
||||||
await ensureUniversePositions();
|
await ensureUniversePositions();
|
||||||
|
|
||||||
// Build indicators: group by from+toRegion
|
// Build indicators: group by from+toRegion; angle from local geometry only (meanNeighborAngle + PI)
|
||||||
const grouped: Map<string, OffIndicator> = new Map();
|
type Agg = { from: string; toRegion: string; count: number; sumRemoteSec: number; sampleTo?: string };
|
||||||
|
const grouped: Map<string, Agg> = new Map();
|
||||||
for (const [fromName, sys] of systems.entries()) {
|
for (const [fromName, sys] of systems.entries()) {
|
||||||
const neighbors = (sys.connectedSystems || '').split(',').map(s => s.trim()).filter(Boolean);
|
const neighbors = (sys.connectedSystems || '').split(',').map(s => s.trim()).filter(Boolean);
|
||||||
for (const n of neighbors) {
|
for (const n of neighbors) {
|
||||||
if (systems.has(n)) continue;
|
if (systems.has(n)) continue;
|
||||||
const toRegion = nameToRegion[n];
|
const toRegion = nameToRegion[n];
|
||||||
if (!toRegion || toRegion === regionName) continue;
|
if (!toRegion || toRegion === regionName) continue;
|
||||||
|
|
||||||
// compute color
|
|
||||||
const remote = regionSystemsCache.get(toRegion)?.get(n);
|
const remote = regionSystemsCache.get(toRegion)?.get(n);
|
||||||
const avgSec = ((sys.security || 0) + (remote?.security || 0)) / 2;
|
|
||||||
const color = getSecurityColor(avgSec);
|
|
||||||
|
|
||||||
// compute angle via universe region centroids
|
|
||||||
let angle: number | undefined = undefined;
|
|
||||||
const inbound = meanInboundAngle[fromName];
|
|
||||||
if (inbound !== undefined) {
|
|
||||||
angle = inbound; // mean outbound bearing already computed
|
|
||||||
} else {
|
|
||||||
const curPos = universeRegionPosCache.get(regionName);
|
|
||||||
const toPos = universeRegionPosCache.get(toRegion);
|
|
||||||
if (curPos && toPos) {
|
|
||||||
const dxr = toPos.x - curPos.x;
|
|
||||||
const dyr = toPos.y - curPos.y;
|
|
||||||
angle = Math.atan2(dxr, -dyr); // bearing to remote region
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (angle === undefined) {
|
|
||||||
// final fallback deterministic angle
|
|
||||||
let h = 0; const key = `${fromName}->${toRegion}`;
|
|
||||||
for (let i = 0; i < key.length; i++) h = (h * 31 + key.charCodeAt(i)) >>> 0;
|
|
||||||
angle = (h % 360) * (Math.PI / 180);
|
|
||||||
}
|
|
||||||
// Flip 180° so indicator points away from existing connections
|
|
||||||
angle = angle + Math.PI;
|
|
||||||
|
|
||||||
const gkey = `${fromName}__${toRegion}`;
|
const gkey = `${fromName}__${toRegion}`;
|
||||||
const prev = grouped.get(gkey);
|
const agg = grouped.get(gkey) || { from: fromName, toRegion, count: 0, sumRemoteSec: 0, sampleTo: n };
|
||||||
if (prev) {
|
agg.count += 1;
|
||||||
prev.count += 1;
|
if (remote) agg.sumRemoteSec += (remote.security || 0);
|
||||||
if (!prev.sampleTo) prev.sampleTo = n;
|
grouped.set(gkey, agg);
|
||||||
} else {
|
|
||||||
grouped.set(gkey, { from: fromName, toRegion, count: 1, color, angle, sampleTo: n });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const out: OffIndicator[] = [];
|
||||||
|
for (const [, agg] of grouped) {
|
||||||
|
if (agg.count === 0) continue;
|
||||||
|
// Angle: point away from existing connections = meanNeighborAngle + PI
|
||||||
|
let angle = meanNeighborAngle[agg.from];
|
||||||
|
if (angle === undefined) {
|
||||||
|
// fallback: away from region centroid
|
||||||
|
// compute centroid of current region nodes
|
||||||
|
let cx = 0, cy = 0, c = 0;
|
||||||
|
systems.forEach(s => { cx += s.x; cy += s.y; c++; });
|
||||||
|
if (c > 0) { cx /= c; cy /= c; }
|
||||||
|
const sys = systems.get(agg.from)!;
|
||||||
|
angle = Math.atan2(sys.y - cy, sys.x - cx);
|
||||||
|
}
|
||||||
|
angle = angle + Math.PI;
|
||||||
|
|
||||||
setOffRegionIndicators(Array.from(grouped.values()));
|
// Color from avg of local system sec and avg remote sec; local from this system
|
||||||
|
const localSec = (systems.get(agg.from)?.security || 0);
|
||||||
|
const remoteAvg = agg.count > 0 ? (agg.sumRemoteSec / agg.count) : 0;
|
||||||
|
const color = getSecurityColor((localSec + remoteAvg) / 2);
|
||||||
|
out.push({ from: agg.from, toRegion: agg.toRegion, count: agg.count, color, angle, sampleTo: agg.sampleTo });
|
||||||
|
}
|
||||||
|
setOffRegionIndicators(out);
|
||||||
};
|
};
|
||||||
computeOffRegion();
|
computeOffRegion();
|
||||||
}, [systems, regionName]);
|
}, [systems, regionName, meanNeighborAngle]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isWormholeRegion) {
|
if (isWormholeRegion) {
|
||||||
@@ -686,8 +677,8 @@ export const RegionMap = ({ regionName, focusSystem, isCompact = false, isWormho
|
|||||||
if (!pos) return null;
|
if (!pos) return null;
|
||||||
const len = 26;
|
const len = 26;
|
||||||
const r0 = 10; // start just outside node
|
const r0 = 10; // start just outside node
|
||||||
const dx = Math.sin(ind.angle);
|
const dx = Math.cos(ind.angle);
|
||||||
const dy = -Math.cos(ind.angle);
|
const dy = Math.sin(ind.angle);
|
||||||
const x1 = pos.x + dx * r0;
|
const x1 = pos.x + dx * r0;
|
||||||
const y1 = pos.y + dy * r0;
|
const y1 = pos.y + dy * r0;
|
||||||
const x2 = x1 + dx * len;
|
const x2 = x1 + dx * len;
|
||||||
|
@@ -3,15 +3,15 @@ import { Card, CardContent } from "@/components/ui/card";
|
|||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
AlertDialogContent,
|
AlertDialogContent,
|
||||||
AlertDialogDescription,
|
AlertDialogDescription,
|
||||||
AlertDialogFooter,
|
AlertDialogFooter,
|
||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
AlertDialogTrigger,
|
AlertDialogTrigger,
|
||||||
} from "@/components/ui/alert-dialog";
|
} from "@/components/ui/alert-dialog";
|
||||||
import { SigviewRecord as Signature } from "@/lib/pbtypes";
|
import { SigviewRecord as Signature } from "@/lib/pbtypes";
|
||||||
import { getSignatureMeta } from "@/hooks/useSignatureCategories";
|
import { getSignatureMeta } from "@/hooks/useSignatureCategories";
|
||||||
@@ -52,7 +52,7 @@ export const SignatureCard = ({ signature, onDelete, onUpdate }: SignatureCardPr
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card
|
<Card
|
||||||
className={`${isGasSite ? 'bg-emerald-900/40 border-emerald-500 shadow-[0_0_15px_rgba(16,185,129,0.5)] hover:shadow-[0_0_20px_rgba(16,185,129,0.7)]' : 'bg-slate-800/40 border-slate-700'} hover:bg-slate-800/60 transition-all duration-200 hover:border-slate-600 relative cursor-pointer`}
|
className={`${isGasSite ? 'bg-emerald-900/40 border-emerald-500 shadow-[0_0_15px_rgba(16,185,129,0.5)] hover:shadow-[0_0_20px_rgba(16,185,129,0.7)]' : 'bg-slate-800/40 border-slate-700'} hover:bg-slate-800/60 transition-all duration-200 hover:border-slate-600 relative cursor-pointer`}
|
||||||
onClick={() => setIsEditModalOpen(true)}
|
onClick={() => setIsEditModalOpen(true)}
|
||||||
>
|
>
|
||||||
@@ -60,15 +60,15 @@ export const SignatureCard = ({ signature, onDelete, onUpdate }: SignatureCardPr
|
|||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{/* Type Badge - Most Important */}
|
{/* Type Badge - Most Important */}
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
<Badge
|
<Badge
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className={`${meta.color} px-3 py-1 text-sm font-semibold flex items-center gap-2`}
|
className={`${meta.color} px-3 py-1 text-sm font-semibold flex items-center gap-2`}
|
||||||
>
|
>
|
||||||
{meta.icon}
|
{meta.icon}
|
||||||
{signature.type || 'Unknown Type'}
|
{signature.type || 'Unknown Type'}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Signature Name */}
|
{/* Signature Name */}
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h3 className="text-white font-medium text-lg">
|
<h3 className="text-white font-medium text-lg">
|
||||||
@@ -82,7 +82,7 @@ export const SignatureCard = ({ signature, onDelete, onUpdate }: SignatureCardPr
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Additional Info */}
|
{/* Additional Info */}
|
||||||
<div className="space-y-2 text-sm text-slate-400">
|
<div className="space-y-2 text-sm text-slate-400">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
|
@@ -53,9 +53,9 @@ export const SignatureCategories = ({ categories, onToggleCategory, onDelete, on
|
|||||||
<CardContent className="p-0">
|
<CardContent className="p-0">
|
||||||
<div className="divide-y divide-slate-700">
|
<div className="divide-y divide-slate-700">
|
||||||
{category.signatures.map((signature) => (
|
{category.signatures.map((signature) => (
|
||||||
<SignatureListItem
|
<SignatureListItem
|
||||||
key={signature.id}
|
key={signature.id}
|
||||||
signature={signature}
|
signature={signature}
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
onUpdate={onUpdate}
|
onUpdate={onUpdate}
|
||||||
/>
|
/>
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
DialogDescription,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
|
@@ -2,15 +2,15 @@ import { useState } from "react";
|
|||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
AlertDialogCancel,
|
AlertDialogCancel,
|
||||||
AlertDialogContent,
|
AlertDialogContent,
|
||||||
AlertDialogDescription,
|
AlertDialogDescription,
|
||||||
AlertDialogFooter,
|
AlertDialogFooter,
|
||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
AlertDialogTrigger,
|
AlertDialogTrigger,
|
||||||
} from "@/components/ui/alert-dialog";
|
} from "@/components/ui/alert-dialog";
|
||||||
import { Clock, AlertTriangle, Skull, Trash2 } from "lucide-react";
|
import { Clock, AlertTriangle, Skull, Trash2 } from "lucide-react";
|
||||||
import { SigviewRecord as Signature } from "@/lib/pbtypes";
|
import { SigviewRecord as Signature } from "@/lib/pbtypes";
|
||||||
@@ -88,9 +88,8 @@ export const SignatureListItem = ({ signature, onDelete, onUpdate }: SignatureLi
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className={`flex items-center justify-between p-4 border-b border-slate-700 hover:bg-slate-800/40 transition-colors cursor-pointer ${
|
className={`flex items-center justify-between p-4 border-b border-slate-700 hover:bg-slate-800/40 transition-colors cursor-pointer ${oldEntry ? "opacity-50" : ""
|
||||||
oldEntry ? "opacity-50" : ""
|
} ${isGasSite ? 'bg-emerald-900/40 border-emerald-500 shadow-[0_0_15px_rgba(16,185,129,0.5)] hover:shadow-[0_0_20px_rgba(16,185,129,0.7)]' : ''}`}
|
||||||
} ${isGasSite ? 'bg-emerald-900/40 border-emerald-500 shadow-[0_0_15px_rgba(16,185,129,0.5)] hover:shadow-[0_0_20px_rgba(16,185,129,0.7)]' : ''}`}
|
|
||||||
onClick={() => setIsEditModalOpen(true)}
|
onClick={() => setIsEditModalOpen(true)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-4 flex-1">
|
<div className="flex items-center gap-4 flex-1">
|
||||||
|
@@ -25,7 +25,7 @@ const badgeVariants = cva(
|
|||||||
|
|
||||||
export interface BadgeProps
|
export interface BadgeProps
|
||||||
extends React.HTMLAttributes<HTMLDivElement>,
|
extends React.HTMLAttributes<HTMLDivElement>,
|
||||||
VariantProps<typeof badgeVariants> {}
|
VariantProps<typeof badgeVariants> { }
|
||||||
|
|
||||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||||
return (
|
return (
|
||||||
|
@@ -35,7 +35,7 @@ const buttonVariants = cva(
|
|||||||
|
|
||||||
export interface ButtonProps
|
export interface ButtonProps
|
||||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
VariantProps<typeof buttonVariants> {
|
VariantProps<typeof buttonVariants> {
|
||||||
asChild?: boolean
|
asChild?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -82,13 +82,13 @@ const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
|
|||||||
([theme, prefix]) => `
|
([theme, prefix]) => `
|
||||||
${prefix} [data-chart=${id}] {
|
${prefix} [data-chart=${id}] {
|
||||||
${colorConfig
|
${colorConfig
|
||||||
.map(([key, itemConfig]) => {
|
.map(([key, itemConfig]) => {
|
||||||
const color =
|
const color =
|
||||||
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
|
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ||
|
||||||
itemConfig.color
|
itemConfig.color
|
||||||
return color ? ` --color-${key}: ${color};` : null
|
return color ? ` --color-${key}: ${color};` : null
|
||||||
})
|
})
|
||||||
.join("\n")}
|
.join("\n")}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
@@ -103,13 +103,13 @@ const ChartTooltip = RechartsPrimitive.Tooltip
|
|||||||
const ChartTooltipContent = React.forwardRef<
|
const ChartTooltipContent = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
|
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
|
||||||
React.ComponentProps<"div"> & {
|
React.ComponentProps<"div"> & {
|
||||||
hideLabel?: boolean
|
hideLabel?: boolean
|
||||||
hideIndicator?: boolean
|
hideIndicator?: boolean
|
||||||
indicator?: "line" | "dot" | "dashed"
|
indicator?: "line" | "dot" | "dashed"
|
||||||
nameKey?: string
|
nameKey?: string
|
||||||
labelKey?: string
|
labelKey?: string
|
||||||
}
|
}
|
||||||
>(
|
>(
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@@ -259,10 +259,10 @@ const ChartLegend = RechartsPrimitive.Legend
|
|||||||
const ChartLegendContent = React.forwardRef<
|
const ChartLegendContent = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.ComponentProps<"div"> &
|
React.ComponentProps<"div"> &
|
||||||
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
|
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
|
||||||
hideIcon?: boolean
|
hideIcon?: boolean
|
||||||
nameKey?: string
|
nameKey?: string
|
||||||
}
|
}
|
||||||
>(
|
>(
|
||||||
(
|
(
|
||||||
{ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
|
{ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey },
|
||||||
@@ -326,8 +326,8 @@ function getPayloadConfigFromPayload(
|
|||||||
|
|
||||||
const payloadPayload =
|
const payloadPayload =
|
||||||
"payload" in payload &&
|
"payload" in payload &&
|
||||||
typeof payload.payload === "object" &&
|
typeof payload.payload === "object" &&
|
||||||
payload.payload !== null
|
payload.payload !== null
|
||||||
? payload.payload
|
? payload.payload
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
|
@@ -21,7 +21,7 @@ const Command = React.forwardRef<
|
|||||||
))
|
))
|
||||||
Command.displayName = CommandPrimitive.displayName
|
Command.displayName = CommandPrimitive.displayName
|
||||||
|
|
||||||
interface CommandDialogProps extends DialogProps {}
|
interface CommandDialogProps extends DialogProps { }
|
||||||
|
|
||||||
const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
|
const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
|
||||||
return (
|
return (
|
||||||
|
@@ -11,7 +11,7 @@ const labelVariants = cva(
|
|||||||
const Label = React.forwardRef<
|
const Label = React.forwardRef<
|
||||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||||
VariantProps<typeof labelVariants>
|
VariantProps<typeof labelVariants>
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<LabelPrimitive.Root
|
<LabelPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
@@ -31,9 +31,9 @@ const ScrollBar = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
"flex touch-none select-none transition-colors",
|
"flex touch-none select-none transition-colors",
|
||||||
orientation === "vertical" &&
|
orientation === "vertical" &&
|
||||||
"h-full w-2.5 border-l border-l-transparent p-[1px]",
|
"h-full w-2.5 border-l border-l-transparent p-[1px]",
|
||||||
orientation === "horizontal" &&
|
orientation === "horizontal" &&
|
||||||
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
"h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
@@ -75,7 +75,7 @@ const SelectContent = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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",
|
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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",
|
||||||
position === "popper" &&
|
position === "popper" &&
|
||||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
position={position}
|
position={position}
|
||||||
@@ -86,7 +86,7 @@ const SelectContent = React.forwardRef<
|
|||||||
className={cn(
|
className={cn(
|
||||||
"p-1",
|
"p-1",
|
||||||
position === "popper" &&
|
position === "popper" &&
|
||||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
@@ -128,4 +128,3 @@ export {
|
|||||||
Sheet, SheetClose,
|
Sheet, SheetClose,
|
||||||
SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger
|
SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -612,7 +612,7 @@ const SidebarMenuAction = React.forwardRef<
|
|||||||
"peer-data-[size=lg]/menu-button:top-2.5",
|
"peer-data-[size=lg]/menu-button:top-2.5",
|
||||||
"group-data-[collapsible=icon]:hidden",
|
"group-data-[collapsible=icon]:hidden",
|
||||||
showOnHover &&
|
showOnHover &&
|
||||||
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0",
|
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
@@ -3,7 +3,7 @@ import * as React from "react"
|
|||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
export interface TextareaProps
|
export interface TextareaProps
|
||||||
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> { }
|
||||||
|
|
||||||
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||||
({ className, ...props }, ref) => {
|
({ className, ...props }, ref) => {
|
||||||
|
@@ -41,7 +41,7 @@ const toastVariants = cva(
|
|||||||
const Toast = React.forwardRef<
|
const Toast = React.forwardRef<
|
||||||
React.ElementRef<typeof ToastPrimitives.Root>,
|
React.ElementRef<typeof ToastPrimitives.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
||||||
VariantProps<typeof toastVariants>
|
VariantProps<typeof toastVariants>
|
||||||
>(({ className, variant, ...props }, ref) => {
|
>(({ className, variant, ...props }, ref) => {
|
||||||
return (
|
return (
|
||||||
<ToastPrimitives.Root
|
<ToastPrimitives.Root
|
||||||
|
@@ -15,7 +15,7 @@ const ToggleGroupContext = React.createContext<
|
|||||||
const ToggleGroup = React.forwardRef<
|
const ToggleGroup = React.forwardRef<
|
||||||
React.ElementRef<typeof ToggleGroupPrimitive.Root>,
|
React.ElementRef<typeof ToggleGroupPrimitive.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
|
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
|
||||||
VariantProps<typeof toggleVariants>
|
VariantProps<typeof toggleVariants>
|
||||||
>(({ className, variant, size, children, ...props }, ref) => (
|
>(({ className, variant, size, children, ...props }, ref) => (
|
||||||
<ToggleGroupPrimitive.Root
|
<ToggleGroupPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@@ -33,7 +33,7 @@ ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName
|
|||||||
const ToggleGroupItem = React.forwardRef<
|
const ToggleGroupItem = React.forwardRef<
|
||||||
React.ElementRef<typeof ToggleGroupPrimitive.Item>,
|
React.ElementRef<typeof ToggleGroupPrimitive.Item>,
|
||||||
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
|
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
|
||||||
VariantProps<typeof toggleVariants>
|
VariantProps<typeof toggleVariants>
|
||||||
>(({ className, children, variant, size, ...props }, ref) => {
|
>(({ className, children, variant, size, ...props }, ref) => {
|
||||||
const context = React.useContext(ToggleGroupContext)
|
const context = React.useContext(ToggleGroupContext)
|
||||||
|
|
||||||
|
@@ -29,7 +29,7 @@ const toggleVariants = cva(
|
|||||||
const Toggle = React.forwardRef<
|
const Toggle = React.forwardRef<
|
||||||
React.ElementRef<typeof TogglePrimitive.Root>,
|
React.ElementRef<typeof TogglePrimitive.Root>,
|
||||||
React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
|
React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
|
||||||
VariantProps<typeof toggleVariants>
|
VariantProps<typeof toggleVariants>
|
||||||
>(({ className, variant, size, ...props }, ref) => (
|
>(({ className, variant, size, ...props }, ref) => (
|
||||||
<TogglePrimitive.Root
|
<TogglePrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
@@ -32,21 +32,21 @@ type ActionType = typeof actionTypes
|
|||||||
|
|
||||||
type Action =
|
type Action =
|
||||||
| {
|
| {
|
||||||
type: ActionType["ADD_TOAST"]
|
type: ActionType["ADD_TOAST"]
|
||||||
toast: ToasterToast
|
toast: ToasterToast
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: ActionType["UPDATE_TOAST"]
|
type: ActionType["UPDATE_TOAST"]
|
||||||
toast: Partial<ToasterToast>
|
toast: Partial<ToasterToast>
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: ActionType["DISMISS_TOAST"]
|
type: ActionType["DISMISS_TOAST"]
|
||||||
toastId?: ToasterToast["id"]
|
toastId?: ToasterToast["id"]
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: ActionType["REMOVE_TOAST"]
|
type: ActionType["REMOVE_TOAST"]
|
||||||
toastId?: ToasterToast["id"]
|
toastId?: ToasterToast["id"]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
toasts: ToasterToast[]
|
toasts: ToasterToast[]
|
||||||
@@ -104,9 +104,9 @@ export const reducer = (state: State, action: Action): State => {
|
|||||||
toasts: state.toasts.map((t) =>
|
toasts: state.toasts.map((t) =>
|
||||||
t.id === toastId || toastId === undefined
|
t.id === toastId || toastId === undefined
|
||||||
? {
|
? {
|
||||||
...t,
|
...t,
|
||||||
open: false,
|
open: false,
|
||||||
}
|
}
|
||||||
: t
|
: t
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@@ -66,7 +64,9 @@
|
|||||||
* {
|
* {
|
||||||
@apply border-border;
|
@apply border-border;
|
||||||
}
|
}
|
||||||
html, body {
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
@@ -74,12 +74,14 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Inter', system-ui, sans-serif;
|
font-family: 'Inter', system-ui, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -25,8 +25,8 @@ export type HTMLString = string
|
|||||||
|
|
||||||
type ExpandType<T> = unknown extends T
|
type ExpandType<T> = unknown extends T
|
||||||
? T extends unknown
|
? T extends unknown
|
||||||
? { expand?: unknown }
|
? { expand?: unknown }
|
||||||
: { expand: T }
|
: { expand: T }
|
||||||
: { expand: T }
|
: { expand: T }
|
||||||
|
|
||||||
// System fields
|
// System fields
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client';
|
||||||
import App from './App.tsx'
|
import App from './App.tsx';
|
||||||
import './index.css'
|
import './index.css';
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(<App />);
|
createRoot(document.getElementById("root")!).render(<App />);
|
||||||
|
@@ -7,14 +7,14 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
export const RegionPage = () => {
|
export const RegionPage = () => {
|
||||||
const { region } = useParams<{ region: string }>();
|
const { region } = useParams<{ region: string }>();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
if (!region) {
|
if (!region) {
|
||||||
return (
|
return (
|
||||||
<div className="h-screen w-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 flex items-center justify-center overflow-hidden">
|
<div className="h-screen w-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 flex items-center justify-center overflow-hidden">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h1 className="text-4xl font-bold text-white mb-4">Region Not Found</h1>
|
<h1 className="text-4xl font-bold text-white mb-4">Region Not Found</h1>
|
||||||
<p className="text-purple-200 mb-6">The requested region does not exist in our database.</p>
|
<p className="text-purple-200 mb-6">The requested region does not exist in our database.</p>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => navigate('/')}
|
onClick={() => navigate('/')}
|
||||||
className="bg-purple-600 hover:bg-purple-700"
|
className="bg-purple-600 hover:bg-purple-700"
|
||||||
>
|
>
|
||||||
@@ -28,8 +28,8 @@ export const RegionPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen w-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 overflow-hidden">
|
<div className="h-screen w-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 overflow-hidden">
|
||||||
<RegionMap
|
<RegionMap
|
||||||
regionName={region}
|
regionName={region}
|
||||||
isWormholeRegion={region === "Wormhole"}
|
isWormholeRegion={region === "Wormhole"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
export const getSecurityColor = (security: number): string => {
|
export const getSecurityColor = (security: number): string => {
|
||||||
// Clamp security between -1 and 1
|
// Clamp security between -1 and 1
|
||||||
const clampedSecurity = Math.max(-1, Math.min(1, security));
|
const clampedSecurity = Math.max(-1, Math.min(1, security));
|
||||||
|
|
||||||
// Define color points for specific security values
|
// Define color points for specific security values
|
||||||
const colorPoints = [
|
const colorPoints = [
|
||||||
{ sec: -1.0, color: [75, 0, 130] }, // Dark purple (same as 0.1)
|
{ sec: -1.0, color: [75, 0, 130] }, // Dark purple (same as 0.1)
|
||||||
@@ -31,7 +31,7 @@ export const getSecurityColor = (security: number): string => {
|
|||||||
|
|
||||||
// Calculate the ratio between the two points
|
// Calculate the ratio between the two points
|
||||||
const ratio = (clampedSecurity - lowerPoint.sec) / (upperPoint.sec - lowerPoint.sec);
|
const ratio = (clampedSecurity - lowerPoint.sec) / (upperPoint.sec - lowerPoint.sec);
|
||||||
|
|
||||||
// Interpolate between the colors
|
// Interpolate between the colors
|
||||||
const red = Math.round(lowerPoint.color[0] + (upperPoint.color[0] - lowerPoint.color[0]) * ratio);
|
const red = Math.round(lowerPoint.color[0] + (upperPoint.color[0] - lowerPoint.color[0]) * ratio);
|
||||||
const green = Math.round(lowerPoint.color[1] + (upperPoint.color[1] - lowerPoint.color[1]) * ratio);
|
const green = Math.round(lowerPoint.color[1] + (upperPoint.color[1] - lowerPoint.color[1]) * ratio);
|
||||||
|
Reference in New Issue
Block a user