diff --git a/lib/new-admin/graphql/resolvers/customer.resolver.js b/lib/new-admin/graphql/resolvers/customer.resolver.js
index d3563669..58461fd8 100644
--- a/lib/new-admin/graphql/resolvers/customer.resolver.js
+++ b/lib/new-admin/graphql/resolvers/customer.resolver.js
@@ -49,7 +49,8 @@ const resolvers = {
},
deleteCustomerNote: (...[, { noteId }]) => {
return customerNotes.deleteCustomerNote(noteId)
- }
+ },
+ createCustomer: (...[, { phoneNumber }]) => customers.add({ phone: phoneNumber })
}
}
diff --git a/lib/new-admin/graphql/types/customer.type.js b/lib/new-admin/graphql/types/customer.type.js
index bdbf3a94..ce8cb3cb 100644
--- a/lib/new-admin/graphql/types/customer.type.js
+++ b/lib/new-admin/graphql/types/customer.type.js
@@ -103,6 +103,7 @@ const typeDef = gql`
createCustomerNote(customerId: ID!, title: String!, content: String!): Boolean @auth
editCustomerNote(noteId: ID!, newContent: String!): Boolean @auth
deleteCustomerNote(noteId: ID!): Boolean @auth
+ createCustomer(phoneNumber: String): Customer @auth
}
`
diff --git a/new-lamassu-admin/package-lock.json b/new-lamassu-admin/package-lock.json
index e64cc12a..b54297ef 100644
--- a/new-lamassu-admin/package-lock.json
+++ b/new-lamassu-admin/package-lock.json
@@ -13870,6 +13870,11 @@
"delegate": "^3.1.2"
}
},
+ "google-libphonenumber": {
+ "version": "3.2.22",
+ "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.22.tgz",
+ "integrity": "sha512-lzEllxWc05n/HEv75SsDrA7zdEVvQzTZimItZm/TZ5XBs7cmx2NJmSlA5I0kZbdKNu8GFETBhSpo+SOhx0JslA=="
+ },
"graceful-fs": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.5.tgz",
diff --git a/new-lamassu-admin/package.json b/new-lamassu-admin/package.json
index bd97a32c..ab5f4f1c 100644
--- a/new-lamassu-admin/package.json
+++ b/new-lamassu-admin/package.json
@@ -26,6 +26,7 @@
"downshift": "3.3.4",
"file-saver": "2.0.2",
"formik": "2.2.0",
+ "google-libphonenumber": "^3.2.22",
"graphql": "^14.5.8",
"graphql-tag": "^2.10.3",
"jss-plugin-extend": "^10.0.0",
diff --git a/new-lamassu-admin/src/components/layout/TitleSection.js b/new-lamassu-admin/src/components/layout/TitleSection.js
index 6858ed5f..f0777103 100644
--- a/new-lamassu-admin/src/components/layout/TitleSection.js
+++ b/new-lamassu-admin/src/components/layout/TitleSection.js
@@ -19,14 +19,14 @@ const TitleSection = ({
buttons = [],
children,
appendix,
- appendixClassName
+ appendixRight
}) => {
const classes = useStyles()
return (
{title}
- {appendix &&
{appendix}
}
+ {!!appendix && appendix}
{error && (
Failed to save
)}
@@ -46,13 +46,14 @@ const TitleSection = ({
>
)}
-
+
{(labels ?? []).map(({ icon, label }, idx) => (
{icon}
{label}
))}
+ {appendixRight}
{children}
diff --git a/new-lamassu-admin/src/pages/Customers/Customers.js b/new-lamassu-admin/src/pages/Customers/Customers.js
index 70e182f6..05c770c3 100644
--- a/new-lamassu-admin/src/pages/Customers/Customers.js
+++ b/new-lamassu-admin/src/pages/Customers/Customers.js
@@ -1,5 +1,5 @@
-import { useQuery } from '@apollo/react-hooks'
-import { makeStyles } from '@material-ui/core/styles'
+import { useQuery, useMutation } from '@apollo/react-hooks'
+import { Box, makeStyles } from '@material-ui/core'
import gql from 'graphql-tag'
import * as R from 'ramda'
import React, { useState } from 'react'
@@ -7,6 +7,7 @@ import { useHistory } from 'react-router-dom'
import SearchBox from 'src/components/SearchBox'
import SearchFilter from 'src/components/SearchFilter'
+import { Link } from 'src/components/buttons'
import TitleSection from 'src/components/layout/TitleSection'
import baseStyles from 'src/pages/Logs.styles'
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
@@ -14,6 +15,7 @@ import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-ou
import { fromNamespace, namespaces } from 'src/utils/config'
import CustomersList from './CustomersList'
+import CreateCustomerModal from './components/CreateCustomerModal'
const GET_CUSTOMER_FILTERS = gql`
query filters {
@@ -49,6 +51,14 @@ const GET_CUSTOMERS = gql`
}
`
+const CREATE_CUSTOMER = gql`
+ mutation createCustomer($phoneNumber: String) {
+ createCustomer(phoneNumber: $phoneNumber) {
+ phone
+ }
+ }
+`
+
const useBaseStyles = makeStyles(baseStyles)
const getFiltersObj = filters =>
@@ -64,6 +74,7 @@ const Customers = () => {
const [filteredCustomers, setFilteredCustomers] = useState([])
const [variables, setVariables] = useState({})
const [filters, setFilters] = useState([])
+ const [showCreationModal, setShowCreationModal] = useState(false)
const {
data: customersResponse,
@@ -78,6 +89,11 @@ const Customers = () => {
GET_CUSTOMER_FILTERS
)
+ const [createNewCustomer] = useMutation(CREATE_CUSTOMER, {
+ onCompleted: () => setShowCreationModal(false),
+ refetchQueries: () => ['configAndCustomers']
+ })
+
const configData = R.path(['config'])(customersResponse) ?? []
const locale = configData && fromNamespace(namespaces.LOCALE, configData)
const customersData = R.sortWith([
@@ -139,7 +155,7 @@ const Customers = () => {
+
{
/>
}
- appendixClassName={baseStyles.buttonsWrapper}
+ appendixRight={
+
+ setShowCreationModal(true)}>
+ Add new user
+
+
+ }
labels={[
{ label: 'Cash-in', icon: },
{ label: 'Cash-out', icon: }
@@ -169,6 +191,11 @@ const Customers = () => {
onClick={handleCustomerClicked}
loading={customerLoading}
/>
+ setShowCreationModal(false)}
+ onSubmit={createNewCustomer}
+ />
>
)
}
diff --git a/new-lamassu-admin/src/pages/Customers/CustomersList.js b/new-lamassu-admin/src/pages/Customers/CustomersList.js
index 43e40c2a..6b9e4167 100644
--- a/new-lamassu-admin/src/pages/Customers/CustomersList.js
+++ b/new-lamassu-admin/src/pages/Customers/CustomersList.js
@@ -19,7 +19,7 @@ const CustomersList = ({ data, locale, onClick, loading }) => {
const elements = [
{
header: 'Phone',
- width: 175,
+ width: 199,
view: it => getFormattedPhone(it.phone, locale.country)
},
{
diff --git a/new-lamassu-admin/src/pages/Customers/components/CreateCustomerModal.js b/new-lamassu-admin/src/pages/Customers/components/CreateCustomerModal.js
new file mode 100644
index 00000000..f36048a6
--- /dev/null
+++ b/new-lamassu-admin/src/pages/Customers/components/CreateCustomerModal.js
@@ -0,0 +1,108 @@
+import { makeStyles } from '@material-ui/core/styles'
+import { Field, Form, Formik } from 'formik'
+import { PhoneNumberUtil } from 'google-libphonenumber'
+import React from 'react'
+import * as Yup from 'yup'
+
+import ErrorMessage from 'src/components/ErrorMessage'
+import Modal from 'src/components/Modal'
+import { Button } from 'src/components/buttons'
+import { TextInput } from 'src/components/inputs/formik'
+import { H1 } from 'src/components/typography'
+import { spacer, primaryColor, fontPrimary } from 'src/styling/variables'
+
+const styles = {
+ modalTitle: {
+ marginTop: -5,
+ color: primaryColor,
+ fontFamily: fontPrimary
+ },
+ footer: {
+ display: 'flex',
+ flexDirection: 'row',
+ margin: [['auto', 0, spacer * 3, 0]]
+ },
+ form: {
+ display: 'flex',
+ flexDirection: 'column',
+ height: '100%'
+ },
+ submit: {
+ margin: [['auto', 0, 0, 'auto']]
+ }
+}
+
+const pnUtilInstance = PhoneNumberUtil.getInstance()
+
+const validationSchema = Yup.object().shape({
+ phoneNumber: Yup.string()
+ .required('A phone number is required')
+ .test('is-valid-number', 'That is not a valid phone number', value => {
+ try {
+ const number = pnUtilInstance.parseAndKeepRawInput(value, 'US')
+ return pnUtilInstance.isValidNumber(number)
+ } catch (e) {}
+ })
+})
+
+const initialValues = {
+ phoneNumber: ''
+}
+
+const useStyles = makeStyles(styles)
+
+const getErrorMsg = (formikErrors, formikTouched) => {
+ if (!formikErrors || !formikTouched) return null
+ if (formikErrors.phoneNumber && formikTouched.phoneNumber)
+ return formikErrors.phoneNumber
+ return null
+}
+
+const CreateCustomerModal = ({ showModal, handleClose, onSubmit }) => {
+ const classes = useStyles()
+
+ return (
+
+ {
+ onSubmit({
+ variables: { phoneNumber: values.phoneNumber }
+ })
+ }}>
+ {({ errors, touched }) => (
+
+ )}
+
+
+ )
+}
+
+export default CreateCustomerModal