partial: customer page css migration

This commit is contained in:
Rafael Taranto 2025-05-05 14:43:58 +01:00
parent 926b7e6933
commit d7b2e12f94
48 changed files with 524 additions and 2183 deletions

View file

@ -7,14 +7,15 @@ import styles from './ActionButton.styles'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const ActionButton = memo( const ActionButton = memo(
({ className, Icon, InverseIcon, color, children, ...props }) => { ({ className, Icon, InverseIcon, color, center, children, ...props }) => {
const classes = useStyles() const classes = useStyles()
const classNames = { const classNames = {
[classes.actionButton]: true, [classes.actionButton]: true,
[classes.primary]: color === 'primary', [classes.primary]: color === 'primary',
[classes.secondary]: color === 'secondary', [classes.secondary]: color === 'secondary',
[classes.spring]: color === 'spring', [classes.spring]: color === 'spring',
[classes.tomato]: color === 'tomato' [classes.tomato]: color === 'tomato',
[classes.center]: center
} }
return ( return (

View file

@ -119,5 +119,9 @@ export default {
} }
} }
}, },
center: {
alignItems: 'center',
justifyContent: 'center'
},
actionButtonIconActive: {} actionButtonIconActive: {}
} }

View file

@ -19,6 +19,7 @@ const TextInput = memo(
suffix, suffix,
textAlign, textAlign,
width, width,
inputClasses,
// lg or sm // lg or sm
size, size,
bold, bold,
@ -29,7 +30,7 @@ const TextInput = memo(
const classes = useStyles({ textAlign, width, size }) const classes = useStyles({ textAlign, width, size })
const isTextFilled = !error && !R.isNil(value) && !R.isEmpty(value) const isTextFilled = !error && !R.isNil(value) && !R.isEmpty(value)
const filled = isPasswordFilled || isTextFilled const filled = isPasswordFilled || isTextFilled
const inputClasses = { const divClass = {
[classes.bold]: bold [classes.bold]: bold
} }
@ -44,15 +45,17 @@ const TextInput = memo(
classes={{ root: classes.root }} classes={{ root: classes.root }}
className={className} className={className}
InputProps={{ InputProps={{
className: classnames(inputClasses), className: classnames(divClass),
classes: { classes: {
root: classes.size, root: classes.size,
underline: filled ? classes.underline : null underline: filled ? classes.underline : null,
input: inputClasses
}, },
...InputProps ...InputProps
}} }}
{...props} /> {...props}
); />
)
} }
) )

View file

@ -1,28 +1,19 @@
import Grid from '@mui/material/Grid'
import { makeStyles } from '@mui/styles'
import * as R from 'ramda' import * as R from 'ramda'
import { useState, React } from 'react' import { useState, React } from 'react'
import ImagePopper from 'src/components/ImagePopper' import ImagePopper from 'src/components/ImagePopper'
import { H3, Info3 } from 'src/components/typography' import { H3, Info3 } from 'src/components/typography'
import CardIcon from 'src/styling/icons/ID/card/comet.svg?react' import CardIcon from 'src/styling/icons/ID/card/comet.svg?react'
import PhoneIcon from 'src/styling/icons/ID/phone/comet.svg?react' import PhoneIcon from 'src/styling/icons/ID/phone/comet.svg?react'
import CrossedCameraIcon from 'src/styling/icons/ID/photo/crossed-camera.svg?react'
import EditIcon from 'src/styling/icons/action/edit/comet.svg?react' import EditIcon from 'src/styling/icons/action/edit/comet.svg?react'
import CustomerListViewReversedIcon from 'src/styling/icons/circle buttons/customer-list-view/white.svg?react'
import CustomerListViewIcon from 'src/styling/icons/circle buttons/customer-list-view/zodiac.svg?react'
import OverviewReversedIcon from 'src/styling/icons/circle buttons/overview/white.svg?react'
import OverviewIcon from 'src/styling/icons/circle buttons/overview/zodiac.svg?react'
import * as Yup from 'yup' import * as Yup from 'yup'
import { FeatureButton } from 'src/components/buttons'
import { TextInput } from 'src/components/inputs/formik' import { TextInput } from 'src/components/inputs/formik'
import { import {
OVERRIDE_AUTHORIZED, OVERRIDE_AUTHORIZED,
OVERRIDE_REJECTED OVERRIDE_REJECTED
} from 'src/pages/Customers/components/propertyCard' } from 'src/pages/Customers/components/consts'
import { onlyFirstToUpper } from 'src/utils/string' import { onlyFirstToUpper } from 'src/utils/string'
import styles from './CustomerData.styles'
import { EditableCard } from './components' import { EditableCard } from './components'
import { import {
customerDataElements, customerDataElements,
@ -32,32 +23,20 @@ import {
getFormattedPhone getFormattedPhone
} from './helper' } from './helper'
const useStyles = makeStyles(styles)
const IMAGE_WIDTH = 165 const IMAGE_WIDTH = 165
const IMAGE_HEIGHT = 32 const IMAGE_HEIGHT = 32
const POPUP_IMAGE_WIDTH = 360 const POPUP_IMAGE_WIDTH = 360
const POPUP_IMAGE_HEIGHT = 240 const POPUP_IMAGE_HEIGHT = 240
const Photo = ({ show, src }) => { const Photo = ({ src }) => {
const classes = useStyles({ width: IMAGE_WIDTH })
return ( return (
<> <ImagePopper
{show ? ( src={src}
<ImagePopper width={IMAGE_WIDTH}
src={src} height={IMAGE_HEIGHT}
width={IMAGE_WIDTH} popupWidth={POPUP_IMAGE_WIDTH}
height={IMAGE_HEIGHT} popupHeight={POPUP_IMAGE_HEIGHT}
popupWidth={POPUP_IMAGE_WIDTH} />
popupHeight={POPUP_IMAGE_HEIGHT}
/>
) : (
<div className={classes.photoWrapper}>
<CrossedCameraIcon />
</div>
)}
</>
) )
} }
@ -75,8 +54,6 @@ const CustomerData = ({
setRetrieve, setRetrieve,
checkAgainstSanctions checkAgainstSanctions
}) => { }) => {
const classes = useStyles()
const [listView, setListView] = useState(false)
const [previewPhoto, setPreviewPhoto] = useState(null) const [previewPhoto, setPreviewPhoto] = useState(null)
const [previewCard, setPreviewCard] = useState(null) const [previewCard, setPreviewCard] = useState(null)
@ -163,7 +140,7 @@ const CustomerData = ({
{ {
fields: customerDataElements.idCardData, fields: customerDataElements.idCardData,
title: 'ID Scan', title: 'ID Scan',
titleIcon: <CardIcon className={classes.cardIcon} />, titleIcon: <CardIcon />,
state: R.path(['idCardDataOverride'])(customer), state: R.path(['idCardDataOverride'])(customer),
authorize: () => authorize: () =>
updateCustomer({ idCardDataOverride: OVERRIDE_AUTHORIZED }), updateCustomer({ idCardDataOverride: OVERRIDE_AUTHORIZED }),
@ -187,7 +164,7 @@ const CustomerData = ({
{ {
fields: smsDataElements, fields: smsDataElements,
title: 'SMS data', title: 'SMS data',
titleIcon: <PhoneIcon className={classes.cardIcon} />, titleIcon: <PhoneIcon />,
state: R.path(['phoneOverride'])(customer), state: R.path(['phoneOverride'])(customer),
authorize: () => updateCustomer({ phoneOverride: OVERRIDE_AUTHORIZED }), authorize: () => updateCustomer({ phoneOverride: OVERRIDE_AUTHORIZED }),
reject: () => updateCustomer({ phoneOverride: OVERRIDE_REJECTED }), reject: () => updateCustomer({ phoneOverride: OVERRIDE_REJECTED }),
@ -208,7 +185,7 @@ const CustomerData = ({
{ {
title: 'Email', title: 'Email',
fields: customerDataElements.email, fields: customerDataElements.email,
titleIcon: <CardIcon className={classes.cardIcon} />, titleIcon: <CardIcon />,
// state: R.path(['emailOverride'])(customer), // state: R.path(['emailOverride'])(customer),
// authorize: () => updateCustomer({ emailOverride: OVERRIDE_AUTHORIZED }), // authorize: () => updateCustomer({ emailOverride: OVERRIDE_AUTHORIZED }),
// reject: () => updateCustomer({ emailOverride: OVERRIDE_REJECTED }), // reject: () => updateCustomer({ emailOverride: OVERRIDE_REJECTED }),
@ -220,13 +197,13 @@ const CustomerData = ({
}, },
{ {
title: 'Name', title: 'Name',
titleIcon: <EditIcon className={classes.editIcon} />, titleIcon: <EditIcon />,
isAvailable: false, isAvailable: false,
editable: true editable: true
}, },
{ {
title: 'Sanctions check', title: 'Sanctions check',
titleIcon: <EditIcon className={classes.editIcon} />, titleIcon: <EditIcon />,
state: R.path(['sanctionsOverride'])(customer), state: R.path(['sanctionsOverride'])(customer),
authorize: () => authorize: () =>
updateCustomer({ sanctionsOverride: OVERRIDE_AUTHORIZED }), updateCustomer({ sanctionsOverride: OVERRIDE_AUTHORIZED }),
@ -238,7 +215,7 @@ const CustomerData = ({
{ {
fields: customerDataElements.frontCamera, fields: customerDataElements.frontCamera,
title: 'Front facing camera', title: 'Front facing camera',
titleIcon: <EditIcon className={classes.editIcon} />, titleIcon: <EditIcon />,
state: R.path(['frontCameraOverride'])(customer), state: R.path(['frontCameraOverride'])(customer),
authorize: () => authorize: () =>
updateCustomer({ frontCameraOverride: OVERRIDE_AUTHORIZED }), updateCustomer({ frontCameraOverride: OVERRIDE_AUTHORIZED }),
@ -259,7 +236,6 @@ const CustomerData = ({
return customer.frontCameraPath ? ( return customer.frontCameraPath ? (
<Photo <Photo
show={customer.frontCameraPath}
src={ src={
!R.isNil(previewPhoto) !R.isNil(previewPhoto)
? URL.createObjectURL(previewPhoto) ? URL.createObjectURL(previewPhoto)
@ -277,7 +253,7 @@ const CustomerData = ({
{ {
fields: customerDataElements.idCardPhoto, fields: customerDataElements.idCardPhoto,
title: 'ID card image', title: 'ID card image',
titleIcon: <EditIcon className={classes.editIcon} />, titleIcon: <EditIcon />,
state: R.path(['idCardPhotoOverride'])(customer), state: R.path(['idCardPhotoOverride'])(customer),
authorize: () => authorize: () =>
updateCustomer({ idCardPhotoOverride: OVERRIDE_AUTHORIZED }), updateCustomer({ idCardPhotoOverride: OVERRIDE_AUTHORIZED }),
@ -298,7 +274,6 @@ const CustomerData = ({
return customer.idCardPhotoPath ? ( return customer.idCardPhotoPath ? (
<Photo <Photo
show={customer.idCardPhotoPath}
src={ src={
!R.isNil(previewCard) !R.isNil(previewCard)
? URL.createObjectURL(previewCard) ? URL.createObjectURL(previewCard)
@ -316,7 +291,7 @@ const CustomerData = ({
{ {
fields: customerDataElements.usSsn, fields: customerDataElements.usSsn,
title: 'US SSN', title: 'US SSN',
titleIcon: <CardIcon className={classes.cardIcon} />, titleIcon: <CardIcon />,
state: R.path(['usSsnOverride'])(customer), state: R.path(['usSsnOverride'])(customer),
authorize: () => updateCustomer({ usSsnOverride: OVERRIDE_AUTHORIZED }), authorize: () => updateCustomer({ usSsnOverride: OVERRIDE_AUTHORIZED }),
reject: () => updateCustomer({ usSsnOverride: OVERRIDE_REJECTED }), reject: () => updateCustomer({ usSsnOverride: OVERRIDE_REJECTED }),
@ -342,7 +317,7 @@ const CustomerData = ({
} }
], ],
title: it.customInfoRequest.customRequest.name, title: it.customInfoRequest.customRequest.name,
titleIcon: <CardIcon className={classes.cardIcon} />, titleIcon: <CardIcon />,
state: R.path(['override'])(it), state: R.path(['override'])(it),
authorize: () => authorize: () =>
authorizeCustomRequest({ authorizeCustomRequest({
@ -395,7 +370,7 @@ const CustomerData = ({
} }
], ],
title: it.label, title: it.label,
titleIcon: <EditIcon className={classes.editIcon} />, titleIcon: <EditIcon />,
save: values => { save: values => {
updateCustomEntry({ updateCustomEntry({
fieldId: it.id, fieldId: it.id,
@ -445,7 +420,7 @@ const CustomerData = ({
editable: false editable: false
} }
], ],
titleIcon: <CardIcon className={classes.cardIcon} />, titleIcon: <CardIcon />,
title: `External Info [${it.service}]`, title: `External Info [${it.service}]`,
initialValues: it ?? { initialValues: it ?? {
externalId: '', externalId: '',
@ -477,26 +452,28 @@ const CustomerData = ({
idx idx
) => { ) => {
return ( return (
<EditableCard <div className="mb-4">
title={title} <EditableCard
key={idx} title={title}
authorize={authorize} key={idx}
reject={reject} authorize={authorize}
state={state} reject={reject}
titleIcon={titleIcon} state={state}
hasImage={hasImage} titleIcon={titleIcon}
hasAdditionalData={hasAdditionalData} hasImage={hasImage}
fields={fields} hasAdditionalData={hasAdditionalData}
validationSchema={validationSchema} fields={fields}
initialValues={initialValues} validationSchema={validationSchema}
save={save} initialValues={initialValues}
cancel={cancel} save={save}
deleteEditedData={deleteEditedData} cancel={cancel}
retrieveAdditionalData={retrieveAdditionalData} deleteEditedData={deleteEditedData}
checkAgainstSanctions={checkAgainstSanctions} retrieveAdditionalData={retrieveAdditionalData}
editable={editable}> checkAgainstSanctions={checkAgainstSanctions}
{children} editable={editable}>
</EditableCard> {children}
</EditableCard>
</div>
) )
} }
@ -505,113 +482,67 @@ const CustomerData = ({
idx idx
) => { ) => {
return ( return (
<EditableCard <div className="mb-4">
title={title} <EditableCard
key={idx} title={title}
state={state} key={idx}
initialValues={initialValues} state={state}
titleIcon={titleIcon} initialValues={initialValues}
editable={false} titleIcon={titleIcon}
hasImage={hasImage} editable={false}
fields={fields}> hasImage={hasImage}
{children} fields={fields}>
</EditableCard> {children}
</EditableCard>
</div>
) )
} }
const visibleCards = getVisibleCards(cards) const visibleCards = getVisibleCards(cards)
const Separator = ({ title }) => (
<div className="w-full my-4 col-span-all">
<div className="flex items-center">
<div className="h-px bg-comet grow-1"></div>
<span className="mx-4 text-comet font-medium">{title}</span>
<div className="h-px bg-comet grow-5"></div>
</div>
</div>
)
return ( return (
<div> <div>
<div className={classes.header}> <H3 className="mt-1 mb-7">{'Customer data'}</H3>
<H3 className={classes.title}>{'Customer data'}</H3>
{
// TODO: Remove false condition for next release
// false && (
// <>
// <FeatureButton
// active={!listView}
// className={classes.viewIcons}
// Icon={OverviewIcon}
// InverseIcon={OverviewReversedIcon}
// onClick={() => setListView(false)}
// />
// <FeatureButton
// active={listView}
// className={classes.viewIcons}
// Icon={CustomerListViewIcon}
// InverseIcon={CustomerListViewReversedIcon}
// onClick={() => setListView(true)}></FeatureButton>
// </>
// )
}
</div>
<div> <div>
{!listView && customer && ( {customer && (
<Grid container> <div className="columns-2 gap-4">
<Grid container direction="column" item xs={6}> {visibleCards.map((elem, idx) => {
{visibleCards.map((elem, idx) => { return editableCard(elem, idx)
return isEven(idx) ? editableCard(elem, idx) : null })}
})} {!R.isEmpty(customFields) && (
</Grid> <>
<Grid container direction="column" item xs={6}> <Separator title="Custom data entry" />
{visibleCards.map((elem, idx) => {
return !isEven(idx) ? editableCard(elem, idx) : null
})}
</Grid>
</Grid>
)}
{!R.isEmpty(customFields) && (
<div className={classes.wrapper}>
<span className={classes.separator}>Custom data entry</span>
<Grid container>
<Grid container direction="column" item xs={6}>
{customFields.map((elem, idx) => { {customFields.map((elem, idx) => {
return isEven(idx) ? editableCard(elem, idx) : null return editableCard(elem, idx)
})} })}
</Grid> </>
<Grid container direction="column" item xs={6}> )}
{customFields.map((elem, idx) => { {!R.isEmpty(customRequirements) && (
return !isEven(idx) ? editableCard(elem, idx) : null <>
})} <Separator title="Custom requirements" />
</Grid>
</Grid>
</div>
)}
{!R.isEmpty(customRequirements) && (
<div className={classes.wrapper}>
<span className={classes.separator}>Custom requirements</span>
<Grid container>
<Grid container direction="column" item xs={6}>
{customRequirements.map((elem, idx) => { {customRequirements.map((elem, idx) => {
return isEven(idx) ? editableCard(elem, idx) : null return editableCard(elem, idx)
})} })}
</Grid> </>
<Grid container direction="column" item xs={6}> )}
{customRequirements.map((elem, idx) => { {!R.isEmpty(externalCompliance) && (
return !isEven(idx) ? editableCard(elem, idx) : null <>
})} <Separator title="External compliance information" />
</Grid>
</Grid>
</div>
)}
{!R.isEmpty(externalCompliance) && (
<div className={classes.wrapper}>
<span className={classes.separator}>
External compliance information
</span>
<Grid container>
<Grid container direction="column" item xs={6}>
{externalCompliance.map((elem, idx) => { {externalCompliance.map((elem, idx) => {
return isEven(idx) ? nonEditableCard(elem, idx) : null return nonEditableCard(elem, idx)
})} })}
</Grid> </>
<Grid container direction="column" item xs={6}> )}
{externalCompliance.map((elem, idx) => {
return !isEven(idx) ? nonEditableCard(elem, idx) : null
})}
</Grid>
</Grid>
</div> </div>
)} )}
</div> </div>

View file

@ -1,49 +0,0 @@
import { offColor } from 'src/styling/variables'
export default {
header: {
display: 'flex',
flexDirection: 'row',
marginBottom: 15
},
title: {
marginTop: 7,
marginRight: 24
},
editIcon: {
marginTop: 5
},
cardIcon: {
marginTop: 7
},
viewIcons: {
marginRight: 12
},
wrapper: {
display: 'block',
overflow: 'hidden',
whiteSpace: 'nowrap'
},
separator: {
color: offColor,
margin: [[8, 0, 8, 150]],
position: 'relative',
display: 'inline-block',
'&:before, &:after': {
content: '""',
position: 'absolute',
background: offColor,
top: '50%',
width: 1000,
height: 1
},
'&:before': {
right: '100%',
marginRight: 15
},
'&:after': {
left: '100%',
marginLeft: 15
}
}
}

View file

@ -1,16 +1,12 @@
import { makeStyles } from '@mui/styles'
import * as R from 'ramda' import * as R from 'ramda'
import { React, useState } from 'react' import { React, useState } from 'react'
import { H3 } from 'src/components/typography' import { H3 } from 'src/components/typography'
import styles from './CustomerNotes.styles'
import NewNoteCard from './components/notes/NewNoteCard' import NewNoteCard from './components/notes/NewNoteCard'
import NewNoteModal from './components/notes/NewNoteModal' import NewNoteModal from './components/notes/NewNoteModal'
import NoteCard from './components/notes/NoteCard' import NoteCard from './components/notes/NoteCard'
import NoteEdit from './components/notes/NoteEdit' import NoteEdit from './components/notes/NoteEdit'
const useStyles = makeStyles(styles)
const CustomerNotes = ({ const CustomerNotes = ({
customer, customer,
createNote, createNote,
@ -18,7 +14,6 @@ const CustomerNotes = ({
editNote, editNote,
timezone timezone
}) => { }) => {
const classes = useStyles()
const [openModal, setOpenModal] = useState(false) const [openModal, setOpenModal] = useState(false)
const [editing, setEditing] = useState(null) const [editing, setEditing] = useState(null)
@ -52,11 +47,9 @@ const CustomerNotes = ({
return ( return (
<div> <div>
<div className={classes.header}> <H3 className="mt-1 mb-7">{'Notes'}</H3>
<H3 className={classes.title}>{'Notes'}</H3>
</div>
{R.isNil(editing) && ( {R.isNil(editing) && (
<div className={classes.notesChipList}> <div className="grid grid-cols-[repeat(4,_200px)] gap-5 auto-rows-[200px]">
<NewNoteCard setOpenModal={setOpenModal} /> <NewNoteCard setOpenModal={setOpenModal} />
{customerNotes.map((it, idx) => ( {customerNotes.map((it, idx) => (
<NoteCard <NoteCard

View file

@ -1,17 +0,0 @@
const styles = {
header: {
display: 'flex',
flexDirection: 'row'
},
title: {
marginTop: 7,
marginRight: 24
},
notesChipList: {
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap'
}
}
export default styles

View file

@ -1,5 +1,4 @@
import Paper from '@mui/material/Paper' import Paper from '@mui/material/Paper'
import { makeStyles } from '@mui/styles'
import { format } from 'date-fns/fp' import { format } from 'date-fns/fp'
import * as R from 'ramda' import * as R from 'ramda'
import { React, useState } from 'react' import { React, useState } from 'react'
@ -7,14 +6,9 @@ import { InformativeDialog } from 'src/components/InformativeDialog'
import { Label2, H3 } from 'src/components/typography' import { Label2, H3 } from 'src/components/typography'
import CameraIcon from 'src/styling/icons/ID/photo/comet.svg?react' import CameraIcon from 'src/styling/icons/ID/photo/comet.svg?react'
import styles from './CustomerPhotos.styles'
import PhotosCarousel from './components/PhotosCarousel' import PhotosCarousel from './components/PhotosCarousel'
const useStyles = makeStyles(styles)
const CustomerPhotos = ({ photosData, timezone }) => { const CustomerPhotos = ({ photosData, timezone }) => {
const classes = useStyles()
const [photosDialog, setPhotosDialog] = useState(false) const [photosDialog, setPhotosDialog] = useState(false)
const [photoClickedIndex, setPhotoClickIndex] = useState(null) const [photoClickedIndex, setPhotoClickIndex] = useState(null)
const orderedPhotosData = !R.isNil(photoClickedIndex) const orderedPhotosData = !R.isNil(photoClickedIndex)
@ -23,10 +17,8 @@ const CustomerPhotos = ({ photosData, timezone }) => {
return ( return (
<div> <div>
<div className={classes.header}> <H3 className="mt-1 mb-7">{'Photos & files'}</H3>
<H3 className={classes.title}>{'Photos & files'}</H3> <div className="flex flex-wrap gap-4">
</div>
<div className={classes.photosChipList}>
{photosData.map((elem, idx) => ( {photosData.map((elem, idx) => (
<PhotoCard <PhotoCard
key={idx} key={idx}
@ -59,21 +51,21 @@ export const PhotoCard = ({
setPhotosDialog, setPhotosDialog,
setPhotoClickIndex setPhotoClickIndex
}) => { }) => {
const classes = useStyles()
return ( return (
<Paper <Paper
className={classes.photoCardChip} className="cursor-pointer overflow-hidden"
onClick={() => { onClick={() => {
setPhotoClickIndex(idx) setPhotoClickIndex(idx)
setPhotosDialog(true) setPhotosDialog(true)
}}> }}>
<img className={classes.image} src={src} alt="" /> <img
<div className={classes.footer}> className="w-56 h-50 object-cover object-center block"
src={src}
alt=""
/>
<div className="flex p-3 gap-3">
<CameraIcon /> <CameraIcon />
<Label2 className={classes.date}> <Label2 noMargin>{format('yyyy-MM-dd', new Date(date))}</Label2>
{format('yyyy-MM-dd', new Date(date))}
</Label2>
</div> </div>
</Paper> </Paper>
) )

View file

@ -1,38 +0,0 @@
const styles = {
header: {
display: 'flex',
flexDirection: 'row'
},
title: {
marginTop: 7,
marginRight: 24,
marginBottom: 32
},
photosChipList: {
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
gap: 14
},
image: {
objectFit: 'cover',
objectPosition: 'center',
width: 224,
height: 200,
borderTopLeftRadius: 4,
borderTopRightRadius: 4
},
photoCardChip: {
cursor: 'pointer'
},
footer: {
display: 'flex',
flexDirection: 'row',
margin: [[8, 0, 0, 8]]
},
date: {
margin: [[0, 0, 8, 12]]
}
}
export default styles

View file

@ -4,7 +4,6 @@ import DialogActions from '@mui/material/DialogActions'
import DialogContent from '@mui/material/DialogContent' import DialogContent from '@mui/material/DialogContent'
import Dialog from '@mui/material/Dialog' import Dialog from '@mui/material/Dialog'
import Switch from '@mui/material/Switch' import Switch from '@mui/material/Switch'
import { makeStyles } from '@mui/styles'
import NavigateNextIcon from '@mui/icons-material/NavigateNext' import NavigateNextIcon from '@mui/icons-material/NavigateNext'
import * as R from 'ramda' import * as R from 'ramda'
import React, { memo, useState } from 'react' import React, { memo, useState } from 'react'
@ -23,7 +22,7 @@ import { Button, IconButton, ActionButton } from 'src/components/buttons'
import { import {
OVERRIDE_AUTHORIZED, OVERRIDE_AUTHORIZED,
OVERRIDE_REJECTED OVERRIDE_REJECTED
} from 'src/pages/Customers/components/propertyCard' } from 'src/pages/Customers/components/consts'
// TODO: Enable for next release // TODO: Enable for next release
// import DiscountReversedIcon from 'src/styling/icons/button/discount/white.svg?react' // import DiscountReversedIcon from 'src/styling/icons/button/discount/white.svg?react'
// import Discount from 'src/styling/icons/button/discount/zodiac.svg?react' // import Discount from 'src/styling/icons/button/discount/zodiac.svg?react'
@ -32,7 +31,6 @@ import { fromNamespace, namespaces } from 'src/utils/config'
import CustomerData from './CustomerData' import CustomerData from './CustomerData'
import CustomerNotes from './CustomerNotes' import CustomerNotes from './CustomerNotes'
import CustomerPhotos from './CustomerPhotos' import CustomerPhotos from './CustomerPhotos'
import styles from './CustomerProfile.styles'
import { import {
CustomerDetails, CustomerDetails,
TransactionsList, TransactionsList,
@ -41,8 +39,6 @@ import {
} from './components' } from './components'
import { getFormattedPhone, getName, formatPhotosData } from './helper' import { getFormattedPhone, getName, formatPhotosData } from './helper'
const useStyles = makeStyles(styles)
const GET_CUSTOMER = gql` const GET_CUSTOMER = gql`
query customer($customerId: ID!) { query customer($customerId: ID!) {
config config
@ -528,23 +524,22 @@ const CustomerProfile = memo(() => {
display: it.customRequest.name display: it.customRequest.name
})) ?? [] })) ?? []
const classes = useStyles()
const email = R.path(['email'])(customerData) const email = R.path(['email'])(customerData)
const phone = R.path(['phone'])(customerData) const phone = R.path(['phone'])(customerData)
return ( return (
<> <>
<Breadcrumbs <Breadcrumbs
classes={{ root: classes.breadcrumbs }} className="my-5"
separator={<NavigateNextIcon fontSize="small" />} separator={<NavigateNextIcon fontSize="small" />}
aria-label="breadcrumb"> aria-label="breadcrumb">
<Label1 <Label1
noMargin noMargin
className={classes.labelLink} className="cursor-pointer text-comet"
onClick={() => history.push('/compliance/customers')}> onClick={() => history.push('/compliance/customers')}>
Customers Customers
</Label1> </Label1>
<Label2 noMargin className={classes.labelLink}> <Label2 noMargin className="cursor-pointer text-comet">
{name.length {name.length
? name ? name
: email?.length : email?.length
@ -552,8 +547,8 @@ const CustomerProfile = memo(() => {
: getFormattedPhone(phone, locale.country)} : getFormattedPhone(phone, locale.country)}
</Label2> </Label2>
</Breadcrumbs> </Breadcrumbs>
<div className={classes.panels}> <div className="flex gap-20">
<div className={classes.leftSidePanel}> <div className="w-55 flex flex-col gap-6">
{!loading && !customerData.isAnonymous && ( {!loading && !customerData.isAnonymous && (
<> <>
<CustomerSidebar <CustomerSidebar
@ -561,10 +556,12 @@ const CustomerProfile = memo(() => {
onClick={onClickSidebarItem} onClick={onClickSidebarItem}
/> />
<div> <div>
<Label1 className={classes.actionLabel}>Actions</Label1> <Label1 noMargin className="text-comet my-1">
<div className={classes.actionBar}> Actions
</Label1>
<div className="flex flex-col gap-1">
<ActionButton <ActionButton
className={classes.actionButton} center
color="primary" color="primary"
Icon={DataIcon} Icon={DataIcon}
InverseIcon={DataReversedIcon} InverseIcon={DataReversedIcon}
@ -572,7 +569,6 @@ const CustomerProfile = memo(() => {
{`Manual data entry`} {`Manual data entry`}
</ActionButton> </ActionButton>
{/* <ActionButton {/* <ActionButton
className={classes.actionButton}
color="primary" color="primary"
Icon={Discount} Icon={Discount}
InverseIcon={DiscountReversedIcon} InverseIcon={DiscountReversedIcon}
@ -581,7 +577,7 @@ const CustomerProfile = memo(() => {
</ActionButton> */} </ActionButton> */}
{isSuspended && ( {isSuspended && (
<ActionButton <ActionButton
className={classes.actionButton} center
color="primary" color="primary"
Icon={AuthorizeIcon} Icon={AuthorizeIcon}
InverseIcon={AuthorizeReversedIcon} InverseIcon={AuthorizeReversedIcon}
@ -595,7 +591,7 @@ const CustomerProfile = memo(() => {
)} )}
<ActionButton <ActionButton
color="primary" color="primary"
className={classes.actionButton} center
Icon={blocked ? AuthorizeIcon : BlockIcon} Icon={blocked ? AuthorizeIcon : BlockIcon}
InverseIcon={ InverseIcon={
blocked ? AuthorizeReversedIcon : BlockReversedIcon blocked ? AuthorizeReversedIcon : BlockReversedIcon
@ -612,11 +608,11 @@ const CustomerProfile = memo(() => {
</div> </div>
</div> </div>
<div> <div>
<Label1 className={classes.actionLabel}> <Label1 className="text-comet my-1">
{`Special user status`} {`Special user status`}
</Label1> </Label1>
<div className={classes.actionBar}> <div className="flex flex-col">
<div className={classes.userStatusAction}> <div className="flex items-center bg-zircon px-1 rounded-lg">
<Switch <Switch
checked={!!R.path(['isTestCustomer'])(customerData)} checked={!!R.path(['isTestCustomer'])(customerData)}
value={!!R.path(['isTestCustomer'])(customerData)} value={!!R.path(['isTestCustomer'])(customerData)}
@ -626,14 +622,14 @@ const CustomerProfile = memo(() => {
: enableTestCustomer() : enableTestCustomer()
} }
/> />
{`Test user`} <Label1 noMargin>Test user</Label1>
</div> </div>
</div> </div>
</div> </div>
</> </>
)} )}
</div> </div>
<div className={classes.rightSidePanel}> <div className="flex-1">
{isOverview && ( {isOverview && (
<div> <div>
<div className="flex justify-between mb-5"> <div className="flex justify-between mb-5">
@ -724,8 +720,6 @@ const RetrieveDataDialog = ({
error, error,
props props
}) => { }) => {
const classes = useStyles()
return ( return (
<Dialog <Dialog
open={open} open={open}
@ -739,31 +733,28 @@ const RetrieveDataDialog = ({
} }
}} }}
{...props}> {...props}>
<div className={classes.closeButton}> <div className="pt-4 pr-4 flex justify-end">
<IconButton <IconButton
size={16} size={32}
aria-label="close" aria-label="close"
onClick={() => onDismissed(false)}> onClick={() => onDismissed(false)}>
<CloseIcon /> <CloseIcon />
</IconButton> </IconButton>
</div> </div>
<H2 className={classes.dialogTitle}>{'Retrieve API data from Twilio'}</H2> <H2 className="mb-2 ml-10">{'Retrieve API data from Twilio'}</H2>
<DialogContent className={classes.dialogContent}> <DialogContent className="w-153 ml-4">
<Info3>{`With this action you'll be using Twilio's API to retrieve additional <Info3>{`With this action you'll be using Twilio's API to retrieve additional
data from this user. This includes name and address, if available.\n`}</Info3> data from this user. This includes name and address, if available.\n`}</Info3>
<Info3>{` There is a small cost from Twilio for each retrieval. Would you like <Info3>{` There is a small cost from Twilio for each retrieval. Would you like
to proceed?`}</Info3> to proceed?`}</Info3>
</DialogContent> </DialogContent>
{error && ( {error && (
<ErrorMessage className={classes.errorMessage}> <ErrorMessage className="ml-10">
Failed to fetch additional data Failed to fetch additional data
</ErrorMessage> </ErrorMessage>
)} )}
<DialogActions className={classes.dialogActions}> <DialogActions className="p-8 pt-4 gap-2">
<Button <Button backgroundColor="grey" onClick={() => onDismissed(false)}>
backgroundColor="grey"
className={classes.cancelButton}
onClick={() => onDismissed(false)}>
Cancel Cancel
</Button> </Button>
<Button <Button

View file

@ -1,81 +0,0 @@
import { comet, subheaderColor, spacer } from 'src/styling/variables'
export default {
labelLink: {
cursor: 'pointer',
color: comet
},
breadcrumbs: {
margin: [[20, 0]]
},
actionLabel: {
color: comet,
margin: [[4, 0]]
},
customerDetails: {
marginBottom: 18
},
actionButton: {
margin: [[0, 0, 4, 0]],
display: 'flex',
flexDirection: 'row',
justifyContent: 'center'
},
actionBar: {
display: 'flex',
flexDirection: 'column',
width: 219
},
panels: {
display: 'flex'
},
rightSidePanel: {
display: 'block',
width: 1100,
marginBottom: 25
},
leftSidePanel: {
width: 300,
'& > *': {
marginBottom: 25
},
'& > *:last-child': {
marginBottom: 0
},
'& > *:first-child': {
marginBottom: 50
}
},
userStatusAction: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
backgroundColor: subheaderColor,
borderRadius: 8,
padding: [[0, 5]]
},
closeButton: {
display: 'flex',
padding: [[spacer * 2, spacer * 2, 0, spacer * 2]],
paddingRight: spacer * 1.5,
justifyContent: 'end'
},
dialogTitle: {
margin: [[0, spacer * 2, spacer, spacer * 4 + spacer]]
},
dialogContent: {
width: 615,
marginLeft: 16
},
dialogActions: {
padding: spacer * 4,
paddingTop: spacer * 2
},
cancelButton: {
marginRight: 8,
padding: 0
},
errorMessage: {
marginLeft: 38
}
}

View file

@ -1,5 +1,4 @@
import { useQuery, useMutation, gql } from '@apollo/client' import { useQuery, useMutation, gql } from '@apollo/client'
import { makeStyles } from '@mui/styles'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useState } from 'react' import React, { useState } from 'react'
import { useHistory } from 'react-router-dom' import { useHistory } from 'react-router-dom'
@ -10,7 +9,6 @@ import TxInIcon from 'src/styling/icons/direction/cash-in.svg?react'
import TxOutIcon from 'src/styling/icons/direction/cash-out.svg?react' import TxOutIcon from 'src/styling/icons/direction/cash-out.svg?react'
import { Link } from 'src/components/buttons' import { Link } from 'src/components/buttons'
import baseStyles from 'src/pages/Logs.styles'
import { fromNamespace, namespaces } from 'src/utils/config' import { fromNamespace, namespaces } from 'src/utils/config'
import CustomersList from './CustomersList' import CustomersList from './CustomersList'
@ -93,13 +91,10 @@ const CREATE_CUSTOMER = gql`
} }
` `
const useBaseStyles = makeStyles(baseStyles)
const getFiltersObj = filters => const getFiltersObj = filters =>
R.reduce((s, f) => ({ ...s, [f.type]: f.value }), {}, filters) R.reduce((s, f) => ({ ...s, [f.type]: f.value }), {}, filters)
const Customers = () => { const Customers = () => {
const baseStyles = useBaseStyles()
const history = useHistory() const history = useHistory()
const handleCustomerClicked = customer => const handleCustomerClicked = customer =>
@ -210,7 +205,7 @@ const Customers = () => {
<TitleSection <TitleSection
title="Customers" title="Customers"
appendix={ appendix={
<div className={baseStyles.buttonsWrapper}> <div className="flex ml-4">
<SearchBox <SearchBox
loading={loadingFilters} loading={loadingFilters}
filters={filters} filters={filters}

View file

@ -1,4 +1,3 @@
import { makeStyles } from '@mui/styles'
import { format } from 'date-fns/fp' import { format } from 'date-fns/fp'
import * as R from 'ramda' import * as R from 'ramda'
import React from 'react' import React from 'react'
@ -7,11 +6,8 @@ import DataTable from 'src/components/tables/DataTable'
import TxInIcon from 'src/styling/icons/direction/cash-in.svg?react' import TxInIcon from 'src/styling/icons/direction/cash-in.svg?react'
import TxOutIcon from 'src/styling/icons/direction/cash-out.svg?react' import TxOutIcon from 'src/styling/icons/direction/cash-out.svg?react'
import styles from './CustomersList.styles'
import { getFormattedPhone, getName } from './helper' import { getFormattedPhone, getName } from './helper'
const useStyles = makeStyles(styles)
const CustomersList = ({ const CustomersList = ({
data, data,
locale, locale,
@ -20,8 +16,6 @@ const CustomersList = ({
triggers, triggers,
customRequests customRequests
}) => { }) => {
const classes = useStyles()
const elements = [ const elements = [
{ {
header: 'Phone/email', header: 'Phone/email',
@ -60,7 +54,7 @@ const CustomersList = ({
view: it => { view: it => {
const hasLastTx = !R.isNil(it.lastTxFiatCode) const hasLastTx = !R.isNil(it.lastTxFiatCode)
const LastTxIcon = it.lastTxClass === 'cashOut' ? TxOutIcon : TxInIcon const LastTxIcon = it.lastTxClass === 'cashOut' ? TxOutIcon : TxInIcon
const lastIcon = <LastTxIcon className={classes.txClassIconRight} /> const lastIcon = <LastTxIcon className="ml-3" />
return ( return (
<> <>
{hasLastTx && {hasLastTx &&

View file

@ -1,117 +0,0 @@
import typographyStyles from 'src/components/typography/styles'
import baseStyles from 'src/pages/Logs.styles'
import { zircon, comet, primaryColor, fontSize4 } from 'src/styling/variables'
const { label1 } = typographyStyles
const { titleWrapper, titleAndButtonsContainer } = baseStyles
export default {
titleWrapper,
titleAndButtonsContainer,
row: {
display: 'flex',
flexFlow: 'row nowrap'
},
rowSpaceBetween: {
display: 'flex',
flexFlow: 'row nowrap',
alignItems: 'center',
justifyContent: 'space-between'
},
column: {
display: 'flex',
flexFlow: 'column nowrap',
width: '100%',
height: '100%',
justifyContent: 'space-between'
},
textInput: {
width: 144
},
p: {
fontFamily: 'MuseoSans',
fontSize: fontSize4,
fontWeight: 500,
fontStretch: 'normal',
fontStyle: 'normal',
lineHeight: 1.14,
letterSpacing: 'normal',
color: primaryColor
},
txId: {
fontFamily: 'MuseoSans',
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis'
},
txClassIconLeft: {
marginRight: 11
},
txClassIconRight: {
marginLeft: 11
},
headerLabels: {
display: 'flex',
flexDirection: 'row',
'& div': {
display: 'flex',
alignItems: 'center'
},
'& > div:first-child': {
marginRight: 24
},
'& span': {
extend: label1,
marginLeft: 6
}
},
photo: {
width: 92,
height: 92,
borderRadius: 8,
backgroundColor: zircon,
margin: [[0, 28, 0, 0]],
alignItems: 'center',
justifyContent: 'center',
display: 'flex'
},
img: {
width: 80
},
customerName: {
marginBottom: 32
},
icon: {
marginRight: 11
},
name: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center'
},
value: {
height: 16
},
label: {
marginBottom: 4,
color: comet
},
txSummaryValue: {
height: 16,
marginRight: 25
},
txSummaryLabel: {
marginBottom: 4,
color: comet,
marginRight: 25
},
idIcon: {
marginRight: 10
},
subpageButton: {
marginLeft: 16
},
txTableSpacing: {
marginTop: 40
}
}

View file

@ -1,4 +1,3 @@
import { makeStyles } from '@mui/styles'
import { Form, Formik } from 'formik' import { Form, Formik } from 'formik'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useState, Fragment } from 'react' import React, { useState, Fragment } from 'react'
@ -7,7 +6,6 @@ import Modal from 'src/components/Modal'
import Stepper from 'src/components/Stepper' import Stepper from 'src/components/Stepper'
import { Button } from 'src/components/buttons' import { Button } from 'src/components/buttons'
import { comet } from 'src/styling/variables'
import { import {
entryType, entryType,
@ -20,43 +18,6 @@ import {
const LAST_STEP = 2 const LAST_STEP = 2
const styles = {
stepper: {
margin: [[16, 0, 14, 0]]
},
submit: {
display: 'flex',
flexDirection: 'row',
margin: [['auto', 0, 24]]
},
button: {
marginLeft: 'auto'
},
form: {
height: '100%',
display: 'flex',
flexDirection: 'column'
},
infoTitle: {
margin: [[18, 0, 20, 0]]
},
infoCurrentText: {
color: comet
},
blankSpace: {
padding: [[0, 30]],
margin: [[0, 4, 0, 2]],
borderBottom: `1px solid ${comet}`,
display: 'inline-block'
},
dropdownField: {
marginTop: 16,
minWidth: 155
}
}
const useStyles = makeStyles(styles)
const getStep = (step, selectedValues) => { const getStep = (step, selectedValues) => {
const elements = const elements =
selectedValues?.entryType === REQUIREMENT && selectedValues?.entryType === REQUIREMENT &&
@ -82,8 +43,6 @@ const Wizard = ({
addCustomerData, addCustomerData,
addPhoto addPhoto
}) => { }) => {
const classes = useStyles()
const [selectedValues, setSelectedValues] = useState(null) const [selectedValues, setSelectedValues] = useState(null)
const [{ step, config }, setState] = useState({ const [{ step, config }, setState] = useState({
@ -98,7 +57,7 @@ const Wizard = ({
const stepOptions = getStep(step, selectedValues) const stepOptions = getStep(step, selectedValues)
const onContinue = async it => { const onContinue = async it => {
const newConfig = R.merge(config, stepOptions.schema.cast(it)) const newConfig = R.mergeRight(config, stepOptions.schema.cast(it))
setSelectedValues(newConfig) setSelectedValues(newConfig)
if (isLastStep) { if (isLastStep) {
@ -135,11 +94,7 @@ const Wizard = ({
width={520} width={520}
height={520} height={520}
open={true}> open={true}>
<Stepper <Stepper steps={LAST_STEP} currentStep={step} className="my-4" />
className={classes.stepper}
steps={LAST_STEP}
currentStep={step}
/>
<Formik <Formik
validateOnBlur={false} validateOnBlur={false}
validateOnChange={false} validateOnChange={false}
@ -148,19 +103,19 @@ const Wizard = ({
initialValues={stepOptions.initialValues} initialValues={stepOptions.initialValues}
validationSchema={stepOptions.schema}> validationSchema={stepOptions.schema}>
{({ errors }) => ( {({ errors }) => (
<Form className={classes.form}> <Form className="h-full flex flex-col">
<stepOptions.Component <stepOptions.Component
selectedValues={selectedValues} selectedValues={selectedValues}
customInfoRequirementOptions={customInfoRequirementOptions} customInfoRequirementOptions={customInfoRequirementOptions}
errors={errors} errors={errors}
{...stepOptions.props} {...stepOptions.props}
/> />
<div className={classes.submit}> <div className="flex mt-auto mb-6">
{error && <ErrorMessage>Failed to save</ErrorMessage>} {error && <ErrorMessage>Failed to save</ErrorMessage>}
{Object.keys(errors).length > 0 && ( {Object.keys(errors).length > 0 && (
<ErrorMessage>{Object.values(errors)[0]}</ErrorMessage> <ErrorMessage>{Object.values(errors)[0]}</ErrorMessage>
)} )}
<Button className={classes.button} type="submit"> <Button className="ml-auto" type="submit">
{isLastStep ? 'Add Data' : 'Next'} {isLastStep ? 'Add Data' : 'Next'}
</Button> </Button>
</div> </div>

View file

@ -1,4 +1,3 @@
import { makeStyles } from '@mui/styles'
import { Field, Form, Formik } from 'formik' import { Field, Form, Formik } from 'formik'
import { parsePhoneNumberWithError } from 'libphonenumber-js' import { parsePhoneNumberWithError } from 'libphonenumber-js'
import * as R from 'ramda' import * as R from 'ramda'
@ -10,28 +9,6 @@ import * as Yup from 'yup'
import { Button } from 'src/components/buttons' import { Button } from 'src/components/buttons'
import { TextInput } from 'src/components/inputs/formik' import { TextInput } from 'src/components/inputs/formik'
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 getValidationSchema = countryCodes => const getValidationSchema = countryCodes =>
Yup.object().shape({ Yup.object().shape({
@ -39,7 +16,9 @@ const getValidationSchema = countryCodes =>
.required('A phone number is required') .required('A phone number is required')
.test('is-valid-number', 'That is not a valid phone number', value => { .test('is-valid-number', 'That is not a valid phone number', value => {
try { try {
return countryCodes.some(countryCode => parsePhoneNumberWithError(value, countryCode).isValid()) return countryCodes.some(countryCode =>
parsePhoneNumberWithError(value, countryCode).isValid()
)
} catch (e) { } catch (e) {
return false return false
} }
@ -60,8 +39,6 @@ const initialValues = {
phoneNumber: '' phoneNumber: ''
} }
const useStyles = makeStyles(styles)
const getErrorMsg = (formikErrors, formikTouched) => { const getErrorMsg = (formikErrors, formikTouched) => {
if (!formikErrors || !formikTouched) return null if (!formikErrors || !formikTouched) return null
if (formikErrors.phoneNumber && formikTouched.phoneNumber) if (formikErrors.phoneNumber && formikTouched.phoneNumber)
@ -70,8 +47,6 @@ const getErrorMsg = (formikErrors, formikTouched) => {
} }
const CreateCustomerModal = ({ showModal, handleClose, onSubmit, locale }) => { const CreateCustomerModal = ({ showModal, handleClose, onSubmit, locale }) => {
const classes = useStyles()
const possibleCountries = R.append( const possibleCountries = R.append(
locale?.country, locale?.country,
R.map(it => it.country, locale?.overrides ?? []) R.map(it => it.country, locale?.overrides ?? [])
@ -99,8 +74,10 @@ const CreateCustomerModal = ({ showModal, handleClose, onSubmit, locale }) => {
}) })
}}> }}>
{({ errors, touched }) => ( {({ errors, touched }) => (
<Form id="customer-registration-form" className={classes.form}> <Form
<H1 className={classes.modalTitle}>Create new customer</H1> id="customer-registration-form"
className="flex flex-col h-full">
<H1 className="-mt-2">Create new customer</H1>
<Field <Field
component={TextInput} component={TextInput}
name="phoneNumber" name="phoneNumber"
@ -108,14 +85,14 @@ const CreateCustomerModal = ({ showModal, handleClose, onSubmit, locale }) => {
autoFocus autoFocus
label="Phone number" label="Phone number"
/> />
<div className={classes.footer}> <div className="flex flex-row mt-auto mb-6">
{getErrorMsg(errors, touched) && ( {getErrorMsg(errors, touched) && (
<ErrorMessage>{getErrorMsg(errors, touched)}</ErrorMessage> <ErrorMessage>{getErrorMsg(errors, touched)}</ErrorMessage>
)} )}
<Button <Button
type="submit" type="submit"
form="customer-registration-form" form="customer-registration-form"
className={classes.submit}> className="ml-auto">
Finish Finish
</Button> </Button>
</div> </div>

View file

@ -1,193 +0,0 @@
import { useMutation, gql } from "@apollo/client";
import { makeStyles } from '@mui/styles'
import classnames from 'classnames'
import React, { useState } from 'react'
import Modal from 'src/components/Modal'
import { MainStatus } from 'src/components/Status'
import {
Table,
THead,
Th,
Tr,
Td,
TBody
} from 'src/components/fake-table/Table'
import { H3, Label1 } from 'src/components/typography'
import AuthorizeReversedIcon from 'src/styling/icons/button/authorize/white.svg?react'
import AuthorizeIcon from 'src/styling/icons/button/authorize/zodiac.svg?react'
import RejectReversedIcon from 'src/styling/icons/button/cancel/white.svg?react'
import RejectIcon from 'src/styling/icons/button/cancel/zodiac.svg?react'
import LinkIcon from 'src/styling/icons/month arrows/right.svg?react'
import { ActionButton } from 'src/components/buttons'
import { white, disabledColor } from 'src/styling/variables'
import DetailsCard from '../../Triggers/CustomInfoRequests/DetailsCard'
const styles = {
white: {
color: white
},
actionButton: {
display: 'flex',
height: 28,
marginRight: 'auto'
},
flex: {
display: 'flex'
},
disabledBtn: {
backgroundColor: disabledColor,
'&:hover': {
backgroundColor: disabledColor
}
},
linkIcon: {
marginTop: 12,
marginLeft: 4,
cursor: 'pointer'
}
}
const SET_AUTHORIZED_REQUEST = gql`
mutation setAuthorizedCustomRequest(
$customerId: ID!
$infoRequestId: ID!
$override: String!
) {
setAuthorizedCustomRequest(
customerId: $customerId
infoRequestId: $infoRequestId
override: $override
)
}
`
const useStyles = makeStyles(styles)
const CustomInfoRequestsData = ({ data }) => {
const classes = useStyles()
const [toView, setToView] = useState(null)
const [setAuthorized] = useMutation(SET_AUTHORIZED_REQUEST, {
onError: () => console.error('Error while clearing notification'),
refetchQueries: () => ['customer']
})
const authorize = it =>
setAuthorized({
variables: {
customerId: it.customerId,
infoRequestId: it.customInfoRequest.id,
isAuthorized: true
}
})
const reject = it =>
setAuthorized({
variables: {
customerId: it.customerId,
infoRequestId: it.customInfoRequest.id,
isAuthorized: false
}
})
const getBtnClasses = (it, isAuthorize) => {
return {
[classes.actionButton]: true,
[classes.disabledBtn]:
(isAuthorize && it.approved === true) ||
(!isAuthorize && it.approved === false)
}
}
const AuthorizeButton = it => (
<ActionButton
className={classnames(getBtnClasses(it, true))}
color="secondary"
Icon={AuthorizeIcon}
InverseIcon={AuthorizeReversedIcon}
onClick={() => authorize(it)}>
Authorize
</ActionButton>
)
const RejectButton = it => (
<ActionButton
className={classnames(getBtnClasses(it, false))}
color="secondary"
Icon={RejectIcon}
InverseIcon={RejectReversedIcon}
onClick={() => reject(it)}>
Reject
</ActionButton>
)
const getActionButtons = it => {
return (
<>
{AuthorizeButton(it)}
{RejectButton(it)}
</>
)
}
const getAuthorizedStatus = it =>
it.approved === null
? { label: 'Pending', type: 'neutral' }
: it.approved === false
? { label: 'Rejected', type: 'error' }
: { label: 'Accepted', type: 'success' }
return (
<>
<H3>Custom Info Requests Data</H3>
<div>
<Table>
<THead>
<Th width={250}>Custom Request Name</Th>
<Th width={500}>Custom Request Data</Th>
<Th width={200}>Status</Th>
<Th width={250} textAlign="center">
Actions
</Th>
</THead>
<TBody>
{data.map((it, idx) => (
<React.Fragment key={idx}>
<Tr>
<Td size="sm" width={250}>
<div className={classes.flex}>
<Label1>{it.customInfoRequest.customRequest.name}</Label1>
<div onClick={() => setToView(it)}>
<LinkIcon className={classes.linkIcon} />
</div>
</div>
</Td>
<Td size="sm" width={500}>
<div>{JSON.stringify(it.customerData.data, null, 2)}</div>
</Td>
<Td size="sm" width={200}>
<MainStatus statuses={[getAuthorizedStatus(it)]} />
</Td>
<Td size="sm" width={250}>
<div className={classes.flex}>{getActionButtons(it)}</div>
</Td>
</Tr>
</React.Fragment>
))}
</TBody>
</Table>
{toView && (
<Modal
width={900}
height={400}
open={true}
handleClose={() => setToView(null)}>
<H3>Custom Information Request Details</H3>
<DetailsCard it={{ ...toView.customInfoRequest }} />
</Modal>
)}
</div>
</>
)
}
export default CustomInfoRequestsData

View file

@ -1,19 +1,13 @@
import { makeStyles } from '@mui/styles'
import * as R from 'ramda' import * as R from 'ramda'
import React, { memo } from 'react' import React, { memo } from 'react'
import { H2, Label1, P } from 'src/components/typography' import { H2, Label1, P } from 'src/components/typography'
import IdIcon from 'src/styling/icons/ID/card/zodiac.svg?react' import IdIcon from 'src/styling/icons/ID/card/zodiac.svg?react'
import mainStyles from '../CustomersList.styles'
import { getFormattedPhone, getName } from '../helper' import { getFormattedPhone, getName } from '../helper'
import PhotosCard from './PhotosCard' import PhotosCard from './PhotosCard'
const useStyles = makeStyles(mainStyles)
const CustomerDetails = memo(({ customer, photosData, locale, timezone }) => { const CustomerDetails = memo(({ customer, photosData, locale, timezone }) => {
const classes = useStyles()
const idNumber = R.path(['idCardData', 'documentNumber'])(customer) const idNumber = R.path(['idCardData', 'documentNumber'])(customer)
const usSsn = R.path(['usSsn'])(customer) const usSsn = R.path(['usSsn'])(customer)
const name = getName(customer) const name = getName(customer)
@ -50,11 +44,11 @@ const CustomerDetails = memo(({ customer, photosData, locale, timezone }) => {
}) })
return ( return (
<div className="flex"> <div className="flex gap-7">
<PhotosCard photosData={photosData} timezone={timezone} /> <PhotosCard photosData={photosData} timezone={timezone} />
<div className="flex flex-col"> <div className="flex flex-col">
<div className={classes.name}> <div className="flex items-center gap-2">
<IdIcon className={classes.idIcon} /> <IdIcon />
<H2 noMargin> <H2 noMargin>
{name.length {name.length
? name ? name
@ -68,7 +62,7 @@ const CustomerDetails = memo(({ customer, photosData, locale, timezone }) => {
<Label1 <Label1
noMargin noMargin
key={idx} key={idx}
className={classes.label} className="mb-1 text-comet"
style={{ width: size }}> style={{ width: size }}>
{header} {header}
</Label1> </Label1>
@ -76,11 +70,7 @@ const CustomerDetails = memo(({ customer, photosData, locale, timezone }) => {
</div> </div>
<div className="flex"> <div className="flex">
{elements.map(({ size, value }, idx) => ( {elements.map(({ size, value }, idx) => (
<P <P noMargin key={idx} style={{ width: size }}>
noMargin
key={idx}
className={classes.value}
style={{ width: size }}>
{value} {value}
</P> </P>
))} ))}

View file

@ -1,4 +1,3 @@
import { makeStyles } from '@mui/styles'
import classnames from 'classnames' import classnames from 'classnames'
import React from 'react' import React from 'react'
import CustomerDataReversedIcon from 'src/styling/icons/customer-nav/data/comet.svg?react' import CustomerDataReversedIcon from 'src/styling/icons/customer-nav/data/comet.svg?react'
@ -10,12 +9,9 @@ import OverviewIcon from 'src/styling/icons/customer-nav/overview/white.svg?reac
import PhotosReversedIcon from 'src/styling/icons/customer-nav/photos/comet.svg?react' import PhotosReversedIcon from 'src/styling/icons/customer-nav/photos/comet.svg?react'
import Photos from 'src/styling/icons/customer-nav/photos/white.svg?react' import Photos from 'src/styling/icons/customer-nav/photos/white.svg?react'
import styles from './CustomerSidebar.styles' import { P } from '/src/components/typography/index.jsx'
const useStyles = makeStyles(styles)
const CustomerSidebar = ({ isSelected, onClick }) => { const CustomerSidebar = ({ isSelected, onClick }) => {
const classes = useStyles()
const sideBarOptions = [ const sideBarOptions = [
{ {
code: 'overview', code: 'overview',
@ -44,19 +40,24 @@ const CustomerSidebar = ({ isSelected, onClick }) => {
] ]
return ( return (
<div className={classes.sidebar}> <div className="flex flex-col rounded-sm w-55 bg-zircon overflow-hidden">
{sideBarOptions?.map(({ Icon, InverseIcon, display, code }, idx) => ( {sideBarOptions?.map(({ Icon, InverseIcon, display, code }, idx) => (
<div <div
key={idx} key={idx}
className={classnames({ className={classnames({
[classes.activeLink]: isSelected(code), 'gap-4 p-4 cursor-pointer flex items-center': true,
[classes.link]: true 'bg-comet2': isSelected(code)
})} })}
onClick={() => onClick(code)}> onClick={() => onClick(code)}>
<div className={classes.icon}> {isSelected(code) ? <Icon /> : <InverseIcon />}
{isSelected(code) ? <Icon /> : <InverseIcon />} <P
</div> noMargin
{display} className={classnames({
'text-comet2': true,
'text-white font-bold': isSelected(code)
})}>
{display}
</P>
</div> </div>
))} ))}
</div> </div>

View file

@ -1,41 +0,0 @@
import typographyStyles from 'src/components/typography/styles'
import { zircon, offDarkColor, white } from 'src/styling/variables'
const { tl2, p } = typographyStyles
const sidebarColor = zircon
export default {
sidebar: {
display: 'flex',
backgroundColor: sidebarColor,
width: 219,
flexDirection: 'column',
borderRadius: 5
},
link: {
alignItems: 'center',
display: 'flex',
extend: p,
position: 'relative',
color: offDarkColor,
padding: 15,
cursor: 'pointer'
},
activeLink: {
display: 'flex',
alignItems: 'center',
extend: tl2,
color: white,
backgroundColor: offDarkColor,
'&:first-child': {
borderRadius: [[5, 5, 0, 0]]
},
'&:last-child': {
borderRadius: [[0, 0, 5, 5]]
}
},
icon: {
marginRight: 15
}
}

View file

@ -1,18 +1,13 @@
import CardContent from '@mui/material/CardContent' import CardContent from '@mui/material/CardContent'
import Card from '@mui/material/Card' import Card from '@mui/material/Card'
import Grid from '@mui/material/Grid'
import { makeStyles } from '@mui/styles'
import classnames from 'classnames' import classnames from 'classnames'
import { Form, Formik, Field as FormikField } from 'formik' import { Form, Formik, Field as FormikField } from 'formik'
import * as R from 'ramda' import * as R from 'ramda'
import { useState, React, useRef } from 'react' import { useState, React, useRef } from 'react'
// import { HelpTooltip } from 'src/components/Tooltip'
import ErrorMessage from 'src/components/ErrorMessage' import ErrorMessage from 'src/components/ErrorMessage'
import PromptWhenDirty from 'src/components/PromptWhenDirty' import PromptWhenDirty from 'src/components/PromptWhenDirty'
import { MainStatus } from 'src/components/Status' import { MainStatus } from 'src/components/Status'
import { Label1, P, H3 } from 'src/components/typography' import { Label1, P, H3 } from 'src/components/typography'
import DeleteIcon from 'src/styling/icons/action/delete/enabled.svg?react'
import DeleteReversedIcon from 'src/styling/icons/action/delete/white.svg?react'
import EditIcon from 'src/styling/icons/action/edit/enabled.svg?react' import EditIcon from 'src/styling/icons/action/edit/enabled.svg?react'
import EditReversedIcon from 'src/styling/icons/action/edit/white.svg?react' import EditReversedIcon from 'src/styling/icons/action/edit/white.svg?react'
import AuthorizeIcon from 'src/styling/icons/button/authorize/white.svg?react' import AuthorizeIcon from 'src/styling/icons/button/authorize/white.svg?react'
@ -25,84 +20,34 @@ import SaveReversedIcon from 'src/styling/icons/circle buttons/save/white.svg?re
import { ActionButton } from 'src/components/buttons' import { ActionButton } from 'src/components/buttons'
import { import {
OVERRIDE_AUTHORIZED,
OVERRIDE_REJECTED, OVERRIDE_REJECTED,
OVERRIDE_PENDING OVERRIDE_PENDING
} from 'src/pages/Customers/components/propertyCard' } from 'src/pages/Customers/components/consts'
import { comet } from 'src/styling/variables'
import styles from './EditableCard.styles' const ReadOnlyField = ({ field, value }) => {
return (
const useStyles = makeStyles(styles) <div className="h-12">
<Label1 noMargin className="text-comet">
const fieldStyles = { {field.label}
field: { </Label1>
position: 'relative', <P noMargin className="overflow-hidden whitespace-nowrap text-ellipsis">
width: 280, {value}
height: 48, </P>
padding: [[0, 4, 4, 0]], </div>
marginTop: 2 )
},
label: {
color: comet,
margin: [[0, 0, 0, 0]]
},
notEditing: {
display: 'flex',
flexDirection: 'column',
'& > p:first-child': {
height: 16,
lineHeight: '16px',
transformOrigin: 'left',
paddingLeft: 0,
margin: [[3, 0, 3, 0]]
},
'& > p:last-child': {
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
margin: 0
}
},
editing: {
'& > div': {
'& > input': {
padding: 0,
fontSize: 14
}
}
},
readOnlyLabel: {
color: comet,
margin: [[3, 0, 3, 0]]
},
readOnlyValue: {
margin: 0
}
} }
const fieldUseStyles = makeStyles(fieldStyles)
const EditableField = ({ editing, field, value, size, ...props }) => { const EditableField = ({ editing, field, value, size, ...props }) => {
const classes = fieldUseStyles() if (!editing) return <ReadOnlyField field={field} value={value} />
const classNames = {
[classes.field]: true,
[classes.notEditing]: !editing
}
return ( return (
<div className={classnames(classNames)}> <div className="h-12">
{!editing && (
<>
<Label1 className={classes.label}>{field.label}</Label1>
<P>{value}</P>
</>
)}
{editing && ( {editing && (
<> <>
<Label1 className={classes.label}>{field.label}</Label1> <Label1 noMargin className="text-comet">
{field.label}
</Label1>
<FormikField <FormikField
className={classes.editing} inputClasses="p-0 text-sm -mt-1"
id={field.name} id={field.name}
name={field.name} name={field.name}
component={field.component} component={field.component}
@ -116,23 +61,6 @@ const EditableField = ({ editing, field, value, size, ...props }) => {
) )
} }
const ReadOnlyField = ({ field, value, ...props }) => {
const classes = fieldUseStyles()
const classNames = {
[classes.field]: true,
[classes.notEditing]: true
}
return (
<>
<div className={classnames(classNames)}>
<Label1 className={classes.readOnlyLabel}>{field.label}</Label1>
<P className={classes.readOnlyValue}>{value}</P>
</div>
</>
)
}
const EditableCard = ({ const EditableCard = ({
fields, fields,
save = () => {}, save = () => {},
@ -152,8 +80,6 @@ const EditableCard = ({
editable, editable,
checkAgainstSanctions checkAgainstSanctions
}) => { }) => {
const classes = useStyles()
const formRef = useRef() const formRef = useRef()
const [editing, setEditing] = useState(false) const [editing, setEditing] = useState(false)
@ -162,12 +88,6 @@ const EditableCard = ({
const triggerInput = () => input.click() const triggerInput = () => input.click()
const label1ClassNames = {
[classes.label1]: true,
[classes.label1Pending]: state === OVERRIDE_PENDING,
[classes.label1Rejected]: state === OVERRIDE_REJECTED,
[classes.label1Accepted]: state === OVERRIDE_AUTHORIZED
}
const authorized = const authorized =
state === OVERRIDE_PENDING state === OVERRIDE_PENDING
? { label: 'Pending', type: 'neutral' } ? { label: 'Pending', type: 'neutral' }
@ -176,247 +96,211 @@ const EditableCard = ({
: { label: 'Accepted', type: 'success' } : { label: 'Accepted', type: 'success' }
return ( return (
<div> <Card className="rounded-xl">
<Card className={classes.card}> <CardContent>
<CardContent> <div className="flex justify-between h-10">
<div className={classes.headerWrapper}> <div className="flex mb-4 gap-4">
<div className={classes.cardHeader}> {titleIcon}
{titleIcon} <H3 noMargin>{title}</H3>
<H3 className={classes.cardTitle}>{title}</H3>
{
// TODO: Enable for next release
/* <HelpTooltip width={304}></HelpTooltip> */
}
</div>
{state && authorize && (
<div className={classnames(label1ClassNames)}>
<MainStatus statuses={[authorized]} />
</div>
)}
</div> </div>
{children(formRef.current?.values ?? {})} {state && authorize && <MainStatus statuses={[authorized]} />}
<Formik </div>
innerRef={formRef} {children(formRef.current?.values ?? {})}
validateOnBlur={false} <Formik
validateOnChange={false} innerRef={formRef}
enableReinitialize validateOnBlur={false}
validationSchema={validationSchema} validateOnChange={false}
initialValues={initialValues} enableReinitialize
onSubmit={values => { validationSchema={validationSchema}
save(values) initialValues={initialValues}
setEditing(false) onSubmit={values => {
}} save(values)
onReset={() => { setEditing(false)
setEditing(false) }}
setError(false) onReset={() => {
}}> setEditing(false)
{({ setFieldValue }) => ( setError(false)
<Form> }}>
<PromptWhenDirty /> {({ setFieldValue }) => (
<div className={classes.row}> <Form>
<Grid container> <PromptWhenDirty />
<Grid container direction="column" item xs={6}> <div className="flex">
{!hasImage && <div className="flex flex-col w-1/2">
fields?.map((field, idx) => { {!hasImage &&
return idx >= 0 && idx < 4 ? ( fields?.map((field, idx) => {
!field.editable ? ( return idx >= 0 && idx < 4 ? (
<ReadOnlyField !field.editable ? (
field={field} <ReadOnlyField
value={initialValues[field.name]} field={field}
/> value={initialValues[field.name]}
) : ( />
<EditableField ) : (
field={field} <EditableField
value={initialValues[field.name]} field={field}
editing={editing} value={initialValues[field.name]}
size={180} editing={editing}
/> size={180}
) />
) : null )
})} ) : null
</Grid> })}
<Grid container direction="column" item xs={6}>
{!hasImage &&
fields?.map((field, idx) => {
return idx >= 4 ? (
!field.editable ? (
<ReadOnlyField
field={field}
value={initialValues[field.name]}
/>
) : (
<EditableField
field={field}
value={initialValues[field.name]}
editing={editing}
size={180}
/>
)
) : null
})}
</Grid>
</Grid>
</div> </div>
<div className={classes.edit}> <div className="flex flex-col w-1/2">
{!editing && ( {!hasImage &&
<div className={classes.editButton}> fields?.map((field, idx) => {
<div className={classes.deleteButton}> return idx >= 4 ? (
{/*{false && (*/} !field.editable ? (
{/* <ActionButton*/} <ReadOnlyField
{/* color="primary"*/} field={field}
{/* type="button"*/} value={initialValues[field.name]}
{/* Icon={DeleteIcon}*/} />
{/* InverseIcon={DeleteReversedIcon}*/} ) : (
{/* onClick={() => deleteEditedData()}>*/} <EditableField
{/* Delete*/} field={field}
{/* </ActionButton>*/} value={initialValues[field.name]}
{/*)}*/} editing={editing}
{!hasAdditionalData && ( size={180}
<ActionButton />
color="primary" )
type="button" ) : null
Icon={DataIcon} })}
InverseIcon={DataReversedIcon} </div>
onClick={() => retrieveAdditionalData()}> </div>
Retrieve API data <div className="flex justify-end mt-5 gap-2">
</ActionButton> {!editing && (
)} <>
{checkAgainstSanctions && ( {!hasAdditionalData && (
<ActionButton <ActionButton
color="primary" color="primary"
type="button" type="button"
Icon={DataIcon} Icon={DataIcon}
InverseIcon={DataReversedIcon} InverseIcon={DataReversedIcon}
onClick={() => checkAgainstSanctions()}> onClick={() => retrieveAdditionalData()}>
Check against OFAC sanction list Retrieve API data
</ActionButton> </ActionButton>
)} )}
</div> {checkAgainstSanctions && (
{editable && ( <ActionButton
color="primary"
type="button"
Icon={DataIcon}
InverseIcon={DataReversedIcon}
onClick={() => checkAgainstSanctions()}>
Check against OFAC sanction list
</ActionButton>
)}
{editable && (
<ActionButton
color="primary"
Icon={EditIcon}
InverseIcon={EditReversedIcon}
onClick={() => setEditing(true)}>
Edit
</ActionButton>
)}
{!editable &&
authorize &&
authorized.label !== 'Accepted' && (
<ActionButton <ActionButton
color="primary" color="spring"
Icon={EditIcon} type="button"
InverseIcon={EditReversedIcon} Icon={AuthorizeIcon}
onClick={() => setEditing(true)}> InverseIcon={AuthorizeIcon}
Edit onClick={() => authorize()}>
Authorize
</ActionButton> </ActionButton>
)} )}
{!editable && {!editable &&
authorize && authorize &&
authorized.label !== 'Accepted' && ( authorized.label !== 'Rejected' && (
<div className={classes.button}> <ActionButton
<ActionButton color="tomato"
color="spring" type="button"
type="button" Icon={BlockIcon}
Icon={AuthorizeIcon} InverseIcon={BlockIcon}
InverseIcon={AuthorizeIcon} onClick={() => reject()}>
onClick={() => authorize()}> Reject
Authorize </ActionButton>
</ActionButton> )}
</>
)}
{editing && (
<>
{hasImage && state !== OVERRIDE_PENDING && (
<ActionButton
color="secondary"
type="button"
Icon={ReplaceReversedIcon}
InverseIcon={ReplaceReversedIcon}
onClick={() => triggerInput()}>
{
<div>
<input
type="file"
alt=""
accept="image/*"
className="hidden"
ref={fileInput => setInput(fileInput)}
onChange={event => {
// need to store it locally if we want to display it even after saving to db
const file = R.head(event.target.files)
if (!file) return
setFieldValue(R.head(fields).name, file)
}}
/>
Replace
</div> </div>
)} }
{!editable && </ActionButton>
authorize && )}
authorized.label !== 'Rejected' && ( {fields && (
<ActionButton <ActionButton
color="tomato" color="secondary"
type="button" Icon={SaveReversedIcon}
Icon={BlockIcon} InverseIcon={SaveReversedIcon}
InverseIcon={BlockIcon} type="submit">
onClick={() => reject()}> Save
Reject </ActionButton>
</ActionButton> )}
)} <ActionButton
</div> color="secondary"
)} Icon={CancelReversedIcon}
{editing && ( InverseIcon={CancelReversedIcon}
<div className={classes.editingWrapper}> onClick={() => cancel()}
<div className={classes.replace}> type="reset">
{hasImage && state !== OVERRIDE_PENDING && ( Cancel
<ActionButton </ActionButton>
color="secondary" {authorize && authorized.label !== 'Accepted' && (
type="button" <ActionButton
Icon={ReplaceReversedIcon} color="spring"
InverseIcon={ReplaceReversedIcon} type="button"
onClick={() => triggerInput()}> Icon={AuthorizeIcon}
{ InverseIcon={AuthorizeIcon}
<div> onClick={() => authorize()}>
<input Authorize
type="file" </ActionButton>
alt="" )}
accept="image/*" {authorize && authorized.label !== 'Rejected' && (
className={classes.input} <ActionButton
ref={fileInput => setInput(fileInput)} color="tomato"
onChange={event => { type="button"
// need to store it locally if we want to display it even after saving to db Icon={BlockIcon}
const file = R.head(event.target.files) InverseIcon={BlockIcon}
if (!file) return onClick={() => reject()}>
setFieldValue(R.head(fields).name, file) Reject
}} </ActionButton>
/> )}
Replace {error && (
</div> <ErrorMessage>Failed to save changes</ErrorMessage>
} )}
</ActionButton> </>
)} )}
</div> </div>
<div className={classes.editingButtons}> </Form>
{fields && ( )}
<div className={classes.button}> </Formik>
<ActionButton </CardContent>
color="secondary" </Card>
Icon={SaveReversedIcon}
InverseIcon={SaveReversedIcon}
type="submit">
Save
</ActionButton>
</div>
)}
<div className={classes.button}>
<ActionButton
color="secondary"
Icon={CancelReversedIcon}
InverseIcon={CancelReversedIcon}
onClick={() => cancel()}
type="reset">
Cancel
</ActionButton>
</div>
{authorize && authorized.label !== 'Accepted' && (
<div className={classes.button}>
<ActionButton
color="spring"
type="button"
Icon={AuthorizeIcon}
InverseIcon={AuthorizeIcon}
onClick={() => authorize()}>
Authorize
</ActionButton>
</div>
)}
{authorize && authorized.label !== 'Rejected' && (
<ActionButton
color="tomato"
type="button"
Icon={BlockIcon}
InverseIcon={BlockIcon}
onClick={() => reject()}>
Reject
</ActionButton>
)}
{error && (
<ErrorMessage>Failed to save changes</ErrorMessage>
)}
</div>
</div>
)}
</div>
</Form>
)}
</Formik>
</CardContent>
</Card>
</div>
) )
} }

View file

@ -1,70 +0,0 @@
import { tomato, spring4, comet } from 'src/styling/variables'
export default {
label1: {
display: 'flex',
width: 85,
justifyContent: 'right'
},
label1Pending: {
color: comet
},
label1Rejected: {
color: tomato
},
label1Accepted: {
color: spring4
},
editButton: {
marginTop: 20,
display: 'flex',
justifyContent: 'right'
},
deleteButton: {
marginRight: 8
},
headerWrapper: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
height: 40
},
editingWrapper: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 20
},
replace: {
marginRight: 5
},
input: {
display: 'none'
},
button: {
marginRight: 5
},
editingButtons: {
display: 'flex',
justifyContent: 'right'
},
card: {
borderRadius: 10,
marginRight: 15,
marginBottom: 15
},
cardHeader: {
display: 'flex',
flexDirection: 'row',
marginBottom: 15
},
editIcon: {
marginTop: 5
},
cardIcon: {
marginTop: 7
},
cardTitle: {
margin: [[8, 15, 15, 15]]
}
}

View file

@ -1,38 +0,0 @@
import { makeStyles } from '@mui/styles'
import classnames from 'classnames'
import React, { memo } from 'react'
import { Info3, Label1 } from 'src/components/typography'
import { comet } from 'src/styling/variables'
const useStyles = makeStyles({
field: {
height: 46
},
label: {
color: comet,
margin: [[0, 3]]
},
value: {
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
margin: 0,
paddingLeft: 4
}
})
const Field = memo(({ label, display, size, className }) => {
const classes = useStyles()
return (
<div
className={classnames(classes.field, className)}
style={{ width: size }}>
<Label1 className={classes.label}>{label}</Label1>
<Info3 className={classes.value}>{display}</Info3>
</div>
)
})
export default Field

View file

@ -1,28 +0,0 @@
import Paper from '@mui/material/Paper'
import { makeStyles } from '@mui/styles'
import React, { memo } from 'react'
import CrossedCameraIcon from 'src/styling/icons/ID/photo/crossed-camera.svg?react'
import mainStyles from '../CustomersList.styles'
const useStyles = makeStyles(mainStyles)
const FrontCameraPhoto = memo(({ frontCameraPath }) => {
const classes = useStyles()
return (
<Paper className={classes.photo} elevation={0}>
{frontCameraPath ? (
<img
className={classes.img}
src={`/front-camera-photo/${frontCameraPath}`}
alt=""
/>
) : (
<CrossedCameraIcon />
)}
</Paper>
)
})
export default FrontCameraPhoto

View file

@ -1,52 +0,0 @@
import { makeStyles } from '@mui/styles'
import * as R from 'ramda'
import React, { memo } from 'react'
import CrossedCameraIcon from 'src/styling/icons/ID/photo/crossed-camera.svg?react'
import {
PropertyCard,
OVERRIDE_AUTHORIZED,
OVERRIDE_REJECTED
} from 'src/pages/Customers/components/propertyCard'
const useStyles = makeStyles({
idCardPhotoCard: {
width: 325,
height: 240,
margin: [[32, 0, 0, 0]]
},
idCardPhoto: {
maxHeight: 130
},
field: {
marginLeft: 14
}
})
const IdCardPhotoCard = memo(({ customerData, updateCustomer }) => {
const classes = useStyles()
return (
<PropertyCard
title={'ID card image'}
state={R.path(['idCardPhotoOverride'])(customerData)}
authorize={() =>
updateCustomer({ idCardPhotoOverride: OVERRIDE_AUTHORIZED })
}
reject={() => updateCustomer({ idCardPhotoOverride: OVERRIDE_REJECTED })}>
<div className="flex flex-1 justify-center items-center">
{customerData.idCardPhotoPath ? (
<img
className={classes.idCardPhoto}
src={`/id-card-photo/${R.path(['idCardPhotoPath'])(customerData)}`}
alt=""
/>
) : (
<CrossedCameraIcon />
)}
</div>
</PropertyCard>
)
})
export default IdCardPhotoCard

View file

@ -1,88 +0,0 @@
import { differenceInYears, format, parse } from 'date-fns/fp'
import * as R from 'ramda'
import React, { memo } from 'react'
import {
PropertyCard,
OVERRIDE_AUTHORIZED,
OVERRIDE_REJECTED
} from 'src/pages/Customers/components/propertyCard'
import { ifNotNull } from 'src/utils/nullCheck'
import { getName } from '../helper'
import Field from './Field'
const IdDataCard = memo(({ customerData, updateCustomer }) => {
const idData = R.path(['idCardData'])(customerData)
const rawExpirationDate = R.path(['expirationDate'])(idData)
const country = R.path(['country'])(idData)
const rawDob = R.path(['dateOfBirth'])(idData)
const elements = [
{
header: 'Name',
display: `${getName(customerData)}`,
size: 190
},
{
header: 'ID number',
display: R.path(['documentNumber'])(idData),
size: 160
},
{
header: 'Birth date',
display:
(rawDob &&
format('yyyy-MM-dd')(parse(new Date(), 'yyyyMMdd', rawDob))) ??
'',
size: 110
},
{
header: 'Age',
display:
(rawDob &&
differenceInYears(
parse(new Date(), 'yyyyMMdd', rawDob),
new Date()
)) ??
'',
size: 50
},
{
header: 'Gender',
display: R.path(['gender'])(idData) ?? R.path(['sex'])(idData),
size: 80
},
{
header: country === 'Canada' ? 'Province' : 'State',
display: R.path(['state'])(idData),
size: 120
},
{
header: 'Expiration date',
display: ifNotNull(
rawExpirationDate,
format('yyyy-MM-dd', rawExpirationDate)
)
}
]
return (
<PropertyCard
title={'ID data'}
state={R.path(['idCardDataOverride'])(customerData)}
authorize={() =>
updateCustomer({ idCardDataOverride: OVERRIDE_AUTHORIZED })
}
reject={() => updateCustomer({ idCardDataOverride: OVERRIDE_REJECTED })}>
<div className="flex items-center">
{elements.map(({ header, display, size }, idx) => (
<Field key={idx} label={header} display={display} size={size} />
))}
</div>
</PropertyCard>
)
})
export default IdDataCard

View file

@ -1,36 +0,0 @@
import { parsePhoneNumberFromString } from 'libphonenumber-js'
import * as R from 'ramda'
import React, { memo } from 'react'
import {
PropertyCard,
OVERRIDE_AUTHORIZED,
OVERRIDE_REJECTED
} from 'src/pages/Customers/components/propertyCard'
import Field from './Field'
const PhoneCard = memo(
({ className, customerData, updateCustomer, locale }) => (
<PropertyCard
className={className}
title={'Phone nº'}
state={R.path(['smsOverride'])(customerData)}
authorize={() => updateCustomer({ smsOverride: OVERRIDE_AUTHORIZED })}
reject={() => updateCustomer({ smsOverride: OVERRIDE_REJECTED })}>
<Field
label={'Phone'}
display={
customerData.phone && locale.country
? parsePhoneNumberFromString(
customerData.phone,
locale.country
).formatInternational()
: ''
}
/>
</PropertyCard>
)
)
export default PhoneCard

View file

@ -1,20 +1,14 @@
import ButtonBase from '@mui/material/ButtonBase' import ButtonBase from '@mui/material/ButtonBase'
import Paper from '@mui/material/Card' import Paper from '@mui/material/Card'
import { makeStyles } from '@mui/styles'
import * as R from 'ramda' import * as R from 'ramda'
import React, { memo, useState } from 'react' import React, { memo, useState } from 'react'
import { InformativeDialog } from 'src/components/InformativeDialog' import { InformativeDialog } from 'src/components/InformativeDialog'
import { Info2 } from 'src/components/typography' import { Info2 } from 'src/components/typography'
import CrossedCameraIcon from 'src/styling/icons/ID/photo/crossed-camera.svg?react' import CrossedCameraIcon from 'src/styling/icons/ID/photo/crossed-camera.svg?react'
import styles from './PhotosCard.styles'
import PhotosCarousel from './PhotosCarousel' import PhotosCarousel from './PhotosCarousel'
const useStyles = makeStyles(styles)
const PhotosCard = memo(({ photosData, timezone }) => { const PhotosCard = memo(({ photosData, timezone }) => {
const classes = useStyles()
const [photosDialog, setPhotosDialog] = useState(false) const [photosDialog, setPhotosDialog] = useState(false)
const sortedPhotosData = R.sortWith( const sortedPhotosData = R.sortWith(
@ -26,24 +20,24 @@ const PhotosCard = memo(({ photosData, timezone }) => {
return ( return (
<> <>
<Paper className={classes.photo} elevation={0}> <Paper
className="flex justify-center items-center bg-zircon rounded-lg h-34 w-34"
elevation={0}>
<ButtonBase <ButtonBase
disabled={!singlePhoto} disabled={!singlePhoto}
className={classes.button}
onClick={() => { onClick={() => {
setPhotosDialog(true) setPhotosDialog(true)
}}> }}>
{singlePhoto ? ( {singlePhoto ? (
<div className={classes.container}> <div>
<img <img
className={classes.img} className="w-34 h-34 object-center object-cover block"
src={`/${singlePhoto.photoDir}/${singlePhoto.path}`} src={`/${singlePhoto.photoDir}/${singlePhoto.path}`}
alt="" alt=""
/> />
<circle className={classes.circle}> <div className=""></div>
<div> <circle className="absolute top-0 right-0 mr-1 mt-1 bg-ghost rounded-full w-6 h-6 flex items-center justify-center">
<Info2>{sortedPhotosData.length}</Info2> <Info2>{sortedPhotosData.length}</Info2>
</div>
</circle> </circle>
</div> </div>
) : ( ) : (

View file

@ -1,42 +0,0 @@
import { zircon, backgroundColor } from 'src/styling/variables'
export default {
photo: {
width: 135,
height: 135,
borderRadius: 8,
backgroundColor: zircon,
margin: [[0, 28, 0, 0]],
alignItems: 'center',
justifyContent: 'center',
display: 'flex'
},
img: {
objectFit: 'cover',
objectPosition: 'center',
width: 135,
height: 135
},
container: {
position: 'relative',
'& > img': {
display: 'block'
},
'& > circle': {
position: 'absolute',
top: '0',
right: '0',
marginRight: 5,
marginTop: 5
}
},
circle: {
background: backgroundColor,
borderRadius: '50%',
width: 25,
height: 25,
alignItems: 'center',
justifyContent: 'center',
display: 'flex'
}
}

View file

@ -1,4 +1,3 @@
import { makeStyles } from '@mui/styles'
import * as R from 'ramda' import * as R from 'ramda'
import React, { memo, useState } from 'react' import React, { memo, useState } from 'react'
import { Carousel } from 'src/components/Carousel' import { Carousel } from 'src/components/Carousel'
@ -8,35 +7,34 @@ import { formatDate } from 'src/utils/timezones'
import CopyToClipboard from '../../Transactions/CopyToClipboard' import CopyToClipboard from '../../Transactions/CopyToClipboard'
import styles from './PhotosCarousel.styles'
const useStyles = makeStyles(styles)
const PhotosCarousel = memo(({ photosData, timezone }) => { const PhotosCarousel = memo(({ photosData, timezone }) => {
const classes = useStyles()
const [currentIndex, setCurrentIndex] = useState(0) const [currentIndex, setCurrentIndex] = useState(0)
const Label = ({ children }) => { const Label = ({ children }) => {
const classes = useStyles() return (
return <Label1 className={classes.label}>{children}</Label1> <Label1 noMargin className="mb-1 text-comet">
{children}
</Label1>
)
} }
const isFaceCustomerPhoto = !R.has('id')(photosData[currentIndex]) const isFaceCustomerPhoto = !R.has('id')(photosData[currentIndex])
const slidePhoto = index => setCurrentIndex(index) const slidePhoto = index => setCurrentIndex(index)
// TODO hide copy to clipboard shit
return ( return (
<> <>
<Carousel photosData={photosData} slidePhoto={slidePhoto} /> <Carousel photosData={photosData} slidePhoto={slidePhoto} />
{!isFaceCustomerPhoto && ( {!isFaceCustomerPhoto && (
<div className={classes.firstRow}> <div className="flex flex-col p-2">
<Label>Session ID</Label> <Label>Session ID</Label>
<CopyToClipboard> <CopyToClipboard>
{photosData && photosData[currentIndex]?.id} {photosData && photosData[currentIndex]?.id}
</CopyToClipboard> </CopyToClipboard>
</div> </div>
)} )}
<div className={classes.secondRow}> <div className="text-zodiac flex p-2 gap-8">
<div> <div>
<> <>
<Label>Date</Label> <Label>Date</Label>

View file

@ -1,30 +0,0 @@
import typographyStyles from 'src/components/typography/styles'
import { offColor } from 'src/styling/variables'
const { p } = typographyStyles
export default {
label: {
color: offColor,
margin: [[0, 0, 6, 0]]
},
firstRow: {
padding: [[8]],
display: 'flex',
flexDirection: 'column'
},
secondRow: {
extend: p,
display: 'flex',
padding: [[8]],
'& > div': {
display: 'flex',
flexDirection: 'column',
'& > div': {
height: 37,
marginBottom: 15,
marginRight: 55
}
}
}
}

View file

@ -1,7 +1,5 @@
import { toUnit } from '@lamassu/coins/lightUtils' import { toUnit } from '@lamassu/coins/lightUtils'
import { makeStyles } from '@mui/styles'
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import classnames from 'classnames'
import * as R from 'ramda' import * as R from 'ramda'
import React from 'react' import React from 'react'
import DataTable from 'src/components/tables/DataTable' import DataTable from 'src/components/tables/DataTable'
@ -13,21 +11,13 @@ import { ifNotNull } from 'src/utils/nullCheck'
import { formatDate } from 'src/utils/timezones' import { formatDate } from 'src/utils/timezones'
import CopyToClipboard from '../../Transactions/CopyToClipboard' import CopyToClipboard from '../../Transactions/CopyToClipboard'
import mainStyles from '../CustomersList.styles'
const useStyles = makeStyles(mainStyles)
const TransactionsList = ({ customer, data, loading }) => { const TransactionsList = ({ customer, data, loading }) => {
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 { lastUsedMachineName } = customer const { lastUsedMachineName } = customer
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
const tableSpacingClasses = {
[classes.titleAndButtonsContainer]: loading || (!loading && !hasData),
[classes.txTableSpacing]: !loading && hasData
}
const summaryElements = [ const summaryElements = [
{ {
@ -61,7 +51,7 @@ const TransactionsList = ({ customer, data, loading }) => {
value: ifNotNull( value: ifNotNull(
customer.lastTxFiat, customer.lastTxFiat,
<> <>
<LastTxIcon className={classes.icon} /> <LastTxIcon className="mr-3" />
{`${Number.parseFloat(customer.lastTxFiat)} {`${Number.parseFloat(customer.lastTxFiat)}
${customer.lastTxFiatCode}`} ${customer.lastTxFiatCode}`}
</> </>
@ -80,9 +70,9 @@ const TransactionsList = ({ customer, data, loading }) => {
view: it => ( view: it => (
<> <>
{it.txClass === 'cashOut' ? ( {it.txClass === 'cashOut' ? (
<TxOutIcon className={classes.txClassIconLeft} /> <TxOutIcon className="mr-3" />
) : ( ) : (
<TxInIcon className={classes.txClassIconLeft} /> <TxInIcon className="mr-3" />
)} )}
</> </>
) )
@ -96,7 +86,9 @@ const TransactionsList = ({ customer, data, loading }) => {
header: 'Transaction ID', header: 'Transaction ID',
width: 145, width: 145,
view: it => ( view: it => (
<CopyToClipboard className={classes.txId}>{it.id}</CopyToClipboard> <CopyToClipboard className="font-museo whitespace-nowrap overflow-hidden text-ellipsis">
{it.id}
</CopyToClipboard>
) )
}, },
{ {
@ -144,7 +136,7 @@ const TransactionsList = ({ customer, data, loading }) => {
<Label1 <Label1
noMargin noMargin
key={idx} key={idx}
className={classes.txSummaryLabel} className="text-comet mb-1 mr-6"
style={{ width: size }}> style={{ width: size }}>
{header} {header}
</Label1> </Label1>
@ -152,28 +144,21 @@ const TransactionsList = ({ customer, data, loading }) => {
</div> </div>
<div className="flex"> <div className="flex">
{summaryElements.map(({ size, value }, idx) => ( {summaryElements.map(({ size, value }, idx) => (
<P <P noMargin key={idx} className="h-4 mr-6" style={{ width: size }}>
noMargin
key={idx}
className={classes.txSummaryValue}
style={{ width: size }}>
{value} {value}
</P> </P>
))} ))}
</div> </div>
</div> </div>
<div className={classes.titleWrapper}> <div className="flex mt-5">
<div className={classnames(tableSpacingClasses)}> {loading ? (
{loading ? ( <H4>Loading</H4>
<H4>Loading</H4> ) : hasData ? (
) : hasData ? ( <DataTable elements={tableElements} data={data} />
'' ) : (
) : ( <H4>No transactions so far</H4>
<H4>No transactions so far</H4> )}
)}
</div>
</div> </div>
{hasData && <DataTable elements={tableElements} data={data} />}
</> </>
) )
} }

View file

@ -1,4 +1,3 @@
import { makeStyles } from '@mui/styles'
import { useFormikContext } from 'formik' import { useFormikContext } from 'formik'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useState, useCallback } from 'react' import React, { useState, useCallback } from 'react'
@ -7,42 +6,9 @@ import { Label3, H3 } from 'src/components/typography'
import UploadPhotoIcon from 'src/styling/icons/button/photo/zodiac-resized.svg?react' import UploadPhotoIcon from 'src/styling/icons/button/photo/zodiac-resized.svg?react'
import UploadFileIcon from 'src/styling/icons/button/upload-file/zodiac-resized.svg?react' import UploadFileIcon from 'src/styling/icons/button/upload-file/zodiac-resized.svg?react'
import { offColor, subheaderColor } from 'src/styling/variables' import classes from './Upload.module.css'
const useStyles = makeStyles({
box: {
boxSizing: 'border-box',
width: 450,
height: 120,
borderStyle: 'dashed',
borderColor: offColor,
borderRadius: 4,
borderWidth: 1,
backgroundColor: subheaderColor,
display: 'flex',
justifyContent: 'center'
},
inputContent: {
marginTop: 35,
display: 'flex'
},
uploadContent: {
marginTop: 50,
display: 'flex'
},
board: {
marginTop: 40,
width: 450,
height: 120
},
icon: {
margin: [[14, 20, 0, 0]]
}
})
const Upload = ({ type }) => { const Upload = ({ type }) => {
const classes = useStyles()
const [data, setData] = useState({}) const [data, setData] = useState({})
const { setFieldValue } = useFormikContext() const { setFieldValue } = useFormikContext()
@ -71,20 +37,14 @@ const Upload = ({ type }) => {
return ( return (
<> <>
<div {...getRootProps()} className={classes.board}> <div {...getRootProps()} className="mt-10 w-112 h-30">
{R.isEmpty(data) && ( {R.isEmpty(data) && (
<div className={classes.box}> <div className={classes.box}>
<input {...getInputProps()} /> <input {...getInputProps()} />
<div className={classes.inputContent}> {isImage ? <UploadPhotoIcon /> : <UploadFileIcon />}
{isImage ? ( <Label3>{`Drag and drop ${
<UploadPhotoIcon className={classes.icon}></UploadPhotoIcon> isImage ? 'an image' : 'a file'
) : ( } or click to open the explorer`}</Label3>
<UploadFileIcon className={classes.icon}></UploadFileIcon>
)}
<Label3>{`Drag and drop ${
isImage ? 'an image' : 'a file'
} or click to open the explorer`}</Label3>
</div>
</div> </div>
)} )}
{!R.isEmpty(data) && isImage && ( {!R.isEmpty(data) && isImage && (
@ -94,7 +54,7 @@ const Upload = ({ type }) => {
)} )}
{!R.isEmpty(data) && !isImage && ( {!R.isEmpty(data) && !isImage && (
<div className={classes.box}> <div className={classes.box}>
<H3 className={classes.uploadContent}>{data.preview}</H3> <H3 className="mt-12 flex">{data.preview}</H3>
</div> </div>
)} )}
</div> </div>

View file

@ -0,0 +1,14 @@
.box {
box-sizing: border-box;
width: 450px;
height: 120px;
border-style: dashed;
border-color: var(--comet);
border-radius: 4px;
border-width: 1px;
background-color: var(--zircon);
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
}

View file

@ -0,0 +1,5 @@
const OVERRIDE_PENDING = 'automatic'
const OVERRIDE_AUTHORIZED = 'verified'
const OVERRIDE_REJECTED = 'blocked'
export { OVERRIDE_PENDING, OVERRIDE_AUTHORIZED, OVERRIDE_REJECTED }

View file

@ -3,19 +3,13 @@ import Wizard from '../Wizard'
import CustomerDetails from './CustomerDetails' import CustomerDetails from './CustomerDetails'
import CustomerSidebar from './CustomerSidebar' import CustomerSidebar from './CustomerSidebar'
import EditableCard from './EditableCard' import EditableCard from './EditableCard'
import Field from './Field'
import IdDataCard from './IdDataCard'
import PhotosCarousel from './PhotosCarousel'
import TransactionsList from './TransactionsList' import TransactionsList from './TransactionsList'
import Upload from './Upload' import Upload from './Upload'
export { export {
PhotosCarousel,
CustomerDetails, CustomerDetails,
IdDataCard,
TransactionsList, TransactionsList,
CustomerSidebar, CustomerSidebar,
Field,
EditableCard, EditableCard,
Wizard, Wizard,
Upload Upload

View file

@ -1,23 +1,16 @@
import Paper from '@mui/material/Paper' import Paper from '@mui/material/Paper'
import { makeStyles } from '@mui/styles'
import classNames from 'classnames'
import { React } from 'react' import { React } from 'react'
import { P } from 'src/components/typography' import { P } from 'src/components/typography'
import AddIcon from 'src/styling/icons/button/add/zodiac.svg?react' import AddIcon from 'src/styling/icons/button/add/zodiac.svg?react'
import styles from './NoteCard.styles'
const useStyles = makeStyles(styles)
const NewNoteCard = ({ setOpenModal }) => { const NewNoteCard = ({ setOpenModal }) => {
const classes = useStyles()
return ( return (
<div className={classes.noteCardWrapper} onClick={() => setOpenModal(true)}> <Paper
<Paper className={classNames(classes.noteCardChip, classes.newNoteCard)}> className="cursor-pointer bg-zircon flex flex-col justify-center items-center"
<AddIcon width={20} height={20} /> onClick={() => setOpenModal(true)}>
<P>Add new</P> <AddIcon width={20} height={20} />
</Paper> <P>Add new</P>
</div> </Paper>
) )
} }

View file

@ -1,4 +1,3 @@
import { makeStyles } from '@mui/styles'
import { Form, Formik, Field } from 'formik' import { Form, Formik, Field } from 'formik'
import { React } from 'react' import { React } from 'react'
import ErrorMessage from 'src/components/ErrorMessage' import ErrorMessage from 'src/components/ErrorMessage'
@ -8,10 +7,6 @@ import * as Yup from 'yup'
import { Button } from 'src/components/buttons' import { Button } from 'src/components/buttons'
import { TextInput } from 'src/components/inputs/formik' import { TextInput } from 'src/components/inputs/formik'
import styles from './NewNoteModal.styles'
const useStyles = makeStyles(styles)
const initialValues = { const initialValues = {
title: '', title: '',
content: '' content: ''
@ -23,8 +18,6 @@ const validationSchema = Yup.object().shape({
}) })
const NewNoteModal = ({ showModal, onClose, onSubmit, errorMsg }) => { const NewNoteModal = ({ showModal, onClose, onSubmit, errorMsg }) => {
const classes = useStyles()
return ( return (
<> <>
<Modal <Modal
@ -42,7 +35,7 @@ const NewNoteModal = ({ showModal, onClose, onSubmit, errorMsg }) => {
onSubmit={({ title, content }) => { onSubmit={({ title, content }) => {
onSubmit({ title, content }) onSubmit({ title, content })
}}> }}>
<Form id="note-form" className={classes.form}> <Form id="note-form" className="flex flex-col h-full gap-5">
<Field <Field
name="title" name="title"
autofocus autofocus
@ -62,9 +55,12 @@ const NewNoteModal = ({ showModal, onClose, onSubmit, errorMsg }) => {
rows={11} rows={11}
label="Note content" label="Note content"
/> />
<div className={classes.footer}> <div className="flex flex-row mt-auto mb-6">
{errorMsg && <ErrorMessage>{errorMsg}</ErrorMessage>} {errorMsg && <ErrorMessage>{errorMsg}</ErrorMessage>}
<Button type="submit" form="note-form" className={classes.submit}> <Button
type="submit"
form="note-form"
className="mt-auto ml-auto">
Add note Add note
</Button> </Button>
</div> </div>

View file

@ -1,25 +0,0 @@
import { spacer } from 'src/styling/variables'
const styles = {
form: {
display: 'flex',
flexDirection: 'column',
height: '100%',
'& > *': {
marginTop: 20
},
'& > *:last-child': {
marginTop: 'auto'
}
},
submit: {
margin: [['auto', 0, 0, 'auto']]
},
footer: {
display: 'flex',
flexDirection: 'row',
margin: [['auto', 0, spacer * 3, 0]]
}
}
export default styles

View file

@ -1,5 +1,4 @@
import Paper from '@mui/material/Paper' import Paper from '@mui/material/Paper'
import { makeStyles } from '@mui/styles'
import * as R from 'ramda' import * as R from 'ramda'
import { React } from 'react' import { React } from 'react'
import { H3, P } from 'src/components/typography' import { H3, P } from 'src/components/typography'
@ -7,10 +6,6 @@ import DeleteIcon from 'src/styling/icons/action/delete/enabled.svg?react'
import { formatDate } from 'src/utils/timezones' import { formatDate } from 'src/utils/timezones'
import styles from './NoteCard.styles'
const useStyles = makeStyles(styles)
const formatContent = content => { const formatContent = content => {
const fragments = R.split(/\n/)(content) const fragments = R.split(/\n/)(content)
return R.map((it, idx) => { return R.map((it, idx) => {
@ -25,31 +20,28 @@ const formatContent = content => {
} }
const NoteCard = ({ note, deleteNote, handleClick, timezone }) => { const NoteCard = ({ note, deleteNote, handleClick, timezone }) => {
const classes = useStyles()
return ( return (
<div className={classes.noteCardWrapper}> <Paper
<Paper className={classes.noteCardChip} onClick={() => handleClick(note)}> className="p-2 cursor-pointer overflow-hidden text-ellipsis"
<div className={classes.noteCardHeader}> onClick={() => handleClick(note)}>
<div className={classes.noteCardTitle}> <div className="flex flex-row justify-between w-full">
<H3 noMargin>{note?.title}</H3> <div className="overflow-hidden whitespace-nowrap overflow-ellipsis">
<P noMargin>{formatDate(note?.created, timezone, 'yyyy-MM-dd')}</P> <H3 noMargin>{note?.title}</H3>
</div> <P noMargin>{formatDate(note?.created, timezone, 'yyyy-MM-dd')}</P>
<div>
<DeleteIcon
className={classes.deleteIcon}
onClick={e => {
e.stopPropagation()
deleteNote({ noteId: note.id })
}}
/>
</div>
</div> </div>
<P noMargin className={classes.noteCardContent}> <div>
{formatContent(note?.content)} <DeleteIcon
</P> onClick={e => {
</Paper> e.stopPropagation()
</div> deleteNote({ noteId: note.id })
}}
/>
</div>
</div>
<P noMargin className="mt-2 line-clamp-8">
{formatContent(note?.content)}
</P>
</Paper>
) )
} }

View file

@ -1,93 +0,0 @@
import { zircon } from 'src/styling/variables'
const styles = {
noteCardWrapper: {
flexGrow: 0,
flexShrink: 0,
flexBasis: `25%`,
minWidth: 0,
maxWidth: 500,
'&:nth-child(4n+1)': {
'& > div': {
margin: [[0, 10, 0, 0]]
}
},
'&:nth-child(4n)': {
'& > div': {
margin: [[0, 0, 0, 10]]
}
},
margin: [[10, 0]]
},
noteCardChip: {
height: 200,
margin: [[0, 10]],
padding: [[10, 10]],
cursor: 'pointer'
},
newNoteCard: {
backgroundColor: zircon,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
},
noteCardHeader: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
width: '100%'
},
noteCardTitle: {
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
marginRight: 10
},
noteCardContent: {
display: 'box',
lineClamp: 7,
boxOrient: 'vertical',
margin: [[15, 0]],
overflow: 'hidden',
textOverflow: 'ellipsis',
wordWrap: 'break-word'
},
editCardChip: {
height: 325,
padding: 15
},
editCardHeader: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 15
},
editCardActions: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
'& > *': {
marginRight: 10
},
'& > *:last-child': {
marginRight: 0
}
},
editNotesContent: {
'& > div': {
'&:after': {
borderBottom: 'none'
},
'&:before': {
borderBottom: 'none'
},
'&:hover:not(.Mui-disabled)::before': {
borderBottom: 'none'
}
}
}
}
export default styles

View file

@ -1,5 +1,4 @@
import Paper from '@mui/material/Paper' import Paper from '@mui/material/Paper'
import { makeStyles } from '@mui/styles'
import { formatDurationWithOptions, intervalToDuration } from 'date-fns/fp' import { formatDurationWithOptions, intervalToDuration } from 'date-fns/fp'
import { Form, Formik, Field } from 'formik' import { Form, Formik, Field } from 'formik'
import { React, useRef } from 'react' import { React, useRef } from 'react'
@ -14,13 +13,8 @@ import { ActionButton } from 'src/components/buttons'
import { TextInput } from 'src/components/inputs/formik' import { TextInput } from 'src/components/inputs/formik'
import { toTimezone } from 'src/utils/timezones' import { toTimezone } from 'src/utils/timezones'
import styles from './NoteCard.styles'
const useStyles = makeStyles(styles)
const NoteEdit = ({ note, cancel, edit, timezone }) => { const NoteEdit = ({ note, cancel, edit, timezone }) => {
const formRef = useRef() const formRef = useRef()
const classes = useStyles()
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
content: Yup.string() content: Yup.string()
@ -31,8 +25,8 @@ const NoteEdit = ({ note, cancel, edit, timezone }) => {
} }
return ( return (
<Paper className={classes.editCardChip}> <Paper className="p-4">
<div className={classes.editCardHeader}> <div className="flex flex-row justify-between items-center mb-4">
<P noMargin> <P noMargin>
{`Last edited `} {`Last edited `}
{formatDurationWithOptions( {formatDurationWithOptions(
@ -44,7 +38,7 @@ const NoteEdit = ({ note, cancel, edit, timezone }) => {
)} )}
{` ago`} {` ago`}
</P> </P>
<div className={classes.editCardActions}> <div className="flex flex-row items-center gap-2">
<ActionButton <ActionButton
color="primary" color="primary"
type="button" type="button"
@ -88,7 +82,7 @@ const NoteEdit = ({ note, cancel, edit, timezone }) => {
<Field <Field
name="content" name="content"
component={TextInput} component={TextInput}
className={classes.editNotesContent} InputProps={{ disableUnderline: true }}
size="sm" size="sm"
autoComplete="off" autoComplete="off"
fullWidth fullWidth

View file

@ -1,132 +0,0 @@
import Paper from '@mui/material/Paper'
import { makeStyles } from '@mui/styles'
import classnames from 'classnames'
import React, { memo } from 'react'
import { MainStatus } from 'src/components/Status'
import { H3 } from 'src/components/typography'
import AuthorizeReversedIcon from 'src/styling/icons/button/authorize/white.svg?react'
import AuthorizeIcon from 'src/styling/icons/button/authorize/zodiac.svg?react'
import RejectReversedIcon from 'src/styling/icons/button/cancel/white.svg?react'
import RejectIcon from 'src/styling/icons/button/cancel/zodiac.svg?react'
import EditReversedIcon from 'src/styling/icons/button/edit/white.svg?react'
import EditIcon from 'src/styling/icons/button/edit/zodiac.svg?react'
import { ActionButton } from 'src/components/buttons'
import { propertyCardStyles } from './PropertyCard.styles'
const useStyles = makeStyles(propertyCardStyles)
const OVERRIDE_PENDING = 'automatic'
const OVERRIDE_AUTHORIZED = 'verified'
const OVERRIDE_REJECTED = 'blocked'
const PropertyCard = memo(
({
className,
contentClassName,
title,
state,
authorize,
reject,
edit,
confirm,
isEditing,
formName,
children
}) => {
const classes = useStyles()
const label1ClassNames = {
[classes.label1]: true,
[classes.label1Pending]: state === OVERRIDE_PENDING,
[classes.label1Rejected]: state === OVERRIDE_REJECTED,
[classes.label1Accepted]: state === OVERRIDE_AUTHORIZED
}
const AuthorizeButton = () => (
<ActionButton
className={classes.cardActionButton}
color="secondary"
Icon={AuthorizeIcon}
InverseIcon={AuthorizeReversedIcon}
onClick={() => authorize()}>
Authorize
</ActionButton>
)
const RejectButton = () => (
<ActionButton
className={classes.cardActionButton}
color="secondary"
Icon={RejectIcon}
InverseIcon={RejectReversedIcon}
onClick={() => reject()}>
Reject
</ActionButton>
)
const EditButton = () => (
<ActionButton
className={classes.cardActionButton}
color="secondary"
Icon={EditIcon}
InverseIcon={EditReversedIcon}
onClick={() => edit()}>
Edit
</ActionButton>
)
const ConfirmButton = () => (
<ActionButton
className={classes.cardActionButton}
type="submit"
form={formName}
color="secondary"
Icon={AuthorizeIcon}
InverseIcon={AuthorizeReversedIcon}>
Confirm
</ActionButton>
)
const authorized =
state === OVERRIDE_PENDING
? { label: 'Pending', type: 'neutral' }
: state === OVERRIDE_REJECTED
? { label: 'Rejected', type: 'error' }
: { label: 'Accepted', type: 'success' }
return (
<Paper
className={classnames(classes.propertyCard, className)}
elevation={0}>
<H3 className={classes.propertyCardTopRow}>{title}</H3>
<div
className={classnames(
classes.propertyCardBottomRow,
contentClassName
)}>
{state && (
<div className={classnames(label1ClassNames)}>
<MainStatus statuses={[authorized]} />
</div>
)}
{children}
<div className={classes.buttonsWrapper}>
{authorize && state !== OVERRIDE_AUTHORIZED && AuthorizeButton()}
{reject && state !== OVERRIDE_REJECTED && RejectButton()}
{edit && !isEditing && EditButton()}
{confirm && isEditing && ConfirmButton()}
</div>
</div>
</Paper>
)
}
)
export {
PropertyCard,
OVERRIDE_PENDING,
OVERRIDE_AUTHORIZED,
OVERRIDE_REJECTED
}

View file

@ -1,69 +0,0 @@
import { white, tomato, spring4, comet } from 'src/styling/variables'
const propertyCardStyles = {
label1: {
display: 'flex',
marginBottom: 2,
marginTop: 'auto',
width: 85
},
label1Pending: {
color: comet
},
label1Rejected: {
color: tomato
},
label1Accepted: {
color: spring4
},
cardActionButton: {
display: 'flex',
height: 28,
marginRight: 'auto',
marginLeft: 12
},
propertyCardTopRow: {
display: 'flex',
margin: [[0, 10, 5, 0]]
},
propertyCardBottomRow: {
display: 'flex',
flexDirection: 'row',
height: 45
},
propertyCard: {
display: 'flex',
flexDirection: 'column',
borderRadius: 8,
width: '100%',
height: 100,
padding: [[20]],
boxSizing: 'border-box',
boxShadow: '0 0 8px 0 rgba(0, 0, 0, 0.04)',
border: 'solid 0',
backgroundColor: white,
margin: [[20, 0, 0, 0]]
},
rowSpaceBetween: {
display: 'flex',
flexFlow: 'row nowrap',
alignItems: 'center',
justifyContent: 'space-between'
},
columnSpaceBetween: {
display: 'flex',
flexFlow: 'column nowrap',
alignItems: 'center',
justifyContent: 'space-between',
width: 90
},
buttonsWrapper: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-end',
marginLeft: 'auto',
marginTop: 'auto'
}
}
export { propertyCardStyles }

View file

@ -1,13 +0,0 @@
import {
PropertyCard,
OVERRIDE_PENDING,
OVERRIDE_AUTHORIZED,
OVERRIDE_REJECTED
} from './PropertyCard'
export {
PropertyCard,
OVERRIDE_PENDING,
OVERRIDE_AUTHORIZED,
OVERRIDE_REJECTED
}

View file

@ -1,6 +1,4 @@
import React from 'react' import React from 'react'
import { makeStyles } from '@mui/styles'
import classnames from 'classnames'
import { parse, isValid, format } from 'date-fns/fp' import { parse, isValid, format } from 'date-fns/fp'
import { Field, useFormikContext } from 'formik' import { Field, useFormikContext } from 'formik'
import { parsePhoneNumberFromString } from 'libphonenumber-js' import { parsePhoneNumberFromString } from 'libphonenumber-js'
@ -14,45 +12,10 @@ import {
TextInput, TextInput,
Autocomplete Autocomplete
} from 'src/components/inputs/formik' } from 'src/components/inputs/formik'
import { errorColor } from 'src/styling/variables'
import { MANUAL } from 'src/utils/constants' import { MANUAL } from 'src/utils/constants'
import { Upload } from './components' import { Upload } from './components'
const useStyles = makeStyles({
radio: {
padding: 4,
margin: 4
},
radioGroup: {
flexDirection: 'row'
},
error: {
color: errorColor
},
specialLabel: {
height: 40,
padding: 0,
width: 250
},
label: {
height: 40,
padding: 0
},
specialGrid: {
display: 'grid',
gridTemplateColumns: [[182, 162, 141]]
},
picker: {
width: 150
},
field: {
'& > *:last-child': {
marginBottom: 24
}
}
})
const CUSTOMER_BLOCKED = 'blocked' const CUSTOMER_BLOCKED = 'blocked'
const CUSTOM = 'custom' const CUSTOM = 'custom'
const REQUIREMENT = 'requirement' const REQUIREMENT = 'requirement'
@ -215,68 +178,50 @@ const updateRequirementOptions = it => [
] ]
const EntryType = ({ customInfoRequirementOptions }) => { const EntryType = ({ customInfoRequirementOptions }) => {
const classes = useStyles()
const { values } = useFormikContext() const { values } = useFormikContext()
const displayCustomOptions = values.entryType === CUSTOM const displayCustomOptions = values.entryType === CUSTOM
const displayRequirementOptions = values.entryType === REQUIREMENT const displayRequirementOptions = values.entryType === REQUIREMENT
return ( const Entry = ({ title, name, options, className }) => (
<> <div>
<div className="flex items-center"> <div className="flex items-center">
<H4>Type of entry</H4> <H4>{title}</H4>
</div> </div>
<Field <Field
component={RadioGroup} component={RadioGroup}
name={name}
options={options}
radioClassName="p-1 m-1"
labelClassName={className}
className="grid grid-cols-[182px_162px_141px]"
/>
</div>
)
return (
<>
<Entry
title="Type of entry"
name="entryType" name="entryType"
options={entryOptions} options={entryOptions}
labelClassName={classes.specialLabel} className="w-62"
radioClassName={classes.radio}
className={classnames(classes.radioGroup, classes.specialGrid)}
/> />
{displayCustomOptions && ( {displayCustomOptions && (
<div> <Entry title="Type of data" name="dataType" options={dataOptions} />
<div className="flex items-center">
<H4>Type of data</H4>
</div>
<Field
component={RadioGroup}
name="dataType"
options={dataOptions}
labelClassName={classes.label}
radioClassName={classes.radio}
className={classnames(classes.radioGroup, classes.specialGrid)}
/>
</div>
)} )}
{displayRequirementOptions && ( {displayRequirementOptions && (
<div> <Entry
<div className="flex items-center"> title="Requirements"
<H4>Requirements</H4> name="requirement"
</div> options={requirementOptions}
<Field />
component={RadioGroup}
name="requirement"
options={
requirementOptions
// TODO: Enable once custom info requirement manual entry is finished
// !R.isEmpty(customInfoRequirementOptions)
// ? updateRequirementOptions(requirementOptions)
// : requirementOptions
}
labelClassName={classes.label}
radioClassName={classes.radio}
className={classnames(classes.radioGroup, classes.specialGrid)}
/>
</div>
)} )}
</> </>
) )
} }
const ManualDataEntry = ({ selectedValues, customInfoRequirementOptions }) => { const ManualDataEntry = ({ selectedValues, customInfoRequirementOptions }) => {
const classes = useStyles()
const typeOfEntrySelected = selectedValues?.entryType const typeOfEntrySelected = selectedValues?.entryType
const dataTypeSelected = selectedValues?.dataType const dataTypeSelected = selectedValues?.dataType
const requirementSelected = selectedValues?.requirement const requirementSelected = selectedValues?.requirement
@ -316,14 +261,14 @@ const ManualDataEntry = ({ selectedValues, customInfoRequirementOptions }) => {
<Autocomplete <Autocomplete
fullWidth fullWidth
label={`Available requests`} label={`Available requests`}
className={classes.picker} className="w-37"
isOptionEqualToValue={R.eqProps('code')} isOptionEqualToValue={R.eqProps('code')}
labelProp={'display'} labelProp={'display'}
options={customInfoRequirementOptions} options={customInfoRequirementOptions}
onChange={(evt, it) => {}} onChange={(evt, it) => {}}
/> />
)} )}
<div className={classes.field}> <div className="mb-6">
{!upload && {!upload &&
!isCustomInfoRequirement && !isCustomInfoRequirement &&
elements.options.map(({ label, name }, idx) => ( elements.options.map(({ label, name }, idx) => (

View file

@ -16,6 +16,7 @@ const CopyToClipboard = ({
className, className,
buttonClassname, buttonClassname,
children, children,
variant,
wrapperClassname, wrapperClassname,
removeSpace = true, removeSpace = true,
...props ...props
@ -71,7 +72,7 @@ const CopyToClipboard = ({
</> </>
)} )}
</div> </div>
); )
} }
export default CopyToClipboard export default CopyToClipboard

View file

@ -27,6 +27,10 @@
--neon: #5a67ff; --neon: #5a67ff;
--malachite: #00CD5A; --malachite: #00CD5A;
--orange-yellow: #ffcc00; --orange-yellow: #ffcc00;
--mont: 'Mont';
--museo: 'MuseoSans';
--bpmono: 'BPmono';
} }
@theme { @theme {
@ -44,6 +48,13 @@
--color-neon: var(--neon); --color-neon: var(--neon);
--color-malachite: var(--malachite); --color-malachite: var(--malachite);
--color-orange-yellow: var(--orange-yellow); --color-orange-yellow: var(--orange-yellow);
--font-mont: var(--mont);
--font-museo: var(--museo);
--font-bpmono: var(--bpmono);
}
@utility col-span-all {
column-span: all;
} }
body { body {