This commit is contained in:
Daniel Ledda
2020-12-24 12:37:26 +01:00
parent 602f9fd3f5
commit 4b409083fd
16 changed files with 760 additions and 357 deletions

6
package-lock.json generated
View File

@@ -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": { "tslint-react": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/tslint-react/-/tslint-react-4.2.0.tgz", "resolved": "https://registry.npmjs.org/tslint-react/-/tslint-react-4.2.0.tgz",

View File

@@ -12,6 +12,11 @@
"start": "webpack-dev-server --mode development", "start": "webpack-dev-server --mode development",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"prettier": {
"tabWidth": 4,
"bracketSpacing": false,
"printWidth": 100
},
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.8.4", "@babel/cli": "^7.8.4",
"@babel/core": "^7.9.6", "@babel/core": "^7.9.6",
@@ -34,6 +39,7 @@
"style-loader": "^1.2.1", "style-loader": "^1.2.1",
"ts-loader": "^7.0.3", "ts-loader": "^7.0.3",
"tslint": "^6.1.1", "tslint": "^6.1.1",
"tslint-config-prettier": "^1.18.0",
"tslint-react": "^4.2.0", "tslint-react": "^4.2.0",
"typescript": "^3.8.3", "typescript": "^3.8.3",
"webpack": "^4.43.0", "webpack": "^4.43.0",

View File

@@ -1,138 +1,228 @@
import React, {useContext, useState} from "react"; import React, {useContext, useState} from "react";
import {CellDef, RulesetSchemaDto} from "../Services/RulesetSchemaDto";
import { import {
Button, BlockDef,
Checkbox, BonusBlockDef,
Form, CellDef,
Header, NoBonusBlockDef,
Segment, RulesetSchemaDto,
Table } from "../Services/RulesetSchemaDto";
} from "semantic-ui-react"; import {Button, Container, Grid, Header, Segment} from "semantic-ui-react";
import UserContext from "../Contexts/UserContext"; import UserContext from "../Contexts/UserContext";
import RulesetBlockTable from "./RulesetBlockTable";
import RulesetDisplayPanel from "./RulesetDisplayPanel"; import RulesetDisplayPanel from "./RulesetDisplayPanel";
import NewBlockForm from "./NewBlockForm";
import axios from "axios";
import {SERVER_BASE_NAME} from "../index";
import EditableHeader from "./EditableHeader";
interface RulesetDisplayPanelProps { interface RulesetDisplayPanelProps {
onSubmitRuleset: (ruleset: RulesetSchemaDto) => any; onRulesetSave: () => any;
onRulesetChange: () => any;
} }
const CreateRulesetPanel: React.FunctionComponent<RulesetDisplayPanelProps> = (props) => { interface RulesetDisplayPanelState {
const {onSubmitRuleset} = props; saving: boolean;
const {strings: Locale} = useContext(UserContext); currentRulesetBuild: RulesetSchemaDto;
const [currentRulesetBuild, updateCurrentRulesetBuild] = useState<RulesetSchemaDto>({ newBlockInput: {
id: "", label: string;
label: Locale.rulesetsPage.newRuleset, hasBonus: boolean;
blocks: {}, bonusFor: number;
}); bonusScore: number;
const [newBlockInput, updateNewBlockInput] = useState({ };
label: "", }
hasBonus: false,
bonusScore: 35,
bonusFor: 63,
cells: {},
});
const handleAddBlock = () => { class CreateRulesetPanel extends React.Component<
if (newBlockInput.label) { RulesetDisplayPanelProps,
updateCurrentRulesetBuild({ RulesetDisplayPanelState
...currentRulesetBuild, > {
blocks: { constructor(props: RulesetDisplayPanelProps) {
...currentRulesetBuild.blocks, super(props);
[newBlockInput.label]: { this.state = {
...newBlockInput, saving: false,
bonusFor: newBlockInput.hasBonus ? newBlockInput.bonusFor : undefined, currentRulesetBuild: {
bonusScore: newBlockInput.hasBonus ? newBlockInput.bonusScore : undefined, id: "",
}, label: "",
} blocks: {},
},
newBlockInput: {
label: "",
hasBonus: false,
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) => { handleAddBlock() {
if (cellDef.label) { if (this.state.newBlockInput.label) {
updateCurrentRulesetBuild({ const blockToSave = {
...currentRulesetBuild, ...this.state.newBlockInput,
blocks: { cells: {},
...currentRulesetBuild.blocks, };
[blockId]: { if (!this.state.newBlockInput.hasBonus) {
...currentRulesetBuild.blocks[blockId], delete blockToSave.bonusScore;
cells: { delete blockToSave.bonusFor;
...currentRulesetBuild.blocks[blockId].cells, }
[cellDef.label]: cellDef this.state.currentRulesetBuild.blocks[blockToSave.label] = blockToSave;
} this.forceUpdate();
}
}
})
} }
}; this.props.onRulesetChange();
}
return ( handleAddCell(cellDef: CellDef, blockId: string) {
<> if (cellDef.label) {
<Header size={"tiny"}> this.state.currentRulesetBuild.blocks[blockId].cells[cellDef.label] = cellDef;
{Locale.rulesetsPage.newRuleset} this.forceUpdate();
</Header> }
<RulesetDisplayPanel this.props.onRulesetChange();
ruleset={currentRulesetBuild} }
editable={true}
loading={false} handleRulesetNameChange(newLabel: string) {
onAddCell={handleAddCell} this.setState({
/> currentRulesetBuild: {
<Header size={"tiny"}> ...this.state.currentRulesetBuild,
{Locale.rulesetsPage.newBlock} label: newLabel,
</Header> id: newLabel,
<Segment> },
<Form> });
<Form.Field> }
<label>{Locale.rulesetsPage.blockName + ": "}</label>
<input handleBlockNameChange(newName: string) {
placeholder={Locale.rulesetsPage.blockNamePlaceholder} this.setState({
onChange={(e) => updateNewBlockInput({...newBlockInput, label: e.target.value})} newBlockInput: {
/> ...this.state.newBlockInput,
</Form.Field> label: newName,
<Form.Field> },
<Checkbox });
toggle={true} }
onChange={(e, c) => updateNewBlockInput({...newBlockInput, hasBonus: c.checked ?? false})}
label={Locale.rulesetsPage.bonus} handleToggleBlockBonus(newHasBonus: boolean) {
/> this.setState({
</Form.Field> newBlockInput: {
{newBlockInput.hasBonus && ( ...this.state.newBlockInput,
<> hasBonus: newHasBonus,
<Form.Field width={4}> },
<label>{Locale.rulesetsPage.bonusScore + ": "}</label> });
<input }
type={"number"}
value={newBlockInput.bonusScore} handleBonusScoreChange(newBonusScore: number) {
onChange={(e) => updateNewBlockInput({...newBlockInput, bonusScore: Number(e.target.value)})} this.setState({
/> newBlockInput: {
</Form.Field> ...this.state.newBlockInput,
<Form.Field width={4}> bonusScore: newBonusScore,
<label>{Locale.rulesetsPage.bonusThreshold + ": "}</label> },
<input });
type={"number"} }
value={newBlockInput.bonusFor}
onChange={(e) => updateNewBlockInput({...newBlockInput, bonusFor: Number(e.target.value)})} handleBonusForChange(newBonusFor: number) {
/> this.setState({
</Form.Field> newBlockInput: {
</> ...this.state.newBlockInput,
)} bonusFor: newBonusFor,
<Form.Field> },
<Button });
onClick={handleAddBlock} }
>
{Locale.rulesetsPage.addBlock} blocksExistAndAllHaveCells(): boolean {
</Button> return (
</Form.Field> Object.values(this.state.currentRulesetBuild.blocks).length > 0 &&
</Form> Object.values(this.state.currentRulesetBuild.blocks).reduce(
</Segment> (prev: boolean, blockDef: BlockDef) =>
<Button Object.values(blockDef.cells).length > 0 && prev,
fluid={true} true
onClick={() => onSubmitRuleset(currentRulesetBuild)} )
> );
{Locale.rulesetsPage.submit} }
</Button>
</> 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 (
<Grid>
<Grid.Column>
<Grid.Row>
<Segment basic={true}>
<EditableHeader
onTextEdit={(t) => this.handleRulesetNameChange(t)}
text={this.state.currentRulesetBuild.label}
headerProps={{size: "small"}}
/>
<RulesetDisplayPanel
ruleset={this.state.currentRulesetBuild}
editable={true}
loading={false}
removeBlock={(id) => this.removeBlock(id)}
removeCell={(cid, bid) => this.removeCell(cid, bid)}
onAddCell={(c, b) => this.handleAddCell(c, b)}
/>
</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
loading={this.state.saving}
primary={true}
disabled={!this.blocksExistAndAllHaveCells()}
onClick={() => this.submitNewRuleset()}
>
{Locale.rulesetsPage.save}
</Button>
</Segment>
</Grid.Column>
</Grid.Column>
</Grid>
);
}
}
CreateRulesetPanel.contextType = UserContext;
export default CreateRulesetPanel; export default CreateRulesetPanel;

View 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;

View 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;

View File

@@ -1,7 +1,7 @@
import {BlockDef, CellDef} from "../Services/RulesetSchemaDto"; import {BlockDef, CellDef} from "../Services/RulesetSchemaDto";
import React, {useContext, useState} from "react"; import React, {useContext, useState} from "react";
import RulesetCellTableRow from "./RulesetCellTableRow"; 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 UserContext from "../Contexts/UserContext";
import {FieldType} from "../enums"; import {FieldType} from "../enums";
@@ -10,32 +10,28 @@ interface RulesetBlockTableProps {
blockDef: BlockDef | null; blockDef: BlockDef | null;
editable?: boolean; editable?: boolean;
onAddCell?: (cellDef: CellDef, blockId: string) => any; onAddCell?: (cellDef: CellDef, blockId: string) => any;
removeBlock?: (blockId: string) => any;
removeCell?: (cellId: string, blockId: string) => any;
} }
const RulesetBlockTable: React.FunctionComponent<RulesetBlockTableProps> = (props) => { const RulesetBlockTable: React.FunctionComponent<RulesetBlockTableProps> = (props) => {
const {id, blockDef, editable, onAddCell} = props; const {id, blockDef, editable, onAddCell, removeCell} = props;
const [currentCellInput, updateCurrentCellInput] = useState({ const [currentCellInput, updateCurrentCellInput] = useState({
label: "", label: "",
maxMultiples: 0, maxMultiples: 1,
multiplier: 0, multiplier: 1,
fieldType: FieldType.number, fieldType: undefined,
score: 0, score: 25,
maxSuperkadis: 5,
}); });
const {strings: Locale} = useContext(UserContext); const {strings: Locale} = useContext(UserContext);
if (!blockDef) { if (!blockDef) {
return ( return (
<Table <Table attached={true} fixed={true} celled={true}>
attached={true}
fixed={true}
celled={true}
>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
<Table.HeaderCell <Table.HeaderCell colSpan={"3"} textAlign={"center"}>
colSpan={"3"}
textAlign={"center"}
>
{Locale.rulesetsPage.noBlocks} {Locale.rulesetsPage.noBlocks}
</Table.HeaderCell> </Table.HeaderCell>
</Table.Row> </Table.Row>
@@ -51,151 +47,200 @@ const RulesetBlockTable: React.FunctionComponent<RulesetBlockTableProps> = (prop
subText += `${Locale.general.yes}, subText += `${Locale.general.yes},
${Locale.rulesetsPage.bonusScore}: ${blockDef.bonusScore}, ${Locale.rulesetsPage.bonusScore}: ${blockDef.bonusScore},
${Locale.rulesetsPage.bonusThreshold}: ${blockDef.bonusFor}`; ${Locale.rulesetsPage.bonusThreshold}: ${blockDef.bonusFor}`;
} } else {
else {
subText += Locale.general.no; subText += Locale.general.no;
} }
const fieldTypeOptions: DropdownItemProps[] = [ const fieldTypeOptions: DropdownItemProps[] = [
{value: FieldType.bool, key: FieldType.bool, text: Locale.rulesetsPage[FieldType.bool]}, {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.multiplier,
{value: FieldType.superkadi, key: FieldType.superkadi, text: Locale.rulesetsPage[FieldType.superkadi]}, 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 onChangeFieldTypeDropdown = (newFieldType: FieldType) => {
const newCell = { const newCell: any = Object.assign({}, currentCellInput);
...currentCellInput, newCell.fieldType = newFieldType;
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;
}
updateCurrentCellInput(newCell); 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 ( return (
<Table <Table attached={true} fixed={true} celled={true}>
attached={true}
fixed={true}
celled={true}
>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
<Table.HeaderCell <Table.HeaderCell verticalAlign={"middle"} colSpan={"3"} textAlign={"center"}>
colSpan={"3"}
textAlign={"center"}
>
{blockDef.label} {blockDef.label}
{editable && (
<Button
floated={"right"}
color={"red"}
icon={"minus"}
size={"mini"}
onClick={() => props.removeBlock?.(id)}
/>
)}
</Table.HeaderCell> </Table.HeaderCell>
</Table.Row> </Table.Row>
</Table.Header> </Table.Header>
<Table.Body> <Table.Body>
{idAndCellList.map(idAndCell => ( {idAndCellList.map((idAndCell) => (
<RulesetCellTableRow <RulesetCellTableRow
key={idAndCell[0]} key={idAndCell[0]}
id={idAndCell[0]} blockId={id}
cellDef={idAndCell[1]} cellDef={idAndCell[1]}
editable={editable}
removeCell={removeCell}
/> />
))} ))}
{editable && ( {editable && (
<Table.Row> <Table.Row>
<Table.Cell <Table.Cell>
style={{overflow: "visible"}} <Input
colSpan={"3"} fluid={true}
> value={currentCellInput.label}
<Grid> placeholder={Locale.rulesetsPage.cellNamePlaceholder}
<Grid.Row onChange={(e) =>
columns={3} updateCurrentCellInput({
verticalAlign={"middle"} ...currentCellInput,
> label: e.target.value,
<Grid.Column width={4}> })
<Input }
fluid={true} />
value={currentCellInput.label} </Table.Cell>
label={Locale.rulesetsPage.cellName} <Table.Cell style={{overflow: "visible"}}>
placeholder={Locale.rulesetsPage.cellNamePlaceholder} <Dropdown
onChange={(e) => updateCurrentCellInput({...currentCellInput, label: e.target.value})} basic={true}
/> placeholder={Locale.rulesetsPage.fieldTypePlaceholder}
</Grid.Column> onChange={(e, c) => onChangeFieldTypeDropdown(c.value as FieldType)}
<Grid.Column width={4}> options={fieldTypeOptions}
<Dropdown button={true}
selectedLabel={currentCellInput.fieldType} />
placeholder={Locale.rulesetsPage.fieldTypePlaceholder} </Table.Cell>
onChange={(e, c) => onChangeDropdown(c.value as FieldType)} <Table.Cell>
options={fieldTypeOptions} <Grid divided={true}>
button={true} <Grid.Column width={12}>
/> {currentCellInput.fieldType === undefined ||
</Grid.Column> currentCellInput.fieldType === FieldType.number ? (
<Grid.Column width={4}> Locale.rulesetsPage.extraCellSettingsInfo
{currentCellInput.fieldType === FieldType.multiplier && ( ) : (
<Input <List>
label={Locale.rulesetsPage.multiplierPlaceholder} {(currentCellInput.fieldType === FieldType.bool ||
fluid={true} currentCellInput.fieldType ===
type={"number"} FieldType.superkadi) && (
value={currentCellInput.multiplier} <List.Item>
onChange={(e) => updateCurrentCellInput( <Input
{...currentCellInput, multiplier: Number(e.target.value)})} label={Locale.rulesetsPage.valuePlaceholder}
/> fluid={true}
)} type={"number"}
{(currentCellInput.fieldType === FieldType.superkadi value={currentCellInput.score}
|| currentCellInput.fieldType === FieldType.multiplier) && ( onChange={(e) =>
<Input updateCurrentCellInput({
label={Locale.rulesetsPage.maxMultiplesPlaceholder} ...currentCellInput,
fluid={true} score: Number(e.target.value),
type={"number"} })
value={currentCellInput.maxMultiples} }
onChange={(e) => updateCurrentCellInput({ />
...currentCellInput, </List.Item>
maxMultiples: Number(e.target.value), )}
})} {currentCellInput.fieldType ===
/> FieldType.multiplier && (
)} <List.Item>
{(currentCellInput.fieldType === FieldType.bool <Input
|| currentCellInput.fieldType === FieldType.superkadi label={
|| currentCellInput.fieldType === FieldType.number) && ( Locale.rulesetsPage
<Input .multiplierPlaceholder
label={Locale.rulesetsPage.valuePlaceholder} }
fluid={true} fluid={true}
type={"number"} type={"number"}
value={currentCellInput.score} value={currentCellInput.multiplier}
onChange={(e) => updateCurrentCellInput( onChange={(e) =>
{...currentCellInput, score: Number(e.target.value)})} updateCurrentCellInput({
/> ...currentCellInput,
)} multiplier: Number(e.target.value),
</Grid.Column> })
<Grid.Column }
width={4} />
textAlign={"right"} </List.Item>
> )}
<Button {(currentCellInput.fieldType === FieldType.superkadi ||
onClick={() => { currentCellInput.fieldType ===
if (onAddCell) { FieldType.multiplier) && (
onAddCell(currentCellInput, id) <List.Item>
} <Input
}} label={
> Locale.rulesetsPage
{Locale.rulesetsPage.addCell} .maxMultiplesPlaceholder
</Button> }
</Grid.Column> fluid={true}
</Grid.Row> type={"number"}
value={currentCellInput.maxMultiples}
onChange={(e) =>
updateCurrentCellInput({
...currentCellInput,
maxMultiples: Number(
e.target.value
),
})
}
/>
</List.Item>
)}
</List>
)}
</Grid.Column>
<Grid.Column width={4}>
<Button
basic={true}
floated={"right"}
disabled={
currentCellInput.label === "" ||
!currentCellInput.fieldType
}
icon={"add"}
onClick={handleAddCell}
/>
</Grid.Column>
</Grid> </Grid>
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>
)} )}
<Table.Row> <Table.Row>
<Table.Cell <Table.Cell colSpan={"3"} textAlign={"center"}>
colSpan={"3"}
textAlign={"center"}
>
{subText} {subText}
</Table.Cell> </Table.Cell>
</Table.Row> </Table.Row>

View File

@@ -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 React, {useContext} from "react";
import UserContext from "../Contexts/UserContext"; 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 { interface TableCellRowProps {
id: string; blockId: string;
cellDef: CellDef; cellDef: CellDef;
editable?: boolean;
removeCell?: (cellId: string, blockId: string) => any;
} }
const RulesetCellTableRow: React.FunctionComponent<TableCellRowProps> = (props) => { const RulesetCellTableRow: React.FunctionComponent<TableCellRowProps> = (props) => {
const {id, cellDef} = props; const {blockId, cellDef, editable} = props;
const {strings: Locale} = useContext(UserContext); const {strings: Locale} = useContext(UserContext);
const displayValue = (cellDef as MultiplierCellDef).multiplier ??
(cellDef as SuperkadiCellDef | BoolCellDef).score ?? Locale.general.nA;
return ( return (
<Table.Row> <Table.Row>
<Table.Cell key={id}> <Table.Cell key={cellDef.label}>{cellDef.label}</Table.Cell>
{cellDef.label} <Table.Cell>{Locale.rulesetsPage[cellDef.fieldType]}</Table.Cell>
</Table.Cell>
<Table.Cell> <Table.Cell>
{Locale.rulesetsPage[cellDef.fieldType]} <Grid>
</Table.Cell> <Grid.Column width={12}>
<Table.Cell disabled={displayValue === Locale.general.nA}> <List>
{displayValue} {(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.Cell>
</Table.Row> </Table.Row>
); );

View File

@@ -1,13 +1,6 @@
import React, {useContext} from "react"; import React, {useContext} from "react";
import { import {CellDef, RulesetSchemaDto} from "../Services/RulesetSchemaDto";
BlockDef, import {Table} from "semantic-ui-react";
BoolCellDef,
CellDef,
MultiplierCellDef,
RulesetSchemaDto,
SuperkadiCellDef
} from "../Services/RulesetSchemaDto";
import {Header, List, Table, TableBody} from "semantic-ui-react";
import UserContext from "../Contexts/UserContext"; import UserContext from "../Contexts/UserContext";
import Loading from "./Loading"; import Loading from "./Loading";
import RulesetBlockTable from "./RulesetBlockTable"; import RulesetBlockTable from "./RulesetBlockTable";
@@ -17,19 +10,20 @@ interface RulesetDisplayPanelProps {
loading: boolean; loading: boolean;
editable?: boolean; editable?: boolean;
onAddCell?: (cellDef: CellDef, blockId: string) => any; onAddCell?: (cellDef: CellDef, blockId: string) => any;
removeBlock?: (blockId: string) => any;
removeCell?: (cellId: string, blockId: string) => any;
} }
const RulesetDisplayPanel: React.FunctionComponent<RulesetDisplayPanelProps> = (props) => { 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); const {strings: Locale} = useContext(UserContext);
if (loading) { if (loading) {
return <Loading/>; return <Loading />;
} } else {
else {
return ( return (
<> <>
<Table attached={true} celled={true}> <Table fixed={true} attached={true} celled={true}>
<Table.Header> <Table.Header>
<Table.Row> <Table.Row>
<Table.HeaderCell> <Table.HeaderCell>
@@ -39,25 +33,25 @@ const RulesetDisplayPanel: React.FunctionComponent<RulesetDisplayPanelProps> = (
{Locale.rulesetsPage.fieldTypeHeader} {Locale.rulesetsPage.fieldTypeHeader}
</Table.HeaderCell> </Table.HeaderCell>
<Table.HeaderCell> <Table.HeaderCell>
{Locale.rulesetsPage.fieldValueHeader} {Locale.rulesetsPage.fieldAttributesHeader}
</Table.HeaderCell> </Table.HeaderCell>
</Table.Row> </Table.Row>
</Table.Header> </Table.Header>
</Table> </Table>
{Object.entries(ruleset.blocks).length > 0 ? {Object.entries(ruleset.blocks).length > 0 ? (
Object.entries(ruleset.blocks).map(idAndBlock => ( Object.entries(ruleset.blocks).map((idAndBlock) => (
<RulesetBlockTable <RulesetBlockTable
key={idAndBlock[0]} key={idAndBlock[0]}
id={idAndBlock[0]} id={idAndBlock[0]}
blockDef={idAndBlock[1]} blockDef={idAndBlock[1]}
editable={editable} editable={editable}
onAddCell={onAddCell} onAddCell={onAddCell}
removeBlock={removeBlock}
removeCell={removeCell}
/> />
)) : ( ))
<RulesetBlockTable ) : (
id={"noBlocks"} <RulesetBlockTable id={"noBlocks"} blockDef={null} />
blockDef={null}
/>
)} )}
</> </>
); );

View File

@@ -1,5 +1,5 @@
import React, {ReactElement} from "react"; 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 UserContext from "../Contexts/UserContext";
import KadiStatsService from "../Services/KadiStatsService"; import KadiStatsService from "../Services/KadiStatsService";
import RulesetList from "./RulesetList"; import RulesetList from "./RulesetList";
@@ -10,10 +10,13 @@ import CreateRulesetPanel from "./CreateRulesetPanel";
interface RulesetsPageProps {} interface RulesetsPageProps {}
interface RulesetsPageState { interface RulesetsPageState {
unsavedChanges: boolean;
loading: boolean; loading: boolean;
addingNewRuleset: boolean; addingNewRuleset: boolean;
rulesets: RulesetSchemaDto[]; rulesets: RulesetSchemaDto[];
selectedRulesetIndex: number; selectedRulesetIndex: number;
modalContinueCallback: () => any;
showWarningModal: boolean;
} }
class RulesetsPage extends React.Component<RulesetsPageProps, RulesetsPageState> { class RulesetsPage extends React.Component<RulesetsPageProps, RulesetsPageState> {
@@ -21,6 +24,9 @@ class RulesetsPage extends React.Component<RulesetsPageProps, RulesetsPageState>
super(props); super(props);
this.state = { this.state = {
modalContinueCallback: () => {},
showWarningModal: false,
unsavedChanges: false,
loading: true, loading: true,
addingNewRuleset: false, addingNewRuleset: false,
rulesets: [], rulesets: [],
@@ -38,28 +44,48 @@ class RulesetsPage extends React.Component<RulesetsPageProps, RulesetsPageState>
} }
onRulesetSelect(newRulesetId: string | "addNewRuleset") { 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") { if (newRulesetId === "addNewRuleset") {
this.setState({addingNewRuleset: true}); this.setState({addingNewRuleset: true});
} } else {
else {
this.setState({ this.setState({
unsavedChanges: false,
addingNewRuleset: 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 { render(): ReactElement {
const Locale = this.context.strings; const Locale = this.context.strings;
return ( return (
<> <>
<Header size={"huge"}> <Header size={"huge"}>{Locale.rulesetsPage.title}</Header>
{Locale.rulesetsPage.title}
</Header>
<Grid> <Grid>
<Grid.Row> <Grid.Row>
<Grid.Column width={4}> <Grid.Column width={4}>
@@ -67,13 +93,14 @@ class RulesetsPage extends React.Component<RulesetsPageProps, RulesetsPageState>
onItemChange={(r) => this.onRulesetSelect(r)} onItemChange={(r) => this.onRulesetSelect(r)}
selectedItemIndex={this.state.selectedRulesetIndex} selectedItemIndex={this.state.selectedRulesetIndex}
creatingRuleset={this.state.addingNewRuleset} creatingRuleset={this.state.addingNewRuleset}
rulesetNames={this.state.rulesets.map(ruleset => ruleset.label)} rulesetNames={this.state.rulesets.map((ruleset) => ruleset.label)}
/> />
</Grid.Column> </Grid.Column>
<Grid.Column width={12}> <Grid.Column width={12}>
{this.state.addingNewRuleset ? ( {this.state.addingNewRuleset ? (
<CreateRulesetPanel <CreateRulesetPanel
onSubmitRuleset={(r) => this.submitNewRuleset(r)} onRulesetSave={() => this.setState({unsavedChanges: false})}
onRulesetChange={() => this.setState({unsavedChanges: true})}
/> />
) : ( ) : (
<RulesetDisplayPanel <RulesetDisplayPanel
@@ -84,6 +111,26 @@ class RulesetsPage extends React.Component<RulesetsPageProps, RulesetsPageState>
</Grid.Column> </Grid.Column>
</Grid.Row> </Grid.Row>
</Grid> </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>
</> </>
); );
} }

View File

@@ -1,20 +1,18 @@
import React, {ReactNode} from "react"; import React, {ReactNode} from "react";
import { import {
Container, Dropdown,
Dropdown, DropdownItemProps, DropdownMenuProps, DropdownItemProps,
Grid, Grid,
GridColumn, GridColumn,
GridRow, GridRow,
Header, Segment, Header,
} from "semantic-ui-react"; } from "semantic-ui-react";
import UserContext from "../Contexts/UserContext"; import UserContext from "../Contexts/UserContext";
import {StatsTable} from "./StatsTable"; import {StatsTable} from "./StatsTable";
import KadiStatsService from "../Services/KadiStatsService"; import KadiStatsService from "../Services/KadiStatsService";
import {ServiceError} from "../errors"; import {ServiceError} from "../errors";
import Loading from "./Loading"; import Loading from "./Loading";
import KadiStatsServiceSingleton from "../Services/KadiStatsService";
import {RulesetSchemaDto} from "../Services/RulesetSchemaDto"; import {RulesetSchemaDto} from "../Services/RulesetSchemaDto";
import {StatsDto} from "../Services/StatsDto";
interface StatsPageProps {} interface StatsPageProps {}
@@ -49,17 +47,14 @@ class StatsPage extends React.Component<StatsPageProps, StatsPageState> {
this.setState({loadingStats: true}, async () => { this.setState({loadingStats: true}, async () => {
try { try {
await this.loadStatsAndRulesetChoices(); await this.loadStatsAndRulesetChoices();
} } catch (e) {
catch (e) {
if (e instanceof ServiceError) { if (e instanceof ServiceError) {
this.setState({error: true}); this.setState({error: true});
console.log(e); console.log(e);
} } else {
else {
throw e; throw e;
} }
} } finally {
finally {
this.setState({loadingStats: false}); 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 rulesetIds = Object.keys(stats.pStats[0].stats.statsByRuleset);
const schemas: Record<string, RulesetSchemaDto> = {}; const schemas: Record<string, RulesetSchemaDto> = {};
for (const rulesetId of rulesetIds) { for (const rulesetId of rulesetIds) {
schemas[rulesetId] = await KadiStatsService.getRulesetById(rulesetId); schemas[rulesetId] = await KadiStatsService.getRulesetById(
rulesetId
);
} }
const rulesetChoices = rulesetIds.map(rulesetId => const rulesetChoices = rulesetIds.map((rulesetId) => ({
({key: rulesetId, value: rulesetId, text: schemas[rulesetId].label})); key: rulesetId,
this.setState({stats, rulesetChoices, rulesetSchemas: schemas, selectedRulesetId: rulesetIds[0]}); value: rulesetId,
text: schemas[rulesetId].label,
}));
this.setState({
stats,
rulesetChoices,
rulesetSchemas: schemas,
selectedRulesetId: rulesetIds[0],
});
} }
handleError(error: any): void { handleError(error: any): void {
@@ -102,23 +107,33 @@ class StatsPage extends React.Component<StatsPageProps, StatsPageState> {
loading={this.state.loadingStats} loading={this.state.loadingStats}
selection={true} selection={true}
options={this.state.rulesetChoices} options={this.state.rulesetChoices}
defaultValue={this.state.rulesetChoices[0]?.value} defaultValue={
onChange={(e, choice) => this.changeRulesetSelection(choice.value as string)} this.state.rulesetChoices[0]?.value
}
onChange={(e, choice) =>
this.changeRulesetSelection(
choice.value as string
)
}
/> />
</GridColumn> </GridColumn>
</GridRow> </GridRow>
<GridRow columns={1} style={{overflow: "auto"}}> <GridRow columns={1} style={{overflow: "auto"}}>
<GridColumn> <GridColumn>
{this.state.error ? {this.state.error ? (
<p>{Locale.general.databaseError}</p> : <p>{Locale.general.databaseError}</p>
this.state.loadingStats ? ) : this.state.loadingStats ? (
<Loading/> : ( <Loading />
<StatsTable ) : (
data={this.state.stats} <StatsTable
displayedRulesetSchema={this.state.rulesetSchemas[this.state.selectedRulesetId]} data={this.state.stats}
/> displayedRulesetSchema={
) this.state.rulesetSchemas[
} this.state.selectedRulesetId
]
}
/>
)}
</GridColumn> </GridColumn>
</GridRow> </GridRow>
</Grid> </Grid>

View 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;

View File

@@ -3,10 +3,6 @@ import {SERVER_BASE_NAME} from "../index";
import {StatsDto} from "./StatsDto"; import {StatsDto} from "./StatsDto";
import {RulesetSchemaDto} from "./RulesetSchemaDto"; 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 { class KadiStatsService {
private userStats: StatsDto | null = null; private userStats: StatsDto | null = null;
private rulesets: Record<string, RulesetSchemaDto> = {}; private rulesets: Record<string, RulesetSchemaDto> = {};
@@ -19,6 +15,18 @@ class KadiStatsService {
return this.userStats; 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> { async getStats(): Promise<StatsDto> {
if (this.userStats) { if (this.userStats) {
return this.userStats; return this.userStats;
@@ -29,14 +37,10 @@ class KadiStatsService {
} }
async getRulesetById(id: string): Promise<RulesetSchemaDto> { async getRulesetById(id: string): Promise<RulesetSchemaDto> {
if (this.rulesets[id]) { if (!this.rulesets[id]) {
return this.rulesets[id]; await this.loadRulesetById(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];
} }
return this.rulesets[id];
} }
async refreshStats(): Promise<void> { async refreshStats(): Promise<void> {
@@ -44,16 +48,10 @@ class KadiStatsService {
} }
async getAllRulesets(): Promise<RulesetSchemaDto[]> { 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;
} }
return Object.values(this.rulesets);
} }
} }

View File

@@ -0,0 +1,6 @@
interface PlayerMongoData {
id: string;
nick: string;
}
export default PlayerDto;

View File

@@ -1,3 +1,5 @@
import {FieldType} from "../enums";
export interface RulesetSchemaDto { export interface RulesetSchemaDto {
id: string; id: string;
label: string; label: string;

View File

@@ -66,7 +66,7 @@ export const IntlStrings = {
blocksHeader: "Blocks", blocksHeader: "Blocks",
fieldLabelHeader: "Label", fieldLabelHeader: "Label",
fieldTypeHeader: "Type", fieldTypeHeader: "Type",
fieldValueHeader: "Value", fieldAttributesHeader: "Attributes",
superkadiField: "Superkadi", superkadiField: "Superkadi",
multiplierField: "Multiplier Cell", multiplierField: "Multiplier Cell",
boolField: "Boolean Cell", boolField: "Boolean Cell",
@@ -84,9 +84,12 @@ export const IntlStrings = {
cellNamePlaceholder: "My New Cell", cellNamePlaceholder: "My New Cell",
fieldTypePlaceholder: "Select a field type", fieldTypePlaceholder: "Select a field type",
multiplierPlaceholder: "Multiplier", multiplierPlaceholder: "Multiplier",
maxMultiplesPlaceholder: "Max of kind", maxMultiplesPlaceholder: "Max Multiples",
maxSuperkadisPlaceholder: "Max Superkadis",
valuePlaceholder: "Value", valuePlaceholder: "Value",
submit: "Submit", save: "Save",
extraCellSettingsInfo: "No attributes available",
newCell: "New Cell",
}, },
friendsPage: { friendsPage: {
title: "Friends", title: "Friends",
@@ -135,7 +138,7 @@ export const IntlStrings = {
blocksHeader: "Blöcke", blocksHeader: "Blöcke",
fieldLabelHeader: "Name", fieldLabelHeader: "Name",
fieldTypeHeader: "Typ", fieldTypeHeader: "Typ",
fieldValueHeader: "Wert", fieldAttributesHeader: "Eigenschaften",
superkadiField: "Superkadi", superkadiField: "Superkadi",
multiplierField: "Multiplikator-Feld", multiplierField: "Multiplikator-Feld",
boolField: "Bool'sches Feld", boolField: "Bool'sches Feld",
@@ -155,8 +158,11 @@ export const IntlStrings = {
fieldTypePlaceholder: "Feldtyp auswählen", fieldTypePlaceholder: "Feldtyp auswählen",
multiplierPlaceholder: "Multiplikator", multiplierPlaceholder: "Multiplikator",
maxMultiplesPlaceholder: "Max. Anzahl", maxMultiplesPlaceholder: "Max. Anzahl",
maxSuperkadisPlaceholder: "Max. Anzahl",
valuePlaceholder: "Wert", valuePlaceholder: "Wert",
submit: "Fertig", save: "Speichern",
extraCellSettingsInfo: "Keine Eigenschaften vorhanden",
newCell: "Neues Feld",
}, },
friendsPage: { friendsPage: {
title: "Freunde", title: "Freunde",
@@ -205,7 +211,7 @@ export const IntlStrings = {
blocksHeader: "===TRANSLATE ME===", blocksHeader: "===TRANSLATE ME===",
fieldLabelHeader: "===TRANSLATE ME===", fieldLabelHeader: "===TRANSLATE ME===",
fieldTypeHeader: "===TRANSLATE ME===", fieldTypeHeader: "===TRANSLATE ME===",
fieldValueHeader: "===TRANSLATE ME===", fieldAttributesHeader: "===TRANSLATE ME===",
superkadiField: "===TRANSLATE ME===", superkadiField: "===TRANSLATE ME===",
multiplierField: "===TRANSLATE ME===", multiplierField: "===TRANSLATE ME===",
boolField: "===TRANSLATE ME===", boolField: "===TRANSLATE ME===",

View File

@@ -1,7 +1,8 @@
{ {
"defaultSeverity": "error", "defaultSeverity": "error",
"extends": [ "extends": [
"tslint-react" "tslint-react",
"tslint-config-prettier"
], ],
"jsRules": { "jsRules": {
}, },