diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5580a2f8..085aa15c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -55,6 +55,34 @@ jobs: namespace: ${{ env.DEPLOYMENT_NAMESPACE }} restart: ${{ env.DEPLOYMENT }} + release: + name: Release new version + runs-on: ubuntu-latest + needs: publish-ghcr + steps: + - name: Check Out Repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Get version + id: version + run: echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + - name: Create changelog text + id: changelog + uses: loopwerk/tag-changelog@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + config_file: .github/config/changelog.js + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.version.outputs.tag }} + release_name: ${{ steps.version.outputs.tag }} + body: "${{ steps.changelog.outputs.changes }}" + notify: needs: deploy if: always() diff --git a/src/App.js b/src/App.js index eba23757..f1ae3655 100644 --- a/src/App.js +++ b/src/App.js @@ -64,6 +64,8 @@ import { import './styles/index.scss'; import SearchPage from './pages/rechercher'; +import ContactPage from './pages/nous-contacter'; +import ProjetEtEquipe from './pages/projet-et-equipe'; import AgendaOutlet from './components/outlets/evenements'; import DocumentsOutlet from './components/outlets/documents'; import ActualitesOutlet from './components/outlets/actualites'; @@ -84,6 +86,8 @@ function App() { } /> } /> } /> + } /> + } /> } /> }> diff --git a/src/components/blocs/identifiers/index.js b/src/components/blocs/identifiers/index.js index e608ea11..d19c2664 100644 --- a/src/components/blocs/identifiers/index.js +++ b/src/components/blocs/identifiers/index.js @@ -168,6 +168,7 @@ export default function IdentifiersComponent() { key={el.id} onEdit={() => onOpenModalHandler(el)} linkTo={getLink(el)} + inactive={!(el?.active === true)} />, ); if (el.type === 'Id unité CNRS') { @@ -181,6 +182,7 @@ export default function IdentifiersComponent() { key={el.id} onEdit={() => onOpenModalHandler(el)} linkTo={getLink({ ...el, type: 'CNRS - grafilabo' })} + inactive={!(el?.active === true)} />, ); } @@ -195,6 +197,7 @@ export default function IdentifiersComponent() { key={el.id} onEdit={() => onOpenModalHandler(el)} linkTo={getLink({ ...el, type: 'Wikidata JSON' })} + inactive={!(el?.active === true)} />, ); } @@ -210,6 +213,7 @@ export default function IdentifiersComponent() { key={el.id} onEdit={() => onOpenModalHandler(el)} linkTo={getLink({ ...el, type: 'Siren' })} + inactive={!(el?.active === true)} />, ); list.push( @@ -221,6 +225,7 @@ export default function IdentifiersComponent() { icon="ri-fingerprint-2-line" key={el.id} onEdit={() => onOpenModalHandler(el)} + inactive={!(el?.active === true)} />, ); } diff --git a/src/components/blocs/relations-associated/index.js b/src/components/blocs/relations-associated/index.js index ec7fad92..ca3c19de 100644 --- a/src/components/blocs/relations-associated/index.js +++ b/src/components/blocs/relations-associated/index.js @@ -5,9 +5,9 @@ import { Bloc, BlocContent, BlocTitle } from '../../bloc'; import useFetch from '../../../hooks/useFetch'; import useUrl from '../../../hooks/useUrl'; -export default function RelationsAssociated({ blocName, tag }) { +export default function RelationsAssociated({ blocName, tag, sort }) { const { id: resourceId } = useUrl(); - const url = `/relations?filters[relationTag]=${tag}&filters[otherAssociatedObjectIds]=${resourceId}&limit=200`; + const url = `/relations?filters[relationTag]=${tag}&filters[otherAssociatedObjectIds]=${resourceId}&limit=200&sort=${sort}`; const { data, isLoading, error } = useFetch(url); const renderCards = () => { @@ -31,8 +31,10 @@ export default function RelationsAssociated({ blocName, tag }) { RelationsAssociated.propTypes = { blocName: PropTypes.string, tag: PropTypes.string.isRequired, + sort: PropTypes.string, }; RelationsAssociated.defaultProps = { blocName: '', + sort: '-startDate', }; diff --git a/src/components/blocs/relations-by-group/index.js b/src/components/blocs/relations-by-group/index.js index e7bb4bd7..94cec8f7 100644 --- a/src/components/blocs/relations-by-group/index.js +++ b/src/components/blocs/relations-by-group/index.js @@ -16,7 +16,7 @@ import { deleteError, saveError, saveSuccess, deleteSuccess } from '../../../uti export default function RelationsByGroup({ group, reloader }) { const { id: groupId, name: groupName, accepts: groupAccepts } = group; const { notice } = useNotice(); - const { id: resourceId, apiObject, url: listUrl } = useUrl('relations-groups'); + const { id: resourceId, apiObject } = useUrl('relations-groups'); const url = `/relations?filters[relationsGroupId]=${groupId}`; const { data, isLoading, error, reload } = useFetch(url); const [showModal, setShowModal] = useState(false); @@ -39,13 +39,13 @@ export default function RelationsByGroup({ group, reloader }) { }; const onSaveListHandler = async (body, id = null) => { - const saveUrl = `${listUrl}/${id}`; + const saveUrl = `/relations-groups/${id}`; await api.patch(saveUrl, body).then(() => { notice(saveSuccess); reloader(); }).catch(() => notice(saveError)); return setShowListModal(false); }; const onDeleteListHandler = async (id) => { - await api.delete(`${listUrl}/${id}`) + await api.delete(`/relations-groups/${id}`) .then(() => { notice(deleteSuccess); reloader(); }) .catch(() => notice(deleteError)); return setShowListModal(false); diff --git a/src/components/blocs/relations-by-tag/index.js b/src/components/blocs/relations-by-tag/index.js index 0383ba01..70392c1d 100644 --- a/src/components/blocs/relations-by-tag/index.js +++ b/src/components/blocs/relations-by-tag/index.js @@ -12,6 +12,19 @@ import useNotice from '../../../hooks/useNotice'; import Map from '../../map/auto-bound-map'; import { deleteError, saveError, saveSuccess, deleteSuccess } from '../../../utils/notice-contents'; +const getMarkers = (structures) => structures.map((element) => { + const { coordinates } = element.currentLocalisation.geometry; + const markersCoordinates = [...coordinates]; + const reversed = markersCoordinates.reverse(); + return ({ + latLng: reversed, + address: `${element.displayName} + ${element.currentLocalisation?.address}, + ${element.currentLocalisation?.postalCode}, + ${element.currentLocalisation?.locality}`, + }); +}); + export default function RelationsByTag({ blocName, tag, resourceType, relatedObjectTypes, inverse, noRelationType, Form, sort }) { const queryObject = inverse ? 'relatedObjectId' : 'resourceId'; const { notice } = useNotice(); @@ -61,19 +74,19 @@ export default function RelationsByTag({ blocName, tag, resourceType, relatedObj const renderCards = () => { const relatedKey = inverse ? 'resource' : 'relatedObject'; if (!data && !data?.data?.length) return null; - const structures = data.data.filter((element) => (element[relatedKey]?.collection === 'structures' && element[relatedKey]?.currentLocalisation?.geometry?.coordinates)); - const markers = structures.map((element) => { - const { coordinates } = element[relatedKey].currentLocalisation.geometry; - const markersCoordinates = [...coordinates]; - const reversed = markersCoordinates.reverse(); - return ({ - latLng: reversed, - address: `${element[relatedKey].displayName} - ${element[relatedKey].currentLocalisation?.address}, - ${element[relatedKey].currentLocalisation?.postalCode}, - ${element[relatedKey].currentLocalisation?.locality}`, - }); - }); + const relatedStructures = data.data + .filter((element) => (element[relatedKey]?.collection === 'structures')) + .filter((element) => element[relatedKey]?.currentLocalisation?.geometry?.coordinates) + .map((element) => element[[relatedKey]]); + const relatedMarkers = getMarkers(relatedStructures); + const associatedStructures = data.data + .map((element) => element.otherAssociatedObjects) + .filter((element) => (element?.length > 0)) + .flat() + .filter((element) => element.collection === 'structures') + .filter((element) => element?.currentLocalisation?.geometry?.coordinates); + const associatedStructuresMarkers = getMarkers(associatedStructures); + const currentRelations = data?.data .filter((relation) => (!relation.endDate || (new Date(relation.endDate) >= new Date()))) .map((relation) => ({ ...relation, current: true })); @@ -90,7 +103,9 @@ export default function RelationsByTag({ blocName, tag, resourceType, relatedObj onEdit={() => onOpenModalHandler(element)} /> )); - if (structures.length) { + + const markers = [...relatedMarkers, ...associatedStructuresMarkers]; + if (markers.length) { return ( diff --git a/src/components/card/key-value-card.js b/src/components/card/key-value-card.js index dd486c71..27545fbe 100644 --- a/src/components/card/key-value-card.js +++ b/src/components/card/key-value-card.js @@ -1,4 +1,4 @@ -import { Icon } from '@dataesr/react-dsfr'; +import { Badge, Icon } from '@dataesr/react-dsfr'; import PropTypes from 'prop-types'; import { Link as RouterLink } from 'react-router-dom'; @@ -17,6 +17,7 @@ export default function KeyValueCard({ onEdit, titleAsText, tooltip, + inactive, }) { const { editMode } = useEditMode(); @@ -51,6 +52,7 @@ export default function KeyValueCard({ title={tooltip} /> )} + {inactive && }

{editMode && onEdit && ( @@ -81,6 +83,7 @@ KeyValueCard.propTypes = { onEdit: PropTypes.func, titleAsText: PropTypes.string, tooltip: PropTypes.string, + inactive: PropTypes.bool, }; KeyValueCard.defaultProps = { className: '', @@ -91,4 +94,5 @@ KeyValueCard.defaultProps = { onEdit: null, titleAsText: false, tooltip: null, + inactive: false, }; diff --git a/src/components/download/file.js b/src/components/download/file.js index 37db6da6..a00e5cfa 100644 --- a/src/components/download/file.js +++ b/src/components/download/file.js @@ -33,7 +33,7 @@ const mapping = { document: { icon: 'ri-file-word-fill', color: 'var(--blue-ecume-main-400)' }, powerpoint: { icon: 'ri-file-ppt-fill', color: 'var(--grey-main-525)' }, excel: { icon: 'ri-file-excel-fill', color: 'var(--green-emeraude-main-632)' }, - archive: { icon: 'ri-file-zip-fill', color: 'var(--grey-main-525)' }, + archive: { icon: 'ri-file-zip-fill', color: 'var(--yellow-tournesol-main-731)' }, }; const getFileIcon = (mimetype) => { diff --git a/src/components/errors/index.js b/src/components/errors/index.js index a59508b2..5eeb8ca2 100644 --- a/src/components/errors/index.js +++ b/src/components/errors/index.js @@ -1,3 +1,4 @@ +import { ButtonGroup, Col, Container, Row, Text, Title } from '@dataesr/react-dsfr'; import PropTypes from 'prop-types'; import { Link } from 'react-router-dom'; import { ReactComponent as TechErrorSVG } from '../../assets/technical-error.svg'; @@ -5,51 +6,56 @@ import { ReactComponent as TechErrorSVG } from '../../assets/technical-error.svg function Error500() { return ( <> -

Erreur inattendue

-

Erreur 500

-

Essayez de rafraichir la page ou bien ressayez plus tard.

-

+ Erreur inattendue + Erreur 500 + Essayez de rafraîchir la page ou bien réessayez plus tard. + Désolé, le service rencontre un problème, nous travaillons pour le résoudre le plus rapidement possible. -

+ ); } function Error404() { return ( <> -

Page non trouvée

-

Erreur 404

-

La page que vous cherchez est introuvable. Excusez-nous pour la gène occasionnée.

-

- Si vous avez tapé l'adresse web dans le navigateur, vérifiez qu'elle est correcte. La page n’est peut-être plus disponible. + Page non trouvée + Erreur 404 + La page que vous cherchez est introuvable. Excusez-nous pour la gêne occasionnée. + + Si vous avez tapé l'adresse web dans le navigateur, vérifiez qu'elle est correcte. La page n'est peut-être plus disponible.
- Dans ce cas, pour continuer votre visite vous pouvez consulter notre page d’accueil, ou effectuer une recherche avec notre moteur de recherche en haut de page. + Dans ce cas, pour continuer votre visite, vous pouvez consulter notre page d'accueil, ou effectuer une recherche avec notre moteur de recherche en haut de page.
- Sinon contactez-nous pour que l’on puisse vous rediriger vers la bonne information. -

+ Sinon contactez-nous pour que l'on puisse vous rediriger vers la bonne information. + ); } export default function Error({ status }) { return ( -
-
-
- {['400', '404'].includes(status) ? : } -
    + + + + {['400', '404', '403', '401'].includes(status) ? : } + +
  • + + Retour à l'accueil + +
  • Contactez-nous
  • -
-
-
+ + + -
-
-
+ +
+ ); } diff --git a/src/components/forms/category-term/index.js b/src/components/forms/category-term/index.js index 8bfc53d6..5f61ae88 100644 --- a/src/components/forms/category-term/index.js +++ b/src/components/forms/category-term/index.js @@ -1,6 +1,7 @@ -import PropTypes from 'prop-types'; import { Col, Container, Row, TextInput, Title } from '@dataesr/react-dsfr'; +import PropTypes from 'prop-types'; import { useEffect, useState } from 'react'; + import api from '../../../utils/api'; import PaysageBlame from '../../paysage-blame'; import useForm from '../../../hooks/useForm'; @@ -39,7 +40,7 @@ function sanitize(form) { return body; } -export default function CategoryTermsForm({ id, data, onSave, onDelete }) { +export default function CategoryTermsForm({ data, id, onDelete, onSave }) { const [showErrors, setShowErrors] = useState(false); const [startDateOfficialTextQuery, setStartDateOfficialTextQuery] = useState(''); const [endDateOfficialTextQuery, setEndDateOfficialTextQuery] = useState(''); @@ -241,13 +242,13 @@ export default function CategoryTermsForm({ id, data, onSave, onDelete }) { } CategoryTermsForm.propTypes = { - id: PropTypes.string, data: PropTypes.oneOfType([PropTypes.shape, null]), - onSave: PropTypes.func.isRequired, + id: PropTypes.string, onDelete: PropTypes.func, + onSave: PropTypes.func.isRequired, }; CategoryTermsForm.defaultProps = { - id: null, data: { relatedObjects: [] }, + id: null, onDelete: null, }; diff --git a/src/components/forms/form-footer/index.js b/src/components/forms/form-footer/index.js index c878ba95..3d7bb1f9 100644 --- a/src/components/forms/form-footer/index.js +++ b/src/components/forms/form-footer/index.js @@ -5,7 +5,7 @@ import classNames from 'classnames'; import Button from '../../button'; import useViewport from '../../../hooks/useViewport'; -export default function FormFooter({ id, onDeleteHandler, onSaveHandler }) { +export default function FormFooter({ id, onDeleteHandler, onSaveHandler, buttonLabel }) { const { mobile } = useViewport(); const [confirm, setConfirm] = useState(false); const classnames = classNames('flex--space-between', { 'flex--col-reverse': mobile }); @@ -50,7 +50,7 @@ export default function FormFooter({ id, onDeleteHandler, onSaveHandler }) { Supprimer ) :
} - + @@ -60,12 +60,14 @@ export default function FormFooter({ id, onDeleteHandler, onSaveHandler }) { } FormFooter.propTypes = { + buttonLabel: PropTypes.string, id: PropTypes.string, onSaveHandler: PropTypes.func.isRequired, onDeleteHandler: PropTypes.func, }; FormFooter.defaultProps = { + buttonLabel: 'Sauvegarder', id: null, onDeleteHandler: () => {}, }; diff --git a/src/components/forms/laureate/index.js b/src/components/forms/laureate/index.js index 655961e4..0eef3576 100644 --- a/src/components/forms/laureate/index.js +++ b/src/components/forms/laureate/index.js @@ -20,8 +20,7 @@ import PaysageBlame from '../../paysage-blame'; function sanitize(form) { const newForm = { ...form }; if (newForm.otherAssociatedObjects?.length) newForm.otherAssociatedObjectIds = newForm.otherAssociatedObjects.map((associated) => associated.id); - const fields = ['resourceId', 'relatedObjectId', 'relationTypeId', 'relationsGroupId', 'relationTag', - 'startDateOfficialTextId', 'endDateOfficialTextId', 'startDate', 'endDate', 'otherAssociatedObjectIds']; + const fields = ['resourceId', 'relatedObjectId', 'relationTypeId', 'relationsGroupId', 'relationTag', 'startDate', 'endDate', 'otherAssociatedObjectIds']; const body = {}; Object.keys(newForm).forEach((key) => { if (fields.includes(key)) { body[key] = newForm[key]; } }); return body; @@ -141,7 +140,7 @@ export default function LaureateForm({ id, resourceType, relatedObjectTypes, dat buttonLabel="Rechercher" value={resourceQuery || ''} label="Prix" - hint="Rechercher parmis les prix" + hint="Rechercher parmi les prix" required scope={form.resourceName} placeholder={form.resourceId ? '' : 'Rechercher...'} @@ -159,7 +158,9 @@ export default function LaureateForm({ id, resourceType, relatedObjectTypes, dat buttonLabel="Rechercher" value={relatedObjectQuery || ''} label="Lauréat" - hint="Rechercher parmis les structures, les personnes et les projets" + // TODO: Restore projects + // hint="Rechercher parmi les structures, les personnes et les projets" + hint="Rechercher parmi les structures et les personnes" required scope={form.relatedObjectName} placeholder={form.relatedObjectId ? '' : 'Rechercher...'} @@ -175,7 +176,7 @@ export default function LaureateForm({ id, resourceType, relatedObjectTypes, dat { setAssociatedQuery(e.target.value); }} options={associatedOptions} diff --git a/src/components/forms/legal-categories/index.js b/src/components/forms/legal-categories/index.js index 249c6a7d..eeda1626 100644 --- a/src/components/forms/legal-categories/index.js +++ b/src/components/forms/legal-categories/index.js @@ -34,13 +34,13 @@ export default function LegalCategoriesForm({ id, data, onSave, onDelete }) { }; const sectorOptions = [ - { value: null, label: "Selectionner un type d'objet" }, + { value: null, label: "Sélectionner un type d'objet" }, { value: 'public', label: 'Public' }, { value: 'privé', label: 'Privé' }, { value: 'sans objet', label: 'Sans objet' }, ]; const legalPersonalityOptions = [ - { value: null, label: "Selectionner un type d'objet" }, + { value: null, label: "Sélectionner un type d'objet" }, { value: 'personne morale de droit public', label: 'Personne morale de droit public' }, { value: 'personne morale de droit privé', label: 'Personne morale de droit privé' }, { value: 'organisation internationale', label: 'Organisation internationale' }, diff --git a/src/components/forms/localisation/index.js b/src/components/forms/localisation/index.js index 10a67190..7a3d55fd 100644 --- a/src/components/forms/localisation/index.js +++ b/src/components/forms/localisation/index.js @@ -26,8 +26,8 @@ function validate(body) { function sanitize(form) { const fields = [ - 'cityId', 'city', 'distributionStatement', 'address', 'postOfficeBoxNumber', 'postalCode', - 'locality', 'place', 'country', 'telephone', 'coordinates', 'startDate', 'endDate', + 'address', 'city', 'cityId', 'coordinates', 'country', 'distributionStatement', 'endDate', + 'locality', 'place', 'postalCode', 'postOfficeBoxNumber', 'startDate', 'telephone', ]; const body = {}; Object.keys(form).forEach((key) => { if (fields.includes(key)) { body[key] = form[key]; } }); diff --git a/src/components/forms/person/index.js b/src/components/forms/person/index.js index 8722f1ca..dd0ba4b2 100644 --- a/src/components/forms/person/index.js +++ b/src/components/forms/person/index.js @@ -38,7 +38,7 @@ export default function PersonForm({ id, data, onSave, onDelete }) { return onSave(body); }; const genderOptions = [ - { value: '', label: 'Selectionner' }, + { value: '', label: 'Sélectionner' }, { value: 'Homme', label: 'Homme' }, { value: 'Femme', label: 'Femme' }, { value: 'Autre', label: 'Autre' }, diff --git a/src/components/forms/relations-group/index.js b/src/components/forms/relations-group/index.js index 7eb9ff1b..5157e59f 100644 --- a/src/components/forms/relations-group/index.js +++ b/src/components/forms/relations-group/index.js @@ -24,7 +24,7 @@ function validate(body) { return validationErrors; } function sanitize(form) { - const fields = ['name', 'otherNames', 'accepts', 'priority']; + const fields = ['name', 'otherNames', 'accepts', 'priority', 'resourceId']; const body = {}; Object.keys(form).forEach((key) => { if (fields.includes(key)) { body[key] = form[key]; } }); return body; diff --git a/src/components/layout/footer.js b/src/components/layout/footer.js index 1c44658e..7d8259a6 100644 --- a/src/components/layout/footer.js +++ b/src/components/layout/footer.js @@ -1,8 +1,6 @@ import { Link as RouterLink } from 'react-router-dom'; import PropTypes from 'prop-types'; import { - FooterTop, - FooterTopCategory, Footer as FooterWrapper, FooterBody, FooterBottom, @@ -19,27 +17,10 @@ export default function Footer({ switchTheme }) { return ( - - - }> - Aide - - }> - Nous contacter - - - - }> - Notes de version - - }> - Github - - - } + asLink={} + splitCharacter={9} > Ministère de l‘enseignement supérieur et de la recherche @@ -63,26 +44,33 @@ export default function Footer({ switchTheme }) { - - Plan du Site + }> + Aide - - Accessibilité + }> + L'équipe et son projet + + }> + Nous contacter Mentions légales + }> + Github + {`Version de l'application v${process.env.REACT_APP_VERSION}`} - setIsOpen(true)}> + diff --git a/src/components/layout/not-found.js b/src/components/layout/not-found.js index 485dcb67..6caad7a4 100644 --- a/src/components/layout/not-found.js +++ b/src/components/layout/not-found.js @@ -1,7 +1,7 @@ import React from 'react'; -import { Button, Text, Title } from '@dataesr/react-dsfr'; +import { Button, Text } from '@dataesr/react-dsfr'; import { useNavigate } from 'react-router-dom'; -import './not-found.scss'; +import styles from './not-found.module.scss'; const randomDisplay = Math.floor(Math.random() * 2); @@ -10,54 +10,54 @@ export default function NotFound() { return randomDisplay === 1 ? (
-
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
- Page Introuvable! - Désolé, la page que vous cherchez n'existe pas. - Vous pouvez relancer une recherche ou vous rendre sur la page d'accueil +
+
+
+ Page Introuvable! + Désolé, la page que vous cherchez n'existe pas. + Vous pouvez relancer une recherche ou vous rendre sur la page d'accueil
- +
) : ( -
-
-
+
+
+
-
- Page Introuvable! +
+ Page Introuvable!
- + Désolé, la page que vous cherchez n'existe pas. - Vous pouvez relancer une recherche ou vous rendre sur la page d'accueil + Vous pouvez relancer une recherche ou vous rendre sur la page d'accueil