datastake-daf 0.6.774 → 0.6.776
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/index.js +2583 -2504
- package/dist/hooks/index.js +0 -72
- package/dist/pages/index.js +1769 -835
- package/dist/style/datastake/mapbox-gl.css +330 -0
- package/dist/utils/index.js +0 -13
- package/package.json +1 -1
- package/src/@daf/core/components/Dashboard/Map/ChainIcon/Markers/StakeholderMarker.js +76 -8
- package/src/@daf/core/components/Dashboard/Map/ChainIcon/index.js +8 -116
- package/src/@daf/core/components/Dashboard/Map/ChainIcon/utils.js +17 -73
- package/src/@daf/core/components/Dashboard/Map/helper.js +0 -1
- package/src/@daf/core/components/Dashboard/Map/hook.js +29 -53
- package/src/@daf/core/components/Dashboard/Map/style.js +5 -20
- package/src/@daf/hooks/useTimeFilter.js +56 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/AssociatedInformation/config.js +548 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/AssociatedInformation/index.jsx +137 -24
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CommunityParticipation/JobsTimeline/index.jsx +33 -102
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/HealthAndSafety/helper.js +8 -6
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/HealthAndSafety/index.jsx +73 -4
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleIndicators/index.jsx +1 -1
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleOutcomes/PlantingActivitiesTimeline.jsx +148 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleOutcomes/RestoredArea.jsx +150 -0
- package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleOutcomes/index.jsx +11 -390
- package/src/@daf/pages/Summary/Activities/PlantingCycle/index.jsx +3 -4
- package/src/@daf/utils/object.js +1 -3
- package/src/@daf/utils/timeFilterUtils.js +226 -0
- package/src/hooks.js +1 -2
- package/src/utils.js +1 -1
package/src/@daf/pages/Summary/Activities/PlantingCycle/components/CycleOutcomes/RestoredArea.jsx
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { Widget, LineChart } from '../../../../../../../../src/index.js';
|
|
3
|
+
import { Select } from 'antd';
|
|
4
|
+
import { useTimeFilter } from '../../../../../../hooks/useTimeFilter.js';
|
|
5
|
+
|
|
6
|
+
const selectOptions = [
|
|
7
|
+
{ label: "Daily", value: "daily" },
|
|
8
|
+
{ label: "Weekly", value: "weekly" },
|
|
9
|
+
{ label: "Monthly", value: "monthly" },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const RestoredArea = ({
|
|
13
|
+
restoredAreaChart,
|
|
14
|
+
t = (s) => s
|
|
15
|
+
}) => {
|
|
16
|
+
const { timeFilter, setTimeFilter, formatDateAxis, processChartDateData } = useTimeFilter({ defaultFilter: 'monthly' });
|
|
17
|
+
|
|
18
|
+
// Map restoredAreaChart data to LineChart format with time filter support
|
|
19
|
+
// Y-axis: total/cumulated value
|
|
20
|
+
// X-axis: date (formatted based on timeFilter)
|
|
21
|
+
// Fill all periods in the range, even if empty
|
|
22
|
+
const restoredAreaChartData = useMemo(() => {
|
|
23
|
+
if (!restoredAreaChart || !Array.isArray(restoredAreaChart) || restoredAreaChart.length === 0) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Process data with cumulative calculation (for restored area)
|
|
28
|
+
return processChartDateData({
|
|
29
|
+
mainData: restoredAreaChart,
|
|
30
|
+
isCumulative: true,
|
|
31
|
+
valueField: 'total',
|
|
32
|
+
});
|
|
33
|
+
}, [restoredAreaChart, processChartDateData]);
|
|
34
|
+
|
|
35
|
+
// Calculate max value for yAxis dynamically (round up to nearest 10)
|
|
36
|
+
const maxYValue = useMemo(() => {
|
|
37
|
+
if (!restoredAreaChartData || restoredAreaChartData.length === 0) {
|
|
38
|
+
return 80;
|
|
39
|
+
}
|
|
40
|
+
const maxValue = Math.max(...restoredAreaChartData.map(item => item.value || 0));
|
|
41
|
+
// If max is 0, set default to 80 to show Y-axis
|
|
42
|
+
// Otherwise, round up to nearest 10
|
|
43
|
+
if (maxValue === 0) {
|
|
44
|
+
return 80;
|
|
45
|
+
}
|
|
46
|
+
return Math.ceil(maxValue / 10) * 10 || 80;
|
|
47
|
+
}, [restoredAreaChartData]);
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Widget
|
|
52
|
+
title={t("Restored Area")}
|
|
53
|
+
className="with-border-header h-w-btn-header"
|
|
54
|
+
addedHeader={
|
|
55
|
+
<>
|
|
56
|
+
<div className="flex-1" />
|
|
57
|
+
<Select
|
|
58
|
+
value={timeFilter}
|
|
59
|
+
style={{ width: 100 }}
|
|
60
|
+
onChange={(value) => setTimeFilter(value)}
|
|
61
|
+
options={selectOptions}
|
|
62
|
+
popupMatchSelectWidth={120}
|
|
63
|
+
/>
|
|
64
|
+
</>
|
|
65
|
+
}
|
|
66
|
+
>
|
|
67
|
+
<div className="flex flex-1 flex-column justify-content-center">
|
|
68
|
+
<div className="flex justify-content-center w-full">
|
|
69
|
+
<LineChart
|
|
70
|
+
animated
|
|
71
|
+
isArea
|
|
72
|
+
color={'#00AEB1'}
|
|
73
|
+
data={restoredAreaChartData}
|
|
74
|
+
fillOpacity={0.7}
|
|
75
|
+
height={200}
|
|
76
|
+
renderTooltipContent={(title, data) => {
|
|
77
|
+
if (!data || data.length === 0) return {};
|
|
78
|
+
const item = data[0]?.data || data[0];
|
|
79
|
+
const periodValue = item?.period !== undefined ? item.period : (item?.value || 0);
|
|
80
|
+
const cumulatedValue = item?.value || 0;
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
title: t("Restored Area"),
|
|
84
|
+
subTitle: formatDateAxis(title),
|
|
85
|
+
items: [
|
|
86
|
+
{
|
|
87
|
+
label: t("Period"),
|
|
88
|
+
value: `${periodValue.toLocaleString()} ha`,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
label: t("Cumulated"),
|
|
92
|
+
value: `${cumulatedValue.toLocaleString()} ha`,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
};
|
|
96
|
+
}}
|
|
97
|
+
xFieldKey="date"
|
|
98
|
+
yFieldKey="value"
|
|
99
|
+
formattedXAxis={formatDateAxis}
|
|
100
|
+
style={{ width: '100%' }}
|
|
101
|
+
yAxis={{
|
|
102
|
+
min: 0,
|
|
103
|
+
max: maxYValue,
|
|
104
|
+
tickMethod: () => {
|
|
105
|
+
// Generate ticks every 10 units for maxYValue of 80
|
|
106
|
+
// For other values, show ticks every 10 units
|
|
107
|
+
const step = maxYValue <= 80 ? 10 : Math.max(10, Math.floor(maxYValue / 8));
|
|
108
|
+
const ticks = [];
|
|
109
|
+
for (let i = 0; i <= maxYValue; i += step) {
|
|
110
|
+
ticks.push(i);
|
|
111
|
+
}
|
|
112
|
+
// Ensure max value is included
|
|
113
|
+
if (ticks.length === 0 || ticks[ticks.length - 1] < maxYValue) {
|
|
114
|
+
ticks.push(maxYValue);
|
|
115
|
+
}
|
|
116
|
+
return ticks;
|
|
117
|
+
},
|
|
118
|
+
label: {
|
|
119
|
+
style: {
|
|
120
|
+
fontSize: 12,
|
|
121
|
+
fill: '#666',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
grid: {
|
|
125
|
+
line: {
|
|
126
|
+
style: {
|
|
127
|
+
stroke: '#E5E7EB',
|
|
128
|
+
lineWidth: 1,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
}}
|
|
133
|
+
xAxis={{
|
|
134
|
+
label: {
|
|
135
|
+
formatter: formatDateAxis,
|
|
136
|
+
style: {
|
|
137
|
+
fontSize: 12,
|
|
138
|
+
fill: '#666',
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</Widget>
|
|
146
|
+
);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export default RestoredArea;
|
|
150
|
+
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
|
-
import
|
|
3
|
-
import { Widget, StatCard, LineChart, ColumnChart } from '../../../../../../../../src/index.js';
|
|
2
|
+
import { Widget, StatCard } from '../../../../../../../../src/index.js';
|
|
4
3
|
import { useWidgetFetch } from '../../../../../../hooks/useWidgetFetch.js';
|
|
5
4
|
import { calculateStatChange } from '../../helper.js';
|
|
5
|
+
import RestoredArea from './RestoredArea.jsx';
|
|
6
|
+
import PlantingActivitiesTimeline from './PlantingActivitiesTimeline.jsx';
|
|
6
7
|
|
|
7
8
|
const CycleOutcomes = ({
|
|
8
9
|
id,
|
|
@@ -11,7 +12,6 @@ const CycleOutcomes = ({
|
|
|
11
12
|
t = (s) => s
|
|
12
13
|
}) => {
|
|
13
14
|
|
|
14
|
-
|
|
15
15
|
const defaultConfig = useMemo(
|
|
16
16
|
() => ({
|
|
17
17
|
basepath: "planting-cycle",
|
|
@@ -71,228 +71,6 @@ const CycleOutcomes = ({
|
|
|
71
71
|
);
|
|
72
72
|
}, [locationsCount, t]);
|
|
73
73
|
|
|
74
|
-
// Map restoredAreaChart data to LineChart format
|
|
75
|
-
// Y-axis: total/cumulated value
|
|
76
|
-
// X-axis: date
|
|
77
|
-
// Fill all months in the range, even if empty
|
|
78
|
-
const restoredAreaChartData = useMemo(() => {
|
|
79
|
-
// Always show last 12 months, even if no data
|
|
80
|
-
const now = dayjs().startOf('month');
|
|
81
|
-
const twelveMonthsAgo = now.subtract(11, 'month'); // 11 months ago + current month = 12 months
|
|
82
|
-
|
|
83
|
-
// Create a map of existing data by month (YYYY-MM format)
|
|
84
|
-
const dataMap = new Map();
|
|
85
|
-
const dates = [];
|
|
86
|
-
|
|
87
|
-
// Process restored area data if available
|
|
88
|
-
if (restoredAreaChart && Array.isArray(restoredAreaChart) && restoredAreaChart.length > 0) {
|
|
89
|
-
restoredAreaChart.forEach((item) => {
|
|
90
|
-
if (typeof item === 'object' && item !== null) {
|
|
91
|
-
// Priority: look for date field first
|
|
92
|
-
const dateValue = item.date || item.label || item.name || item.period || item.month;
|
|
93
|
-
if (dateValue) {
|
|
94
|
-
const date = dayjs(dateValue);
|
|
95
|
-
if (date.isValid()) {
|
|
96
|
-
const monthKey = date.format('YYYY-MM');
|
|
97
|
-
// Total/cumulated value for Y-axis (this is what gets plotted)
|
|
98
|
-
const totalValue = Number(item.cumulated || item.cumulative || item.total || item.value || 0) || 0;
|
|
99
|
-
// Period value for tooltip only
|
|
100
|
-
const periodValue = Number(item.period || item.area || item.count || 0) || 0;
|
|
101
|
-
dates.push(date);
|
|
102
|
-
|
|
103
|
-
// If multiple entries for same month, use the latest one (or sum if needed)
|
|
104
|
-
if (!dataMap.has(monthKey) || dataMap.get(monthKey).value < totalValue) {
|
|
105
|
-
dataMap.set(monthKey, {
|
|
106
|
-
date: dateValue,
|
|
107
|
-
value: totalValue,
|
|
108
|
-
period: periodValue,
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Determine date range
|
|
118
|
-
let minDate = twelveMonthsAgo;
|
|
119
|
-
let maxDate = now;
|
|
120
|
-
|
|
121
|
-
// If we have data, adjust range to include it
|
|
122
|
-
if (dates.length > 0) {
|
|
123
|
-
const sortedDates = dates.sort((a, b) => a.valueOf() - b.valueOf());
|
|
124
|
-
const firstDataDate = sortedDates[0].startOf('month');
|
|
125
|
-
const lastDataDate = sortedDates[sortedDates.length - 1].startOf('month');
|
|
126
|
-
|
|
127
|
-
// Start from the earlier of: 12 months ago, or first data date
|
|
128
|
-
minDate = twelveMonthsAgo.isBefore(firstDataDate) ? twelveMonthsAgo : firstDataDate;
|
|
129
|
-
|
|
130
|
-
// End at the later of: current month, or last data date
|
|
131
|
-
maxDate = now.isAfter(lastDataDate) ? now : lastDataDate;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Generate all months in the range
|
|
135
|
-
const result = [];
|
|
136
|
-
let currentDate = minDate.clone();
|
|
137
|
-
let lastKnownValue = 0; // For cumulative data, carry forward the last known value
|
|
138
|
-
|
|
139
|
-
while (currentDate.isBefore(maxDate) || currentDate.isSame(maxDate, 'month')) {
|
|
140
|
-
const monthKey = currentDate.format('YYYY-MM');
|
|
141
|
-
const existingData = dataMap.get(monthKey);
|
|
142
|
-
|
|
143
|
-
if (existingData) {
|
|
144
|
-
lastKnownValue = existingData.value;
|
|
145
|
-
result.push(existingData);
|
|
146
|
-
} else {
|
|
147
|
-
// Fill missing month - for cumulative data, use last known value
|
|
148
|
-
result.push({
|
|
149
|
-
date: currentDate.format('YYYY-MM-DD'),
|
|
150
|
-
value: lastKnownValue, // Carry forward cumulative value
|
|
151
|
-
period: 0,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
currentDate = currentDate.add(1, 'month');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return result;
|
|
159
|
-
}, [restoredAreaChart]);
|
|
160
|
-
|
|
161
|
-
// Calculate max value for yAxis dynamically (round up to nearest 10)
|
|
162
|
-
const maxYValue = useMemo(() => {
|
|
163
|
-
if (!restoredAreaChartData || restoredAreaChartData.length === 0) {
|
|
164
|
-
return 80;
|
|
165
|
-
}
|
|
166
|
-
const maxValue = Math.max(...restoredAreaChartData.map(item => item.value || 0));
|
|
167
|
-
// If max is 0, set default to 80 to show Y-axis
|
|
168
|
-
// Otherwise, round up to nearest 10
|
|
169
|
-
if (maxValue === 0) {
|
|
170
|
-
return 80;
|
|
171
|
-
}
|
|
172
|
-
return Math.ceil(maxValue / 10) * 10 || 80;
|
|
173
|
-
}, [restoredAreaChartData]);
|
|
174
|
-
|
|
175
|
-
// Format date to "Mmm YY" format for X-axis
|
|
176
|
-
const formatDateAxis = useMemo(() => {
|
|
177
|
-
return (label) => {
|
|
178
|
-
if (!label) return label;
|
|
179
|
-
|
|
180
|
-
// Try to parse the date using dayjs with various formats
|
|
181
|
-
let date = dayjs(label);
|
|
182
|
-
|
|
183
|
-
// If first attempt fails, try parsing as ISO date string
|
|
184
|
-
if (!date.isValid() && typeof label === 'string') {
|
|
185
|
-
date = dayjs(label, ['YYYY-MM-DD', 'YYYY-MM', 'MMM YY', 'MMM YYYY'], true);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// If it's a valid date, format it as "Mmm YY"
|
|
189
|
-
if (date.isValid()) {
|
|
190
|
-
return date.format('MMM YY');
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// If it's already in "Mmm YY" format or similar, return as is
|
|
194
|
-
// Otherwise return the original label
|
|
195
|
-
return label;
|
|
196
|
-
};
|
|
197
|
-
}, []);
|
|
198
|
-
|
|
199
|
-
// Map activitiesTimelineChart data to ColumnChart format
|
|
200
|
-
// Data structure: [{count: 2, date: "2025-11-03"}]
|
|
201
|
-
// Fill all months in the range, even if empty
|
|
202
|
-
const activitiesTimelineData = useMemo(() => {
|
|
203
|
-
// Always show last 12 months, even if no data
|
|
204
|
-
const now = dayjs().startOf('month');
|
|
205
|
-
const twelveMonthsAgo = now.subtract(11, 'month'); // 11 months ago + current month = 12 months
|
|
206
|
-
|
|
207
|
-
// Create a map of existing data by month (YYYY-MM format)
|
|
208
|
-
const dataMap = new Map();
|
|
209
|
-
const dates = [];
|
|
210
|
-
|
|
211
|
-
// Process activities timeline data if available
|
|
212
|
-
if (activitiesTimelineChart && Array.isArray(activitiesTimelineChart) && activitiesTimelineChart.length > 0) {
|
|
213
|
-
activitiesTimelineChart.forEach((item) => {
|
|
214
|
-
if (typeof item === 'object' && item !== null && item.date) {
|
|
215
|
-
const date = dayjs(item.date);
|
|
216
|
-
if (date.isValid()) {
|
|
217
|
-
const monthKey = date.format('YYYY-MM');
|
|
218
|
-
const count = Number(item.count || item.jobs || item.value || 0) || 0;
|
|
219
|
-
dates.push(date);
|
|
220
|
-
|
|
221
|
-
// If multiple entries for same month, sum them
|
|
222
|
-
if (dataMap.has(monthKey)) {
|
|
223
|
-
dataMap.set(monthKey, {
|
|
224
|
-
...dataMap.get(monthKey),
|
|
225
|
-
jobs: dataMap.get(monthKey).jobs + count,
|
|
226
|
-
});
|
|
227
|
-
} else {
|
|
228
|
-
dataMap.set(monthKey, {
|
|
229
|
-
month: item.date,
|
|
230
|
-
jobs: count,
|
|
231
|
-
date: item.date,
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Determine date range
|
|
240
|
-
let minDate = twelveMonthsAgo;
|
|
241
|
-
let maxDate = now;
|
|
242
|
-
|
|
243
|
-
// If we have data, adjust range to include it
|
|
244
|
-
if (dates.length > 0) {
|
|
245
|
-
const sortedDates = dates.sort((a, b) => a.valueOf() - b.valueOf());
|
|
246
|
-
const firstDataDate = sortedDates[0].startOf('month');
|
|
247
|
-
const lastDataDate = sortedDates[sortedDates.length - 1].startOf('month');
|
|
248
|
-
|
|
249
|
-
// Start from the earlier of: 12 months ago, or first data date
|
|
250
|
-
minDate = twelveMonthsAgo.isBefore(firstDataDate) ? twelveMonthsAgo : firstDataDate;
|
|
251
|
-
|
|
252
|
-
// End at the later of: current month, or last data date
|
|
253
|
-
maxDate = now.isAfter(lastDataDate) ? now : lastDataDate;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Generate all months in the range
|
|
257
|
-
const result = [];
|
|
258
|
-
let currentDate = minDate.clone();
|
|
259
|
-
|
|
260
|
-
while (currentDate.isBefore(maxDate) || currentDate.isSame(maxDate, 'month')) {
|
|
261
|
-
const monthKey = currentDate.format('YYYY-MM');
|
|
262
|
-
const existingData = dataMap.get(monthKey);
|
|
263
|
-
|
|
264
|
-
if (existingData) {
|
|
265
|
-
result.push(existingData);
|
|
266
|
-
} else {
|
|
267
|
-
// Fill missing month with 0
|
|
268
|
-
result.push({
|
|
269
|
-
month: currentDate.format('YYYY-MM-DD'), // Use first day of month
|
|
270
|
-
jobs: 0,
|
|
271
|
-
date: currentDate.format('YYYY-MM-DD'),
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
currentDate = currentDate.add(1, 'month');
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return result;
|
|
279
|
-
}, [activitiesTimelineChart]);
|
|
280
|
-
|
|
281
|
-
// Calculate max value for Y-axis (default to 100 if all values are 0 or very small)
|
|
282
|
-
const maxActivitiesYValue = useMemo(() => {
|
|
283
|
-
if (!activitiesTimelineData || activitiesTimelineData.length === 0) {
|
|
284
|
-
return 100;
|
|
285
|
-
}
|
|
286
|
-
const maxValue = Math.max(...activitiesTimelineData.map(item => item.jobs || 0));
|
|
287
|
-
// If max is 0, set default to 100 to show Y-axis
|
|
288
|
-
if (maxValue === 0) {
|
|
289
|
-
return 100;
|
|
290
|
-
}
|
|
291
|
-
// Round up to nearest 10, but ensure minimum of 100
|
|
292
|
-
const roundedMax = Math.ceil(maxValue / 10) * 10;
|
|
293
|
-
return Math.max(100, roundedMax);
|
|
294
|
-
}, [activitiesTimelineData]);
|
|
295
|
-
|
|
296
74
|
return (
|
|
297
75
|
<section>
|
|
298
76
|
<Widget
|
|
@@ -322,173 +100,16 @@ const CycleOutcomes = ({
|
|
|
322
100
|
|
|
323
101
|
<div style={{ display: "flex", gap: "24px" }}>
|
|
324
102
|
<section style={{ flex: 1 }}>
|
|
325
|
-
<
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
<div className="flex flex-1 flex-column justify-content-center">
|
|
330
|
-
<div className="flex justify-content-center w-full">
|
|
331
|
-
<LineChart
|
|
332
|
-
animated
|
|
333
|
-
isArea
|
|
334
|
-
color={'#00AEB1'}
|
|
335
|
-
data={restoredAreaChartData}
|
|
336
|
-
fillOpacity={0.7}
|
|
337
|
-
height={200}
|
|
338
|
-
renderTooltipContent={(title, data) => {
|
|
339
|
-
if (!data || data.length === 0) return {};
|
|
340
|
-
const item = data[0]?.data || data[0];
|
|
341
|
-
const periodValue = item?.period !== undefined ? item.period : (item?.value || 0);
|
|
342
|
-
const cumulatedValue = item?.value || 0;
|
|
343
|
-
|
|
344
|
-
return {
|
|
345
|
-
title: t("Restored Area"),
|
|
346
|
-
subTitle: formatDateAxis(title),
|
|
347
|
-
items: [
|
|
348
|
-
{
|
|
349
|
-
label: t("Period"),
|
|
350
|
-
value: `${periodValue.toLocaleString()} ha`,
|
|
351
|
-
},
|
|
352
|
-
{
|
|
353
|
-
label: t("Cumulated"),
|
|
354
|
-
value: `${cumulatedValue.toLocaleString()} ha`,
|
|
355
|
-
},
|
|
356
|
-
],
|
|
357
|
-
};
|
|
358
|
-
}}
|
|
359
|
-
xFieldKey="date"
|
|
360
|
-
yFieldKey="value"
|
|
361
|
-
formattedXAxis={formatDateAxis}
|
|
362
|
-
style={{ width: '100%' }}
|
|
363
|
-
yAxis={{
|
|
364
|
-
min: 0,
|
|
365
|
-
max: maxYValue,
|
|
366
|
-
tickMethod: () => {
|
|
367
|
-
// Generate ticks every 10 units for maxYValue of 80
|
|
368
|
-
// For other values, show ticks every 10 units
|
|
369
|
-
const step = maxYValue <= 80 ? 10 : Math.max(10, Math.floor(maxYValue / 8));
|
|
370
|
-
const ticks = [];
|
|
371
|
-
for (let i = 0; i <= maxYValue; i += step) {
|
|
372
|
-
ticks.push(i);
|
|
373
|
-
}
|
|
374
|
-
// Ensure max value is included
|
|
375
|
-
if (ticks.length === 0 || ticks[ticks.length - 1] < maxYValue) {
|
|
376
|
-
ticks.push(maxYValue);
|
|
377
|
-
}
|
|
378
|
-
return ticks;
|
|
379
|
-
},
|
|
380
|
-
label: {
|
|
381
|
-
style: {
|
|
382
|
-
fontSize: 12,
|
|
383
|
-
fill: '#666',
|
|
384
|
-
},
|
|
385
|
-
},
|
|
386
|
-
grid: {
|
|
387
|
-
line: {
|
|
388
|
-
style: {
|
|
389
|
-
stroke: '#E5E7EB',
|
|
390
|
-
lineWidth: 1,
|
|
391
|
-
},
|
|
392
|
-
},
|
|
393
|
-
},
|
|
394
|
-
}}
|
|
395
|
-
xAxis={{
|
|
396
|
-
label: {
|
|
397
|
-
formatter: formatDateAxis, // Ensure formatter is applied
|
|
398
|
-
style: {
|
|
399
|
-
fontSize: 12,
|
|
400
|
-
fill: '#666',
|
|
401
|
-
},
|
|
402
|
-
},
|
|
403
|
-
}}
|
|
404
|
-
/>
|
|
405
|
-
</div>
|
|
406
|
-
</div>
|
|
407
|
-
</Widget>
|
|
103
|
+
<RestoredArea
|
|
104
|
+
restoredAreaChart={restoredAreaChart}
|
|
105
|
+
t={t}
|
|
106
|
+
/>
|
|
408
107
|
</section>
|
|
409
108
|
<section style={{ flex: 1 }}>
|
|
410
|
-
<
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
<div className="flex flex-1 flex-column justify-content-center">
|
|
415
|
-
<div className="flex justify-content-center w-full">
|
|
416
|
-
<ColumnChart
|
|
417
|
-
data={activitiesTimelineData}
|
|
418
|
-
xFieldKey="month"
|
|
419
|
-
yFieldKey="jobs"
|
|
420
|
-
animated={true}
|
|
421
|
-
height={400}
|
|
422
|
-
color="#016C6E"
|
|
423
|
-
renderTooltipContent={(title, data) => {
|
|
424
|
-
if (!data || data.length === 0) return {};
|
|
425
|
-
// For ColumnChart, data structure: data[0]?.data contains the actual data point
|
|
426
|
-
const item = data[0]?.data || data[0];
|
|
427
|
-
const count = item?.jobs || item?.value || 0;
|
|
428
|
-
// Title is the X-axis value (month/date), use it for formatting
|
|
429
|
-
const dateValue = item?.date || title || '';
|
|
430
|
-
|
|
431
|
-
return {
|
|
432
|
-
title: t("Planting Activities"),
|
|
433
|
-
subTitle: formatDateAxis(dateValue),
|
|
434
|
-
items: [
|
|
435
|
-
{
|
|
436
|
-
label: t("Total"),
|
|
437
|
-
value: count,
|
|
438
|
-
},
|
|
439
|
-
],
|
|
440
|
-
};
|
|
441
|
-
}}
|
|
442
|
-
formattedXAxis={formatDateAxis}
|
|
443
|
-
formattedYAxis={(value) => {
|
|
444
|
-
return `${value}`.replace(/\d{1,3}(?=(\d{3})+$)/g, (s) => `${s},`);
|
|
445
|
-
}}
|
|
446
|
-
yAxis={{
|
|
447
|
-
min: 0,
|
|
448
|
-
max: maxActivitiesYValue,
|
|
449
|
-
tickMethod: () => {
|
|
450
|
-
// Generate ticks: for 100 max, show 0, 20, 40, 60, 80, 100
|
|
451
|
-
// For other values, show ticks every 20 units
|
|
452
|
-
const step = maxActivitiesYValue <= 100 ? 20 : Math.max(20, Math.floor(maxActivitiesYValue / 5));
|
|
453
|
-
const ticks = [];
|
|
454
|
-
for (let i = 0; i <= maxActivitiesYValue; i += step) {
|
|
455
|
-
ticks.push(i);
|
|
456
|
-
}
|
|
457
|
-
// Ensure max value is included
|
|
458
|
-
if (ticks.length === 0 || ticks[ticks.length - 1] < maxActivitiesYValue) {
|
|
459
|
-
ticks.push(maxActivitiesYValue);
|
|
460
|
-
}
|
|
461
|
-
return ticks;
|
|
462
|
-
},
|
|
463
|
-
label: {
|
|
464
|
-
style: {
|
|
465
|
-
fontSize: 12,
|
|
466
|
-
fill: '#666',
|
|
467
|
-
},
|
|
468
|
-
},
|
|
469
|
-
grid: {
|
|
470
|
-
line: {
|
|
471
|
-
style: {
|
|
472
|
-
stroke: '#E5E7EB',
|
|
473
|
-
lineWidth: 1,
|
|
474
|
-
},
|
|
475
|
-
},
|
|
476
|
-
},
|
|
477
|
-
}}
|
|
478
|
-
xAxis={{
|
|
479
|
-
label: {
|
|
480
|
-
formatter: formatDateAxis,
|
|
481
|
-
autoHide: true,
|
|
482
|
-
style: {
|
|
483
|
-
fontSize: 12,
|
|
484
|
-
fill: '#666',
|
|
485
|
-
},
|
|
486
|
-
},
|
|
487
|
-
}}
|
|
488
|
-
/>
|
|
489
|
-
</div>
|
|
490
|
-
</div>
|
|
491
|
-
</Widget>
|
|
109
|
+
<PlantingActivitiesTimeline
|
|
110
|
+
activitiesTimelineChart={activitiesTimelineChart}
|
|
111
|
+
t={t}
|
|
112
|
+
/>
|
|
492
113
|
</section>
|
|
493
114
|
</div>
|
|
494
115
|
</Widget>
|
|
@@ -6,13 +6,12 @@ import CommunityParticipation from './components/CommunityParticipation/index.js
|
|
|
6
6
|
import AssociatedInformation from './components/AssociatedInformation/index.jsx';
|
|
7
7
|
import KeyInformation from './components/KeyInformation/index.jsx';
|
|
8
8
|
|
|
9
|
-
const PlantingCycleSummary = ({ header, activityData, loading = false, id, t = () => { }, getSummaryDetail }) => {
|
|
10
|
-
|
|
9
|
+
const PlantingCycleSummary = ({ header, activityData, loading = false, id, projectId, t = () => { }, getSummaryDetail, navigate, selectOptions }) => {
|
|
11
10
|
return (
|
|
12
11
|
<DashboardLayout
|
|
13
12
|
header={
|
|
14
13
|
<Header
|
|
15
|
-
title={header?.title || ''}
|
|
14
|
+
title={header?.title + ' Summary' || ''}
|
|
16
15
|
supportText={header?.supportText || ''}
|
|
17
16
|
onDownload={header?.onDownload}
|
|
18
17
|
downloadDisabled={header?.downloadDisabled}
|
|
@@ -28,7 +27,7 @@ const PlantingCycleSummary = ({ header, activityData, loading = false, id, t = (
|
|
|
28
27
|
<CycleOutcomes id={id} getSummaryDetail={getSummaryDetail} loading={loading} t={t} />
|
|
29
28
|
<CycleIndicators id={id} getSummaryDetail={getSummaryDetail} loading={loading} t={t} />
|
|
30
29
|
<CommunityParticipation id={id} getSummaryDetail={getSummaryDetail} loading={loading} t={t} />
|
|
31
|
-
<AssociatedInformation id={id} getSummaryDetail={getSummaryDetail} loading={loading} t={t} />
|
|
30
|
+
<AssociatedInformation id={id} projectId={projectId} getSummaryDetail={getSummaryDetail} loading={loading} t={t} navigate={navigate} selectOptions={selectOptions} />
|
|
32
31
|
</DashboardLayout>
|
|
33
32
|
)
|
|
34
33
|
}
|