@dhiraj0720/report1chart 2.2.8 → 2.2.9
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/package.json
CHANGED
|
@@ -1,84 +1,77 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { View, Text } from 'react-native';
|
|
3
|
-
import Svg, { Rect,
|
|
2
|
+
import { View, Text, ScrollView } from 'react-native';
|
|
3
|
+
import Svg, { Rect, Circle, Text as SvgText } from 'react-native-svg';
|
|
4
4
|
|
|
5
5
|
const SvgBarLineChart = ({ data }) => {
|
|
6
|
-
if (!data?.series
|
|
6
|
+
if (!data?.series) return null;
|
|
7
7
|
|
|
8
|
-
const width = 360;
|
|
9
|
-
const height =
|
|
10
|
-
const padding =
|
|
11
|
-
const barWidth = 12;
|
|
8
|
+
const width = Math.max(360, data.labels.length * 90);
|
|
9
|
+
const height = 240;
|
|
10
|
+
const padding = 40;
|
|
12
11
|
|
|
13
12
|
const max = Math.max(...data.series.flatMap(s => s.data));
|
|
14
|
-
|
|
15
|
-
const scaleX = i =>
|
|
16
|
-
padding + i * ((width - padding * 2) / data.labels.length);
|
|
17
|
-
|
|
18
|
-
const scaleY = v =>
|
|
19
|
-
height - padding - (v / max) * (height - padding * 2);
|
|
13
|
+
const barWidth = 14;
|
|
20
14
|
|
|
21
15
|
return (
|
|
22
|
-
<View style={{
|
|
23
|
-
<Text style={{ fontWeight: '700',
|
|
16
|
+
<View style={{ marginVertical: 16 }}>
|
|
17
|
+
<Text style={{ fontWeight: '700', marginBottom: 8 }}>
|
|
24
18
|
{data.title}
|
|
25
19
|
</Text>
|
|
26
20
|
|
|
27
|
-
<
|
|
28
|
-
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
21
|
+
<ScrollView horizontal>
|
|
22
|
+
<Svg width={width} height={height}>
|
|
23
|
+
{data.labels.map((label, i) => {
|
|
24
|
+
const x = padding + i * 70;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<React.Fragment key={i}>
|
|
28
|
+
{/* 2024 bar */}
|
|
29
|
+
<Rect
|
|
30
|
+
x={x}
|
|
31
|
+
y={height - (data.series[0].data[i] / max) * 150}
|
|
32
|
+
width={barWidth}
|
|
33
|
+
height={(data.series[0].data[i] / max) * 150}
|
|
34
|
+
fill="#E07A3F"
|
|
35
|
+
/>
|
|
36
|
+
|
|
37
|
+
{/* 2025 bar */}
|
|
38
|
+
<Rect
|
|
39
|
+
x={x + barWidth + 4}
|
|
40
|
+
y={height - (data.series[1].data[i] / max) * 150}
|
|
41
|
+
width={barWidth}
|
|
42
|
+
height={(data.series[1].data[i] / max) * 150}
|
|
43
|
+
fill="#4E79A7"
|
|
44
|
+
/>
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
fill="none"
|
|
51
|
-
stroke="#90CAF9"
|
|
52
|
-
strokeWidth="2"
|
|
53
|
-
strokeDasharray="4,4"
|
|
54
|
-
/>
|
|
46
|
+
{/* Budget dot */}
|
|
47
|
+
<Circle
|
|
48
|
+
cx={x + barWidth}
|
|
49
|
+
cy={height - (data.series[2].data[i] / max) * 150}
|
|
50
|
+
r={4}
|
|
51
|
+
fill="#8AB6E8"
|
|
52
|
+
/>
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
<SvgText
|
|
55
|
+
x={x + barWidth}
|
|
56
|
+
y={height - 6}
|
|
57
|
+
fontSize="10"
|
|
58
|
+
textAnchor="middle"
|
|
59
|
+
>
|
|
60
|
+
{label}
|
|
61
|
+
</SvgText>
|
|
62
|
+
</React.Fragment>
|
|
63
|
+
);
|
|
64
|
+
})}
|
|
65
|
+
</Svg>
|
|
66
|
+
</ScrollView>
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
{data.series
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
y={scaleY(v) - 8}
|
|
74
|
-
fontSize="9"
|
|
75
|
-
fill="#333"
|
|
76
|
-
textAnchor="middle"
|
|
77
|
-
>
|
|
78
|
-
{(v / 1_000_000).toFixed(0)}M
|
|
79
|
-
</SvgText>
|
|
68
|
+
<View style={{ flexDirection: 'row', marginTop: 8 }}>
|
|
69
|
+
{data.series.map((s, i) => (
|
|
70
|
+
<Text key={i} style={{ marginRight: 16 }}>
|
|
71
|
+
● {s.name}
|
|
72
|
+
</Text>
|
|
80
73
|
))}
|
|
81
|
-
</
|
|
74
|
+
</View>
|
|
82
75
|
</View>
|
|
83
76
|
);
|
|
84
77
|
};
|
|
@@ -1,84 +1,88 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { View, Text } from 'react-native';
|
|
3
|
-
import Svg, {
|
|
2
|
+
import { View, Text, ScrollView } from 'react-native';
|
|
3
|
+
import Svg, { Line, Circle, Text as SvgText } from 'react-native-svg';
|
|
4
4
|
|
|
5
5
|
const SvgLineChart = ({ data }) => {
|
|
6
|
-
if (!data?.series
|
|
6
|
+
if (!data?.series) return null;
|
|
7
7
|
|
|
8
|
-
const width =
|
|
9
|
-
const height =
|
|
10
|
-
const padding =
|
|
8
|
+
const width = Math.max(360, data.labels.length * 80);
|
|
9
|
+
const height = 220;
|
|
10
|
+
const padding = 40;
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
const max = Math.max(...
|
|
14
|
-
const min = Math.min(...
|
|
12
|
+
const values = data.series.flatMap(s => s.data);
|
|
13
|
+
const max = Math.max(...values);
|
|
14
|
+
const min = Math.min(...values);
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
|
|
16
|
+
const y = v =>
|
|
17
|
+
height - padding -
|
|
18
|
+
((v - min) / (max - min || 1)) * (height - padding * 2);
|
|
18
19
|
|
|
19
|
-
const
|
|
20
|
-
|
|
20
|
+
const xStep = (width - padding * 2) / (data.labels.length - 1 || 1);
|
|
21
|
+
const colors = ['#E07A3F', '#4E79A7'];
|
|
21
22
|
|
|
22
23
|
return (
|
|
23
|
-
<View style={{
|
|
24
|
-
<Text style={{ fontWeight: '700',
|
|
24
|
+
<View style={{ marginVertical: 16 }}>
|
|
25
|
+
<Text style={{ fontWeight: '700', marginBottom: 8 }}>
|
|
25
26
|
{data.title}
|
|
26
27
|
</Text>
|
|
27
28
|
|
|
28
|
-
<
|
|
29
|
-
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
29
|
+
<ScrollView horizontal>
|
|
30
|
+
<Svg width={width} height={height}>
|
|
31
|
+
{/* Grid */}
|
|
32
|
+
{[0.25, 0.5, 0.75].map((p, i) => (
|
|
33
|
+
<Line
|
|
34
|
+
key={i}
|
|
35
|
+
x1={padding}
|
|
36
|
+
x2={width - padding}
|
|
37
|
+
y1={padding + (height - padding * 2) * p}
|
|
38
|
+
y2={padding + (height - padding * 2) * p}
|
|
39
|
+
stroke="#ccc"
|
|
40
|
+
strokeDasharray="4"
|
|
41
|
+
/>
|
|
42
|
+
))}
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
.join(' ');
|
|
44
|
+
{data.series.map((s, si) =>
|
|
45
|
+
s.data.map((v, i) => {
|
|
46
|
+
const x = padding + i * xStep;
|
|
47
|
+
const yVal = y(v);
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
<Polyline
|
|
50
|
-
key={s.name}
|
|
51
|
-
points={points}
|
|
52
|
-
fill="none"
|
|
53
|
-
stroke={idx === 0 ? '#FB8C00' : '#1E88E5'}
|
|
54
|
-
strokeWidth="2"
|
|
55
|
-
/>
|
|
56
|
-
);
|
|
57
|
-
})}
|
|
49
|
+
if (i === s.data.length - 1) return null;
|
|
58
50
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
51
|
+
return (
|
|
52
|
+
<React.Fragment key={`${si}-${i}`}>
|
|
53
|
+
<Line
|
|
54
|
+
x1={x}
|
|
55
|
+
y1={yVal}
|
|
56
|
+
x2={x + xStep}
|
|
57
|
+
y2={y(s.data[i + 1])}
|
|
58
|
+
stroke={colors[si]}
|
|
59
|
+
strokeWidth={2}
|
|
60
|
+
strokeDasharray={si === 0 ? '4' : '0'}
|
|
61
|
+
/>
|
|
62
|
+
<Circle cx={x} cy={yVal} r={4} fill={colors[si]} />
|
|
63
|
+
<SvgText
|
|
64
|
+
x={x}
|
|
65
|
+
y={yVal - 8}
|
|
66
|
+
fontSize="10"
|
|
67
|
+
textAnchor="middle"
|
|
68
|
+
>
|
|
69
|
+
{v.toLocaleString()}
|
|
70
|
+
</SvgText>
|
|
71
|
+
</React.Fragment>
|
|
72
|
+
);
|
|
73
|
+
})
|
|
74
|
+
)}
|
|
75
|
+
</Svg>
|
|
76
|
+
</ScrollView>
|
|
77
|
+
|
|
78
|
+
{/* Legend */}
|
|
79
|
+
<View style={{ flexDirection: 'row', marginTop: 8 }}>
|
|
80
|
+
{data.series.map((s, i) => (
|
|
81
|
+
<Text key={i} style={{ marginRight: 16, color: colors[i] }}>
|
|
82
|
+
● {s.name}
|
|
83
|
+
</Text>
|
|
84
|
+
))}
|
|
85
|
+
</View>
|
|
82
86
|
</View>
|
|
83
87
|
);
|
|
84
88
|
};
|
package/src/index.jsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import { View, ActivityIndicator } from 'react-native';
|
|
2
3
|
|
|
3
4
|
import ReportListScreen from './screens/ReportListScreen';
|
|
4
5
|
import Report1Screen from './screens/Report1Screen';
|
|
@@ -7,11 +8,38 @@ import Report3Screen from './screens/Report3Screen';
|
|
|
7
8
|
|
|
8
9
|
const AnalyticsReports = ({ config }) => {
|
|
9
10
|
const [active, setActive] = useState(null);
|
|
11
|
+
const [loading, setLoading] = useState(true);
|
|
10
12
|
|
|
13
|
+
// SPLASH / LOADER (5 seconds)
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const timer = setTimeout(() => {
|
|
16
|
+
setLoading(false);
|
|
17
|
+
}, 5000);
|
|
18
|
+
|
|
19
|
+
return () => clearTimeout(timer);
|
|
20
|
+
}, []);
|
|
21
|
+
|
|
22
|
+
// 👉 SHOW LOADER FIRST (BEFORE REPORT LIST)
|
|
23
|
+
if (loading) {
|
|
24
|
+
return (
|
|
25
|
+
<View
|
|
26
|
+
style={{
|
|
27
|
+
flex: 1,
|
|
28
|
+
justifyContent: 'center',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
<ActivityIndicator size="large" />
|
|
33
|
+
</View>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 👉 AFTER LOADER → SHOW REPORT LIST
|
|
11
38
|
if (!active) {
|
|
12
39
|
return <ReportListScreen onSelect={setActive} />;
|
|
13
40
|
}
|
|
14
41
|
|
|
42
|
+
// 👉 REPORT 1
|
|
15
43
|
if (active === 1) {
|
|
16
44
|
return (
|
|
17
45
|
<Report1Screen
|
|
@@ -22,6 +50,7 @@ const AnalyticsReports = ({ config }) => {
|
|
|
22
50
|
);
|
|
23
51
|
}
|
|
24
52
|
|
|
53
|
+
// 👉 REPORT 2
|
|
25
54
|
if (active === 2) {
|
|
26
55
|
return (
|
|
27
56
|
<Report2Screen
|
|
@@ -32,6 +61,7 @@ const AnalyticsReports = ({ config }) => {
|
|
|
32
61
|
);
|
|
33
62
|
}
|
|
34
63
|
|
|
64
|
+
// 👉 REPORT 3
|
|
35
65
|
if (active === 3) {
|
|
36
66
|
return (
|
|
37
67
|
<Report3Screen
|
|
@@ -39,11 +39,31 @@ const Report2Screen = ({ api, token, onBack }) => {
|
|
|
39
39
|
|
|
40
40
|
if (!table || !line || !bar) return <ActivityIndicator />;
|
|
41
41
|
|
|
42
|
+
const filterChartByMonth = (chart, month) => {
|
|
43
|
+
if (month === 'ALL') return chart;
|
|
44
|
+
const index = chart.labels.indexOf(month);
|
|
45
|
+
if (index === -1) return chart;
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
...chart,
|
|
49
|
+
labels: [chart.labels[index]],
|
|
50
|
+
series: chart.series.map(s => ({
|
|
51
|
+
...s,
|
|
52
|
+
data: [s.data[index]],
|
|
53
|
+
})),
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
|
|
42
58
|
const rows =
|
|
43
59
|
month === 'ALL'
|
|
44
60
|
? table.rows
|
|
45
61
|
: table.rows.filter(r => r.monthLabel === month);
|
|
46
62
|
|
|
63
|
+
const filteredLine = filterChartByMonth(line, month);
|
|
64
|
+
const filteredBar = filterChartByMonth(bar, month);
|
|
65
|
+
|
|
66
|
+
|
|
47
67
|
return (
|
|
48
68
|
<ScrollView style={{ padding: 16 }}>
|
|
49
69
|
<Text onPress={onBack}>‹ Back</Text>
|
|
@@ -58,16 +78,18 @@ const Report2Screen = ({ api, token, onBack }) => {
|
|
|
58
78
|
onSelect={setMonth}
|
|
59
79
|
/>
|
|
60
80
|
|
|
61
|
-
<FrozenTableReport2 rows={rows} />
|
|
62
|
-
|
|
63
|
-
<SvgLineChart data={line} />
|
|
64
|
-
<SvgBarLineChart data={bar} />
|
|
65
|
-
|
|
66
81
|
<DivisionSelector
|
|
67
82
|
divisions={divisions}
|
|
68
83
|
selected={division}
|
|
69
84
|
onSelect={setDivision}
|
|
70
85
|
/>
|
|
86
|
+
|
|
87
|
+
<FrozenTableReport2 rows={rows} />
|
|
88
|
+
|
|
89
|
+
<SvgLineChart data={filteredLine} />
|
|
90
|
+
<SvgBarLineChart data={filteredBar} />
|
|
91
|
+
|
|
92
|
+
|
|
71
93
|
</ScrollView>
|
|
72
94
|
);
|
|
73
95
|
};
|
|
@@ -26,6 +26,21 @@ const Report3Screen = ({ api, token, onBack }) => {
|
|
|
26
26
|
|
|
27
27
|
if (!table || !line || !bar) return <ActivityIndicator />;
|
|
28
28
|
|
|
29
|
+
const filterChartByMonth = (chart, month) => {
|
|
30
|
+
if (month === 'ALL') return chart;
|
|
31
|
+
const index = chart.labels.indexOf(month);
|
|
32
|
+
if (index === -1) return chart;
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
...chart,
|
|
36
|
+
labels: [chart.labels[index]],
|
|
37
|
+
series: chart.series.map(s => ({
|
|
38
|
+
...s,
|
|
39
|
+
data: [s.data[index]],
|
|
40
|
+
})),
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
|
|
29
44
|
const months = table.rows.filter(r => r.month !== 99).map(r => r.monthLabel);
|
|
30
45
|
|
|
31
46
|
const rows =
|
|
@@ -33,6 +48,10 @@ const Report3Screen = ({ api, token, onBack }) => {
|
|
|
33
48
|
? table.rows
|
|
34
49
|
: table.rows.filter(r => r.monthLabel === month);
|
|
35
50
|
|
|
51
|
+
const filteredLine = filterChartByMonth(line, month);
|
|
52
|
+
const filteredBar = filterChartByMonth(bar, month);
|
|
53
|
+
|
|
54
|
+
|
|
36
55
|
return (
|
|
37
56
|
<ScrollView style={{ padding: 16 }}>
|
|
38
57
|
<Text onPress={onBack}>‹ Back</Text>
|
|
@@ -49,8 +68,8 @@ const Report3Screen = ({ api, token, onBack }) => {
|
|
|
49
68
|
|
|
50
69
|
<FrozenTableReport3 rows={rows} />
|
|
51
70
|
|
|
52
|
-
<SvgLineChart data={
|
|
53
|
-
|
|
71
|
+
<SvgLineChart data={filteredLine} />
|
|
72
|
+
<SvgBarLineChart data={filteredBar} />
|
|
54
73
|
</ScrollView>
|
|
55
74
|
);
|
|
56
75
|
};
|