@dhiraj0720/report1chart 3.0.6 → 3.0.7

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.
@@ -8,10 +8,16 @@ import {
8
8
  TouchableOpacity,
9
9
  View,
10
10
  } from 'react-native';
11
- import Svg, { Circle, Line, Polyline } from 'react-native-svg';
12
- import { fetchReport3Table } from '../api/report3Fetcher';
11
+ import Svg, { Circle } from 'react-native-svg';
12
+ import {
13
+ fetchReport3Bar,
14
+ fetchReport3Line,
15
+ fetchReport3Table,
16
+ } from '../api/report3Fetcher';
13
17
  import MonthFilterModal from '../components/MonthFilterModal';
14
- import { formatNumber } from '../utils/formatNumber';
18
+ import CompactAxisBarLineChart from '../components/CompactAxisBarLineChart';
19
+ import CompactAxisLineChart from '../components/CompactAxisLineChart';
20
+ import { filterChartByMonths } from '../utils/filterChartByMonths';
15
21
 
16
22
  const toNumber = (value) => {
17
23
  const numeric = Number(value);
@@ -45,7 +51,7 @@ const StatTile = ({ label, value, hint, tone }) => (
45
51
  );
46
52
 
47
53
  const RingGauge = ({ progress }) => {
48
- const size = 60;
54
+ const size = 58;
49
55
  const stroke = 8;
50
56
  const radius = (size - stroke) / 2;
51
57
  const circumference = 2 * Math.PI * radius;
@@ -78,124 +84,37 @@ const RingGauge = ({ progress }) => {
78
84
  );
79
85
  };
80
86
 
81
- const SignalChart = ({ rows, mode }) => {
82
- if (!rows.length) return null;
83
-
84
- const values2024 = rows.map((row) =>
85
- mode === 'loads' ? toNumber(row.loadCount2024) : toNumber(row.revenueTl2024),
86
- );
87
- const values2025 = rows.map((row) =>
88
- mode === 'loads' ? toNumber(row.loadCount2025) : toNumber(row.revenueTl2025),
89
- );
90
-
91
- const width = Math.max(360, rows.length * 72);
92
- const height = 190;
93
- const left = 18;
94
- const right = 12;
95
- const top = 16;
96
- const bottom = 38;
97
- const chartHeight = height - top - bottom;
98
-
99
- const all = [...values2024, ...values2025];
100
- const min = Math.min(...all);
101
- const max = Math.max(...all);
102
- const pad = Math.max(1, (max - min) * 0.14);
103
- const yMin = min - pad;
104
- const yMax = max + pad;
105
-
106
- const stepX = rows.length > 1 ? (width - left - right) / (rows.length - 1) : width / 2;
107
- const toY = (value) => top + ((yMax - value) / Math.max(1, yMax - yMin)) * chartHeight;
108
- const toX = (index) => left + index * stepX;
109
-
110
- const points2024 = values2024.map((value, i) => `${toX(i)},${toY(value)}`).join(' ');
111
- const points2025 = values2025.map((value, i) => `${toX(i)},${toY(value)}`).join(' ');
112
-
113
- return (
114
- <View style={styles.signalCard}>
115
- <View style={styles.signalHeader}>
116
- <Text style={styles.signalTitle}>
117
- {mode === 'loads' ? 'Load Movement Signal' : 'Revenue Movement Signal'}
118
- </Text>
119
- <View style={styles.signalLegend}>
120
- <Text style={styles.legend2024}>2024</Text>
121
- <Text style={styles.legend2025}>2025</Text>
122
- </View>
123
- </View>
124
-
125
- <ScrollView horizontal showsHorizontalScrollIndicator={false}>
126
- <Svg width={width} height={height}>
127
- {[0, 0.5, 1].map((ratio) => {
128
- const y = top + ratio * chartHeight;
129
- return (
130
- <Line
131
- key={ratio}
132
- x1={left}
133
- y1={y}
134
- x2={width - right}
135
- y2={y}
136
- stroke="#e7e3d9"
137
- strokeDasharray="4 5"
138
- strokeWidth={1}
139
- />
140
- );
141
- })}
142
-
143
- <Polyline
144
- points={points2024}
145
- fill="none"
146
- stroke="#f59f40"
147
- strokeWidth={3}
148
- strokeLinecap="round"
149
- strokeLinejoin="round"
150
- strokeDasharray="8 6"
151
- />
152
- <Polyline
153
- points={points2025}
154
- fill="none"
155
- stroke="#168a73"
156
- strokeWidth={3}
157
- strokeLinecap="round"
158
- strokeLinejoin="round"
159
- />
160
-
161
- {rows.map((row, index) => (
162
- <React.Fragment key={`${row.monthLabel}-${index}`}>
163
- <Circle cx={toX(index)} cy={toY(values2024[index])} r={4.8} fill="#f59f40" />
164
- <Circle cx={toX(index)} cy={toY(values2025[index])} r={4.8} fill="#168a73" />
165
- </React.Fragment>
166
- ))}
167
- </Svg>
168
- </ScrollView>
169
-
170
- <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.signalMonthStrip}>
171
- {rows.map((row) => (
172
- <View key={`month-${row.monthLabel}`} style={styles.signalMonthChip}>
173
- <Text style={styles.signalMonthText}>{row.monthLabel}</Text>
174
- </View>
175
- ))}
176
- </ScrollView>
177
- </View>
178
- );
179
- };
180
-
181
87
  const Report3ModernScreen = ({ api, token, onBack }) => {
182
88
  const [loading, setLoading] = useState(true);
183
89
  const [refreshing, setRefreshing] = useState(false);
184
90
  const [monthsModal, setMonthsModal] = useState(false);
185
91
  const [table, setTable] = useState(null);
92
+ const [lineData, setLineData] = useState(null);
93
+ const [barData, setBarData] = useState(null);
186
94
  const [selectedMonths, setSelectedMonths] = useState([]);
187
- const [focusMonth, setFocusMonth] = useState('ALL');
188
95
  const [mode, setMode] = useState('loads');
189
96
 
190
- const loadData = useCallback(async () => {
191
- const response = await fetchReport3Table(api.table, token);
192
- setTable(response);
193
- const months = (response?.rows || [])
194
- .filter((row) => row.month !== 99 && row.monthLabel !== 'Total')
195
- .map((row) => row.monthLabel);
196
- setSelectedMonths(months);
197
- setFocusMonth('ALL');
198
- }, [api.table, token]);
97
+ const loadData = useCallback(
98
+ async (preserveSelection = false) => {
99
+ const [tableRes, lineRes, barRes] = await Promise.all([
100
+ fetchReport3Table(api.table, token),
101
+ fetchReport3Line(api.line, token),
102
+ fetchReport3Bar(api.bar, token),
103
+ ]);
104
+
105
+ setTable(tableRes);
106
+ setLineData(lineRes);
107
+ setBarData(barRes);
108
+
109
+ const months = lineRes?.labels || [];
110
+ setSelectedMonths((prev) => {
111
+ if (!preserveSelection) return months;
112
+ const kept = prev.filter((month) => months.includes(month));
113
+ return kept.length ? kept : months;
114
+ });
115
+ },
116
+ [api.bar, api.line, api.table, token],
117
+ );
199
118
 
200
119
  useEffect(() => {
201
120
  setLoading(true);
@@ -207,28 +126,34 @@ const Report3ModernScreen = ({ api, token, onBack }) => {
207
126
  const onRefresh = useCallback(async () => {
208
127
  setRefreshing(true);
209
128
  try {
210
- await loadData();
129
+ await loadData(true);
211
130
  } finally {
212
131
  setRefreshing(false);
213
132
  }
214
133
  }, [loadData]);
215
134
 
135
+ const monthOptions = useMemo(() => {
136
+ return lineData?.labels || [];
137
+ }, [lineData]);
138
+
216
139
  const baseRows = useMemo(() => {
217
140
  return (table?.rows || []).filter((row) => row.month !== 99 && row.monthLabel !== 'Total');
218
141
  }, [table]);
219
142
 
220
143
  const rows = useMemo(() => {
221
- let filtered = baseRows;
222
- if (selectedMonths.length) {
223
- filtered = filtered.filter((row) => selectedMonths.includes(row.monthLabel));
224
- }
225
- if (focusMonth !== 'ALL') {
226
- filtered = filtered.filter((row) => row.monthLabel === focusMonth);
227
- }
228
- return filtered;
229
- }, [baseRows, selectedMonths, focusMonth]);
144
+ if (!selectedMonths.length) return [];
145
+ return baseRows.filter((row) => selectedMonths.includes(row.monthLabel));
146
+ }, [baseRows, selectedMonths]);
230
147
 
231
- const quickMonths = useMemo(() => ['ALL', ...selectedMonths.slice(0, 6)], [selectedMonths]);
148
+ const filteredLine = useMemo(() => {
149
+ if (!lineData) return null;
150
+ return filterChartByMonths(lineData, selectedMonths);
151
+ }, [lineData, selectedMonths]);
152
+
153
+ const filteredBar = useMemo(() => {
154
+ if (!barData) return null;
155
+ return filterChartByMonths(barData, selectedMonths);
156
+ }, [barData, selectedMonths]);
232
157
 
233
158
  const load2024Total = useMemo(
234
159
  () => rows.reduce((sum, row) => sum + toNumber(row.loadCount2024), 0),
@@ -267,15 +192,15 @@ const Report3ModernScreen = ({ api, token, onBack }) => {
267
192
 
268
193
  const maxLaneValue = useMemo(() => {
269
194
  if (!rows.length) return 1;
270
- const values = rows.flatMap((row) => (
195
+ const values = rows.flatMap((row) =>
271
196
  mode === 'loads'
272
197
  ? [toNumber(row.loadCount2024), toNumber(row.loadCount2025)]
273
- : [toNumber(row.revenueTl2024), toNumber(row.revenueTl2025), toNumber(row.budgetRevenueTl2025)]
274
- ));
198
+ : [toNumber(row.revenueTl2024), toNumber(row.revenueTl2025), toNumber(row.budgetRevenueTl2025)],
199
+ );
275
200
  return Math.max(1, ...values);
276
201
  }, [rows, mode]);
277
202
 
278
- if (loading && !table) {
203
+ if (loading && (!table || !lineData || !barData)) {
279
204
  return (
280
205
  <View style={styles.loaderWrap}>
281
206
  <ActivityIndicator size="large" color="#13866f" />
@@ -292,7 +217,7 @@ const Report3ModernScreen = ({ api, token, onBack }) => {
292
217
  <Text style={styles.backIcon}>‹</Text>
293
218
  </TouchableOpacity>
294
219
  <Text style={styles.heroTitle}>Transportation Command Center</Text>
295
- <Text style={styles.heroSubtitle}>Operational pulse for load and revenue movement</Text>
220
+ <Text style={styles.heroSubtitle}>Filtered API visuals with compact axis charts</Text>
296
221
  </View>
297
222
 
298
223
  <ScrollView
@@ -308,7 +233,7 @@ const Report3ModernScreen = ({ api, token, onBack }) => {
308
233
  >
309
234
  <Text style={styles.controlChipLabel}>Months</Text>
310
235
  <Text style={styles.controlChipValue}>
311
- {selectedMonths.length}/{baseRows.length || selectedMonths.length}
236
+ {selectedMonths.length}/{monthOptions.length || selectedMonths.length}
312
237
  </Text>
313
238
  </TouchableOpacity>
314
239
 
@@ -332,20 +257,6 @@ const Report3ModernScreen = ({ api, token, onBack }) => {
332
257
  </View>
333
258
  </View>
334
259
 
335
- <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.quickMonthRow}>
336
- {quickMonths.map((month) => (
337
- <TouchableOpacity
338
- key={month}
339
- style={[styles.monthChip, focusMonth === month && styles.monthChipActive]}
340
- onPress={() => setFocusMonth(month)}
341
- >
342
- <Text style={[styles.monthChipText, focusMonth === month && styles.monthChipTextActive]}>
343
- {month}
344
- </Text>
345
- </TouchableOpacity>
346
- ))}
347
- </ScrollView>
348
-
349
260
  <View style={styles.statRow}>
350
261
  <StatTile
351
262
  label="Load YoY"
@@ -375,7 +286,24 @@ const Report3ModernScreen = ({ api, token, onBack }) => {
375
286
  />
376
287
  </View>
377
288
 
378
- <SignalChart rows={rows} mode={mode} />
289
+ <CompactAxisLineChart
290
+ data={filteredLine}
291
+ title="Load Amount Trend (Line)"
292
+ legend={[
293
+ { label: filteredLine?.series?.[0]?.name || '2024 Load', color: '#F29F45' },
294
+ { label: filteredLine?.series?.[1]?.name || '2025 Load', color: '#2E7DD1' },
295
+ ]}
296
+ />
297
+
298
+ <CompactAxisBarLineChart
299
+ data={filteredBar}
300
+ title="Revenue vs Budget (Compact)"
301
+ legend={[
302
+ { label: filteredBar?.series?.[0]?.name || '2024 Revenue', color: '#F29F45' },
303
+ { label: filteredBar?.series?.[1]?.name || '2025 Revenue', color: '#2E7DD1' },
304
+ { label: filteredBar?.series?.[2]?.name || '2025 Budget', color: '#8AB6E8' },
305
+ ]}
306
+ />
379
307
 
380
308
  <Text style={styles.sectionTitle}>Operational lanes</Text>
381
309
  {rows.map((row) => {
@@ -429,14 +357,16 @@ const Report3ModernScreen = ({ api, token, onBack }) => {
429
357
 
430
358
  {!rows.length ? (
431
359
  <View style={styles.emptyWrap}>
432
- <Text style={styles.emptyText}>No data for selected filters.</Text>
360
+ <Text style={styles.emptyText}>No data for selected month filter.</Text>
433
361
  </View>
434
362
  ) : null}
363
+
364
+ <View style={styles.footerSpacing} />
435
365
  </ScrollView>
436
366
 
437
367
  <MonthFilterModal
438
368
  visible={monthsModal}
439
- months={baseRows.map((row) => row.monthLabel)}
369
+ months={monthOptions}
440
370
  selected={selectedMonths}
441
371
  onApply={setSelectedMonths}
442
372
  onClose={() => setMonthsModal(false)}
@@ -563,30 +493,6 @@ const styles = StyleSheet.create({
563
493
  modeButtonTextActive: {
564
494
  color: '#fff',
565
495
  },
566
- quickMonthRow: {
567
- marginBottom: 12,
568
- },
569
- monthChip: {
570
- backgroundColor: '#fffdf8',
571
- borderWidth: 1,
572
- borderColor: '#ddd2be',
573
- borderRadius: 999,
574
- paddingVertical: 7,
575
- paddingHorizontal: 12,
576
- marginRight: 8,
577
- },
578
- monthChipActive: {
579
- backgroundColor: '#e3912f',
580
- borderColor: '#e3912f',
581
- },
582
- monthChipText: {
583
- fontSize: 12,
584
- color: '#635640',
585
- fontWeight: '700',
586
- },
587
- monthChipTextActive: {
588
- color: '#fff',
589
- },
590
496
  statRow: {
591
497
  flexDirection: 'row',
592
498
  marginBottom: 10,
@@ -626,54 +532,6 @@ const styles = StyleSheet.create({
626
532
  fontSize: 11,
627
533
  color: '#6b6860',
628
534
  },
629
- signalCard: {
630
- backgroundColor: '#fffdf8',
631
- borderWidth: 1,
632
- borderColor: '#e0d7c6',
633
- borderRadius: 16,
634
- padding: 12,
635
- marginBottom: 12,
636
- },
637
- signalHeader: {
638
- flexDirection: 'row',
639
- justifyContent: 'space-between',
640
- alignItems: 'center',
641
- marginBottom: 8,
642
- },
643
- signalTitle: {
644
- fontSize: 14,
645
- fontWeight: '800',
646
- color: '#2c2a22',
647
- },
648
- signalLegend: {
649
- flexDirection: 'row',
650
- },
651
- legend2024: {
652
- marginRight: 10,
653
- color: '#d78835',
654
- fontWeight: '700',
655
- fontSize: 12,
656
- },
657
- legend2025: {
658
- color: '#15896f',
659
- fontWeight: '700',
660
- fontSize: 12,
661
- },
662
- signalMonthStrip: {
663
- marginTop: 6,
664
- },
665
- signalMonthChip: {
666
- backgroundColor: '#f5efe2',
667
- borderRadius: 999,
668
- paddingVertical: 5,
669
- paddingHorizontal: 10,
670
- marginRight: 6,
671
- },
672
- signalMonthText: {
673
- color: '#635742',
674
- fontSize: 11,
675
- fontWeight: '700',
676
- },
677
535
  sectionTitle: {
678
536
  fontSize: 15,
679
537
  fontWeight: '800',
@@ -780,6 +638,9 @@ const styles = StyleSheet.create({
780
638
  fontSize: 13,
781
639
  color: '#6d675a',
782
640
  },
641
+ footerSpacing: {
642
+ height: 10,
643
+ },
783
644
  });
784
645
 
785
646
  export default Report3ModernScreen;
@@ -46,6 +46,11 @@ const OLD_REPORTS = [
46
46
  ];
47
47
 
48
48
  const NEW_REPORTS = [
49
+ {
50
+ id: '1N1',
51
+ title: 'Performance Report',
52
+ desc: 'Modern performance studio',
53
+ },
49
54
  {
50
55
  id: '2N1',
51
56
  title: 'Gross Profit by Company & Division',