- Add comprehensive database migrations (001-024) for schema evolution - Enhance API schema with expanded type definitions and resolvers - Add new middleware: audit logging, rate limiting, MFA enforcement, security, tenant auth - Implement new services: AI optimization, billing, blockchain, compliance, marketplace - Add adapter layer for cloud integrations (Cloudflare, Kubernetes, Proxmox, storage) - Update Crossplane provider with enhanced VM management capabilities - Add comprehensive test suite for API endpoints and services - Update frontend components with improved GraphQL subscriptions and real-time updates - Enhance security configurations and headers (CSP, CORS, etc.) - Update documentation and configuration files - Add new CI/CD workflows and validation scripts - Implement design system improvements and UI enhancements
117 lines
3.7 KiB
TypeScript
117 lines
3.7 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useCallback } from 'react'
|
|
import Map, { Marker, Popup, Layer, Source } from 'react-map-gl'
|
|
import 'mapbox-gl/dist/mapbox-gl.css'
|
|
import type { ComplianceRequirement } from '@/lib/types/infrastructure'
|
|
import { Card, CardContent } from '@/components/ui/card'
|
|
import { Badge } from '@/components/ui/badge'
|
|
|
|
interface ComplianceMapViewProps {
|
|
requirements: ComplianceRequirement[]
|
|
onCountryClick?: (country: string) => void
|
|
}
|
|
|
|
const statusColors: Record<string, string> = {
|
|
Compliant: '#10b981',
|
|
Partial: '#f59e0b',
|
|
Pending: '#3b82f6',
|
|
'Non-Compliant': '#ef4444',
|
|
}
|
|
|
|
// Basic country coordinates (in production, use a proper geocoding service)
|
|
const countryCoordinates: Record<string, { lat: number; lng: number }> = {
|
|
Italy: { lat: 41.9028, lng: 12.4964 },
|
|
Germany: { lat: 51.1657, lng: 10.4515 },
|
|
France: { lat: 46.2276, lng: 2.2137 },
|
|
Spain: { lat: 40.4637, lng: -3.7492 },
|
|
// Add more as needed
|
|
}
|
|
|
|
export function ComplianceMapView({ requirements, onCountryClick }: ComplianceMapViewProps) {
|
|
const [popupInfo, setPopupInfo] = useState<ComplianceRequirement | null>(null)
|
|
const [viewState, setViewState] = useState({
|
|
longitude: 12.4964,
|
|
latitude: 41.9028,
|
|
zoom: 3,
|
|
})
|
|
|
|
const handleMarkerClick = useCallback(
|
|
(requirement: ComplianceRequirement) => {
|
|
setPopupInfo(requirement)
|
|
onCountryClick?.(requirement.country)
|
|
},
|
|
[onCountryClick]
|
|
)
|
|
|
|
const mapboxToken = process.env.NEXT_PUBLIC_MAPBOX_TOKEN || ''
|
|
|
|
if (!mapboxToken) {
|
|
return (
|
|
<Card>
|
|
<CardContent className="p-8 text-center">
|
|
<p className="text-studio-medium">
|
|
Mapbox token not configured. Please set NEXT_PUBLIC_MAPBOX_TOKEN in your environment
|
|
variables.
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="w-full h-96 rounded-lg overflow-hidden border border-studio-medium">
|
|
<Map
|
|
{...viewState}
|
|
onMove={(evt) => setViewState(evt.viewState)}
|
|
mapboxAccessToken={mapboxToken}
|
|
style={{ width: '100%', height: '100%' }}
|
|
mapStyle="mapbox://styles/mapbox/dark-v10"
|
|
>
|
|
{requirements.map((req) => {
|
|
const coords = countryCoordinates[req.country]
|
|
if (!coords) return null
|
|
|
|
return (
|
|
<Marker
|
|
key={req.country}
|
|
longitude={coords.lng}
|
|
latitude={coords.lat}
|
|
anchor="bottom"
|
|
onClick={() => handleMarkerClick(req)}
|
|
>
|
|
<div
|
|
className="w-4 h-4 rounded-full border-2 border-white cursor-pointer"
|
|
style={{ backgroundColor: statusColors[req.status] || '#6b7280' }}
|
|
/>
|
|
</Marker>
|
|
)
|
|
})}
|
|
|
|
{popupInfo && (
|
|
<Popup
|
|
longitude={countryCoordinates[popupInfo.country]?.lng || 0}
|
|
latitude={countryCoordinates[popupInfo.country]?.lat || 0}
|
|
anchor="top"
|
|
onClose={() => setPopupInfo(null)}
|
|
closeButton
|
|
closeOnClick={false}
|
|
>
|
|
<div className="p-2 min-w-[200px]">
|
|
<h3 className="font-semibold text-sm mb-2">{popupInfo.country}</h3>
|
|
<Badge className={statusColors[popupInfo.status] ? `bg-${statusColors[popupInfo.status]}/20 text-${statusColors[popupInfo.status]}` : ''}>
|
|
{popupInfo.status}
|
|
</Badge>
|
|
<div className="mt-2 text-xs">
|
|
<div>Frameworks: {popupInfo.frameworks.join(', ')}</div>
|
|
<div>Requirements: {popupInfo.requirements.length}</div>
|
|
</div>
|
|
</div>
|
|
</Popup>
|
|
)}
|
|
</Map>
|
|
</div>
|
|
)
|
|
}
|
|
|