@suflon/rnmd-reporting 0.0.2 → 0.0.4

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.
@@ -1,6 +1,6 @@
1
- import React, { useEffect, useState, useMemo } from 'react';
1
+ import React, { useEffect, useState, useMemo, useContext } from 'react';
2
2
  import { ScrollView, View, Text, TouchableOpacity, SafeAreaView, useColorScheme } from 'react-native';
3
- import { useRoute, useNavigation } from '@react-navigation/native';
3
+ import { useNavigation, useRoute } from '@react-navigation/native';
4
4
  import { useReportStore, IReport, Header, SearchFilter, Loader, Icon } from '@suflon/native-ui';
5
5
  import { themeColors } from '@/theme/colors';
6
6
  import ReportChart from './ReportChart';
@@ -12,10 +12,19 @@ const formatKeyLabel = (key: string) => {
12
12
  .replace(/\b\w/g, (char) => char.toUpperCase());
13
13
  };
14
14
 
15
- const ReportingDetail = () => {
16
- const navigation = useNavigation<any>();
17
- const route = useRoute<any>();
18
- const report = route.params?.report as IReport;
15
+ const ReportingDetail = (props: any) => {
16
+ const navHook = useNavigation<any>();
17
+ const navigation = props.navigation || navHook;
18
+
19
+ let routeParams: any = {};
20
+ try {
21
+ const r = useRoute<any>();
22
+ routeParams = props.route?.params || r?.params || {};
23
+ } catch (e) {
24
+ routeParams = props.route?.params || {};
25
+ }
26
+
27
+ const report = routeParams?.report as IReport;
19
28
  const colorScheme = useColorScheme();
20
29
  const isDarkMode = colorScheme === 'dark';
21
30
 
@@ -137,6 +146,7 @@ const ReportingDetail = () => {
137
146
  reportMetadata?.metadata?.meta?.is_dynamic === true ||
138
147
  report?.is_dynamic === true
139
148
  );
149
+
140
150
  executeReport(report.id, isMetaDynamic, payload).finally(() => {
141
151
  setIsLocalLoading(false);
142
152
  });
@@ -203,23 +213,6 @@ const ReportingDetail = () => {
203
213
  });
204
214
  }, [records]);
205
215
 
206
- const activeFilters = useMemo(() => {
207
- const filters: string[] = [];
208
- const filterList = reportMetadata?.metadata?.filter_by || [];
209
- filterList.forEach((f: any) => {
210
- if (f.defaultValue) {
211
- if (typeof f.defaultValue === 'object' && (f.defaultValue.from_date || f.defaultValue.from)) {
212
- const from = f.defaultValue.from_date || f.defaultValue.from;
213
- const to = f.defaultValue.to_date || f.defaultValue.to;
214
- filters.push(`${from} - ${to}`);
215
- } else if (typeof f.defaultValue === 'string') {
216
- filters.push(f.defaultValue);
217
- }
218
- }
219
- });
220
- return filters.length > 0 ? filters : ['All Filters'];
221
- }, [reportMetadata]);
222
-
223
216
  return (
224
217
  <SafeAreaView className="flex-1 bg-slate-50 dark:bg-background-lightBlack">
225
218
  {/* Suflon Header Component */}
@@ -237,7 +230,7 @@ const ReportingDetail = () => {
237
230
 
238
231
  {/* Top View Mode Switcher Pills - Hidden in Chart View mode */}
239
232
  {!isChartReport && (
240
- <View className="flex-row bg-white dark:bg-background-darkSecondaryBg px-4 py-2.5 gap-2 border-b border-slate-100 dark:border-gray-800">
233
+ <View className="flex-row bg-white dark:bg-background-darkSecondaryBg px-4 py-2.5 gap-2 border-b border-slate-200/70 dark:border-gray-800">
241
234
  {VIEW_TABS.map((tab) => {
242
235
  const isActive = viewTab === tab.key;
243
236
  return (
@@ -253,10 +246,10 @@ const ReportingDetail = () => {
253
246
  name={tab.icon}
254
247
  type="Feather"
255
248
  size={14}
256
- color={isActive ? '#FFFFFF' : (isDarkMode ? '#FFFFFF' : themeColors.slate[600])}
249
+ color={isActive ? '#FFFFFF' : (isDarkMode ? '#94a3b8' : themeColors.slate[600])}
257
250
  />
258
251
  <Text
259
- style={{ color: isActive ? '#FFFFFF' : (isDarkMode ? '#FFFFFF' : '#334155') }}
252
+ style={{ color: isActive ? '#FFFFFF' : (isDarkMode ? '#E2E8F0' : '#334155') }}
260
253
  className="text-xs font-extrabold"
261
254
  >
262
255
  {tab.label}
@@ -278,96 +271,91 @@ const ReportingDetail = () => {
278
271
  showsVerticalScrollIndicator={false}
279
272
  contentContainerStyle={{ paddingHorizontal: 16, paddingTop: 14, paddingBottom: 130 }}
280
273
  >
281
- {/* Active Filter Banner */}
282
- <View className="bg-white dark:bg-background-darkSecondaryBg px-3 py-2.5 rounded-2xl border border-slate-200 dark:border-gray-800 flex-row items-center justify-between mb-3">
283
- <View className="flex-row items-center gap-1.5 flex-wrap flex-1">
284
- <Text className="text-[9px] font-extrabold text-slate-400 dark:text-gray-400 tracking-wider">
285
- ACTIVE FILTER:
286
- </Text>
287
- {activeFilters.map((f, i) => (
288
- <View key={i} className="bg-slate-100 dark:bg-gray-800 px-2 py-0.5 rounded-md">
289
- <Text className="text-[10px] font-bold text-slate-700 dark:text-gray-100">
290
- {f}
291
- </Text>
292
- </View>
293
- ))}
294
- </View>
295
-
296
- <TouchableOpacity onPress={() => setFilterModalVisible(true)} className="flex-row items-center gap-1">
297
- <Icon name="edit-3" type="Feather" size={12} color={themeColors.brand[600]} />
298
- <Text className="text-[11px] font-extrabold text-violet-600 dark:text-violet-400">
299
- Edit
300
- </Text>
301
- </TouchableOpacity>
302
- </View>
303
-
304
- {/* Chart View Content */}
305
- {viewTab === 'chart' ? (
306
- <ReportChart
307
- data={chartData}
308
- records={records}
309
- visibleKeys={visibleKeys}
310
- title={report?.name || reportMetadata?.report_name || 'Diagnosis Frequency Curve'}
311
- subtitle="GRAPH VISUALIZATION"
312
- isDarkMode={isDarkMode}
313
- />
314
- ) : (
315
- <View className="gap-3">
316
- {/* Search Component from Suflon Native UI */}
274
+ {/* Search and Action Bar */}
275
+ <View className="flex-row gap-2 mb-4">
276
+ <View className="flex-1">
317
277
  <SearchFilter
318
- placeholder="Search records..."
278
+ placeholder="Search in results..."
319
279
  value={searchQuery}
320
280
  onChangeText={setSearchQuery}
321
281
  />
282
+ </View>
283
+ </View>
284
+
285
+ {/* Chart Mode */}
286
+ {viewTab === 'chart' && (
287
+ <View className="gap-4">
288
+ <ReportChart
289
+ data={chartData}
290
+ records={filteredRecords}
291
+ visibleKeys={visibleKeys}
292
+ title={report?.name || 'Analytics Chart'}
293
+ subtitle={`Visual breakdown of ${filteredRecords.length} records`}
294
+ color={themeColors.brand[600]}
295
+ height={240}
296
+ isDarkMode={isDarkMode}
297
+ />
298
+ </View>
299
+ )}
300
+
301
+ {/* Records List / Table */}
302
+ {viewTab !== 'chart' && (
303
+ <View className="gap-3">
304
+ <View className="flex-row justify-between items-center px-1">
305
+ <Text style={{ color: isDarkMode ? '#E2E8F0' : '#0F172A' }} className="text-xs font-black uppercase tracking-wider">
306
+ Records Dataset ({filteredRecords.length})
307
+ </Text>
308
+ </View>
322
309
 
323
- {/* Mobile Cards Mode */}
324
310
  {filteredRecords.length === 0 ? (
325
- <View className="bg-white dark:bg-background-darkSecondaryBg p-6 rounded-2xl items-center mt-2.5 border border-slate-200 dark:border-gray-800">
326
- <Text className="text-slate-500 dark:text-gray-300 text-xs font-semibold">No records found</Text>
311
+ <View className="bg-white dark:bg-background-darkSecondaryBg p-8 rounded-2xl border border-slate-200 dark:border-gray-800 items-center justify-center">
312
+ <Icon name="inbox" type="Feather" size={32} color={isDarkMode ? '#6B7280' : themeColors.slate[300]} />
313
+ <Text style={{ color: isDarkMode ? '#E2E8F0' : '#1E293B' }} className="text-xs font-bold mt-2">No records found</Text>
314
+ <Text style={{ color: isDarkMode ? '#9CA3AF' : '#64748B' }} className="text-[10px] text-center mt-1">
315
+ Try adjusting your search query or reset active filters.
316
+ </Text>
327
317
  </View>
328
318
  ) : viewTab === 'card' ? (
319
+ /* Structured Clean Card View */
329
320
  filteredRecords.map((item: any, idx: number) => {
330
- const cardId = item.txn_number || item.id || item.app_id || item.code || item.sysid || item.appointment_num || `#${idx + 1}`;
321
+ const primaryKey = visibleKeys[0] || 'id';
322
+ const primaryVal = item[primaryKey] ?? `#${idx + 1}`;
323
+
324
+ const secondaryKeys = visibleKeys.slice(1, 7);
325
+ const detailKeys = visibleKeys.slice(7);
326
+ const cardId = item.id || idx;
331
327
  const isExpanded = expandedCardId === cardId;
332
328
 
333
- const statusKey = visibleKeys.find(k => k.toLowerCase().includes('status')) || '';
334
- const statusVal = statusKey ? String(item[statusKey] || '') : '';
335
- const isSuccess = statusVal.toUpperCase().includes('COMPLETED') || statusVal.toUpperCase().includes('PAID') || statusVal.toUpperCase().includes('APPROVED');
336
-
337
- const gridKeys = visibleKeys.filter(k => k !== statusKey).slice(0, 4);
338
- const detailKeys = visibleKeys.filter(k => k !== statusKey && !gridKeys.includes(k));
339
-
340
329
  return (
341
330
  <View
342
331
  key={idx}
343
- className="bg-white dark:bg-background-darkSecondaryBg rounded-2xl p-3.5 border border-slate-200 dark:border-gray-800 gap-2.5"
332
+ className="bg-white dark:bg-background-darkSecondaryBg rounded-2xl p-4 border border-slate-200/70 dark:border-gray-800 gap-2.5 "
344
333
  >
345
- {/* Card Top Header Row */}
346
- <View className="flex-row justify-between items-center border-b border-slate-100 dark:border-gray-800 pb-2">
347
- <View className="flex-row items-center gap-1.5">
348
- <View className={`w-2 h-2 rounded-full ${isSuccess ? 'bg-emerald-500' : 'bg-amber-500'}`} />
349
- <Text style={{ color: isDarkMode ? '#FFFFFF' : '#0F172A' }} className="text-xs font-extrabold">
350
- {cardId}
334
+ {/* Top Card Header */}
335
+ <View className="flex-row justify-between items-center">
336
+ <View className="flex-row items-center gap-2">
337
+ <View className="w-2 h-2 rounded-full bg-violet-600 dark:bg-violet-400" />
338
+ <Text style={{ color: isDarkMode ? '#FFFFFF' : '#0F172A' }} className="text-sm font-black">
339
+ {formatKeyLabel(primaryKey)}: {String(primaryVal)}
351
340
  </Text>
352
341
  </View>
353
-
354
- {statusVal ? (
355
- <View className={`px-2 py-0.5 rounded-md ${isSuccess ? 'bg-emerald-100 dark:bg-emerald-950/50' : 'bg-amber-100 dark:bg-amber-950/50'}`}>
356
- <Text className={`text-[9px] font-extrabold ${isSuccess ? 'text-emerald-800 dark:text-emerald-300' : 'text-amber-800 dark:text-amber-300'}`}>
357
- {statusVal}
342
+ {item.status && (
343
+ <View className="px-2 py-0.5 rounded-full bg-violet-50 dark:bg-violet-950/40 border border-violet-100 dark:border-violet-800/40">
344
+ <Text className="text-[9px] font-extrabold text-violet-700 dark:text-violet-300 uppercase">
345
+ {String(item.status)}
358
346
  </Text>
359
347
  </View>
360
- ) : null}
348
+ )}
361
349
  </View>
362
350
 
363
- {/* 2x2 Grid Dynamic Info */}
364
- <View className="flex-row flex-wrap gap-2.5">
365
- {gridKeys.map((key) => {
351
+ {/* Key Metrics Grid */}
352
+ <View className="flex-row flex-wrap gap-y-2 pt-1 border-t border-slate-50 dark:border-gray-800/60">
353
+ {secondaryKeys.map((key) => {
366
354
  const rawVal = item[key];
367
355
  const displayVal = (rawVal === null || rawVal === undefined || rawVal === '' || String(rawVal).trim() === '') ? '-' : String(rawVal);
368
356
  return (
369
- <View key={key} className="w-[47%]">
370
- <Text style={{ color: isDarkMode ? '#9CA3AF' : '#64748B' }} className="text-[8px] font-extrabold tracking-wider">
357
+ <View key={key} className="w-1/2 pr-2">
358
+ <Text style={{ color: isDarkMode ? '#9CA3AF' : '#64748B' }} className="text-[10px] font-semibold" numberOfLines={1}>
371
359
  {formatKeyLabel(key)}
372
360
  </Text>
373
361
  <Text style={{ color: isDarkMode ? '#FFFFFF' : '#0F172A' }} className="text-xs font-bold mt-0.5" numberOfLines={1}>
@@ -383,7 +371,7 @@ const ReportingDetail = () => {
383
371
  <>
384
372
  <TouchableOpacity
385
373
  onPress={() => setExpandedCardId(isExpanded ? null : cardId)}
386
- className="flex-row justify-between items-center pt-2 border-t border-slate-100 dark:border-gray-800"
374
+ className="flex-row justify-between items-center pt-2 border-t border-slate-200/70 dark:border-gray-800"
387
375
  >
388
376
  <Text className="text-[10px] font-bold text-violet-600 dark:text-violet-400">
389
377
  View Full Response Details
@@ -397,7 +385,7 @@ const ReportingDetail = () => {
397
385
  </TouchableOpacity>
398
386
 
399
387
  {isExpanded && (
400
- <View className="bg-slate-50 dark:bg-background-lightBlack p-2.5 rounded-xl gap-1.5 border border-slate-100 dark:border-gray-800">
388
+ <View className="bg-slate-50 dark:bg-background-lightBlack p-2.5 rounded-xl gap-1.5 border border-slate-200/70 dark:border-gray-800">
401
389
  {detailKeys.map((key) => {
402
390
  const rawDetailVal = item[key];
403
391
  const displayDetailVal = (rawDetailVal === null || rawDetailVal === undefined || rawDetailVal === '' || String(rawDetailVal).trim() === '') ? '-' : String(rawDetailVal);
@@ -430,7 +418,7 @@ const ReportingDetail = () => {
430
418
  ))}
431
419
  </View>
432
420
  {filteredRecords.map((item: any, idx: number) => (
433
- <View key={idx} className="flex-row p-2.5 border-b border-slate-100 dark:border-gray-800 items-center">
421
+ <View key={idx} className="flex-row p-2.5 border-b border-slate-200/70 dark:border-gray-800 items-center">
434
422
  {visibleKeys.map((key) => {
435
423
  const rawCellVal = item[key];
436
424
  const displayCellVal = (rawCellVal === null || rawCellVal === undefined || rawCellVal === '' || String(rawCellVal).trim() === '') ? '-' : String(rawCellVal);
@@ -449,14 +437,14 @@ const ReportingDetail = () => {
449
437
 
450
438
  {/* Pagination Footer */}
451
439
  <View className="flex-row justify-between items-center pt-2">
452
- <Text className="text-[11px] text-slate-500 dark:text-gray-300">
440
+ <Text style={{ color: isDarkMode ? '#9CA3AF' : '#64748B' }} className="text-[11px]">
453
441
  Showing 1-{filteredRecords.length} of {filteredRecords.length} records
454
442
  </Text>
455
443
  <View className="flex-row items-center gap-1.5">
456
444
  <TouchableOpacity className="w-6 h-6 rounded-md bg-white dark:bg-background-darkSecondaryBg border border-slate-200 dark:border-gray-800 items-center justify-center">
457
445
  <Icon name="chevron-left" type="Feather" size={14} color={isDarkMode ? '#FFFFFF' : themeColors.slate[400]} />
458
446
  </TouchableOpacity>
459
- <Text className="text-[11px] font-extrabold text-slate-900 dark:text-white">1</Text>
447
+ <Text style={{ color: isDarkMode ? '#FFFFFF' : '#0F172A' }} className="text-[11px] font-extrabold">1</Text>
460
448
  <TouchableOpacity className="w-6 h-6 rounded-md bg-white dark:bg-background-darkSecondaryBg border border-slate-200 dark:border-gray-800 items-center justify-center">
461
449
  <Icon name="chevron-right" type="Feather" size={14} color={isDarkMode ? '#FFFFFF' : themeColors.slate[400]} />
462
450
  </TouchableOpacity>
@@ -71,13 +71,11 @@ const Reporting = ({ onBackPress }: { onBackPress?: () => void } = {}) => {
71
71
 
72
72
  const featuredPicks = useMemo(() => {
73
73
  if (!filteredReports || filteredReports.length === 0) return [];
74
- // Show featured reports first, fallback to first 4
75
- const featured = filteredReports.filter((r: IReport) => r.featured);
74
+ const featured = filteredReports.filter((r: IReport) => (r as any).featured);
76
75
  return featured.length > 0 ? featured.slice(0, 4) : filteredReports.slice(0, 4);
77
76
  }, [filteredReports]);
78
77
 
79
78
  const iconBrandColor = isDark ? '#a78bfa' : themeColors.brand[600];
80
- const iconMutedColor = isDark ? '#9CA3AF' : themeColors.slate[400];
81
79
 
82
80
  return (
83
81
  <SafeAreaView className="flex-1 bg-slate-50 dark:bg-background-lightBlack">
@@ -88,7 +86,8 @@ const Reporting = ({ onBackPress }: { onBackPress?: () => void } = {}) => {
88
86
  showActionIcon={false}
89
87
  />
90
88
 
91
- <ScrollView className="flex-1" contentContainerStyle={{ paddingBottom: 30 }}>
89
+ {/* Fixed Top Controls */}
90
+ <View>
92
91
  {/* Search Bar */}
93
92
  <View className="px-4 pt-3.5">
94
93
  <SearchFilter
@@ -100,9 +99,9 @@ const Reporting = ({ onBackPress }: { onBackPress?: () => void } = {}) => {
100
99
 
101
100
  {/* Quick Access Featured Carousel */}
102
101
  {featuredPicks.length > 0 && (
103
- <View className="pt-4">
104
- <View className="px-4 flex-row justify-between items-center mb-2.5">
105
- <Text className="text-[11px] font-extrabold text-slate-400 dark:text-gray-400 tracking-wider">
102
+ <View className="pt-3.5">
103
+ <View className="px-4 flex-row justify-between items-center mb-2">
104
+ <Text style={{ color: isDark ? '#9CA3AF' : '#94A3B8' }} className="text-[11px] font-extrabold tracking-wider">
106
105
  FEATURED REPORTS
107
106
  </Text>
108
107
  <Text className="text-[11px] font-bold text-violet-600 dark:text-violet-400">
@@ -116,15 +115,15 @@ const Reporting = ({ onBackPress }: { onBackPress?: () => void } = {}) => {
116
115
  key={pick.id || i}
117
116
  onPress={() => handleReportPress(pick)}
118
117
  activeOpacity={0.8}
119
- className="w-[170px] bg-white dark:bg-background-darkSecondaryBg rounded-2xl p-3 border border-slate-200 dark:border-gray-800 gap-1.5"
118
+ className="w-[170px] bg-white dark:bg-background-darkSecondaryBg rounded-2xl p-3 border border-slate-200/70 dark:border-slate-800 gap-1.5"
120
119
  >
121
120
  <View className="w-7 h-7 rounded-lg bg-violet-50 dark:bg-violet-950/40 items-center justify-center">
122
- <Icon name="bar-chart-2" type="Feather" size={16} color={iconBrandColor} />
121
+ <Icon name="bar-chart-2" type="Feather" size={15} color={iconBrandColor} />
123
122
  </View>
124
- <Text className="text-xs font-extrabold text-slate-900 dark:text-text-dark-primary" numberOfLines={1}>
123
+ <Text style={{ color: isDark ? '#FFFFFF' : '#0F172A' }} className="text-xs font-extrabold" numberOfLines={1}>
125
124
  {pick.name}
126
125
  </Text>
127
- <Text className="text-[9px] font-semibold text-slate-500 dark:text-gray-400" numberOfLines={1}>
126
+ <Text style={{ color: isDark ? '#9CA3AF' : '#94A3B8' }} className="text-[9px] font-semibold" numberOfLines={1}>
128
127
  {pick.sub_report_class ? SUB_CLASS_LABEL_MAP[pick.sub_report_class] || pick.sub_report_class : 'Report'}
129
128
  </Text>
130
129
  </TouchableOpacity>
@@ -133,8 +132,8 @@ const Reporting = ({ onBackPress }: { onBackPress?: () => void } = {}) => {
133
132
  </View>
134
133
  )}
135
134
 
136
- {/* Category Pills Switcher - Dynamic from response */}
137
- <View className="pt-4">
135
+ {/* Category Pills Switcher */}
136
+ <View className="py-3.5">
138
137
  <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ paddingHorizontal: 16, gap: 8 }}>
139
138
  {categoryTabs.map((tab) => {
140
139
  const isActive = activeTabId === tab.id;
@@ -146,40 +145,34 @@ const Reporting = ({ onBackPress }: { onBackPress?: () => void } = {}) => {
146
145
  key={tab.id}
147
146
  onPress={() => setActiveTabId(tab.id)}
148
147
  activeOpacity={0.8}
149
- className={`flex-row items-center gap-1.5 px-3.5 py-2 rounded-full border ${
148
+ className={`flex-row items-center gap-1.5 px-3.5 py-1.5 rounded-xl border ${
150
149
  isActive
151
150
  ? 'bg-violet-600 border-violet-600'
152
- : 'bg-white dark:bg-background-darkSecondaryBg border-slate-200 dark:border-gray-800'
151
+ : 'bg-white dark:bg-background-darkSecondaryBg border-slate-200/70 dark:border-slate-800'
153
152
  }`}
154
153
  >
155
154
  <Icon
156
155
  name={tab.icon}
157
156
  type="Feather"
158
- size={13}
159
- color={isActive ? '#FFFFFF' : (isDark ? '#FFFFFF' : themeColors.slate[600])}
157
+ size={12}
158
+ color={isActive ? '#FFFFFF' : '#94a3b8'}
160
159
  />
161
160
  <Text
162
- className={`text-[11px] font-bold ${
163
- isActive
164
- ? 'text-white'
165
- : 'text-slate-700 dark:text-gray-200'
166
- }`}
161
+ style={{ color: isActive ? '#FFFFFF' : (isDark ? '#9CA3AF' : '#94A3B8') }}
162
+ className={`text-xs ${isActive ? 'font-bold' : 'font-normal'}`}
167
163
  >
168
164
  {tab.label}
169
165
  </Text>
170
166
  <View
171
- className={`rounded-full px-1.5 py-0.5 ${
167
+ className={`px-1.5 py-0.5 rounded-full ${
172
168
  isActive
173
- ? 'bg-white/25'
174
- : 'bg-slate-100 dark:bg-gray-800'
169
+ ? 'bg-white/20'
170
+ : 'bg-slate-100 dark:bg-slate-800'
175
171
  }`}
176
172
  >
177
173
  <Text
178
- className={`text-[9px] font-extrabold ${
179
- isActive
180
- ? 'text-white'
181
- : 'text-slate-600 dark:text-gray-300'
182
- }`}
174
+ style={{ color: isActive ? '#FFFFFF' : (isDark ? '#9CA3AF' : '#94A3B8') }}
175
+ className={`text-[9px] ${isActive ? 'font-bold' : 'font-normal'}`}
183
176
  >
184
177
  {count}
185
178
  </Text>
@@ -189,59 +182,60 @@ const Reporting = ({ onBackPress }: { onBackPress?: () => void } = {}) => {
189
182
  })}
190
183
  </ScrollView>
191
184
  </View>
185
+ </View>
192
186
 
193
- {/* Report Catalog List */}
194
- <View className="px-4 pt-3.5 gap-2.5">
195
- {loading ? (
196
- <View className="py-10">
197
- <Loader message="Loading reports catalog..." color={iconBrandColor} />
198
- </View>
199
- ) : filteredReports.length === 0 ? (
200
- <View className="bg-white dark:bg-background-darkSecondaryBg p-8 rounded-2xl items-center mt-2.5 border border-slate-200 dark:border-gray-800">
201
- <Text className="text-slate-500 dark:text-gray-400 text-xs font-semibold">No reports match the selected category</Text>
187
+ {/* Content Area */}
188
+ {loading ? (
189
+ <View className="flex-1 justify-center items-center">
190
+ <Loader message="Loading reports catalog..." color={themeColors.brand[600]} />
191
+ </View>
192
+ ) : (
193
+ <ScrollView
194
+ className="flex-1"
195
+ showsVerticalScrollIndicator={false}
196
+ contentContainerStyle={{ paddingHorizontal: 16, paddingBottom: 120 }}
197
+ >
198
+ {filteredReports.length === 0 ? (
199
+ <View className="bg-white dark:bg-background-darkSecondaryBg p-8 rounded-2xl border border-slate-200/70 dark:border-slate-800 items-center justify-center mt-2">
200
+ <Icon name="inbox" type="Feather" size={32} color={isDark ? '#6B7280' : themeColors.slate[300]} />
201
+ <Text style={{ color: isDark ? '#FFFFFF' : '#0F172A' }} className="text-xs font-bold mt-2">No reports found</Text>
202
+ <Text style={{ color: isDark ? '#9CA3AF' : '#64748B' }} className="text-[10px] text-center mt-1">
203
+ {searchQuery ? 'Try matching another keyword or filter' : 'No available reports listed for this category'}
204
+ </Text>
202
205
  </View>
203
206
  ) : (
204
- filteredReports.map((item) => (
205
- <TouchableOpacity
206
- key={item.id}
207
- onPress={() => handleReportPress(item)}
208
- activeOpacity={0.8}
209
- className="bg-white dark:bg-background-darkSecondaryBg rounded-2xl p-3.5 border border-slate-200 dark:border-gray-800 flex-row items-center"
210
- >
211
- <View className="flex-row items-center gap-3 flex-1">
212
- <View className="w-10 h-10 rounded-xl bg-violet-50 dark:bg-violet-950/40 items-center justify-center">
213
- <Icon
214
- name={SUB_CLASS_ICON_MAP[item.sub_report_class] || (item.is_dynamic ? "pie-chart" : "file-text")}
215
- type="Feather"
216
- size={20}
217
- color={iconBrandColor}
218
- />
219
- </View>
207
+ <View className="gap-2">
208
+ <Text style={{ color: isDark ? '#9CA3AF' : '#94A3B8' }} className="text-[10px] font-extrabold uppercase tracking-wider px-1 mb-0.5">
209
+ {activeTabId === 'all' ? 'All Catalog Reports' : `${SUB_CLASS_LABEL_MAP[activeTabId] || activeTabId} Reports`} ({filteredReports.length})
210
+ </Text>
220
211
 
221
- <View className="flex-1">
222
- <Text className="text-[13px] font-extrabold text-slate-900 dark:text-text-dark-primary" numberOfLines={1}>
223
- {item.name}
224
- </Text>
225
- <View className="flex-row items-center gap-1.5 mt-0.5">
226
- <Text className="text-[10px] font-bold text-violet-600 dark:text-violet-400">
227
- {SUB_CLASS_LABEL_MAP[item.sub_report_class] || item.sub_report_class || 'Report'}
212
+ {filteredReports.map((report: IReport) => (
213
+ <TouchableOpacity
214
+ key={report.id}
215
+ onPress={() => handleReportPress(report)}
216
+ activeOpacity={0.8}
217
+ className="flex-row items-center justify-between bg-white dark:bg-background-darkSecondaryBg p-3 rounded-2xl border border-slate-200/70 dark:border-slate-800"
218
+ >
219
+ <View className="flex-row items-center gap-3 flex-1 mr-2">
220
+ <View className="w-9 h-9 rounded-xl bg-violet-50 dark:bg-violet-950/40 items-center justify-center shrink-0">
221
+ <Icon name="file-text" type="Feather" size={15} color={iconBrandColor} />
222
+ </View>
223
+ <View className="flex-1">
224
+ <Text style={{ color: isDark ? '#FFFFFF' : '#0F172A' }} className="text-xs font-bold" numberOfLines={1}>
225
+ {report.name}
228
226
  </Text>
229
- <Text className="text-[10px] text-slate-400 dark:text-gray-500">•</Text>
230
- <Text className="text-[10px] font-semibold text-slate-500 dark:text-gray-400">
231
- {item.report_type || 'LISTING'}
227
+ <Text style={{ color: isDark ? '#9CA3AF' : '#64748B' }} className="text-[10px] mt-0.5" numberOfLines={1}>
228
+ {report.description || (report.sub_report_class ? SUB_CLASS_LABEL_MAP[report.sub_report_class] : 'Ready for export & view')}
232
229
  </Text>
233
230
  </View>
234
231
  </View>
235
- </View>
236
-
237
- <View className="flex-row items-center gap-1.5">
238
- <Icon name="chevron-right" type="Feather" size={18} color={iconMutedColor} />
239
- </View>
240
- </TouchableOpacity>
241
- ))
232
+ <Icon name="chevron-right" type="Feather" size={14} color={isDark ? '#6B7280' : themeColors.slate[400]} />
233
+ </TouchableOpacity>
234
+ ))}
235
+ </View>
242
236
  )}
243
- </View>
244
- </ScrollView>
237
+ </ScrollView>
238
+ )}
245
239
  </SafeAreaView>
246
240
  );
247
241
  };
@@ -3,6 +3,7 @@ import { View, StyleSheet } from 'react-native';
3
3
  import { createNativeStackNavigator } from '@react-navigation/native-stack';
4
4
  import Reporting from '@/modules/Reporting';
5
5
  import ReportingDetail from '@/modules/Reporting/component/ReportingDetail';
6
+ import ManagementReport from '@/modules/ManagementReport';
6
7
 
7
8
  export type AppNavigationProps = {
8
9
  onBackPress?: () => void;
@@ -15,10 +16,20 @@ export function AppNavigation({ onBackPress }: AppNavigationProps = {}) {
15
16
  return (
16
17
  <View style={styles.container}>
17
18
  <Stack.Navigator screenOptions={{ headerShown: false }} initialRouteName="ReportingList">
18
- <Stack.Screen name="ReportingList">
19
- {(props) => <Reporting {...props} onBackPress={onBackPress} />}
20
- </Stack.Screen>
21
- <Stack.Screen name="ReportingDetail" component={ReportingDetail} />
19
+ <Stack.Screen
20
+ name="ReportingList"
21
+ component={Reporting}
22
+ initialParams={{ onBackPress }}
23
+ />
24
+ <Stack.Screen
25
+ name="ReportingDetail"
26
+ component={ReportingDetail}
27
+ />
28
+ <Stack.Screen
29
+ name="ManagementReport"
30
+ component={ManagementReport}
31
+ initialParams={{ onBackPress }}
32
+ />
22
33
  </Stack.Navigator>
23
34
  </View>
24
35
  );