feat: created the search component

style: added spec styles

fix: fixed font color on search input

style: added box-shadow to the search component

feat: added local search functionality to the search component

feat: integrated search component into the transactions page

feat: allow multiple filter selection on the search component

fix: let the user select only one filter for each type

feat: added chips for the selected filters on the transactions page

feat: added the remove function on the filter chips

style: styled items according to spec

refactor: simplified search component (moved logic to the outside)

feat: added transaction filters to the gql query

feat: added a 'clear all filters' button

feat: added a filters query

feat: added a gql query for the transaction filters

fix: fixed the transactions gql query so it haves the same options as
the transaction filters

feat: added a 'loading' feature to the search box (shown while loading
the filters)

fix: fetch transactions and filters separately in the transactions page

fix: style export

fix: packages

fix: transaction conflicts
This commit is contained in:
Liordino Neto 2020-11-05 17:33:58 -03:00 committed by Josh Harvey
parent 468f2cb28b
commit 852bf7b089
14 changed files with 1343 additions and 888 deletions

View file

@ -16,7 +16,16 @@ const cashInLow = require('./cash-in-low')
const PENDING_INTERVAL = '60 minutes' const PENDING_INTERVAL = '60 minutes'
const MAX_PENDING = 10 const MAX_PENDING = 10
module.exports = { post, monitorPending, cancel, PENDING_INTERVAL } const TRANSACTION_STATES = `
case
when operator_completed then 'Cancelled'
when error is not null then 'Error'
when send_confirmed then 'Sent'
when ((not send_confirmed) and (created <= now() - interval '${PENDING_INTERVAL}')) then 'Expired'
else 'Pending'
end`
module.exports = {post, monitorPending, cancel, PENDING_INTERVAL, TRANSACTION_STATES}
function post (machineTx, pi) { function post (machineTx, pi) {
return cashInAtomic.atomic(machineTx, pi) return cashInAtomic.atomic(machineTx, pi)

View file

@ -6,7 +6,15 @@ const BN = require('../bn')
const REDEEMABLE_AGE = T.day const REDEEMABLE_AGE = T.day
module.exports = { redeemableTxs, toObj, toDb, REDEEMABLE_AGE } const CASH_OUT_TRANSACTION_STATES = `
case
when error is not null then 'Error'
when dispense then 'Success'
when (extract(epoch from (now() - greatest(created, confirmed_at))) * 1000) >= ${REDEEMABLE_AGE} then 'Expired'
else 'Pending'
end`
module.exports = { redeemableTxs, toObj, toDb, REDEEMABLE_AGE, CASH_OUT_TRANSACTION_STATES }
const mapValuesWithKey = _.mapValues.convert({cap: false}) const mapValuesWithKey = _.mapValues.convert({cap: false})

30
lib/new-admin/filters.js Normal file
View file

@ -0,0 +1,30 @@
const db = require('../db')
const cashInTx = require('../cash-in/cash-in-tx')
const { CASH_OUT_TRANSACTION_STATES } = require('../cash-out/cash-out-helper')
function transaction() {
const sql = `select distinct * from (
select 'type' as type, 'Cash In' as value union
select 'type' as type, 'Cash Out' as value union
select 'machine' as type, name as value from devices d inner join cash_in_txs t on d.device_id = t.device_id union
select 'machine' as type, name as value from devices d inner join cash_out_txs t on d.device_id = t.device_id union
select 'customer' as type, concat(id_card_data::json->>'firstName', ' ', id_card_data::json->>'lastName') as value
from customers c inner join cash_in_txs t on c.id = t.customer_id
where c.id_card_data::json->>'firstName' is not null or c.id_card_data::json->>'lastName' is not null union
select 'customer' as type, concat(id_card_data::json->>'firstName', ' ', id_card_data::json->>'lastName') as value
from customers c inner join cash_out_txs t on c.id = t.customer_id
where c.id_card_data::json->>'firstName' is not null or c.id_card_data::json->>'lastName' is not null union
select 'fiat' as type, fiat_code as value from cash_in_txs union
select 'fiat' as type, fiat_code as value from cash_out_txs union
select 'crypto' as type, crypto_code as value from cash_in_txs union
select 'crypto' as type, crypto_code as value from cash_out_txs union
select 'address' as type, to_address as value from cash_in_txs union
select 'address' as type, to_address as value from cash_in_txs union
select 'status' as type, ${cashInTx.TRANSACTION_STATES} as value from cash_in_txs union
select 'status' as type, ${CASH_OUT_TRANSACTION_STATES} as value from cash_out_txs
) f`
return db.any(sql)
}
module.exports = { transaction }

View file

@ -1,6 +1,7 @@
const DataLoader = require('dataloader') const DataLoader = require('dataloader')
const { parseAsync } = require('json2csv') const { parseAsync } = require('json2csv')
const filters = require('../../filters')
const transactions = require('../../services/transactions') const transactions = require('../../services/transactions')
const anonymous = require('../../../constants').anonymousCustomer const anonymous = require('../../../constants').anonymousCustomer
@ -25,14 +26,16 @@ const resolvers = {
isAnonymous: parent => (parent.customerId === anonymous.uuid) isAnonymous: parent => (parent.customerId === anonymous.uuid)
}, },
Query: { Query: {
transactions: (...[, { from, until, limit, offset, deviceId }]) => transactions: (...[, { from, until, limit, offset, deviceId, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status }]) =>
transactions.batch(from, until, limit, offset, deviceId), transactions.batch(from, until, limit, offset, deviceId, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status),
transactionsCsv: (...[, { from, until, limit, offset }]) => transactionsCsv: (...[, { from, until, limit, offset, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status }]) =>
transactions.batch(from, until, limit, offset).then(data => parseAsync(data, { fields: txLogFields })), transactions.batch(from, until, limit, offset, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status)
.then(data => parseAsync(data, {fields: tx_logFields})),
transactionCsv: (...[, { id, txClass }]) => transactionCsv: (...[, { id, txClass }]) =>
transactions.getTx(id, txClass).then(parseAsync), transactions.getTx(id, txClass).then(parseAsync),
txAssociatedDataCsv: (...[, { id, txClass }]) => txAssociatedDataCsv: (...[, { id, txClass }]) =>
transactions.getTxAssociatedData(id, txClass).then(parseAsync) transactions.getTxAssociatedData(id, txClass).then(parseAsync),
transactionFilters: () => filters.transaction()
} }
} }

View file

@ -45,11 +45,17 @@ const typeDef = gql`
discount: Int discount: Int
} }
type Filter {
type: String
value: String
}
type Query { type Query {
transactions(from: Date, until: Date, limit: Int, offset: Int, deviceId: ID): [Transaction] @auth transactions(from: Date, until: Date, limit: Int, offset: Int, deviceId: ID, txClass: String, machineName: String, customerName: String, fiatCode: String, cryptoCode: String, toAddress: String, status: String): [Transaction] @auth
transactionsCsv(from: Date, until: Date, limit: Int, offset: Int): String @auth transactionsCsv(from: Date, until: Date, limit: Int, offset: Int, txClass: String, machineName: String, customerName: String, fiatCode: String, cryptoCode: String, toAddress: String, status: String): String @auth
transactionCsv(id: ID, txClass: String): String @auth transactionCsv(id: ID, txClass: String): String @auth
txAssociatedDataCsv(id: ID, txClass: String): String @auth txAssociatedDataCsv(id: ID, txClass: String): String @auth
transactionFilters: [Filter] @auth
} }
` `

View file

@ -5,7 +5,7 @@ const db = require('../../db')
const machineLoader = require('../../machine-loader') const machineLoader = require('../../machine-loader')
const tx = require('../../tx') const tx = require('../../tx')
const cashInTx = require('../../cash-in/cash-in-tx') const cashInTx = require('../../cash-in/cash-in-tx')
const { REDEEMABLE_AGE } = require('../../cash-out/cash-out-helper') const { REDEEMABLE_AGE, CASH_OUT_TRANSACTION_STATES } = require('../../cash-out/cash-out-helper')
const NUM_RESULTS = 1000 const NUM_RESULTS = 1000
@ -24,7 +24,20 @@ function addNames (txs) {
const camelize = _.mapKeys(_.camelCase) const camelize = _.mapKeys(_.camelCase)
function batch (from = new Date(0).toISOString(), until = new Date().toISOString(), limit = null, offset = 0, id = null) { function batch (
from = new Date(0).toISOString(),
until = new Date().toISOString(),
limit = null,
offset = 0,
id = null,
txClass = null,
machineName = null,
customerName = null,
fiatCode = null,
cryptoCode = null,
toAddress = null,
status = null
) {
const packager = _.flow(_.flatten, _.orderBy(_.property('created'), ['desc']), _.map(camelize), addNames) const packager = _.flow(_.flatten, _.orderBy(_.property('created'), ['desc']), _.map(camelize), addNames)
const cashInSql = `select 'cashIn' as tx_class, txs.*, const cashInSql = `select 'cashIn' as tx_class, txs.*,
@ -32,15 +45,23 @@ function batch (from = new Date(0).toISOString(), until = new Date().toISOString
c.id_card_data_number as customer_id_card_data_number, c.id_card_data_number as customer_id_card_data_number,
c.id_card_data_expiration as customer_id_card_data_expiration, c.id_card_data_expiration as customer_id_card_data_expiration,
c.id_card_data as customer_id_card_data, c.id_card_data as customer_id_card_data,
c.name as customer_name, concat(c.id_card_data::json->>'firstName', ' ', c.id_card_data::json->>'lastName') as customer_name,
c.front_camera_path as customer_front_camera_path, c.front_camera_path as customer_front_camera_path,
c.id_card_photo_path as customer_id_card_photo_path, c.id_card_photo_path as customer_id_card_photo_path,
((not txs.send_confirmed) and (txs.created <= now() - interval $1)) as expired ((not txs.send_confirmed) and (txs.created <= now() - interval $1)) as expired
from cash_in_txs as txs from (select *, ${cashInTx.TRANSACTION_STATES} as txStatus from cash_in_txs) as txs
left outer join customers c on txs.customer_id = c.id left outer join customers c on txs.customer_id = c.id
inner join devices d on txs.device_id = d.device_id
where txs.created >= $2 and txs.created <= $3 ${ where txs.created >= $2 and txs.created <= $3 ${
id !== null ? `and txs.device_id = $6` : `` id !== null ? `and txs.device_id = $6` : ``
} }
and ($7 is null or $7 = 'Cash In')
and ($8 is null or d.name = $8)
and ($9 is null or concat(c.id_card_data::json->>'firstName', ' ', c.id_card_data::json->>'lastName') = $9)
and ($10 is null or txs.fiat_code = $10)
and ($11 is null or txs.crypto_code = $11)
and ($12 is null or txs.to_address = $12)
and ($13 is null or txs.txStatus = $13)
order by created desc limit $4 offset $5` order by created desc limit $4 offset $5`
const cashOutSql = `select 'cashOut' as tx_class, const cashOutSql = `select 'cashOut' as tx_class,
@ -50,22 +71,30 @@ function batch (from = new Date(0).toISOString(), until = new Date().toISOString
c.id_card_data_number as customer_id_card_data_number, c.id_card_data_number as customer_id_card_data_number,
c.id_card_data_expiration as customer_id_card_data_expiration, c.id_card_data_expiration as customer_id_card_data_expiration,
c.id_card_data as customer_id_card_data, c.id_card_data as customer_id_card_data,
c.name as customer_name, concat(c.id_card_data::json->>'firstName', ' ', c.id_card_data::json->>'lastName') as customer_name,
c.front_camera_path as customer_front_camera_path, c.front_camera_path as customer_front_camera_path,
c.id_card_photo_path as customer_id_card_photo_path, c.id_card_photo_path as customer_id_card_photo_path,
(extract(epoch from (now() - greatest(txs.created, txs.confirmed_at))) * 1000) >= $1 as expired (extract(epoch from (now() - greatest(txs.created, txs.confirmed_at))) * 1000) >= $1 as expired
from cash_out_txs txs from (select *, ${CASH_OUT_TRANSACTION_STATES} as txStatus from cash_out_txs) txs
inner join cash_out_actions actions on txs.id = actions.tx_id inner join cash_out_actions actions on txs.id = actions.tx_id
and actions.action = 'provisionAddress' and actions.action = 'provisionAddress'
left outer join customers c on txs.customer_id = c.id left outer join customers c on txs.customer_id = c.id
inner join devices d on txs.device_id = d.device_id
where txs.created >= $2 and txs.created <= $3 ${ where txs.created >= $2 and txs.created <= $3 ${
id !== null ? `and txs.device_id = $6` : `` id !== null ? `and txs.device_id = $6` : ``
} }
and ($7 is null or $7 = 'Cash Out')
and ($8 is null or d.name = $8)
and ($9 is null or concat(c.id_card_data::json->>'firstName', ' ', c.id_card_data::json->>'lastName') = $9)
and ($10 is null or txs.fiat_code = $10)
and ($11 is null or txs.crypto_code = $11)
and ($12 is null or txs.to_address = $12)
and ($13 is null or txs.txStatus = $13)
order by created desc limit $4 offset $5` order by created desc limit $4 offset $5`
return Promise.all([ return Promise.all([
db.any(cashInSql, [cashInTx.PENDING_INTERVAL, from, until, limit, offset, id]), db.any(cashInSql, [cashInTx.PENDING_INTERVAL, from, until, limit, offset, id, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status]),
db.any(cashOutSql, [REDEEMABLE_AGE, from, until, limit, offset, id]) db.any(cashOutSql, [REDEEMABLE_AGE, from, until, limit, offset, id, txClass, machineName, customerName, fiatCode, cryptoCode, toAddress, status])
]) ])
.then(packager) .then(packager)
} }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,91 @@
import InputBase from '@material-ui/core/InputBase'
import Paper from '@material-ui/core/Paper'
import { makeStyles } from '@material-ui/core/styles'
import MAutocomplete from '@material-ui/lab/Autocomplete'
import classnames from 'classnames'
import React, { memo, useState, useEffect } from 'react'
import { P } from 'src/components/typography'
import { ReactComponent as SearchIcon } from 'src/styling/icons/circle buttons/search/zodiac.svg'
import styles from './SearchBox.styles'
const useStyles = makeStyles(styles)
const SearchBox = memo(
({
loading = false,
filters = [],
options = [],
inputPlaceholder = '',
size,
onChange,
...props
}) => {
const classes = useStyles({ size })
const [popupOpen, setPopupOpen] = useState(false)
const inputClasses = {
[classes.input]: true,
[classes.inputWithPopup]: popupOpen
}
const innerOnChange = filters => onChange(filters)
// eslint-disable-next-line
useEffect(() => innerOnChange(filters), [filters])
return (
<MAutocomplete
loading={loading}
classes={{ option: classes.autocomplete }}
value={filters}
options={options}
getOptionLabel={it => it.value}
renderOption={it => (
<div className={classes.item}>
<P className={classes.itemLabel}>{it.value}</P>
<P className={classes.itemType}>{it.type}</P>
</div>
)}
autoHighlight
disableClearable
clearOnEscape
multiple
filterSelectedOptions
getOptionSelected={(option, value) => option.type === value.type}
PaperComponent={({ children }) => (
<Paper elevation={0} className={classes.popup}>
<div className={classes.separator} />
{children}
</Paper>
)}
renderInput={params => {
return (
<InputBase
ref={params.InputProps.ref}
{...params}
className={classnames(inputClasses)}
startAdornment={<SearchIcon className={classes.iconButton} />}
placeholder={inputPlaceholder}
inputProps={{
className: classes.bold,
classes: {
root: classes.size
},
...params.inputProps
}}
/>
)
}}
onOpen={() => setPopupOpen(true)}
onClose={() => setPopupOpen(false)}
onChange={(_, filters) => innerOnChange(filters)}
{...props}
/>
)
}
)
export default SearchBox

View file

@ -0,0 +1,78 @@
import baseButtonStyles from 'src/components/buttons/BaseButton.styles'
import { bySize, bold } from 'src/styling/helpers'
import { zircon, comet, primaryColor } from 'src/styling/variables'
const { baseButton } = baseButtonStyles
const searchBoxBorderRadius = baseButton.height / 2
const searchBoxHeight = 32
const popupBorderRadiusFocus = baseButton.height / 4
const hoverColor = 'rgba(0, 0, 0, 0.08)'
const boxShadow = `0 4px 4px 0 ${hoverColor}`
const styles = {
size: ({ size }) => ({
marginTop: size === 'lg' ? 0 : 2,
...bySize(size)
}),
bold,
autocomplete: {
'&[data-focus="true"]': {
backgroundColor: hoverColor
}
},
popup: {
display: 'flex',
flexDirection: 'column',
borderRadius: [[0, 0, popupBorderRadiusFocus, popupBorderRadiusFocus]],
backgroundColor: zircon,
boxShadow
},
separator: {
width: '88%',
height: 1,
margin: '0 auto',
border: 'solid 0.5px',
borderColor: comet
},
item: {
display: 'flex',
flexDirection: 'row',
width: '100%',
height: 36,
alignItems: 'center'
},
itemLabel: {
margin: [0],
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis'
},
itemType: {
marginLeft: 'auto',
fontSize: 12,
color: comet,
margin: [0]
},
input: {
display: 'flex',
flex: 1,
width: 273,
padding: [[8, 12]],
alignItems: 'center',
height: searchBoxHeight,
borderRadius: searchBoxBorderRadius,
backgroundColor: zircon,
color: primaryColor
},
inputWithPopup: {
borderRadius: [[popupBorderRadiusFocus, popupBorderRadiusFocus, 0, 0]],
boxShadow
},
iconButton: {
marginRight: 12
}
}
export default styles

View file

@ -4,22 +4,27 @@ import BigNumber from 'bignumber.js'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import { utils as coinUtils } from 'lamassu-coins' import { utils as coinUtils } from 'lamassu-coins'
import * as R from 'ramda' import * as R from 'ramda'
import React from 'react' import React, { useState } from 'react'
import { useHistory } from 'react-router-dom' import { useHistory } from 'react-router-dom'
import Chip from 'src/components/Chip'
import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper' import LogsDowloaderPopover from 'src/components/LogsDownloaderPopper'
import SearchBox from 'src/components/SearchBox'
import Title from 'src/components/Title' import Title from 'src/components/Title'
import DataTable from 'src/components/tables/DataTable' import DataTable from 'src/components/tables/DataTable'
import { P } from 'src/components/typography'
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg' import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
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 { formatDate } from 'src/utils/timezones' import { formatDate } from 'src/utils/timezones'
import DetailsRow from './DetailsCard' import DetailsRow from './DetailsCard'
import { mainStyles } from './Transactions.styles' import { mainStyles, chipStyles } from './Transactions.styles'
import { getStatus } from './helper' import { getStatus /*, getStatusProperties */ } from './helper'
const useStyles = makeStyles(mainStyles) const useStyles = makeStyles(mainStyles)
const useChipStyles = makeStyles(chipStyles)
const NUM_LOG_RESULTS = 1000 const NUM_LOG_RESULTS = 1000
@ -35,9 +40,40 @@ const GET_TRANSACTIONS_CSV = gql`
} }
` `
const GET_TRANSACTION_FILTERS = gql`
query filters {
transactionFilters {
type
value
}
}
`
const GET_TRANSACTIONS = gql` const GET_TRANSACTIONS = gql`
query transactions($limit: Int, $from: DateTime, $until: DateTime) { query transactions(
transactions(limit: $limit, from: $from, until: $until) { $limit: Int
$from: DateTime
$until: DateTime
$txClass: String
$machineName: String
$customerName: String
$fiatCode: String
$cryptoCode: String
$toAddress: String
$status: String
) {
transactions(
limit: $limit
from: $from
until: $until
txClass: $txClass
machineName: $machineName
customerName: $customerName
fiatCode: $fiatCode
cryptoCode: $cryptoCode
toAddress: $toAddress
status: $status
) {
id id
txClass txClass
txHash txHash
@ -72,12 +108,23 @@ const GET_TRANSACTIONS = gql`
const Transactions = () => { const Transactions = () => {
const classes = useStyles() const classes = useStyles()
const history = useHistory() const history = useHistory()
const { data: txResponse, loading } = useQuery(GET_TRANSACTIONS, { const chipClasses = useChipStyles()
variables: {
limit: NUM_LOG_RESULTS const [filters, setFilters] = useState([])
}, const { data: filtersResponse, loading: loadingFilters } = useQuery(
pollInterval: 10000 GET_TRANSACTION_FILTERS
}) )
const [filteredTransactions, setFilteredTransactions] = useState([])
const [variables, setVariables] = useState({ limit: NUM_LOG_RESULTS })
const { data: txResponse, loading: loadingTransactions, refetch } = useQuery(
GET_TRANSACTIONS,
{
variables,
onCompleted: data =>
setFilteredTransactions(R.path(['transactions'])(data)),
pollInterval: 10000
}
)
const { data: configResponse, configLoading } = useQuery(GET_DATA) const { data: configResponse, configLoading } = useQuery(GET_DATA)
const timezone = R.path(['config', 'locale_timezone'], configResponse) const timezone = R.path(['config', 'locale_timezone'], configResponse)
@ -167,11 +214,51 @@ const Transactions = () => {
} }
] ]
const onFilterChange = filters => {
const filtersObject = R.compose(
R.mergeAll,
R.map(f => ({
[f.type]: f.value
}))
)(filters)
setFilters(filters)
setVariables({
limit: NUM_LOG_RESULTS,
txClass: filtersObject.type,
machineName: filtersObject.machine,
customerName: filtersObject.customer,
fiatCode: filtersObject.fiat,
cryptoCode: filtersObject.crypto,
toAddress: filtersObject.address,
status: filtersObject.status
})
refetch && refetch()
}
const onFilterDelete = filter =>
setFilters(
R.filter(f => !R.whereEq(R.pick(['type', 'value'], f), filter))(filters)
)
const filterOptions = R.path(['transactionFilters'])(filtersResponse)
return ( return (
<> <>
<div className={classes.titleWrapper}> <div className={classes.titleWrapper}>
<div className={classes.titleAndButtonsContainer}> <div className={classes.titleAndButtonsContainer}>
<Title>Transactions</Title> <Title>Transactions</Title>
<div className={classes.buttonsWrapper}>
<SearchBox
loading={loadingFilters}
filters={filters}
options={filterOptions}
inputPlaceholder={'Search Transactions'}
onChange={onFilterChange}
/>
</div>
{txResponse && ( {txResponse && (
<div className={classes.buttonsWrapper}> <div className={classes.buttonsWrapper}>
<LogsDowloaderPopover <LogsDowloaderPopover
@ -194,11 +281,33 @@ const Transactions = () => {
</div> </div>
</div> </div>
</div> </div>
{filters.length > 0 && (
<>
<P className={classes.text}>{'Filters:'}</P>
<div>
{filters.map((f, idx) => (
<Chip
key={idx}
classes={chipClasses}
label={`${f.type}: ${f.value}`}
onDelete={() => onFilterDelete(f)}
deleteIcon={<CloseIcon className={classes.button} />}
/>
))}
<Chip
classes={chipClasses}
label={`Delete filters`}
onDelete={() => setFilters([])}
deleteIcon={<CloseIcon className={classes.button} />}
/>
</div>
</>
)}
<DataTable <DataTable
loading={loading && configLoading} loading={loadingTransactions && configLoading}
emptyText="No transactions so far" emptyText="No transactions so far"
elements={elements} elements={elements}
data={R.path(['transactions'])(txResponse)} data={filteredTransactions}
Details={DetailsRow} Details={DetailsRow}
expandable expandable
rowSize="sm" rowSize="sm"

View file

@ -1,6 +1,15 @@
import typographyStyles from 'src/components/typography/styles' import typographyStyles from 'src/components/typography/styles'
import baseStyles from 'src/pages/Logs.styles' import baseStyles from 'src/pages/Logs.styles'
import { offColor, white } from 'src/styling/variables' import {
offColor,
white,
primaryColor,
zircon,
smallestFontSize,
inputFontFamily,
inputFontWeight,
spacer
} from 'src/styling/variables'
const { label1, mono, p } = typographyStyles const { label1, mono, p } = typographyStyles
const { titleWrapper, titleAndButtonsContainer, buttonsWrapper } = baseStyles const { titleWrapper, titleAndButtonsContainer, buttonsWrapper } = baseStyles
@ -64,6 +73,10 @@ const mainStyles = {
titleWrapper, titleWrapper,
titleAndButtonsContainer, titleAndButtonsContainer,
buttonsWrapper, buttonsWrapper,
text: {
marginTop: 0,
marginBottom: 0
},
headerLabels: { headerLabels: {
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'row',
@ -102,7 +115,35 @@ const mainStyles = {
marginLeft: 10, marginLeft: 10,
paddingLeft: 5, paddingLeft: 5,
paddingRight: 5 paddingRight: 5
},
button: {
width: 8,
height: 8,
marginLeft: 8
} }
} }
export { cpcStyles, detailsRowStyles, labelStyles, mainStyles } const chipStyles = {
root: {
borderRadius: spacer / 2,
marginTop: spacer / 2,
marginRight: spacer / 4,
marginBottom: spacer / 2,
marginLeft: spacer / 4,
height: spacer * 3,
backgroundColor: zircon,
'&:hover, &:focus, &:active': {
backgroundColor: zircon
}
},
label: {
fontSize: smallestFontSize,
fontWeight: inputFontWeight,
fontFamily: inputFontFamily,
paddingRight: spacer / 2,
paddingLeft: spacer / 2,
color: primaryColor
}
}
export { cpcStyles, detailsRowStyles, labelStyles, mainStyles, chipStyles }

View file

@ -24,4 +24,12 @@ const getStatusDetails = it => {
return it.hasError ? it.hasError : null return it.hasError ? it.hasError : null
} }
export { getStatus, getStatusDetails } const getStatusProperties = status => ({
hasError: status === 'Error' || null,
dispense: status === 'Success' || null,
expired: status === 'Expired' || null,
operatorCompleted: status === 'Cancelled' || null,
sendConfirmed: status === 'Sent' || null
})
export { getStatus, getStatusProperties, getStatusDetails }

View file

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com --> <title>icon/search/dark02</title>
<desc>Created with Sketch.</desc> <g id="icon/search/dark02" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="icon/sf-small/search/zodiac" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="Group" transform="translate(1.000000, 1.000000)" stroke="#1B2559" stroke-width="2">
<path d="M15.8635238,8.17028571 C15.8635238,12.4198095 12.4187619,15.8645714 8.1692381,15.8645714 C3.92066667,15.8645714 0.475904762,12.4198095 0.475904762,8.17028571 C0.475904762,3.9207619 3.92066667,0.476 8.1692381,0.476 C12.4187619,0.476 15.8635238,3.9207619 15.8635238,8.17028571 Z" id="Stroke-1" stroke="#1B2559" stroke-width="2"></path> <path d="M14.2771714,7.35325714 C14.2771714,11.1778286 11.1768857,14.2781143 7.35231429,14.2781143 C3.5286,14.2781143 0.428314286,11.1778286 0.428314286,7.35325714 C0.428314286,3.52868571 3.5286,0.4284 7.35231429,0.4284 C11.1768857,0.4284 14.2771714,3.52868571 14.2771714,7.35325714 Z" id="Stroke-1"></path>
<line x1="13.7035238" y1="13.7046667" x2="19.4844762" y2="19.485619" id="Stroke-3" stroke="#1B2559" stroke-width="2" stroke-linecap="round"></line> <line x1="12.3331714" y1="12.3342" x2="17.5360286" y2="17.5370571" id="Stroke-3" stroke-linecap="round"></line>
</g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 913 B

After

Width:  |  Height:  |  Size: 888 B

Before After
Before After

1015
package-lock.json generated

File diff suppressed because it is too large Load diff