@dhiraj0720/report1chart 2.6.6 → 2.6.7

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.6",
3
+ "version": "2.6.7",
4
4
  "main": "src/index.jsx",
5
5
  "scripts": {
6
6
  "test": "echo 'No tests'"
@@ -8,6 +8,7 @@
8
8
  "dependencies": {
9
9
  "axios": "^1.13.2",
10
10
  "react-native-gesture-handler": "^2.30.0",
11
+ "react-native-reanimated": "^4.2.1",
11
12
  "react-native-svg": "^15.15.1"
12
13
  },
13
14
  "peerDependencies": {
@@ -8,9 +8,9 @@ import {
8
8
  Modal,
9
9
  Dimensions,
10
10
  ActivityIndicator,
11
+ Alert,
11
12
  } from 'react-native';
12
- import { PinchGestureHandler, State } from 'react-native-gesture-handler';
13
- import Svg from 'react-native-svg'; // if not imported globally
13
+ import Svg, { Path } from 'react-native-svg';
14
14
  import { filterChartByMonths } from '../utils/filterChartByMonths';
15
15
  import { getDivisions, getTable, getLine, getBar } from '../api/report2Fetcher';
16
16
  import MonthFilterModal from '../components/MonthFilterModal';
@@ -18,57 +18,35 @@ import DivisionFilterModal from '../components/DivisionFilterModal';
18
18
  import FrozenTableReport2A from '../components/FrozenTableReport2A';
19
19
  import SvgLineChartCompact from '../components/SvgLineChartCompact';
20
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
- );
35
21
 
36
22
  const Report2AScreen = ({ api, token, onBack }) => {
37
23
  const [monthsModal, setMonthsModal] = useState(false);
38
24
  const [divisionModal, setDivisionModal] = useState(false);
25
+ const [fullscreen, setFullscreen] = useState(false);
26
+ const [zoomScale, setZoomScale] = useState(1);
39
27
 
40
28
  const [months, setMonths] = useState([]);
41
29
  const [selectedMonths, setSelectedMonths] = useState([]);
42
30
  const [divisions, setDivisions] = useState([]);
43
31
  const [division, setDivision] = useState(null);
44
- const [fullscreen, setFullscreen] = useState(false);
45
- const [zoomScale, setZoomScale] = useState(1);
46
32
 
47
33
  const [table, setTable] = useState(null);
48
34
  const [line, setLine] = useState(null);
49
35
  const [bar, setBar] = useState(null);
50
36
  const [loading, setLoading] = useState(true);
51
- const [error, setError] = useState(null);
52
37
 
53
-
54
38
  useEffect(() => {
55
- if (!api?.divisions || !token) return;
56
-
57
39
  getDivisions(api.divisions, token)
58
40
  .then((d) => {
59
41
  setDivisions(d);
60
42
  if (d.length > 0) setDivision(d[0].code);
61
43
  })
62
- .catch(() => setError('Failed to load divisions'))
63
- .finally(() => setLoading(false));
64
- }, [api?.divisions, token]);
44
+ .catch(() => Alert.alert('Error', 'Failed to load divisions'));
45
+ }, [api.divisions, token]);
65
46
 
66
47
  useEffect(() => {
67
- if (!division || !api || !token) return;
68
-
48
+ if (!division) return;
69
49
  setLoading(true);
70
- setError(null);
71
-
72
50
  Promise.all([
73
51
  getTable(api.table, division, token),
74
52
  getLine(api.line, division, token),
@@ -81,61 +59,78 @@ const Report2AScreen = ({ api, token, onBack }) => {
81
59
  setMonths(l.labels || []);
82
60
  setSelectedMonths(l.labels || []);
83
61
  })
84
- .catch(() => setError('Failed to load data'))
85
62
  .finally(() => setLoading(false));
86
- }, [division, api, token]);
63
+ }, [division]);
87
64
 
88
65
  if (loading) {
89
66
  return (
90
67
  <View style={styles.center}>
91
68
  <ActivityIndicator size="large" color="#000" />
92
- <Text style={styles.loadingText}>Loading...</Text>
93
- </View>
94
- );
95
- }
96
-
97
- if (error) {
98
- return (
99
- <View style={styles.center}>
100
- <Text style={styles.errorText}>{error}</Text>
101
- <TouchableOpacity onPress={onBack}>
102
- <Text style={styles.backLink}>‹ Back to Reports</Text>
103
- </TouchableOpacity>
104
69
  </View>
105
70
  );
106
71
  }
107
72
 
108
- if (!table || !line || !bar || !division) {
109
- return <View style={styles.center}><Text>No data available</Text></View>;
110
- }
73
+ const currentDivisionName = divisions.find(d => d.code === division)?.displayName || 'Division';
111
74
 
112
- const currentDivisionName = divisions.find(d => d.code === division)?.displayName || 'Division';
113
75
  const filteredRows = table.rows.filter(r => selectedMonths.includes(r.monthLabel));
114
76
  const filteredLine = filterChartByMonths(line, selectedMonths);
115
77
  const filteredBar = filterChartByMonths(bar, selectedMonths);
116
78
 
79
+ // Detect landscape
80
+ const { width, height } = Dimensions.get('window');
81
+ const isLandscape = width > height;
82
+
117
83
  return (
118
84
  <View style={styles.container}>
119
- {/* HEADER WITH BACK ICON + HEADING */}
85
+ {/* HEADER */}
120
86
  <View style={styles.header}>
121
- <TouchableOpacity onPress={onBack} style={styles.backButton}>
122
- <Text style={styles.backIcon}>‹</Text>
87
+ <TouchableOpacity onPress={onBack}>
88
+ <Text style={styles.backIcon}>←</Text>
123
89
  </TouchableOpacity>
124
-
125
- <Text style={styles.headerTitle}>
126
- {currentDivisionName} Transportation
127
- </Text>
90
+ <Text style={styles.headerTitle}>{currentDivisionName} Transportation</Text>
128
91
  </View>
129
92
 
130
- {/* FILTERS - BLACK TEXT + PROPER FUNNEL ICON */}
93
+ {/* FILTERS */}
131
94
  <View style={styles.filterBar}>
132
95
  <TouchableOpacity onPress={() => setMonthsModal(true)} style={styles.filterItem}>
133
- <FilterIcon />
96
+ {/* FILTER FUNNEL ICON */}
97
+ <Svg width={22} height={22} viewBox="0 0 24 24" fill="none">
98
+ <Path
99
+ d="M20 4H4V6.5L12 14.5L20 6.5V4Z"
100
+ stroke="#000"
101
+ strokeWidth={2}
102
+ strokeLinecap="round"
103
+ strokeLinejoin="round"
104
+ />
105
+ <Path
106
+ d="M8 11V19L16 19V11"
107
+ stroke="#000"
108
+ strokeWidth={2}
109
+ strokeLinecap="round"
110
+ strokeLinejoin="round"
111
+ />
112
+ </Svg>
134
113
  <Text style={styles.filterText}>Months ({selectedMonths.length}/{months.length})</Text>
135
114
  </TouchableOpacity>
136
115
 
137
116
  <TouchableOpacity onPress={() => setDivisionModal(true)} style={styles.filterItem}>
138
- <FilterIcon />
117
+ {/* SAME FILTER ICON */}
118
+ <Svg width={22} height={22} viewBox="0 0 24 24" fill="none">
119
+ <Path
120
+ d="M20 4H4V6.5L12 14.5L20 6.5V4Z"
121
+ stroke="#000"
122
+ strokeWidth={2}
123
+ strokeLinecap="round"
124
+ strokeLinejoin="round"
125
+ />
126
+ <Path
127
+ d="M8 11V19L16 19V11"
128
+ stroke="#000"
129
+ strokeWidth={2}
130
+ strokeLinecap="round"
131
+ strokeLinejoin="round"
132
+ />
133
+ </Svg>
139
134
  <Text style={styles.filterText}>{currentDivisionName}</Text>
140
135
  </TouchableOpacity>
141
136
  </View>
@@ -143,11 +138,24 @@ const currentDivisionName = divisions.find(d => d.code === division)?.displayNam
143
138
  <ScrollView style={styles.content}>
144
139
  {/* FULL SCREEN ICON ABOVE TABLE */}
145
140
  <TouchableOpacity
146
- onPress={() => setFullscreen(true)}
141
+ onPress={() => {
142
+ setFullscreen(true);
143
+ setZoomScale(1);
144
+ Alert.alert('Tip', 'Rotate your device to landscape for full view');
145
+ }}
147
146
  style={styles.fullScreenBtn}
148
147
  >
149
- <FullScreenIcon size={28} />
150
- <Text style={styles.fullScreenText}>Full Screen View</Text>
148
+ {/* FULL SCREEN EXPAND ICON */}
149
+ <Svg width={28} height={28} viewBox="0 0 24 24" fill="none">
150
+ <Path
151
+ 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"
152
+ stroke="#000"
153
+ strokeWidth={2}
154
+ strokeLinecap="round"
155
+ strokeLinejoin="round"
156
+ />
157
+ </Svg>
158
+ <Text style={styles.fullScreenText}>Full Screen Table</Text>
151
159
  </TouchableOpacity>
152
160
 
153
161
  <FrozenTableReport2A rows={filteredRows} />
@@ -161,60 +169,39 @@ const currentDivisionName = divisions.find(d => d.code === division)?.displayNam
161
169
  </View>
162
170
  </ScrollView>
163
171
 
164
- {/* MODALS */}
165
-
166
- {/* FULL SCREEN MODAL */}
172
+ {/* FULL SCREEN MODAL - SIMPLE ZOOM */}
167
173
  <Modal visible={fullscreen} supportedOrientations={['portrait', 'landscape']}>
168
174
  <View style={styles.fullModal}>
169
175
  <View style={styles.fullHeader}>
170
176
  <TouchableOpacity onPress={() => setFullscreen(false)}>
171
177
  <Text style={styles.closeFull}>✕</Text>
172
178
  </TouchableOpacity>
173
- <Text style={styles.fullTitle}>Rotate device to landscape for best view</Text>
179
+ <Text style={styles.fullHint}>Rotate to landscape Pinch to zoom</Text>
174
180
  </View>
175
181
 
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
- }}
182
+ <ScrollView
183
+ maximumZoomScale={3}
184
+ minimumZoomScale={0.8}
185
+ centerContent
186
+ contentContainerStyle={{ flexGrow: 1, justifyContent: 'center', alignItems: 'center' }}
183
187
  >
184
- <View style={styles.zoomView}>
185
- <View style={{ transform: [{ scale: zoomScale }] }}>
186
- <FrozenTableReport2A rows={table.rows} isFullscreen /> {/* all rows */}
187
- </View>
188
+ <View style={{ transform: [{ scale: isLandscape ? 1.4 : 1 }] }}>
189
+ <FrozenTableReport2A rows={table.rows} isFullscreen />
188
190
  </View>
189
- </PinchGestureHandler>
191
+ </ScrollView>
190
192
  </View>
191
193
  </Modal>
192
194
 
193
- <MonthFilterModal
194
- visible={monthsModal}
195
- months={months}
196
- selected={selectedMonths}
197
- onApply={setSelectedMonths}
198
- onClose={() => setMonthsModal(false)}
199
- />
200
-
201
- <DivisionFilterModal
202
- visible={divisionModal}
203
- divisions={divisions}
204
- selected={division}
205
- onSelect={setDivision}
206
- onClose={() => setDivisionModal(false)}
207
- />
195
+ {/* Existing modals */}
196
+ <MonthFilterModal visible={monthsModal} months={months} selected={selectedMonths} onApply={setSelectedMonths} onClose={() => setMonthsModal(false)} />
197
+ <DivisionFilterModal visible={divisionModal} divisions={divisions} selected={division} onSelect={setDivision} onClose={() => setDivisionModal(false)} />
208
198
  </View>
209
199
  );
210
200
  };
211
201
 
212
202
  const styles = StyleSheet.create({
213
203
  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' },
204
+ center: { flex: 1, justifyContent: 'center', alignItems: 'center' },
218
205
 
219
206
  header: {
220
207
  flexDirection: 'row',
@@ -225,15 +212,8 @@ const styles = StyleSheet.create({
225
212
  borderBottomWidth: 1,
226
213
  borderColor: '#eee',
227
214
  },
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,
235
- textAlign: 'center',
236
- },
215
+ backIcon: { fontSize: 28, color: '#000' },
216
+ headerTitle: { fontSize: 19, fontWeight: '700', color: '#000', flex: 1, textAlign: 'center' },
237
217
 
238
218
  filterBar: {
239
219
  flexDirection: 'row',
@@ -243,75 +223,27 @@ const styles = StyleSheet.create({
243
223
  borderBottomWidth: 1,
244
224
  borderColor: '#eee',
245
225
  },
246
- filterItem: {
247
- flexDirection: 'row',
248
- alignItems: 'center',
249
- paddingHorizontal: 16,
250
- paddingVertical: 8,
251
- },
252
- filterText: {
253
- fontSize: 16,
254
- color: '#000',
255
- fontWeight: '600',
256
- marginLeft: 8,
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 },
226
+ filterItem: { flexDirection: 'row', alignItems: 'center' },
227
+ filterText: { fontSize: 16, color: '#000', fontWeight: '600', marginLeft: 8 },
291
228
 
292
- content: { flex: 1, padding: 12 },
293
- chartContainer: {
294
- borderWidth: 1,
295
- borderColor: '#ddd',
296
- borderRadius: 10,
297
- overflow: 'hidden',
298
- marginVertical: 12,
299
- backgroundColor: '#fff',
300
- },
301
229
  fullScreenBtn: {
302
230
  flexDirection: 'row',
303
231
  alignItems: 'center',
304
232
  justifyContent: 'center',
305
- padding: 12,
306
- marginBottom: 12,
233
+ padding: 14,
234
+ backgroundColor: '#f0f0f0',
235
+ borderRadius: 8,
236
+ marginVertical: 12,
307
237
  },
308
- fullScreenText: { marginLeft: 8, fontSize: 16, color: '#000', fontWeight: '600' },
238
+ fullScreenText: { marginLeft: 10, fontSize: 16, color: '#000', fontWeight: '600' },
239
+
240
+ content: { flex: 1, padding: 12 },
241
+ chartContainer: { borderWidth: 1, borderColor: '#ddd', borderRadius: 10, overflow: 'hidden', marginVertical: 12, backgroundColor: '#fff' },
309
242
 
310
243
  fullModal: { flex: 1, backgroundColor: '#fff' },
311
- fullHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', padding: 16, backgroundColor: '#f8f9fa', position: 'relative' },
244
+ fullHeader: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', padding: 16, backgroundColor: '#f8f8f8', position: 'relative' },
312
245
  closeFull: { position: 'absolute', left: 16, fontSize: 28, color: '#000' },
313
- fullTitle: { fontSize: 16, color: '#000' },
314
- zoomView: { flex: 1, justifyContent: 'center', alignItems: 'center' },
246
+ fullHint: { fontSize: 15, color: '#000' },
315
247
  });
316
248
 
317
249
  export default Report2AScreen;