Skip to content

Commit 13687b7

Browse files
authored
Merge pull request #1361 from IFRCGo/feature/per-consideration
Add color in the per dashboard and per export
2 parents 93f30c6 + 81dc3bd commit 13687b7

File tree

24 files changed

+377
-140
lines changed

24 files changed

+377
-140
lines changed

.changeset/spicy-dragons-teach.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"@ifrc-go/ui": patch
3+
---
4+
5+
- PieChart Component
6+
7+
- Added a `colorSelector` prop to select color for each pie
8+
9+
- ProgressBar Component
10+
- Introduced a `color` prop to customize the progress bar's color

.changeset/tricky-hornets-trade.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"go-web-app": patch
3+
---
4+
5+
Added color mapping based on PER Area and Rating across all PER charts
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"namespace": "common",
33
"strings": {
4-
"multiImageArea": "Area {areaId}",
4+
"perArea": "Area {areaId}",
55
"benchmarksAssessed": "{totalQuestionCount} benchmarks assessed",
66
"perAssessmentSummaryHeading": "Summary",
7-
"benchmarksAssessedTitle": "benchmarks assessed : {allAnsweredResponses} / {totalQuestionCount}"
7+
"benchmarksAssessedTitle": "Benchmarks assessed : {allAnsweredResponses} / {totalQuestionCount}"
88
}
99
}

app/src/components/domain/PerAssessmentSummary/index.tsx

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ import {
2222
} from '@togglecorp/fujs';
2323
import { PartialForm } from '@togglecorp/toggle-form';
2424

25+
import {
26+
getPerAreaColor,
27+
getPerRatingColor,
28+
} from '#utils/domain/per';
2529
import { type GoApiResponse } from '#utils/restRequest';
2630

2731
import i18n from './i18n.json';
@@ -43,14 +47,25 @@ interface Props {
4347
areaIdToTitleMap: Record<number, string | undefined>;
4448
}
4549

46-
const colors = [
47-
'var(--go-ui-color-red-90)',
48-
'var(--go-ui-color-red-70)',
49-
'var(--go-ui-color-red-50)',
50-
'var(--go-ui-color-red-40)',
51-
'var(--go-ui-color-red-20)',
52-
'var(--go-ui-color-red-10)',
53-
];
50+
const progressBarColor = 'var(--go-ui-color-dark-blue-40)';
51+
52+
function numberOfComponentsSelector({ components } : { components: unknown[]}) {
53+
return components.length;
54+
}
55+
56+
function perRatingColorSelector({ ratingValue }: { ratingValue: number | undefined }) {
57+
return getPerRatingColor(ratingValue);
58+
}
59+
60+
function perRatingLabelSelector({
61+
ratingValue,
62+
ratingDisplay,
63+
}: {
64+
ratingValue?: number;
65+
ratingDisplay?: string;
66+
}): string {
67+
return `${ratingValue}-${ratingDisplay}`;
68+
}
5469

5570
function PerAssessmentSummary(props: Props) {
5671
const {
@@ -142,9 +157,10 @@ function PerAssessmentSummary(props: Props) {
142157
(areaResponse) => {
143158
// NOTE: do we take the average of only rated components or of all the
144159
// components?
160+
// Also, 'component.rating' refers to the component ID and is misnamed.
145161
const filteredComponents = areaResponse?.component_responses?.filter(
146162
(component) => isDefined(component?.rating)
147-
&& component.rating !== 0,
163+
&& component.rating !== 1,
148164
) ?? [];
149165

150166
if (filteredComponents.length === 0) {
@@ -173,7 +189,7 @@ function PerAssessmentSummary(props: Props) {
173189
areaId,
174190
rating: averageRatingByAreaMap[Number(areaId)] ?? 0,
175191
areaDisplay: resolveToString(
176-
strings.multiImageArea,
192+
strings.perArea,
177193
{ areaId },
178194
),
179195
}),
@@ -208,6 +224,7 @@ function PerAssessmentSummary(props: Props) {
208224
},
209225
)}
210226
value={allAnsweredResponses?.length ?? 0}
227+
color={progressBarColor}
211228
totalValue={totalQuestionCount}
212229
description={(
213230
<div className={styles.answerCounts}>
@@ -228,18 +245,9 @@ function PerAssessmentSummary(props: Props) {
228245
<StackedProgressBar
229246
className={styles.componentRating}
230247
data={statusGroupedComponentList ?? []}
231-
// FIXME: don't use inline selectors
232-
valueSelector={
233-
(statusGroupedComponent) => (
234-
statusGroupedComponent.components.length
235-
)
236-
}
237-
// FIXME: don't use inline selectors
238-
colorSelector={(_, i) => colors[i]}
239-
// FIXME: don't use inline selectors
240-
labelSelector={
241-
(statusGroupedComponent) => `${statusGroupedComponent.ratingValue}-${statusGroupedComponent.ratingDisplay}`
242-
}
248+
valueSelector={numberOfComponentsSelector}
249+
colorSelector={perRatingColorSelector}
250+
labelSelector={perRatingLabelSelector}
243251
/>
244252
<div className={styles.avgComponentRating}>
245253
{averageRatingByAreaList.map(
@@ -257,6 +265,7 @@ function PerAssessmentSummary(props: Props) {
257265
className={styles.filledBar}
258266
style={{
259267
height: `${getPercentage(rating.rating, averageRatingByAreaList.length)}%`,
268+
backgroundColor: getPerAreaColor(Number(rating.areaId)),
260269
}}
261270
/>
262271
</div>

app/src/components/domain/PerAssessmentSummary/styles.module.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
.total-progress {
88
.answer-counts {
99
display: flex;
10+
color: var(--go-ui-color-dark-blue-40);
1011
gap: var(--go-ui-spacing-lg);
1112
}
1213
}
@@ -37,7 +38,7 @@
3738
height: 4rem;
3839

3940
.filled-bar {
40-
background-color: var(--go-ui-color-primary-red);
41+
background-color: var(--go-ui-color-dark-blue-40);
4142
width: 100%;
4243
}
4344
}

app/src/hooks/useNumericChartData.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ interface Options<DATUM> {
3838
keySelector: (d: DATUM, index: number) => number | string;
3939
xValueSelector: (d: DATUM, index: number) => number | undefined | null;
4040
yValueSelector: (d: DATUM, index: number) => number | undefined | null;
41+
colorSelector?: (d: DATUM, index: number) => string;
4142
xAxisHeight?: number;
4243
yAxisWidth?: number;
4344
chartMargin?: Rect;
@@ -57,6 +58,7 @@ function useNumericChartData<DATUM>(data: DATUM[] | null | undefined, options: O
5758
keySelector,
5859
xValueSelector,
5960
yValueSelector,
61+
colorSelector,
6062
chartMargin = defaultChartMargin,
6163
chartPadding = defaultChartPadding,
6264
numXAxisTicks: numXAxisTicksFromProps = 'auto',
@@ -87,6 +89,7 @@ function useNumericChartData<DATUM>(data: DATUM[] | null | undefined, options: O
8789
() => data?.map(
8890
(datum, i) => {
8991
const key = keySelector(datum, i);
92+
const color = isDefined(colorSelector) ? colorSelector(datum, i) : undefined;
9093
const xValue = xValueSelector(datum, i);
9194
const yValue = yValueSelector(datum, i);
9295

@@ -96,6 +99,7 @@ function useNumericChartData<DATUM>(data: DATUM[] | null | undefined, options: O
9699

97100
return {
98101
key,
102+
color,
99103
originalData: datum,
100104
xValue,
101105
yValue,
@@ -104,7 +108,7 @@ function useNumericChartData<DATUM>(data: DATUM[] | null | undefined, options: O
104108
).filter(isDefined).sort(
105109
(a, b) => compareNumber(a.xValue, b.xValue),
106110
) ?? [],
107-
[data, keySelector, xValueSelector, yValueSelector],
111+
[data, keySelector, xValueSelector, yValueSelector, colorSelector],
108112
);
109113

110114
const dataDomain = useMemo(

app/src/utils/domain/per.ts

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,84 @@ export function getCurrentPerProcessStep(status: PerProcessStatusResponse | unde
3939

4040
export function getFormattedComponentName(component: PerComponent): string {
4141
const { component_num, component_letter, title } = component;
42-
4342
const prefix = [component_num, component_letter].filter(isDefined).join(' ').trim();
43+
return `${prefix}: ${title}`;
44+
}
45+
46+
export const PER_FALLBACK_COLOR = 'var(--go-ui-color-gray-40)';
47+
48+
export type PerRatingValue = 0 | 1 | 2 | 3 | 4 | 5;
49+
const PER_RATING_VALUE_NOT_REVIEWED = 0 satisfies PerRatingValue;
50+
const PER_RATING_VALUE_DOES_NOT_EXIST = 1 satisfies PerRatingValue;
51+
const PER_RATING_VALUE_PARTIALLY_EXISTS = 2 satisfies PerRatingValue;
52+
const PER_RATING_VALUE_NEEDS_IMPROVEMENT = 3 satisfies PerRatingValue;
53+
const PER_RATING_VALUE_EXISTS_COULD_BE_STRENGTHENED = 4 satisfies PerRatingValue;
54+
const PER_RATING_VALUE_HIGH_PERFORMANCE = 5 satisfies PerRatingValue;
55+
56+
const perRatingColorMap: Record<PerRatingValue, string> = {
57+
[PER_RATING_VALUE_HIGH_PERFORMANCE]: 'var(--go-ui-color-dark-blue-40)',
58+
[PER_RATING_VALUE_EXISTS_COULD_BE_STRENGTHENED]: 'var(--go-ui-color-dark-blue-30)',
59+
[PER_RATING_VALUE_NEEDS_IMPROVEMENT]: 'var(--go-ui-color-dark-blue-20)',
60+
[PER_RATING_VALUE_PARTIALLY_EXISTS]: 'var(--go-ui-color-dark-blue-10)',
61+
[PER_RATING_VALUE_DOES_NOT_EXIST]: 'var(--go-ui-color-gray-40)',
62+
[PER_RATING_VALUE_NOT_REVIEWED]: 'var(--go-ui-color-gray-30)',
63+
};
64+
65+
export function getPerRatingColor(value: number | undefined) {
66+
if (isDefined(value)) {
67+
return perRatingColorMap[value as PerRatingValue] ?? PER_FALLBACK_COLOR;
68+
}
69+
return PER_FALLBACK_COLOR;
70+
}
71+
export function perRatingColorSelector(item: { value: number | undefined }) {
72+
return getPerRatingColor(item.value);
73+
}
4474

45-
return `${prefix} : ${title}`;
75+
export type PerAreaNumber = 1 | 2 | 3 | 4 | 5;
76+
const PER_AREA_NUMBER_1 = 1 satisfies PerAreaNumber;
77+
const PER_AREA_NUMBER_2 = 2 satisfies PerAreaNumber;
78+
const PER_AREA_NUMBER_3 = 3 satisfies PerAreaNumber;
79+
const PER_AREA_NUMBER_4 = 4 satisfies PerAreaNumber;
80+
const PER_AREA_NUMBER_5 = 5 satisfies PerAreaNumber;
81+
82+
const perAreaColorMap: Record<PerAreaNumber, string> = {
83+
[PER_AREA_NUMBER_1]: 'var(--go-ui-color-purple-per)',
84+
[PER_AREA_NUMBER_2]: 'var(--go-ui-color-orange-per)',
85+
[PER_AREA_NUMBER_3]: 'var(--go-ui-color-blue-per)',
86+
[PER_AREA_NUMBER_4]: 'var(--go-ui-color-teal-per)',
87+
[PER_AREA_NUMBER_5]: 'var(--go-ui-color-red-per)',
88+
};
89+
90+
export function getPerAreaColor(value: number | undefined) {
91+
if (isDefined(value)) {
92+
return perAreaColorMap[value as PerAreaNumber] ?? PER_FALLBACK_COLOR;
93+
}
94+
return PER_FALLBACK_COLOR;
95+
}
96+
export function perAreaColorSelector(item: {
97+
value: number | undefined;
98+
}) {
99+
return getPerAreaColor(item.value);
100+
}
101+
102+
type PerBenchmarkId = 1 | 2 | 5;
103+
const PER_BENCHMARK_YES = 1 satisfies PerBenchmarkId;
104+
const PER_BENCHMARK_NO = 2 satisfies PerBenchmarkId;
105+
const PER_BENCHMARK_PARTIALLY_EXISTS = 5 satisfies PerBenchmarkId;
106+
107+
const perBenchmarkColorMap: Record<PerBenchmarkId, string> = {
108+
[PER_BENCHMARK_YES]: 'var(--go-ui-color-dark-blue-40)',
109+
[PER_BENCHMARK_NO]: 'var(--go-ui-color-dark-blue-30)',
110+
[PER_BENCHMARK_PARTIALLY_EXISTS]: 'var(--go-ui-color-dark-blue-10)',
111+
};
112+
113+
export function getPerBenchmarkColor(id: number | undefined) {
114+
return perBenchmarkColorMap[id as PerBenchmarkId] ?? PER_FALLBACK_COLOR;
115+
}
116+
export function perBenchmarkColorSelector(item: {
117+
id: number;
118+
label: string;
119+
count: number;
120+
}) {
121+
return getPerBenchmarkColor(item.id);
46122
}

app/src/views/CountryPreparedness/PreviousAssessmentChart/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@ function PreviousAssessmentCharts(props: Props) {
4949
const ratingTitleMap = listToMap(
5050
ratingOptions,
5151
(option) => option.value,
52-
(option) => option.title,
52+
(option) => `${option.title} ${option.value}`,
5353
);
5454

5555
const chartData = useNumericChartData(
5656
[...data].sort((a, b) => compareNumber(a.assessment_number, b.assessment_number)),
5757
{
5858
chartMargin: {
5959
...defaultChartMargin,
60-
top: 10,
60+
top: 30,
6161
},
6262
keySelector: (datum) => datum.assessment_number,
6363
xValueSelector: (datum) => datum.assessment_number,

app/src/views/CountryPreparedness/PreviousAssessmentChart/styles.module.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
height: 20rem;
33

44
.path {
5-
stroke: var(--go-ui-color-primary-red);
5+
stroke: var(--go-ui-color-dark-blue-30);
66
fill: none;
77
pointer-events: none;
88
}
@@ -13,7 +13,7 @@
1313
}
1414

1515
.circle {
16-
color: var(--go-ui-color-primary-red);
16+
color: var(--go-ui-color-dark-blue-30);
1717
pointer-events: none;
1818
}
1919
}

app/src/views/CountryPreparedness/PrivateCountryPreparedness/i18n.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
"lastUpdatedLabel": "Last update",
2929
"perExport": "Export",
3030
"upload": "Upload",
31+
"typeOfOperation": "Type of Operation",
3132
"relevantDocumentHeader": "Relevant Documents",
32-
"uploadLimitDisclaimer": "Note: You can only upload upto 10 documents."
33+
"uploadLimitDisclaimer": "Note: You can only upload upto 10 documents.",
34+
"perArea": "Area {areaNumber}: {title}"
3335
}
3436
}

0 commit comments

Comments
 (0)