@dhiraj0720/report1chart 2.6.6 → 2.6.8
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.
|
|
3
|
+
"version": "2.6.8",
|
|
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": {
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { View, Text, ScrollView, StyleSheet } from 'react-native';
|
|
3
3
|
|
|
4
|
-
// Helper: Format numbers with commas (e.g., 1083203 → 1,083,203)
|
|
5
4
|
const formatNumber = (value) => {
|
|
6
5
|
if (value === null || value === undefined || value === '') return '-';
|
|
7
6
|
return Number(value).toLocaleString('en-US');
|
|
8
7
|
};
|
|
9
8
|
|
|
10
|
-
// Percentage cell with up/down arrow and color
|
|
11
9
|
const PercentCell = ({ value }) => {
|
|
12
10
|
if (value === null || value === undefined) return <Text>-</Text>;
|
|
13
11
|
const positive = value >= 0;
|
|
@@ -21,10 +19,10 @@ const PercentCell = ({ value }) => {
|
|
|
21
19
|
);
|
|
22
20
|
};
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
const Cell = ({ children, bold = false, highlight = false }) => (
|
|
22
|
+
const Cell = ({ children, bold = false, highlight = false, width }) => (
|
|
26
23
|
<View style={[
|
|
27
24
|
styles.cell,
|
|
25
|
+
{ width }, // dynamic width
|
|
28
26
|
bold && styles.bold,
|
|
29
27
|
highlight && styles.highlightCell
|
|
30
28
|
]}>
|
|
@@ -34,24 +32,24 @@ const Cell = ({ children, bold = false, highlight = false }) => (
|
|
|
34
32
|
</View>
|
|
35
33
|
);
|
|
36
34
|
|
|
37
|
-
const FrozenTableReport2A = ({ rows = [] }) => {
|
|
38
|
-
if (!rows
|
|
39
|
-
return <Text>No data available</Text>;
|
|
35
|
+
const FrozenTableReport2A = ({ rows = [], isFullscreen = false }) => {
|
|
36
|
+
if (!rows.length) {
|
|
37
|
+
return <Text style={{ textAlign: 'center', padding: 20 }}>No data available</Text>;
|
|
40
38
|
}
|
|
41
39
|
|
|
40
|
+
// Dynamic widths based on fullscreen mode
|
|
41
|
+
const frozenColumnWidth = isFullscreen ? 180 : 120;
|
|
42
|
+
const cellWidth = isFullscreen ? 140 : 130;
|
|
43
|
+
|
|
42
44
|
return (
|
|
43
45
|
<View style={styles.container}>
|
|
44
|
-
{/* Frozen
|
|
45
|
-
<View style={styles.frozenColumn}>
|
|
46
|
-
<Cell bold>AY</Cell>
|
|
47
|
-
{rows.map((row,
|
|
46
|
+
{/* Frozen Column */}
|
|
47
|
+
<View style={[styles.frozenColumn, { width: frozenColumnWidth }]}>
|
|
48
|
+
<Cell bold width={frozenColumnWidth}>AY</Cell>
|
|
49
|
+
{rows.map((row, i) => {
|
|
48
50
|
const isTotal = row.monthLabel === 'Total';
|
|
49
51
|
return (
|
|
50
|
-
<Cell
|
|
51
|
-
key={index}
|
|
52
|
-
bold={isTotal}
|
|
53
|
-
highlight={isTotal}
|
|
54
|
-
>
|
|
52
|
+
<Cell key={i} bold={isTotal} highlight={isTotal} width={frozenColumnWidth}>
|
|
55
53
|
{row.monthLabel}
|
|
56
54
|
</Cell>
|
|
57
55
|
);
|
|
@@ -61,41 +59,24 @@ const FrozenTableReport2A = ({ rows = [] }) => {
|
|
|
61
59
|
{/* Scrollable Columns */}
|
|
62
60
|
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
|
|
63
61
|
<View>
|
|
64
|
-
{/* Header Row */}
|
|
65
62
|
<View style={styles.headerRow}>
|
|
66
|
-
{[
|
|
67
|
-
|
|
68
|
-
'2024 Kar', '2025 Kar', 'Kar %',
|
|
69
|
-
'2025 Bütçe', 'Bütçe %'
|
|
70
|
-
].map((header) => (
|
|
71
|
-
<Cell key={header} bold>
|
|
72
|
-
{header}
|
|
73
|
-
</Cell>
|
|
63
|
+
{['2024 TEU', '2025 TEU', 'TEU %', '2024 Kar', '2025 Kar', 'Kar %', '2025 Bütçe', 'Bütçe %'].map((h) => (
|
|
64
|
+
<Cell key={h} bold width={cellWidth}>{h}</Cell>
|
|
74
65
|
))}
|
|
75
66
|
</View>
|
|
76
67
|
|
|
77
|
-
{
|
|
78
|
-
{rows.map((row, index) => {
|
|
68
|
+
{rows.map((row, i) => {
|
|
79
69
|
const isTotal = row.monthLabel === 'Total';
|
|
80
|
-
|
|
81
70
|
return (
|
|
82
|
-
<View key={
|
|
83
|
-
<Cell highlight={isTotal}>{formatNumber(row.teu2024)}</Cell>
|
|
84
|
-
<Cell highlight={isTotal}>{formatNumber(row.teu2025)}</Cell>
|
|
85
|
-
<Cell highlight={isTotal}>
|
|
86
|
-
|
|
87
|
-
</Cell>
|
|
88
|
-
|
|
89
|
-
<Cell highlight={isTotal}>{formatNumber(row.
|
|
90
|
-
<Cell highlight={isTotal}
|
|
91
|
-
<Cell highlight={isTotal}>
|
|
92
|
-
<PercentCell value={row.profitChangePercent} />
|
|
93
|
-
</Cell>
|
|
94
|
-
|
|
95
|
-
<Cell highlight={isTotal}>{formatNumber(row.budgetProfitUsd2025)}</Cell>
|
|
96
|
-
<Cell highlight={isTotal}>
|
|
97
|
-
<PercentCell value={row.budgetChangePercent} />
|
|
98
|
-
</Cell>
|
|
71
|
+
<View key={i} style={styles.dataRow}>
|
|
72
|
+
<Cell highlight={isTotal} width={cellWidth}>{formatNumber(row.teu2024)}</Cell>
|
|
73
|
+
<Cell highlight={isTotal} width={cellWidth}>{formatNumber(row.teu2025)}</Cell>
|
|
74
|
+
<Cell highlight={isTotal} width={cellWidth}><PercentCell value={row.teuChangePercent} /></Cell>
|
|
75
|
+
<Cell highlight={isTotal} width={cellWidth}>{formatNumber(row.profitUsd2024)}</Cell>
|
|
76
|
+
<Cell highlight={isTotal} width={cellWidth}>{formatNumber(row.profitUsd2025)}</Cell>
|
|
77
|
+
<Cell highlight={isTotal} width={cellWidth}><PercentCell value={row.profitChangePercent} /></Cell>
|
|
78
|
+
<Cell highlight={isTotal} width={cellWidth}>{formatNumber(row.budgetProfitUsd2025)}</Cell>
|
|
79
|
+
<Cell highlight={isTotal} width={cellWidth}><PercentCell value={row.budgetChangePercent} /></Cell>
|
|
99
80
|
</View>
|
|
100
81
|
);
|
|
101
82
|
})}
|
|
@@ -118,20 +99,13 @@ const styles = StyleSheet.create({
|
|
|
118
99
|
backgroundColor: '#fff',
|
|
119
100
|
},
|
|
120
101
|
frozenColumn: {
|
|
121
|
-
width: 120,
|
|
122
102
|
backgroundColor: '#f4f6f8',
|
|
123
103
|
borderRightWidth: 1,
|
|
124
104
|
borderColor: '#ddd',
|
|
125
105
|
},
|
|
126
|
-
headerRow: {
|
|
127
|
-
|
|
128
|
-
backgroundColor: '#f4f6f8',
|
|
129
|
-
},
|
|
130
|
-
dataRow: {
|
|
131
|
-
flexDirection: 'row',
|
|
132
|
-
},
|
|
106
|
+
headerRow: { flexDirection: 'row', backgroundColor: '#f4f6f8' },
|
|
107
|
+
dataRow: { flexDirection: 'row' },
|
|
133
108
|
cell: {
|
|
134
|
-
width: 130,
|
|
135
109
|
paddingVertical: 10,
|
|
136
110
|
paddingHorizontal: 6,
|
|
137
111
|
justifyContent: 'center',
|
|
@@ -143,18 +117,8 @@ const styles = StyleSheet.create({
|
|
|
143
117
|
textAlign: 'center',
|
|
144
118
|
color: '#333',
|
|
145
119
|
},
|
|
146
|
-
bold: {
|
|
147
|
-
|
|
148
|
-
},
|
|
149
|
-
|
|
150
|
-
fontWeight: '700',
|
|
151
|
-
},
|
|
152
|
-
highlightCell: {
|
|
153
|
-
backgroundColor: '#e9f0f8',
|
|
154
|
-
},
|
|
155
|
-
percentText: {
|
|
156
|
-
fontWeight: '700',
|
|
157
|
-
fontSize: 12,
|
|
158
|
-
textAlign: 'center',
|
|
159
|
-
},
|
|
120
|
+
bold: { backgroundColor: '#e9f0f8' },
|
|
121
|
+
boldText: { fontWeight: '700' },
|
|
122
|
+
highlightCell: { backgroundColor: '#e9f0f8' },
|
|
123
|
+
percentText: { fontWeight: '700', fontSize: 12, textAlign: 'center' },
|
|
160
124
|
});
|
|
@@ -93,7 +93,7 @@ const SvgBarLineChartCompact = ({ data }) => {
|
|
|
93
93
|
fill="white"
|
|
94
94
|
fontWeight="700"
|
|
95
95
|
>
|
|
96
|
-
|
|
96
|
+
{formatNumber(val2024)}
|
|
97
97
|
</SvgText>
|
|
98
98
|
)}
|
|
99
99
|
|
|
@@ -114,7 +114,7 @@ const SvgBarLineChartCompact = ({ data }) => {
|
|
|
114
114
|
fill="white"
|
|
115
115
|
fontWeight="700"
|
|
116
116
|
>
|
|
117
|
-
|
|
117
|
+
{formatNumber(val2025)}
|
|
118
118
|
</SvgText>
|
|
119
119
|
)}
|
|
120
120
|
|
|
@@ -150,7 +150,7 @@ const SvgBarLineChartCompact = ({ data }) => {
|
|
|
150
150
|
fill="#333"
|
|
151
151
|
fontWeight="700"
|
|
152
152
|
>
|
|
153
|
-
|
|
153
|
+
{formatNumber(budget)}
|
|
154
154
|
</SvgText>
|
|
155
155
|
|
|
156
156
|
{/* X Axis Label - Rotated */}
|
|
@@ -9,8 +9,7 @@ import {
|
|
|
9
9
|
Dimensions,
|
|
10
10
|
ActivityIndicator,
|
|
11
11
|
} from 'react-native';
|
|
12
|
-
import {
|
|
13
|
-
import Svg from 'react-native-svg'; // if not imported globally
|
|
12
|
+
import Svg, { Path } from 'react-native-svg';
|
|
14
13
|
import { filterChartByMonths } from '../utils/filterChartByMonths';
|
|
15
14
|
import { getDivisions, getTable, getLine, getBar } from '../api/report2Fetcher';
|
|
16
15
|
import MonthFilterModal from '../components/MonthFilterModal';
|
|
@@ -18,57 +17,33 @@ import DivisionFilterModal from '../components/DivisionFilterModal';
|
|
|
18
17
|
import FrozenTableReport2A from '../components/FrozenTableReport2A';
|
|
19
18
|
import SvgLineChartCompact from '../components/SvgLineChartCompact';
|
|
20
19
|
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
20
|
|
|
36
21
|
const Report2AScreen = ({ api, token, onBack }) => {
|
|
37
22
|
const [monthsModal, setMonthsModal] = useState(false);
|
|
38
23
|
const [divisionModal, setDivisionModal] = useState(false);
|
|
24
|
+
const [fullscreen, setFullscreen] = useState(false);
|
|
39
25
|
|
|
40
26
|
const [months, setMonths] = useState([]);
|
|
41
27
|
const [selectedMonths, setSelectedMonths] = useState([]);
|
|
42
28
|
const [divisions, setDivisions] = useState([]);
|
|
43
29
|
const [division, setDivision] = useState(null);
|
|
44
|
-
const [fullscreen, setFullscreen] = useState(false);
|
|
45
|
-
const [zoomScale, setZoomScale] = useState(1);
|
|
46
30
|
|
|
47
31
|
const [table, setTable] = useState(null);
|
|
48
32
|
const [line, setLine] = useState(null);
|
|
49
33
|
const [bar, setBar] = useState(null);
|
|
50
34
|
const [loading, setLoading] = useState(true);
|
|
51
|
-
const [error, setError] = useState(null);
|
|
52
35
|
|
|
53
|
-
|
|
54
36
|
useEffect(() => {
|
|
55
|
-
if (!api?.divisions || !token) return;
|
|
56
|
-
|
|
57
37
|
getDivisions(api.divisions, token)
|
|
58
38
|
.then((d) => {
|
|
59
39
|
setDivisions(d);
|
|
60
40
|
if (d.length > 0) setDivision(d[0].code);
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
.finally(() => setLoading(false));
|
|
64
|
-
}, [api?.divisions, token]);
|
|
41
|
+
});
|
|
42
|
+
}, [api.divisions, token]);
|
|
65
43
|
|
|
66
44
|
useEffect(() => {
|
|
67
|
-
if (!division
|
|
68
|
-
|
|
45
|
+
if (!division) return;
|
|
69
46
|
setLoading(true);
|
|
70
|
-
setError(null);
|
|
71
|
-
|
|
72
47
|
Promise.all([
|
|
73
48
|
getTable(api.table, division, token),
|
|
74
49
|
getLine(api.line, division, token),
|
|
@@ -81,73 +56,63 @@ const Report2AScreen = ({ api, token, onBack }) => {
|
|
|
81
56
|
setMonths(l.labels || []);
|
|
82
57
|
setSelectedMonths(l.labels || []);
|
|
83
58
|
})
|
|
84
|
-
.catch(() => setError('Failed to load data'))
|
|
85
59
|
.finally(() => setLoading(false));
|
|
86
|
-
}, [division
|
|
60
|
+
}, [division]);
|
|
87
61
|
|
|
88
62
|
if (loading) {
|
|
89
63
|
return (
|
|
90
64
|
<View style={styles.center}>
|
|
91
65
|
<ActivityIndicator size="large" color="#000" />
|
|
92
|
-
<Text style={styles.loadingText}>Loading...</Text>
|
|
93
66
|
</View>
|
|
94
67
|
);
|
|
95
68
|
}
|
|
96
69
|
|
|
97
|
-
|
|
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
|
-
</View>
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (!table || !line || !bar || !division) {
|
|
109
|
-
return <View style={styles.center}><Text>No data available</Text></View>;
|
|
110
|
-
}
|
|
70
|
+
const currentDivisionName = divisions.find(d => d.code === division)?.displayName || 'Division';
|
|
111
71
|
|
|
112
|
-
const currentDivisionName = divisions.find(d => d.code === division)?.displayName || 'Division';
|
|
113
72
|
const filteredRows = table.rows.filter(r => selectedMonths.includes(r.monthLabel));
|
|
114
73
|
const filteredLine = filterChartByMonths(line, selectedMonths);
|
|
115
74
|
const filteredBar = filterChartByMonths(bar, selectedMonths);
|
|
116
75
|
|
|
76
|
+
const { width, height } = Dimensions.get('window');
|
|
77
|
+
const isLandscape = width > height;
|
|
78
|
+
|
|
117
79
|
return (
|
|
118
80
|
<View style={styles.container}>
|
|
119
|
-
{/* HEADER
|
|
81
|
+
{/* HEADER */}
|
|
120
82
|
<View style={styles.header}>
|
|
121
|
-
<TouchableOpacity onPress={onBack}
|
|
122
|
-
<Text style={styles.backIcon}
|
|
83
|
+
<TouchableOpacity onPress={onBack}>
|
|
84
|
+
<Text style={styles.backIcon}>←</Text>
|
|
123
85
|
</TouchableOpacity>
|
|
124
|
-
|
|
125
|
-
<Text style={styles.headerTitle}>
|
|
126
|
-
{currentDivisionName} Transportation
|
|
127
|
-
</Text>
|
|
86
|
+
<Text style={styles.headerTitle}>{currentDivisionName} Transportation</Text>
|
|
128
87
|
</View>
|
|
129
88
|
|
|
130
|
-
{/* FILTERS
|
|
89
|
+
{/* FILTERS */}
|
|
131
90
|
<View style={styles.filterBar}>
|
|
132
91
|
<TouchableOpacity onPress={() => setMonthsModal(true)} style={styles.filterItem}>
|
|
133
|
-
<
|
|
92
|
+
<Svg width={22} height={22} viewBox="0 0 24 24" fill="none">
|
|
93
|
+
<Path d="M20 4H4V6.5L12 14.5L20 6.5V4Z" stroke="#000" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
|
|
94
|
+
<Path d="M8 11V19L16 19V11" stroke="#000" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
|
|
95
|
+
</Svg>
|
|
134
96
|
<Text style={styles.filterText}>Months ({selectedMonths.length}/{months.length})</Text>
|
|
135
97
|
</TouchableOpacity>
|
|
136
98
|
|
|
137
99
|
<TouchableOpacity onPress={() => setDivisionModal(true)} style={styles.filterItem}>
|
|
138
|
-
<
|
|
100
|
+
<Svg width={22} height={22} viewBox="0 0 24 24" fill="none">
|
|
101
|
+
<Path d="M20 4H4V6.5L12 14.5L20 6.5V4Z" stroke="#000" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
|
|
102
|
+
<Path d="M8 11V19L16 19V11" stroke="#000" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
|
|
103
|
+
</Svg>
|
|
139
104
|
<Text style={styles.filterText}>{currentDivisionName}</Text>
|
|
140
105
|
</TouchableOpacity>
|
|
141
106
|
</View>
|
|
142
107
|
|
|
143
108
|
<ScrollView style={styles.content}>
|
|
144
|
-
{/* FULL SCREEN
|
|
145
|
-
<TouchableOpacity
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
<Text style={styles.fullScreenText}>Full Screen
|
|
109
|
+
{/* FULL SCREEN BUTTON */}
|
|
110
|
+
<TouchableOpacity onPress={() => setFullscreen(true)} style={styles.fullScreenBtn}>
|
|
111
|
+
<Svg width={28} height={28} viewBox="0 0 24 24" fill="none">
|
|
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
|
+
stroke="#000" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
|
|
114
|
+
</Svg>
|
|
115
|
+
<Text style={styles.fullScreenText}>Full Screen Table</Text>
|
|
151
116
|
</TouchableOpacity>
|
|
152
117
|
|
|
153
118
|
<FrozenTableReport2A rows={filteredRows} />
|
|
@@ -161,60 +126,48 @@ const currentDivisionName = divisions.find(d => d.code === division)?.displayNam
|
|
|
161
126
|
</View>
|
|
162
127
|
</ScrollView>
|
|
163
128
|
|
|
164
|
-
{/*
|
|
165
|
-
|
|
166
|
-
{/* FULL SCREEN MODAL */}
|
|
167
|
-
<Modal visible={fullscreen} supportedOrientations={['portrait', 'landscape']}>
|
|
129
|
+
{/* FULL SCREEN MODAL - PERFECT LANDSCAPE FIT + ZOOM */}
|
|
130
|
+
<Modal visible={fullscreen} supportedOrientations={['portrait', 'landscape']} animationType="fade">
|
|
168
131
|
<View style={styles.fullModal}>
|
|
132
|
+
{/* TOP BAR - ONE LINE */}
|
|
169
133
|
<View style={styles.fullHeader}>
|
|
134
|
+
<Text style={styles.fullHintText}>
|
|
135
|
+
Rotate device • Pinch to zoom
|
|
136
|
+
</Text>
|
|
170
137
|
<TouchableOpacity onPress={() => setFullscreen(false)}>
|
|
171
|
-
<Text style={styles.
|
|
138
|
+
<Text style={styles.closeBtn}>✕</Text>
|
|
172
139
|
</TouchableOpacity>
|
|
173
|
-
<Text style={styles.fullTitle}>Rotate device to landscape for best view</Text>
|
|
174
140
|
</View>
|
|
175
141
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
142
|
+
{/* ZOOMABLE & FIT TABLE */}
|
|
143
|
+
<ScrollView
|
|
144
|
+
maximumZoomScale={3}
|
|
145
|
+
minimumZoomScale={0.7}
|
|
146
|
+
showsHorizontalScrollIndicator={false}
|
|
147
|
+
showsVerticalScrollIndicator={false}
|
|
148
|
+
contentContainerStyle={styles.zoomContent}
|
|
149
|
+
pinchGestureEnabled={true}
|
|
150
|
+
bouncesZoom={true}
|
|
183
151
|
>
|
|
184
|
-
<View style={
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
152
|
+
<View style={[
|
|
153
|
+
styles.tableWrapper,
|
|
154
|
+
isLandscape && styles.landscapeFit
|
|
155
|
+
]}>
|
|
156
|
+
<FrozenTableReport2A rows={table.rows} isFullscreen />
|
|
188
157
|
</View>
|
|
189
|
-
</
|
|
158
|
+
</ScrollView>
|
|
190
159
|
</View>
|
|
191
160
|
</Modal>
|
|
192
161
|
|
|
193
|
-
<MonthFilterModal
|
|
194
|
-
|
|
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
|
-
/>
|
|
162
|
+
<MonthFilterModal visible={monthsModal} months={months} selected={selectedMonths} onApply={setSelectedMonths} onClose={() => setMonthsModal(false)} />
|
|
163
|
+
<DivisionFilterModal visible={divisionModal} divisions={divisions} selected={division} onSelect={setDivision} onClose={() => setDivisionModal(false)} />
|
|
208
164
|
</View>
|
|
209
165
|
);
|
|
210
166
|
};
|
|
211
167
|
|
|
212
168
|
const styles = StyleSheet.create({
|
|
213
169
|
container: { flex: 1, backgroundColor: '#f8f9fa' },
|
|
214
|
-
center: { flex: 1, justifyContent: 'center', alignItems: 'center'
|
|
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' },
|
|
170
|
+
center: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
|
218
171
|
|
|
219
172
|
header: {
|
|
220
173
|
flexDirection: 'row',
|
|
@@ -225,15 +178,8 @@ const styles = StyleSheet.create({
|
|
|
225
178
|
borderBottomWidth: 1,
|
|
226
179
|
borderColor: '#eee',
|
|
227
180
|
},
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
headerTitle: {
|
|
231
|
-
fontSize: 19,
|
|
232
|
-
fontWeight: '700',
|
|
233
|
-
color: '#000',
|
|
234
|
-
flex: 1,
|
|
235
|
-
textAlign: 'center',
|
|
236
|
-
},
|
|
181
|
+
backIcon: { fontSize: 28, color: '#000' },
|
|
182
|
+
headerTitle: { fontSize: 19, fontWeight: '700', color: '#000', flex: 1, textAlign: 'center' },
|
|
237
183
|
|
|
238
184
|
filterBar: {
|
|
239
185
|
flexDirection: 'row',
|
|
@@ -243,75 +189,60 @@ const styles = StyleSheet.create({
|
|
|
243
189
|
borderBottomWidth: 1,
|
|
244
190
|
borderColor: '#eee',
|
|
245
191
|
},
|
|
246
|
-
filterItem: {
|
|
192
|
+
filterItem: { flexDirection: 'row', alignItems: 'center' },
|
|
193
|
+
filterText: { fontSize: 16, color: '#000', fontWeight: '600', marginLeft: 8 },
|
|
194
|
+
|
|
195
|
+
fullScreenBtn: {
|
|
196
|
+
flexDirection: 'row',
|
|
197
|
+
alignItems: 'center',
|
|
198
|
+
justifyContent: 'center',
|
|
199
|
+
padding: 14,
|
|
200
|
+
backgroundColor: '#f0f0f0',
|
|
201
|
+
borderRadius: 8,
|
|
202
|
+
marginVertical: 12,
|
|
203
|
+
},
|
|
204
|
+
fullScreenText: { marginLeft: 10, fontSize: 16, color: '#000', fontWeight: '600' },
|
|
205
|
+
|
|
206
|
+
content: { flex: 1, padding: 12 },
|
|
207
|
+
chartContainer: { borderWidth: 1, borderColor: '#ddd', borderRadius: 10, overflow: 'hidden', marginVertical: 12, backgroundColor: '#fff' },
|
|
208
|
+
|
|
209
|
+
fullModal: { flex: 1, backgroundColor: '#fff' },
|
|
210
|
+
fullHeader: {
|
|
247
211
|
flexDirection: 'row',
|
|
212
|
+
justifyContent: 'center',
|
|
248
213
|
alignItems: 'center',
|
|
249
|
-
|
|
250
|
-
|
|
214
|
+
paddingVertical: 16,
|
|
215
|
+
paddingHorizontal: 20,
|
|
216
|
+
backgroundColor: '#f8f8f8',
|
|
217
|
+
borderBottomWidth: 1,
|
|
218
|
+
borderColor: '#ddd',
|
|
251
219
|
},
|
|
252
|
-
|
|
220
|
+
fullHintText: {
|
|
253
221
|
fontSize: 16,
|
|
254
222
|
color: '#000',
|
|
255
223
|
fontWeight: '600',
|
|
256
|
-
|
|
224
|
+
textAlign: 'center',
|
|
225
|
+
flex: 1,
|
|
257
226
|
},
|
|
227
|
+
closeBtn: { fontSize: 28, color: '#000', paddingLeft: 20 },
|
|
258
228
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
width: 20,
|
|
262
|
-
height: 20,
|
|
229
|
+
zoomContent: {
|
|
230
|
+
flexGrow: 1,
|
|
263
231
|
justifyContent: 'center',
|
|
264
232
|
alignItems: 'center',
|
|
233
|
+
padding: 20,
|
|
265
234
|
},
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
height:
|
|
269
|
-
|
|
270
|
-
|
|
235
|
+
tableWrapper: {
|
|
236
|
+
shadowColor: '#000',
|
|
237
|
+
shadowOffset: { width: 0, height: 2 },
|
|
238
|
+
shadowOpacity: 0.1,
|
|
239
|
+
shadowRadius: 8,
|
|
240
|
+
elevation: 5,
|
|
271
241
|
},
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
borderLeftWidth: 4,
|
|
276
|
-
borderRightWidth: 4,
|
|
277
|
-
borderBottomWidth: 8,
|
|
278
|
-
borderColor: 'transparent',
|
|
279
|
-
borderBottomColor: '#000',
|
|
280
|
-
marginTop: -2,
|
|
242
|
+
landscapeFit: {
|
|
243
|
+
transform: [{ scale: 1.4 }], // Perfect fit in landscape
|
|
244
|
+
maxWidth: '100%',
|
|
281
245
|
},
|
|
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 },
|
|
293
|
-
chartContainer: {
|
|
294
|
-
borderWidth: 1,
|
|
295
|
-
borderColor: '#ddd',
|
|
296
|
-
borderRadius: 10,
|
|
297
|
-
overflow: 'hidden',
|
|
298
|
-
marginVertical: 12,
|
|
299
|
-
backgroundColor: '#fff',
|
|
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' },
|
|
315
246
|
});
|
|
316
247
|
|
|
317
248
|
export default Report2AScreen;
|