huge refactor

This commit is contained in:
Dawid Wysokiński 2020-12-19 09:40:51 +01:00
parent 1968e9f1c0
commit 3b74695803
21 changed files with 286 additions and 265 deletions

View File

@ -15,10 +15,10 @@ export interface Props {
}
function Spinner({
size,
containerProps,
typographyProps,
description,
size = 200,
containerProps = {},
typographyProps = { variant: 'h3' },
description = '',
}: Props) {
return (
<Box {...containerProps}>
@ -32,13 +32,4 @@ function Spinner({
);
}
Spinner.defaultProps = {
size: 200,
containerProps: {} as BoxProps,
typographyProps: {
variant: 'h3',
} as TypographyProps,
description: '',
} as Props;
export default Spinner;

View File

@ -2,6 +2,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { TABLE } from '@config/namespaces';
import isObjKey from '@utils/isObjKey';
import { validateRowsPerPage } from './helpers';
import { Action, Column, OrderDirection } from './types';
import {
@ -60,6 +61,10 @@ function Table<T extends object>({
getRowKey,
}: Props<T>) {
const { t } = useTranslation(TABLE);
const rowsPerPage = validateRowsPerPage(
footerProps?.rowsPerPage,
footerProps?.rowsPerPageOptions
);
const isSelected = (row: T): boolean => {
return (
@ -95,7 +100,7 @@ function Table<T extends object>({
<TableLoading
columns={columns}
size={size}
rowsPerPage={footerProps?.rowsPerPage ?? 50}
rowsPerPage={rowsPerPage}
/>
) : data.length > 0 ? (
data.map((item, index) => {
@ -133,6 +138,7 @@ function Table<T extends object>({
count={footerProps?.count ?? data.length}
size={size}
{...footerProps}
rowsPerPage={rowsPerPage}
/>
)}
</MUITable>

View File

@ -1,5 +1,4 @@
import React from 'react';
import { validateRowsPerPage } from './helpers';
import {
TablePagination,
@ -18,13 +17,16 @@ export interface Props {
size?: 'small' | 'medium';
}
export const ROWS_PER_PAGE_DEFAULT = 25;
export const ROWS_PER_PAGE_OPTIONS_DEFAULT = [25, 50, 100];
function TableFooter({
onChangePage,
page = 0,
count = 0,
onChangeRowsPerPage,
rowsPerPageOptions = [25, 50, 100],
rowsPerPage = 50,
rowsPerPageOptions = ROWS_PER_PAGE_OPTIONS_DEFAULT,
rowsPerPage = ROWS_PER_PAGE_DEFAULT,
size = 'small',
t,
}: Props & { t: TFunction }) {
@ -52,7 +54,7 @@ function TableFooter({
page={page}
onChangePage={handlePageChange}
onChangeRowsPerPage={handleRowsPerPageChange}
rowsPerPage={validateRowsPerPage(rowsPerPage, rowsPerPageOptions)}
rowsPerPage={rowsPerPage}
rowsPerPageOptions={rowsPerPageOptions}
size={size}
colSpan={100}

View File

@ -1,10 +1,13 @@
import {
ROWS_PER_PAGE_DEFAULT,
ROWS_PER_PAGE_OPTIONS_DEFAULT,
} from './TableFooter';
export const validateRowsPerPage = (
rowsPerPage: number,
rowsPerPageOptions: Array<number | { value: number; label: string }> = [
25,
50,
100,
]
rowsPerPage: number = ROWS_PER_PAGE_DEFAULT,
rowsPerPageOptions: Array<
number | { value: number; label: string }
> = ROWS_PER_PAGE_OPTIONS_DEFAULT
) => {
const rpp =
rowsPerPageOptions.find(opt =>

View File

@ -1,11 +1,8 @@
import React, { useState } from 'react';
import { useQuery } from '@apollo/client';
import { useTranslation } from 'react-i18next';
import useVersions from './useVersions';
import { COMMON } from '@config/namespaces';
import { VERSIONS } from './queries';
import extractVersionCodeFromHostname from '@utils/extractVersionCodeFromHostname';
import { VersionsQueryVariables } from '@libs/graphql/types';
import { VersionList } from './types';
import { Button, Menu, MenuItem, Link, Tooltip } from '@material-ui/core';
import { Language as LanguageIcon } from '@material-ui/icons';
@ -14,19 +11,7 @@ function VersionSelector() {
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const { t } = useTranslation(COMMON);
const versionCode = extractVersionCodeFromHostname(window.location.hostname);
const { data, loading } = useQuery<VersionList, VersionsQueryVariables>(
VERSIONS,
{
fetchPolicy: 'cache-first',
variables: {
sort: ['code ASC'],
filter: {
codeNEQ: [versionCode],
},
},
}
);
const versions = data?.versions?.items ?? [];
const { versions, loading } = useVersions(versionCode);
const handleClick = (
event: React.MouseEvent<HTMLButtonElement, MouseEvent>

View File

@ -0,0 +1,30 @@
import { useQuery } from '@apollo/client';
import { VERSIONS } from './queries';
import { VersionsQueryVariables } from '@libs/graphql/types';
import { VersionList, Version } from './types';
export type QueryResult = {
versions: Version[];
loading: boolean;
};
const useVersions = (versionCode: string): QueryResult => {
const { data, loading } = useQuery<VersionList, VersionsQueryVariables>(
VERSIONS,
{
fetchPolicy: 'cache-first',
variables: {
sort: ['code ASC'],
filter: {
codeNEQ: [versionCode],
},
},
}
);
return {
versions: data?.versions?.items ?? [],
loading,
};
};
export default useVersions;

View File

@ -0,0 +1,74 @@
import React from 'react';
import * as ROUTES from '@config/routes';
import * as NAMESPACES from '@config/namespaces';
import { SERVER_STATUS } from '@config/app';
import {
Grid,
Accordion,
Typography,
AccordionSummary,
AccordionDetails,
} from '@material-ui/core';
import { Skeleton } from '@material-ui/lab';
import { ExpandMore as ExpandMoreIcon } from '@material-ui/icons';
import Link from '@common/Link/Link';
import { TFunction } from 'i18next';
import { Server } from './types';
export interface Props {
server?: Server;
t: TFunction;
}
function GridItem({ t, server }: Props) {
return (
<Grid item xs={12} sm={6} md={4} lg={3}>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
{server ? (
<Typography variant="h5">
<Link
to={ROUTES.SERVER_PAGE.INDEX_PAGE}
params={{ key: server.key }}
>
{server.key}{' '}
{SERVER_STATUS.CLOSED === server.status
? `(${t(
NAMESPACES.COMMON + `:serverStatus.${server.status}`
).toLowerCase()})`
: ''}
</Link>
</Typography>
) : (
<Skeleton variant="text" width="100%" />
)}
</AccordionSummary>
{server && (
<AccordionDetails>
<Typography>
{t('serverSelection.numberOfPlayers', {
count: server.numberOfPlayers,
num: server.numberOfPlayers.toLocaleString(),
})}
<br />
{t('serverSelection.numberOfTribes', {
count: server.numberOfTribes,
num: server.numberOfTribes.toLocaleString(),
})}
<br />
{t('serverSelection.numberOfVillages', {
count: server.numberOfVillages,
num: server.numberOfVillages.toLocaleString(),
})}
.
</Typography>
</AccordionDetails>
)}
</Accordion>
</Grid>
);
}
export default GridItem;

View File

@ -1,5 +1,4 @@
import React from 'react';
import { useQuery } from '@apollo/client';
import {
useQueryParams,
StringParam,
@ -7,29 +6,15 @@ import {
withDefault,
} from 'use-query-params';
import { useTranslation } from 'react-i18next';
import { generatePath } from 'react-router';
import * as ROUTES from '@config/routes';
import useServers from './useServers';
import * as NAMESPACES from '@config/namespaces';
import { SERVER_STATUS } from '@config/app';
import { ServersQueryVariables } from '@libs/graphql/types';
import { ServerList } from './types';
import { SERVERS } from './queries';
import extractVersionCodeFromHostname from '@utils/extractVersionCodeFromHostname';
import {
Grid,
Box,
Accordion,
Typography,
AccordionSummary,
AccordionDetails,
} from '@material-ui/core';
import { Grid, Box } from '@material-ui/core';
import { Skeleton } from '@material-ui/lab';
import { ExpandMore as ExpandMoreIcon } from '@material-ui/icons';
import Pagination, {
Props as PaginationProps,
} from '@common/Pagination/Pagination';
import Link from '@common/Link/Link';
import GridItem from './GridItem';
const PER_PAGE = 48;
const arr = new Array(PER_PAGE).fill(0);
@ -40,24 +25,7 @@ export default function ServerSelection() {
q: withDefault(StringParam, ''),
});
const { t } = useTranslation(NAMESPACES.INDEX_PAGE);
const { data, loading: loadingServers } = useQuery<
ServerList,
ServersQueryVariables
>(SERVERS, {
fetchPolicy: 'cache-and-network',
variables: {
sort: ['status DESC', 'key ASC'],
offset: (query.page - 1) * PER_PAGE,
limit: PER_PAGE,
filter: {
keyIEQ: '%' + query.q + '%',
versionCode: [extractVersionCodeFromHostname(window.location.hostname)],
},
},
});
const servers = data?.servers?.items ?? [];
const total = data?.servers?.total ?? 0;
const loading = loadingServers && servers.length === 0;
const { servers, loading, total } = useServers(query.page, PER_PAGE, query.q);
const handlePageChange = (_: React.ChangeEvent<unknown>, page: number) => {
setQuery({ page });
@ -93,59 +61,10 @@ export default function ServerSelection() {
<Grid container spacing={2}>
{loading
? arr.map((_, index) => {
return (
<Grid key={index} item xs={12} sm={6} md={4}>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Skeleton variant="text" width="100%" />
</AccordionSummary>
</Accordion>
</Grid>
);
return <GridItem t={t} key={index} />;
})
: servers.map(server => {
return (
<Grid key={server.key} item xs={12} sm={6} md={4} lg={3}>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography variant="h5">
<Link
to={generatePath(ROUTES.SERVER_PAGE.INDEX_PAGE, {
key: server.key,
})}
>
{server.key}{' '}
{SERVER_STATUS.CLOSED === server.status
? `(${t(
NAMESPACES.COMMON +
`:serverStatus.${server.status}`
).toLowerCase()})`
: ''}
</Link>
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
{t('serverSelection.numberOfPlayers', {
count: server.numberOfPlayers,
num: server.numberOfPlayers.toLocaleString(),
})}
<br />
{t('serverSelection.numberOfTribes', {
count: server.numberOfTribes,
num: server.numberOfTribes.toLocaleString(),
})}
<br />
{t('serverSelection.numberOfVillages', {
count: server.numberOfVillages,
num: server.numberOfVillages.toLocaleString(),
})}
.
</Typography>
</AccordionDetails>
</Accordion>
</Grid>
);
return <GridItem t={t} key={server.key} server={server} />;
})}
</Grid>
{renderPagination(false)}

View File

@ -0,0 +1,39 @@
import { useQuery } from '@apollo/client';
import { ServersQueryVariables } from '@libs/graphql/types';
import { ServerList, Server } from './types';
import { SERVERS } from './queries';
import extractVersionCodeFromHostname from '@utils/extractVersionCodeFromHostname';
export type QueryResult = {
servers: Server[];
loading: boolean;
total: number;
};
const useServers = (page: number, perPage: number, q: string): QueryResult => {
const { data, loading: loadingServers } = useQuery<
ServerList,
ServersQueryVariables
>(SERVERS, {
fetchPolicy: 'cache-and-network',
variables: {
sort: ['status DESC', 'key ASC'],
offset: (page - 1) * perPage,
limit: perPage,
filter: {
keyIEQ: '%' + q + '%',
versionCode: [extractVersionCodeFromHostname(window.location.hostname)],
},
},
});
const servers = data?.servers?.items ?? [];
const total = data?.servers?.total ?? 0;
const loading = loadingServers && servers.length === 0;
return {
servers,
total,
loading,
};
};
export default useServers;

View File

@ -2,7 +2,7 @@ import React from 'react';
import { SERVER_PAGE } from '@config/routes';
import { Switch, Route, RouteProps } from 'react-router-dom';
import ServerProvider from '@features/ServerPage/libs/ServerContext/Provider';
import ServerProvider from './libs/ServerContext/Provider';
import IndexPage from './features/IndexPage/IndexPage';
import PlayerPage from './features/PlayerPage/PlayerPage';
import TribePage from './features/TribePage/TribePage';

View File

@ -3,20 +3,13 @@ import { Property } from 'csstype';
import { makeStyles } from '@material-ui/core/styles';
interface UseStylesProps {
interface Props {
backgroundColor?: Property.BackgroundColor;
}
interface Props extends UseStylesProps {}
function Dot({ backgroundColor }: Props) {
function Dot({ backgroundColor = 'green' }: Props) {
const classes = useStyles();
return (
<div
className={classes.dot}
style={{ backgroundColor: backgroundColor ?? 'green' }}
/>
);
return <div className={classes.dot} style={{ backgroundColor }} />;
}
const useStyles = makeStyles(theme => ({

View File

@ -15,26 +15,6 @@ export interface Props {
buttonProps?: ButtonProps;
}
const useStyles = makeStyles(theme => ({
container: {
padding: theme.spacing(1, 0),
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
flexWrap: 'wrap',
'& > *': {
padding: theme.spacing(0.5),
},
[theme.breakpoints.down('xs')]: {
flexDirection: 'column',
'& > *': {
width: '100%',
},
},
},
}));
function ModeSelector({ modes, onSelect, buttonProps = {} }: Props) {
const classes = useStyles();
@ -63,4 +43,24 @@ function ModeSelector({ modes, onSelect, buttonProps = {} }: Props) {
);
}
const useStyles = makeStyles(theme => ({
container: {
padding: theme.spacing(1, 0),
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
flexWrap: 'wrap',
'& > *': {
padding: theme.spacing(0.5),
},
[theme.breakpoints.down('xs')]: {
flexDirection: 'column',
'& > *': {
width: '100%',
},
},
},
}));
export default ModeSelector;

View File

@ -15,26 +15,6 @@ export interface Props {
noPadding?: boolean;
}
const useStyles = makeStyles(theme => ({
root: {
paddingTop: 56,
height: '100%',
[theme.breakpoints.up('sm')]: {
paddingTop: 64,
},
},
shiftContent: {
paddingLeft: DRAWER_WIDTH,
},
content: {
height: '100%',
padding: theme.spacing(3, 0),
'&.no-padding': {
padding: '0 0',
},
},
}));
function PageLayout({ children, noPadding }: Props) {
const [open, setOpen] = useState(false);
const classes = useStyles();
@ -79,4 +59,24 @@ function PageLayout({ children, noPadding }: Props) {
);
}
const useStyles = makeStyles(theme => ({
root: {
paddingTop: 56,
height: '100%',
[theme.breakpoints.up('sm')]: {
paddingTop: 64,
},
},
shiftContent: {
paddingLeft: DRAWER_WIDTH,
},
content: {
height: '100%',
padding: theme.spacing(3, 0),
'&.no-padding': {
padding: '0 0',
},
},
}));
export default PageLayout;

View File

@ -1,6 +1,5 @@
import React from 'react';
import clsx from 'clsx';
import { generatePath } from 'react-router';
import { TFunction } from 'i18next';
import useServer from '@features/ServerPage/libs/ServerContext/useServer';
import useStyles from './useStyles';
@ -41,9 +40,8 @@ const Sidebar = ({ t, className, open, variant, onClose, onOpen }: Props) => {
const routes: Route[] = [
{
name: t('pageLayout.sidebar.routes.dashboard'),
to: generatePath(ROUTES.SERVER_PAGE.INDEX_PAGE, {
key: key,
}),
to: ROUTES.SERVER_PAGE.INDEX_PAGE,
params: { key },
Icon: <DashboardIcon color="inherit" />,
},
{
@ -52,75 +50,52 @@ const Sidebar = ({ t, className, open, variant, onClose, onOpen }: Props) => {
nested: [
{
name: t('pageLayout.sidebar.routes.rankings.player.index'),
to: generatePath(
ROUTES.SERVER_PAGE.RANKING_PAGE.PLAYER_PAGE.INDEX_PAGE,
{
key: key,
}
),
to: ROUTES.SERVER_PAGE.RANKING_PAGE.PLAYER_PAGE.INDEX_PAGE,
params: { key },
Icon: <GradeIcon color="inherit" />,
},
{
name: t('pageLayout.sidebar.routes.rankings.player.od'),
to: generatePath(
ROUTES.SERVER_PAGE.RANKING_PAGE.PLAYER_PAGE.OD_PAGE,
{
key: key,
}
),
to: ROUTES.SERVER_PAGE.RANKING_PAGE.PLAYER_PAGE.OD_PAGE,
params: { key },
Icon: <GradeIcon color="inherit" />,
},
{
name: t('pageLayout.sidebar.routes.rankings.player.archive'),
to: generatePath(
ROUTES.SERVER_PAGE.RANKING_PAGE.PLAYER_PAGE.ARCHIVE_PAGE,
{
key: key,
}
),
to: ROUTES.SERVER_PAGE.RANKING_PAGE.PLAYER_PAGE.ARCHIVE_PAGE,
params: { key },
Icon: <GradeIcon color="inherit" />,
},
{
name: t('pageLayout.sidebar.routes.rankings.tribe.index'),
to: generatePath(
ROUTES.SERVER_PAGE.RANKING_PAGE.TRIBE_PAGE.INDEX_PAGE,
{
key: key,
}
),
to: ROUTES.SERVER_PAGE.RANKING_PAGE.TRIBE_PAGE.INDEX_PAGE,
params: { key },
Icon: <GradeIcon color="inherit" />,
},
{
name: t('pageLayout.sidebar.routes.rankings.tribe.od'),
to: generatePath(ROUTES.SERVER_PAGE.RANKING_PAGE.TRIBE_PAGE.OD_PAGE, {
key: key,
}),
to: ROUTES.SERVER_PAGE.RANKING_PAGE.TRIBE_PAGE.OD_PAGE,
params: { key },
Icon: <GradeIcon color="inherit" />,
},
{
name: t('pageLayout.sidebar.routes.rankings.tribe.archive'),
to: generatePath(
ROUTES.SERVER_PAGE.RANKING_PAGE.TRIBE_PAGE.ARCHIVE_PAGE,
{
key: key,
}
),
to: ROUTES.SERVER_PAGE.RANKING_PAGE.TRIBE_PAGE.ARCHIVE_PAGE,
params: { key },
Icon: <GradeIcon color="inherit" />,
},
],
},
{
name: t('pageLayout.sidebar.routes.ennoblements'),
to: generatePath(ROUTES.SERVER_PAGE.ENNOBLEMENTS_PAGE, {
key: key,
}),
to: ROUTES.SERVER_PAGE.ENNOBLEMENTS_PAGE,
params: { key },
Icon: <BeenhereIcon color="inherit" />,
},
{
name: t('pageLayout.sidebar.routes.map'),
to: generatePath(ROUTES.SERVER_PAGE.MAP_PAGE, {
key: key,
}),
to: ROUTES.SERVER_PAGE.MAP_PAGE,
params: { key },
Icon: <MapIcon color="inherit" />,
},
];

View File

@ -20,16 +20,6 @@ export interface Props {
nestedLevel: number;
}
const useStyles = makeStyles(theme => ({
link: {
display: 'block',
width: '100%',
},
activeLink: {
color: theme.palette.secondary.main,
},
}));
function ListItem({ route, nestedLevel }: Props) {
const [open, setOpen] = useState(false);
const classes = useStyles();
@ -63,6 +53,7 @@ function ListItem({ route, nestedLevel }: Props) {
{!hasNested && route.to ? (
<Link
to={route.to}
params={route.params}
className={classes.link}
color={pathname === route.to ? 'secondary' : 'inherit'}
>
@ -90,4 +81,14 @@ function ListItem({ route, nestedLevel }: Props) {
);
}
const useStyles = makeStyles(theme => ({
link: {
display: 'block',
width: '100%',
},
activeLink: {
color: theme.palette.secondary.main,
},
}));
export default ListItem;

View File

@ -1,6 +1,9 @@
import { Props } from '@common/Link/Link';
export interface Route {
name: string;
to?: string;
params?: Props['params'];
Icon: React.ReactElement;
nested?: Route[];
}

View File

@ -1,6 +1,5 @@
import React from 'react';
import clsx from 'clsx';
import { generatePath } from 'react-router';
import { TFunction } from 'i18next';
import useServer from '@features/ServerPage/libs/ServerContext/useServer';
import * as ROUTES from '@config/routes';
@ -25,28 +24,6 @@ export interface Props {
openSidebar?: () => void;
}
const useStyles = makeStyles(theme => ({
toolbar: {
justifyContent: 'space-between',
},
leftSideContainer: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
'& > *': {
marginRight: theme.spacing(1),
},
},
rightSideContainer: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
'& > *:not(:last-child)': {
marginRight: theme.spacing(1),
},
},
}));
const TopBar = ({ className, openSidebar, t }: Props) => {
const { key } = useServer();
const classes = useStyles();
@ -63,7 +40,8 @@ const TopBar = ({ className, openSidebar, t }: Props) => {
<Typography variant="h4">
<Link
color="inherit"
to={generatePath(ROUTES.SERVER_PAGE.INDEX_PAGE, { key })}
params={{ key }}
to={ROUTES.SERVER_PAGE.INDEX_PAGE}
>
{key}
</Link>
@ -95,4 +73,26 @@ const TopBar = ({ className, openSidebar, t }: Props) => {
);
};
const useStyles = makeStyles(theme => ({
toolbar: {
justifyContent: 'space-between',
},
leftSideContainer: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
'& > *': {
marginRight: theme.spacing(1),
},
},
rightSideContainer: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
'& > *:not(:last-child)': {
marginRight: theme.spacing(1),
},
},
}));
export default TopBar;

View File

@ -11,7 +11,7 @@ const translations = {
},
},
fields: {
joinedAt: 'Joined at',
joinedAt: 'Joined on',
points: 'Points',
totalVillages: 'Villages',
dailyGrowth: 'Daily growth',
@ -19,7 +19,7 @@ const translations = {
scoreDef: 'ODD',
scoreSup: 'ODS',
scoreTotal: 'OD',
deletedAt: 'Deleted at',
deletedAt: 'Deleted on',
bestRank: 'Best rank',
mostPoints: 'Most points',
mostVillages: 'Most villages',
@ -28,7 +28,7 @@ const translations = {
nameChanges: {
title: 'Name changes',
columns: {
changeDate: 'Date',
changeDate: 'Changed on',
newName: 'New name',
oldName: 'Old name',
},

View File

@ -10,7 +10,7 @@ const translations = {
},
},
fields: {
createdAt: 'Created at',
createdAt: 'Created on',
points: 'Points',
allPoints: 'All points',
totalVillages: 'Villages',
@ -19,7 +19,7 @@ const translations = {
scoreAtt: 'ODA',
scoreDef: 'ODD',
scoreTotal: 'OD',
deletedAt: 'Deleted at',
deletedAt: 'Deleted on',
bestRank: 'Best rank',
mostPoints: 'Most points',
mostVillages: 'Most villages',

View File

@ -11,7 +11,7 @@ const translations = {
},
},
fields: {
joinedAt: 'Dołączył o',
joinedAt: 'Dołączył',
points: 'Punkty',
totalVillages: 'Wioski',
dailyGrowth: 'Dzienny przyrost',
@ -19,7 +19,7 @@ const translations = {
scoreDef: 'Obrońca',
scoreSup: 'Wspierający',
scoreTotal: 'Pokonani ogólnie',
deletedAt: 'Usunięty o',
deletedAt: 'Usunięty',
bestRank: 'Najlepszy ranking',
mostPoints: 'Najwięcej punktów',
mostVillages: 'Najwięcej wiosek',

View File

@ -10,7 +10,7 @@ const translations = {
},
},
fields: {
createdAt: 'Utworzone o',
createdAt: 'Utworzone',
points: 'Punkty',
allPoints: 'Wszystkie punkty',
totalVillages: 'Wioski',
@ -18,7 +18,7 @@ const translations = {
scoreAtt: 'Agresor',
scoreDef: 'Obrońca',
scoreTotal: 'Pokonani ogólnie',
deletedAt: 'Usunięte o',
deletedAt: 'Usunięte',
bestRank: 'Najlepszy ranking',
mostPoints: 'Najwięcej punktów',
mostVillages: 'Najwięcej wiosek',