From 53e3acaa37e05756de90b27014d227e9ee29756b Mon Sep 17 00:00:00 2001 From: Yana Date: Wed, 21 May 2025 17:11:52 +0300 Subject: [PATCH] Add countries and locations to WorldMap --- explorer-v2/src/app/api/index.tsx | 72 +-- .../src/components/worldMap/WorldMap.tsx | 459 ++++++++++-------- 2 files changed, 302 insertions(+), 229 deletions(-) diff --git a/explorer-v2/src/app/api/index.tsx b/explorer-v2/src/app/api/index.tsx index 40097c8242..4923f87e58 100644 --- a/explorer-v2/src/app/api/index.tsx +++ b/explorer-v2/src/app/api/index.tsx @@ -253,41 +253,57 @@ export const fetchNSApiNodes = async (): Promise => { return allNodes; }; -export const fetchWorldMapCountries = - async (): Promise => { - // Fetch all nodes from the NS API - const nodes = await fetchNSApiNodes(); +export const fetchWorldMapCountries = async (): Promise<{ + countries: CountryDataResponse; + totalCountries: number; + uniqueLocations: number; + totalServers: number; +}> => { + // Fetch all nodes from the NS API + const nodes = await fetchNSApiNodes(); - // Create a map to count nodes by country - const countryCounts: Record = {}; + // Create a map to count nodes by country + const countryCounts: Record = {}; + // Set to track unique cities + const uniqueCities = new Set(); - // Process each node - for (const node of nodes) { - // Get the 2-letter country code from the node's geoip data - const twoLetterCode = node.geoip?.country; + // Process each node + for (const node of nodes) { + // Get the 2-letter country code from the node's geoip data + const twoLetterCode = node.geoip?.country; - if (twoLetterCode) { - // Convert to 3-letter country code - const threeLetterCode = countryCodeMap[twoLetterCode] || twoLetterCode; + if (twoLetterCode) { + // Convert to 3-letter country code + const threeLetterCode = countryCodeMap[twoLetterCode] || twoLetterCode; - // Increment the count for this country - countryCounts[threeLetterCode] = - (countryCounts[threeLetterCode] || 0) + 1; - } else { - // If no geoip data, count it as unknown - countryCounts[""] = (countryCounts[""] || 0) + 1; + // Increment the count for this country + countryCounts[threeLetterCode] = + (countryCounts[threeLetterCode] || 0) + 1; + + // Add city to unique cities set if it exists + if (node.geoip?.city) { + uniqueCities.add(node.geoip.city); } + } else { + // If no geoip data, count it as unknown + countryCounts[""] = (countryCounts[""] || 0) + 1; } + } - // Convert the counts to the required format - const result: CountryDataResponse = {}; + // Convert the counts to the required format + const result: CountryDataResponse = {}; - for (const [threeLetterCode, count] of Object.entries(countryCounts)) { - result[threeLetterCode] = { - ISO3: threeLetterCode, - nodes: count, - }; - } + for (const [threeLetterCode, count] of Object.entries(countryCounts)) { + result[threeLetterCode] = { + ISO3: threeLetterCode, + nodes: count, + }; + } - return result; + return { + countries: result, + totalCountries: Object.keys(countryCounts).length, + uniqueLocations: uniqueCities.size, + totalServers: nodes.length, }; +}; diff --git a/explorer-v2/src/components/worldMap/WorldMap.tsx b/explorer-v2/src/components/worldMap/WorldMap.tsx index 162d9cac72..ed700e5ac9 100644 --- a/explorer-v2/src/components/worldMap/WorldMap.tsx +++ b/explorer-v2/src/components/worldMap/WorldMap.tsx @@ -33,7 +33,17 @@ export const WorldMap = (): JSX.Element => { }>({ coordinates: [0, 0], zoom: 1 }); const { - data: countries = [], + data: { + countries = [], + totalCountries = 0, + uniqueLocations = 0, + totalServers = 0, + } = { + countries: [], + totalCountries: 0, + uniqueLocations: 0, + totalServers: 0, + }, isLoading: isLoadingCountries, isError: isCountriesError, } = useQuery({ @@ -129,221 +139,268 @@ export const WorldMap = (): JSX.Element => { } return ( - .MuiCardContent-root": { - height: { - xs: "200px", - sm: "auto", - }, - aspectRatio: { - xs: "unset", - sm: "16/7", - }, - }, - }} - > - + .MuiCardContent-root": { + height: { + xs: "200px", + sm: "auto", + }, + aspectRatio: { + xs: "unset", + sm: "16/7", + }, + }, }} > - - { - setPosition({ coordinates, zoom }); - }} - onClick={(e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - }} - > - - {({ geographies }: { geographies: GeoJSON.Feature[] }) => - geographies.map((geo) => { - const d = Array.isArray(countries) - ? { nodes: 0 } - : (countries as CountryDataResponse)[ - geo.properties?.ISO_A3 as string - ] || { nodes: 0 }; - return ( - { - const { NAME_LONG } = geo.properties as { - NAME_LONG: string; - }; - setTooltipContent(`${NAME_LONG} | ${d?.nodes || 0}`); - }} - onMouseLeave={() => { - setTooltipContent(""); - }} - style={{ - hover: countries - ? { - fill: theme.palette.accent.main, - outline: "white", - cursor: "pointer", - } - : undefined, - default: { - outline: "none", - }, - pressed: { - outline: "none", - }, - }} - /> - ); - }) - } - - - - -
- - setPosition((prev) => ({ - ...prev, - zoom: Math.min(prev.zoom + 0.5, 8), - })) - } - sx={{ - backgroundColor: isDarkMode - ? "rgba(255,255,255,0.1)" - : "rgba(0,0,0,0.1)", - "&:hover": { - backgroundColor: isDarkMode - ? "rgba(255,255,255,0.2)" - : "rgba(0,0,0,0.2)", - }, + - - - - setPosition((prev) => ({ - ...prev, - zoom: Math.max(prev.zoom - 0.5, 1), - })) - } - sx={{ + { + setPosition({ coordinates, zoom }); + }} + onClick={(e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + }} + > + + {({ geographies }: { geographies: GeoJSON.Feature[] }) => + geographies.map((geo) => { + const d = Array.isArray(countries) + ? { nodes: 0 } + : (countries as CountryDataResponse)[ + geo.properties?.ISO_A3 as string + ] || { nodes: 0 }; + return ( + { + const { NAME_LONG } = geo.properties as { + NAME_LONG: string; + }; + setTooltipContent(`${NAME_LONG} | ${d?.nodes || 0}`); + }} + onMouseLeave={() => { + setTooltipContent(""); + }} + style={{ + hover: countries + ? { + fill: theme.palette.accent.main, + outline: "white", + cursor: "pointer", + } + : undefined, + default: { + outline: "none", + }, + pressed: { + outline: "none", + }, + }} + /> + ); + }) + } + + + + +
- - - setPosition({ coordinates: [0, 0], zoom: 1 })} - sx={{ - backgroundColor: isDarkMode - ? "rgba(255,255,255,0.1)" - : "rgba(0,0,0,0.1)", - "&:hover": { + + setPosition((prev) => ({ + ...prev, + zoom: Math.min(prev.zoom + 0.5, 8), + })) + } + sx={{ backgroundColor: isDarkMode - ? "rgba(255,255,255,0.2)" - : "rgba(0,0,0,0.2)", - }, - }} - > - - -
- - + +
+ + setPosition((prev) => ({ + ...prev, + zoom: Math.max(prev.zoom - 0.5, 1), + })) + } + sx={{ + backgroundColor: isDarkMode + ? "rgba(255,255,255,0.1)" + : "rgba(0,0,0,0.1)", + "&:hover": { + backgroundColor: isDarkMode + ? "rgba(255,255,255,0.2)" + : "rgba(0,0,0,0.2)", + }, + }} + > + + + setPosition({ coordinates: [0, 0], zoom: 1 })} + sx={{ + backgroundColor: isDarkMode + ? "rgba(255,255,255,0.1)" + : "rgba(0,0,0,0.1)", + "&:hover": { + backgroundColor: isDarkMode + ? "rgba(255,255,255,0.2)" + : "rgba(0,0,0,0.2)", + }, + }} + > + + +
+
+ +
+ - + > + + + {totalServers} + + + + + {totalCountries} + + + + + {uniqueLocations} + + + + ); };