@dhiraj0720/report1chart 2.6.8 → 2.7.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,27 +1,106 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Modal,
|
|
4
|
+
View,
|
|
5
|
+
Text,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
ScrollView,
|
|
8
|
+
StyleSheet,
|
|
9
|
+
TouchableWithoutFeedback,
|
|
10
|
+
} from 'react-native';
|
|
3
11
|
|
|
4
|
-
const DivisionFilterModal = ({ visible, divisions, selected, onSelect, onClose }) =>
|
|
5
|
-
|
|
6
|
-
<
|
|
7
|
-
<
|
|
8
|
-
<
|
|
12
|
+
const DivisionFilterModal = ({ visible, divisions, selected, onSelect, onClose }) => {
|
|
13
|
+
return (
|
|
14
|
+
<Modal visible={visible} transparent animationType="fade">
|
|
15
|
+
<TouchableWithoutFeedback onPress={onClose}>
|
|
16
|
+
<View style={styles.overlay}>
|
|
17
|
+
<TouchableWithoutFeedback>
|
|
18
|
+
<View style={styles.modalContent}>
|
|
19
|
+
{/* Header with Close Icon */}
|
|
20
|
+
<View style={styles.header}>
|
|
21
|
+
<Text style={styles.title}>Select Division</Text>
|
|
22
|
+
<TouchableOpacity onPress={onClose}>
|
|
23
|
+
<Text style={styles.closeIcon}>✕</Text>
|
|
24
|
+
</TouchableOpacity>
|
|
25
|
+
</View>
|
|
9
26
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
<ScrollView style={styles.list}>
|
|
28
|
+
{divisions.map(d => (
|
|
29
|
+
<TouchableOpacity
|
|
30
|
+
key={d.code}
|
|
31
|
+
onPress={() => {
|
|
32
|
+
onSelect(d.code);
|
|
33
|
+
onClose();
|
|
34
|
+
}}
|
|
35
|
+
style={styles.item}
|
|
36
|
+
>
|
|
37
|
+
<Text style={[
|
|
38
|
+
styles.itemText,
|
|
39
|
+
selected === d.code && styles.selectedText
|
|
40
|
+
]}>
|
|
41
|
+
{selected === d.code ? '✓ ' : ' '}{d.displayName}
|
|
42
|
+
</Text>
|
|
43
|
+
</TouchableOpacity>
|
|
44
|
+
))}
|
|
45
|
+
</ScrollView>
|
|
46
|
+
</View>
|
|
47
|
+
</TouchableWithoutFeedback>
|
|
48
|
+
</View>
|
|
49
|
+
</TouchableWithoutFeedback>
|
|
50
|
+
</Modal>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
26
53
|
|
|
27
|
-
|
|
54
|
+
const styles = StyleSheet.create({
|
|
55
|
+
overlay: {
|
|
56
|
+
flex: 1,
|
|
57
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
58
|
+
justifyContent: 'center',
|
|
59
|
+
alignItems: 'center',
|
|
60
|
+
},
|
|
61
|
+
modalContent: {
|
|
62
|
+
width: '85%',
|
|
63
|
+
maxHeight: '80%',
|
|
64
|
+
backgroundColor: '#fff',
|
|
65
|
+
borderRadius: 12,
|
|
66
|
+
padding: 16,
|
|
67
|
+
shadowColor: '#000',
|
|
68
|
+
shadowOffset: { width: 0, height: 4 },
|
|
69
|
+
shadowOpacity: 0.3,
|
|
70
|
+
shadowRadius: 8,
|
|
71
|
+
elevation: 10,
|
|
72
|
+
},
|
|
73
|
+
header: {
|
|
74
|
+
flexDirection: 'row',
|
|
75
|
+
justifyContent: 'space-between',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
marginBottom: 16,
|
|
78
|
+
},
|
|
79
|
+
title: {
|
|
80
|
+
fontSize: 18,
|
|
81
|
+
fontWeight: '700',
|
|
82
|
+
color: '#000',
|
|
83
|
+
},
|
|
84
|
+
closeIcon: {
|
|
85
|
+
fontSize: 24,
|
|
86
|
+
color: '#000',
|
|
87
|
+
fontWeight: '300',
|
|
88
|
+
},
|
|
89
|
+
list: {
|
|
90
|
+
maxHeight: 300,
|
|
91
|
+
},
|
|
92
|
+
item: {
|
|
93
|
+
paddingVertical: 12,
|
|
94
|
+
paddingHorizontal: 4,
|
|
95
|
+
},
|
|
96
|
+
itemText: {
|
|
97
|
+
fontSize: 16,
|
|
98
|
+
color: '#333',
|
|
99
|
+
},
|
|
100
|
+
selectedText: {
|
|
101
|
+
fontWeight: '700',
|
|
102
|
+
color: '#000',
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
export default DivisionFilterModal;
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Modal,
|
|
4
|
+
View,
|
|
5
|
+
Text,
|
|
6
|
+
TouchableOpacity,
|
|
7
|
+
ScrollView,
|
|
8
|
+
StyleSheet,
|
|
9
|
+
TouchableWithoutFeedback,
|
|
10
|
+
} from 'react-native';
|
|
3
11
|
|
|
4
12
|
const MonthFilterModal = ({ visible, months, selected, onApply, onClose }) => {
|
|
5
|
-
const [local, setLocal] = useState(selected);
|
|
13
|
+
const [local, setLocal] = useState(selected || []);
|
|
6
14
|
|
|
7
15
|
const toggle = (m) => {
|
|
8
16
|
if (local.includes(m)) {
|
|
@@ -12,36 +20,144 @@ const MonthFilterModal = ({ visible, months, selected, onApply, onClose }) => {
|
|
|
12
20
|
}
|
|
13
21
|
};
|
|
14
22
|
|
|
23
|
+
const selectAll = () => setLocal(months);
|
|
24
|
+
const clearAll = () => setLocal([]);
|
|
25
|
+
|
|
15
26
|
return (
|
|
16
27
|
<Modal visible={visible} transparent animationType="fade">
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
{/* Tap outside to close */}
|
|
29
|
+
<TouchableWithoutFeedback onPress={onClose}>
|
|
30
|
+
<View style={styles.overlay}>
|
|
31
|
+
<TouchableWithoutFeedback>
|
|
32
|
+
<View style={styles.modalContent}>
|
|
33
|
+
{/* Header with Close Icon */}
|
|
34
|
+
<View style={styles.header}>
|
|
35
|
+
<Text style={styles.title}>Select Months</Text>
|
|
36
|
+
<TouchableOpacity onPress={onClose}>
|
|
37
|
+
<Text style={styles.closeIcon}>✕</Text>
|
|
38
|
+
</TouchableOpacity>
|
|
39
|
+
</View>
|
|
40
|
+
|
|
41
|
+
{/* Select All / Clear All */}
|
|
42
|
+
<View style={styles.actionsRow}>
|
|
43
|
+
<TouchableOpacity onPress={selectAll}>
|
|
44
|
+
<Text style={styles.actionTextSelect}>Select All</Text>
|
|
45
|
+
</TouchableOpacity>
|
|
46
|
+
<TouchableOpacity onPress={clearAll}>
|
|
47
|
+
<Text style={styles.actionTextClear}>Clear All</Text>
|
|
48
|
+
</TouchableOpacity>
|
|
49
|
+
</View>
|
|
50
|
+
|
|
51
|
+
{/* Month List */}
|
|
52
|
+
<ScrollView style={styles.list}>
|
|
53
|
+
{months.map(m => (
|
|
54
|
+
<TouchableOpacity key={m} onPress={() => toggle(m)} style={styles.item}>
|
|
55
|
+
<Text style={[
|
|
56
|
+
styles.itemText,
|
|
57
|
+
local.includes(m) && styles.selectedText
|
|
58
|
+
]}>
|
|
59
|
+
{local.includes(m) ? '✓ ' : ' '}{m}
|
|
60
|
+
</Text>
|
|
61
|
+
</TouchableOpacity>
|
|
62
|
+
))}
|
|
63
|
+
</ScrollView>
|
|
64
|
+
|
|
65
|
+
{/* Apply Button */}
|
|
66
|
+
<TouchableOpacity
|
|
67
|
+
style={styles.applyBtn}
|
|
68
|
+
onPress={() => {
|
|
69
|
+
onApply(local);
|
|
70
|
+
onClose();
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
<Text style={styles.applyText}>Apply</Text>
|
|
31
74
|
</TouchableOpacity>
|
|
32
|
-
|
|
33
|
-
</
|
|
34
|
-
|
|
35
|
-
<TouchableOpacity
|
|
36
|
-
style={{ marginTop: 12, alignSelf: 'flex-end' }}
|
|
37
|
-
onPress={() => { onApply(local); onClose(); }}
|
|
38
|
-
>
|
|
39
|
-
<Text style={{ fontWeight: '700' }}>Apply</Text>
|
|
40
|
-
</TouchableOpacity>
|
|
75
|
+
</View>
|
|
76
|
+
</TouchableWithoutFeedback>
|
|
41
77
|
</View>
|
|
42
|
-
</
|
|
78
|
+
</TouchableWithoutFeedback>
|
|
43
79
|
</Modal>
|
|
44
80
|
);
|
|
45
81
|
};
|
|
46
82
|
|
|
47
|
-
|
|
83
|
+
const styles = StyleSheet.create({
|
|
84
|
+
overlay: {
|
|
85
|
+
flex: 1,
|
|
86
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
87
|
+
justifyContent: 'center',
|
|
88
|
+
alignItems: 'center',
|
|
89
|
+
},
|
|
90
|
+
modalContent: {
|
|
91
|
+
width: '85%',
|
|
92
|
+
maxHeight: '80%',
|
|
93
|
+
backgroundColor: '#fff',
|
|
94
|
+
borderRadius: 12,
|
|
95
|
+
padding: 16,
|
|
96
|
+
shadowColor: '#000',
|
|
97
|
+
shadowOffset: { width: 0, height: 4 },
|
|
98
|
+
shadowOpacity: 0.3,
|
|
99
|
+
shadowRadius: 8,
|
|
100
|
+
elevation: 10,
|
|
101
|
+
},
|
|
102
|
+
header: {
|
|
103
|
+
flexDirection: 'row',
|
|
104
|
+
justifyContent: 'space-between',
|
|
105
|
+
alignItems: 'center',
|
|
106
|
+
marginBottom: 12,
|
|
107
|
+
},
|
|
108
|
+
title: {
|
|
109
|
+
fontSize: 18,
|
|
110
|
+
fontWeight: '700',
|
|
111
|
+
color: '#000',
|
|
112
|
+
},
|
|
113
|
+
closeIcon: {
|
|
114
|
+
fontSize: 24,
|
|
115
|
+
color: '#000',
|
|
116
|
+
fontWeight: '300',
|
|
117
|
+
},
|
|
118
|
+
actionsRow: {
|
|
119
|
+
flexDirection: 'row',
|
|
120
|
+
justifyContent: 'space-between',
|
|
121
|
+
marginBottom: 12,
|
|
122
|
+
},
|
|
123
|
+
actionTextSelect: {
|
|
124
|
+
color: '#1e88e5',
|
|
125
|
+
fontWeight: '600',
|
|
126
|
+
fontSize: 15,
|
|
127
|
+
},
|
|
128
|
+
actionTextClear: {
|
|
129
|
+
color: '#d32f2f',
|
|
130
|
+
fontWeight: '600',
|
|
131
|
+
fontSize: 15,
|
|
132
|
+
},
|
|
133
|
+
list: {
|
|
134
|
+
maxHeight: 300,
|
|
135
|
+
},
|
|
136
|
+
item: {
|
|
137
|
+
paddingVertical: 10,
|
|
138
|
+
paddingHorizontal: 4,
|
|
139
|
+
},
|
|
140
|
+
itemText: {
|
|
141
|
+
fontSize: 16,
|
|
142
|
+
color: '#333',
|
|
143
|
+
},
|
|
144
|
+
selectedText: {
|
|
145
|
+
fontWeight: '700',
|
|
146
|
+
color: '#000',
|
|
147
|
+
},
|
|
148
|
+
applyBtn: {
|
|
149
|
+
marginTop: 16,
|
|
150
|
+
alignSelf: 'flex-end',
|
|
151
|
+
paddingHorizontal: 20,
|
|
152
|
+
paddingVertical: 10,
|
|
153
|
+
backgroundColor: '#1e88e5',
|
|
154
|
+
borderRadius: 8,
|
|
155
|
+
},
|
|
156
|
+
applyText: {
|
|
157
|
+
color: '#fff',
|
|
158
|
+
fontWeight: '700',
|
|
159
|
+
fontSize: 16,
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
export default MonthFilterModal;
|
|
@@ -16,11 +16,18 @@ const Report1AScreen = ({ endpoint, token, onBack }) => {
|
|
|
16
16
|
return (
|
|
17
17
|
<>
|
|
18
18
|
<ScrollView style={{ padding: 16 }}>
|
|
19
|
-
<Text onPress={onBack} style={{ marginBottom: 12 }}>‹ Back</Text>
|
|
19
|
+
{/* <Text onPress={onBack} style={{ marginBottom: 12 }}>‹ Back</Text>
|
|
20
20
|
|
|
21
21
|
<Text style={{ fontSize: 18, fontWeight: '700', marginBottom: 12 }}>
|
|
22
22
|
PERFORMANS RAPORU (USD)
|
|
23
|
-
</Text>
|
|
23
|
+
</Text> */}
|
|
24
|
+
|
|
25
|
+
<View style={styles.header}>
|
|
26
|
+
<TouchableOpacity onPress={onBack}>
|
|
27
|
+
<Text style={styles.backIcon}>‹</Text>
|
|
28
|
+
</TouchableOpacity>
|
|
29
|
+
<Text style={styles.headerTitle}>PERFORMANS RAPORU (USD)</Text>
|
|
30
|
+
</View>
|
|
24
31
|
|
|
25
32
|
{/* FULLSCREEN BUTTON */}
|
|
26
33
|
<TouchableOpacity
|
|
@@ -81,7 +81,7 @@ const Report2AScreen = ({ api, token, onBack }) => {
|
|
|
81
81
|
{/* HEADER */}
|
|
82
82
|
<View style={styles.header}>
|
|
83
83
|
<TouchableOpacity onPress={onBack}>
|
|
84
|
-
<Text style={styles.backIcon}
|
|
84
|
+
<Text style={styles.backIcon}>‹</Text>
|
|
85
85
|
</TouchableOpacity>
|
|
86
86
|
<Text style={styles.headerTitle}>{currentDivisionName} Transportation</Text>
|
|
87
87
|
</View>
|
|
@@ -106,14 +106,20 @@ const Report2AScreen = ({ api, token, onBack }) => {
|
|
|
106
106
|
</View>
|
|
107
107
|
|
|
108
108
|
<ScrollView style={styles.content}>
|
|
109
|
-
{/* FULL SCREEN BUTTON
|
|
109
|
+
{/* FULL SCREEN BUTTON
|
|
110
110
|
<TouchableOpacity onPress={() => setFullscreen(true)} style={styles.fullScreenBtn}>
|
|
111
111
|
<Svg width={28} height={28} viewBox="0 0 24 24" fill="none">
|
|
112
112
|
<Path 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"
|
|
113
113
|
stroke="#000" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
|
|
114
114
|
</Svg>
|
|
115
115
|
<Text style={styles.fullScreenText}>Full Screen Table</Text>
|
|
116
|
-
</TouchableOpacity>
|
|
116
|
+
</TouchableOpacity> */}
|
|
117
|
+
|
|
118
|
+
<TouchableOpacity
|
|
119
|
+
onPress={() => setFullscreen(true)} style={styles.fullScreenBtn}
|
|
120
|
+
>
|
|
121
|
+
<Text style={{ fontWeight: '700' }}>⤢ Full Screen</Text>
|
|
122
|
+
</TouchableOpacity>
|
|
117
123
|
|
|
118
124
|
<FrozenTableReport2A rows={filteredRows} />
|
|
119
125
|
|
|
@@ -4,9 +4,12 @@ import {
|
|
|
4
4
|
Text,
|
|
5
5
|
TouchableOpacity,
|
|
6
6
|
View,
|
|
7
|
-
ActivityIndicator,
|
|
8
7
|
StyleSheet,
|
|
8
|
+
Modal,
|
|
9
|
+
Dimensions,
|
|
10
|
+
ActivityIndicator,
|
|
9
11
|
} from 'react-native';
|
|
12
|
+
import Svg, { Path } from 'react-native-svg';
|
|
10
13
|
import {
|
|
11
14
|
fetchReport3Table,
|
|
12
15
|
fetchReport3Line,
|
|
@@ -19,28 +22,26 @@ import SvgBarLineChartCompact3A from '../components/SvgBarLineChartCompact3A';
|
|
|
19
22
|
|
|
20
23
|
const Report3AScreen = ({ api, token, onBack }) => {
|
|
21
24
|
const [monthsModal, setMonthsModal] = useState(false);
|
|
25
|
+
const [fullscreen, setFullscreen] = useState(false);
|
|
26
|
+
|
|
22
27
|
const [months, setMonths] = useState([]);
|
|
23
28
|
const [selectedMonths, setSelectedMonths] = useState([]);
|
|
24
29
|
const [table, setTable] = useState(null);
|
|
25
30
|
const [line, setLine] = useState(null);
|
|
26
31
|
const [bar, setBar] = useState(null);
|
|
27
32
|
const [loading, setLoading] = useState(true);
|
|
28
|
-
const [error, setError] = useState(null);
|
|
29
33
|
|
|
30
34
|
useEffect(() => {
|
|
31
35
|
if (!api || !token) return;
|
|
32
36
|
|
|
33
37
|
setLoading(true);
|
|
34
|
-
setError(null);
|
|
35
38
|
|
|
36
39
|
Promise.all([
|
|
37
|
-
fetchReport3Table(api.table, token)
|
|
38
|
-
fetchReport3Line(api.line, token)
|
|
39
|
-
fetchReport3Bar(api.bar, token)
|
|
40
|
+
fetchReport3Table(api.table, token),
|
|
41
|
+
fetchReport3Line(api.line, token),
|
|
42
|
+
fetchReport3Bar(api.bar, token),
|
|
40
43
|
])
|
|
41
44
|
.then(([t, l, b]) => {
|
|
42
|
-
if (!t || !l || !b) throw new Error('Failed to load data');
|
|
43
|
-
|
|
44
45
|
setTable(t);
|
|
45
46
|
setLine(l);
|
|
46
47
|
setBar(b);
|
|
@@ -50,11 +51,10 @@ const Report3AScreen = ({ api, token, onBack }) => {
|
|
|
50
51
|
.map(r => r.monthLabel);
|
|
51
52
|
|
|
52
53
|
setMonths(monthLabels);
|
|
53
|
-
setSelectedMonths(monthLabels);
|
|
54
|
+
setSelectedMonths(monthLabels);
|
|
54
55
|
})
|
|
55
56
|
.catch((err) => {
|
|
56
57
|
console.error(err);
|
|
57
|
-
setError('Failed to load Report 3A data');
|
|
58
58
|
})
|
|
59
59
|
.finally(() => setLoading(false));
|
|
60
60
|
}, [api, token]);
|
|
@@ -62,19 +62,7 @@ const Report3AScreen = ({ api, token, onBack }) => {
|
|
|
62
62
|
if (loading) {
|
|
63
63
|
return (
|
|
64
64
|
<View style={styles.center}>
|
|
65
|
-
<ActivityIndicator size="large" color="#
|
|
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>
|
|
65
|
+
<ActivityIndicator size="large" color="#000" />
|
|
78
66
|
</View>
|
|
79
67
|
);
|
|
80
68
|
}
|
|
@@ -103,36 +91,93 @@ const Report3AScreen = ({ api, token, onBack }) => {
|
|
|
103
91
|
const filteredLine = filterChart(line);
|
|
104
92
|
const filteredBar = filterChart(bar);
|
|
105
93
|
|
|
94
|
+
const { width, height } = Dimensions.get('window');
|
|
95
|
+
const isLandscape = width > height;
|
|
96
|
+
|
|
106
97
|
return (
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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} />
|
|
98
|
+
<View style={styles.container}>
|
|
99
|
+
{/* HEADER WITH BACK ICON */}
|
|
100
|
+
<View style={styles.header}>
|
|
101
|
+
<TouchableOpacity onPress={onBack}>
|
|
102
|
+
<Text style={styles.backIcon}>‹</Text>
|
|
103
|
+
</TouchableOpacity>
|
|
104
|
+
<Text style={styles.headerTitle}>Ulaştırma Hizmetleri Analizi (A)</Text>
|
|
128
105
|
</View>
|
|
129
106
|
|
|
130
|
-
{/*
|
|
131
|
-
<View style={styles.
|
|
132
|
-
<
|
|
107
|
+
{/* FILTER WITH FUNNEL ICON */}
|
|
108
|
+
<View style={styles.filterBar}>
|
|
109
|
+
<TouchableOpacity onPress={() => setMonthsModal(true)} style={styles.filterItem}>
|
|
110
|
+
<Svg width={22} height={22} viewBox="0 0 24 24" fill="none">
|
|
111
|
+
<Path d="M20 4H4V6.5L12 14.5L20 6.5V4Z" stroke="#000" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
|
|
112
|
+
<Path d="M8 11V19L16 19V11" stroke="#000" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
|
|
113
|
+
</Svg>
|
|
114
|
+
<Text style={styles.filterText}>
|
|
115
|
+
Months ({selectedMonths.length}/{months.length})
|
|
116
|
+
</Text>
|
|
117
|
+
</TouchableOpacity>
|
|
133
118
|
</View>
|
|
134
119
|
|
|
135
|
-
{
|
|
120
|
+
<ScrollView style={styles.content}>
|
|
121
|
+
{/* FULL SCREEN BUTTON
|
|
122
|
+
<TouchableOpacity onPress={() => setFullscreen(true)} style={styles.fullScreenBtn}>
|
|
123
|
+
<Svg width={28} height={28} viewBox="0 0 24 24" fill="none">
|
|
124
|
+
<Path 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"
|
|
125
|
+
stroke="#000" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
|
|
126
|
+
</Svg>
|
|
127
|
+
<Text style={styles.fullScreenText}>Full Screen Table</Text>
|
|
128
|
+
</TouchableOpacity> */}
|
|
129
|
+
|
|
130
|
+
<TouchableOpacity
|
|
131
|
+
onPress={() => setFullscreen(true)} style={styles.fullScreenBtn}
|
|
132
|
+
>
|
|
133
|
+
<Text style={{ fontWeight: '700' }}>⤢ Full Screen</Text>
|
|
134
|
+
</TouchableOpacity>
|
|
135
|
+
|
|
136
|
+
{/* TABLE */}
|
|
137
|
+
<FrozenTableReport3A rows={filteredRows} />
|
|
138
|
+
|
|
139
|
+
{/* CHARTS */}
|
|
140
|
+
<View style={styles.chartContainer}>
|
|
141
|
+
<SvgLineChartCompact3A data={filteredLine} />
|
|
142
|
+
</View>
|
|
143
|
+
|
|
144
|
+
<View style={styles.chartContainer}>
|
|
145
|
+
<SvgBarLineChartCompact3A data={filteredBar} />
|
|
146
|
+
</View>
|
|
147
|
+
</ScrollView>
|
|
148
|
+
|
|
149
|
+
{/* FULL SCREEN MODAL */}
|
|
150
|
+
<Modal visible={fullscreen} supportedOrientations={['portrait', 'landscape']} animationType="fade">
|
|
151
|
+
<View style={styles.fullModal}>
|
|
152
|
+
<View style={styles.fullHeader}>
|
|
153
|
+
<Text style={styles.fullHintText}>
|
|
154
|
+
Rotate device • Pinch to zoom
|
|
155
|
+
</Text>
|
|
156
|
+
<TouchableOpacity onPress={() => setFullscreen(false)}>
|
|
157
|
+
<Text style={styles.closeBtn}>✕</Text>
|
|
158
|
+
</TouchableOpacity>
|
|
159
|
+
</View>
|
|
160
|
+
|
|
161
|
+
<ScrollView
|
|
162
|
+
maximumZoomScale={3}
|
|
163
|
+
minimumZoomScale={0.7}
|
|
164
|
+
showsHorizontalScrollIndicator={false}
|
|
165
|
+
showsVerticalScrollIndicator={false}
|
|
166
|
+
contentContainerStyle={styles.zoomContent}
|
|
167
|
+
pinchGestureEnabled={true}
|
|
168
|
+
bouncesZoom={true}
|
|
169
|
+
>
|
|
170
|
+
<View style={[
|
|
171
|
+
styles.tableWrapper,
|
|
172
|
+
isLandscape && styles.landscapeFit
|
|
173
|
+
]}>
|
|
174
|
+
<FrozenTableReport3A rows={table.rows} isFullscreen />
|
|
175
|
+
</View>
|
|
176
|
+
</ScrollView>
|
|
177
|
+
</View>
|
|
178
|
+
</Modal>
|
|
179
|
+
|
|
180
|
+
{/* MONTH FILTER MODAL */}
|
|
136
181
|
<MonthFilterModal
|
|
137
182
|
visible={monthsModal}
|
|
138
183
|
months={months}
|
|
@@ -140,21 +185,51 @@ const Report3AScreen = ({ api, token, onBack }) => {
|
|
|
140
185
|
onApply={setSelectedMonths}
|
|
141
186
|
onClose={() => setMonthsModal(false)}
|
|
142
187
|
/>
|
|
143
|
-
</
|
|
188
|
+
</View>
|
|
144
189
|
);
|
|
145
190
|
};
|
|
146
191
|
|
|
147
192
|
const styles = StyleSheet.create({
|
|
148
|
-
container: {
|
|
149
|
-
center: { flex: 1, justifyContent: 'center', alignItems: 'center'
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
193
|
+
container: { flex: 1, backgroundColor: '#f8f9fa' },
|
|
194
|
+
center: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
|
195
|
+
|
|
196
|
+
header: {
|
|
197
|
+
flexDirection: 'row',
|
|
198
|
+
alignItems: 'center',
|
|
199
|
+
paddingHorizontal: 16,
|
|
200
|
+
paddingVertical: 18,
|
|
201
|
+
backgroundColor: '#fff',
|
|
202
|
+
borderBottomWidth: 1,
|
|
203
|
+
borderColor: '#eee',
|
|
204
|
+
},
|
|
205
|
+
backIcon: { fontSize: 28, color: '#000' },
|
|
206
|
+
headerTitle: { fontSize: 19, fontWeight: '700', color: '#000', flex: 1, textAlign: 'center' },
|
|
207
|
+
|
|
208
|
+
filterBar: {
|
|
209
|
+
flexDirection: 'row',
|
|
210
|
+
justifyContent: 'flex-start',
|
|
211
|
+
paddingHorizontal: 16,
|
|
212
|
+
paddingVertical: 14,
|
|
213
|
+
backgroundColor: '#fff',
|
|
214
|
+
borderBottomWidth: 1,
|
|
215
|
+
borderColor: '#eee',
|
|
216
|
+
},
|
|
217
|
+
filterItem: { flexDirection: 'row', alignItems: 'center' },
|
|
218
|
+
filterText: { fontSize: 16, color: '#000', fontWeight: '600', marginLeft: 8 },
|
|
219
|
+
|
|
220
|
+
fullScreenBtn: {
|
|
221
|
+
flexDirection: 'row',
|
|
222
|
+
alignItems: 'center',
|
|
223
|
+
justifyContent: 'center',
|
|
224
|
+
padding: 14,
|
|
225
|
+
backgroundColor: '#f0f0f0',
|
|
226
|
+
borderRadius: 8,
|
|
227
|
+
marginVertical: 12,
|
|
228
|
+
},
|
|
229
|
+
fullScreenText: { marginLeft: 10, fontSize: 16, color: '#000', fontWeight: '600' },
|
|
230
|
+
|
|
231
|
+
content: { flex: 1, padding: 12 },
|
|
232
|
+
chartContainer: {
|
|
158
233
|
borderWidth: 1,
|
|
159
234
|
borderColor: '#ddd',
|
|
160
235
|
borderRadius: 10,
|
|
@@ -162,6 +237,44 @@ const styles = StyleSheet.create({
|
|
|
162
237
|
marginVertical: 12,
|
|
163
238
|
backgroundColor: '#fff',
|
|
164
239
|
},
|
|
240
|
+
|
|
241
|
+
fullModal: { flex: 1, backgroundColor: '#fff' },
|
|
242
|
+
fullHeader: {
|
|
243
|
+
flexDirection: 'row',
|
|
244
|
+
justifyContent: 'center',
|
|
245
|
+
alignItems: 'center',
|
|
246
|
+
paddingVertical: 16,
|
|
247
|
+
paddingHorizontal: 20,
|
|
248
|
+
backgroundColor: '#f8f8f8',
|
|
249
|
+
borderBottomWidth: 1,
|
|
250
|
+
borderColor: '#ddd',
|
|
251
|
+
},
|
|
252
|
+
fullHintText: {
|
|
253
|
+
fontSize: 16,
|
|
254
|
+
color: '#000',
|
|
255
|
+
fontWeight: '600',
|
|
256
|
+
textAlign: 'center',
|
|
257
|
+
flex: 1,
|
|
258
|
+
},
|
|
259
|
+
closeBtn: { fontSize: 28, color: '#000', paddingLeft: 20 },
|
|
260
|
+
|
|
261
|
+
zoomContent: {
|
|
262
|
+
flexGrow: 1,
|
|
263
|
+
justifyContent: 'center',
|
|
264
|
+
alignItems: 'center',
|
|
265
|
+
padding: 20,
|
|
266
|
+
},
|
|
267
|
+
tableWrapper: {
|
|
268
|
+
shadowColor: '#000',
|
|
269
|
+
shadowOffset: { width: 0, height: 2 },
|
|
270
|
+
shadowOpacity: 0.1,
|
|
271
|
+
shadowRadius: 8,
|
|
272
|
+
elevation: 5,
|
|
273
|
+
},
|
|
274
|
+
landscapeFit: {
|
|
275
|
+
transform: [{ scale: 1.4 }],
|
|
276
|
+
maxWidth: '100%',
|
|
277
|
+
},
|
|
165
278
|
});
|
|
166
279
|
|
|
167
280
|
export default Report3AScreen;
|