@suflon/rnmd-reporting 0.0.3 → 0.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/App.tsx +2 -2
- package/index.js +3 -0
- package/metro.config.js +31 -0
- package/package.json +4 -3
- package/patches/@suflon+native-ui+0.0.18.patch +1715 -4257
- package/src/index.ts +1 -0
- package/src/modules/ManagementReport/components/AnalyticsChartView.tsx +530 -0
- package/src/modules/ManagementReport/components/CategoryTabs.tsx +85 -0
- package/src/modules/ManagementReport/components/DetailsListingView.tsx +781 -0
- package/src/modules/ManagementReport/components/DoctorFilterDropdown.tsx +157 -0
- package/src/modules/ManagementReport/components/KpiMetricsGrid.tsx +183 -0
- package/src/modules/ManagementReport/components/ManagementFilterModal.tsx +142 -0
- package/src/modules/ManagementReport/components/ManagementReportHeader.tsx +115 -0
- package/src/modules/ManagementReport/components/SubModuleTabs.tsx +58 -0
- package/src/modules/ManagementReport/components/TimeframeDropdown.tsx +162 -0
- package/src/modules/ManagementReport/components/ViewModeSwitcher.tsx +71 -0
- package/src/modules/ManagementReport/index.tsx +202 -0
- package/src/modules/Reporting/component/ReportChart.tsx +1 -1
- package/src/modules/Reporting/component/ReportFilterModal.tsx +1 -1
- package/src/modules/Reporting/component/ReportingDetail.tsx +86 -98
- package/src/modules/Reporting/index.tsx +72 -79
- package/src/navigation/index.tsx +17 -6
- package/src/screens/DevToolsCorner.tsx +43 -8
- package/tailwind.config.js +11 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import React, { useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
Text,
|
|
5
|
+
TouchableOpacity,
|
|
6
|
+
Modal,
|
|
7
|
+
TouchableWithoutFeedback,
|
|
8
|
+
Dimensions,
|
|
9
|
+
useColorScheme,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
import { Icon } from '@suflon/native-ui';
|
|
12
|
+
import { themeColors } from '../../../theme/colors';
|
|
13
|
+
|
|
14
|
+
export type TimeframeOption = 'Daily' | 'Weekly' | 'Monthly';
|
|
15
|
+
export const TIMEFRAMES: TimeframeOption[] = ['Daily', 'Weekly', 'Monthly'];
|
|
16
|
+
|
|
17
|
+
export function getTimeframeDateFilter(timeframe: TimeframeOption): { from_date: string; to_date: string } {
|
|
18
|
+
const now = new Date();
|
|
19
|
+
const to_date = now.toISOString().slice(0, 10);
|
|
20
|
+
const from = new Date(now);
|
|
21
|
+
|
|
22
|
+
if (timeframe === 'Daily') {
|
|
23
|
+
from.setDate(now.getDate());
|
|
24
|
+
} else if (timeframe === 'Weekly') {
|
|
25
|
+
from.setDate(now.getDate() - 7);
|
|
26
|
+
} else if (timeframe === 'Monthly') {
|
|
27
|
+
from.setDate(now.getDate() - 30);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const from_date = from.toISOString().slice(0, 10);
|
|
31
|
+
return { from_date, to_date };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface TimeframeDropdownProps {
|
|
35
|
+
value: TimeframeOption;
|
|
36
|
+
onChange: (value: TimeframeOption) => void;
|
|
37
|
+
size?: 'sm' | 'md';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const TimeframeDropdown: React.FC<TimeframeDropdownProps> = ({
|
|
41
|
+
value,
|
|
42
|
+
onChange,
|
|
43
|
+
size = 'md',
|
|
44
|
+
}) => {
|
|
45
|
+
const colorScheme = useColorScheme();
|
|
46
|
+
const isDark = colorScheme === 'dark';
|
|
47
|
+
const [open, setOpen] = useState(false);
|
|
48
|
+
const [coords, setCoords] = useState<{ x: number; y: number; width: number; height: number }>({
|
|
49
|
+
x: 0,
|
|
50
|
+
y: 0,
|
|
51
|
+
width: 0,
|
|
52
|
+
height: 0,
|
|
53
|
+
});
|
|
54
|
+
const buttonRef = useRef<any>(null);
|
|
55
|
+
|
|
56
|
+
const handleOpen = () => {
|
|
57
|
+
buttonRef.current?.measureInWindow((x: number, y: number, width: number, height: number) => {
|
|
58
|
+
setCoords({ x, y, width, height });
|
|
59
|
+
setOpen(true);
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const screenWidth = Dimensions.get('window').width;
|
|
64
|
+
const rightOffset = Math.max(12, screenWidth - (coords.x + coords.width));
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<>
|
|
68
|
+
<TouchableOpacity
|
|
69
|
+
ref={buttonRef}
|
|
70
|
+
onPress={handleOpen}
|
|
71
|
+
activeOpacity={0.8}
|
|
72
|
+
className={`${
|
|
73
|
+
size === 'sm' ? 'px-1.5 py-0.5' : 'px-2 py-1'
|
|
74
|
+
} rounded-xl bg-slate-100 dark:bg-slate-800 border border-slate-200/80 dark:border-slate-700 flex-row items-center gap-1 shrink-0`}
|
|
75
|
+
>
|
|
76
|
+
<Icon name="sliders" type="Feather" size={size === 'sm' ? 8 : 10} color={themeColors.brand[600]} />
|
|
77
|
+
<Text
|
|
78
|
+
style={{ color: isDark ? '#FFFFFF' : '#0F172A' }}
|
|
79
|
+
className={`${
|
|
80
|
+
size === 'sm' ? 'text-[8px]' : 'text-[10px]'
|
|
81
|
+
} font-bold`}
|
|
82
|
+
>
|
|
83
|
+
{value}
|
|
84
|
+
</Text>
|
|
85
|
+
</TouchableOpacity>
|
|
86
|
+
|
|
87
|
+
<Modal
|
|
88
|
+
visible={open}
|
|
89
|
+
transparent={true}
|
|
90
|
+
animationType="none"
|
|
91
|
+
onRequestClose={() => setOpen(false)}
|
|
92
|
+
>
|
|
93
|
+
<TouchableWithoutFeedback onPress={() => setOpen(false)}>
|
|
94
|
+
<View className="flex-1 bg-transparent">
|
|
95
|
+
<View
|
|
96
|
+
style={{
|
|
97
|
+
position: 'absolute',
|
|
98
|
+
top: coords.y + coords.height + 4,
|
|
99
|
+
right: rightOffset,
|
|
100
|
+
backgroundColor: isDark ? themeColors.slate[900] : themeColors.white,
|
|
101
|
+
borderColor: isDark ? themeColors.slate[700] : themeColors.slate[200],
|
|
102
|
+
borderWidth: 1,
|
|
103
|
+
borderRadius: 16,
|
|
104
|
+
paddingVertical: 5,
|
|
105
|
+
paddingHorizontal: 4,
|
|
106
|
+
minWidth: 115,
|
|
107
|
+
shadowColor: '#000',
|
|
108
|
+
shadowOffset: { width: 0, height: 6 },
|
|
109
|
+
shadowOpacity: isDark ? 0.45 : 0.18,
|
|
110
|
+
shadowRadius: 10,
|
|
111
|
+
elevation: 25,
|
|
112
|
+
zIndex: 99999,
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
{TIMEFRAMES.map((tf) => {
|
|
116
|
+
const isSelected = tf === value;
|
|
117
|
+
return (
|
|
118
|
+
<TouchableOpacity
|
|
119
|
+
key={tf}
|
|
120
|
+
activeOpacity={0.8}
|
|
121
|
+
onPress={() => {
|
|
122
|
+
onChange(tf);
|
|
123
|
+
setOpen(false);
|
|
124
|
+
}}
|
|
125
|
+
style={{
|
|
126
|
+
backgroundColor: isSelected
|
|
127
|
+
? (isDark ? themeColors.slate[800] : themeColors.brand[50])
|
|
128
|
+
: themeColors.transparent,
|
|
129
|
+
borderRadius: 10,
|
|
130
|
+
paddingVertical: 6,
|
|
131
|
+
paddingHorizontal: 8,
|
|
132
|
+
flexDirection: 'row',
|
|
133
|
+
alignItems: 'center',
|
|
134
|
+
gap: 6,
|
|
135
|
+
}}
|
|
136
|
+
>
|
|
137
|
+
<View style={{ width: 14, alignItems: 'center', justifyContent: 'center' }}>
|
|
138
|
+
{isSelected && (
|
|
139
|
+
<Icon name="check" type="Feather" size={11} color={themeColors.brand[600]} />
|
|
140
|
+
)}
|
|
141
|
+
</View>
|
|
142
|
+
<Text
|
|
143
|
+
style={{
|
|
144
|
+
fontSize: 11,
|
|
145
|
+
fontWeight: isSelected ? '700' : '500',
|
|
146
|
+
color: isSelected
|
|
147
|
+
? (isDark ? themeColors.brand[400] : themeColors.brand[600])
|
|
148
|
+
: (isDark ? themeColors.slate[200] : themeColors.slate[700]),
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
{tf}
|
|
152
|
+
</Text>
|
|
153
|
+
</TouchableOpacity>
|
|
154
|
+
);
|
|
155
|
+
})}
|
|
156
|
+
</View>
|
|
157
|
+
</View>
|
|
158
|
+
</TouchableWithoutFeedback>
|
|
159
|
+
</Modal>
|
|
160
|
+
</>
|
|
161
|
+
);
|
|
162
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, TouchableOpacity, Text } from 'react-native';
|
|
3
|
+
import { Icon } from '@suflon/native-ui';
|
|
4
|
+
|
|
5
|
+
interface ViewModeSwitcherProps {
|
|
6
|
+
activeTab: 'chart' | 'listing';
|
|
7
|
+
onSwitchTab: (tab: 'chart' | 'listing') => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const ViewModeSwitcher: React.FC<ViewModeSwitcherProps> = ({
|
|
11
|
+
activeTab,
|
|
12
|
+
onSwitchTab,
|
|
13
|
+
}) => {
|
|
14
|
+
const isChart = activeTab === 'chart';
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<View className="bg-slate-200/70 dark:bg-slate-800/80 p-1 rounded-2xl flex-row items-center border border-slate-200 dark:border-slate-700/60 shadow-xs mb-3.5">
|
|
18
|
+
<TouchableOpacity
|
|
19
|
+
onPress={() => onSwitchTab('chart')}
|
|
20
|
+
activeOpacity={0.8}
|
|
21
|
+
className={`flex-1 py-2 rounded-xl flex-row items-center justify-center gap-2 ${
|
|
22
|
+
isChart
|
|
23
|
+
? 'bg-white dark:bg-slate-700'
|
|
24
|
+
: 'bg-transparent'
|
|
25
|
+
}`}
|
|
26
|
+
>
|
|
27
|
+
<Icon
|
|
28
|
+
name="trending-up"
|
|
29
|
+
type="Feather"
|
|
30
|
+
size={12}
|
|
31
|
+
color={isChart ? '#7c3aed' : '#94a3b8'}
|
|
32
|
+
/>
|
|
33
|
+
<Text
|
|
34
|
+
className={`text-xs ${
|
|
35
|
+
isChart
|
|
36
|
+
? 'font-extrabold text-brand-600 dark:text-brand-300'
|
|
37
|
+
: 'font-semibold text-slate-500 dark:text-slate-400'
|
|
38
|
+
}`}
|
|
39
|
+
>
|
|
40
|
+
Analytics & Chart
|
|
41
|
+
</Text>
|
|
42
|
+
</TouchableOpacity>
|
|
43
|
+
|
|
44
|
+
<TouchableOpacity
|
|
45
|
+
onPress={() => onSwitchTab('listing')}
|
|
46
|
+
activeOpacity={0.8}
|
|
47
|
+
className={`flex-1 py-2 rounded-xl flex-row items-center justify-center gap-2 ${
|
|
48
|
+
!isChart
|
|
49
|
+
? 'bg-white dark:bg-slate-700'
|
|
50
|
+
: 'bg-transparent'
|
|
51
|
+
}`}
|
|
52
|
+
>
|
|
53
|
+
<Icon
|
|
54
|
+
name="list"
|
|
55
|
+
type="Feather"
|
|
56
|
+
size={12}
|
|
57
|
+
color={!isChart ? '#7c3aed' : '#94a3b8'}
|
|
58
|
+
/>
|
|
59
|
+
<Text
|
|
60
|
+
className={`text-xs ${
|
|
61
|
+
!isChart
|
|
62
|
+
? 'font-extrabold text-brand-600 dark:text-brand-300'
|
|
63
|
+
: 'font-semibold text-slate-500 dark:text-slate-400'
|
|
64
|
+
}`}
|
|
65
|
+
>
|
|
66
|
+
Details & Reports
|
|
67
|
+
</Text>
|
|
68
|
+
</TouchableOpacity>
|
|
69
|
+
</View>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
SafeAreaView,
|
|
4
|
+
ScrollView,
|
|
5
|
+
RefreshControl,
|
|
6
|
+
Alert,
|
|
7
|
+
View,
|
|
8
|
+
Text,
|
|
9
|
+
ActivityIndicator,
|
|
10
|
+
} from 'react-native';
|
|
11
|
+
import {
|
|
12
|
+
useManagementReportStore,
|
|
13
|
+
ServerErrorWrapper,
|
|
14
|
+
} from '@suflon/native-ui';
|
|
15
|
+
import { ManagementReportHeader } from './components/ManagementReportHeader';
|
|
16
|
+
import { KpiMetricsGrid } from './components/KpiMetricsGrid';
|
|
17
|
+
import { AnalyticsChartView } from './components/AnalyticsChartView';
|
|
18
|
+
import { DetailsListingView } from './components/DetailsListingView';
|
|
19
|
+
|
|
20
|
+
interface ManagementReportScreenProps {
|
|
21
|
+
onBackPress?: () => void;
|
|
22
|
+
navigation?: any;
|
|
23
|
+
route?: any;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const ManagementReportScreen: React.FC<ManagementReportScreenProps> = ({
|
|
27
|
+
onBackPress,
|
|
28
|
+
navigation,
|
|
29
|
+
route,
|
|
30
|
+
}) => {
|
|
31
|
+
const handleBack = onBackPress || route?.params?.onBackPress || navigation?.goBack;
|
|
32
|
+
const {
|
|
33
|
+
detailLoading,
|
|
34
|
+
executingLoading,
|
|
35
|
+
errorStatus,
|
|
36
|
+
managementReports,
|
|
37
|
+
selectedReport,
|
|
38
|
+
selectedReportDetails,
|
|
39
|
+
activeCategory,
|
|
40
|
+
selectedReportId,
|
|
41
|
+
searchQuery,
|
|
42
|
+
reportExecutionData,
|
|
43
|
+
getManagementReports,
|
|
44
|
+
getManagementReportById,
|
|
45
|
+
setActiveCategory,
|
|
46
|
+
setSelectedReportId,
|
|
47
|
+
setSearchQuery,
|
|
48
|
+
} = useManagementReportStore();
|
|
49
|
+
|
|
50
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
51
|
+
const [selectedDoctor, setSelectedDoctor] = useState<string>('ALL');
|
|
52
|
+
|
|
53
|
+
// Fetch reports on mount
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
getManagementReports(true);
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
// Pull to refresh handler
|
|
59
|
+
const handleRefresh = useCallback(async () => {
|
|
60
|
+
setRefreshing(true);
|
|
61
|
+
await getManagementReports(true);
|
|
62
|
+
if (selectedReportId) {
|
|
63
|
+
await getManagementReportById(selectedReportId, true);
|
|
64
|
+
}
|
|
65
|
+
setRefreshing(false);
|
|
66
|
+
}, [getManagementReports, getManagementReportById, selectedReportId]);
|
|
67
|
+
|
|
68
|
+
// Reports in active category
|
|
69
|
+
const categoryReports = useMemo(() => {
|
|
70
|
+
if (!managementReports || managementReports.length === 0) return [];
|
|
71
|
+
return managementReports.filter(
|
|
72
|
+
(r) => r.mgmt_report_type?.toUpperCase() === activeCategory?.toUpperCase()
|
|
73
|
+
);
|
|
74
|
+
}, [managementReports, activeCategory]);
|
|
75
|
+
|
|
76
|
+
// Report counts per category
|
|
77
|
+
const categoryCounts = useMemo(() => {
|
|
78
|
+
const counts: Record<string, number> = {};
|
|
79
|
+
(managementReports || []).forEach((r) => {
|
|
80
|
+
const type = r.mgmt_report_type?.toUpperCase() || 'OTHER';
|
|
81
|
+
counts[type] = (counts[type] || 0) + 1;
|
|
82
|
+
});
|
|
83
|
+
return counts;
|
|
84
|
+
}, [managementReports]);
|
|
85
|
+
|
|
86
|
+
// Extract unique doctors dynamically from loaded dataset
|
|
87
|
+
const availableDoctors = useMemo(() => {
|
|
88
|
+
const docSet = new Set<string>();
|
|
89
|
+
Object.values(reportExecutionData || {}).forEach((rows) => {
|
|
90
|
+
if (Array.isArray(rows)) {
|
|
91
|
+
rows.forEach((r: any) => {
|
|
92
|
+
const doc = r?.staff || r?.doctor || r?.consultant;
|
|
93
|
+
if (doc && typeof doc === 'string' && doc.trim() !== '' && doc !== '-') {
|
|
94
|
+
docSet.add(doc.trim());
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return Array.from(docSet);
|
|
100
|
+
}, [reportExecutionData]);
|
|
101
|
+
|
|
102
|
+
// Export report action
|
|
103
|
+
const handleExport = useCallback(() => {
|
|
104
|
+
Alert.alert(
|
|
105
|
+
'Export Report Dataset',
|
|
106
|
+
`Exporting data for ${selectedReport?.name || activeCategory} module.`,
|
|
107
|
+
[{ text: 'OK' }]
|
|
108
|
+
);
|
|
109
|
+
}, [selectedReport, activeCategory]);
|
|
110
|
+
|
|
111
|
+
const screenTitle = selectedReport?.name
|
|
112
|
+
? `${selectedReport.name} Reports`
|
|
113
|
+
: 'Reports Catalog';
|
|
114
|
+
const screenSubtitle = `${activeCategory} Management Module`;
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<SafeAreaView className="flex-1 bg-slate-50 dark:bg-background-lightBlack">
|
|
118
|
+
<ServerErrorWrapper
|
|
119
|
+
errorStatus={errorStatus}
|
|
120
|
+
onRetry={handleRefresh}
|
|
121
|
+
>
|
|
122
|
+
{/* Sticky Management Header & Tabs */}
|
|
123
|
+
<ManagementReportHeader
|
|
124
|
+
title={screenTitle}
|
|
125
|
+
subtitle={screenSubtitle}
|
|
126
|
+
activeCategory={activeCategory}
|
|
127
|
+
onSelectCategory={setActiveCategory}
|
|
128
|
+
categoryReports={categoryReports}
|
|
129
|
+
selectedReportId={selectedReportId}
|
|
130
|
+
onSelectReport={setSelectedReportId}
|
|
131
|
+
searchQuery={searchQuery}
|
|
132
|
+
onSearchChange={setSearchQuery}
|
|
133
|
+
onBackPress={handleBack}
|
|
134
|
+
onRefresh={handleRefresh}
|
|
135
|
+
onExport={handleExport}
|
|
136
|
+
selectedDoctor={selectedDoctor}
|
|
137
|
+
onSelectDoctor={setSelectedDoctor}
|
|
138
|
+
availableDoctors={availableDoctors}
|
|
139
|
+
categoryCounts={categoryCounts}
|
|
140
|
+
/>
|
|
141
|
+
|
|
142
|
+
{/* Main Content Area */}
|
|
143
|
+
<ScrollView
|
|
144
|
+
className="flex-1 px-3.5 pt-3.5 space-y-3.5"
|
|
145
|
+
showsVerticalScrollIndicator={false}
|
|
146
|
+
contentContainerStyle={{ paddingBottom: 40 }}
|
|
147
|
+
refreshControl={
|
|
148
|
+
<RefreshControl
|
|
149
|
+
refreshing={refreshing}
|
|
150
|
+
onRefresh={handleRefresh}
|
|
151
|
+
colors={['#7c3aed']}
|
|
152
|
+
tintColor="#7c3aed"
|
|
153
|
+
/>
|
|
154
|
+
}
|
|
155
|
+
>
|
|
156
|
+
{detailLoading ? (
|
|
157
|
+
/* Centered Lower Content Loader during Tab Switching */
|
|
158
|
+
<View className="py-20 items-center justify-center">
|
|
159
|
+
<View className="w-12 h-12 rounded-2xl bg-violet-50 dark:bg-slate-800 items-center justify-center mb-3">
|
|
160
|
+
<ActivityIndicator size="small" color="#7c3aed" />
|
|
161
|
+
</View>
|
|
162
|
+
<Text className="text-xs font-bold text-slate-800 dark:text-slate-200">
|
|
163
|
+
Fetching Report Data
|
|
164
|
+
</Text>
|
|
165
|
+
<Text className="text-[10px] text-slate-400 mt-0.5">
|
|
166
|
+
Loading {selectedReport?.name || activeCategory} metrics & charts...
|
|
167
|
+
</Text>
|
|
168
|
+
</View>
|
|
169
|
+
) : (
|
|
170
|
+
/* Sequential Content Flow: KPIs -> Charts -> Listing Tables */
|
|
171
|
+
<>
|
|
172
|
+
{/* SECTION 1: Key Performance Overview Cards */}
|
|
173
|
+
<KpiMetricsGrid
|
|
174
|
+
details={selectedReportDetails}
|
|
175
|
+
executionData={reportExecutionData}
|
|
176
|
+
loading={executingLoading}
|
|
177
|
+
/>
|
|
178
|
+
|
|
179
|
+
{/* SECTION 2: Analytics Charts */}
|
|
180
|
+
<AnalyticsChartView
|
|
181
|
+
details={selectedReportDetails}
|
|
182
|
+
executionData={reportExecutionData}
|
|
183
|
+
loading={executingLoading}
|
|
184
|
+
/>
|
|
185
|
+
|
|
186
|
+
{/* SECTION 3: Records Listing & Tables */}
|
|
187
|
+
<DetailsListingView
|
|
188
|
+
details={selectedReportDetails}
|
|
189
|
+
executionData={reportExecutionData}
|
|
190
|
+
searchQuery={searchQuery}
|
|
191
|
+
doctorFilter={selectedDoctor}
|
|
192
|
+
loading={executingLoading}
|
|
193
|
+
/>
|
|
194
|
+
</>
|
|
195
|
+
)}
|
|
196
|
+
</ScrollView>
|
|
197
|
+
</ServerErrorWrapper>
|
|
198
|
+
</SafeAreaView>
|
|
199
|
+
);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export default ManagementReportScreen;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useMemo } from 'react';
|
|
2
2
|
import { View, Text, Dimensions, TouchableOpacity, ScrollView, PanResponder } from 'react-native';
|
|
3
3
|
import Svg, { Path, Circle, G, Line as SvgLine, Text as SvgText } from 'react-native-svg';
|
|
4
|
-
import { themeColors } from '
|
|
4
|
+
import { themeColors } from '../../../theme/colors';
|
|
5
5
|
import { Icon } from '@suflon/native-ui';
|
|
6
6
|
|
|
7
7
|
interface ChartData {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
useStaffStore,
|
|
12
12
|
IReportMetadata
|
|
13
13
|
} from '@suflon/native-ui';
|
|
14
|
-
import { themeColors } from '
|
|
14
|
+
import { themeColors } from '../../../theme/colors';
|
|
15
15
|
|
|
16
16
|
const CustomView = View;
|
|
17
17
|
const CustomText = ({ variant, className, style, ...props }: any) => <Text style={style} {...props} />;
|