Initial commit: MOPC platform with Docker deployment setup
Full Next.js 15 platform with tRPC, Prisma, PostgreSQL, NextAuth. Includes production Dockerfile (multi-stage, port 7600), docker-compose with registry-based image pull, Gitea Actions CI workflow, nginx config for portal.monaco-opc.com, deployment scripts, and DEPLOYMENT.md guide. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
78
src/components/charts/leaflet-map.tsx
Normal file
78
src/components/charts/leaflet-map.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
'use client'
|
||||
|
||||
import { useMemo } from 'react'
|
||||
import { MapContainer, TileLayer, CircleMarker, Tooltip } from 'react-leaflet'
|
||||
import 'leaflet/dist/leaflet.css'
|
||||
import { COUNTRIES } from '@/lib/countries'
|
||||
|
||||
|
||||
type CountryData = { countryCode: string; count: number }
|
||||
|
||||
type LeafletMapProps = {
|
||||
data: CountryData[]
|
||||
compact?: boolean
|
||||
}
|
||||
|
||||
export default function LeafletMap({ data, compact }: LeafletMapProps) {
|
||||
const markers = useMemo(() => {
|
||||
const maxCount = Math.max(...data.map((d) => d.count), 1)
|
||||
return data
|
||||
.filter((d) => d.countryCode !== 'UNKNOWN' && COUNTRIES[d.countryCode])
|
||||
.map((d) => {
|
||||
const country = COUNTRIES[d.countryCode]
|
||||
const ratio = d.count / maxCount
|
||||
const radius = 5 + ratio * 15
|
||||
return {
|
||||
code: d.countryCode,
|
||||
name: country.name,
|
||||
position: [country.lat, country.lng] as [number, number],
|
||||
count: d.count,
|
||||
radius,
|
||||
ratio,
|
||||
}
|
||||
})
|
||||
}, [data])
|
||||
|
||||
return (
|
||||
<MapContainer
|
||||
center={[20, 0]}
|
||||
zoom={compact ? 1 : 2}
|
||||
scrollWheelZoom
|
||||
zoomControl
|
||||
dragging
|
||||
doubleClickZoom
|
||||
style={{
|
||||
height: compact ? 400 : 500,
|
||||
width: '100%',
|
||||
borderRadius: '0.5rem',
|
||||
background: '#f0f0f0',
|
||||
}}
|
||||
attributionControl={false}
|
||||
>
|
||||
<TileLayer
|
||||
url="https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png"
|
||||
/>
|
||||
{markers.map((marker) => (
|
||||
<CircleMarker
|
||||
key={marker.code}
|
||||
center={marker.position}
|
||||
radius={marker.radius}
|
||||
pathOptions={{
|
||||
color: '#de0f1e',
|
||||
fillColor: '#de0f1e',
|
||||
fillOpacity: 0.35 + marker.ratio * 0.45,
|
||||
weight: 1.5,
|
||||
}}
|
||||
>
|
||||
<Tooltip direction="top" offset={[0, -marker.radius]}>
|
||||
<div className="text-xs font-medium">
|
||||
<span className="font-semibold">{marker.name}</span>
|
||||
<br />
|
||||
{marker.count} project{marker.count !== 1 ? 's' : ''}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</CircleMarker>
|
||||
))}
|
||||
</MapContainer>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user