Skip to content

Commit

Permalink
feat: createdat in new reports
Browse files Browse the repository at this point in the history
  • Loading branch information
Arnaud AMBROSELLI committed Feb 15, 2024
1 parent 0ab933f commit 8d526e8
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 38 deletions.
4 changes: 2 additions & 2 deletions dashboard/src/components/ActionModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useHistory, useLocation } from 'react-router-dom';
import { actionsState, allowedActionFieldsInHistory, CANCEL, DONE, prepareActionForEncryption, TODO } from '../recoil/actions';
import { currentTeamState, organisationState, teamsState, userState } from '../recoil/auth';
import { dayjsInstance, now, outOfBoundariesDate } from '../services/date';
import { dayjsInstance, formatDateWithNameOfDay, now, outOfBoundariesDate } from '../services/date';
import API from '../services/api';
import SelectPerson from './SelectPerson';
import SelectStatus from './SelectStatus';
Expand Down Expand Up @@ -350,7 +350,7 @@ function ActionContent({ onClose, action, personId = null, personIds = null, isM
<UserName
className="tw-block tw-text-right tw-text-base tw-font-normal tw-italic"
id={action.user}
wrapper={(name) => ` (créée par ${name})`}
wrapper={(name) => ` (créée par ${name} le ${formatDateWithNameOfDay(action.createdAt)})`}
/>
)}
</>
Expand Down
15 changes: 13 additions & 2 deletions dashboard/src/components/ActionsSortableList.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import useSearchParamState from '../services/useSearchParamState';
import DescriptionIcon from './DescriptionIcon';
import { AgendaMutedIcon } from '../assets/icons/AgendaMutedIcon';

const ActionsSortableList = ({ data, limit }) => {
const ActionsSortableList = ({ data, limit, showCreatedAt }) => {
useTitle('Agenda');
const history = useHistory();
const user = useRecoilValue(userState);
Expand Down Expand Up @@ -154,7 +154,18 @@ const ActionsSortableList = ({ data, limit }) => {
);
},
},
]}
{
title: 'Créée le',
dataKey: 'createdAt',
onSortOrder: setSortOrder,
onSortBy: setSortBy,
sortBy,
sortOrder,
render: (action) => {
return <DateBloc date={action.createdAt} />;
},
},
].filter((e) => showCreatedAt || e.dataKey !== 'createdAt')}
/>
{limit > 0 && <Page page={page} limit={limit} total={total} onChange={({ page }) => setPage(page, true)} />}
</>
Expand Down
156 changes: 138 additions & 18 deletions dashboard/src/scenes/report/components/ActionsOrConsultationsReport.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,46 @@ import { useRecoilValue } from 'recoil';
import { userState } from '../../../recoil/auth';
import { dayjsInstance } from '../../../services/date';

export const ActionsOrConsultationsReport = ({ actions, consultations, period }) => {
const formatEcheanceLabelPeriod = (period) => {
if (!!period.startDate && !!period.endDate) {
const start = dayjsInstance(period.startDate);
const end = dayjsInstance(period.endDate);
const today = dayjsInstance();
const showYear = start.year() !== end.year() || start.year() !== today.year();
const startFormatted = dayjsInstance(period.startDate).format(showYear ? 'D MMM YYYY' : 'D MMM');
const endFormatted = dayjsInstance(period.endDate).format(showYear ? 'D MMM YYYY' : 'D MMM');
if (startFormatted === endFormatted) return `le ${startFormatted}`;
return `entre le ${startFormatted} et le ${endFormatted}`;
}
return '';
};

export const ActionsOrConsultationsReport = ({
actionsDueOrCompletedAt,
actionsCreatedAt,
consultationsDueOrCompletedAt,
consultationsCreatedAt,
period,
}) => {
const [activeTab, setActiveTab] = useLocalStorage('reports-actions-consultation-toggle', 'Actions');
const [fullScreen, setFullScreen] = useState(false);
const [filterStatus, setFilterStatus] = useState([]);
const filteredActions = actions.filter((item) => !filterStatus.length || filterStatus.includes(item.status));
const filteredConsultations = consultations.filter((item) => !filterStatus.length || filterStatus.includes(item.status));
const [switchCreatedAt, setSwitchCreatedAt] = useLocalStorage('reports-actions-switch-created-at', 'dueOrCompletedAt'); // 'createdAt' or 'dueOrCompletedAt'
const [filterStatus, setFilterStatus] = useLocalStorage('reports-actions-filter-status', []);

const actions = switchCreatedAt === 'createdAt' ? actionsCreatedAt : actionsDueOrCompletedAt;
const consultations = switchCreatedAt === 'createdAt' ? consultationsCreatedAt : consultationsDueOrCompletedAt;
const data = activeTab.includes('Actions') ? actions : consultations;

const filteredActionsDueOrCompletedAt = actionsDueOrCompletedAt.filter((item) => !filterStatus.length || filterStatus.includes(item.status));
const filteredActionsCreatedAt = actionsCreatedAt.filter((item) => !filterStatus.length || filterStatus.includes(item.status));
const filteredConsultationsDueOrCompletedAt = consultationsDueOrCompletedAt.filter(
(item) => !filterStatus.length || filterStatus.includes(item.status)
);
const filteredConsultationsCreatedAt = consultationsCreatedAt.filter((item) => !filterStatus.length || filterStatus.includes(item.status));
const filteredActions = switchCreatedAt === 'createdAt' ? filteredActionsCreatedAt : filteredActionsDueOrCompletedAt;
const filteredConsultations = switchCreatedAt === 'createdAt' ? filteredConsultationsCreatedAt : filteredConsultationsDueOrCompletedAt;
const filteredData = activeTab.includes('Actions') ? filteredActions : filteredConsultations;

const history = useHistory();
const user = useRecoilValue(userState);

Expand Down Expand Up @@ -67,8 +99,15 @@ export const ActionsOrConsultationsReport = ({ actions, consultations, period })
</button>
</div>
</div>
<div className="w-full tw-max-w-lg tw-bg-white tw-px-7 tw-pb-1">
<ActionsOrConsultationsFilters setFilterStatus={setFilterStatus} filterStatus={filterStatus} disabled={!data.length} />
<div className="w-full tw-max-w-3xl tw-bg-white tw-px-7 tw-pb-1">
<ActionsOrConsultationsFilters
switchCreatedAt={switchCreatedAt}
setSwitchCreatedAt={setSwitchCreatedAt}
setFilterStatus={setFilterStatus}
filterStatus={filterStatus}
disabled={!data.length}
period={period}
/>
</div>
<div className="tw-grow tw-overflow-y-auto tw-border-t tw-border-main tw-border-opacity-20">
<ActionsSortableList data={filteredData} />
Expand All @@ -78,7 +117,30 @@ export const ActionsOrConsultationsReport = ({ actions, consultations, period })
aria-hidden="true"
className="printonly tw-flex tw-h-full tw-flex-col tw-overflow-hidden tw-rounded-lg tw-border tw-border-zinc-200 tw-shadow">
<div className="tw-flex tw-flex-col tw-items-stretch tw-bg-white tw-px-3 tw-py-3">
<h3 className="tw-m-0 tw-text-base tw-font-medium">Actions ({filteredActions.length})</h3>
<h3 className="tw-m-0 tw-text-base tw-font-medium">
Actions À FAIRE/FAITE/ANNULÉE {formatEcheanceLabelPeriod(period)} ({filteredActionsDueOrCompletedAt.length})
</h3>
{filterStatus.length > 0 && (
<h4 className="tw-m-0 tw-text-base tw-font-medium">
Filtrées par status:{' '}
{mappedIdsToLabels
.filter((s) => filterStatus.includes(s._id))
.map((status) => status.name)
.join(', ')}
</h4>
)}
</div>
<div className="tw-grow tw-overflow-y-auto tw-border-t tw-border-main tw-border-opacity-20">
<ActionsSortableList data={filteredActionsDueOrCompletedAt} showCreatedAt />
</div>
</section>
<section
aria-hidden="true"
className="printonly tw-mt-12 tw-flex tw-h-full tw-flex-col tw-overflow-hidden tw-rounded-lg tw-border tw-border-zinc-200 tw-shadow">
<div className="tw-flex tw-flex-col tw-items-stretch tw-bg-white tw-px-3 tw-py-3">
<h3 className="tw-m-0 tw-text-base tw-font-medium">
Actions créées {formatEcheanceLabelPeriod(period)} ({filteredActionsCreatedAt.length})
</h3>
{filterStatus.length > 0 && (
<h4 className="tw-m-0 tw-text-base tw-font-medium">
Filtrées par status:{' '}
Expand All @@ -90,14 +152,17 @@ export const ActionsOrConsultationsReport = ({ actions, consultations, period })
)}
</div>
<div className="tw-grow tw-overflow-y-auto tw-border-t tw-border-main tw-border-opacity-20">
<ActionsSortableList data={filteredActions} />
<ActionsSortableList data={filteredActionsCreatedAt} showCreatedAt />
</div>
</section>

<section
aria-hidden="true"
className="printonly tw-mt-12 tw-flex tw-h-full tw-flex-col tw-overflow-hidden tw-rounded-lg tw-border tw-border-zinc-200 tw-shadow">
<div className="tw-flex tw-flex-col tw-items-stretch tw-bg-white tw-px-3 tw-py-3">
<h3 className="tw-m-0 tw-text-base tw-font-medium">Consultations ({filteredConsultations.length})</h3>
<h3 className="tw-m-0 tw-text-base tw-font-medium">
Consultations À FAIRE/FAITE/ANNULÉE {formatEcheanceLabelPeriod(period)} ({filteredConsultationsDueOrCompletedAt.length})
</h3>
{filterStatus.length > 0 && (
<h4 className="tw-m-0 tw-text-base tw-font-medium">
Filtrées par status:{' '}
Expand All @@ -109,17 +174,45 @@ export const ActionsOrConsultationsReport = ({ actions, consultations, period })
)}
</div>
<div className="tw-grow tw-overflow-y-auto tw-border-t tw-border-main tw-border-opacity-20">
<ActionsSortableList data={filteredConsultations} />
<ActionsSortableList data={filteredConsultationsDueOrCompletedAt} showCreatedAt />
</div>
</section>
<section
aria-hidden="true"
className="printonly tw-mt-12 tw-flex tw-h-full tw-flex-col tw-overflow-hidden tw-rounded-lg tw-border tw-border-zinc-200 tw-shadow">
<div className="tw-flex tw-flex-col tw-items-stretch tw-bg-white tw-px-3 tw-py-3">
<h3 className="tw-m-0 tw-text-base tw-font-medium">
Consultations créées {formatEcheanceLabelPeriod(period)} ({filteredConsultationsCreatedAt.length})
</h3>
{filterStatus.length > 0 && (
<h4 className="tw-m-0 tw-text-base tw-font-medium">
Filtrées par status:{' '}
{mappedIdsToLabels
.filter((s) => filterStatus.includes(s._id))
.map((status) => status.name)
.join(', ')}
</h4>
)}
</div>
<div className="tw-grow tw-overflow-y-auto tw-border-t tw-border-main tw-border-opacity-20">
<ActionsSortableList data={filteredConsultationsCreatedAt} showCreatedAt />
</div>
</section>
<ModalContainer open={!!fullScreen} className="" size="full" onClose={() => setFullScreen(false)}>
<ModalHeader title={`${activeTab} (${filteredData.length})`} onClose={() => setFullScreen(false)}>
<div className="tw-mx-auto tw-mt-2 tw-w-full tw-max-w-lg">
<ActionsOrConsultationsFilters setFilterStatus={setFilterStatus} filterStatus={filterStatus} disabled={!data.length} />
<div className="tw-mx-auto tw-mt-2 tw-w-full tw-max-w-3xl">
<ActionsOrConsultationsFilters
switchCreatedAt={switchCreatedAt}
setSwitchCreatedAt={setSwitchCreatedAt}
setFilterStatus={setFilterStatus}
filterStatus={filterStatus}
disabled={!data.length}
period={period}
/>
</div>
</ModalHeader>
<ModalBody>
<ActionsSortableList data={filteredData} />
<ActionsSortableList data={filteredData} showCreatedAt />
</ModalBody>
<ModalFooter>
<button type="button" name="cancel" className="button-cancel" onClick={() => setFullScreen(false)}>
Expand All @@ -141,17 +234,44 @@ export const ActionsOrConsultationsReport = ({ actions, consultations, period })
);
};

const ActionsOrConsultationsFilters = ({ setFilterStatus, filterStatus, disabled }) => {
const ActionsOrConsultationsFilters = ({ switchCreatedAt, setSwitchCreatedAt, setFilterStatus, filterStatus, disabled, period }) => {
const echeanceOptions = [
{
value: 'createdAt',
label: `Créées ${formatEcheanceLabelPeriod(period)}`,
},
{
value: 'dueOrCompletedAt',
label: `Échéance ${formatEcheanceLabelPeriod(period)} (À FAIRE/FAITE/ANNULÉE ${formatEcheanceLabelPeriod(period)})`,
},
];

return (
<>
<div className="tw-flex tw-justify-between">
<div className="tw-flex tw-w-full tw-shrink-0 tw-grow tw-items-center tw-pl-1 tw-pr-2">
<label htmlFor="action-select-status-filter" className="tw-text-xs">
<div className="tw-flex tw-w-full tw-justify-between tw-gap-x-4">
<div className="tw-flex tw-shrink-0 tw-grow tw-basis-1/2 tw-items-center tw-pl-1 tw-pr-2">
<div className="tw-w-full">
<SelectCustom
inputId="action-switch-created-at"
options={echeanceOptions}
isMulti={false}
name="switchCreatedAt"
value={echeanceOptions.find((o) => o.value === switchCreatedAt)}
onChange={(e) => {
console.log(e, e.value);
setSwitchCreatedAt(e.value);
}}
/>
</div>
</div>
<div className="tw-flex tw-shrink-0 tw-grow tw-basis-1/2 tw-items-center tw-pl-1 tw-pr-2">
{/* <label htmlFor="action-select-status-filter" className="tw-text-xs">
Filtrer par statut
</label>
</label> */}
<div className="tw-w-full">
<SelectCustom
inputId="action-select-status-filter"
placeholder="Filtrer par statut"
options={mappedIdsToLabels}
getOptionValue={(s) => s._id}
getOptionLabel={(s) => s.name}
Expand Down
61 changes: 45 additions & 16 deletions dashboard/src/scenes/report/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ const itemsForReportsSelector = selectorFamily({

const personsCreated = {};
const personsUpdated = {};
const actions = {};
const consultations = {};
const actionsDueOrCompletedAt = {};
const actionsCreatedAt = {};
const consultationsDueOrCompletedAt = {};
const consultationsCreatedAt = {};
const comments = {};
const commentsMedical = {};
const passages = {};
Expand Down Expand Up @@ -97,39 +99,45 @@ const itemsForReportsSelector = selectorFamily({
for (const action of person.actions || []) {
if (!filterItemByTeam(action, 'teams')) continue;
if (Array.isArray(action.teams)) {
let isIncluded = false;
let isDueOrCompletedAt = false;
for (const team of action.teams) {
const { isoStartDate, isoEndDate } = selectedTeamsObjectWithOwnPeriod[team] ?? defaultIsoDates;
if (action.createdAt >= isoStartDate && action.createdAt < isoEndDate) {
actionsCreatedAt[action._id] = action;
}
if (action.completedAt >= isoStartDate && action.completedAt < isoEndDate) {
isIncluded = true;
isDueOrCompletedAt = true;
continue;
}
if (action.status !== TODO) continue;
if (action.dueAt >= isoStartDate && action.dueAt < isoEndDate) {
isIncluded = true;
isDueOrCompletedAt = true;
}
}
if (!isIncluded) continue;
actions[action._id] = action;
if (!isDueOrCompletedAt) continue;
actionsDueOrCompletedAt[action._id] = action;
}
}
for (const consultation of person.consultations || []) {
if (!filterItemByTeam(consultation, 'teams')) continue;
if (Array.isArray(consultation.teams)) {
let isIncluded = false;
let isDueOrCompletedAt = false;
for (const team of consultation.teams) {
const { isoStartDate, isoEndDate } = selectedTeamsObjectWithOwnPeriod[team] ?? defaultIsoDates;
if (consultation.createdAt >= isoStartDate && consultation.createdAt < isoEndDate) {
consultationsCreatedAt[consultation._id] = consultation;
}
if (consultation.completedAt >= isoStartDate && consultation.completedAt < isoEndDate) {
isIncluded = true;
isDueOrCompletedAt = true;
continue;
}
if (consultation.status !== TODO) continue;
if (consultation.dueAt >= isoStartDate && consultation.dueAt < isoEndDate) {
isIncluded = true;
isDueOrCompletedAt = true;
}
}
if (!isIncluded) continue;
consultations[consultation._id] = consultation;
if (!isDueOrCompletedAt) continue;
consultationsDueOrCompletedAt[consultation._id] = consultation;
}
}
for (const rencontre of person.rencontres || []) {
Expand Down Expand Up @@ -189,8 +197,10 @@ const itemsForReportsSelector = selectorFamily({
return {
personsCreated: Object.values(personsCreated),
personsUpdated: Object.values(personsUpdated),
actions: Object.values(actions),
consultations: Object.values(consultations),
actionsDueOrCompletedAt: Object.values(actionsDueOrCompletedAt),
actionsCreatedAt: Object.values(actionsCreatedAt),
consultationsDueOrCompletedAt: Object.values(consultationsDueOrCompletedAt),
consultationsCreatedAt: Object.values(consultationsCreatedAt),
comments: Object.values(comments),
commentsMedical: Object.values(commentsMedical),
passages: Object.values(passages).sort((a, b) => (a.date >= b.date ? -1 : 1)),
Expand Down Expand Up @@ -244,7 +254,19 @@ const View = () => {
return teamsIdsObject;
}, [selectedTeams, period]);

const { personsCreated, actions, consultations, comments, commentsMedical, passages, rencontres, observations, reports } = useRecoilValue(
const {
personsCreated,
actionsDueOrCompletedAt,
actionsCreatedAt,
consultationsDueOrCompletedAt,
consultationsCreatedAt,
comments,
commentsMedical,
passages,
rencontres,
observations,
reports,
} = useRecoilValue(
itemsForReportsSelector({
period,
viewAllOrganisationData,
Expand Down Expand Up @@ -358,7 +380,14 @@ const View = () => {
].join(' ')}>
<div className="tw-mb-12 tw-min-h-1/2 tw-basis-6/12 tw-overflow-auto print:tw-min-h-0 print:tw-basis-full">
<div className="tw-mb-4 tw-h-[60vh] tw-overflow-hidden tw-rounded-lg tw-border tw-border-zinc-200 tw-shadow print:tw-h-auto print:tw-border-none print:tw-shadow-none">
<ActionsOrConsultationsReport actions={actions} consultations={consultations} period={period} />
<ActionsOrConsultationsReport
actionsDueOrCompletedAt={actionsDueOrCompletedAt}
actionsCreatedAt={actionsCreatedAt}
consultationsDueOrCompletedAt={consultationsDueOrCompletedAt}
consultationsCreatedAt={consultationsCreatedAt}
period={period}
preset={preset}
/>
</div>
{canSeeComments && (
<div className="tw-mb-4 tw-h-[60vh] tw-overflow-hidden tw-rounded-lg tw-border tw-border-zinc-200 tw-shadow print:tw-h-auto print:tw-border-none print:tw-shadow-none">
Expand Down

0 comments on commit 8d526e8

Please sign in to comment.