diff --git a/back/api/application/config/routes.json b/back/api/application/config/routes.json index 7d690f32..1c71878c 100644 --- a/back/api/application/config/routes.json +++ b/back/api/application/config/routes.json @@ -2,7 +2,7 @@ "routes": [ { "method": "GET", - "path": "/applications/me", + "path": "/applications/me/:campaignId", "handler": "application.myApplications", "config": { "policies": ["global::is-authenticated"], diff --git a/back/api/application/controllers/application.js b/back/api/application/controllers/application.js index 4f39e1bd..7783800b 100644 --- a/back/api/application/controllers/application.js +++ b/back/api/application/controllers/application.js @@ -8,7 +8,8 @@ module.exports = { async myApplications(ctx) { const { id, type } = ctx.state.user; - const query = type === "place" ? { 'disponibility.espace.users_permissions_user.id':id} : { company: id }; + const {query: initialQuery}=ctx.request; + const query = type === "place" ? {...initialQuery, 'disponibility.espace.users_permissions_user.id':id} : {...initialQuery, company: id }; const populate = type==="place"? ['disponibility.espace','company']:['disponibility.espace', 'place','disponibility.espace.users_permissions_user'] return strapi diff --git a/back/api/application/documentation/1.0.0/application.json b/back/api/application/documentation/1.0.0/application.json index 9de5ea80..41d56228 100644 --- a/back/api/application/documentation/1.0.0/application.json +++ b/back/api/application/documentation/1.0.0/application.json @@ -1,6 +1,6 @@ { "paths": { - "/applications/me": { + "/applications/me/{campaignId}": { "get": { "deprecated": false, "description": "Get applications related to current user", @@ -54,7 +54,18 @@ "tags": [ "Application" ], - "parameters": [] + "parameters": [ + { + "name": "campaignId", + "in": "path", + "description": "", + "deprecated": false, + "required": true, + "schema": { + "type": "string" + } + } + ] } }, "/applications": { @@ -853,6 +864,9 @@ "type": "string" } }, + "preselections_max": { + "type": "integer" + }, "published_at": { "type": "string" }, @@ -1166,6 +1180,13 @@ } } }, + "status": { + "type": "string", + "enum": [ + "preselected", + "confirmed" + ] + }, "published_at": { "type": "string", "format": "date-time" @@ -1216,6 +1237,13 @@ "espace": { "type": "string" }, + "status": { + "type": "string", + "enum": [ + "preselected", + "confirmed" + ] + }, "published_at": { "type": "string", "format": "date-time" diff --git a/back/api/application/documentation/1.0.0/overrides/application.json b/back/api/application/documentation/1.0.0/overrides/application.json index 85184a52..cfb6ccfc 100644 --- a/back/api/application/documentation/1.0.0/overrides/application.json +++ b/back/api/application/documentation/1.0.0/overrides/application.json @@ -1,6 +1,6 @@ { "paths": { - "/applications/me": { + "/applications/me/{campaignId}": { "get": { "deprecated": false, "description": "Get applications related to current user", @@ -55,6 +55,153 @@ "Application" ], "parameters": [ + { + "name": "campaignId", + "in": "path", + "description": "", + "deprecated": false, + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "_limit", + "in": "query", + "required": false, + "description": "Maximum number of results possible", + "schema": { + "type": "integer" + }, + "deprecated": false + }, + { + "name": "_sort", + "in": "query", + "required": false, + "description": "Sort according to a specific field.", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_start", + "in": "query", + "required": false, + "description": "Skip a specific number of entries (especially useful for pagination)", + "schema": { + "type": "integer" + }, + "deprecated": false + }, + { + "name": "=", + "in": "query", + "required": false, + "description": "Get entries that matches exactly your input", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_ne", + "in": "query", + "required": false, + "description": "Get records that are not equals to something", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_lt", + "in": "query", + "required": false, + "description": "Get record that are lower than a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_lte", + "in": "query", + "required": false, + "description": "Get records that are lower than or equal to a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_gt", + "in": "query", + "required": false, + "description": "Get records that are greater than a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_gte", + "in": "query", + "required": false, + "description": "Get records that are greater than or equal a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_contains", + "in": "query", + "required": false, + "description": "Get records that contains a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_containss", + "in": "query", + "required": false, + "description": "Get records that contains (case sensitive) a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_in", + "in": "query", + "required": false, + "description": "Get records that matches any value in the array of values", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "deprecated": false + }, + { + "name": "_nin", + "in": "query", + "required": false, + "description": "Get records that doesn't match any value in the array of values", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "deprecated": false + } + ] } diff --git a/back/api/application/models/application.settings.json b/back/api/application/models/application.settings.json index e31dd672..e74379a9 100644 --- a/back/api/application/models/application.settings.json +++ b/back/api/application/models/application.settings.json @@ -59,8 +59,8 @@ "type": "json" }, "campaign": { - "model": "campaign", - "via": "applications" + "via": "applications", + "model": "campaign" }, "company": { "plugin": "users-permissions", @@ -70,6 +70,13 @@ "espace": { "model": "espace", "via": "applications" + }, + "status": { + "type": "enumeration", + "enum": [ + "preselected", + "confirmed" + ] } } } diff --git a/back/api/campaign/documentation/1.0.0/campaign.json b/back/api/campaign/documentation/1.0.0/campaign.json index 09de7479..7dac1418 100644 --- a/back/api/campaign/documentation/1.0.0/campaign.json +++ b/back/api/campaign/documentation/1.0.0/campaign.json @@ -896,6 +896,13 @@ "espace": { "type": "string" }, + "status": { + "type": "string", + "enum": [ + "preselected", + "confirmed" + ] + }, "published_at": { "type": "string" }, @@ -908,6 +915,9 @@ } } }, + "preselections_max": { + "type": "integer" + }, "published_at": { "type": "string", "format": "date-time" @@ -1003,6 +1013,9 @@ "type": "string" } }, + "preselections_max": { + "type": "integer" + }, "published_at": { "type": "string", "format": "date-time" diff --git a/back/api/campaign/models/campaign.settings.json b/back/api/campaign/models/campaign.settings.json index 941a0b4f..ff5bace3 100644 --- a/back/api/campaign/models/campaign.settings.json +++ b/back/api/campaign/models/campaign.settings.json @@ -87,6 +87,9 @@ "applications": { "via": "campaign", "collection": "application" + }, + "preselections_max": { + "type": "integer" } } } diff --git a/back/api/disponibility/documentation/1.0.0/disponibility.json b/back/api/disponibility/documentation/1.0.0/disponibility.json index 15930958..828bf740 100644 --- a/back/api/disponibility/documentation/1.0.0/disponibility.json +++ b/back/api/disponibility/documentation/1.0.0/disponibility.json @@ -1023,6 +1023,9 @@ "type": "string" } }, + "preselections_max": { + "type": "integer" + }, "published_at": { "type": "string" }, @@ -1092,6 +1095,13 @@ "espace": { "type": "string" }, + "status": { + "type": "string", + "enum": [ + "preselected", + "confirmed" + ] + }, "published_at": { "type": "string" }, diff --git a/back/api/espace/controllers/espace.js b/back/api/espace/controllers/espace.js index 4f6b11d2..5e96d424 100644 --- a/back/api/espace/controllers/espace.js +++ b/back/api/espace/controllers/espace.js @@ -22,9 +22,11 @@ const populate = [ module.exports = { async myPlaces(ctx) { + const {query}=ctx.request; const { id } = ctx.state.user; return strapi.query("espace").find( { + ...query, users_permissions_user: id, deleted: false, _sort: "name:asc", diff --git a/back/api/espace/documentation/1.0.0/espace.json b/back/api/espace/documentation/1.0.0/espace.json index 19bea0d2..146e41e4 100644 --- a/back/api/espace/documentation/1.0.0/espace.json +++ b/back/api/espace/documentation/1.0.0/espace.json @@ -1211,6 +1211,13 @@ "espace": { "type": "string" }, + "status": { + "type": "string", + "enum": [ + "preselected", + "confirmed" + ] + }, "published_at": { "type": "string" }, diff --git a/back/api/espace/documentation/1.0.0/overrides/espace.json b/back/api/espace/documentation/1.0.0/overrides/espace.json index 9765e8d9..ccca8547 100644 --- a/back/api/espace/documentation/1.0.0/overrides/espace.json +++ b/back/api/espace/documentation/1.0.0/overrides/espace.json @@ -110,7 +110,144 @@ "tags": [ "Espace" ], - "parameters": [] + "parameters": [ + { + "name": "_limit", + "in": "query", + "required": false, + "description": "Maximum number of results possible", + "schema": { + "type": "integer" + }, + "deprecated": false + }, + { + "name": "_sort", + "in": "query", + "required": false, + "description": "Sort according to a specific field.", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_start", + "in": "query", + "required": false, + "description": "Skip a specific number of entries (especially useful for pagination)", + "schema": { + "type": "integer" + }, + "deprecated": false + }, + { + "name": "=", + "in": "query", + "required": false, + "description": "Get entries that matches exactly your input", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_ne", + "in": "query", + "required": false, + "description": "Get records that are not equals to something", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_lt", + "in": "query", + "required": false, + "description": "Get record that are lower than a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_lte", + "in": "query", + "required": false, + "description": "Get records that are lower than or equal to a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_gt", + "in": "query", + "required": false, + "description": "Get records that are greater than a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_gte", + "in": "query", + "required": false, + "description": "Get records that are greater than or equal a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_contains", + "in": "query", + "required": false, + "description": "Get records that contains a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_containss", + "in": "query", + "required": false, + "description": "Get records that contains (case sensitive) a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_in", + "in": "query", + "required": false, + "description": "Get records that matches any value in the array of values", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "deprecated": false + }, + { + "name": "_nin", + "in": "query", + "required": false, + "description": "Get records that doesn't match any value in the array of values", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "deprecated": false + } + ] } }, "/espaces": { @@ -906,4 +1043,4 @@ } } } -} \ No newline at end of file +} 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 614c19d2..bf50c7f3 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/21/2024 1:33:15 PM" + "x-generation-date": "02/22/2024 12:58:52 PM" }, "x-strapi-config": { "path": "/documentation", @@ -556,7 +556,7 @@ ] } }, - "/applications/me": { + "/applications/me/{campaignId}": { "get": { "deprecated": false, "description": "Get applications related to current user", @@ -609,7 +609,154 @@ "tags": [ "Application" ], - "parameters": [], + "parameters": [ + { + "name": "campaignId", + "in": "path", + "description": "", + "deprecated": false, + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "_limit", + "in": "query", + "required": false, + "description": "Maximum number of results possible", + "schema": { + "type": "integer" + }, + "deprecated": false + }, + { + "name": "_sort", + "in": "query", + "required": false, + "description": "Sort according to a specific field.", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_start", + "in": "query", + "required": false, + "description": "Skip a specific number of entries (especially useful for pagination)", + "schema": { + "type": "integer" + }, + "deprecated": false + }, + { + "name": "=", + "in": "query", + "required": false, + "description": "Get entries that matches exactly your input", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_ne", + "in": "query", + "required": false, + "description": "Get records that are not equals to something", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_lt", + "in": "query", + "required": false, + "description": "Get record that are lower than a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_lte", + "in": "query", + "required": false, + "description": "Get records that are lower than or equal to a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_gt", + "in": "query", + "required": false, + "description": "Get records that are greater than a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_gte", + "in": "query", + "required": false, + "description": "Get records that are greater than or equal a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_contains", + "in": "query", + "required": false, + "description": "Get records that contains a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_containss", + "in": "query", + "required": false, + "description": "Get records that contains (case sensitive) a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_in", + "in": "query", + "required": false, + "description": "Get records that matches any value in the array of values", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "deprecated": false + }, + { + "name": "_nin", + "in": "query", + "required": false, + "description": "Get records that doesn't match any value in the array of values", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "deprecated": false + } + ], "operationId": "getMyApplications" } }, @@ -4471,7 +4618,144 @@ "tags": [ "Espace" ], - "parameters": [], + "parameters": [ + { + "name": "_limit", + "in": "query", + "required": false, + "description": "Maximum number of results possible", + "schema": { + "type": "integer" + }, + "deprecated": false + }, + { + "name": "_sort", + "in": "query", + "required": false, + "description": "Sort according to a specific field.", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_start", + "in": "query", + "required": false, + "description": "Skip a specific number of entries (especially useful for pagination)", + "schema": { + "type": "integer" + }, + "deprecated": false + }, + { + "name": "=", + "in": "query", + "required": false, + "description": "Get entries that matches exactly your input", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_ne", + "in": "query", + "required": false, + "description": "Get records that are not equals to something", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_lt", + "in": "query", + "required": false, + "description": "Get record that are lower than a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_lte", + "in": "query", + "required": false, + "description": "Get records that are lower than or equal to a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_gt", + "in": "query", + "required": false, + "description": "Get records that are greater than a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_gte", + "in": "query", + "required": false, + "description": "Get records that are greater than or equal a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_contains", + "in": "query", + "required": false, + "description": "Get records that contains a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_containss", + "in": "query", + "required": false, + "description": "Get records that contains (case sensitive) a value", + "schema": { + "type": "string" + }, + "deprecated": false + }, + { + "name": "_in", + "in": "query", + "required": false, + "description": "Get records that matches any value in the array of values", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "deprecated": false + }, + { + "name": "_nin", + "in": "query", + "required": false, + "description": "Get records that doesn't match any value in the array of values", + "schema": { + "type": "array", + "items": { + "type": "string" + } + }, + "deprecated": false + } + ], "operationId": "myPlaces" } }, @@ -10931,6 +11215,9 @@ "type": "string" } }, + "preselections_max": { + "type": "integer" + }, "published_at": { "type": "string" }, @@ -11244,6 +11531,13 @@ } } }, + "status": { + "type": "string", + "enum": [ + "preselected", + "confirmed" + ] + }, "published_at": { "type": "string", "format": "date-time" @@ -11294,6 +11588,13 @@ "espace": { "type": "string" }, + "status": { + "type": "string", + "enum": [ + "preselected", + "confirmed" + ] + }, "published_at": { "type": "string", "format": "date-time" @@ -11779,6 +12080,13 @@ "espace": { "type": "string" }, + "status": { + "type": "string", + "enum": [ + "preselected", + "confirmed" + ] + }, "published_at": { "type": "string" }, @@ -11791,6 +12099,9 @@ } } }, + "preselections_max": { + "type": "integer" + }, "published_at": { "type": "string", "format": "date-time" @@ -11886,6 +12197,9 @@ "type": "string" } }, + "preselections_max": { + "type": "integer" + }, "published_at": { "type": "string", "format": "date-time" @@ -12589,6 +12903,9 @@ "type": "string" } }, + "preselections_max": { + "type": "integer" + }, "published_at": { "type": "string" }, @@ -12658,6 +12975,13 @@ "espace": { "type": "string" }, + "status": { + "type": "string", + "enum": [ + "preselected", + "confirmed" + ] + }, "published_at": { "type": "string" }, diff --git a/back/extensions/users-permissions/documentation/1.0.0/users-permissions-User.json b/back/extensions/users-permissions/documentation/1.0.0/users-permissions-User.json index 2aeeb787..0c273b00 100644 --- a/back/extensions/users-permissions/documentation/1.0.0/users-permissions-User.json +++ b/back/extensions/users-permissions/documentation/1.0.0/users-permissions-User.json @@ -1663,6 +1663,9 @@ "type": "string" } }, + "preselections_max": { + "type": "integer" + }, "published_at": { "type": "string" }, @@ -1733,6 +1736,13 @@ "espace": { "type": "string" }, + "status": { + "type": "string", + "enum": [ + "preselected", + "confirmed" + ] + }, "published_at": { "type": "string" }, diff --git a/web/components/Account/Application/Company/ApplicationCompanyList.tsx b/web/components/Account/Application/Company/ApplicationCompanyList.tsx index 27657592..91db1475 100644 --- a/web/components/Account/Application/Company/ApplicationCompanyList.tsx +++ b/web/components/Account/Application/Company/ApplicationCompanyList.tsx @@ -39,8 +39,6 @@ const ApplicationCompanyList = ({ applications = [] }: Props) => { setList(list.reverse()) } - console.log(applications) - return ( { {t('company.title', { title: currentCampaign?.title })} - + {t('company.table.head.number')} @@ -91,9 +89,7 @@ const ApplicationCompanyList = ({ applications = [] }: Props) => { {t('company.table.head.creation')} - - - + {} {list.map((application) => ( { return ( - {application?.id} + + {application?.id} + - + { //@ts-expect-error application.disponibility.espace.users_permissions_user @@ -43,14 +45,16 @@ const ApplicationCompanyListItem = ({ application }: Props) => { - {/* @ts-expect-error */} - {application?.disponibility?.espace?.name} + + {/* @ts-expect-error */} + {application?.disponibility?.espace?.name} + - {`${format(application?.disponibility.start, 'dd/MM')} → ${format( - application?.disponibility.end, + {`${format( + application?.disponibility.start, 'dd/MM', - )}`} + )} → ${format(application?.disponibility.end, 'dd/MM')}`} {application?.creation_title} diff --git a/web/components/Account/Application/Place/ApplicationPlaceData.tsx b/web/components/Account/Application/Place/ApplicationPlaceData.tsx new file mode 100644 index 00000000..44d40ec1 --- /dev/null +++ b/web/components/Account/Application/Place/ApplicationPlaceData.tsx @@ -0,0 +1,43 @@ +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' + +const ApplicationPlaceData = ({ searchParams }) => { + const { data: user } = useCurrentUser() + const { currentCampaign } = useCampaignContext() + + const { + data: applications, + isLoading, + refetch, + isFetching, + } = useMyApplications({ + campaignId: currentCampaign?.id, + searchParams, + options: { + enabled: + Boolean(searchParams?.disponibility_eq) && + Boolean(searchParams?.espace_eq), + }, + }) + + useEffect(() => { + refetch() + }, [JSON.stringify(searchParams)]) + + return ( + + {applications?.length === 0 ? ( + + ) : ( + + )} + + ) +} + +export default ApplicationPlaceData diff --git a/web/components/Account/Application/Place/ApplicationPlaceList.tsx b/web/components/Account/Application/Place/ApplicationPlaceList.tsx index 1804f77b..e8deff2f 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 ApplicationSelector from '~components/Account/Application/Place/ApplicationSelector' +import ApplicationPlaceHelper from '~components/Account/Application/Place/SelectionHelpers/ApplicationPlaceHelper' interface Props { applications: Application[] @@ -41,26 +41,9 @@ const ApplicationPlaceList = ({ applications = [] }: Props) => { return ( - - - {t('place.title', { title: currentCampaign?.title })} - - - + diff --git a/web/components/Account/Application/Place/ApplicationPlaceListItem.tsx b/web/components/Account/Application/Place/ApplicationPlaceListItem.tsx index 6779caf2..acc238d2 100644 --- a/web/components/Account/Application/Place/ApplicationPlaceListItem.tsx +++ b/web/components/Account/Application/Place/ApplicationPlaceListItem.tsx @@ -1,15 +1,14 @@ import React, { Fragment } from 'react' -import { format } from '~utils/date' import { Application } from '~typings/api' -import { Text, Button } from '@chakra-ui/react' +import { Text, Button, IconButton, ButtonGroup, HStack } from '@chakra-ui/react' import { useTranslation } from 'next-i18next' import Cell from '~components/Account/Booking/Cell' -import ConfirmButton from '~components/Account/Application/ConfirmButton' 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' interface Props { application: Application @@ -34,26 +33,44 @@ const ApplicationPlaceListItem = ({ application }: Props) => { return ( - {application?.id} + + {application?.id} + - {`${application?.company?.structureName} (${application.company.firstname} ${application.company.lastname})`} + {`${application?.company?.structureName} (${application.company.firstname} ${application.company.lastname})`} - {application?.company?.email} + + {application?.company?.email} + - {application?.creation_title} + + {application?.creation_title} + {currentCampaign?.mode === 'preselections' && ( - + + } + /> - + )} diff --git a/web/components/Account/Application/Place/ApplicationSelector.tsx b/web/components/Account/Application/Place/ApplicationSelector.tsx index 405a768f..749a0f38 100644 --- a/web/components/Account/Application/Place/ApplicationSelector.tsx +++ b/web/components/Account/Application/Place/ApplicationSelector.tsx @@ -1,17 +1,104 @@ import { HStack, Select } from '@chakra-ui/react' -import { useMyPlaces } from '~hooks/useMyPlaces' +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 = () => { - const { data: places, isLoading } = useMyPlaces() - if (isLoading || !places) return null +const ApplicationSelector = ({ places }: { places: Espace[] }) => { + const { currentCampaign } = useCampaignContext() + + const router = useRouter() + const { queryEspace, queryDisponibility } = router.query + const [espace, setEspace] = useState(queryEspace) + const [disponibility, setDisponibility] = useState(queryDisponibility) + + useEffect(() => { + if (queryEspace) { + setEspace(queryEspace) + } + }, [queryEspace]) + + const initState = () => { + const espace = places?.[0]?.id + const disponibility = places?.[0]?.id + setEspace(espace) + setDisponibility(disponibility) + router.push({ + pathname: router.pathname, + query: { espace, disponibility }, + }) + } + + useEffect(() => { + initState() + }, []) + + useEffect(() => { + if (!espace) { + initState() + } + }, [places]) + + 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 [] + } + + const dispoOptions = useMemo(() => getDispoOptions(espace), [espace]) return ( - - { + setEspace(e.target.value) + const disponibility = getDispoOptions(e.target.value)[0]?.id + router.push({ + pathname: router.pathname, + query: { espace: e.target.value, disponibility }, + }) + }} + > {places.map((place) => ( - + ))} + + {Boolean(dispoOptions?.length) && ( + + )} ) } diff --git a/web/components/Account/Application/Place/SelectionHelpers/ApplicationPlaceHelper.tsx b/web/components/Account/Application/Place/SelectionHelpers/ApplicationPlaceHelper.tsx new file mode 100644 index 00000000..0df1b392 --- /dev/null +++ b/web/components/Account/Application/Place/SelectionHelpers/ApplicationPlaceHelper.tsx @@ -0,0 +1,39 @@ +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 { Application } from '~typings/api' + +const ApplicationPlaceHelper = ({ + applications, +}: { + applications: Application[] +}) => { + const { t } = useTranslation('application') + const { currentCampaign } = useCampaignContext() + const preselections = applications?.filter( + (application) => application?.status === 'preselected', + ).length + const missingPreselections = + currentCampaign?.preselections_max - preselections + + const validatedApplications = applications?.filter( + (application) => application?.status === 'confirmed', + ).length + + if (currentCampaign?.mode === 'preselections' && validatedApplications > 0) { + return + } + if (currentCampaign?.mode === 'preselections' && missingPreselections > 0) { + return + } + + if (currentCampaign?.mode === 'preselections' && missingPreselections === 0) { + return + } + + return null +} + +export default ApplicationPlaceHelper diff --git a/web/components/Account/Application/Place/SelectionHelpers/ConfirmSelections.tsx b/web/components/Account/Application/Place/SelectionHelpers/ConfirmSelections.tsx new file mode 100644 index 00000000..91ffbfb4 --- /dev/null +++ b/web/components/Account/Application/Place/SelectionHelpers/ConfirmSelections.tsx @@ -0,0 +1,46 @@ +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' + +const ConfirmSelections = ({ preselections }: { preselections: number }) => { + const { t } = useTranslation('application') + + return ( + + + + + + {t( + `place.helper.confirm_preselection${ + preselections > 1 ? 's' : '' + }`, + { + num: preselections, + }, + )} + + + + + + ) +} + +export default ConfirmSelections diff --git a/web/components/Account/Application/Place/SelectionHelpers/MissingSelections.tsx b/web/components/Account/Application/Place/SelectionHelpers/MissingSelections.tsx new file mode 100644 index 00000000..9ed4ac63 --- /dev/null +++ b/web/components/Account/Application/Place/SelectionHelpers/MissingSelections.tsx @@ -0,0 +1,41 @@ +import { Box, HStack, Text } from '@chakra-ui/react' +import { useTranslation } from 'next-i18next' +import useCampaignContext from '~components/Campaign/useCampaignContext' +import { format } from '~utils/date' +import PreselectionsWarning from 'public/assets/img/preselectionsWarning.svg' + +const MissingSelections = ({ + missingPreselections, +}: { + missingPreselections: number +}) => { + const { t } = useTranslation('application') + const { currentCampaign } = useCampaignContext() + + return ( + + + + + + + {t('place.helper.preselection_start', { + date: format(currentCampaign?.preselection_end), + })} + + + {t( + `place.helper.missing_preselection${ + missingPreselections > 0 ? 's' : '' + }`, + { num: missingPreselections }, + )} + + + + + + ) +} + +export default MissingSelections diff --git a/web/components/Account/Application/Place/SelectionHelpers/ValidatedSelections.tsx b/web/components/Account/Application/Place/SelectionHelpers/ValidatedSelections.tsx new file mode 100644 index 00000000..0ed1ea11 --- /dev/null +++ b/web/components/Account/Application/Place/SelectionHelpers/ValidatedSelections.tsx @@ -0,0 +1,24 @@ +import { Box, HStack, Text } from '@chakra-ui/react' +import PreselectionsValidated from 'public/assets/img/PreselectionsValidated.svg' +import { useTranslation } from 'next-i18next' + +const ValidatedSelections = () => { + const { t } = useTranslation('application') + + return ( + + + + + {t('place.helper.validated')} + + + + ) +} + +export default ValidatedSelections diff --git a/web/components/Campaign/Places/Admin/CampaignScheduleForm.tsx b/web/components/Campaign/Places/Admin/CampaignScheduleForm.tsx index 629ea1f7..50ce874e 100644 --- a/web/components/Campaign/Places/Admin/CampaignScheduleForm.tsx +++ b/web/components/Campaign/Places/Admin/CampaignScheduleForm.tsx @@ -82,6 +82,7 @@ const CampaignScheduleForm = ({ place, hideForm }: Props) => { scene_grid, status: 'available', exclude_days, + isCampaign: true, }, ]) .then((res) => { diff --git a/web/components/Campaign/Places/Application/ApplicationForm.tsx b/web/components/Campaign/Places/Application/ApplicationForm.tsx index 657bab7e..d5c7649f 100644 --- a/web/components/Campaign/Places/Application/ApplicationForm.tsx +++ b/web/components/Campaign/Places/Application/ApplicationForm.tsx @@ -99,7 +99,7 @@ const ApplicationForm = ({ form: new FormData(), data: { company: user.id, - place: place.id, + espace: place.id, disponibility: event?.extendedProps?.id, campaign: currentCampaign?.id, }, diff --git a/web/hooks/useMyApplications.ts b/web/hooks/useMyApplications.ts index 288d4ef1..5ec33ae1 100644 --- a/web/hooks/useMyApplications.ts +++ b/web/hooks/useMyApplications.ts @@ -1,13 +1,22 @@ import { useQuery, UseQueryOptions } from 'react-query' import { client } from '~api/client-api' -import { Application } from '~typings/api' +import { Application, Applications } from '~typings/api' -export const useMyApplications = ( - options: UseQueryOptions = {}, -) => { +export const useMyApplications = ({ + searchParams = {}, + options = {}, + campaignId, +}: { + searchParams?: Applications.ApplicationsList.RequestQuery + campaignId: string + options?: UseQueryOptions +}) => { return useQuery( ['myApplications'], - () => client.applications.getMyApplications().then((res) => res.data), + () => + client.applications + .getMyApplications(campaignId, searchParams) + .then((res) => res.data), options, ) } diff --git a/web/hooks/useMyPlaces.tsx b/web/hooks/useMyPlaces.tsx index 656720bb..f3576393 100644 --- a/web/hooks/useMyPlaces.tsx +++ b/web/hooks/useMyPlaces.tsx @@ -1,8 +1,10 @@ import { useQuery } from 'react-query' import { client } from '~api/client-api' -export const useMyPlaces = () => { - return useQuery('myPlaces', () => - client.espaces.myPlaces().then((res) => res.data), +export const useMyPlaces = (params = {}, options = {}) => { + return useQuery( + 'myPlaces', + () => client.espaces.myPlaces(params).then((res) => res.data), + options, ) } diff --git a/web/pages/compte/candidatures/index.tsx b/web/pages/compte/candidatures/index.tsx index 26c10ccc..3972970c 100644 --- a/web/pages/compte/candidatures/index.tsx +++ b/web/pages/compte/candidatures/index.tsx @@ -1,36 +1,82 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' import { SSRConfig } from 'next-i18next' import { GetServerSideProps } from 'next' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' -import InfoRequest from '~components/Account/Info/InfoRequest' -import BookingList from '~components/Account/Booking/BookingList' -import { UsersPermissionsUser } from '~typings/api' import { requireAuth } from '~utils/auth' -import Loading from '~components/Loading' -import { useMyBookings } from '~hooks/useMyBookings' import { NextSeo } from 'next-seo' import { useTranslation } from 'next-i18next' -import ApplicationPlaceList from '~components/Account/Application/Place/ApplicationPlaceList' -import InfoPlaceApplications from '~components/Account/Info/InfoPlaceApplications' -import { useMyApplications } from '~hooks/useMyApplications' -interface Props { - user: UsersPermissionsUser -} - -const AccountApplications = ({ user }: Props) => { - const { t } = useTranslation('account') - const { data: applications, isLoading } = useMyApplications() +import useCampaignContext from '~components/Campaign/useCampaignContext' +import { Flex, Text } from '@chakra-ui/react' +import ApplicationSelector from '~components/Account/Application/Place/ApplicationSelector' +import ApplicationPlaceData from '~components/Account/Application/Place/ApplicationPlaceData' +import { useRouter } from 'next/router' +import { useMyPlaces } from '~hooks/useMyPlaces' - return ( - - - {applications?.length === 0 ? ( - - ) : ( - - )} - +const PlaceApplications = () => { + const { t } = useTranslation('application') + const { t: tAccount } = useTranslation('account') + const { currentCampaign } = useCampaignContext() + const [searchParams, setSearchParams] = useState() + const router = useRouter() + const { disponibility, espace } = router.query + const { data: places } = useMyPlaces( + { + 'disponibilities.campaign': currentCampaign?.id, + }, + { enabled: Boolean(currentCampaign?.id) }, ) + + useEffect(() => { + const search = {} + if (disponibility) { + search['disponibility_eq'] = Number(disponibility) + } + if (espace) { + search['espace_eq'] = Number(espace) + } + setSearchParams(search) + }, [router.query]) + + if (currentCampaign) + return ( + <> + + + + {t('place.title', { title: currentCampaign?.title })} + + + {Boolean(places?.length) && ( + ({ + ...p, + disponibilities: p.disponibilities?.filter( + //@ts-expect-error + (d) => d.campaign === currentCampaign?.id, + ), + }))} + /> + )} + + {Boolean(searchParams && Object.keys(searchParams)?.length) && ( + + )} + + ) + + return null } export const getServerSideProps: GetServerSideProps = requireAuth( @@ -43,4 +89,4 @@ export const getServerSideProps: GetServerSideProps = requireAuth( }, ) -export default AccountApplications +export default PlaceApplications diff --git a/web/pages/compte/mes-candidatures/index.tsx b/web/pages/compte/mes-candidatures/index.tsx index f4507bd2..811aa996 100644 --- a/web/pages/compte/mes-candidatures/index.tsx +++ b/web/pages/compte/mes-candidatures/index.tsx @@ -10,14 +10,19 @@ import { useTranslation } from 'next-i18next' import { useMyApplications } from '~hooks/useMyApplications' import InfoCompanyApplications from '~components/Account/Info/InfoCompanyApplications' import ApplicationCompanyList from '~components/Account/Application/Company/ApplicationCompanyList' +import useCampaignContext from '~components/Campaign/useCampaignContext' interface Props { user: UsersPermissionsUser } const CompanyApplications = ({ user }: Props) => { const { t } = useTranslation('account') - const { data: applications, isLoading } = useMyApplications() - + const { currentCampaign } = useCampaignContext() + const { data: applications, isLoading } = useMyApplications({ + campaignId: currentCampaign?.id, + options: { enabled: !!currentCampaign?.id }, + }) + if (!currentCampaign) return null return ( diff --git a/web/public/assets/img/downloadApplication.svg b/web/public/assets/img/downloadApplication.svg new file mode 100644 index 00000000..15d3a945 --- /dev/null +++ b/web/public/assets/img/downloadApplication.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/web/public/assets/img/preselectionsValidated.svg b/web/public/assets/img/preselectionsValidated.svg new file mode 100644 index 00000000..b44073aa --- /dev/null +++ b/web/public/assets/img/preselectionsValidated.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/web/public/assets/img/preselectionsWarning.svg b/web/public/assets/img/preselectionsWarning.svg new file mode 100644 index 00000000..584557ba --- /dev/null +++ b/web/public/assets/img/preselectionsWarning.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/web/public/locales/fr/account.json b/web/public/locales/fr/account.json index a3e073fd..14b1040d 100644 --- a/web/public/locales/fr/account.json +++ b/web/public/locales/fr/account.json @@ -28,7 +28,7 @@ "text": { "next": "Le dispositif {{title}} se prépare : l’appel à candidatures sera ouvert du {{application_start}} au {{application_end}}. Encore un peu de patience !", "no_applications": "Vous n’avez déposé aucune candidature {{title}} cette saison. Candidatez jusqu’au {{application_end}} auprès de nos espaces partenaires ({{applications_max}} candidatures maximum)", - "no_applications_company": "Vous n'avez pas reçu de candidaure pour le dispositif {{title}} pour le moment. La période de candidature se termine le {{date}}." + "no_applications_company": "Vous n'avez pas reçu de candidature pour le dispositif {{title}} pour le moment. La période de candidature se termine le {{date}}." }, "cta": { "next": "Découvrir le dispositif Émergence", diff --git a/web/public/locales/fr/application.json b/web/public/locales/fr/application.json index 1685ffca..8102fc30 100644 --- a/web/public/locales/fr/application.json +++ b/web/public/locales/fr/application.json @@ -30,7 +30,19 @@ "artist": "Artiste", "email": "Email", "creation": "Nom de la création" + }, + "buttons": { + "details": "Détails" } + }, + "helper": { + "preselection_start": "Établissez votre présélection avant le {{date}}.", + "missing_preselections": "{{num}} manquantes.", + "missing_preselection": "{{num}} manquante.", + "confirm_preselection": "Vous avez pré-sélectionné {{num}} candidatures.", + "confirm_preselections": "Vous avez pré-sélectionné {{num}} candidatures.", + "confirm_cta": "Confirmer et transmettre la pré-selection à StudioD", + "validated": "Votre pré-sélection a bien été prise en compte pour ce créneau !" } } } diff --git a/web/typings/api.ts b/web/typings/api.ts index 27bf96a3..7ba4f074 100644 --- a/web/typings/api.ts +++ b/web/typings/api.ts @@ -126,11 +126,12 @@ export interface Application { eligibility?: string; chart_url?: string; applications?: string[]; + preselections_max?: number; published_at?: string; created_by?: string; updated_by?: string; }; - place?: { + company?: { id: string; email: string; provider?: string; @@ -170,46 +171,43 @@ export interface Application { created_by?: string; updated_by?: string; }; - company?: { + espace?: { id: string; - email: string; - provider?: string; - password?: string; - resetPasswordToken?: string; - confirmationToken?: string; - role?: string; - username: string; - confirmed?: boolean; - blocked?: boolean; - accepted?: boolean; - firstname: string; - lastname: string; - structureName: string; - socialReason?: string; + name: string; + surface: number; + roomLength: number; + width: number; + height: number; + mirror: boolean; + danceBar: boolean; + accomodation: boolean; + technicalStaff: boolean; + floor: "plancherDanse" | "parquetTraditionnel" | "other" | "todefine"; + otherFloor?: string; + about?: string; + details?: string; address: string; - zipCode: string; - city: string; + files?: string[]; + images?: string[]; + users_permissions_user?: string; + disponibilities?: string[]; + scheduleDetails?: string; + filledUntil?: string; + published?: boolean; + bookings?: string[]; country: string; - siret: string; - ape: string; - phone: string; - license: string; - website?: string; - legalRepresentative?: string; - statusRepresentative?: string; - insuranceNumber?: string; - insuranceName?: string; - choreographer?: string; - espaces?: string[]; - type: "company" | "place"; external_id?: number; - companyDispositifs?: string[]; - placeDispositifs?: string[]; - campaigns?: string[]; - companyApplications?: string[]; + danceCarpet?: "true" | "false" | "possible"; + slug?: string; + city?: string; + latitude: number; + longitude?: number; + deleted?: boolean; + applications?: string[]; created_by?: string; updated_by?: string; }; + status?: "preselected" | "confirmed"; /** @format date-time */ published_at?: string; @@ -228,8 +226,9 @@ export interface NewApplication { cv?: string; references?: object; campaign?: string; - place?: string; company?: string; + espace?: string; + status?: "preselected" | "confirmed"; /** @format date-time */ published_at?: string; @@ -385,12 +384,14 @@ export interface Campaign { cv?: string; references?: object; campaign?: string; - place?: string; company?: string; + espace?: string; + status?: "preselected" | "confirmed"; published_at?: string; created_by?: string; updated_by?: string; }[]; + preselections_max?: number; /** @format date-time */ published_at?: string; @@ -433,6 +434,7 @@ export interface NewCampaign { eligibility?: string; chart_url?: string; applications?: string[]; + preselections_max?: number; /** @format date-time */ published_at?: string; @@ -475,6 +477,7 @@ export interface City { latitude: number; longitude?: number; deleted?: boolean; + applications?: string[]; created_by?: string; updated_by?: string; }[]; @@ -549,6 +552,7 @@ export interface Disponibility { latitude: number; longitude?: number; deleted?: boolean; + applications?: string[]; created_by?: string; updated_by?: string; }; @@ -630,6 +634,7 @@ export interface Disponibility { eligibility?: string; chart_url?: string; applications?: string[]; + preselections_max?: number; published_at?: string; created_by?: string; updated_by?: string; @@ -649,8 +654,9 @@ export interface Disponibility { cv?: string; references?: object; campaign?: string; - place?: string; company?: string; + espace?: string; + status?: "preselected" | "confirmed"; published_at?: string; created_by?: string; updated_by?: string; @@ -879,6 +885,7 @@ export interface NewEspace { latitude: number; longitude?: number; deleted?: boolean; + applications?: string[]; created_by?: string; updated_by?: string; } @@ -1308,12 +1315,26 @@ export namespace Applications { * @description Get applications related to current user * @tags Application * @name GetMyApplications - * @request GET:/applications/me + * @request GET:/applications/me/{campaignId} * @secure */ export namespace GetMyApplications { - export type RequestParams = {}; - export type RequestQuery = {}; + export type RequestParams = { campaignId: string }; + export type RequestQuery = { + _limit?: number; + _sort?: string; + _start?: number; + "="?: string; + _ne?: string; + _lt?: string; + _lte?: string; + _gt?: string; + _gte?: string; + _contains?: string; + _containss?: string; + _in?: string[]; + _nin?: string[]; + }; export type RequestBody = never; export type RequestHeaders = {}; export type ResponseBody = Application[]; @@ -2079,7 +2100,21 @@ export namespace Espaces { */ export namespace MyPlaces { export type RequestParams = {}; - export type RequestQuery = {}; + export type RequestQuery = { + _limit?: number; + _sort?: string; + _start?: number; + "="?: string; + _ne?: string; + _lt?: string; + _lte?: string; + _gt?: string; + _gte?: string; + _contains?: string; + _containss?: string; + _in?: string[]; + _nin?: string[]; + }; export type RequestBody = never; export type RequestHeaders = {}; export type ResponseBody = Espace[]; @@ -3509,13 +3544,32 @@ export class Api extends HttpClient + getMyApplications: ( + campaignId: string, + query?: { + _limit?: number; + _sort?: string; + _start?: number; + "="?: string; + _ne?: string; + _lt?: string; + _lte?: string; + _gt?: string; + _gte?: string; + _contains?: string; + _containss?: string; + _in?: string[]; + _nin?: string[]; + }, + params: RequestParams = {}, + ) => this.request({ - path: `/applications/me`, + path: `/applications/me/${campaignId}`, method: "GET", + query: query, secure: true, format: "json", ...params, @@ -4460,10 +4514,28 @@ export class Api extends HttpClient + myPlaces: ( + query?: { + _limit?: number; + _sort?: string; + _start?: number; + "="?: string; + _ne?: string; + _lt?: string; + _lte?: string; + _gt?: string; + _gte?: string; + _contains?: string; + _containss?: string; + _in?: string[]; + _nin?: string[]; + }, + params: RequestParams = {}, + ) => this.request({ path: `/espaces/me`, method: "GET", + query: query, secure: true, format: "json", ...params,