@dhiraj0720/report1chart 3.0.3 → 3.0.5

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": "3.0.3",
3
+ "version": "3.0.5",
4
4
  "main": "src/index.jsx",
5
5
  "scripts": {
6
6
  "test": "echo 'No tests'"
@@ -118,9 +118,8 @@ const PercentCell = ({ value, trend }) => {
118
118
  styles.percentText,
119
119
  isUp ? styles.percentUp : styles.percentDown,
120
120
  ]}
121
- numberOfLines={1}
122
- adjustsFontSizeToFit
123
- minimumFontScale={0.8}
121
+ numberOfLines={2}
122
+ ellipsizeMode="tail"
124
123
  >
125
124
  {isUp ? '↑' : '↓'} {Math.abs(value)}%
126
125
  </Text>
@@ -141,9 +140,8 @@ const RatioCell = ({ value }) => {
141
140
  styles.percentText,
142
141
  isUp ? styles.percentUp : styles.percentDown,
143
142
  ]}
144
- numberOfLines={1}
145
- adjustsFontSizeToFit
146
- minimumFontScale={0.8}
143
+ numberOfLines={2}
144
+ ellipsizeMode="tail"
147
145
  >
148
146
  {isUp ? '↑' : '↓'} %{value}
149
147
  </Text>
@@ -164,10 +162,8 @@ const Cell = ({ children, bold = false, highlight = false, width = 100 }) => (
164
162
  styles.cellText,
165
163
  bold && styles.boldText,
166
164
  ]}
167
- numberOfLines={1}
165
+ numberOfLines={2}
168
166
  ellipsizeMode="tail"
169
- adjustsFontSizeToFit
170
- minimumFontScale={0.72}
171
167
  >
172
168
  {children}
173
169
  </Text>
@@ -291,14 +287,17 @@ const styles = StyleSheet.create({
291
287
  },
292
288
  cell: {
293
289
  width: 100,
294
- paddingVertical: 12,
290
+ height: 48,
291
+ paddingVertical: 6,
295
292
  paddingHorizontal: 8,
296
293
  justifyContent: 'center',
294
+ alignItems: 'center',
297
295
  borderBottomWidth: 1,
298
296
  borderColor: '#e0e0e0',
299
297
  },
300
298
  cellText: {
301
299
  fontSize: 12,
300
+ lineHeight: 14,
302
301
  textAlign: 'center',
303
302
  color: '#333',
304
303
  },
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
  import {
3
3
  Modal,
4
4
  View,
@@ -12,6 +12,12 @@ import {
12
12
  const MonthFilterModal = ({ visible, months, selected, onApply, onClose }) => {
13
13
  const [local, setLocal] = useState(selected || []);
14
14
 
15
+ useEffect(() => {
16
+ if (visible) {
17
+ setLocal(Array.isArray(selected) ? selected : []);
18
+ }
19
+ }, [visible, selected]);
20
+
15
21
  const toggle = (m) => {
16
22
  if (local.includes(m)) {
17
23
  setLocal(local.filter(x => x !== m));
@@ -20,7 +26,7 @@ const MonthFilterModal = ({ visible, months, selected, onApply, onClose }) => {
20
26
  }
21
27
  };
22
28
 
23
- const selectAll = () => setLocal(months);
29
+ const selectAll = () => setLocal([...(months || [])]);
24
30
  const clearAll = () => setLocal([]);
25
31
 
26
32
  return (
@@ -160,4 +166,4 @@ const styles = StyleSheet.create({
160
166
  },
161
167
  });
162
168
 
163
- export default MonthFilterModal;
169
+ export default MonthFilterModal;
package/src/index.jsx CHANGED
@@ -4,6 +4,7 @@ import SafeScreen from './components/SafeScreen';
4
4
  import ReportListScreen from './screens/ReportListScreen';
5
5
  import Report1Screen from './screens/Report1Screen';
6
6
  import Report2Screen from './screens/Report2Screen';
7
+ import Report2ModernScreen from './screens/Report2ModernScreen';
7
8
  import Report3Screen from './screens/Report3Screen';
8
9
  import Report1AScreen from './screens/Report1AScreen';
9
10
  import Report2AScreen from './screens/Report2AScreen';
@@ -85,6 +86,18 @@ if (active === '2A') {
85
86
  );
86
87
  }
87
88
 
89
+ if (active === '2N1') {
90
+ return (
91
+ <SafeScreen>
92
+ <Report2ModernScreen
93
+ api={config.report2}
94
+ token={config.token}
95
+ onBack={() => setActive(null)}
96
+ />
97
+ </SafeScreen>
98
+ );
99
+ }
100
+
88
101
 
89
102
  // 👉 REPORT 2
90
103
  if (active === 2) {
@@ -0,0 +1,747 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ RefreshControl,
5
+ ScrollView,
6
+ StyleSheet,
7
+ Text,
8
+ TouchableOpacity,
9
+ View,
10
+ } from 'react-native';
11
+ import { getDivisions, getTable } from '../api/report2Fetcher';
12
+ import DivisionFilterModal from '../components/DivisionFilterModal';
13
+ import MonthFilterModal from '../components/MonthFilterModal';
14
+ import { formatNumber } from '../utils/formatNumber';
15
+
16
+ const toNumber = (value) => {
17
+ const numeric = Number(value);
18
+ return Number.isFinite(numeric) ? numeric : 0;
19
+ };
20
+
21
+ const toPercent = (current, previous) => {
22
+ if (!previous) return 0;
23
+ return ((current - previous) / Math.abs(previous)) * 100;
24
+ };
25
+
26
+ const TrendBadge = ({ value }) => {
27
+ const positive = value >= 0;
28
+ return (
29
+ <View style={[styles.trendBadge, positive ? styles.trendUp : styles.trendDown]}>
30
+ <Text style={[styles.trendText, positive ? styles.trendTextUp : styles.trendTextDown]}>
31
+ {positive ? '+' : '-'} {Math.abs(value).toFixed(1)}%
32
+ </Text>
33
+ </View>
34
+ );
35
+ };
36
+
37
+ const KpiCard = ({ label, value, hint, tone }) => {
38
+ const toneStyle = tone === 'warm' ? styles.kpiWarm : tone === 'mint' ? styles.kpiMint : styles.kpiCool;
39
+ return (
40
+ <View style={[styles.kpiCard, toneStyle]}>
41
+ <Text style={styles.kpiLabel}>{label}</Text>
42
+ <Text style={styles.kpiValue}>{value}</Text>
43
+ <Text style={styles.kpiHint}>{hint}</Text>
44
+ </View>
45
+ );
46
+ };
47
+
48
+ const MetricCell = ({ label, value, accent }) => (
49
+ <View style={styles.metricCell}>
50
+ <Text style={styles.metricLabel}>{label}</Text>
51
+ <Text style={[styles.metricValue, accent && styles.metricValueAccent]}>{value}</Text>
52
+ </View>
53
+ );
54
+
55
+ const ModernBars = ({ rows, metric }) => {
56
+ const key2024 = metric === 'profit' ? 'profitUsd2024' : 'teu2024';
57
+ const key2025 = metric === 'profit' ? 'profitUsd2025' : 'teu2025';
58
+ const maxValue = Math.max(
59
+ 1,
60
+ ...rows.map((row) => Math.max(toNumber(row[key2024]), toNumber(row[key2025]))),
61
+ );
62
+
63
+ return (
64
+ <View style={styles.chartCard}>
65
+ <View style={styles.chartHeader}>
66
+ <Text style={styles.chartTitle}>
67
+ {metric === 'profit' ? 'Profit Trend by Month' : 'TEU Trend by Month'}
68
+ </Text>
69
+ <View style={styles.legendRow}>
70
+ <Text style={styles.legendTextOrange}>2024</Text>
71
+ <Text style={styles.legendTextBlue}>2025</Text>
72
+ </View>
73
+ </View>
74
+
75
+ <ScrollView horizontal showsHorizontalScrollIndicator={false}>
76
+ <View style={styles.chartColumns}>
77
+ {rows.map((row) => {
78
+ const value2024 = toNumber(row[key2024]);
79
+ const value2025 = toNumber(row[key2025]);
80
+ const pct = toPercent(value2025, value2024);
81
+ const bar2024Height = Math.max(6, (value2024 / maxValue) * 122);
82
+ const bar2025Height = Math.max(6, (value2025 / maxValue) * 122);
83
+
84
+ return (
85
+ <View key={row.monthLabel} style={styles.chartGroup}>
86
+ <View style={styles.barPair}>
87
+ <View style={styles.barTrack}>
88
+ <View style={[styles.barFill2024, { height: bar2024Height }]} />
89
+ </View>
90
+ <View style={styles.barTrack}>
91
+ <View style={[styles.barFill2025, { height: bar2025Height }]} />
92
+ </View>
93
+ </View>
94
+ <Text style={styles.chartMonthText}>{row.monthLabel.slice(0, 3)}</Text>
95
+ <Text style={[styles.chartDelta, pct >= 0 ? styles.deltaUp : styles.deltaDown]}>
96
+ {pct >= 0 ? '+' : ''}
97
+ {pct.toFixed(0)}%
98
+ </Text>
99
+ </View>
100
+ );
101
+ })}
102
+ </View>
103
+ </ScrollView>
104
+ </View>
105
+ );
106
+ };
107
+
108
+ const Report2ModernScreen = ({ api, token, onBack }) => {
109
+ const [divisionModal, setDivisionModal] = useState(false);
110
+ const [monthsModal, setMonthsModal] = useState(false);
111
+ const [divisions, setDivisions] = useState([]);
112
+ const [division, setDivision] = useState(null);
113
+ const [table, setTable] = useState(null);
114
+ const [loading, setLoading] = useState(true);
115
+ const [refreshing, setRefreshing] = useState(false);
116
+ const [selectedMonths, setSelectedMonths] = useState([]);
117
+ const [focusMonth, setFocusMonth] = useState('ALL');
118
+ const [metric, setMetric] = useState('profit');
119
+
120
+ const loadDivisions = useCallback(async () => {
121
+ const data = await getDivisions(api.divisions, token);
122
+ setDivisions(data);
123
+ setDivision((prev) => prev || data[0]?.code || prev);
124
+ }, [api.divisions, token]);
125
+
126
+ const loadTable = useCallback(async (divisionCode) => {
127
+ const reportTable = await getTable(api.table, divisionCode, token);
128
+ setTable(reportTable);
129
+ const monthLabels = (reportTable?.rows || [])
130
+ .filter((row) => row.monthLabel && row.monthLabel !== 'Total')
131
+ .map((row) => row.monthLabel);
132
+ setSelectedMonths(monthLabels);
133
+ setFocusMonth('ALL');
134
+ }, [api.table, token]);
135
+
136
+ useEffect(() => {
137
+ setLoading(true);
138
+ loadDivisions()
139
+ .catch(() => {})
140
+ .finally(() => setLoading(false));
141
+ }, [loadDivisions]);
142
+
143
+ useEffect(() => {
144
+ if (!division) return;
145
+ setLoading(true);
146
+ loadTable(division)
147
+ .catch(() => {})
148
+ .finally(() => setLoading(false));
149
+ }, [division, loadTable]);
150
+
151
+ const onRefresh = useCallback(async () => {
152
+ if (!division) return;
153
+ setRefreshing(true);
154
+ try {
155
+ await loadTable(division);
156
+ } finally {
157
+ setRefreshing(false);
158
+ }
159
+ }, [division, loadTable]);
160
+
161
+ const divisionName = useMemo(() => {
162
+ return divisions.find((item) => item.code === division)?.displayName || 'Division';
163
+ }, [division, divisions]);
164
+
165
+ const baseRows = useMemo(() => {
166
+ return (table?.rows || []).filter((row) => row.monthLabel && row.monthLabel !== 'Total');
167
+ }, [table]);
168
+
169
+ const rows = useMemo(() => {
170
+ let filtered = baseRows;
171
+ if (selectedMonths.length > 0) {
172
+ filtered = filtered.filter((row) => selectedMonths.includes(row.monthLabel));
173
+ }
174
+ if (focusMonth !== 'ALL') {
175
+ filtered = filtered.filter((row) => row.monthLabel === focusMonth);
176
+ }
177
+ return filtered;
178
+ }, [baseRows, focusMonth, selectedMonths]);
179
+
180
+ const total2024Profit = useMemo(() => rows.reduce((sum, row) => sum + toNumber(row.profitUsd2024), 0), [rows]);
181
+ const total2025Profit = useMemo(() => rows.reduce((sum, row) => sum + toNumber(row.profitUsd2025), 0), [rows]);
182
+ const total2025Budget = useMemo(() => rows.reduce((sum, row) => sum + toNumber(row.budgetProfitUsd2025), 0), [rows]);
183
+ const budgetRate = total2025Budget ? (total2025Profit / total2025Budget) * 100 : 0;
184
+ const yoyPercent = toPercent(total2025Profit, total2024Profit);
185
+
186
+ const bestMonth = useMemo(() => {
187
+ if (!rows.length) return null;
188
+ return rows.reduce((best, row) => {
189
+ if (!best) return row;
190
+ const bestPct = toNumber(best.profitChangePercent);
191
+ const currentPct = toNumber(row.profitChangePercent);
192
+ return currentPct > bestPct ? row : best;
193
+ }, null);
194
+ }, [rows]);
195
+
196
+ const quickMonths = useMemo(() => ['ALL', ...selectedMonths.slice(0, 5)], [selectedMonths]);
197
+
198
+ if (loading && !table) {
199
+ return (
200
+ <View style={styles.loaderWrap}>
201
+ <ActivityIndicator size="large" color="#144a8a" />
202
+ </View>
203
+ );
204
+ }
205
+
206
+ return (
207
+ <View style={styles.screen}>
208
+ <View style={styles.hero}>
209
+ <View style={styles.heroDotLarge} />
210
+ <View style={styles.heroDotSmall} />
211
+ <TouchableOpacity onPress={onBack} style={styles.backButton}>
212
+ <Text style={styles.backIcon}>‹</Text>
213
+ </TouchableOpacity>
214
+ <Text style={styles.heroTitle}>Gross Profit Dashboard</Text>
215
+ <Text style={styles.heroSubtitle}>Modern view - interactive monthly insights</Text>
216
+ </View>
217
+
218
+ <ScrollView
219
+ style={styles.content}
220
+ refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
221
+ showsVerticalScrollIndicator={false}
222
+ >
223
+ <View style={styles.filterRow}>
224
+ <TouchableOpacity
225
+ style={[styles.filterChip, styles.filterChipSpacing]}
226
+ onPress={() => setDivisionModal(true)}
227
+ activeOpacity={0.9}
228
+ >
229
+ <Text style={styles.filterChipLabel}>Division</Text>
230
+ <Text style={styles.filterChipValue}>{divisionName}</Text>
231
+ </TouchableOpacity>
232
+ <TouchableOpacity
233
+ style={styles.filterChip}
234
+ onPress={() => setMonthsModal(true)}
235
+ activeOpacity={0.9}
236
+ >
237
+ <Text style={styles.filterChipLabel}>Months</Text>
238
+ <Text style={styles.filterChipValue}>
239
+ {selectedMonths.length}/{baseRows.length || selectedMonths.length}
240
+ </Text>
241
+ </TouchableOpacity>
242
+ </View>
243
+
244
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.kpiScroll}>
245
+ <KpiCard
246
+ label="2024 Profit"
247
+ value={formatNumber(total2024Profit)}
248
+ hint="Baseline"
249
+ tone="warm"
250
+ />
251
+ <KpiCard
252
+ label="2025 Profit"
253
+ value={formatNumber(total2025Profit)}
254
+ hint="Current"
255
+ tone="cool"
256
+ />
257
+ <KpiCard
258
+ label="Budget Coverage"
259
+ value={`${budgetRate.toFixed(1)}%`}
260
+ hint="2025 vs Budget"
261
+ tone="mint"
262
+ />
263
+ </ScrollView>
264
+
265
+ <View style={styles.insightStrip}>
266
+ <View>
267
+ <Text style={styles.insightTitle}>YoY Profit Change</Text>
268
+ <TrendBadge value={yoyPercent} />
269
+ </View>
270
+ <View>
271
+ <Text style={styles.insightTitle}>Best Month</Text>
272
+ <Text style={styles.insightValue}>{bestMonth?.monthLabel || '-'}</Text>
273
+ </View>
274
+ </View>
275
+
276
+ <View style={styles.metricSwitch}>
277
+ <TouchableOpacity
278
+ style={[styles.metricButton, metric === 'profit' && styles.metricButtonActive]}
279
+ onPress={() => setMetric('profit')}
280
+ >
281
+ <Text style={[styles.metricButtonText, metric === 'profit' && styles.metricButtonTextActive]}>
282
+ Profit
283
+ </Text>
284
+ </TouchableOpacity>
285
+ <TouchableOpacity
286
+ style={[styles.metricButton, metric === 'teu' && styles.metricButtonActive]}
287
+ onPress={() => setMetric('teu')}
288
+ >
289
+ <Text style={[styles.metricButtonText, metric === 'teu' && styles.metricButtonTextActive]}>
290
+ TEU
291
+ </Text>
292
+ </TouchableOpacity>
293
+ </View>
294
+
295
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.monthQuickScroll}>
296
+ {quickMonths.map((month) => (
297
+ <TouchableOpacity
298
+ key={month}
299
+ style={[styles.monthChip, focusMonth === month && styles.monthChipActive]}
300
+ onPress={() => setFocusMonth(month)}
301
+ >
302
+ <Text style={[styles.monthChipText, focusMonth === month && styles.monthChipTextActive]}>
303
+ {month}
304
+ </Text>
305
+ </TouchableOpacity>
306
+ ))}
307
+ </ScrollView>
308
+
309
+ <ModernBars rows={rows} metric={metric} />
310
+
311
+ <Text style={styles.sectionTitle}>Monthly insight cards</Text>
312
+ {rows.map((row) => (
313
+ <View key={row.monthLabel} style={styles.monthCard}>
314
+ <View style={styles.monthCardHeader}>
315
+ <Text style={styles.monthCardTitle}>{row.monthLabel}</Text>
316
+ <TrendBadge value={toNumber(row.profitChangePercent)} />
317
+ </View>
318
+
319
+ <View style={styles.metricGrid}>
320
+ <MetricCell label="2024 Profit" value={formatNumber(row.profitUsd2024)} />
321
+ <MetricCell label="2025 Profit" value={formatNumber(row.profitUsd2025)} />
322
+ <MetricCell label="2025 Budget" value={formatNumber(row.budgetProfitUsd2025)} />
323
+ <MetricCell
324
+ label="Budget Delta"
325
+ value={`${toNumber(row.budgetChangePercent).toFixed(1)}%`}
326
+ accent
327
+ />
328
+ </View>
329
+
330
+ <View style={styles.progressTrack}>
331
+ <View
332
+ style={[
333
+ styles.progressFill,
334
+ {
335
+ width: `${Math.min(
336
+ 100,
337
+ Math.max(
338
+ 0,
339
+ (toNumber(row.profitUsd2025) / Math.max(1, toNumber(row.budgetProfitUsd2025))) * 100,
340
+ ),
341
+ )}%`,
342
+ },
343
+ ]}
344
+ />
345
+ </View>
346
+ </View>
347
+ ))}
348
+
349
+ {!rows.length ? (
350
+ <View style={styles.emptyWrap}>
351
+ <Text style={styles.emptyText}>No months selected. Please update month filters.</Text>
352
+ </View>
353
+ ) : null}
354
+ </ScrollView>
355
+
356
+ <DivisionFilterModal
357
+ visible={divisionModal}
358
+ divisions={divisions}
359
+ selected={division}
360
+ onSelect={setDivision}
361
+ onClose={() => setDivisionModal(false)}
362
+ />
363
+
364
+ <MonthFilterModal
365
+ visible={monthsModal}
366
+ months={baseRows.map((row) => row.monthLabel)}
367
+ selected={selectedMonths}
368
+ onApply={setSelectedMonths}
369
+ onClose={() => setMonthsModal(false)}
370
+ />
371
+ </View>
372
+ );
373
+ };
374
+
375
+ const styles = StyleSheet.create({
376
+ screen: {
377
+ flex: 1,
378
+ backgroundColor: '#f1f5fb',
379
+ },
380
+ loaderWrap: {
381
+ flex: 1,
382
+ justifyContent: 'center',
383
+ alignItems: 'center',
384
+ backgroundColor: '#f1f5fb',
385
+ },
386
+ hero: {
387
+ backgroundColor: '#0d3d77',
388
+ paddingHorizontal: 16,
389
+ paddingTop: 14,
390
+ paddingBottom: 16,
391
+ overflow: 'hidden',
392
+ },
393
+ heroDotLarge: {
394
+ position: 'absolute',
395
+ width: 180,
396
+ height: 180,
397
+ borderRadius: 90,
398
+ right: -64,
399
+ top: -90,
400
+ backgroundColor: '#1f65ae',
401
+ },
402
+ heroDotSmall: {
403
+ position: 'absolute',
404
+ width: 120,
405
+ height: 120,
406
+ borderRadius: 60,
407
+ left: -40,
408
+ bottom: -70,
409
+ backgroundColor: '#2d7ac8',
410
+ },
411
+ backButton: {
412
+ width: 42,
413
+ height: 42,
414
+ borderRadius: 21,
415
+ backgroundColor: 'rgba(255,255,255,0.18)',
416
+ justifyContent: 'center',
417
+ alignItems: 'center',
418
+ },
419
+ backIcon: {
420
+ fontSize: 28,
421
+ color: '#fff',
422
+ fontWeight: '700',
423
+ marginTop: -2,
424
+ },
425
+ heroTitle: {
426
+ marginTop: 10,
427
+ fontSize: 22,
428
+ fontWeight: '800',
429
+ color: '#fff',
430
+ },
431
+ heroSubtitle: {
432
+ marginTop: 6,
433
+ fontSize: 13,
434
+ color: '#cfe5ff',
435
+ fontWeight: '500',
436
+ },
437
+ content: {
438
+ flex: 1,
439
+ paddingHorizontal: 14,
440
+ paddingTop: 14,
441
+ },
442
+ filterRow: {
443
+ flexDirection: 'row',
444
+ marginBottom: 12,
445
+ },
446
+ filterChip: {
447
+ flex: 1,
448
+ backgroundColor: '#fff',
449
+ borderRadius: 14,
450
+ paddingVertical: 10,
451
+ paddingHorizontal: 12,
452
+ borderWidth: 1,
453
+ borderColor: '#d2deee',
454
+ },
455
+ filterChipSpacing: {
456
+ marginRight: 10,
457
+ },
458
+ filterChipLabel: {
459
+ fontSize: 11,
460
+ color: '#5e6878',
461
+ marginBottom: 3,
462
+ },
463
+ filterChipValue: {
464
+ fontSize: 14,
465
+ color: '#14253d',
466
+ fontWeight: '700',
467
+ },
468
+ kpiScroll: {
469
+ marginBottom: 12,
470
+ },
471
+ kpiCard: {
472
+ width: 165,
473
+ borderRadius: 16,
474
+ paddingHorizontal: 12,
475
+ paddingVertical: 12,
476
+ marginRight: 10,
477
+ borderWidth: 1,
478
+ },
479
+ kpiCool: {
480
+ backgroundColor: '#eaf2ff',
481
+ borderColor: '#94baff',
482
+ },
483
+ kpiWarm: {
484
+ backgroundColor: '#fff1e7',
485
+ borderColor: '#ffc89f',
486
+ },
487
+ kpiMint: {
488
+ backgroundColor: '#ebfff8',
489
+ borderColor: '#9fe8ca',
490
+ },
491
+ kpiLabel: {
492
+ fontSize: 12,
493
+ color: '#30425a',
494
+ marginBottom: 8,
495
+ },
496
+ kpiValue: {
497
+ fontSize: 18,
498
+ fontWeight: '800',
499
+ color: '#11253f',
500
+ },
501
+ kpiHint: {
502
+ marginTop: 6,
503
+ fontSize: 11,
504
+ color: '#57667b',
505
+ },
506
+ insightStrip: {
507
+ backgroundColor: '#fff',
508
+ borderRadius: 14,
509
+ borderWidth: 1,
510
+ borderColor: '#d2deee',
511
+ paddingVertical: 12,
512
+ paddingHorizontal: 12,
513
+ flexDirection: 'row',
514
+ justifyContent: 'space-between',
515
+ marginBottom: 12,
516
+ },
517
+ insightTitle: {
518
+ fontSize: 11,
519
+ color: '#65748a',
520
+ marginBottom: 6,
521
+ },
522
+ insightValue: {
523
+ fontSize: 14,
524
+ fontWeight: '700',
525
+ color: '#152842',
526
+ },
527
+ trendBadge: {
528
+ borderRadius: 999,
529
+ paddingVertical: 4,
530
+ paddingHorizontal: 8,
531
+ alignSelf: 'flex-start',
532
+ },
533
+ trendUp: {
534
+ backgroundColor: '#e4f8ef',
535
+ },
536
+ trendDown: {
537
+ backgroundColor: '#ffe9ea',
538
+ },
539
+ trendText: {
540
+ fontSize: 12,
541
+ fontWeight: '700',
542
+ },
543
+ trendTextUp: {
544
+ color: '#1f8b58',
545
+ },
546
+ trendTextDown: {
547
+ color: '#c0393f',
548
+ },
549
+ metricSwitch: {
550
+ flexDirection: 'row',
551
+ borderRadius: 12,
552
+ borderWidth: 1,
553
+ borderColor: '#d2deee',
554
+ backgroundColor: '#fff',
555
+ padding: 4,
556
+ marginBottom: 10,
557
+ },
558
+ metricButton: {
559
+ flex: 1,
560
+ paddingVertical: 10,
561
+ borderRadius: 10,
562
+ alignItems: 'center',
563
+ },
564
+ metricButtonActive: {
565
+ backgroundColor: '#174b8f',
566
+ },
567
+ metricButtonText: {
568
+ fontSize: 13,
569
+ fontWeight: '700',
570
+ color: '#3a4a62',
571
+ },
572
+ metricButtonTextActive: {
573
+ color: '#fff',
574
+ },
575
+ monthQuickScroll: {
576
+ marginBottom: 10,
577
+ },
578
+ monthChip: {
579
+ backgroundColor: '#fff',
580
+ borderRadius: 999,
581
+ borderWidth: 1,
582
+ borderColor: '#d5dfed',
583
+ paddingVertical: 7,
584
+ paddingHorizontal: 12,
585
+ marginRight: 8,
586
+ },
587
+ monthChipActive: {
588
+ backgroundColor: '#133f79',
589
+ borderColor: '#133f79',
590
+ },
591
+ monthChipText: {
592
+ fontSize: 12,
593
+ fontWeight: '700',
594
+ color: '#3f4f64',
595
+ },
596
+ monthChipTextActive: {
597
+ color: '#fff',
598
+ },
599
+ chartCard: {
600
+ backgroundColor: '#fff',
601
+ borderRadius: 14,
602
+ borderWidth: 1,
603
+ borderColor: '#d2deee',
604
+ padding: 12,
605
+ marginBottom: 14,
606
+ },
607
+ chartHeader: {
608
+ flexDirection: 'row',
609
+ justifyContent: 'space-between',
610
+ alignItems: 'center',
611
+ marginBottom: 8,
612
+ },
613
+ chartTitle: {
614
+ fontSize: 14,
615
+ fontWeight: '800',
616
+ color: '#142a45',
617
+ },
618
+ legendRow: {
619
+ flexDirection: 'row',
620
+ },
621
+ legendTextOrange: {
622
+ marginRight: 12,
623
+ color: '#e57d2f',
624
+ fontSize: 12,
625
+ fontWeight: '700',
626
+ },
627
+ legendTextBlue: {
628
+ color: '#2e74c4',
629
+ fontSize: 12,
630
+ fontWeight: '700',
631
+ },
632
+ chartColumns: {
633
+ flexDirection: 'row',
634
+ paddingTop: 8,
635
+ },
636
+ chartGroup: {
637
+ alignItems: 'center',
638
+ width: 46,
639
+ marginRight: 8,
640
+ },
641
+ barPair: {
642
+ flexDirection: 'row',
643
+ alignItems: 'flex-end',
644
+ marginBottom: 7,
645
+ },
646
+ barTrack: {
647
+ width: 12,
648
+ height: 126,
649
+ borderRadius: 10,
650
+ justifyContent: 'flex-end',
651
+ backgroundColor: '#eef3fa',
652
+ marginHorizontal: 2,
653
+ overflow: 'hidden',
654
+ },
655
+ barFill2024: {
656
+ backgroundColor: '#f19a54',
657
+ borderRadius: 10,
658
+ },
659
+ barFill2025: {
660
+ backgroundColor: '#2f7cca',
661
+ borderRadius: 10,
662
+ },
663
+ chartMonthText: {
664
+ fontSize: 10,
665
+ color: '#4f6078',
666
+ fontWeight: '700',
667
+ },
668
+ chartDelta: {
669
+ marginTop: 3,
670
+ fontSize: 10,
671
+ fontWeight: '700',
672
+ },
673
+ deltaUp: {
674
+ color: '#229060',
675
+ },
676
+ deltaDown: {
677
+ color: '#cb4346',
678
+ },
679
+ sectionTitle: {
680
+ fontSize: 15,
681
+ fontWeight: '800',
682
+ color: '#152a43',
683
+ marginBottom: 8,
684
+ },
685
+ monthCard: {
686
+ backgroundColor: '#fff',
687
+ borderRadius: 14,
688
+ borderWidth: 1,
689
+ borderColor: '#d2deee',
690
+ padding: 12,
691
+ marginBottom: 10,
692
+ },
693
+ monthCardHeader: {
694
+ flexDirection: 'row',
695
+ justifyContent: 'space-between',
696
+ alignItems: 'center',
697
+ marginBottom: 10,
698
+ },
699
+ monthCardTitle: {
700
+ fontSize: 15,
701
+ fontWeight: '800',
702
+ color: '#132844',
703
+ },
704
+ metricGrid: {
705
+ flexDirection: 'row',
706
+ flexWrap: 'wrap',
707
+ marginHorizontal: -4,
708
+ },
709
+ metricCell: {
710
+ width: '50%',
711
+ paddingHorizontal: 4,
712
+ marginBottom: 10,
713
+ },
714
+ metricLabel: {
715
+ fontSize: 11,
716
+ color: '#62738a',
717
+ marginBottom: 3,
718
+ },
719
+ metricValue: {
720
+ fontSize: 13,
721
+ color: '#12263f',
722
+ fontWeight: '700',
723
+ },
724
+ metricValueAccent: {
725
+ color: '#0f5ba5',
726
+ },
727
+ progressTrack: {
728
+ height: 8,
729
+ borderRadius: 999,
730
+ backgroundColor: '#ecf2fa',
731
+ overflow: 'hidden',
732
+ },
733
+ progressFill: {
734
+ height: '100%',
735
+ backgroundColor: '#3f8fe0',
736
+ },
737
+ emptyWrap: {
738
+ paddingVertical: 18,
739
+ alignItems: 'center',
740
+ },
741
+ emptyText: {
742
+ color: '#63748a',
743
+ fontSize: 13,
744
+ },
745
+ });
746
+
747
+ export default Report2ModernScreen;
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useMemo, useState } from 'react';
2
2
  import {
3
3
  View,
4
4
  Text,
@@ -7,53 +7,61 @@ import {
7
7
  ScrollView,
8
8
  } from 'react-native';
9
9
 
10
- const REPORTS = [
11
-
10
+ const OLD_REPORTS = [
12
11
  {
13
12
  id: '4A',
14
13
  title: 'Performans Raporu (USD) - NEW',
15
- desc: 'New report formate',
14
+ desc: 'Current package flow',
16
15
  },
17
-
18
16
  {
19
17
  id: '2A',
20
18
  title: 'Gross Profit by Company & Division',
21
- desc: 'Option 1',
19
+ desc: 'Current option 1',
22
20
  },
23
21
  {
24
22
  id: 2,
25
23
  title: 'Gross Profit by Company & Division',
26
- desc: 'Option 2',
24
+ desc: 'Current option 2',
27
25
  },
28
-
29
26
  {
30
27
  id: '3A',
31
28
  title: 'Transportation Business Analysis',
32
- desc: 'Option 1',
29
+ desc: 'Current option 1',
33
30
  },
34
31
  {
35
32
  id: 3,
36
33
  title: 'Transportation Business Analysis',
37
- desc: 'Option 2',
34
+ desc: 'Current option 2',
38
35
  },
39
-
40
36
  {
41
37
  id: '1A',
42
38
  title: 'Performans Raporu (USD) - OLD',
43
- desc: 'Option 1',
39
+ desc: 'Current option 1',
44
40
  },
45
41
  {
46
42
  id: 1,
47
43
  title: 'Performans Raporu (USD) - OLD',
48
- desc: 'Option 2',
44
+ desc: 'Current option 2',
45
+ },
46
+ ];
47
+
48
+ const NEW_REPORTS = [
49
+ {
50
+ id: '2N1',
51
+ title: 'Gross Profit by Company & Division',
52
+ desc: 'Modern interactive layout',
49
53
  },
50
-
51
54
  ];
52
55
 
53
56
  const ReportListScreen = ({ onSelect, onExit }) => {
57
+ const [activeMode, setActiveMode] = useState('old');
58
+
59
+ const reports = useMemo(() => {
60
+ return activeMode === 'old' ? OLD_REPORTS : NEW_REPORTS;
61
+ }, [activeMode]);
62
+
54
63
  return (
55
64
  <View style={styles.container}>
56
- {/* HEADER */}
57
65
  <View style={styles.header}>
58
66
  <TouchableOpacity
59
67
  onPress={onExit}
@@ -64,19 +72,52 @@ const ReportListScreen = ({ onSelect, onExit }) => {
64
72
  </TouchableOpacity>
65
73
 
66
74
  <Text style={styles.title}>Analytics Reports</Text>
75
+ <View style={styles.headerSpacer} />
76
+ </View>
77
+
78
+ <Text style={styles.subtitle}>
79
+ Select report mode before opening report tabs
80
+ </Text>
67
81
 
68
- {/* Spacer */}
69
- <View style={{ width: 28 }} />
82
+ <View style={styles.modeRow}>
83
+ <TouchableOpacity
84
+ style={[styles.modeBtn, activeMode === 'old' && styles.modeBtnActive]}
85
+ onPress={() => setActiveMode('old')}
86
+ >
87
+ <Text
88
+ style={[
89
+ styles.modeBtnText,
90
+ activeMode === 'old' && styles.modeBtnTextActive,
91
+ ]}
92
+ >
93
+ Option 1 - Old
94
+ </Text>
95
+ </TouchableOpacity>
96
+ <TouchableOpacity
97
+ style={[styles.modeBtn, activeMode === 'new' && styles.modeBtnActive]}
98
+ onPress={() => setActiveMode('new')}
99
+ >
100
+ <Text
101
+ style={[
102
+ styles.modeBtnText,
103
+ activeMode === 'new' && styles.modeBtnTextActive,
104
+ ]}
105
+ >
106
+ Option 2 - New
107
+ </Text>
108
+ </TouchableOpacity>
70
109
  </View>
71
110
 
72
- {/* REPORT CARDS */}
73
111
  <ScrollView showsVerticalScrollIndicator={false}>
74
- {REPORTS.map(report => (
112
+ {reports.map((report) => (
75
113
  <TouchableOpacity
76
114
  key={report.id}
77
- style={styles.card}
115
+ style={[
116
+ styles.card,
117
+ activeMode === 'new' && styles.cardNewTheme,
118
+ ]}
78
119
  onPress={() => onSelect(report.id)}
79
- activeOpacity={0.85}
120
+ activeOpacity={0.88}
80
121
  >
81
122
  <Text style={styles.cardTitle}>{report.title}</Text>
82
123
  <Text style={styles.cardDesc}>{report.desc}</Text>
@@ -92,28 +133,23 @@ export default ReportListScreen;
92
133
  const styles = StyleSheet.create({
93
134
  container: {
94
135
  flex: 1,
95
- backgroundColor: '#f4f6f8',
136
+ backgroundColor: '#edf1f7',
96
137
  padding: 16,
97
138
  },
98
-
99
- /* HEADER */
100
139
  header: {
101
140
  flexDirection: 'row',
102
141
  alignItems: 'center',
103
- marginBottom: 20,
142
+ marginBottom: 6,
104
143
  },
105
-
106
144
  backBtn: {
107
145
  width: 28,
108
146
  alignItems: 'flex-start',
109
147
  },
110
-
111
148
  back: {
112
149
  fontSize: 28,
113
150
  fontWeight: '700',
114
151
  color: '#111',
115
152
  },
116
-
117
153
  title: {
118
154
  flex: 1,
119
155
  textAlign: 'center',
@@ -121,34 +157,65 @@ const styles = StyleSheet.create({
121
157
  fontWeight: '700',
122
158
  color: '#111',
123
159
  },
124
-
125
- /* CARDS */
160
+ headerSpacer: {
161
+ width: 28,
162
+ },
163
+ subtitle: {
164
+ fontSize: 12,
165
+ color: '#5b6570',
166
+ marginBottom: 14,
167
+ textAlign: 'center',
168
+ },
169
+ modeRow: {
170
+ flexDirection: 'row',
171
+ backgroundColor: '#dce3ed',
172
+ borderRadius: 12,
173
+ padding: 4,
174
+ marginBottom: 16,
175
+ },
176
+ modeBtn: {
177
+ flex: 1,
178
+ borderRadius: 10,
179
+ paddingVertical: 10,
180
+ alignItems: 'center',
181
+ },
182
+ modeBtnActive: {
183
+ backgroundColor: '#0f3d75',
184
+ },
185
+ modeBtnText: {
186
+ fontSize: 13,
187
+ fontWeight: '700',
188
+ color: '#2f3a46',
189
+ },
190
+ modeBtnTextActive: {
191
+ color: '#fff',
192
+ },
126
193
  card: {
127
194
  backgroundColor: '#fff',
128
195
  padding: 18,
129
196
  borderRadius: 14,
130
197
  marginBottom: 14,
131
-
132
198
  borderWidth: 1,
133
- borderColor: '#e0e0e0',
134
-
135
- shadowColor: '#000',
136
- shadowOpacity: 0.06,
137
- shadowRadius: 8,
138
- shadowOffset: { width: 0, height: 3 },
199
+ borderColor: '#d9e0ea',
200
+ shadowColor: '#0b1f3a',
201
+ shadowOpacity: 0.08,
202
+ shadowRadius: 10,
203
+ shadowOffset: { width: 0, height: 4 },
139
204
  elevation: 2,
140
205
  },
141
-
206
+ cardNewTheme: {
207
+ borderColor: '#8bb4ff',
208
+ backgroundColor: '#f4f8ff',
209
+ },
142
210
  cardTitle: {
143
211
  fontSize: 16,
144
212
  fontWeight: '700',
145
213
  marginBottom: 6,
146
214
  color: '#111',
147
215
  },
148
-
149
216
  cardDesc: {
150
217
  fontSize: 13,
151
- color: '#555',
218
+ color: '#4e5968',
152
219
  lineHeight: 18,
153
220
  },
154
221
  });