@dhiraj0720/report1chart 2.6.4 → 2.6.6

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.6.4",
3
+ "version": "2.6.6",
4
4
  "main": "src/index.jsx",
5
5
  "scripts": {
6
6
  "test": "echo 'No tests'"
@@ -1,13 +1,16 @@
1
- // src/screens/Report2AScreen.jsx
2
1
  import React, { useEffect, useState } from 'react';
3
2
  import {
4
3
  ScrollView,
5
4
  Text,
6
5
  TouchableOpacity,
7
6
  View,
8
- ActivityIndicator,
9
7
  StyleSheet,
8
+ Modal,
9
+ Dimensions,
10
+ ActivityIndicator,
10
11
  } from 'react-native';
12
+ import { PinchGestureHandler, State } from 'react-native-gesture-handler';
13
+ import Svg from 'react-native-svg'; // if not imported globally
11
14
  import { filterChartByMonths } from '../utils/filterChartByMonths';
12
15
  import { getDivisions, getTable, getLine, getBar } from '../api/report2Fetcher';
13
16
  import MonthFilterModal from '../components/MonthFilterModal';
@@ -15,6 +18,20 @@ import DivisionFilterModal from '../components/DivisionFilterModal';
15
18
  import FrozenTableReport2A from '../components/FrozenTableReport2A';
16
19
  import SvgLineChartCompact from '../components/SvgLineChartCompact';
17
20
  import SvgBarLineChartCompact from '../components/SvgBarLineChartCompact';
21
+ import { FilterIcon, FullScreenIcon } from './icons'; // or inline
22
+
23
+ // Proper SVG Funnel Icon Component (clean black filter icon)
24
+ const FunnelIcon = () => (
25
+ <View style={styles.funnelIcon}>
26
+ <View style={styles.funnelTop} />
27
+ <View style={styles.funnelBody} />
28
+ <View style={styles.funnelLines}>
29
+ <View style={styles.line1} />
30
+ <View style={styles.line2} />
31
+ <View style={styles.line3} />
32
+ </View>
33
+ </View>
34
+ );
18
35
 
19
36
  const Report2AScreen = ({ api, token, onBack }) => {
20
37
  const [monthsModal, setMonthsModal] = useState(false);
@@ -24,6 +41,8 @@ const Report2AScreen = ({ api, token, onBack }) => {
24
41
  const [selectedMonths, setSelectedMonths] = useState([]);
25
42
  const [divisions, setDivisions] = useState([]);
26
43
  const [division, setDivision] = useState(null);
44
+ const [fullscreen, setFullscreen] = useState(false);
45
+ const [zoomScale, setZoomScale] = useState(1);
27
46
 
28
47
  const [table, setTable] = useState(null);
29
48
  const [line, setLine] = useState(null);
@@ -31,25 +50,19 @@ const Report2AScreen = ({ api, token, onBack }) => {
31
50
  const [loading, setLoading] = useState(true);
32
51
  const [error, setError] = useState(null);
33
52
 
34
- // Fetch divisions
53
+
35
54
  useEffect(() => {
36
55
  if (!api?.divisions || !token) return;
37
56
 
38
57
  getDivisions(api.divisions, token)
39
58
  .then((d) => {
40
59
  setDivisions(d);
41
- if (d.length > 0) {
42
- setDivision(d[0].code);
43
- }
60
+ if (d.length > 0) setDivision(d[0].code);
44
61
  })
45
- .catch((err) => {
46
- console.error('Failed to load divisions:', err);
47
- setError('Failed to load divisions');
48
- setLoading(false);
49
- });
62
+ .catch(() => setError('Failed to load divisions'))
63
+ .finally(() => setLoading(false));
50
64
  }, [api?.divisions, token]);
51
65
 
52
- // Fetch data when division changes
53
66
  useEffect(() => {
54
67
  if (!division || !api || !token) return;
55
68
 
@@ -57,106 +70,126 @@ const Report2AScreen = ({ api, token, onBack }) => {
57
70
  setError(null);
58
71
 
59
72
  Promise.all([
60
- getTable(api.table, division, token).catch(() => null),
61
- getLine(api.line, division, token).catch(() => null),
62
- getBar(api.bar, division, token).catch(() => null),
73
+ getTable(api.table, division, token),
74
+ getLine(api.line, division, token),
75
+ getBar(api.bar, division, token),
63
76
  ])
64
77
  .then(([t, l, b]) => {
65
- if (!t || !l || !b) {
66
- setError('Failed to load report data');
67
- return;
68
- }
69
-
70
78
  setTable(t);
71
79
  setLine(l);
72
80
  setBar(b);
73
81
  setMonths(l.labels || []);
74
82
  setSelectedMonths(l.labels || []);
75
83
  })
76
- .catch((err) => {
77
- console.error('Data fetch error:', err);
78
- setError('Network error or data unavailable');
79
- })
80
- .finally(() => {
81
- setLoading(false);
82
- });
84
+ .catch(() => setError('Failed to load data'))
85
+ .finally(() => setLoading(false));
83
86
  }, [division, api, token]);
84
87
 
85
- // Loading State
86
88
  if (loading) {
87
89
  return (
88
90
  <View style={styles.center}>
89
- <ActivityIndicator size="large" color="#1e88e5" />
90
- <Text style={styles.loadingText}>Loading Report 2A...</Text>
91
+ <ActivityIndicator size="large" color="#000" />
92
+ <Text style={styles.loadingText}>Loading...</Text>
91
93
  </View>
92
94
  );
93
95
  }
94
96
 
95
- // Error State
96
97
  if (error) {
97
98
  return (
98
99
  <View style={styles.center}>
99
100
  <Text style={styles.errorText}>{error}</Text>
100
101
  <TouchableOpacity onPress={onBack}>
101
- <Text style={styles.backLink}>← Go Back</Text>
102
+ <Text style={styles.backLink}>‹ Back to Reports</Text>
102
103
  </TouchableOpacity>
103
104
  </View>
104
105
  );
105
106
  }
106
107
 
107
- // Safety check
108
108
  if (!table || !line || !bar || !division) {
109
- return (
110
- <View style={styles.center}>
111
- <Text>No data available</Text>
112
- </View>
113
- );
109
+ return <View style={styles.center}><Text>No data available</Text></View>;
114
110
  }
115
111
 
116
- const filteredRows = table.rows.filter((r) =>
117
- selectedMonths.includes(r.monthLabel)
118
- );
119
-
112
+ const currentDivisionName = divisions.find(d => d.code === division)?.displayName || 'Division';
113
+ const filteredRows = table.rows.filter(r => selectedMonths.includes(r.monthLabel));
120
114
  const filteredLine = filterChartByMonths(line, selectedMonths);
121
115
  const filteredBar = filterChartByMonths(bar, selectedMonths);
122
116
 
123
117
  return (
124
- <ScrollView style={styles.container}>
125
- {/* Header */}
126
- <Text onPress={onBack} style={styles.backButton}>
127
- Back
128
- </Text>
129
-
130
- {/* Filters */}
131
- <View style={styles.filterRow}>
132
- <TouchableOpacity onPress={() => setMonthsModal(true)}>
133
- <Text style={styles.filterText}>Months ⛃</Text>
118
+ <View style={styles.container}>
119
+ {/* HEADER WITH BACK ICON + HEADING */}
120
+ <View style={styles.header}>
121
+ <TouchableOpacity onPress={onBack} style={styles.backButton}>
122
+ <Text style={styles.backIcon}>‹</Text>
134
123
  </TouchableOpacity>
135
124
 
125
+ <Text style={styles.headerTitle}>
126
+ {currentDivisionName} Transportation
127
+ </Text>
128
+ </View>
129
+
130
+ {/* FILTERS - BLACK TEXT + PROPER FUNNEL ICON */}
131
+ <View style={styles.filterBar}>
132
+ <TouchableOpacity onPress={() => setMonthsModal(true)} style={styles.filterItem}>
133
+ <FilterIcon />
134
+ <Text style={styles.filterText}>Months ({selectedMonths.length}/{months.length})</Text>
135
+ </TouchableOpacity>
136
+
137
+ <TouchableOpacity onPress={() => setDivisionModal(true)} style={styles.filterItem}>
138
+ <FilterIcon />
139
+ <Text style={styles.filterText}>{currentDivisionName}</Text>
140
+ </TouchableOpacity>
141
+ </View>
142
+
143
+ <ScrollView style={styles.content}>
144
+ {/* FULL SCREEN ICON ABOVE TABLE */}
136
145
  <TouchableOpacity
137
- onPress={() => setDivisionModal(true)}
138
- style={{ marginLeft: 20 }}
146
+ onPress={() => setFullscreen(true)}
147
+ style={styles.fullScreenBtn}
139
148
  >
140
- <Text style={styles.filterText}>
141
- Division: {divisions.find((d) => d.code === division)?.displayName || division}
142
- </Text>
149
+ <FullScreenIcon size={28} />
150
+ <Text style={styles.fullScreenText}>Full Screen View</Text>
143
151
  </TouchableOpacity>
144
- </View>
145
152
 
146
- {/* Table */}
147
- <FrozenTableReport2A rows={filteredRows} />
153
+ <FrozenTableReport2A rows={filteredRows} />
148
154
 
149
- {/* Line Chart */}
150
- <View style={styles.chartContainer}>
151
- <SvgLineChartCompact data={filteredLine} />
152
- </View>
155
+ <View style={styles.chartContainer}>
156
+ <SvgLineChartCompact data={filteredLine} />
157
+ </View>
153
158
 
154
- {/* Bar + Budget Chart */}
155
- <View style={styles.chartContainer}>
156
- <SvgBarLineChartCompact data={filteredBar} />
157
- </View>
159
+ <View style={styles.chartContainer}>
160
+ <SvgBarLineChartCompact data={filteredBar} />
161
+ </View>
162
+ </ScrollView>
163
+
164
+ {/* MODALS */}
165
+
166
+ {/* FULL SCREEN MODAL */}
167
+ <Modal visible={fullscreen} supportedOrientations={['portrait', 'landscape']}>
168
+ <View style={styles.fullModal}>
169
+ <View style={styles.fullHeader}>
170
+ <TouchableOpacity onPress={() => setFullscreen(false)}>
171
+ <Text style={styles.closeFull}>✕</Text>
172
+ </TouchableOpacity>
173
+ <Text style={styles.fullTitle}>Rotate device to landscape for best view</Text>
174
+ </View>
175
+
176
+ <PinchGestureHandler
177
+ onGestureEvent={(e) => setZoomScale(e.nativeEvent.scale)}
178
+ onHandlerStateChange={(e) => {
179
+ if (e.nativeEvent.state === State.END) {
180
+ setZoomScale(Math.max(0.8, Math.min(e.nativeEvent.scale, 3)));
181
+ }
182
+ }}
183
+ >
184
+ <View style={styles.zoomView}>
185
+ <View style={{ transform: [{ scale: zoomScale }] }}>
186
+ <FrozenTableReport2A rows={table.rows} isFullscreen /> {/* all rows */}
187
+ </View>
188
+ </View>
189
+ </PinchGestureHandler>
190
+ </View>
191
+ </Modal>
158
192
 
159
- {/* Modals */}
160
193
  <MonthFilterModal
161
194
  visible={monthsModal}
162
195
  months={months}
@@ -172,50 +205,91 @@ const Report2AScreen = ({ api, token, onBack }) => {
172
205
  onSelect={setDivision}
173
206
  onClose={() => setDivisionModal(false)}
174
207
  />
175
- </ScrollView>
208
+ </View>
176
209
  );
177
210
  };
178
211
 
179
212
  const styles = StyleSheet.create({
180
- container: { padding: 12, backgroundColor: '#f8f9fa' },
181
- center: {
182
- flex: 1,
183
- justifyContent: 'center',
213
+ container: { flex: 1, backgroundColor: '#f8f9fa' },
214
+ center: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 },
215
+ loadingText: { marginTop: 12, fontSize: 16, color: '#000' },
216
+ errorText: { fontSize: 16, color: '#d32f2f', textAlign: 'center', marginBottom: 20 },
217
+ backLink: { fontSize: 18, color: '#000', fontWeight: '600' },
218
+
219
+ header: {
220
+ flexDirection: 'row',
184
221
  alignItems: 'center',
185
- padding: 20,
186
- },
187
- loadingText: {
188
- marginTop: 12,
189
- fontSize: 16,
190
- color: '#555',
222
+ paddingHorizontal: 16,
223
+ paddingVertical: 18,
224
+ backgroundColor: '#fff',
225
+ borderBottomWidth: 1,
226
+ borderColor: '#eee',
191
227
  },
192
- errorText: {
193
- fontSize: 16,
194
- color: '#d32f2f',
228
+ backButton: { paddingRight: 12 },
229
+ backIcon: { fontSize: 28, color: '#000', fontWeight: '300' },
230
+ headerTitle: {
231
+ fontSize: 19,
232
+ fontWeight: '700',
233
+ color: '#000',
234
+ flex: 1,
195
235
  textAlign: 'center',
196
- marginBottom: 20,
197
236
  },
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',
237
+
238
+ filterBar: {
239
+ flexDirection: 'row',
240
+ justifyContent: 'space-around',
241
+ paddingVertical: 14,
242
+ backgroundColor: '#fff',
243
+ borderBottomWidth: 1,
244
+ borderColor: '#eee',
208
245
  },
209
- filterRow: {
246
+ filterItem: {
210
247
  flexDirection: 'row',
211
248
  alignItems: 'center',
212
- marginVertical: 12,
249
+ paddingHorizontal: 16,
250
+ paddingVertical: 8,
213
251
  },
214
252
  filterText: {
215
253
  fontSize: 16,
216
- color: '#1e88e5',
254
+ color: '#000',
217
255
  fontWeight: '600',
256
+ marginLeft: 8,
218
257
  },
258
+
259
+ // Proper Funnel Icon (SVG-style with Views)
260
+ funnelIcon: {
261
+ width: 20,
262
+ height: 20,
263
+ justifyContent: 'center',
264
+ alignItems: 'center',
265
+ },
266
+ funnelTop: {
267
+ width: 16,
268
+ height: 4,
269
+ backgroundColor: '#000',
270
+ borderRadius: 2,
271
+ },
272
+ funnelBody: {
273
+ width: 10,
274
+ height: 10,
275
+ borderLeftWidth: 4,
276
+ borderRightWidth: 4,
277
+ borderBottomWidth: 8,
278
+ borderColor: 'transparent',
279
+ borderBottomColor: '#000',
280
+ marginTop: -2,
281
+ },
282
+ funnelLines: {
283
+ position: 'absolute',
284
+ top: 6,
285
+ left: 4,
286
+ right: 4,
287
+ },
288
+ line1: { height: 1, backgroundColor: '#000', marginVertical: 2 },
289
+ line2: { height: 1, backgroundColor: '#000', marginVertical: 2 },
290
+ line3: { height: 1, backgroundColor: '#000', marginVertical: 2 },
291
+
292
+ content: { flex: 1, padding: 12 },
219
293
  chartContainer: {
220
294
  borderWidth: 1,
221
295
  borderColor: '#ddd',
@@ -224,6 +298,20 @@ const styles = StyleSheet.create({
224
298
  marginVertical: 12,
225
299
  backgroundColor: '#fff',
226
300
  },
301
+ fullScreenBtn: {
302
+ flexDirection: 'row',
303
+ alignItems: 'center',
304
+ justifyContent: 'center',
305
+ padding: 12,
306
+ marginBottom: 12,
307
+ },
308
+ fullScreenText: { marginLeft: 8, fontSize: 16, color: '#000', fontWeight: '600' },
309
+
310
+ fullModal: { flex: 1, backgroundColor: '#fff' },
311
+ fullHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', padding: 16, backgroundColor: '#f8f9fa', position: 'relative' },
312
+ closeFull: { position: 'absolute', left: 16, fontSize: 28, color: '#000' },
313
+ fullTitle: { fontSize: 16, color: '#000' },
314
+ zoomView: { flex: 1, justifyContent: 'center', alignItems: 'center' },
227
315
  });
228
316
 
229
317
  export default Report2AScreen;
@@ -0,0 +1,34 @@
1
+ import Svg, { Path } from 'react-native-svg';
2
+
3
+ // Filter Funnel Icon (exact match to your image)
4
+ const FilterIcon = ({ size = 20, color = '#000' }) => (
5
+ <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
6
+ <Path
7
+ d="M20 4H4V6.5L12 14.5L20 6.5V4Z"
8
+ stroke={color}
9
+ strokeWidth={2}
10
+ strokeLinecap="round"
11
+ strokeLinejoin="round"
12
+ />
13
+ <Path
14
+ d="M8 11V19L16 19V11"
15
+ stroke={color}
16
+ strokeWidth={2}
17
+ strokeLinecap="round"
18
+ strokeLinejoin="round"
19
+ />
20
+ </Svg>
21
+ );
22
+
23
+ // Full Screen Expand Icon
24
+ const FullScreenIcon = ({ size = 24, color = '#000' }) => (
25
+ <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
26
+ <Path
27
+ d="M8 3H5C4.44772 3 4 3.44772 4 4V8M20 8V4C20 3.44772 19.5523 3 19 3H16M16 21H19C19.5523 21 20 20.5523 20 20V16M4 16V20C4 20.5523 4.44772 21 5 21H8"
28
+ stroke={color}
29
+ strokeWidth={2}
30
+ strokeLinecap="round"
31
+ strokeLinejoin="round"
32
+ />
33
+ </Svg>
34
+ );