@dhiraj0720/report1chart 2.5.7 → 2.5.8

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhiraj0720/report1chart",
3
- "version": "2.5.7",
3
+ "version": "2.5.8",
4
4
  "main": "src/index.jsx",
5
5
  "scripts": {
6
6
  "test": "echo 'No tests'"
@@ -1,60 +1,160 @@
1
1
  import React from 'react';
2
2
  import { View, Text, ScrollView, StyleSheet } from 'react-native';
3
- import { formatNumber } from '../utils/formatNumber';
4
3
 
5
- const Arrow = ({ v }) => (
6
- <Text style={{ color: v >= 0 ? 'green' : 'red', fontWeight: '700' }}>
7
- {v >= 0 ? '' : ''} {Math.abs(v)}%
8
- </Text>
9
- );
4
+ // Helper: Format numbers with commas (e.g., 1083203 → 1,083,203)
5
+ const formatNumber = (value) => {
6
+ if (value === null || value === undefined || value === '') return '-';
7
+ return Number(value).toLocaleString('en-US');
8
+ };
9
+
10
+ // Percentage cell with up/down arrow and color
11
+ const PercentCell = ({ value }) => {
12
+ if (value === null || value === undefined) return <Text>-</Text>;
13
+ const positive = value >= 0;
14
+ return (
15
+ <Text style={[
16
+ styles.percentText,
17
+ { color: positive ? '#2e7d32' : '#d32f2f' }
18
+ ]}>
19
+ {positive ? '↑' : '↓'} {Math.abs(value)}%
20
+ </Text>
21
+ );
22
+ };
10
23
 
11
- const Cell = ({ children, bold }) => (
12
- <View style={styles.cell}>
13
- <Text numberOfLines={1} style={bold && styles.bold}>{children}</Text>
24
+ // Reusable cell component
25
+ const Cell = ({ children, bold = false, highlight = false }) => (
26
+ <View style={[
27
+ styles.cell,
28
+ bold && styles.bold,
29
+ highlight && styles.highlightCell
30
+ ]}>
31
+ <Text style={[styles.cellText, bold && styles.boldText]}>
32
+ {children}
33
+ </Text>
14
34
  </View>
15
35
  );
16
36
 
17
- const FrozenTableReport2A = ({ rows }) => (
18
- <View style={styles.container}>
19
- <View style={styles.frozen}>
20
- <Cell bold>AY</Cell>
21
- {rows.map((r, i) => <Cell key={i} bold>{r.monthLabel}</Cell>)}
22
- </View>
37
+ const FrozenTableReport2A = ({ rows = [] }) => {
38
+ if (!rows || rows.length === 0) {
39
+ return <Text>No data available</Text>;
40
+ }
23
41
 
24
- <ScrollView horizontal>
25
- <View>
26
- <View style={styles.header}>
27
- <Cell bold>2024</Cell>
28
- <Cell bold>2025</Cell>
29
- <Cell bold>%</Cell>
30
- </View>
42
+ return (
43
+ <View style={styles.container}>
44
+ {/* Frozen Left Column - AY (Month) */}
45
+ <View style={styles.frozenColumn}>
46
+ <Cell bold>AY</Cell>
47
+ {rows.map((row, index) => {
48
+ const isTotal = row.monthLabel === 'Total';
49
+ return (
50
+ <Cell
51
+ key={index}
52
+ bold={isTotal}
53
+ highlight={isTotal}
54
+ >
55
+ {row.monthLabel}
56
+ </Cell>
57
+ );
58
+ })}
59
+ </View>
31
60
 
32
- {rows.map((r, i) => (
33
- <View key={i} style={styles.row}>
34
- <Cell>{formatNumber(r.teu2024)}</Cell>
35
- <Cell>{formatNumber(r.teu2025)}</Cell>
36
- <Cell><Arrow v={r.teuChangePercent} /></Cell>
61
+ {/* Scrollable Columns */}
62
+ <ScrollView horizontal showsHorizontalScrollIndicator={false}>
63
+ <View>
64
+ {/* Header Row */}
65
+ <View style={styles.headerRow}>
66
+ {[
67
+ '2024 TEU', '2025 TEU', 'TEU %',
68
+ '2024 Kar', '2025 Kar', 'Kar %',
69
+ '2025 Bütçe', 'Bütçe %'
70
+ ].map((header) => (
71
+ <Cell key={header} bold>
72
+ {header}
73
+ </Cell>
74
+ ))}
37
75
  </View>
38
- ))}
39
- </View>
40
- </ScrollView>
41
- </View>
42
- );
76
+
77
+ {/* Data Rows */}
78
+ {rows.map((row, index) => {
79
+ const isTotal = row.monthLabel === 'Total';
80
+
81
+ return (
82
+ <View key={index} style={styles.dataRow}>
83
+ <Cell highlight={isTotal}>{formatNumber(row.teu2024)}</Cell>
84
+ <Cell highlight={isTotal}>{formatNumber(row.teu2025)}</Cell>
85
+ <Cell highlight={isTotal}>
86
+ <PercentCell value={row.teuChangePercent} />
87
+ </Cell>
88
+
89
+ <Cell highlight={isTotal}>{formatNumber(row.profitUsd2024)}</Cell>
90
+ <Cell highlight={isTotal}>{formatNumber(row.profitUsd2025)}</Cell>
91
+ <Cell highlight={isTotal}>
92
+ <PercentCell value={row.profitChangePercent} />
93
+ </Cell>
94
+
95
+ <Cell highlight={isTotal}>{formatNumber(row.budgetProfitUsd2025)}</Cell>
96
+ <Cell highlight={isTotal}>
97
+ <PercentCell value={row.budgetChangePercent} />
98
+ </Cell>
99
+ </View>
100
+ );
101
+ })}
102
+ </View>
103
+ </ScrollView>
104
+ </View>
105
+ );
106
+ };
43
107
 
44
108
  export default FrozenTableReport2A;
45
109
 
46
110
  const styles = StyleSheet.create({
47
- container: { flexDirection: 'row', marginVertical: 8 },
48
- frozen: { width: 90, backgroundColor: '#f4f4f4' },
49
- header: { flexDirection: 'row', backgroundColor: '#f4f4f4' },
50
- row: { flexDirection: 'row' },
51
- cell: {
52
- width: 90,
53
- padding: 6,
54
- borderBottomWidth: 1,
111
+ container: {
112
+ flexDirection: 'row',
113
+ borderWidth: 1,
114
+ borderColor: '#ddd',
115
+ borderRadius: 8,
116
+ overflow: 'hidden',
117
+ marginVertical: 12,
118
+ backgroundColor: '#fff',
119
+ },
120
+ frozenColumn: {
121
+ width: 120,
122
+ backgroundColor: '#f4f6f8',
123
+ borderRightWidth: 1,
55
124
  borderColor: '#ddd',
125
+ },
126
+ headerRow: {
127
+ flexDirection: 'row',
128
+ backgroundColor: '#f4f6f8',
129
+ },
130
+ dataRow: {
131
+ flexDirection: 'row',
132
+ },
133
+ cell: {
134
+ width: 130,
135
+ paddingVertical: 10,
136
+ paddingHorizontal: 6,
56
137
  justifyContent: 'center',
57
- alignItems: 'center',
138
+ borderBottomWidth: 1,
139
+ borderColor: '#e0e0e0',
140
+ },
141
+ cellText: {
142
+ fontSize: 12,
143
+ textAlign: 'center',
144
+ color: '#333',
145
+ },
146
+ bold: {
147
+ backgroundColor: '#e9f0f8',
148
+ },
149
+ boldText: {
150
+ fontWeight: '700',
151
+ },
152
+ highlightCell: {
153
+ backgroundColor: '#e9f0f8',
154
+ },
155
+ percentText: {
156
+ fontWeight: '700',
157
+ fontSize: 12,
158
+ textAlign: 'center',
58
159
  },
59
- bold: { fontWeight: '700' },
60
- });
160
+ });
@@ -6,107 +6,125 @@ import { formatNumber } from '../utils/formatNumber';
6
6
  const SvgBarLineChartCompact = ({ data }) => {
7
7
  if (!data?.series || !data.labels?.length) return null;
8
8
 
9
- const height = 220;
10
- const graphWidth = Math.max(320, data.labels.length * 80);
11
-
12
- const paddingLeft = 50;
13
- const paddingRight = 10;
9
+ const height = 240;
10
+ const paddingLeft = 60;
11
+ const paddingRight = 20;
14
12
  const paddingTop = 20;
15
- const paddingBottom = 35;
13
+ const paddingBottom = 50;
16
14
 
17
15
  const chartHeight = height - paddingTop - paddingBottom;
16
+ const graphWidth = Math.max(400, data.labels.length * 90);
17
+
18
+ const MAX = 1279958; // Slightly above max in image
19
+ const barWidth = 16;
20
+ const groupGap = 20;
21
+ const groupWidth = barWidth * 2 + 4 + groupGap;
18
22
 
19
- const MAX = 1000000; // 1.0M
20
- const barWidth = 14;
23
+ const y = (v) => paddingTop + ((MAX - v) / MAX) * chartHeight;
21
24
 
22
- const y = (v) =>
23
- paddingTop + ((MAX - v) / MAX) * chartHeight;
25
+ const getX = (i) => paddingLeft + i * groupWidth + groupGap / 2;
24
26
 
25
27
  return (
26
28
  <View style={styles.container}>
27
29
  <Text style={styles.title}>{data.title}</Text>
28
30
 
29
31
  <View style={{ flexDirection: 'row' }}>
30
- {/* FIXED Y AXIS */}
31
32
  <Svg width={paddingLeft} height={height}>
32
- {[1000000, 500000, 0].map((v, i) => (
33
+ {[1000000, 500000, 0].map((v) => (
33
34
  <SvgText
34
- key={i}
35
- x={paddingLeft - 6}
36
- y={y(v) + 4}
37
- fontSize="10"
35
+ key={v}
36
+ x={paddingLeft - 8}
37
+ y={y(v) + 5}
38
+ fontSize="11"
38
39
  textAnchor="end"
39
40
  fill="#444"
40
41
  >
41
- {(v / 1_000_000).toFixed(1)}M
42
+ {(v / 1000000).toFixed(1)}M
42
43
  </SvgText>
43
44
  ))}
44
45
  </Svg>
45
46
 
46
- {/* SCROLLABLE GRAPH */}
47
47
  <ScrollView horizontal showsHorizontalScrollIndicator={false}>
48
48
  <Svg width={graphWidth} height={height}>
49
- {/* GRID */}
50
- {[1000000, 500000, 0].map((v, i) => (
51
- <Line
52
- key={i}
53
- x1={0}
54
- x2={graphWidth}
55
- y1={y(v)}
56
- y2={y(v)}
57
- stroke="#ccc"
58
- strokeDasharray="4"
59
- />
49
+ {/* Grid */}
50
+ {[1000000, 500000, 0].map((v) => (
51
+ <Line key={v} x1={0} x2={graphWidth} y1={y(v)} y2={y(v)} stroke="#eee" strokeDasharray="5,5" />
60
52
  ))}
61
53
 
62
54
  {data.labels.map((label, i) => {
63
- const x = paddingLeft + i * 70;
55
+ const xCenter = getX(i);
56
+ const bar2024 = data.series[0].data[i] || 0;
57
+ const bar2025 = data.series[1].data[i] || 0;
58
+ const budget = data.series[2].data[i] || 0;
59
+
60
+ const height2024 = chartHeight - (y(bar2024) - paddingTop);
61
+ const height2025 = chartHeight - (y(bar2025) - paddingTop);
64
62
 
65
63
  return (
66
64
  <React.Fragment key={i}>
67
- {/* 2024 BAR */}
65
+ {/* 2024 Bar */}
68
66
  <Rect
69
- x={x}
70
- y={y(data.series[0].data[i])}
67
+ x={xCenter - barWidth - 2}
68
+ y={y(bar2024)}
71
69
  width={barWidth}
72
- height={chartHeight - (y(data.series[0].data[i]) - paddingTop)}
70
+ height={height2024}
73
71
  fill="#E07A3F"
74
72
  />
75
-
76
- {/* 2025 BAR */}
73
+ {bar2024 > MAX * 0.1 && (
74
+ <SvgText
75
+ x={xCenter - barWidth - 2 + barWidth / 2}
76
+ y={y(bar2024) + height2024 / 2 + 5}
77
+ fontSize="9"
78
+ textAnchor="middle"
79
+ fill="white"
80
+ fontWeight="700"
81
+ >
82
+ {formatNumber(bar2024)}
83
+ </SvgText>
84
+ )}
85
+
86
+ {/* 2025 Bar */}
77
87
  <Rect
78
- x={x + barWidth + 4}
79
- y={y(data.series[1].data[i])}
88
+ x={xCenter + 2}
89
+ y={y(bar2025)}
80
90
  width={barWidth}
81
- height={chartHeight - (y(data.series[1].data[i]) - paddingTop)}
91
+ height={height2025}
82
92
  fill="#4E79A7"
83
93
  />
84
-
85
- {/* BUDGET DOT + LINE */}
86
- <Circle
87
- cx={x + barWidth}
88
- cy={y(data.series[2].data[i])}
89
- r={4}
90
- fill="#8AB6E8"
91
- />
92
-
93
- {/* VALUE */}
94
+ {bar2025 > MAX * 0.1 && (
95
+ <SvgText
96
+ x={xCenter + 2 + barWidth / 2}
97
+ y={y(bar2025) + height2025 / 2 + 5}
98
+ fontSize="9"
99
+ textAnchor="middle"
100
+ fill="white"
101
+ fontWeight="700"
102
+ >
103
+ {formatNumber(bar2025)}
104
+ </SvgText>
105
+ )}
106
+
107
+ {/* Budget Line + Dot */}
108
+ <Circle cx={xCenter} cy={y(budget)} r={5} fill="#8AB6E8" stroke="#fff" strokeWidth={2} />
94
109
  <SvgText
95
- x={x + barWidth}
96
- y={y(data.series[2].data[i]) - 8}
97
- fontSize="9"
110
+ x={xCenter}
111
+ y={y(budget) - 10}
112
+ fontSize="10"
98
113
  textAnchor="middle"
114
+ fill="#333"
115
+ fontWeight="700"
99
116
  >
100
- {formatNumber(data.series[2].data[i])}
117
+ {formatNumber(budget)}
101
118
  </SvgText>
102
119
 
103
- {/* X LABEL */}
120
+ {/* X Label */}
104
121
  <SvgText
105
- x={x + barWidth}
106
- y={height - 10}
107
- fontSize="9"
122
+ x={xCenter}
123
+ y={height - 20}
124
+ fontSize="11"
108
125
  textAnchor="middle"
109
- transform={`rotate(-30 ${x + barWidth} ${height - 10})`}
126
+ fill="#555"
127
+ transform={`rotate(-45 ${xCenter} ${height - 20})`}
110
128
  >
111
129
  {label}
112
130
  </SvgText>
@@ -117,15 +135,10 @@ const SvgBarLineChartCompact = ({ data }) => {
117
135
  </ScrollView>
118
136
  </View>
119
137
 
120
- {/* LEGEND */}
121
138
  <View style={styles.legend}>
122
- <Text style={{ color: '#E07A3F' }}>● {data.series[0].name}</Text>
123
- <Text style={{ color: '#4E79A7', marginLeft: 12 }}>
124
- {data.series[1].name}
125
- </Text>
126
- <Text style={{ color: '#8AB6E8', marginLeft: 12 }}>
127
- ● {data.series[2].name}
128
- </Text>
139
+ <Text style={{ color: '#E07A3F', marginRight: 16 }}>● {data.series[0].name}</Text>
140
+ <Text style={{ color: '#4E79A7', marginRight: 16 }}>● {data.series[1].name}</Text>
141
+ <Text style={{ color: '#8AB6E8' }}>● {data.series[2].name}</Text>
129
142
  </View>
130
143
  </View>
131
144
  );
@@ -7,43 +7,38 @@ const SvgLineChartCompact = ({ data }) => {
7
7
  if (!data?.series || !data.labels?.length) return null;
8
8
 
9
9
  const height = 200;
10
- const graphWidth = Math.max(320, data.labels.length * 70);
11
-
12
- const paddingLeft = 50; // Y axis space
13
- const paddingRight = 10;
10
+ const paddingLeft = 60;
11
+ const paddingRight = 20;
14
12
  const paddingTop = 20;
15
- const paddingBottom = 35;
13
+ const paddingBottom = 40;
14
+
15
+ const chartHeight = height - paddingTop - paddingBottom;
16
+ const graphWidth = Math.max(400, data.labels.length * 80);
16
17
 
17
18
  // FIXED AXIS (as per image)
18
19
  const MIN = 5000;
19
20
  const MAX = 10000;
20
21
 
21
- const chartHeight = height - paddingTop - paddingBottom;
22
- const xStep =
23
- data.labels.length === 1
24
- ? 0
25
- : (graphWidth - paddingLeft - paddingRight) /
26
- (data.labels.length - 1);
22
+ const y = (v) => paddingTop + ((MAX - Math.max(MIN, Math.min(MAX, v))) / (MAX - MIN)) * chartHeight;
27
23
 
28
- const y = (v) =>
29
- paddingTop +
30
- ((MAX - v) / (MAX - MIN)) * chartHeight;
24
+ // Fix for single point: use fixed spacing
25
+ const totalPoints = data.labels.length;
26
+ const xStep = totalPoints === 1 ? 0 : (graphWidth - paddingLeft - paddingRight) / (totalPoints - 1);
31
27
 
32
- const colors = ['#E07A3F', '#4E79A7']; // orange / blue
28
+ const colors = ['#E07A3F', '#4E79A7'];// orange / blue
33
29
 
34
30
  return (
35
31
  <View style={styles.container}>
36
32
  <Text style={styles.title}>{data.title}</Text>
37
33
 
38
34
  <View style={{ flexDirection: 'row' }}>
39
- {/* FIXED Y AXIS */}
40
35
  <Svg width={paddingLeft} height={height}>
41
- {[10000, 5000].map((v, i) => (
36
+ {[10000, 5000].map((v) => (
42
37
  <SvgText
43
- key={i}
44
- x={paddingLeft - 6}
45
- y={y(v) + 4}
46
- fontSize="10"
38
+ key={v}
39
+ x={paddingLeft - 8}
40
+ y={y(v) + 5}
41
+ fontSize="11"
47
42
  textAnchor="end"
48
43
  fill="#444"
49
44
  >
@@ -52,55 +47,33 @@ const SvgLineChartCompact = ({ data }) => {
52
47
  ))}
53
48
  </Svg>
54
49
 
55
- {/* SCROLLABLE GRAPH */}
56
50
  <ScrollView horizontal showsHorizontalScrollIndicator={false}>
57
51
  <Svg width={graphWidth} height={height}>
58
- {/* GRID LINES */}
59
- {[10000, 5000].map((v, i) => (
60
- <Line
61
- key={i}
62
- x1={0}
63
- x2={graphWidth}
64
- y1={y(v)}
65
- y2={y(v)}
66
- stroke="#ccc"
67
- strokeDasharray="4"
68
- />
52
+ {/* Grid */}
53
+ {[10000, 5000].map((v) => (
54
+ <Line key={v} x1={0} x2={graphWidth} y1={y(v)} y2={y(v)} stroke="#eee" strokeDasharray="5,5" />
69
55
  ))}
70
56
 
71
- {/* LINES + DOTS */}
72
- {data.series.map((s, si) =>
73
- s.data.map((val, i) => {
74
- const x =
75
- paddingLeft + (data.labels.length === 1 ? 0 : i * xStep);
76
- const yVal = y(val);
57
+ {/* Series */}
58
+ {data.series.map((series, si) =>
59
+ series.data.map((val, i) => {
60
+ const x = paddingLeft + i * (totalPoints === 1 ? graphWidth / 2 - paddingLeft : xStep);
77
61
 
78
62
  return (
79
63
  <React.Fragment key={`${si}-${i}`}>
80
- {/* CONNECTING LINE */}
81
- {i < s.data.length - 1 && (
64
+ {i > 0 && (
82
65
  <Line
83
- x1={x}
84
- y1={yVal}
85
- x2={paddingLeft + (i + 1) * xStep}
86
- y2={y(s.data[i + 1])}
66
+ x1={paddingLeft + (i - 1) * (totalPoints === 1 ? graphWidth / 2 - paddingLeft : xStep)}
67
+ y1={y(series.data[i - 1])}
68
+ x2={x}
69
+ y2={y(val)}
87
70
  stroke={colors[si]}
88
- strokeWidth={2}
89
- strokeDasharray={si === 0 ? '4' : '0'}
71
+ strokeWidth={2.5}
72
+ strokeDasharray={si === 0 ? '6,4' : '0'}
90
73
  />
91
74
  )}
92
-
93
- {/* DOT */}
94
- <Circle cx={x} cy={yVal} r={4} fill={colors[si]} />
95
-
96
- {/* VALUE */}
97
- <SvgText
98
- x={x}
99
- y={yVal - 8}
100
- fontSize="9"
101
- textAnchor="middle"
102
- fill="#333"
103
- >
75
+ <Circle cx={x} cy={y(val)} r={5} fill={colors[si]} />
76
+ <SvgText x={x} y={y(val) - 10} fontSize="10" textAnchor="middle" fill="#333">
104
77
  {formatNumber(val)}
105
78
  </SvgText>
106
79
  </React.Fragment>
@@ -108,18 +81,18 @@ const SvgLineChartCompact = ({ data }) => {
108
81
  })
109
82
  )}
110
83
 
111
- {/* X AXIS LABELS */}
84
+ {/* X Labels */}
112
85
  {data.labels.map((label, i) => {
113
- const x =
114
- paddingLeft + (data.labels.length === 1 ? 0 : i * xStep);
86
+ const x = paddingLeft + i * (totalPoints === 1 ? graphWidth / 2 - paddingLeft : xStep);
115
87
  return (
116
88
  <SvgText
117
89
  key={i}
118
90
  x={x}
119
- y={height - 10}
120
- fontSize="9"
91
+ y={height - 15}
92
+ fontSize="11"
121
93
  textAnchor="middle"
122
- transform={`rotate(-30 ${x} ${height - 10})`}
94
+ fill="#555"
95
+ transform={`rotate(-45 ${x} ${height - 15})`}
123
96
  >
124
97
  {label}
125
98
  </SvgText>
@@ -129,12 +102,12 @@ const SvgLineChartCompact = ({ data }) => {
129
102
  </ScrollView>
130
103
  </View>
131
104
 
132
- {/* LEGEND */}
133
105
  <View style={styles.legend}>
134
- <Text style={{ color: colors[0] }}>● {data.series[0].name}</Text>
135
- <Text style={{ color: colors[1], marginLeft: 16 }}>
136
- ● {data.series[1].name}
137
- </Text>
106
+ {data.series.map((s, i) => (
107
+ <Text key={i} style={{ color: colors[i], marginRight: 16 }}>
108
+ ● {s.name}
109
+ </Text>
110
+ ))}
138
111
  </View>
139
112
  </View>
140
113
  );
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
3
-
3
+ import { filterChartByMonths } from '../utils/filterChartByMonths';
4
4
  import { getDivisions, getTable, getLine, getBar } from '../api/report2Fetcher';
5
5
  import MonthFilterModal from '../components/MonthFilterModal';
6
6
  import DivisionFilterModal from '../components/DivisionFilterModal';
@@ -24,12 +24,13 @@ const Report2AScreen = ({ api, token, onBack }) => {
24
24
  useEffect(() => {
25
25
  getDivisions(api.divisions, token).then(d => {
26
26
  setDivisions(d);
27
- setDivision(d[0]?.code);
27
+ if (d.length > 0) setDivision(d[0]?.code);
28
28
  });
29
- }, []);
29
+ }, [api.divisions, token]);
30
30
 
31
31
  useEffect(() => {
32
32
  if (!division) return;
33
+
33
34
  Promise.all([
34
35
  getTable(api.table, division, token),
35
36
  getLine(api.line, division, token),
@@ -39,44 +40,50 @@ const Report2AScreen = ({ api, token, onBack }) => {
39
40
  setLine(l);
40
41
  setBar(b);
41
42
  setMonths(l.labels);
42
- setSelectedMonths(l.labels);
43
+ setSelectedMonths(l.labels); // default: all selected
43
44
  });
44
- }, [division]);
45
+ }, [division, api, token]);
45
46
 
46
- if (!table || !line || !bar) return null;
47
+ if (!table || !line || !bar || !division) return null;
47
48
 
48
- const rows = table.rows.filter(r =>
49
+ const filteredRows = table.rows.filter(r =>
49
50
  selectedMonths.includes(r.monthLabel)
50
51
  );
51
52
 
53
+ const filteredLine = filterChartByMonths(line, selectedMonths);
54
+ const filteredBar = filterChartByMonths(bar, selectedMonths);
55
+
52
56
  return (
53
57
  <ScrollView style={{ padding: 12 }}>
54
- <Text onPress={onBack}>‹ Back</Text>
58
+ <Text onPress={onBack} style={{ fontSize: 18, marginBottom: 12 }}>
59
+ ‹ Back
60
+ </Text>
55
61
 
56
- {/* FILTER BAR */}
57
- <View style={{ flexDirection: 'row', marginVertical: 6 }}>
62
+ {/* FILTER BUTTONS */}
63
+ <View style={{ flexDirection: 'row', marginVertical: 10, gap: 16 }}>
58
64
  <TouchableOpacity onPress={() => setMonthsModal(true)}>
59
- <Text>Months ⛃</Text>
65
+ <Text style={{ fontSize: 16, color: '#1e88e5' }}>Months ⛃</Text>
60
66
  </TouchableOpacity>
61
67
 
62
- <TouchableOpacity
63
- onPress={() => setDivisionModal(true)}
64
- style={{ marginLeft: 16 }}
65
- >
66
- <Text>Divisions ⛃</Text>
68
+ <TouchableOpacity onPress={() => setDivisionModal(true)}>
69
+ <Text style={{ fontSize: 16, color: '#1e88e5' }}>Divisions ⛃</Text>
67
70
  </TouchableOpacity>
68
71
  </View>
69
72
 
70
- <FrozenTableReport2A rows={rows} />
73
+ {/* TABLE */}
74
+ <FrozenTableReport2A rows={filteredRows} />
71
75
 
72
- <View style={{ borderTopWidth: 1, borderBottomWidth: 1, marginVertical: 6 }}>
73
- <SvgLineChartCompact data={line} />
76
+ {/* LINE CHART */}
77
+ <View style={{ borderWidth: 1, borderColor: '#ddd', borderRadius: 8, marginVertical: 12 }}>
78
+ <SvgLineChartCompact data={filteredLine} />
74
79
  </View>
75
80
 
76
- <View style={{ borderTopWidth: 1, borderBottomWidth: 1, marginVertical: 6 }}>
77
- <SvgBarLineChartCompact data={bar} />
81
+ {/* BAR + LINE CHART */}
82
+ <View style={{ borderWidth: 1, borderColor: '#ddd', borderRadius: 8, marginVertical: 12 }}>
83
+ <SvgBarLineChartCompact data={filteredBar} />
78
84
  </View>
79
85
 
86
+ {/* MODALS */}
80
87
  <MonthFilterModal
81
88
  visible={monthsModal}
82
89
  months={months}
@@ -96,4 +103,4 @@ const Report2AScreen = ({ api, token, onBack }) => {
96
103
  );
97
104
  };
98
105
 
99
- export default Report2AScreen;
106
+ export default Report2AScreen;
@@ -0,0 +1,16 @@
1
+ export const filterChartByMonths = (chart, selectedMonths) => {
2
+ if (!selectedMonths || selectedMonths.length === 0) return chart;
3
+
4
+ const indices = chart.labels
5
+ .map((label, i) => (selectedMonths.includes(label) ? i : -1))
6
+ .filter(i => i !== -1);
7
+
8
+ return {
9
+ ...chart,
10
+ labels: indices.map(i => chart.labels[i]),
11
+ series: chart.series.map(s => ({
12
+ ...s,
13
+ data: indices.map(i => s.data[i]),
14
+ })),
15
+ };
16
+ };