From 7b59e36cb4e27e5934a8c24aea2c812555e07f78 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Luis=20F=C3=A9lix?=
<5789328+lcsfelix@users.noreply.github.com>
Date: Wed, 15 Jan 2020 22:41:47 +0000
Subject: [PATCH] feat: add operator info page
feat: save/load operator info
feat: add formik switch component
feat: add input validation
fix: correct formik switch behaviour
fix: change infoCardEnabled to a radio input
style: move styles out of js
feat: add error feedback
---
.../src/components/ErrorMessage.js | 41 +++
.../src/components/inputs/base/RadioGroup.js | 6 +-
.../src/components/inputs/base/index.js | 4 +-
.../inputs/formik/PhoneNumberInput.js | 5 +-
.../components/inputs/formik/RadioGroup.js | 42 +++
.../src/pages/OperatorInfo/ContactInfo.js | 304 ++++++++++++++++++
.../src/pages/OperatorInfo/OperatorInfo.js | 58 ++++
.../pages/OperatorInfo/OperatorInfo.styles.js | 54 ++++
new-lamassu-admin/src/routing/routes.js | 5 +-
.../src/styling/icons/action/edit/enabled.svg | 15 +-
10 files changed, 516 insertions(+), 18 deletions(-)
create mode 100644 new-lamassu-admin/src/components/ErrorMessage.js
create mode 100644 new-lamassu-admin/src/components/inputs/formik/RadioGroup.js
create mode 100644 new-lamassu-admin/src/pages/OperatorInfo/ContactInfo.js
create mode 100644 new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.js
create mode 100644 new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js
diff --git a/new-lamassu-admin/src/components/ErrorMessage.js b/new-lamassu-admin/src/components/ErrorMessage.js
new file mode 100644
index 00000000..2d95bbb4
--- /dev/null
+++ b/new-lamassu-admin/src/components/ErrorMessage.js
@@ -0,0 +1,41 @@
+import React from 'react'
+import classnames from 'classnames'
+import { makeStyles } from '@material-ui/core'
+
+import { ReactComponent as ErrorIcon } from 'src/styling/icons/warning-icon/tomato.svg'
+import { errorColor } from 'src/styling/variables'
+
+import { Info3 } from './typography'
+
+const styles = {
+ wrapper: {
+ display: 'flex',
+ alignItems: 'center',
+ '& > svg': {
+ marginRight: 10
+ }
+ },
+ message: {
+ display: 'flex',
+ alignItems: 'center',
+ color: errorColor,
+ margin: 0,
+ whiteSpace: 'break-spaces',
+ width: 250
+ }
+}
+
+const useStyles = makeStyles(styles)
+
+const ErrorMessage = ({ className, children, ...props }) => {
+ const classes = useStyles()
+
+ return (
+
+
+ {children}
+
+ )
+}
+
+export default ErrorMessage
diff --git a/new-lamassu-admin/src/components/inputs/base/RadioGroup.js b/new-lamassu-admin/src/components/inputs/base/RadioGroup.js
index ded7fbe9..a6953dd9 100644
--- a/new-lamassu-admin/src/components/inputs/base/RadioGroup.js
+++ b/new-lamassu-admin/src/components/inputs/base/RadioGroup.js
@@ -48,12 +48,12 @@ const RadioGroup = ({
value={value}
onChange={onChange}
className={classnames(className)}>
- {options.map((options, idx) => (
+ {options.map((option, idx) => (
}
- label={options.label}
+ label={option.label}
/>
))}
diff --git a/new-lamassu-admin/src/components/inputs/base/index.js b/new-lamassu-admin/src/components/inputs/base/index.js
index 66e1e1e2..f52fc5e8 100644
--- a/new-lamassu-admin/src/components/inputs/base/index.js
+++ b/new-lamassu-admin/src/components/inputs/base/index.js
@@ -1,4 +1,6 @@
import Checkbox from './Checkbox'
import TextInput from './TextInput'
+import Switch from './Switch'
+import RadioGroup from './RadioGroup'
-export { Checkbox, TextInput }
+export { Checkbox, TextInput, Switch, RadioGroup }
diff --git a/new-lamassu-admin/src/components/inputs/formik/PhoneNumberInput.js b/new-lamassu-admin/src/components/inputs/formik/PhoneNumberInput.js
index e0744515..bd0e5ce0 100644
--- a/new-lamassu-admin/src/components/inputs/formik/PhoneNumberInput.js
+++ b/new-lamassu-admin/src/components/inputs/formik/PhoneNumberInput.js
@@ -6,8 +6,9 @@ import { styles } from './TextInput.styles'
const useStyles = makeStyles(styles)
-const mask = /(\+9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)(\d{1,3}){0,1}(\d{1,3}){0,1}(\d{1,3}){0,1}(\d{1,3}){0,1}(\d{1,2}){0,1}$/
-const maskValue = value => value.replace(mask, '$1 $2 $3 $4 $5 $6')
+const mask = /(\+)(9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)(\d{1,3}){0,1}(\d{1,3}){0,1}(\d{1,3}){0,1}(\d{1,3}){0,1}(\d{1,2}){0,1}$/
+const maskValue = value =>
+ value ? value.replace(mask, '$1 $2 $3 $4 $5 $6') : ''
const PhoneNumberInputFormik = memo(({ ...props }) => {
const { onChange, value } = props.field
diff --git a/new-lamassu-admin/src/components/inputs/formik/RadioGroup.js b/new-lamassu-admin/src/components/inputs/formik/RadioGroup.js
new file mode 100644
index 00000000..da19410e
--- /dev/null
+++ b/new-lamassu-admin/src/components/inputs/formik/RadioGroup.js
@@ -0,0 +1,42 @@
+import React, { memo } from 'react'
+import { makeStyles } from '@material-ui/core'
+
+import { Label1 } from 'src/components/typography'
+
+import { RadioGroup } from '../base'
+
+const styles = {
+ label: {
+ height: 16,
+ lineHeight: '16px',
+ margin: [[0, 0, 4, 0]]
+ }
+}
+
+const useStyles = makeStyles(styles)
+
+const RadioGroupFormik = memo(({ ...props }) => {
+ const classes = useStyles()
+
+ const { name, onChange, value } = props.field
+
+ return (
+ <>
+ {props.label && {props.label} }
+ {
+ onChange(e)
+ props.resetError()
+ }}
+ className={props.className}
+ {...props}
+ />
+ >
+ )
+})
+
+export default RadioGroupFormik
diff --git a/new-lamassu-admin/src/pages/OperatorInfo/ContactInfo.js b/new-lamassu-admin/src/pages/OperatorInfo/ContactInfo.js
new file mode 100644
index 00000000..b6b4952b
--- /dev/null
+++ b/new-lamassu-admin/src/pages/OperatorInfo/ContactInfo.js
@@ -0,0 +1,304 @@
+import React, { useState } from 'react'
+import classnames from 'classnames'
+import * as R from 'ramda'
+import * as Yup from 'yup'
+import { gql } from 'apollo-boost'
+import { Form, Formik, Field as FormikField } from 'formik'
+import { makeStyles } from '@material-ui/core'
+import { useQuery, useMutation } from '@apollo/react-hooks'
+
+import { Info2, Info3, Label1 } from 'src/components/typography'
+import TextInputFormik from 'src/components/inputs/formik/TextInput'
+import RadioGroupFormik from 'src/components/inputs/formik/RadioGroup'
+import {
+ PhoneNumberInputFormik,
+ maskValue,
+ mask
+} from 'src/components/inputs/formik/PhoneNumberInput'
+import { Link } from 'src/components/buttons'
+import ErrorMessage from 'src/components/ErrorMessage'
+import { ReactComponent as EditIcon } from 'src/styling/icons/action/edit/enabled.svg'
+import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
+
+import {
+ styles as globalStyles,
+ contactInfoStyles
+} from './OperatorInfo.styles'
+
+const fieldStyles = {
+ field: {
+ position: 'relative',
+ width: 280,
+ height: 46,
+ padding: [[0, 4, 4, 4]]
+ },
+ notEditing: {
+ display: 'flex',
+ flexDirection: 'column',
+ '& > p:first-child': {
+ height: 16,
+ lineHeight: '16px',
+ margin: [[0, 0, 4, 0]]
+ },
+ '& > p:last-child': {
+ margin: 0
+ }
+ }
+}
+
+const fieldUseStyles = makeStyles(fieldStyles)
+
+const Field = ({ editing, field, displayValue, ...props }) => {
+ const classes = fieldUseStyles()
+
+ const classNames = {
+ [classes.field]: true,
+ [classes.notEditing]: !editing
+ }
+
+ return (
+ <>
+
+ {!editing && (
+ <>
+ {field.label}
+ {displayValue(field.value)}
+ >
+ )}
+ {editing && (
+
+ )}
+
+ >
+ )
+}
+
+const GET_CONFIG = gql`
+ {
+ config
+ }
+`
+
+const SAVE_CONFIG = gql`
+ mutation Save($config: JSONObject) {
+ saveConfig(config: $config)
+ }
+`
+
+const INFO_CARD_ENABLED = 'On'
+const INFO_CARD_DISABLED = 'Off'
+
+const styles = R.merge(globalStyles, contactInfoStyles)
+
+const contactUseStyles = makeStyles(styles)
+
+const ContactInfo = () => {
+ const [editing, setEditing] = useState(false)
+ const [info, setInfo] = useState(null)
+ const [error, setError] = useState(null)
+ const [saveConfig] = useMutation(SAVE_CONFIG, {
+ onCompleted: data => {
+ const { operatorInfo } = data.saveConfig
+ setInfo(operatorInfo)
+ setEditing(false)
+ },
+ onError: e => setError(e)
+ })
+
+ useQuery(GET_CONFIG, {
+ onCompleted: data => {
+ const operatorInfo = data.config
+ setInfo(operatorInfo ?? {})
+ }
+ })
+
+ const save = it => {
+ return saveConfig({ variables: { config: { operatorInfo: it } } })
+ }
+
+ const classes = contactUseStyles()
+
+ if (!info) return null
+
+ const fields = [
+ {
+ name: 'infoCardEnabled',
+ label: 'Info Card Enabled',
+ value: info.infoCardEnabled ?? INFO_CARD_DISABLED,
+ type: 'select',
+ component: RadioGroupFormik
+ },
+ {
+ name: 'fullName',
+ label: 'Full name',
+ value: info.fullName ?? '',
+ type: 'text',
+ component: TextInputFormik
+ },
+ {
+ name: 'phoneNumber',
+ label: 'Phone number',
+ value: maskValue(info.phoneNumber) ?? '',
+ type: 'text',
+ component: PhoneNumberInputFormik
+ },
+ {
+ name: 'email',
+ label: 'Email',
+ value: info.email ?? '',
+ type: 'text',
+ component: TextInputFormik
+ },
+ {
+ name: 'website',
+ label: 'Website',
+ value: info.website ?? '',
+ type: 'text',
+ component: TextInputFormik
+ },
+ {
+ name: 'companyNumber',
+ label: 'Company number',
+ value: info.companyNumber ?? '',
+ type: 'text',
+ component: TextInputFormik
+ }
+ ]
+
+ const findField = name => R.find(R.propEq('name', name))(fields)
+ const findValue = name => findField(name).value
+
+ const displayTextValue = value => value
+
+ return (
+ <>
+
+ Contact information
+ {!editing && (
+ setEditing(true)}>
+
+
+ )}
+
+
+
{
+ save(values)
+ }}>
+
+
+
+
+
+
+ Sharing your information with your customers through your machines
+ allows them to contact you in case there's a problem with a machine in
+ your network or a transaction.
+
+
+ >
+ )
+}
+
+export default ContactInfo
diff --git a/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.js b/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.js
new file mode 100644
index 00000000..ec31ff8d
--- /dev/null
+++ b/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.js
@@ -0,0 +1,58 @@
+import React, { useState } from 'react'
+import * as R from 'ramda'
+import { makeStyles } from '@material-ui/core'
+
+import Sidebar from 'src/components/Sidebar'
+import Title from 'src/components/Title'
+
+import logsStyles from '../Logs.styles'
+
+import ContactInfo from './ContactInfo'
+
+const localStyles = {
+ contentWrapper: {
+ marginLeft: 48,
+ paddingTop: 15
+ }
+}
+
+const styles = R.merge(logsStyles, localStyles)
+
+const useStyles = makeStyles(styles)
+
+const CONTACT_INFORMATION = 'Contact information'
+const RECEIPT = 'Receipt'
+const COIN_ATM_RADAR = 'Coin ATM Radar'
+const TERMS_CONDITIONS = 'Terms & Conditions'
+
+const pages = [CONTACT_INFORMATION, RECEIPT, COIN_ATM_RADAR, TERMS_CONDITIONS]
+
+const OperatorInfo = () => {
+ const [selected, setSelected] = useState(CONTACT_INFORMATION)
+ const classes = useStyles()
+
+ const isSelected = it => selected === it
+
+ return (
+ <>
+
+
+
Operator information
+
+
+
+
it}
+ onClick={it => setSelected(it)}
+ />
+
+ {isSelected(CONTACT_INFORMATION) && }
+
+
+ >
+ )
+}
+
+export default OperatorInfo
diff --git a/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js b/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js
new file mode 100644
index 00000000..254360ce
--- /dev/null
+++ b/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.styles.js
@@ -0,0 +1,54 @@
+import { offColor } from 'src/styling/variables'
+
+const styles = {
+ header: {
+ display: 'flex',
+ '& > button': {
+ border: 'none',
+ backgroundColor: 'transparent',
+ marginLeft: 20,
+ cursor: 'pointer'
+ }
+ },
+ section: {
+ marginBottom: 52
+ }
+}
+
+const contactInfoStyles = {
+ row: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ marginBottom: 28,
+ '&:last-child': {
+ marginBottom: 0
+ }
+ },
+ infoMessage: {
+ display: 'flex',
+ marginBottom: 52,
+ '& > p': {
+ width: 330,
+ color: offColor,
+ marginTop: 4,
+ marginLeft: 16
+ }
+ },
+ radioButtonsRow: {
+ height: 60
+ },
+ radioButtons: {
+ display: 'flex',
+ flexDirection: 'row'
+ },
+ submit: {
+ justifyContent: 'flex-start',
+ padding: [[0, 4, 4, 4]],
+ height: 20,
+ '& > button': {
+ marginRight: 40
+ }
+ }
+}
+
+export { styles, contactInfoStyles }
diff --git a/new-lamassu-admin/src/routing/routes.js b/new-lamassu-admin/src/routing/routes.js
index 477adb7c..0a253701 100644
--- a/new-lamassu-admin/src/routing/routes.js
+++ b/new-lamassu-admin/src/routing/routes.js
@@ -10,6 +10,7 @@ import ServerLogs from 'src/pages/ServerLogs'
import Transactions from 'src/pages/Transactions/Transactions'
import Services from 'src/pages/Services/Services'
import AuthRegister from 'src/pages/AuthRegister'
+import OperatorInfo from 'src/pages/OperatorInfo/OperatorInfo'
const tree = [
{ key: 'transactions', label: 'Transactions', route: '/transactions' },
@@ -44,7 +45,8 @@ const tree = [
key: 'services',
label: '3rd party services',
route: '/settings/3rd-party-services'
- }
+ },
+ { key: 'info', label: 'Operator Info', route: '/settings/operator-info' }
]
}
// compliance: { label: 'Compliance', children: [{ label: 'Locale', route: '/locale' }] }
@@ -72,6 +74,7 @@ const Routes = () => (
+
diff --git a/new-lamassu-admin/src/styling/icons/action/edit/enabled.svg b/new-lamassu-admin/src/styling/icons/action/edit/enabled.svg
index 8accc240..fb5bdb42 100644
--- a/new-lamassu-admin/src/styling/icons/action/edit/enabled.svg
+++ b/new-lamassu-admin/src/styling/icons/action/edit/enabled.svg
@@ -1,17 +1,10 @@
-
+
icon/action/edit/enabled
Created with Sketch.
-
-
-
-
-
-
-
-
-
-
+
+
+
\ No newline at end of file