Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -293,5 +293,6 @@ jobs:
- run: mix compile --warnings-as-errors --all-warnings
- run: mix format --check-formatted
- run: mix deps.unlock --check-unused
- run: mix generate_countries_meta && git diff --exit-code -- assets/data/countries_meta.json
- run: mix credo diff --from-git-merge-base origin/master
- run: mix dialyzer
1 change: 1 addition & 0 deletions assets/data/countries_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"YE":{"flag":"🇾🇪","alpha_3":"YEM"},"QA":{"flag":"🇶🇦","alpha_3":"QAT"},"MR":{"flag":"🇲🇷","alpha_3":"MRT"},"CH":{"flag":"🇨🇭","alpha_3":"CHE"},"AF":{"flag":"🇦🇫","alpha_3":"AFG"},"AO":{"flag":"🇦🇴","alpha_3":"AGO"},"BT":{"flag":"🇧🇹","alpha_3":"BTN"},"HK":{"flag":"🇭🇰","alpha_3":"HKG"},"CU":{"flag":"🇨🇺","alpha_3":"CUB"},"HU":{"flag":"🇭🇺","alpha_3":"HUN"},"AZ":{"flag":"🇦🇿","alpha_3":"AZE"},"SS":{"flag":"🇸🇸","alpha_3":"SSD"},"BY":{"flag":"🇧🇾","alpha_3":"BLR"},"MA":{"flag":"🇲🇦","alpha_3":"MAR"},"MZ":{"flag":"🇲🇿","alpha_3":"MOZ"},"TR":{"flag":"🇹🇷","alpha_3":"TUR"},"PL":{"flag":"🇵🇱","alpha_3":"POL"},"US":{"flag":"🇺🇸","alpha_3":"USA"},"SR":{"flag":"🇸🇷","alpha_3":"SUR"},"LR":{"flag":"🇱🇷","alpha_3":"LBR"},"OM":{"flag":"🇴🇲","alpha_3":"OMN"},"MH":{"flag":"🇲🇭","alpha_3":"MHL"},"TG":{"flag":"🇹🇬","alpha_3":"TGO"},"CW":{"flag":"🇨🇼","alpha_3":"CUW"},"AT":{"flag":"🇦🇹","alpha_3":"AUT"},"CO":{"flag":"🇨🇴","alpha_3":"COL"},"SV":{"flag":"🇸🇻","alpha_3":"SLV"},"RE":{"flag":"🇷🇪","alpha_3":"REU"},"LI":{"flag":"🇱🇮","alpha_3":"LIE"},"PR":{"flag":"🇵🇷","alpha_3":"PRI"},"GI":{"flag":"🇬🇮","alpha_3":"GIB"},"CV":{"flag":"🇨🇻","alpha_3":"CPV"},"KG":{"flag":"🇰🇬","alpha_3":"KGZ"},"SK":{"flag":"🇸🇰","alpha_3":"SVK"},"LT":{"flag":"🇱🇹","alpha_3":"LTU"},"AL":{"flag":"🇦🇱","alpha_3":"ALB"},"BL":{"flag":"🇧🇱","alpha_3":"BLM"},"FK":{"flag":"🇫🇰","alpha_3":"FLK"},"TV":{"flag":"🇹🇻","alpha_3":"TUV"},"SJ":{"flag":"🇸🇯","alpha_3":"SJM"},"CX":{"flag":"🇨🇽","alpha_3":"CXR"},"VG":{"flag":"🇻🇬","alpha_3":"VGB"},"VI":{"flag":"🇻🇮","alpha_3":"VIR"},"TT":{"flag":"🇹🇹","alpha_3":"TTO"},"AI":{"flag":"🇦🇮","alpha_3":"AIA"},"GE":{"flag":"🇬🇪","alpha_3":"GEO"},"GB":{"flag":"🇬🇧","alpha_3":"GBR"},"TW":{"flag":"🇹🇼","alpha_3":"TWN"},"BV":{"flag":"🇧🇻","alpha_3":"BVT"},"AU":{"flag":"🇦🇺","alpha_3":"AUS"},"CD":{"flag":"🇨🇩","alpha_3":"COD"},"IL":{"flag":"🇮🇱","alpha_3":"ISR"},"FJ":{"flag":"🇫🇯","alpha_3":"FJI"},"HM":{"flag":"🇭🇲","alpha_3":"HMD"},"MO":{"flag":"🇲🇴","alpha_3":"MAC"},"LU":{"flag":"🇱🇺","alpha_3":"LUX"},"TH":{"flag":"🇹🇭","alpha_3":"THA"},"TZ":{"flag":"🇹🇿","alpha_3":"TZA"},"MC":{"flag":"🇲🇨","alpha_3":"MCO"},"AD":{"flag":"🇦🇩","alpha_3":"AND"},"IQ":{"flag":"🇮🇶","alpha_3":"IRQ"},"NI":{"flag":"🇳🇮","alpha_3":"NIC"},"KW":{"flag":"🇰🇼","alpha_3":"KWT"},"MW":{"flag":"🇲🇼","alpha_3":"MWI"},"GH":{"flag":"🇬🇭","alpha_3":"GHA"},"DM":{"flag":"🇩🇲","alpha_3":"DMA"},"EE":{"flag":"🇪🇪","alpha_3":"EST"},"IS":{"flag":"🇮🇸","alpha_3":"ISL"},"BM":{"flag":"🇧🇲","alpha_3":"BMU"},"EC":{"flag":"🇪🇨","alpha_3":"ECU"},"KM":{"flag":"🇰🇲","alpha_3":"COM"},"SB":{"flag":"🇸🇧","alpha_3":"SLB"},"AE":{"flag":"🇦🇪","alpha_3":"ARE"},"CM":{"flag":"🇨🇲","alpha_3":"CMR"},"EH":{"flag":"🇪🇭","alpha_3":"ESH"},"CC":{"flag":"🇨🇨","alpha_3":"CCK"},"AS":{"flag":"🇦🇸","alpha_3":"ASM"},"KH":{"flag":"🇰🇭","alpha_3":"KHM"},"MD":{"flag":"🇲🇩","alpha_3":"MDA"},"NA":{"flag":"🇳🇦","alpha_3":"NAM"},"SA":{"flag":"🇸🇦","alpha_3":"SAU"},"HN":{"flag":"🇭🇳","alpha_3":"HND"},"MK":{"flag":"🇲🇰","alpha_3":"MKD"},"LC":{"flag":"🇱🇨","alpha_3":"LCA"},"PA":{"flag":"🇵🇦","alpha_3":"PAN"},"VC":{"flag":"🇻🇨","alpha_3":"VCT"},"TM":{"flag":"🇹🇲","alpha_3":"TKM"},"SI":{"flag":"🇸🇮","alpha_3":"SVN"},"GQ":{"flag":"🇬🇶","alpha_3":"GNQ"},"PH":{"flag":"🇵🇭","alpha_3":"PHL"},"CZ":{"flag":"🇨🇿","alpha_3":"CZE"},"BO":{"flag":"🇧🇴","alpha_3":"BOL"},"SY":{"flag":"🇸🇾","alpha_3":"SYR"},"NO":{"flag":"🇳🇴","alpha_3":"NOR"},"IM":{"flag":"🇮🇲","alpha_3":"IMN"},"SX":{"flag":"🇸🇽","alpha_3":"SXM"},"GG":{"flag":"🇬🇬","alpha_3":"GGY"},"GW":{"flag":"🇬🇼","alpha_3":"GNB"},"SH":{"flag":"🇸🇭","alpha_3":"SHN"},"NC":{"flag":"🇳🇨","alpha_3":"NCL"},"BE":{"flag":"🇧🇪","alpha_3":"BEL"},"JP":{"flag":"🇯🇵","alpha_3":"JPN"},"LV":{"flag":"🇱🇻","alpha_3":"LVA"},"AM":{"flag":"🇦🇲","alpha_3":"ARM"},"SD":{"flag":"🇸🇩","alpha_3":"SDN"},"GT":{"flag":"🇬🇹","alpha_3":"GTM"},"PY":{"flag":"🇵🇾","alpha_3":"PRY"},"MN":{"flag":"🇲🇳","alpha_3":"MNG"},"TK":{"flag":"🇹🇰","alpha_3":"TKL"},"DZ":{"flag":"🇩🇿","alpha_3":"DZA"},"KZ":{"flag":"🇰🇿","alpha_3":"KAZ"},"LY":{"flag":"🇱🇾","alpha_3":"LBY"},"AW":{"flag":"🇦🇼","alpha_3":"ABW"},"UY":{"flag":"🇺🇾","alpha_3":"URY"},"GL":{"flag":"🇬🇱","alpha_3":"GRL"},"SN":{"flag":"🇸🇳","alpha_3":"SEN"},"UM":{"flag":"🇺🇲","alpha_3":"UMI"},"JO":{"flag":"🇯🇴","alpha_3":"JOR"},"MT":{"flag":"🇲🇹","alpha_3":"MLT"},"BS":{"flag":"🇧🇸","alpha_3":"BHS"},"BI":{"flag":"🇧🇮","alpha_3":"BDI"},"BA":{"flag":"🇧🇦","alpha_3":"BIH"},"MQ":{"flag":"🇲🇶","alpha_3":"MTQ"},"MU":{"flag":"🇲🇺","alpha_3":"MUS"},"MS":{"flag":"🇲🇸","alpha_3":"MSR"},"BW":{"flag":"🇧🇼","alpha_3":"BWA"},"YT":{"flag":"🇾🇹","alpha_3":"MYT"},"PN":{"flag":"🇵🇳","alpha_3":"PCN"},"MP":{"flag":"🇲🇵","alpha_3":"MNP"},"ML":{"flag":"🇲🇱","alpha_3":"MLI"},"BH":{"flag":"🇧🇭","alpha_3":"BHR"},"LB":{"flag":"🇱🇧","alpha_3":"LBN"},"AR":{"flag":"🇦🇷","alpha_3":"ARG"},"PG":{"flag":"🇵🇬","alpha_3":"PNG"},"GR":{"flag":"🇬🇷","alpha_3":"GRC"},"HT":{"flag":"🇭🇹","alpha_3":"HTI"},"WS":{"flag":"🇼🇸","alpha_3":"WSM"},"SG":{"flag":"🇸🇬","alpha_3":"SGP"},"GP":{"flag":"🇬🇵","alpha_3":"GLP"},"BF":{"flag":"🇧🇫","alpha_3":"BFA"},"ME":{"flag":"🇲🇪","alpha_3":"MNE"},"AQ":{"flag":"🇦🇶","alpha_3":"ATA"},"PK":{"flag":"🇵🇰","alpha_3":"PAK"},"FM":{"flag":"🇫🇲","alpha_3":"FSM"},"MV":{"flag":"🇲🇻","alpha_3":"MDV"},"GS":{"flag":"🇬🇸","alpha_3":"SGS"},"BN":{"flag":"🇧🇳","alpha_3":"BRN"},"CK":{"flag":"🇨🇰","alpha_3":"COK"},"IO":{"flag":"🇮🇴","alpha_3":"IOT"},"SE":{"flag":"🇸🇪","alpha_3":"SWE"},"SC":{"flag":"🇸🇨","alpha_3":"SYC"},"ZW":{"flag":"🇿🇼","alpha_3":"ZWE"},"SL":{"flag":"🇸🇱","alpha_3":"SLE"},"AG":{"flag":"🇦🇬","alpha_3":"ATG"},"PF":{"flag":"🇵🇫","alpha_3":"PYF"},"CF":{"flag":"🇨🇫","alpha_3":"CAF"},"BD":{"flag":"🇧🇩","alpha_3":"BGD"},"AX":{"flag":"🇦🇽","alpha_3":"ALA"},"SZ":{"flag":"🇸🇿","alpha_3":"SWZ"},"HR":{"flag":"🇭🇷","alpha_3":"HRV"},"RS":{"flag":"🇷🇸","alpha_3":"SRB"},"NF":{"flag":"🇳🇫","alpha_3":"NFK"},"IE":{"flag":"🇮🇪","alpha_3":"IRL"},"NR":{"flag":"🇳🇷","alpha_3":"NRU"},"ZA":{"flag":"🇿🇦","alpha_3":"ZAF"},"CA":{"flag":"🇨🇦","alpha_3":"CAN"},"KR":{"flag":"🇰🇷","alpha_3":"KOR"},"A1":{"flag":"🏳️","alpha_3":null},"VA":{"flag":"🇻🇦","alpha_3":"VAT"},"NU":{"flag":"🇳🇺","alpha_3":"NIU"},"JE":{"flag":"🇯🇪","alpha_3":"JEY"},"TD":{"flag":"🇹🇩","alpha_3":"TCD"},"IR":{"flag":"🇮🇷","alpha_3":"IRN"},"NL":{"flag":"🇳🇱","alpha_3":"NLD"},"BB":{"flag":"🇧🇧","alpha_3":"BRB"},"FI":{"flag":"🇫🇮","alpha_3":"FIN"},"UA":{"flag":"🇺🇦","alpha_3":"UKR"},"ID":{"flag":"🇮🇩","alpha_3":"IDN"},"ST":{"flag":"🇸🇹","alpha_3":"STP"},"VU":{"flag":"🇻🇺","alpha_3":"VUT"},"RU":{"flag":"🇷🇺","alpha_3":"RUS"},"NE":{"flag":"🇳🇪","alpha_3":"NER"},"TO":{"flag":"🇹🇴","alpha_3":"TON"},"UZ":{"flag":"🇺🇿","alpha_3":"UZB"},"GN":{"flag":"🇬🇳","alpha_3":"GIN"},"JM":{"flag":"🇯🇲","alpha_3":"JAM"},"FR":{"flag":"🇫🇷","alpha_3":"FRA"},"TL":{"flag":"🇹🇱","alpha_3":"TLS"},"ET":{"flag":"🇪🇹","alpha_3":"ETH"},"KI":{"flag":"🇰🇮","alpha_3":"KIR"},"CG":{"flag":"🇨🇬","alpha_3":"COG"},"DE":{"flag":"🇩🇪","alpha_3":"DEU"},"RW":{"flag":"🇷🇼","alpha_3":"RWA"},"DO":{"flag":"🇩🇴","alpha_3":"DOM"},"VE":{"flag":"🇻🇪","alpha_3":"VEN"},"PW":{"flag":"🇵🇼","alpha_3":"PLW"},"TC":{"flag":"🇹🇨","alpha_3":"TCA"},"ZM":{"flag":"🇿🇲","alpha_3":"ZMB"},"NG":{"flag":"🇳🇬","alpha_3":"NGA"},"WF":{"flag":"🇼🇫","alpha_3":"WLF"},"GF":{"flag":"🇬🇫","alpha_3":"GUF"},"KN":{"flag":"🇰🇳","alpha_3":"KNA"},"ES":{"flag":"🇪🇸","alpha_3":"ESP"},"GM":{"flag":"🇬🇲","alpha_3":"GMB"},"KP":{"flag":"🇰🇵","alpha_3":"PRK"},"GY":{"flag":"🇬🇾","alpha_3":"GUY"},"MX":{"flag":"🇲🇽","alpha_3":"MEX"},"IN":{"flag":"🇮🇳","alpha_3":"IND"},"SM":{"flag":"🇸🇲","alpha_3":"SMR"},"BG":{"flag":"🇧🇬","alpha_3":"BGR"},"MF":{"flag":"🇲🇫","alpha_3":"MAF"},"CL":{"flag":"🇨🇱","alpha_3":"CHL"},"VN":{"flag":"🇻🇳","alpha_3":"VNM"},"NP":{"flag":"🇳🇵","alpha_3":"NPL"},"CR":{"flag":"🇨🇷","alpha_3":"CRI"},"ER":{"flag":"🇪🇷","alpha_3":"ERI"},"LK":{"flag":"🇱🇰","alpha_3":"LKA"},"CI":{"flag":"🇨🇮","alpha_3":"CIV"},"PT":{"flag":"🇵🇹","alpha_3":"PRT"},"TJ":{"flag":"🇹🇯","alpha_3":"TJK"},"MY":{"flag":"🇲🇾","alpha_3":"MYS"},"PS":{"flag":"🇵🇸","alpha_3":"PSE"},"PE":{"flag":"🇵🇪","alpha_3":"PER"},"LS":{"flag":"🇱🇸","alpha_3":"LSO"},"CY":{"flag":"🇨🇾","alpha_3":"CYP"},"TN":{"flag":"🇹🇳","alpha_3":"TUN"},"XK":{"flag":"🇽🇰","alpha_3":"XKX"},"KY":{"flag":"🇰🇾","alpha_3":"CYM"},"DK":{"flag":"🇩🇰","alpha_3":"DNK"},"BZ":{"flag":"🇧🇿","alpha_3":"BLZ"},"FO":{"flag":"🇫🇴","alpha_3":"FRO"},"LA":{"flag":"🇱🇦","alpha_3":"LAO"},"RO":{"flag":"🇷🇴","alpha_3":"ROU"},"DJ":{"flag":"🇩🇯","alpha_3":"DJI"},"EG":{"flag":"🇪🇬","alpha_3":"EGY"},"KE":{"flag":"🇰🇪","alpha_3":"KEN"},"UG":{"flag":"🇺🇬","alpha_3":"UGA"},"MM":{"flag":"🇲🇲","alpha_3":"MMR"},"BQ":{"flag":"🇧🇶","alpha_3":"BES"},"PM":{"flag":"🇵🇲","alpha_3":"SPM"},"GD":{"flag":"🇬🇩","alpha_3":"GRD"},"GU":{"flag":"🇬🇺","alpha_3":"GUM"},"CN":{"flag":"🇨🇳","alpha_3":"CHN"},"SO":{"flag":"🇸🇴","alpha_3":"SOM"},"BJ":{"flag":"🇧🇯","alpha_3":"BEN"},"BR":{"flag":"🇧🇷","alpha_3":"BRA"},"GA":{"flag":"🇬🇦","alpha_3":"GAB"},"NZ":{"flag":"🇳🇿","alpha_3":"NZL"},"MG":{"flag":"🇲🇬","alpha_3":"MDG"},"IT":{"flag":"🇮🇹","alpha_3":"ITA"},"TF":{"flag":"🇹🇫","alpha_3":"ATF"}}
Comment thread
RobertJoonas marked this conversation as resolved.
Outdated
14 changes: 7 additions & 7 deletions assets/js/dashboard/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
BREAKDOWN_REPORTS,
BreakdownReportKey
} from './stats/reports/reports-config'
import LocationsModal from './stats/modals/locations-modal'
import { LocationsDetails } from './stats/locations/details'
import PropsModal from './stats/modals/props'
import ConversionsModal from './stats/modals/conversions'
import FilterModal from './stats/modals/filter-modal'
Expand Down Expand Up @@ -113,18 +113,18 @@ export const exitPagesRoute = {
}

export const countriesRoute = {
path: 'countries',
element: <LocationsModal currentView="countries" />
path: BREAKDOWN_REPORTS.countries.detailsPath,
element: <LocationsDetails reportKey={BreakdownReportKey.countries} />
}

export const regionsRoute = {
path: 'regions',
element: <LocationsModal currentView="regions" />
path: BREAKDOWN_REPORTS.regions.detailsPath,
element: <LocationsDetails reportKey={BreakdownReportKey.regions} />
}

export const citiesRoute = {
path: 'cities',
element: <LocationsModal currentView="cities" />
path: BREAKDOWN_REPORTS.cities.detailsPath,
element: <LocationsDetails reportKey={BreakdownReportKey.cities} />
}

export const browsersRoute = {
Expand Down
29 changes: 29 additions & 0 deletions assets/js/dashboard/stats/locations/countries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import worldJson from 'visionscarto-world-atlas/world/110m.json'
import countriesMeta from '../../../../data/countries_meta.json'
import * as topojson from 'topojson-client'

// The actual type is more extensive, this is only the part that we care about
export type WorldJsonCountryData = { properties: { a3: string } }

export function parseWorldTopoJsonToGeoJsonFeatures(): Array<WorldJsonCountryData> {
const collection = topojson.feature(
// @ts-expect-error strings in worldJson not recongizable as the enum values declared in library
worldJson,
worldJson.objects.countries
)
// @ts-expect-error topojson.feature return type incorrectly inferred as not a collection
return collection.features
}

export type CountryEntry = {
// alpha_3 is null for non-country override entries like "A1" (Anonymous VPN)
alpha_3: string | null
flag: string
}

type CountryTwoLetterCode = string

export type CountriesLookup = Record<CountryTwoLetterCode, CountryEntry>

export const COUNTRIES_BY_TWO_LETTER_CODE =
countriesMeta as unknown as CountriesLookup
104 changes: 104 additions & 0 deletions assets/js/dashboard/stats/locations/details.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, { ReactNode } from 'react'
import { useDashboardStateContext } from '../../dashboard-state-context'
import {
hasConversionGoalFilter,
isRealTimeDashboard
} from '../../util/filters'
import { chooseBreakdownMetricsByContext } from '../breakdowns'
import {
BREAKDOWN_REPORTS,
BreakdownReportKey
} from '../reports/reports-config'
import {
DetailsBreakdown,
DimensionCell,
DimensionCellProps
} from '../modals/details-breakdown'
import Modal from '../modals/modal'
import {
getCitiesFilterInfo,
getCountriesFilterInfo,
getRegionsFilterInfo,
LocationsReportKey
} from '.'
import { FlagEmoji } from './flag-emoji'

export function LocationsDetails({
reportKey
}: {
reportKey: LocationsReportKey
}) {
const { dashboardState } = useDashboardStateContext()
const reportConfig = BREAKDOWN_REPORTS[reportKey]

const metrics = chooseBreakdownMetricsByContext(
reportConfig.metricsByContext,
{
hasConversionGoalFilter: hasConversionGoalFilter(dashboardState),
isRealtime: isRealTimeDashboard(dashboardState),
isDetailed: true,
isRevenueAvailable: false
Comment thread
RobertJoonas marked this conversation as resolved.
Outdated
}
)

const DimensionElement = DIMENSION_ELEMENTS[reportKey]

return (
<Modal>
<DetailsBreakdown
title={reportConfig.detailsTitle}
dimensionLabel={reportConfig.dimensionLabel}
dimensions={reportConfig.dimensions}
metrics={metrics}
defaultOrderBy={[['visitors', 'desc']]}
searchEnabled={false}
Comment thread
RobertJoonas marked this conversation as resolved.
Outdated
DimensionElement={DimensionElement}
/>
</Modal>
)
}

const CountryDimensionCell = (props: DimensionCellProps) => {
const [countryCode, countryName] = props.row.dimensions
return (
<DimensionCell
{...props}
text={countryName}
icon={<FlagEmoji countryCode={countryCode} />}
getFilterInfo={getCountriesFilterInfo}
/>
)
}

const RegionsDimensionCell = (props: DimensionCellProps) => {
const [_regionCode, regionName, countryCode] = props.row.dimensions
return (
<DimensionCell
{...props}
text={regionName}
icon={<FlagEmoji countryCode={countryCode} />}
getFilterInfo={getRegionsFilterInfo}
/>
)
}

const CitiesDimensionCell = (props: DimensionCellProps) => {
const [_cityCode, cityName, countryCode] = props.row.dimensions
return (
<DimensionCell
{...props}
text={cityName}
icon={<FlagEmoji countryCode={countryCode} />}
getFilterInfo={getCitiesFilterInfo}
/>
)
}

const DIMENSION_ELEMENTS: Record<
LocationsReportKey,
(props: DimensionCellProps) => ReactNode
> = {
[BreakdownReportKey.countries]: CountryDimensionCell,
[BreakdownReportKey.regions]: RegionsDimensionCell,
[BreakdownReportKey.cities]: CitiesDimensionCell
}
13 changes: 13 additions & 0 deletions assets/js/dashboard/stats/locations/flag-emoji.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react'
import { COUNTRIES_BY_TWO_LETTER_CODE } from './countries'

export const FlagEmoji = ({ countryCode }: { countryCode: string | null }) => {
Comment thread
RobertJoonas marked this conversation as resolved.
if (!countryCode) {
return null
}
const entry = COUNTRIES_BY_TWO_LETTER_CODE[countryCode]
if (!entry?.flag) {
return null
}
return <span className="mr-1">{entry.flag}</span>
}
Loading
Loading