diff --git a/src/App.tsx b/src/App.tsx
index 9ea9163..4a7e188 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -7,6 +7,7 @@ import { BrowserRouter, Routes, Route } from "react-router-dom";
import Index from "./pages/Index";
import RegionPage from "./pages/RegionPage";
import NotFound from "./pages/NotFound";
+import SystemView from "./pages/SystemView";
const queryClient = new QueryClient();
@@ -19,6 +20,7 @@ const App = () => (
} />
} />
+ } />
{/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
} />
diff --git a/src/components/RegionMap.tsx b/src/components/RegionMap.tsx
index e32419f..466eccc 100644
--- a/src/components/RegionMap.tsx
+++ b/src/components/RegionMap.tsx
@@ -102,8 +102,7 @@ export const RegionMap: React.FC<{ regionName: string }> = ({ regionName }) => {
}, [systems]);
const handleSystemClick = (systemName: string) => {
- console.log(`Clicked system: ${systemName}`);
- // Future: Navigate to system details
+ navigate(`/systems/${systemName}`);
};
const handleMouseDown = useCallback((e: React.MouseEvent) => {
diff --git a/src/components/SignatureCard.tsx b/src/components/SignatureCard.tsx
new file mode 100644
index 0000000..0596784
--- /dev/null
+++ b/src/components/SignatureCard.tsx
@@ -0,0 +1,77 @@
+
+import { Card, CardContent } from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { Zap, Shield, Coins, HelpCircle } from "lucide-react";
+
+interface SignatureItem {
+ collectionId: string;
+ collectionName: string;
+ id: string;
+ signame: string;
+ sysid: string;
+ system: string;
+ type: string;
+}
+
+interface SignatureCardProps {
+ signature: SignatureItem;
+}
+
+const SignatureCard = ({ signature }: SignatureCardProps) => {
+ const getTypeIcon = (type: string) => {
+ const lowerType = type.toLowerCase();
+ if (lowerType.includes('combat')) return ;
+ if (lowerType.includes('exploration') || lowerType.includes('relic') || lowerType.includes('data')) return ;
+ if (lowerType.includes('ore') || lowerType.includes('gas')) return ;
+ return ;
+ };
+
+ const getTypeColor = (type: string) => {
+ const lowerType = type.toLowerCase();
+ if (lowerType.includes('combat')) return 'bg-red-900/30 text-red-300 border-red-600';
+ if (lowerType.includes('exploration') || lowerType.includes('relic') || lowerType.includes('data')) return 'bg-blue-900/30 text-blue-300 border-blue-600';
+ if (lowerType.includes('ore') || lowerType.includes('gas')) return 'bg-yellow-900/30 text-yellow-300 border-yellow-600';
+ if (!type || type === '') return 'bg-slate-700 text-slate-300 border-slate-600';
+ return 'bg-purple-900/30 text-purple-300 border-purple-600';
+ };
+
+ return (
+
+
+
+ {/* Type Badge - Most Important */}
+
+
+ {getTypeIcon(signature.type)}
+ {signature.type || 'Unknown Type'}
+
+
+
+ {/* Signature Name */}
+
+
+ {signature.signame || 'Unnamed Signature'}
+
+
+
+ {/* Additional Info */}
+
+
+ System:
+ {signature.system}
+
+
+ ID:
+ {signature.id}
+
+
+
+
+
+ );
+};
+
+export default SignatureCard;
diff --git a/src/components/SignatureListItem.tsx b/src/components/SignatureListItem.tsx
new file mode 100644
index 0000000..4106d76
--- /dev/null
+++ b/src/components/SignatureListItem.tsx
@@ -0,0 +1,136 @@
+import { Badge } from "@/components/ui/badge";
+import { Zap, Shield, Coins, HelpCircle, Clock, AlertTriangle, Skull } from "lucide-react";
+
+interface SignatureItem {
+ collectionId: string;
+ collectionName: string;
+ id: string;
+ identifier: string;
+ signame: string;
+ sysid: string;
+ system: string;
+ type: string;
+ updated?: string;
+ created?: string;
+ dangerous?: boolean;
+}
+
+interface SignatureListItemProps {
+ signature: SignatureItem;
+}
+
+const SignatureListItem = ({ signature }: SignatureListItemProps) => {
+ const getTypeIcon = (type: string) => {
+ const lowerType = type.toLowerCase();
+ if (lowerType.includes("combat")) return ;
+ if (lowerType.includes("exploration") || lowerType.includes("relic") || lowerType.includes("data"))
+ return ;
+ if (lowerType.includes("ore") || lowerType.includes("gas")) return ;
+ return ;
+ };
+
+ const getTypeColor = (type: string, dangerous: boolean = false) => {
+ if (dangerous) return "bg-red-900/50 text-red-200 border-red-500";
+
+ const lowerType = type.toLowerCase();
+ if (lowerType.includes("combat")) return "bg-red-900/30 text-red-300 border-red-600";
+ if (lowerType.includes("exploration") || lowerType.includes("relic") || lowerType.includes("data"))
+ return "bg-blue-900/30 text-blue-300 border-blue-600";
+ if (lowerType.includes("ore") || lowerType.includes("gas"))
+ return "bg-yellow-900/30 text-yellow-300 border-yellow-600";
+ if (!type || type === "") return "bg-slate-700 text-slate-300 border-slate-600";
+ return "bg-purple-900/30 text-purple-300 border-purple-600";
+ };
+
+ const isOld = () => {
+ if (!signature.updated) return false;
+ const updatedTime = new Date(signature.updated);
+ if (isNaN(updatedTime.getTime())) return false; // Handle invalid date
+ const now = new Date();
+ const diffHours = (now.getTime() - updatedTime.getTime()) / (1000 * 60 * 60);
+ return diffHours > 3;
+ };
+
+ const formatDate = (dateStr: string | undefined) => {
+ if (!dateStr) return "Unknown";
+ const date = new Date(dateStr);
+ if (isNaN(date.getTime())) return "Invalid date"; // Handle invalid date
+
+ const now = new Date();
+ const diffMs = now.getTime() - date.getTime();
+
+ // Handle cases where the time difference is very small or negative
+ if (diffMs < 0) return "Just now";
+
+ const diffMinutes = Math.max(0, Math.floor(diffMs / (1000 * 60)));
+ const diffHours = Math.max(0, Math.floor(diffMs / (1000 * 60 * 60)));
+
+ if (diffMinutes === 0) {
+ return "Just now";
+ } else if (diffMinutes < 60) {
+ return `${diffMinutes}m ago`;
+ } else if (diffHours < 24) {
+ return `${diffHours}h ago`;
+ } else {
+ return date.toLocaleString();
+ }
+ };
+
+ const oldEntry = isOld();
+
+ return (
+
+
+ {/* Type Badge - Most Important */}
+
+ {signature.dangerous ? : getTypeIcon(signature.type)}
+ {signature.type || "Unknown Type"}
+
+
+ {/* Signature Name and ID */}
+
+
+ {signature.id}
+
+ {signature.signame || "Unnamed Signature"}
+ {signature.dangerous && (
+
+ DANGEROUS
+
+ )}
+
+
+
{signature.identifier}
+
+
+ {/* Dates */}
+
+ {signature.updated && (
+
+ {oldEntry &&
}
+
+
Updated: {formatDate(signature.updated)}
+
+ )}
+ {signature.created && (
+
+ Created: {formatDate(signature.created)}
+
+ )}
+
+
+
+ );
+};
+
+export default SignatureListItem;
diff --git a/src/components/SystemTracker.tsx b/src/components/SystemTracker.tsx
new file mode 100644
index 0000000..998d2d6
--- /dev/null
+++ b/src/components/SystemTracker.tsx
@@ -0,0 +1,250 @@
+import { useState, useEffect } from "react";
+import { useQuery } from "@tanstack/react-query";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { RefreshCw, AlertCircle, Radar } from "lucide-react";
+import SignatureListItem from "./SignatureListItem";
+import { toast } from "@/hooks/use-toast";
+import { useNavigate } from "react-router-dom";
+
+interface SignatureItem {
+ collectionId: string;
+ collectionName: string;
+ id: string;
+ identifier: string;
+ signame: string;
+ sysid: string;
+ system: string;
+ type: string;
+ updated?: string;
+ created?: string;
+ dangerous?: boolean;
+}
+
+interface ApiResponse {
+ items: SignatureItem[];
+}
+
+interface SystemTrackerProps {
+ initialSystem?: string;
+}
+
+const SystemTracker = ({ initialSystem }: SystemTrackerProps) => {
+ const [currentSystem, setCurrentSystem] = useState(initialSystem || "");
+ const navigate = useNavigate();
+
+ // Query to get current system from localhost:7000 (always enabled to track system changes)
+ const { data: systemData, refetch: refetchSystem, isLoading: systemLoading } = useQuery({
+ queryKey: ['currentSystem'],
+ queryFn: async () => {
+ const response = await fetch('http://localhost:7000');
+ if (!response.ok) {
+ throw new Error('Failed to fetch current system');
+ }
+ const text = await response.text();
+ return text.trim();
+ },
+ refetchInterval: 1000, // Always refetch to track system changes
+ retry: 3,
+ });
+
+ // Query to get signatures for the current system
+ const {
+ data: signaturesData,
+ refetch: refetchSignatures,
+ isLoading: signaturesLoading,
+ error: signaturesError
+ } = useQuery({
+ queryKey: ['signatures', currentSystem],
+ queryFn: async () => {
+ if (!currentSystem) return null;
+
+ const encodedSystem = encodeURIComponent(currentSystem);
+ const url = `https://evebase.site.quack-lab.dev/api/collections/sigview/records?filter=(system%3D'${encodedSystem}')`;
+
+ console.log('Fetching signatures for system:', currentSystem);
+ console.log('API URL:', url);
+
+ const response = await fetch(url);
+ if (!response.ok) {
+ throw new Error('Failed to fetch signatures');
+ }
+ const data: ApiResponse = await response.json();
+ return data;
+ },
+ enabled: !!currentSystem,
+ refetchInterval: 60000, // Keep signature refresh at 1 minute
+ });
+
+ // Update current system when data changes and navigate to new system
+ useEffect(() => {
+ if (systemData && systemData !== currentSystem) {
+ setCurrentSystem(systemData);
+ navigate(`/${systemData}`);
+ console.log('Current system updated to:', systemData);
+ // Immediately refetch signatures when system changes
+ refetchSignatures();
+ toast({
+ title: "System Updated",
+ description: `Now tracking: ${systemData}`,
+ });
+ }
+ }, [systemData, currentSystem, refetchSignatures, navigate]);
+
+ // Refresh signatures every 5 seconds
+ useEffect(() => {
+ const refreshInterval = setInterval(() => {
+ console.log('Auto-refreshing signatures...');
+ if (currentSystem) {
+ refetchSignatures();
+ }
+ }, 5000);
+
+ // Cleanup interval on component unmount
+ return () => clearInterval(refreshInterval);
+ }, [refetchSignatures, currentSystem]);
+
+ const handleRefresh = () => {
+ refetchSystem();
+ if (currentSystem) {
+ refetchSignatures();
+ }
+ toast({
+ title: "Refreshing Data",
+ description: "Updating system and signature information...",
+ });
+ };
+
+ const signatures = signaturesData?.items || [];
+ const isLoading = systemLoading || signaturesLoading;
+
+ // Sort signatures by date (newest first) and prioritize unknown types
+ const sortedSignatures = [...signatures].sort((a, b) => {
+ // First, prioritize unknown types
+ const aIsUnknown = !a.type || a.type === '';
+ const bIsUnknown = !b.type || b.type === '';
+ if (aIsUnknown && !bIsUnknown) return -1;
+ if (!aIsUnknown && bIsUnknown) return 1;
+
+ // If both are unknown or both are known, sort by type
+ if (a.type !== b.type) {
+ return a.type.localeCompare(b.type);
+ }
+
+ // If same type, sort by date
+ const dateA = a.updated || a.created || '';
+ const dateB = b.updated || b.created || '';
+ return dateB.localeCompare(dateA);
+ });
+
+ // Group signatures by type for better organization
+ const signaturesByType = sortedSignatures.reduce((acc, sig) => {
+ const type = sig.type || 'Unknown';
+ if (!acc[type]) acc[type] = [];
+ acc[type].push(sig);
+ return acc;
+ }, {} as Record);
+
+ return (
+
+ {/* System Status Card */}
+
+
+
+
+ {initialSystem ? 'System View' : 'Current System'}
+
+
+
+ Refresh
+
+
+
+
+
+ {systemLoading && !initialSystem ? (
+
Loading system...
+ ) : currentSystem ? (
+
{currentSystem}
+ ) : (
+
No system data
+ )}
+
+
+ {signatures.length} Signatures
+
+
+
+
+
+ {/* Error Display */}
+ {signaturesError && (
+
+
+
+
+
Error loading signatures: {signaturesError.message}
+
+
+
+ )}
+
+ {/* Signatures Display */}
+ {currentSystem && !signaturesLoading && (
+
+ {sortedSignatures.length === 0 ? (
+
+
+ No signatures found for {currentSystem}
+
+
+ ) : (
+
+
+
+ Signatures
+
+ {sortedSignatures.length} Total
+
+
+
+
+
+ {sortedSignatures.map((signature) => (
+
+ ))}
+
+
+
+ )}
+
+ )}
+
+ {/* Loading State */}
+ {signaturesLoading && currentSystem && (
+
+
+
+ {[...Array(6)].map((_, i) => (
+
+ ))}
+
+
+
+ )}
+
+ );
+};
+
+export default SystemTracker;
diff --git a/src/pages/SystemView.tsx b/src/pages/SystemView.tsx
new file mode 100644
index 0000000..1e67236
--- /dev/null
+++ b/src/pages/SystemView.tsx
@@ -0,0 +1,26 @@
+import { useParams, useNavigate } from "react-router-dom";
+import SystemTracker from "@/components/SystemTracker";
+
+const SystemView = () => {
+ const { system } = useParams();
+ const navigate = useNavigate();
+
+ if (!system) {
+ navigate("/");
+ return null;
+ }
+
+ return (
+
+
+
+
Cosmic Region Navigator
+
Viewing signatures for system: {system}
+
+
+
+
+ );
+};
+
+export default SystemView;
\ No newline at end of file