chore: reformat code
This commit is contained in:
parent
3d930aa73b
commit
aedabcbdee
509 changed files with 6030 additions and 4266 deletions
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"trailingComma": "none",
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import js from '@eslint/js'
|
|||
import globals from 'globals'
|
||||
import pluginReact from 'eslint-plugin-react'
|
||||
import json from '@eslint/json'
|
||||
import css from '@eslint/css'
|
||||
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||
import reactCompiler from 'eslint-plugin-react-compiler'
|
||||
import eslintConfigPrettier from 'eslint-config-prettier/flat'
|
||||
|
|
@ -16,7 +15,13 @@ export default defineConfig([
|
|||
},
|
||||
{
|
||||
files: ['packages/admin-ui/**/*.{js,mjs,jsx}'],
|
||||
languageOptions: { sourceType: 'module', globals: globals.browser }
|
||||
languageOptions: {
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
...globals.browser,
|
||||
process: 'readonly'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['packages/server/**/*.{js,cjs}'],
|
||||
|
|
@ -35,15 +40,8 @@ export default defineConfig([
|
|||
language: 'json/json',
|
||||
extends: ['json/recommended']
|
||||
},
|
||||
{
|
||||
files: ['**/*.css'],
|
||||
plugins: { css },
|
||||
language: 'css/css',
|
||||
extends: ['css/recommended']
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
// 'no-unused-vars': 'on',
|
||||
'react/prop-types': 'off',
|
||||
'react/display-name': 'off',
|
||||
'react/no-unescaped-entities': 'off',
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ const Main = () => {
|
|||
onCompleted: userResponse => {
|
||||
if (!userData && userResponse?.userData)
|
||||
setUserData(userResponse.userData)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const route = location.pathname
|
||||
|
|
|
|||
|
|
@ -42,4 +42,4 @@
|
|||
|
||||
.navButton:hover {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import classnames from 'classnames'
|
|||
const cardState = Object.freeze({
|
||||
DEFAULT: 'default',
|
||||
SHRUNK: 'shrunk',
|
||||
EXPANDED: 'expanded'
|
||||
EXPANDED: 'expanded',
|
||||
})
|
||||
|
||||
const CollapsibleCard = ({ className, state, shrunkComponent, children }) => {
|
||||
|
|
@ -19,7 +19,7 @@ const CollapsibleCard = ({ className, state, shrunkComponent, children }) => {
|
|||
}
|
||||
|
||||
CollapsibleCard.propTypes = {
|
||||
shrunkComponent: PropTypes.node.isRequired
|
||||
shrunkComponent: PropTypes.node.isRequired,
|
||||
}
|
||||
|
||||
export default CollapsibleCard
|
||||
|
|
|
|||
|
|
@ -101,5 +101,5 @@ export const ConfirmDialog = memo(
|
|||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ const CopyToClipboard = ({
|
|||
buttonClassname,
|
||||
children,
|
||||
wrapperClassname,
|
||||
removeSpace = true
|
||||
removeSpace = true,
|
||||
}) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ const CopyToClipboard = ({
|
|||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export default CopyToClipboard
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export const DeleteDialog = ({
|
|||
item = 'item',
|
||||
confirmationMessage = `Are you sure you want to delete this ${item}?`,
|
||||
extraMessage,
|
||||
errorMessage = ''
|
||||
errorMessage = '',
|
||||
}) => {
|
||||
return (
|
||||
<Dialog open={open} aria-labelledby="form-dialog-title">
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ const ImagePopper = memo(
|
|||
</div>
|
||||
</ClickAwayListener>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default ImagePopper
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { H1 } from 'src/components/typography'
|
|||
import CloseIcon from 'src/styling/icons/action/close/zodiac.svg?react'
|
||||
|
||||
export const InformativeDialog = memo(
|
||||
({ title = '', open, onDissmised, disabled = false, data, ...props }) => {
|
||||
({ title = '', open, onDissmised, data, ...props }) => {
|
||||
const innerOnClose = () => {
|
||||
onDissmised()
|
||||
}
|
||||
|
|
@ -16,8 +16,8 @@ export const InformativeDialog = memo(
|
|||
<Dialog
|
||||
PaperProps={{
|
||||
style: {
|
||||
borderRadius: 8
|
||||
}
|
||||
borderRadius: 8,
|
||||
},
|
||||
}}
|
||||
fullWidth
|
||||
open={open}
|
||||
|
|
@ -35,5 +35,5 @@ export const InformativeDialog = memo(
|
|||
<DialogContent>{data}</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import { formatDate } from 'src/utils/timezones'
|
|||
import Popper from './Popper'
|
||||
import DateRangePicker from './date-range-picker/DateRangePicker'
|
||||
import { RadioGroup } from './inputs'
|
||||
import typographyStyles from './typography/styles'
|
||||
import { H4, Info1, Label1, Label2 } from './typography/index.jsx'
|
||||
|
||||
const DateContainer = ({ date, children }) => {
|
||||
|
|
@ -31,7 +30,7 @@ const DateContainer = ({ date, children }) => {
|
|||
<div className="flex flex-col">
|
||||
<Label2 noMargin>{`${format(
|
||||
'MMM',
|
||||
date
|
||||
date,
|
||||
)} ${format('yyyy', date)}`}</Label2>
|
||||
<Label1 noMargin className="text-comet">
|
||||
{format('EEEE', date)}
|
||||
|
|
@ -57,7 +56,7 @@ const LogsDownloaderPopover = ({
|
|||
getLogs,
|
||||
timezone,
|
||||
simplified,
|
||||
className
|
||||
className,
|
||||
}) => {
|
||||
const [selectedRadio, setSelectedRadio] = useState(ALL)
|
||||
const [selectedAdvancedRadio, setSelectedAdvancedRadio] = useState(ADVANCED)
|
||||
|
|
@ -65,12 +64,12 @@ const LogsDownloaderPopover = ({
|
|||
const [range, setRange] = useState({ from: null, until: null })
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
const [fetchLogs] = useLazyQuery(query, {
|
||||
onCompleted: data => createLogsFile(getLogs(data), range)
|
||||
onCompleted: data => createLogsFile(getLogs(data), range),
|
||||
})
|
||||
|
||||
const dateRangePickerClasses = {
|
||||
'block h-full': selectedRadio === RANGE,
|
||||
hidden: selectedRadio === ALL
|
||||
hidden: selectedRadio === ALL,
|
||||
}
|
||||
|
||||
const handleRadioButtons = evt => {
|
||||
|
|
@ -88,7 +87,7 @@ const LogsDownloaderPopover = ({
|
|||
(from, until) => {
|
||||
setRange({ from, until })
|
||||
},
|
||||
[setRange]
|
||||
[setRange],
|
||||
)
|
||||
|
||||
const downloadLogs = (range, args) => {
|
||||
|
|
@ -97,8 +96,8 @@ const LogsDownloaderPopover = ({
|
|||
variables: {
|
||||
...args,
|
||||
simplified: selectedAdvancedRadio === SIMPLIFIED,
|
||||
excludeTestingCustomers: true
|
||||
}
|
||||
excludeTestingCustomers: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -112,8 +111,8 @@ const LogsDownloaderPopover = ({
|
|||
from: range.from,
|
||||
until: range.until,
|
||||
simplified: selectedAdvancedRadio === SIMPLIFIED,
|
||||
excludeTestingCustomers: true
|
||||
}
|
||||
excludeTestingCustomers: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -124,7 +123,7 @@ const LogsDownloaderPopover = ({
|
|||
}
|
||||
|
||||
const blob = new window.Blob([logs], {
|
||||
type: 'text/plain;charset=utf-8'
|
||||
type: 'text/plain;charset=utf-8',
|
||||
})
|
||||
|
||||
FileSaver.saveAs(
|
||||
|
|
@ -132,8 +131,8 @@ const LogsDownloaderPopover = ({
|
|||
selectedRadio === ALL
|
||||
? `${formatDateFile(new Date())}_${name}.csv`
|
||||
: `${formatDateFile(range.from)}_${formatDateFile(
|
||||
range.until
|
||||
)}_${name}.csv`
|
||||
range.until,
|
||||
)}_${name}.csv`,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -147,12 +146,12 @@ const LogsDownloaderPopover = ({
|
|||
|
||||
const radioButtonOptions = [
|
||||
{ display: 'All logs', code: ALL },
|
||||
{ display: 'Date range', code: RANGE }
|
||||
{ display: 'Date range', code: RANGE },
|
||||
]
|
||||
|
||||
const advancedRadioButtonOptions = [
|
||||
{ display: 'Advanced logs', code: ADVANCED },
|
||||
{ display: 'Simplified logs', code: SIMPLIFIED }
|
||||
{ display: 'Simplified logs', code: SIMPLIFIED },
|
||||
]
|
||||
|
||||
const open = Boolean(anchorEl)
|
||||
|
|
@ -201,9 +200,9 @@ const LogsDownloaderPopover = ({
|
|||
hours: 23,
|
||||
minutes: 59,
|
||||
seconds: 59,
|
||||
milliseconds: 999
|
||||
milliseconds: 999,
|
||||
},
|
||||
new Date()
|
||||
new Date(),
|
||||
)}
|
||||
onRangeChange={handleRangeChange}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -17,14 +17,12 @@ const Modal = ({
|
|||
infoPanel,
|
||||
handleClose,
|
||||
children,
|
||||
secondaryModal,
|
||||
className,
|
||||
closeOnEscape,
|
||||
closeOnBackdropClick,
|
||||
...props
|
||||
}) => {
|
||||
const TitleCase = small ? H4 : H1
|
||||
const closeSize = xl ? 28 : small ? 16 : 20
|
||||
|
||||
const innerClose = (evt, reason) => {
|
||||
if (!closeOnBackdropClick && reason === 'backdropClick') return
|
||||
|
|
@ -44,7 +42,7 @@ const Modal = ({
|
|||
style={{ width, height, minHeight: height ?? 400 }}
|
||||
className={classnames(
|
||||
'flex flex-col max-h-[90vh] rounded-lg outline-0',
|
||||
className
|
||||
className,
|
||||
)}>
|
||||
<div className="flex">
|
||||
{title && (
|
||||
|
|
@ -78,11 +76,11 @@ const Modal = ({
|
|||
style={{
|
||||
width,
|
||||
height: infoPanelHeight,
|
||||
minHeight: infoPanelHeight ?? 200
|
||||
minHeight: infoPanelHeight ?? 200,
|
||||
}}
|
||||
className={classnames(
|
||||
'mt-4 flex flex-col max-h-[90vh] overflow-y-auto rounded-lg outline-0',
|
||||
className
|
||||
className,
|
||||
)}>
|
||||
<div className="w-full flex flex-col flex-1 py-0 px-6">
|
||||
{infoPanel}
|
||||
|
|
|
|||
|
|
@ -52,28 +52,28 @@ const NotificationCenter = ({
|
|||
hasUnreadProp,
|
||||
buttonCoords,
|
||||
popperRef,
|
||||
refetchHasUnreadHeader
|
||||
refetchHasUnreadHeader,
|
||||
}) => {
|
||||
const { data, loading } = useQuery(GET_NOTIFICATIONS, {
|
||||
pollInterval: 60000
|
||||
pollInterval: 60000,
|
||||
})
|
||||
const [xOffset, setXoffset] = useState(300)
|
||||
|
||||
const [showingUnread, setShowingUnread] = useState(false)
|
||||
const machines = R.compose(
|
||||
R.map(R.prop('name')),
|
||||
R.indexBy(R.prop('deviceId'))
|
||||
R.indexBy(R.prop('deviceId')),
|
||||
)(R.path(['machines'])(data) ?? [])
|
||||
const notifications = R.path(['notifications'])(data) ?? []
|
||||
const [hasUnread, setHasUnread] = useState(hasUnreadProp)
|
||||
|
||||
const [toggleClearNotification] = useMutation(TOGGLE_CLEAR_NOTIFICATION, {
|
||||
onError: () => console.error('Error while clearing notification'),
|
||||
refetchQueries: () => ['getNotifications']
|
||||
refetchQueries: () => ['getNotifications'],
|
||||
})
|
||||
const [clearAllNotifications] = useMutation(CLEAR_ALL_NOTIFICATIONS, {
|
||||
onError: () => console.error('Error while clearing all notifications'),
|
||||
refetchQueries: () => ['getNotifications']
|
||||
refetchQueries: () => ['getNotifications'],
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -103,7 +103,7 @@ const NotificationCenter = ({
|
|||
valid={n.valid}
|
||||
toggleClear={() =>
|
||||
toggleClearNotification({
|
||||
variables: { id: n.id, read: !n.read }
|
||||
variables: { id: n.id, read: !n.read },
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
|
@ -121,7 +121,7 @@ const NotificationCenter = ({
|
|||
className={classes.notificationIcon}
|
||||
style={{
|
||||
top: buttonCoords?.y ?? 0,
|
||||
left: buttonCoords?.x ? buttonCoords.x - xOffset : 0
|
||||
left: buttonCoords?.x ? buttonCoords.x - xOffset : 0,
|
||||
}}>
|
||||
<NotificationIconZodiac />
|
||||
{hasUnread && <div className={classes.hasUnread} />}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@
|
|||
box-shadow: 0 0 14px 0 rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
.container @media only screen and (max-width: 1920px) {
|
||||
width: 30vw;
|
||||
@media only screen and (max-width: 1920px) {
|
||||
.container {
|
||||
width: 30vw;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
|
|
@ -75,7 +77,7 @@
|
|||
}
|
||||
|
||||
.unread {
|
||||
background-color: var(--spring3)
|
||||
background-color: var(--spring3);
|
||||
}
|
||||
|
||||
.notificationRowIcon {
|
||||
|
|
@ -83,11 +85,11 @@
|
|||
}
|
||||
|
||||
.notificationRowIcon > * {
|
||||
margin-left: 24px
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
.readIconWrapper {
|
||||
flex-grow: 1
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.unreadIcon {
|
||||
|
|
@ -118,7 +120,7 @@
|
|||
}
|
||||
|
||||
.notificationBody {
|
||||
margin: 0
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.notificationSubtitle {
|
||||
|
|
@ -142,4 +144,4 @@
|
|||
height: 9px;
|
||||
background-color: var(--spring);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,37 +12,36 @@ import classes from './NotificationCenter.module.css'
|
|||
const types = {
|
||||
transaction: {
|
||||
display: 'Transactions',
|
||||
icon: <Transaction height={16} width={16} />
|
||||
icon: <Transaction height={16} width={16} />,
|
||||
},
|
||||
highValueTransaction: {
|
||||
display: 'Transactions',
|
||||
icon: <Transaction height={16} width={16} />
|
||||
icon: <Transaction height={16} width={16} />,
|
||||
},
|
||||
fiatBalance: {
|
||||
display: 'Maintenance',
|
||||
icon: <Wrench height={16} width={16} />
|
||||
icon: <Wrench height={16} width={16} />,
|
||||
},
|
||||
cryptoBalance: {
|
||||
display: 'Maintenance',
|
||||
icon: <Wrench height={16} width={16} />
|
||||
icon: <Wrench height={16} width={16} />,
|
||||
},
|
||||
compliance: {
|
||||
display: 'Compliance',
|
||||
icon: <WarningIcon height={16} width={16} />
|
||||
icon: <WarningIcon height={16} width={16} />,
|
||||
},
|
||||
error: { display: 'Error', icon: <WarningIcon height={16} width={16} /> }
|
||||
error: { display: 'Error', icon: <WarningIcon height={16} width={16} /> },
|
||||
}
|
||||
|
||||
const NotificationRow = ({
|
||||
id,
|
||||
type,
|
||||
detail,
|
||||
message,
|
||||
deviceName,
|
||||
created,
|
||||
read,
|
||||
valid,
|
||||
toggleClear
|
||||
toggleClear,
|
||||
}) => {
|
||||
const typeDisplay = R.path([type, 'display'])(types) ?? null
|
||||
const icon = R.path([type, 'icon'])(types) ?? (
|
||||
|
|
@ -50,7 +49,7 @@ const NotificationRow = ({
|
|||
)
|
||||
const age = prettyMs(new Date().getTime() - new Date(created).getTime(), {
|
||||
compact: true,
|
||||
verbose: true
|
||||
verbose: true,
|
||||
})
|
||||
const notificationTitle =
|
||||
typeDisplay && deviceName
|
||||
|
|
@ -61,13 +60,13 @@ const NotificationRow = ({
|
|||
|
||||
const iconClass = {
|
||||
[classes.readIcon]: read,
|
||||
[classes.unreadIcon]: !read
|
||||
[classes.unreadIcon]: !read,
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
classes.notificationRow,
|
||||
!read && valid ? classes.unread : ''
|
||||
!read && valid ? classes.unread : '',
|
||||
)}>
|
||||
<div className={classes.notificationRowIcon}>
|
||||
<div>{icon}</div>
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
import NotificationCenter from './NotificationCenter'
|
||||
|
||||
export default NotificationCenter
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ const Popover = ({ children, bgColor = white, className, ...props }) => {
|
|||
top: ['bottom'],
|
||||
bottom: ['top'],
|
||||
left: ['right'],
|
||||
right: ['left']
|
||||
right: ['left'],
|
||||
}
|
||||
|
||||
const modifiers = [
|
||||
|
|
@ -22,36 +22,36 @@ const Popover = ({ children, bgColor = white, className, ...props }) => {
|
|||
name: 'flip',
|
||||
enabled: R.defaultTo(false, props.flip),
|
||||
options: {
|
||||
allowedAutoPlacements: flipPlacements[props.placement]
|
||||
}
|
||||
allowedAutoPlacements: flipPlacements[props.placement],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'preventOverflow',
|
||||
enabled: true,
|
||||
options: {
|
||||
rootBoundary: 'scrollParent'
|
||||
}
|
||||
rootBoundary: 'scrollParent',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'offset',
|
||||
enabled: true,
|
||||
options: {
|
||||
offset: [0, 10]
|
||||
}
|
||||
offset: [0, 10],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'arrow',
|
||||
enabled: R.defaultTo(true, props.showArrow),
|
||||
options: {
|
||||
element: arrowRef
|
||||
}
|
||||
element: arrowRef,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'computeStyles',
|
||||
options: {
|
||||
gpuAcceleration: false
|
||||
}
|
||||
}
|
||||
gpuAcceleration: false,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -30,4 +30,4 @@
|
|||
|
||||
.tooltip[data-popper-placement^='right'] > div > span {
|
||||
left: -4px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ const SearchBox = memo(
|
|||
filters = [],
|
||||
options = [],
|
||||
inputPlaceholder = '',
|
||||
size,
|
||||
onChange,
|
||||
...props
|
||||
}) => {
|
||||
|
|
@ -20,7 +19,7 @@ const SearchBox = memo(
|
|||
|
||||
const inputClasses = {
|
||||
'flex flex-1 h-8 px-2 py-2 font-md items-center rounded-2xl bg-zircon text-comet': true,
|
||||
'rounded-b-none': popupOpen
|
||||
'rounded-b-none': popupOpen,
|
||||
}
|
||||
|
||||
const innerOnChange = filters => onChange(filters)
|
||||
|
|
@ -57,7 +56,7 @@ const SearchBox = memo(
|
|||
placeholder={inputPlaceholder}
|
||||
inputProps={{
|
||||
className: 'font-bold',
|
||||
...params.inputProps
|
||||
...params.inputProps,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
|
@ -74,10 +73,11 @@ const SearchBox = memo(
|
|||
<div className="w-[88%] h-[1px] my-p mx-auto border-1 border-comet" />
|
||||
{children}
|
||||
</Paper>
|
||||
)
|
||||
}} />
|
||||
);
|
||||
}
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
export default SearchBox
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const SearchFilter = ({
|
|||
filters,
|
||||
onFilterDelete,
|
||||
deleteAllFilters,
|
||||
entries = 0
|
||||
entries = 0,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
|
|
@ -33,7 +33,7 @@ const SearchFilter = ({
|
|||
<Label3 className="text-comet m-auto mr-3">{`${entries} ${singularOrPlural(
|
||||
entries,
|
||||
`entry`,
|
||||
`entries`
|
||||
`entries`,
|
||||
)}`}</Label3>
|
||||
}
|
||||
<ActionButton
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@ const Stepper = memo(({ steps, currentStep, color = 'spring', className }) => {
|
|||
const separatorClasses = {
|
||||
'w-7 h-[2px] border-2 z-1': true,
|
||||
'border-spring': color === 'spring',
|
||||
'border-zodiac': color === 'zodiac'
|
||||
'border-zodiac': color === 'zodiac',
|
||||
}
|
||||
|
||||
const separatorEmptyClasses = {
|
||||
'w-7 h-[2px] border-2 z-1': true,
|
||||
'border-dust': color === 'spring',
|
||||
'border-comet': color === 'zodiac'
|
||||
'border-comet': color === 'zodiac',
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@
|
|||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { TL1 } from './typography'
|
|||
const Subtitle = memo(({ children, className, extraMarginTop }) => {
|
||||
const classNames = {
|
||||
'text-comet my-4': true,
|
||||
'mt-18': extraMarginTop
|
||||
'mt-18': extraMarginTop,
|
||||
}
|
||||
|
||||
return <TL1 className={classnames(classNames, className)}>{children}</TL1>
|
||||
|
|
|
|||
|
|
@ -4,30 +4,6 @@ import React, { useState, memo } from 'react'
|
|||
import Popper from 'src/components/Popper'
|
||||
import HelpIcon from 'src/styling/icons/action/help/zodiac.svg?react'
|
||||
|
||||
const useStyles = {
|
||||
transparentButton: {
|
||||
border: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
outline: 'none',
|
||||
cursor: 'pointer',
|
||||
marginTop: 4
|
||||
},
|
||||
relativelyPositioned: {
|
||||
position: 'relative'
|
||||
},
|
||||
safeSpace: {
|
||||
position: 'absolute',
|
||||
backgroundColor: '#0000',
|
||||
height: 40,
|
||||
left: '-50%',
|
||||
width: '200%'
|
||||
},
|
||||
popoverContent: ({ width }) => ({
|
||||
width,
|
||||
padding: [[10, 15]]
|
||||
})
|
||||
}
|
||||
|
||||
const usePopperHandler = () => {
|
||||
const [helpPopperAnchorEl, setHelpPopperAnchorEl] = useState(null)
|
||||
|
||||
|
|
@ -50,7 +26,7 @@ const usePopperHandler = () => {
|
|||
helpPopperOpen,
|
||||
handleOpenHelpPopper,
|
||||
openHelpPopper,
|
||||
handleCloseHelpPopper
|
||||
handleCloseHelpPopper,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,27 +25,27 @@ const BooleanPropertiesTable = memo(
|
|||
const [editing, setEditing] = useState(forcedEditing)
|
||||
|
||||
const initialValues = R.fromPairs(
|
||||
elements.map(it => [it.name, data[it.name]?.toString() ?? 'false'])
|
||||
elements.map(it => [it.name, data[it.name]?.toString() ?? 'false']),
|
||||
)
|
||||
|
||||
const validationSchema = Yup.object().shape(
|
||||
R.fromPairs(
|
||||
elements.map(it => [
|
||||
it.name,
|
||||
Yup.mixed().oneOf(['true', 'false', true, false]).required()
|
||||
])
|
||||
)
|
||||
Yup.mixed().oneOf(['true', 'false', true, false]).required(),
|
||||
]),
|
||||
),
|
||||
)
|
||||
|
||||
const innerSave = async values => {
|
||||
const toBoolean = (num, _) => R.equals(num, 'true')
|
||||
const toBoolean = num => R.equals(num, 'true')
|
||||
save(R.mapObjIndexed(toBoolean, R.filter(R.complement(R.isNil))(values)))
|
||||
setEditing(false)
|
||||
}
|
||||
|
||||
const radioButtonOptions = [
|
||||
{ display: 'Yes', code: 'true' },
|
||||
{ display: 'No', code: 'false' }
|
||||
{ display: 'No', code: 'false' },
|
||||
]
|
||||
return (
|
||||
<div className="flex w-sm flex-col ">
|
||||
|
|
@ -117,7 +117,7 @@ const BooleanPropertiesTable = memo(
|
|||
</Formik>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default BooleanPropertiesTable
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ const ActionButton = memo(
|
|||
[moduleStyles.secondary]: color === 'secondary',
|
||||
[moduleStyles.spring]: color === 'spring',
|
||||
[moduleStyles.tomato]: color === 'tomato',
|
||||
[moduleStyles.center]: center
|
||||
[moduleStyles.center]: center,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -35,7 +35,7 @@ const ActionButton = memo(
|
|||
<div
|
||||
className={classnames(
|
||||
moduleStyles.actionButtonIcon,
|
||||
moduleStyles.actionButtonIconActive
|
||||
moduleStyles.actionButtonIconActive,
|
||||
)}>
|
||||
<InverseIcon />
|
||||
</div>
|
||||
|
|
@ -43,7 +43,7 @@ const ActionButton = memo(
|
|||
{children && <div>{children}</div>}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default ActionButton
|
||||
|
|
|
|||
|
|
@ -4,18 +4,18 @@ import {
|
|||
subheaderColor,
|
||||
subheaderDarkColor,
|
||||
offColor,
|
||||
offDarkColor
|
||||
offDarkColor,
|
||||
} from 'src/styling/variables'
|
||||
|
||||
const colors = (color1, color2, color3) => {
|
||||
return {
|
||||
backgroundColor: color1,
|
||||
'&:hover': {
|
||||
backgroundColor: color2
|
||||
backgroundColor: color2,
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: color3
|
||||
}
|
||||
backgroundColor: color3,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -30,23 +30,23 @@ export default {
|
|||
height: buttonHeight,
|
||||
color: fontColor,
|
||||
'&:active': {
|
||||
color: white
|
||||
}
|
||||
color: white,
|
||||
},
|
||||
},
|
||||
primary: {
|
||||
extend: colors(subheaderColor, subheaderDarkColor, offColor),
|
||||
'&:active': {
|
||||
color: white,
|
||||
'& $buttonIcon': {
|
||||
display: 'none'
|
||||
display: 'none',
|
||||
},
|
||||
'& $buttonIconActive': {
|
||||
display: 'block'
|
||||
}
|
||||
display: 'block',
|
||||
},
|
||||
},
|
||||
'& $buttonIconActive': {
|
||||
display: 'none'
|
||||
}
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
secondary: {
|
||||
extend: colors(offColor, offDarkColor, white),
|
||||
|
|
@ -54,17 +54,17 @@ export default {
|
|||
'&:active': {
|
||||
color: fontColor,
|
||||
'& $buttonIcon': {
|
||||
display: 'flex'
|
||||
display: 'flex',
|
||||
},
|
||||
'& $buttonIconActive': {
|
||||
display: 'none'
|
||||
}
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
'& $buttonIcon': {
|
||||
display: 'none'
|
||||
display: 'none',
|
||||
},
|
||||
'& $buttonIconActive': {
|
||||
display: 'flex'
|
||||
}
|
||||
}
|
||||
display: 'flex',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,15 +29,15 @@ const ActionButton = memo(
|
|||
'text-white',
|
||||
{
|
||||
[moduleStyles.buttonSm]: size === 'sm',
|
||||
[moduleStyles.buttonXl]: size === 'xl'
|
||||
}
|
||||
[moduleStyles.buttonXl]: size === 'xl',
|
||||
},
|
||||
)}
|
||||
{...props}>
|
||||
{children}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default ActionButton
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@
|
|||
.buttonXl {
|
||||
composes: h1 from '../typography/typography.module.css';
|
||||
height: 61px;
|
||||
border-radius: 15px
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.buttonSm {
|
||||
height: 32px;
|
||||
padding: 0 16px;
|
||||
border-radius: 8px
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.button:disabled {
|
||||
|
|
@ -46,4 +46,4 @@
|
|||
margin-top: 2px;
|
||||
background-color: var(--spring2);
|
||||
box-shadow: 0 2px var(--spring4);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ const FeatureButton = memo(
|
|||
classes.baseButton,
|
||||
classes.roundButton,
|
||||
classes.primary,
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}>
|
||||
{Icon && (
|
||||
|
|
@ -23,7 +23,7 @@ const FeatureButton = memo(
|
|||
<div
|
||||
className={classnames(
|
||||
classes.buttonIcon,
|
||||
classes.buttonIconActive
|
||||
classes.buttonIconActive,
|
||||
)}>
|
||||
<InverseIcon />
|
||||
</div>
|
||||
|
|
@ -31,7 +31,7 @@ const FeatureButton = memo(
|
|||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default FeatureButton
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ const IDButton = memo(
|
|||
className,
|
||||
Icon,
|
||||
InverseIcon,
|
||||
popoverWidth = 152,
|
||||
children,
|
||||
popoverClassname,
|
||||
...props
|
||||
|
|
@ -25,11 +24,11 @@ const IDButton = memo(
|
|||
[classes.idButton]: true,
|
||||
[classes.primary]: true,
|
||||
[classes.open]: open,
|
||||
[classes.closed]: !open
|
||||
[classes.closed]: !open,
|
||||
}
|
||||
|
||||
const iconClassNames = {
|
||||
[classes.buttonIcon]: true
|
||||
[classes.buttonIcon]: true,
|
||||
}
|
||||
|
||||
const handleClick = event => {
|
||||
|
|
@ -74,7 +73,7 @@ const IDButton = memo(
|
|||
</Popover>
|
||||
</>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default IDButton
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const Link = memo(
|
|||
[classes.primary]: color === 'primary',
|
||||
[classes.secondary]: color === 'secondary',
|
||||
[classes.noColor]: color === 'noColor',
|
||||
[classes.action]: color === 'action'
|
||||
[classes.action]: color === 'action',
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -21,7 +21,7 @@ const Link = memo(
|
|||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default Link
|
||||
|
|
|
|||
|
|
@ -44,4 +44,4 @@
|
|||
.action:hover {
|
||||
box-shadow: none;
|
||||
background-color: rgba(72, 246, 148, 0.8);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,14 +12,14 @@ const SubpageButton = memo(
|
|||
InverseIcon,
|
||||
toggle,
|
||||
forceDisable = false,
|
||||
children
|
||||
children,
|
||||
}) => {
|
||||
const [active, setActive] = useState(false)
|
||||
const isActive = forceDisable ? false : active
|
||||
const classNames = {
|
||||
[classes.button]: true,
|
||||
[classes.normal]: !isActive,
|
||||
[classes.active]: isActive
|
||||
[classes.active]: isActive,
|
||||
}
|
||||
|
||||
const normalButton = <Icon className={classes.buttonIcon} />
|
||||
|
|
@ -29,14 +29,14 @@ const SubpageButton = memo(
|
|||
<InverseIcon
|
||||
className={classnames(
|
||||
classes.buttonIcon,
|
||||
classes.buttonIconActiveLeft
|
||||
classes.buttonIconActiveLeft,
|
||||
)}
|
||||
/>
|
||||
<H4 className="text-white">{children}</H4>
|
||||
<CancelIconInverse
|
||||
className={classnames(
|
||||
classes.buttonIcon,
|
||||
classes.buttonIconActiveRight
|
||||
classes.buttonIconActiveRight,
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
|
|
@ -56,7 +56,7 @@ const SubpageButton = memo(
|
|||
{isActive ? activeButton : normalButton}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default SubpageButton
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
}
|
||||
|
||||
.active:hover {
|
||||
background-color: var(--comet);
|
||||
background-color: var(--comet);
|
||||
}
|
||||
|
||||
.buttonIcon {
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ export default {
|
|||
extend: baseButton,
|
||||
padding: 0,
|
||||
color: white,
|
||||
borderRadius: baseButton.height / 2
|
||||
borderRadius: baseButton.height / 2,
|
||||
},
|
||||
normalButton: {
|
||||
width: baseButton.height
|
||||
width: baseButton.height,
|
||||
},
|
||||
activeButton: {
|
||||
display: 'flex',
|
||||
|
|
@ -21,26 +21,26 @@ export default {
|
|||
fontWeight: 'bold',
|
||||
padding: '0 5px',
|
||||
'&:hover': {
|
||||
backgroundColor: offColor
|
||||
}
|
||||
backgroundColor: offColor,
|
||||
},
|
||||
},
|
||||
buttonIcon: {
|
||||
width: 16,
|
||||
height: 16,
|
||||
overflow: 'visible',
|
||||
'& g': {
|
||||
strokeWidth: 1.8
|
||||
}
|
||||
strokeWidth: 1.8,
|
||||
},
|
||||
},
|
||||
buttonIconActiveLeft: {
|
||||
marginRight: 12,
|
||||
marginLeft: 4
|
||||
marginLeft: 4,
|
||||
},
|
||||
buttonIconActiveRight: {
|
||||
marginRight: 5,
|
||||
marginLeft: 20
|
||||
marginLeft: 20,
|
||||
},
|
||||
white: {
|
||||
color: white
|
||||
}
|
||||
color: white,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,5 +15,5 @@ export {
|
|||
IDButton,
|
||||
AddButton,
|
||||
SupportLinkButton,
|
||||
SubpageButton
|
||||
SubpageButton,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
lastDayOfMonth,
|
||||
startOfMonth,
|
||||
startOfWeek,
|
||||
sub
|
||||
sub,
|
||||
} from 'date-fns/fp'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
|
|
@ -24,7 +24,7 @@ const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => {
|
|||
const [currentDisplayedMonth, setCurrentDisplayedMonth] = useState(new Date())
|
||||
|
||||
const weekdays = Array.from(Array(7)).map((_, i) =>
|
||||
format('EEEEE', add({ days: i }, startOfWeek(new Date())))
|
||||
format('EEEEE', add({ days: i }, startOfWeek(new Date()))),
|
||||
)
|
||||
|
||||
const monthLength = month => getDaysInMonth(month)
|
||||
|
|
@ -33,21 +33,21 @@ const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => {
|
|||
const lastMonth = sub({ months: 1 }, month)
|
||||
const lastMonthRange = R.range(0, getDay(startOfMonth(month))).reverse()
|
||||
const lastMonthDays = R.map(i =>
|
||||
sub({ days: i }, lastDayOfMonth(lastMonth))
|
||||
sub({ days: i }, lastDayOfMonth(lastMonth)),
|
||||
)(lastMonthRange)
|
||||
|
||||
const thisMonthRange = R.range(0, monthLength(month))
|
||||
const thisMonthDays = R.map(i => add({ days: i }, startOfMonth(month)))(
|
||||
thisMonthRange
|
||||
thisMonthRange,
|
||||
)
|
||||
|
||||
const nextMonth = add({ months: 1 }, month)
|
||||
const nextMonthRange = R.range(
|
||||
0,
|
||||
42 - lastMonthDays.length - thisMonthDays.length
|
||||
42 - lastMonthDays.length - thisMonthDays.length,
|
||||
)
|
||||
const nextMonthDays = R.map(i => add({ days: i }, startOfMonth(nextMonth)))(
|
||||
nextMonthRange
|
||||
nextMonthRange,
|
||||
)
|
||||
|
||||
return R.concat(R.concat(lastMonthDays, thisMonthDays), nextMonthDays)
|
||||
|
|
@ -63,7 +63,7 @@ const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => {
|
|||
isSameMonth(minDate, prevMonth) ||
|
||||
differenceInMonths(minDate, prevMonth) > 0
|
||||
? prevMonth
|
||||
: currentDisplayedMonth
|
||||
: currentDisplayedMonth,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => {
|
|||
isSameMonth(maxDate, nextMonth) ||
|
||||
differenceInMonths(nextMonth, maxDate) > 0
|
||||
? nextMonth
|
||||
: currentDisplayedMonth
|
||||
: currentDisplayedMonth,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -91,7 +91,7 @@ const Calendar = ({ minDate, maxDate, handleSelect, ...props }) => {
|
|||
<span>
|
||||
{`${format('MMMM', currentDisplayedMonth)} ${format(
|
||||
'yyyy',
|
||||
currentDisplayedMonth
|
||||
currentDisplayedMonth,
|
||||
)}`}
|
||||
</span>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table th,
|
||||
.table td {
|
||||
margin: 0;
|
||||
padding: 3px 0 3px 0;
|
||||
|
|
@ -63,4 +63,4 @@
|
|||
font-size: 13px;
|
||||
font-family: var(--museo);
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,14 +23,14 @@ const DateRangePicker = ({ minDate, maxDate, className, onRangeChange }) => {
|
|||
if (from && !to) {
|
||||
if (differenceInDays(from, day) >= 0) {
|
||||
setTo(
|
||||
set({ hours: 23, minutes: 59, seconds: 59, milliseconds: 999 }, day)
|
||||
set({ hours: 23, minutes: 59, seconds: 59, milliseconds: 999 }, day),
|
||||
)
|
||||
} else {
|
||||
setTo(
|
||||
set(
|
||||
{ hours: 23, minutes: 59, seconds: 59, milliseconds: 999 },
|
||||
R.clone(from)
|
||||
)
|
||||
R.clone(from),
|
||||
),
|
||||
)
|
||||
setFrom(day)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,24 +8,24 @@ const Tile = ({
|
|||
isUpperBound,
|
||||
isBetween,
|
||||
isDisabled,
|
||||
children
|
||||
children,
|
||||
}) => {
|
||||
const selected = isLowerBound || isUpperBound
|
||||
|
||||
const rangeClasses = {
|
||||
[classes.between]: isBetween && !(isLowerBound && isUpperBound),
|
||||
[classes.lowerBound]: isLowerBound && !isUpperBound,
|
||||
[classes.upperBound]: isUpperBound && !isLowerBound
|
||||
[classes.upperBound]: isUpperBound && !isLowerBound,
|
||||
}
|
||||
|
||||
const buttonWrapperClasses = {
|
||||
[classes.wrapper]: true,
|
||||
[classes.selected]: selected
|
||||
[classes.selected]: selected,
|
||||
}
|
||||
|
||||
const buttonClasses = {
|
||||
[classes.button]: true,
|
||||
[classes.disabled]: isDisabled
|
||||
[classes.disabled]: isDisabled,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
Td,
|
||||
THead,
|
||||
TDoubleLevelHead,
|
||||
ThDoubleLevel
|
||||
ThDoubleLevel,
|
||||
} from 'src/components/fake-table/Table'
|
||||
|
||||
import { sentenceCase } from 'src/utils/string'
|
||||
|
|
@ -24,11 +24,11 @@ const groupSecondHeader = elements => {
|
|||
{
|
||||
width: R.sum(R.map(R.prop('width'), group)),
|
||||
elements: group,
|
||||
name: doubleHeader(group[0])
|
||||
}
|
||||
]
|
||||
name: doubleHeader(group[0]),
|
||||
},
|
||||
],
|
||||
),
|
||||
R.reduce(R.concat, [])
|
||||
R.reduce(R.concat, []),
|
||||
)
|
||||
|
||||
return R.all(R.pipe(doubleHeader, R.isNil), elements)
|
||||
|
|
@ -47,7 +47,7 @@ const Header = () => {
|
|||
enableToggle,
|
||||
toggleWidth,
|
||||
orderedBy,
|
||||
DEFAULT_COL_SIZE
|
||||
DEFAULT_COL_SIZE,
|
||||
} = useContext(TableCtx)
|
||||
|
||||
const mapElement2 = (it, idx) => {
|
||||
|
|
@ -66,11 +66,13 @@ const Header = () => {
|
|||
|
||||
const mapElement = (
|
||||
{ name, display, width = DEFAULT_COL_SIZE, header, textAlign },
|
||||
idx
|
||||
idx,
|
||||
) => {
|
||||
const orderClasses = classnames({
|
||||
'whitespace-nowrap':
|
||||
R.isNil(header) && !R.isNil(orderedBy) && R.equals(name, orderedBy.code)
|
||||
R.isNil(header) &&
|
||||
!R.isNil(orderedBy) &&
|
||||
R.equals(name, orderedBy.code),
|
||||
})
|
||||
|
||||
const attachOrderedByToComplexHeader = header => {
|
||||
|
|
@ -82,6 +84,7 @@ const Header = () => {
|
|||
spanChild.props.children = R.append(' -', spanChild.props.children)
|
||||
return cloneHeader
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return header
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const NamespacedTable = ({
|
|||
|
||||
const innerData = R.map(it => ({
|
||||
id: it,
|
||||
...fromNamespace(it)(data)
|
||||
...fromNamespace(it)(data),
|
||||
}))(namespaces)
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const ActionCol = ({ disabled, editing }) => {
|
|||
forceAdd,
|
||||
clearError,
|
||||
actionColSize,
|
||||
error
|
||||
error,
|
||||
} = useContext(TableCtx)
|
||||
|
||||
const disableEdit = disabled || (disableRowEdit && disableRowEdit(values))
|
||||
|
|
@ -136,10 +136,10 @@ const ECol = ({ editing, focus, config, extraPaddingRight, extraPadding }) => {
|
|||
PrefixComponent = Label2,
|
||||
suffix,
|
||||
SuffixComponent = Label2,
|
||||
textStyle = it => {},
|
||||
isHidden = it => false,
|
||||
textStyle = () => {},
|
||||
isHidden = () => false,
|
||||
view = it => it?.toString(),
|
||||
inputProps = {}
|
||||
inputProps = {},
|
||||
} = config
|
||||
|
||||
const fields = names ?? [name]
|
||||
|
|
@ -158,7 +158,7 @@ const ECol = ({ editing, focus, config, extraPaddingRight, extraPadding }) => {
|
|||
size,
|
||||
bold,
|
||||
textAlign: isEditing ? editingAlign : textAlign,
|
||||
...inputProps
|
||||
...inputProps,
|
||||
}
|
||||
|
||||
const newAlign = isEditing ? editingAlign : textAlign
|
||||
|
|
@ -174,7 +174,7 @@ const ECol = ({ editing, focus, config, extraPaddingRight, extraPadding }) => {
|
|||
className={{
|
||||
[moduleStyles.extraPaddingRight]: extraPaddingRight,
|
||||
[moduleStyles.extraPadding]: extraPadding,
|
||||
'flex items-center': suffix || prefix
|
||||
'flex items-center': suffix || prefix,
|
||||
}}
|
||||
width={width}
|
||||
size={size}
|
||||
|
|
@ -225,7 +225,7 @@ const groupStriped = elements => {
|
|||
return R.insert(
|
||||
index,
|
||||
{ width, editable: false, view: () => <StripesSvg /> },
|
||||
noStripe
|
||||
noStripe,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -238,7 +238,7 @@ const ERow = ({ editing, disabled, lastOfGroup, newRow }) => {
|
|||
error,
|
||||
enableToggle,
|
||||
rowSize,
|
||||
stripeWhen
|
||||
stripeWhen,
|
||||
} = useContext(TableCtx)
|
||||
|
||||
const shouldStripe = !editing && stripeWhen && stripeWhen(values)
|
||||
|
|
@ -255,11 +255,11 @@ const ERow = ({ editing, disabled, lastOfGroup, newRow }) => {
|
|||
: -1
|
||||
|
||||
const elementToFocusIndex = innerElements.findIndex(
|
||||
it => it.editable === undefined || it.editable
|
||||
it => it.editable === undefined || it.editable,
|
||||
)
|
||||
|
||||
const classNames = {
|
||||
[moduleStyles.lastOfGroup]: lastOfGroup
|
||||
[moduleStyles.lastOfGroup]: lastOfGroup,
|
||||
}
|
||||
|
||||
const touchedErrors = R.pick(R.keys(touched), errors)
|
||||
|
|
|
|||
|
|
@ -26,4 +26,4 @@
|
|||
.fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const DEFAULT_COL_SIZE = 100
|
|||
|
||||
const getWidth = R.compose(
|
||||
R.reduce(R.add)(0),
|
||||
R.map(it => it.width ?? DEFAULT_COL_SIZE)
|
||||
R.map(it => it.width ?? DEFAULT_COL_SIZE),
|
||||
)
|
||||
|
||||
const ETable = ({
|
||||
|
|
@ -54,7 +54,7 @@ const ETable = ({
|
|||
createText = 'Add override',
|
||||
forceAdd = false,
|
||||
tbodyWrapperClass,
|
||||
orderedBy = null
|
||||
orderedBy = null,
|
||||
}) => {
|
||||
const [editingId, setEditingId] = useState(null)
|
||||
const [adding, setAdding] = useState(false)
|
||||
|
|
@ -80,6 +80,7 @@ const ETable = ({
|
|||
try {
|
||||
await save({ [name]: list }, it)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
setSaving(false)
|
||||
return
|
||||
}
|
||||
|
|
@ -152,7 +153,7 @@ const ETable = ({
|
|||
stripeWhen,
|
||||
forceAdd,
|
||||
orderedBy,
|
||||
DEFAULT_COL_SIZE
|
||||
DEFAULT_COL_SIZE,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -35,20 +35,20 @@ const Td = ({
|
|||
size,
|
||||
bold,
|
||||
textAlign,
|
||||
action
|
||||
action,
|
||||
}) => {
|
||||
const inlineStyle = {
|
||||
...style,
|
||||
width,
|
||||
textAlign,
|
||||
fontSize: size === 'sm' ? '14px' : size === 'lg' ? '24px' : ''
|
||||
fontSize: size === 'sm' ? '14px' : size === 'lg' ? '24px' : '',
|
||||
}
|
||||
|
||||
const cssClasses = {
|
||||
[styles.td]: !header,
|
||||
[styles.tdHeader]: header,
|
||||
'font-bold': !header && (bold || size === 'lg'),
|
||||
[styles.actionCol]: action
|
||||
[styles.actionCol]: action,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -88,21 +88,21 @@ const Tr = ({
|
|||
children,
|
||||
className,
|
||||
size,
|
||||
newRow
|
||||
newRow,
|
||||
}) => {
|
||||
const inlineStyle = {
|
||||
minHeight: size === 'sm' ? '34px' : size === 'lg' ? '68px' : '48px'
|
||||
minHeight: size === 'sm' ? '34px' : size === 'lg' ? '68px' : '48px',
|
||||
}
|
||||
const cardClasses = {
|
||||
[styles.card]: true,
|
||||
[styles.trError]: error,
|
||||
[styles.trAdding]: newRow
|
||||
[styles.trAdding]: newRow,
|
||||
}
|
||||
|
||||
const mainContentClasses = {
|
||||
[styles.mainContent]: true,
|
||||
[styles.sizeSm]: size === 'sm',
|
||||
[styles.sizeLg]: size === 'lg'
|
||||
[styles.sizeLg]: size === 'lg',
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -141,5 +141,5 @@ export {
|
|||
Td,
|
||||
Th,
|
||||
ThDoubleLevel,
|
||||
EditCell
|
||||
EditCell,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import { P } from 'src/components/typography'
|
|||
import TextInput from './TextInput'
|
||||
|
||||
const Autocomplete = ({
|
||||
optionsLimit = 5, // set limit = null for no limit
|
||||
limit,
|
||||
options,
|
||||
label,
|
||||
|
|
@ -17,7 +16,6 @@ const Autocomplete = ({
|
|||
multiple,
|
||||
onChange,
|
||||
labelProp,
|
||||
shouldStayOpen,
|
||||
value: outsideValue,
|
||||
error,
|
||||
fullWidth,
|
||||
|
|
@ -61,11 +59,11 @@ const Autocomplete = ({
|
|||
const filterOptions = (array, { inputValue }) =>
|
||||
R.union(
|
||||
R.isEmpty(inputValue) ? valueArray() : [],
|
||||
filter(array, inputValue)
|
||||
filter(array, inputValue),
|
||||
).slice(
|
||||
0,
|
||||
R.defaultTo(undefined)(limit) &&
|
||||
Math.max(limit, R.isEmpty(inputValue) ? valueArray().length : 0)
|
||||
Math.max(limit, R.isEmpty(inputValue) ? valueArray().length : 0),
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
@ -105,7 +103,7 @@ const Autocomplete = ({
|
|||
'flex w-4 h-4 rounded-md': true,
|
||||
'bg-spring4': props.warning === 'clean',
|
||||
'bg-orange-yellow': props.warning === 'partial',
|
||||
'bg-tomato': props.warning === 'important'
|
||||
'bg-tomato': props.warning === 'important',
|
||||
}
|
||||
|
||||
const hoverableElement = <div className={classnames(className)} />
|
||||
|
|
@ -122,9 +120,10 @@ const Autocomplete = ({
|
|||
)
|
||||
}}
|
||||
slotProps={{
|
||||
chip: { onDelete: null }
|
||||
}} />
|
||||
);
|
||||
chip: { onDelete: null },
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default Autocomplete
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ const CodeInput = ({
|
|||
numInputs,
|
||||
error,
|
||||
inputStyle,
|
||||
containerStyle
|
||||
containerStyle,
|
||||
}) => {
|
||||
return (
|
||||
<OtpInput
|
||||
|
|
@ -26,7 +26,7 @@ const CodeInput = ({
|
|||
inputStyle,
|
||||
classes.input,
|
||||
'font-museo font-black text-4xl',
|
||||
error && 'border-tomato'
|
||||
error && 'border-tomato',
|
||||
)}
|
||||
inputType={'tel'}
|
||||
renderInput={props => <input {...props} />}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const Dropdown = ({ label, name, options, onChange, value, className }) => {
|
|||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export default Dropdown
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ const NumberInput = memo(
|
|||
bold,
|
||||
className,
|
||||
decimalPlaces,
|
||||
InputProps,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
|
|
@ -41,14 +40,14 @@ const NumberInput = memo(
|
|||
onChange({
|
||||
target: {
|
||||
id: name,
|
||||
value: values.floatValue
|
||||
}
|
||||
value: values.floatValue,
|
||||
},
|
||||
})
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default NumberInput
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const RadioGroup = ({
|
|||
onChange,
|
||||
className,
|
||||
labelClassName,
|
||||
radioClassName
|
||||
radioClassName,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ const SecretInput = memo(
|
|||
placeholder={isPasswordFilled ? placeholder : ''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default SecretInput
|
||||
|
|
|
|||
|
|
@ -7,20 +7,19 @@ import Arrowdown from 'src/styling/icons/action/arrow/regular.svg?react'
|
|||
import styles from './Select.module.css'
|
||||
|
||||
function Select({ className, label, items, ...props }) {
|
||||
|
||||
const {
|
||||
isOpen,
|
||||
selectedItem,
|
||||
getToggleButtonProps,
|
||||
getLabelProps,
|
||||
getMenuProps,
|
||||
getItemProps
|
||||
getItemProps,
|
||||
} = useSelect({
|
||||
items,
|
||||
selectedItem: props.selectedItem,
|
||||
onSelectedItemChange: item => {
|
||||
props.onSelectedItemChange(item.selectedItem)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const selectClassNames = {
|
||||
|
|
@ -28,7 +27,7 @@ function Select({ className, label, items, ...props }) {
|
|||
[styles.selectFiltered]: props.defaultAsFilter
|
||||
? true
|
||||
: !R.equals(selectedItem, props.default),
|
||||
[styles.open]: isOpen
|
||||
[styles.open]: isOpen,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -97,4 +97,4 @@
|
|||
|
||||
.open button {
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ const TextInput = memo(
|
|||
onBlur,
|
||||
value,
|
||||
error,
|
||||
suffix,
|
||||
textAlign,
|
||||
width,
|
||||
inputClasses,
|
||||
|
|
@ -29,7 +28,7 @@ const TextInput = memo(
|
|||
|
||||
const style = {
|
||||
width: width,
|
||||
textAlign: textAlign
|
||||
textAlign: textAlign,
|
||||
}
|
||||
|
||||
const sizeClass =
|
||||
|
|
@ -40,7 +39,7 @@ const TextInput = memo(
|
|||
: styles.size
|
||||
|
||||
const divClass = {
|
||||
[styles.bold]: bold
|
||||
[styles.bold]: bold,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -60,15 +59,16 @@ const TextInput = memo(
|
|||
classes: {
|
||||
root: sizeClass,
|
||||
underline: filled ? styles.underline : null,
|
||||
input: inputClasses
|
||||
input: inputClasses,
|
||||
},
|
||||
...InputProps
|
||||
...InputProps,
|
||||
},
|
||||
|
||||
htmlInput: { style: { textAlign } }
|
||||
}} />
|
||||
);
|
||||
}
|
||||
htmlInput: { style: { textAlign } },
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
export default TextInput
|
||||
|
|
|
|||
|
|
@ -21,4 +21,4 @@
|
|||
|
||||
.underline:hover:not(.Mui-disabled)::before {
|
||||
border-bottom-color: var(--spring);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import RadioGroup from './RadioGroup'
|
|||
import SecretInput from './SecretInput'
|
||||
import TextInput from './TextInput'
|
||||
import ToggleButtonGroup from './ToggleButtonGroup'
|
||||
|
||||
export {
|
||||
Checkbox,
|
||||
CodeInput,
|
||||
|
|
@ -16,5 +17,5 @@ export {
|
|||
RadioGroup,
|
||||
Autocomplete,
|
||||
ToggleButtonGroup,
|
||||
Dropdown
|
||||
Dropdown,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ import { primaryColor as zodiac, tomato } from '../../../styling/variables.js'
|
|||
const colors = {
|
||||
cashOut: {
|
||||
empty: tomato,
|
||||
full: zodiac
|
||||
full: zodiac,
|
||||
},
|
||||
cashIn: {
|
||||
empty: zodiac,
|
||||
full: tomato
|
||||
}
|
||||
full: tomato,
|
||||
},
|
||||
}
|
||||
|
||||
const Cashbox = ({
|
||||
|
|
@ -28,7 +28,7 @@ const Cashbox = ({
|
|||
emptyPartClassName,
|
||||
labelClassName,
|
||||
omitInnerPercentage,
|
||||
isLow
|
||||
isLow,
|
||||
}) => {
|
||||
const ltHalf = percent <= 51
|
||||
const color =
|
||||
|
|
@ -72,7 +72,7 @@ const CashIn = ({
|
|||
width,
|
||||
height,
|
||||
total,
|
||||
omitInnerPercentage
|
||||
omitInnerPercentage,
|
||||
}) => {
|
||||
const percent = (100 * notes) / capacity
|
||||
const isLow = percent < threshold
|
||||
|
|
@ -117,7 +117,7 @@ const CashOut = ({
|
|||
threshold,
|
||||
width,
|
||||
height,
|
||||
omitInnerPercentage
|
||||
omitInnerPercentage,
|
||||
}) => {
|
||||
const percent = (100 * notes) / capacity
|
||||
const isLow = percent < threshold
|
||||
|
|
@ -159,7 +159,7 @@ const CashOutLite = ({
|
|||
currency,
|
||||
notes,
|
||||
threshold,
|
||||
width
|
||||
width,
|
||||
}) => {
|
||||
const percent = (100 * notes) / capacity
|
||||
const isLow = percent < threshold
|
||||
|
|
|
|||
|
|
@ -49,4 +49,4 @@
|
|||
border: 2px solid;
|
||||
text-align: end;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ const CashCassetteInput = memo(
|
|||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default CashCassetteInput
|
||||
|
|
|
|||
|
|
@ -3,20 +3,13 @@ import React, { memo } from 'react'
|
|||
import { Checkbox } from '../base'
|
||||
|
||||
const CheckboxInput = memo(
|
||||
({
|
||||
label,
|
||||
textAlign,
|
||||
fullWidth,
|
||||
enabled = true,
|
||||
disabledMessage = '',
|
||||
...props
|
||||
}) => {
|
||||
({ label, enabled = true, disabledMessage = '', ...props }) => {
|
||||
const { name, onChange, value } = props.field
|
||||
|
||||
const settings = {
|
||||
enabled: enabled,
|
||||
label: label,
|
||||
disabledMessage: disabledMessage
|
||||
disabledMessage: disabledMessage,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -28,7 +21,7 @@ const CheckboxInput = memo(
|
|||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default CheckboxInput
|
||||
|
|
|
|||
|
|
@ -15,5 +15,5 @@ export {
|
|||
SecretInput,
|
||||
RadioGroup,
|
||||
CashCassetteInput,
|
||||
Dropdown
|
||||
Dropdown,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,5 +14,5 @@ export {
|
|||
Select,
|
||||
RadioGroup,
|
||||
CashIn,
|
||||
CashOut
|
||||
CashOut,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -177,16 +177,16 @@ const Header = memo(({ tree, user }) => {
|
|||
name: 'offset',
|
||||
enabled: true,
|
||||
options: {
|
||||
offset: ['100vw', '100vw']
|
||||
}
|
||||
offset: ['100vw', '100vw'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'preventOverflow',
|
||||
enabled: true,
|
||||
options: {
|
||||
rootBoundary: 'viewport'
|
||||
}
|
||||
}
|
||||
rootBoundary: 'viewport',
|
||||
},
|
||||
},
|
||||
]}>
|
||||
<NotificationCenter
|
||||
popperRef={popperRef}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
}
|
||||
|
||||
.li::after {
|
||||
content: "";
|
||||
content: '';
|
||||
display: block;
|
||||
background: white;
|
||||
width: 0;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ const Sidebar = ({
|
|||
onClick,
|
||||
children,
|
||||
itemRender,
|
||||
loading = false
|
||||
loading = false,
|
||||
}) => {
|
||||
return (
|
||||
<div className={styles.sidebar}>
|
||||
|
|
@ -30,7 +30,7 @@ const Sidebar = ({
|
|||
[styles.activeLink]: isSelected(it),
|
||||
[styles.customRenderActiveLink]: itemRender && isSelected(it),
|
||||
[styles.customRenderLink]: itemRender,
|
||||
[styles.link]: true
|
||||
[styles.link]: true,
|
||||
})}>
|
||||
{itemRender ? itemRender(it, isSelected(it)) : displayName(it)}
|
||||
</div>
|
||||
|
|
@ -54,7 +54,7 @@ const Stepper = ({ step, it, idx, steps }) => {
|
|||
className={classnames({
|
||||
[styles.itemText]: true,
|
||||
[styles.itemTextActive]: active,
|
||||
[styles.itemTextPast]: past
|
||||
[styles.itemTextPast]: past,
|
||||
})}>
|
||||
{it.label}
|
||||
</span>
|
||||
|
|
@ -65,7 +65,7 @@ const Stepper = ({ step, it, idx, steps }) => {
|
|||
<div
|
||||
className={classnames({
|
||||
[styles.stepperPath]: true,
|
||||
[styles.stepperPast]: past
|
||||
[styles.stepperPast]: past,
|
||||
})}></div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
}
|
||||
|
||||
.link::after {
|
||||
content: "";
|
||||
content: '';
|
||||
display: block;
|
||||
background: var(--zodiac);
|
||||
width: 4px;
|
||||
|
|
@ -103,4 +103,4 @@
|
|||
|
||||
.stepperPast {
|
||||
border: 1px solid var(--zodiac);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ const TitleSection = ({
|
|||
buttons = [],
|
||||
children,
|
||||
appendix,
|
||||
appendixRight
|
||||
appendixRight,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
'flex justify-between items-center flex-row',
|
||||
className
|
||||
className,
|
||||
)}>
|
||||
<div className="flex items-center">
|
||||
<Title>{title}</Title>
|
||||
|
|
@ -44,7 +44,7 @@ const TitleSection = ({
|
|||
{button.text}
|
||||
</Info1>
|
||||
</SubpageButton>
|
||||
)
|
||||
),
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const STATES = {
|
|||
EMPTY: 'EMPTY',
|
||||
RUNNING: 'RUNNING',
|
||||
FAILURE: 'FAILURE',
|
||||
FILLED: 'FILLED'
|
||||
FILLED: 'FILLED',
|
||||
}
|
||||
|
||||
const MACHINE = gql`
|
||||
|
|
@ -47,7 +47,7 @@ const MACHINE_LOGS = gql`
|
|||
|
||||
const createCsv = async ({ machineLogsCsv }) => {
|
||||
const machineLogs = new Blob([machineLogsCsv], {
|
||||
type: 'text/plain;charset=utf-8'
|
||||
type: 'text/plain;charset=utf-8',
|
||||
})
|
||||
|
||||
FileSaver.saveAs(machineLogs, 'machineLogs.csv')
|
||||
|
|
@ -59,11 +59,11 @@ const DiagnosticsModal = ({ onClose, deviceId, sendAction }) => {
|
|||
let timeout = null
|
||||
|
||||
const [fetchSummary, { loading }] = useLazyQuery(MACHINE_LOGS, {
|
||||
onCompleted: data => createCsv(data)
|
||||
onCompleted: data => createCsv(data),
|
||||
})
|
||||
|
||||
const { data, stopPolling, startPolling } = useQuery(MACHINE, {
|
||||
variables: { deviceId }
|
||||
variables: { deviceId },
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -168,8 +168,8 @@ const DiagnosticsModal = ({ onClose, deviceId, sendAction }) => {
|
|||
variables: {
|
||||
from: subMinutes(new Date(timestamp), 5),
|
||||
deviceId,
|
||||
limit: 500
|
||||
}
|
||||
limit: 500,
|
||||
},
|
||||
})
|
||||
}}
|
||||
className="mt-auto ml-auto mr-2 mb-0">
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ const isStaticState = machineState => {
|
|||
'unpaired',
|
||||
'maintenance',
|
||||
'virgin',
|
||||
'wifiList'
|
||||
'wifiList',
|
||||
]
|
||||
return staticStates.includes(machineState)
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
|||
|
||||
const [fetchMachineEvents, { loading: loadingEvents }] = useLazyQuery(
|
||||
MACHINE,
|
||||
preflightOptions
|
||||
preflightOptions,
|
||||
)
|
||||
|
||||
const [simpleMachineAction] = useMutation(MACHINE_ACTION)
|
||||
|
|
@ -86,7 +86,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
|||
onCompleted: () => {
|
||||
onActionSuccess && onActionSuccess()
|
||||
setAction({ display: action.display, command: null })
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const confirmDialogOpen = Boolean(action.command)
|
||||
|
|
@ -100,7 +100,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
|||
? warningMessage
|
||||
: null
|
||||
setAction({ ...actionToDo, message })
|
||||
}
|
||||
},
|
||||
})
|
||||
fetchMachineEvents()
|
||||
}
|
||||
|
|
@ -118,7 +118,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
|||
setAction({
|
||||
command: 'rename',
|
||||
display: 'Rename',
|
||||
confirmationMessage: 'Write the new name for this machine'
|
||||
confirmationMessage: 'Write the new name for this machine',
|
||||
})
|
||||
}>
|
||||
Rename
|
||||
|
|
@ -131,7 +131,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
|||
onClick={() =>
|
||||
setAction({
|
||||
command: 'unpair',
|
||||
display: 'Unpair'
|
||||
display: 'Unpair',
|
||||
})
|
||||
}>
|
||||
Unpair
|
||||
|
|
@ -144,7 +144,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
|||
onClick={() =>
|
||||
setAction({
|
||||
command: 'reboot',
|
||||
display: 'Reboot'
|
||||
display: 'Reboot',
|
||||
})
|
||||
}>
|
||||
Reboot
|
||||
|
|
@ -159,7 +159,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
|||
command: 'shutdown',
|
||||
display: 'Shutdown',
|
||||
message:
|
||||
'In order to bring it back online, the machine will need to be visited and its power reset.'
|
||||
'In order to bring it back online, the machine will need to be visited and its power reset.',
|
||||
})
|
||||
}>
|
||||
Shutdown
|
||||
|
|
@ -172,7 +172,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
|||
onClick={() => {
|
||||
machineStatusPreflight({
|
||||
command: 'restartServices',
|
||||
display: 'Restart services for'
|
||||
display: 'Restart services for',
|
||||
})
|
||||
}}>
|
||||
Restart services
|
||||
|
|
@ -188,7 +188,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
|||
command: 'emptyUnit',
|
||||
display: 'Empty',
|
||||
message:
|
||||
"Triggering this action will move all cash inside the machine towards its cashbox (if possible), allowing for the collection of cash from the machine via only its cashbox. Depending on how full the cash units are, it's possible that this action will need to be used more than once to ensure that the unit is left completely empty."
|
||||
"Triggering this action will move all cash inside the machine towards its cashbox (if possible), allowing for the collection of cash from the machine via only its cashbox. Depending on how full the cash units are, it's possible that this action will need to be used more than once to ensure that the unit is left completely empty.",
|
||||
})
|
||||
}}>
|
||||
Empty Unit
|
||||
|
|
@ -205,7 +205,7 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
|||
command: 'refillUnit',
|
||||
display: 'Refill',
|
||||
message:
|
||||
'Triggering this action will refill the recyclers in this machine, by using bills present in its cassettes. This action may require manual operation of the cassettes and close attention to make sure that the denominations in the cassettes match the denominations in the recyclers.'
|
||||
'Triggering this action will refill the recyclers in this machine, by using bills present in its cassettes. This action may require manual operation of the cassettes and close attention to make sure that the denominations in the cassettes match the denominations in the recyclers.',
|
||||
})
|
||||
}}>
|
||||
Refill Unit
|
||||
|
|
@ -228,8 +228,8 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
|||
simpleMachineAction({
|
||||
variables: {
|
||||
deviceId: machine.deviceId,
|
||||
action: 'diagnostics'
|
||||
}
|
||||
action: 'diagnostics',
|
||||
},
|
||||
})
|
||||
}
|
||||
deviceId={machine.deviceId}
|
||||
|
|
@ -253,8 +253,8 @@ const MachineActions = memo(({ machine, onActionSuccess }) => {
|
|||
variables: {
|
||||
deviceId: machine.deviceId,
|
||||
action: `${action?.command}`,
|
||||
...(action?.command === 'rename' && { newName: value })
|
||||
}
|
||||
...(action?.command === 'rename' && { newName: value }),
|
||||
},
|
||||
})
|
||||
}}
|
||||
onDismissed={() => {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
TBody,
|
||||
Td,
|
||||
Th,
|
||||
Tr
|
||||
Tr,
|
||||
} from 'src/components/fake-table/Table'
|
||||
import EditIcon from 'src/styling/icons/action/edit/white.svg?react'
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ const SingleRowTable = ({
|
|||
title,
|
||||
items,
|
||||
onEdit,
|
||||
className
|
||||
className,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const EmptyTable = memo(({ message, className }) => {
|
|||
<div
|
||||
className={classNames(
|
||||
className,
|
||||
'flex flex-col items-center w-full mt-13 text-sm font-bold font-museo'
|
||||
'flex flex-col items-center w-full mt-13 text-sm font-bold font-museo',
|
||||
)}>
|
||||
<EmptyTableIcon />
|
||||
<H4>{message}</H4>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const Table = memo(({ className, children, ...props }) => {
|
|||
{...props}
|
||||
className={classnames(
|
||||
'table-fixed border-separate border-spacing-0',
|
||||
className
|
||||
className,
|
||||
)}>
|
||||
{children}
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const TableCell = memo(
|
|||
({ colspan, rightAlign, className, children, ...props }) => {
|
||||
const styles = {
|
||||
[classes.tableCell]: true,
|
||||
'text-right': rightAlign
|
||||
'text-right': rightAlign,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -18,7 +18,7 @@ const TableCell = memo(
|
|||
{children}
|
||||
</td>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default TableCell
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ const TableHeaderCell = memo(
|
|||
({ rightAlign, children, className, ...props }) => {
|
||||
const styles = {
|
||||
'bg-zodiac text-white py-0 px-6 h-8 text-sm text-left': true,
|
||||
'text-right': rightAlign
|
||||
'text-right': rightAlign,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -13,7 +13,7 @@ const TableHeaderCell = memo(
|
|||
{children}
|
||||
</th>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default TableHeaderCell
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const TableRow = memo(
|
|||
'h-8': !header && size === 'sm',
|
||||
'h-9 font-bold text-base ': !header && size === 'lg',
|
||||
'bg-misty-rose': error,
|
||||
'bg-spring3': success
|
||||
'bg-spring3': success,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -16,7 +16,7 @@ const TableRow = memo(
|
|||
{children}
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
export default TableRow
|
||||
|
|
|
|||
|
|
@ -15,5 +15,5 @@ export {
|
|||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
TableBody
|
||||
TableBody,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
AutoSizer,
|
||||
List,
|
||||
CellMeasurer,
|
||||
CellMeasurerCache
|
||||
CellMeasurerCache,
|
||||
} from 'react-virtualized'
|
||||
import {
|
||||
Table,
|
||||
|
|
@ -13,7 +13,7 @@ import {
|
|||
THead,
|
||||
Tr,
|
||||
Td,
|
||||
Th
|
||||
Th,
|
||||
} from 'src/components/fake-table/Table'
|
||||
import { H4 } from 'src/components/typography'
|
||||
import ExpandClosedIcon from 'src/styling/icons/action/expand/closed.svg?react'
|
||||
|
|
@ -40,7 +40,7 @@ const Row = ({
|
|||
const trClasses = {
|
||||
'cursor-pointer': hasPointer,
|
||||
'border-2 border-transparent': true,
|
||||
'border-2 border-zircon shadow-md': expanded
|
||||
'border-2 border-zircon shadow-md': expanded,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -79,7 +79,7 @@ const Row = ({
|
|||
<div className="pb-3">
|
||||
<Tr
|
||||
className={classnames({
|
||||
'border-2 border-zircon shadow-md': expanded
|
||||
'border-2 border-zircon shadow-md': expanded,
|
||||
})}>
|
||||
<Td width={width}>
|
||||
<Details it={data} timezone={props.timezone} />
|
||||
|
|
@ -126,7 +126,7 @@ const DataTable = ({
|
|||
|
||||
const cache = new CellMeasurerCache({
|
||||
defaultHeight: 58,
|
||||
fixedWidth: true
|
||||
fixedWidth: true,
|
||||
})
|
||||
|
||||
function rowRenderer({ index, key, parent, style }) {
|
||||
|
|
@ -168,12 +168,12 @@ const DataTable = ({
|
|||
<div
|
||||
className={classnames({
|
||||
'flex flex-1 flex-col': true,
|
||||
[className]: !!className
|
||||
[className]: !!className,
|
||||
})}>
|
||||
<Table
|
||||
className={classnames(
|
||||
'mb-12 min-h-50 flex-1 flex flex-col',
|
||||
tableClassName
|
||||
tableClassName,
|
||||
)}
|
||||
style={{ width }}>
|
||||
<THead>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ function H1({ children, noMargin, className, ...props }) {
|
|||
const classNames = {
|
||||
[styles.h1]: true,
|
||||
[styles.noMargin]: noMargin,
|
||||
[className]: !!className
|
||||
[className]: !!className,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -21,7 +21,7 @@ function H2({ children, noMargin, className, ...props }) {
|
|||
const classNames = {
|
||||
[styles.h2]: true,
|
||||
[styles.noMargin]: noMargin,
|
||||
[className]: !!className
|
||||
[className]: !!className,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -35,7 +35,7 @@ function H3({ children, noMargin, className, ...props }) {
|
|||
const classNames = {
|
||||
[styles.h3]: true,
|
||||
[styles.noMargin]: noMargin,
|
||||
[className]: !!className
|
||||
[className]: !!className,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -49,7 +49,7 @@ function H4({ children, noMargin, className, ...props }) {
|
|||
const classNames = {
|
||||
[styles.h4]: true,
|
||||
[styles.noMargin]: noMargin,
|
||||
[className]: !!className
|
||||
[className]: !!className,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -63,7 +63,7 @@ function H5({ children, noMargin, className, ...props }) {
|
|||
const classNames = {
|
||||
[styles.h5]: true,
|
||||
[styles.noMargin]: noMargin,
|
||||
[className]: !!className
|
||||
[className]: !!className,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -90,7 +90,7 @@ function pBuilder(elementClass) {
|
|||
[className]: !!className,
|
||||
[styles[elementClass]]: elementClass,
|
||||
[styles.inline]: inline,
|
||||
[styles.noMargin]: noMargin
|
||||
[styles.noMargin]: noMargin,
|
||||
}
|
||||
return (
|
||||
<p className={classnames(classNames)} {...props}>
|
||||
|
|
@ -115,5 +115,5 @@ export {
|
|||
Mono,
|
||||
Label1,
|
||||
Label2,
|
||||
Label3
|
||||
Label3,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,132 +7,132 @@ import {
|
|||
fontSize5,
|
||||
fontPrimary,
|
||||
fontSecondary,
|
||||
fontMonospaced
|
||||
fontMonospaced,
|
||||
} from 'src/styling/variables'
|
||||
|
||||
const base = {
|
||||
lineHeight: '120%',
|
||||
color: fontColor
|
||||
color: fontColor,
|
||||
}
|
||||
|
||||
export default {
|
||||
base: {
|
||||
lineHeight: '120%',
|
||||
color: fontColor
|
||||
color: fontColor,
|
||||
},
|
||||
h1: {
|
||||
extend: base,
|
||||
fontSize: fontSize1,
|
||||
fontFamily: fontPrimary,
|
||||
fontWeight: 900
|
||||
fontWeight: 900,
|
||||
},
|
||||
h2: {
|
||||
extend: base,
|
||||
fontSize: fontSize2,
|
||||
fontFamily: fontPrimary,
|
||||
fontWeight: 900
|
||||
fontWeight: 900,
|
||||
},
|
||||
h3: {
|
||||
extend: base,
|
||||
fontSize: fontSize4,
|
||||
fontFamily: fontPrimary,
|
||||
fontWeight: 900
|
||||
fontWeight: 900,
|
||||
},
|
||||
h4: {
|
||||
extend: base,
|
||||
fontSize: fontSize4,
|
||||
fontFamily: fontPrimary,
|
||||
fontWeight: 700
|
||||
fontWeight: 700,
|
||||
},
|
||||
h5: {
|
||||
extend: base,
|
||||
fontSize: fontSize3,
|
||||
fontFamily: fontPrimary,
|
||||
fontWeight: 700
|
||||
fontWeight: 700,
|
||||
},
|
||||
p: {
|
||||
...base,
|
||||
fontSize: fontSize4,
|
||||
fontFamily: fontSecondary,
|
||||
fontWeight: 500
|
||||
fontWeight: 500,
|
||||
},
|
||||
tl1: {
|
||||
extend: base,
|
||||
fontSize: fontSize2,
|
||||
fontFamily: fontSecondary,
|
||||
fontWeight: 700
|
||||
fontWeight: 700,
|
||||
},
|
||||
tl2: {
|
||||
extend: base,
|
||||
fontSize: fontSize4,
|
||||
fontFamily: fontSecondary,
|
||||
fontWeight: 700
|
||||
fontWeight: 700,
|
||||
},
|
||||
info1: {
|
||||
extend: base,
|
||||
fontSize: fontSize1,
|
||||
fontFamily: fontSecondary,
|
||||
fontWeight: 700
|
||||
fontWeight: 700,
|
||||
},
|
||||
info2: {
|
||||
extend: base,
|
||||
fontSize: fontSize3,
|
||||
fontFamily: fontSecondary,
|
||||
fontWeight: 700
|
||||
fontWeight: 700,
|
||||
},
|
||||
info3: {
|
||||
extend: base,
|
||||
fontSize: fontSize3,
|
||||
fontFamily: fontSecondary,
|
||||
fontWeight: 500
|
||||
fontWeight: 500,
|
||||
},
|
||||
mono: {
|
||||
extend: base,
|
||||
fontSize: fontSize4,
|
||||
fontFamily: fontMonospaced,
|
||||
fontWeight: 500
|
||||
fontWeight: 500,
|
||||
},
|
||||
monoBold: {
|
||||
fontWeight: 700
|
||||
fontWeight: 700,
|
||||
},
|
||||
monoSmall: {
|
||||
fontSize: fontSize5
|
||||
fontSize: fontSize5,
|
||||
},
|
||||
inputFont: {
|
||||
fontSize: fontSize2,
|
||||
fontFamily: fontSecondary,
|
||||
fontWeight: 500,
|
||||
lineHeight: '110%',
|
||||
color: fontColor
|
||||
color: fontColor,
|
||||
},
|
||||
regularLabel: {
|
||||
fontSize: fontSize4,
|
||||
fontFamily: fontSecondary,
|
||||
fontWeight: 500,
|
||||
lineHeight: '110%'
|
||||
lineHeight: '110%',
|
||||
},
|
||||
label1: {
|
||||
fontSize: fontSize5,
|
||||
fontFamily: fontSecondary,
|
||||
fontWeight: 500,
|
||||
color: fontColor
|
||||
color: fontColor,
|
||||
},
|
||||
label2: {
|
||||
fontSize: fontSize5,
|
||||
fontFamily: fontSecondary,
|
||||
fontWeight: 700,
|
||||
color: fontColor
|
||||
color: fontColor,
|
||||
},
|
||||
label3: {
|
||||
fontSize: fontSize4,
|
||||
fontFamily: fontSecondary,
|
||||
fontWeight: 500,
|
||||
color: fontColor
|
||||
color: fontColor,
|
||||
},
|
||||
inline: {
|
||||
display: 'inline'
|
||||
display: 'inline',
|
||||
},
|
||||
noMargin: {
|
||||
margin: 0
|
||||
}
|
||||
margin: 0,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ ReactDOM.render(
|
|||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
document.getElementById('root'),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ const QrCodeComponent = ({ qrCode, name, count, onPaired }) => {
|
|||
if (hasNewMachine) {
|
||||
timeout.current = setTimeout(
|
||||
() => onPaired(addedMachine),
|
||||
CLOSE_SCREEN_TIMEOUT
|
||||
CLOSE_SCREEN_TIMEOUT,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +111,7 @@ const QrCodeComponent = ({ qrCode, name, count, onPaired }) => {
|
|||
}
|
||||
|
||||
const initialValues = {
|
||||
name: ''
|
||||
name: '',
|
||||
}
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
|
|
@ -124,9 +124,9 @@ const validationSchema = Yup.object().shape({
|
|||
(value, context) =>
|
||||
!R.includes(
|
||||
R.toLower(value),
|
||||
R.map(R.toLower, context.options.context.machineNames)
|
||||
)
|
||||
)
|
||||
R.map(R.toLower, context.options.context.machineNames),
|
||||
),
|
||||
),
|
||||
})
|
||||
|
||||
const MachineNameComponent = ({ nextStep, setQrCode, setName }) => {
|
||||
|
|
@ -138,7 +138,7 @@ const MachineNameComponent = ({ nextStep, setQrCode, setName }) => {
|
|||
setQrCode(createPairingTotem)
|
||||
nextStep()
|
||||
},
|
||||
onError: e => console.log(e)
|
||||
onError: e => console.log(e),
|
||||
})
|
||||
|
||||
const { data } = useQuery(GET_MACHINES)
|
||||
|
|
@ -147,7 +147,7 @@ const MachineNameComponent = ({ nextStep, setQrCode, setName }) => {
|
|||
const uniqueNameValidator = value => {
|
||||
try {
|
||||
validationSchema.validateSync(value, {
|
||||
context: { machineNames: machineNames }
|
||||
context: { machineNames: machineNames },
|
||||
})
|
||||
} catch (error) {
|
||||
return error
|
||||
|
|
@ -189,12 +189,12 @@ const MachineNameComponent = ({ nextStep, setQrCode, setName }) => {
|
|||
const steps = [
|
||||
{
|
||||
label: 'Machine name',
|
||||
component: MachineNameComponent
|
||||
component: MachineNameComponent,
|
||||
},
|
||||
{
|
||||
label: 'Scan QR code',
|
||||
component: QrCodeComponent
|
||||
}
|
||||
component: QrCodeComponent,
|
||||
},
|
||||
]
|
||||
|
||||
const renderStepper = (step, it, idx) => {
|
||||
|
|
@ -208,7 +208,7 @@ const renderStepper = (step, it, idx) => {
|
|||
className={classnames({
|
||||
'mr-6 text-comet': true,
|
||||
'text-zodiac font-bold': active,
|
||||
'text-zodiac': past
|
||||
'text-zodiac': past,
|
||||
})}>
|
||||
{it.label}
|
||||
</span>
|
||||
|
|
@ -219,7 +219,7 @@ const renderStepper = (step, it, idx) => {
|
|||
<div
|
||||
className={classnames({
|
||||
'absolute h-7 w-px border border-comet border-solid right-2 top-[18px]': true,
|
||||
'border-zodiac': past
|
||||
'border-zodiac': past,
|
||||
})}></div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -26,29 +26,29 @@ const REPRESENTING_OPTIONS = [
|
|||
{ code: 'overTime', display: 'Over time' },
|
||||
{ code: 'volumeOverTime', display: 'Volume' },
|
||||
{ code: 'topMachines', display: 'Top machines' },
|
||||
{ code: 'hourOfTheDay', display: 'Hour of the day' }
|
||||
{ code: 'hourOfTheDay', display: 'Hour of the day' },
|
||||
]
|
||||
const PERIOD_OPTIONS = [
|
||||
{ code: 'day', display: 'Last 24 hours' },
|
||||
{ code: 'threeDays', display: 'Last 3 days' },
|
||||
{ code: 'week', display: 'Last 7 days' },
|
||||
{ code: 'month', display: 'Last 30 days' }
|
||||
{ code: 'month', display: 'Last 30 days' },
|
||||
]
|
||||
const TIME_OPTIONS = {
|
||||
day: DAY,
|
||||
threeDays: 3 * DAY,
|
||||
week: WEEK,
|
||||
month: MONTH
|
||||
month: MONTH,
|
||||
}
|
||||
|
||||
const DAY_OPTIONS = R.map(
|
||||
it => ({
|
||||
code: R.toLower(it),
|
||||
display: it
|
||||
display: it,
|
||||
}),
|
||||
Array.from(Array(7)).map((_, i) =>
|
||||
format('EEEE', add({ days: i }, startOfWeek(new Date())))
|
||||
)
|
||||
format('EEEE', add({ days: i }, startOfWeek(new Date()))),
|
||||
),
|
||||
)
|
||||
|
||||
const GET_TRANSACTIONS = gql`
|
||||
|
|
@ -112,7 +112,7 @@ const OverviewEntry = ({ label, value, oldValue, currency }) => {
|
|||
const growthClasses = {
|
||||
'font-bold': true,
|
||||
'text-malachite': R.gt(value, oldValue),
|
||||
'text-tomato': R.gt(oldValue, value)
|
||||
'text-tomato': R.gt(oldValue, value),
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -139,8 +139,8 @@ const Analytics = () => {
|
|||
variables: {
|
||||
from: subDays(65, endOfToday()),
|
||||
until: endOfToday(),
|
||||
excludeTestingCustomers: true
|
||||
}
|
||||
excludeTestingCustomers: true,
|
||||
},
|
||||
})
|
||||
const { data: configResponse, loading: configLoading } = useQuery(GET_DATA)
|
||||
|
||||
|
|
@ -148,7 +148,7 @@ const Analytics = () => {
|
|||
const [period, setPeriod] = useState(PERIOD_OPTIONS[0])
|
||||
const [machine, setMachine] = useState(MACHINE_OPTIONS[0])
|
||||
const [selectedDay, setSelectedDay] = useState(
|
||||
R.equals(representing.code, 'hourOfTheDay') ? DAY_OPTIONS[0] : null
|
||||
R.equals(representing.code, 'hourOfTheDay') ? DAY_OPTIONS[0] : null,
|
||||
)
|
||||
|
||||
const loading = txLoading || configLoading
|
||||
|
|
@ -175,20 +175,20 @@ const Analytics = () => {
|
|||
tx =>
|
||||
(!tx.dispensed || !tx.expired) &&
|
||||
(tx.sendConfirmed || tx.dispense) &&
|
||||
!tx.hasError
|
||||
)
|
||||
!tx.hasError,
|
||||
),
|
||||
) ?? []
|
||||
|
||||
const machineOptions = R.clone(MACHINE_OPTIONS)
|
||||
|
||||
R.forEach(
|
||||
m => machineOptions.push({ code: m.deviceId, display: m.name }),
|
||||
machines
|
||||
machines,
|
||||
)
|
||||
|
||||
const machineTxs = R.filter(
|
||||
tx => (machine.code === 'all' ? true : tx.deviceId === machine.code),
|
||||
data
|
||||
data,
|
||||
)
|
||||
|
||||
const filteredData = timeInterval => ({
|
||||
|
|
@ -213,35 +213,35 @@ const Analytics = () => {
|
|||
txDay < Date.now() - TIME_OPTIONS[timeInterval] &&
|
||||
txDay >= Date.now() - 2 * TIME_OPTIONS[timeInterval]
|
||||
)
|
||||
}) ?? []
|
||||
}) ?? [],
|
||||
})
|
||||
|
||||
const txs = {
|
||||
current: filteredData(period.code).current.length,
|
||||
previous: filteredData(period.code).previous.length
|
||||
previous: filteredData(period.code).previous.length,
|
||||
}
|
||||
|
||||
const median = values => (values.length === 0 ? 0 : R.median(values))
|
||||
|
||||
const medianAmount = {
|
||||
current: median(R.map(d => d.fiat, filteredData(period.code).current)),
|
||||
previous: median(R.map(d => d.fiat, filteredData(period.code).previous))
|
||||
previous: median(R.map(d => d.fiat, filteredData(period.code).previous)),
|
||||
}
|
||||
|
||||
const txVolume = {
|
||||
current: R.sum(R.map(d => d.fiat, filteredData(period.code).current)),
|
||||
previous: R.sum(R.map(d => d.fiat, filteredData(period.code).previous))
|
||||
previous: R.sum(R.map(d => d.fiat, filteredData(period.code).previous)),
|
||||
}
|
||||
|
||||
const commissions = {
|
||||
current: R.sum(R.map(d => d.profit, filteredData(period.code).current)),
|
||||
previous: R.sum(R.map(d => d.profit, filteredData(period.code).previous))
|
||||
previous: R.sum(R.map(d => d.profit, filteredData(period.code).previous)),
|
||||
}
|
||||
|
||||
const handleRepresentationChange = newRepresentation => {
|
||||
setRepresenting(newRepresentation)
|
||||
setSelectedDay(
|
||||
R.equals(newRepresentation.code, 'hourOfTheDay') ? DAY_OPTIONS[0] : null
|
||||
R.equals(newRepresentation.code, 'hourOfTheDay') ? DAY_OPTIONS[0] : null,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,20 +13,19 @@ const GraphTooltip = ({
|
|||
coords,
|
||||
data,
|
||||
dateInterval,
|
||||
period,
|
||||
currency,
|
||||
representing
|
||||
representing,
|
||||
}) => {
|
||||
const formattedDateInterval = !R.includes('hourOfDay', representing.code)
|
||||
? [
|
||||
formatDate(dateInterval[1], null, 'MMM d'),
|
||||
formatDate(dateInterval[1], null, 'HH:mm'),
|
||||
formatDate(dateInterval[0], null, 'HH:mm')
|
||||
formatDate(dateInterval[0], null, 'HH:mm'),
|
||||
]
|
||||
: [
|
||||
formatDate(dateInterval[1], null, 'MMM d'),
|
||||
formatDateNonUtc(dateInterval[1], 'HH:mm'),
|
||||
formatDateNonUtc(dateInterval[0], 'HH:mm')
|
||||
formatDateNonUtc(dateInterval[0], 'HH:mm'),
|
||||
]
|
||||
|
||||
const transactions = R.reduce(
|
||||
|
|
@ -37,7 +36,7 @@ const GraphTooltip = ({
|
|||
return acc
|
||||
},
|
||||
{ volume: 0, cashIn: 0, cashOut: 0 },
|
||||
data
|
||||
data,
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import classes from './wrappers.module.css'
|
|||
|
||||
const options = [
|
||||
{ code: 'hourOfDayTransactions', display: 'Transactions' },
|
||||
{ code: 'hourOfDayVolume', display: 'Volume' }
|
||||
{ code: 'hourOfDayVolume', display: 'Volume' },
|
||||
]
|
||||
|
||||
const HourOfDayBarGraphHeader = ({
|
||||
|
|
@ -26,13 +26,13 @@ const HourOfDayBarGraphHeader = ({
|
|||
dayOptions,
|
||||
handleDayChange,
|
||||
timezone,
|
||||
currency
|
||||
currency,
|
||||
}) => {
|
||||
const [graphType /*, setGraphType */] = useState(options[0].code)
|
||||
|
||||
const legend = {
|
||||
cashIn: <div className={classes.cashInIcon}></div>,
|
||||
cashOut: <div className={classes.cashOutIcon}></div>
|
||||
cashOut: <div className={classes.cashOutIcon}></div>,
|
||||
}
|
||||
|
||||
const offset = getTimezoneOffset(timezone)
|
||||
|
|
@ -41,7 +41,7 @@ const HourOfDayBarGraphHeader = ({
|
|||
(acc, value) => {
|
||||
const created = new Date(value.created)
|
||||
created.setTime(
|
||||
created.getTime() + created.getTimezoneOffset() * MINUTE + offset
|
||||
created.getTime() + created.getTimezoneOffset() * MINUTE + offset,
|
||||
)
|
||||
switch (created.getDay()) {
|
||||
case 0:
|
||||
|
|
@ -71,7 +71,7 @@ const HourOfDayBarGraphHeader = ({
|
|||
return acc
|
||||
},
|
||||
R.fromPairs(R.map(it => [it.code, []], dayOptions)),
|
||||
data
|
||||
data,
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const OverTimeDotGraphHeader = ({
|
|||
selectedMachine,
|
||||
handleMachineChange,
|
||||
timezone,
|
||||
currency
|
||||
currency,
|
||||
}) => {
|
||||
const [logarithmic, setLogarithmic] = useState()
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ const OverTimeDotGraphHeader = ({
|
|||
d="M 5 6 l 20 0"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import classes from './wrappers.module.css'
|
|||
|
||||
const options = [
|
||||
{ code: 'topMachinesTransactions', display: 'Transactions' },
|
||||
{ code: 'topMachinesVolume', display: 'Volume' }
|
||||
{ code: 'topMachinesVolume', display: 'Volume' },
|
||||
]
|
||||
|
||||
const TopMachinesBarGraphHeader = ({
|
||||
|
|
@ -18,13 +18,13 @@ const TopMachinesBarGraphHeader = ({
|
|||
machines,
|
||||
selectedMachine,
|
||||
timezone,
|
||||
currency
|
||||
currency,
|
||||
}) => {
|
||||
const [graphType /*, setGraphType */] = useState(options[0].code)
|
||||
|
||||
const legend = {
|
||||
cashIn: <div className={classes.cashInIcon}></div>,
|
||||
cashOut: <div className={classes.cashOutIcon}></div>
|
||||
cashOut: <div className={classes.cashOutIcon}></div>,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const VolumeOverTimeGraphHeader = ({
|
|||
selectedMachine,
|
||||
handleMachineChange,
|
||||
timezone,
|
||||
currency
|
||||
currency,
|
||||
}) => {
|
||||
const [logarithmic, setLogarithmic] = useState()
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ const VolumeOverTimeGraphHeader = ({
|
|||
strokeLinecap="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
.graphHeaderRight {
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
gap: 30px
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.cashInIcon {
|
||||
|
|
@ -45,12 +45,12 @@
|
|||
.graphHeaderSwitchBox {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/*'& > *': {*/
|
||||
/* margin: 0*/
|
||||
/*},*/
|
||||
/*'& > :first-child': {*/
|
||||
/* marginBottom: 2,*/
|
||||
/* extend: label1,*/
|
||||
/* color: offColor*/
|
||||
/*}*/
|
||||
/*'& > *': {*/
|
||||
/* margin: 0*/
|
||||
/*},*/
|
||||
/*'& > :first-child': {*/
|
||||
/* marginBottom: 2,*/
|
||||
/* extend: label1,*/
|
||||
/* color: offColor*/
|
||||
/*}*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const GraphWrapper = ({
|
|||
selectedMachine,
|
||||
machines,
|
||||
selectedDay,
|
||||
log
|
||||
log,
|
||||
}) => {
|
||||
const [selectionCoords, setSelectionCoords] = useState(null)
|
||||
const [selectionDateInterval, setSelectionDateInterval] = useState(null)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
subheaderDarkColor,
|
||||
fontColor,
|
||||
fontSecondary,
|
||||
subheaderColor
|
||||
subheaderColor,
|
||||
} from 'src/styling/variables'
|
||||
import { MINUTE } from 'src/utils/time'
|
||||
import { toUtc } from 'src/utils/timezones'
|
||||
|
|
@ -22,7 +22,6 @@ const Graph = ({
|
|||
setSelectionCoords,
|
||||
setSelectionData,
|
||||
setSelectionDateInterval,
|
||||
selectedMachine
|
||||
}) => {
|
||||
const ref = useRef(null)
|
||||
|
||||
|
|
@ -36,9 +35,9 @@ const Graph = ({
|
|||
top: 25,
|
||||
right: 0.5,
|
||||
bottom: 27,
|
||||
left: 36.5
|
||||
left: 36.5,
|
||||
}),
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const offset = getTimezoneOffset(timezone)
|
||||
|
|
@ -64,7 +63,7 @@ const Graph = ({
|
|||
const tzCreated = new Date(it.created).setTime(
|
||||
new Date(it.created).getTime() +
|
||||
new Date(it.created).getTimezoneOffset() * MINUTE +
|
||||
offset
|
||||
offset,
|
||||
)
|
||||
const created = new Date(tzCreated)
|
||||
|
||||
|
|
@ -77,7 +76,7 @@ const Graph = ({
|
|||
created.getUTCHours() < new Date(upperBound).getUTCHours())
|
||||
)
|
||||
}, data),
|
||||
[data, offset]
|
||||
[data, offset],
|
||||
)
|
||||
|
||||
const txClassByHourInterval = useCallback(
|
||||
|
|
@ -91,16 +90,16 @@ const Graph = ({
|
|||
return acc
|
||||
},
|
||||
{ cashIn: 0, cashOut: 0 },
|
||||
filterByHourInterval(lowerBound, upperBound)
|
||||
filterByHourInterval(lowerBound, upperBound),
|
||||
),
|
||||
[filterByHourInterval]
|
||||
[filterByHourInterval],
|
||||
)
|
||||
|
||||
const x = d3
|
||||
.scaleUtc()
|
||||
.domain([
|
||||
toUtc(startOfDay(new Date())),
|
||||
toUtc(add({ days: 1 }, startOfDay(new Date())))
|
||||
toUtc(add({ days: 1 }, startOfDay(new Date()))),
|
||||
])
|
||||
.rangeRound([GRAPH_MARGIN.left, GRAPH_WIDTH - GRAPH_MARGIN.right])
|
||||
|
||||
|
|
@ -111,7 +110,7 @@ const Graph = ({
|
|||
const upperBound = R.clone(it)
|
||||
return [lowerBound, filterByHourInterval(lowerBound, upperBound)]
|
||||
},
|
||||
R.init(getTickIntervals(x.domain(), 2))
|
||||
R.init(getTickIntervals(x.domain(), 2)),
|
||||
)
|
||||
|
||||
const groupedByTxClass = R.map(
|
||||
|
|
@ -121,7 +120,7 @@ const Graph = ({
|
|||
const upperBound = R.clone(it)
|
||||
return [lowerBound, txClassByHourInterval(lowerBound, upperBound)]
|
||||
},
|
||||
R.init(getTickIntervals(x.domain(), 2))
|
||||
R.init(getTickIntervals(x.domain(), 2)),
|
||||
)
|
||||
|
||||
const y = d3
|
||||
|
|
@ -130,13 +129,13 @@ const Graph = ({
|
|||
0,
|
||||
d3.max(
|
||||
groupedByTxClass.map(it => it[1]),
|
||||
d => d.cashIn + d.cashOut
|
||||
d => d.cashIn + d.cashOut,
|
||||
) !== 0
|
||||
? d3.max(
|
||||
groupedByTxClass.map(it => it[1]),
|
||||
d => d.cashIn + d.cashOut
|
||||
d => d.cashIn + d.cashOut,
|
||||
)
|
||||
: 50
|
||||
: 50,
|
||||
])
|
||||
.range([GRAPH_HEIGHT - GRAPH_MARGIN.bottom, GRAPH_MARGIN.top])
|
||||
|
||||
|
|
@ -145,15 +144,15 @@ const Graph = ({
|
|||
g
|
||||
.attr(
|
||||
'transform',
|
||||
`translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`
|
||||
`translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`,
|
||||
)
|
||||
.call(
|
||||
d3
|
||||
.axisBottom(x)
|
||||
.ticks(d3.timeHour.every(2))
|
||||
.tickFormat(d3.timeFormat('%H:%M'))
|
||||
.tickFormat(d3.timeFormat('%H:%M')),
|
||||
),
|
||||
[GRAPH_MARGIN, x]
|
||||
[GRAPH_MARGIN, x],
|
||||
)
|
||||
|
||||
const buildYAxis = useCallback(
|
||||
|
|
@ -165,10 +164,10 @@ const Graph = ({
|
|||
.axisLeft(y)
|
||||
.ticks(GRAPH_HEIGHT / 100)
|
||||
.tickSize(0)
|
||||
.tickFormat(``)
|
||||
.tickFormat(``),
|
||||
)
|
||||
.call(g => g.select('.domain').remove()),
|
||||
[GRAPH_MARGIN, y]
|
||||
[GRAPH_MARGIN, y],
|
||||
)
|
||||
|
||||
const buildVerticalLines = useCallback(
|
||||
|
|
@ -195,7 +194,7 @@ const Graph = ({
|
|||
})
|
||||
.attr('y1', GRAPH_MARGIN.top)
|
||||
.attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom),
|
||||
[GRAPH_MARGIN, x]
|
||||
[GRAPH_MARGIN, x],
|
||||
)
|
||||
|
||||
const buildHoverableEventRects = useCallback(
|
||||
|
|
@ -227,15 +226,15 @@ const Graph = ({
|
|||
const endDate = R.clone(date)
|
||||
|
||||
const filteredData = groupedByDateInterval.find(it =>
|
||||
R.equals(startDate, it[0])
|
||||
R.equals(startDate, it[0]),
|
||||
)[1]
|
||||
|
||||
const rectXCoords = {
|
||||
left: R.clone(d.target.getBoundingClientRect().x),
|
||||
right: R.clone(
|
||||
d.target.getBoundingClientRect().x +
|
||||
d.target.getBoundingClientRect().width
|
||||
)
|
||||
d.target.getBoundingClientRect().width,
|
||||
),
|
||||
}
|
||||
|
||||
const xCoord =
|
||||
|
|
@ -248,18 +247,18 @@ const Graph = ({
|
|||
setSelectionData(filteredData)
|
||||
setSelectionCoords({
|
||||
x: Math.round(xCoord),
|
||||
y: Math.round(yCoord)
|
||||
y: Math.round(yCoord),
|
||||
})
|
||||
|
||||
d3.select(`#event-rect-${x(d.target.__data__)}`).attr(
|
||||
'fill',
|
||||
subheaderColor
|
||||
subheaderColor,
|
||||
)
|
||||
})
|
||||
.on('mouseleave', d => {
|
||||
d3.select(`#event-rect-${x(d.target.__data__)}`).attr(
|
||||
'fill',
|
||||
'transparent'
|
||||
'transparent',
|
||||
)
|
||||
setSelectionDateInterval(null)
|
||||
setSelectionData(null)
|
||||
|
|
@ -271,8 +270,8 @@ const Graph = ({
|
|||
setSelectionCoords,
|
||||
setSelectionData,
|
||||
setSelectionDateInterval,
|
||||
x
|
||||
]
|
||||
x,
|
||||
],
|
||||
)
|
||||
|
||||
const buildEventRects = useCallback(
|
||||
|
|
@ -298,7 +297,7 @@ const Graph = ({
|
|||
.attr('height', GRAPH_HEIGHT - GRAPH_MARGIN.bottom - GRAPH_MARGIN.top)
|
||||
.attr('stroke', 'transparent')
|
||||
.attr('fill', 'transparent'),
|
||||
[GRAPH_MARGIN, x]
|
||||
[GRAPH_MARGIN, x],
|
||||
)
|
||||
|
||||
const formatTicksText = useCallback(
|
||||
|
|
@ -309,7 +308,7 @@ const Graph = ({
|
|||
.style('fill', fontColor)
|
||||
.style('stroke-width', 0.5)
|
||||
.style('font-family', fontSecondary),
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const drawCashIn = useCallback(
|
||||
|
|
@ -334,7 +333,7 @@ const Graph = ({
|
|||
GRAPH_HEIGHT -
|
||||
y(interval[1].cashIn) -
|
||||
GRAPH_MARGIN.bottom -
|
||||
BAR_MARGIN / 2
|
||||
BAR_MARGIN / 2,
|
||||
)
|
||||
})
|
||||
.attr('width', d => {
|
||||
|
|
@ -348,7 +347,7 @@ const Graph = ({
|
|||
})
|
||||
.attr('rx', 2.5)
|
||||
},
|
||||
[x, y, GRAPH_MARGIN, groupedByTxClass]
|
||||
[x, y, GRAPH_MARGIN, groupedByTxClass],
|
||||
)
|
||||
|
||||
const drawCashOut = useCallback(
|
||||
|
|
@ -377,7 +376,7 @@ const Graph = ({
|
|||
GRAPH_HEIGHT -
|
||||
y(interval[1].cashOut) -
|
||||
GRAPH_MARGIN.bottom -
|
||||
BAR_MARGIN / 2
|
||||
BAR_MARGIN / 2,
|
||||
)
|
||||
})
|
||||
.attr('width', d => {
|
||||
|
|
@ -391,7 +390,7 @@ const Graph = ({
|
|||
})
|
||||
.attr('rx', 2.5)
|
||||
},
|
||||
[x, y, GRAPH_MARGIN, groupedByTxClass]
|
||||
[x, y, GRAPH_MARGIN, groupedByTxClass],
|
||||
)
|
||||
|
||||
const drawChart = useCallback(() => {
|
||||
|
|
@ -417,7 +416,7 @@ const Graph = ({
|
|||
buildVerticalLines,
|
||||
drawCashIn,
|
||||
formatTicksText,
|
||||
drawCashOut
|
||||
drawCashOut,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -433,5 +432,5 @@ export default memo(
|
|||
(prev, next) =>
|
||||
R.equals(prev.period, next.period) &&
|
||||
R.equals(prev.selectedDay, next.selectedDay) &&
|
||||
R.equals(prev.selectedMachine, next.selectedMachine)
|
||||
R.equals(prev.selectedMachine, next.selectedMachine),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
fontColor,
|
||||
primaryColor,
|
||||
fontSecondary,
|
||||
subheaderColor
|
||||
subheaderColor,
|
||||
} from 'src/styling/variables'
|
||||
import { numberToFiatAmount } from 'src/utils/number'
|
||||
import { MINUTE, DAY, WEEK, MONTH } from 'src/utils/time'
|
||||
|
|
@ -25,7 +25,7 @@ const Graph = ({
|
|||
setSelectionCoords,
|
||||
setSelectionData,
|
||||
setSelectionDateInterval,
|
||||
log = false
|
||||
log = false,
|
||||
}) => {
|
||||
const ref = useRef(null)
|
||||
|
||||
|
|
@ -38,9 +38,9 @@ const Graph = ({
|
|||
top: 25,
|
||||
right: 3.5,
|
||||
bottom: 27,
|
||||
left: 38
|
||||
left: 38,
|
||||
}),
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const offset = getTimezoneOffset(timezone)
|
||||
|
|
@ -50,7 +50,7 @@ const Graph = ({
|
|||
day: [NOW - DAY, NOW],
|
||||
threeDays: [NOW - 3 * DAY, NOW],
|
||||
week: [NOW - WEEK, NOW],
|
||||
month: [NOW - MONTH, NOW]
|
||||
month: [NOW - MONTH, NOW],
|
||||
}
|
||||
|
||||
const dataPoints = useMemo(
|
||||
|
|
@ -59,28 +59,28 @@ const Graph = ({
|
|||
freq: 24,
|
||||
step: 60 * 60 * 1000,
|
||||
tick: d3.utcHour.every(1),
|
||||
labelFormat: '%H:%M'
|
||||
labelFormat: '%H:%M',
|
||||
},
|
||||
threeDays: {
|
||||
freq: 12,
|
||||
step: 6 * 60 * 60 * 1000,
|
||||
tick: d3.utcDay.every(1),
|
||||
labelFormat: '%a %d'
|
||||
labelFormat: '%a %d',
|
||||
},
|
||||
week: {
|
||||
freq: 7,
|
||||
step: 24 * 60 * 60 * 1000,
|
||||
tick: d3.utcDay.every(1),
|
||||
labelFormat: '%a %d'
|
||||
labelFormat: '%a %d',
|
||||
},
|
||||
month: {
|
||||
freq: 30,
|
||||
step: 24 * 60 * 60 * 1000,
|
||||
tick: d3.utcDay.every(1),
|
||||
labelFormat: '%d'
|
||||
}
|
||||
labelFormat: '%d',
|
||||
},
|
||||
}),
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const getPastAndCurrentDayLabels = useCallback(d => {
|
||||
|
|
@ -97,11 +97,11 @@ const Graph = ({
|
|||
const previousDateMonth = previousDate.getUTCMonth()
|
||||
|
||||
const daysOfWeek = Array.from(Array(7)).map((_, i) =>
|
||||
format('EEE', add({ days: i }, startOfWeek(new Date())))
|
||||
format('EEE', add({ days: i }, startOfWeek(new Date()))),
|
||||
)
|
||||
|
||||
const months = Array.from(Array(12)).map((_, i) =>
|
||||
format('LLL', add({ months: i }, startOfYear(new Date())))
|
||||
format('LLL', add({ months: i }, startOfYear(new Date()))),
|
||||
)
|
||||
|
||||
return {
|
||||
|
|
@ -112,7 +112,7 @@ const Graph = ({
|
|||
current:
|
||||
currentDateMonth !== previousDateMonth
|
||||
? months[currentDateMonth]
|
||||
: `${daysOfWeek[currentDateWeekday]} ${currentDateDay}`
|
||||
: `${daysOfWeek[currentDateWeekday]} ${currentDateDay}`,
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ const Graph = ({
|
|||
|
||||
return points
|
||||
},
|
||||
[NOW, dataPoints, period.code]
|
||||
[NOW, dataPoints, period.code],
|
||||
)
|
||||
|
||||
const buildAreas = useCallback(
|
||||
|
|
@ -159,7 +159,7 @@ const Graph = ({
|
|||
|
||||
return points
|
||||
},
|
||||
[NOW, dataPoints, period.code]
|
||||
[NOW, dataPoints, period.code],
|
||||
)
|
||||
|
||||
const x = d3
|
||||
|
|
@ -177,7 +177,7 @@ const Graph = ({
|
|||
.scaleLinear()
|
||||
.domain([
|
||||
0,
|
||||
(d3.max(data, d => new BigNumber(d.fiat).toNumber()) ?? 1000) * 1.03
|
||||
(d3.max(data, d => new BigNumber(d.fiat).toNumber()) ?? 1000) * 1.03,
|
||||
])
|
||||
.nice()
|
||||
.range([GRAPH_HEIGHT - GRAPH_MARGIN.bottom, GRAPH_MARGIN.top])
|
||||
|
|
@ -186,7 +186,7 @@ const Graph = ({
|
|||
.scaleLog()
|
||||
.domain([
|
||||
(d3.min(data, d => new BigNumber(d.fiat).toNumber()) ?? 1) * 0.9,
|
||||
(d3.max(data, d => new BigNumber(d.fiat).toNumber()) ?? 1000) * 1.1
|
||||
(d3.max(data, d => new BigNumber(d.fiat).toNumber()) ?? 1000) * 1.1,
|
||||
])
|
||||
.range([GRAPH_HEIGHT - GRAPH_MARGIN.bottom, GRAPH_MARGIN.top])
|
||||
|
||||
|
|
@ -196,7 +196,7 @@ const Graph = ({
|
|||
const fullBreakpoints = [
|
||||
graphLimits[1],
|
||||
...R.filter(it => it > dataLimits[0] && it < dataLimits[1], breakpoints),
|
||||
dataLimits[0]
|
||||
dataLimits[0],
|
||||
]
|
||||
|
||||
const intervals = []
|
||||
|
|
@ -227,7 +227,7 @@ const Graph = ({
|
|||
g
|
||||
.attr(
|
||||
'transform',
|
||||
`translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`
|
||||
`translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`,
|
||||
)
|
||||
.call(
|
||||
d3
|
||||
|
|
@ -235,18 +235,18 @@ const Graph = ({
|
|||
.ticks(dataPoints[period.code].tick)
|
||||
.tickFormat(d => {
|
||||
return d3.timeFormat(dataPoints[period.code].labelFormat)(
|
||||
d.getTime() + d.getTimezoneOffset() * MINUTE
|
||||
d.getTime() + d.getTimezoneOffset() * MINUTE,
|
||||
)
|
||||
})
|
||||
.tickSizeOuter(0)
|
||||
.tickSizeOuter(0),
|
||||
)
|
||||
.call(g =>
|
||||
g
|
||||
.select('.domain')
|
||||
.attr('stroke', primaryColor)
|
||||
.attr('stroke-width', 1)
|
||||
.attr('stroke-width', 1),
|
||||
),
|
||||
[GRAPH_MARGIN, dataPoints, period.code, x]
|
||||
[GRAPH_MARGIN, dataPoints, period.code, x],
|
||||
)
|
||||
|
||||
const buildYAxis = useCallback(
|
||||
|
|
@ -264,12 +264,12 @@ const Graph = ({
|
|||
if (d >= 1000) return numberToFiatAmount(d / 1000) + 'k'
|
||||
|
||||
return numberToFiatAmount(d)
|
||||
})
|
||||
}),
|
||||
)
|
||||
.select('.domain')
|
||||
.attr('stroke', primaryColor)
|
||||
.attr('stroke-width', 1),
|
||||
[GRAPH_MARGIN, y, log]
|
||||
[GRAPH_MARGIN, y, log],
|
||||
)
|
||||
|
||||
const buildGrid = useCallback(
|
||||
|
|
@ -286,7 +286,7 @@ const Graph = ({
|
|||
.attr('x1', d => 0.5 + x(d))
|
||||
.attr('x2', d => 0.5 + x(d))
|
||||
.attr('y1', GRAPH_MARGIN.top)
|
||||
.attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom)
|
||||
.attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom),
|
||||
)
|
||||
// Horizontal lines
|
||||
.call(g =>
|
||||
|
|
@ -297,13 +297,13 @@ const Graph = ({
|
|||
d3
|
||||
.axisLeft(y)
|
||||
.scale()
|
||||
.ticks(GRAPH_HEIGHT / 100)
|
||||
.ticks(GRAPH_HEIGHT / 100),
|
||||
)
|
||||
.join('line')
|
||||
.attr('y1', d => 0.5 + y(d))
|
||||
.attr('y2', d => 0.5 + y(d))
|
||||
.attr('x1', GRAPH_MARGIN.left)
|
||||
.attr('x2', GRAPH_WIDTH)
|
||||
.attr('x2', GRAPH_WIDTH),
|
||||
)
|
||||
// Vertical transparent rectangles for events
|
||||
.call(g =>
|
||||
|
|
@ -319,14 +319,14 @@ const Graph = ({
|
|||
const intervals = getAreaInterval(
|
||||
buildAreas(x.domain()).map(it => Math.round(x(it) * 100) / 100),
|
||||
x.range(),
|
||||
x2.range()
|
||||
x2.range(),
|
||||
)
|
||||
const interval = getAreaIntervalByX(intervals, xValue)
|
||||
return Math.round((interval[0] - interval[1]) * 100) / 100
|
||||
})
|
||||
.attr(
|
||||
'height',
|
||||
GRAPH_HEIGHT - GRAPH_MARGIN.bottom - GRAPH_MARGIN.top
|
||||
GRAPH_HEIGHT - GRAPH_MARGIN.bottom - GRAPH_MARGIN.top,
|
||||
)
|
||||
.attr('stroke', 'transparent')
|
||||
.attr('fill', 'transparent')
|
||||
|
|
@ -336,7 +336,7 @@ const Graph = ({
|
|||
const intervals = getAreaInterval(
|
||||
buildAreas(x.domain()).map(it => Math.round(x(it) * 100) / 100),
|
||||
x.range(),
|
||||
x2.range()
|
||||
x2.range(),
|
||||
)
|
||||
|
||||
const dateInterval = getDateIntervalByX(areas, intervals, xValue)
|
||||
|
|
@ -354,8 +354,8 @@ const Graph = ({
|
|||
left: R.clone(d.target.getBoundingClientRect().x),
|
||||
right: R.clone(
|
||||
d.target.getBoundingClientRect().x +
|
||||
d.target.getBoundingClientRect().width
|
||||
)
|
||||
d.target.getBoundingClientRect().width,
|
||||
),
|
||||
}
|
||||
|
||||
const xCoord =
|
||||
|
|
@ -370,7 +370,7 @@ const Graph = ({
|
|||
setSelectionData(filteredData)
|
||||
setSelectionCoords({
|
||||
x: Math.round(xCoord),
|
||||
y: Math.round(yCoord)
|
||||
y: Math.round(yCoord),
|
||||
})
|
||||
|
||||
d3.select(d.target).attr('fill', subheaderColor)
|
||||
|
|
@ -380,7 +380,7 @@ const Graph = ({
|
|||
setSelectionDateInterval(null)
|
||||
setSelectionData(null)
|
||||
setSelectionCoords(null)
|
||||
})
|
||||
}),
|
||||
)
|
||||
// Thick vertical lines
|
||||
.call(g =>
|
||||
|
|
@ -391,7 +391,7 @@ const Graph = ({
|
|||
buildTicks(x.domain()).filter(x => {
|
||||
if (period.code === 'day') return x.getUTCHours() === 0
|
||||
return x.getUTCDate() === 1
|
||||
})
|
||||
}),
|
||||
)
|
||||
.join('line')
|
||||
.attr('class', 'dateSeparator')
|
||||
|
|
@ -400,7 +400,7 @@ const Graph = ({
|
|||
.attr('y1', GRAPH_MARGIN.top - 50)
|
||||
.attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom)
|
||||
.attr('stroke-width', 5)
|
||||
.join('text')
|
||||
.join('text'),
|
||||
)
|
||||
// Left side breakpoint label
|
||||
.call(g => {
|
||||
|
|
@ -458,8 +458,8 @@ const Graph = ({
|
|||
offset,
|
||||
setSelectionCoords,
|
||||
setSelectionData,
|
||||
setSelectionDateInterval
|
||||
]
|
||||
setSelectionDateInterval,
|
||||
],
|
||||
)
|
||||
|
||||
const formatTicksText = useCallback(
|
||||
|
|
@ -470,7 +470,7 @@ const Graph = ({
|
|||
.style('fill', fontColor)
|
||||
.style('stroke-width', 0.5)
|
||||
.style('font-family', fontSecondary),
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const formatText = useCallback(
|
||||
|
|
@ -481,7 +481,7 @@ const Graph = ({
|
|||
.style('fill', offColor)
|
||||
.style('stroke-width', 0.5)
|
||||
.style('font-family', fontSecondary),
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const formatTicks = useCallback(() => {
|
||||
|
|
@ -505,10 +505,10 @@ const Graph = ({
|
|||
.attr('y1', 0.5 + y(median))
|
||||
.attr('y2', 0.5 + y(median))
|
||||
.attr('x1', GRAPH_MARGIN.left)
|
||||
.attr('x2', GRAPH_WIDTH)
|
||||
.attr('x2', GRAPH_WIDTH),
|
||||
)
|
||||
},
|
||||
[GRAPH_MARGIN, y, data, log]
|
||||
[GRAPH_MARGIN, y, data, log],
|
||||
)
|
||||
|
||||
const drawData = useCallback(
|
||||
|
|
@ -524,7 +524,7 @@ const Graph = ({
|
|||
.attr('fill', d => (d.txClass === 'cashIn' ? java : neon))
|
||||
.attr('r', 3.5)
|
||||
},
|
||||
[data, offset, x, y]
|
||||
[data, offset, x, y],
|
||||
)
|
||||
|
||||
const drawChart = useCallback(() => {
|
||||
|
|
@ -550,7 +550,7 @@ const Graph = ({
|
|||
drawData,
|
||||
formatText,
|
||||
formatTicks,
|
||||
formatTicksText
|
||||
formatTicksText,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -566,5 +566,5 @@ export default memo(
|
|||
(prev, next) =>
|
||||
R.equals(prev.period, next.period) &&
|
||||
R.equals(prev.selectedMachine, next.selectedMachine) &&
|
||||
R.equals(prev.log, next.log)
|
||||
R.equals(prev.log, next.log),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
differenceInMilliseconds,
|
||||
format,
|
||||
startOfWeek,
|
||||
startOfYear
|
||||
startOfYear,
|
||||
} from 'date-fns/fp'
|
||||
import * as R from 'ramda'
|
||||
import React, { memo, useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
|
|
@ -21,7 +21,7 @@ import {
|
|||
fontColor,
|
||||
primaryColor,
|
||||
fontSecondary,
|
||||
subheaderColor
|
||||
subheaderColor,
|
||||
} from 'src/styling/variables'
|
||||
import { numberToFiatAmount } from 'src/utils/number'
|
||||
import { MINUTE, DAY, WEEK, MONTH } from 'src/utils/time'
|
||||
|
|
@ -33,7 +33,7 @@ const Graph = ({
|
|||
setSelectionCoords,
|
||||
setSelectionData,
|
||||
setSelectionDateInterval,
|
||||
log = false
|
||||
log = false,
|
||||
}) => {
|
||||
const ref = useRef(null)
|
||||
|
||||
|
|
@ -46,9 +46,9 @@ const Graph = ({
|
|||
top: 25,
|
||||
right: 3.5,
|
||||
bottom: 27,
|
||||
left: 38
|
||||
left: 38,
|
||||
}),
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const offset = getTimezoneOffset(timezone)
|
||||
|
|
@ -58,7 +58,7 @@ const Graph = ({
|
|||
day: [NOW - DAY, NOW],
|
||||
threeDays: [NOW - 3 * DAY, NOW],
|
||||
week: [NOW - WEEK, NOW],
|
||||
month: [NOW - MONTH, NOW]
|
||||
month: [NOW - MONTH, NOW],
|
||||
}
|
||||
|
||||
const dataPoints = useMemo(
|
||||
|
|
@ -67,28 +67,28 @@ const Graph = ({
|
|||
freq: 24,
|
||||
step: 60 * 60 * 1000,
|
||||
tick: d3.utcHour.every(1),
|
||||
labelFormat: '%H:%M'
|
||||
labelFormat: '%H:%M',
|
||||
},
|
||||
threeDays: {
|
||||
freq: 12,
|
||||
step: 6 * 60 * 60 * 1000,
|
||||
tick: d3.utcDay.every(1),
|
||||
labelFormat: '%a %d'
|
||||
labelFormat: '%a %d',
|
||||
},
|
||||
week: {
|
||||
freq: 7,
|
||||
step: 24 * 60 * 60 * 1000,
|
||||
tick: d3.utcDay.every(1),
|
||||
labelFormat: '%a %d'
|
||||
labelFormat: '%a %d',
|
||||
},
|
||||
month: {
|
||||
freq: 30,
|
||||
step: 24 * 60 * 60 * 1000,
|
||||
tick: d3.utcDay.every(1),
|
||||
labelFormat: '%d'
|
||||
}
|
||||
labelFormat: '%d',
|
||||
},
|
||||
}),
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const getPastAndCurrentDayLabels = useCallback(d => {
|
||||
|
|
@ -105,11 +105,11 @@ const Graph = ({
|
|||
const previousDateMonth = previousDate.getUTCMonth()
|
||||
|
||||
const daysOfWeek = Array.from(Array(7)).map((_, i) =>
|
||||
format('EEE', add({ days: i }, startOfWeek(new Date())))
|
||||
format('EEE', add({ days: i }, startOfWeek(new Date()))),
|
||||
)
|
||||
|
||||
const months = Array.from(Array(12)).map((_, i) =>
|
||||
format('LLL', add({ months: i }, startOfYear(new Date())))
|
||||
format('LLL', add({ months: i }, startOfYear(new Date()))),
|
||||
)
|
||||
|
||||
return {
|
||||
|
|
@ -120,7 +120,7 @@ const Graph = ({
|
|||
current:
|
||||
currentDateMonth !== previousDateMonth
|
||||
? months[currentDateMonth]
|
||||
: `${daysOfWeek[currentDateWeekday]} ${currentDateDay}`
|
||||
: `${daysOfWeek[currentDateWeekday]} ${currentDateDay}`,
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
|
@ -142,7 +142,7 @@ const Graph = ({
|
|||
|
||||
return points
|
||||
},
|
||||
[NOW, dataPoints, period.code]
|
||||
[NOW, dataPoints, period.code],
|
||||
)
|
||||
|
||||
const buildAreas = useCallback(
|
||||
|
|
@ -167,7 +167,7 @@ const Graph = ({
|
|||
|
||||
return points
|
||||
},
|
||||
[NOW, dataPoints, period.code]
|
||||
[NOW, dataPoints, period.code],
|
||||
)
|
||||
|
||||
const x = d3
|
||||
|
|
@ -192,14 +192,14 @@ const Graph = ({
|
|||
else if (i === dates.length - 1)
|
||||
return addMilliseconds(
|
||||
-dataPoints[period.code].step,
|
||||
dates[dates.length - 2]
|
||||
dates[dates.length - 2],
|
||||
)
|
||||
else return date
|
||||
})
|
||||
.map(date => {
|
||||
const middleOfBin = addMilliseconds(
|
||||
dataPoints[period.code].step / 2,
|
||||
date
|
||||
date,
|
||||
)
|
||||
|
||||
const txs = data.filter(tx => {
|
||||
|
|
@ -236,7 +236,7 @@ const Graph = ({
|
|||
.scaleLog()
|
||||
.domain([
|
||||
min === 0 ? 0.9 : min * 0.9,
|
||||
(max === min ? min + Math.pow(10, 2 * min + 1) : max) * 2
|
||||
(max === min ? min + Math.pow(10, 2 * min + 1) : max) * 2,
|
||||
])
|
||||
.clamp(true)
|
||||
.range([GRAPH_HEIGHT - GRAPH_MARGIN.bottom, GRAPH_MARGIN.top])
|
||||
|
|
@ -247,7 +247,7 @@ const Graph = ({
|
|||
const fullBreakpoints = [
|
||||
graphLimits[1],
|
||||
...R.filter(it => it > dataLimits[0] && it < dataLimits[1], breakpoints),
|
||||
dataLimits[0]
|
||||
dataLimits[0],
|
||||
]
|
||||
|
||||
const intervals = []
|
||||
|
|
@ -278,7 +278,7 @@ const Graph = ({
|
|||
g
|
||||
.attr(
|
||||
'transform',
|
||||
`translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`
|
||||
`translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`,
|
||||
)
|
||||
.call(
|
||||
d3
|
||||
|
|
@ -286,18 +286,18 @@ const Graph = ({
|
|||
.ticks(dataPoints[period.code].tick)
|
||||
.tickFormat(d => {
|
||||
return d3.timeFormat(dataPoints[period.code].labelFormat)(
|
||||
d.getTime() + d.getTimezoneOffset() * MINUTE
|
||||
d.getTime() + d.getTimezoneOffset() * MINUTE,
|
||||
)
|
||||
})
|
||||
.tickSizeOuter(0)
|
||||
.tickSizeOuter(0),
|
||||
)
|
||||
.call(g =>
|
||||
g
|
||||
.select('.domain')
|
||||
.attr('stroke', primaryColor)
|
||||
.attr('stroke-width', 1)
|
||||
.attr('stroke-width', 1),
|
||||
),
|
||||
[GRAPH_MARGIN, dataPoints, period.code, x]
|
||||
[GRAPH_MARGIN, dataPoints, period.code, x],
|
||||
)
|
||||
|
||||
const buildYAxis = useCallback(
|
||||
|
|
@ -315,12 +315,12 @@ const Graph = ({
|
|||
if (d >= 1000) return numberToFiatAmount(d / 1000) + 'k'
|
||||
|
||||
return numberToFiatAmount(d)
|
||||
})
|
||||
}),
|
||||
)
|
||||
.select('.domain')
|
||||
.attr('stroke', primaryColor)
|
||||
.attr('stroke-width', 1),
|
||||
[GRAPH_MARGIN, y, log]
|
||||
[GRAPH_MARGIN, y, log],
|
||||
)
|
||||
|
||||
const buildGrid = useCallback(
|
||||
|
|
@ -337,7 +337,7 @@ const Graph = ({
|
|||
.attr('x1', d => 0.5 + x(d))
|
||||
.attr('x2', d => 0.5 + x(d))
|
||||
.attr('y1', GRAPH_MARGIN.top)
|
||||
.attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom)
|
||||
.attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom),
|
||||
)
|
||||
// Horizontal lines
|
||||
.call(g =>
|
||||
|
|
@ -348,13 +348,13 @@ const Graph = ({
|
|||
d3
|
||||
.axisLeft(y)
|
||||
.scale()
|
||||
.ticks(GRAPH_HEIGHT / 100)
|
||||
.ticks(GRAPH_HEIGHT / 100),
|
||||
)
|
||||
.join('line')
|
||||
.attr('y1', d => 0.5 + y(d))
|
||||
.attr('y2', d => 0.5 + y(d))
|
||||
.attr('x1', GRAPH_MARGIN.left)
|
||||
.attr('x2', GRAPH_WIDTH)
|
||||
.attr('x2', GRAPH_WIDTH),
|
||||
)
|
||||
// Vertical transparent rectangles for events
|
||||
.call(g =>
|
||||
|
|
@ -370,14 +370,14 @@ const Graph = ({
|
|||
const intervals = getAreaInterval(
|
||||
buildAreas(x.domain()).map(it => Math.round(x(it) * 100) / 100),
|
||||
x.range(),
|
||||
x2.range()
|
||||
x2.range(),
|
||||
)
|
||||
const interval = getAreaIntervalByX(intervals, xValue)
|
||||
return Math.round((interval[0] - interval[1]) * 100) / 100
|
||||
})
|
||||
.attr(
|
||||
'height',
|
||||
GRAPH_HEIGHT - GRAPH_MARGIN.bottom - GRAPH_MARGIN.top
|
||||
GRAPH_HEIGHT - GRAPH_MARGIN.bottom - GRAPH_MARGIN.top,
|
||||
)
|
||||
.attr('stroke', 'transparent')
|
||||
.attr('fill', 'transparent')
|
||||
|
|
@ -387,7 +387,7 @@ const Graph = ({
|
|||
const intervals = getAreaInterval(
|
||||
buildAreas(x.domain()).map(it => Math.round(x(it) * 100) / 100),
|
||||
x.range(),
|
||||
x2.range()
|
||||
x2.range(),
|
||||
)
|
||||
|
||||
const dateInterval = getDateIntervalByX(areas, intervals, xValue)
|
||||
|
|
@ -405,8 +405,8 @@ const Graph = ({
|
|||
left: R.clone(d.target.getBoundingClientRect().x),
|
||||
right: R.clone(
|
||||
d.target.getBoundingClientRect().x +
|
||||
d.target.getBoundingClientRect().width
|
||||
)
|
||||
d.target.getBoundingClientRect().width,
|
||||
),
|
||||
}
|
||||
|
||||
const xCoord =
|
||||
|
|
@ -421,7 +421,7 @@ const Graph = ({
|
|||
setSelectionData(filteredData)
|
||||
setSelectionCoords({
|
||||
x: Math.round(xCoord),
|
||||
y: Math.round(yCoord)
|
||||
y: Math.round(yCoord),
|
||||
})
|
||||
|
||||
d3.select(d.target).attr('fill', subheaderColor)
|
||||
|
|
@ -431,7 +431,7 @@ const Graph = ({
|
|||
setSelectionDateInterval(null)
|
||||
setSelectionData(null)
|
||||
setSelectionCoords(null)
|
||||
})
|
||||
}),
|
||||
)
|
||||
// Thick vertical lines
|
||||
.call(g =>
|
||||
|
|
@ -442,7 +442,7 @@ const Graph = ({
|
|||
buildTicks(x.domain()).filter(x => {
|
||||
if (period.code === 'day') return x.getUTCHours() === 0
|
||||
return x.getUTCDate() === 1
|
||||
})
|
||||
}),
|
||||
)
|
||||
.join('line')
|
||||
.attr('class', 'dateSeparator')
|
||||
|
|
@ -451,7 +451,7 @@ const Graph = ({
|
|||
.attr('y1', GRAPH_MARGIN.top - 50)
|
||||
.attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom)
|
||||
.attr('stroke-width', 5)
|
||||
.join('text')
|
||||
.join('text'),
|
||||
)
|
||||
// Left side breakpoint label
|
||||
.call(g => {
|
||||
|
|
@ -509,8 +509,8 @@ const Graph = ({
|
|||
offset,
|
||||
setSelectionCoords,
|
||||
setSelectionData,
|
||||
setSelectionDateInterval
|
||||
]
|
||||
setSelectionDateInterval,
|
||||
],
|
||||
)
|
||||
|
||||
const formatTicksText = useCallback(
|
||||
|
|
@ -521,7 +521,7 @@ const Graph = ({
|
|||
.style('fill', fontColor)
|
||||
.style('stroke-width', 0.5)
|
||||
.style('font-family', fontSecondary),
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const formatText = useCallback(
|
||||
|
|
@ -532,7 +532,7 @@ const Graph = ({
|
|||
.style('fill', offColor)
|
||||
.style('stroke-width', 0.5)
|
||||
.style('font-family', fontSecondary),
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const formatTicks = useCallback(() => {
|
||||
|
|
@ -574,7 +574,7 @@ const Graph = ({
|
|||
.line()
|
||||
.curve(d3.curveMonotoneX)
|
||||
.x(d => x(d.date))
|
||||
.y(d => y(d.cashIn))
|
||||
.y(d => y(d.cashIn)),
|
||||
)
|
||||
|
||||
g.append('g')
|
||||
|
|
@ -599,10 +599,10 @@ const Graph = ({
|
|||
.line()
|
||||
.curve(d3.curveMonotoneX)
|
||||
.x(d => x(d.date))
|
||||
.y(d => y(d.cashOut))
|
||||
.y(d => y(d.cashOut)),
|
||||
)
|
||||
},
|
||||
[x, y, bins, GRAPH_MARGIN]
|
||||
[x, y, bins, GRAPH_MARGIN],
|
||||
)
|
||||
|
||||
const drawChart = useCallback(() => {
|
||||
|
|
@ -626,7 +626,7 @@ const Graph = ({
|
|||
drawData,
|
||||
formatText,
|
||||
formatTicks,
|
||||
formatTicksText
|
||||
formatTicksText,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -642,5 +642,5 @@ export default memo(
|
|||
(prev, next) =>
|
||||
R.equals(prev.period, next.period) &&
|
||||
R.equals(prev.selectedMachine, next.selectedMachine) &&
|
||||
R.equals(prev.log, next.log)
|
||||
R.equals(prev.log, next.log),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import {
|
|||
neon,
|
||||
subheaderDarkColor,
|
||||
fontColor,
|
||||
fontSecondary
|
||||
fontSecondary,
|
||||
} from 'src/styling/variables'
|
||||
|
||||
const Graph = ({ data, machines, currency, selectedMachine }) => {
|
||||
const Graph = ({ data, machines, currency }) => {
|
||||
const ref = useRef(null)
|
||||
|
||||
const AMOUNT_OF_MACHINES = 5
|
||||
|
|
@ -24,9 +24,9 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
top: 25,
|
||||
right: 0.5,
|
||||
bottom: 27,
|
||||
left: 36.5
|
||||
left: 36.5,
|
||||
}),
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const machinesClone = R.clone(machines)
|
||||
|
|
@ -41,7 +41,7 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
if (!R.isNil(machinesClone[it])) return machinesClone[it]
|
||||
return { code: `ghostMachine${it}`, display: `` }
|
||||
},
|
||||
R.times(R.identity, AMOUNT_OF_MACHINES)
|
||||
R.times(R.identity, AMOUNT_OF_MACHINES),
|
||||
)
|
||||
|
||||
const txByDevice = R.reduce(
|
||||
|
|
@ -50,14 +50,14 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
return acc
|
||||
},
|
||||
{},
|
||||
filledMachines
|
||||
filledMachines,
|
||||
)
|
||||
|
||||
const getDeviceVolume = deviceId =>
|
||||
R.reduce(
|
||||
(acc, value) => acc + BigNumber(value.fiat).toNumber(),
|
||||
0,
|
||||
txByDevice[deviceId]
|
||||
txByDevice[deviceId],
|
||||
)
|
||||
|
||||
const getDeviceVolumeByTxClass = deviceId =>
|
||||
|
|
@ -70,18 +70,18 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
return acc
|
||||
},
|
||||
{ cashIn: 0, cashOut: 0 },
|
||||
txByDevice[deviceId]
|
||||
txByDevice[deviceId],
|
||||
)
|
||||
|
||||
const devicesByVolume = R.sort(
|
||||
(a, b) => b[1] - a[1],
|
||||
R.map(m => [m.code, getDeviceVolume(m.code)], filledMachines)
|
||||
R.map(m => [m.code, getDeviceVolume(m.code)], filledMachines),
|
||||
)
|
||||
|
||||
const topMachines = R.take(AMOUNT_OF_MACHINES, devicesByVolume)
|
||||
|
||||
const txClassVolumeByDevice = R.fromPairs(
|
||||
R.map(v => [v[0], getDeviceVolumeByTxClass(v[0])], topMachines)
|
||||
R.map(v => [v[0], getDeviceVolumeByTxClass(v[0])], topMachines),
|
||||
)
|
||||
|
||||
const x = d3
|
||||
|
|
@ -94,7 +94,9 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
.scaleLinear()
|
||||
.domain([
|
||||
0,
|
||||
d3.max(topMachines, d => d[1]) !== 0 ? d3.max(topMachines, d => d[1]) : 50
|
||||
d3.max(topMachines, d => d[1]) !== 0
|
||||
? d3.max(topMachines, d => d[1])
|
||||
: 50,
|
||||
])
|
||||
.range([GRAPH_HEIGHT - GRAPH_MARGIN.bottom, GRAPH_MARGIN.top])
|
||||
|
||||
|
|
@ -104,7 +106,7 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
.attr('class', 'x-axis-1')
|
||||
.attr(
|
||||
'transform',
|
||||
`translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`
|
||||
`translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`,
|
||||
)
|
||||
.call(
|
||||
d3
|
||||
|
|
@ -113,12 +115,12 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
d =>
|
||||
`${
|
||||
R.find(it => it.code === d[0], filledMachines).display ?? ''
|
||||
}`
|
||||
}`,
|
||||
)
|
||||
.tickSize(0)
|
||||
.tickPadding(10)
|
||||
.tickPadding(10),
|
||||
),
|
||||
[GRAPH_MARGIN, x, filledMachines]
|
||||
[GRAPH_MARGIN, x, filledMachines],
|
||||
)
|
||||
|
||||
const buildXAxis2 = useCallback(
|
||||
|
|
@ -126,7 +128,7 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
g.attr('class', 'x-axis-2')
|
||||
.attr(
|
||||
'transform',
|
||||
`translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`
|
||||
`translate(0, ${GRAPH_HEIGHT - GRAPH_MARGIN.bottom})`,
|
||||
)
|
||||
.call(
|
||||
d3
|
||||
|
|
@ -134,24 +136,24 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
.tickFormat(d =>
|
||||
R.includes(`ghostMachine`, d[0])
|
||||
? ``
|
||||
: `${d[1].toFixed(2)} ${currency}`
|
||||
: `${d[1].toFixed(2)} ${currency}`,
|
||||
)
|
||||
.tickSize(0)
|
||||
.tickPadding(10)
|
||||
.tickPadding(10),
|
||||
)
|
||||
},
|
||||
[GRAPH_MARGIN, x, currency]
|
||||
[GRAPH_MARGIN, x, currency],
|
||||
)
|
||||
|
||||
const positionXAxisLabels = useCallback(() => {
|
||||
d3.selectAll('.x-axis-1 .tick text').attr('transform', function (d) {
|
||||
d3.selectAll('.x-axis-1 .tick text').attr('transform', function () {
|
||||
const widthPerEntry = (x.range()[1] - x.range()[0]) / AMOUNT_OF_MACHINES
|
||||
return `translate(${-widthPerEntry / 2.25 + this.getBBox().width / 2}, 0)`
|
||||
})
|
||||
}, [x])
|
||||
|
||||
const positionXAxis2Labels = useCallback(() => {
|
||||
d3.selectAll('.x-axis-2 .tick text').attr('transform', function (d) {
|
||||
d3.selectAll('.x-axis-2 .tick text').attr('transform', function () {
|
||||
const widthPerEntry = (x.range()[1] - x.range()[0]) / AMOUNT_OF_MACHINES
|
||||
return `translate(${widthPerEntry / 2.25 - this.getBBox().width / 2}, 0)`
|
||||
})
|
||||
|
|
@ -166,10 +168,10 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
.axisLeft(y)
|
||||
.ticks(GRAPH_HEIGHT / 100)
|
||||
.tickSize(0)
|
||||
.tickFormat(``)
|
||||
.tickFormat(``),
|
||||
)
|
||||
.call(g => g.select('.domain').remove()),
|
||||
[GRAPH_MARGIN, y]
|
||||
[GRAPH_MARGIN, y],
|
||||
)
|
||||
|
||||
const formatTicksText = useCallback(
|
||||
|
|
@ -180,7 +182,7 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
.style('fill', fontColor)
|
||||
.style('stroke-width', 0.5)
|
||||
.style('font-family', fontSecondary),
|
||||
[]
|
||||
[],
|
||||
)
|
||||
|
||||
const buildGrid = useCallback(
|
||||
|
|
@ -213,10 +215,10 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
return 0.5 + x(d) - paddedXValue
|
||||
})
|
||||
.attr('y1', GRAPH_MARGIN.top)
|
||||
.attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom)
|
||||
.attr('y2', GRAPH_HEIGHT - GRAPH_MARGIN.bottom),
|
||||
)
|
||||
},
|
||||
[GRAPH_MARGIN, x]
|
||||
[GRAPH_MARGIN, x],
|
||||
)
|
||||
|
||||
const drawCashIn = useCallback(
|
||||
|
|
@ -231,13 +233,13 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
R.clamp(
|
||||
0,
|
||||
GRAPH_HEIGHT,
|
||||
GRAPH_HEIGHT - y(d[1].cashIn) - GRAPH_MARGIN.bottom - BAR_MARGIN
|
||||
)
|
||||
GRAPH_HEIGHT - y(d[1].cashIn) - GRAPH_MARGIN.bottom - BAR_MARGIN,
|
||||
),
|
||||
)
|
||||
.attr('width', x.bandwidth())
|
||||
.attr('rx', 2.5)
|
||||
},
|
||||
[txClassVolumeByDevice, x, y, GRAPH_MARGIN]
|
||||
[txClassVolumeByDevice, x, y, GRAPH_MARGIN],
|
||||
)
|
||||
|
||||
const drawCashOut = useCallback(
|
||||
|
|
@ -252,7 +254,7 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
d =>
|
||||
y(d[1].cashIn + d[1].cashOut) -
|
||||
GRAPH_MARGIN.top +
|
||||
GRAPH_MARGIN.bottom
|
||||
GRAPH_MARGIN.bottom,
|
||||
)
|
||||
.attr('height', d => {
|
||||
return R.clamp(
|
||||
|
|
@ -261,13 +263,13 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
GRAPH_HEIGHT -
|
||||
y(d[1].cashOut) -
|
||||
GRAPH_MARGIN.bottom -
|
||||
BAR_MARGIN / 2
|
||||
BAR_MARGIN / 2,
|
||||
)
|
||||
})
|
||||
.attr('width', x.bandwidth())
|
||||
.attr('rx', 2.5)
|
||||
},
|
||||
[txClassVolumeByDevice, x, y, GRAPH_MARGIN]
|
||||
[txClassVolumeByDevice, x, y, GRAPH_MARGIN],
|
||||
)
|
||||
|
||||
const drawChart = useCallback(() => {
|
||||
|
|
@ -295,7 +297,7 @@ const Graph = ({ data, machines, currency, selectedMachine }) => {
|
|||
formatTicksText,
|
||||
buildGrid,
|
||||
drawCashIn,
|
||||
drawCashOut
|
||||
drawCashOut,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -310,5 +312,5 @@ export default memo(
|
|||
Graph,
|
||||
(prev, next) =>
|
||||
R.equals(prev.period, next.period) &&
|
||||
R.equals(prev.selectedMachine, next.selectedMachine)
|
||||
R.equals(prev.selectedMachine, next.selectedMachine),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
.welcomeBackground {
|
||||
background: var(--ghost) url(/wizard-background.svg) no-repeat fixed center center;
|
||||
background: var(--ghost) url(/wizard-background.svg) no-repeat fixed center
|
||||
center;
|
||||
background-size: cover;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
|
|
@ -22,7 +23,7 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 30px
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
|
@ -70,4 +71,3 @@
|
|||
.confirm2FAInput {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ const Input2FAState = ({ state, dispatch }) => {
|
|||
onCompleted: ({ userData }) => {
|
||||
setUserData(userData)
|
||||
history.push('/')
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const [input2FA, { error: mutationError }] = useMutation(INPUT_2FA, {
|
||||
|
|
@ -55,15 +55,15 @@ const Input2FAState = ({ state, dispatch }) => {
|
|||
return getUserData()
|
||||
}
|
||||
return setInvalidToken(true)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const handle2FAChange = value => {
|
||||
dispatch({
|
||||
type: STATES.INPUT_2FA,
|
||||
payload: {
|
||||
twoFAField: value
|
||||
}
|
||||
twoFAField: value,
|
||||
},
|
||||
})
|
||||
setInvalidToken(false)
|
||||
}
|
||||
|
|
@ -79,8 +79,8 @@ const Input2FAState = ({ state, dispatch }) => {
|
|||
username: state.clientField,
|
||||
password: state.passwordField,
|
||||
code: state.twoFAField,
|
||||
rememberMe: state.rememberMeField
|
||||
}
|
||||
rememberMe: state.rememberMeField,
|
||||
},
|
||||
}
|
||||
|
||||
input2FA(options)
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ const validationSchema = Yup.object().shape({
|
|||
localClient: Yup.string()
|
||||
.required('Client field is required!')
|
||||
.email('Username field should be in an email format!'),
|
||||
localRememberMe: Yup.boolean()
|
||||
localRememberMe: Yup.boolean(),
|
||||
})
|
||||
|
||||
const initialValues = {
|
||||
localClient: '',
|
||||
localRememberMe: false
|
||||
localRememberMe: false,
|
||||
}
|
||||
|
||||
const InputFIDOState = ({ state, strategy }) => {
|
||||
|
|
@ -74,8 +74,8 @@ const InputFIDOState = ({ state, strategy }) => {
|
|||
{
|
||||
onCompleted: ({ validateAssertion: success }) => {
|
||||
success ? getUserData() : setInvalidToken(true)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
const [assertionOptions, { error: assertionQueryError }] = useLazyQuery(
|
||||
|
|
@ -86,11 +86,11 @@ const InputFIDOState = ({ state, strategy }) => {
|
|||
? {
|
||||
username: state.clientField,
|
||||
password: state.passwordField,
|
||||
domain: window.location.hostname
|
||||
domain: window.location.hostname,
|
||||
}
|
||||
: {
|
||||
username: localClientField,
|
||||
domain: window.location.hostname
|
||||
domain: window.location.hostname,
|
||||
},
|
||||
onCompleted: ({ generateAssertionOptions: options }) => {
|
||||
startAssertion(options)
|
||||
|
|
@ -102,31 +102,31 @@ const InputFIDOState = ({ state, strategy }) => {
|
|||
password: state.passwordField,
|
||||
rememberMe: state.rememberMeField,
|
||||
assertionResponse: res,
|
||||
domain: window.location.hostname
|
||||
domain: window.location.hostname,
|
||||
}
|
||||
: {
|
||||
username: localClientField,
|
||||
rememberMe: localRememberMeField,
|
||||
assertionResponse: res,
|
||||
domain: window.location.hostname
|
||||
domain: window.location.hostname,
|
||||
}
|
||||
validateAssertion({
|
||||
variables
|
||||
variables,
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
setInvalidToken(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
const [getUserData, { error: queryError }] = useLazyQuery(GET_USER_DATA, {
|
||||
onCompleted: ({ userData }) => {
|
||||
setUserData(userData)
|
||||
history.push('/')
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const getErrorMsg = (formikErrors, formikTouched) => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import Paper from '@mui/material/Paper'
|
||||
import React, { useReducer } from 'react'
|
||||
import { H5 } from 'src/components/typography'
|
||||
import Logo from 'src/styling/icons/menu/logo.svg?react'
|
||||
|
||||
import Input2FAState from './Input2FAState'
|
||||
|
|
@ -18,7 +17,7 @@ const initialState = {
|
|||
clientField: '',
|
||||
passwordField: '',
|
||||
rememberMeField: false,
|
||||
loginState: STATES.LOGIN
|
||||
loginState: STATES.LOGIN,
|
||||
}
|
||||
|
||||
const reducer = (state, action) => {
|
||||
|
|
|
|||
|
|
@ -44,13 +44,13 @@ const GET_USER_DATA = gql`
|
|||
const validationSchema = Yup.object().shape({
|
||||
email: Yup.string().label('Email').required().email(),
|
||||
password: Yup.string().required('Password field is required'),
|
||||
rememberMe: Yup.boolean()
|
||||
rememberMe: Yup.boolean(),
|
||||
})
|
||||
|
||||
const initialValues = {
|
||||
email: '',
|
||||
password: '',
|
||||
rememberMe: false
|
||||
rememberMe: false,
|
||||
}
|
||||
|
||||
const getErrorMsg = (formikErrors, formikTouched, mutationError) => {
|
||||
|
|
@ -62,7 +62,7 @@ const getErrorMsg = (formikErrors, formikTouched, mutationError) => {
|
|||
return null
|
||||
}
|
||||
|
||||
const LoginState = ({ state, dispatch, strategy }) => {
|
||||
const LoginState = ({ dispatch, strategy }) => {
|
||||
const history = useHistory()
|
||||
const { setUserData } = useContext(AppContext)
|
||||
|
||||
|
|
@ -72,8 +72,8 @@ const LoginState = ({ state, dispatch, strategy }) => {
|
|||
const options = {
|
||||
variables: {
|
||||
username,
|
||||
password
|
||||
}
|
||||
password,
|
||||
},
|
||||
}
|
||||
const { data: loginResponse } = await login(options)
|
||||
|
||||
|
|
@ -84,16 +84,16 @@ const LoginState = ({ state, dispatch, strategy }) => {
|
|||
payload: {
|
||||
clientField: username,
|
||||
passwordField: password,
|
||||
rememberMeField: rememberMe
|
||||
}
|
||||
rememberMeField: rememberMe,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const [validateAssertion, { error: FIDOMutationError }] = useMutation(
|
||||
VALIDATE_ASSERTION,
|
||||
{
|
||||
onCompleted: ({ validateAssertion: success }) => success && getUserData()
|
||||
}
|
||||
onCompleted: ({ validateAssertion: success }) => success && getUserData(),
|
||||
},
|
||||
)
|
||||
|
||||
const [assertionOptions, { error: assertionQueryError }] = useLazyQuery(
|
||||
|
|
@ -105,15 +105,15 @@ const LoginState = ({ state, dispatch, strategy }) => {
|
|||
validateAssertion({
|
||||
variables: {
|
||||
assertionResponse: res,
|
||||
domain: window.location.hostname
|
||||
}
|
||||
domain: window.location.hostname,
|
||||
},
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
const [getUserData, { error: userDataQueryError }] = useLazyQuery(
|
||||
|
|
@ -122,8 +122,8 @@ const LoginState = ({ state, dispatch, strategy }) => {
|
|||
onCompleted: ({ userData }) => {
|
||||
setUserData(userData)
|
||||
history.push('/')
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
@ -149,7 +149,7 @@ const LoginState = ({ state, dispatch, strategy }) => {
|
|||
loginMutationError ||
|
||||
FIDOMutationError ||
|
||||
assertionQueryError ||
|
||||
userDataQueryError
|
||||
userDataQueryError,
|
||||
)}
|
||||
/>
|
||||
<Field
|
||||
|
|
@ -164,7 +164,7 @@ const LoginState = ({ state, dispatch, strategy }) => {
|
|||
loginMutationError ||
|
||||
FIDOMutationError ||
|
||||
assertionQueryError ||
|
||||
userDataQueryError
|
||||
userDataQueryError,
|
||||
)}
|
||||
/>
|
||||
<div className="mt-9 flex">
|
||||
|
|
@ -183,7 +183,7 @@ const LoginState = ({ state, dispatch, strategy }) => {
|
|||
loginMutationError ||
|
||||
FIDOMutationError ||
|
||||
assertionQueryError ||
|
||||
userDataQueryError
|
||||
userDataQueryError,
|
||||
) && (
|
||||
<P className="text-tomato">
|
||||
{getErrorMsg(
|
||||
|
|
@ -192,7 +192,7 @@ const LoginState = ({ state, dispatch, strategy }) => {
|
|||
loginMutationError ||
|
||||
FIDOMutationError ||
|
||||
assertionQueryError ||
|
||||
userDataQueryError
|
||||
userDataQueryError,
|
||||
)}
|
||||
</P>
|
||||
)}
|
||||
|
|
@ -202,11 +202,11 @@ const LoginState = ({ state, dispatch, strategy }) => {
|
|||
onClick={() => {
|
||||
return strategy === 'FIDOUsernameless'
|
||||
? assertionOptions({
|
||||
variables: { domain: window.location.hostname }
|
||||
variables: { domain: window.location.hostname },
|
||||
})
|
||||
: dispatch({
|
||||
type: 'FIDO',
|
||||
payload: {}
|
||||
payload: {},
|
||||
})
|
||||
}}
|
||||
buttonClassName="w-full"
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue