@dhiraj0720/report1chart 2.2.3 → 2.2.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": "2.2.3",
3
+ "version": "2.2.5",
4
4
  "main": "src/index.jsx",
5
5
  "scripts": {
6
6
  "test": "echo 'No tests'"
@@ -0,0 +1,10 @@
1
+ import axios from 'axios';
2
+
3
+ const fetchReport1 = async (endpoint, token) => {
4
+ const res = await axios.get(endpoint, {
5
+ headers: { Authorization: token },
6
+ });
7
+ return res.data.rows || [];
8
+ };
9
+
10
+ export default fetchReport1;
@@ -0,0 +1,29 @@
1
+ import axios from 'axios';
2
+
3
+ export const fetchDivisions = async (endpoint, token) => {
4
+ const res = await axios.get(endpoint, {
5
+ headers: { Authorization: token },
6
+ });
7
+ return res.data.data || [];
8
+ };
9
+
10
+ export const fetchTable = async (endpoint, division, token) => {
11
+ const res = await axios.get(`${endpoint}?division=${division}`, {
12
+ headers: { Authorization: token },
13
+ });
14
+ return res.data;
15
+ };
16
+
17
+ export const fetchLineChart = async (endpoint, division, token) => {
18
+ const res = await axios.get(`${endpoint}?division=${division}`, {
19
+ headers: { Authorization: token },
20
+ });
21
+ return res.data;
22
+ };
23
+
24
+ export const fetchBarChart = async (endpoint, division, token) => {
25
+ const res = await axios.get(`${endpoint}?division=${division}`, {
26
+ headers: { Authorization: token },
27
+ });
28
+ return res.data;
29
+ };
@@ -0,0 +1,48 @@
1
+ import React from 'react';
2
+ import { View, Text, ScrollView, StyleSheet } from 'react-native';
3
+
4
+ const BarChart = ({ data }) => {
5
+ if (!data?.series) return null;
6
+
7
+ const max = Math.max(...data.series.flatMap(s => s.data));
8
+
9
+ const colors = ['#FB8C00', '#1E88E5', '#9E9E9E'];
10
+
11
+ return (
12
+ <View style={styles.container}>
13
+ <Text style={styles.title}>{data.title}</Text>
14
+ <ScrollView horizontal>
15
+ <View style={styles.chart}>
16
+ {data.labels.map((label, i) => (
17
+ <View key={i} style={styles.group}>
18
+ {data.series.map((s, idx) => (
19
+ <View
20
+ key={idx}
21
+ style={[
22
+ styles.bar,
23
+ {
24
+ height: (s.data[i] / max) * 120,
25
+ backgroundColor: colors[idx]
26
+ }
27
+ ]}
28
+ />
29
+ ))}
30
+ <Text style={styles.label}>{label}</Text>
31
+ </View>
32
+ ))}
33
+ </View>
34
+ </ScrollView>
35
+ </View>
36
+ );
37
+ };
38
+
39
+ export default BarChart;
40
+
41
+ const styles = StyleSheet.create({
42
+ container: { marginVertical: 12 },
43
+ title: { fontWeight: '700', marginBottom: 8 },
44
+ chart: { flexDirection: 'row', alignItems: 'flex-end' },
45
+ group: { flexDirection: 'row', alignItems: 'flex-end', marginHorizontal: 6 },
46
+ bar: { width: 10, marginHorizontal: 2, borderRadius: 2 },
47
+ label: { fontSize: 10, marginTop: 4, textAlign: 'center', width: 40 }
48
+ });
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import { ScrollView, Text, TouchableOpacity, StyleSheet } from 'react-native';
3
+
4
+ const DivisionSelector = ({ divisions, selected, onSelect }) => (
5
+ <ScrollView horizontal showsHorizontalScrollIndicator={false}>
6
+ {divisions.map(d => (
7
+ <TouchableOpacity
8
+ key={d.code}
9
+ style={[styles.card, selected === d.code && styles.active]}
10
+ onPress={() => onSelect(d.code)}
11
+ >
12
+ <Text style={[styles.text, selected === d.code && styles.activeText]}>
13
+ {d.displayName}
14
+ </Text>
15
+ </TouchableOpacity>
16
+ ))}
17
+ </ScrollView>
18
+ );
19
+
20
+ export default DivisionSelector;
21
+
22
+ const styles = StyleSheet.create({
23
+ card: {
24
+ paddingHorizontal: 14,
25
+ paddingVertical: 8,
26
+ borderRadius: 16,
27
+ borderWidth: 1,
28
+ borderColor: '#ccc',
29
+ marginRight: 8,
30
+ },
31
+ active: { backgroundColor: '#000' },
32
+ text: { fontSize: 12 },
33
+ activeText: { color: '#fff', fontWeight: '700' },
34
+ });
@@ -0,0 +1,71 @@
1
+ import React from 'react';
2
+ import { View, Text, ScrollView, StyleSheet } from 'react-native';
3
+
4
+ const Cell = ({ children, bold }) => (
5
+ <View style={styles.cell}>
6
+ <Text style={[styles.text, bold && styles.bold]}>{children}</Text>
7
+ </View>
8
+ );
9
+
10
+ const FrozenTable = ({ rows = [] }) => {
11
+ if (!rows.length) {
12
+ return <Text style={styles.noData}>No data available</Text>;
13
+ }
14
+
15
+ return (
16
+ <View style={styles.container}>
17
+ {/* Frozen Column */}
18
+ <View style={styles.frozen}>
19
+ <Cell bold>AY</Cell>
20
+ {rows.map((r, i) => (
21
+ <Cell key={i} bold={r.monthLabel === 'Total'}>
22
+ {r.monthLabel}
23
+ </Cell>
24
+ ))}
25
+ </View>
26
+
27
+ {/* Scrollable Columns */}
28
+ <ScrollView horizontal>
29
+ <View>
30
+ <View style={styles.headerRow}>
31
+ <Cell bold>2024 TEU</Cell>
32
+ <Cell bold>2025 TEU</Cell>
33
+ <Cell bold>TEU %</Cell>
34
+ </View>
35
+
36
+ {rows.map((r, i) => (
37
+ <View key={i} style={styles.row}>
38
+ <Cell>{r.teu2024}</Cell>
39
+ <Cell>{r.teu2025}</Cell>
40
+ <Cell style={r.teuChangePercent < 0 ? styles.down : styles.up}>
41
+ {r.teuChangePercent}%
42
+ </Cell>
43
+ </View>
44
+ ))}
45
+ </View>
46
+ </ScrollView>
47
+ </View>
48
+ );
49
+ };
50
+
51
+ export default FrozenTable;
52
+
53
+ const styles = StyleSheet.create({
54
+ container: { flexDirection: 'row', marginVertical: 12 },
55
+ frozen: { width: 90, backgroundColor: '#f4f4f4' },
56
+ headerRow: { flexDirection: 'row', backgroundColor: '#f4f4f4' },
57
+ row: { flexDirection: 'row' },
58
+ cell: {
59
+ width: 90,
60
+ padding: 8,
61
+ borderBottomWidth: 1,
62
+ borderColor: '#ddd',
63
+ justifyContent: 'center',
64
+ alignItems: 'center'
65
+ },
66
+ text: { fontSize: 12 },
67
+ bold: { fontWeight: '700' },
68
+ up: { color: 'green' },
69
+ down: { color: 'red' },
70
+ noData: { textAlign: 'center', padding: 16 }
71
+ });
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { View, Text, ScrollView, StyleSheet } from 'react-native';
3
+
4
+ const LineChart = ({ data }) => {
5
+ if (!data?.series) return null;
6
+
7
+ const max = Math.max(...data.series.flatMap(s => s.data));
8
+
9
+ return (
10
+ <View style={styles.container}>
11
+ <Text style={styles.title}>{data.title}</Text>
12
+ <ScrollView horizontal>
13
+ <View style={styles.chart}>
14
+ {data.labels.map((label, i) => (
15
+ <View key={i} style={styles.column}>
16
+ {data.series.map((s, idx) => (
17
+ <View
18
+ key={idx}
19
+ style={[
20
+ styles.bar,
21
+ {
22
+ height: (s.data[i] / max) * 120,
23
+ backgroundColor: idx === 0 ? '#2962FF' : '#9E9E9E'
24
+ }
25
+ ]}
26
+ />
27
+ ))}
28
+ <Text style={styles.label}>{label}</Text>
29
+ </View>
30
+ ))}
31
+ </View>
32
+ </ScrollView>
33
+ </View>
34
+ );
35
+ };
36
+
37
+ export default LineChart;
38
+
39
+ const styles = StyleSheet.create({
40
+ container: { marginVertical: 12 },
41
+ title: { fontWeight: '700', marginBottom: 8 },
42
+ chart: { flexDirection: 'row', alignItems: 'flex-end' },
43
+ column: { alignItems: 'center', marginHorizontal: 6 },
44
+ bar: { width: 8, marginHorizontal: 1, borderRadius: 2 },
45
+ label: { fontSize: 10, marginTop: 4 }
46
+ });
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import { ScrollView, Text, TouchableOpacity, StyleSheet } from 'react-native';
3
+
4
+ const MonthSelector = ({ months, selected, onSelect }) => (
5
+ <ScrollView horizontal showsHorizontalScrollIndicator={false}>
6
+ {months.map(m => (
7
+ <TouchableOpacity
8
+ key={m}
9
+ style={[styles.card, selected === m && styles.active]}
10
+ onPress={() => onSelect(m)}
11
+ >
12
+ <Text style={[styles.text, selected === m && styles.activeText]}>
13
+ {m}
14
+ </Text>
15
+ </TouchableOpacity>
16
+ ))}
17
+ </ScrollView>
18
+ );
19
+
20
+ export default MonthSelector;
21
+
22
+ const styles = StyleSheet.create({
23
+ card: {
24
+ paddingHorizontal: 14,
25
+ paddingVertical: 8,
26
+ borderRadius: 16,
27
+ borderWidth: 1,
28
+ borderColor: '#ccc',
29
+ marginRight: 8,
30
+ },
31
+ active: { backgroundColor: '#000' },
32
+ text: { fontSize: 13 },
33
+ activeText: { color: '#fff', fontWeight: '700' },
34
+ });
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import { View, StyleSheet } from 'react-native';
3
+
4
+ const ProgressBar = ({ value }) => {
5
+ const width = Math.min(Math.max(value || 0, 0), 100);
6
+
7
+ return (
8
+ <View style={styles.track}>
9
+ <View style={[styles.fill, { width: `${width}%` }]} />
10
+ </View>
11
+ );
12
+ };
13
+
14
+ export default ProgressBar;
15
+
16
+ const styles = StyleSheet.create({
17
+ track: {
18
+ height: 6,
19
+ backgroundColor: '#e5e5e5',
20
+ borderRadius: 4,
21
+ overflow: 'hidden',
22
+ marginTop: 6,
23
+ },
24
+ fill: {
25
+ height: '100%',
26
+ backgroundColor: '#f57c00', // orange like original
27
+ },
28
+ });
@@ -0,0 +1,103 @@
1
+ import React from 'react';
2
+ import { View, Text, StyleSheet } from 'react-native';
3
+ import ProgressBar from './ProgressBar';
4
+
5
+ const format = (num) => (num ?? 0).toLocaleString();
6
+
7
+ const Trend = ({ value }) => {
8
+ const positive = value >= 0;
9
+ return (
10
+ <Text style={[styles.trend, positive ? styles.up : styles.down]}>
11
+ {positive ? '↑' : '↓'} {Math.abs(value)}%
12
+ </Text>
13
+ );
14
+ };
15
+
16
+ const Row = ({ label, value }) => (
17
+ <View style={styles.row}>
18
+ <Text style={styles.label}>{label}</Text>
19
+ <Text style={styles.value}>{value}</Text>
20
+ </View>
21
+ );
22
+
23
+ const Report1Card = ({ item }) => {
24
+ return (
25
+ <View style={styles.card}>
26
+ {/* TITLE */}
27
+ <Text style={styles.title}>{item.name}</Text>
28
+
29
+ {/* VALUES */}
30
+ <Row label="2024 Actual" value={format(item.actual2024)} />
31
+ <Row label="2025 Actual" value={format(item.actual2025)} />
32
+
33
+ <View style={styles.row}>
34
+ <Text style={styles.label}>Change YoY</Text>
35
+ <Trend value={item.actualChangePercent} />
36
+ </View>
37
+
38
+ <Row label="2025 Budget" value={format(item.budget2025)} />
39
+
40
+ <View style={styles.row}>
41
+ <Text style={styles.label}>Budget Variance</Text>
42
+ <Trend value={item.budgetVariancePercent} />
43
+ </View>
44
+
45
+ {/* OPEX / GROSS PROFIT */}
46
+ {item.opexToGrossProfitPercent !== undefined && (
47
+ <>
48
+ <View style={styles.row}>
49
+ <Text style={styles.label}>OPEX / Gross Profit</Text>
50
+ <Text style={styles.value}>
51
+ {item.opexToGrossProfitPercent}%
52
+ </Text>
53
+ </View>
54
+
55
+ <ProgressBar value={item.opexToGrossProfitPercent} />
56
+ </>
57
+ )}
58
+ </View>
59
+ );
60
+ };
61
+
62
+ export default Report1Card;
63
+
64
+ const styles = StyleSheet.create({
65
+ card: {
66
+ backgroundColor: '#fff',
67
+ borderRadius: 12,
68
+ padding: 16,
69
+ marginBottom: 12,
70
+ borderWidth: 1,
71
+ borderColor: '#e0e0e0',
72
+ },
73
+ title: {
74
+ fontSize: 16,
75
+ fontWeight: '700',
76
+ marginBottom: 12,
77
+ color: '#000',
78
+ },
79
+ row: {
80
+ flexDirection: 'row',
81
+ justifyContent: 'space-between',
82
+ marginBottom: 6,
83
+ },
84
+ label: {
85
+ fontSize: 13,
86
+ color: '#666',
87
+ },
88
+ value: {
89
+ fontSize: 13,
90
+ fontWeight: '600',
91
+ color: '#000',
92
+ },
93
+ trend: {
94
+ fontSize: 13,
95
+ fontWeight: '700',
96
+ },
97
+ up: {
98
+ color: '#2e7d32',
99
+ },
100
+ down: {
101
+ color: '#d32f2f',
102
+ },
103
+ });
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { TouchableOpacity, Text, StyleSheet } from 'react-native';
3
+
4
+ const ReportCard = ({ title, onPress }) => (
5
+ <TouchableOpacity style={styles.card} onPress={onPress}>
6
+ <Text style={styles.text}>{title}</Text>
7
+ </TouchableOpacity>
8
+ );
9
+
10
+ export default ReportCard;
11
+
12
+ const styles = StyleSheet.create({
13
+ card: {
14
+ padding: 18,
15
+ borderRadius: 12,
16
+ backgroundColor: '#fff',
17
+ marginBottom: 12,
18
+ borderWidth: 1,
19
+ borderColor: '#ddd',
20
+ },
21
+ text: {
22
+ fontSize: 16,
23
+ fontWeight: '700',
24
+ },
25
+ });
package/src/index.jsx CHANGED
@@ -1,18 +1,22 @@
1
- import React from 'react';
2
- import ReportScreen from './screens/ReportScreen';
1
+ import React, { useState } from 'react';
2
+ import ReportsHome from './screens/ReportsHome';
3
+ import Report1Screen from './screens/Report1Screen';
4
+ import Report2Screen from './screens/Report2Screen';
5
+ import Report3Screen from './screens/Report3Screen';
3
6
 
4
- const ReportChart = ({
5
- onReportSelect,
6
- onClose,
7
- reports = []
8
- }) => {
9
- return (
10
- <ReportScreen
11
- reports={reports}
12
- onReportSelect={onReportSelect}
13
- onClose={onClose}
14
- />
15
- );
7
+ const AnalyticsReports = (props) => {
8
+ const [active, setActive] = useState(null);
9
+
10
+ if (active === 1)
11
+ return <Report1Screen {...props} onBack={() => setActive(null)} />;
12
+
13
+ if (active === 2)
14
+ return <Report2Screen {...props} onBack={() => setActive(null)} />;
15
+
16
+ if (active === 3)
17
+ return <Report3Screen onBack={() => setActive(null)} />;
18
+
19
+ return <ReportsHome onSelect={setActive} />;
16
20
  };
17
21
 
18
- export default ReportChart;
22
+ export default AnalyticsReports;
@@ -0,0 +1,25 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { ScrollView, Text, ActivityIndicator } from 'react-native';
3
+ import fetchReport1 from '../api/report1Fetcher';
4
+ import Report1Card from '../components/Report1Card';
5
+
6
+ const Report1Screen = ({ report1Endpoint, token, onBack }) => {
7
+ const [rows, setRows] = useState(null);
8
+
9
+ useEffect(() => {
10
+ fetchReport1(report1Endpoint, token).then(setRows);
11
+ }, []);
12
+
13
+ if (!rows) return <ActivityIndicator />;
14
+
15
+ return (
16
+ <ScrollView>
17
+ <Text onPress={onBack}>‹ Back</Text>
18
+ {rows.map((r, i) => (
19
+ <Report1Card key={i} item={r} />
20
+ ))}
21
+ </ScrollView>
22
+ );
23
+ };
24
+
25
+ export default Report1Screen;
@@ -0,0 +1,69 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { ScrollView, Text, ActivityIndicator } from 'react-native';
3
+ import { getDivisions, getTable, getLine, getBar } from '../api/report2Fetcher';
4
+ import MonthSelector from '../components/MonthSelector';
5
+ import DivisionSelector from '../components/DivisionSelector';
6
+ import FrozenTable from '../components/FrozenTable';
7
+ import LineChart from '../components/LineChart';
8
+ import BarChart from '../components/BarChart';
9
+
10
+ const Report2Screen = ({ report2, token, onBack }) => {
11
+ const [divisions, setDivisions] = useState([]);
12
+ const [division, setDivision] = useState(null);
13
+ const [table, setTable] = useState(null);
14
+ const [line, setLine] = useState(null);
15
+ const [bar, setBar] = useState(null);
16
+ const [month, setMonth] = useState(null);
17
+
18
+ useEffect(() => {
19
+ getDivisions(report2.divisions, token).then(d => {
20
+ setDivisions(d);
21
+ setDivision(d[0]?.code);
22
+ });
23
+ }, []);
24
+
25
+ useEffect(() => {
26
+ if (!division) return;
27
+ Promise.all([
28
+ getTable(report2.table, division, token),
29
+ getLine(report2.lineChart, division, token),
30
+ getBar(report2.barChart, division, token)
31
+ ]).then(([t, l, b]) => {
32
+ setTable(t);
33
+ setLine(l);
34
+ setBar(b);
35
+ setMonth(null);
36
+ });
37
+ }, [division]);
38
+
39
+ if (!table || !line || !bar) return <ActivityIndicator />;
40
+
41
+ const rows = month
42
+ ? table.rows.filter(r => r.monthLabel === month)
43
+ : table.rows;
44
+
45
+ return (
46
+ <ScrollView>
47
+ <Text onPress={onBack}>‹ Back</Text>
48
+
49
+ <MonthSelector
50
+ months={line.labels}
51
+ selected={month}
52
+ onSelect={setMonth}
53
+ />
54
+
55
+ <FrozenTable rows={rows} />
56
+
57
+ <LineChart data={line} />
58
+ <BarChart data={bar} />
59
+
60
+ <DivisionSelector
61
+ divisions={divisions}
62
+ selected={division}
63
+ onSelect={setDivision}
64
+ />
65
+ </ScrollView>
66
+ );
67
+ };
68
+
69
+ export default Report2Screen;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { View, Text } from 'react-native';
3
+
4
+ const Report3Screen = ({ onBack }) => (
5
+ <View style={{ padding: 16 }}>
6
+ <Text onPress={onBack}>‹ Back</Text>
7
+ <Text style={{ marginTop: 20, fontSize: 16 }}>
8
+ Report 3 – Coming Soon
9
+ </Text>
10
+ </View>
11
+ );
12
+
13
+ export default Report3Screen;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { View, StyleSheet } from 'react-native';
3
+ import ReportCard from '../components/ReportCard';
4
+
5
+ const ReportsHome = ({ onSelect }) => (
6
+ <View style={styles.container}>
7
+ <ReportCard title="Report 1 – Operating Profit" onPress={() => onSelect(1)} />
8
+ <ReportCard title="Report 2 – TEU & Profit" onPress={() => onSelect(2)} />
9
+ <ReportCard title="Report 3 – Coming Soon" onPress={() => onSelect(3)} />
10
+ </View>
11
+ );
12
+
13
+ export default ReportsHome;
14
+
15
+ const styles = StyleSheet.create({
16
+ container: { padding: 16 },
17
+ });
@@ -1,70 +0,0 @@
1
- import axios from 'axios';
2
-
3
- export class ApiFetcher {
4
- static async fetchReportData(reportConfig) {
5
- try {
6
- console.log('Fetching report data from:', reportConfig.apiConfig.url);
7
-
8
- const headers = {
9
- 'Content-Type': 'application/json',
10
- 'Accept': 'application/json'
11
- };
12
-
13
- // Add token if provided
14
- if (reportConfig.apiConfig.token) {
15
- headers['Authorization'] = `Bearer ${reportConfig.apiConfig.token}`;
16
- }
17
-
18
- const response = await axios.get(reportConfig.apiConfig.url, {
19
- timeout: 15000,
20
- headers: headers
21
- });
22
-
23
- console.log('API Response received:', response.status);
24
-
25
- // Transform data based on report type
26
- return this.transformData(response.data, reportConfig);
27
-
28
- } catch (error) {
29
- console.error('API Fetch Error:', error);
30
- throw new Error(`Failed to fetch report: ${error.message}`);
31
- }
32
- }
33
-
34
- static transformData(data, reportConfig) {
35
- // For performance reports (type 1, 1.1, 1.2)
36
- if (reportConfig.id === 1 || reportConfig.id === 1.1 || reportConfig.id === 1.2) {
37
- return {
38
- data: data.data || [],
39
- title: reportConfig.title,
40
- type: 'table',
41
- subType: reportConfig.subType
42
- };
43
- }
44
-
45
- // For report 2 (Revenue by Mode)
46
- if (reportConfig.id === 2) {
47
- return {
48
- data: Array.isArray(data) ? data : data.data || [],
49
- title: reportConfig.title,
50
- type: 'bar'
51
- };
52
- }
53
-
54
- // For report 3 (Shipment by Direction)
55
- if (reportConfig.id === 3) {
56
- return {
57
- data: Array.isArray(data) ? data : data.data || [],
58
- title: reportConfig.title,
59
- type: 'pie'
60
- };
61
- }
62
-
63
- // Default transformation
64
- return {
65
- data: data,
66
- title: reportConfig.title,
67
- type: reportConfig.type || 'bar'
68
- };
69
- }
70
- }