added geography page

This commit is contained in:
Juthatip McDevitt 2024-04-02 22:36:40 -05:00
parent 980e69273a
commit 39b2da4a00
13 changed files with 14014 additions and 5 deletions

View file

@ -9,6 +9,7 @@ import Dashboard from "pages/dashboard/Dashboard";
import Products from "pages/products/Products"; import Products from "pages/products/Products";
import Customers from "pages/customers/Customers"; import Customers from "pages/customers/Customers";
import Transactions from "pages/transactions/Transactions"; import Transactions from "pages/transactions/Transactions";
import Geography from "pages/geography/Geography";
function App() { function App() {
@ -29,6 +30,7 @@ function App() {
<Route path="/products" element={<Products/>} /> <Route path="/products" element={<Products/>} />
<Route path="/customers" element={<Customers/>} /> <Route path="/customers" element={<Customers/>} />
<Route path="/transactions" element={<Transactions/>} /> <Route path="/transactions" element={<Transactions/>} />
<Route path="/geography" element={<Geography/>} />
</Route> </Route>
</Routes> </Routes>
</ThemeProvider> </ThemeProvider>

View file

@ -0,0 +1,88 @@
import { Box, useTheme } from '@mui/material'
import Header from 'components/Header'
import { useGetGeographyQuery } from 'state/api'
import { ResponsiveChoropleth } from '@nivo/geo'
import { geographyData } from 'state/geographyData'
const Geography = () => {
const theme = useTheme();
const {data} = useGetGeographyQuery();
return (
<Box m="1.5rem 2.5rem">
<Header title="Geography" subtitle="Find your user location "/>
<Box mt="40px" height="75vh" border={`1px solid ${theme.palette.secondary[200]}`} borderRadius="4px">
{data? (
<ResponsiveChoropleth data={data}
theme={{
axis: {
domain: {
line: {
stroke: theme.palette.secondary[200],
},
},
legend: {
text: {
fill: theme.palette.secondary[200],
},
},
ticks: {
line: {
stroke: theme.palette.secondary[200],
strokeWidth: 1,
},
text: {
fill: theme.palette.secondary[200],
},
},
},
legends: {
text: {
fill: theme.palette.secondary[200],
},
},
tooltip: {
container: {
color: theme.palette.primary.main,
},
},
}}
features={geographyData.features}
margin={{ top: 0, right: 0, bottom: 0, left: -50 }}
colors="YlGnBu"
domain={[0, 60]}
unknownColor="#666666"
label="properties.name"
valueFormat=".2s"
projectionScale={150}
projectionTranslation={[0.45, 0.6]}
projectionRotation={[0, 0, 0]}
borderWidth={1.3}
borderColor="#ADBC9F"
legends={[
{
anchor: "bottom-right",
direction: "column",
justify: true,
translateX: 0,
translateY: -125,
itemsSpacing: 0,
itemWidth: 94,
itemHeight: 18,
itemDirection: "left-to-right",
itemTextColor: theme.palette.secondary[200],
itemOpacity: 0.85,
symbolSize: 18,
},
]}/>
) : (
<>Loading...</>
)}
</Box>
</Box>
)
}
export default Geography

View file

@ -3,7 +3,7 @@ import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"
export const api = createApi({ export const api = createApi({
baseQuery: fetchBaseQuery({baseUrl: process.env.REACT_APP_BASE_URL}), baseQuery: fetchBaseQuery({baseUrl: process.env.REACT_APP_BASE_URL}),
reducerPath: "adminApi", reducerPath: "adminApi",
tagTypes: ["User", "Products", "Customers", "Transactions", ], tagTypes: ["User", "Products", "Customers", "Transactions", "Geography" ],
endpoints: (build) => ({ endpoints: (build) => ({
getUser: build.query({ getUser: build.query({
query: (id) => `general/user/${id}`, query: (id) => `general/user/${id}`,
@ -15,7 +15,7 @@ export const api = createApi({
}), }),
getCustomers: build.query({ getCustomers: build.query({
query: () => "client/customers", query: () => "client/customers",
providesTags: ["Customers"] providesTags: ["Customers"],
}), }),
getTransactions: build.query({ getTransactions: build.query({
query: ({page, pageSize, sort, search}) => ({ query: ({page, pageSize, sort, search}) => ({
@ -24,8 +24,12 @@ export const api = createApi({
params: {page, pageSize, sort, search}, params: {page, pageSize, sort, search},
}), }),
providesTags: ["Transactions"] providesTags: ["Transactions"]
}),
getGeography: build.query({
query: () => "client/geography",
providesTags: ["Geography"],
}) })
}) })
}) })
export const {useGetUserQuery, useGetProductsQuery, useGetCustomersQuery, useGetTransactionsQuery} = api; export const {useGetUserQuery, useGetProductsQuery, useGetCustomersQuery, useGetTransactionsQuery, useGetGeographyQuery} = api;

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@ import Product from "../models/Product.js";
import ProductStat from "../models/ProductStat.js"; import ProductStat from "../models/ProductStat.js";
import Transaction from "../models/Transaction.js"; import Transaction from "../models/Transaction.js";
import User from "../models/User.js" import User from "../models/User.js"
import getCountryIso3 from "country-iso-2-to-3"
export const getProducts = async(req, res) => { export const getProducts = async(req, res) => {
@ -63,3 +64,27 @@ export const getTransactions = async(req, res) => {
} }
}; };
export const getGeography = async(req, res) => {
try {
const users = await User.find();
const mapLocations = users.reduce((acc, {country}) => {
const countryISO3 = getCountryIso3(country);
if(!acc [countryISO3]){
acc[countryISO3] = 0;
}
acc[countryISO3]++;
return acc;
}, {});
const formattedLocations = Object.entries(mapLocations).map(
([country, count]) => {
return{id: country, value: count}
}
);
res.status(200).json(formattedLocations)
} catch (error) {
res.status(404).json({message: error.message})
}
}

View file

@ -240,6 +240,11 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/country-iso-2-to-3": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/country-iso-2-to-3/-/country-iso-2-to-3-1.1.0.tgz",
"integrity": "sha512-+xaB6XPuuLuSQEjejWXjff7s5AMyisPk3jBmIqM7aymup79mEcinNkUiIpe4VedSPWCT+/bMW/EDJvRaiDaWKw=="
},
"node_modules/debug": { "node_modules/debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",

View file

@ -0,0 +1,37 @@
# country-iso-2-to-3
> Convert a country code ISO 3166-1 Alpha-2 to ISO 3166-1 Alpha-3
## Install
```sh
$ npm install country-iso-2-to-3
```
## Usage
```js
const getCountryISO3 = require("country-iso-2-to-3");
getCountryISO3("BR")
// "BRA"
getCountryISO3("US")
// "USA"
```
## API
### getCountryISO3(countryCode)
**Parameter**:
A string with a country code in ISO 3166-1 Alpha-2
**Return**:
A string with the country code in ISO 3166-1 Alpha-3
---
## License
MIT © [VTEX](https://www.vtex.com)

View file

@ -0,0 +1,254 @@
var countryISOMapping = {
AF: 'AFG',
AX: 'ALA',
AL: 'ALB',
DZ: 'DZA',
AS: 'ASM',
AD: 'AND',
AO: 'AGO',
AI: 'AIA',
AQ: 'ATA',
AG: 'ATG',
AR: 'ARG',
AM: 'ARM',
AW: 'ABW',
AU: 'AUS',
AT: 'AUT',
AZ: 'AZE',
BS: 'BHS',
BH: 'BHR',
BD: 'BGD',
BB: 'BRB',
BY: 'BLR',
BE: 'BEL',
BZ: 'BLZ',
BJ: 'BEN',
BM: 'BMU',
BT: 'BTN',
BO: 'BOL',
BA: 'BIH',
BW: 'BWA',
BV: 'BVT',
BR: 'BRA',
VG: 'VGB',
IO: 'IOT',
BN: 'BRN',
BG: 'BGR',
BF: 'BFA',
BI: 'BDI',
KH: 'KHM',
CM: 'CMR',
CA: 'CAN',
CV: 'CPV',
KY: 'CYM',
CF: 'CAF',
TD: 'TCD',
CL: 'CHL',
CN: 'CHN',
HK: 'HKG',
MO: 'MAC',
CX: 'CXR',
CC: 'CCK',
CO: 'COL',
KM: 'COM',
CG: 'COG',
CD: 'COD',
CK: 'COK',
CR: 'CRI',
CI: 'CIV',
HR: 'HRV',
CU: 'CUB',
CY: 'CYP',
CZ: 'CZE',
DK: 'DNK',
DJ: 'DJI',
DM: 'DMA',
DO: 'DOM',
EC: 'ECU',
EG: 'EGY',
SV: 'SLV',
GQ: 'GNQ',
ER: 'ERI',
EE: 'EST',
ET: 'ETH',
FK: 'FLK',
FO: 'FRO',
FJ: 'FJI',
FI: 'FIN',
FR: 'FRA',
GF: 'GUF',
PF: 'PYF',
TF: 'ATF',
GA: 'GAB',
GM: 'GMB',
GE: 'GEO',
DE: 'DEU',
GH: 'GHA',
GI: 'GIB',
GR: 'GRC',
GL: 'GRL',
GD: 'GRD',
GP: 'GLP',
GU: 'GUM',
GT: 'GTM',
GG: 'GGY',
GN: 'GIN',
GW: 'GNB',
GY: 'GUY',
HT: 'HTI',
HM: 'HMD',
VA: 'VAT',
HN: 'HND',
HU: 'HUN',
IS: 'ISL',
IN: 'IND',
ID: 'IDN',
IR: 'IRN',
IQ: 'IRQ',
IE: 'IRL',
IM: 'IMN',
IL: 'ISR',
IT: 'ITA',
JM: 'JAM',
JP: 'JPN',
JE: 'JEY',
JO: 'JOR',
KZ: 'KAZ',
KE: 'KEN',
KI: 'KIR',
KP: 'PRK',
KR: 'KOR',
KW: 'KWT',
KG: 'KGZ',
LA: 'LAO',
LV: 'LVA',
LB: 'LBN',
LS: 'LSO',
LR: 'LBR',
LY: 'LBY',
LI: 'LIE',
LT: 'LTU',
LU: 'LUX',
MK: 'MKD',
MG: 'MDG',
MW: 'MWI',
MY: 'MYS',
MV: 'MDV',
ML: 'MLI',
MT: 'MLT',
MH: 'MHL',
MQ: 'MTQ',
MR: 'MRT',
MU: 'MUS',
YT: 'MYT',
MX: 'MEX',
FM: 'FSM',
MD: 'MDA',
MC: 'MCO',
MN: 'MNG',
ME: 'MNE',
MS: 'MSR',
MA: 'MAR',
MZ: 'MOZ',
MM: 'MMR',
NA: 'NAM',
NR: 'NRU',
NP: 'NPL',
NL: 'NLD',
AN: 'ANT',
NC: 'NCL',
NZ: 'NZL',
NI: 'NIC',
NE: 'NER',
NG: 'NGA',
NU: 'NIU',
NF: 'NFK',
MP: 'MNP',
NO: 'NOR',
OM: 'OMN',
PK: 'PAK',
PW: 'PLW',
PS: 'PSE',
PA: 'PAN',
PG: 'PNG',
PY: 'PRY',
PE: 'PER',
PH: 'PHL',
PN: 'PCN',
PL: 'POL',
PT: 'PRT',
PR: 'PRI',
QA: 'QAT',
RE: 'REU',
RO: 'ROU',
RU: 'RUS',
RW: 'RWA',
BL: 'BLM',
SH: 'SHN',
KN: 'KNA',
LC: 'LCA',
MF: 'MAF',
PM: 'SPM',
VC: 'VCT',
WS: 'WSM',
SM: 'SMR',
ST: 'STP',
SA: 'SAU',
SN: 'SEN',
RS: 'SRB',
SC: 'SYC',
SL: 'SLE',
SG: 'SGP',
SK: 'SVK',
SI: 'SVN',
SB: 'SLB',
SO: 'SOM',
ZA: 'ZAF',
GS: 'SGS',
SS: 'SSD',
ES: 'ESP',
LK: 'LKA',
SD: 'SDN',
SR: 'SUR',
SJ: 'SJM',
SZ: 'SWZ',
SE: 'SWE',
CH: 'CHE',
SY: 'SYR',
TW: 'TWN',
TJ: 'TJK',
TZ: 'TZA',
TH: 'THA',
TL: 'TLS',
TG: 'TGO',
TK: 'TKL',
TO: 'TON',
TT: 'TTO',
TN: 'TUN',
TR: 'TUR',
TM: 'TKM',
TC: 'TCA',
TV: 'TUV',
UG: 'UGA',
UA: 'UKR',
AE: 'ARE',
GB: 'GBR',
US: 'USA',
UM: 'UMI',
UY: 'URY',
UZ: 'UZB',
VU: 'VUT',
VE: 'VEN',
VN: 'VNM',
VI: 'VIR',
WF: 'WLF',
EH: 'ESH',
YE: 'YEM',
ZM: 'ZMB',
ZW: 'ZWE',
XK: 'XKX'
}
module.exports = function getCountryISO3(countryCode) {
return countryISOMapping[countryCode]
}

View file

@ -0,0 +1,32 @@
{
"name": "country-iso-2-to-3",
"version": "1.1.0",
"description": "Convert a country code ISO 3166-1 Alpha-2 to ISO 3166-1 Alpha-3",
"main": "index.js",
"scripts": {
"test": "node test.js"
},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/vtex/country-iso-2-to-3.git"
},
"keywords": [
"country",
"code",
"country",
"iso",
"alpha-3",
"alpha",
"3",
"alpha-2",
"alpha",
"2",
"3166"
],
"author": "Breno Calazans (@klzns)",
"license": "MIT",
"bugs": {
"url": "https://github.com/vtex/country-iso-2-to-3/issues"
},
"homepage": "https://github.com/vtex/country-iso-2-to-3#readme"
}

View file

@ -0,0 +1,4 @@
var getCountryISO3 = require("./index")
var result = getCountryISO3("BR")
console.assert(result === "BRA")

View file

@ -11,6 +11,7 @@
"dependencies": { "dependencies": {
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
"cors": "^2.8.5", "cors": "^2.8.5",
"country-iso-2-to-3": "^1.1.0",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.19.2", "express": "^4.19.2",
"helmet": "^7.1.0", "helmet": "^7.1.0",
@ -255,6 +256,11 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/country-iso-2-to-3": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/country-iso-2-to-3/-/country-iso-2-to-3-1.1.0.tgz",
"integrity": "sha512-+xaB6XPuuLuSQEjejWXjff7s5AMyisPk3jBmIqM7aymup79mEcinNkUiIpe4VedSPWCT+/bMW/EDJvRaiDaWKw=="
},
"node_modules/debug": { "node_modules/debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",

View file

@ -15,6 +15,7 @@
"dependencies": { "dependencies": {
"body-parser": "^1.20.2", "body-parser": "^1.20.2",
"cors": "^2.8.5", "cors": "^2.8.5",
"country-iso-2-to-3": "^1.1.0",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^4.19.2", "express": "^4.19.2",
"helmet": "^7.1.0", "helmet": "^7.1.0",

View file

@ -1,10 +1,11 @@
import express from "express" import express from "express"
import {getProducts, getCustomers, getTransactions} from "../controllers/client.js" import {getProducts, getCustomers, getTransactions, getGeography} from "../controllers/client.js"
const router = express.Router(); const router = express.Router();
router.get("/products", getProducts); router.get("/products", getProducts);
router.get("/customers", getCustomers); router.get("/customers", getCustomers);
router.get("/transactions", getTransactions) router.get("/transactions", getTransactions)
router.get("/geography", getGeography)
export default router; export default router;