update
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@@ -7149,6 +7149,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tslint-config-prettier": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz",
|
||||
"integrity": "sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==",
|
||||
"dev": true
|
||||
},
|
||||
"tslint-react": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint-react/-/tslint-react-4.2.0.tgz",
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
"start": "webpack-dev-server --mode development",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"prettier": {
|
||||
"tabWidth": 4,
|
||||
"bracketSpacing": false,
|
||||
"printWidth": 100
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.4",
|
||||
"@babel/core": "^7.9.6",
|
||||
@@ -34,6 +39,7 @@
|
||||
"style-loader": "^1.2.1",
|
||||
"ts-loader": "^7.0.3",
|
||||
"tslint": "^6.1.1",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"tslint-react": "^4.2.0",
|
||||
"typescript": "^3.8.3",
|
||||
"webpack": "^4.43.0",
|
||||
|
||||
@@ -1,138 +1,228 @@
|
||||
import React, {useContext, useState} from "react";
|
||||
import {CellDef, RulesetSchemaDto} from "../Services/RulesetSchemaDto";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Form,
|
||||
Header,
|
||||
Segment,
|
||||
Table
|
||||
} from "semantic-ui-react";
|
||||
BlockDef,
|
||||
BonusBlockDef,
|
||||
CellDef,
|
||||
NoBonusBlockDef,
|
||||
RulesetSchemaDto,
|
||||
} from "../Services/RulesetSchemaDto";
|
||||
import {Button, Container, Grid, Header, Segment} from "semantic-ui-react";
|
||||
import UserContext from "../Contexts/UserContext";
|
||||
import RulesetBlockTable from "./RulesetBlockTable";
|
||||
import RulesetDisplayPanel from "./RulesetDisplayPanel";
|
||||
import NewBlockForm from "./NewBlockForm";
|
||||
import axios from "axios";
|
||||
import {SERVER_BASE_NAME} from "../index";
|
||||
import EditableHeader from "./EditableHeader";
|
||||
|
||||
interface RulesetDisplayPanelProps {
|
||||
onSubmitRuleset: (ruleset: RulesetSchemaDto) => any;
|
||||
onRulesetSave: () => any;
|
||||
onRulesetChange: () => any;
|
||||
}
|
||||
|
||||
const CreateRulesetPanel: React.FunctionComponent<RulesetDisplayPanelProps> = (props) => {
|
||||
const {onSubmitRuleset} = props;
|
||||
const {strings: Locale} = useContext(UserContext);
|
||||
const [currentRulesetBuild, updateCurrentRulesetBuild] = useState<RulesetSchemaDto>({
|
||||
interface RulesetDisplayPanelState {
|
||||
saving: boolean;
|
||||
currentRulesetBuild: RulesetSchemaDto;
|
||||
newBlockInput: {
|
||||
label: string;
|
||||
hasBonus: boolean;
|
||||
bonusFor: number;
|
||||
bonusScore: number;
|
||||
};
|
||||
}
|
||||
|
||||
class CreateRulesetPanel extends React.Component<
|
||||
RulesetDisplayPanelProps,
|
||||
RulesetDisplayPanelState
|
||||
> {
|
||||
constructor(props: RulesetDisplayPanelProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
saving: false,
|
||||
currentRulesetBuild: {
|
||||
id: "",
|
||||
label: Locale.rulesetsPage.newRuleset,
|
||||
label: "",
|
||||
blocks: {},
|
||||
});
|
||||
const [newBlockInput, updateNewBlockInput] = useState({
|
||||
},
|
||||
newBlockInput: {
|
||||
label: "",
|
||||
hasBonus: false,
|
||||
bonusScore: 35,
|
||||
bonusFor: 63,
|
||||
cells: {},
|
||||
});
|
||||
|
||||
const handleAddBlock = () => {
|
||||
if (newBlockInput.label) {
|
||||
updateCurrentRulesetBuild({
|
||||
...currentRulesetBuild,
|
||||
blocks: {
|
||||
...currentRulesetBuild.blocks,
|
||||
[newBlockInput.label]: {
|
||||
...newBlockInput,
|
||||
bonusFor: newBlockInput.hasBonus ? newBlockInput.bonusFor : undefined,
|
||||
bonusScore: newBlockInput.hasBonus ? newBlockInput.bonusScore : undefined,
|
||||
bonusFor: 0,
|
||||
bonusScore: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
if (this.state.currentRulesetBuild.label === "") {
|
||||
this.setState({
|
||||
currentRulesetBuild: {
|
||||
...this.state.currentRulesetBuild,
|
||||
id: this.context.strings.rulesetsPage.newRuleset,
|
||||
label: this.context.strings.rulesetsPage.newRuleset,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleAddCell = (cellDef: CellDef, blockId: string) => {
|
||||
if (cellDef.label) {
|
||||
updateCurrentRulesetBuild({
|
||||
...currentRulesetBuild,
|
||||
blocks: {
|
||||
...currentRulesetBuild.blocks,
|
||||
[blockId]: {
|
||||
...currentRulesetBuild.blocks[blockId],
|
||||
cells: {
|
||||
...currentRulesetBuild.blocks[blockId].cells,
|
||||
[cellDef.label]: cellDef
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
handleAddBlock() {
|
||||
if (this.state.newBlockInput.label) {
|
||||
const blockToSave = {
|
||||
...this.state.newBlockInput,
|
||||
cells: {},
|
||||
};
|
||||
if (!this.state.newBlockInput.hasBonus) {
|
||||
delete blockToSave.bonusScore;
|
||||
delete blockToSave.bonusFor;
|
||||
}
|
||||
this.state.currentRulesetBuild.blocks[blockToSave.label] = blockToSave;
|
||||
this.forceUpdate();
|
||||
}
|
||||
this.props.onRulesetChange();
|
||||
}
|
||||
|
||||
handleAddCell(cellDef: CellDef, blockId: string) {
|
||||
if (cellDef.label) {
|
||||
this.state.currentRulesetBuild.blocks[blockId].cells[cellDef.label] = cellDef;
|
||||
this.forceUpdate();
|
||||
}
|
||||
this.props.onRulesetChange();
|
||||
}
|
||||
|
||||
handleRulesetNameChange(newLabel: string) {
|
||||
this.setState({
|
||||
currentRulesetBuild: {
|
||||
...this.state.currentRulesetBuild,
|
||||
label: newLabel,
|
||||
id: newLabel,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
handleBlockNameChange(newName: string) {
|
||||
this.setState({
|
||||
newBlockInput: {
|
||||
...this.state.newBlockInput,
|
||||
label: newName,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
handleToggleBlockBonus(newHasBonus: boolean) {
|
||||
this.setState({
|
||||
newBlockInput: {
|
||||
...this.state.newBlockInput,
|
||||
hasBonus: newHasBonus,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
handleBonusScoreChange(newBonusScore: number) {
|
||||
this.setState({
|
||||
newBlockInput: {
|
||||
...this.state.newBlockInput,
|
||||
bonusScore: newBonusScore,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
handleBonusForChange(newBonusFor: number) {
|
||||
this.setState({
|
||||
newBlockInput: {
|
||||
...this.state.newBlockInput,
|
||||
bonusFor: newBonusFor,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
blocksExistAndAllHaveCells(): boolean {
|
||||
return (
|
||||
Object.values(this.state.currentRulesetBuild.blocks).length > 0 &&
|
||||
Object.values(this.state.currentRulesetBuild.blocks).reduce(
|
||||
(prev: boolean, blockDef: BlockDef) =>
|
||||
Object.values(blockDef.cells).length > 0 && prev,
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
removeBlock(blockId: string) {
|
||||
this.props.onRulesetChange();
|
||||
delete this.state.currentRulesetBuild.blocks[blockId];
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
||||
removeCell(cellId: string, blockId: string) {
|
||||
this.props.onRulesetChange();
|
||||
delete this.state.currentRulesetBuild.blocks[blockId].cells[cellId];
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
||||
async submitNewRuleset() {
|
||||
this.setState({saving: true}, async () => {
|
||||
const response = await axios.post(
|
||||
SERVER_BASE_NAME + "/api/ruleset",
|
||||
this.state.currentRulesetBuild
|
||||
);
|
||||
console.log(response);
|
||||
this.setState({saving: false}, () => {
|
||||
this.props.onRulesetSave();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const Locale = this.context.strings;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header size={"tiny"}>
|
||||
{Locale.rulesetsPage.newRuleset}
|
||||
</Header>
|
||||
<Grid>
|
||||
<Grid.Column>
|
||||
<Grid.Row>
|
||||
<Segment basic={true}>
|
||||
<EditableHeader
|
||||
onTextEdit={(t) => this.handleRulesetNameChange(t)}
|
||||
text={this.state.currentRulesetBuild.label}
|
||||
headerProps={{size: "small"}}
|
||||
/>
|
||||
<RulesetDisplayPanel
|
||||
ruleset={currentRulesetBuild}
|
||||
ruleset={this.state.currentRulesetBuild}
|
||||
editable={true}
|
||||
loading={false}
|
||||
onAddCell={handleAddCell}
|
||||
removeBlock={(id) => this.removeBlock(id)}
|
||||
removeCell={(cid, bid) => this.removeCell(cid, bid)}
|
||||
onAddCell={(c, b) => this.handleAddCell(c, b)}
|
||||
/>
|
||||
<Header size={"tiny"}>
|
||||
{Locale.rulesetsPage.newBlock}
|
||||
</Header>
|
||||
<Segment>
|
||||
<Form>
|
||||
<Form.Field>
|
||||
<label>{Locale.rulesetsPage.blockName + ": "}</label>
|
||||
<input
|
||||
placeholder={Locale.rulesetsPage.blockNamePlaceholder}
|
||||
onChange={(e) => updateNewBlockInput({...newBlockInput, label: e.target.value})}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<Checkbox
|
||||
toggle={true}
|
||||
onChange={(e, c) => updateNewBlockInput({...newBlockInput, hasBonus: c.checked ?? false})}
|
||||
label={Locale.rulesetsPage.bonus}
|
||||
/>
|
||||
</Form.Field>
|
||||
{newBlockInput.hasBonus && (
|
||||
<>
|
||||
<Form.Field width={4}>
|
||||
<label>{Locale.rulesetsPage.bonusScore + ": "}</label>
|
||||
<input
|
||||
type={"number"}
|
||||
value={newBlockInput.bonusScore}
|
||||
onChange={(e) => updateNewBlockInput({...newBlockInput, bonusScore: Number(e.target.value)})}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field width={4}>
|
||||
<label>{Locale.rulesetsPage.bonusThreshold + ": "}</label>
|
||||
<input
|
||||
type={"number"}
|
||||
value={newBlockInput.bonusFor}
|
||||
onChange={(e) => updateNewBlockInput({...newBlockInput, bonusFor: Number(e.target.value)})}
|
||||
/>
|
||||
</Form.Field>
|
||||
</>
|
||||
)}
|
||||
<Form.Field>
|
||||
<Button
|
||||
onClick={handleAddBlock}
|
||||
>
|
||||
{Locale.rulesetsPage.addBlock}
|
||||
</Button>
|
||||
</Form.Field>
|
||||
</Form>
|
||||
</Segment>
|
||||
</Grid.Row>
|
||||
<Grid.Row>
|
||||
<Segment basic={true}>
|
||||
<Header size={"tiny"}>{Locale.rulesetsPage.newBlock}</Header>
|
||||
<NewBlockForm
|
||||
newBlockInput={this.state.newBlockInput}
|
||||
onNameChange={(x) => this.handleBlockNameChange(x)}
|
||||
onBonusToggle={(x) => this.handleToggleBlockBonus(x)}
|
||||
onBonusScoreChange={(x) => this.handleBonusScoreChange(x)}
|
||||
onBonusForChange={(x) => this.handleBonusForChange(x)}
|
||||
onSubmitClick={() => this.handleAddBlock()}
|
||||
/>
|
||||
</Segment>
|
||||
</Grid.Row>
|
||||
<Grid.Column>
|
||||
<Segment textAlign={"center"} basic={true}>
|
||||
<Button
|
||||
fluid={true}
|
||||
onClick={() => onSubmitRuleset(currentRulesetBuild)}
|
||||
loading={this.state.saving}
|
||||
primary={true}
|
||||
disabled={!this.blocksExistAndAllHaveCells()}
|
||||
onClick={() => this.submitNewRuleset()}
|
||||
>
|
||||
{Locale.rulesetsPage.submit}
|
||||
{Locale.rulesetsPage.save}
|
||||
</Button>
|
||||
</>
|
||||
</Segment>
|
||||
</Grid.Column>
|
||||
</Grid.Column>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
CreateRulesetPanel.contextType = UserContext;
|
||||
|
||||
export default CreateRulesetPanel;
|
||||
39
src/Components/EditableHeader.tsx
Normal file
39
src/Components/EditableHeader.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import React, {useState, KeyboardEvent} from "react";
|
||||
import {Header, Icon, Input, StrictHeaderProps} from "semantic-ui-react";
|
||||
|
||||
interface EditableHeaderProps {
|
||||
onTextEdit: (newText: string) => any;
|
||||
headerProps: StrictHeaderProps;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const EditableHeader: React.FunctionComponent<EditableHeaderProps> = (props) => {
|
||||
const {text, onTextEdit, headerProps} = props;
|
||||
const [isEditing, updateIsEditing] = useState(false);
|
||||
|
||||
const handleKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === "Enter") {
|
||||
onTextEdit(e.currentTarget.value);
|
||||
updateIsEditing(false);
|
||||
}
|
||||
};
|
||||
|
||||
return isEditing ? (
|
||||
<Header {...headerProps}>
|
||||
<Input
|
||||
onBlur={() => updateIsEditing(false)}
|
||||
autoFocus={true}
|
||||
onChange={(e) => onTextEdit(e.target.value)}
|
||||
value={text}
|
||||
onKeyUp={(e: KeyboardEvent<HTMLInputElement>) => handleKeyUp(e)}
|
||||
/>
|
||||
</Header>
|
||||
) : (
|
||||
<Header style={{cursor: "pointer"}} onClick={() => updateIsEditing(true)} {...headerProps}>
|
||||
{text + " "}
|
||||
<Icon name={"edit"} size={"tiny"} disabled={true}/>
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditableHeader;
|
||||
69
src/Components/NewBlockForm.tsx
Normal file
69
src/Components/NewBlockForm.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import React, {useContext} from "react";
|
||||
import UserContext from "../Contexts/UserContext";
|
||||
import {Button, Checkbox, Form, Segment} from "semantic-ui-react";
|
||||
|
||||
function NewBlockForm(props: {
|
||||
newBlockInput: {
|
||||
bonusScore: number;
|
||||
bonusFor: number;
|
||||
label: string;
|
||||
hasBonus: boolean;
|
||||
};
|
||||
onNameChange: (newName: string) => void;
|
||||
onBonusToggle: (newHasBonus: boolean) => void;
|
||||
onBonusScoreChange: (newBonusScore: number) => void;
|
||||
onBonusForChange: (newBonusFor: number) => void;
|
||||
onSubmitClick: () => void;
|
||||
}) {
|
||||
const {strings: Locale} = useContext(UserContext);
|
||||
return (
|
||||
<Segment>
|
||||
<Form>
|
||||
<Form.Field>
|
||||
<label>{Locale.rulesetsPage.blockName + ": "}</label>
|
||||
<input
|
||||
placeholder={Locale.rulesetsPage.blockNamePlaceholder}
|
||||
onChange={(e) => props.onNameChange(e.target.value)}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<Checkbox
|
||||
toggle={true}
|
||||
onChange={(e, c) => props.onBonusToggle(c.checked ?? false)}
|
||||
label={Locale.rulesetsPage.bonus}
|
||||
/>
|
||||
</Form.Field>
|
||||
{props.newBlockInput.hasBonus && (
|
||||
<>
|
||||
<Form.Field width={4}>
|
||||
<label>{Locale.rulesetsPage.bonusScore + ": "}</label>
|
||||
<input
|
||||
type={"number"}
|
||||
value={props.newBlockInput.bonusScore}
|
||||
onChange={(e) => props.onBonusScoreChange(Number(e.target.value))}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field width={4}>
|
||||
<label>{Locale.rulesetsPage.bonusThreshold + ": "}</label>
|
||||
<input
|
||||
type={"number"}
|
||||
value={props.newBlockInput.bonusFor}
|
||||
onChange={(e) => props.onBonusForChange(Number(e.target.value))}
|
||||
/>
|
||||
</Form.Field>
|
||||
</>
|
||||
)}
|
||||
<Form.Field>
|
||||
<Button
|
||||
disabled={props.newBlockInput.label === ""}
|
||||
onClick={props.onSubmitClick}
|
||||
>
|
||||
{Locale.rulesetsPage.addBlock}
|
||||
</Button>
|
||||
</Form.Field>
|
||||
</Form>
|
||||
</Segment>
|
||||
);
|
||||
}
|
||||
|
||||
export default NewBlockForm;
|
||||
@@ -1,7 +1,7 @@
|
||||
import {BlockDef, CellDef} from "../Services/RulesetSchemaDto";
|
||||
import React, {useContext, useState} from "react";
|
||||
import RulesetCellTableRow from "./RulesetCellTableRow";
|
||||
import {Button, Dropdown, DropdownItemProps, Grid, Input, Table} from "semantic-ui-react";
|
||||
import {Button, Dropdown, DropdownItemProps, Grid, Input, List, Table} from "semantic-ui-react";
|
||||
import UserContext from "../Contexts/UserContext";
|
||||
import {FieldType} from "../enums";
|
||||
|
||||
@@ -10,32 +10,28 @@ interface RulesetBlockTableProps {
|
||||
blockDef: BlockDef | null;
|
||||
editable?: boolean;
|
||||
onAddCell?: (cellDef: CellDef, blockId: string) => any;
|
||||
removeBlock?: (blockId: string) => any;
|
||||
removeCell?: (cellId: string, blockId: string) => any;
|
||||
}
|
||||
|
||||
const RulesetBlockTable: React.FunctionComponent<RulesetBlockTableProps> = (props) => {
|
||||
const {id, blockDef, editable, onAddCell} = props;
|
||||
const {id, blockDef, editable, onAddCell, removeCell} = props;
|
||||
const [currentCellInput, updateCurrentCellInput] = useState({
|
||||
label: "",
|
||||
maxMultiples: 0,
|
||||
multiplier: 0,
|
||||
fieldType: FieldType.number,
|
||||
score: 0,
|
||||
maxMultiples: 1,
|
||||
multiplier: 1,
|
||||
fieldType: undefined,
|
||||
score: 25,
|
||||
maxSuperkadis: 5,
|
||||
});
|
||||
const {strings: Locale} = useContext(UserContext);
|
||||
|
||||
if (!blockDef) {
|
||||
return (
|
||||
<Table
|
||||
attached={true}
|
||||
fixed={true}
|
||||
celled={true}
|
||||
>
|
||||
<Table attached={true} fixed={true} celled={true}>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell
|
||||
colSpan={"3"}
|
||||
textAlign={"center"}
|
||||
>
|
||||
<Table.HeaderCell colSpan={"3"} textAlign={"center"}>
|
||||
{Locale.rulesetsPage.noBlocks}
|
||||
</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
@@ -51,151 +47,200 @@ const RulesetBlockTable: React.FunctionComponent<RulesetBlockTableProps> = (prop
|
||||
subText += `${Locale.general.yes},
|
||||
${Locale.rulesetsPage.bonusScore}: ${blockDef.bonusScore},
|
||||
${Locale.rulesetsPage.bonusThreshold}: ${blockDef.bonusFor}`;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
subText += Locale.general.no;
|
||||
}
|
||||
|
||||
const fieldTypeOptions: DropdownItemProps[] = [
|
||||
{value: FieldType.bool, key: FieldType.bool, text: Locale.rulesetsPage[FieldType.bool]},
|
||||
{value: FieldType.multiplier, key: FieldType.multiplier, text: Locale.rulesetsPage[FieldType.multiplier]},
|
||||
{value: FieldType.number, key: FieldType.number, text: Locale.rulesetsPage[FieldType.number]},
|
||||
{value: FieldType.superkadi, key: FieldType.superkadi, text: Locale.rulesetsPage[FieldType.superkadi]},
|
||||
{
|
||||
value: FieldType.multiplier,
|
||||
key: FieldType.multiplier,
|
||||
text: Locale.rulesetsPage[FieldType.multiplier],
|
||||
},
|
||||
{
|
||||
value: FieldType.number,
|
||||
key: FieldType.number,
|
||||
text: Locale.rulesetsPage[FieldType.number],
|
||||
},
|
||||
{
|
||||
value: FieldType.superkadi,
|
||||
key: FieldType.superkadi,
|
||||
text: Locale.rulesetsPage[FieldType.superkadi],
|
||||
},
|
||||
];
|
||||
|
||||
const onChangeDropdown = (value: FieldType) => {
|
||||
const newCell = {
|
||||
...currentCellInput,
|
||||
fieldType: value,
|
||||
};
|
||||
if (value === FieldType.bool) {
|
||||
newCell.score = 0;
|
||||
}
|
||||
else if (value === FieldType.multiplier) {
|
||||
newCell.multiplier = 0;
|
||||
newCell.maxMultiples = 5;
|
||||
}
|
||||
else if (value === FieldType.superkadi) {
|
||||
newCell.maxMultiples = 5;
|
||||
newCell.score = 50;
|
||||
}
|
||||
const onChangeFieldTypeDropdown = (newFieldType: FieldType) => {
|
||||
const newCell: any = Object.assign({}, currentCellInput);
|
||||
newCell.fieldType = newFieldType;
|
||||
updateCurrentCellInput(newCell);
|
||||
};
|
||||
|
||||
const handleAddCell = () => {
|
||||
if (onAddCell) {
|
||||
const newCell: any = Object.assign({}, currentCellInput);
|
||||
if (newCell.fieldType === FieldType.bool) {
|
||||
delete newCell.maxMultiples;
|
||||
delete newCell.maxSuperkadis;
|
||||
delete newCell.multiplier;
|
||||
} else if (newCell.fieldType === FieldType.multiplier) {
|
||||
delete newCell.score;
|
||||
delete newCell.maxSuperkadis;
|
||||
} else if (newCell.fieldType === FieldType.superkadi) {
|
||||
delete newCell.multiplier;
|
||||
delete newCell.maxMultiples;
|
||||
} else if (newCell.fieldType === FieldType.number) {
|
||||
delete newCell.multiplier;
|
||||
delete newCell.maxMultiples;
|
||||
delete newCell.maxSuperkadis;
|
||||
delete newCell.score;
|
||||
}
|
||||
onAddCell(newCell, id);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Table
|
||||
attached={true}
|
||||
fixed={true}
|
||||
celled={true}
|
||||
>
|
||||
<Table attached={true} fixed={true} celled={true}>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell
|
||||
colSpan={"3"}
|
||||
textAlign={"center"}
|
||||
>
|
||||
<Table.HeaderCell verticalAlign={"middle"} colSpan={"3"} textAlign={"center"}>
|
||||
{blockDef.label}
|
||||
{editable && (
|
||||
<Button
|
||||
floated={"right"}
|
||||
color={"red"}
|
||||
icon={"minus"}
|
||||
size={"mini"}
|
||||
onClick={() => props.removeBlock?.(id)}
|
||||
/>
|
||||
)}
|
||||
</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{idAndCellList.map(idAndCell => (
|
||||
{idAndCellList.map((idAndCell) => (
|
||||
<RulesetCellTableRow
|
||||
key={idAndCell[0]}
|
||||
id={idAndCell[0]}
|
||||
blockId={id}
|
||||
cellDef={idAndCell[1]}
|
||||
editable={editable}
|
||||
removeCell={removeCell}
|
||||
/>
|
||||
))}
|
||||
{editable && (
|
||||
<Table.Row>
|
||||
<Table.Cell
|
||||
style={{overflow: "visible"}}
|
||||
colSpan={"3"}
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Row
|
||||
columns={3}
|
||||
verticalAlign={"middle"}
|
||||
>
|
||||
<Grid.Column width={4}>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
fluid={true}
|
||||
value={currentCellInput.label}
|
||||
label={Locale.rulesetsPage.cellName}
|
||||
placeholder={Locale.rulesetsPage.cellNamePlaceholder}
|
||||
onChange={(e) => updateCurrentCellInput({...currentCellInput, label: e.target.value})}
|
||||
onChange={(e) =>
|
||||
updateCurrentCellInput({
|
||||
...currentCellInput,
|
||||
label: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Grid.Column>
|
||||
<Grid.Column width={4}>
|
||||
</Table.Cell>
|
||||
<Table.Cell style={{overflow: "visible"}}>
|
||||
<Dropdown
|
||||
selectedLabel={currentCellInput.fieldType}
|
||||
basic={true}
|
||||
placeholder={Locale.rulesetsPage.fieldTypePlaceholder}
|
||||
onChange={(e, c) => onChangeDropdown(c.value as FieldType)}
|
||||
onChange={(e, c) => onChangeFieldTypeDropdown(c.value as FieldType)}
|
||||
options={fieldTypeOptions}
|
||||
button={true}
|
||||
/>
|
||||
</Grid.Column>
|
||||
<Grid.Column width={4}>
|
||||
{currentCellInput.fieldType === FieldType.multiplier && (
|
||||
<Input
|
||||
label={Locale.rulesetsPage.multiplierPlaceholder}
|
||||
fluid={true}
|
||||
type={"number"}
|
||||
value={currentCellInput.multiplier}
|
||||
onChange={(e) => updateCurrentCellInput(
|
||||
{...currentCellInput, multiplier: Number(e.target.value)})}
|
||||
/>
|
||||
)}
|
||||
{(currentCellInput.fieldType === FieldType.superkadi
|
||||
|| currentCellInput.fieldType === FieldType.multiplier) && (
|
||||
<Input
|
||||
label={Locale.rulesetsPage.maxMultiplesPlaceholder}
|
||||
fluid={true}
|
||||
type={"number"}
|
||||
value={currentCellInput.maxMultiples}
|
||||
onChange={(e) => updateCurrentCellInput({
|
||||
...currentCellInput,
|
||||
maxMultiples: Number(e.target.value),
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
{(currentCellInput.fieldType === FieldType.bool
|
||||
|| currentCellInput.fieldType === FieldType.superkadi
|
||||
|| currentCellInput.fieldType === FieldType.number) && (
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Grid divided={true}>
|
||||
<Grid.Column width={12}>
|
||||
{currentCellInput.fieldType === undefined ||
|
||||
currentCellInput.fieldType === FieldType.number ? (
|
||||
Locale.rulesetsPage.extraCellSettingsInfo
|
||||
) : (
|
||||
<List>
|
||||
{(currentCellInput.fieldType === FieldType.bool ||
|
||||
currentCellInput.fieldType ===
|
||||
FieldType.superkadi) && (
|
||||
<List.Item>
|
||||
<Input
|
||||
label={Locale.rulesetsPage.valuePlaceholder}
|
||||
fluid={true}
|
||||
type={"number"}
|
||||
value={currentCellInput.score}
|
||||
onChange={(e) => updateCurrentCellInput(
|
||||
{...currentCellInput, score: Number(e.target.value)})}
|
||||
onChange={(e) =>
|
||||
updateCurrentCellInput({
|
||||
...currentCellInput,
|
||||
score: Number(e.target.value),
|
||||
})
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
{currentCellInput.fieldType ===
|
||||
FieldType.multiplier && (
|
||||
<List.Item>
|
||||
<Input
|
||||
label={
|
||||
Locale.rulesetsPage
|
||||
.multiplierPlaceholder
|
||||
}
|
||||
fluid={true}
|
||||
type={"number"}
|
||||
value={currentCellInput.multiplier}
|
||||
onChange={(e) =>
|
||||
updateCurrentCellInput({
|
||||
...currentCellInput,
|
||||
multiplier: Number(e.target.value),
|
||||
})
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
{(currentCellInput.fieldType === FieldType.superkadi ||
|
||||
currentCellInput.fieldType ===
|
||||
FieldType.multiplier) && (
|
||||
<List.Item>
|
||||
<Input
|
||||
label={
|
||||
Locale.rulesetsPage
|
||||
.maxMultiplesPlaceholder
|
||||
}
|
||||
fluid={true}
|
||||
type={"number"}
|
||||
value={currentCellInput.maxMultiples}
|
||||
onChange={(e) =>
|
||||
updateCurrentCellInput({
|
||||
...currentCellInput,
|
||||
maxMultiples: Number(
|
||||
e.target.value
|
||||
),
|
||||
})
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
</List>
|
||||
)}
|
||||
</Grid.Column>
|
||||
<Grid.Column
|
||||
width={4}
|
||||
textAlign={"right"}
|
||||
>
|
||||
<Grid.Column width={4}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (onAddCell) {
|
||||
onAddCell(currentCellInput, id)
|
||||
basic={true}
|
||||
floated={"right"}
|
||||
disabled={
|
||||
currentCellInput.label === "" ||
|
||||
!currentCellInput.fieldType
|
||||
}
|
||||
}}
|
||||
>
|
||||
{Locale.rulesetsPage.addCell}
|
||||
</Button>
|
||||
icon={"add"}
|
||||
onClick={handleAddCell}
|
||||
/>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)}
|
||||
<Table.Row>
|
||||
<Table.Cell
|
||||
colSpan={"3"}
|
||||
textAlign={"center"}
|
||||
>
|
||||
<Table.Cell colSpan={"3"} textAlign={"center"}>
|
||||
{subText}
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
|
||||
@@ -1,28 +1,79 @@
|
||||
import {BoolCellDef, CellDef, MultiplierCellDef, SuperkadiCellDef} from "../Services/RulesetSchemaDto";
|
||||
import {
|
||||
BoolCellDef,
|
||||
CellDef,
|
||||
MultiplierCellDef,
|
||||
NumberCellDef,
|
||||
SuperkadiCellDef,
|
||||
} from "../Services/RulesetSchemaDto";
|
||||
import React, {useContext} from "react";
|
||||
import UserContext from "../Contexts/UserContext";
|
||||
import {Table} from "semantic-ui-react";
|
||||
import {Button, Grid, List, Table} from "semantic-ui-react";
|
||||
import {FieldType} from "../enums";
|
||||
|
||||
interface TableCellRowProps {
|
||||
id: string;
|
||||
blockId: string;
|
||||
cellDef: CellDef;
|
||||
editable?: boolean;
|
||||
removeCell?: (cellId: string, blockId: string) => any;
|
||||
}
|
||||
|
||||
const RulesetCellTableRow: React.FunctionComponent<TableCellRowProps> = (props) => {
|
||||
const {id, cellDef} = props;
|
||||
const {blockId, cellDef, editable} = props;
|
||||
const {strings: Locale} = useContext(UserContext);
|
||||
const displayValue = (cellDef as MultiplierCellDef).multiplier ??
|
||||
(cellDef as SuperkadiCellDef | BoolCellDef).score ?? Locale.general.nA;
|
||||
return (
|
||||
<Table.Row>
|
||||
<Table.Cell key={id}>
|
||||
{cellDef.label}
|
||||
</Table.Cell>
|
||||
<Table.Cell key={cellDef.label}>{cellDef.label}</Table.Cell>
|
||||
<Table.Cell>{Locale.rulesetsPage[cellDef.fieldType]}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{Locale.rulesetsPage[cellDef.fieldType]}
|
||||
</Table.Cell>
|
||||
<Table.Cell disabled={displayValue === Locale.general.nA}>
|
||||
{displayValue}
|
||||
<Grid>
|
||||
<Grid.Column width={12}>
|
||||
<List>
|
||||
{(cellDef as MultiplierCellDef).multiplier && (
|
||||
<List.Item>
|
||||
{Locale.rulesetsPage.multiplierPlaceholder +
|
||||
": " +
|
||||
(cellDef as MultiplierCellDef).multiplier}
|
||||
</List.Item>
|
||||
)}
|
||||
{(cellDef as BoolCellDef | SuperkadiCellDef).score && (
|
||||
<List.Item>
|
||||
{Locale.rulesetsPage.valuePlaceholder +
|
||||
": " +
|
||||
(cellDef as BoolCellDef | SuperkadiCellDef).score}
|
||||
</List.Item>
|
||||
)}
|
||||
{(cellDef as MultiplierCellDef).maxMultiples && (
|
||||
<List.Item>
|
||||
{Locale.rulesetsPage.maxMultiplesPlaceholder +
|
||||
": " +
|
||||
(cellDef as MultiplierCellDef).maxMultiples}
|
||||
</List.Item>
|
||||
)}
|
||||
{(cellDef as SuperkadiCellDef).maxSuperkadis && (
|
||||
<List.Item>
|
||||
{Locale.rulesetsPage.maxSuperkadisPlaceholder +
|
||||
": " +
|
||||
(cellDef as SuperkadiCellDef).maxSuperkadis}
|
||||
</List.Item>
|
||||
)}
|
||||
<List.Item disabled={true}>
|
||||
{cellDef.fieldType === FieldType.number && (
|
||||
<p>{Locale.general.nA}</p>
|
||||
)}
|
||||
</List.Item>
|
||||
</List>
|
||||
</Grid.Column>
|
||||
{editable && (
|
||||
<Grid.Column textAlign={"right"}>
|
||||
<Button
|
||||
size={"mini"}
|
||||
color={"red"}
|
||||
icon={"minus"}
|
||||
onClick={() => props.removeCell?.(cellDef.label, blockId)}
|
||||
/>
|
||||
</Grid.Column>
|
||||
)}
|
||||
</Grid>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import React, {useContext} from "react";
|
||||
import {
|
||||
BlockDef,
|
||||
BoolCellDef,
|
||||
CellDef,
|
||||
MultiplierCellDef,
|
||||
RulesetSchemaDto,
|
||||
SuperkadiCellDef
|
||||
} from "../Services/RulesetSchemaDto";
|
||||
import {Header, List, Table, TableBody} from "semantic-ui-react";
|
||||
import {CellDef, RulesetSchemaDto} from "../Services/RulesetSchemaDto";
|
||||
import {Table} from "semantic-ui-react";
|
||||
import UserContext from "../Contexts/UserContext";
|
||||
import Loading from "./Loading";
|
||||
import RulesetBlockTable from "./RulesetBlockTable";
|
||||
@@ -17,19 +10,20 @@ interface RulesetDisplayPanelProps {
|
||||
loading: boolean;
|
||||
editable?: boolean;
|
||||
onAddCell?: (cellDef: CellDef, blockId: string) => any;
|
||||
removeBlock?: (blockId: string) => any;
|
||||
removeCell?: (cellId: string, blockId: string) => any;
|
||||
}
|
||||
|
||||
const RulesetDisplayPanel: React.FunctionComponent<RulesetDisplayPanelProps> = (props) => {
|
||||
const {ruleset, loading, editable, onAddCell} = props;
|
||||
const {ruleset, loading, editable, onAddCell, removeBlock, removeCell} = props;
|
||||
const {strings: Locale} = useContext(UserContext);
|
||||
|
||||
if (loading) {
|
||||
return <Loading/>;
|
||||
}
|
||||
else {
|
||||
return <Loading />;
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<Table attached={true} celled={true}>
|
||||
<Table fixed={true} attached={true} celled={true}>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell>
|
||||
@@ -39,25 +33,25 @@ const RulesetDisplayPanel: React.FunctionComponent<RulesetDisplayPanelProps> = (
|
||||
{Locale.rulesetsPage.fieldTypeHeader}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell>
|
||||
{Locale.rulesetsPage.fieldValueHeader}
|
||||
{Locale.rulesetsPage.fieldAttributesHeader}
|
||||
</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
</Table>
|
||||
{Object.entries(ruleset.blocks).length > 0 ?
|
||||
Object.entries(ruleset.blocks).map(idAndBlock => (
|
||||
{Object.entries(ruleset.blocks).length > 0 ? (
|
||||
Object.entries(ruleset.blocks).map((idAndBlock) => (
|
||||
<RulesetBlockTable
|
||||
key={idAndBlock[0]}
|
||||
id={idAndBlock[0]}
|
||||
blockDef={idAndBlock[1]}
|
||||
editable={editable}
|
||||
onAddCell={onAddCell}
|
||||
removeBlock={removeBlock}
|
||||
removeCell={removeCell}
|
||||
/>
|
||||
)) : (
|
||||
<RulesetBlockTable
|
||||
id={"noBlocks"}
|
||||
blockDef={null}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<RulesetBlockTable id={"noBlocks"} blockDef={null} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, {ReactElement} from "react";
|
||||
import {Container, Grid, Header, List, Segment} from "semantic-ui-react";
|
||||
import {Button, Grid, Header, Modal} from "semantic-ui-react";
|
||||
import UserContext from "../Contexts/UserContext";
|
||||
import KadiStatsService from "../Services/KadiStatsService";
|
||||
import RulesetList from "./RulesetList";
|
||||
@@ -10,10 +10,13 @@ import CreateRulesetPanel from "./CreateRulesetPanel";
|
||||
interface RulesetsPageProps {}
|
||||
|
||||
interface RulesetsPageState {
|
||||
unsavedChanges: boolean;
|
||||
loading: boolean;
|
||||
addingNewRuleset: boolean;
|
||||
rulesets: RulesetSchemaDto[];
|
||||
selectedRulesetIndex: number;
|
||||
modalContinueCallback: () => any;
|
||||
showWarningModal: boolean;
|
||||
}
|
||||
|
||||
class RulesetsPage extends React.Component<RulesetsPageProps, RulesetsPageState> {
|
||||
@@ -21,6 +24,9 @@ class RulesetsPage extends React.Component<RulesetsPageProps, RulesetsPageState>
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
modalContinueCallback: () => {},
|
||||
showWarningModal: false,
|
||||
unsavedChanges: false,
|
||||
loading: true,
|
||||
addingNewRuleset: false,
|
||||
rulesets: [],
|
||||
@@ -38,28 +44,48 @@ class RulesetsPage extends React.Component<RulesetsPageProps, RulesetsPageState>
|
||||
}
|
||||
|
||||
onRulesetSelect(newRulesetId: string | "addNewRuleset") {
|
||||
if (newRulesetId === "addNewRuleset" || !this.state.unsavedChanges) {
|
||||
this.selectRuleset(newRulesetId);
|
||||
}
|
||||
else if (this.state.unsavedChanges) {
|
||||
this.warn(() => this.selectRuleset(newRulesetId));
|
||||
}
|
||||
}
|
||||
|
||||
warn(callback: () => any) {
|
||||
this.setState({showWarningModal: true, modalContinueCallback: callback});
|
||||
}
|
||||
|
||||
selectRuleset(newRulesetId: string | "addNewRuleset") {
|
||||
if (newRulesetId === "addNewRuleset") {
|
||||
this.setState({addingNewRuleset: true});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.setState({
|
||||
unsavedChanges: false,
|
||||
addingNewRuleset: false,
|
||||
selectedRulesetIndex: this.state.rulesets.findIndex(item => item.label === newRulesetId)
|
||||
selectedRulesetIndex: this.state.rulesets.findIndex(
|
||||
(item) => item.label === newRulesetId
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
submitNewRuleset(ruleset: RulesetSchemaDto) {
|
||||
onWarningModalActionClick() {
|
||||
const oldContinueCallback = this.state.modalContinueCallback;
|
||||
this.setState({showWarningModal: false, modalContinueCallback: () => {}}, () =>
|
||||
oldContinueCallback()
|
||||
);
|
||||
}
|
||||
|
||||
onWarningModalClose() {
|
||||
this.setState({showWarningModal: false, modalContinueCallback: () => {}});
|
||||
}
|
||||
|
||||
render(): ReactElement {
|
||||
const Locale = this.context.strings;
|
||||
return (
|
||||
<>
|
||||
<Header size={"huge"}>
|
||||
{Locale.rulesetsPage.title}
|
||||
</Header>
|
||||
<Header size={"huge"}>{Locale.rulesetsPage.title}</Header>
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
<Grid.Column width={4}>
|
||||
@@ -67,13 +93,14 @@ class RulesetsPage extends React.Component<RulesetsPageProps, RulesetsPageState>
|
||||
onItemChange={(r) => this.onRulesetSelect(r)}
|
||||
selectedItemIndex={this.state.selectedRulesetIndex}
|
||||
creatingRuleset={this.state.addingNewRuleset}
|
||||
rulesetNames={this.state.rulesets.map(ruleset => ruleset.label)}
|
||||
rulesetNames={this.state.rulesets.map((ruleset) => ruleset.label)}
|
||||
/>
|
||||
</Grid.Column>
|
||||
<Grid.Column width={12}>
|
||||
{this.state.addingNewRuleset ? (
|
||||
<CreateRulesetPanel
|
||||
onSubmitRuleset={(r) => this.submitNewRuleset(r)}
|
||||
onRulesetSave={() => this.setState({unsavedChanges: false})}
|
||||
onRulesetChange={() => this.setState({unsavedChanges: true})}
|
||||
/>
|
||||
) : (
|
||||
<RulesetDisplayPanel
|
||||
@@ -84,6 +111,26 @@ class RulesetsPage extends React.Component<RulesetsPageProps, RulesetsPageState>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
<Modal
|
||||
open={this.state.showWarningModal}
|
||||
dimmer={"inverted"}
|
||||
size={"tiny"}
|
||||
onClose={() => this.onWarningModalClose()}
|
||||
>
|
||||
<Modal.Header>Discard ruleset?</Modal.Header>
|
||||
<Modal.Content>Are you sure you want to discard the current ruleset? Any changes you've made will be lost.</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button color="black" onClick={() => this.onWarningModalClose()}>
|
||||
Return to editing
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => this.onWarningModalActionClick()}
|
||||
negative={true}
|
||||
>
|
||||
Discard ruleset
|
||||
</Button>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import React, {ReactNode} from "react";
|
||||
import {
|
||||
Container,
|
||||
Dropdown, DropdownItemProps, DropdownMenuProps,
|
||||
Dropdown,
|
||||
DropdownItemProps,
|
||||
Grid,
|
||||
GridColumn,
|
||||
GridRow,
|
||||
Header, Segment,
|
||||
Header,
|
||||
} from "semantic-ui-react";
|
||||
import UserContext from "../Contexts/UserContext";
|
||||
import {StatsTable} from "./StatsTable";
|
||||
import KadiStatsService from "../Services/KadiStatsService";
|
||||
import {ServiceError} from "../errors";
|
||||
import Loading from "./Loading";
|
||||
import KadiStatsServiceSingleton from "../Services/KadiStatsService";
|
||||
import {RulesetSchemaDto} from "../Services/RulesetSchemaDto";
|
||||
import {StatsDto} from "../Services/StatsDto";
|
||||
|
||||
interface StatsPageProps {}
|
||||
|
||||
@@ -49,17 +47,14 @@ class StatsPage extends React.Component<StatsPageProps, StatsPageState> {
|
||||
this.setState({loadingStats: true}, async () => {
|
||||
try {
|
||||
await this.loadStatsAndRulesetChoices();
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
if (e instanceof ServiceError) {
|
||||
this.setState({error: true});
|
||||
console.log(e);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
this.setState({loadingStats: false});
|
||||
}
|
||||
});
|
||||
@@ -70,11 +65,21 @@ class StatsPage extends React.Component<StatsPageProps, StatsPageState> {
|
||||
const rulesetIds = Object.keys(stats.pStats[0].stats.statsByRuleset);
|
||||
const schemas: Record<string, RulesetSchemaDto> = {};
|
||||
for (const rulesetId of rulesetIds) {
|
||||
schemas[rulesetId] = await KadiStatsService.getRulesetById(rulesetId);
|
||||
schemas[rulesetId] = await KadiStatsService.getRulesetById(
|
||||
rulesetId
|
||||
);
|
||||
}
|
||||
const rulesetChoices = rulesetIds.map(rulesetId =>
|
||||
({key: rulesetId, value: rulesetId, text: schemas[rulesetId].label}));
|
||||
this.setState({stats, rulesetChoices, rulesetSchemas: schemas, selectedRulesetId: rulesetIds[0]});
|
||||
const rulesetChoices = rulesetIds.map((rulesetId) => ({
|
||||
key: rulesetId,
|
||||
value: rulesetId,
|
||||
text: schemas[rulesetId].label,
|
||||
}));
|
||||
this.setState({
|
||||
stats,
|
||||
rulesetChoices,
|
||||
rulesetSchemas: schemas,
|
||||
selectedRulesetId: rulesetIds[0],
|
||||
});
|
||||
}
|
||||
|
||||
handleError(error: any): void {
|
||||
@@ -102,23 +107,33 @@ class StatsPage extends React.Component<StatsPageProps, StatsPageState> {
|
||||
loading={this.state.loadingStats}
|
||||
selection={true}
|
||||
options={this.state.rulesetChoices}
|
||||
defaultValue={this.state.rulesetChoices[0]?.value}
|
||||
onChange={(e, choice) => this.changeRulesetSelection(choice.value as string)}
|
||||
defaultValue={
|
||||
this.state.rulesetChoices[0]?.value
|
||||
}
|
||||
onChange={(e, choice) =>
|
||||
this.changeRulesetSelection(
|
||||
choice.value as string
|
||||
)
|
||||
}
|
||||
/>
|
||||
</GridColumn>
|
||||
</GridRow>
|
||||
<GridRow columns={1} style={{overflow: "auto"}}>
|
||||
<GridColumn>
|
||||
{this.state.error ?
|
||||
<p>{Locale.general.databaseError}</p> :
|
||||
this.state.loadingStats ?
|
||||
<Loading/> : (
|
||||
{this.state.error ? (
|
||||
<p>{Locale.general.databaseError}</p>
|
||||
) : this.state.loadingStats ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<StatsTable
|
||||
data={this.state.stats}
|
||||
displayedRulesetSchema={this.state.rulesetSchemas[this.state.selectedRulesetId]}
|
||||
/>
|
||||
)
|
||||
displayedRulesetSchema={
|
||||
this.state.rulesetSchemas[
|
||||
this.state.selectedRulesetId
|
||||
]
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</GridColumn>
|
||||
</GridRow>
|
||||
</Grid>
|
||||
|
||||
28
src/Services/KadiPlayerService.ts
Normal file
28
src/Services/KadiPlayerService.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import {RulesetSchemaDto} from "./RulesetSchemaDto";
|
||||
|
||||
class KadiPlayerService {
|
||||
private readonly players: PlayerDto[] = [];
|
||||
constructor() {}
|
||||
|
||||
async getPlayerById(id: string): Promise<PlayerDto> {
|
||||
if (!this.rulesets[id]) {
|
||||
await this.loadRulesetById(id);
|
||||
}
|
||||
return this.rulesets[id];
|
||||
}
|
||||
|
||||
async refreshStats(): Promise<void> {
|
||||
await this.loadStats();
|
||||
}
|
||||
|
||||
async getAllRulesets(): Promise<RulesetSchemaDto[]> {
|
||||
if (!this.allRulesetsLoaded) {
|
||||
await this.loadAllRulesets();
|
||||
}
|
||||
return Object.values(this.rulesets);
|
||||
}
|
||||
}
|
||||
|
||||
const KadiStatsServiceSingleton = new KadiStatsService();
|
||||
|
||||
export default KadiStatsServiceSingleton;
|
||||
@@ -3,10 +3,6 @@ import {SERVER_BASE_NAME} from "../index";
|
||||
import {StatsDto} from "./StatsDto";
|
||||
import {RulesetSchemaDto} from "./RulesetSchemaDto";
|
||||
|
||||
const dummyRulesets = [
|
||||
{"id":"DEFAULT_RULESET","label":"Standard Kadi Rules (en)","blocks":{"top":{"label":"Upper","hasBonus":true,"bonusScore":35,"bonusFor":63,"cells":{"aces":{"fieldType":"multiplierField","label":"Aces","multiplier":1,"maxMultiples":5},"twos":{"fieldType":"multiplierField","label":"Twos","multiplier":2,"maxMultiples":5},"threes":{"fieldType":"multiplierField","label":"Threes","multiplier":3,"maxMultiples":5},"fours":{"fieldType":"multiplierField","label":"Fours","multiplier":4,"maxMultiples":5},"fives":{"fieldType":"multiplierField","label":"Fives","multiplier":5,"maxMultiples":5},"sixes":{"fieldType":"multiplierField","label":"Sixes","multiplier":6,"maxMultiples":5}}},"bottom":{"label":"Lower","hasBonus":false,"cells":{"threeKind":{"fieldType":"numberField","label":"Three of a Kind"},"fourKind":{"fieldType":"numberField","label":"Four of a Kind"},"fullHouse":{"fieldType":"boolField","label":"Full House","score":25},"smlStraight":{"fieldType":"boolField","label":"Small Straight","score":30},"lgSraight":{"fieldType":"boolField","label":"Large Straight","score":40},"superkadi":{"fieldType":"superkadiField","label":"Super Kadis","score":50,"maxSuperkadis":5},"chance":{"fieldType":"numberField","label":"Chance"}}}}}
|
||||
];
|
||||
|
||||
class KadiStatsService {
|
||||
private userStats: StatsDto | null = null;
|
||||
private rulesets: Record<string, RulesetSchemaDto> = {};
|
||||
@@ -19,6 +15,18 @@ class KadiStatsService {
|
||||
return this.userStats;
|
||||
}
|
||||
|
||||
private async loadAllRulesets(): Promise<void> {
|
||||
const rulesetSchemas = (await axios.get(SERVER_BASE_NAME + "/api/rulesets/")).data.rulesets as RulesetSchemaDto[];
|
||||
console.log(rulesetSchemas);
|
||||
rulesetSchemas.forEach(schema => this.rulesets[schema.id] = schema);
|
||||
this.allRulesetsLoaded = true;
|
||||
}
|
||||
|
||||
private async loadRulesetById(id: string): Promise<void> {
|
||||
const rulesetSchema = (await axios.get(SERVER_BASE_NAME + "/api/ruleset/" + id)).data as RulesetSchemaDto;
|
||||
this.rulesets[rulesetSchema.id] = rulesetSchema;
|
||||
}
|
||||
|
||||
async getStats(): Promise<StatsDto> {
|
||||
if (this.userStats) {
|
||||
return this.userStats;
|
||||
@@ -29,32 +37,22 @@ class KadiStatsService {
|
||||
}
|
||||
|
||||
async getRulesetById(id: string): Promise<RulesetSchemaDto> {
|
||||
if (this.rulesets[id]) {
|
||||
if (!this.rulesets[id]) {
|
||||
await this.loadRulesetById(id);
|
||||
}
|
||||
return this.rulesets[id];
|
||||
}
|
||||
else {
|
||||
const rulesetSchema = (await axios.get(SERVER_BASE_NAME + "/api/ruleset/" + id)).data as RulesetSchemaDto;
|
||||
this.rulesets[rulesetSchema.id] = rulesetSchema;
|
||||
return this.rulesets[rulesetSchema.id];
|
||||
}
|
||||
}
|
||||
|
||||
async refreshStats(): Promise<void> {
|
||||
await this.loadStats();
|
||||
}
|
||||
|
||||
async getAllRulesets(): Promise<RulesetSchemaDto[]> {
|
||||
return dummyRulesets as RulesetSchemaDto[];
|
||||
if (this.allRulesetsLoaded) {
|
||||
if (!this.allRulesetsLoaded) {
|
||||
await this.loadAllRulesets();
|
||||
}
|
||||
return Object.values(this.rulesets);
|
||||
}
|
||||
else {
|
||||
const rulesetSchemas = (await axios.get(SERVER_BASE_NAME + "/api/rulesets/")).data as RulesetSchemaDto[];
|
||||
rulesetSchemas.forEach(schema => this.rulesets[schema.id] = schema);
|
||||
this.allRulesetsLoaded = true;
|
||||
return rulesetSchemas;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const KadiStatsServiceSingleton = new KadiStatsService();
|
||||
|
||||
6
src/Services/PlayerDto.ts
Normal file
6
src/Services/PlayerDto.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
interface PlayerMongoData {
|
||||
id: string;
|
||||
nick: string;
|
||||
}
|
||||
|
||||
export default PlayerDto;
|
||||
@@ -1,3 +1,5 @@
|
||||
import {FieldType} from "../enums";
|
||||
|
||||
export interface RulesetSchemaDto {
|
||||
id: string;
|
||||
label: string;
|
||||
|
||||
@@ -66,7 +66,7 @@ export const IntlStrings = {
|
||||
blocksHeader: "Blocks",
|
||||
fieldLabelHeader: "Label",
|
||||
fieldTypeHeader: "Type",
|
||||
fieldValueHeader: "Value",
|
||||
fieldAttributesHeader: "Attributes",
|
||||
superkadiField: "Superkadi",
|
||||
multiplierField: "Multiplier Cell",
|
||||
boolField: "Boolean Cell",
|
||||
@@ -84,9 +84,12 @@ export const IntlStrings = {
|
||||
cellNamePlaceholder: "My New Cell",
|
||||
fieldTypePlaceholder: "Select a field type",
|
||||
multiplierPlaceholder: "Multiplier",
|
||||
maxMultiplesPlaceholder: "Max of kind",
|
||||
maxMultiplesPlaceholder: "Max Multiples",
|
||||
maxSuperkadisPlaceholder: "Max Superkadis",
|
||||
valuePlaceholder: "Value",
|
||||
submit: "Submit",
|
||||
save: "Save",
|
||||
extraCellSettingsInfo: "No attributes available",
|
||||
newCell: "New Cell",
|
||||
},
|
||||
friendsPage: {
|
||||
title: "Friends",
|
||||
@@ -135,7 +138,7 @@ export const IntlStrings = {
|
||||
blocksHeader: "Blöcke",
|
||||
fieldLabelHeader: "Name",
|
||||
fieldTypeHeader: "Typ",
|
||||
fieldValueHeader: "Wert",
|
||||
fieldAttributesHeader: "Eigenschaften",
|
||||
superkadiField: "Superkadi",
|
||||
multiplierField: "Multiplikator-Feld",
|
||||
boolField: "Bool'sches Feld",
|
||||
@@ -155,8 +158,11 @@ export const IntlStrings = {
|
||||
fieldTypePlaceholder: "Feldtyp auswählen",
|
||||
multiplierPlaceholder: "Multiplikator",
|
||||
maxMultiplesPlaceholder: "Max. Anzahl",
|
||||
maxSuperkadisPlaceholder: "Max. Anzahl",
|
||||
valuePlaceholder: "Wert",
|
||||
submit: "Fertig",
|
||||
save: "Speichern",
|
||||
extraCellSettingsInfo: "Keine Eigenschaften vorhanden",
|
||||
newCell: "Neues Feld",
|
||||
},
|
||||
friendsPage: {
|
||||
title: "Freunde",
|
||||
@@ -205,7 +211,7 @@ export const IntlStrings = {
|
||||
blocksHeader: "===TRANSLATE ME===",
|
||||
fieldLabelHeader: "===TRANSLATE ME===",
|
||||
fieldTypeHeader: "===TRANSLATE ME===",
|
||||
fieldValueHeader: "===TRANSLATE ME===",
|
||||
fieldAttributesHeader: "===TRANSLATE ME===",
|
||||
superkadiField: "===TRANSLATE ME===",
|
||||
multiplierField: "===TRANSLATE ME===",
|
||||
boolField: "===TRANSLATE ME===",
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint-react"
|
||||
"tslint-react",
|
||||
"tslint-config-prettier"
|
||||
],
|
||||
"jsRules": {
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user