From cb4c6b9a7fe8ae367bb181c3c5e4c2127d990bd3 Mon Sep 17 00:00:00 2001 From: Josmar Soares Trigueiro Junior Date: Thu, 12 Sep 2024 10:43:12 -0300 Subject: [PATCH 01/11] fix: implement cost center autocomplete and lazyload in organization autocomplete --- react/components/CostCentersAutocomplete.tsx | 88 +++++++++++++ .../components/OrganizationsAutocomplete.tsx | 124 +++++++++++------- react/components/customers-admin.tsx | 26 ++-- 3 files changed, 179 insertions(+), 59 deletions(-) create mode 100644 react/components/CostCentersAutocomplete.tsx diff --git a/react/components/CostCentersAutocomplete.tsx b/react/components/CostCentersAutocomplete.tsx new file mode 100644 index 0000000..baff58e --- /dev/null +++ b/react/components/CostCentersAutocomplete.tsx @@ -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' + +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 + + return () => { + clearTimeout(handler) + } + }, [costCenterTextInput]) + + useEffect(() => { + if (debouncedSearchTerm) { + refetch({ + ...initialState, + id: organizationId, + search: debouncedSearchTerm, + }) + } else if (debouncedSearchTerm === '') { + onClear() + } + }, [debouncedSearchTerm]) + + const options = { + 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 +} + +export default CostCenterAutocomplete diff --git a/react/components/OrganizationsAutocomplete.tsx b/react/components/OrganizationsAutocomplete.tsx index 1b3e065..f0829aa 100644 --- a/react/components/OrganizationsAutocomplete.tsx +++ b/react/components/OrganizationsAutocomplete.tsx @@ -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' @@ -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>( + [] + ) + 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 + 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 } diff --git a/react/components/customers-admin.tsx b/react/components/customers-admin.tsx index b032451..0971c80 100644 --- a/react/components/customers-admin.tsx +++ b/react/components/customers-admin.tsx @@ -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: { @@ -455,15 +456,22 @@ const UserEdit: FC = (props: any) => { )} {state.orgId && ( -
- { - setState({ ...state, costId }) - }} - /> +
+
+
+ +
+
)} From df79f5679deaf4d6628460a122c3d9f892838226 Mon Sep 17 00:00:00 2001 From: Josmar Soares Trigueiro Junior Date: Thu, 12 Sep 2024 10:44:27 -0300 Subject: [PATCH 02/11] doc: update change log file --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f0c1ac..a6765b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Fixed + +- Run schedule job only on saturday + ### Removed - [ENGINEERS-1247] - Disable cypress tests in PR level From 2c529e6000ad6a7286040e7e70228cc8633709c9 Mon Sep 17 00:00:00 2001 From: Josmar Soares Trigueiro Junior Date: Tue, 24 Sep 2024 14:52:08 -0300 Subject: [PATCH 03/11] doc: update change log file --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6765b1..b2ed1d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed +- Implement cost center autocomplete and lazyload in organization autocomplete + +### Fixed + - Run schedule job only on saturday ### Removed From ae252077cffc013e05eebb4f0cacb3e42170694d Mon Sep 17 00:00:00 2001 From: Josmar Soares Trigueiro Junior Date: Wed, 25 Sep 2024 11:02:03 -0300 Subject: [PATCH 04/11] feat: add height limit to autocomplete dropdown --- CHANGELOG.md | 2 +- react/components/CostCentersAutocomplete.tsx | 1 + react/components/OrganizationsAutocomplete.tsx | 1 + react/package.json | 2 +- react/yarn.lock | 6 +++--- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2ed1d1..2b0930f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed -- Implement cost center autocomplete and lazyload in organization autocomplete +- Implement cost center autocomplete, lazyload in organization autocomplete and maxHeight prop ### Fixed diff --git a/react/components/CostCentersAutocomplete.tsx b/react/components/CostCentersAutocomplete.tsx index baff58e..4770a45 100644 --- a/react/components/CostCentersAutocomplete.tsx +++ b/react/components/CostCentersAutocomplete.tsx @@ -63,6 +63,7 @@ const CostCenterAutocomplete = ({ onChange, organizationId }: Props) => { }, [debouncedSearchTerm]) const options = { + maxHeight: 200, onSelect: onChange, loading, value: data?.getCostCentersByOrganizationId?.data?.map( diff --git a/react/components/OrganizationsAutocomplete.tsx b/react/components/OrganizationsAutocomplete.tsx index f0829aa..df608ba 100644 --- a/react/components/OrganizationsAutocomplete.tsx +++ b/react/components/OrganizationsAutocomplete.tsx @@ -105,6 +105,7 @@ const OrganizationsAutocomplete = ({ onChange, organizationId }: Props) => { const options = useMemo( () => ({ + maxHeight: 250, onSelect: onChange, loading, value: values, diff --git a/react/package.json b/react/package.json index 581699e..00c58d6 100644 --- a/react/package.json +++ b/react/package.json @@ -18,7 +18,7 @@ "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.132.4/public/@types/vtex.render-runtime", "vtex.storefront-permissions": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.storefront-permissions@1.23.0/public/@types/vtex.storefront-permissions", "vtex.storefront-permissions-components": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.storefront-permissions-components@0.2.0/public/@types/vtex.storefront-permissions-components", - "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.3/public/@types/vtex.styleguide" + "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.13/public/@types/vtex.styleguide" }, "dependencies": { "faker": "^4.1.0", diff --git a/react/yarn.lock b/react/yarn.lock index 2b20dac..e0c8167 100644 --- a/react/yarn.lock +++ b/react/yarn.lock @@ -500,9 +500,9 @@ typescript@3.9.7: version "1.23.0" resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.storefront-permissions@1.23.0/public/@types/vtex.storefront-permissions#2a12e48a1b630d6d9cd64115572bcae55a7aa756" -"vtex.styleguide@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.3/public/@types/vtex.styleguide": - version "9.146.3" - resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.3/public/@types/vtex.styleguide#05558160f29cd8f4aefe419844a4bd66e2b3fdbb" +"vtex.styleguide@http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.13/public/@types/vtex.styleguide": + version "9.146.13" + resolved "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.146.13/public/@types/vtex.styleguide#f4ccbc54621bf5114ddd115b6032ae320f2eba55" zen-observable-ts@^0.8.21: version "0.8.21" From 6cd6d8473e116432075152c32b6b387220605376 Mon Sep 17 00:00:00 2001 From: Josmar Soares Trigueiro Junior Date: Wed, 25 Sep 2024 13:47:07 -0300 Subject: [PATCH 05/11] fix: remove lint rule comment --- react/components/OrganizationsAutocomplete.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/react/components/OrganizationsAutocomplete.tsx b/react/components/OrganizationsAutocomplete.tsx index d780bd8..250d777 100644 --- a/react/components/OrganizationsAutocomplete.tsx +++ b/react/components/OrganizationsAutocomplete.tsx @@ -70,14 +70,14 @@ const OrganizationsAutocomplete = ({ onChange, organizationId }: Props) => { }, [debouncedTerm, refetch]) useEffect(() => { - // eslint-disable-next-line vtex/prefer-early-return - if (organization?.getOrganizationById) { - const { name, id } = organization.getOrganizationById + if (!organization?.getOrganizationById) { + return + } - setTerm(name) + const { name, id } = organization.getOrganizationById - onChange({ value: id, label: name }) - } + setTerm(name) + onChange({ value: id, label: name }) }, [organization, onChange]) useEffect(() => { From 628de2a7ff38ac9ecdd2d1a1a9808858ee8db108 Mon Sep 17 00:00:00 2001 From: Josmar Soares Trigueiro Junior Date: Wed, 25 Sep 2024 13:47:45 -0300 Subject: [PATCH 06/11] fix: remove lint rule comment --- react/components/OrganizationsAutocomplete.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/react/components/OrganizationsAutocomplete.tsx b/react/components/OrganizationsAutocomplete.tsx index 250d777..51f380e 100644 --- a/react/components/OrganizationsAutocomplete.tsx +++ b/react/components/OrganizationsAutocomplete.tsx @@ -93,7 +93,6 @@ const OrganizationsAutocomplete = ({ onChange, organizationId }: Props) => { } }, [data]) - const onClear = useCallback(() => { setTerm('') @@ -126,7 +125,6 @@ const OrganizationsAutocomplete = ({ onChange, organizationId }: Props) => { } }, [term]) - const input = useMemo( () => ({ onChange: (_term: string) => setTerm(_term), From e37007c7912b990a77c0385aca4f6b42b5d4822f Mon Sep 17 00:00:00 2001 From: Josmar Soares Trigueiro Junior Date: Wed, 25 Sep 2024 14:04:31 -0300 Subject: [PATCH 07/11] doc: update change log file --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22879bf..fdb7848 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed - Run schedule job only on saturday -======= + ## [0.3.2] - 2024-08-22 ### Fixed From 884a6c57c63b40a7e920ee0b584c4ccd34246b77 Mon Sep 17 00:00:00 2001 From: Josmar Soares Trigueiro Junior Date: Wed, 25 Sep 2024 14:35:28 -0300 Subject: [PATCH 08/11] fix: rename the method GET_COST --- react/components/CostCentersAutocomplete.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/react/components/CostCentersAutocomplete.tsx b/react/components/CostCentersAutocomplete.tsx index 4770a45..d70ea6e 100644 --- a/react/components/CostCentersAutocomplete.tsx +++ b/react/components/CostCentersAutocomplete.tsx @@ -4,7 +4,7 @@ import { AutocompleteInput } from 'vtex.styleguide' import { useIntl } from 'react-intl' import { messages } from './customers-admin' -import GET_COST from '../queries/costCentersByOrg.gql' +import GET_COST_CENTER_BY_ORG from '../queries/costCentersByOrg.gql' interface Props { onChange: (value: { value: string | null; label: string }) => void @@ -25,7 +25,7 @@ const CostCenterAutocomplete = ({ onChange, organizationId }: Props) => { const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(costCenterTextInput) - const { data, loading, refetch } = useQuery(GET_COST, { + const { data, loading, refetch } = useQuery(GET_COST_CENTER_BY_ORG, { variables: { ...initialState, id: organizationId, From b528e25a929458c76901fc45216a02e0942176f3 Mon Sep 17 00:00:00 2001 From: Josmar Soares Trigueiro Junior Date: Wed, 25 Sep 2024 15:05:00 -0300 Subject: [PATCH 09/11] fix: remove set state --- react/components/OrganizationsAutocomplete.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/react/components/OrganizationsAutocomplete.tsx b/react/components/OrganizationsAutocomplete.tsx index 51f380e..c030d15 100644 --- a/react/components/OrganizationsAutocomplete.tsx +++ b/react/components/OrganizationsAutocomplete.tsx @@ -115,7 +115,6 @@ const OrganizationsAutocomplete = ({ onChange, organizationId }: Props) => { useEffect(() => { if (term && term.length > 1) { - setHasChanged(true) refetch({ ...initialState, search: term, From 64463c8a9381a84c16c8b8a38e1bdcceae3942c4 Mon Sep 17 00:00:00 2001 From: Josmar Soares Trigueiro Junior Date: Wed, 25 Sep 2024 16:39:40 -0300 Subject: [PATCH 10/11] fix: add const to storage a delay value --- react/components/CostCentersAutocomplete.tsx | 3 ++- react/components/OrganizationsAutocomplete.tsx | 3 ++- react/constants/debounceDelay.ts | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 react/constants/debounceDelay.ts diff --git a/react/components/CostCentersAutocomplete.tsx b/react/components/CostCentersAutocomplete.tsx index d70ea6e..bdcbe66 100644 --- a/react/components/CostCentersAutocomplete.tsx +++ b/react/components/CostCentersAutocomplete.tsx @@ -5,6 +5,7 @@ import { useIntl } from 'react-intl' import { messages } from './customers-admin' import GET_COST_CENTER_BY_ORG from '../queries/costCentersByOrg.gql' +import { SEARCH_TERM_DELAY_MS } from '../constants/debounceDelay' interface Props { onChange: (value: { value: string | null; label: string }) => void @@ -43,7 +44,7 @@ const CostCenterAutocomplete = ({ onChange, organizationId }: Props) => { useEffect(() => { const handler = setTimeout(() => { setDebouncedSearchTerm(costCenterTextInput) - }, 500) // 500ms delay + }, SEARCH_TERM_DELAY_MS) // 500ms delay return () => { clearTimeout(handler) diff --git a/react/components/OrganizationsAutocomplete.tsx b/react/components/OrganizationsAutocomplete.tsx index c030d15..9ae4489 100644 --- a/react/components/OrganizationsAutocomplete.tsx +++ b/react/components/OrganizationsAutocomplete.tsx @@ -6,6 +6,7 @@ import { useIntl } from 'react-intl' import { messages } from './customers-admin' import GET_ORGANIZATIONS from '../queries/listOrganizations.gql' import GET_ORGANIZATION_BY_ID from '../queries/getOrganization.graphql' +import { SEARCH_TERM_DELAY_MS } from '../constants/debounceDelay' const initialState = { status: ['active', 'on-hold', 'inactive'], @@ -50,7 +51,7 @@ const OrganizationsAutocomplete = ({ onChange, organizationId }: Props) => { useEffect(() => { const handler = setTimeout(() => { setDebouncedTerm(term) - }, 500) // 500ms delay + }, SEARCH_TERM_DELAY_MS) // 500ms delay return () => clearTimeout(handler) }, [term]) diff --git a/react/constants/debounceDelay.ts b/react/constants/debounceDelay.ts new file mode 100644 index 0000000..029db33 --- /dev/null +++ b/react/constants/debounceDelay.ts @@ -0,0 +1 @@ +export const SEARCH_TERM_DELAY_MS = 500 From baa375ab4970f7066e4106c30aa14e119994f74d Mon Sep 17 00:00:00 2001 From: Enzo Mercanti <131273915+enzomerca@users.noreply.github.com> Date: Thu, 26 Sep 2024 08:33:36 -0300 Subject: [PATCH 11/11] Update CHANGELOG.md --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdb7848..6263c1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,10 +11,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Implement cost center autocomplete, lazyload in organization autocomplete and maxHeight prop -### Fixed - -- Run schedule job only on saturday - ## [0.3.2] - 2024-08-22 ### Fixed