@dhiraj0720/report1chart 1.0.6 → 2.1.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/src/index.jsx CHANGED
@@ -1,1358 +1,18 @@
1
- import React, { useState } from '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
- <View style={styles.container}>
711
- <View style={styles.header}>
712
- <Text style={styles.headerTitle}>Reports Dashboard</Text>
713
- <TouchableOpacity onPress={handleClose} style={styles.closeButton}>
714
- <Text style={styles.closeText}>×</Text>
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;