From 93c439004bc8ec4cb3a5f22a6525fdb389a6e2b7 Mon Sep 17 00:00:00 2001 From: shinework Date: Fri, 15 Mar 2024 11:33:52 +0100 Subject: [PATCH] feat: review --- web/components/Account/AccountMenu.tsx | 60 +++++++------ .../Place/ApplicationPlaceList.tsx | 20 ++--- .../Place/ApplicationPlaceListItem.tsx | 12 +-- .../DetailDrawer/ApplicationDetailDrawer.tsx | 88 +++++-------------- web/components/pdfs/ApplicationHeader.tsx | 2 +- web/pages/api/pdfs/all/[id].tsx | 24 ++--- web/pages/api/pdfs/selected/[id].tsx | 36 +++++--- web/pages/compte/mes-candidatures/index.tsx | 29 +++--- web/utils/pdf.ts | 17 +--- 9 files changed, 130 insertions(+), 158 deletions(-) diff --git a/web/components/Account/AccountMenu.tsx b/web/components/Account/AccountMenu.tsx index 195df34..9ae93bf 100644 --- a/web/components/Account/AccountMenu.tsx +++ b/web/components/Account/AccountMenu.tsx @@ -1,35 +1,35 @@ -import React, { useMemo } from 'react' -import { Box, Image, Flex, Text, VStack, BoxProps } from '@chakra-ui/react' -import { - ROUTE_USE_POLICY, - ROUTE_ACCOUNT_INFORMATION, - ROUTE_ACCOUNT_REQUEST, - ROUTE_ACCOUNT_BOOKING, - ROUTE_ACCOUNT_MESSAGE, - ROUTE_ACCOUNT_PLACES, - ROUTE_ACCOUNT_APPLICATIONS, - ROUTE_ACCOUNT_MY_APPLICATIONS, -} from '~constants' -import Link from '~components/Link' -import Back from 'public/assets/img/back.svg' -import Notif from '~components/Notif' -import Profile from 'public/assets/img/user.svg' +import { Box, BoxProps, Flex, Image, Text, VStack } from '@chakra-ui/react' +import { signOut } from 'next-auth/client' +import { useTranslation } from 'next-i18next' +import { useRouter } from 'next/router' import Applications from 'public/assets/img/applicationsSmall.svg' import ApplicationsLoading from 'public/assets/img/applicationsSmallLoading.svg' +import Back from 'public/assets/img/back.svg' +import Calendar from 'public/assets/img/calendar.svg' import Charte from 'public/assets/img/charte.svg' -import Logout from 'public/assets/img/logout.svg' import Home from 'public/assets/img/home.svg' -import Calendar from 'public/assets/img/calendar.svg' +import Logout from 'public/assets/img/logout.svg' import Message from 'public/assets/img/message.svg' import Question from 'public/assets/img/question.svg' -import { useTranslation } from 'next-i18next' -import { signOut } from 'next-auth/client' -import { useRouter } from 'next/router' -import { UsersPermissionsUser } from '~typings/api' -import { useMyNotifications } from '~hooks/useMyNotifications' -import { useUserIsComplete } from '~hooks/useUserIsComplete' +import Profile from 'public/assets/img/user.svg' +import { useMemo } from 'react' import useCampaignContext from '~components/Campaign/useCampaignContext' +import Link from '~components/Link' +import Notif from '~components/Notif' +import { + ROUTE_ACCOUNT_APPLICATIONS, + ROUTE_ACCOUNT_BOOKING, + ROUTE_ACCOUNT_INFORMATION, + ROUTE_ACCOUNT_MESSAGE, + ROUTE_ACCOUNT_MY_APPLICATIONS, + ROUTE_ACCOUNT_PLACES, + ROUTE_ACCOUNT_REQUEST, + ROUTE_USE_POLICY, +} from '~constants' import { useMyApplications } from '~hooks/useMyApplications' +import { useMyNotifications } from '~hooks/useMyNotifications' +import { useUserIsComplete } from '~hooks/useUserIsComplete' +import { UsersPermissionsUser } from '~typings/api' const accountItems = { title: 'myAccount', @@ -155,8 +155,14 @@ const AccountMenu = ({ user }: { user: UsersPermissionsUser }) => { (user?.type === 'place' && placeCampaigns?.length && applications?.length) || - (user?.type === 'company' && currentCampaign && currentCampaign.mode !== "closed") + (user?.type === 'company' && + currentCampaign && + currentCampaign.mode !== 'closed') + const hideApplication = + currentCampaign?.mode === 'preselections' && + !applications?.length && + user?.type === 'company' const displayMenu = ({ title, items, translationParams = {} }) => { const isDisactivated = !isComplete && title === 'dashboard' @@ -259,7 +265,9 @@ const AccountMenu = ({ user }: { user: UsersPermissionsUser }) => { {user?.confirmed && user?.accepted && displayMenu(user?.type === 'company' ? companyItems : placeItems)} - {displayApplications && displayMenu(applicationItems)} + {displayApplications && + !hideApplication && + displayMenu(applicationItems)} {displayMenu(accountItems)} diff --git a/web/components/Account/Application/Place/ApplicationPlaceList.tsx b/web/components/Account/Application/Place/ApplicationPlaceList.tsx index 6fd3310..a4ee980 100644 --- a/web/components/Account/Application/Place/ApplicationPlaceList.tsx +++ b/web/components/Account/Application/Place/ApplicationPlaceList.tsx @@ -1,22 +1,22 @@ -import React, { useEffect, useMemo, useState } from 'react' import { - Flex, - Text, - SimpleGrid, Box, Divider as ChakraDivider, DividerProps, + Flex, + SimpleGrid, + Text, useDisclosure, } from '@chakra-ui/react' import { useTranslation } from 'next-i18next' -import { Application } from '~typings/api' +import { useRouter } from 'next/router' import Chevron from 'public/assets/img/chevron-down.svg' -import Cell from '~components/Account/Booking/Cell' -import ApplicationPlaceHelper from '~components/Account/Application/Place/ApplicationsHelpers/ApplicationPlaceHelper' +import { useEffect, useMemo, useState } from 'react' import ApplicationPlaceListItem from '~components/Account/Application/Place/ApplicationPlaceListItem' -import useSelectedCampaign from '~hooks/useSelectedCampaign' +import ApplicationPlaceHelper from '~components/Account/Application/Place/ApplicationsHelpers/ApplicationPlaceHelper' import ApplicationDetailDrawer from '~components/Account/Application/Place/DetailDrawer/ApplicationDetailDrawer' -import { useRouter } from 'next/router' +import Cell from '~components/Account/Booking/Cell' +import useSelectedCampaign from '~hooks/useSelectedCampaign' +import { Application } from '~typings/api' interface Props { applications: Application[] @@ -55,7 +55,7 @@ const ApplicationPlaceList = ({ applications = [] }: Props) => { list.filter((application) => { return ( !query.search?.length || - `${application?.company?.structureName} (${application.company.firstname} ${application.company.lastname})` + `${application?.company?.structureName} (${application.company?.firstname} ${application.company?.lastname})` .toLowerCase() ?.includes((query.search as string)?.toLowerCase()) ) diff --git a/web/components/Account/Application/Place/ApplicationPlaceListItem.tsx b/web/components/Account/Application/Place/ApplicationPlaceListItem.tsx index e5572e0..3fe8c8e 100644 --- a/web/components/Account/Application/Place/ApplicationPlaceListItem.tsx +++ b/web/components/Account/Application/Place/ApplicationPlaceListItem.tsx @@ -1,12 +1,12 @@ -import React, { Fragment } from 'react' -import { Application } from '~typings/api' -import { Text, Button, HStack } from '@chakra-ui/react' +import { Button, HStack, Text } from '@chakra-ui/react' import { useTranslation } from 'next-i18next' +import { Fragment } from 'react' +import ApplicationDownloadButton from '~components/Account/Application/Place/ApplicationDownloadButton' +import ApplicationStatusIcon from '~components/Account/Application/Place/ApplicationStatusIcon' import Cell from '~components/Account/Booking/Cell' import Link from '~components/Link' import useSelectedCampaign from '~hooks/useSelectedCampaign' -import ApplicationStatusIcon from '~components/Account/Application/Place/ApplicationStatusIcon' -import ApplicationDownloadButton from '~components/Account/Application/Place/ApplicationDownloadButton' +import { Application } from '~typings/api' interface Props { application: Application @@ -30,7 +30,7 @@ const ApplicationPlaceListItem = ({ application, onSelect }: Props) => { fontWeight="500" pr={2} as="span" - >{`${application?.company?.structureName} (${application.company.firstname} ${application.company.lastname})`} + >{`${application?.company?.structureName} (${application.company?.firstname} ${application.company?.lastname})`} diff --git a/web/components/Account/Application/Place/DetailDrawer/ApplicationDetailDrawer.tsx b/web/components/Account/Application/Place/DetailDrawer/ApplicationDetailDrawer.tsx index 70e2cda..7b76ffa 100644 --- a/web/components/Account/Application/Place/DetailDrawer/ApplicationDetailDrawer.tsx +++ b/web/components/Account/Application/Place/DetailDrawer/ApplicationDetailDrawer.tsx @@ -1,27 +1,25 @@ import { + Box, + Divider, Drawer, DrawerCloseButton, DrawerContent, DrawerHeader, DrawerOverlay, - VStack, - Divider, Grid, GridItem, - Box, - Skeleton, + VStack, } from '@chakra-ui/react' import { useTranslation } from 'next-i18next' import ApplicationDetailHeader from '~components/Account/Application/Place/DetailDrawer/ApplicationDetailHeader' -import { Application } from '~typings/api' import ApplicationRightPanel from '~components/Account/Application/Place/DetailDrawer/ApplicationRightPanel' +import { Application } from '~typings/api' -import { useEffect, useState } from 'react' -import { Document, Page } from 'react-pdf' +import { useState } from 'react' +import ApplicationDetails from '~components/Account/Application/Place/DetailDrawer/ApplicationDetails' import useToast from '~hooks/useToast' import { handleApplicationDownload } from '~utils/pdf' -import ApplicationDetails from '~components/Account/Application/Place/DetailDrawer/ApplicationDetails' const ApplicationDetailDrawer = ({ isOpen, @@ -36,11 +34,8 @@ const ApplicationDetailDrawer = ({ }) => { const { t } = useTranslation('application') const { id } = application ?? {} - const [displayPdf, setDisplayPdf] = useState(false) const [isDownloading, setIsDownloading] = useState(false) - const [scales, setScales] = useState([]) const { errorToast } = useToast() - const [numPages, setNumPages] = useState(0) const handleDownload = async () => { setIsDownloading(true) @@ -57,22 +52,17 @@ const ApplicationDetailDrawer = ({ } } - useEffect(() => { - setTimeout(() => { - setDisplayPdf(true) - }, 1500) - }, [id, isOpen]) - if (!application) { return null } + const pdfUrl = application?.creation_file?.[0]?.url + return ( { - setDisplayPdf(false) onClose() }} size="xl" @@ -87,11 +77,7 @@ const ApplicationDetailDrawer = ({ - + - - - {displayPdf && ( - { - setNumPages(numPages) - }} - loading={ - - } - style={{ height: '100%' }} - > - {Array.from(new Array(numPages), (_el, index) => ( - { - const viewport = page.getViewport({ scale: 1 }) - if (viewport) { - const orientation = - viewport.width > viewport.height - ? 'landscape' - : 'portrait' - // Adjust the scale based on the orientation - const scale = - orientation === 'landscape' ? 0.7 : 1.0 - setScales((prevScales) => { - const newScales = [...prevScales] - newScales[index] = scale - return newScales - }) - } - }} - scale={scales[index] || 1.0} - /> - ))} - - )} + + + + {pdfUrl && ( + + )} + { {application?.disponibility?.espace?.name} - {`${format(application?.disponibility?.start, 'dd/MM')} → ${format( + {`${format(application?.disponibility?.start, 'dd/MM')} - ${format( application?.disponibility?.end, 'dd/MM', )}`} diff --git a/web/pages/api/pdfs/all/[id].tsx b/web/pages/api/pdfs/all/[id].tsx index 41e11cd..a148602 100644 --- a/web/pages/api/pdfs/all/[id].tsx +++ b/web/pages/api/pdfs/all/[id].tsx @@ -1,10 +1,10 @@ // @ts-ignore import { renderToStream } from '@react-pdf/renderer' +import AdmZip from 'adm-zip' +import { getSession } from 'next-auth/client' import { client } from '~api/client-api' import ApplicationDocument from '~components/pdfs/ApplicationDocument' -import { getSession } from 'next-auth/client' import { formatDisponibilityZipName, getBufferFromStream } from '~utils/pdf' -import AdmZip from "adm-zip" const MultipleApplication = async (req, res) => { const { id: disponibilityId } = req.query @@ -29,34 +29,36 @@ const MultipleApplication = async (req, res) => { ) const disponibility = applications?.[0]?.disponibility - const campaign = applications?.[0]?.campaign - - const zip = new AdmZip(); + const zip = new AdmZip() for (const application of applications) { - const name = application.company?.structureName; + const refLabel = `Ref. ${application.id}` + const name = `${refLabel} - ${application.company?.structureName}` const stream = await renderToStream( , ) const streamBuffer = await getBufferFromStream(stream) - await zip.addFile(`${name}/candidature.pdf`, streamBuffer); + await zip.addFile(`${name}/${refLabel} - Candidature.pdf`, streamBuffer) if (application?.creation_file?.[0]?.url) { const creationFile = await fetch(application?.creation_file?.[0]?.url) - + // @ts-ignore const creationFileArrayBuffer = await creationFile.buffer() - await zip.addFile(`${name}/dossier-artistique.pdf`, creationFileArrayBuffer); + await zip.addFile( + `${name}/${refLabel} - Dossier artistique.pdf`, + creationFileArrayBuffer, + ) } } - const zipBuffer = zip.toBuffer(); + const zipBuffer = zip.toBuffer() res.setHeader('Content-Type', 'application/zip') res.setHeader( 'Content-Disposition', // @ts-expect-error - 'attachment; filename=' + formatDisponibilityZipName(disponibility, campaign), + 'attachment; filename=' + formatDisponibilityZipName(disponibility), ) res.send(zipBuffer) } diff --git a/web/pages/api/pdfs/selected/[id].tsx b/web/pages/api/pdfs/selected/[id].tsx index bee73cf..6e8a5ff 100644 --- a/web/pages/api/pdfs/selected/[id].tsx +++ b/web/pages/api/pdfs/selected/[id].tsx @@ -1,10 +1,10 @@ // @ts-ignore import { renderToStream } from '@react-pdf/renderer' +import AdmZip from 'adm-zip' +import { getSession } from 'next-auth/client' import { client } from '~api/client-api' import ApplicationDocument from '~components/pdfs/ApplicationDocument' -import { getSession } from 'next-auth/client' import { formatCampaignZipName, getBufferFromStream } from '~utils/pdf' -import AdmZip from "adm-zip" const SelectedCampaignApplications = async (req, res) => { const { id: campaignId } = req.query @@ -16,15 +16,15 @@ const SelectedCampaignApplications = async (req, res) => { return } - const zip = new AdmZip(); + const zip = new AdmZip() const { data: campaign } = await client.campaigns.campaignsDetail(campaignId) + console.log({jwt:session.user.jwt}) try { const { data: selectedApplications, } = await client.applications.getConfirmedApplicationsByCampaign( campaignId, - { headers: { Authorization: `Bearer ${session.user.jwt}`, @@ -32,6 +32,7 @@ const SelectedCampaignApplications = async (req, res) => { }, ) + // Group by place const groupedApplications = selectedApplications.reduce( (grouped, application) => { @@ -48,6 +49,7 @@ const SelectedCampaignApplications = async (req, res) => { }, {}, ) + // Group by espace AND disponibility for (const userId in groupedApplications) { groupedApplications[userId].sort((a, b) => { @@ -64,23 +66,31 @@ const SelectedCampaignApplications = async (req, res) => { const applications = groupedApplications[userId] const place = applications[0]?.disponibility?.espace?.users_permissions_user - const name = place?.structureName - - + const name = place?.structureName + for (const application of applications) { + const structureName = application.company?.structureName + const stream = await renderToStream( , - ) - + ) + + const refLabel = `Ref. ${application.id}` const streamBuffer = await getBufferFromStream(stream) - await zip.addFile(`${name}/candidature.pdf`, streamBuffer); + await zip.addFile( + `${name}/${refLabel} - ${structureName}/${refLabel} - Candidature.pdf`, + streamBuffer, + ) if (application?.creation_file?.[0]?.url) { const creationFile = await fetch(application?.creation_file?.[0]?.url) // @ts-ignore const creationFileArrayBuffer = await creationFile.buffer() - - await zip.addFile(`${name}/dossier-artistique.pdf`, creationFileArrayBuffer); + + await zip.addFile( + `${name}/${refLabel} - ${structureName}/${refLabel} - Dossier artistique.pdf`, + creationFileArrayBuffer, + ) } } } @@ -92,7 +102,7 @@ const SelectedCampaignApplications = async (req, res) => { return } - const zipBuffer = zip.toBuffer(); + const zipBuffer = zip.toBuffer() res.setHeader('Content-Type', 'application/zip') res.setHeader( diff --git a/web/pages/compte/mes-candidatures/index.tsx b/web/pages/compte/mes-candidatures/index.tsx index c961854..efcea90 100644 --- a/web/pages/compte/mes-candidatures/index.tsx +++ b/web/pages/compte/mes-candidatures/index.tsx @@ -1,18 +1,17 @@ -import React, { useEffect } from 'react' -import { SSRConfig } from 'next-i18next' import { GetServerSideProps } from 'next' +import { SSRConfig, useTranslation } from 'next-i18next' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' -import { UsersPermissionsUser } from '~typings/api' -import { requireAuth } from '~utils/auth' -import Loading from '~components/Loading' import { NextSeo } from 'next-seo' -import { useTranslation } from 'next-i18next' -import { useMyApplications } from '~hooks/useMyApplications' -import InfoCompanyApplications from '~components/Account/Info/InfoCompanyApplications' +import { useRouter } from 'next/router' +import { useEffect } from 'react' import ApplicationCompanyList from '~components/Account/Application/Company/ApplicationCompanyList' +import InfoCompanyApplications from '~components/Account/Info/InfoCompanyApplications' import useCampaignContext from '~components/Campaign/useCampaignContext' -import { useRouter } from 'next/router' +import Loading from '~components/Loading' import { ROUTE_ACCOUNT } from '~constants' +import { useMyApplications } from '~hooks/useMyApplications' +import { UsersPermissionsUser } from '~typings/api' +import { requireAuth } from '~utils/auth' interface Props { user: UsersPermissionsUser } @@ -23,6 +22,7 @@ const CompanyApplications = ({ user }: Props) => { const { currentCampaign, isLoading: isLoadingCampaign } = useCampaignContext() const { data: applications, isLoading } = useMyApplications({ options: { enabled: !!currentCampaign?.id }, + name: ['myApplications', currentCampaign?.id], searchParams: { _sort: 'disponibility.start:asc', //@ts-expect-error @@ -36,15 +36,20 @@ const CompanyApplications = ({ user }: Props) => { } }, [currentCampaign]) - if (!currentCampaign) { + if (!currentCampaign) { + return null + } + + if (currentCampaign?.mode === 'closed') { + router.push(ROUTE_ACCOUNT) return null } - if (currentCampaign?.mode === "closed") { + if (currentCampaign?.mode === 'preselections' && applications?.length === 0) { router.push(ROUTE_ACCOUNT) return null } - + return ( diff --git a/web/utils/pdf.ts b/web/utils/pdf.ts index 2f34dd4..8f16ac7 100644 --- a/web/utils/pdf.ts +++ b/web/utils/pdf.ts @@ -47,22 +47,13 @@ export const handleApplicationDownload = async ({ link.parentNode?.removeChild(link) } -export const formatDisponibilityZipName = ( - disponibility: Disponibility, - campaign: Campaign, -) => { - return `${disponibility?.espace?.name?.split(' ').join('_')}_${format( +export const formatDisponibilityZipName = (disponibility: Disponibility) => { + return `${disponibility?.espace?.name} - Candidatures période du ${format( disponibility?.start, 'dd-MM-yyyy', - )}_${format( - disponibility?.end, - 'dd-MM-yyyy', - //@ts-expect-error - )}_${disponibility?.espace?.users_permissions_user?.structureName - ?.split(' ') - .join('_')}_${campaign?.title?.split(' ').join('_')}.zip` + )} au ${format(disponibility?.end, 'dd-MM-yyyy')}.zip` } export const formatCampaignZipName = (campaign: Campaign) => { - return `${campaign?.title?.split(' ').join('_')}.zip` + return `${campaign?.title}.zip` }