@dhiraj0720/report1chart 2.5.8 → 2.6.0

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.8",
3
+ "version": "2.6.0",
4
4
  "main": "src/index.jsx",
5
5
  "scripts": {
6
6
  "test": "echo 'No tests'"
@@ -6,125 +6,161 @@ 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 = 240;
10
- const paddingLeft = 60;
11
- const paddingRight = 20;
9
+ const height = 260;
10
+ const paddingLeft = 70;
11
+ const paddingRight = 30;
12
12
  const paddingTop = 20;
13
- const paddingBottom = 50;
13
+ const paddingBottom = 60;
14
14
 
15
15
  const chartHeight = height - paddingTop - paddingBottom;
16
- const graphWidth = Math.max(400, data.labels.length * 90);
16
+ const graphWidth = Math.max(500, data.labels.length * 100); // Wider spacing like image
17
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
+ // Max value slightly above the highest in image
19
+ const MAX = 1279958;
20
+ const barWidth = 20;
21
+ const barGap = 6; // Gap between 2024 and 2025 bar
22
+ const groupGap = 30; // Gap between months (matches image)
22
23
 
23
24
  const y = (v) => paddingTop + ((MAX - v) / MAX) * chartHeight;
24
25
 
25
- const getX = (i) => paddingLeft + i * groupWidth + groupGap / 2;
26
+ // Center X for each month group
27
+ const getGroupCenterX = (i) => paddingLeft + i * (barWidth * 2 + barGap + groupGap) + (barWidth + barGap / 2);
26
28
 
27
29
  return (
28
30
  <View style={styles.container}>
29
- <Text style={styles.title}>{data.title}</Text>
31
+ <Text style={styles.title}>{data.title || 'AY BAZINDA KAR KARŞILAŞTIRMALARI'}</Text>
30
32
 
31
33
  <View style={{ flexDirection: 'row' }}>
34
+ {/* Y Axis Labels */}
32
35
  <Svg width={paddingLeft} height={height}>
33
- {[1000000, 500000, 0].map((v) => (
36
+ {[1279958, 1000000, 500000, 0].map((v, i) => (
34
37
  <SvgText
35
- key={v}
36
- x={paddingLeft - 8}
38
+ key={i}
39
+ x={paddingLeft - 10}
37
40
  y={y(v) + 5}
38
41
  fontSize="11"
39
42
  textAnchor="end"
40
43
  fill="#444"
41
44
  >
42
- {(v / 1000000).toFixed(1)}M
45
+ {v >= 1000000 ? `$${ (v / 1000000).toFixed(1) }M` : formatNumber(v)}
43
46
  </SvgText>
44
47
  ))}
45
48
  </Svg>
46
49
 
50
+ {/* Main Chart - Scrollable */}
47
51
  <ScrollView horizontal showsHorizontalScrollIndicator={false}>
48
52
  <Svg width={graphWidth} height={height}>
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" />
53
+ {/* Horizontal Grid Lines */}
54
+ {[1279958, 1000000, 500000, 0].map((v) => (
55
+ <Line
56
+ key={v}
57
+ x1={0}
58
+ x2={graphWidth}
59
+ y1={y(v)}
60
+ y2={y(v)}
61
+ stroke="#eee"
62
+ strokeDasharray="5,5"
63
+ />
52
64
  ))}
53
65
 
66
+ {/* Bars & Budget Line */}
54
67
  {data.labels.map((label, i) => {
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;
68
+ const centerX = getGroupCenterX(i);
59
69
 
60
- const height2024 = chartHeight - (y(bar2024) - paddingTop);
61
- const height2025 = chartHeight - (y(bar2025) - paddingTop);
70
+ const val2024 = data.series[0]?.data[i] || 0;
71
+ const val2025 = data.series[1]?.data[i] || 0;
72
+ const budget = data.series[2]?.data[i] || 0;
73
+
74
+ const barHeight2024 = chartHeight - (y(val2024) - paddingTop);
75
+ const barHeight2025 = chartHeight - (y(val2025) - paddingTop);
62
76
 
63
77
  return (
64
78
  <React.Fragment key={i}>
65
- {/* 2024 Bar */}
79
+ {/* 2024 Bar - Orange */}
66
80
  <Rect
67
- x={xCenter - barWidth - 2}
68
- y={y(bar2024)}
81
+ x={centerX - barWidth - barGap / 2}
82
+ y={y(val2024)}
69
83
  width={barWidth}
70
- height={height2024}
84
+ height={barHeight2024}
71
85
  fill="#E07A3F"
72
86
  />
73
- {bar2024 > MAX * 0.1 && (
87
+ {val2024 > MAX * 0.08 && (
74
88
  <SvgText
75
- x={xCenter - barWidth - 2 + barWidth / 2}
76
- y={y(bar2024) + height2024 / 2 + 5}
77
- fontSize="9"
89
+ x={centerX - barWidth - barGap / 2 + barWidth / 2}
90
+ y={y(val2024) + barHeight2024 / 2 + 4}
91
+ fontSize="10"
78
92
  textAnchor="middle"
79
93
  fill="white"
80
94
  fontWeight="700"
81
95
  >
82
- {formatNumber(bar2024)}
96
+ ${formatNumber(val2024)}
83
97
  </SvgText>
84
98
  )}
85
99
 
86
- {/* 2025 Bar */}
100
+ {/* 2025 Bar - Blue */}
87
101
  <Rect
88
- x={xCenter + 2}
89
- y={y(bar2025)}
102
+ x={centerX + barGap / 2}
103
+ y={y(val2025)}
90
104
  width={barWidth}
91
- height={height2025}
105
+ height={barHeight2025}
92
106
  fill="#4E79A7"
93
107
  />
94
- {bar2025 > MAX * 0.1 && (
108
+ {val2025 > MAX * 0.08 && (
95
109
  <SvgText
96
- x={xCenter + 2 + barWidth / 2}
97
- y={y(bar2025) + height2025 / 2 + 5}
98
- fontSize="9"
110
+ x={centerX + barGap / 2 + barWidth / 2}
111
+ y={y(val2025) + barHeight2025 / 2 + 4}
112
+ fontSize="10"
99
113
  textAnchor="middle"
100
114
  fill="white"
101
115
  fontWeight="700"
102
116
  >
103
- {formatNumber(bar2025)}
117
+ ${formatNumber(val2025)}
104
118
  </SvgText>
105
119
  )}
106
120
 
107
- {/* Budget Line + Dot */}
108
- <Circle cx={xCenter} cy={y(budget)} r={5} fill="#8AB6E8" stroke="#fff" strokeWidth={2} />
121
+ {/* Budget Dotted Line */}
122
+ {i > 0 && (
123
+ <Line
124
+ x1={getGroupCenterX(i - 1)}
125
+ y1={y(data.series[2].data[i - 1])}
126
+ x2={centerX}
127
+ y2={y(budget)}
128
+ stroke="#8AB6E8"
129
+ strokeWidth={2}
130
+ strokeDasharray="6,4"
131
+ />
132
+ )}
133
+
134
+ {/* Budget Dot */}
135
+ <Circle
136
+ cx={centerX}
137
+ cy={y(budget)}
138
+ r={5}
139
+ fill="#8AB6E8"
140
+ stroke="#fff"
141
+ strokeWidth={2}
142
+ />
143
+
144
+ {/* Budget Value Above Dot */}
109
145
  <SvgText
110
- x={xCenter}
146
+ x={centerX}
111
147
  y={y(budget) - 10}
112
148
  fontSize="10"
113
149
  textAnchor="middle"
114
150
  fill="#333"
115
151
  fontWeight="700"
116
152
  >
117
- {formatNumber(budget)}
153
+ ${formatNumber(budget)}
118
154
  </SvgText>
119
155
 
120
- {/* X Label */}
156
+ {/* X Axis Label - Rotated */}
121
157
  <SvgText
122
- x={xCenter}
123
- y={height - 20}
158
+ x={centerX}
159
+ y={height - 25}
124
160
  fontSize="11"
125
161
  textAnchor="middle"
126
162
  fill="#555"
127
- transform={`rotate(-45 ${xCenter} ${height - 20})`}
163
+ transform={`rotate(-45 ${centerX} ${height - 25})`}
128
164
  >
129
165
  {label}
130
166
  </SvgText>
@@ -135,10 +171,11 @@ const SvgBarLineChartCompact = ({ data }) => {
135
171
  </ScrollView>
136
172
  </View>
137
173
 
174
+ {/* Legend */}
138
175
  <View style={styles.legend}>
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>
176
+ <Text style={{ color: '#E07A3F', marginRight: 20 }}>● 2024 Kar USD</Text>
177
+ <Text style={{ color: '#4E79A7', marginRight: 20 }}>● 2025 Kar USD</Text>
178
+ <Text style={{ color: '#8AB6E8' }}>● Bütçe Kar USD</Text>
142
179
  </View>
143
180
  </View>
144
181
  );
@@ -147,15 +184,25 @@ const SvgBarLineChartCompact = ({ data }) => {
147
184
  export default SvgBarLineChartCompact;
148
185
 
149
186
  const styles = StyleSheet.create({
150
- container: { marginVertical: 6 },
187
+ container: {
188
+ marginVertical: 12,
189
+ backgroundColor: '#fff',
190
+ borderRadius: 10,
191
+ padding: 10,
192
+ borderWidth: 1,
193
+ borderColor: '#ddd',
194
+ },
151
195
  title: {
152
196
  fontWeight: '700',
153
- marginBottom: 4,
154
- fontSize: 13,
197
+ fontSize: 14,
198
+ textAlign: 'center',
199
+ marginBottom: 8,
200
+ color: '#111',
155
201
  },
156
202
  legend: {
157
203
  flexDirection: 'row',
158
- marginTop: 4,
204
+ justifyContent: 'center',
205
+ marginTop: 10,
159
206
  flexWrap: 'wrap',
160
207
  },
161
- });
208
+ });
@@ -0,0 +1,144 @@
1
+ // src/components/SvgBarLineChartCompact3A.jsx
2
+ import React from 'react';
3
+ import { View, Text, ScrollView, StyleSheet } from 'react-native';
4
+ import Svg, { Rect, Line, Circle, Text as SvgText } from 'react-native-svg';
5
+ import { formatNumber } from '../utils/formatNumber';
6
+
7
+ const SvgBarLineChartCompact3A = ({ data }) => {
8
+ if (!data?.series || !data.labels?.length) return null;
9
+
10
+ const height = 280;
11
+ const paddingLeft = 80;
12
+ const paddingRight = 30;
13
+ const paddingTop = 30;
14
+ const paddingBottom = 70;
15
+
16
+ const chartHeight = height - paddingTop - paddingBottom;
17
+ const graphWidth = Math.max(500, data.labels.length * 110);
18
+
19
+ const MAX = 600000000; // 0.6bn
20
+ const barWidth = 22;
21
+ const barGap = 8;
22
+ const groupGap = 35;
23
+
24
+ const y = (v) => paddingTop + ((MAX - v) / MAX) * chartHeight;
25
+ const getCenterX = (i) => paddingLeft + i * (barWidth * 2 + barGap + groupGap) + (barWidth + barGap / 2);
26
+
27
+ return (
28
+ <View style={styles.container}>
29
+ <Text style={styles.title}>ULAŞTIRMA HİZMETLERİ CİRO GRAFİĞİ</Text>
30
+
31
+ <View style={{ flexDirection: 'row' }}>
32
+ <Svg width={paddingLeft} height={height}>
33
+ {[500000000, 0].map((v) => (
34
+ <SvgText
35
+ key={v}
36
+ x={paddingLeft - 10}
37
+ y={y(v) + 5}
38
+ fontSize="11"
39
+ textAnchor="end"
40
+ fill="#444"
41
+ >
42
+ {v === 0 ? '0.0bn' : '0.5bn'}
43
+ </SvgText>
44
+ ))}
45
+ </Svg>
46
+
47
+ <ScrollView horizontal showsHorizontalScrollIndicator={false}>
48
+ <Svg width={graphWidth} height={height}>
49
+ {[500000000, 0].map((v) => (
50
+ <Line key={v} x1={0} x2={graphWidth} y1={y(v)} y2={y(v)} stroke="#eee" strokeDasharray="5,5" />
51
+ ))}
52
+
53
+ {data.labels.map((label, i) => {
54
+ const centerX = getCenterX(i);
55
+ const val2024 = data.series[0]?.data[i] || 0;
56
+ const val2025 = data.series[1]?.data[i] || 0;
57
+ const budget = data.series[2]?.data[i] || 0;
58
+
59
+ const h2024 = chartHeight - (y(val2024) - paddingTop);
60
+ const h2025 = chartHeight - (y(val2025) - paddingTop);
61
+
62
+ return (
63
+ <React.Fragment key={i}>
64
+ {/* 2024 Bar */}
65
+ <Rect x={centerX - barWidth - barGap / 2} y={y(val2024)} width={barWidth} height={h2024} fill="#E07A3F" />
66
+ {val2024 > MAX * 0.1 && (
67
+ <SvgText
68
+ x={centerX - barWidth - barGap / 2 + barWidth / 2}
69
+ y={y(val2024) + h2024 / 2 + 5}
70
+ fontSize="10"
71
+ textAnchor="middle"
72
+ fill="white"
73
+ fontWeight="700"
74
+ >
75
+ {formatNumber(val2024)}M
76
+ </SvgText>
77
+ )}
78
+
79
+ {/* 2025 Bar */}
80
+ <Rect x={centerX + barGap / 2} y={y(val2025)} width={barWidth} height={h2025} fill="#4E79A7" />
81
+ {val2025 > MAX * 0.1 && (
82
+ <SvgText
83
+ x={centerX + barGap / 2 + barWidth / 2}
84
+ y={y(val2025) + h2025 / 2 + 5}
85
+ fontSize="10"
86
+ textAnchor="middle"
87
+ fill="white"
88
+ fontWeight="700"
89
+ >
90
+ {formatNumber(val2025)}M
91
+ </SvgText>
92
+ )}
93
+
94
+ {/* Budget Dotted Line & Dot */}
95
+ {i > 0 && (
96
+ <Line
97
+ x1={getCenterX(i - 1)}
98
+ y1={y(data.series[2].data[i - 1])}
99
+ x2={centerX}
100
+ y2={y(budget)}
101
+ stroke="#8AB6E8"
102
+ strokeWidth={2}
103
+ strokeDasharray="6,4"
104
+ />
105
+ )}
106
+ <Circle cx={centerX} cy={y(budget)} r={6} fill="#8AB6E8" stroke="#fff" strokeWidth={2} />
107
+ <SvgText x={centerX} y={y(budget) - 12} fontSize="11" textAnchor="middle" fill="#333" fontWeight="700">
108
+ {formatNumber(budget)}M
109
+ </SvgText>
110
+
111
+ {/* X Label */}
112
+ <SvgText
113
+ x={centerX}
114
+ y={height - 30}
115
+ fontSize="11"
116
+ textAnchor="middle"
117
+ fill="#555"
118
+ transform={`rotate(-45 ${centerX} ${height - 30})`}
119
+ >
120
+ {label}
121
+ </SvgText>
122
+ </React.Fragment>
123
+ );
124
+ })}
125
+ </Svg>
126
+ </ScrollView>
127
+ </View>
128
+
129
+ <View style={styles.legend}>
130
+ <Text style={{ color: '#E07A3F', marginRight: 20 }}>● 2024 Gelir Tutar (TL)</Text>
131
+ <Text style={{ color: '#4E79A7', marginRight: 20 }}>● 2025 Gelir Tutar (TL)</Text>
132
+ <Text style={{ color: '#8AB6E8' }}>● 2025 Bütçe Gelir (TL)</Text>
133
+ </View>
134
+ </View>
135
+ );
136
+ };
137
+
138
+ const styles = StyleSheet.create({
139
+ container: { padding: 10 },
140
+ title: { fontWeight: '700', fontSize: 14, textAlign: 'center', marginBottom: 10 },
141
+ legend: { flexDirection: 'row', justifyContent: 'center', marginTop: 12, flexWrap: 'wrap' },
142
+ });
143
+
144
+ export default SvgBarLineChartCompact3A;
@@ -0,0 +1,115 @@
1
+ // src/components/SvgLineChartCompact3A.jsx
2
+ import React from 'react';
3
+ import { View, Text, ScrollView, StyleSheet } from 'react-native';
4
+ import Svg, { Line, Circle, Text as SvgText } from 'react-native-svg';
5
+ import { formatNumber } from '../utils/formatNumber';
6
+
7
+ const SvgLineChartCompact3A = ({ data }) => {
8
+ if (!data?.series || !data.labels?.length) return null;
9
+
10
+ const height = 260;
11
+ const paddingLeft = 70;
12
+ const paddingRight = 30;
13
+ const paddingTop = 30;
14
+ const paddingBottom = 60;
15
+
16
+ const chartHeight = height - paddingTop - paddingBottom;
17
+ const graphWidth = Math.max(500, data.labels.length * 100);
18
+
19
+ const MIN = 35000;
20
+ const MAX = 55000;
21
+
22
+ const y = (v) => paddingTop + ((MAX - v) / (MAX - MIN)) * chartHeight;
23
+
24
+ const totalPoints = data.labels.length;
25
+ const xStep = totalPoints === 1 ? graphWidth / 2 : (graphWidth - paddingLeft - paddingRight) / (totalPoints - 1);
26
+
27
+ const colors = ['#E07A3F', '#4E79A7']; // Orange 2024, Blue 2025
28
+
29
+ return (
30
+ <View style={styles.container}>
31
+ <Text style={styles.title}>ULAŞTIRMA HİZMETLERİ YÜK ADET GRAFİĞİ</Text>
32
+
33
+ <View style={{ flexDirection: 'row' }}>
34
+ <Svg width={paddingLeft} height={height}>
35
+ {[50000, 40000].map((v) => (
36
+ <SvgText
37
+ key={v}
38
+ x={paddingLeft - 10}
39
+ y={y(v) + 5}
40
+ fontSize="11"
41
+ textAnchor="end"
42
+ fill="#444"
43
+ >
44
+ {v / 1000}K
45
+ </SvgText>
46
+ ))}
47
+ </Svg>
48
+
49
+ <ScrollView horizontal showsHorizontalScrollIndicator={false}>
50
+ <Svg width={graphWidth} height={height}>
51
+ {[50000, 40000].map((v) => (
52
+ <Line key={v} x1={0} x2={graphWidth} y1={y(v)} y2={y(v)} stroke="#eee" strokeDasharray="5,5" />
53
+ ))}
54
+
55
+ {data.series.map((series, si) =>
56
+ series.data.map((val, i) => {
57
+ const x = paddingLeft + i * (totalPoints === 1 ? 0 : xStep) + (totalPoints === 1 ? graphWidth / 2 - paddingLeft : 0);
58
+
59
+ return (
60
+ <React.Fragment key={`${si}-${i}`}>
61
+ {i > 0 && (
62
+ <Line
63
+ x1={paddingLeft + (i - 1) * xStep + (totalPoints === 1 ? graphWidth / 2 - paddingLeft : 0)}
64
+ y1={y(series.data[i - 1])}
65
+ x2={x}
66
+ y2={y(val)}
67
+ stroke={colors[si]}
68
+ strokeWidth={3}
69
+ strokeDasharray={si === 0 ? "6,4" : "0"}
70
+ />
71
+ )}
72
+ <Circle cx={x} cy={y(val)} r={6} fill={colors[si]} stroke="#fff" strokeWidth={2} />
73
+ <SvgText x={x} y={y(val) - 12} fontSize="11" textAnchor="middle" fill="#333" fontWeight="700">
74
+ {formatNumber(val)}
75
+ </SvgText>
76
+ </React.Fragment>
77
+ );
78
+ })
79
+ )}
80
+
81
+ {data.labels.map((label, i) => {
82
+ const x = paddingLeft + i * xStep + (totalPoints === 1 ? graphWidth / 2 - paddingLeft : 0);
83
+ return (
84
+ <SvgText
85
+ key={i}
86
+ x={x}
87
+ y={height - 25}
88
+ fontSize="11"
89
+ textAnchor="middle"
90
+ fill="#555"
91
+ transform={`rotate(-45 ${x} ${height - 25})`}
92
+ >
93
+ {label}
94
+ </SvgText>
95
+ );
96
+ })}
97
+ </Svg>
98
+ </ScrollView>
99
+ </View>
100
+
101
+ <View style={styles.legend}>
102
+ <Text style={{ color: '#E07A3F', marginRight: 20 }}>● 2024 Yük Adet</Text>
103
+ <Text style={{ color: '#4E79A7' }}>● 2025 Yük Adet</Text>
104
+ </View>
105
+ </View>
106
+ );
107
+ };
108
+
109
+ const styles = StyleSheet.create({
110
+ container: { padding: 10 },
111
+ title: { fontWeight: '700', fontSize: 14, textAlign: 'center', marginBottom: 10 },
112
+ legend: { flexDirection: 'row', justifyContent: 'center', marginTop: 12 },
113
+ });
114
+
115
+ export default SvgLineChartCompact3A;
package/src/index.jsx CHANGED
@@ -7,6 +7,7 @@ import Report2Screen from './screens/Report2Screen';
7
7
  import Report3Screen from './screens/Report3Screen';
8
8
  import Report1AScreen from './screens/Report1AScreen';
9
9
  import Report2AScreen from './screens/Report2AScreen';
10
+ import Report3AScreen from './screens/Report3AScreen';
10
11
 
11
12
  const AnalyticsReports = ({ config, onExit }) => {
12
13
  const [active, setActive] = useState(null);
@@ -110,6 +111,14 @@ if (active === '2A') {
110
111
  );
111
112
  }
112
113
 
114
+ if (active === '3A') {
115
+ return (
116
+ <SafeScreen>
117
+ <Report3AScreen api={config.report3} token={config.token} onBack={() => setActive(null)} />
118
+ </SafeScreen>
119
+ );
120
+ }
121
+
113
122
  return null;
114
123
  };
115
124
 
@@ -1,5 +1,13 @@
1
+ // src/screens/Report2AScreen.jsx
1
2
  import React, { useEffect, useState } from 'react';
2
- import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
3
+ import {
4
+ ScrollView,
5
+ Text,
6
+ TouchableOpacity,
7
+ View,
8
+ ActivityIndicator,
9
+ StyleSheet,
10
+ } from 'react-native';
3
11
  import { filterChartByMonths } from '../utils/filterChartByMonths';
4
12
  import { getDivisions, getTable, getLine, getBar } from '../api/report2Fetcher';
5
13
  import MonthFilterModal from '../components/MonthFilterModal';
@@ -20,33 +28,92 @@ const Report2AScreen = ({ api, token, onBack }) => {
20
28
  const [table, setTable] = useState(null);
21
29
  const [line, setLine] = useState(null);
22
30
  const [bar, setBar] = useState(null);
31
+ const [loading, setLoading] = useState(true);
32
+ const [error, setError] = useState(null);
23
33
 
34
+ // Fetch divisions
24
35
  useEffect(() => {
25
- getDivisions(api.divisions, token).then(d => {
26
- setDivisions(d);
27
- if (d.length > 0) setDivision(d[0]?.code);
28
- });
29
- }, [api.divisions, token]);
30
-
36
+ if (!api?.divisions || !token) return;
37
+
38
+ getDivisions(api.divisions, token)
39
+ .then((d) => {
40
+ setDivisions(d);
41
+ if (d.length > 0) {
42
+ setDivision(d[0].code);
43
+ }
44
+ })
45
+ .catch((err) => {
46
+ console.error('Failed to load divisions:', err);
47
+ setError('Failed to load divisions');
48
+ setLoading(false);
49
+ });
50
+ }, [api?.divisions, token]);
51
+
52
+ // Fetch data when division changes
31
53
  useEffect(() => {
32
- if (!division) return;
54
+ if (!division || !api || !token) return;
55
+
56
+ setLoading(true);
57
+ setError(null);
33
58
 
34
59
  Promise.all([
35
- getTable(api.table, division, token),
36
- getLine(api.line, division, token),
37
- getBar(api.bar, division, token),
38
- ]).then(([t, l, b]) => {
39
- setTable(t);
40
- setLine(l);
41
- setBar(b);
42
- setMonths(l.labels);
43
- setSelectedMonths(l.labels); // default: all selected
44
- });
60
+ getTable(api.table, division, token).catch(() => null),
61
+ getLine(api.line, division, token).catch(() => null),
62
+ getBar(api.bar, division, token).catch(() => null),
63
+ ])
64
+ .then(([t, l, b]) => {
65
+ if (!t || !l || !b) {
66
+ setError('Failed to load report data');
67
+ return;
68
+ }
69
+
70
+ setTable(t);
71
+ setLine(l);
72
+ setBar(b);
73
+ setMonths(l.labels || []);
74
+ setSelectedMonths(l.labels || []);
75
+ })
76
+ .catch((err) => {
77
+ console.error('Data fetch error:', err);
78
+ setError('Network error or data unavailable');
79
+ })
80
+ .finally(() => {
81
+ setLoading(false);
82
+ });
45
83
  }, [division, api, token]);
46
84
 
47
- if (!table || !line || !bar || !division) return null;
85
+ // Loading State
86
+ if (loading) {
87
+ return (
88
+ <View style={styles.center}>
89
+ <ActivityIndicator size="large" color="#1e88e5" />
90
+ <Text style={styles.loadingText}>Loading Report 2A...</Text>
91
+ </View>
92
+ );
93
+ }
94
+
95
+ // Error State
96
+ if (error) {
97
+ return (
98
+ <View style={styles.center}>
99
+ <Text style={styles.errorText}>{error}</Text>
100
+ <TouchableOpacity onPress={onBack}>
101
+ <Text style={styles.backLink}>← Go Back</Text>
102
+ </TouchableOpacity>
103
+ </View>
104
+ );
105
+ }
106
+
107
+ // Safety check
108
+ if (!table || !line || !bar || !division) {
109
+ return (
110
+ <View style={styles.center}>
111
+ <Text>No data available</Text>
112
+ </View>
113
+ );
114
+ }
48
115
 
49
- const filteredRows = table.rows.filter(r =>
116
+ const filteredRows = table.rows.filter((r) =>
50
117
  selectedMonths.includes(r.monthLabel)
51
118
  );
52
119
 
@@ -54,36 +121,42 @@ const Report2AScreen = ({ api, token, onBack }) => {
54
121
  const filteredBar = filterChartByMonths(bar, selectedMonths);
55
122
 
56
123
  return (
57
- <ScrollView style={{ padding: 12 }}>
58
- <Text onPress={onBack} style={{ fontSize: 18, marginBottom: 12 }}>
124
+ <ScrollView style={styles.container}>
125
+ {/* Header */}
126
+ <Text onPress={onBack} style={styles.backButton}>
59
127
  ‹ Back
60
128
  </Text>
61
129
 
62
- {/* FILTER BUTTONS */}
63
- <View style={{ flexDirection: 'row', marginVertical: 10, gap: 16 }}>
130
+ {/* Filters */}
131
+ <View style={styles.filterRow}>
64
132
  <TouchableOpacity onPress={() => setMonthsModal(true)}>
65
- <Text style={{ fontSize: 16, color: '#1e88e5' }}>Months ⛃</Text>
133
+ <Text style={styles.filterText}>Months ⛃</Text>
66
134
  </TouchableOpacity>
67
135
 
68
- <TouchableOpacity onPress={() => setDivisionModal(true)}>
69
- <Text style={{ fontSize: 16, color: '#1e88e5' }}>Divisions ⛃</Text>
136
+ <TouchableOpacity
137
+ onPress={() => setDivisionModal(true)}
138
+ style={{ marginLeft: 20 }}
139
+ >
140
+ <Text style={styles.filterText}>
141
+ Division: {divisions.find((d) => d.code === division)?.displayName || division}
142
+ </Text>
70
143
  </TouchableOpacity>
71
144
  </View>
72
145
 
73
- {/* TABLE */}
146
+ {/* Table */}
74
147
  <FrozenTableReport2A rows={filteredRows} />
75
148
 
76
- {/* LINE CHART */}
77
- <View style={{ borderWidth: 1, borderColor: '#ddd', borderRadius: 8, marginVertical: 12 }}>
149
+ {/* Line Chart */}
150
+ <View style={styles.chartContainer}>
78
151
  <SvgLineChartCompact data={filteredLine} />
79
152
  </View>
80
153
 
81
- {/* BAR + LINE CHART */}
82
- <View style={{ borderWidth: 1, borderColor: '#ddd', borderRadius: 8, marginVertical: 12 }}>
154
+ {/* Bar + Budget Chart */}
155
+ <View style={styles.chartContainer}>
83
156
  <SvgBarLineChartCompact data={filteredBar} />
84
157
  </View>
85
158
 
86
- {/* MODALS */}
159
+ {/* Modals */}
87
160
  <MonthFilterModal
88
161
  visible={monthsModal}
89
162
  months={months}
@@ -103,4 +176,54 @@ const Report2AScreen = ({ api, token, onBack }) => {
103
176
  );
104
177
  };
105
178
 
179
+ const styles = StyleSheet.create({
180
+ container: { padding: 12, backgroundColor: '#f8f9fa' },
181
+ center: {
182
+ flex: 1,
183
+ justifyContent: 'center',
184
+ alignItems: 'center',
185
+ padding: 20,
186
+ },
187
+ loadingText: {
188
+ marginTop: 12,
189
+ fontSize: 16,
190
+ color: '#555',
191
+ },
192
+ errorText: {
193
+ fontSize: 16,
194
+ color: '#d32f2f',
195
+ textAlign: 'center',
196
+ marginBottom: 20,
197
+ },
198
+ backLink: {
199
+ fontSize: 18,
200
+ color: '#1e88e5',
201
+ fontWeight: '600',
202
+ },
203
+ backButton: {
204
+ fontSize: 20,
205
+ color: '#1e88e5',
206
+ marginBottom: 16,
207
+ fontWeight: '600',
208
+ },
209
+ filterRow: {
210
+ flexDirection: 'row',
211
+ alignItems: 'center',
212
+ marginVertical: 12,
213
+ },
214
+ filterText: {
215
+ fontSize: 16,
216
+ color: '#1e88e5',
217
+ fontWeight: '600',
218
+ },
219
+ chartContainer: {
220
+ borderWidth: 1,
221
+ borderColor: '#ddd',
222
+ borderRadius: 10,
223
+ overflow: 'hidden',
224
+ marginVertical: 12,
225
+ backgroundColor: '#fff',
226
+ },
227
+ });
228
+
106
229
  export default Report2AScreen;
@@ -0,0 +1,167 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import {
3
+ ScrollView,
4
+ Text,
5
+ TouchableOpacity,
6
+ View,
7
+ ActivityIndicator,
8
+ StyleSheet,
9
+ } from 'react-native';
10
+ import {
11
+ fetchReport3Table,
12
+ fetchReport3Line,
13
+ fetchReport3Bar,
14
+ } from '../api/report3Fetcher';
15
+ import MonthFilterModal from '../components/MonthFilterModal';
16
+ import FrozenTableReport3A from '../components/FrozenTableReport3A';
17
+ import SvgLineChartCompact3A from '../components/SvgLineChartCompact3A';
18
+ import SvgBarLineChartCompact3A from '../components/SvgBarLineChartCompact3A';
19
+
20
+ const Report3AScreen = ({ api, token, onBack }) => {
21
+ const [monthsModal, setMonthsModal] = useState(false);
22
+ const [months, setMonths] = useState([]);
23
+ const [selectedMonths, setSelectedMonths] = useState([]);
24
+ const [table, setTable] = useState(null);
25
+ const [line, setLine] = useState(null);
26
+ const [bar, setBar] = useState(null);
27
+ const [loading, setLoading] = useState(true);
28
+ const [error, setError] = useState(null);
29
+
30
+ useEffect(() => {
31
+ if (!api || !token) return;
32
+
33
+ setLoading(true);
34
+ setError(null);
35
+
36
+ Promise.all([
37
+ fetchReport3Table(api.table, token).catch(() => null),
38
+ fetchReport3Line(api.line, token).catch(() => null),
39
+ fetchReport3Bar(api.bar, token).catch(() => null),
40
+ ])
41
+ .then(([t, l, b]) => {
42
+ if (!t || !l || !b) throw new Error('Failed to load data');
43
+
44
+ setTable(t);
45
+ setLine(l);
46
+ setBar(b);
47
+
48
+ const monthLabels = t.rows
49
+ .filter(r => r.month !== 99)
50
+ .map(r => r.monthLabel);
51
+
52
+ setMonths(monthLabels);
53
+ setSelectedMonths(monthLabels); // default: all selected
54
+ })
55
+ .catch((err) => {
56
+ console.error(err);
57
+ setError('Failed to load Report 3A data');
58
+ })
59
+ .finally(() => setLoading(false));
60
+ }, [api, token]);
61
+
62
+ if (loading) {
63
+ return (
64
+ <View style={styles.center}>
65
+ <ActivityIndicator size="large" color="#1e88e5" />
66
+ <Text style={styles.loadingText}>Loading Report 3A...</Text>
67
+ </View>
68
+ );
69
+ }
70
+
71
+ if (error) {
72
+ return (
73
+ <View style={styles.center}>
74
+ <Text style={styles.errorText}>{error}</Text>
75
+ <TouchableOpacity onPress={onBack}>
76
+ <Text style={styles.backLink}>← Go Back</Text>
77
+ </TouchableOpacity>
78
+ </View>
79
+ );
80
+ }
81
+
82
+ const filteredRows = table.rows.filter(r =>
83
+ selectedMonths.includes(r.monthLabel)
84
+ );
85
+
86
+ const filterChart = (chart) => {
87
+ if (selectedMonths.length === months.length) return chart;
88
+
89
+ const indices = chart.labels
90
+ .map((label, i) => selectedMonths.includes(label) ? i : -1)
91
+ .filter(i => i !== -1);
92
+
93
+ return {
94
+ ...chart,
95
+ labels: indices.map(i => chart.labels[i]),
96
+ series: chart.series.map(s => ({
97
+ ...s,
98
+ data: indices.map(i => s.data[i]),
99
+ })),
100
+ };
101
+ };
102
+
103
+ const filteredLine = filterChart(line);
104
+ const filteredBar = filterChart(bar);
105
+
106
+ return (
107
+ <ScrollView style={styles.container}>
108
+ <Text onPress={onBack} style={styles.backButton}>‹ Back</Text>
109
+
110
+ <Text style={styles.screenTitle}>Ulaştırma Hizmetleri Analizi (A)</Text>
111
+
112
+ {/* Month Filter */}
113
+ <TouchableOpacity
114
+ onPress={() => setMonthsModal(true)}
115
+ style={styles.filterButton}
116
+ >
117
+ <Text style={styles.filterText}>
118
+ Months ({selectedMonths.length}/{months.length}) ⛃
119
+ </Text>
120
+ </TouchableOpacity>
121
+
122
+ {/* Table */}
123
+ <FrozenTableReport3A rows={filteredRows} />
124
+
125
+ {/* Line Chart - Yük Adet */}
126
+ <View style={styles.chartWrapper}>
127
+ <SvgLineChartCompact3A data={filteredLine} />
128
+ </View>
129
+
130
+ {/* Bar + Budget Chart - Ciro */}
131
+ <View style={styles.chartWrapper}>
132
+ <SvgBarLineChartCompact3A data={filteredBar} />
133
+ </View>
134
+
135
+ {/* Modal */}
136
+ <MonthFilterModal
137
+ visible={monthsModal}
138
+ months={months}
139
+ selected={selectedMonths}
140
+ onApply={setSelectedMonths}
141
+ onClose={() => setMonthsModal(false)}
142
+ />
143
+ </ScrollView>
144
+ );
145
+ };
146
+
147
+ const styles = StyleSheet.create({
148
+ container: { padding: 12, backgroundColor: '#f8f9fa' },
149
+ center: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 },
150
+ loadingText: { marginTop: 12, fontSize: 16, color: '#555' },
151
+ errorText: { fontSize: 16, color: '#d32f2f', textAlign: 'center', marginBottom: 20 },
152
+ backLink: { fontSize: 18, color: '#1e88e5', fontWeight: '600' },
153
+ backButton: { fontSize: 20, color: '#1e88e5', marginBottom: 16, fontWeight: '600' },
154
+ screenTitle: { fontSize: 18, fontWeight: '700', textAlign: 'center', marginVertical: 12 },
155
+ filterButton: { alignSelf: 'flex-start', marginVertical: 12 },
156
+ filterText: { fontSize: 16, color: '#1e88e5', fontWeight: '600' },
157
+ chartWrapper: {
158
+ borderWidth: 1,
159
+ borderColor: '#ddd',
160
+ borderRadius: 10,
161
+ overflow: 'hidden',
162
+ marginVertical: 12,
163
+ backgroundColor: '#fff',
164
+ },
165
+ });
166
+
167
+ export default Report3AScreen;