make table translatable

This commit is contained in:
Dawid Wysokiński 2020-11-13 15:26:30 +01:00
parent 88eb65ff65
commit b51b33d7b6
18 changed files with 243 additions and 67 deletions

View File

@ -1,4 +1,6 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { TABLE } from '@config/namespaces';
import isObjKey from '@utils/isObjKey';
import { Action, Column, OrderDirection } from './types';
@ -7,6 +9,7 @@ import {
TableBody,
TableProps,
TableBodyProps,
TableContainer,
} from '@material-ui/core';
import TableHead from './TableHead';
import TableRow from './TableRow';
@ -32,6 +35,7 @@ export interface Props<T> {
tableBodyProps?: TableBodyProps;
footerProps?: TableFooterProps;
hideFooter?: boolean;
size?: 'medium' | 'small';
}
function Table<T extends object>({
@ -48,46 +52,58 @@ function Table<T extends object>({
tableProps = {},
hideFooter = false,
footerProps,
size,
}: Props<T>) {
const { t } = useTranslation(TABLE);
return (
<MUITable {...tableProps}>
<TableHead
columns={columns}
selection={selection}
orderBy={orderBy}
orderDirection={orderDirection}
onRequestSort={onRequestSort}
allSelected={false}
/>
<TableBody {...tableBodyProps}>
{loading ? (
<TableLoading />
) : data.length > 0 ? (
data.map((item, index) => {
return (
<TableRow
key={
isObjKey(item, idFieldName) ? item[idFieldName] + '' : index
}
row={item}
actions={actions}
selected={false}
selection={selection}
columns={columns}
/>
);
})
) : (
<TableEmpty />
)}
</TableBody>
{!hideFooter && (
<TableFooter
count={footerProps?.count ?? data.length}
{...footerProps}
<TableContainer>
<MUITable size={size} {...tableProps}>
<TableHead
columns={columns}
selection={selection}
orderBy={orderBy}
orderDirection={orderDirection}
onRequestSort={onRequestSort}
allSelected={false}
size={size}
/>
)}
</MUITable>
<TableBody {...tableBodyProps}>
{loading ? (
<TableLoading
columns={columns}
size={size}
rowsPerPage={footerProps?.rowsPerPage ?? 50}
/>
) : data.length > 0 ? (
data.map((item, index) => {
return (
<TableRow
key={
isObjKey(item, idFieldName) ? item[idFieldName] + '' : index
}
row={item}
actions={actions}
selected={false}
selection={selection}
columns={columns}
size={size}
/>
);
})
) : (
<TableEmpty t={t} />
)}
</TableBody>
{!hideFooter && (
<TableFooter
t={t}
count={footerProps?.count ?? data.length}
size={size}
{...footerProps}
/>
)}
</MUITable>
</TableContainer>
);
}

View File

@ -1,12 +1,17 @@
import React from 'react';
import { TFunction } from 'i18next';
import { TableRow, TableCell, Typography } from '@material-ui/core';
function TableEmpty() {
export interface Props {
t: TFunction;
}
function TableEmpty({ t }: Props) {
return (
<TableRow>
<TableCell colSpan={100}>
<Typography align="center">No records to display</Typography>
<Typography align="center">{t('emptyDataSourceMessage')}</Typography>
</TableCell>
</TableRow>
);

View File

@ -1,4 +1,5 @@
import React from 'react';
import { TFunction } from 'i18next';
import {
TablePagination,
@ -24,7 +25,8 @@ function TableFooter({
rowsPerPageOptions = [25, 50, 100],
rowsPerPage = 50,
size = 'small',
}: Props) {
t,
}: Props & { t: TFunction }) {
const handlePageChange = (
event: React.MouseEvent<HTMLButtonElement> | null,
page: number
@ -53,6 +55,10 @@ function TableFooter({
rowsPerPageOptions={rowsPerPageOptions}
size={size}
colSpan={100}
labelDisplayedRows={payload => t('labelDisplayedRows', payload)}
labelRowsPerPage={t('labelRowsPerPage')}
nextIconButtonText={t('next')}
backIconButtonText={t('prev')}
/>
</TableRow>
</MUITableFooter>

View File

@ -17,6 +17,7 @@ export interface Props {
allSelected: boolean;
orderDirection: OrderDirection;
orderBy: string;
size?: 'small' | 'medium';
onRequestSort?: (
property: string,
orderDirection: OrderDirection
@ -31,6 +32,7 @@ function TableHead({
selection = false,
allSelected = false,
onRequestSort,
size = 'medium',
}: Props) {
const createSortHandler = (property: string) => () => {
if (onRequestSort) {
@ -52,13 +54,14 @@ function TableHead({
<MUITableHead>
<TableRow>
{selection && (
<TableCell padding="checkbox">
<TableCell size={size} padding="checkbox">
<Checkbox checked={allSelected} onClick={handleSelectAll} />
</TableCell>
)}
{columns.map(col => {
return (
<TableCell
size={size}
key={col.field}
padding={col.disablePadding ? 'none' : 'default'}
align={col.align ? col.align : 'left'}

View File

@ -1,21 +1,30 @@
import React from 'react';
import React, { Fragment } from 'react';
import { Column } from './types';
import { TableRow, CircularProgress, Box, TableCell } from '@material-ui/core';
import { TableRow, TableCell } from '@material-ui/core';
import { Skeleton } from '@material-ui/lab';
function TableLoading() {
export interface Props {
rowsPerPage: number;
columns: Column[];
size?: 'small' | 'medium';
}
function TableLoading({ rowsPerPage, columns, size = 'medium' }: Props) {
return (
<TableRow>
<TableCell colSpan={100}>
<Box
paddingY={2}
display="flex"
alignItems="center"
justifyContent="center"
>
<CircularProgress size={200} />
</Box>
</TableCell>
</TableRow>
<Fragment>
{new Array(rowsPerPage).fill(0).map((_, index) => {
return (
<TableRow key={index}>
{columns.map(col => (
<TableCell size={size} key={col.label}>
<Skeleton variant="text" />
</TableCell>
))}
</TableRow>
);
})}
</Fragment>
);
}

View File

@ -11,6 +11,7 @@ export type Props<T> = {
row: T;
selection: boolean;
selected: boolean;
size?: 'small' | 'medium';
onSelect?: (row: T) => void;
};
@ -21,6 +22,7 @@ function EnhancedTableRow<T extends object>({
selection = false,
selected = false,
onSelect,
size = 'medium',
}: Props<T>) {
const handleSelect = () => {
if (onSelect) {
@ -44,7 +46,7 @@ function EnhancedTableRow<T extends object>({
return (
<TableRow>
{selection && (
<TableCell padding="checkbox">
<TableCell size={size} padding="checkbox">
<Checkbox checked={selected} onClick={handleSelect} />
</TableCell>
)}
@ -52,6 +54,7 @@ function EnhancedTableRow<T extends object>({
const val = get(row, col.field, '');
return (
<TableCell
size={size}
key={col.field}
padding={col.disablePadding ? 'none' : 'default'}
align={col.align ? col.align : 'left'}
@ -65,7 +68,7 @@ function EnhancedTableRow<T extends object>({
);
})}
{actions.length > 0 && (
<TableCell>
<TableCell size={size}>
{actions.map((action, index) =>
action.tooltip ? (
<Tooltip key={index} title={action.tooltip}>

View File

@ -1,4 +1,5 @@
export const COMMON = 'common';
export const TABLE = 'table';
export const INDEX_PAGE = 'index-page';
export const NOT_FOUND_PAGE = 'not-found-page';
export const SERVER_PAGE = {

View File

@ -1,16 +1,24 @@
import React from 'react';
import useServer from '../../libs/ServerContext/useServer';
import { Container } from '@material-ui/core';
import { Container, Grid } from '@material-ui/core';
import PageLayout from '@features/ServerPage/common/PageLayout/PageLayout';
import RecentlyDeletedPlayers from './components/RecentlyDeletedPlayers/RecentlyDeletedPlayers';
import RecentlyDeletedTribes from './components/RecentlyDeletedTribes/RecentlyDeletedTribes';
function IndexPage() {
const { key } = useServer();
return (
<PageLayout>
<Container>
<RecentlyDeletedPlayers server={key} />
<Grid container spacing={1}>
<Grid item xs={6}>
<RecentlyDeletedPlayers server={key} />
</Grid>
<Grid item xs={6}>
<RecentlyDeletedTribes server={key} />
</Grid>
</Grid>
</Container>
</PageLayout>
);

View File

@ -34,13 +34,15 @@ function RecentlyDeletedPlayers({ server }: Props) {
return (
<Paper>
<Toolbar>
<Typography variant="h4">Test?</Typography>
<Typography variant="h4">Recently deleted players</Typography>
</Toolbar>
<Table
columns={COLUMNS}
loading={loading}
data={players}
footerProps={{ rowsPerPage: LIMIT }}
size="small"
hideFooter
footerProps={{ rowsPerPage: LIMIT, rowsPerPageOptions: [LIMIT] }}
/>
</Paper>
);

View File

@ -0,0 +1,51 @@
import React from 'react';
import { useQuery } from '@apollo/client';
import { RECENTLY_DELETED_TRIBES } from './queries';
import { COLUMNS, LIMIT } from './constants';
import { PlayersQueryVariables } from '@libs/graphql/types';
import { TribeList } from './types';
import { Paper, Toolbar, Typography } from '@material-ui/core';
import Table from '@common/Table/Table';
export interface Props {
server: string;
}
function RecentlyDeletedPlayers({ server }: Props) {
const { loading: loadingPlayers, data } = useQuery<
TribeList,
PlayersQueryVariables
>(RECENTLY_DELETED_TRIBES, {
fetchPolicy: 'cache-and-network',
variables: {
filter: {
limit: LIMIT,
sort: 'deletedAt DESC',
deletedAtGT: new Date(0),
},
server,
},
});
const tribes = data?.tribes?.items ?? [];
const loading = loadingPlayers && tribes.length === 0;
console.log(tribes, loading);
return (
<Paper>
<Toolbar>
<Typography variant="h4">Recently deleted tribes</Typography>
</Toolbar>
<Table
columns={COLUMNS}
loading={loading}
data={tribes}
size="small"
hideFooter
footerProps={{ rowsPerPage: LIMIT, rowsPerPageOptions: [LIMIT] }}
/>
</Paper>
);
}
export default RecentlyDeletedPlayers;

View File

@ -0,0 +1,24 @@
import { Column } from '@common/Table/types';
export const COLUMNS: Column[] = [
{
field: 'name',
label: 'recentlyDeletedPlayers.columns.name',
sortable: false,
},
{
field: 'mostPoints',
label: 'recentlyDeletedPlayers.columns.mostPoints',
sortable: false,
valueFormatter: (param: string | number | boolean) =>
(param as number).toLocaleString(),
},
{
field: 'deletedAt',
label: 'recentlyDeletedPlayers.columns.deletedAt',
sortable: false,
type: 'datetime',
},
];
export const LIMIT = 5;

View File

@ -0,0 +1,14 @@
import { gql } from '@apollo/client';
export const RECENTLY_DELETED_TRIBES = gql`
query tribes($server: String!, $filter: TribeFilter) {
tribes(server: $server, filter: $filter) {
items {
id
name
mostPoints
deletedAt
}
}
}
`;

View File

@ -0,0 +1,12 @@
import { List } from '@libs/graphql/types';
export type Tribe = {
id: number;
name: string;
mostPoints: number;
deletedAt: string | Date;
};
export type TribeList = {
tribes?: List<Tribe[]>;
};

View File

@ -3,11 +3,13 @@ import common from './common';
import indexPage from './index-page';
import notFoundPage from './not-found-page';
import serverPage from './server-page';
import table from './table';
const translations = {
[NAMESPACES.COMMON]: common,
[NAMESPACES.INDEX_PAGE]: indexPage,
[NAMESPACES.NOT_FOUND_PAGE]: notFoundPage,
[NAMESPACES.TABLE]: table,
...serverPage,
};

View File

@ -1,11 +1,11 @@
const translations = {
emptyDataSourceMessage: 'No records to display',
labelDisplayedRows: '{from}-{to} of {count}',
labelDisplayedRows: '{{from}}-{{to}} of {{count}}',
labelRowsPerPage: 'Rows per page:',
firstTooltip: 'First Page',
previousTooltip: 'Previous Page',
nextTooltip: 'Next Page',
lastTooltip: 'Last Page',
first: 'First page',
last: 'Last page',
next: 'Next page',
previous: 'Previous page',
};
export default translations;

View File

@ -3,11 +3,13 @@ import common from './common';
import indexPage from './index-page';
import notFoundPage from './not-found-page';
import serverPage from './server-page';
import table from './table';
const translations = {
[NAMESPACES.COMMON]: common,
[NAMESPACES.INDEX_PAGE]: indexPage,
[NAMESPACES.NOT_FOUND_PAGE]: notFoundPage,
[NAMESPACES.TABLE]: table,
...serverPage,
};

11
src/libs/i18n/pl/table.ts Normal file
View File

@ -0,0 +1,11 @@
const translations = {
emptyDataSourceMessage: 'Brak danych do wyświetlenia',
labelDisplayedRows: '{{from}}-{{to}} z {{count}}',
labelRowsPerPage: 'Wierszy na stronę:',
first: 'Pierwsza strona',
last: 'Ostatnia strona',
next: 'Następna strona',
previous: 'Poprzednia strona',
};
export default translations;

View File

@ -23,6 +23,13 @@ const createTheme = (): Theme => {
color: 'default',
},
},
overrides: {
MuiTableContainer: {
root: {
overflow: 'auto',
},
},
},
})
);
};