'use client' import { useState, useEffect } from 'react' import { formatNumber } from '@ciphera-net/ui' import * as Flags from 'country-flag-icons/react/3x2' import iso3166 from 'iso-3166-2' import WorldMap from './WorldMap' import { Modal, GlobeIcon } from '@ciphera-net/ui' import { ListSkeleton } from '@/components/skeletons' import { SiTorproject } from 'react-icons/si' import { FaUserSecret, FaSatellite } from 'react-icons/fa' import { getCountries, getCities, getRegions } from '@/lib/api/stats' interface LocationProps { countries: Array<{ country: string; pageviews: number }> cities: Array<{ city: string; country: string; pageviews: number }> regions: Array<{ region: string; country: string; pageviews: number }> geoDataLevel?: 'full' | 'country' | 'none' siteId: string dateRange: { start: string, end: string } } type Tab = 'map' | 'countries' | 'regions' | 'cities' const LIMIT = 7 export default function Locations({ countries, cities, regions, geoDataLevel = 'full', siteId, dateRange }: LocationProps) { const [activeTab, setActiveTab] = useState('map') const [isModalOpen, setIsModalOpen] = useState(false) type LocationItem = { country?: string; city?: string; region?: string; pageviews: number } const [fullData, setFullData] = useState([]) const [isLoadingFull, setIsLoadingFull] = useState(false) useEffect(() => { if (isModalOpen) { const fetchData = async () => { setIsLoadingFull(true) try { let data: LocationItem[] = [] if (activeTab === 'countries') { data = await getCountries(siteId, dateRange.start, dateRange.end, 250) } else if (activeTab === 'regions') { data = await getRegions(siteId, dateRange.start, dateRange.end, 250) } else if (activeTab === 'cities') { data = await getCities(siteId, dateRange.start, dateRange.end, 250) } setFullData(data) } catch (e) { console.error(e) } finally { setIsLoadingFull(false) } } fetchData() } else { setFullData([]) } }, [isModalOpen, activeTab, siteId, dateRange]) const getFlagComponent = (countryCode: string) => { if (!countryCode || countryCode === 'Unknown') return null switch (countryCode) { case 'T1': return case 'A1': return case 'A2': return case 'O1': case 'EU': case 'AP': return } const FlagComponent = (Flags as Record>)[countryCode] return FlagComponent ? : null } const getCountryName = (code: string) => { if (!code || code === 'Unknown') return 'Unknown' switch (code) { case 'T1': return 'Tor Network' case 'A1': return 'Anonymous Proxy' case 'A2': return 'Satellite Provider' case 'O1': return 'Other' case 'EU': return 'Europe' case 'AP': return 'Asia/Pacific' } try { const regionNames = new Intl.DisplayNames(['en'], { type: 'region' }) return regionNames.of(code) || code } catch (e) { return code } } const getRegionName = (regionCode: string, countryCode: string) => { // Check for special country codes first switch (countryCode) { case 'T1': return 'Tor Network' case 'A1': return 'Anonymous Proxy' case 'A2': return 'Satellite Provider' case 'O1': return 'Other' case 'EU': return 'Europe' case 'AP': return 'Asia/Pacific' } if (!regionCode || regionCode === 'Unknown' || !countryCode || countryCode === 'Unknown') return 'Unknown' try { const countryData = iso3166.data[countryCode] if (!countryData || !countryData.sub) return regionCode // ISO 3166-2 structure keys are typically "US-OR" const fullCode = `${countryCode}-${regionCode}` const regionData = countryData.sub[fullCode] if (regionData && regionData.name) { return regionData.name } return regionCode } catch (e) { return regionCode } } const getCityName = (city: string) => { // Check for special codes that might appear in city field switch (city) { case 'T1': return 'Tor Network' case 'A1': return 'Anonymous Proxy' case 'A2': return 'Satellite Provider' case 'O1': return 'Other' } if (!city || city === 'Unknown') return 'Unknown' return city } const getData = () => { switch (activeTab) { case 'countries': return countries case 'regions': return regions case 'cities': return cities default: return [] } } // Check if the current tab's data is disabled by privacy settings const isTabDisabled = () => { if (geoDataLevel === 'none') return true if (geoDataLevel === 'country' && (activeTab === 'regions' || activeTab === 'cities')) return true return false } // Filter out "Unknown" entries that result from disabled collection const filterUnknown = (data: LocationItem[]) => { return data.filter(item => { if (activeTab === 'countries') return item.country && item.country !== 'Unknown' && item.country !== '' if (activeTab === 'regions') return item.region && item.region !== 'Unknown' && item.region !== '' if (activeTab === 'cities') return item.city && item.city !== 'Unknown' && item.city !== '' return true }) } const rawData = activeTab === 'map' ? [] : getData() const data = filterUnknown(rawData) const hasData = activeTab === 'map' ? (countries && filterUnknown(countries).length > 0) : (data && data.length > 0) const displayedData = (activeTab !== 'map' && hasData) ? data.slice(0, LIMIT) : [] const emptySlots = Math.max(0, LIMIT - displayedData.length) const showViewAll = activeTab !== 'map' && hasData && data.length > LIMIT const getDisabledMessage = () => { if (geoDataLevel === 'none') { return 'Geographic data collection is disabled in site settings' } if (geoDataLevel === 'country' && (activeTab === 'regions' || activeTab === 'cities')) { return `${activeTab === 'regions' ? 'Region' : 'City'} tracking is disabled. Only country-level data is collected.` } return 'No data available' } return ( <>

Locations

{showViewAll && ( )}
{(['map', 'countries', 'regions', 'cities'] as Tab[]).map((tab) => ( ))}
{isTabDisabled() ? (

{getDisabledMessage()}

) : activeTab === 'map' ? ( hasData ? : (

No location data yet

Visitor locations will appear here based on anonymous geographic data.

) ) : ( hasData ? ( <> {displayedData.map((item, index) => (
{activeTab === 'countries' && {getFlagComponent(item.country ?? '')}} {activeTab !== 'countries' && {getFlagComponent(item.country ?? '')}} {activeTab === 'countries' ? getCountryName(item.country ?? '') : activeTab === 'regions' ? getRegionName(item.region ?? '', item.country ?? '') : getCityName(item.city ?? '')}
{formatNumber(item.pageviews)}
))} {Array.from({ length: emptySlots }).map((_, i) => (
setIsModalOpen(false)} title={`Locations - ${activeTab.charAt(0).toUpperCase() + activeTab.slice(1)}`} >
{isLoadingFull ? (
) : ( (fullData.length > 0 ? fullData : data).map((item, index) => (
{getFlagComponent(item.country ?? '')} {activeTab === 'countries' ? getCountryName(item.country ?? '') : activeTab === 'regions' ? getRegionName(item.region ?? '', item.country ?? '') : getCityName(item.city ?? '')}
{formatNumber(item.pageviews)}
)) )}
) }