From e1a6ba9a2d19d40d49e6a2070d1ebb3cf903e909 Mon Sep 17 00:00:00 2001 From: Bridget Bailey <10903271+bbailey27@users.noreply.github.com> Date: Sun, 17 Nov 2024 03:49:13 -0800 Subject: [PATCH 1/4] stateful button initial commit --- src/Components/DataEntry.jsx | 7 +--- src/Components/Options.jsx | 19 ++++++++++ .../Subcomponents/StatefulButton.jsx | 38 +++++++++++++++++++ 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 src/Components/Subcomponents/StatefulButton.jsx diff --git a/src/Components/DataEntry.jsx b/src/Components/DataEntry.jsx index 71482cb..5de30d5 100644 --- a/src/Components/DataEntry.jsx +++ b/src/Components/DataEntry.jsx @@ -3,6 +3,7 @@ import Options from './Options'; import Details from './Details'; import Algorithms from './Algorithms'; import Tables from './Tables'; +import StatefulButton from './Subcomponents/StatefulButton'; import { runOrganizer } from '../worker'; const defaultData = { @@ -203,14 +204,10 @@ class DataEntry extends Component { handleTablesChange={this.handleTablesChange}/> + alert('changed')} selected={true} name='change-tables' /> ); } } -//TODO render tables without isKidsTable option for now -/*TODO add Configuration and Constraints middle pane or bottom/top section: -* Runtime options: run once, get the best of x number of times, or maybe run until the conditions are met (with a max/timeout returning the best so far?) -* Constraints: conditions to define best (e.g. no playedWithCount over 3, minimize all playedWithCounts, no stayAtTableCount over 2) -*/ export default DataEntry; diff --git a/src/Components/Options.jsx b/src/Components/Options.jsx index e9852b4..42a4d5f 100644 --- a/src/Components/Options.jsx +++ b/src/Components/Options.jsx @@ -8,6 +8,16 @@ // onOptionsChange: PropTypes.func.isRequired // } +// render() { +// return ( +//
+//

Options

+// // render() { // return ( //
@@ -19,6 +29,13 @@ // value={this.props.options} // onChange={this.props.onOptionsChange}> +// +// +// +// +//
+// ); +// } // // // @@ -29,3 +46,5 @@ // } // export default Options; +// } +// export default Options; diff --git a/src/Components/Subcomponents/StatefulButton.jsx b/src/Components/Subcomponents/StatefulButton.jsx new file mode 100644 index 0000000..2e14480 --- /dev/null +++ b/src/Components/Subcomponents/StatefulButton.jsx @@ -0,0 +1,38 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +StatefulButton.propTypes = { + type: PropTypes.oneOf(['icon', 'text', 'composite']).isRequired, + selected: PropTypes.bool.isRequired, + text: PropTypes.string, + icon: PropTypes.string, + name: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, + // TODO pull a simple selected/unselected toggle handler out into a util for reuse + // but I don't think I should just put that logic here since I'll want the parent to be aware of changes? Could add context instead +}; + +function StatefulButton({ + type, + selected, + text, + icon, + name, + onChange +}) { + + const className = selected ? 'stateful-button-selected' : 'stateful-button-unselected' + + return ( +
+ {(type === 'icon' || type === 'composite') && + {name} + } + {(type === 'text' || type === 'composite') && +

{text}

+ } +
+ ) +} + +export default StatefulButton; From 74d5669772b6450cb91dbeadefde4866f4f0dbe1 Mon Sep 17 00:00:00 2001 From: Bridget Bailey <10903271+bbailey27@users.noreply.github.com> Date: Wed, 27 Nov 2024 02:38:50 -0600 Subject: [PATCH 2/4] Add back options buttons, start dark theme conversion --- src/App.css | 78 +++++++++++++++-- src/Components/Algorithms.jsx | 2 +- src/Components/Constraints.jsx | 15 ++-- src/Components/DataEntry.jsx | 38 +++----- src/Components/Details.jsx | 19 +--- src/Components/Options.jsx | 86 +++++++++---------- .../Subcomponents/StatefulButton.jsx | 11 +-- src/Images/ChangePeopleIcon.svg | 17 ++++ src/Images/ChangeTablesIcon.svg | 19 ++++ src/worker.js | 6 +- 10 files changed, 178 insertions(+), 113 deletions(-) create mode 100644 src/Images/ChangePeopleIcon.svg create mode 100644 src/Images/ChangeTablesIcon.svg diff --git a/src/App.css b/src/App.css index 33cbf42..ccad2e6 100644 --- a/src/App.css +++ b/src/App.css @@ -1,13 +1,59 @@ +:root { + /* https://colorffy.com/dark-theme-generator?colors=00c3ff-121212 */ + + /** Dark theme primary colors */ + --color-primary-a0: #00c3ff; + --color-primary-a10: #4dcaff; + --color-primary-a20: #6ed0ff; + --color-primary-a30: #88d7ff; + --color-primary-a40: #9fdeff; + --color-primary-a50: #b4e4ff; + + /** Dark theme surface colors */ + --color-surface-a0: #121212; + --color-surface-a10: #282828; + --color-surface-a20: #3f3f3f; + --color-surface-a30: #575757; + --color-surface-a40: #717171; + --color-surface-a50: #8b8b8b; + + /** Dark theme mixed surface colors */ + --color-surface-mixed-a0: #1f313a; + --color-surface-mixed-a10: #35454d; + --color-surface-mixed-a20: #4b5a61; + --color-surface-mixed-a30: #636f76; + --color-surface-mixed-a40: #7b868b; + --color-surface-mixed-a50: #949da2; + + --color-text-white: #fff; + +} + +html, body { + margin: 0; /* Remove default margins */ + padding: 0; /* Remove default padding */ + height: 100%; /* Ensure it covers the viewport */ + background-color: var(--color-surface-mixed-a0); /* Set your desired color */ +} + + .App { text-align: center; + background-color: var(--color-surface-mixed-a0); + color: var(--color-text-white); + min-height: 100vh; } .App-body { - min-height: 100vh; display: flex; justify-content: space-around; } +h1 { + margin: 0; + padding: 1rem; +} + .left-pane, .right-pane { width: 50%; @@ -55,11 +101,33 @@ padding: 0 0.25rem; } -.checkbox-group { +.options-group { display: flex; - flex-direction: column; - align-items: flex-start; - text-align: left; + flex-direction: row; + align-items: center; + justify-content: space-around; + text-align: center; +} + +.stateful-button { + width: 30%; + border-radius: .7rem; + padding: 1rem; + height: 100%; + color: var(--color-primary-a10) +} + +.stateful-button--selected { + background-color: var(--color-surface-mixed-a10); + font-weight: bold; +} + +.stateful-button:hover { + +} + +.stateful-button--unselected { + } .form-group { diff --git a/src/Components/Algorithms.jsx b/src/Components/Algorithms.jsx index 562e9be..3e85cca 100644 --- a/src/Components/Algorithms.jsx +++ b/src/Components/Algorithms.jsx @@ -7,7 +7,7 @@ class Algorithms extends Component { static propTypes = { algorithmChoice: PropTypes.string.isRequired, totalRounds: PropTypes.number.isRequired, - options: PropTypes.array.isRequired, + options: PropTypes.object.isRequired, maxPlayedWithAllowed: PropTypes.number.isRequired, maxAveragePlayedWithAllowed: PropTypes.number.isRequired, minUniqueTablesAllowed: PropTypes.number.isRequired, diff --git a/src/Components/Constraints.jsx b/src/Components/Constraints.jsx index ab68b3c..354a69f 100644 --- a/src/Components/Constraints.jsx +++ b/src/Components/Constraints.jsx @@ -9,7 +9,7 @@ class Constraints extends Component { static propTypes = { algorithmChoice: PropTypes.string.isRequired, totalRounds: PropTypes.number.isRequired, - options: PropTypes.array.isRequired, + options: PropTypes.object.isRequired, numTimesToRun: PropTypes.number.isRequired, maxPlayedWithAllowed: PropTypes.number.isRequired, maxAveragePlayedWithAllowed: PropTypes.number.isRequired, @@ -19,15 +19,14 @@ class Constraints extends Component { } renderRunUntilConstraints() { - const {options} = this.props; - const changePeopleOption = options.includes('changePeople'); - const changeTablesOption = options.includes('changeTables'); + const {changePeople, changeTables} = this.props.options; + return (
- {changePeopleOption && this.renderMaxPlayedWithAllowedInput()} - {changePeopleOption && this.renderMaxAveragePlayedWithAllowedInput()} - {changeTablesOption && this.renderMinUniqueTablesAllowedInput()} - {changeTablesOption && this.renderMaxRunsInput()} + {changePeople && this.renderMaxPlayedWithAllowedInput()} + {changePeople && this.renderMaxAveragePlayedWithAllowedInput()} + {changeTables && this.renderMinUniqueTablesAllowedInput()} + {changeTables && this.renderMaxRunsInput()}
); } diff --git a/src/Components/DataEntry.jsx b/src/Components/DataEntry.jsx index 5de30d5..8b7b1a6 100644 --- a/src/Components/DataEntry.jsx +++ b/src/Components/DataEntry.jsx @@ -8,10 +8,12 @@ import { runOrganizer } from '../worker'; const defaultData = { firstRun: true, - options: ['changePeople', 'changeTables'], + options: { + changePeople: false, + changeTables: false, + }, totalPlayers: 20, totalRounds: 4, - totalKids: 0, algorithmChoice: 'runRandomXTimes', numTimesToRun: 500, maxPlayedWithAllowed: 4, @@ -60,7 +62,6 @@ class DataEntry extends Component { this.handleOptionsChange = this.handleOptionsChange.bind(this); this.handleNumPlayersChange = this.handleNumPlayersChange.bind(this); this.handleNumRoundsChange = this.handleNumRoundsChange.bind(this); - this.handleNumKidsChange = this.handleNumKidsChange.bind(this); this.handleNumberChange = this.handleNumberChange.bind(this); this.handleAlgorithmChange = this.handleAlgorithmChange.bind(this); this.handleTablesChange = this.handleTablesChange.bind(this); @@ -70,10 +71,12 @@ class DataEntry extends Component { this.state = JSON.parse(JSON.stringify(defaultData)); } - //TODO handle other options - handleOptionsChange = (newOptions) => { + handleOptionsChange = (option, value) => { this.setState({ - options: newOptions + options: { + ...this.state.options, + [option]: value + } }); } @@ -89,13 +92,6 @@ class DataEntry extends Component { }); } - handleNumKidsChange = (e) => { - this.setState({ - totalKids: parseInt(e.target.value) ? parseInt(e.target.value) : 0 - }); - } -//todo use this for other number values -//todo variable defaults if this is an error? handleNumberChange = (e, property) => { this.setState({ [property]: parseInt(e.target.value) ? parseInt(e.target.value) : 0 @@ -123,12 +119,9 @@ class DataEntry extends Component { e.preventDefault(); const formPayload = { - changePeople: this.state.options.includes('changePeople'), - changeTables: this.state.options.includes('changeTables'), - kidsTable: this.state.options.includes('kidsTable'), + options: this.state.options, totalPlayers: this.state.totalPlayers, totalRounds: this.state.totalRounds, - totalKids: this.state.options.includes('kidsTable') ? this.state.totalKids : 0, algorithmChoice: this.state.algorithmChoice, numTimesToRun: this.state.numTimesToRun, maxPlayedWithAllowed: this.state.maxPlayedWithAllowed, @@ -159,7 +152,6 @@ class DataEntry extends Component { options, totalPlayers, totalRounds, - totalKids, algorithmChoice, numTimesToRun, maxPlayedWithAllowed, @@ -172,19 +164,16 @@ class DataEntry extends Component {

Enter Your Data

- {/* */} + handleOptionsChange={this.handleOptionsChange} />
+ handleNumRoundsChange={this.handleNumRoundsChange} />
- alert('changed')} selected={true} name='change-tables' />
); } diff --git a/src/Components/Details.jsx b/src/Components/Details.jsx index e22d2d8..6ab974e 100644 --- a/src/Components/Details.jsx +++ b/src/Components/Details.jsx @@ -4,30 +4,14 @@ import SingleInput from './Subcomponents/SingleInput'; class Details extends Component { static propTypes = { - isKidsTable: PropTypes.bool.isRequired, - totalKids: PropTypes.number.isRequired, totalPlayers: PropTypes.number.isRequired, totalRounds: PropTypes.number.isRequired, - handleNumKidsChange: PropTypes.func.isRequired, handleNumPlayersChange: PropTypes.func.isRequired, handleNumRoundsChange: PropTypes.func.isRequired } - renderKidsCount() { - const {totalKids, handleNumKidsChange} = this.props; - return ( - - ); - } -//TODO decide whether kids count towards total and handle them render() { - const {totalPlayers, totalRounds, handleNumPlayersChange, handleNumRoundsChange, isKidsTable} = this.props; + const {totalPlayers, totalRounds, handleNumPlayersChange, handleNumRoundsChange} = this.props; return (

Details

@@ -45,7 +29,6 @@ class Details extends Component { controlFunc={handleNumRoundsChange} content={totalRounds} /> - {isKidsTable && this.renderKidsCount()}
); } diff --git a/src/Components/Options.jsx b/src/Components/Options.jsx index 42a4d5f..889b4da 100644 --- a/src/Components/Options.jsx +++ b/src/Components/Options.jsx @@ -1,50 +1,44 @@ -// import React, { Component } from 'react'; -// import PropTypes from 'prop-types'; -// import {Checkbox, CheckboxGroup} from 'react-checkbox-group'; //TODO remove react-checkbox-group +import PropTypes from 'prop-types'; +import StatefulButton from './Subcomponents/StatefulButton'; +import { ReactComponent as ChangePeopleIcon } from '../Images/ChangePeopleIcon.svg'; +import { ReactComponent as ChangeTablesIcon } from '../Images/ChangeTablesIcon.svg'; -// class Options extends Component { -// static propTypes = { -// options: PropTypes.array.isRequired, -// onOptionsChange: PropTypes.func.isRequired -// } -// render() { -// return ( -//
-//

Options

-// -// render() { -// return ( -//
-//

Options

-// -// -// -// -// -//
-// ); -// } -// -// -// -//
-//
-// ); -// } +Options.propTypes = { + options: PropTypes.shape({ + changePeople: PropTypes.bool, + changeTables: PropTypes.bool, + }).isRequired, + handleOptionsChange: PropTypes.func.isRequired + } -// } -// export default Options; -// } -// export default Options; +function Options({ + options, + handleOptionsChange +}) { + + return ( +
+

Options

+
+ handleOptionsChange('changePeople', !options.changePeople)} + /> + handleOptionsChange('changeTables', !options.changeTables)} + /> +
+
+ ); +} + +export default Options; diff --git a/src/Components/Subcomponents/StatefulButton.jsx b/src/Components/Subcomponents/StatefulButton.jsx index 2e14480..5572984 100644 --- a/src/Components/Subcomponents/StatefulButton.jsx +++ b/src/Components/Subcomponents/StatefulButton.jsx @@ -6,30 +6,31 @@ StatefulButton.propTypes = { selected: PropTypes.bool.isRequired, text: PropTypes.string, icon: PropTypes.string, - name: PropTypes.string.isRequired, + name: PropTypes.string, onChange: PropTypes.func.isRequired, // TODO pull a simple selected/unselected toggle handler out into a util for reuse // but I don't think I should just put that logic here since I'll want the parent to be aware of changes? Could add context instead + // TODO decide what the selected status does in terms of styling - fill in the background? }; function StatefulButton({ type, selected, text, - icon, + Icon, name, onChange }) { - const className = selected ? 'stateful-button-selected' : 'stateful-button-unselected' + const className = selected ? 'stateful-button stateful-button--selected' : 'stateful-button stateful-button--unselected' return (
{(type === 'icon' || type === 'composite') && - {name} + } {(type === 'text' || type === 'composite') && -

{text}

+ }
) diff --git a/src/Images/ChangePeopleIcon.svg b/src/Images/ChangePeopleIcon.svg new file mode 100644 index 0000000..416e3dd --- /dev/null +++ b/src/Images/ChangePeopleIcon.svg @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/src/Images/ChangeTablesIcon.svg b/src/Images/ChangeTablesIcon.svg new file mode 100644 index 0000000..61aa6cf --- /dev/null +++ b/src/Images/ChangeTablesIcon.svg @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/src/worker.js b/src/worker.js index 3ad3da0..a288b89 100644 --- a/src/worker.js +++ b/src/worker.js @@ -16,11 +16,7 @@ function initializeData(userInput) { totalRounds = userInput.totalRounds; totalPlayers = userInput.totalPlayers; tableDetails = userInput.tables; - options = { - kidsTable: userInput.kidsTable, - changePeople: userInput.changePeople, - changeTables: userInput.changeTables - }; + options = userInput.options; playerList = getEmptyPlayerList(); tableList = getEmptyTableList(); algorithmChoice = userInput.algorithmChoice; From 4e26b81ed69997ef8048f5f0f7947069d9b6eb23 Mon Sep 17 00:00:00 2001 From: Bridget Bailey <10903271+bbailey27@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:01:59 -0600 Subject: [PATCH 3/4] Replace deprecated selectors and add UI for average unique tables constraint --- src/App.css | 36 +++++++++---------- src/Components/Algorithms.jsx | 35 ++++++++++++------ src/Components/Constraints.jsx | 34 ++++++++++++++---- src/Components/DataEntry.jsx | 26 +++++++++----- src/Components/Results.jsx | 8 ++--- src/Components/Subcomponents/SingleInput.jsx | 8 +++-- .../Subcomponents/StatefulButton.jsx | 3 -- src/worker.js | 10 +++--- 8 files changed, 103 insertions(+), 57 deletions(-) diff --git a/src/App.css b/src/App.css index ccad2e6..a0bd5eb 100644 --- a/src/App.css +++ b/src/App.css @@ -64,10 +64,6 @@ h1 { text-align: left; } -.radio-button { - width: 70%; -} - .column { display: flex; flex-direction: column; @@ -101,20 +97,27 @@ h1 { padding: 0 0.25rem; } +.algorithm-button { + width: 70%; +} + .options-group { display: flex; flex-direction: row; - align-items: center; justify-content: space-around; text-align: center; + padding: 1rem 2rem; } .stateful-button { width: 30%; border-radius: .7rem; padding: 1rem; - height: 100%; - color: var(--color-primary-a10) + align-content: center; + color: var(--color-primary-a10); + border: transparent .2rem solid; + font-size: min(10vw, 1.5rem); + } .stateful-button--selected { @@ -123,27 +126,24 @@ h1 { } .stateful-button:hover { - + background-color: var(--color-surface-mixed-a10); + border-color: var(--color-surface-mixed-a20); } -.stateful-button--unselected { - +textarea, select, input { + field-sizing: content; } .form-group { padding: 1px 6px; } -.form-input { - width: 4rem; -} - -.form-input[type='number'] { - width: 3rem; +.game-input { + min-width: 8rem; } -.game-input { - width: 8rem; +.remove-button { + border-radius: 50%; } .table-list { diff --git a/src/Components/Algorithms.jsx b/src/Components/Algorithms.jsx index 3e85cca..a230359 100644 --- a/src/Components/Algorithms.jsx +++ b/src/Components/Algorithms.jsx @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; // import { RadioGroup, RadioButton } from 'react-radio-buttons'; import Constraints from './Constraints'; +import StatefulButton from './Subcomponents/StatefulButton'; class Algorithms extends Component { static propTypes = { @@ -9,25 +10,37 @@ class Algorithms extends Component { totalRounds: PropTypes.number.isRequired, options: PropTypes.object.isRequired, maxPlayedWithAllowed: PropTypes.number.isRequired, - maxAveragePlayedWithAllowed: PropTypes.number.isRequired, + maxAveragePlayedWithAllowed: PropTypes.string.isRequired, minUniqueTablesAllowed: PropTypes.number.isRequired, + minAverageUniqueTablesAllowed: PropTypes.string.isRequired, maxRuns: PropTypes.number.isRequired, handleAlgorithmChange: PropTypes.func.isRequired, - handleNumberChange: PropTypes.func.isRequired + handleNumberChange: PropTypes.func.isRequired, + handleDecimalChange: PropTypes.func.isRequired, } render() { return (

Algorithm Options

- {/* - - Best of X Random Runs - - - Run Until Custom Constraints Met - - */} +
+ this.props.handleAlgorithmChange('runRandomNTimes')} + selected={this.props.algorithmChoice === 'runRandomNTimes'} + text="Best of N random runs" + name={'runRandomNTimes'} + type={'text'} + /> + this.props.handleAlgorithmChange('runUntilConstraints')} + selected={this.props.algorithmChoice === 'runUntilConstraints'} + text="Run until constraints met" + name={'runUntilConstraints'} + type={'text'} + /> +
); diff --git a/src/Components/Constraints.jsx b/src/Components/Constraints.jsx index 354a69f..c6554a4 100644 --- a/src/Components/Constraints.jsx +++ b/src/Components/Constraints.jsx @@ -12,10 +12,12 @@ class Constraints extends Component { options: PropTypes.object.isRequired, numTimesToRun: PropTypes.number.isRequired, maxPlayedWithAllowed: PropTypes.number.isRequired, - maxAveragePlayedWithAllowed: PropTypes.number.isRequired, + maxAveragePlayedWithAllowed: PropTypes.string.isRequired, minUniqueTablesAllowed: PropTypes.number.isRequired, + minAverageUniqueTablesAllowed: PropTypes.string.isRequired, maxRuns: PropTypes.number.isRequired, - handleNumberChange: PropTypes.func.isRequired + handleNumberChange: PropTypes.func.isRequired, + handleDecimalChange: PropTypes.func.isRequired, } renderRunUntilConstraints() { @@ -26,6 +28,7 @@ class Constraints extends Component { {changePeople && this.renderMaxPlayedWithAllowedInput()} {changePeople && this.renderMaxAveragePlayedWithAllowedInput()} {changeTables && this.renderMinUniqueTablesAllowedInput()} + {changeTables && this.renderMinAverageUniqueTablesAllowedInput()} {changeTables && this.renderMaxRunsInput()}
); @@ -42,21 +45,23 @@ class Constraints extends Component { content={maxPlayedWithAllowed} max={totalRounds} min={1} + step={1} /> ); } renderMaxAveragePlayedWithAllowedInput() { - const {totalRounds, maxAveragePlayedWithAllowed, handleNumberChange} = this.props; + const {totalRounds, maxAveragePlayedWithAllowed, handleDecimalChange} = this.props; return ( handleNumberChange(e, 'maxAveragePlayedWithAllowed')} + controlFunc={(e) => handleDecimalChange(e, 'maxAveragePlayedWithAllowed')} content={maxAveragePlayedWithAllowed} max={totalRounds} min={1} + step={0.01} /> ); } @@ -72,6 +77,23 @@ class Constraints extends Component { content={minUniqueTablesAllowed} max={totalRounds} min={1} + step={1} + /> + ); + } + + renderMinAverageUniqueTablesAllowedInput() { + const {totalRounds, minAverageUniqueTablesAllowed, handleDecimalChange} = this.props; + return ( + handleDecimalChange(e, 'minAverageUniqueTablesAllowed')} + content={minAverageUniqueTablesAllowed} + max={totalRounds} + min={1} + step={0.01} /> ); } @@ -91,7 +113,7 @@ class Constraints extends Component { ); } - renderRunXTimesInput() { + renderRunNTimesInput() { const {numTimesToRun, handleNumberChange} = this.props; return ( {this.props.algorithmChoice === 'runUntilConstraints' && this.renderRunUntilConstraints()} - {this.props.algorithmChoice === 'runRandomXTimes' && this.renderRunXTimesInput()} + {this.props.algorithmChoice === 'runRandomNTimes' && this.renderRunNTimesInput()} ); } diff --git a/src/Components/DataEntry.jsx b/src/Components/DataEntry.jsx index 8b7b1a6..c61dc45 100644 --- a/src/Components/DataEntry.jsx +++ b/src/Components/DataEntry.jsx @@ -3,23 +3,22 @@ import Options from './Options'; import Details from './Details'; import Algorithms from './Algorithms'; import Tables from './Tables'; -import StatefulButton from './Subcomponents/StatefulButton'; import { runOrganizer } from '../worker'; const defaultData = { firstRun: true, options: { - changePeople: false, - changeTables: false, + changePeople: true, + changeTables: true, }, totalPlayers: 20, totalRounds: 4, - algorithmChoice: 'runRandomXTimes', + algorithmChoice: 'runRandomNTimes', numTimesToRun: 500, maxPlayedWithAllowed: 4, - maxAveragePlayedWithAllowed: 4, + maxAveragePlayedWithAllowed: '4.0', minUniqueTablesAllowed: 1, - minAverageUniqueTablesAllowed: 1, + minAverageUniqueTablesAllowed: '1.0', maxRuns: 50000, tables: [ { @@ -63,6 +62,7 @@ class DataEntry extends Component { this.handleNumPlayersChange = this.handleNumPlayersChange.bind(this); this.handleNumRoundsChange = this.handleNumRoundsChange.bind(this); this.handleNumberChange = this.handleNumberChange.bind(this); + this.handleDecimalChange = this.handleDecimalChange.bind(this); this.handleAlgorithmChange = this.handleAlgorithmChange.bind(this); this.handleTablesChange = this.handleTablesChange.bind(this); this.handleClearForm = this.handleClearForm.bind(this); @@ -98,7 +98,14 @@ class DataEntry extends Component { }); } + handleDecimalChange = (e, property) => { + this.setState({ + [property]: e.target.value + }); + } + handleAlgorithmChange = (newAlgo) => { + console.log(newAlgo) this.setState({ algorithmChoice: newAlgo }); @@ -125,9 +132,9 @@ class DataEntry extends Component { algorithmChoice: this.state.algorithmChoice, numTimesToRun: this.state.numTimesToRun, maxPlayedWithAllowed: this.state.maxPlayedWithAllowed, - maxAveragePlayedWithAllowed: this.state.maxAveragePlayedWithAllowed, + maxAveragePlayedWithAllowed: Number.parseFloat(this.state.maxAveragePlayedWithAllowed), minUniqueTablesAllowed: this.state.minUniqueTablesAllowed, - minAverageUniqueTablesAllowed: this.state.minAverageUniqueTablesAllowed, + minAverageUniqueTablesAllowed: Number.parseFloat(this.state.minAverageUniqueTablesAllowed), maxRuns: this.state.maxRuns, numTables: this.state.tables.length, tables: this.state.tables @@ -186,7 +193,8 @@ class DataEntry extends Component { minAverageUniqueTablesAllowed={minAverageUniqueTablesAllowed} maxRuns={maxRuns} handleAlgorithmChange={this.handleAlgorithmChange} - handleNumberChange={this.handleNumberChange} /> + handleNumberChange={this.handleNumberChange} + handleDecimalChange={this.handleDecimalChange} />

Results

-

Max of Everyone's Max Times Played With Someone: {result.maxPlayedWithCount}

-

Average of Everyone's Max Times Played With Someone: {Math.round(result.averageMaxPlayedWithCount * 100) / 100}

-

Minimum Number of Unique Tables Visited: {result.minUniqueTablesVisited}

-

Average Number of Unique Tables Visited: {Math.round(result.averageUniqueTablesVisited * 100) / 100}

+

Max times seeing the same person: {result.maxPlayedWithCount}

+

Average of everyone's max times seeing one person: {Math.round(result.averageMaxPlayedWithCount * 100) / 100}

+

Minimum unique tables visited: {result.minUniqueTablesVisited}

+

Average unique tables visited: {Math.round(result.averageUniqueTablesVisited * 100) / 100}

    {result.playerList.length > 0 && diff --git a/src/Components/Subcomponents/SingleInput.jsx b/src/Components/Subcomponents/SingleInput.jsx index 20f7307..d2c2d3d 100644 --- a/src/Components/Subcomponents/SingleInput.jsx +++ b/src/Components/Subcomponents/SingleInput.jsx @@ -1,15 +1,16 @@ import React from 'react'; import PropTypes from 'prop-types'; -// Pure functional component defined as a const + const SingleInput = (props) => (
    @@ -26,6 +27,9 @@ SingleInput.propTypes = { PropTypes.number, ]).isRequired, placeholder: PropTypes.string, + min: PropTypes.number, + max: PropTypes.number, + step: PropTypes.number, }; export default SingleInput; diff --git a/src/Components/Subcomponents/StatefulButton.jsx b/src/Components/Subcomponents/StatefulButton.jsx index 5572984..d61193b 100644 --- a/src/Components/Subcomponents/StatefulButton.jsx +++ b/src/Components/Subcomponents/StatefulButton.jsx @@ -8,9 +8,6 @@ StatefulButton.propTypes = { icon: PropTypes.string, name: PropTypes.string, onChange: PropTypes.func.isRequired, - // TODO pull a simple selected/unselected toggle handler out into a util for reuse - // but I don't think I should just put that logic here since I'll want the parent to be aware of changes? Could add context instead - // TODO decide what the selected status does in terms of styling - fill in the background? }; function StatefulButton({ diff --git a/src/worker.js b/src/worker.js index a288b89..6be2246 100644 --- a/src/worker.js +++ b/src/worker.js @@ -66,14 +66,14 @@ function runAlgorithm() { // Different Run Options let result; switch (algorithmChoice) { - case 'runRandomXTimes': - result = runRandomXTimes(algorithmDetails.numTimesToRun); // 500 is a good number + case 'runRandomNTimes': + result = runRandomNTimes(algorithmDetails.numTimesToRun); // 500 is a good number break; case 'runUntilConstraints': - result = runRandomXTimes(algorithmDetails.maxRuns); + result = runRandomNTimes(algorithmDetails.maxRuns); break; default: - result = runRandomXTimes(algorithmDetails.numTimesToRun); // 500 is a good number + result = runRandomNTimes(algorithmDetails.numTimesToRun); // 500 is a good number break; } console.log('RESULT', result); @@ -92,7 +92,7 @@ function runRandomAndChooseBest(currentBestRun) { return compareResults({playerList: resultPlayerList, ...resultStats}, currentBestRun); } -function runRandomXTimes(numRuns) { +function runRandomNTimes(numRuns) { let bestRun = { playerList: [], maxPlayedWithCount: 100, From fc75b02124231e91c5ceb64b935455e12995b621 Mon Sep 17 00:00:00 2001 From: Bridget Bailey <10903271+bbailey27@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:35:02 -0600 Subject: [PATCH 4/4] Remove manual input for total player count --- src/Components/Constraints.jsx | 12 ++--- src/Components/DataEntry.jsx | 46 ++++++-------------- src/Components/Details.jsx | 15 ++----- src/Components/Subcomponents/SingleInput.jsx | 4 +- src/Components/Tables.jsx | 6 +-- 5 files changed, 27 insertions(+), 56 deletions(-) diff --git a/src/Components/Constraints.jsx b/src/Components/Constraints.jsx index c6554a4..dd84180 100644 --- a/src/Components/Constraints.jsx +++ b/src/Components/Constraints.jsx @@ -41,7 +41,7 @@ class Constraints extends Component { inputType='number' title='Max number of plays with same person allowed: ' name='maxPlayedWithAllowed' - controlFunc={(e) => handleNumberChange(e, 'maxPlayedWithAllowed')} + onChange={(e) => handleNumberChange(e, 'maxPlayedWithAllowed')} content={maxPlayedWithAllowed} max={totalRounds} min={1} @@ -57,7 +57,7 @@ class Constraints extends Component { inputType='number' title='Max allowed average number of plays with same person: ' name='maxAveragePlayedWithAllowed' - controlFunc={(e) => handleDecimalChange(e, 'maxAveragePlayedWithAllowed')} + onChange={(e) => handleDecimalChange(e, 'maxAveragePlayedWithAllowed')} content={maxAveragePlayedWithAllowed} max={totalRounds} min={1} @@ -73,7 +73,7 @@ class Constraints extends Component { inputType='number' title='Minimum allowed number of unique tables visited: ' name='minUniqueTablesAllowed' - controlFunc={(e) => handleNumberChange(e, 'minUniqueTablesAllowed')} + onChange={(e) => handleNumberChange(e, 'minUniqueTablesAllowed')} content={minUniqueTablesAllowed} max={totalRounds} min={1} @@ -89,7 +89,7 @@ class Constraints extends Component { inputType='number' title='Minimum allowed average number of unique tables visited: ' name='minAverageUniqueTablesAllowed' - controlFunc={(e) => handleDecimalChange(e, 'minAverageUniqueTablesAllowed')} + onChange={(e) => handleDecimalChange(e, 'minAverageUniqueTablesAllowed')} content={minAverageUniqueTablesAllowed} max={totalRounds} min={1} @@ -105,7 +105,7 @@ class Constraints extends Component { inputType='number' title='Max times to run: ' name='maxRuns' - controlFunc={(e) => handleNumberChange(e, 'maxRuns')} + onChange={(e) => handleNumberChange(e, 'maxRuns')} content={maxRuns} max={10000} min={1} @@ -120,7 +120,7 @@ class Constraints extends Component { inputType='number' title='Number of times to run: ' name='numTimesToRun' - controlFunc={(e) => handleNumberChange(e, 'numTimesToRun')} + onChange={(e) => handleNumberChange(e, 'numTimesToRun')} content={numTimesToRun} max={10000} min={1} diff --git a/src/Components/DataEntry.jsx b/src/Components/DataEntry.jsx index c61dc45..4131e15 100644 --- a/src/Components/DataEntry.jsx +++ b/src/Components/DataEntry.jsx @@ -11,7 +11,6 @@ const defaultData = { changePeople: true, changeTables: true, }, - totalPlayers: 20, totalRounds: 4, algorithmChoice: 'runRandomNTimes', numTimesToRun: 500, @@ -59,8 +58,6 @@ class DataEntry extends Component { constructor(props) { super(props); this.handleOptionsChange = this.handleOptionsChange.bind(this); - this.handleNumPlayersChange = this.handleNumPlayersChange.bind(this); - this.handleNumRoundsChange = this.handleNumRoundsChange.bind(this); this.handleNumberChange = this.handleNumberChange.bind(this); this.handleDecimalChange = this.handleDecimalChange.bind(this); this.handleAlgorithmChange = this.handleAlgorithmChange.bind(this); @@ -80,21 +77,10 @@ class DataEntry extends Component { }); } - handleNumPlayersChange = (e) => { + handleNumberChange = (e, property, value = null) => { + const newValue = value !== null ? value : parseInt(e.target.value) || 0; this.setState({ - totalPlayers: parseInt(e.target.value) ? parseInt(e.target.value) : 0 - }); - } - - handleNumRoundsChange = (e) => { - this.setState({ - totalRounds: parseInt(e.target.value) ? parseInt(e.target.value) : 0 - }); - } - - handleNumberChange = (e, property) => { - this.setState({ - [property]: parseInt(e.target.value) ? parseInt(e.target.value) : 0 + [property]: newValue }); } @@ -125,9 +111,13 @@ class DataEntry extends Component { handleFormSubmit = (e) => { e.preventDefault(); + const totalPlayers = this.state.tables.reduce((accumulator, currentItem) => { + return accumulator + currentItem.size + }, 0) + const formPayload = { options: this.state.options, - totalPlayers: this.state.totalPlayers, + totalPlayers, totalRounds: this.state.totalRounds, algorithmChoice: this.state.algorithmChoice, numTimesToRun: this.state.numTimesToRun, @@ -141,23 +131,15 @@ class DataEntry extends Component { }; console.log('Send this in a POST request:', formPayload); - const totalTableSpots = formPayload.tables.reduce(function(a, b) { - return a + b.size; - }, 0); - if (formPayload.totalPlayers !== totalTableSpots) { - throw new Error('Number of players must match number of table spots'); - } else { - let result = runOrganizer(formPayload); - this.props.handleTablesReady(this.state.tables); - this.props.handleResultReady(result); - this.setState({firstRun: false}); - } + let result = runOrganizer(formPayload); + this.props.handleTablesReady(this.state.tables); + this.props.handleResultReady(result); + this.setState({firstRun: false}); } // TODO change to across the top to have more room for table data entry render() { const { options, - totalPlayers, totalRounds, algorithmChoice, numTimesToRun, @@ -177,10 +159,8 @@ class DataEntry extends Component { handleOptionsChange={this.handleOptionsChange} />
    + handleNumberChange={this.handleNumberChange} />

    Details

    - handleNumberChange(e, 'totalRounds')} content={totalRounds} /> diff --git a/src/Components/Subcomponents/SingleInput.jsx b/src/Components/Subcomponents/SingleInput.jsx index d2c2d3d..eeaa253 100644 --- a/src/Components/Subcomponents/SingleInput.jsx +++ b/src/Components/Subcomponents/SingleInput.jsx @@ -12,7 +12,7 @@ const SingleInput = (props) => ( max={props.max} step={props.step} value={props.content} - onChange={props.controlFunc} + onChange={props.onChange} placeholder={props.placeholder} /> ); @@ -21,7 +21,7 @@ SingleInput.propTypes = { inputType: PropTypes.oneOf(['text', 'number']).isRequired, title: PropTypes.string.isRequired, name: PropTypes.string.isRequired, - controlFunc: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, content: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, diff --git a/src/Components/Tables.jsx b/src/Components/Tables.jsx index b0d5b31..a7c978e 100644 --- a/src/Components/Tables.jsx +++ b/src/Components/Tables.jsx @@ -92,14 +92,14 @@ class Tables extends Component { inputType='text' title='Name: ' name='tableName' - controlFunc={this.handleTableNameChange} + onChange={this.handleTableNameChange} content={table.name} />