Spaces:
Paused
Paused
| import { ExternalLink, Info } from 'lucide-react'; | |
| import { | |
| Tooltip, | |
| TooltipContent, | |
| TooltipTrigger, | |
| } from '@/components/ui/tooltip'; | |
| import { cn } from '@/lib/utils'; | |
| export interface DataSource { | |
| id: string; | |
| name: string; | |
| url?: string; | |
| lastUpdated?: string; | |
| description?: string; | |
| } | |
| interface SourceLinkProps { | |
| source: DataSource; | |
| variant?: 'icon' | 'text' | 'badge'; | |
| className?: string; | |
| } | |
| const SourceLink = ({ source, variant = 'icon', className }: SourceLinkProps) => { | |
| const handleClick = (e: React.MouseEvent) => { | |
| e.stopPropagation(); | |
| if (source.url) { | |
| window.open(source.url, '_blank', 'noopener,noreferrer'); | |
| } | |
| }; | |
| const content = ( | |
| <div className="space-y-2 max-w-xs"> | |
| <div className="flex items-center gap-2"> | |
| <span className="font-mono text-xs text-primary font-semibold">{source.name}</span> | |
| {source.url && <ExternalLink className="w-3 h-3 text-primary" />} | |
| </div> | |
| {source.description && ( | |
| <p className="text-xs text-muted-foreground">{source.description}</p> | |
| )} | |
| {source.lastUpdated && ( | |
| <p className="text-[10px] text-muted-foreground/70"> | |
| Sidst opdateret: {source.lastUpdated} | |
| </p> | |
| )} | |
| {source.url && ( | |
| <p className="text-[10px] text-primary/70">Klik for at åbne kilde</p> | |
| )} | |
| </div> | |
| ); | |
| if (variant === 'badge') { | |
| return ( | |
| <Tooltip> | |
| <TooltipTrigger asChild> | |
| <button | |
| onClick={handleClick} | |
| className={cn( | |
| "inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[8px] font-mono", | |
| "bg-primary/10 text-primary/70 hover:bg-primary/20 hover:text-primary", | |
| "transition-colors cursor-pointer border border-primary/20", | |
| className | |
| )} | |
| > | |
| <Info className="w-2.5 h-2.5" /> | |
| KILDE | |
| </button> | |
| </TooltipTrigger> | |
| <TooltipContent side="top" className="bg-card border-primary/30"> | |
| {content} | |
| </TooltipContent> | |
| </Tooltip> | |
| ); | |
| } | |
| if (variant === 'text') { | |
| return ( | |
| <Tooltip> | |
| <TooltipTrigger asChild> | |
| <button | |
| onClick={handleClick} | |
| className={cn( | |
| "inline-flex items-center gap-1 text-[9px] font-mono", | |
| "text-muted-foreground/50 hover:text-primary", | |
| "transition-colors cursor-pointer underline-offset-2 hover:underline", | |
| className | |
| )} | |
| > | |
| <span>{source.name}</span> | |
| <ExternalLink className="w-2.5 h-2.5" /> | |
| </button> | |
| </TooltipTrigger> | |
| <TooltipContent side="top" className="bg-card border-primary/30"> | |
| {content} | |
| </TooltipContent> | |
| </Tooltip> | |
| ); | |
| } | |
| // Default: icon variant | |
| return ( | |
| <Tooltip> | |
| <TooltipTrigger asChild> | |
| <button | |
| onClick={handleClick} | |
| className={cn( | |
| "inline-flex items-center justify-center w-4 h-4 rounded-full", | |
| "bg-primary/10 text-primary/50 hover:bg-primary/20 hover:text-primary", | |
| "transition-colors cursor-pointer", | |
| className | |
| )} | |
| > | |
| <Info className="w-2.5 h-2.5" /> | |
| </button> | |
| </TooltipTrigger> | |
| <TooltipContent side="top" className="bg-card border-primary/30"> | |
| {content} | |
| </TooltipContent> | |
| </Tooltip> | |
| ); | |
| }; | |
| export default SourceLink; | |
| // Common data sources for the platform | |
| export const dataSources: Record<string, DataSource> = { | |
| systemMetrics: { | |
| id: 'system-metrics', | |
| name: 'System Metrics API', | |
| url: 'https://api.cyberterm.io/metrics', | |
| lastUpdated: new Date().toLocaleString('da-DK'), | |
| description: 'Real-time system performance data fra intern overvågning' | |
| }, | |
| userAnalytics: { | |
| id: 'user-analytics', | |
| name: 'User Analytics', | |
| url: 'https://analytics.cyberterm.io', | |
| lastUpdated: new Date().toLocaleString('da-DK'), | |
| description: 'Brugerstatistik og engagement metrics' | |
| }, | |
| securityLog: { | |
| id: 'security-log', | |
| name: 'Security Log', | |
| url: 'https://security.cyberterm.io/logs', | |
| lastUpdated: new Date().toLocaleString('da-DK'), | |
| description: 'Sikkerhedslogfiler og trusselsvurderinger' | |
| }, | |
| networkStatus: { | |
| id: 'network-status', | |
| name: 'Network Monitor', | |
| url: 'https://network.cyberterm.io/status', | |
| lastUpdated: new Date().toLocaleString('da-DK'), | |
| description: 'Netværksstatus og forbindelsesdata' | |
| }, | |
| cryptoMarket: { | |
| id: 'crypto-market', | |
| name: 'CoinGecko API', | |
| url: 'https://www.coingecko.com', | |
| lastUpdated: new Date().toLocaleString('da-DK'), | |
| description: 'Cryptocurrency markedsdata' | |
| }, | |
| weatherApi: { | |
| id: 'weather-api', | |
| name: 'OpenWeather', | |
| url: 'https://openweathermap.org', | |
| lastUpdated: new Date().toLocaleString('da-DK'), | |
| description: 'Vejrdata og prognoser' | |
| }, | |
| internalDb: { | |
| id: 'internal-db', | |
| name: 'Intern Database', | |
| url: 'https://db.cyberterm.io', | |
| lastUpdated: new Date().toLocaleString('da-DK'), | |
| description: 'Intern datalagring og registre' | |
| }, | |
| aiEngine: { | |
| id: 'ai-engine', | |
| name: 'AI Analysis Engine', | |
| url: 'https://ai.cyberterm.io', | |
| lastUpdated: new Date().toLocaleString('da-DK'), | |
| description: 'Machine learning baserede analyser' | |
| }, | |
| threatIntel: { | |
| id: 'threat-intel', | |
| name: 'Threat Intelligence Feed', | |
| url: 'https://threatintel.cyberterm.io', | |
| lastUpdated: new Date().toLocaleString('da-DK'), | |
| description: 'Real-time trussel information og IOC feeds' | |
| }, | |
| }; | |