handle Chips and Chip Values
This commit is contained in:
@@ -41,3 +41,7 @@
|
|||||||
z-index: 996;
|
z-index: 996;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mr-10 {
|
||||||
|
margin-right: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,43 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import { Tabs, Tab, Navbar } from 'react-materialize';
|
import { Navbar } from 'react-materialize';
|
||||||
import MakeMoneyMove from './cash/MakeMoneyMove';
|
import MakeMoneyMove from './cash/MakeMoneyMove';
|
||||||
import Cash from './cash/Cash';
|
import Cash from './cash/Cash';
|
||||||
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
import Chips from './chips/Chips';
|
||||||
|
import { BrowserRouter as Router, Route } from "react-router-dom";
|
||||||
import RoutableNavItem from './common/RoutableNavItem';
|
import RoutableNavItem from './common/RoutableNavItem';
|
||||||
import {
|
import {
|
||||||
CRIB,
|
CRIB,
|
||||||
|
CHIPS,
|
||||||
MAKE_MONEY_MOVE
|
MAKE_MONEY_MOVE
|
||||||
} from './RouteNames';
|
} from './RouteNames';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Router>
|
||||||
|
<div className="navbar-fixed">
|
||||||
|
<Navbar brand={<div>GKS</div>} alignLinks="right">
|
||||||
|
<RoutableNavItem href={CRIB}>
|
||||||
|
Crib
|
||||||
|
</RoutableNavItem>
|
||||||
|
|
||||||
<Router>
|
<RoutableNavItem href={CHIPS}>
|
||||||
<div className="navbar-fixed">
|
Chips
|
||||||
<Navbar brand={<div>GKS</div>} alignLinks="right">
|
</RoutableNavItem>
|
||||||
<RoutableNavItem href={CRIB}>
|
|
||||||
Crib
|
|
||||||
</RoutableNavItem>
|
|
||||||
|
|
||||||
<RoutableNavItem>
|
|
||||||
Homies
|
|
||||||
</RoutableNavItem>
|
|
||||||
|
|
||||||
<RoutableNavItem href={MAKE_MONEY_MOVE}>
|
<RoutableNavItem>
|
||||||
Make Money Move
|
Homies
|
||||||
</RoutableNavItem>
|
</RoutableNavItem>
|
||||||
|
|
||||||
</Navbar>
|
<RoutableNavItem href={MAKE_MONEY_MOVE}>
|
||||||
|
Make Money Move
|
||||||
|
</RoutableNavItem>
|
||||||
|
</Navbar>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
<div className="autoscrolling">
|
||||||
<div className="autoscrolling">
|
|
||||||
<Route exact path={CRIB} component={Cash} />
|
<Route exact path={CRIB} component={Cash} />
|
||||||
|
<Route path={CHIPS} component={Chips} />
|
||||||
<Route path={MAKE_MONEY_MOVE} component={MakeMoneyMove} />
|
<Route path={MAKE_MONEY_MOVE} component={MakeMoneyMove} />
|
||||||
</div>
|
</div>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
export const CRIB = '/';
|
export const CRIB = '/';
|
||||||
|
export const CHIPS = '/chips';
|
||||||
export const MAKE_MONEY_MOVE = '/make-money-move';
|
export const MAKE_MONEY_MOVE = '/make-money-move';
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { Button, Table } from 'react-materialize';
|
import { Button, Table } from 'react-materialize';
|
||||||
import './Cash.css';
|
import './Cash.css';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { MAKE_MONEY_MOVE } from '../RouteNames';
|
import { MAKE_MONEY_MOVE } from '../RouteNames';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
|||||||
86
client/src/chips/AddChip.js
Normal file
86
client/src/chips/AddChip.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { TextInput, Button } from "react-materialize";
|
||||||
|
import axios from "axios";
|
||||||
|
import M from "materialize-css";
|
||||||
|
|
||||||
|
const AddChip = (props) => {
|
||||||
|
const [chipName, setChipName] = useState('');
|
||||||
|
const [chipSymbol, setChipSymbol] = useState('');
|
||||||
|
const [submitInProgress, setSubmitInProgress] = useState(false);
|
||||||
|
|
||||||
|
const handleInputChange = (e) => {
|
||||||
|
const newValue = e.target.value;
|
||||||
|
switch (e.target.id){
|
||||||
|
case 'chipName':
|
||||||
|
setChipName(newValue);
|
||||||
|
break;
|
||||||
|
case 'chipSymbol':
|
||||||
|
setChipSymbol(newValue);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const disableSubmit = () => {
|
||||||
|
return submitInProgress || chipName.length === 0 || chipSymbol.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearForm = () => {
|
||||||
|
setChipName('');
|
||||||
|
setChipSymbol('');
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorToast = () => M.toast({ html: "Yo! It ain't workin'" });
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
setSubmitInProgress(true);
|
||||||
|
const chipRequest = {
|
||||||
|
chip: {
|
||||||
|
name: chipName,
|
||||||
|
symbol: chipSymbol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
const submitResponse = await axios.post('/chips', chipRequest);
|
||||||
|
|
||||||
|
if (submitResponse && submitResponse.status === 200 && submitResponse.data) {
|
||||||
|
M.toast({ html: "Chipped In" });
|
||||||
|
clearForm();
|
||||||
|
} else {
|
||||||
|
errorToast();
|
||||||
|
}
|
||||||
|
}catch (e) {
|
||||||
|
errorToast();
|
||||||
|
}
|
||||||
|
|
||||||
|
setSubmitInProgress(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='section'>
|
||||||
|
<h5>Add New Chip</h5>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
id='chipName'
|
||||||
|
label="Chip Name"
|
||||||
|
value={chipName}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
id='chipSymbol'
|
||||||
|
label="Chip Symbol"
|
||||||
|
value={chipSymbol}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button disabled={disableSubmit()} waves="light" onClick={handleSubmit}>
|
||||||
|
Do It
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AddChip;
|
||||||
14
client/src/chips/Chips.js
Normal file
14
client/src/chips/Chips.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { withRouter } from 'react-router-dom';
|
||||||
|
import AddChip from "./AddChip";
|
||||||
|
import ListChips from "./ListChips";
|
||||||
|
|
||||||
|
const Chips = (props) => (
|
||||||
|
<div className='container'>
|
||||||
|
<AddChip />
|
||||||
|
|
||||||
|
<ListChips />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default withRouter(Chips);
|
||||||
248
client/src/chips/ListChips.js
Normal file
248
client/src/chips/ListChips.js
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import axios from "axios";
|
||||||
|
import { Table, Collapsible, CollapsibleItem, Button, TextInput, Row, Col, Select } from "react-materialize";
|
||||||
|
import M from "materialize-css";
|
||||||
|
import YesNoModal from "../common/YesNoModal";
|
||||||
|
|
||||||
|
const ListChips = (props) => {
|
||||||
|
const [chipsList, setChipsList] = useState([]);
|
||||||
|
const [chipValuePairs, setChipValuePairs] = useState([]);
|
||||||
|
const [chipValueActiveIndex, setChipValueActiveIndex] = useState(undefined);
|
||||||
|
const [editingChipValue, setEditingChipValue] = useState("");
|
||||||
|
const [newChipValueSecondaryChipId, setNewChipValueSecondaryChipId] = useState("");
|
||||||
|
|
||||||
|
const reloadChipsListEffect = () => {
|
||||||
|
(async() => {
|
||||||
|
try {
|
||||||
|
const chipsResponse = await axios.get(`/chips`);
|
||||||
|
|
||||||
|
if (chipsResponse && chipsResponse.status === 200 && Array.isArray(chipsResponse.data)) {
|
||||||
|
setChipsList(chipsResponse.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
errorToast();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateChipValuePairsEffect = () => {
|
||||||
|
const result = [];
|
||||||
|
chipsList.forEach(chip => {
|
||||||
|
const chipValues = chip['base_chip_values'];
|
||||||
|
chipValues.forEach(chipValue => {
|
||||||
|
result.push({
|
||||||
|
baseChipId: chip.id,
|
||||||
|
secondaryChipId: chipValue['secondary_chip_id']
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
setChipValuePairs(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(reloadChipsListEffect, []);
|
||||||
|
useEffect(updateChipValuePairsEffect, [chipsList]);
|
||||||
|
|
||||||
|
const deleteChip = async (chipId) => {
|
||||||
|
try {
|
||||||
|
const chipsResponse = await axios.delete(`/chips/${chipId}`);
|
||||||
|
|
||||||
|
if (chipsResponse && chipsResponse.status === 200 && Array.isArray(chipsResponse.data)){
|
||||||
|
setChipsList(chipsResponse.data);
|
||||||
|
M.toast({ html: 'Chip destroyed!' });
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
errorToast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addNewChipValue = async (baseChipId) => {
|
||||||
|
try{
|
||||||
|
const newChipValueObject = {
|
||||||
|
'chip_value': {
|
||||||
|
base_chip_id: baseChipId,
|
||||||
|
secondary_chip_id: newChipValueSecondaryChipId,
|
||||||
|
value: editingChipValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const chipsResponse = await axios.post(`/chip_values`, newChipValueObject);
|
||||||
|
|
||||||
|
if (chipsResponse && chipsResponse.status === 200){
|
||||||
|
setChipsList(chipsResponse.data);
|
||||||
|
setNewChipValueSecondaryChipId("");
|
||||||
|
setChipValueActiveIndex(undefined);
|
||||||
|
setEditingChipValue("");
|
||||||
|
M.toast({ html: 'I smell money $$$' });
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
errorToast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateChipValue = async (chipValueId) => {
|
||||||
|
try{
|
||||||
|
const updatedChipValue = {
|
||||||
|
'chip_value': {
|
||||||
|
id: chipValueId,
|
||||||
|
value: editingChipValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const chipsResponse = await axios.put(`/chip_values/${chipValueId}`, updatedChipValue);
|
||||||
|
|
||||||
|
if (chipsResponse && chipsResponse.status === 200){
|
||||||
|
setChipsList(chipsResponse.data);
|
||||||
|
setChipValueActiveIndex(undefined);
|
||||||
|
setEditingChipValue("");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
errorToast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteChipValue = async (chipValueId) => {
|
||||||
|
try {
|
||||||
|
const chipsResponse = await axios.delete(`/chip_values/${chipValueId}`);
|
||||||
|
|
||||||
|
if (chipsResponse && chipsResponse.status === 200){
|
||||||
|
setChipsList(chipsResponse.data);
|
||||||
|
M.toast({ html: 'Destroyed!' });
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
errorToast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const errorToast = () => M.toast({ html: "Yo! It ain't workin'" });
|
||||||
|
|
||||||
|
const getChipData = (chipId) => chipsList.find(chip => chip.id === chipId);
|
||||||
|
|
||||||
|
const checkIfPairExists = (baseChipId, secondaryChipId) => {
|
||||||
|
return chipValuePairs.find(chipValuePair =>
|
||||||
|
chipValuePair.baseChipId === baseChipId &&
|
||||||
|
chipValuePair.secondaryChipId === secondaryChipId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const secondaryChipOptions = (baseChipId) => {
|
||||||
|
const options = chipsList.map((chip, index) => {
|
||||||
|
if (chip.id !== baseChipId && !checkIfPairExists(baseChipId, chip.id)) {
|
||||||
|
return <option key={`chip-option-${index}`} value={chip.id}>{chip.name}</option>
|
||||||
|
}else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return options.filter(option => option !== null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const chipActions = (id) => (
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
<Button flat node="button" icon="add" onClick={() => setChipValueActiveIndex(id)} />
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<YesNoModal
|
||||||
|
body={"Maan, y'a sure about this?"}
|
||||||
|
yesAction={() => deleteChip(id)}
|
||||||
|
triggerNode={<Button flat node="button" icon="delete" />} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
|
||||||
|
const newChipValueForm = (id) => {
|
||||||
|
if (chipValueActiveIndex !== id){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
<Select value={newChipValueSecondaryChipId} name="secondary_chip" onChange={(e) => setNewChipValueSecondaryChipId(e.target.value)}>
|
||||||
|
<option disabled value="">Secondary chip</option>
|
||||||
|
{secondaryChipOptions(id)}
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<TextInput label="Value" type="number" value={editingChipValue} onChange={(e) => setEditingChipValue(e.target.value)} />,
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<Button flat icon="check" onClick={() => addNewChipValue(id)} />
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<Button flat icon="cancel" onClick={() => setChipValueActiveIndex(undefined)} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const singleChipValueInfo = (chipValueData, index) => {
|
||||||
|
const secondaryChipId = chipValueData && chipValueData.secondary_chip_id ? chipValueData.secondary_chip_id : undefined;
|
||||||
|
const { name:secondaryChipName, symbol:secondaryChipSymbol } = getChipData(secondaryChipId) || {};
|
||||||
|
return(
|
||||||
|
<tr key={`secondary-chip-${index}`}>
|
||||||
|
<td>{ `${secondaryChipName} [${secondaryChipSymbol}]` }</td>
|
||||||
|
{
|
||||||
|
chipValueActiveIndex === chipValueData.id &&
|
||||||
|
[
|
||||||
|
<td key={`chip-value-input-${index}`}><TextInput defaultValue={chipValueData.value} onChange={(e) => setEditingChipValue(e.target.value)} /></td>,
|
||||||
|
<td key={`chip-value-save-${index}`}><Button flat node="button" icon="check" onClick={() => updateChipValue(chipValueData.id)} /></td>,
|
||||||
|
<td key={`chip-value-cancel-${index}`}><Button flat node="button" icon="cancel" onClick={() => setChipValueActiveIndex(undefined)} /></td>
|
||||||
|
]
|
||||||
|
}
|
||||||
|
{
|
||||||
|
chipValueActiveIndex !== chipValueData.id &&
|
||||||
|
[
|
||||||
|
<td key={`chip-value-${index}`}>{ chipValueData.value }</td>,
|
||||||
|
<td key={`chip-value-edit-${index}`}><Button flat node="button" icon="edit" onClick={() => setChipValueActiveIndex(chipValueData.id)} /></td>
|
||||||
|
]
|
||||||
|
}
|
||||||
|
<td>
|
||||||
|
<YesNoModal
|
||||||
|
body={"Maan, y'a sure about this?"}
|
||||||
|
yesAction={() => deleteChipValue(chipValueData.id)}
|
||||||
|
triggerNode={<Button flat node="button" icon="delete" />} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const chipValuesTable = (chip) => (
|
||||||
|
<Table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Secondary chip</th>
|
||||||
|
<th>Value</th>
|
||||||
|
<th colSpan={3}>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{ chip.base_chip_values.map((chipValueData, index) => singleChipValueInfo(chipValueData, index)) }
|
||||||
|
</tbody>
|
||||||
|
</Table>
|
||||||
|
)
|
||||||
|
|
||||||
|
const singleChipInfo = (chip, index) => (
|
||||||
|
<CollapsibleItem key={`base-chip-${index}`} expanded={false} header={`${chip.name} [${chip.symbol}]`} node="div">
|
||||||
|
{ chipActions(chip.id) }
|
||||||
|
{ newChipValueForm(chip.id) }
|
||||||
|
{ chipValuesTable(chip) }
|
||||||
|
</CollapsibleItem>
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='section'>
|
||||||
|
<h5>Chips In Tha Hood</h5>
|
||||||
|
|
||||||
|
<Collapsible>
|
||||||
|
{
|
||||||
|
chipsList.map((chip, index) => singleChipInfo(chip, index))
|
||||||
|
}
|
||||||
|
</Collapsible>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ListChips;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Navbar, NavItem } from 'react-materialize';
|
import { NavItem } from 'react-materialize';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
const RoutableNavItem = (props) => {
|
const RoutableNavItem = (props) => {
|
||||||
|
|||||||
43
client/src/common/YesNoModal.js
Normal file
43
client/src/common/YesNoModal.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Modal, Button, Row, Col } from 'react-materialize';
|
||||||
|
|
||||||
|
const YesNoModal = (props) => {
|
||||||
|
const { title, body, yesAction, noAction, triggerNode } = props;
|
||||||
|
return(
|
||||||
|
<Modal
|
||||||
|
actions={[
|
||||||
|
<Row>
|
||||||
|
<Col s={3}>
|
||||||
|
<Button modal="confirm" node="button" waves="green" onClick={yesAction}>Yes</Button>
|
||||||
|
</Col>
|
||||||
|
<Col s={3}>
|
||||||
|
<Button modal="close" node="button" waves="red" onClick={noAction}>No</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
]}
|
||||||
|
bottomSheet
|
||||||
|
fixedFooter={false}
|
||||||
|
header={title}
|
||||||
|
id="Modal-0"
|
||||||
|
open={false}
|
||||||
|
options={{
|
||||||
|
dismissible: true,
|
||||||
|
endingTop: '10%',
|
||||||
|
inDuration: 250,
|
||||||
|
onCloseEnd: null,
|
||||||
|
onCloseStart: null,
|
||||||
|
onOpenEnd: null,
|
||||||
|
onOpenStart: null,
|
||||||
|
opacity: 0.5,
|
||||||
|
outDuration: 250,
|
||||||
|
preventScrolling: true,
|
||||||
|
startingTop: '4%'
|
||||||
|
}}
|
||||||
|
trigger={triggerNode}
|
||||||
|
>
|
||||||
|
<p>{body}</p>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default YesNoModal;
|
||||||
Reference in New Issue
Block a user