feat: added multicassette to notifications override

This commit is contained in:
Sérgio Salgado 2021-10-27 15:32:19 +01:00
parent f32d02a808
commit 20a3c40c48
5 changed files with 171 additions and 130 deletions

View file

@ -132,7 +132,7 @@ const ECol = ({ editing, focus, config, extraPaddingRight, extraPadding }) => {
SuffixComponent = TL2, SuffixComponent = TL2,
textStyle = it => {}, textStyle = it => {},
isHidden = it => false, isHidden = it => false,
view = it => it?.toString(), view = it => it?.toString() ?? '—',
inputProps = {} inputProps = {}
} = config } = config

View file

@ -22,6 +22,7 @@ const GET_INFO = gql`
machines { machines {
name name
deviceId deviceId
numberOfCassettes
} }
cryptoCurrencies { cryptoCurrencies {
code code
@ -52,7 +53,7 @@ const Notifications = ({
const [error, setError] = useState(null) const [error, setError] = useState(null)
const [editingKey, setEditingKey] = useState(null) const [editingKey, setEditingKey] = useState(null)
const { data } = useQuery(GET_INFO) const { data, loading } = useQuery(GET_INFO)
const [saveConfig] = useMutation(SAVE_CONFIG, { const [saveConfig] = useMutation(SAVE_CONFIG, {
refetchQueries: ['getData'], refetchQueries: ['getData'],
@ -100,40 +101,42 @@ const Notifications = ({
} }
return ( return (
<NotificationsCtx.Provider value={contextValue}> !loading && (
{displayTitle && <TitleSection title="Notifications" />} <NotificationsCtx.Provider value={contextValue}>
{displaySetup && ( {displayTitle && <TitleSection title="Notifications" />}
<Section title="Setup" error={error && !section}> {displaySetup && (
<Setup forceDisable={!!editingKey} wizard={wizard} /> <Section title="Setup" error={error && !section}>
</Section> <Setup forceDisable={!!editingKey} wizard={wizard} />
)} </Section>
{displayTransactionAlerts && ( )}
<Section title="Transaction alerts" error={error && section === 'tx'}> {displayTransactionAlerts && (
<TransactionAlerts section="tx" fieldWidth={FIELDS_WIDTH} /> <Section title="Transaction alerts" error={error && section === 'tx'}>
</Section> <TransactionAlerts section="tx" fieldWidth={FIELDS_WIDTH} />
)} </Section>
{displayFiatAlerts && ( )}
<Section {displayFiatAlerts && (
title="Fiat balance alerts" <Section
error={error && section === 'fiat'}> title="Fiat balance alerts"
<FiatBalanceAlerts section="fiat" max={500} fieldWidth={50} /> error={error && section === 'fiat'}>
{displayOverrides && <FiatBalanceOverrides section="fiat" />} <FiatBalanceAlerts section="fiat" max={500} fieldWidth={50} />
</Section> {displayOverrides && <FiatBalanceOverrides section="fiat" />}
)} </Section>
{displayCryptoAlerts && ( )}
<Section {displayCryptoAlerts && (
title="Crypto balance alerts" <Section
error={error && section === 'crypto'}> title="Crypto balance alerts"
<CryptoBalanceAlerts section="crypto" fieldWidth={FIELDS_WIDTH} /> error={error && section === 'crypto'}>
{displayOverrides && ( <CryptoBalanceAlerts section="crypto" fieldWidth={FIELDS_WIDTH} />
<CryptoBalanceOverrides {displayOverrides && (
section="crypto" <CryptoBalanceOverrides
fieldWidth={FIELDS_WIDTH} section="crypto"
/> fieldWidth={FIELDS_WIDTH}
)} />
</Section> )}
)} </Section>
</NotificationsCtx.Provider> )}
</NotificationsCtx.Provider>
)
) )
} }

View file

@ -1,5 +1,6 @@
import { makeStyles } from '@material-ui/core' import { makeStyles } from '@material-ui/core'
import { Form, Formik } from 'formik' import { Form, Formik } from 'formik'
import * as R from 'ramda'
import React, { useContext } from 'react' import React, { useContext } from 'react'
import * as Yup from 'yup' import * as Yup from 'yup'
@ -17,6 +18,7 @@ import styles from './FiatBalanceAlerts.styles.js'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const NAME = 'fiatBalanceAlerts' const NAME = 'fiatBalanceAlerts'
const DEFAULT_NUMBER_OF_CASSETTES = 2
const FiatBalance = ({ const FiatBalance = ({
section, section,
@ -24,11 +26,20 @@ const FiatBalance = ({
max = Number.MAX_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER,
fieldWidth = 80 fieldWidth = 80
}) => { }) => {
const { isEditing, isDisabled, setEditing, data, save } = useContext( const {
NotificationsCtx isEditing,
) isDisabled,
setEditing,
data,
save,
machines
} = useContext(NotificationsCtx)
const classes = useStyles() const classes = useStyles()
const maxNumberOfCassettes =
Math.max(...R.map(it => it.numberOfCassettes, machines)) ??
DEFAULT_NUMBER_OF_CASSETTES
const editing = isEditing(NAME) const editing = isEditing(NAME)
const schema = Yup.object().shape({ const schema = Yup.object().shape({
@ -39,6 +50,18 @@ const FiatBalance = ({
.max(max) .max(max)
.nullable(), .nullable(),
fiatBalanceCassette2: Yup.number() fiatBalanceCassette2: Yup.number()
.transform(transformNumber)
.integer()
.min(min)
.max(max)
.nullable(),
fiatBalanceCassette3: Yup.number()
.transform(transformNumber)
.integer()
.min(min)
.max(max)
.nullable(),
fiatBalanceCassette4: Yup.number()
.transform(transformNumber) .transform(transformNumber)
.integer() .integer()
.min(min) .min(min)
@ -46,10 +69,12 @@ const FiatBalance = ({
.nullable() .nullable()
}) })
const fiatBalanceCassette1Percent = const percentages = {
(100 * (data?.fiatBalanceCassette1 ?? 0)) / max cassette1: (100 * (data?.fiatBalanceCassette1 ?? 0)) / max,
const fiatBalanceCassette2Percent = cassette2: (100 * (data?.fiatBalanceCassette2 ?? 0)) / max,
(100 * (data?.fiatBalanceCassette2 ?? 0)) / max cassette3: (100 * (data?.fiatBalanceCassette3 ?? 0)) / max,
cassette4: (100 * (data?.fiatBalanceCassette4 ?? 0)) / max
}
return ( return (
<Formik <Formik
@ -58,7 +83,9 @@ const FiatBalance = ({
enableReinitialize enableReinitialize
initialValues={{ initialValues={{
fiatBalanceCassette1: data?.fiatBalanceCassette1 ?? '', fiatBalanceCassette1: data?.fiatBalanceCassette1 ?? '',
fiatBalanceCassette2: data?.fiatBalanceCassette2 ?? '' fiatBalanceCassette2: data?.fiatBalanceCassette2 ?? '',
fiatBalanceCassette3: data?.fiatBalanceCassette3 ?? '',
fiatBalanceCassette4: data?.fiatBalanceCassette4 ?? ''
}} }}
validationSchema={schema} validationSchema={schema}
onSubmit={it => save(section, schema.cast(it))} onSubmit={it => save(section, schema.cast(it))}
@ -74,46 +101,32 @@ const FiatBalance = ({
setEditing={it => setEditing(NAME, it)} setEditing={it => setEditing(NAME, it)}
/> />
<div className={classes.wrapper}> <div className={classes.wrapper}>
<div className={classes.first}> {R.map(
<div className={classes.row}> it => (
<Cashbox <>
labelClassName={classes.cashboxLabel} <div className={classes.row}>
emptyPartClassName={classes.cashboxEmptyPart} <Cashbox
percent={fiatBalanceCassette1Percent} labelClassName={classes.cashboxLabel}
cashOut emptyPartClassName={classes.cashboxEmptyPart}
/> percent={percentages[`cassette${it + 1}`]}
<div className={classes.col2}> cashOut
<TL2 className={classes.title}>Cassette 1 (Top)</TL2> />
<EditableNumber <div className={classes.col2}>
label="Alert me under" <TL2 className={classes.title}>Cassette {it + 1}</TL2>
name="fiatBalanceCassette1" <EditableNumber
editing={editing} label="Alert me under"
displayValue={x => (x === '' ? '-' : x)} name={`fiatBalanceCassette${it + 1}`}
decoration="notes" editing={editing}
width={fieldWidth} displayValue={x => (x === '' ? '-' : x)}
/> decoration="notes"
</div> width={fieldWidth}
</div> />
</div> </div>
<div className={classes.row}> </div>
<Cashbox </>
labelClassName={classes.cashboxLabel} ),
emptyPartClassName={classes.cashboxEmptyPart} R.times(R.identity, maxNumberOfCassettes)
percent={fiatBalanceCassette2Percent} )}
cashOut
/>
<div className={classes.col2}>
<TL2 className={classes.title}>Cassette 2 (Bottom)</TL2>
<EditableNumber
label="Alert me under"
name="fiatBalanceCassette2"
editing={editing}
displayValue={x => (x === '' ? '-' : x)}
decoration="notes"
width={fieldWidth}
/>
</div>
</div>
</div> </div>
</Form> </Form>
</Formik> </Formik>

View file

@ -7,14 +7,11 @@ export default {
form: { form: {
marginBottom: 36 marginBottom: 36
}, },
first: {
width: 236
},
title: { title: {
marginTop: 0 marginTop: 0
}, },
row: { row: {
width: 183, width: 236,
display: 'grid', display: 'grid',
gridTemplateColumns: 'repeat(2,1fr)', gridTemplateColumns: 'repeat(2,1fr)',
gridTemplateRows: '1fr', gridTemplateRows: '1fr',

View file

@ -11,9 +11,18 @@ import NotificationsCtx from '../NotificationsContext'
const CASSETTE_1_KEY = 'fiatBalanceCassette1' const CASSETTE_1_KEY = 'fiatBalanceCassette1'
const CASSETTE_2_KEY = 'fiatBalanceCassette2' const CASSETTE_2_KEY = 'fiatBalanceCassette2'
const CASSETTE_3_KEY = 'fiatBalanceCassette3'
const CASSETTE_4_KEY = 'fiatBalanceCassette4'
const MACHINE_KEY = 'machine' const MACHINE_KEY = 'machine'
const NAME = 'fiatBalanceOverrides' const NAME = 'fiatBalanceOverrides'
const CASSETTE_LIST = [
CASSETTE_1_KEY,
CASSETTE_2_KEY,
CASSETTE_3_KEY,
CASSETTE_4_KEY
]
const FiatBalanceOverrides = ({ section }) => { const FiatBalanceOverrides = ({ section }) => {
const { const {
machines = [], machines = [],
@ -41,42 +50,62 @@ const FiatBalanceOverrides = ({ section }) => {
const initialValues = { const initialValues = {
[MACHINE_KEY]: null, [MACHINE_KEY]: null,
[CASSETTE_1_KEY]: '', [CASSETTE_1_KEY]: '',
[CASSETTE_2_KEY]: '' [CASSETTE_2_KEY]: '',
[CASSETTE_3_KEY]: '',
[CASSETTE_4_KEY]: ''
} }
const maxNumberOfCassettes = Math.max(
...R.map(it => it.numberOfCassettes, machines)
)
const notesMin = 0 const notesMin = 0
const notesMax = 9999999 const notesMax = 9999999
const validationSchema = Yup.object().shape( const validationSchema = Yup.object()
{ .shape({
[MACHINE_KEY]: Yup.string() [MACHINE_KEY]: Yup.string()
.label('Machine') .label('Machine')
.nullable() .nullable()
.required(), .required(),
[CASSETTE_1_KEY]: Yup.number() [CASSETTE_1_KEY]: Yup.number()
.label('Cassette 1 (top)') .label('Cassette 1')
.when(CASSETTE_2_KEY, {
is: CASSETTE_2_KEY => !CASSETTE_2_KEY,
then: Yup.number().required()
})
.transform(transformNumber) .transform(transformNumber)
.integer() .integer()
.min(notesMin) .min(notesMin)
.max(notesMax) .max(notesMax)
.nullable(), .nullable(),
[CASSETTE_2_KEY]: Yup.number() [CASSETTE_2_KEY]: Yup.number()
.label('Cassette 2 (bottom)') .label('Cassette 2')
.when(CASSETTE_1_KEY, { .transform(transformNumber)
is: CASSETTE_1_KEY => !CASSETTE_1_KEY, .integer()
then: Yup.number().required() .min(notesMin)
}) .max(notesMax)
.nullable(),
[CASSETTE_3_KEY]: Yup.number()
.label('Cassette 3')
.transform(transformNumber)
.integer()
.min(notesMin)
.max(notesMax)
.nullable(),
[CASSETTE_4_KEY]: Yup.number()
.label('Cassette 4')
.transform(transformNumber) .transform(transformNumber)
.integer() .integer()
.min(notesMin) .min(notesMin)
.max(notesMax) .max(notesMax)
.nullable() .nullable()
}, })
[CASSETTE_1_KEY, CASSETTE_2_KEY] .test((values, context) => {
) const picked = R.pick(CASSETTE_LIST, values)
if (CASSETTE_LIST.some(it => !R.isNil(picked[it]))) return
return context.createError({
path: CASSETTE_1_KEY,
message: 'At least one of the cassettes must have a value'
})
})
const viewMachine = it => const viewMachine = it =>
R.compose(R.path(['name']), R.find(R.propEq('deviceId', it)))(machines) R.compose(R.path(['name']), R.find(R.propEq('deviceId', it)))(machines)
@ -93,35 +122,34 @@ const FiatBalanceOverrides = ({ section }) => {
valueProp: 'deviceId', valueProp: 'deviceId',
labelProp: 'name' labelProp: 'name'
} }
},
{
name: CASSETTE_1_KEY,
display: 'Cash-out 1',
width: 155,
textAlign: 'right',
doubleHeader: 'Cash-out (Cassette Empty)',
bold: true,
input: NumberInput,
suffix: 'notes',
inputProps: {
decimalPlaces: 0
}
},
{
name: CASSETTE_2_KEY,
display: 'Cash-out 2',
width: 155,
textAlign: 'right',
doubleHeader: 'Cash-out (Cassette Empty)',
bold: true,
input: NumberInput,
suffix: 'notes',
inputProps: {
decimalPlaces: 0
}
} }
] ]
R.until(
R.gt(R.__, maxNumberOfCassettes),
it => {
elements.push({
name: `fiatBalanceCassette${it}`,
display: `Cash-out ${it}`,
width: 155,
textAlign: 'right',
doubleHeader: 'Cash-out (Cassette Empty)',
bold: true,
input: NumberInput,
suffix: 'notes',
inputProps: {
decimalPlaces: 0
},
isHidden: value =>
it >
machines.find(({ deviceId }) => deviceId === value.machine)
?.numberOfCassettes
})
return R.add(1, it)
},
1
)
return ( return (
<EditableTable <EditableTable
name={NAME} name={NAME}