Skip to content

Commit

Permalink
beautiful
Browse files Browse the repository at this point in the history
  • Loading branch information
Arnaud AMBROSELLI committed Feb 6, 2024
1 parent 7d079d0 commit 8b63678
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 203 deletions.
76 changes: 9 additions & 67 deletions dashboard/src/components/EvolutiveStatsSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useState } from 'react';
import SelectCustom from './SelectCustom';
import { dayjsInstance } from '../services/date';
import DatePicker from './DatePicker';
import type { FilterField } from '../types/field';
import type { IndicatorValue, IndicatorsSelection, IndicatorsBase } from '../types/evolutivesStats';
import { useRecoilValue } from 'recoil';
Expand Down Expand Up @@ -181,9 +180,14 @@ const EvolutiveStatsSelector = ({ onChange, selection, title = '', saveInURLPara
<div className="tw-flex tw-w-full">
<button
type="button"
className="tw-h-full tw-rounded tw-text-main disabled:tw-opacity-20"
className={[
'tw-h-full tw-rounded tw-text-main',
'tw-hidden', // FIXME
selection.find((f) => f.fieldName === null || f.toValue === null || f.fromValue === null) ? 'tw-opacity-0' : '',
].join(' ')}
onClick={onAddIndicator}
disabled={!!selection.find((f) => !f.fieldName)}>
// disabled={!!selection.find((f) => !f.fieldName)}
disabled>
+ Ajouter un indicateur
</button>
</div>
Expand All @@ -192,25 +196,6 @@ const EvolutiveStatsSelector = ({ onChange, selection, title = '', saveInURLPara
);
};

const dateOptions = [
{
label: 'Avant',
value: 'before',
},
{
label: 'Après',
value: 'after',
},
{
label: 'Date exacte',
value: 'equals',
},
{
label: 'Non renseigné',
value: 'unfilled',
},
];

const numberOptions = [
{
label: 'Inférieur à',
Expand Down Expand Up @@ -249,49 +234,6 @@ const ValueSelector = ({ fieldName, indicatorValues, value, onChangeValue, base
if (!current) return <></>;
const { type, name } = current;

if (['text', 'textarea'].includes(type)) {
return (
<input
name={name}
className="tailwindui !tw-mt-0"
type="text"
value={value || ''}
onChange={(e) => {
e.preventDefault();
onChangeValue(e.target.value);
}}
/>
);
}

if (['date-with-time', 'date'].includes(type)) {
return (
<div className="tw-flex tw-gap-x-2">
<div className="tw-grow">
<SelectCustom
options={dateOptions}
value={dateOptions.find((opt) => opt.value === value?.comparator)}
isClearable={!value}
onChange={(option) => {
if (!option) return setComparator(null);
setComparator(option.value);
onChangeValue({ date: value?.date, comparator: option.value });
}}
/>
</div>
{value?.comparator !== 'unfilled' && (
<div className="tw-grow">
<DatePicker
id={name}
defaultValue={value?.date ? new Date(value?.date) : null}
onChange={(date) => onChangeValue({ date: date.target.value, comparator })}
/>
</div>
)}
</div>
);
}

if (['number'].includes(type)) {
return (
<div className="-tw-mx-4 tw-flex tw-flex-wrap tw-items-center">
Expand Down Expand Up @@ -357,7 +299,7 @@ const ValueSelector = ({ fieldName, indicatorValues, value, onChangeValue, base
getOptionLabel={(f) => f.label}
getOptionValue={(f) => f.value}
onChange={(newValue) => onChangeValue(newValue?.value)}
isClearable={!value?.length}
isClearable
/>
);
} catch (e) {
Expand All @@ -373,7 +315,7 @@ const ValueSelector = ({ fieldName, indicatorValues, value, onChangeValue, base
getOptionLabel={(f) => f.label}
getOptionValue={(f) => f.value}
onChange={(option) => onChangeValue(option?.value)}
isClearable={!value}
isClearable
/>
);
};
Expand Down
204 changes: 121 additions & 83 deletions dashboard/src/components/EvolutiveStatsViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,16 @@ interface EvolutiveStatsViewerProps {
export default function EvolutiveStatsViewer({ evolutiveStatsIndicators, period, persons }: EvolutiveStatsViewerProps) {
const startDate = period.startDate;
const endDate = period.endDate;
const indicatorsBase = useRecoilValue(evolutiveStatsIndicatorsBaseSelector);
const evolutiveStatsPerson = useRecoilValue(
evolutiveStatsPersonSelector({
persons,
startDate: period.startDate ? dayjsInstance(period.startDate).format('YYYY-MM-DD') : null,
evolutiveStatsIndicators,
})
);
const indicatorsBase = useRecoilValue(evolutiveStatsIndicatorsBaseSelector);
if (!evolutiveStatsIndicators.length) return null;
const indicator = evolutiveStatsIndicators[0];

console.log({
evolutiveStatsPerson,
startDate,
endDate,
evolutiveStatsIndicators,
indicator,
});

if (!indicator.fieldName) return null;

const startDateFormatted = dayjsInstance(startDate ?? startHistoryFeatureDate);
Expand All @@ -46,12 +38,18 @@ export default function EvolutiveStatsViewer({ evolutiveStatsIndicators, period,
const fieldStart = indicator.fromValue;
const fieldEnd = indicator.toValue;

if (fieldStart == null || fieldEnd == null) {
const field = indicatorsBase.find((field) => field.name === indicator.fieldName);

console.log({
fieldStart,
fieldEnd,
evolutiveStatsPerson,
});
if (fieldEnd == null) {
return (
<>
<h4 className="tw-mb-4">
Évolution du champ {indicatorsBase.find((field) => field.name === indicator.fieldName)?.label} entre le{' '}
{startDateFormatted.format('DD/MM/YYYY')} et le {endDateFormatted.format('DD/MM/YYYY')}
Évolution du champ {field?.label} entre le {startDateFormatted.format('DD/MM/YYYY')} et le {endDateFormatted.format('DD/MM/YYYY')}
</h4>

<MyResponsiveStream
Expand All @@ -72,30 +70,35 @@ export default function EvolutiveStatsViewer({ evolutiveStatsIndicators, period,
fieldEnd,
valueStart,
valueEnd,
evolutiveStatsPerson,
});

return (
<div className="tw-flex tw-w-full tw-justify-around">
<div className="tw-flex tw-basis-1/4 tw-flex-col tw-items-center tw-justify-end tw-gap-y-4">
<div className="tw-flex tw-shrink-0 tw-basis-1/4 tw-flex-col tw-items-center tw-justify-end tw-gap-y-4">
<h5>Au {startDateFormatted.format('DD/MM/YYYY')}</h5>
<div className="tw-flex tw-flex-col tw-items-center tw-justify-around tw-rounded-lg tw-border tw-p-4">
<div className="tw-flex tw-w-full tw-flex-col tw-items-center tw-justify-around tw-rounded-lg tw-border tw-p-4">
<p className="tw-text-6xl tw-font-bold tw-text-main">{valueStart}</p>
<p>{fieldStart}</p>
</div>
</div>
<div className="tw-flex tw-basis-1/2 tw-flex-col tw-items-center tw-justify-end tw-gap-y-4">
<div className="tw-flex tw-flex-col tw-items-center tw-justify-around tw-p-4">
<p className="tw-text-6xl tw-font-bold tw-text-main">45%</p>
<p className="tw-text-6xl tw-font-bold tw-text-main">{Math.round((valueEnd / valueStart) * 1000) / 10}%</p>
<p className="tw-m-0 tw-text-center">
des “Sans” Couverture Médicale au 31/01/2022
des{' '}
<strong>
{field?.label}: {fieldStart}
</strong>{' '}
au {startDateFormatted.format('DD/MM/YYYY')}
<br />
ont évolué vers “AME” au 01/02/2023
ont évolué vers <strong>{fieldEnd}</strong> au {endDateFormatted.format('DD/MM/YYYY')}
</p>
</div>
</div>
<div className="tw-flex tw-basis-1/4 tw-flex-col tw-items-center tw-justify-end tw-gap-y-4">
<div className="tw-flex tw-shrink-0 tw-basis-1/4 tw-flex-col tw-items-center tw-justify-end tw-gap-y-4">
<h5>Au {endDateFormatted.format('DD/MM/YYYY')}</h5>
<div className="tw-flex tw-flex-col tw-items-center tw-justify-around tw-rounded-lg tw-border tw-p-4">
<div className="tw-flex tw-w-full tw-flex-col tw-items-center tw-justify-around tw-rounded-lg tw-border tw-p-4">
<p className="tw-text-6xl tw-font-bold tw-text-main">{valueEnd}</p>
<p>{fieldEnd}</p>
</div>
Expand All @@ -111,7 +114,7 @@ function MyResponsiveStream({ indicator, evolutiveStatsPerson, startDateFormatte
const legend = [];
const fieldData = evolutiveStatsPerson[indicator.fieldName];
const daysDiff = endDateFormatted.diff(startDateFormatted, 'days');
const spacing = Math.floor(Math.max(1, daysDiff / 12));
const spacing = Math.floor(Math.max(1, daysDiff / 6));
for (let i = 0; i < daysDiff; i += spacing) {
const date = startDateFormatted.add(i, 'days');
legend.push(date.format('DD/MM/YYYY'));
Expand All @@ -137,69 +140,104 @@ function MyResponsiveStream({ indicator, evolutiveStatsPerson, startDateFormatte
}, [startDateFormatted, endDateFormatted, evolutiveStatsPerson, indicator.fieldName]);

return (
<div
className={[
'tw-mx-auto tw-flex tw-h-[50vh] tw-max-w-3xl tw-basis-full tw-items-center tw-justify-center tw-font-bold print:tw-w-[600px] print:tw-max-w-[55%] print:!tw-grow print:!tw-basis-0',
// onItemClick ? '[&_path]:tw-cursor-pointer' : '',
].join(' ')}>
<ResponsiveStream
data={chartData.data}
keys={chartData.keys}
margin={{ top: 50, right: 110, bottom: 50, left: 60 }}
axisTop={null}
axisRight={null}
axisBottom={{
// ori: 'bottom',
tickSize: 5,
tickPadding: 5,
tickRotation: -15,
legend: '',
legendOffset: 36,
format: (index) => chartData.legend[index],
}}
axisLeft={{
// orient: 'left',
tickSize: 5,
tickPadding: 5,
tickRotation: 0,
legend: '',
legendOffset: -40,
}}
curve="basis"
enableGridX={true}
enableGridY={true}
offsetType="diverging"
colors={{ scheme: 'set2' }}
fillOpacity={0.85}
borderColor={{ theme: 'background' }}
dotSize={8}
dotColor={{ from: 'color' }}
dotBorderWidth={2}
dotBorderColor={{
from: 'color',
modifiers: [['darker', 0.7]],
}}
legends={[
{
anchor: 'bottom-right',
direction: 'column',
translateX: 100,
itemWidth: 80,
itemHeight: 20,
itemTextColor: '#999999',
symbolSize: 12,
symbolShape: 'circle',
effects: [
{
on: 'hover',
style: {
itemTextColor: '#000000',
<div>
<div
className={[
'tw-mx-auto tw-flex tw-h-[50vh] tw-w-[50vw] tw-basis-full tw-items-center tw-justify-center tw-font-bold print:tw-w-[600px] print:tw-max-w-[55%] print:!tw-grow print:!tw-basis-0',
// onItemClick ? '[&_path]:tw-cursor-pointer' : '',
].join(' ')}>
<ResponsiveStream
data={chartData.data}
keys={chartData.keys}
margin={{ top: 50, right: 110, bottom: 50, left: 60 }}
axisTop={null}
axisRight={null}
axisBottom={{
// ori: 'bottom',
tickSize: 5,
tickPadding: 5,
tickRotation: -15,
legend: '',
legendOffset: 36,
format: (index) => chartData.legend[index],
}}
axisLeft={{
// orient: 'left',
tickSize: 5,
tickPadding: 5,
tickRotation: 0,
truncateTickAt: 1,
legend: '',
legendOffset: -40,
format: (e) => (Math.floor(e) === e ? e : ''),
}}
curve="basis"
enableGridX={true}
enableGridY={false}
offsetType="diverging"
colors={{ scheme: 'set2' }}
fillOpacity={0.85}
borderColor={{ theme: 'background' }}
dotSize={8}
dotColor={{ from: 'color' }}
dotBorderWidth={2}
dotBorderColor={{
from: 'color',
modifiers: [['darker', 0.7]],
}}
legends={[
{
anchor: 'bottom-right',
direction: 'column',
translateX: 100,
itemWidth: 80,
itemHeight: 20,
itemTextColor: '#999999',
symbolSize: 12,
symbolShape: 'circle',
effects: [
{
on: 'hover',
style: {
itemTextColor: '#000000',
},
},
},
],
},
]}
/>
],
},
]}
/>
</div>
<div className="tw-flex tw-basis-1/3 tw-items-center tw-justify-center">
<table className="tw-w-full tw-border tw-border-zinc-400">
<thead>
<tr>
<td className="tw-border tw-border-zinc-400 tw-p-1">Option</td>
<td className="tw-border tw-border-zinc-400 tw-p-1 tw-text-center">Au {startDateFormatted.format('DD/MM/YYYY')}</td>
<td className="tw-border tw-border-zinc-400 tw-p-1 tw-text-center">Au {endDateFormatted.format('DD/MM/YYYY')}</td>
<td className="tw-border tw-border-zinc-400 tw-p-1 tw-text-center">Différence</td>
<td className="tw-border tw-border-zinc-400 tw-p-1 tw-text-center">Différence (%)</td>
</tr>
</thead>
<tbody>
{chartData.keys.map((option) => {
const startValue = chartData.data[0][option];
const endValue = chartData.data.at(-1)[option];
const diff = endValue - startValue;
const sign = diff > 0 ? '+' : '';
const percentDiff = startValue === 0 || endValue === 0 ? 0 : Math.round((diff / startValue) * 1000) / 10;
return (
<tr key={option}>
<td className="tw-border tw-border-zinc-400 tw-p-1">{option}</td>
<td className="tw-border tw-border-zinc-400 tw-p-1 tw-text-center">{startValue}</td>
<td className="tw-border tw-border-zinc-400 tw-p-1 tw-text-center">{endValue}</td>
<td className="tw-border tw-border-zinc-400 tw-p-1 tw-text-center">{diff === 0 ? '' : `${sign}${diff}`}</td>
<td className="tw-border tw-border-zinc-400 tw-p-1 tw-text-center">{percentDiff === 0 ? '' : `${sign}${percentDiff}%`}</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
);
}
Loading

0 comments on commit 8b63678

Please sign in to comment.