mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: easier to read evolution
This commit is contained in:
1 parent
90d4b76b31
commit
e34a6c6e4b
8 files changed
+85
-90
No files matched your search
@@ -20,7 +20,7 @@ import { AccountInfo } from '../types/accountInfo.js';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: false,
|
inDevelopment: true,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: addStatTypeSubCommandGroups(
|
command: addStatTypeSubCommandGroups(
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { AccountInfo } from '../types/accountInfo.js';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
inPublic: true,
|
inPublic: true,
|
||||||
|
|
||||||
command: addStatTypeSubCommandGroups(
|
command: addStatTypeSubCommandGroups(
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import { blue, green, red } from '@radix-ui/colors';
|
import { blue, green, red, slateDark } from '@radix-ui/colors';
|
||||||
import { GRAPH_HEIGHT, GRAPH_WIDTH } from './Root.js';
|
import { GRAPH_HEIGHT, GRAPH_WIDTH } from './Root.js';
|
||||||
|
|
||||||
export enum LineColor {
|
export enum LineColor {
|
||||||
Red,
|
Red,
|
||||||
Green,
|
Green,
|
||||||
Blue,
|
Blue,
|
||||||
|
White,
|
||||||
}
|
}
|
||||||
|
|
||||||
const LINE_COLORS = {
|
const LINE_COLORS = {
|
||||||
[LineColor.Red]: red.red9,
|
[LineColor.Red]: red.red9,
|
||||||
[LineColor.Green]: green.green9,
|
[LineColor.Green]: green.green9,
|
||||||
[LineColor.Blue]: blue.blue9,
|
[LineColor.Blue]: blue.blue9,
|
||||||
|
[LineColor.White]: slateDark.slate11,
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum LineStyle {
|
export enum LineStyle {
|
||||||
|
|||||||
+16
-14
@@ -1,32 +1,34 @@
|
|||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
import { theme } from '../../../stitches.config.js';
|
import { theme } from '../../../stitches.config.js';
|
||||||
import { MarginProps } from './Root.js';
|
import { MarginProps as RootMarginProps } from './Root.js';
|
||||||
|
|
||||||
export enum VerticalMarginAlign {
|
export enum MarginOrientation {
|
||||||
Left,
|
Vertical,
|
||||||
Right,
|
Horizontal,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VerticalMarginProps extends MarginProps {
|
export interface MarginProps extends RootMarginProps {
|
||||||
verticalSeparations: number;
|
verticalSeparations: number;
|
||||||
align?: VerticalMarginAlign;
|
orientation?: MarginOrientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function VerticalMargin({
|
export function Margin({
|
||||||
verticalSeparations,
|
verticalSeparations,
|
||||||
max,
|
max,
|
||||||
min,
|
min,
|
||||||
suffix,
|
suffix,
|
||||||
align = VerticalMarginAlign.Left,
|
precision = 0,
|
||||||
}: VerticalMarginProps) {
|
orientation = MarginOrientation.Vertical,
|
||||||
|
}: MarginProps) {
|
||||||
|
const isVertical = orientation === MarginOrientation.Vertical;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: 32,
|
paddingLeft: isVertical ? 0 : 64,
|
||||||
|
width: isVertical ? 48 : '100%',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: isVertical ? 'column' : 'row-reverse',
|
||||||
alignItems:
|
|
||||||
align === VerticalMarginAlign.Right ? 'flex-end' : 'flex-start',
|
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -43,7 +45,7 @@ export function VerticalMargin({
|
|||||||
((verticalSeparations - (index + 1)) /
|
((verticalSeparations - (index + 1)) /
|
||||||
(verticalSeparations - 1)) +
|
(verticalSeparations - 1)) +
|
||||||
min
|
min
|
||||||
).toFixed(0)}${suffix ?? ''}`}
|
).toFixed(precision)}${suffix ?? ''}`}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -1,42 +1,55 @@
|
|||||||
import { times } from 'lodash';
|
import { times } from 'lodash';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { theme } from '../../../stitches.config.js';
|
import { theme } from '../../../stitches.config.js';
|
||||||
import { VerticalMargin, VerticalMarginAlign } from './VerticalMargin.js';
|
import { Margin, MarginOrientation } from './Margin.js';
|
||||||
|
|
||||||
export const GRAPH_WIDTH = 512;
|
export const GRAPH_WIDTH = 544;
|
||||||
export const GRAPH_HEIGHT = 320;
|
export const GRAPH_HEIGHT = 320;
|
||||||
|
|
||||||
export interface MarginProps {
|
export interface MarginProps {
|
||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
suffix?: string;
|
suffix?: string;
|
||||||
|
precision?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RootProps {
|
export interface RootProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
leftVerticalMargin?: MarginProps;
|
verticalMargin?: MarginProps;
|
||||||
rightVerticalMargin?: MarginProps;
|
horizontalMargin?: MarginProps;
|
||||||
verticalSeparations?: number;
|
separations?: number;
|
||||||
xMinLabel: string;
|
xMinLabel: string;
|
||||||
xMaxLabel: string;
|
xMaxLabel: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Root({
|
export function Root({
|
||||||
children,
|
children,
|
||||||
leftVerticalMargin,
|
verticalMargin,
|
||||||
rightVerticalMargin,
|
horizontalMargin,
|
||||||
verticalSeparations = 5,
|
separations = 5,
|
||||||
xMinLabel,
|
xMinLabel,
|
||||||
xMaxLabel,
|
xMaxLabel,
|
||||||
}: RootProps) {
|
}: RootProps) {
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
paddingLeft: 64,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
|
||||||
|
{xMinLabel}
|
||||||
|
</span>
|
||||||
|
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
|
||||||
|
{xMaxLabel}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'flex', gap: 16 }}>
|
<div style={{ display: 'flex', gap: 16 }}>
|
||||||
{leftVerticalMargin && (
|
{verticalMargin && (
|
||||||
<VerticalMargin
|
<Margin {...verticalMargin} verticalSeparations={separations} />
|
||||||
{...leftVerticalMargin}
|
|
||||||
verticalSeparations={verticalSeparations}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
@@ -49,8 +62,8 @@ export function Root({
|
|||||||
border: theme.borderStyles.nonInteractive,
|
border: theme.borderStyles.nonInteractive,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{times(verticalSeparations - 2, (index) => {
|
{times(separations - 2, (index) => {
|
||||||
const y = GRAPH_HEIGHT * ((index + 1) / (verticalSeparations - 1));
|
const y = GRAPH_HEIGHT * ((index + 1) / (separations - 1));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<polyline
|
<polyline
|
||||||
@@ -66,31 +79,15 @@ export function Root({
|
|||||||
|
|
||||||
{children}
|
{children}
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
{rightVerticalMargin && (
|
|
||||||
<VerticalMargin
|
|
||||||
{...rightVerticalMargin}
|
|
||||||
verticalSeparations={verticalSeparations}
|
|
||||||
align={VerticalMarginAlign.Right}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
{horizontalMargin && (
|
||||||
style={{
|
<Margin
|
||||||
paddingLeft: 48,
|
{...horizontalMargin}
|
||||||
paddingRight: 48,
|
verticalSeparations={separations}
|
||||||
display: 'flex',
|
orientation={MarginOrientation.Horizontal}
|
||||||
justifyContent: 'space-between',
|
/>
|
||||||
}}
|
)}
|
||||||
>
|
|
||||||
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
|
|
||||||
{xMinLabel}
|
|
||||||
</span>
|
|
||||||
<span style={{ fontSize: 16, color: theme.colors.textLowContrast }}>
|
|
||||||
{xMaxLabel}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
export * from './components/Line.js';
|
export * from './components/Line.js';
|
||||||
|
export * from './components/Margin.js';
|
||||||
export * from './components/Root.js';
|
export * from './components/Root.js';
|
||||||
export * from './components/VerticalMargin.js';
|
|
||||||
+27
-33
@@ -61,24 +61,22 @@ export default async function evolution<Type extends StatType>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const careerWinrate = histories.map(
|
const plot = histories.map(
|
||||||
(history) =>
|
(history) =>
|
||||||
[
|
[
|
||||||
history.last_battle_time,
|
history.all.battles,
|
||||||
(history.all.wins / history.all.battles) * 100,
|
(history.all.wins / history.all.battles) * 100,
|
||||||
] as Graph.PlotItem,
|
] as Graph.PlotItem,
|
||||||
);
|
);
|
||||||
const careerBattles = histories.map(
|
const times = histories.map((history) => history.last_battle_time);
|
||||||
(history) =>
|
const minTime = Math.min(...times);
|
||||||
[history.last_battle_time, history.all.battles] as Graph.PlotItem,
|
const maxTime = Math.max(...times);
|
||||||
);
|
const ys = plot.map(([, y]) => y);
|
||||||
const winrateYs = careerWinrate.map(([, y]) => y);
|
const xs = plot.map(([x]) => x);
|
||||||
const maxWinrate = Math.max(...winrateYs);
|
const maxY = Math.max(...ys);
|
||||||
const minWinrate = Math.min(...winrateYs);
|
const minY = Math.min(...ys);
|
||||||
const battleYs = careerBattles.map(([, y]) => y);
|
const maxX = Math.max(...xs);
|
||||||
const maxBattle = Math.max(...battleYs);
|
const minX = Math.min(...xs);
|
||||||
const minBattle = Math.min(...battleYs);
|
|
||||||
const minTime = Math.min(...careerBattles.map(([x]) => x));
|
|
||||||
|
|
||||||
const accountInfo = await getWargamingResponse<AccountInfo>(
|
const accountInfo = await getWargamingResponse<AccountInfo>(
|
||||||
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
`https://api.wotblitz.${server}/wotb/account/info/?application_id=${WARGAMING_APPLICATION_ID}&account_id=${id}`,
|
||||||
@@ -96,37 +94,33 @@ export default async function evolution<Type extends StatType>(
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* goofy ahh bug forces me to call them as functions */}
|
{/* goofy ahh bug forces me to call them as functions */}
|
||||||
{careerWinrate.length > 0 && (
|
{plot.length > 0 && (
|
||||||
<Graph.Root
|
<Graph.Root
|
||||||
xMinLabel={new Date(minTime * 1000).toDateString()}
|
verticalMargin={{
|
||||||
xMaxLabel={new Date(end * 1000).toDateString()}
|
min: minY,
|
||||||
leftVerticalMargin={{
|
max: maxY,
|
||||||
min: minWinrate,
|
|
||||||
max: maxWinrate,
|
|
||||||
suffix: '%',
|
suffix: '%',
|
||||||
|
precision: 1,
|
||||||
}}
|
}}
|
||||||
rightVerticalMargin={{
|
horizontalMargin={{
|
||||||
min: minBattle,
|
min: minX,
|
||||||
max: maxBattle,
|
max: maxX,
|
||||||
|
precision: 0,
|
||||||
}}
|
}}
|
||||||
|
xMinLabel={new Date(minTime * 1000).toDateString()}
|
||||||
|
xMaxLabel={new Date(maxTime * 1000).toDateString()}
|
||||||
>
|
>
|
||||||
{Graph.Line({
|
|
||||||
// total battles
|
|
||||||
plot: careerBattles!,
|
|
||||||
color: Graph.LineColor.Blue,
|
|
||||||
})}
|
|
||||||
{Graph.Line({
|
{Graph.Line({
|
||||||
// career winrate
|
// career winrate
|
||||||
plot: careerWinrate!,
|
color: Graph.LineColor.Red,
|
||||||
minY: minWinrate,
|
plot: plot,
|
||||||
maxY: maxWinrate,
|
minY,
|
||||||
|
maxY,
|
||||||
})}
|
})}
|
||||||
</Graph.Root>
|
</Graph.Root>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{careerWinrate.length === 0 && (
|
{plot.length === 0 && <NoData type={NoDataType.BattlesInPeriod} />}
|
||||||
<NoData type={NoDataType.BattlesInPeriod} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<PoweredBy type={PoweredByType.BlitzStars} />
|
<PoweredBy type={PoweredByType.BlitzStars} />
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { RouteRegistry } from '../server.js';
|
|||||||
export default {
|
export default {
|
||||||
route: '/stats/:type/:period',
|
route: '/stats/:type/:period',
|
||||||
inProduction: true,
|
inProduction: true,
|
||||||
inDevelopment: true,
|
inDevelopment: false,
|
||||||
|
|
||||||
async handler(req) {
|
async handler(req) {
|
||||||
const type = req.params.type as StatType;
|
const type = req.params.type as StatType;
|
||||||
|
|||||||
Reference in new issue
Block a user