Merge pull request #1017 from chaotixkilla/fix-ui-ux-tweaks

Improve UI/UX on multiple screens
This commit is contained in:
Rafael Taranto 2022-01-20 12:31:49 +00:00 committed by GitHub
commit 412f3ac2b5
25 changed files with 246 additions and 153 deletions

View file

@ -1,7 +1,7 @@
import { useLazyQuery } from '@apollo/react-hooks'
import { makeStyles, ClickAwayListener } from '@material-ui/core'
import classnames from 'classnames'
import { format, isSameDay } from 'date-fns/fp'
import { format } from 'date-fns/fp'
import FileSaver from 'file-saver'
import * as R from 'ramda'
import React, { useState, useCallback } from 'react'
@ -189,7 +189,6 @@ const LogsDownloaderPopover = ({
if (!range || !range.from) return
if (range.from && !range.until) range.until = new Date()
if (isSameDay(range.until, range.from)) range.until = new Date()
if (selectedRadio === RANGE) {
fetchLogs({

View file

@ -1,4 +1,5 @@
import { makeStyles, ClickAwayListener } from '@material-ui/core'
import * as R from 'ramda'
import React, { useState, memo } from 'react'
import Popper from 'src/components/Popper'
@ -8,9 +9,9 @@ const useStyles = makeStyles({
transparentButton: {
border: 'none',
backgroundColor: 'transparent',
marginTop: 4,
outline: 'none',
cursor: 'pointer'
cursor: 'pointer',
marginTop: 4
},
popoverContent: ({ width }) => ({
width,
@ -69,11 +70,22 @@ const HoverableTooltip = memo(({ parentElements, children, width }) => {
return (
<div>
{!R.isNil(parentElements) && (
<div
onMouseEnter={handler.handleOpenHelpPopper}
onMouseLeave={handler.handleCloseHelpPopper}>
{parentElements}
</div>
)}
{R.isNil(parentElements) && (
<button
type="button"
onMouseEnter={handler.handleOpenHelpPopper}
onMouseLeave={handler.handleCloseHelpPopper}
className={handler.classes.transparentButton}>
<HelpIcon />
</button>
)}
<Popper
open={handler.helpPopperOpen}
anchorEl={handler.helpPopperAnchorEl}

View file

@ -1,3 +1,5 @@
import { makeStyles } from '@material-ui/core'
import classnames from 'classnames'
import * as R from 'ramda'
import React, { useContext } from 'react'
@ -11,6 +13,14 @@ import { startCase } from 'src/utils/string'
import TableCtx from './Context'
const styles = {
orderedBySpan: {
whiteSpace: 'nowrap'
}
}
const useStyles = makeStyles(styles)
const groupSecondHeader = elements => {
const [toSHeader, noSHeader] = R.partition(R.has('doubleHeader'))(elements)
@ -31,6 +41,7 @@ const groupSecondHeader = elements => {
}
const Header = () => {
const classes = useStyles()
const {
elements,
enableEdit,
@ -40,6 +51,7 @@ const Header = () => {
deleteWidth,
enableToggle,
toggleWidth,
orderedBy,
DEFAULT_COL_SIZE
} = useContext(TableCtx)
@ -60,11 +72,40 @@ const Header = () => {
const mapElement = (
{ name, width = DEFAULT_COL_SIZE, header, textAlign },
idx
) => (
) => {
const orderClasses = classnames({
[classes.orderedBySpan]:
R.isNil(header) && !R.isNil(orderedBy) && R.equals(name, orderedBy.code)
})
const attachOrderedByToComplexHeader = header => {
if (!R.isNil(orderedBy) && R.equals(name, orderedBy.code)) {
try {
const cloneHeader = R.clone(header)
const children = R.path(['props', 'children'], cloneHeader)
const spanChild = R.find(it => R.equals(it.type, 'span'), children)
spanChild.props.children = R.append(' -', spanChild.props.children)
return cloneHeader
} catch (e) {
return header
}
}
return header
}
return (
<Td header key={idx} width={width} textAlign={textAlign}>
{header || startCase(name)}
{!R.isNil(header) ? (
<>{attachOrderedByToComplexHeader(header) ?? header}</>
) : (
<span className={orderClasses}>
{startCase(name)}{' '}
{!R.isNil(orderedBy) && R.equals(name, orderedBy.code) && '-'}
</span>
)}
</Td>
)
}
const [innerElements, HeaderElement] = groupSecondHeader(elements)

View file

@ -56,7 +56,8 @@ const ETable = ({
sortBy,
createText = 'Add override',
forceAdd = false,
tbodyWrapperClass
tbodyWrapperClass,
orderedBy = null
}) => {
const [editingId, setEditingId] = useState(null)
const [adding, setAdding] = useState(false)
@ -155,6 +156,7 @@ const ETable = ({
actionColSize,
stripeWhen,
forceAdd,
orderedBy,
DEFAULT_COL_SIZE
}

View file

@ -1,6 +1,7 @@
import { makeStyles } from '@material-ui/core'
import classnames from 'classnames'
import { useSelect } from 'downshift'
import * as R from 'ramda'
import React from 'react'
import { ReactComponent as Arrowdown } from 'src/styling/icons/action/arrow/regular.svg'
@ -29,7 +30,9 @@ function Select({ className, label, items, ...props }) {
const selectClassNames = {
[classes.select]: true,
[classes.selectFiltered]: selectedItem !== props.default,
[classes.selectFiltered]: props.defaultAsFilter
? true
: !R.equals(selectedItem, props.default),
[classes.open]: isOpen
}

View file

@ -27,6 +27,7 @@ const Sidebar = ({
{loading && <P>Loading...</P>}
{!loading &&
data?.map((it, idx) => (
<div className={classes.linkWrapper} onClick={() => onClick(it)}>
<div
key={idx}
className={classnames({
@ -34,10 +35,10 @@ const Sidebar = ({
[classes.customRenderActiveLink]: itemRender && isSelected(it),
[classes.customRenderLink]: itemRender,
[classes.link]: true
})}
onClick={() => onClick(it)}>
})}>
{itemRender ? itemRender(it, isSelected(it)) : displayName(it)}
</div>
</div>
))}
{!loading && children}
</div>

View file

@ -30,7 +30,9 @@ export default {
boxShadow: `-200px 0px 0px 0px ${sidebarColor}`
}
},
linkWrapper: {
cursor: 'pointer'
},
link: {
extend: p,
position: 'relative',

View file

@ -19,6 +19,7 @@ export default {
},
buttonText: {
color: backgroundColor,
fontFamily: 'Mont',
fontSize: 15
},
icon: {

View file

@ -5,7 +5,7 @@ import * as R from 'ramda'
import React, { useContext } from 'react'
import AppContext from 'src/AppContext'
import { Tooltip } from 'src/components/Tooltip'
import { HoverableTooltip } from 'src/components/Tooltip'
import TitleSection from 'src/components/layout/TitleSection'
import DataTable from 'src/components/tables/DataTable'
import { H4, Info2, P } from 'src/components/typography'
@ -127,9 +127,9 @@ const Accounting = () => {
<span className={classes.operation}>
{it.description}
{!!it.extraInfo && (
<Tooltip width={175}>
<HoverableTooltip width={175}>
<P>{it.extraInfo}</P>
</Tooltip>
</HoverableTooltip>
)}
</span>
)

View file

@ -300,6 +300,7 @@ const Analytics = () => {
items={REPRESENTING_OPTIONS}
default={REPRESENTING_OPTIONS[0]}
selectedItem={representing}
defaultAsFilter
/>
<Select
label="Time period"
@ -307,6 +308,7 @@ const Analytics = () => {
items={PERIOD_OPTIONS}
default={PERIOD_OPTIONS[0]}
selectedItem={period}
defaultAsFilter
/>
</div>
<div className={classes.overview}>

View file

@ -7,7 +7,7 @@ import { utils as coinUtils } from 'lamassu-coins'
import * as R from 'ramda'
import React, { useState } from 'react'
import { Tooltip } from 'src/components/Tooltip'
import { HoverableTooltip } from 'src/components/Tooltip'
import { Link } from 'src/components/buttons'
import { Switch } from 'src/components/inputs'
import Sidebar from 'src/components/layout/Sidebar'
@ -186,13 +186,13 @@ const Blacklist = () => {
value={rejectAddressReuse}
/>
<Label2>{rejectAddressReuse ? 'On' : 'Off'}</Label2>
<Tooltip width={304}>
<HoverableTooltip width={304}>
<P>
The "Reject reused addresses" option means that all addresses
that are used once will be automatically rejected if there's
an attempt to use them again on a new transaction.
</P>
</Tooltip>
</HoverableTooltip>
</Box>
</Box>
<BlacklistTable

View file

@ -4,7 +4,7 @@ import gql from 'graphql-tag'
import * as R from 'ramda'
import React, { useState } from 'react'
import { Tooltip } from 'src/components/Tooltip'
import { HoverableTooltip } from 'src/components/Tooltip'
import { NamespacedTable as EditableTable } from 'src/components/editableTable'
import { Switch } from 'src/components/inputs'
import TitleSection from 'src/components/layout/TitleSection'
@ -54,7 +54,7 @@ const GET_INFO = gql`
const CashOut = ({ name: SCREEN_KEY }) => {
const classes = useStyles()
const [wizard, setWizard] = useState(false)
const { data } = useQuery(GET_INFO)
const { data, loading } = useQuery(GET_INFO)
const [saveConfig, { error }] = useMutation(SAVE_CONFIG, {
onCompleted: () => setWizard(false),
@ -81,6 +81,7 @@ const CashOut = ({ name: SCREEN_KEY }) => {
const wasNeverEnabled = it => R.compose(R.length, R.keys)(it) === 1
return (
!loading && (
<>
<TitleSection title="Cash-out">
<div className={classes.fudgeFactor}>
@ -95,16 +96,16 @@ const CashOut = ({ name: SCREEN_KEY }) => {
<Label2 className={classes.switchLabel}>
{fudgeFactorActive ? 'On' : 'Off'}
</Label2>
<Tooltip width={304}>
<HoverableTooltip width={304}>
<P>
Automatically accept customer deposits as complete if their
received amount is 100 crypto atoms or less.
</P>
<P>
(Crypto atoms are the smallest unit in each cryptocurrency. E.g.,
satoshis in Bitcoin, or wei in Ethereum.)
(Crypto atoms are the smallest unit in each cryptocurrency.
E.g., satoshis in Bitcoin, or wei in Ethereum.)
</P>
</Tooltip>
</HoverableTooltip>
</div>
</TitleSection>
<EditableTable
@ -134,6 +135,7 @@ const CashOut = ({ name: SCREEN_KEY }) => {
)}
</>
)
)
}
export default CashOut

View file

@ -8,6 +8,12 @@ import { getBillOptions } from 'src/utils/bill-options'
import { CURRENCY_MAX } from 'src/utils/constants'
import { transformNumber } from 'src/utils/number'
const widthsByNumberOfCassettes = {
2: { machine: 300, cassette: 225, zeroConf: 200 },
3: { machine: 210, cassette: 180, zeroConf: 200 },
4: { machine: 200, cassette: 150, zeroConf: 150 }
}
const DenominationsSchema = Yup.object().shape({
cassette1: Yup.number()
.label('Cassette 1')
@ -60,7 +66,7 @@ const getElements = (machines, locale = {}, classes) => {
{
name: 'id',
header: 'Machine',
width: 300,
width: widthsByNumberOfCassettes[maxNumberOfCassettes].machine,
view: it => machines.find(({ deviceId }) => deviceId === it).name,
size: 'sm',
editable: false
@ -76,7 +82,7 @@ const getElements = (machines, locale = {}, classes) => {
size: 'sm',
stripe: true,
textAlign: 'right',
width: (maxNumberOfCassettes > 2 ? 600 : 460) / maxNumberOfCassettes,
width: widthsByNumberOfCassettes[maxNumberOfCassettes].cassette,
suffix: fiatCurrency,
bold: bold,
view: it => it,
@ -99,7 +105,7 @@ const getElements = (machines, locale = {}, classes) => {
size: 'sm',
stripe: true,
textAlign: 'right',
width: maxNumberOfCassettes > 2 ? 150 : 290,
width: widthsByNumberOfCassettes[maxNumberOfCassettes].zeroConf,
input: NumberInput,
inputProps: {
decimalPlaces: 0

View file

@ -160,6 +160,7 @@ const CommissionsList = memo(
default={ORDER_OPTIONS[0]}
items={ORDER_OPTIONS}
selectedItem={orderProp}
defaultAsFilter
/>
</div>
<div className={classes.tableWrapper}>
@ -172,6 +173,7 @@ const CommissionsList = memo(
validationSchema={getListCommissionsSchema(localeConfig)}
data={tableData}
elements={commissionsList(data, currency)}
orderedBy={orderProp}
/>
</div>
</div>

View file

@ -23,7 +23,7 @@ const ALL_COINS = {
code: 'ALL_COINS'
}
const cashInAndOutHeaderStyle = { marginLeft: 6 }
const cashInAndOutHeaderStyle = { marginLeft: 6, whiteSpace: 'nowrap' }
const cashInHeader = (
<div>
@ -488,7 +488,7 @@ const getListCommissionsFields = (getData, currency, defaults) => {
{
name: 'cryptoCurrencies',
display: 'Crypto Currency',
width: 265,
width: 255,
view: R.prop(0),
size: 'sm',
editable: false
@ -510,7 +510,7 @@ const getListCommissionsFields = (getData, currency, defaults) => {
header: cashOutHeader,
name: 'cashOut',
display: 'Cash-out',
width: 130,
width: 140,
input: NumberInput,
textAlign: 'right',
greenText: true,

View file

@ -8,7 +8,7 @@ import { useState, React } from 'react'
import ErrorMessage from 'src/components/ErrorMessage'
import PromptWhenDirty from 'src/components/PromptWhenDirty'
import { MainStatus } from 'src/components/Status'
import { Tooltip } from 'src/components/Tooltip'
import { HoverableTooltip } from 'src/components/Tooltip'
import { ActionButton } from 'src/components/buttons'
import { Label1, P, H3 } from 'src/components/typography'
import {
@ -148,7 +148,7 @@ const EditableCard = ({
<div className={classes.cardHeader}>
{titleIcon}
<H3 className={classes.cardTitle}>{title}</H3>
<Tooltip width={304}></Tooltip>
<HoverableTooltip width={304}></HoverableTooltip>
</div>
{state && authorize && (
<div className={classnames(label1ClassNames)}>

View file

@ -6,7 +6,7 @@ import * as Yup from 'yup'
import ErrorMessage from 'src/components/ErrorMessage'
import Modal from 'src/components/Modal'
import { Tooltip } from 'src/components/Tooltip'
import { HoverableTooltip } from 'src/components/Tooltip'
import { Button } from 'src/components/buttons'
import { NumberInput, Autocomplete } from 'src/components/inputs/formik'
import { H3, TL1, P } from 'src/components/typography'
@ -99,7 +99,7 @@ const IndividualDiscountModal = ({
<div>
<div className={classes.discountRateWrapper}>
<H3>Define discount rate</H3>
<Tooltip width={304}>
<HoverableTooltip width={304}>
<P>
This is a percentage discount off of your existing
commission rates for a customer entering this code at
@ -110,7 +110,7 @@ const IndividualDiscountModal = ({
code is set for 50%, then you'll instead be charging 4%
on transactions using the code.
</P>
</Tooltip>
</HoverableTooltip>
</div>
<div className={classes.discountInput}>
<Field

View file

@ -6,7 +6,7 @@ import * as Yup from 'yup'
import ErrorMessage from 'src/components/ErrorMessage'
import Modal from 'src/components/Modal'
import { Tooltip } from 'src/components/Tooltip'
import { HoverableTooltip } from 'src/components/Tooltip'
import { Button } from 'src/components/buttons'
import { TextInput, NumberInput } from 'src/components/inputs/formik'
import { H3, TL1, P } from 'src/components/typography'
@ -69,7 +69,7 @@ const PromoCodesModal = ({ showModal, onClose, errorMsg, addCode }) => {
/>
<div className={classes.modalLabel2Wrapper}>
<H3 className={classes.modalLabel2}>Define discount rate</H3>
<Tooltip width={304}>
<HoverableTooltip width={304}>
<P>
This is a percentage discount off of your existing
commission rates for a customer entering this code at the
@ -80,7 +80,7 @@ const PromoCodesModal = ({ showModal, onClose, errorMsg, addCode }) => {
set for 50%, then you'll instead be charging 4% on
transactions using the code.
</P>
</Tooltip>
</HoverableTooltip>
</div>
<div className={classes.discountInput}>
<Field

View file

@ -5,7 +5,7 @@ import * as R from 'ramda'
import React from 'react'
import Stepper from 'src/components/Stepper'
import { Tooltip } from 'src/components/Tooltip'
import { HoverableTooltip } from 'src/components/Tooltip'
import { Button } from 'src/components/buttons'
import { Cashbox } from 'src/components/inputs/cashbox/Cashbox'
import { NumberInput, RadioGroup } from 'src/components/inputs/formik'
@ -188,12 +188,12 @@ const WizardStep = ({
classes.centerAlignment
)}>
<P>Since previous update</P>
<Tooltip width={215}>
<HoverableTooltip width={215}>
<P>
Number of bills inside the cash box, since the last
cash box changes.
</P>
</Tooltip>
</HoverableTooltip>
</div>
<div
className={classnames(

View file

@ -53,7 +53,7 @@ const Notifications = ({
const [error, setError] = useState(null)
const [editingKey, setEditingKey] = useState(null)
const { data } = useQuery(GET_INFO)
const { data, loading } = useQuery(GET_INFO)
const [saveConfig] = useMutation(SAVE_CONFIG, {
refetchQueries: ['getData'],
@ -101,6 +101,7 @@ const Notifications = ({
}
return (
!loading && (
<NotificationsCtx.Provider value={contextValue}>
{displayTitle && <TitleSection title="Notifications" />}
{displaySetup && (
@ -118,7 +119,12 @@ const Notifications = ({
title="Fiat balance alerts"
error={error && section === 'fiat'}>
<FiatBalanceAlerts section="fiat" max={100} fieldWidth={50} />
{displayOverrides && <FiatBalanceOverrides section="fiat" />}
{displayOverrides && (
<FiatBalanceOverrides
config={fromNamespace(namespaces.CASH_OUT)(data?.config)}
section="fiat"
/>
)}
</Section>
)}
{displayCryptoAlerts && (
@ -136,6 +142,7 @@ const Notifications = ({
)}
</NotificationsCtx.Provider>
)
)
}
export default Notifications

View file

@ -5,6 +5,7 @@ import * as Yup from 'yup'
import { Table as EditableTable } from 'src/components/editableTable'
import { NumberInput } from 'src/components/inputs/formik/'
import Autocomplete from 'src/components/inputs/formik/Autocomplete'
import { fromNamespace } from 'src/utils/config'
import { transformNumber } from 'src/utils/number'
import NotificationsCtx from '../NotificationsContext'
@ -23,7 +24,13 @@ const CASSETTE_LIST = [
CASSETTE_4_KEY
]
const FiatBalanceOverrides = ({ section }) => {
const widthsByNumberOfCassettes = {
2: { machine: 230, cassette: 250 },
3: { machine: 216, cassette: 270 },
4: { machine: 210, cassette: 204 }
}
const FiatBalanceOverrides = ({ config, section }) => {
const {
machines = [],
data,
@ -36,9 +43,13 @@ const FiatBalanceOverrides = ({ section }) => {
const setupValues = data?.fiatBalanceOverrides ?? []
const innerSetEditing = it => setEditing(NAME, it)
const cashoutConfig = it => fromNamespace(it)(config)
const overridenMachines = R.map(override => override.machine, setupValues)
const suggestionFilter = R.filter(
it => !R.contains(it.deviceId, overridenMachines)
it =>
!R.includes(it.deviceId, overridenMachines) &&
cashoutConfig(it.deviceId).active
)
const suggestions = suggestionFilter(machines)
@ -114,7 +125,7 @@ const FiatBalanceOverrides = ({ section }) => {
const elements = [
{
name: MACHINE_KEY,
width: 238,
width: widthsByNumberOfCassettes[maxNumberOfCassettes].machine,
size: 'sm',
view: viewMachine,
input: Autocomplete,
@ -132,7 +143,7 @@ const FiatBalanceOverrides = ({ section }) => {
elements.push({
name: `fillingPercentageCassette${it}`,
display: `Cash cassette ${it}`,
width: 155,
width: widthsByNumberOfCassettes[maxNumberOfCassettes].cassette,
textAlign: 'right',
doubleHeader: 'Cash Cassette Empty',
bold: true,

View file

@ -16,7 +16,7 @@ import { startCase } from 'src/utils/string'
import NotificationsCtx from '../NotificationsContext'
const channelSize = 129
const channelSize = 229
const sizes = {
balance: 152,
transactions: 184,
@ -26,7 +26,7 @@ const sizes = {
active: 263
}
const Row = ({ namespace, forceDisable }) => {
const Row = ({ namespace, forceDisable, shouldUpperCase }) => {
const { data: rawData, save: rawSave } = useContext(NotificationsCtx)
const save = R.compose(rawSave(null), toNamespace(namespace))
@ -53,7 +53,9 @@ const Row = ({ namespace, forceDisable }) => {
return (
<Tr>
<Td width={channelSize}>{startCase(namespace)}</Td>
<Td width={channelSize}>
{shouldUpperCase ? R.toUpper(namespace) : startCase(namespace)}
</Td>
<Cell name="balance" disabled={disabled} />
<Cell name="transactions" disabled={disabled} />
<Cell name="compliance" disabled={disabled} />
@ -84,7 +86,7 @@ const Setup = ({ wizard, forceDisable }) => {
</THead>
<TBody>
<Row namespace="email" forceDisable={forceDisable} />
<Row namespace="sms" forceDisable={forceDisable} />
<Row namespace="sms" shouldUpperCase forceDisable={forceDisable} />
<Row namespace="notificationCenter" forceDisable={forceDisable} />
</TBody>
</Table>

View file

@ -3,7 +3,7 @@ import { makeStyles } from '@material-ui/core/styles'
import gql from 'graphql-tag'
import React, { memo } from 'react'
import { Tooltip } from 'src/components/Tooltip'
import { HoverableTooltip } from 'src/components/Tooltip'
import { BooleanPropertiesTable } from 'src/components/booleanPropertiesTable'
import { Switch } from 'src/components/inputs'
import { H4, P, Label2 } from 'src/components/typography'
@ -66,7 +66,7 @@ const CoinATMRadar = memo(({ wizard }) => {
<div>
<div className={classes.header}>
<H4>Coin ATM Radar share settings</H4>
<Tooltip width={304}>
<HoverableTooltip width={304}>
<P>
For details on configuring this panel, please read the relevant
knowledgebase article{' '}
@ -78,7 +78,7 @@ const CoinATMRadar = memo(({ wizard }) => {
</a>
.
</P>
</Tooltip>
</HoverableTooltip>
</div>
<Row
title={'Share information?'}

View file

@ -5,7 +5,7 @@ import gql from 'graphql-tag'
import * as R from 'ramda'
import React, { useState } from 'react'
import { Tooltip } from 'src/components/Tooltip'
import { HoverableTooltip } from 'src/components/Tooltip'
import { Link } from 'src/components/buttons'
import { Switch } from 'src/components/inputs'
import TitleSection from 'src/components/layout/TitleSection'
@ -141,13 +141,13 @@ const Triggers = () => {
<Label2 className={classes.switchLabel}>
{rejectAddressReuse ? 'On' : 'Off'}
</Label2>
<Tooltip width={304}>
<HoverableTooltip width={304}>
<P>
This option requires a user to scan a different cryptocurrency
address if they attempt to scan one that had been previously
used for a transaction in your network
</P>
</Tooltip>
</HoverableTooltip>
</Box>
</Box>
)}

View file

@ -5,7 +5,7 @@ import gql from 'graphql-tag'
import React, { useState } from 'react'
import InfoMessage from 'src/components/InfoMessage'
import { Tooltip } from 'src/components/Tooltip'
import { HoverableTooltip } from 'src/components/Tooltip'
import { Button, SupportLinkButton } from 'src/components/buttons'
import { RadioGroup } from 'src/components/inputs'
import { H1, H4, P } from 'src/components/typography'
@ -102,7 +102,7 @@ function Twilio({ doContinue }) {
<H4 noMargin className={classnames(titleClasses)}>
Will you setup a two way machine or compliance?
</H4>
<Tooltip width={304}>
<HoverableTooltip width={304}>
<P>
Two-way machines allow your customers not only to buy (cash-in)
but also sell cryptocurrencies (cash-out).
@ -111,7 +111,7 @@ function Twilio({ doContinue }) {
Youll need an SMS service for cash-out transactions and for any
compliance triggers
</P>
</Tooltip>
</HoverableTooltip>
</Box>
<RadioGroup