[FEATURE]: add a new route - /server/:key/player/:id/ennoblements
This commit is contained in:
parent
024130b8d3
commit
6839ffb700
|
@ -25,6 +25,7 @@ export interface Props<T> {
|
|||
orderDirection?: OrderDirection;
|
||||
selection?: boolean;
|
||||
idFieldName?: string;
|
||||
getRowKey?: (row: T, index: number) => string | number | null | undefined;
|
||||
onRequestSort?: (
|
||||
property: string,
|
||||
orderDirection: OrderDirection
|
||||
|
@ -56,6 +57,7 @@ function Table<T extends object>({
|
|||
size,
|
||||
selected,
|
||||
onSelect,
|
||||
getRowKey,
|
||||
}: Props<T>) {
|
||||
const { t } = useTranslation(TABLE);
|
||||
|
||||
|
@ -100,7 +102,11 @@ function Table<T extends object>({
|
|||
return (
|
||||
<TableRow
|
||||
key={
|
||||
isObjKey(item, idFieldName) ? item[idFieldName] + '' : index
|
||||
getRowKey
|
||||
? getRowKey(item, index)
|
||||
: isObjKey(item, idFieldName)
|
||||
? item[idFieldName] + ''
|
||||
: index
|
||||
}
|
||||
row={item}
|
||||
actions={actions}
|
||||
|
|
|
@ -17,5 +17,5 @@ export const DATE_FORMAT = {
|
|||
MONTH_AND_YEAR: 'yyyy-MM',
|
||||
DAY_MONTH_AND_YEAR: 'yyyy-MM-dd',
|
||||
HOUR_MINUTES_DAY_MONTH_AND_YEAR: 'yyyy-MM-dd HH:mm',
|
||||
HOUR_MINUTES_SECONDS_DAY_MONTH_AND_YEAR: 'yyyy-MM-dd HH:mm',
|
||||
HOUR_MINUTES_SECONDS_DAY_MONTH_AND_YEAR: 'yyyy-MM-dd HH:mm:ss',
|
||||
};
|
||||
|
|
|
@ -10,5 +10,6 @@ export const SERVER_PAGE = {
|
|||
COMMON: 'server-page/player-page/common',
|
||||
INDEX_PAGE: '/server-page/player-page/index-page',
|
||||
HISTORY_PAGE: '/server-page/player-page/history-page',
|
||||
ENNOBLEMENTS_PAGE: '/server-page/player-page/ennoblements-page',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -7,13 +7,13 @@ export const SERVER_PAGE = {
|
|||
INDEX_PAGE: '/server/:key/tribe/:id',
|
||||
HISTORY_PAGE: '/server/:key/tribe/:id/history',
|
||||
TRIBE_CHANGES_PAGE: '/server/:key/tribe/:id/tribe-changes',
|
||||
CONQUERS_PAGE: '/server/:key/tribe/:id/conquers',
|
||||
ENNOBLEMENTS_PAGE: '/server/:key/tribe/:id/ennoblements',
|
||||
},
|
||||
PLAYER_PAGE: {
|
||||
INDEX_PAGE: '/server/:key/player/:id',
|
||||
HISTORY_PAGE: '/server/:key/player/:id/history',
|
||||
TRIBE_CHANGES_PAGE: '/server/:key/player/:id/tribe-changes',
|
||||
CONQUERS_PAGE: '/server/:key/player/:id/conquers',
|
||||
ENNOBLEMENTS_PAGE: '/server/:key/player/:id/ennoblements',
|
||||
},
|
||||
VILLAGE_PAGE: {
|
||||
INDEX_PAGE: '/server/:key/village/:id',
|
||||
|
|
30
src/features/ServerPage/common/Dot/Dot.tsx
Normal file
30
src/features/ServerPage/common/Dot/Dot.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
import React from 'react';
|
||||
import { Property } from 'csstype';
|
||||
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
interface UseStylesProps {
|
||||
backgroundColor?: Property.BackgroundColor;
|
||||
}
|
||||
|
||||
interface Props extends UseStylesProps {}
|
||||
|
||||
function Dot({ backgroundColor }: Props) {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<div
|
||||
className={classes.dot}
|
||||
style={{ backgroundColor: backgroundColor ?? 'green' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
dot: {
|
||||
borderRadius: '50%',
|
||||
width: theme.spacing(2),
|
||||
height: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
export default Dot;
|
|
@ -2,21 +2,25 @@ import React from 'react';
|
|||
import { SERVER_PAGE } from '@config/routes';
|
||||
import Link from '@common/Link/Link';
|
||||
|
||||
export interface Tribe {
|
||||
id: number;
|
||||
tag: string;
|
||||
}
|
||||
|
||||
export interface Player {
|
||||
id: number;
|
||||
name: string;
|
||||
tribe?: {
|
||||
id: number;
|
||||
tag: string;
|
||||
};
|
||||
tribe?: Tribe;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
player: Player;
|
||||
server: string;
|
||||
tribe?: Tribe;
|
||||
}
|
||||
|
||||
function PlayerProfileLink({ player, server }: Props) {
|
||||
function PlayerProfileLink({ player, server, tribe }: Props) {
|
||||
const t = tribe ? tribe : player.tribe ? player.tribe : undefined;
|
||||
return (
|
||||
<span>
|
||||
<Link
|
||||
|
@ -25,14 +29,14 @@ function PlayerProfileLink({ player, server }: Props) {
|
|||
>
|
||||
{player.name}
|
||||
</Link>
|
||||
{player.tribe && (
|
||||
{t && (
|
||||
<span>
|
||||
{` (`}
|
||||
<Link
|
||||
to={SERVER_PAGE.TRIBE_PAGE.INDEX_PAGE}
|
||||
params={{ key: server, id: player.tribe.id }}
|
||||
params={{ key: server, id: t.id }}
|
||||
>
|
||||
{player.tribe.tag}
|
||||
{t.tag}
|
||||
</Link>
|
||||
)
|
||||
</span>
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Switch, Route, RouteProps } from 'react-router-dom';
|
|||
import PlayerProvider from './libs/PlayerPageContext/Provider';
|
||||
import IndexPage from './features/IndexPage/IndexPage';
|
||||
import HistoryPage from './features/HistoryPage/HistoryPage';
|
||||
import EnnoblementsPage from './features/EnnoblementsPage/EnnoblementsPage';
|
||||
import NotFoundPage from '../NotFoundPage/NotFoundPage';
|
||||
|
||||
const EnhancedRoute = ({ children, ...rest }: RouteProps) => {
|
||||
|
@ -24,6 +25,9 @@ function PlayerPage() {
|
|||
<EnhancedRoute exact path={SERVER_PAGE.PLAYER_PAGE.HISTORY_PAGE}>
|
||||
<HistoryPage />
|
||||
</EnhancedRoute>
|
||||
<EnhancedRoute exact path={SERVER_PAGE.PLAYER_PAGE.ENNOBLEMENTS_PAGE}>
|
||||
<EnnoblementsPage />
|
||||
</EnhancedRoute>
|
||||
<Route path="*">
|
||||
<NotFoundPage />
|
||||
</Route>
|
||||
|
|
|
@ -46,8 +46,8 @@ function PageLayout({ children }: Props) {
|
|||
label: t('pageLayout.tabs.tribeChanges'),
|
||||
},
|
||||
{
|
||||
to: ROUTES.SERVER_PAGE.PLAYER_PAGE.CONQUERS_PAGE,
|
||||
label: t('pageLayout.tabs.conquersPage'),
|
||||
to: ROUTES.SERVER_PAGE.PLAYER_PAGE.ENNOBLEMENTS_PAGE,
|
||||
label: t('pageLayout.tabs.ennoblementsPage'),
|
||||
},
|
||||
];
|
||||
}, [t]);
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useTitle from '@libs/useTitle';
|
||||
import useServer from '@features/ServerPage/libs/ServerContext/useServer';
|
||||
import usePlayer from '../../libs/PlayerPageContext/usePlayer';
|
||||
import { SERVER_PAGE } from '@config/namespaces';
|
||||
|
||||
import { Container } from '@material-ui/core';
|
||||
import PageLayout from '../../common/PageLayout/PageLayout';
|
||||
import Ennoblements from './components/Ennoblements/Ennoblements';
|
||||
|
||||
function EnnoblementsPage() {
|
||||
const { key } = useServer();
|
||||
const player = usePlayer();
|
||||
const { t } = useTranslation(SERVER_PAGE.PLAYER_PAGE.ENNOBLEMENTS_PAGE);
|
||||
useTitle(t('title', { key, name: player.name }));
|
||||
return (
|
||||
<PageLayout>
|
||||
<Container>
|
||||
<Ennoblements t={t} server={key} playerID={player.id} />
|
||||
</Container>
|
||||
</PageLayout>
|
||||
);
|
||||
}
|
||||
|
||||
export default EnnoblementsPage;
|
|
@ -0,0 +1,153 @@
|
|||
import React from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { useQueryParams, NumberParam, withDefault } from 'use-query-params';
|
||||
import { SERVER_PAGE } from '@config/routes';
|
||||
import { ENNOBLEMENTS } from './queries';
|
||||
import { LIMIT } from './constants';
|
||||
import buildVillageName from '@utils/buildVillageName';
|
||||
|
||||
import { Paper } from '@material-ui/core';
|
||||
import Table from '@common/Table/Table';
|
||||
import Link from '@common/Link/Link';
|
||||
import PlayerProfileLink from '@features/ServerPage/common/PlayerProfileLink/PlayerProfileLink';
|
||||
import Dot from '@features/ServerPage/common/Dot/Dot';
|
||||
|
||||
import { TFunction } from 'i18next';
|
||||
import { EnnoblementsQueryVariables } from '@libs/graphql/types';
|
||||
import { Ennoblements as EnnoblementsT, Ennoblement } from './types';
|
||||
|
||||
export interface Props {
|
||||
server: string;
|
||||
playerID: number;
|
||||
t: TFunction;
|
||||
}
|
||||
|
||||
function Ennoblements({ t, server, playerID }: Props) {
|
||||
const [query, setQuery] = useQueryParams({
|
||||
page: withDefault(NumberParam, 0),
|
||||
limit: withDefault(NumberParam, LIMIT),
|
||||
});
|
||||
const { data: queryData, loading: queryLoading } = useQuery<
|
||||
EnnoblementsT,
|
||||
EnnoblementsQueryVariables
|
||||
>(ENNOBLEMENTS, {
|
||||
fetchPolicy: 'cache-and-network',
|
||||
variables: {
|
||||
limit: query.limit,
|
||||
offset: query.page * query.limit,
|
||||
sort: ['ennobledAt DESC'],
|
||||
filter: {
|
||||
or: {
|
||||
newOwnerID: [playerID],
|
||||
oldOwnerID: [playerID],
|
||||
},
|
||||
},
|
||||
server,
|
||||
},
|
||||
});
|
||||
const ennoblements = queryData?.ennoblements?.items ?? [];
|
||||
const loading = ennoblements.length === 0 && queryLoading;
|
||||
const total = queryData?.ennoblements?.total ?? 0;
|
||||
|
||||
return (
|
||||
<Paper>
|
||||
<Table
|
||||
columns={[
|
||||
{
|
||||
field: 'type',
|
||||
label: '',
|
||||
sortable: false,
|
||||
valueFormatter: (e: Ennoblement) => {
|
||||
if (
|
||||
e.newOwner.id === playerID &&
|
||||
e.oldOwner &&
|
||||
e.oldOwner.id === playerID
|
||||
) {
|
||||
return <Dot backgroundColor="yellow" />;
|
||||
} else if (e.oldOwner && e.oldOwner.id === playerID) {
|
||||
return <Dot backgroundColor="red" />;
|
||||
}
|
||||
return <Dot backgroundColor="green" />;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'ennobledAt',
|
||||
label: t('ennoblements.columns.ennobledAt'),
|
||||
sortable: false,
|
||||
type: 'datetime',
|
||||
},
|
||||
{
|
||||
field: 'village',
|
||||
label: t('ennoblements.columns.village'),
|
||||
sortable: false,
|
||||
valueFormatter: (e: Ennoblement) => {
|
||||
return (
|
||||
<Link
|
||||
to={SERVER_PAGE.VILLAGE_PAGE.INDEX_PAGE}
|
||||
params={{ key: server, id: e.village.id }}
|
||||
>
|
||||
{buildVillageName(e.village.name, e.village.x, e.village.y)}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'oldOwner',
|
||||
label: t('ennoblements.columns.oldOwner'),
|
||||
sortable: false,
|
||||
valueFormatter: (e: Ennoblement) => {
|
||||
if (e.oldOwner) {
|
||||
return (
|
||||
<PlayerProfileLink
|
||||
server={server}
|
||||
player={e.oldOwner}
|
||||
tribe={e.oldOwnerTribe}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'newOwner',
|
||||
label: t('ennoblements.columns.newOwner'),
|
||||
sortable: false,
|
||||
valueFormatter: (e: Ennoblement) => {
|
||||
return (
|
||||
<PlayerProfileLink
|
||||
server={server}
|
||||
player={e.newOwner}
|
||||
tribe={e.newOwnerTribe}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
]}
|
||||
loading={loading}
|
||||
data={ennoblements}
|
||||
size="small"
|
||||
footerProps={{
|
||||
page: loading ? 0 : query.page,
|
||||
rowsPerPage: query.limit,
|
||||
count: total,
|
||||
onChangePage: page => {
|
||||
if (window.scrollTo) {
|
||||
window.scrollTo({ top: 0, behavior: `smooth` });
|
||||
}
|
||||
setQuery({ page });
|
||||
},
|
||||
onChangeRowsPerPage: rowsPerPage => {
|
||||
if (window.scrollTo) {
|
||||
window.scrollTo({ top: 0, behavior: `smooth` });
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
setQuery({ limit: rowsPerPage, page: 0 });
|
||||
});
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
export default Ennoblements;
|
|
@ -0,0 +1 @@
|
|||
export const LIMIT = 25;
|
|
@ -0,0 +1,46 @@
|
|||
import { gql } from '@apollo/client';
|
||||
|
||||
export const ENNOBLEMENTS = gql`
|
||||
query ennoblements(
|
||||
$server: String!
|
||||
$sort: [String!]
|
||||
$limit: Int
|
||||
$offset: Int
|
||||
$filter: EnnoblementFilter
|
||||
) {
|
||||
ennoblements(
|
||||
server: $server
|
||||
filter: $filter
|
||||
sort: $sort
|
||||
limit: $limit
|
||||
offset: $offset
|
||||
) {
|
||||
items {
|
||||
village {
|
||||
name
|
||||
x
|
||||
y
|
||||
id
|
||||
}
|
||||
newOwner {
|
||||
id
|
||||
name
|
||||
}
|
||||
newOwnerTribe {
|
||||
id
|
||||
tag
|
||||
}
|
||||
oldOwner {
|
||||
id
|
||||
name
|
||||
}
|
||||
oldOwnerTribe {
|
||||
id
|
||||
tag
|
||||
}
|
||||
ennobledAt
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,31 @@
|
|||
import { List } from '@libs/graphql/types';
|
||||
|
||||
export type Ennoblement = {
|
||||
village: {
|
||||
id: number;
|
||||
name: string;
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
newOwner: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
newOwnerTribe?: {
|
||||
id: number;
|
||||
tag: string;
|
||||
};
|
||||
oldOwner?: {
|
||||
id: number;
|
||||
name: string;
|
||||
};
|
||||
oldOwnerTribe?: {
|
||||
id: number;
|
||||
tag: string;
|
||||
};
|
||||
ennobledAt: Date | string;
|
||||
};
|
||||
|
||||
export type Ennoblements = {
|
||||
ennoblements?: List<Ennoblement[]>;
|
||||
};
|
|
@ -67,6 +67,19 @@ export type TribeFilter = {
|
|||
|
||||
export type TribesQueryVariables = QueryVariablesWithServer<TribeFilter>;
|
||||
|
||||
export type EnnoblementFilter = {
|
||||
or?: {
|
||||
newOwnerID?: number[];
|
||||
newOwnerTribeID?: number[];
|
||||
oldOwnerID?: number[];
|
||||
oldOwnerTribeID?: number[];
|
||||
};
|
||||
};
|
||||
|
||||
export type EnnoblementsQueryVariables = QueryVariablesWithServer<
|
||||
EnnoblementFilter
|
||||
>;
|
||||
|
||||
export type DailyPlayerStatsFilter = {
|
||||
createDateGT?: Date | 'string';
|
||||
player?: PlayerFilter;
|
||||
|
|
|
@ -9,7 +9,7 @@ const translations = {
|
|||
indexPage: 'Profile',
|
||||
historyPage: 'History',
|
||||
tribeChanges: 'Tribe changes',
|
||||
conquersPage: 'Conquers',
|
||||
ennoblementsPage: 'Ennoblements',
|
||||
},
|
||||
achievements: {
|
||||
playerRank: '#{{rank}} points',
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
const translations = {
|
||||
title: `{{key}} - {{name}}'s ennoblements`,
|
||||
ennoblements: {
|
||||
columns: {
|
||||
ennobledAt: 'Date',
|
||||
village: 'Village',
|
||||
oldOwner: 'Old owner',
|
||||
newOwner: 'New owner',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default translations;
|
|
@ -1,5 +1,5 @@
|
|||
const translations = {
|
||||
title: `{{key}} - {{name}}'s player history`,
|
||||
title: `{{key}} - {{name}}'s history`,
|
||||
playerHistory: {
|
||||
columns: {
|
||||
createDate: 'Date',
|
||||
|
|
|
@ -2,11 +2,13 @@ import * as NAMESPACES from '@config/namespaces';
|
|||
import common from './common';
|
||||
import indexPage from './index-page';
|
||||
import historyPage from './history-page';
|
||||
import ennoblementsPage from './ennoblements-page';
|
||||
|
||||
const translations = {
|
||||
[NAMESPACES.SERVER_PAGE.PLAYER_PAGE.COMMON]: common,
|
||||
[NAMESPACES.SERVER_PAGE.PLAYER_PAGE.INDEX_PAGE]: indexPage,
|
||||
[NAMESPACES.SERVER_PAGE.PLAYER_PAGE.HISTORY_PAGE]: historyPage,
|
||||
[NAMESPACES.SERVER_PAGE.PLAYER_PAGE.ENNOBLEMENTS_PAGE]: ennoblementsPage,
|
||||
};
|
||||
|
||||
export default translations;
|
||||
|
|
|
@ -9,7 +9,7 @@ const translations = {
|
|||
indexPage: 'Profil',
|
||||
historyPage: 'Historia',
|
||||
tribeChanges: 'Zmiany plemion',
|
||||
conquersPage: 'Przejęcia',
|
||||
ennoblementsPage: 'Przejęcia',
|
||||
},
|
||||
achievements: {
|
||||
playerRank: '#{{rank}} punkty',
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
const translations = {
|
||||
title: `{{key}} - podboje gracza {{name}}`,
|
||||
ennoblements: {
|
||||
columns: {
|
||||
ennobledAt: 'Data',
|
||||
village: 'Wioska',
|
||||
oldOwner: 'Stary właściciel',
|
||||
newOwner: 'Nowy właściciel',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default translations;
|
|
@ -2,11 +2,13 @@ import * as NAMESPACES from '@config/namespaces';
|
|||
import common from './common';
|
||||
import indexPage from './index-page';
|
||||
import historyPage from './history-page';
|
||||
import ennoblementsPage from './ennoblements-page';
|
||||
|
||||
const translations = {
|
||||
[NAMESPACES.SERVER_PAGE.PLAYER_PAGE.COMMON]: common,
|
||||
[NAMESPACES.SERVER_PAGE.PLAYER_PAGE.INDEX_PAGE]: indexPage,
|
||||
[NAMESPACES.SERVER_PAGE.PLAYER_PAGE.HISTORY_PAGE]: historyPage,
|
||||
[NAMESPACES.SERVER_PAGE.PLAYER_PAGE.ENNOBLEMENTS_PAGE]: ennoblementsPage,
|
||||
};
|
||||
|
||||
export default translations;
|
||||
|
|
5
src/utils/buildVillageName.ts
Normal file
5
src/utils/buildVillageName.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
const buildVillageName = (name = '', x = 0, y = 0) => {
|
||||
return `${name} (${x}|${y}) K${y.toString()[0] + x.toString()[0]}`;
|
||||
};
|
||||
|
||||
export default buildVillageName;
|
Reference in New Issue
Block a user