Skip to content

Commit

Permalink
Onglet fiches actions liées sur la page indicateur
Browse files Browse the repository at this point in the history
  • Loading branch information
mariheck committed Jan 16, 2025
1 parent ada346e commit 179372c
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Button, EmptyCard } from '@/ui';
import { useState } from 'react';
import { objectToSnake } from 'ts-case-convert';
import FichePicto from '../../PlansActions/FicheAction/FichesLiees/FichePicto';
import ModaleFichesLiees from '../../PlansActions/FicheAction/FichesLiees/ModaleFichesLiees';
import FichesActionListe from '../../PlansActions/ToutesLesFichesAction/FichesActionListe';
import {
useFichesActionLiees,
useUpdateFichesActionLiees,
} from '../Indicateur/useFichesActionLiees';
import { TIndicateurDefinition } from '../types';

type Props = {
definition: TIndicateurDefinition;
isReadonly: boolean;
};

const FichesLiees = ({ definition, isReadonly }: Props) => {
const [isModalOpen, setIsModalOpen] = useState(false);

const { data: fiches } = useFichesActionLiees(definition);
const fichesLiees = (fiches ? objectToSnake(fiches) : []).map((f) => f.id);

const { mutate: updateFichesActionLiees } =
useUpdateFichesActionLiees(definition);

const isEmpty = fiches.length === 0;

return (
<>
{isEmpty ? (
<EmptyCard
picto={(props) => <FichePicto {...props} />}
title="Aucune fiche action de vos plans d'actions n'est liée !"
isReadonly={isReadonly}
actions={[
{
children: 'Lier une fiche action',
icon: 'link',
onClick: () => setIsModalOpen(true),
},
]}
size="xs"
/>
) : (
<div>
<div className="flex justify-between items-center flex-wrap mb-5">
<h6 className="text-lg mb-0">Fiches des plans liées</h6>
{!isReadonly && (
<Button
icon="link"
size="xs"
className="w-fit"
onClick={() => setIsModalOpen(true)}
>
Lier une fiche action
</Button>
)}
</div>
<FichesActionListe
filtres={{ ficheActionIds: fichesLiees }}
sortSettings={{
defaultSort: 'titre',
}}
isReadOnly={isReadonly}
enableGroupedActions
containeClassName="bg-white"
/>
</div>
)}

{isModalOpen && (
<ModaleFichesLiees
isOpen={isModalOpen && !isReadonly}
setIsOpen={setIsModalOpen}
currentFicheId={null}
linkedFicheIds={fichesLiees}
updateLinkedFicheIds={updateFichesActionLiees}
/>
)}
</>
);
};

export default FichesLiees;
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import ScrollTopButton from '@/app/ui/buttons/ScrollTopButton';
import { BadgeACompleter } from '@/app/ui/shared/Badge/BadgeACompleter';
import { Badge, Tab, Tabs } from '@/ui';
import ActionsLieesListe from '../../PlansActions/FicheAction/ActionsLiees/ActionsLieesListe';
import { FichesActionLiees } from '../Indicateur/FichesActionLiees';
import { HeaderIndicateur } from '../Indicateur/detail/HeaderIndicateur';
import { useUpdateIndicateurDefinition } from '../Indicateur/useUpdateIndicateurDefinition';
import BadgeOpenData from '../components/BadgeOpenData';
import { TIndicateurDefinition } from '../types';
import DonneesIndicateur from './DonneesIndicateur';
import FichesLiees from './FichesLiees';
import IndicateurToolbar from './IndicateurToolbar';
import SousIndicateurs from './SousIndicateurs';

Expand Down Expand Up @@ -114,7 +114,10 @@ const IndicateurLayout = ({
) : (
// Indicateur sans enfant, groupe d'indicateurs avec agrégation,
// ou indicateur personnalisé
<Tabs className="mt-12" tabsListClassName="!justify-start">
<Tabs
className="mt-12"
tabsListClassName="!justify-start flex-nowrap overflow-x-scroll"
>
{/* Données */}
<Tab label="Données">
<DonneesIndicateur
Expand Down Expand Up @@ -146,7 +149,7 @@ const IndicateurLayout = ({

{/* Fiches des plans liées */}
<Tab label="Fiches des plans liées">
<FichesActionLiees definition={definition} />
<FichesLiees definition={definition} isReadonly={isReadonly} />
</Tab>
</Tabs>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { useState } from 'react';
import { QueryKey } from 'react-query';
import BadgePriorite from '../../components/BadgePriorite';
import BadgeStatut from '../../components/BadgeStatut';
import { generateTitle } from '../data/utils';
import ModaleSuppression from '../Header/actions/ModaleSuppression';
import { generateTitle } from '../data/utils';
import FicheActionFooterInfo from './FicheActionFooterInfo';
import ModifierFicheModale from './ModifierFicheModale';

Expand Down Expand Up @@ -118,7 +118,7 @@ const FicheActionCard = ({
dataTest="FicheActionCarte"
id={carteId}
className={classNames(
'h-full px-4 py-[1.125rem] !gap-3 !text-grey-8 !shadow-none transition',
'h-full !p-4 !gap-2 !text-grey-8 !shadow-none transition',
{
'hover:border-primary-3 hover:!bg-primary-1': !isNotClickable,
}
Expand Down Expand Up @@ -155,23 +155,27 @@ const FicheActionCard = ({
}
footer={
// Bas de la carte
<div className="flex flex-col gap-3 text-grey-8 font-normal">
{/* Date de dernière modification */}
{!!ficheAction.modifiedAt && (
<span
className="text-xs font-medium italic"
title="Dernière modification"
>
Modifié {getModifiedSince(ficheAction.modifiedAt)}
</span>
)}

<div className="flex flex-col gap-2 font-normal">
{/* Personnes pilote et date de fin prévisionnelle */}
<FicheActionFooterInfo
pilotes={ficheAction.pilotes}
dateDeFin={ficheAction.dateFinProvisoire}
services={ficheAction.services}
ameliorationContinue={ficheAction.ameliorationContinue}
/>

{/* Date de dernière modification */}
{!!ficheAction.modifiedAt && (
<>
<div className="h-[0.5px] w-full bg-primary-3 mt-1" />
<span
className="text-xs font-medium italic text-grey-6"
title="Dernière modification"
>
Modifié {getModifiedSince(ficheAction.modifiedAt)}
</span>
</>
)}
</div>
}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,80 +1,109 @@
import { Personne } from '@/api/collectivites';
import { getTextFormattedDate } from '@/app/utils/formatUtils';
import { Tag } from '@/domain/collectivites';
import { Icon, Tooltip } from '@/ui';
import classNames from 'classnames';
import { isBefore, startOfToday } from 'date-fns';
import { Fragment } from 'react';

type FicheActionFooterInfoProps = {
pilotes: Personne[] | null | undefined;
services: Tag[] | null | undefined;
dateDeFin: string | null | undefined;
ameliorationContinue: boolean | null | undefined;
};

const FicheActionFooterInfo = ({
pilotes,
services,
dateDeFin,
ameliorationContinue,
}: FicheActionFooterInfoProps) => {
const hasPilotes = !!pilotes && pilotes.length > 0;
const hasServices = !!services && services.length > 0;
const hasDateDeFin = !!dateDeFin;

if (!hasPilotes && !hasDateDeFin && !ameliorationContinue) return null;

const isLate = hasDateDeFin && isBefore(new Date(dateDeFin), startOfToday());

return (
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm">
{/* Personnes pilote */}
{hasPilotes && (
<span title="Pilotes">
<Icon icon="user-line" size="sm" className="mr-1" />
{pilotes[0].nom}
{pilotes.length > 1 && (
<Tooltip
openingDelay={250}
label={
<ul className="max-w-xs list-disc list-inside">
{pilotes.map((pilote, i) => (
<li key={i}>{pilote.nom}</li>
))}
</ul>
}
>
<span className="ml-1.5 font-medium text-primary-8">
+{pilotes.length - 1}
</span>
</Tooltip>
)}
</span>
)}

<div className="flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-primary-10 min-h-5">
{/* Date de fin prévisionnelle */}
{!!dateDeFin && (
<Fragment>
{hasPilotes && <div className="w-[1px] h-4 bg-grey-5" />}
<span
title="Échéance"
className={classNames({ 'text-error-1': isLate })}
>
<Icon icon="calendar-line" size="sm" className="mr-1" />
{getTextFormattedDate({
date: dateDeFin,
shortMonth: true,
})}
</span>
</Fragment>
<span
title="Échéance"
className={classNames({ 'text-error-1': isLate })}
>
<Icon icon="calendar-line" size="sm" className="mr-1" />
{getTextFormattedDate({
date: dateDeFin,
shortMonth: true,
})}
</span>
)}

{/* Action récurrente */}
{!hasDateDeFin && ameliorationContinue && (
<Fragment>
{hasPilotes && <div className="w-[1px] h-4 bg-grey-5" />}
<span title="Échéance">
<Icon icon="loop-left-line" size="sm" className="mr-1" />
Tous les ans
<span title="Échéance">
<Icon icon="loop-left-line" size="sm" className="mr-1" />
Tous les ans
</span>
)}

{/* Personnes pilote */}
{hasPilotes && (
<>
{(hasDateDeFin || ameliorationContinue) && (
<div className="w-[0.5px] h-4 bg-grey-5" />
)}
<span title="Pilotes">
<Icon icon="user-line" size="sm" className="mr-1" />
{pilotes[0].nom}
{pilotes.length > 1 && (
<Tooltip
openingDelay={250}
label={
<ul className="max-w-xs list-disc list-inside">
{pilotes.map((pilote, i) => (
<li key={i}>{pilote.nom}</li>
))}
</ul>
}
>
<span className="ml-1.5 font-medium text-primary-8">
+{pilotes.length - 1}
</span>
</Tooltip>
)}
</span>
</>
)}

{/* Services pilote */}
{hasServices && (
<>
{(hasDateDeFin || ameliorationContinue || hasPilotes) && (
<div className="w-[0.5px] h-4 bg-grey-5" />
)}{' '}
<span title="Direction ou service pilote">
<Icon icon="leaf-line" size="sm" className="mr-1" />
{services[0].nom}
{services.length > 1 && (
<Tooltip
openingDelay={250}
label={
<ul className="max-w-xs list-disc list-inside">
{services.map((service, i) => (
<li key={i}>{service.nom}</li>
))}
</ul>
}
>
<span className="ml-1.5 font-medium text-primary-8">
+{services.length - 1}
</span>
</Tooltip>
)}
</span>
</Fragment>
</>
)}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useEffect, useState } from 'react';
type ModaleFichesLieesProps = {
isOpen: boolean;
setIsOpen: (opened: boolean) => void;
currentFicheId: number;
currentFicheId: number | null;
linkedFicheIds: number[];
updateLinkedFicheIds: (ficheIds: number[]) => void;
};
Expand Down
Loading

0 comments on commit 179372c

Please sign in to comment.