Skip to content

Commit

Permalink
Actions groupées primaires sur les fiches actions
Browse files Browse the repository at this point in the history
  • Loading branch information
mariheck committed Dec 10, 2024
1 parent 777966d commit 1952336
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Alert } from '@tet/ui';
import classNames from 'classnames';
import ExportFicheActionGroupeesButton from '../ExportPdf/ExportFicheActionGroupeesButton';
import EditionPilote from './EditionPilote';
import EditionStatut from './EditionStatut';

type ActionsGroupeesMenuProps = {
isGroupedActionsOn: boolean;
selectedIds: number[];
};

const ActionsGroupeesMenu = ({
isGroupedActionsOn,
selectedIds,
}: ActionsGroupeesMenuProps) => {
return (
<Alert
className={classNames(
'absolute left-0 bottom-0 border-t border-t-info-1 pt-2 pb-4 transition-all duration-500',
{
'opacity-100 z-50': isGroupedActionsOn && selectedIds.length > 1,
'opacity-0 -z-10': selectedIds.length <= 1,
}
)}
title="Appliquer une action groupée"
description={
<div className="flex gap-3 flex-wrap">
<EditionPilote selectedIds={selectedIds} />
<EditionStatut selectedIds={selectedIds} />
<ExportFicheActionGroupeesButton fichesIds={selectedIds} />
</div>
}
fullPageWidth
noIcon
/>
);
};

export default ActionsGroupeesMenu;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { FormSectionGrid, Modal, ModalFooterOKCancel } from '@tet/ui';
import { OpenState } from '@tet/ui/utils/types';

type ActionsGroupeesModaleProps = {
children: JSX.Element;
openState: OpenState;
title: string;
actionsCount: number;
onSave: () => void;
};

const ActionsGroupeesModale = ({
children,
actionsCount,
onSave,
...props
}: ActionsGroupeesModaleProps) => {
return (
<Modal
{...props}
subTitle={`Sur les ${actionsCount} fiches sélectionnées`}
render={({ descriptionId }) => (
<FormSectionGrid formSectionId={descriptionId}>
{children}
</FormSectionGrid>
)}
renderFooter={({ close }) => (
<ModalFooterOKCancel
btnCancelProps={{ onClick: close }}
btnOKProps={{
children: `Modifier les ${actionsCount} fiches`,
onClick: () => {
close();
onSave();
},
}}
/>
)}
/>
);
};

export default ActionsGroupeesModale;
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Personne } from '@tet/api/collectivites';
import { Button, Field, useEventTracker } from '@tet/ui';
import { OpenState } from '@tet/ui/utils/types';
import { useCollectiviteId } from 'core-logic/hooks/params';
import { useState } from 'react';
import PersonnesDropdown from 'ui/dropdownLists/PersonnesDropdown/PersonnesDropdown';
import { getPersonneStringId } from 'ui/dropdownLists/PersonnesDropdown/utils';
import ActionsGroupeesModale from './ActionsGroupeesModale';
import { useFichesActionsBulkEdit } from './useFichesActionsBulkEdit';

type ModaleEditionPiloteProps = {
openState: OpenState;
selectedIds: number[];
};

const ModaleEditionPilote = ({
openState,
selectedIds,
}: ModaleEditionPiloteProps) => {
const [pilotesToAdd, setPilotesToAdd] = useState<Personne[] | undefined>();
const [pilotesToRemove, setPilotesToRemove] = useState<
Personne[] | undefined
>();

const collectiviteId = useCollectiviteId()!;
const tracker = useEventTracker('app/actions-groupees-fiches-action');

const mutation = useFichesActionsBulkEdit();

return (
<ActionsGroupeesModale
openState={openState}
title="Éditer la personne pilote"
actionsCount={selectedIds.length}
onSave={() => {
tracker('editer_personne_pilote_groupe', {
collectivite_id: collectiviteId,
});
mutation.mutate({
ficheIds: selectedIds,
pilotes: {
add: pilotesToAdd?.map((p) => ({
tagId: p.tagId,
userId: p.userId,
})),
remove: pilotesToRemove?.map((p) => ({
tagId: p.tagId,
userId: p.userId,
})),
},
});
}}
>
<>
<Field title="Ajouter une personne pilote" className="col-span-2">
<PersonnesDropdown
values={pilotesToAdd?.map((p) => getPersonneStringId(p))}
placeholder="Sélectionnez ou créez un pilote"
onChange={({ personnes }) => setPilotesToAdd(personnes)}
/>
</Field>
<Field title="Dissocier une personne pilote" className="col-span-2">
<PersonnesDropdown
disableEdition
values={pilotesToRemove?.map((p) => getPersonneStringId(p))}
placeholder="Sélectionnez un ou plusieurs pilotes"
onChange={({ personnes }) => setPilotesToRemove(personnes)}
/>
</Field>
</>
</ActionsGroupeesModale>
);
};

type EditionPiloteProps = {
selectedIds: number[];
};

const EditionPilote = ({ selectedIds }: EditionPiloteProps) => {
const [isModalOpen, setIsModalOpen] = useState(false);

return (
<>
<Button icon="user-line" size="xs" onClick={() => setIsModalOpen(true)}>
Éditer la personne pilote
</Button>
{isModalOpen && (
<ModaleEditionPilote
openState={{ isOpen: isModalOpen, setIsOpen: setIsModalOpen }}
selectedIds={selectedIds}
/>
)}
</>
);
};

export default EditionPilote;
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { RouterInput } from '@tet/api/utils/trpc/client';
import { Button, Field, useEventTracker } from '@tet/ui';
import { OpenState } from '@tet/ui/utils/types';
import { useCollectiviteId } from 'core-logic/hooks/params';
import { useState } from 'react';
import StatutsSelectDropdown from 'ui/dropdownLists/ficheAction/statuts/StatutsSelectDropdown';
import ActionsGroupeesModale from './ActionsGroupeesModale';
import { useFichesActionsBulkEdit } from './useFichesActionsBulkEdit';

type StatusEnumType = RouterInput['plans']['fiches']['bulkEdit']['statut'];

type ModaleEditionStatutProps = {
openState: OpenState;
selectedIds: number[];
};

const ModaleEditionStatut = ({
openState,
selectedIds,
}: ModaleEditionStatutProps) => {
const [status, setStatus] = useState<StatusEnumType>();

const collectiviteId = useCollectiviteId()!;
const tracker = useEventTracker('app/actions-groupees-fiches-action');

const mutation = useFichesActionsBulkEdit();

return (
<ActionsGroupeesModale
openState={openState}
title="Associer un statut"
actionsCount={selectedIds.length}
onSave={() => {
tracker('associer_statut_groupe', {
collectivite_id: collectiviteId,
});
mutation.mutate({
ficheIds: selectedIds,
statut: status,
});
}}
>
<Field title="Statut" className="col-span-2">
<StatutsSelectDropdown
values={status}
onChange={(statut) => setStatus((statut as StatusEnumType) ?? null)}
/>
</Field>
</ActionsGroupeesModale>
);
};

type EditionStatutProps = {
selectedIds: number[];
};

const EditionStatut = ({ selectedIds }: EditionStatutProps) => {
const [isModalOpen, setIsModalOpen] = useState(false);

return (
<>
<Button
icon="stop-circle-line"
size="xs"
onClick={() => setIsModalOpen(true)}
>
Associer un statut
</Button>
{isModalOpen && (
<ModaleEditionStatut
openState={{ isOpen: isModalOpen, setIsOpen: setIsModalOpen }}
selectedIds={selectedIds}
/>
)}
</>
);
};

export default EditionStatut;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { trpc } from '@tet/api/utils/trpc/client';
import { useCollectiviteId } from 'core-logic/hooks/params';
import { useQueryClient } from 'react-query';

export const useFichesActionsBulkEdit = () => {
// TODO: utiliser invalidate de trpc.useUtils()
const collectiviteId = useCollectiviteId()!;
const queryClient = useQueryClient();

const onMutationSuccess = () => {
queryClient.invalidateQueries([
'fiches_resume_collectivite',
collectiviteId,
]);
};

return trpc.plans.fiches.bulkEdit.useMutation({
onSuccess() {
onMutationSuccess();
},
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
SortFichesAction,
SortFichesActionValue,
} from '@tet/api/plan-actions/fiche-resumes.list/domain/fetch-options.schema';
import { Alert, Button, Checkbox, Input, Pagination, Select } from '@tet/ui';
import { Button, Checkbox, Input, Pagination, Select } from '@tet/ui';
import { OpenState } from '@tet/ui/utils/types';
import FicheActionCard from 'app/pages/collectivite/PlansActions/FicheAction/Carte/FicheActionCard';
import PictoExpert from 'ui/pictogrammes/PictoExpert';
Expand All @@ -23,7 +23,7 @@ import FilterBadges, {
CustomFilterBadges,
useFiltersToBadges,
} from 'ui/shared/filters/filter-badges';
import ExportFicheActionGroupeesButton from '../ExportPdf/ExportFicheActionGroupeesButton';
import ActionsGroupeesMenu from '../ActionsGroupees/ActionsGroupeesMenu';

type sortByOptionsType = SortFichesAction & {
label: string;
Expand Down Expand Up @@ -322,23 +322,7 @@ const FichesActionListe = ({
</div>
)}

<Alert
className={classNames(
'absolute left-0 bottom-0 border-t border-t-info-1 pt-2 pb-4 transition-all duration-500',
{
'opacity-100 z-50': isGroupedActionsOn && selectedIds.length > 1,
'opacity-0 -z-10': selectedIds.length <= 1,
}
)}
title="Appliquer une action groupée"
description={
<div className="flex gap-2">
<ExportFicheActionGroupeesButton fichesIds={selectedIds} />
</div>
}
fullPageWidth
noIcon
/>
<ActionsGroupeesMenu {...{ isGroupedActionsOn, selectedIds }} />
</>
);
};
Expand Down
Loading

0 comments on commit 1952336

Please sign in to comment.