Skip to content

Commit

Permalink
(PC-32278)[PRO] fix: support offerer change
Browse files Browse the repository at this point in the history
  • Loading branch information
asaez-pass committed Nov 19, 2024
1 parent 1b4f43f commit cb7ac78
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 146 deletions.
25 changes: 25 additions & 0 deletions pro/src/pages/Reimbursements/Income/Income.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,31 @@
}
}

&-by-venue {
max-width: rem.torem(300px);

// Selected tags must be displayed to occupy available width.
// Width must be calculated based on the viewport width minus
// various padding values since they belong to a component that
// is not a direct child of the main content container and
// has a restricted width.
// Check Layout.module.scss for reference.
&-selected-tags {
width: calc(100vw - rem.torem(24px) * 2);

@media (min-width: size.$mobile) {
width: calc(100vw - (size.$main-content-padding) * 2)
}

@media (min-width: size.$laptop) {
// 16.75rem is the width of the sidebar
// On very large screens the layout does not exceed 90rem.
width: calc(100vw - (size.$main-content-padding * 2) - 16.75rem);
max-width: calc(90rem - 4rem - 16.75rem);
}
}
}

&-by-year {
display: flex;
flex-flow: row wrap;
Expand Down
122 changes: 80 additions & 42 deletions pro/src/pages/Reimbursements/Income/Income.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,99 @@
import classnames from 'classnames'
import isEqual from 'lodash.isequal'
import { useState, useEffect, useRef, useCallback } from 'react'
import classnames from 'classnames'
import { useState, useEffect, useRef } from 'react'
import { FormikProvider, useFormik } from 'formik'
import * as yup from 'yup'

import { FormLayout } from 'components/FormLayout/FormLayout'
import { Spinner } from 'ui-kit/Spinner/Spinner'
import { SelectAutocomplete } from 'ui-kit/form/SelectAutoComplete/SelectAutocomplete'

import { useVenues, useIncome } from './hooks'
import styles from './Income.module.scss'
import { IncomeError } from './IncomeError/IncomeError'
import { IncomeNoData } from './IncomeNoData/IncomeNoData'
import { IncomeResultsBox } from './IncomeResultsBox/IncomeResultsBox'
import { IncomeVenueSelector } from './IncomeVenueSelector/IncomeVenueSelector'

type VenueFormValues = {
selectedVenues: string[]
'search-selectedVenues': string
}

export const Income = () => {
const venueSelectorRef = useRef<HTMLInputElement>(null)
const firstYearFilterRef = useRef<HTMLButtonElement>(null)

const [selectedVenues, setSelectedVenues] = useState<string[]>([])
const [previouslySelectedOffererId, setPreviouslySelectedOffererId] = useState<number | null>(null)
const [debouncedSelectedVenues, setDebouncedSelectedVenues] = useState<string[]>([])
const [activeYear, setActiveYear] = useState<number>()

const [hasAutofocusedOnce, setHasAutofocusedOnce] = useState(false)

const { areVenuesLoading, venuesApiError, venuesDataReady, venues } =
useVenues()
const {
areVenuesLoading,
venuesApiError,
venuesDataReady,
venues,
selectedOffererId
} = useVenues()
const hasSingleVenue = venuesDataReady && venues.length === 1
const venueValues = venues.map((v) => v.value)

const formik = useFormik<VenueFormValues>({
// SelectAutocomplete has two fields:
// - selectedVenues: for the <select> menu & SelectedValuesTags
// - 'search-selectedVenues': for the search input
initialValues: {
selectedVenues: venueValues,
'search-selectedVenues': '',
},
validationSchema: yup.object().shape({
selectedVenues: yup.array().test({
name: 'selectedVenues',
message: 'Vous devez sélectionner au moins un partenaire',
test: (value) => {
return (value || []).length > 0
},
}),
'search-selectedVenues': yup.string(),
}),
enableReinitialize: true,
onSubmit: () => {},
})

// Handles offerer changes.
useEffect(() => {
const newSelectedVenues = [...formik.values.selectedVenues]
const selectionHasChanged = !isEqual(newSelectedVenues, debouncedSelectedVenues)

if (selectionHasChanged) {
if (selectedOffererId !== previouslySelectedOffererId) {
// Offerer has changed.
setDebouncedSelectedVenues(newSelectedVenues)
setPreviouslySelectedOffererId(selectedOffererId)
} else if (newSelectedVenues.length !== 0) {
// Venues have changed.
const debounced = setTimeout(() => {
setDebouncedSelectedVenues(newSelectedVenues)
setActiveYear(undefined)
}, 1000)
return () => clearTimeout(debounced)
}
}

return
}, [selectedOffererId, formik.values])

const {
isIncomeLoading,
incomeApiError,
incomeDataReady,
incomeByYear,
years,
} = useIncome(selectedVenues)
} = useIncome(debouncedSelectedVenues)
const finalActiveYear = activeYear || years[0]
const activeYearIncome = incomeByYear?.[finalActiveYear] || {}
const activeYearHasData = Object.keys(activeYearIncome).length > 0

useEffect(() => {
if (venuesDataReady && selectedVenues.length === 0) {
setSelectedVenues(venues.map((venue) => venue.value))
}
}, [venuesDataReady, venues, selectedVenues])

useEffect(() => {
if (!hasAutofocusedOnce) {
if (venuesDataReady && venueSelectorRef.current) {
Expand All @@ -58,27 +110,6 @@ export const Income = () => {
}
}, [hasAutofocusedOnce, venuesDataReady, incomeDataReady])

const onChange = useCallback(
(formiklySelectedVenues: string[]) => {
const selectedVenuesWereInitiated = selectedVenues.length > 0
const formiklySelectionIsNotEmpty = formiklySelectedVenues.length > 0
const parentStateIsOutdated = !isEqual(
formiklySelectedVenues,
selectedVenues
)

if (
selectedVenuesWereInitiated &&
formiklySelectionIsNotEmpty &&
parentStateIsOutdated
) {
setSelectedVenues(formiklySelectedVenues)
setActiveYear(undefined)
}
},
[selectedVenues]
)

return (
<>
<div role="status">
Expand All @@ -98,13 +129,20 @@ export const Income = () => {
<>
{!hasSingleVenue && <FormLayout.MandatoryInfo />}
<div className={styles['income-filters']}>
{!hasSingleVenue && (
<IncomeVenueSelector
venues={venues}
onChange={onChange}
refForInput={venueSelectorRef}
/>
)}
<FormikProvider value={formik}>
{!hasSingleVenue && (
<SelectAutocomplete
className={styles['income-filters-by-venue']}
selectedValuesTagsClassName={styles['income-filters-by-venue-selected-tags']}
name="selectedVenues"
label="Partenaire(s) sélectionné(s)"
options={venues}
multi
preventOpenOnFirstFocus
refForInput={venueSelectorRef}
/>
)}
</FormikProvider>
{incomeDataReady && (
<>
{!hasSingleVenue && (
Expand Down

This file was deleted.

This file was deleted.

15 changes: 8 additions & 7 deletions pro/src/pages/Reimbursements/Income/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export const useVenues = () => {
data: selectedOfferer,
error: venuesApiError,
isLoading: areVenuesLoading,
} = useSWR<GetOffererResponseModel | null, string, [string, number | null]>(
[GET_OFFERER_QUERY_KEY, selectedOffererId],
([, offererIdParam]) => offererIdParam ? api.getOfferer(offererIdParam) : null
} = useSWR<GetOffererResponseModel | null, string, [string, number] | null>(
selectedOffererId ? [GET_OFFERER_QUERY_KEY, selectedOffererId] : null,
([, offererIdParam]) => api.getOfferer(offererIdParam),
)

const physicalVenues = getPhysicalVenuesFromOfferer(selectedOfferer)
Expand All @@ -38,20 +38,21 @@ export const useVenues = () => {
venuesApiError,
venuesDataReady,
venues,
selectedOffererId,
}
}

export const useIncome = (selectedVenues: string[]) => {
// Sorting venues makes sure passed query keys are always the same
// so SWR can cache the data correctly.
const selectedVenuesAsNumbers = selectedVenues.sort().map(Number)
const selectedVenuesAsNumbers = selectedVenues.map(Number).sort()
const {
data,
error: incomeApiError,
isLoading: isIncomeLoading,
} = useSWR<StatisticsModel | null, string, [string, number[]]>(
[GET_STATISTICS_QUERY_KEY, selectedVenuesAsNumbers],
([, selectedVenuesParam]) => selectedVenuesParam.length > 0 ? api.getStatistics(selectedVenuesParam) : null,
} = useSWR<StatisticsModel | null, string, [string, number[]] | null>(
selectedVenuesAsNumbers.length > 0 ? [GET_STATISTICS_QUERY_KEY, selectedVenuesAsNumbers] : null,
([, selectedVenuesParam]) => api.getStatistics(selectedVenuesParam),
{ revalidateOnMount: true }
)

Expand Down

0 comments on commit cb7ac78

Please sign in to comment.