Skip to content

Commit c125669

Browse files
authored
Merge pull request #240 from mikecao/dev
v0.67.0 Localize country names
2 parents dc735f5 + 074f248 commit c125669

36 files changed

Lines changed: 176 additions & 312 deletions

components/common/WorldMap.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import tinycolor from 'tinycolor2';
66
import useTheme from 'hooks/useTheme';
77
import { THEME_COLORS } from 'lib/constants';
88
import styles from './WorldMap.module.css';
9+
import useCountryNames from 'hooks/useCountryNames';
10+
import useLocale from 'hooks/useLocale';
911

1012
const geoUrl = '/world-110m.json';
1113

@@ -21,6 +23,8 @@ export default function WorldMap({ data, className }) {
2123
}),
2224
[theme],
2325
);
26+
const [locale] = useLocale();
27+
const countryNames = useCountryNames(locale);
2428

2529
function getFillColor(code) {
2630
if (code === 'AQ') return;
@@ -39,10 +43,10 @@ export default function WorldMap({ data, className }) {
3943
return code === 'AQ' ? 0 : 1;
4044
}
4145

42-
function handleHover({ ISO_A2: code, NAME: name }) {
46+
function handleHover(code) {
4347
if (code === 'AQ') return;
4448
const country = data?.find(({ x }) => x === code);
45-
setTooltip(`${name}: ${country?.y || 0} visitors`);
49+
setTooltip(`${countryNames[code]}: ${country?.y || 0} visitors`);
4650
}
4751

4852
return (
@@ -70,7 +74,7 @@ export default function WorldMap({ data, className }) {
7074
hover: { outline: 'none', fill: colors.hoverColor },
7175
pressed: { outline: 'none' },
7276
}}
73-
onMouseOver={() => handleHover(geo.properties)}
77+
onMouseOver={() => handleHover(code)}
7478
onMouseOut={() => setTooltip(null)}
7579
/>
7680
);

components/forms/AccountEditForm.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useState } from 'react';
22
import { FormattedMessage } from 'react-intl';
33
import { Formik, Form, Field } from 'formik';
4+
import { useRouter } from 'next/router';
45
import { post } from 'lib/web';
56
import Button from 'components/common/Button';
67
import FormLayout, {
@@ -29,18 +30,17 @@ const validate = ({ user_id, username, password }) => {
2930
};
3031

3132
export default function AccountEditForm({ values, onSave, onClose }) {
33+
const { basePath } = useRouter();
3234
const [message, setMessage] = useState();
3335

3436
const handleSubmit = async values => {
35-
const response = await post(`/api/account`, values);
37+
const { ok, data } = await post(`${basePath}/api/account`, values);
3638

37-
if (typeof response !== 'string') {
39+
if (ok) {
3840
onSave();
3941
} else {
4042
setMessage(
41-
response || (
42-
<FormattedMessage id="message.failure" defaultMessage="Something went wrong." />
43-
),
43+
data || <FormattedMessage id="message.failure" defaultMessage="Something went wrong." />,
4444
);
4545
}
4646
};

components/forms/ChangePasswordForm.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useState } from 'react';
22
import { FormattedMessage } from 'react-intl';
3+
import { useRouter } from 'next/router';
34
import { Formik, Form, Field } from 'formik';
45
import { post } from 'lib/web';
56
import Button from 'components/common/Button';
@@ -37,18 +38,17 @@ const validate = ({ current_password, new_password, confirm_password }) => {
3738
};
3839

3940
export default function ChangePasswordForm({ values, onSave, onClose }) {
41+
const { basePath } = useRouter();
4042
const [message, setMessage] = useState();
4143

4244
const handleSubmit = async values => {
43-
const response = await post(`/api/account/password`, values);
45+
const { ok, data } = await post(`${basePath}/api/account/password`, values);
4446

45-
if (typeof response !== 'string') {
47+
if (ok) {
4648
onSave();
4749
} else {
4850
setMessage(
49-
response || (
50-
<FormattedMessage id="message.failure" defaultMessage="Something went wrong." />
51-
),
51+
data || <FormattedMessage id="message.failure" defaultMessage="Something went wrong." />,
5252
);
5353
}
5454
};

components/forms/DeleteForm.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import React, { useState } from 'react';
2+
import { FormattedMessage } from 'react-intl';
3+
import { useRouter } from 'next/router';
24
import { Formik, Form, Field } from 'formik';
35
import { del } from 'lib/web';
46
import Button from 'components/common/Button';
@@ -8,7 +10,6 @@ import FormLayout, {
810
FormMessage,
911
FormRow,
1012
} from 'components/layout/FormLayout';
11-
import { FormattedMessage } from 'react-intl';
1213

1314
const CONFIRMATION_WORD = 'DELETE';
1415

@@ -27,15 +28,18 @@ const validate = ({ confirmation }) => {
2728
};
2829

2930
export default function DeleteForm({ values, onSave, onClose }) {
31+
const { basePath } = useRouter();
3032
const [message, setMessage] = useState();
3133

3234
const handleSubmit = async ({ type, id }) => {
33-
const response = await del(`/api/${type}/${id}`);
35+
const { ok, data } = await del(`${basePath}/api/${type}/${id}`);
3436

35-
if (typeof response !== 'string') {
37+
if (ok) {
3638
onSave();
3739
} else {
38-
setMessage(<FormattedMessage id="message.failure" defaultMessage="Something went wrong." />);
40+
setMessage(
41+
data || <FormattedMessage id="message.failure" defaultMessage="Something went wrong." />,
42+
);
3943
}
4044
};
4145

components/forms/LoginForm.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useState } from 'react';
22
import { FormattedMessage } from 'react-intl';
33
import { Formik, Form, Field } from 'formik';
4-
import Router from 'next/router';
4+
import { useRouter } from 'next/router';
55
import { post } from 'lib/web';
66
import Button from 'components/common/Button';
77
import FormLayout, {
@@ -28,22 +28,26 @@ const validate = ({ username, password }) => {
2828
};
2929

3030
export default function LoginForm() {
31+
const router = useRouter();
3132
const [message, setMessage] = useState();
3233

3334
const handleSubmit = async ({ username, password }) => {
34-
const response = await post('/api/auth/login', { username, password });
35+
const { ok, status, data } = await post(`${router.basePath}/api/auth/login`, {
36+
username,
37+
password,
38+
});
3539

36-
if (typeof response !== 'string') {
37-
await Router.push('/');
40+
if (ok) {
41+
return router.push('/');
3842
} else {
3943
setMessage(
40-
response.startsWith('401') ? (
44+
status === 401 ? (
4145
<FormattedMessage
4246
id="message.incorrect-username-password"
4347
defaultMessage="Incorrect username/password."
4448
/>
4549
) : (
46-
response
50+
data
4751
),
4852
);
4953
}

components/forms/WebsiteEditForm.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import FormLayout, {
1111
} from 'components/layout/FormLayout';
1212
import Checkbox from 'components/common/Checkbox';
1313
import { DOMAIN_REGEX } from 'lib/constants';
14+
import { useRouter } from 'next/router';
1415

1516
const initialValues = {
1617
name: '',
@@ -34,15 +35,18 @@ const validate = ({ name, domain }) => {
3435
};
3536

3637
export default function WebsiteEditForm({ values, onSave, onClose }) {
38+
const { basePath } = useRouter();
3739
const [message, setMessage] = useState();
3840

3941
const handleSubmit = async values => {
40-
const response = await post(`/api/website`, values);
42+
const { ok, data } = await post(`${basePath}/api/website`, values);
4143

42-
if (typeof response !== 'string') {
44+
if (ok) {
4345
onSave();
4446
} else {
45-
setMessage(<FormattedMessage id="message.failure" defaultMessage="Something went wrong." />);
47+
setMessage(
48+
data || <FormattedMessage id="message.failure" defaultMessage="Something went wrong." />,
49+
);
4650
}
4751
};
4852

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import React from 'react';
22
import MetricsTable from './MetricsTable';
3-
import { countryFilter, percentFilter } from 'lib/filters';
3+
import { percentFilter } from 'lib/filters';
44
import { FormattedMessage } from 'react-intl';
5+
import useCountryNames from 'hooks/useCountryNames';
6+
import useLocale from 'hooks/useLocale';
57

68
export default function CountriesTable({ websiteId, token, limit, onDataLoad = () => {} }) {
9+
const [locale] = useLocale();
10+
const countryNames = useCountryNames(locale);
11+
12+
function renderLabel({ x }) {
13+
return <div className={locale}>{countryNames[x]}</div>;
14+
}
15+
716
return (
817
<MetricsTable
918
title={<FormattedMessage id="metrics.countries" defaultMessage="Countries" />}
@@ -12,8 +21,8 @@ export default function CountriesTable({ websiteId, token, limit, onDataLoad = (
1221
websiteId={websiteId}
1322
token={token}
1423
limit={limit}
15-
dataFilter={countryFilter}
1624
onDataLoad={data => onDataLoad(percentFilter(data))}
25+
renderLabel={renderLabel}
1726
/>
1827
);
1928
}

components/pages/WebsiteDetails.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export default function WebsiteDetails({ websiteId, token }) {
4242
} = usePageQuery();
4343

4444
const BackButton = () => (
45-
<div className={styles.backButton}>
45+
<div key="back-button" className={styles.backButton}>
4646
<Link
4747
key="back-button"
4848
href={router.pathname}

hooks/useCountryNames.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { useState, useEffect } from 'react';
2+
import { useRouter } from 'next/router';
3+
import { get } from 'lib/web';
4+
import enUS from 'public/country/en-US.json';
5+
6+
const countryNames = {
7+
'en-US': enUS,
8+
};
9+
10+
export default function useCountryNames(locale) {
11+
const [list, setList] = useState(countryNames[locale] || enUS);
12+
const { basePath } = useRouter();
13+
14+
async function loadData(locale) {
15+
const { ok, data } = await get(`${basePath}/country/${locale}.json`);
16+
17+
if (ok) {
18+
countryNames[locale] = data;
19+
setList(countryNames[locale]);
20+
} else {
21+
setList(enUS);
22+
}
23+
}
24+
25+
useEffect(() => {
26+
if (!countryNames[locale]) {
27+
loadData(locale);
28+
} else {
29+
setList(countryNames[locale]);
30+
}
31+
}, [locale]);
32+
33+
return list;
34+
}

hooks/useFetch.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ import { useState, useEffect } from 'react';
22
import { useDispatch } from 'react-redux';
33
import { get } from 'lib/web';
44
import { updateQuery } from 'redux/actions/queries';
5+
import { useRouter } from 'next/router';
56

67
export default function useFetch(url, params = {}, options = {}) {
78
const dispatch = useDispatch();
89
const [data, setData] = useState();
10+
const [status, setStatus] = useState();
911
const [error, setError] = useState();
1012
const [loading, setLoadiing] = useState(false);
13+
const { basePath } = useRouter();
1114
const keys = Object.keys(params)
1215
.sort()
1316
.map(key => params[key]);
@@ -18,11 +21,12 @@ export default function useFetch(url, params = {}, options = {}) {
1821
setLoadiing(true);
1922
setError(null);
2023
const time = performance.now();
21-
const data = await get(url, params);
24+
const { data, status } = await get(`${basePath}${url}`, params);
2225

2326
dispatch(updateQuery({ url, time: performance.now() - time, completed: Date.now() }));
2427

2528
setData(data);
29+
setStatus(status);
2630
onDataLoad(data);
2731
} catch (e) {
2832
console.error(e);
@@ -46,5 +50,5 @@ export default function useFetch(url, params = {}, options = {}) {
4650
}
4751
}, [url, ...keys, ...update]);
4852

49-
return { data, error, loading, loadData };
53+
return { data, status, error, loading };
5054
}

0 commit comments

Comments
 (0)