@dhiraj0720/report1chart 2.6.5 → 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.5",
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": {
@@ -5,8 +5,12 @@ import {
5
5
  TouchableOpacity,
6
6
  View,
7
7
  StyleSheet,
8
+ Modal,
9
+ Dimensions,
8
10
  ActivityIndicator,
11
+ Alert,
9
12
  } from 'react-native';
13
+ import Svg, { Path } from 'react-native-svg';
10
14
  import { filterChartByMonths } from '../utils/filterChartByMonths';
11
15
  import { getDivisions, getTable, getLine, getBar } from '../api/report2Fetcher';
12
16
  import MonthFilterModal from '../components/MonthFilterModal';
@@ -15,22 +19,11 @@ import FrozenTableReport2A from '../components/FrozenTableReport2A';
15
19
  import SvgLineChartCompact from '../components/SvgLineChartCompact';
16
20
  import SvgBarLineChartCompact from '../components/SvgBarLineChartCompact';
17
21
 
18
- // Proper SVG Funnel Icon Component (clean black filter icon)
19
- const FunnelIcon = () => (
20
- <View style={styles.funnelIcon}>
21
- <View style={styles.funnelTop} />
22
- <View style={styles.funnelBody} />
23
- <View style={styles.funnelLines}>
24
- <View style={styles.line1} />
25
- <View style={styles.line2} />
26
- <View style={styles.line3} />
27
- </View>
28
- </View>
29
- );
30
-
31
22
  const Report2AScreen = ({ api, token, onBack }) => {
32
23
  const [monthsModal, setMonthsModal] = useState(false);
33
24
  const [divisionModal, setDivisionModal] = useState(false);
25
+ const [fullscreen, setFullscreen] = useState(false);
26
+ const [zoomScale, setZoomScale] = useState(1);
34
27
 
35
28
  const [months, setMonths] = useState([]);
36
29
  const [selectedMonths, setSelectedMonths] = useState([]);
@@ -41,26 +34,19 @@ const Report2AScreen = ({ api, token, onBack }) => {
41
34
  const [line, setLine] = useState(null);
42
35
  const [bar, setBar] = useState(null);
43
36
  const [loading, setLoading] = useState(true);
44
- const [error, setError] = useState(null);
45
37
 
46
38
  useEffect(() => {
47
- if (!api?.divisions || !token) return;
48
-
49
39
  getDivisions(api.divisions, token)
50
40
  .then((d) => {
51
41
  setDivisions(d);
52
42
  if (d.length > 0) setDivision(d[0].code);
53
43
  })
54
- .catch(() => setError('Failed to load divisions'))
55
- .finally(() => setLoading(false));
56
- }, [api?.divisions, token]);
44
+ .catch(() => Alert.alert('Error', 'Failed to load divisions'));
45
+ }, [api.divisions, token]);
57
46
 
58
47
  useEffect(() => {
59
- if (!division || !api || !token) return;
60
-
48
+ if (!division) return;
61
49
  setLoading(true);
62
- setError(null);
63
-
64
50
  Promise.all([
65
51
  getTable(api.table, division, token),
66
52
  getLine(api.line, division, token),
@@ -73,77 +59,105 @@ const Report2AScreen = ({ api, token, onBack }) => {
73
59
  setMonths(l.labels || []);
74
60
  setSelectedMonths(l.labels || []);
75
61
  })
76
- .catch(() => setError('Failed to load data'))
77
62
  .finally(() => setLoading(false));
78
- }, [division, api, token]);
63
+ }, [division]);
79
64
 
80
65
  if (loading) {
81
66
  return (
82
67
  <View style={styles.center}>
83
68
  <ActivityIndicator size="large" color="#000" />
84
- <Text style={styles.loadingText}>Loading...</Text>
85
- </View>
86
- );
87
- }
88
-
89
- if (error) {
90
- return (
91
- <View style={styles.center}>
92
- <Text style={styles.errorText}>{error}</Text>
93
- <TouchableOpacity onPress={onBack}>
94
- <Text style={styles.backLink}>← Back to Reports</Text>
95
- </TouchableOpacity>
96
69
  </View>
97
70
  );
98
71
  }
99
72
 
100
- if (!table || !line || !bar || !division) {
101
- return <View style={styles.center}><Text>No data available</Text></View>;
102
- }
103
-
104
73
  const currentDivisionName = divisions.find(d => d.code === division)?.displayName || 'Division';
105
74
 
106
75
  const filteredRows = table.rows.filter(r => selectedMonths.includes(r.monthLabel));
107
76
  const filteredLine = filterChartByMonths(line, selectedMonths);
108
77
  const filteredBar = filterChartByMonths(bar, selectedMonths);
109
78
 
79
+ // Detect landscape
80
+ const { width, height } = Dimensions.get('window');
81
+ const isLandscape = width > height;
82
+
110
83
  return (
111
84
  <View style={styles.container}>
112
- {/* HEADER WITH BACK ICON + HEADING */}
85
+ {/* HEADER */}
113
86
  <View style={styles.header}>
114
- <TouchableOpacity onPress={onBack} style={styles.backButton}>
87
+ <TouchableOpacity onPress={onBack}>
115
88
  <Text style={styles.backIcon}>←</Text>
116
89
  </TouchableOpacity>
117
-
118
- <Text style={styles.headerTitle}>
119
- {currentDivisionName} Transportation
120
- </Text>
90
+ <Text style={styles.headerTitle}>{currentDivisionName} Transportation</Text>
121
91
  </View>
122
92
 
123
- {/* FILTERS - BLACK TEXT + PROPER FUNNEL ICON */}
93
+ {/* FILTERS */}
124
94
  <View style={styles.filterBar}>
125
- <TouchableOpacity
126
- onPress={() => setMonthsModal(true)}
127
- style={styles.filterItem}
128
- >
129
- <FunnelIcon />
130
- <Text style={styles.filterText}>
131
- Months ({selectedMonths.length}/{months.length})
132
- </Text>
95
+ <TouchableOpacity onPress={() => setMonthsModal(true)} style={styles.filterItem}>
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>
113
+ <Text style={styles.filterText}>Months ({selectedMonths.length}/{months.length})</Text>
133
114
  </TouchableOpacity>
134
115
 
135
- <TouchableOpacity
136
- onPress={() => setDivisionModal(true)}
137
- style={styles.filterItem}
138
- >
139
- <FunnelIcon />
140
- <Text style={styles.filterText}>
141
- {currentDivisionName}
142
- </Text>
116
+ <TouchableOpacity onPress={() => setDivisionModal(true)} style={styles.filterItem}>
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>
134
+ <Text style={styles.filterText}>{currentDivisionName}</Text>
143
135
  </TouchableOpacity>
144
136
  </View>
145
137
 
146
138
  <ScrollView style={styles.content}>
139
+ {/* FULL SCREEN ICON ABOVE TABLE */}
140
+ <TouchableOpacity
141
+ onPress={() => {
142
+ setFullscreen(true);
143
+ setZoomScale(1);
144
+ Alert.alert('Tip', 'Rotate your device to landscape for full view');
145
+ }}
146
+ style={styles.fullScreenBtn}
147
+ >
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>
159
+ </TouchableOpacity>
160
+
147
161
  <FrozenTableReport2A rows={filteredRows} />
148
162
 
149
163
  <View style={styles.chartContainer}>
@@ -155,32 +169,39 @@ const Report2AScreen = ({ api, token, onBack }) => {
155
169
  </View>
156
170
  </ScrollView>
157
171
 
158
- {/* MODALS */}
159
- <MonthFilterModal
160
- visible={monthsModal}
161
- months={months}
162
- selected={selectedMonths}
163
- onApply={setSelectedMonths}
164
- onClose={() => setMonthsModal(false)}
165
- />
172
+ {/* FULL SCREEN MODAL - SIMPLE ZOOM */}
173
+ <Modal visible={fullscreen} supportedOrientations={['portrait', 'landscape']}>
174
+ <View style={styles.fullModal}>
175
+ <View style={styles.fullHeader}>
176
+ <TouchableOpacity onPress={() => setFullscreen(false)}>
177
+ <Text style={styles.closeFull}>✕</Text>
178
+ </TouchableOpacity>
179
+ <Text style={styles.fullHint}>Rotate to landscape • Pinch to zoom</Text>
180
+ </View>
181
+
182
+ <ScrollView
183
+ maximumZoomScale={3}
184
+ minimumZoomScale={0.8}
185
+ centerContent
186
+ contentContainerStyle={{ flexGrow: 1, justifyContent: 'center', alignItems: 'center' }}
187
+ >
188
+ <View style={{ transform: [{ scale: isLandscape ? 1.4 : 1 }] }}>
189
+ <FrozenTableReport2A rows={table.rows} isFullscreen />
190
+ </View>
191
+ </ScrollView>
192
+ </View>
193
+ </Modal>
166
194
 
167
- <DivisionFilterModal
168
- visible={divisionModal}
169
- divisions={divisions}
170
- selected={division}
171
- onSelect={setDivision}
172
- onClose={() => setDivisionModal(false)}
173
- />
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)} />
174
198
  </View>
175
199
  );
176
200
  };
177
201
 
178
202
  const styles = StyleSheet.create({
179
203
  container: { flex: 1, backgroundColor: '#f8f9fa' },
180
- center: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 },
181
- loadingText: { marginTop: 12, fontSize: 16, color: '#000' },
182
- errorText: { fontSize: 16, color: '#d32f2f', textAlign: 'center', marginBottom: 20 },
183
- backLink: { fontSize: 18, color: '#000', fontWeight: '600' },
204
+ center: { flex: 1, justifyContent: 'center', alignItems: 'center' },
184
205
 
185
206
  header: {
186
207
  flexDirection: 'row',
@@ -191,15 +212,8 @@ const styles = StyleSheet.create({
191
212
  borderBottomWidth: 1,
192
213
  borderColor: '#eee',
193
214
  },
194
- backButton: { paddingRight: 12 },
195
- backIcon: { fontSize: 28, color: '#000', fontWeight: '300' },
196
- headerTitle: {
197
- fontSize: 19,
198
- fontWeight: '700',
199
- color: '#000',
200
- flex: 1,
201
- textAlign: 'center',
202
- },
215
+ backIcon: { fontSize: 28, color: '#000' },
216
+ headerTitle: { fontSize: 19, fontWeight: '700', color: '#000', flex: 1, textAlign: 'center' },
203
217
 
204
218
  filterBar: {
205
219
  flexDirection: 'row',
@@ -209,61 +223,27 @@ const styles = StyleSheet.create({
209
223
  borderBottomWidth: 1,
210
224
  borderColor: '#eee',
211
225
  },
212
- filterItem: {
226
+ filterItem: { flexDirection: 'row', alignItems: 'center' },
227
+ filterText: { fontSize: 16, color: '#000', fontWeight: '600', marginLeft: 8 },
228
+
229
+ fullScreenBtn: {
213
230
  flexDirection: 'row',
214
231
  alignItems: 'center',
215
- paddingHorizontal: 16,
216
- paddingVertical: 8,
217
- },
218
- filterText: {
219
- fontSize: 16,
220
- color: '#000',
221
- fontWeight: '600',
222
- marginLeft: 8,
223
- },
224
-
225
- // Proper Funnel Icon (SVG-style with Views)
226
- funnelIcon: {
227
- width: 20,
228
- height: 20,
229
232
  justifyContent: 'center',
230
- alignItems: 'center',
231
- },
232
- funnelTop: {
233
- width: 16,
234
- height: 4,
235
- backgroundColor: '#000',
236
- borderRadius: 2,
237
- },
238
- funnelBody: {
239
- width: 10,
240
- height: 10,
241
- borderLeftWidth: 4,
242
- borderRightWidth: 4,
243
- borderBottomWidth: 8,
244
- borderColor: 'transparent',
245
- borderBottomColor: '#000',
246
- marginTop: -2,
247
- },
248
- funnelLines: {
249
- position: 'absolute',
250
- top: 6,
251
- left: 4,
252
- right: 4,
233
+ padding: 14,
234
+ backgroundColor: '#f0f0f0',
235
+ borderRadius: 8,
236
+ marginVertical: 12,
253
237
  },
254
- line1: { height: 1, backgroundColor: '#000', marginVertical: 2 },
255
- line2: { height: 1, backgroundColor: '#000', marginVertical: 2 },
256
- line3: { height: 1, backgroundColor: '#000', marginVertical: 2 },
238
+ fullScreenText: { marginLeft: 10, fontSize: 16, color: '#000', fontWeight: '600' },
257
239
 
258
240
  content: { flex: 1, padding: 12 },
259
- chartContainer: {
260
- borderWidth: 1,
261
- borderColor: '#ddd',
262
- borderRadius: 10,
263
- overflow: 'hidden',
264
- marginVertical: 12,
265
- backgroundColor: '#fff',
266
- },
241
+ chartContainer: { borderWidth: 1, borderColor: '#ddd', borderRadius: 10, overflow: 'hidden', marginVertical: 12, backgroundColor: '#fff' },
242
+
243
+ fullModal: { flex: 1, backgroundColor: '#fff' },
244
+ fullHeader: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', padding: 16, backgroundColor: '#f8f8f8', position: 'relative' },
245
+ closeFull: { position: 'absolute', left: 16, fontSize: 28, color: '#000' },
246
+ fullHint: { fontSize: 15, color: '#000' },
267
247
  });
268
248
 
269
249
  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
+ );