Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/implement cost center autocomplete #31

Merged
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Fixed
enzomerca marked this conversation as resolved.
Show resolved Hide resolved

- Run schedule job only on saturday

### Removed
- [ENGINEERS-1247] - Disable cypress tests in PR level

Expand Down
88 changes: 88 additions & 0 deletions react/components/CostCentersAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, { useEffect, useState } from 'react'
import { useQuery } from 'react-apollo'
import { AutocompleteInput } from 'vtex.styleguide'
import { useIntl } from 'react-intl'

import { messages } from './customers-admin'
import GET_COST from '../queries/costCentersByOrg.gql'
Josmar-jr marked this conversation as resolved.
Show resolved Hide resolved

interface Props {
onChange: (value: { value: string | null; label: string }) => void
organizationId?: string
}

const initialState = {
search: '',
page: 1,
pageSize: 25,
sortOrder: 'ASC',
sortedBy: 'name',
}

const CostCenterAutocomplete = ({ onChange, organizationId }: Props) => {
const { formatMessage } = useIntl()
const [costCenterTextInput, setCostCenterTextInput] = useState('')
const [debouncedSearchTerm, setDebouncedSearchTerm] =
useState(costCenterTextInput)

const { data, loading, refetch } = useQuery(GET_COST, {
variables: {
...initialState,
id: organizationId,
},
ssr: false,
notifyOnNetworkStatusChange: true,
skip: !organizationId,
})

const onClear = () => {
setCostCenterTextInput('')
onChange({ value: null, label: '' })
}

useEffect(() => {
const handler = setTimeout(() => {
setDebouncedSearchTerm(costCenterTextInput)
}, 500) // 500ms delay
Josmar-jr marked this conversation as resolved.
Show resolved Hide resolved

return () => {
clearTimeout(handler)
}
}, [costCenterTextInput])

useEffect(() => {
if (debouncedSearchTerm) {
refetch({
...initialState,
id: organizationId,
search: debouncedSearchTerm,
})
} else if (debouncedSearchTerm === '') {
onClear()
}
}, [debouncedSearchTerm])

const options = {
Josmar-jr marked this conversation as resolved.
Show resolved Hide resolved
onSelect: onChange,
loading,
value: data?.getCostCentersByOrganizationId?.data?.map(
(costCenter: { id: string; name: string }) => ({
value: costCenter.id,
label: costCenter.name,
})
),
}

const input = {
onChange: (_term: string) => {
setCostCenterTextInput(_term)
},
onClear,
placeholder: formatMessage(messages.costCenter),
value: costCenterTextInput,
}

return <AutocompleteInput input={input} options={options} />
}

export default CostCenterAutocomplete
124 changes: 74 additions & 50 deletions react/components/OrganizationsAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { useEffect, useState, useMemo, useCallback } from 'react'
import { useQuery } from 'react-apollo'
import { AutocompleteInput } from 'vtex.styleguide'
import { useIntl } from 'react-intl'
Expand All @@ -24,79 +24,103 @@ interface Props {
const OrganizationsAutocomplete = ({ onChange, organizationId }: Props) => {
const { formatMessage } = useIntl()
const [term, setTerm] = useState('')
const [hasChanged, setHasChanged] = useState(false)
const [values, setValues] = useState([] as any)
const [debouncedTerm, setDebouncedTerm] = useState(term)

const [values, setValues] = useState<Array<{ value: string; label: string }>>(
[]
)

const { data, loading, refetch } = useQuery(GET_ORGANIZATIONS, {
variables: initialState,
ssr: false,
notifyOnNetworkStatusChange: true,
})

const { data: organization } = useQuery(GET_ORGANIZATION_BY_ID, {
variables: { id: organizationId },
ssr: false,
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
skip: !organizationId,
})
const { data: organization, loading: orgLoading } = useQuery(
GET_ORGANIZATION_BY_ID,
{
variables: { id: organizationId },
ssr: false,
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
skip: !organizationId,
}
)

const options = {
onSelect: (value: any) => onChange(value),
loading,
value: values,
}
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedTerm(term)
}, 500) // 500ms delay

const onClear = () => {
if (!hasChanged) return
setTerm('')
onChange({ value: null, label: '' })
}
return () => clearTimeout(handler)
}, [term])

useEffect(() => {
if (!organization) {
return
if (debouncedTerm.length > 2) {
refetch({
...initialState,
search: debouncedTerm,
})
} else if (debouncedTerm === '') {
refetch({
...initialState,
search: '',
})
}
}, [debouncedTerm, refetch])

useEffect(() => {
// eslint-disable-next-line vtex/prefer-early-return
Josmar-jr marked this conversation as resolved.
Show resolved Hide resolved
if (organization?.getOrganizationById) {
const { name, id } = organization.getOrganizationById

const { name, id } = organization.getOrganizationById
setTerm(name)

setTerm(name)
setHasChanged(true)
onChange({ value: id, label: name })
}, [organization])
onChange({ value: id, label: name })
}
}, [organization, onChange])

useEffect(() => {
if (data?.getOrganizations?.data) {
setValues(
data.getOrganizations.data.map((item: any) => {
return {
data.getOrganizations.data.map(
(item: { id: string; name: string }) => ({
value: item.id,
label: item.name,
}
})
})
)
)
}
}, [data])

useEffect(() => {
if (term && term.length > 2) {
setHasChanged(true)
refetch({
...initialState,
search: term,
})
} else if (term === '') {
onClear()
}
}, [term])
const onClear = useCallback(() => {
setTerm('')

refetch({
...initialState,
search: '',
})
onChange({ value: null, label: '' })
}, [onChange, refetch])

const options = useMemo(
() => ({
onSelect: onChange,
loading,
value: values,
}),
[loading, values, onChange, orgLoading]
)

const input = {
onChange: (_term: string) => {
setTerm(_term)
},
onClear,
placeholder: formatMessage(messages.searchOrganizations),
value: term,
}
const input = useMemo(
() => ({
onChange: (_term: string) => setTerm(_term),
onClear,
placeholder: formatMessage(messages.searchOrganizations),
value: term,
}),
[term, onClear, formatMessage]
)

return <AutocompleteInput input={input} options={options} />
}
Expand Down
26 changes: 17 additions & 9 deletions react/components/customers-admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import GET_COST from '../queries/costCentersByOrg.gql'
import GET_ORGANIZATIONS from '../queries/getOrganizationsByEmail.graphql'
import ADD_USER from '../mutations/addUser.gql'
import DELETE_USER from '../mutations/deleteUser.gql'
import CostCenterAutocomplete from './CostCentersAutocomplete'

export const messages = defineMessages({
b2bInfo: {
Expand Down Expand Up @@ -455,15 +456,22 @@ const UserEdit: FC<any> = (props: any) => {
)}

{state.orgId && (
<div className="mb5">
<Dropdown
label={formatMessage(messages.costCenter)}
options={optionsCost}
value={state.costId}
onChange={(_: any, costId: string) => {
setState({ ...state, costId })
}}
/>
<div className="mb5 w-100">
<div className="flex">
<div className="w-100">
<label className="h-100">
<span className="db mt0 mb3 c-on-base t-small">
{formatMessage(messages.costCenter)}
</span>
<CostCenterAutocomplete
organizationId={state.orgId}
onChange={(event) => {
setState({ ...state, costId: event.value })
}}
/>
</label>
</div>
</div>
</div>
)}

Expand Down