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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,20 @@ 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

- Implement cost center autocomplete, lazyload in organization autocomplete and maxHeight prop

### Fixed

- Run schedule job only on saturday
=======
enzomerca marked this conversation as resolved.
Show resolved Hide resolved
## [0.3.2] - 2024-08-22

### Fixed
- shows more options in the cost centers dropdown


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

Expand Down
89 changes: 89 additions & 0 deletions react/components/CostCentersAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
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
maxHeight: 200,
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
117 changes: 78 additions & 39 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,59 +24,96 @@ 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])

const { name, id } = organization.getOrganizationById
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

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

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])


const onClear = useCallback(() => {
setTerm('')

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

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

useEffect(() => {
if (term && term.length > 1) {
setHasChanged(true)
Expand All @@ -89,14 +126,16 @@ const OrganizationsAutocomplete = ({ onChange, organizationId }: Props) => {
}
}, [term])

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
2 changes: 1 addition & 1 deletion react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.render-runtime",
"vtex.storefront-permissions": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.storefront-permissions",
"vtex.storefront-permissions-components": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.storefront-permissions-components",
"vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected].3/public/@types/vtex.styleguide"
"vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected].13/public/@types/vtex.styleguide"
},
"dependencies": {
"faker": "^4.1.0",
Expand Down
6 changes: 3 additions & 3 deletions react/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -500,9 +500,9 @@ [email protected]:
version "1.23.0"
resolved "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.storefront-permissions#2a12e48a1b630d6d9cd64115572bcae55a7aa756"

"vtex.styleguide@http://vtex.vtexassets.com/_v/public/typings/v1/[email protected].3/public/@types/vtex.styleguide":
version "9.146.3"
resolved "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected].3/public/@types/vtex.styleguide#05558160f29cd8f4aefe419844a4bd66e2b3fdbb"
"vtex.styleguide@http://vtex.vtexassets.com/_v/public/typings/v1/[email protected].13/public/@types/vtex.styleguide":
version "9.146.13"
resolved "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected].13/public/@types/vtex.styleguide#f4ccbc54621bf5114ddd115b6032ae320f2eba55"

zen-observable-ts@^0.8.21:
version "0.8.21"
Expand Down