@dhiraj0720/report1chart 3.0.0 → 3.0.2

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.0",
3
+ "version": "3.0.2",
4
4
  "main": "src/index.jsx",
5
5
  "scripts": {
6
6
  "test": "echo 'No tests'"
@@ -1,8 +1,7 @@
1
1
  import React from 'react';
2
- import { ScrollView, StyleSheet, Text, View } from 'react-native';
3
- import { formatNumber } from '../utils/formatNumber';
2
+ import { View, Text, ScrollView, StyleSheet } from 'react-native';
4
3
 
5
- const TAB_TABLE_CONFIG = {
4
+ const TABLE_CONFIG = {
6
5
  kumule: {
7
6
  title: '2025 YILI ŞİRKET ve İŞTİPİ BAZINDA BRÜT KAR & FAALİYET KAR PERFORMANS TABLOSU',
8
7
  columns: [
@@ -71,171 +70,176 @@ const TAB_TABLE_CONFIG = {
71
70
  },
72
71
  };
73
72
 
74
- const trendUi = (trendValue) => {
75
- if (trendValue === 1) {
76
- return { icon: '', color: '#2e7d32' };
73
+ const formatNumber = (value) => {
74
+ if (value === null || value === undefined || value === '') {
75
+ return '-';
77
76
  }
78
77
 
79
- if (trendValue === 2) {
80
- return { icon: '↓', color: '#c62828' };
81
- }
78
+ return Number(value).toLocaleString('en-US');
79
+ };
82
80
 
83
- if (trendValue === 0) {
84
- return { icon: '●', color: '#c62828' };
81
+ const isTotalRow = (row) => row?.rowType === 1 || row?.rowType === 3;
82
+
83
+ const trendFromValue = (value) => {
84
+ if (value === null || value === undefined) {
85
+ return 0;
85
86
  }
86
87
 
87
- return { icon: '●', color: '#e65100' };
88
+ return value >= 0 ? 1 : 2;
88
89
  };
89
90
 
90
- const ratioTrend = (ratioValue) => {
91
- if (ratioValue === null || ratioValue === undefined) {
92
- return null;
91
+ const ratioTrend = (value) => {
92
+ if (value === null || value === undefined) {
93
+ return 0;
93
94
  }
94
95
 
95
- if (ratioValue < 0) {
96
+ if (value < 0) {
96
97
  return 0;
97
98
  }
98
99
 
99
- if (ratioValue <= 50) {
100
+ if (value <= 50) {
100
101
  return 1;
101
102
  }
102
103
 
103
104
  return 2;
104
105
  };
105
106
 
106
- const formatPercent = (value) => {
107
+ const PercentCell = ({ value, trend }) => {
107
108
  if (value === null || value === undefined) {
108
- return '-';
109
+ return <Text>-</Text>;
109
110
  }
110
111
 
111
- return Number(value).toLocaleString('en-US');
112
+ const resolvedTrend = trend || trendFromValue(value);
113
+ const isUp = resolvedTrend === 1;
114
+
115
+ return (
116
+ <Text
117
+ style={[
118
+ styles.percentText,
119
+ isUp ? styles.percentUp : styles.percentDown,
120
+ ]}
121
+ >
122
+ {isUp ? '↑' : '↓'} {Math.abs(value)}%
123
+ </Text>
124
+ );
112
125
  };
113
126
 
114
- const formatRatio = (value) => {
127
+ const RatioCell = ({ value }) => {
115
128
  if (value === null || value === undefined) {
116
- return '-';
129
+ return <Text>-</Text>;
117
130
  }
118
131
 
119
- if (value < 0) {
120
- return `-%${Math.abs(value)}`;
121
- }
132
+ const trend = ratioTrend(value);
133
+ const isUp = trend === 1;
122
134
 
123
- return `%${value}`;
135
+ return (
136
+ <Text
137
+ style={[
138
+ styles.percentText,
139
+ isUp ? styles.percentUp : styles.percentDown,
140
+ ]}
141
+ >
142
+ {isUp ? '↑' : '↓'} %{value}
143
+ </Text>
144
+ );
124
145
  };
125
146
 
126
- const DataCell = ({ children, style }) => (
127
- <View style={[styles.dataCell, style]}>
128
- {children}
147
+ const Cell = ({ children, bold = false, highlight = false, width = 100 }) => (
148
+ <View
149
+ style={[
150
+ styles.cell,
151
+ { width },
152
+ bold && styles.boldCell,
153
+ highlight && styles.highlightCell,
154
+ ]}
155
+ >
156
+ <Text
157
+ style={[
158
+ styles.cellText,
159
+ bold && styles.boldText,
160
+ ]}
161
+ numberOfLines={2}
162
+ >
163
+ {children}
164
+ </Text>
129
165
  </View>
130
166
  );
131
167
 
132
- const TrendValue = ({ value, trend }) => {
133
- const trendStyle = trendUi(trend);
134
-
135
- return (
136
- <View style={styles.trendWrapper}>
137
- <Text style={[styles.trendIcon, { color: trendStyle.color }]}>
138
- {trendStyle.icon}
139
- </Text>
140
- <Text numberOfLines={1} style={styles.dataCellText}>
141
- {value}
142
- </Text>
143
- </View>
144
- );
145
- };
146
-
147
- const renderCellValue = (row, column) => {
168
+ const renderDataCell = (row, column, isTotal) => {
148
169
  if (column.type === 'number') {
149
- return <Text style={styles.dataCellText}>{formatNumber(row[column.key])}</Text>;
170
+ return <Cell key={column.key} highlight={isTotal}>{formatNumber(row[column.key])}</Cell>;
150
171
  }
151
172
 
152
173
  if (column.type === 'trendPercent') {
153
174
  return (
154
- <TrendValue
155
- value={formatPercent(row[column.key])}
156
- trend={row[column.trendKey]}
157
- />
175
+ <Cell key={column.key} highlight={isTotal}>
176
+ <PercentCell value={row[column.key]} trend={row[column.trendKey]} />
177
+ </Cell>
158
178
  );
159
179
  }
160
180
 
161
181
  if (column.type === 'ratio') {
162
182
  return (
163
- <TrendValue
164
- value={formatRatio(row[column.key])}
165
- trend={ratioTrend(row[column.key])}
166
- />
183
+ <Cell key={column.key} highlight={isTotal}>
184
+ <RatioCell value={row[column.key]} />
185
+ </Cell>
167
186
  );
168
187
  }
169
188
 
170
- return <Text style={styles.dataCellText}>-</Text>;
171
- };
172
-
173
- const rowTypeStyle = (row, index) => {
174
- if (row.rowType === 1 || row.rowType === 3) {
175
- return styles.totalRow;
176
- }
177
-
178
- return index % 2 === 0 ? styles.evenRow : styles.oddRow;
189
+ return <Cell key={column.key} highlight={isTotal}>-</Cell>;
179
190
  };
180
191
 
181
192
  const FrozenTableReport4A = ({ rows = [], tabKey = 'kumule' }) => {
182
- const config = TAB_TABLE_CONFIG[tabKey] || TAB_TABLE_CONFIG.kumule;
193
+ const config = TABLE_CONFIG[tabKey] || TABLE_CONFIG.kumule;
183
194
 
184
- if (!rows.length) {
185
- return <Text style={styles.noData}>No data available</Text>;
195
+ if (!rows || rows.length === 0) {
196
+ return (
197
+ <Text style={styles.emptyText}>
198
+ No data available
199
+ </Text>
200
+ );
186
201
  }
187
202
 
188
203
  return (
189
- <View style={styles.wrapper}>
190
- <Text style={styles.tableTitle}>{config.title}</Text>
204
+ <View>
205
+ <Text style={styles.title}>{config.title}</Text>
191
206
 
192
- <View style={styles.tableContainer}>
207
+ <View style={styles.container}>
193
208
  <View style={styles.frozenColumn}>
194
- <View style={[styles.nameCell, styles.headerCell]}>
195
- <Text style={styles.headerText}>ŞİRKET / İŞTİPİ</Text>
196
- </View>
209
+ <Cell bold width={180}>ŞİRKET / İŞTİPİ</Cell>
197
210
  {rows.map((row, index) => {
198
- const isTotal = row.rowType === 1 || row.rowType === 3;
211
+ const total = isTotalRow(row);
199
212
  return (
200
- <View
201
- key={`name-${row.sortOrder || index}`}
202
- style={[styles.nameCell, rowTypeStyle(row, index)]}
213
+ <Cell
214
+ key={`${row.sortOrder || index}-name`}
215
+ bold={total}
216
+ highlight={total}
217
+ width={180}
203
218
  >
204
- <View style={styles.nameCellContent}>
205
- <Text style={styles.star}>{isTotal ? '★' : ' '}</Text>
206
- <Text numberOfLines={1} style={styles.nameText}>
207
- {row.name}
208
- </Text>
209
- </View>
210
- </View>
219
+ {row.name}
220
+ </Cell>
211
221
  );
212
222
  })}
213
223
  </View>
214
224
 
215
225
  <ScrollView horizontal showsHorizontalScrollIndicator={false}>
216
226
  <View>
217
- <View style={styles.row}>
227
+ <View style={styles.headerRow}>
218
228
  {config.columns.map((column) => (
219
- <View key={column.key} style={[styles.dataCell, styles.headerCell]}>
220
- <Text numberOfLines={2} style={styles.headerText}>
221
- {column.label}
222
- </Text>
223
- </View>
229
+ <Cell key={column.key} bold>
230
+ {column.label}
231
+ </Cell>
224
232
  ))}
225
233
  </View>
226
234
 
227
- {rows.map((row, index) => (
228
- <View key={`row-${row.sortOrder || index}`} style={styles.row}>
229
- {config.columns.map((column) => (
230
- <DataCell
231
- key={`${row.sortOrder || index}-${column.key}`}
232
- style={rowTypeStyle(row, index)}
233
- >
234
- {renderCellValue(row, column)}
235
- </DataCell>
236
- ))}
237
- </View>
238
- ))}
235
+ {rows.map((row, index) => {
236
+ const total = isTotalRow(row);
237
+ return (
238
+ <View key={`${row.sortOrder || index}-row`} style={styles.dataRow}>
239
+ {config.columns.map((column) => renderDataCell(row, column, total))}
240
+ </View>
241
+ );
242
+ })}
239
243
  </View>
240
244
  </ScrollView>
241
245
  </View>
@@ -246,105 +250,72 @@ const FrozenTableReport4A = ({ rows = [], tabKey = 'kumule' }) => {
246
250
  export default FrozenTableReport4A;
247
251
 
248
252
  const styles = StyleSheet.create({
249
- wrapper: {
250
- backgroundColor: '#eef1f5',
251
- borderRadius: 8,
252
- borderWidth: 1,
253
- borderColor: '#97aec4',
254
- padding: 8,
255
- },
256
- tableTitle: {
253
+ title: {
257
254
  textAlign: 'center',
258
255
  fontSize: 16,
259
256
  fontWeight: '700',
260
257
  color: '#2e2e2e',
261
258
  marginBottom: 8,
259
+ marginTop: 4,
262
260
  },
263
- tableContainer: {
261
+ container: {
264
262
  flexDirection: 'row',
265
263
  borderWidth: 1,
266
- borderColor: '#97aec4',
267
- backgroundColor: '#d9dce1',
264
+ borderColor: '#ddd',
265
+ borderRadius: 10,
266
+ overflow: 'hidden',
267
+ marginVertical: 12,
268
+ backgroundColor: '#fff',
268
269
  },
269
270
  frozenColumn: {
270
- width: 290,
271
+ width: 180,
272
+ backgroundColor: '#f4f6f8',
271
273
  borderRightWidth: 1,
272
- borderRightColor: '#f3f3f3',
274
+ borderColor: '#ddd',
273
275
  },
274
- row: {
276
+ headerRow: {
275
277
  flexDirection: 'row',
278
+ backgroundColor: '#4E79A7',
276
279
  },
277
- nameCell: {
278
- height: 46,
279
- justifyContent: 'center',
280
- borderBottomWidth: 1,
281
- borderBottomColor: '#f0f0f0',
282
- paddingHorizontal: 8,
283
- },
284
- nameCellContent: {
280
+ dataRow: {
285
281
  flexDirection: 'row',
286
- alignItems: 'center',
287
282
  },
288
- nameText: {
289
- flex: 1,
290
- fontSize: 13,
291
- color: '#1f1f1f',
292
- },
293
- star: {
294
- width: 20,
295
- textAlign: 'center',
296
- color: '#c37f18',
297
- fontSize: 16,
298
- marginRight: 4,
299
- },
300
- dataCell: {
301
- width: 120,
302
- height: 46,
283
+ cell: {
284
+ width: 100,
285
+ paddingVertical: 12,
286
+ paddingHorizontal: 8,
303
287
  justifyContent: 'center',
304
- alignItems: 'center',
305
- paddingHorizontal: 6,
306
288
  borderBottomWidth: 1,
307
- borderBottomColor: '#f0f0f0',
289
+ borderColor: '#e0e0e0',
308
290
  },
309
- dataCellText: {
310
- fontSize: 13,
311
- color: '#303030',
291
+ cellText: {
292
+ fontSize: 12,
312
293
  textAlign: 'center',
294
+ color: '#333',
313
295
  },
314
- headerCell: {
315
- backgroundColor: '#5f7f9f',
316
- height: 58,
296
+ boldCell: {
297
+ backgroundColor: '#e9f0f8',
317
298
  },
318
- headerText: {
319
- color: '#ffffff',
299
+ boldText: {
320
300
  fontWeight: '700',
321
- textAlign: 'center',
322
- fontSize: 12,
323
301
  },
324
- evenRow: {
325
- backgroundColor: '#dfdfdf',
302
+ highlightCell: {
303
+ backgroundColor: '#e9f0f8',
326
304
  },
327
- oddRow: {
328
- backgroundColor: '#d2d2d2',
305
+ percentText: {
306
+ fontWeight: '700',
307
+ fontSize: 12,
308
+ textAlign: 'center',
329
309
  },
330
- totalRow: {
331
- backgroundColor: '#9db6cb',
310
+ percentUp: {
311
+ color: '#2e7d32',
332
312
  },
333
- trendWrapper: {
334
- flexDirection: 'row',
335
- alignItems: 'center',
336
- justifyContent: 'center',
337
- },
338
- trendIcon: {
339
- fontSize: 18,
340
- fontWeight: '700',
341
- marginRight: 4,
342
- marginTop: -1,
313
+ percentDown: {
314
+ color: '#d32f2f',
343
315
  },
344
- noData: {
316
+ emptyText: {
345
317
  textAlign: 'center',
318
+ padding: 20,
346
319
  color: '#666',
347
- paddingVertical: 20,
348
- fontSize: 14,
349
320
  },
350
321
  });
@@ -1,14 +1,17 @@
1
1
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
2
  import {
3
3
  ActivityIndicator,
4
+ Modal,
4
5
  ScrollView,
5
6
  StyleSheet,
6
7
  Text,
7
8
  TouchableOpacity,
8
9
  View,
10
+ useWindowDimensions,
9
11
  } from 'react-native';
10
12
  import fetchReport4 from '../api/report4Fetcher';
11
13
  import FrozenTableReport4A from '../components/FrozenTableReport4A';
14
+ import Report1Card from '../components/Report1Card';
12
15
 
13
16
  const TABS = [
14
17
  { key: 'kumule', label: 'KÜMÜLE' },
@@ -16,12 +19,52 @@ const TABS = [
16
19
  { key: 'fg', label: 'FG / BRÜT KAR ORANI' },
17
20
  ];
18
21
 
22
+ const mapRowToCardItem = (row, tabKey) => {
23
+ if (tabKey === 'faaliyet') {
24
+ return {
25
+ name: row.name,
26
+ actual2024: row.fk2024,
27
+ actual2025: row.fk2025,
28
+ actualChangePercent: row.changePercent ?? 0,
29
+ budget2025: row.budget2025,
30
+ budgetVariancePercent: row.budgetVariancePercent ?? 0,
31
+ };
32
+ }
33
+
34
+ if (tabKey === 'fg') {
35
+ const ratioDiff = (row.ratio2025 ?? 0) - (row.ratio2024 ?? 0);
36
+ return {
37
+ name: row.name,
38
+ actual2024: row.gross2024,
39
+ actual2025: row.gross2025,
40
+ actualChangePercent: ratioDiff,
41
+ budget2025: row.fk2025,
42
+ budgetVariancePercent: ratioDiff,
43
+ opexToGrossProfitPercent: row.ratio2025,
44
+ };
45
+ }
46
+
47
+ return {
48
+ name: row.name,
49
+ actual2024: row.gross2024,
50
+ actual2025: row.gross2025,
51
+ actualChangePercent: row.grossChangePercent ?? 0,
52
+ budget2025: row.budgetGross2025,
53
+ budgetVariancePercent: row.budgetGrossVariancePercent ?? 0,
54
+ opexToGrossProfitPercent: row.ratio2025,
55
+ };
56
+ };
57
+
19
58
  const Report4AScreen = ({ api, token, onBack }) => {
59
+ const { width, height } = useWindowDimensions();
20
60
  const [activeTab, setActiveTab] = useState('kumule');
21
61
  const [rowsByTab, setRowsByTab] = useState({});
22
62
  const [loadedByTab, setLoadedByTab] = useState({});
23
63
  const [errorByTab, setErrorByTab] = useState({});
24
64
  const [loading, setLoading] = useState(false);
65
+ const [fullscreen, setFullscreen] = useState(false);
66
+
67
+ const isLandscape = width > height;
25
68
 
26
69
  const endpointByTab = useMemo(() => ({
27
70
  kumule: api?.kumule,
@@ -79,7 +122,7 @@ const Report4AScreen = ({ api, token, onBack }) => {
79
122
  <TouchableOpacity onPress={onBack} style={styles.backButton}>
80
123
  <Text style={styles.backIcon}>‹</Text>
81
124
  </TouchableOpacity>
82
- <Text style={styles.headerTitle}>New report Formate</Text>
125
+ <Text style={styles.headerTitle}>Performans Raporu (USD) - NEW</Text>
83
126
  </View>
84
127
 
85
128
  <View style={styles.tabsContainer}>
@@ -122,10 +165,63 @@ const Report4AScreen = ({ api, token, onBack }) => {
122
165
 
123
166
  {activeRows.length ? (
124
167
  <ScrollView style={styles.tableScroll} contentContainerStyle={styles.tableContent}>
168
+ <View style={styles.tableToolbar}>
169
+ <TouchableOpacity
170
+ onPress={() => setFullscreen(true)}
171
+ style={styles.fullViewButton}
172
+ >
173
+ <Text style={styles.fullViewButtonText}>⤢ Full View</Text>
174
+ </TouchableOpacity>
175
+ </View>
176
+
125
177
  <FrozenTableReport4A rows={activeRows} tabKey={activeTab} />
178
+
179
+ <Text style={styles.sectionTitle}>Individual Reports</Text>
180
+ {activeRows.map((row, index) => (
181
+ <Report1Card
182
+ key={`card-${row.sortOrder || index}`}
183
+ item={mapRowToCardItem(row, activeTab)}
184
+ />
185
+ ))}
126
186
  </ScrollView>
127
187
  ) : null}
128
188
  </View>
189
+
190
+ <Modal
191
+ visible={fullscreen}
192
+ animationType="fade"
193
+ onRequestClose={() => setFullscreen(false)}
194
+ supportedOrientations={['portrait', 'landscape-left', 'landscape-right']}
195
+ >
196
+ <View style={styles.fullModal}>
197
+ <View style={styles.fullHeader}>
198
+ <Text style={styles.fullHintText}>
199
+ Full View • Auto fit • Pinch to zoom
200
+ </Text>
201
+ <TouchableOpacity onPress={() => setFullscreen(false)}>
202
+ <Text style={styles.closeBtn}>✕</Text>
203
+ </TouchableOpacity>
204
+ </View>
205
+
206
+ <ScrollView
207
+ maximumZoomScale={3.5}
208
+ minimumZoomScale={0.45}
209
+ showsHorizontalScrollIndicator={false}
210
+ showsVerticalScrollIndicator={false}
211
+ contentContainerStyle={styles.zoomContent}
212
+ pinchGestureEnabled
213
+ bouncesZoom
214
+ >
215
+ <View style={[styles.tableWrapper, isLandscape && styles.tableWrapperLandscape]}>
216
+ <FrozenTableReport4A
217
+ rows={activeRows}
218
+ tabKey={activeTab}
219
+ isFullscreen
220
+ />
221
+ </View>
222
+ </ScrollView>
223
+ </View>
224
+ </Modal>
129
225
  </View>
130
226
  );
131
227
  };
@@ -220,7 +316,67 @@ const styles = StyleSheet.create({
220
316
  flex: 1,
221
317
  },
222
318
  tableContent: {
223
- padding: 10,
319
+ paddingHorizontal: 10,
224
320
  paddingBottom: 16,
225
321
  },
322
+ tableToolbar: {
323
+ alignItems: 'flex-end',
324
+ marginTop: 8,
325
+ marginBottom: 6,
326
+ },
327
+ sectionTitle: {
328
+ fontSize: 16,
329
+ fontWeight: '700',
330
+ marginTop: 8,
331
+ marginBottom: 14,
332
+ color: '#111',
333
+ },
334
+ fullViewButton: {
335
+ backgroundColor: '#4E79A7',
336
+ borderRadius: 7,
337
+ paddingHorizontal: 12,
338
+ paddingVertical: 7,
339
+ },
340
+ fullViewButtonText: {
341
+ color: '#fff',
342
+ fontWeight: '700',
343
+ fontSize: 12,
344
+ },
345
+ fullModal: {
346
+ flex: 1,
347
+ backgroundColor: '#f3f6fa',
348
+ },
349
+ fullHeader: {
350
+ flexDirection: 'row',
351
+ alignItems: 'center',
352
+ paddingVertical: 14,
353
+ paddingHorizontal: 14,
354
+ borderBottomWidth: 1,
355
+ borderBottomColor: '#d3dde7',
356
+ backgroundColor: '#fff',
357
+ },
358
+ fullHintText: {
359
+ flex: 1,
360
+ fontSize: 14,
361
+ fontWeight: '600',
362
+ color: '#23384a',
363
+ textAlign: 'center',
364
+ },
365
+ closeBtn: {
366
+ fontSize: 24,
367
+ color: '#1f2d3a',
368
+ paddingHorizontal: 8,
369
+ },
370
+ zoomContent: {
371
+ flexGrow: 1,
372
+ justifyContent: 'center',
373
+ alignItems: 'center',
374
+ padding: 12,
375
+ },
376
+ tableWrapper: {
377
+ width: '100%',
378
+ },
379
+ tableWrapperLandscape: {
380
+ maxWidth: '96%',
381
+ },
226
382
  });
@@ -8,16 +8,11 @@ import {
8
8
  } from 'react-native';
9
9
 
10
10
  const REPORTS = [
11
-
12
- {
13
- id: '1A',
14
- title: 'Performans Raporu (USD)',
15
- desc: 'Option 1',
16
- },
11
+
17
12
  {
18
- id: 1,
19
- title: 'Performans Raporu (USD)',
20
- desc: 'Option 2',
13
+ id: '4A',
14
+ title: 'Performans Raporu (USD) - NEW',
15
+ desc: 'New report formate',
21
16
  },
22
17
 
23
18
  {
@@ -41,11 +36,18 @@ const REPORTS = [
41
36
  title: 'Transportation Business Analysis',
42
37
  desc: 'Option 2',
43
38
  },
39
+
44
40
  {
45
- id: '4A',
46
- title: 'New performance Report',
47
- desc: 'New report formate',
41
+ id: '1A',
42
+ title: 'Performans Raporu (USD) - OLD',
43
+ desc: 'Option 1',
48
44
  },
45
+ {
46
+ id: 1,
47
+ title: 'Performans Raporu (USD) - OLD',
48
+ desc: 'Option 2',
49
+ },
50
+
49
51
  ];
50
52
 
51
53
  const ReportListScreen = ({ onSelect, onExit }) => {