Hook up system signature tracker
This commit is contained in:
		@@ -7,6 +7,7 @@ import { BrowserRouter, Routes, Route } from "react-router-dom";
 | 
				
			|||||||
import Index from "./pages/Index";
 | 
					import Index from "./pages/Index";
 | 
				
			||||||
import RegionPage from "./pages/RegionPage";
 | 
					import RegionPage from "./pages/RegionPage";
 | 
				
			||||||
import NotFound from "./pages/NotFound";
 | 
					import NotFound from "./pages/NotFound";
 | 
				
			||||||
 | 
					import SystemView from "./pages/SystemView";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const queryClient = new QueryClient();
 | 
					const queryClient = new QueryClient();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -19,6 +20,7 @@ const App = () => (
 | 
				
			|||||||
        <Routes>
 | 
					        <Routes>
 | 
				
			||||||
          <Route path="/" element={<Index />} />
 | 
					          <Route path="/" element={<Index />} />
 | 
				
			||||||
          <Route path="/regions/:region" element={<RegionPage />} />
 | 
					          <Route path="/regions/:region" element={<RegionPage />} />
 | 
				
			||||||
 | 
					          <Route path="/systems/:system" element={<SystemView />} />
 | 
				
			||||||
          {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
 | 
					          {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
 | 
				
			||||||
          <Route path="*" element={<NotFound />} />
 | 
					          <Route path="*" element={<NotFound />} />
 | 
				
			||||||
        </Routes>
 | 
					        </Routes>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -102,8 +102,7 @@ export const RegionMap: React.FC<{ regionName: string }> = ({ regionName }) => {
 | 
				
			|||||||
  }, [systems]);
 | 
					  }, [systems]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleSystemClick = (systemName: string) => {
 | 
					  const handleSystemClick = (systemName: string) => {
 | 
				
			||||||
    console.log(`Clicked system: ${systemName}`);
 | 
					    navigate(`/systems/${systemName}`);
 | 
				
			||||||
    // Future: Navigate to system details
 | 
					 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleMouseDown = useCallback((e: React.MouseEvent) => {
 | 
					  const handleMouseDown = useCallback((e: React.MouseEvent) => {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										77
									
								
								src/components/SignatureCard.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/components/SignatureCard.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -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 <Zap className="h-4 w-4" />;
 | 
				
			||||||
 | 
					    if (lowerType.includes('exploration') || lowerType.includes('relic') || lowerType.includes('data')) return <Shield className="h-4 w-4" />;
 | 
				
			||||||
 | 
					    if (lowerType.includes('ore') || lowerType.includes('gas')) return <Coins className="h-4 w-4" />;
 | 
				
			||||||
 | 
					    return <HelpCircle className="h-4 w-4" />;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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 (
 | 
				
			||||||
 | 
					    <Card className="bg-slate-800/40 border-slate-700 hover:bg-slate-800/60 transition-all duration-200 hover:border-slate-600">
 | 
				
			||||||
 | 
					      <CardContent className="pt-6">
 | 
				
			||||||
 | 
					        <div className="space-y-3">
 | 
				
			||||||
 | 
					          {/* Type Badge - Most Important */}
 | 
				
			||||||
 | 
					          <div className="flex items-center justify-center">
 | 
				
			||||||
 | 
					            <Badge 
 | 
				
			||||||
 | 
					              variant="outline" 
 | 
				
			||||||
 | 
					              className={`${getTypeColor(signature.type)} px-3 py-1 text-sm font-semibold flex items-center gap-2`}
 | 
				
			||||||
 | 
					            >
 | 
				
			||||||
 | 
					              {getTypeIcon(signature.type)}
 | 
				
			||||||
 | 
					              {signature.type || 'Unknown Type'}
 | 
				
			||||||
 | 
					            </Badge>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          {/* Signature Name */}
 | 
				
			||||||
 | 
					          <div className="text-center">
 | 
				
			||||||
 | 
					            <h3 className="text-white font-medium text-lg">
 | 
				
			||||||
 | 
					              {signature.signame || 'Unnamed Signature'}
 | 
				
			||||||
 | 
					            </h3>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					          {/* Additional Info */}
 | 
				
			||||||
 | 
					          <div className="space-y-2 text-sm text-slate-400">
 | 
				
			||||||
 | 
					            <div className="flex justify-between">
 | 
				
			||||||
 | 
					              <span>System:</span>
 | 
				
			||||||
 | 
					              <span className="text-slate-200">{signature.system}</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div className="flex justify-between">
 | 
				
			||||||
 | 
					              <span>ID:</span>
 | 
				
			||||||
 | 
					              <span className="text-slate-200 font-mono text-xs">{signature.id}</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </CardContent>
 | 
				
			||||||
 | 
					    </Card>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default SignatureCard;
 | 
				
			||||||
							
								
								
									
										136
									
								
								src/components/SignatureListItem.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/components/SignatureListItem.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -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 <Zap className="h-4 w-4" />;
 | 
				
			||||||
 | 
							if (lowerType.includes("exploration") || lowerType.includes("relic") || lowerType.includes("data"))
 | 
				
			||||||
 | 
								return <Shield className="h-4 w-4" />;
 | 
				
			||||||
 | 
							if (lowerType.includes("ore") || lowerType.includes("gas")) return <Coins className="h-4 w-4" />;
 | 
				
			||||||
 | 
							return <HelpCircle className="h-4 w-4" />;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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 (
 | 
				
			||||||
 | 
							<div
 | 
				
			||||||
 | 
								className={`flex items-center justify-between p-4 border-b border-slate-700 hover:bg-slate-800/40 transition-colors ${
 | 
				
			||||||
 | 
									oldEntry ? "opacity-50" : ""
 | 
				
			||||||
 | 
								}`}
 | 
				
			||||||
 | 
							>
 | 
				
			||||||
 | 
								<div className="flex items-center gap-4 flex-1">
 | 
				
			||||||
 | 
									{/* Type Badge - Most Important */}
 | 
				
			||||||
 | 
									<Badge
 | 
				
			||||||
 | 
										variant="outline"
 | 
				
			||||||
 | 
										className={`${getTypeColor(
 | 
				
			||||||
 | 
											signature.type,
 | 
				
			||||||
 | 
											signature.dangerous
 | 
				
			||||||
 | 
										)} px-3 py-1 text-sm font-semibold flex items-center gap-2 min-w-[120px] justify-center`}
 | 
				
			||||||
 | 
									>
 | 
				
			||||||
 | 
										{signature.dangerous ? <Skull className="h-4 w-4 text-red-400 animate-pulse" /> : getTypeIcon(signature.type)}
 | 
				
			||||||
 | 
										{signature.type || "Unknown Type"}
 | 
				
			||||||
 | 
									</Badge>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									{/* Signature Name and ID */}
 | 
				
			||||||
 | 
									<div className="flex-1 min-w-[200px]">
 | 
				
			||||||
 | 
										<div className="flex items-center gap-2">
 | 
				
			||||||
 | 
											<span className="font-mono text-sm text-slate-400 min-w-[60px]">{signature.id}</span>
 | 
				
			||||||
 | 
											<h3 className="text-white font-medium flex items-center gap-2">
 | 
				
			||||||
 | 
												{signature.signame || "Unnamed Signature"}
 | 
				
			||||||
 | 
												{signature.dangerous && (
 | 
				
			||||||
 | 
													<Badge variant="outline" className="bg-red-900/50 text-red-200 border-red-500 px-2 py-0.5 text-xs">
 | 
				
			||||||
 | 
														DANGEROUS
 | 
				
			||||||
 | 
													</Badge>
 | 
				
			||||||
 | 
												)}
 | 
				
			||||||
 | 
											</h3>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
										<div className="text-xs text-slate-400 font-mono ml-[60px]">{signature.identifier}</div>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									{/* Dates */}
 | 
				
			||||||
 | 
									<div className="flex flex-col gap-1 text-sm text-slate-400 min-w-[200px]">
 | 
				
			||||||
 | 
										{signature.updated && (
 | 
				
			||||||
 | 
											<div className="flex items-center gap-2">
 | 
				
			||||||
 | 
												{oldEntry && <AlertTriangle className="h-4 w-4 text-yellow-500" />}
 | 
				
			||||||
 | 
												<Clock className="h-4 w-4" />
 | 
				
			||||||
 | 
												<span>Updated: {formatDate(signature.updated)}</span>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										)}
 | 
				
			||||||
 | 
										{signature.created && (
 | 
				
			||||||
 | 
											<div className="flex items-center gap-2 text-xs text-slate-500">
 | 
				
			||||||
 | 
												<span>Created: {formatDate(signature.created)}</span>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
										)}
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default SignatureListItem;
 | 
				
			||||||
							
								
								
									
										250
									
								
								src/components/SystemTracker.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								src/components/SystemTracker.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -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<string>(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<string, SignatureItem[]>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <div className="space-y-6">
 | 
				
			||||||
 | 
					      {/* System Status Card */}
 | 
				
			||||||
 | 
					      <Card className="bg-slate-800/50 border-slate-700">
 | 
				
			||||||
 | 
					        <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-4">
 | 
				
			||||||
 | 
					          <CardTitle className="text-xl text-white flex items-center gap-2">
 | 
				
			||||||
 | 
					            <Radar className="h-5 w-5 text-blue-400" />
 | 
				
			||||||
 | 
					            {initialSystem ? 'System View' : 'Current System'}
 | 
				
			||||||
 | 
					          </CardTitle>
 | 
				
			||||||
 | 
					          <Button 
 | 
				
			||||||
 | 
					            onClick={handleRefresh} 
 | 
				
			||||||
 | 
					            disabled={isLoading}
 | 
				
			||||||
 | 
					            variant="outline"
 | 
				
			||||||
 | 
					            size="sm"
 | 
				
			||||||
 | 
					            className="bg-slate-700 border-slate-600 text-slate-200 hover:bg-slate-600"
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
 | 
					            <RefreshCw className={`h-4 w-4 mr-2 ${isLoading ? 'animate-spin' : ''}`} />
 | 
				
			||||||
 | 
					            Refresh
 | 
				
			||||||
 | 
					          </Button>
 | 
				
			||||||
 | 
					        </CardHeader>
 | 
				
			||||||
 | 
					        <CardContent>
 | 
				
			||||||
 | 
					          <div className="flex items-center justify-between">
 | 
				
			||||||
 | 
					            <div>
 | 
				
			||||||
 | 
					              {systemLoading && !initialSystem ? (
 | 
				
			||||||
 | 
					                <div className="text-slate-400">Loading system...</div>
 | 
				
			||||||
 | 
					              ) : currentSystem ? (
 | 
				
			||||||
 | 
					                <div className="text-2xl font-bold text-white">{currentSystem}</div>
 | 
				
			||||||
 | 
					              ) : (
 | 
				
			||||||
 | 
					                <div className="text-slate-400">No system data</div>
 | 
				
			||||||
 | 
					              )}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <Badge variant="outline" className="bg-slate-700 text-slate-200 border-slate-600">
 | 
				
			||||||
 | 
					              {signatures.length} Signatures
 | 
				
			||||||
 | 
					            </Badge>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </CardContent>
 | 
				
			||||||
 | 
					      </Card>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {/* Error Display */}
 | 
				
			||||||
 | 
					      {signaturesError && (
 | 
				
			||||||
 | 
					        <Card className="bg-red-900/20 border-red-700">
 | 
				
			||||||
 | 
					          <CardContent className="pt-6">
 | 
				
			||||||
 | 
					            <div className="flex items-center gap-2 text-red-400">
 | 
				
			||||||
 | 
					              <AlertCircle className="h-5 w-5" />
 | 
				
			||||||
 | 
					              <span>Error loading signatures: {signaturesError.message}</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </CardContent>
 | 
				
			||||||
 | 
					        </Card>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {/* Signatures Display */}
 | 
				
			||||||
 | 
					      {currentSystem && !signaturesLoading && (
 | 
				
			||||||
 | 
					        <div className="space-y-4">
 | 
				
			||||||
 | 
					          {sortedSignatures.length === 0 ? (
 | 
				
			||||||
 | 
					            <Card className="bg-slate-800/30 border-slate-700">
 | 
				
			||||||
 | 
					              <CardContent className="pt-6 text-center">
 | 
				
			||||||
 | 
					                <div className="text-slate-400">No signatures found for {currentSystem}</div>
 | 
				
			||||||
 | 
					              </CardContent>
 | 
				
			||||||
 | 
					            </Card>
 | 
				
			||||||
 | 
					          ) : (
 | 
				
			||||||
 | 
					            <Card className="bg-slate-800/30 border-slate-700">
 | 
				
			||||||
 | 
					              <CardHeader>
 | 
				
			||||||
 | 
					                <CardTitle className="text-white flex items-center justify-between">
 | 
				
			||||||
 | 
					                  <span>Signatures</span>
 | 
				
			||||||
 | 
					                  <Badge variant="outline" className="bg-slate-700 text-slate-200 border-slate-600">
 | 
				
			||||||
 | 
					                    {sortedSignatures.length} Total
 | 
				
			||||||
 | 
					                  </Badge>
 | 
				
			||||||
 | 
					                </CardTitle>
 | 
				
			||||||
 | 
					              </CardHeader>
 | 
				
			||||||
 | 
					              <CardContent className="p-0">
 | 
				
			||||||
 | 
					                <div className="divide-y divide-slate-700">
 | 
				
			||||||
 | 
					                  {sortedSignatures.map((signature) => (
 | 
				
			||||||
 | 
					                    <SignatureListItem key={signature.id} signature={signature} />
 | 
				
			||||||
 | 
					                  ))}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </CardContent>
 | 
				
			||||||
 | 
					            </Card>
 | 
				
			||||||
 | 
					          )}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {/* Loading State */}
 | 
				
			||||||
 | 
					      {signaturesLoading && currentSystem && (
 | 
				
			||||||
 | 
					        <Card className="bg-slate-800/30 border-slate-700">
 | 
				
			||||||
 | 
					          <CardContent className="pt-6">
 | 
				
			||||||
 | 
					            <div className="space-y-4">
 | 
				
			||||||
 | 
					              {[...Array(6)].map((_, i) => (
 | 
				
			||||||
 | 
					                <div key={i} className="flex items-center gap-4 p-4 animate-pulse">
 | 
				
			||||||
 | 
					                  <div className="h-6 bg-slate-700 rounded w-24"></div>
 | 
				
			||||||
 | 
					                  <div className="h-4 bg-slate-600 rounded flex-1"></div>
 | 
				
			||||||
 | 
					                  <div className="h-4 bg-slate-700 rounded w-16"></div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              ))}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </CardContent>
 | 
				
			||||||
 | 
					        </Card>
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default SystemTracker;
 | 
				
			||||||
							
								
								
									
										26
									
								
								src/pages/SystemView.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/pages/SystemView.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -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 (
 | 
				
			||||||
 | 
					    <div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900">
 | 
				
			||||||
 | 
					      <div className="container mx-auto px-4 py-8">
 | 
				
			||||||
 | 
					        <div className="text-center mb-8">
 | 
				
			||||||
 | 
					          <h1 className="text-4xl font-bold text-white mb-2">Cosmic Region Navigator</h1>
 | 
				
			||||||
 | 
					          <p className="text-slate-300">Viewing signatures for system: {system}</p>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <SystemTracker initialSystem={system} />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default SystemView; 
 | 
				
			||||||
		Reference in New Issue
	
	Block a user