lamassu-server/packages/admin-ui/src/pages/Notifications/sections/FiatBalanceAlerts.jsx
2025-06-30 07:17:41 +01:00

230 lines
8.5 KiB
JavaScript

import { Form, Formik } from 'formik'
import * as R from 'ramda'
import React, { useContext } from 'react'
import PromptWhenDirty from '../../../components/PromptWhenDirty'
import { TL2 } from '../../../components/typography'
import * as Yup from 'yup'
import { transformNumber } from '../../../utils/number'
import { Cashbox } from '../../../components/inputs/cashbox/Cashbox'
import NotificationsCtx from '../NotificationsContext'
import Header from '../components/EditHeader'
import EditableNumber from '../components/EditableNumber'
const CASH_IN_KEY = 'fiatBalanceAlertsCashIn'
const CASH_OUT_KEY = 'fiatBalanceAlertsCashOut'
const RECYCLER_STACKER_KEY = 'fiatBalanceAlertsRecyclerStacker'
const DEFAULT_NUMBER_OF_CASSETTES = 2
const DEFAULT_NUMBER_OF_RECYCLERS = 0
const notesMin = 0
const notesMax = 9999999
const FiatBalance = ({ section, fieldWidth = 80 }) => {
const {
isEditing,
isDisabled,
setEditing,
data,
save,
machines = [],
} = useContext(NotificationsCtx)
const maxNumberOfCassettes = Math.max(
...R.map(it => it.numberOfCassettes, machines),
DEFAULT_NUMBER_OF_CASSETTES,
)
const maxNumberOfRecyclers = Math.max(
...R.map(it => it.numberOfRecyclers, machines),
DEFAULT_NUMBER_OF_RECYCLERS,
)
const percentValidation = Yup.number()
.transform(transformNumber)
.integer()
.min(0)
.max(100)
.nullable()
const schema = Yup.object().shape({
cashInAlertThreshold: Yup.number()
.transform(transformNumber)
.integer()
.min(notesMin)
.max(notesMax)
.nullable(),
fillingPercentageCassette1: percentValidation,
fillingPercentageCassette2: percentValidation,
fillingPercentageCassette3: percentValidation,
fillingPercentageCassette4: percentValidation,
fillingPercentageRecycler1: percentValidation,
fillingPercentageRecycler2: percentValidation,
fillingPercentageRecycler3: percentValidation,
fillingPercentageRecycler4: percentValidation,
fillingPercentageRecycler5: percentValidation,
fillingPercentageRecycler6: percentValidation,
})
return (
<Formik
validateOnBlur={false}
validateOnChange={false}
enableReinitialize
initialValues={{
cashInAlertThreshold: data?.cashInAlertThreshold ?? '',
fillingPercentageCassette1: data?.fillingPercentageCassette1 ?? '',
fillingPercentageCassette2: data?.fillingPercentageCassette2 ?? '',
fillingPercentageCassette3: data?.fillingPercentageCassette3 ?? '',
fillingPercentageCassette4: data?.fillingPercentageCassette4 ?? '',
fillingPercentageRecycler1: data?.fillingPercentageRecycler1 ?? '',
fillingPercentageRecycler2: data?.fillingPercentageRecycler2 ?? '',
fillingPercentageRecycler3: data?.fillingPercentageRecycler3 ?? '',
fillingPercentageRecycler4: data?.fillingPercentageRecycler4 ?? '',
fillingPercentageRecycler5: data?.fillingPercentageRecycler5 ?? '',
fillingPercentageRecycler6: data?.fillingPercentageRecycler6 ?? '',
}}
validationSchema={schema}
onSubmit={it => save(section, schema.cast(it))}
onReset={() => {
setEditing(CASH_IN_KEY, false)
setEditing(CASH_OUT_KEY, false)
}}>
{({ values }) => (
<div className="flex flex-col gap-9">
<Form>
<PromptWhenDirty />
<Header
title="Cash box"
editing={isEditing(CASH_IN_KEY)}
disabled={isDisabled(CASH_IN_KEY)}
setEditing={it => setEditing(CASH_IN_KEY, it)}
/>
<EditableNumber
label="Alert me over"
name="cashInAlertThreshold"
editing={isEditing(CASH_IN_KEY)}
displayValue={x => (x === '' ? '-' : x)}
decoration="notes"
width={fieldWidth}
/>
</Form>
<Form>
<PromptWhenDirty />
<Header
title="Cash out (Empty)"
editing={isEditing(CASH_OUT_KEY)}
disabled={isDisabled(CASH_OUT_KEY)}
setEditing={it => setEditing(CASH_OUT_KEY, it)}
/>
<div className="flex flex-wrap gap-8">
{R.map(
it => (
<>
<div className="flex w-50 gap-4">
<Cashbox
percent={
values[`fillingPercentageCassette${it + 1}`] ??
data[`cassette${it + 1}`]
}
isLow={false}
className="border-4 inline-block"
omitInnerPercentage
cashOut
/>
<div className="w-30">
<TL2 className="mt-0">Cassette {it + 1}</TL2>
<EditableNumber
label="Alert me under"
name={`fillingPercentageCassette${it + 1}`}
editing={isEditing(CASH_OUT_KEY)}
displayValue={x => (x === '' ? '-' : x)}
decoration="%"
width={fieldWidth}
/>
</div>
</div>
</>
),
R.times(R.identity, maxNumberOfCassettes),
)}
</div>
</Form>
{maxNumberOfRecyclers > 0 && (
<Form>
<PromptWhenDirty />
<Header
title="Cash recycling"
editing={isEditing(RECYCLER_STACKER_KEY)}
disabled={isDisabled(RECYCLER_STACKER_KEY)}
setEditing={it => setEditing(RECYCLER_STACKER_KEY, it)}
/>
<div className="flex flex-wrap gap-8">
{R.chain(
it => [
<>
<div className="flex w-50 gap-4">
<Cashbox
percent={
values[
`fillingPercentageRecycler${(it + 1) * 2 - 1}`
] ?? data[`recycler${(it + 1) * 2 - 1}`]
}
isLow={false}
className="border-4 inline-block"
omitInnerPercentage
cashOut
/>
<div className="w-30">
<TL2 className="mt-0">
Recycler {(it + 1) * 2 - 1}
</TL2>
<EditableNumber
label="Alert me under"
name={`fillingPercentageRecycler${(it + 1) * 2 - 1}`}
editing={isEditing(RECYCLER_STACKER_KEY)}
displayValue={x => (x === '' ? '-' : x)}
decoration="%"
width={fieldWidth}
/>
</div>
</div>
</>,
<>
<div className="flex w-50 gap-4">
<Cashbox
percent={
values[
`fillingPercentageRecycler${(it + 1) * 2}`
] ?? data[`recycler${(it + 1) * 2}`]
}
isLow={false}
className="border-4 inline-block"
omitInnerPercentage
cashOut
/>
<div className="w-30">
<TL2 className="mt-0">Recycler {(it + 1) * 2}</TL2>
<EditableNumber
label="Alert me under"
name={`fillingPercentageRecycler${(it + 1) * 2}`}
editing={isEditing(RECYCLER_STACKER_KEY)}
displayValue={x => (x === '' ? '-' : x)}
decoration="%"
width={fieldWidth}
/>
</div>
</div>
</>,
],
R.times(R.identity, maxNumberOfRecyclers / 2),
)}
</div>
</Form>
)}
</div>
)}
</Formik>
)
}
export default FiatBalance