@dhiraj0720/report1chart 1.0.6 → 2.1.1
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 +48 -8
- package/src/api/ApiService.jsx +180 -0
- package/src/api/ApiService.test.jsx +27 -0
- package/src/components/charts/BarChart.jsx +121 -0
- package/src/components/charts/ChartUtils.jsx +38 -0
- package/src/components/charts/LineChart.jsx +142 -0
- package/src/components/charts/PieChart.jsx +125 -0
- package/src/components/common/ErrorDisplay.jsx +51 -0
- package/src/components/common/LoadingSpinner.jsx +32 -0
- package/src/components/common/ReportCard.jsx +98 -0
- package/src/components/tables/CompactTable.jsx +269 -0
- package/src/components/tables/DefaultTable.jsx +225 -0
- package/src/components/tables/FreezeTable.jsx +264 -0
- package/src/components/tables/TableUtils.jsx +47 -0
- package/src/index.jsx +9 -1349
- package/src/screens/ReportDashboard.jsx +132 -0
- package/src/screens/ReportDetail.jsx +265 -0
- package/src/utils/Constants.jsx +38 -0
- package/src/utils/Formatters.jsx +24 -0
- package/src/utils/Validators.jsx +60 -0
package/src/index.jsx
CHANGED
|
@@ -1,1358 +1,18 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import
|
|
3
|
-
View,
|
|
4
|
-
Text,
|
|
5
|
-
StyleSheet,
|
|
6
|
-
TouchableOpacity,
|
|
7
|
-
ScrollView,
|
|
8
|
-
ActivityIndicator,
|
|
9
|
-
Dimensions,
|
|
10
|
-
} from 'react-native';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReportDashboard from './screens/ReportDashboard';
|
|
11
3
|
|
|
12
4
|
const ReportChart = ({
|
|
5
|
+
reports = [],
|
|
13
6
|
onReportSelect,
|
|
14
|
-
onClose
|
|
15
|
-
reports = [],
|
|
16
|
-
onFetchReportData
|
|
7
|
+
onClose
|
|
17
8
|
}) => {
|
|
18
|
-
const [selectedReport, setSelectedReport] = useState(null);
|
|
19
|
-
const [reportData, setReportData] = useState(null);
|
|
20
|
-
const [loading, setLoading] = useState(false);
|
|
21
|
-
const [error, setError] = useState(null);
|
|
22
|
-
const [viewType, setViewType] = useState('default');
|
|
23
|
-
|
|
24
|
-
// Default reports if none provided
|
|
25
|
-
const defaultReports = [
|
|
26
|
-
{
|
|
27
|
-
id: 1,
|
|
28
|
-
name: 'Performance Report',
|
|
29
|
-
title: 'PERFORMANS RAPORU (USD)',
|
|
30
|
-
type: 'table',
|
|
31
|
-
description: 'Fallyet Kar/Zarar tablosu - Varsayılan Görünüm'
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
id: 1.1,
|
|
35
|
-
name: 'Performance Report A',
|
|
36
|
-
title: 'PERFORMANS RAPORU (USD)',
|
|
37
|
-
type: 'table',
|
|
38
|
-
description: 'Fallyet Kar/Zarar tablosu - Kompakt Görünüm'
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
id: 1.2,
|
|
42
|
-
name: 'Performance Report B',
|
|
43
|
-
title: 'PERFORMANS RAPORU (USD)',
|
|
44
|
-
type: 'table',
|
|
45
|
-
description: 'Fallyet Kar/Zarar tablosu - Sabit Kolonlu Görünüm'
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
id: 2,
|
|
49
|
-
name: 'Revenue by Mode',
|
|
50
|
-
title: 'Revenue by Mode',
|
|
51
|
-
type: 'bar',
|
|
52
|
-
description: 'Revenue distribution across modes'
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
id: 3,
|
|
56
|
-
name: 'Shipment by Direction',
|
|
57
|
-
title: 'Shipment by Direction',
|
|
58
|
-
type: 'pie',
|
|
59
|
-
description: 'Shipment direction analysis'
|
|
60
|
-
},
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
const reportList = reports.length > 0 ? reports : defaultReports;
|
|
64
|
-
|
|
65
|
-
const handleReportClick = async (report) => {
|
|
66
|
-
setLoading(true);
|
|
67
|
-
setError(null);
|
|
68
|
-
setReportData(null);
|
|
69
|
-
setSelectedReport(report);
|
|
70
|
-
setViewType('default'); // Reset view type when selecting new report
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
if (onFetchReportData) {
|
|
74
|
-
const data = await onFetchReportData(report);
|
|
75
|
-
if (data) {
|
|
76
|
-
setReportData({
|
|
77
|
-
...data,
|
|
78
|
-
title: report.title || report.name,
|
|
79
|
-
type: report.type || 'bar',
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (onReportSelect) {
|
|
85
|
-
onReportSelect(report.name);
|
|
86
|
-
}
|
|
87
|
-
} catch (err) {
|
|
88
|
-
setError(err.message || 'Failed to fetch report data');
|
|
89
|
-
} finally {
|
|
90
|
-
setLoading(false);
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const formatCurrency = (value) => {
|
|
95
|
-
const num = Number(value);
|
|
96
|
-
if (isNaN(num)) return '0';
|
|
97
|
-
|
|
98
|
-
if (num >= 1000000) {
|
|
99
|
-
return `${(num / 1000000).toFixed(1)}M`;
|
|
100
|
-
} else if (num >= 1000) {
|
|
101
|
-
return `${(num / 1000).toFixed(1)}K`;
|
|
102
|
-
}
|
|
103
|
-
return num.toLocaleString();
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const formatNumber = (num) => {
|
|
107
|
-
return num.toLocaleString();
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const getColor = (index) => {
|
|
111
|
-
const colors = ['#4CAF50', '#2196F3', '#FF9800', '#E91E63', '#9C27B0', '#00BCD4'];
|
|
112
|
-
return colors[index % colors.length];
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const handleClose = () => {
|
|
116
|
-
setSelectedReport(null);
|
|
117
|
-
setReportData(null);
|
|
118
|
-
if (onClose) onClose();
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
// ========== TABLE RENDERERS ==========
|
|
122
|
-
const renderTable = (data) => {
|
|
123
|
-
if (!Array.isArray(data) || data.length === 0) {
|
|
124
|
-
return (
|
|
125
|
-
<View style={styles.noDataContainer}>
|
|
126
|
-
<Text style={styles.noDataText}>No data available for this report</Text>
|
|
127
|
-
</View>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return (
|
|
132
|
-
<ScrollView horizontal showsHorizontalScrollIndicator={true}>
|
|
133
|
-
<View style={styles.table}>
|
|
134
|
-
{/* Table Header */}
|
|
135
|
-
<View style={styles.tableHeader}>
|
|
136
|
-
<View style={[styles.headerCell, styles.firstColumn]}>
|
|
137
|
-
<Text style={styles.headerText}>FALİYET KAR/ZARAR</Text>
|
|
138
|
-
</View>
|
|
139
|
-
<View style={styles.headerCell}>
|
|
140
|
-
<Text style={styles.headerText}>2024 Fiili</Text>
|
|
141
|
-
</View>
|
|
142
|
-
<View style={styles.headerCell}>
|
|
143
|
-
<Text style={styles.headerText}>2025 Fiili</Text>
|
|
144
|
-
</View>
|
|
145
|
-
<View style={styles.headerCell}>
|
|
146
|
-
<Text style={styles.headerText}>Fiili Artış %</Text>
|
|
147
|
-
</View>
|
|
148
|
-
<View style={styles.headerCell}>
|
|
149
|
-
<Text style={styles.headerText}>2025 Bütçe</Text>
|
|
150
|
-
</View>
|
|
151
|
-
<View style={styles.headerCell}>
|
|
152
|
-
<Text style={styles.headerText}>Sapma %</Text>
|
|
153
|
-
</View>
|
|
154
|
-
<View style={styles.headerCell}>
|
|
155
|
-
<Text style={styles.headerText}>BRÜT KAR %</Text>
|
|
156
|
-
</View>
|
|
157
|
-
</View>
|
|
158
|
-
|
|
159
|
-
{/* Table Rows */}
|
|
160
|
-
{data.map((item, index) => (
|
|
161
|
-
<View
|
|
162
|
-
key={index}
|
|
163
|
-
style={[
|
|
164
|
-
styles.tableRow,
|
|
165
|
-
item.isTotal ? styles.totalRow : (index % 2 === 0 ? styles.evenRow : styles.oddRow)
|
|
166
|
-
]}
|
|
167
|
-
>
|
|
168
|
-
<View style={[styles.cell, styles.firstColumn]}>
|
|
169
|
-
<Text style={item.isTotal ? styles.totalText : styles.cellText}>
|
|
170
|
-
{item.name}
|
|
171
|
-
</Text>
|
|
172
|
-
</View>
|
|
173
|
-
|
|
174
|
-
<View style={styles.cell}>
|
|
175
|
-
<Text style={styles.numberText}>
|
|
176
|
-
{formatCurrency(item.actual2024)}
|
|
177
|
-
</Text>
|
|
178
|
-
</View>
|
|
179
|
-
|
|
180
|
-
<View style={styles.cell}>
|
|
181
|
-
<Text style={styles.numberText}>
|
|
182
|
-
{formatCurrency(item.actual2025)}
|
|
183
|
-
</Text>
|
|
184
|
-
</View>
|
|
185
|
-
|
|
186
|
-
<View style={styles.cell}>
|
|
187
|
-
<Text style={[
|
|
188
|
-
styles.percentText,
|
|
189
|
-
item.yoYChangePercent >= 0 ? styles.positive : styles.negative
|
|
190
|
-
]}>
|
|
191
|
-
{item.yoYChangePercent >= 0 ? '↑' : '↓'} {Math.abs(item.yoYChangePercent)}%
|
|
192
|
-
</Text>
|
|
193
|
-
</View>
|
|
194
|
-
|
|
195
|
-
<View style={styles.cell}>
|
|
196
|
-
<Text style={styles.numberText}>
|
|
197
|
-
{formatCurrency(item.budget2025)}
|
|
198
|
-
</Text>
|
|
199
|
-
</View>
|
|
200
|
-
|
|
201
|
-
<View style={styles.cell}>
|
|
202
|
-
<Text style={[
|
|
203
|
-
styles.percentText,
|
|
204
|
-
item.budgetVariancePercent >= 0 ? styles.positive : styles.negative
|
|
205
|
-
]}>
|
|
206
|
-
{item.budgetVariancePercent >= 0 ? '↑' : '↓'} {Math.abs(item.budgetVariancePercent)}%
|
|
207
|
-
</Text>
|
|
208
|
-
</View>
|
|
209
|
-
|
|
210
|
-
<View style={styles.cell}>
|
|
211
|
-
<Text style={[
|
|
212
|
-
styles.percentText,
|
|
213
|
-
styles.grossMargin
|
|
214
|
-
]}>
|
|
215
|
-
{item.grossMarginPercent}%
|
|
216
|
-
</Text>
|
|
217
|
-
</View>
|
|
218
|
-
</View>
|
|
219
|
-
))}
|
|
220
|
-
</View>
|
|
221
|
-
</ScrollView>
|
|
222
|
-
);
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
const renderCompactTable = (data) => {
|
|
226
|
-
if (!Array.isArray(data) || data.length === 0) {
|
|
227
|
-
return (
|
|
228
|
-
<View style={styles.noDataContainer}>
|
|
229
|
-
<Text style={styles.noDataText}>No data available for this report</Text>
|
|
230
|
-
</View>
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return (
|
|
235
|
-
<ScrollView style={styles.compactContainer}>
|
|
236
|
-
{data.map((item, index) => (
|
|
237
|
-
<View
|
|
238
|
-
key={index}
|
|
239
|
-
style={[
|
|
240
|
-
styles.compactCard,
|
|
241
|
-
item.isTotal && styles.compactTotalCard,
|
|
242
|
-
index % 2 === 0 ? styles.evenCard : styles.oddCard
|
|
243
|
-
]}
|
|
244
|
-
>
|
|
245
|
-
{/* Card Header */}
|
|
246
|
-
<View style={styles.compactHeader}>
|
|
247
|
-
<Text style={[
|
|
248
|
-
styles.compactTitle,
|
|
249
|
-
item.isTotal && styles.totalTitle
|
|
250
|
-
]}>
|
|
251
|
-
{item.name}
|
|
252
|
-
</Text>
|
|
253
|
-
<Text style={[
|
|
254
|
-
styles.compactGrossMargin,
|
|
255
|
-
styles.grossMargin
|
|
256
|
-
]}>
|
|
257
|
-
Brüt Kar: {item.grossMarginPercent}%
|
|
258
|
-
</Text>
|
|
259
|
-
</View>
|
|
260
|
-
|
|
261
|
-
{/* Card Body - Grid Layout */}
|
|
262
|
-
<View style={styles.compactGrid}>
|
|
263
|
-
{/* Column 1 */}
|
|
264
|
-
<View style={styles.compactColumn}>
|
|
265
|
-
<Text style={styles.compactLabel}>2024 Fiili</Text>
|
|
266
|
-
<Text style={styles.compactValue}>
|
|
267
|
-
{formatCurrency(item.actual2024)}
|
|
268
|
-
</Text>
|
|
269
|
-
</View>
|
|
270
|
-
|
|
271
|
-
{/* Column 2 */}
|
|
272
|
-
<View style={styles.compactColumn}>
|
|
273
|
-
<Text style={styles.compactLabel}>2025 Fiili</Text>
|
|
274
|
-
<Text style={styles.compactValue}>
|
|
275
|
-
{formatCurrency(item.actual2025)}
|
|
276
|
-
</Text>
|
|
277
|
-
</View>
|
|
278
|
-
|
|
279
|
-
{/* Column 3 */}
|
|
280
|
-
<View style={styles.compactColumn}>
|
|
281
|
-
<Text style={styles.compactLabel}>Fiili Artış</Text>
|
|
282
|
-
<View style={styles.percentBadge}>
|
|
283
|
-
<Text style={[
|
|
284
|
-
styles.percentText,
|
|
285
|
-
item.yoYChangePercent >= 0 ? styles.positive : styles.negative
|
|
286
|
-
]}>
|
|
287
|
-
{item.yoYChangePercent >= 0 ? '↑' : '↓'} {Math.abs(item.yoYChangePercent)}%
|
|
288
|
-
</Text>
|
|
289
|
-
</View>
|
|
290
|
-
</View>
|
|
291
|
-
|
|
292
|
-
{/* Column 4 */}
|
|
293
|
-
<View style={styles.compactColumn}>
|
|
294
|
-
<Text style={styles.compactLabel}>2025 Bütçe</Text>
|
|
295
|
-
<Text style={styles.compactValue}>
|
|
296
|
-
{formatCurrency(item.budget2025)}
|
|
297
|
-
</Text>
|
|
298
|
-
</View>
|
|
299
|
-
|
|
300
|
-
{/* Column 5 */}
|
|
301
|
-
<View style={styles.compactColumn}>
|
|
302
|
-
<Text style={styles.compactLabel}>Sapma</Text>
|
|
303
|
-
<View style={styles.percentBadge}>
|
|
304
|
-
<Text style={[
|
|
305
|
-
styles.percentText,
|
|
306
|
-
item.budgetVariancePercent >= 0 ? styles.positive : styles.negative
|
|
307
|
-
]}>
|
|
308
|
-
{item.budgetVariancePercent >= 0 ? '↑' : '↓'} {Math.abs(item.budgetVariancePercent)}%
|
|
309
|
-
</Text>
|
|
310
|
-
</View>
|
|
311
|
-
</View>
|
|
312
|
-
</View>
|
|
313
|
-
|
|
314
|
-
{/* Status Indicator */}
|
|
315
|
-
<View style={styles.statusIndicator}>
|
|
316
|
-
<View style={[
|
|
317
|
-
styles.statusDot,
|
|
318
|
-
(item.yoYChangePercent >= 0 && item.budgetVariancePercent >= 0)
|
|
319
|
-
? styles.statusGood
|
|
320
|
-
: styles.statusWarning
|
|
321
|
-
]} />
|
|
322
|
-
<Text style={styles.statusText}>
|
|
323
|
-
{item.yoYChangePercent >= 0 && item.budgetVariancePercent >= 0
|
|
324
|
-
? 'Performans İyi'
|
|
325
|
-
: 'İnceleme Gerekli'}
|
|
326
|
-
</Text>
|
|
327
|
-
</View>
|
|
328
|
-
</View>
|
|
329
|
-
))}
|
|
330
|
-
</ScrollView>
|
|
331
|
-
);
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
const renderFreezeTable = (data) => {
|
|
335
|
-
if (!Array.isArray(data) || data.length === 0) {
|
|
336
|
-
return (
|
|
337
|
-
<View style={styles.noDataContainer}>
|
|
338
|
-
<Text style={styles.noDataText}>No data available for this report</Text>
|
|
339
|
-
</View>
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return (
|
|
344
|
-
<View style={styles.freezeContainer}>
|
|
345
|
-
{/* Fixed First Column */}
|
|
346
|
-
<View style={styles.freezeColumn}>
|
|
347
|
-
<View style={[styles.freezeHeader, styles.freezeFirstCell]}>
|
|
348
|
-
<Text style={styles.freezeHeaderText}>FALİYET KAR/ZARAR</Text>
|
|
349
|
-
</View>
|
|
350
|
-
{data.map((item, index) => (
|
|
351
|
-
<View
|
|
352
|
-
key={index}
|
|
353
|
-
style={[
|
|
354
|
-
styles.freezeRow,
|
|
355
|
-
item.isTotal && styles.freezeTotalRow,
|
|
356
|
-
index % 2 === 0 ? styles.evenRow : styles.oddRow
|
|
357
|
-
]}
|
|
358
|
-
>
|
|
359
|
-
<Text style={[
|
|
360
|
-
styles.freezeCellText,
|
|
361
|
-
item.isTotal && styles.freezeTotalText
|
|
362
|
-
]}>
|
|
363
|
-
{item.name}
|
|
364
|
-
</Text>
|
|
365
|
-
</View>
|
|
366
|
-
))}
|
|
367
|
-
</View>
|
|
368
|
-
|
|
369
|
-
{/* Scrollable Data Columns */}
|
|
370
|
-
<ScrollView horizontal showsHorizontalScrollIndicator={true}>
|
|
371
|
-
<View style={styles.scrollableColumns}>
|
|
372
|
-
{/* Headers for scrollable columns */}
|
|
373
|
-
<View style={styles.freezeHeaderRow}>
|
|
374
|
-
{['2024 Fiili', '2025 Fiili', 'Fiili Artış %', '2025 Bütçe', 'Sapma %', 'BRÜT KAR %'].map((header, idx) => (
|
|
375
|
-
<View key={idx} style={styles.freezeHeader}>
|
|
376
|
-
<Text style={styles.freezeHeaderText}>{header}</Text>
|
|
377
|
-
</View>
|
|
378
|
-
))}
|
|
379
|
-
</View>
|
|
380
|
-
|
|
381
|
-
{/* Data rows for scrollable columns */}
|
|
382
|
-
{data.map((item, rowIndex) => (
|
|
383
|
-
<View
|
|
384
|
-
key={rowIndex}
|
|
385
|
-
style={[
|
|
386
|
-
styles.freezeDataRow,
|
|
387
|
-
item.isTotal && styles.freezeTotalDataRow,
|
|
388
|
-
rowIndex % 2 === 0 ? styles.evenRow : styles.oddRow
|
|
389
|
-
]}
|
|
390
|
-
>
|
|
391
|
-
<View style={styles.freezeDataCell}>
|
|
392
|
-
<Text style={styles.freezeDataText}>
|
|
393
|
-
{formatCurrency(item.actual2024)}
|
|
394
|
-
</Text>
|
|
395
|
-
</View>
|
|
396
|
-
|
|
397
|
-
<View style={styles.freezeDataCell}>
|
|
398
|
-
<Text style={styles.freezeDataText}>
|
|
399
|
-
{formatCurrency(item.actual2025)}
|
|
400
|
-
</Text>
|
|
401
|
-
</View>
|
|
402
|
-
|
|
403
|
-
<View style={styles.freezeDataCell}>
|
|
404
|
-
<Text style={[
|
|
405
|
-
styles.freezePercent,
|
|
406
|
-
item.yoYChangePercent >= 0 ? styles.positive : styles.negative
|
|
407
|
-
]}>
|
|
408
|
-
{item.yoYChangePercent >= 0 ? '↑' : '↓'} {Math.abs(item.yoYChangePercent)}%
|
|
409
|
-
</Text>
|
|
410
|
-
</View>
|
|
411
|
-
|
|
412
|
-
<View style={styles.freezeDataCell}>
|
|
413
|
-
<Text style={styles.freezeDataText}>
|
|
414
|
-
{formatCurrency(item.budget2025)}
|
|
415
|
-
</Text>
|
|
416
|
-
</View>
|
|
417
|
-
|
|
418
|
-
<View style={styles.freezeDataCell}>
|
|
419
|
-
<Text style={[
|
|
420
|
-
styles.freezePercent,
|
|
421
|
-
item.budgetVariancePercent >= 0 ? styles.positive : styles.negative
|
|
422
|
-
]}>
|
|
423
|
-
{item.budgetVariancePercent >= 0 ? '↑' : '↓'} {Math.abs(item.budgetVariancePercent)}%
|
|
424
|
-
</Text>
|
|
425
|
-
</View>
|
|
426
|
-
|
|
427
|
-
<View style={styles.freezeDataCell}>
|
|
428
|
-
<Text style={[
|
|
429
|
-
styles.freezePercent,
|
|
430
|
-
styles.grossMargin
|
|
431
|
-
]}>
|
|
432
|
-
{item.grossMarginPercent}%
|
|
433
|
-
</Text>
|
|
434
|
-
</View>
|
|
435
|
-
</View>
|
|
436
|
-
))}
|
|
437
|
-
</View>
|
|
438
|
-
</ScrollView>
|
|
439
|
-
</View>
|
|
440
|
-
);
|
|
441
|
-
};
|
|
442
|
-
|
|
443
|
-
// ========== CHART RENDERERS ==========
|
|
444
|
-
const renderLineChart = (data) => {
|
|
445
|
-
const values = data.values || data.map(d => d.value || d.revenue || d.shipments || 0);
|
|
446
|
-
const labels = data.labels || data.map(d => d.label || d.month || d.mode || d.direction || '');
|
|
447
|
-
|
|
448
|
-
const maxValue = Math.max(...values, 1);
|
|
449
|
-
const chartHeight = 200;
|
|
450
|
-
const chartWidth = Dimensions.get('window').width - 64;
|
|
451
|
-
const barWidth = (chartWidth / values.length) - 10;
|
|
452
|
-
|
|
453
|
-
return (
|
|
454
|
-
<View style={styles.chartContainer}>
|
|
455
|
-
<Text style={styles.chartTitle}>{reportData?.title || ''}</Text>
|
|
456
|
-
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
|
|
457
|
-
<View style={[styles.chart, { height: chartHeight, width: chartWidth }]}>
|
|
458
|
-
{values.map((value, index) => {
|
|
459
|
-
const height = (value / maxValue) * chartHeight;
|
|
460
|
-
const label = labels[index] || `M${index + 1}`;
|
|
461
|
-
|
|
462
|
-
return (
|
|
463
|
-
<View key={index} style={styles.linePointWrapper}>
|
|
464
|
-
<View style={[styles.linePoint, {
|
|
465
|
-
height: Math.max(height, 2),
|
|
466
|
-
width: barWidth
|
|
467
|
-
}]} />
|
|
468
|
-
<View style={styles.lineConnector} />
|
|
469
|
-
<Text style={styles.lineLabel} numberOfLines={1}>{label}</Text>
|
|
470
|
-
<Text style={styles.valueText}>{formatNumber(value)}</Text>
|
|
471
|
-
</View>
|
|
472
|
-
);
|
|
473
|
-
})}
|
|
474
|
-
</View>
|
|
475
|
-
</ScrollView>
|
|
476
|
-
</View>
|
|
477
|
-
);
|
|
478
|
-
};
|
|
479
|
-
|
|
480
|
-
const renderBarChart = (data) => {
|
|
481
|
-
const isArray = Array.isArray(data);
|
|
482
|
-
const values = isArray ? data.map(d => d.revenue || d.value || d.shipments || 0) : data.values;
|
|
483
|
-
const labels = isArray ? data.map(d => d.mode || d.label || '') : data.labels;
|
|
484
|
-
|
|
485
|
-
const maxValue = Math.max(...values, 1);
|
|
486
|
-
const chartHeight = 200;
|
|
487
|
-
const chartWidth = Dimensions.get('window').width - 64;
|
|
488
|
-
const barWidth = Math.min(40, (chartWidth / values.length) - 10);
|
|
489
|
-
|
|
490
|
-
return (
|
|
491
|
-
<View style={styles.chartContainer}>
|
|
492
|
-
<Text style={styles.chartTitle}>{reportData?.title || ''}</Text>
|
|
493
|
-
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
|
|
494
|
-
<View style={[styles.chart, { height: chartHeight, width: chartWidth }]}>
|
|
495
|
-
{values.map((value, index) => {
|
|
496
|
-
const height = (value / maxValue) * chartHeight;
|
|
497
|
-
const label = labels?.[index] || `Item ${index + 1}`;
|
|
498
|
-
|
|
499
|
-
return (
|
|
500
|
-
<View key={index} style={styles.barWrapper}>
|
|
501
|
-
<View style={[styles.bar, {
|
|
502
|
-
height,
|
|
503
|
-
width: barWidth,
|
|
504
|
-
backgroundColor: getColor(index)
|
|
505
|
-
}]} />
|
|
506
|
-
<Text style={styles.barLabel} numberOfLines={1}>{label}</Text>
|
|
507
|
-
<Text style={styles.valueText}>{formatNumber(value)}</Text>
|
|
508
|
-
</View>
|
|
509
|
-
);
|
|
510
|
-
})}
|
|
511
|
-
</View>
|
|
512
|
-
</ScrollView>
|
|
513
|
-
</View>
|
|
514
|
-
);
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
const renderPieChart = (data) => {
|
|
518
|
-
const total = data.reduce((sum, item) => sum + (item.shipments || item.revenue || item.value || 0), 0);
|
|
519
|
-
|
|
520
|
-
return (
|
|
521
|
-
<View style={styles.chartContainer}>
|
|
522
|
-
<Text style={styles.chartTitle}>{reportData?.title || ''}</Text>
|
|
523
|
-
<View style={styles.pieContainer}>
|
|
524
|
-
{data.map((item, index) => {
|
|
525
|
-
const value = item.shipments || item.revenue || item.value || 0;
|
|
526
|
-
const label = item.direction || item.mode || item.label || `Item ${index + 1}`;
|
|
527
|
-
const percentage = total > 0 ? ((value / total) * 100).toFixed(1) : '0';
|
|
528
|
-
|
|
529
|
-
return (
|
|
530
|
-
<View key={index} style={styles.pieItem}>
|
|
531
|
-
<View style={[styles.pieColor, { backgroundColor: getColor(index) }]} />
|
|
532
|
-
<Text style={styles.pieLabel}>{label}:</Text>
|
|
533
|
-
<Text style={styles.pieValue}>
|
|
534
|
-
{formatNumber(value)} ({percentage}%)
|
|
535
|
-
</Text>
|
|
536
|
-
</View>
|
|
537
|
-
);
|
|
538
|
-
})}
|
|
539
|
-
</View>
|
|
540
|
-
{total > 0 && (
|
|
541
|
-
<Text style={styles.totalText}>Total: {formatNumber(total)}</Text>
|
|
542
|
-
)}
|
|
543
|
-
</View>
|
|
544
|
-
);
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
// ========== MAIN RENDER LOGIC ==========
|
|
548
|
-
const renderChart = () => {
|
|
549
|
-
if (!reportData || !reportData.data) {
|
|
550
|
-
return (
|
|
551
|
-
<View style={styles.noDataContainer}>
|
|
552
|
-
<Text style={styles.noDataText}>No data available for this report</Text>
|
|
553
|
-
</View>
|
|
554
|
-
);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
const { type, data } = reportData;
|
|
558
|
-
|
|
559
|
-
// For performance reports
|
|
560
|
-
if (type === 'table' && selectedReport &&
|
|
561
|
-
(selectedReport.id === 1 || selectedReport.id === 1.1 || selectedReport.id === 1.2)) {
|
|
562
|
-
switch (viewType) {
|
|
563
|
-
case 'compact':
|
|
564
|
-
return renderCompactTable(data);
|
|
565
|
-
case 'freeze':
|
|
566
|
-
return renderFreezeTable(data);
|
|
567
|
-
case 'default':
|
|
568
|
-
default:
|
|
569
|
-
return renderTable(data);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// For other table reports
|
|
574
|
-
if (type === 'table') {
|
|
575
|
-
return renderTable(data);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// For chart reports
|
|
579
|
-
switch (type) {
|
|
580
|
-
case 'line':
|
|
581
|
-
return renderLineChart(data);
|
|
582
|
-
case 'bar':
|
|
583
|
-
return renderBarChart(data);
|
|
584
|
-
case 'pie':
|
|
585
|
-
return renderPieChart(data);
|
|
586
|
-
default:
|
|
587
|
-
return renderBarChart(data);
|
|
588
|
-
}
|
|
589
|
-
};
|
|
590
|
-
|
|
591
|
-
// ========== RENDER SELECTED REPORT VIEW ==========
|
|
592
|
-
if (selectedReport) {
|
|
593
|
-
return (
|
|
594
|
-
<View style={styles.container}>
|
|
595
|
-
<View style={styles.header}>
|
|
596
|
-
<TouchableOpacity
|
|
597
|
-
style={styles.backButton}
|
|
598
|
-
onPress={() => {
|
|
599
|
-
setSelectedReport(null);
|
|
600
|
-
setReportData(null);
|
|
601
|
-
setViewType('default');
|
|
602
|
-
}}
|
|
603
|
-
>
|
|
604
|
-
<Text style={styles.backText}>‹</Text>
|
|
605
|
-
<Text style={styles.backButtonText}>Back</Text>
|
|
606
|
-
</TouchableOpacity>
|
|
607
|
-
<Text style={styles.headerTitle} numberOfLines={1}>
|
|
608
|
-
{selectedReport.title || selectedReport.name}
|
|
609
|
-
</Text>
|
|
610
|
-
<TouchableOpacity onPress={handleClose} style={styles.closeButton}>
|
|
611
|
-
<Text style={styles.closeText}>×</Text>
|
|
612
|
-
</TouchableOpacity>
|
|
613
|
-
</View>
|
|
614
|
-
|
|
615
|
-
{loading ? (
|
|
616
|
-
<View style={styles.loadingContainer}>
|
|
617
|
-
<ActivityIndicator size="large" color="#4CAF50" />
|
|
618
|
-
<Text style={styles.loadingText}>Loading report data...</Text>
|
|
619
|
-
</View>
|
|
620
|
-
) : error ? (
|
|
621
|
-
<View style={styles.errorContainer}>
|
|
622
|
-
<Text style={styles.errorText}>Error: {error}</Text>
|
|
623
|
-
<TouchableOpacity
|
|
624
|
-
style={styles.retryButton}
|
|
625
|
-
onPress={() => handleReportClick(selectedReport)}
|
|
626
|
-
>
|
|
627
|
-
<Text style={styles.retryText}>Retry</Text>
|
|
628
|
-
</TouchableOpacity>
|
|
629
|
-
</View>
|
|
630
|
-
) : (
|
|
631
|
-
<ScrollView style={styles.chartView}>
|
|
632
|
-
{/* View Type Selector for Performance Reports */}
|
|
633
|
-
{selectedReport &&
|
|
634
|
-
(selectedReport.id === 1 || selectedReport.id === 1.1 || selectedReport.id === 1.2) &&
|
|
635
|
-
reportData && (
|
|
636
|
-
<View style={styles.viewTypeSelector}>
|
|
637
|
-
<TouchableOpacity
|
|
638
|
-
style={[
|
|
639
|
-
styles.viewTypeButton,
|
|
640
|
-
viewType === 'default' && styles.activeViewType
|
|
641
|
-
]}
|
|
642
|
-
onPress={() => setViewType('default')}
|
|
643
|
-
>
|
|
644
|
-
<Text style={[
|
|
645
|
-
styles.viewTypeText,
|
|
646
|
-
viewType === 'default' && styles.activeViewTypeText
|
|
647
|
-
]}>Varsayılan</Text>
|
|
648
|
-
</TouchableOpacity>
|
|
649
|
-
|
|
650
|
-
<TouchableOpacity
|
|
651
|
-
style={[
|
|
652
|
-
styles.viewTypeButton,
|
|
653
|
-
viewType === 'compact' && styles.activeViewType
|
|
654
|
-
]}
|
|
655
|
-
onPress={() => setViewType('compact')}
|
|
656
|
-
>
|
|
657
|
-
<Text style={[
|
|
658
|
-
styles.viewTypeText,
|
|
659
|
-
viewType === 'compact' && styles.activeViewTypeText
|
|
660
|
-
]}>Kompakt</Text>
|
|
661
|
-
</TouchableOpacity>
|
|
662
|
-
|
|
663
|
-
<TouchableOpacity
|
|
664
|
-
style={[
|
|
665
|
-
styles.viewTypeButton,
|
|
666
|
-
viewType === 'freeze' && styles.activeViewType
|
|
667
|
-
]}
|
|
668
|
-
onPress={() => setViewType('freeze')}
|
|
669
|
-
>
|
|
670
|
-
<Text style={[
|
|
671
|
-
styles.viewTypeText,
|
|
672
|
-
viewType === 'freeze' && styles.activeViewTypeText
|
|
673
|
-
]}>Sabit Kolon</Text>
|
|
674
|
-
</TouchableOpacity>
|
|
675
|
-
</View>
|
|
676
|
-
)}
|
|
677
|
-
|
|
678
|
-
<View style={styles.chartContainer}>
|
|
679
|
-
<Text style={styles.chartTitle}>{reportData?.title || selectedReport.title}</Text>
|
|
680
|
-
{selectedReport.id === 1 || selectedReport.id === 1.1 || selectedReport.id === 1.2 ? (
|
|
681
|
-
<Text style={styles.reportSubtitle}>
|
|
682
|
-
{viewType === 'default' ? 'Varsayılan Görünüm' :
|
|
683
|
-
viewType === 'compact' ? 'Kompakt Görünüm' :
|
|
684
|
-
'Sabit Kolonlu Görünüm'}
|
|
685
|
-
</Text>
|
|
686
|
-
) : null}
|
|
687
|
-
{renderChart()}
|
|
688
|
-
</View>
|
|
689
|
-
|
|
690
|
-
{reportData && reportData.data && (
|
|
691
|
-
<View style={styles.dataInfo}>
|
|
692
|
-
<Text style={styles.infoTitle}>Report Information</Text>
|
|
693
|
-
<Text style={styles.infoText}>
|
|
694
|
-
{selectedReport.description || 'Detailed analysis report'}
|
|
695
|
-
</Text>
|
|
696
|
-
<Text style={styles.dataSource}>
|
|
697
|
-
Data points: {Array.isArray(reportData.data) ? reportData.data.length :
|
|
698
|
-
reportData.data.values?.length || reportData.data.labels?.length || 'N/A'}
|
|
699
|
-
</Text>
|
|
700
|
-
</View>
|
|
701
|
-
)}
|
|
702
|
-
</ScrollView>
|
|
703
|
-
)}
|
|
704
|
-
</View>
|
|
705
|
-
);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
// ========== RENDER REPORT LIST VIEW ==========
|
|
709
9
|
return (
|
|
710
|
-
<
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
</TouchableOpacity>
|
|
716
|
-
</View>
|
|
717
|
-
|
|
718
|
-
<ScrollView style={styles.reportsList}>
|
|
719
|
-
{reportList.map((report) => (
|
|
720
|
-
<TouchableOpacity
|
|
721
|
-
key={report.id}
|
|
722
|
-
style={styles.reportCard}
|
|
723
|
-
onPress={() => handleReportClick(report)}
|
|
724
|
-
>
|
|
725
|
-
<View style={[styles.reportIcon, { backgroundColor: getColor(report.id) }]}>
|
|
726
|
-
<Text style={styles.reportIconText}>{report.id}</Text>
|
|
727
|
-
</View>
|
|
728
|
-
<View style={styles.reportInfo}>
|
|
729
|
-
<Text style={styles.reportName}>{report.name}</Text>
|
|
730
|
-
<Text style={styles.reportTitle}>{report.title}</Text>
|
|
731
|
-
<Text style={styles.reportDesc}>{report.description}</Text>
|
|
732
|
-
</View>
|
|
733
|
-
<Text style={styles.arrow}>›</Text>
|
|
734
|
-
</TouchableOpacity>
|
|
735
|
-
))}
|
|
736
|
-
</ScrollView>
|
|
737
|
-
</View>
|
|
10
|
+
<ReportDashboard
|
|
11
|
+
reports={reports}
|
|
12
|
+
onReportSelect={onReportSelect}
|
|
13
|
+
onClose={onClose}
|
|
14
|
+
/>
|
|
738
15
|
);
|
|
739
16
|
};
|
|
740
17
|
|
|
741
|
-
const styles = StyleSheet.create({
|
|
742
|
-
container: {
|
|
743
|
-
flex: 1,
|
|
744
|
-
backgroundColor: '#f8f9fa',
|
|
745
|
-
},
|
|
746
|
-
header: {
|
|
747
|
-
flexDirection: 'row',
|
|
748
|
-
alignItems: 'center',
|
|
749
|
-
justifyContent: 'space-between',
|
|
750
|
-
backgroundColor: '#fff',
|
|
751
|
-
paddingHorizontal: 16,
|
|
752
|
-
paddingVertical: 12,
|
|
753
|
-
borderBottomWidth: 1,
|
|
754
|
-
borderBottomColor: '#e0e0e0',
|
|
755
|
-
},
|
|
756
|
-
headerTitle: {
|
|
757
|
-
fontSize: 18,
|
|
758
|
-
fontWeight: '700',
|
|
759
|
-
color: '#000',
|
|
760
|
-
flex: 1,
|
|
761
|
-
textAlign: 'center',
|
|
762
|
-
},
|
|
763
|
-
backButton: {
|
|
764
|
-
flexDirection: 'row',
|
|
765
|
-
alignItems: 'center',
|
|
766
|
-
padding: 8,
|
|
767
|
-
},
|
|
768
|
-
backText: {
|
|
769
|
-
fontSize: 24,
|
|
770
|
-
color: '#4CAF50',
|
|
771
|
-
marginRight: 4,
|
|
772
|
-
},
|
|
773
|
-
backButtonText: {
|
|
774
|
-
fontSize: 16,
|
|
775
|
-
color: '#4CAF50',
|
|
776
|
-
fontWeight: '600',
|
|
777
|
-
},
|
|
778
|
-
closeButton: {
|
|
779
|
-
width: 32,
|
|
780
|
-
height: 32,
|
|
781
|
-
borderRadius: 16,
|
|
782
|
-
backgroundColor: '#f0f0f0',
|
|
783
|
-
justifyContent: 'center',
|
|
784
|
-
alignItems: 'center',
|
|
785
|
-
},
|
|
786
|
-
closeText: {
|
|
787
|
-
fontSize: 20,
|
|
788
|
-
color: '#666',
|
|
789
|
-
fontWeight: '300',
|
|
790
|
-
},
|
|
791
|
-
reportsList: {
|
|
792
|
-
flex: 1,
|
|
793
|
-
padding: 16,
|
|
794
|
-
},
|
|
795
|
-
reportCard: {
|
|
796
|
-
flexDirection: 'row',
|
|
797
|
-
alignItems: 'center',
|
|
798
|
-
backgroundColor: '#fff',
|
|
799
|
-
borderRadius: 12,
|
|
800
|
-
padding: 16,
|
|
801
|
-
marginBottom: 12,
|
|
802
|
-
borderWidth: 1,
|
|
803
|
-
borderColor: '#e8e8e8',
|
|
804
|
-
shadowColor: '#000',
|
|
805
|
-
shadowOffset: { width: 0, height: 1 },
|
|
806
|
-
shadowOpacity: 0.05,
|
|
807
|
-
shadowRadius: 2,
|
|
808
|
-
elevation: 2,
|
|
809
|
-
},
|
|
810
|
-
reportIcon: {
|
|
811
|
-
width: 44,
|
|
812
|
-
height: 44,
|
|
813
|
-
borderRadius: 22,
|
|
814
|
-
justifyContent: 'center',
|
|
815
|
-
alignItems: 'center',
|
|
816
|
-
marginRight: 12,
|
|
817
|
-
},
|
|
818
|
-
reportIconText: {
|
|
819
|
-
fontSize: 18,
|
|
820
|
-
fontWeight: '700',
|
|
821
|
-
color: '#fff',
|
|
822
|
-
},
|
|
823
|
-
reportInfo: {
|
|
824
|
-
flex: 1,
|
|
825
|
-
},
|
|
826
|
-
reportName: {
|
|
827
|
-
fontSize: 16,
|
|
828
|
-
fontWeight: '600',
|
|
829
|
-
color: '#000',
|
|
830
|
-
marginBottom: 2,
|
|
831
|
-
},
|
|
832
|
-
reportTitle: {
|
|
833
|
-
fontSize: 14,
|
|
834
|
-
fontWeight: '500',
|
|
835
|
-
color: '#4CAF50',
|
|
836
|
-
marginBottom: 4,
|
|
837
|
-
},
|
|
838
|
-
reportDesc: {
|
|
839
|
-
fontSize: 13,
|
|
840
|
-
color: '#666',
|
|
841
|
-
lineHeight: 18,
|
|
842
|
-
},
|
|
843
|
-
arrow: {
|
|
844
|
-
fontSize: 24,
|
|
845
|
-
color: '#ccc',
|
|
846
|
-
fontWeight: '300',
|
|
847
|
-
},
|
|
848
|
-
chartView: {
|
|
849
|
-
flex: 1,
|
|
850
|
-
padding: 16,
|
|
851
|
-
},
|
|
852
|
-
chartContainer: {
|
|
853
|
-
backgroundColor: '#fff',
|
|
854
|
-
borderRadius: 12,
|
|
855
|
-
padding: 16,
|
|
856
|
-
marginBottom: 16,
|
|
857
|
-
borderWidth: 1,
|
|
858
|
-
borderColor: '#e8e8e8',
|
|
859
|
-
shadowColor: '#000',
|
|
860
|
-
shadowOffset: { width: 0, height: 1 },
|
|
861
|
-
shadowOpacity: 0.05,
|
|
862
|
-
shadowRadius: 2,
|
|
863
|
-
elevation: 2,
|
|
864
|
-
},
|
|
865
|
-
chartTitle: {
|
|
866
|
-
fontSize: 18,
|
|
867
|
-
fontWeight: '700',
|
|
868
|
-
color: '#000',
|
|
869
|
-
marginBottom: 20,
|
|
870
|
-
textAlign: 'center',
|
|
871
|
-
},
|
|
872
|
-
chart: {
|
|
873
|
-
flexDirection: 'row',
|
|
874
|
-
alignItems: 'flex-end',
|
|
875
|
-
justifyContent: 'space-between',
|
|
876
|
-
paddingHorizontal: 8,
|
|
877
|
-
},
|
|
878
|
-
barWrapper: {
|
|
879
|
-
alignItems: 'center',
|
|
880
|
-
marginHorizontal: 4,
|
|
881
|
-
},
|
|
882
|
-
bar: {
|
|
883
|
-
borderRadius: 4,
|
|
884
|
-
},
|
|
885
|
-
barLabel: {
|
|
886
|
-
marginTop: 8,
|
|
887
|
-
fontSize: 11,
|
|
888
|
-
color: '#666',
|
|
889
|
-
textAlign: 'center',
|
|
890
|
-
width: 60,
|
|
891
|
-
},
|
|
892
|
-
linePointWrapper: {
|
|
893
|
-
alignItems: 'center',
|
|
894
|
-
marginHorizontal: 4,
|
|
895
|
-
},
|
|
896
|
-
linePoint: {
|
|
897
|
-
backgroundColor: '#2196F3',
|
|
898
|
-
borderRadius: 2,
|
|
899
|
-
},
|
|
900
|
-
lineConnector: {
|
|
901
|
-
width: 2,
|
|
902
|
-
backgroundColor: '#2196F3',
|
|
903
|
-
opacity: 0.3,
|
|
904
|
-
marginVertical: 2,
|
|
905
|
-
flex: 1,
|
|
906
|
-
},
|
|
907
|
-
lineLabel: {
|
|
908
|
-
marginTop: 8,
|
|
909
|
-
fontSize: 11,
|
|
910
|
-
color: '#666',
|
|
911
|
-
textAlign: 'center',
|
|
912
|
-
width: 60,
|
|
913
|
-
},
|
|
914
|
-
valueText: {
|
|
915
|
-
fontSize: 10,
|
|
916
|
-
color: '#999',
|
|
917
|
-
marginTop: 4,
|
|
918
|
-
fontWeight: '500',
|
|
919
|
-
},
|
|
920
|
-
pieContainer: {
|
|
921
|
-
padding: 10,
|
|
922
|
-
},
|
|
923
|
-
pieItem: {
|
|
924
|
-
flexDirection: 'row',
|
|
925
|
-
alignItems: 'center',
|
|
926
|
-
marginBottom: 12,
|
|
927
|
-
padding: 8,
|
|
928
|
-
backgroundColor: '#f9f9f9',
|
|
929
|
-
borderRadius: 8,
|
|
930
|
-
},
|
|
931
|
-
pieColor: {
|
|
932
|
-
width: 16,
|
|
933
|
-
height: 16,
|
|
934
|
-
borderRadius: 8,
|
|
935
|
-
marginRight: 12,
|
|
936
|
-
},
|
|
937
|
-
pieLabel: {
|
|
938
|
-
fontSize: 15,
|
|
939
|
-
fontWeight: '600',
|
|
940
|
-
color: '#000',
|
|
941
|
-
flex: 1,
|
|
942
|
-
},
|
|
943
|
-
pieValue: {
|
|
944
|
-
fontSize: 14,
|
|
945
|
-
color: '#666',
|
|
946
|
-
fontWeight: '500',
|
|
947
|
-
},
|
|
948
|
-
totalText: {
|
|
949
|
-
fontSize: 16,
|
|
950
|
-
fontWeight: '700',
|
|
951
|
-
color: '#4CAF50',
|
|
952
|
-
textAlign: 'center',
|
|
953
|
-
marginTop: 16,
|
|
954
|
-
paddingTop: 16,
|
|
955
|
-
borderTopWidth: 1,
|
|
956
|
-
borderTopColor: '#eee',
|
|
957
|
-
},
|
|
958
|
-
dataInfo: {
|
|
959
|
-
backgroundColor: '#e8f5e9',
|
|
960
|
-
borderRadius: 12,
|
|
961
|
-
padding: 16,
|
|
962
|
-
marginTop: 8,
|
|
963
|
-
},
|
|
964
|
-
infoTitle: {
|
|
965
|
-
fontSize: 16,
|
|
966
|
-
fontWeight: '700',
|
|
967
|
-
color: '#2e7d32',
|
|
968
|
-
marginBottom: 8,
|
|
969
|
-
},
|
|
970
|
-
infoText: {
|
|
971
|
-
fontSize: 14,
|
|
972
|
-
color: '#555',
|
|
973
|
-
lineHeight: 20,
|
|
974
|
-
marginBottom: 8,
|
|
975
|
-
},
|
|
976
|
-
dataSource: {
|
|
977
|
-
fontSize: 13,
|
|
978
|
-
color: '#777',
|
|
979
|
-
fontStyle: 'italic',
|
|
980
|
-
},
|
|
981
|
-
loadingContainer: {
|
|
982
|
-
flex: 1,
|
|
983
|
-
justifyContent: 'center',
|
|
984
|
-
alignItems: 'center',
|
|
985
|
-
padding: 40,
|
|
986
|
-
},
|
|
987
|
-
loadingText: {
|
|
988
|
-
marginTop: 16,
|
|
989
|
-
fontSize: 16,
|
|
990
|
-
color: '#666',
|
|
991
|
-
},
|
|
992
|
-
errorContainer: {
|
|
993
|
-
flex: 1,
|
|
994
|
-
justifyContent: 'center',
|
|
995
|
-
alignItems: 'center',
|
|
996
|
-
padding: 40,
|
|
997
|
-
},
|
|
998
|
-
errorText: {
|
|
999
|
-
fontSize: 16,
|
|
1000
|
-
color: '#d32f2f',
|
|
1001
|
-
textAlign: 'center',
|
|
1002
|
-
marginBottom: 20,
|
|
1003
|
-
},
|
|
1004
|
-
retryButton: {
|
|
1005
|
-
backgroundColor: '#4CAF50',
|
|
1006
|
-
paddingHorizontal: 24,
|
|
1007
|
-
paddingVertical: 12,
|
|
1008
|
-
borderRadius: 8,
|
|
1009
|
-
},
|
|
1010
|
-
retryText: {
|
|
1011
|
-
color: '#fff',
|
|
1012
|
-
fontSize: 16,
|
|
1013
|
-
fontWeight: '600',
|
|
1014
|
-
},
|
|
1015
|
-
noDataContainer: {
|
|
1016
|
-
padding: 40,
|
|
1017
|
-
alignItems: 'center',
|
|
1018
|
-
},
|
|
1019
|
-
noDataText: {
|
|
1020
|
-
fontSize: 16,
|
|
1021
|
-
color: '#999',
|
|
1022
|
-
textAlign: 'center',
|
|
1023
|
-
},
|
|
1024
|
-
|
|
1025
|
-
// Table Styles
|
|
1026
|
-
table: {
|
|
1027
|
-
minWidth: 700,
|
|
1028
|
-
marginBottom: 20,
|
|
1029
|
-
},
|
|
1030
|
-
tableHeader: {
|
|
1031
|
-
flexDirection: 'row',
|
|
1032
|
-
backgroundColor: '#2c3e50',
|
|
1033
|
-
borderBottomWidth: 1,
|
|
1034
|
-
borderBottomColor: '#ddd',
|
|
1035
|
-
},
|
|
1036
|
-
headerCell: {
|
|
1037
|
-
flex: 1,
|
|
1038
|
-
padding: 10,
|
|
1039
|
-
minWidth: 100,
|
|
1040
|
-
justifyContent: 'center',
|
|
1041
|
-
alignItems: 'center',
|
|
1042
|
-
borderRightWidth: 1,
|
|
1043
|
-
borderRightColor: '#444',
|
|
1044
|
-
},
|
|
1045
|
-
firstColumn: {
|
|
1046
|
-
flex: 1.5,
|
|
1047
|
-
minWidth: 180,
|
|
1048
|
-
},
|
|
1049
|
-
headerText: {
|
|
1050
|
-
color: '#fff',
|
|
1051
|
-
fontWeight: '600',
|
|
1052
|
-
fontSize: 11,
|
|
1053
|
-
textAlign: 'center',
|
|
1054
|
-
},
|
|
1055
|
-
tableRow: {
|
|
1056
|
-
flexDirection: 'row',
|
|
1057
|
-
borderBottomWidth: 1,
|
|
1058
|
-
borderBottomColor: '#eee',
|
|
1059
|
-
minHeight: 40,
|
|
1060
|
-
},
|
|
1061
|
-
evenRow: {
|
|
1062
|
-
backgroundColor: '#fff',
|
|
1063
|
-
},
|
|
1064
|
-
oddRow: {
|
|
1065
|
-
backgroundColor: '#f9f9f9',
|
|
1066
|
-
},
|
|
1067
|
-
totalRow: {
|
|
1068
|
-
backgroundColor: '#e8f5e9',
|
|
1069
|
-
borderTopWidth: 2,
|
|
1070
|
-
borderTopColor: '#4CAF50',
|
|
1071
|
-
borderBottomWidth: 2,
|
|
1072
|
-
borderBottomColor: '#4CAF50',
|
|
1073
|
-
},
|
|
1074
|
-
cell: {
|
|
1075
|
-
flex: 1,
|
|
1076
|
-
padding: 8,
|
|
1077
|
-
minWidth: 100,
|
|
1078
|
-
justifyContent: 'center',
|
|
1079
|
-
alignItems: 'center',
|
|
1080
|
-
borderRightWidth: 1,
|
|
1081
|
-
borderRightColor: '#eee',
|
|
1082
|
-
},
|
|
1083
|
-
cellText: {
|
|
1084
|
-
fontSize: 12,
|
|
1085
|
-
color: '#333',
|
|
1086
|
-
textAlign: 'center',
|
|
1087
|
-
},
|
|
1088
|
-
numberText: {
|
|
1089
|
-
fontSize: 11,
|
|
1090
|
-
fontWeight: '500',
|
|
1091
|
-
fontFamily: 'monospace',
|
|
1092
|
-
color: '#333',
|
|
1093
|
-
},
|
|
1094
|
-
percentText: {
|
|
1095
|
-
fontSize: 12,
|
|
1096
|
-
fontWeight: '600',
|
|
1097
|
-
fontFamily: 'monospace',
|
|
1098
|
-
},
|
|
1099
|
-
positive: {
|
|
1100
|
-
color: '#27ae60',
|
|
1101
|
-
},
|
|
1102
|
-
negative: {
|
|
1103
|
-
color: '#e74c3c',
|
|
1104
|
-
},
|
|
1105
|
-
grossMargin: {
|
|
1106
|
-
color: '#2c3e50',
|
|
1107
|
-
fontWeight: '700',
|
|
1108
|
-
},
|
|
1109
|
-
|
|
1110
|
-
// Compact Table Styles
|
|
1111
|
-
compactContainer: {
|
|
1112
|
-
flex: 1,
|
|
1113
|
-
paddingHorizontal: 8,
|
|
1114
|
-
},
|
|
1115
|
-
compactCard: {
|
|
1116
|
-
backgroundColor: '#fff',
|
|
1117
|
-
borderRadius: 12,
|
|
1118
|
-
marginBottom: 12,
|
|
1119
|
-
padding: 16,
|
|
1120
|
-
borderWidth: 1,
|
|
1121
|
-
borderColor: '#e8e8e8',
|
|
1122
|
-
shadowColor: '#000',
|
|
1123
|
-
shadowOffset: { width: 0, height: 2 },
|
|
1124
|
-
shadowOpacity: 0.1,
|
|
1125
|
-
shadowRadius: 3,
|
|
1126
|
-
elevation: 2,
|
|
1127
|
-
},
|
|
1128
|
-
compactTotalCard: {
|
|
1129
|
-
backgroundColor: '#e8f5e9',
|
|
1130
|
-
borderColor: '#4CAF50',
|
|
1131
|
-
borderWidth: 2,
|
|
1132
|
-
},
|
|
1133
|
-
evenCard: {
|
|
1134
|
-
backgroundColor: '#fff',
|
|
1135
|
-
},
|
|
1136
|
-
oddCard: {
|
|
1137
|
-
backgroundColor: '#f9f9f9',
|
|
1138
|
-
},
|
|
1139
|
-
compactHeader: {
|
|
1140
|
-
flexDirection: 'row',
|
|
1141
|
-
justifyContent: 'space-between',
|
|
1142
|
-
alignItems: 'center',
|
|
1143
|
-
marginBottom: 12,
|
|
1144
|
-
paddingBottom: 12,
|
|
1145
|
-
borderBottomWidth: 1,
|
|
1146
|
-
borderBottomColor: '#eee',
|
|
1147
|
-
},
|
|
1148
|
-
compactTitle: {
|
|
1149
|
-
fontSize: 14,
|
|
1150
|
-
fontWeight: '600',
|
|
1151
|
-
color: '#2c3e50',
|
|
1152
|
-
flex: 1,
|
|
1153
|
-
},
|
|
1154
|
-
totalTitle: {
|
|
1155
|
-
fontWeight: '700',
|
|
1156
|
-
color: '#2c3e50',
|
|
1157
|
-
fontSize: 15,
|
|
1158
|
-
},
|
|
1159
|
-
compactGrossMargin: {
|
|
1160
|
-
fontSize: 13,
|
|
1161
|
-
fontWeight: '600',
|
|
1162
|
-
paddingHorizontal: 8,
|
|
1163
|
-
paddingVertical: 4,
|
|
1164
|
-
borderRadius: 6,
|
|
1165
|
-
backgroundColor: '#f0f0f0',
|
|
1166
|
-
},
|
|
1167
|
-
compactGrid: {
|
|
1168
|
-
flexDirection: 'row',
|
|
1169
|
-
flexWrap: 'wrap',
|
|
1170
|
-
justifyContent: 'space-between',
|
|
1171
|
-
},
|
|
1172
|
-
compactColumn: {
|
|
1173
|
-
width: '48%',
|
|
1174
|
-
marginBottom: 12,
|
|
1175
|
-
backgroundColor: '#f8f9fa',
|
|
1176
|
-
padding: 10,
|
|
1177
|
-
borderRadius: 8,
|
|
1178
|
-
alignItems: 'center',
|
|
1179
|
-
},
|
|
1180
|
-
compactLabel: {
|
|
1181
|
-
fontSize: 11,
|
|
1182
|
-
color: '#666',
|
|
1183
|
-
marginBottom: 4,
|
|
1184
|
-
textAlign: 'center',
|
|
1185
|
-
},
|
|
1186
|
-
compactValue: {
|
|
1187
|
-
fontSize: 13,
|
|
1188
|
-
fontWeight: '600',
|
|
1189
|
-
color: '#2c3e50',
|
|
1190
|
-
fontFamily: 'monospace',
|
|
1191
|
-
},
|
|
1192
|
-
percentBadge: {
|
|
1193
|
-
backgroundColor: '#fff',
|
|
1194
|
-
paddingHorizontal: 10,
|
|
1195
|
-
paddingVertical: 4,
|
|
1196
|
-
borderRadius: 12,
|
|
1197
|
-
borderWidth: 1,
|
|
1198
|
-
borderColor: '#e0e0e0',
|
|
1199
|
-
},
|
|
1200
|
-
statusIndicator: {
|
|
1201
|
-
flexDirection: 'row',
|
|
1202
|
-
alignItems: 'center',
|
|
1203
|
-
marginTop: 12,
|
|
1204
|
-
paddingTop: 12,
|
|
1205
|
-
borderTopWidth: 1,
|
|
1206
|
-
borderTopColor: '#eee',
|
|
1207
|
-
},
|
|
1208
|
-
statusDot: {
|
|
1209
|
-
width: 8,
|
|
1210
|
-
height: 8,
|
|
1211
|
-
borderRadius: 4,
|
|
1212
|
-
marginRight: 8,
|
|
1213
|
-
},
|
|
1214
|
-
statusGood: {
|
|
1215
|
-
backgroundColor: '#4CAF50',
|
|
1216
|
-
},
|
|
1217
|
-
statusWarning: {
|
|
1218
|
-
backgroundColor: '#FF9800',
|
|
1219
|
-
},
|
|
1220
|
-
statusText: {
|
|
1221
|
-
fontSize: 12,
|
|
1222
|
-
color: '#666',
|
|
1223
|
-
},
|
|
1224
|
-
|
|
1225
|
-
// Freeze Table Styles
|
|
1226
|
-
freezeContainer: {
|
|
1227
|
-
flex: 1,
|
|
1228
|
-
flexDirection: 'row',
|
|
1229
|
-
},
|
|
1230
|
-
freezeColumn: {
|
|
1231
|
-
width: 180,
|
|
1232
|
-
borderRightWidth: 2,
|
|
1233
|
-
borderRightColor: '#ddd',
|
|
1234
|
-
backgroundColor: '#fff',
|
|
1235
|
-
},
|
|
1236
|
-
freezeFirstCell: {
|
|
1237
|
-
backgroundColor: '#2c3e50',
|
|
1238
|
-
justifyContent: 'center',
|
|
1239
|
-
alignItems: 'center',
|
|
1240
|
-
},
|
|
1241
|
-
freezeHeader: {
|
|
1242
|
-
height: 50,
|
|
1243
|
-
justifyContent: 'center',
|
|
1244
|
-
alignItems: 'center',
|
|
1245
|
-
backgroundColor: '#2c3e50',
|
|
1246
|
-
borderBottomWidth: 1,
|
|
1247
|
-
borderBottomColor: '#444',
|
|
1248
|
-
borderRightWidth: 1,
|
|
1249
|
-
borderRightColor: '#444',
|
|
1250
|
-
minWidth: 120,
|
|
1251
|
-
},
|
|
1252
|
-
freezeHeaderRow: {
|
|
1253
|
-
flexDirection: 'row',
|
|
1254
|
-
},
|
|
1255
|
-
freezeHeaderText: {
|
|
1256
|
-
color: '#fff',
|
|
1257
|
-
fontWeight: '600',
|
|
1258
|
-
fontSize: 12,
|
|
1259
|
-
textAlign: 'center',
|
|
1260
|
-
},
|
|
1261
|
-
freezeRow: {
|
|
1262
|
-
height: 45,
|
|
1263
|
-
justifyContent: 'center',
|
|
1264
|
-
paddingHorizontal: 10,
|
|
1265
|
-
borderBottomWidth: 1,
|
|
1266
|
-
borderBottomColor: '#eee',
|
|
1267
|
-
},
|
|
1268
|
-
freezeTotalRow: {
|
|
1269
|
-
backgroundColor: '#e8f5e9',
|
|
1270
|
-
borderTopWidth: 2,
|
|
1271
|
-
borderTopColor: '#4CAF50',
|
|
1272
|
-
borderBottomWidth: 2,
|
|
1273
|
-
borderBottomColor: '#4CAF50',
|
|
1274
|
-
},
|
|
1275
|
-
freezeCellText: {
|
|
1276
|
-
fontSize: 12,
|
|
1277
|
-
color: '#333',
|
|
1278
|
-
fontWeight: '500',
|
|
1279
|
-
},
|
|
1280
|
-
freezeTotalText: {
|
|
1281
|
-
fontWeight: '700',
|
|
1282
|
-
color: '#2c3e50',
|
|
1283
|
-
},
|
|
1284
|
-
scrollableColumns: {
|
|
1285
|
-
flex: 1,
|
|
1286
|
-
},
|
|
1287
|
-
freezeDataRow: {
|
|
1288
|
-
flexDirection: 'row',
|
|
1289
|
-
height: 45,
|
|
1290
|
-
borderBottomWidth: 1,
|
|
1291
|
-
borderBottomColor: '#eee',
|
|
1292
|
-
},
|
|
1293
|
-
freezeTotalDataRow: {
|
|
1294
|
-
backgroundColor: '#e8f5e9',
|
|
1295
|
-
},
|
|
1296
|
-
freezeDataCell: {
|
|
1297
|
-
minWidth: 120,
|
|
1298
|
-
justifyContent: 'center',
|
|
1299
|
-
alignItems: 'center',
|
|
1300
|
-
borderRightWidth: 1,
|
|
1301
|
-
borderRightColor: '#eee',
|
|
1302
|
-
paddingHorizontal: 8,
|
|
1303
|
-
},
|
|
1304
|
-
freezeDataText: {
|
|
1305
|
-
fontSize: 12,
|
|
1306
|
-
fontWeight: '500',
|
|
1307
|
-
fontFamily: 'monospace',
|
|
1308
|
-
color: '#333',
|
|
1309
|
-
},
|
|
1310
|
-
freezePercent: {
|
|
1311
|
-
fontSize: 12,
|
|
1312
|
-
fontWeight: '600',
|
|
1313
|
-
fontFamily: 'monospace',
|
|
1314
|
-
},
|
|
1315
|
-
|
|
1316
|
-
// View Type Selector
|
|
1317
|
-
viewTypeSelector: {
|
|
1318
|
-
flexDirection: 'row',
|
|
1319
|
-
backgroundColor: '#f0f0f0',
|
|
1320
|
-
borderRadius: 8,
|
|
1321
|
-
padding: 4,
|
|
1322
|
-
marginBottom: 16,
|
|
1323
|
-
alignSelf: 'center',
|
|
1324
|
-
},
|
|
1325
|
-
viewTypeButton: {
|
|
1326
|
-
paddingHorizontal: 16,
|
|
1327
|
-
paddingVertical: 8,
|
|
1328
|
-
borderRadius: 6,
|
|
1329
|
-
marginHorizontal: 2,
|
|
1330
|
-
},
|
|
1331
|
-
activeViewType: {
|
|
1332
|
-
backgroundColor: '#fff',
|
|
1333
|
-
shadowColor: '#000',
|
|
1334
|
-
shadowOffset: { width: 0, height: 1 },
|
|
1335
|
-
shadowOpacity: 0.1,
|
|
1336
|
-
shadowRadius: 2,
|
|
1337
|
-
elevation: 2,
|
|
1338
|
-
},
|
|
1339
|
-
viewTypeText: {
|
|
1340
|
-
fontSize: 13,
|
|
1341
|
-
fontWeight: '500',
|
|
1342
|
-
color: '#666',
|
|
1343
|
-
},
|
|
1344
|
-
activeViewTypeText: {
|
|
1345
|
-
color: '#4CAF50',
|
|
1346
|
-
fontWeight: '600',
|
|
1347
|
-
},
|
|
1348
|
-
reportSubtitle: {
|
|
1349
|
-
fontSize: 14,
|
|
1350
|
-
fontWeight: '600',
|
|
1351
|
-
color: '#4CAF50',
|
|
1352
|
-
textAlign: 'center',
|
|
1353
|
-
marginBottom: 16,
|
|
1354
|
-
fontStyle: 'italic',
|
|
1355
|
-
},
|
|
1356
|
-
});
|
|
1357
|
-
|
|
1358
18
|
export default ReportChart;
|