From af54f3a5b2d2f9e29445c31f0fac3d164effef9d Mon Sep 17 00:00:00 2001 From: Lucieo Date: Thu, 22 Feb 2024 19:23:47 +0000 Subject: [PATCH] Place campaign listing history (#79) * History on campaigns * Require selected campaign * Fix import --- .../application/controllers/application.js | 5 +- .../1.0.0/full_documentation.json | 2 +- web/components/Account/AccountMenu.tsx | 16 ++-- ...ceData.tsx => ApplicationPlaceFetcher.tsx} | 14 ++-- .../Place/ApplicationPlaceList.tsx | 2 +- .../Place/ApplicationPlaceListItem.tsx | 37 ++++----- .../ApplicationPlaceHelper.tsx | 32 +++++--- .../ApplicationsHelpers/ClosedCampaign.tsx | 20 +++++ .../ConfirmSelections.tsx | 1 - .../MissingSelections.tsx | 0 .../ValidatedSelections.tsx | 0 .../CampaignSelector/CampaignSelector.tsx | 49 ++++++++++++ .../CampaignSelectorField.tsx | 51 ++++++++++++ .../DisponibilitiesSelector.tsx | 64 +++++++++++++++ .../DisponibilitiesSelectorFields.tsx} | 23 +++--- .../Place/Selectors/SelectMenu.tsx | 44 ----------- web/components/Campaign/CampaignContext.tsx | 9 ++- web/components/Campaign/CampaignProvider.tsx | 59 ++++++++++---- .../Places/PlacesListCampaignHelper.tsx | 14 +--- web/components/Home/HomePlaces.tsx | 4 +- web/hooks/useCampaigns.ts | 8 +- web/hooks/useMyApplications.ts | 4 +- web/hooks/useMyPlaces.tsx | 4 +- web/pages/compte/candidatures/index.tsx | 78 ++++--------------- web/pages/compte/mes-candidatures/index.tsx | 1 + web/public/locales/fr/application.json | 9 ++- 26 files changed, 340 insertions(+), 210 deletions(-) rename web/components/Account/Application/Place/{ApplicationPlaceData.tsx => ApplicationPlaceFetcher.tsx} (73%) rename web/components/Account/Application/Place/{SelectionHelpers => ApplicationsHelpers}/ApplicationPlaceHelper.tsx (51%) create mode 100644 web/components/Account/Application/Place/ApplicationsHelpers/ClosedCampaign.tsx rename web/components/Account/Application/Place/{SelectionHelpers => ApplicationsHelpers}/ConfirmSelections.tsx (97%) rename web/components/Account/Application/Place/{SelectionHelpers => ApplicationsHelpers}/MissingSelections.tsx (100%) rename web/components/Account/Application/Place/{SelectionHelpers => ApplicationsHelpers}/ValidatedSelections.tsx (100%) create mode 100644 web/components/Account/Application/Place/CampaignSelector/CampaignSelector.tsx create mode 100644 web/components/Account/Application/Place/CampaignSelector/CampaignSelectorField.tsx create mode 100644 web/components/Account/Application/Place/DisponibilitiesSelector/DisponibilitiesSelector.tsx rename web/components/Account/Application/Place/{Selectors/ApplicationSelector.tsx => DisponibilitiesSelector/DisponibilitiesSelectorFields.tsx} (79%) delete mode 100644 web/components/Account/Application/Place/Selectors/SelectMenu.tsx diff --git a/back/api/application/controllers/application.js b/back/api/application/controllers/application.js index 7783800b..04e92f62 100644 --- a/back/api/application/controllers/application.js +++ b/back/api/application/controllers/application.js @@ -15,10 +15,7 @@ module.exports = { return strapi .query("application") .find( - { - ...query, - _sort: "disponibility.start:desc", - }, + query, populate ) .then((res) => { diff --git a/back/extensions/documentation/documentation/1.0.0/full_documentation.json b/back/extensions/documentation/documentation/1.0.0/full_documentation.json index 15d438a5..15cdf43a 100644 --- a/back/extensions/documentation/documentation/1.0.0/full_documentation.json +++ b/back/extensions/documentation/documentation/1.0.0/full_documentation.json @@ -14,7 +14,7 @@ "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" }, - "x-generation-date": "02/22/2024 1:55:11 PM" + "x-generation-date": "02/22/2024 6:38:10 PM" }, "x-strapi-config": { "path": "/documentation", diff --git a/web/components/Account/AccountMenu.tsx b/web/components/Account/AccountMenu.tsx index 73a861f4..95cc82ae 100644 --- a/web/components/Account/AccountMenu.tsx +++ b/web/components/Account/AccountMenu.tsx @@ -70,8 +70,8 @@ const getApplicationsItems = ({ ], }) -const getPlaceItems = (isCampaignMode) => ({ - title: isCampaignMode ? 'solidarity' : 'dashboard', +const getPlaceItems = (hasCampaigns) => ({ + title: hasCampaigns ? 'solidarity' : 'dashboard', items: [ { icon: , @@ -132,7 +132,7 @@ const AccountMenu = ({ user }: { user: UsersPermissionsUser }) => { const isComplete = useUserIsComplete(user) const { data: notifs } = useMyNotifications() - const { currentCampaign, isCampaignPlace } = useCampaignContext() + const { currentCampaign, allPlaceCampaigns } = useCampaignContext() const applicationItems = useMemo( () => getApplicationsItems({ @@ -145,13 +145,16 @@ const AccountMenu = ({ user }: { user: UsersPermissionsUser }) => { [currentCampaign, user?.type], ) - const placeItems = useMemo(() => getPlaceItems(isCampaignPlace), [ - isCampaignPlace, + const placeItems = useMemo(() => getPlaceItems(allPlaceCampaigns?.length), [ + allPlaceCampaigns?.length, ]) const companyItems = useMemo( () => getCompanyItems(Boolean(currentCampaign)), [currentCampaign], ) + const displayApplications = + (user?.type === 'place' && allPlaceCampaigns?.length) || + (user?.type === 'company' && currentCampaign) const displayMenu = ({ title, items, translationParams = {} }) => { const isDisactivated = !isComplete && title === 'dashboard' @@ -241,8 +244,7 @@ const AccountMenu = ({ user }: { user: UsersPermissionsUser }) => { {user?.confirmed && user?.accepted && displayMenu(user?.type === 'company' ? companyItems : placeItems)} - {((user?.type === 'place' && isCampaignPlace) || currentCampaign) && - displayMenu(applicationItems)} + {displayApplications && displayMenu(applicationItems)} {displayMenu(accountItems)} diff --git a/web/components/Account/Application/Place/ApplicationPlaceData.tsx b/web/components/Account/Application/Place/ApplicationPlaceFetcher.tsx similarity index 73% rename from web/components/Account/Application/Place/ApplicationPlaceData.tsx rename to web/components/Account/Application/Place/ApplicationPlaceFetcher.tsx index 44d40ec1..cd6dc2b6 100644 --- a/web/components/Account/Application/Place/ApplicationPlaceData.tsx +++ b/web/components/Account/Application/Place/ApplicationPlaceFetcher.tsx @@ -1,23 +1,23 @@ -import useCampaignContext from '~components/Campaign/useCampaignContext' import { useMyApplications } from '~hooks/useMyApplications' import ApplicationPlaceList from '~components/Account/Application/Place/ApplicationPlaceList' import InfoPlaceApplications from '~components/Account/Info/InfoPlaceApplications' import Loading from '~components/Loading' import { useCurrentUser } from '~hooks/useCurrentUser' import { useEffect } from 'react' +import { useRouter } from 'next/router' -const ApplicationPlaceData = ({ searchParams }) => { +const ApplicationPlaceFetcher = ({ searchParams }) => { const { data: user } = useCurrentUser() - const { currentCampaign } = useCampaignContext() - + const { query } = useRouter() const { data: applications, isLoading, refetch, isFetching, } = useMyApplications({ - campaignId: currentCampaign?.id, - searchParams, + name: ['myApplications', searchParams.disponibility_eq as string], + campaignId: query.campaign as string, + searchParams: { ...searchParams, _sort: 'company.structureName:asc' }, options: { enabled: Boolean(searchParams?.disponibility_eq) && @@ -40,4 +40,4 @@ const ApplicationPlaceData = ({ searchParams }) => { ) } -export default ApplicationPlaceData +export default ApplicationPlaceFetcher diff --git a/web/components/Account/Application/Place/ApplicationPlaceList.tsx b/web/components/Account/Application/Place/ApplicationPlaceList.tsx index e8deff2f..ad184d02 100644 --- a/web/components/Account/Application/Place/ApplicationPlaceList.tsx +++ b/web/components/Account/Application/Place/ApplicationPlaceList.tsx @@ -13,7 +13,7 @@ import Chevron from 'public/assets/img/chevron-down.svg' import Cell from '~components/Account/Booking/Cell' import useCampaignContext from '~components/Campaign/useCampaignContext' import ApplicationPlaceListItem from '~components/Account/Application/Place/ApplicationPlaceListItem' -import ApplicationPlaceHelper from '~components/Account/Application/Place/SelectionHelpers/ApplicationPlaceHelper' +import ApplicationPlaceHelper from '~components/Account/Application/Place/ApplicationsHelpers/ApplicationPlaceHelper' interface Props { applications: Application[] diff --git a/web/components/Account/Application/Place/ApplicationPlaceListItem.tsx b/web/components/Account/Application/Place/ApplicationPlaceListItem.tsx index acc238d2..7f2fa069 100644 --- a/web/components/Account/Application/Place/ApplicationPlaceListItem.tsx +++ b/web/components/Account/Application/Place/ApplicationPlaceListItem.tsx @@ -1,34 +1,24 @@ import React, { Fragment } from 'react' import { Application } from '~typings/api' -import { Text, Button, IconButton, ButtonGroup, HStack } from '@chakra-ui/react' +import { Text, Button, IconButton, HStack } from '@chakra-ui/react' import { useTranslation } from 'next-i18next' import Cell from '~components/Account/Booking/Cell' -import { client } from '~api/client-api' -import useToast from '~hooks/useToast' -import { useQueryClient } from 'react-query' import Link from '~components/Link' import useCampaignContext from '~components/Campaign/useCampaignContext' import DownloadApplication from 'public/assets/img/downloadApplication.svg' +import { useRouter } from 'next/router' interface Props { application: Application } const ApplicationPlaceListItem = ({ application }: Props) => { - const { currentCampaign } = useCampaignContext() - const { errorToast, successToast } = useToast() + const { allPlaceCampaigns } = useCampaignContext() + const { query } = useRouter() + const selectedCampaign = allPlaceCampaigns?.find( + (c) => c.id.toString() === query.campaign.toString(), + ) const { t } = useTranslation('application') - const queryClient = useQueryClient() - - const onDelete = async () => { - try { - await client.applications.applicationsDelete(application.id) - successToast(t('company.delete_success')) - queryClient.refetchQueries(['myApplications']) - } catch (e) { - errorToast(t('company.delete_error')) - } - } return ( @@ -55,8 +45,9 @@ const ApplicationPlaceListItem = ({ application }: Props) => { {application?.creation_title} - {currentCampaign?.mode === 'preselections' && ( - + + + {['preselections', 'closed']?.includes(selectedCampaign?.mode) ? ( { aria-label="dowload" borderColor="rgba(98,103,130, 0.6)" icon={} + isDisabled={selectedCampaign?.mode === 'closed'} /> - - )} + ) : ( + '' + )} + ) } diff --git a/web/components/Account/Application/Place/SelectionHelpers/ApplicationPlaceHelper.tsx b/web/components/Account/Application/Place/ApplicationsHelpers/ApplicationPlaceHelper.tsx similarity index 51% rename from web/components/Account/Application/Place/SelectionHelpers/ApplicationPlaceHelper.tsx rename to web/components/Account/Application/Place/ApplicationsHelpers/ApplicationPlaceHelper.tsx index 0df1b392..8fc26a86 100644 --- a/web/components/Account/Application/Place/SelectionHelpers/ApplicationPlaceHelper.tsx +++ b/web/components/Account/Application/Place/ApplicationsHelpers/ApplicationPlaceHelper.tsx @@ -1,38 +1,50 @@ -import { useTranslation } from 'next-i18next' import useCampaignContext from '~components/Campaign/useCampaignContext' -import MissingSelections from '~components/Account/Application/Place/SelectionHelpers/MissingSelections' -import ConfirmSelections from '~components/Account/Application/Place/SelectionHelpers/ConfirmSelections' -import ValidatedSelections from '~components/Account/Application/Place/SelectionHelpers/ValidatedSelections' +import MissingSelections from '~components/Account/Application/Place/ApplicationsHelpers/MissingSelections' +import ConfirmSelections from '~components/Account/Application/Place/ApplicationsHelpers/ConfirmSelections' +import ValidatedSelections from '~components/Account/Application/Place/ApplicationsHelpers/ValidatedSelections' import { Application } from '~typings/api' +import { useRouter } from 'next/router' +import ClosedCampaign from '~components/Account/Application/Place/ApplicationsHelpers/ClosedCampaign' const ApplicationPlaceHelper = ({ applications, }: { applications: Application[] }) => { - const { t } = useTranslation('application') - const { currentCampaign } = useCampaignContext() + const { allPlaceCampaigns } = useCampaignContext() const preselections = applications?.filter( (application) => application?.status === 'preselected', ).length + + const { query } = useRouter() + const selectedCampaign = allPlaceCampaigns?.find( + (c) => c.id.toString() === query.campaign.toString(), + ) const missingPreselections = - currentCampaign?.preselections_max - preselections + selectedCampaign?.preselections_max - preselections const validatedApplications = applications?.filter( (application) => application?.status === 'confirmed', ).length - if (currentCampaign?.mode === 'preselections' && validatedApplications > 0) { + if (selectedCampaign?.mode === 'preselections' && validatedApplications > 0) { return } - if (currentCampaign?.mode === 'preselections' && missingPreselections > 0) { + if (selectedCampaign?.mode === 'preselections' && missingPreselections > 0) { return } - if (currentCampaign?.mode === 'preselections' && missingPreselections === 0) { + if ( + selectedCampaign?.mode === 'preselections' && + missingPreselections === 0 + ) { return } + if (selectedCampaign?.mode === 'closed') { + return + } + return null } diff --git a/web/components/Account/Application/Place/ApplicationsHelpers/ClosedCampaign.tsx b/web/components/Account/Application/Place/ApplicationsHelpers/ClosedCampaign.tsx new file mode 100644 index 00000000..52e6a411 --- /dev/null +++ b/web/components/Account/Application/Place/ApplicationsHelpers/ClosedCampaign.tsx @@ -0,0 +1,20 @@ +import { Box, HStack, Text } from '@chakra-ui/react' +import PreselectionsValidated from 'public/assets/img/preselectionsValidated.svg' +import { useTranslation } from 'next-i18next' + +const ClosedCampaign = () => { + const { t } = useTranslation('application') + + return ( + + + + + {t('place.helper.closed_campaign')} + + + + ) +} + +export default ClosedCampaign diff --git a/web/components/Account/Application/Place/SelectionHelpers/ConfirmSelections.tsx b/web/components/Account/Application/Place/ApplicationsHelpers/ConfirmSelections.tsx similarity index 97% rename from web/components/Account/Application/Place/SelectionHelpers/ConfirmSelections.tsx rename to web/components/Account/Application/Place/ApplicationsHelpers/ConfirmSelections.tsx index 91ffbfb4..3e8829a4 100644 --- a/web/components/Account/Application/Place/SelectionHelpers/ConfirmSelections.tsx +++ b/web/components/Account/Application/Place/ApplicationsHelpers/ConfirmSelections.tsx @@ -1,5 +1,4 @@ import { Box, Button, HStack, Stack, Text } from '@chakra-ui/react' -import { format } from '~utils/date' import PreselectionsWarning from 'public/assets/img/preselectionsWarning.svg' import { useTranslation } from 'next-i18next' diff --git a/web/components/Account/Application/Place/SelectionHelpers/MissingSelections.tsx b/web/components/Account/Application/Place/ApplicationsHelpers/MissingSelections.tsx similarity index 100% rename from web/components/Account/Application/Place/SelectionHelpers/MissingSelections.tsx rename to web/components/Account/Application/Place/ApplicationsHelpers/MissingSelections.tsx diff --git a/web/components/Account/Application/Place/SelectionHelpers/ValidatedSelections.tsx b/web/components/Account/Application/Place/ApplicationsHelpers/ValidatedSelections.tsx similarity index 100% rename from web/components/Account/Application/Place/SelectionHelpers/ValidatedSelections.tsx rename to web/components/Account/Application/Place/ApplicationsHelpers/ValidatedSelections.tsx diff --git a/web/components/Account/Application/Place/CampaignSelector/CampaignSelector.tsx b/web/components/Account/Application/Place/CampaignSelector/CampaignSelector.tsx new file mode 100644 index 00000000..6ac25309 --- /dev/null +++ b/web/components/Account/Application/Place/CampaignSelector/CampaignSelector.tsx @@ -0,0 +1,49 @@ +import { HStack, Text } from '@chakra-ui/react' +import { useTranslation } from 'next-i18next' +import { useRouter } from 'next/router' +import { useEffect } from 'react' +import CampaignSelectorField from '~components/Account/Application/Place/CampaignSelector/CampaignSelectorField' +import useCampaignContext from '~components/Campaign/useCampaignContext' +import { ROUTE_ACCOUNT_APPLICATIONS } from '~constants' + +const CampaignSelector = ({ children }) => { + const router = useRouter() + const { campaign } = router.query + const { allPlaceCampaigns } = useCampaignContext() + const { t } = useTranslation('application') + + // If no campaign defined in url select last one + useEffect(() => { + if (!campaign && allPlaceCampaigns) { + router.push( + `${ROUTE_ACCOUNT_APPLICATIONS}?campaign=${allPlaceCampaigns?.[0]?.id}`, + ) + } + }, [allPlaceCampaigns]) + + const selectedCampaign = allPlaceCampaigns?.find( + (c) => c.id?.toString() === campaign?.toString(), + ) + + if (!selectedCampaign) return null + + return ( + <> + + + {t('place.title')} + + + + {selectedCampaign && children} + + ) +} + +export default CampaignSelector diff --git a/web/components/Account/Application/Place/CampaignSelector/CampaignSelectorField.tsx b/web/components/Account/Application/Place/CampaignSelector/CampaignSelectorField.tsx new file mode 100644 index 00000000..edf0c879 --- /dev/null +++ b/web/components/Account/Application/Place/CampaignSelector/CampaignSelectorField.tsx @@ -0,0 +1,51 @@ +import { Select, Text } from '@chakra-ui/react' +import { useTranslation } from 'next-i18next' +import { useRouter } from 'next/router' +import useCampaignContext from '~components/Campaign/useCampaignContext' + +const CampaignSelectorField = () => { + const { t } = useTranslation('application') + const router = useRouter() + const { allPlaceCampaigns, isLoadingAllPlaceCampaigns } = useCampaignContext() + if (isLoadingAllPlaceCampaigns || !allPlaceCampaigns) { + return null + } + + if (allPlaceCampaigns.length === 1) { + return ( + + {t('place.title_single_tag', { title: allPlaceCampaigns[0]?.title })} + + ) + } + return ( + + ) +} + +export default CampaignSelectorField diff --git a/web/components/Account/Application/Place/DisponibilitiesSelector/DisponibilitiesSelector.tsx b/web/components/Account/Application/Place/DisponibilitiesSelector/DisponibilitiesSelector.tsx new file mode 100644 index 00000000..a9864cbc --- /dev/null +++ b/web/components/Account/Application/Place/DisponibilitiesSelector/DisponibilitiesSelector.tsx @@ -0,0 +1,64 @@ +import { HStack, Box, Text } from '@chakra-ui/react' +import { useTranslation } from 'next-i18next' +import { useRouter } from 'next/router' +import { useEffect, useState } from 'react' +import ApplicationPlaceFetcher from '~components/Account/Application/Place/ApplicationPlaceFetcher' +import ApplicationSelector from '~components/Account/Application/Place/DisponibilitiesSelector/DisponibilitiesSelectorFields' +import { useMyPlaces } from '~hooks/useMyPlaces' + +const DisponibilitiesSelector = () => { + const { t } = useTranslation('application') + const [searchParams, setSearchParams] = useState() + const { query } = useRouter() + const { data: places, isLoading, isFetching } = useMyPlaces( + { + 'disponibilities.campaign': query.campaign, + }, + { enabled: Boolean(query.campaign) }, + ['myPlaces', query.campaign as string], + ) + + console.log(query.campaign, places) + + useEffect(() => { + const search = {} + if (query.disponibility) { + search['disponibility_eq'] = Number(query.disponibility) + } + if (query.espace) { + search['espace_eq'] = Number(query.espace) + } + setSearchParams(search) + }, [query]) + + if (!places?.length) { + return ( + + {t('place.no_places')} + + ) + } + + return ( + <> + {Boolean(places?.length) && !isLoading && !isFetching && ( + + ({ + ...p, + disponibilities: p.disponibilities?.filter( + (d) => d.campaign?.toString() === query.campaign.toString(), + ), + }))} + /> + + )} + + {Boolean(searchParams && Object.keys(searchParams)?.length) && ( + + )} + + ) +} + +export default DisponibilitiesSelector diff --git a/web/components/Account/Application/Place/Selectors/ApplicationSelector.tsx b/web/components/Account/Application/Place/DisponibilitiesSelector/DisponibilitiesSelectorFields.tsx similarity index 79% rename from web/components/Account/Application/Place/Selectors/ApplicationSelector.tsx rename to web/components/Account/Application/Place/DisponibilitiesSelector/DisponibilitiesSelectorFields.tsx index 2de3da91..4ae329f2 100644 --- a/web/components/Account/Application/Place/Selectors/ApplicationSelector.tsx +++ b/web/components/Account/Application/Place/DisponibilitiesSelector/DisponibilitiesSelectorFields.tsx @@ -1,31 +1,29 @@ import { HStack, Select } from '@chakra-ui/react' import { useRouter } from 'next/router' import { useEffect, useMemo, useState } from 'react' -import useCampaignContext from '~components/Campaign/useCampaignContext' import { ROUTE_ACCOUNT_APPLICATIONS } from '~constants' import { Espace } from '~typings/api' import { format } from '~utils/date' const ApplicationSelector = ({ places }: { places: Espace[] }) => { - const { currentCampaign } = useCampaignContext() - const router = useRouter() const { espace: queryEspace, disponibility: queryDisponibility, + campaign, } = router.query const [espace, setEspace] = useState(queryEspace) const [disponibility, setDisponibility] = useState(queryDisponibility) const initState = () => { - const espace = queryEspace || places?.[0]?.id - const disponibility = places?.[0]?.id + const espace = places?.[0]?.id + const disponibility = places?.[0]?.disponibilities[0]?.id setEspace(espace) setDisponibility(disponibility) if (!queryEspace || !queryDisponibility) { router.push({ pathname: router.pathname, - query: { espace, disponibility }, + query: { ...router.query, espace, disponibility }, }) } } @@ -42,15 +40,16 @@ const ApplicationSelector = ({ places }: { places: Espace[] }) => { const getDispoOptions = (espace) => { if (espace) { - return places - ?.find((p) => p.id.toString() === espace.toString()) - ?.disponibilities //@ts-expect-error - ?.filter((d) => d?.campaign === currentCampaign?.id) + return places?.find((p) => p.id.toString() === espace.toString()) + ?.disponibilities } return [] } - const dispoOptions = useMemo(() => getDispoOptions(espace), [espace]) + const dispoOptions = useMemo(() => getDispoOptions(espace), [ + espace, + campaign, + ]) return ( @@ -65,7 +64,7 @@ const ApplicationSelector = ({ places }: { places: Espace[] }) => { const disponibility = getDispoOptions(e.target.value)[0]?.id router.push({ pathname: router.pathname, - query: { espace: e.target.value, disponibility }, + query: { ...router.query, espace: e.target.value, disponibility }, }) }} > diff --git a/web/components/Account/Application/Place/Selectors/SelectMenu.tsx b/web/components/Account/Application/Place/Selectors/SelectMenu.tsx deleted file mode 100644 index fb5436bc..00000000 --- a/web/components/Account/Application/Place/Selectors/SelectMenu.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { useTranslation } from 'next-i18next' -import Select from 'react-select' - -const colourStyles = { - container: (styles) => ({ - ...styles, - width: '100%', - }), - valueContainer: (styles) => ({ - ...styles, - padding: '2px 8px 2px 3px', - }), - control: (styles, state) => ({ - ...styles, - backgroundColor: 'white', - width: '100%', - }), -} - -const SelectMenu = ({ - options = [], - onChange, - value, -}: { - options: any - onChange: any - value: string -}) => { - const { t } = useTranslation('application') - return ( -