feat: timezone conversion

This commit is contained in:
Sérgio Salgado 2021-04-29 19:48:14 +01:00 committed by Josh Harvey
parent 72dbeccb4b
commit b2b4fedf42
10 changed files with 101 additions and 36 deletions

View file

@ -192,6 +192,7 @@ const CustomerProfile = memo(() => {
<TransactionsList <TransactionsList
customer={customerData} customer={customerData}
data={sortedTransactions} data={sortedTransactions}
locale={locale}
loading={loading} loading={loading}
/> />
)} )}

View file

@ -1,6 +1,5 @@
import { makeStyles, Box } from '@material-ui/core' import { makeStyles, Box } from '@material-ui/core'
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import moment from 'moment'
import * as R from 'ramda' import * as R from 'ramda'
import React from 'react' import React from 'react'
@ -10,17 +9,20 @@ import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg' import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
import { toUnit } from 'src/utils/coin' import { toUnit } from 'src/utils/coin'
import { ifNotNull } from 'src/utils/nullCheck' import { ifNotNull } from 'src/utils/nullCheck'
import { formatDate } from 'src/utils/timezones'
import CopyToClipboard from '../../Transactions/CopyToClipboard' import CopyToClipboard from '../../Transactions/CopyToClipboard'
import mainStyles from '../CustomersList.styles' import mainStyles from '../CustomersList.styles'
const useStyles = makeStyles(mainStyles) const useStyles = makeStyles(mainStyles)
const TransactionsList = ({ customer, data, loading }) => { const TransactionsList = ({ customer, data, loading, locale }) => {
const classes = useStyles() const classes = useStyles()
const LastTxIcon = customer.lastTxClass === 'cashOut' ? TxOutIcon : TxInIcon const LastTxIcon = customer.lastTxClass === 'cashOut' ? TxOutIcon : TxInIcon
const hasData = !(R.isEmpty(data) || R.isNil(data)) const hasData = !(R.isEmpty(data) || R.isNil(data))
const timezone = locale.timezone
const summaryElements = [ const summaryElements = [
{ {
header: 'Transactions', header: 'Transactions',
@ -41,10 +43,12 @@ const TransactionsList = ({ customer, data, loading }) => {
{ {
header: 'Last active', header: 'Last active',
size: 142, size: 142,
value: ifNotNull( value:
customer.lastActive, !R.isNil(timezone) &&
moment.utc(customer.lastActive).format('YYYY-MM-D') ifNotNull(
) customer.lastActive,
formatDate(customer.lastActive, timezone.dstOffset, 'YYYY-MM-D')
)
}, },
{ {
header: 'Last transaction', header: 'Last transaction',
@ -109,12 +113,12 @@ const TransactionsList = ({ customer, data, loading }) => {
{ {
header: 'Date', header: 'Date',
width: 157, width: 157,
view: it => moment.utc(it.created).format('YYYY-MM-D') view: it => formatDate(it.created, timezone.dstOffset, 'YYYY-MM-D')
}, },
{ {
header: 'Time (h:m:s)', header: 'Time (h:m:s)',
width: 134, width: 134,
view: it => moment.utc(it.created).format('hh:mm:ss') view: it => formatDate(it.created, timezone.dstOffset, 'HH:mm:ss')
} }
] ]

View file

@ -1,7 +1,6 @@
import { useQuery } from '@apollo/react-hooks' import { useQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import moment from 'moment'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useState } from 'react' import React, { useState } from 'react'
@ -17,6 +16,7 @@ import {
TableCell TableCell
} from 'src/components/table' } from 'src/components/table'
import { Info3, H4 } from 'src/components/typography' import { Info3, H4 } from 'src/components/typography'
import { formatDate } from 'src/utils/timezones'
import styles from './Logs.styles' import styles from './Logs.styles'
@ -70,9 +70,11 @@ const GET_MACHINE_LOGS = gql`
} }
` `
const formatDate = date => { const GET_DATA = gql`
return moment(date).format('YYYY-MM-DD HH:mm') query getData {
} config
}
`
const Logs = () => { const Logs = () => {
const classes = useStyles() const classes = useStyles()
@ -84,6 +86,9 @@ const Logs = () => {
const { data: machineResponse } = useQuery(GET_MACHINES) const { data: machineResponse } = useQuery(GET_MACHINES)
const { data: configResponse } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse)
const { data: logsResponse, loading } = useQuery(GET_MACHINE_LOGS, { const { data: logsResponse, loading } = useQuery(GET_MACHINE_LOGS, {
variables: { deviceId, limit: NUM_LOG_RESULTS }, variables: { deviceId, limit: NUM_LOG_RESULTS },
skip: !selected, skip: !selected,
@ -137,7 +142,14 @@ const Logs = () => {
{logsResponse && {logsResponse &&
logsResponse.machineLogs.map((log, idx) => ( logsResponse.machineLogs.map((log, idx) => (
<TableRow key={idx} size="sm"> <TableRow key={idx} size="sm">
<TableCell>{formatDate(log.timestamp)}</TableCell> <TableCell>
{timezone &&
formatDate(
log.timestamp,
timezone.dstOffset,
'YYYY-MM-DD HH:mm'
)}
</TableCell>
<TableCell>{log.logLevel}</TableCell> <TableCell>{log.logLevel}</TableCell>
<TableCell>{log.message}</TableCell> <TableCell>{log.message}</TableCell>
</TableRow> </TableRow>

View file

@ -1,13 +1,14 @@
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import moment from 'moment'
import React from 'react' import React from 'react'
import { Label3, P } from 'src/components/typography' import { Label3, P } from 'src/components/typography'
import { formatDate } from 'src/utils/timezones'
import styles from '../Machines.styles' import styles from '../Machines.styles'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const Details = ({ data }) => { const Details = ({ data, timezone }) => {
console.log(timezone)
const classes = useStyles() const classes = useStyles()
return ( return (
<div className={classes.row}> <div className={classes.row}>
@ -15,7 +16,11 @@ const Details = ({ data }) => {
<Label3 className={classes.label3}>Paired at</Label3> <Label3 className={classes.label3}>Paired at</Label3>
<P> <P>
{data.pairedAt {data.pairedAt
? moment(data.pairedAt).format('YYYY-MM-DD HH:mm:ss') ? formatDate(
data.pairedAt,
timezone.dstOffset,
'YYYY-MM-DD HH:mm:ss'
)
: ''} : ''}
</P> </P>
</div> </div>

View file

@ -53,6 +53,7 @@ const Machines = () => {
const machines = getMachines(data) ?? [] const machines = getMachines(data) ?? []
const machineInfo = getMachineInfo(selectedMachine)(machines) ?? {} const machineInfo = getMachineInfo(selectedMachine)(machines) ?? {}
const timezone = R.path(['config', 'locale_timezone'], data) ?? {}
// pre-selects first machine from the list, if there is a machine configured. // pre-selects first machine from the list, if there is a machine configured.
useEffect(() => { useEffect(() => {
@ -98,7 +99,7 @@ const Machines = () => {
<div <div
className={classnames(classes.detailItem, classes.detailsMargin)}> className={classnames(classes.detailItem, classes.detailsMargin)}>
<TL1 className={classes.subtitle}>{'Details'}</TL1> <TL1 className={classes.subtitle}>{'Details'}</TL1>
<Details data={machineInfo} /> <Details data={machineInfo} timezone={timezone} />
</div> </div>
<div className={classes.detailItem}> <div className={classes.detailItem}>
<TL1 className={classes.subtitle}>{'Cash cassettes'}</TL1> <TL1 className={classes.subtitle}>{'Cash cassettes'}</TL1>

View file

@ -2,7 +2,6 @@ import { useMutation, useLazyQuery } from '@apollo/react-hooks'
import { Grid /*, Divider */ } from '@material-ui/core' import { Grid /*, Divider */ } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import moment from 'moment'
import React, { useState } from 'react' import React, { useState } from 'react'
import { ConfirmDialog } from 'src/components/ConfirmDialog' import { ConfirmDialog } from 'src/components/ConfirmDialog'
@ -18,6 +17,7 @@ import { ReactComponent as ShutdownIcon } from 'src/styling/icons/button/shut do
import { ReactComponent as UnpairReversedIcon } from 'src/styling/icons/button/unpair/white.svg' import { ReactComponent as UnpairReversedIcon } from 'src/styling/icons/button/unpair/white.svg'
import { ReactComponent as UnpairIcon } from 'src/styling/icons/button/unpair/zodiac.svg' import { ReactComponent as UnpairIcon } from 'src/styling/icons/button/unpair/zodiac.svg'
import { modelPrettifier } from 'src/utils/machine' import { modelPrettifier } from 'src/utils/machine'
import { formatDate } from 'src/utils/timezones'
import { labelStyles, machineDetailsStyles } from './MachineDetailsCard.styles' import { labelStyles, machineDetailsStyles } from './MachineDetailsCard.styles'
@ -101,7 +101,7 @@ const getState = machineEventsLazy =>
JSON.parse(machineEventsLazy.machine.latestEvent?.note ?? '{"state": null}') JSON.parse(machineEventsLazy.machine.latestEvent?.note ?? '{"state": null}')
.state .state
const MachineDetailsRow = ({ it: machine, onActionSuccess }) => { const MachineDetailsRow = ({ it: machine, onActionSuccess, timezone }) => {
const [action, setAction] = useState({ command: null }) const [action, setAction] = useState({ command: null })
const [errorMessage, setErrorMessage] = useState(null) const [errorMessage, setErrorMessage] = useState(null)
const classes = useMDStyles() const classes = useMDStyles()
@ -214,7 +214,12 @@ const MachineDetailsRow = ({ it: machine, onActionSuccess }) => {
<Item xs={4}> <Item xs={4}>
<Label>Paired at</Label> <Label>Paired at</Label>
<span> <span>
{moment(machine.pairedAt).format('YYYY-MM-DD HH:mm:ss')} {timezone &&
formatDate(
machine.pairedAt,
timezone.dstOffset,
'YYYY-MM-DD HH:mm:ss'
)}
</span> </span>
</Item> </Item>
<Item xs={6}> <Item xs={6}>

View file

@ -38,6 +38,12 @@ const GET_MACHINES = gql`
} }
` `
const GET_DATA = gql`
query getData {
config
}
`
const useStyles = makeStyles(mainStyles) const useStyles = makeStyles(mainStyles)
const MachineStatus = () => { const MachineStatus = () => {
@ -46,6 +52,8 @@ const MachineStatus = () => {
const { state } = useLocation() const { state } = useLocation()
const addedMachineId = state?.id const addedMachineId = state?.id
const { data: machinesResponse, refetch, loading } = useQuery(GET_MACHINES) const { data: machinesResponse, refetch, loading } = useQuery(GET_MACHINES)
const { data: configResponse, configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse)
const elements = [ const elements = [
{ {
@ -95,7 +103,7 @@ const MachineStatus = () => {
) )
const InnerMachineDetailsRow = ({ it }) => ( const InnerMachineDetailsRow = ({ it }) => (
<MachineDetailsRow it={it} onActionSuccess={refetch} /> <MachineDetailsRow it={it} onActionSuccess={refetch} timezone={timezone} />
) )
return ( return (
@ -116,7 +124,7 @@ const MachineStatus = () => {
</div> </div>
</div> </div>
<DataTable <DataTable
loading={loading} loading={loading && configLoading}
elements={elements} elements={elements}
data={machines} data={machines}
Details={InnerMachineDetailsRow} Details={InnerMachineDetailsRow}

View file

@ -1,7 +1,6 @@
import { useQuery } from '@apollo/react-hooks' import { useQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core' import { makeStyles } from '@material-ui/core'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import moment from 'moment'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useState, useRef } from 'react' import React, { useState, useRef } from 'react'
@ -21,6 +20,7 @@ import { Info3, H4 } from 'src/components/typography'
import typographyStyles from 'src/components/typography/styles' import typographyStyles from 'src/components/typography/styles'
import { offColor } from 'src/styling/variables' import { offColor } from 'src/styling/variables'
import { startCase } from 'src/utils/string' import { startCase } from 'src/utils/string'
import { formatDate } from 'src/utils/timezones'
import logsStyles from './Logs.styles' import logsStyles from './Logs.styles'
@ -54,10 +54,6 @@ const useStyles = makeStyles(styles)
const SHOW_ALL = { code: 'SHOW_ALL', display: 'Show all' } const SHOW_ALL = { code: 'SHOW_ALL', display: 'Show all' }
const formatDate = date => {
return moment(date).format('YYYY-MM-DD HH:mm')
}
const NUM_LOG_RESULTS = 500 const NUM_LOG_RESULTS = 500
const GET_CSV = gql` const GET_CSV = gql`
@ -66,7 +62,7 @@ const GET_CSV = gql`
} }
` `
const GET_DATA = gql` const GET_SERVER_DATA = gql`
query ServerData($limit: Int, $from: DateTime, $until: DateTime) { query ServerData($limit: Int, $from: DateTime, $until: DateTime) {
serverVersion serverVersion
uptime { uptime {
@ -83,6 +79,12 @@ const GET_DATA = gql`
} }
` `
const GET_DATA = gql`
query getData {
config
}
`
const Logs = () => { const Logs = () => {
const classes = useStyles() const classes = useStyles()
@ -91,12 +93,14 @@ const Logs = () => {
const [saveMessage, setSaveMessage] = useState(null) const [saveMessage, setSaveMessage] = useState(null)
const [logLevel, setLogLevel] = useState(SHOW_ALL) const [logLevel, setLogLevel] = useState(SHOW_ALL)
const { data, loading } = useQuery(GET_DATA, { const { data, loading } = useQuery(GET_SERVER_DATA, {
onCompleted: () => setSaveMessage(''), onCompleted: () => setSaveMessage(''),
variables: { variables: {
limit: NUM_LOG_RESULTS limit: NUM_LOG_RESULTS
} }
}) })
const { data: configResponse, configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse)
const defaultLogLevels = [ const defaultLogLevels = [
{ code: 'error', display: 'Error' }, { code: 'error', display: 'Error' },
@ -181,15 +185,22 @@ const Logs = () => {
) )
.map((log, idx) => ( .map((log, idx) => (
<TableRow key={idx} size="sm"> <TableRow key={idx} size="sm">
<TableCell>{formatDate(log.timestamp)}</TableCell> <TableCell>
{timezone &&
formatDate(
log.timestamp,
timezone.dstOffset,
'YYYY-MM-DD HH:mm'
)}
</TableCell>
<TableCell>{log.logLevel}</TableCell> <TableCell>{log.logLevel}</TableCell>
<TableCell>{log.message}</TableCell> <TableCell>{log.message}</TableCell>
</TableRow> </TableRow>
))} ))}
</TableBody> </TableBody>
</Table> </Table>
{loading && <H4>{'Loading...'}</H4>} {loading && configLoading && <H4>{'Loading...'}</H4>}
{!loading && !data?.serverLogs?.length && ( {!loading && !configLoading && !data?.serverLogs?.length && (
<H4>{'No activity so far'}</H4> <H4>{'No activity so far'}</H4>
)} )}
</div> </div>

View file

@ -2,7 +2,6 @@ import { useQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core' import { makeStyles } from '@material-ui/core'
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import moment from 'moment'
import * as R from 'ramda' import * as R from 'ramda'
import React from 'react' import React from 'react'
import { useHistory } from 'react-router-dom' import { useHistory } from 'react-router-dom'
@ -14,6 +13,7 @@ import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg' import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'
import { ReactComponent as CustomerLinkIcon } from 'src/styling/icons/month arrows/right.svg' import { ReactComponent as CustomerLinkIcon } from 'src/styling/icons/month arrows/right.svg'
import { toUnit, formatCryptoAddress } from 'src/utils/coin' import { toUnit, formatCryptoAddress } from 'src/utils/coin'
import { formatDate } from 'src/utils/timezones'
import DetailsRow from './DetailsCard' import DetailsRow from './DetailsCard'
import { mainStyles } from './Transactions.styles' import { mainStyles } from './Transactions.styles'
@ -23,6 +23,12 @@ const useStyles = makeStyles(mainStyles)
const NUM_LOG_RESULTS = 1000 const NUM_LOG_RESULTS = 1000
const GET_DATA = gql`
query getData {
config
}
`
const GET_TRANSACTIONS_CSV = gql` const GET_TRANSACTIONS_CSV = gql`
query transactions($limit: Int, $from: DateTime, $until: DateTime) { query transactions($limit: Int, $from: DateTime, $until: DateTime) {
transactionsCsv(limit: $limit, from: $from, until: $until) transactionsCsv(limit: $limit, from: $from, until: $until)
@ -73,6 +79,9 @@ const Transactions = () => {
pollInterval: 10000 pollInterval: 10000
}) })
const { data: configResponse, configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse)
const redirect = customerId => { const redirect = customerId => {
return history.push(`/compliance/customer/${customerId}`) return history.push(`/compliance/customer/${customerId}`)
} }
@ -143,7 +152,9 @@ const Transactions = () => {
}, },
{ {
header: 'Date (UTC)', header: 'Date (UTC)',
view: it => moment.utc(it.created).format('YYYY-MM-DD HH:mm:ss'), view: it =>
timezone &&
formatDate(it.created, timezone.dstOffset, 'YYYY-MM-DD HH:mm:ss'),
textAlign: 'right', textAlign: 'right',
size: 'sm', size: 'sm',
width: 195 width: 195
@ -185,7 +196,7 @@ const Transactions = () => {
</div> </div>
</div> </div>
<DataTable <DataTable
loading={loading} loading={loading && configLoading}
emptyText="No transactions so far" emptyText="No transactions so far"
elements={elements} elements={elements}
data={R.path(['transactions'])(txResponse)} data={R.path(['transactions'])(txResponse)}

View file

@ -1,3 +1,4 @@
import moment from 'moment'
import * as R from 'ramda' import * as R from 'ramda'
const getPossibleUTCDSTPairs = timezones => const getPossibleUTCDSTPairs = timezones =>
@ -73,4 +74,10 @@ const getTzLabels = timezones =>
getFinalTimezones(timezones) getFinalTimezones(timezones)
) )
export { getTzLabels } const formatDate = (date, offset, format) =>
moment
.utc(date)
.utcOffset(offset)
.format(format)
export { getTzLabels, formatDate }