OSA and promotion details page updated

This commit is contained in:
CPM
2025-08-14 09:25:09 +05:30
parent 55fd811683
commit 6900df4fdf
6 changed files with 872 additions and 51 deletions
@@ -263,7 +263,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 5; CURRENT_PROJECT_VERSION = 6;
DEVELOPMENT_TEAM = JGDHGNH9XY; DEVELOPMENT_TEAM = JGDHGNH9XY;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = PerformicsStoreDNA/Info.plist; INFOPLIST_FILE = PerformicsStoreDNA/Info.plist;
@@ -272,7 +272,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.5; MARKETING_VERSION = 1.6;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
@@ -292,7 +292,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 5; CURRENT_PROJECT_VERSION = 6;
DEVELOPMENT_TEAM = JGDHGNH9XY; DEVELOPMENT_TEAM = JGDHGNH9XY;
INFOPLIST_FILE = PerformicsStoreDNA/Info.plist; INFOPLIST_FILE = PerformicsStoreDNA/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1; IPHONEOS_DEPLOYMENT_TARGET = 15.1;
@@ -300,7 +300,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.5; MARKETING_VERSION = 1.6;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
+3
View File
@@ -12,6 +12,7 @@ import Dashboard from '../screens/MainScreen/Dashboard';
import FeedbackCategories from '../screens/MainScreen/Feedback/FeedbackCategories'; import FeedbackCategories from '../screens/MainScreen/Feedback/FeedbackCategories';
import { Platform, StatusBar, View } from 'react-native'; import { Platform, StatusBar, View } from 'react-native';
import Welcome from '../screens/MainScreen/WelcomePage'; import Welcome from '../screens/MainScreen/WelcomePage';
import Details from '../screens/MainScreen/Dashboard/Details';
const Stack = createNativeStackNavigator(); const Stack = createNativeStackNavigator();
@@ -38,6 +39,8 @@ const Routes = () => {
<Stack.Screen name="Dashboard" component={Dashboard} /> <Stack.Screen name="Dashboard" component={Dashboard} />
<Stack.Screen name="Feedback" component={Feedback} /> <Stack.Screen name="Feedback" component={Feedback} />
<Stack.Screen name="FeedbackCategories" component={FeedbackCategories} /> <Stack.Screen name="FeedbackCategories" component={FeedbackCategories} />
<Stack.Screen name="Details" component={Details} />
</Stack.Navigator> </Stack.Navigator>
<ToastComponent /> <ToastComponent />
</NavigationContainer> </NavigationContainer>
@@ -0,0 +1,406 @@
import React, { useEffect, useState } from 'react';
import { View, Text, TouchableOpacity, ScrollView, Image, Modal, Platform, StyleSheet, Alert } from 'react-native';
import { useRoute, useNavigation } from '@react-navigation/native';
import { post } from '../../../api/ApiService';
import IMAGES from '../../../constants/Images';
import { GlobalTheme } from '../../../theme';
import Loader from '../../../constants/Loader';
import { SafeAreaView } from 'react-native-safe-area-context';
import { toastError } from '../../../constants/Toast';
const Details = () => {
const route = useRoute();
const navigation = useNavigation();
const { selectedDetails = [], storeData, year, month, mainTabIndex } = route.params || {};
const [modalGraphData, setModalGraphData] = useState({});
const [activePromoTab, setActivePromoTab] = useState('Executed');
const [allCatData, setAllCatData] = useState([])
const [selectedCategoryData, setSelectedCategoryData] = useState([]);
const [categoryModalVisible, setCategoryModalVisible] = useState(false);
useEffect(() => {
if (selectedDetails.length > 0) {
fetchDetailGraphs(selectedDetails);
}
}, [selectedDetails]);
useEffect(() => {
getAllCatData();
}, [])
const getAllCatData = () => {
let data = {
parameters: {
projectid: 41654,
year: year,
monthno: month,
storeid: storeData?.StoreId
},
}
const apiUrl = mainTabIndex === 0
? 'https://dax.parinaam.in/execute/dabur/detmtd/oos_sku_list_for_all_visits_mtd'
: 'https://dax.parinaam.in/execute/dabur/detlsv/oos_sku_list_on_lsv';
post(apiUrl, data)
.then(res => {
setAllCatData(res?.data);
})
.catch(err => {
console.log('Error =>', err);
});
}
const fetchDetailGraphs = async (detailPages) => {
try {
const resultMap = {};
for (let item of detailPages) {
const response = await post(item.GraphUrl, {
parameters: {
projectid: 41654,
year: year,
monthno: month,
storeid: storeData?.StoreId
},
});
resultMap[item.GraphUrl] = response?.data || [];
}
setModalGraphData(resultMap);
} catch (error) {
console.log("❌ Error fetching detail graphs:", error);
}
};
const showCategoryDetails = (data) => {
setCategoryModalVisible(!categoryModalVisible)
// Filter SKUs from allCatData where the category name matches
const filteredCategory = allCatData.filter(item =>
item.Product_CategoryCategory_Name === data.Product_CategoryCategory_Name
);
// Save for display
setSelectedCategoryData(filteredCategory);
}
const isOSATab = selectedDetails.some(item => item.GraphTitle === "OSA - Category");
return (
<SafeAreaView style={{ flex: 1, backgroundColor: '#113F8C' }}>
<View style={{ flex: 1, backgroundColor: '#fff' }}>
{/* Header */}
<View style={styled.header}>
<TouchableOpacity onPress={() => navigation.goBack()} style={{ padding: 5 }}>
<Image source={IMAGES.backIcon} style={{ height: 20, width: 20, tintColor: '#fff' }} />
</TouchableOpacity>
<Text style={{ color: '#fff', fontSize: 20, fontWeight: 'bold' }}>Details</Text>
<View style={{ width: 25 }} /> {/* spacer */}
</View>
<ScrollView style={{ padding: 20 }}>
{selectedDetails.map((detail, index) => {
const values = modalGraphData[detail.GraphUrl] || [];
if (!modalGraphData[detail.GraphUrl]) {
return (
<View key={index} style={{ flex: 1, marginBottom: 20 }}>
<Loader visible={true} />
</View>
);
}
switch (detail.GraphType) {
case 'Table':
// Check if it's the Promotion table
const isPromotionTable =
values.length > 0 &&
values[0].hasOwnProperty('Product_CategoryCategory_Name') &&
values[0].hasOwnProperty('Promotion_MasterPromotion_Definition');
if (isPromotionTable) {
// Filter data based on active tab
const filteredValues = values.filter(row =>
activePromoTab === 'Executed'
? row.Executed?.toLowerCase() === 'yes'
: row.Executed?.toLowerCase() === 'no'
);
// Group data by Product_CategoryCategory_Name
const groupedData = filteredValues.reduce((acc, curr) => {
const category = curr.Product_CategoryCategory_Name || 'Unknown';
if (!acc[category]) acc[category] = [];
acc[category].push(curr);
return acc;
}, {});
return (
<View key={index} style={{ marginBottom: 10 }}>
{/* Horizontal Tabs - show once at top */}
{index === 0 && ( // ensures only first promotion table renders the toggle
<View style={{ flexDirection: 'row', marginBottom: 20 }}>
{['Executed', 'Not Executed'].map(tab => {
const isSelected = activePromoTab === tab;
return (
<TouchableOpacity
key={tab}
onPress={() => setActivePromoTab(tab)}
style={{
flex: 1,
paddingVertical: 10,
backgroundColor: isSelected ? '#113F8C' : '#E3EBF8',
borderRadius: 8,
marginHorizontal: 5,
alignItems: 'center',
}}
>
<Text style={{ color: isSelected ? '#fff' : '#000', fontWeight: '600' }}>
{tab}
</Text>
</TouchableOpacity>
);
})}
</View>
)}
{/* No data message */}
<View style={{}}>
{filteredValues.length === 0 ? (
<View style={{ marginTop: 0, backgroundColor: 'red' }} />
) : (
Object.keys(groupedData).map((categoryName, catIdx) => (
<View key={catIdx} style={{ marginBottom: 20, }}>
{/* Category Header */}
<View style={{ backgroundColor: '#E8F0FF', padding: 8, borderRadius: 6 }}>
<Text style={{ fontSize: 15, fontWeight: '700', color: '#113F8C' }}>
{String(categoryName)}
</Text>
</View>
{/* Table Header */}
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: '#f5f5f5',
paddingVertical: 8,
paddingHorizontal: 9,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
marginTop: 5
}}
>
<Text style={{ color: '#000', fontWeight: 'bold', width: '80%' }}>
Definition
</Text>
<Text style={{ color: '#000', fontWeight: 'bold', width: '20%', textAlign: 'center' }}>
Executed
</Text>
</View>
{/* Table Rows */}
<View
style={{
backgroundColor: '#fff',
paddingHorizontal: 10,
borderBottomLeftRadius: 8,
borderBottomRightRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2
}}
>
{groupedData[categoryName].map((row, rowIdx) => (
<View
key={rowIdx}
style={{
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: 8,
borderBottomWidth: rowIdx === groupedData[categoryName].length - 1 ? 0 : 1,
borderColor: '#eee'
}}
>
<Text style={{ color: '#000', width: '80%' }}>
{String(row.Promotion_MasterPromotion_Definition) || '-'}
</Text>
<Text style={{ color: '#000', width: '20%', textAlign: 'center' }}>
{row.Executed || '-'}
</Text>
</View>
))}
</View>
</View>
))
)}
</View>
</View>
);
}
let displayKey = null;
let presentKey = null;
if (values.length > 0) {
const presentKeys = Object.keys(values[0]).filter(k =>
(typeof values[0][k] === 'string' && ['Yes', 'No'].includes(values[0][k])) ||
typeof values[0][k] === 'boolean' ||
typeof values[0][k] === 'number'
);
presentKey = presentKeys.length > 0 ? presentKeys[0] : null;
const displayKeys = Object.keys(values[0]).filter(k => k !== presentKey && typeof values[0][k] === 'string');
displayKey = displayKeys.length > 0 ? displayKeys[0] : null;
}
return (
<View key={index} style={{ marginBottom: 30 }}>
<Text style={{ color: '#000', fontSize: 16, fontWeight: '600', marginBottom: 10 }}>
{detail.GraphTitle}
</Text>
<View style={styled.categoryHeader}>
<Text style={{ color: '#000', fontWeight: 'bold', width: '80%' }}>
{displayKey ? displayKey.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2') : ''}
</Text>
<Text style={{ color: '#000', fontWeight: 'bold', width: '20%', textAlign: 'center' }}>
{presentKey ? presentKey.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2') : ''}
</Text>
</View>
<View style={styled.itemContainer}>
{values.map((row, idx) => {
const presentKeys = Object.keys(row).filter(k =>
(typeof row[k] === 'string' && ['Yes', 'No'].includes(row[k])) ||
typeof row[k] === 'boolean' ||
typeof row[k] === 'number'
);
const presentKey = presentKeys.length > 0 ? presentKeys[0] : null;
const displayKeys = Object.keys(row).filter(k => k !== presentKey && typeof row[k] === 'string');
const displayKey = displayKeys.length > 0 ? displayKeys[0] : null;
return (
<View
key={idx}
style={{
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: 8,
borderBottomWidth: idx === values.length - 1 ? 0 : 1,
borderColor: '#eee',
}}>
<Text style={{ color: '#000', width: '80%' }}>{row[displayKey] || '--'}</Text>
<TouchableOpacity disabled={isOSATab ? false : true}
onPress={() => {
if (row[presentKey] == 100 || row[presentKey] == '100') {
toastError('Alert', 'No Data Available')
} else {
showCategoryDetails(row)
}
}}
style={{ width: '20%', }}>
<Text style={{ color: '#113F8C', textAlign: 'center', fontWeight: '500' }}>
{presentKey ? (
typeof row[presentKey] === 'string' && ['Yes', 'No'].includes(row[presentKey]) ? row[presentKey] :
typeof row[presentKey] === 'boolean' ? (row[presentKey] ? 'Yes' : 'No') :
typeof row[presentKey] === 'number' ? row[presentKey].toFixed(2) : row[presentKey] || '-'
) : '-'}
</Text>
</TouchableOpacity>
</View>
);
})}
</View>
</View>
);
default:
return (
<View key={index} style={{ marginBottom: 20 }}>
<Text>Unsupported GraphType: {detail.GraphType}</Text>
</View>
);
}
})}
</ScrollView>
<Modal
visible={categoryModalVisible}
animationType="slide"
onRequestClose={() => setCategoryModalVisible(false)}
>
<View style={{ flex: 1, }}>
{/* Header */}
<View style={{ height: Platform.OS === 'ios' ? 55 : 0, backgroundColor: '#113F8C' }} />
<View style={{ width: '100%', backgroundColor: '#113F8C', borderBottomWidth: 1, borderColor: 'gray', padding: 5, paddingHorizontal: 20, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
<View style={{ width: '93%', alignItems: 'center' }}>
<Text style={{ color: '#fff', fontSize: 20, fontWeight: 'bold', marginBottom: 10, alignSelf: 'center' }}>{selectedCategoryData[0]?.Product_CategoryCategory_Name || 'Category'}</Text>
</View>
<TouchableOpacity onPress={() => setCategoryModalVisible(false)} style={{ width: '7%', alignItems: 'center' }}>
<Image source={IMAGES.crossIcon} style={{ height: 25, width: 25, resizeMode: 'contain' }} />
</TouchableOpacity>
</View>
{/* SKU List */}
<ScrollView contentContainerStyle={{ padding: 15 }}>
{/* <Text style={{ color: '#000', fontSize: 16, fontWeight: 'bold', marginBottom: 5 }}>OOS SKU details</Text> */}
{selectedCategoryData.map((item, idx) => (
<View
key={idx}
style={{
marginTop: 2,
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: 8,
borderBottomWidth: idx === selectedCategoryData.length - 1 ? 0 : 1,
borderColor: '#eee',
}}
>
<Text style={{ color: '#000', width: '80%' }}>
{item.Product_MasterProduct_Name}
</Text>
{/* <Text style={{ color: '#000', width: '20%', textAlign: 'center' }}>
{item['#_of_OOS_SKU_for_all_visits']}
</Text> */}
</View>
))}
</ScrollView>
</View>
</Modal>
</View>
</SafeAreaView>
);
};
const styled = StyleSheet.create({
header: {
width: '100%',
backgroundColor: '#113F8C',
padding: 10,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
},
categoryHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: '#EDEDED',
paddingVertical: 8,
paddingHorizontal: 10,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
},
itemContainer: {
backgroundColor: '#fff',
paddingHorizontal: 10,
borderBottomLeftRadius: 8,
borderBottomRightRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2
}
})
export default Details;
+405
View File
@@ -0,0 +1,405 @@
import React, { useEffect, useState } from 'react';
import { View, Text, TouchableOpacity, ScrollView, Image, Modal, Platform, StyleSheet, Alert } from 'react-native';
import { useRoute, useNavigation } from '@react-navigation/native';
import { post } from '../../../api/ApiService';
import IMAGES from '../../../constants/Images';
import { GlobalTheme } from '../../../theme';
import Loader from '../../../constants/Loader';
import { SafeAreaView } from 'react-native-safe-area-context';
import { toastError } from '../../../constants/Toast';
const Details = () => {
const route = useRoute();
const navigation = useNavigation();
const { selectedDetails = [], storeData, year, month, mainTabIndex } = route.params || {};
const [modalGraphData, setModalGraphData] = useState({});
const [activePromoTab, setActivePromoTab] = useState('Executed');
const [allCatData, setAllCatData] = useState([])
const [selectedCategoryData, setSelectedCategoryData] = useState([]);
const [categoryModalVisible, setCategoryModalVisible] = useState(false);
useEffect(() => {
if (selectedDetails.length > 0) {
fetchDetailGraphs(selectedDetails);
}
}, [selectedDetails]);
useEffect(() => {
getAllCatData();
}, [])
const getAllCatData = () => {
let data = {
parameters: {
projectid: 41654,
year: year,
monthno: month,
storeid: storeData?.StoreId
},
}
const apiUrl = mainTabIndex === 0
? 'https://dax.parinaam.in/execute/dabur/detmtd/oos_sku_list_for_all_visits_mtd'
: 'https://dax.parinaam.in/execute/dabur/detlsv/oos_sku_list_on_lsv';
post(apiUrl, data)
.then(res => {
setAllCatData(res?.data);
})
.catch(err => {
console.log('Error =>', err);
});
}
const fetchDetailGraphs = async (detailPages) => {
try {
const resultMap = {};
for (let item of detailPages) {
const response = await post(item.GraphUrl, {
parameters: {
projectid: 41654,
year: year,
monthno: month,
storeid: storeData?.StoreId
},
});
resultMap[item.GraphUrl] = response?.data || [];
}
setModalGraphData(resultMap);
} catch (error) {
console.log("❌ Error fetching detail graphs:", error);
}
};
const showCategoryDetails = (data) => {
setCategoryModalVisible(!categoryModalVisible)
// Filter SKUs from allCatData where the category name matches
const filteredCategory = allCatData.filter(item =>
item.Product_CategoryCategory_Name === data.Product_CategoryCategory_Name
);
// Save for display
setSelectedCategoryData(filteredCategory);
}
const isOSATab = selectedDetails.some(item => item.GraphTitle === "OSA - Category");
return (
<SafeAreaView style={{ flex: 1, backgroundColor: '#113F8C' }}>
<View style={{ flex: 1, backgroundColor: '#fff' }}>
{/* Header */}
<View style={styled.header}>
<TouchableOpacity onPress={() => navigation.goBack()} style={{ padding: 5 }}>
<Image source={IMAGES.backIcon} style={{ height: 20, width: 20, tintColor: '#fff' }} />
</TouchableOpacity>
<Text style={{ color: '#fff', fontSize: 20, fontWeight: 'bold' }}>Details</Text>
<View style={{ width: 25 }} /> {/* spacer */}
</View>
<ScrollView style={{ padding: 20 }}>
{selectedDetails.map((detail, index) => {
const values = modalGraphData[detail.GraphUrl] || [];
if (!modalGraphData[detail.GraphUrl]) {
return (
<View key={index} style={{ flex: 1, marginBottom: 20 }}>
<Loader visible={true} />
</View>
);
}
switch (detail.GraphType) {
case 'Table':
// Check if it's the Promotion table
const isPromotionTable =
values.length > 0 &&
values[0].hasOwnProperty('Product_CategoryCategory_Name') &&
values[0].hasOwnProperty('Promotion_MasterPromotion_Definition');
if (isPromotionTable) {
// Filter data based on active tab
const filteredValues = values.filter(row =>
activePromoTab === 'Executed'
? row.Executed?.toLowerCase() === 'yes'
: row.Executed?.toLowerCase() === 'no'
);
// Group data by Product_CategoryCategory_Name
const groupedData = filteredValues.reduce((acc, curr) => {
const category = curr.Product_CategoryCategory_Name || 'Unknown';
if (!acc[category]) acc[category] = [];
acc[category].push(curr);
return acc;
}, {});
return (
<View key={index} style={{ marginBottom: 10 }}>
{/* Horizontal Tabs - show once at top */}
{index === 0 && ( // ensures only first promotion table renders the toggle
<View style={{ flexDirection: 'row', marginBottom: 20 }}>
{['Executed', 'Not Executed'].map(tab => {
const isSelected = activePromoTab === tab;
return (
<TouchableOpacity
key={tab}
onPress={() => setActivePromoTab(tab)}
style={{
flex: 1,
paddingVertical: 10,
backgroundColor: isSelected ? '#113F8C' : '#E3EBF8',
borderRadius: 8,
marginHorizontal: 5,
alignItems: 'center',
}}
>
<Text style={{ color: isSelected ? '#fff' : '#000', fontWeight: '600' }}>
{tab}
</Text>
</TouchableOpacity>
);
})}
</View>
)}
{/* No data message */}
<View style={{}}>
{filteredValues.length === 0 ? (
<View style={{ marginTop: 0, backgroundColor: 'red' }} />
) : (
Object.keys(groupedData).map((categoryName, catIdx) => (
<View key={catIdx} style={{ marginBottom: 20 }}>
{/* Category Header */}
<View style={{ backgroundColor: '#E8F0FF', padding: 8, borderRadius: 6 }}>
<Text style={{ fontSize: 15, fontWeight: '700', color: '#113F8C' }}>
{String(categoryName)}
</Text>
</View>
{/* Table Header */}
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: '#f5f5f5',
paddingVertical: 8,
paddingHorizontal: 9,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
marginTop: 5
}}
>
<Text style={{ color: '#000', fontWeight: 'bold', width: '80%' }}>
Definition
</Text>
<Text style={{ color: '#000', fontWeight: 'bold', width: '20%', textAlign: 'center' }}>
Executed
</Text>
</View>
{/* Table Rows */}
<View
style={{
backgroundColor: '#fff',
paddingHorizontal: 10,
borderBottomLeftRadius: 8,
borderBottomRightRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2
}}
>
{groupedData[categoryName].map((row, rowIdx) => (
<View
key={rowIdx}
style={{
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: 8,
borderBottomWidth: rowIdx === groupedData[categoryName].length - 1 ? 0 : 1,
borderColor: '#eee'
}}
>
<Text style={{ color: '#000', width: '80%' }}>
{String(row.Promotion_MasterPromotion_Definition) || '-'}
</Text>
<Text style={{ color: '#000', width: '20%', textAlign: 'center' }}>
{row.Executed || '-'}
</Text>
</View>
))}
</View>
</View>
))
)}
</View>
</View>
);
}
let displayKey = null;
let presentKey = null;
if (values.length > 0) {
const presentKeys = Object.keys(values[0]).filter(k =>
(typeof values[0][k] === 'string' && ['Yes', 'No'].includes(values[0][k])) ||
typeof values[0][k] === 'boolean' ||
typeof values[0][k] === 'number'
);
presentKey = presentKeys.length > 0 ? presentKeys[0] : null;
const displayKeys = Object.keys(values[0]).filter(k => k !== presentKey && typeof values[0][k] === 'string');
displayKey = displayKeys.length > 0 ? displayKeys[0] : null;
}
return (
<View key={index} style={{ marginBottom: 30 }}>
<Text style={{ color: '#000', fontSize: 16, fontWeight: '600', marginBottom: 10 }}>
{detail.GraphTitle}
</Text>
<View style={styled.categoryHeader}>
<Text style={{ color: '#000', fontWeight: 'bold', width: '80%' }}>
{displayKey ? displayKey.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2') : ''}
</Text>
<Text style={{ color: '#000', fontWeight: 'bold', width: '20%', textAlign: 'center' }}>
{presentKey ? presentKey.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2') : ''}
</Text>
</View>
<View style={styled.itemContainer}>
{values.map((row, idx) => {
const presentKeys = Object.keys(row).filter(k =>
(typeof row[k] === 'string' && ['Yes', 'No'].includes(row[k])) ||
typeof row[k] === 'boolean' ||
typeof row[k] === 'number'
);
const presentKey = presentKeys.length > 0 ? presentKeys[0] : null;
const displayKeys = Object.keys(row).filter(k => k !== presentKey && typeof row[k] === 'string');
const displayKey = displayKeys.length > 0 ? displayKeys[0] : null;
return (
<View
key={idx}
style={{
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: 8,
borderBottomWidth: idx === values.length - 1 ? 0 : 1,
borderColor: '#eee',
}}>
<Text style={{ color: '#000', width: '80%' }}>{row[displayKey] || '--'}</Text>
<TouchableOpacity disabled={isOSATab ? false : true}
onPress={() => {
if (row[presentKey] == 100 || row[presentKey] == '100') {
toastError('Alert', 'No Data Available')
} else {
showCategoryDetails(row)
}
}}
style={{ width: '20%', }}>
<Text style={{ color: '#113F8C', textAlign: 'center', fontWeight: '500' }}>
{presentKey ? (
typeof row[presentKey] === 'string' && ['Yes', 'No'].includes(row[presentKey]) ? row[presentKey] :
typeof row[presentKey] === 'boolean' ? (row[presentKey] ? 'Yes' : 'No') :
typeof row[presentKey] === 'number' ? row[presentKey].toFixed(2) : row[presentKey] || '-'
) : '-'}
</Text>
</TouchableOpacity>
</View>
);
})}
</View>
</View>
);
default:
return (
<View key={index} style={{ marginBottom: 20 }}>
<Text>Unsupported GraphType: {detail.GraphType}</Text>
</View>
);
}
})}
</ScrollView>
<Modal
visible={categoryModalVisible}
animationType="slide"
onRequestClose={() => setCategoryModalVisible(false)}
>
<View style={{ flex: 1, }}>
{/* Header */}
<View style={{ height: Platform.OS === 'ios' ? 55 : 0, backgroundColor: '#113F8C' }} />
<View style={{ width: '100%', backgroundColor: '#113F8C', borderBottomWidth: 1, borderColor: 'gray', padding: 5, paddingHorizontal: 20, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
<View style={{ width: '93%', alignItems: 'center' }}>
<Text style={{ color: '#fff', fontSize: 20, fontWeight: 'bold', marginBottom: 10, alignSelf: 'center' }}>{selectedCategoryData[0]?.Product_CategoryCategory_Name || 'Category'}</Text>
</View>
<TouchableOpacity onPress={() => setCategoryModalVisible(false)} style={{ width: '7%', alignItems: 'center' }}>
<Image source={IMAGES.crossIcon} style={{ height: 25, width: 25, resizeMode: 'contain' }} />
</TouchableOpacity>
</View>
{/* SKU List */}
<ScrollView contentContainerStyle={{ padding: 15 }}>
{/* <Text style={{ color: '#000', fontSize: 16, fontWeight: 'bold', marginBottom: 5 }}>OOS SKU details</Text> */}
{selectedCategoryData.map((item, idx) => (
<View
key={idx}
style={{
marginTop: 2,
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: 8,
borderBottomWidth: idx === selectedCategoryData.length - 1 ? 0 : 1,
borderColor: '#eee',
}}
>
<Text style={{ color: '#000', width: '80%' }}>
{item.Product_MasterProduct_Name}
</Text>
{/* <Text style={{ color: '#000', width: '20%', textAlign: 'center' }}>
{item['#_of_OOS_SKU_for_all_visits']}
</Text> */}
</View>
))}
</ScrollView>
</View>
</Modal>
</View>
</SafeAreaView>
);
};
const styled = StyleSheet.create({
header: {
width: '100%',
backgroundColor: '#113F8C',
padding: 10,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
},
categoryHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: '#EDEDED',
paddingVertical: 8,
paddingHorizontal: 10,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
},
itemContainer: {
backgroundColor: '#fff',
paddingHorizontal: 10,
borderBottomLeftRadius: 8,
borderBottomRightRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 2
}
})
export default Details;
+23 -8
View File
@@ -303,7 +303,7 @@ const Dashboard = (props) => {
[d2], [d2],
(tx, results) => { (tx, results) => {
console.log("results===", results); // console.log("results===", results);
const rows = []; const rows = [];
for (let i = 0; i < results.rows.length; i++) { for (let i = 0; i < results.rows.length; i++) {
rows.push(results.rows.item(i)); rows.push(results.rows.item(i));
@@ -429,7 +429,7 @@ const Dashboard = (props) => {
const openBottomSheet = () => { const openBottomSheet = () => {
refRBSheet.current.open() refRBSheet.current.open()
} };
const onSelectStore = async (item) => { const onSelectStore = async (item) => {
await insertStoreInfoDNALocal([item]); await insertStoreInfoDNALocal([item]);
@@ -442,11 +442,11 @@ const Dashboard = (props) => {
getTabData(currentTab?.MainTabData); getTabData(currentTab?.MainTabData);
refRBSheet.current.close() refRBSheet.current.close()
} };
const onSelectSubTab = (item) => { const onSelectSubTab = (item) => {
setActiveTab(item?.TabId) setActiveTab(item?.TabId)
} };
const getFilterStateCity = async () => { const getFilterStateCity = async () => {
try { try {
@@ -516,7 +516,7 @@ const Dashboard = (props) => {
setStoreList(resData) setStoreList(resData)
setLoading(false) setLoading(false)
console.log('storeSearchApi====>', JSON.stringify(resData)); // console.log('storeSearchApi====>', JSON.stringify(resData));
} catch (error) { } catch (error) {
setLoading(false) setLoading(false)
@@ -707,6 +707,9 @@ const Dashboard = (props) => {
); );
}); });
// console.log('mainTabIndex----->',mainTabIndex);
return ( return (
<SafeAreaView style={{ flex: 1, backgroundColor: GlobalTheme.colors.primary }}> <SafeAreaView style={{ flex: 1, backgroundColor: GlobalTheme.colors.primary }}>
<View style={{ flex: 1, backgroundColor: '#fff' }}> <View style={{ flex: 1, backgroundColor: '#fff' }}>
@@ -914,11 +917,23 @@ const Dashboard = (props) => {
{firstItem && ( {firstItem && (
<TouchableOpacity <TouchableOpacity
activeOpacity={0.8} activeOpacity={0.8}
// onPress={() => {
// if (firstItem.clickable === 1 && firstItem.DetailsPage?.length > 0) {
// setSelectedDetails(firstItem.DetailsPage);
// setShowDetailsModal(true);
// fetchDetailGraphs(firstItem.DetailsPage);
// }
// }}
onPress={() => { onPress={() => {
if (firstItem.clickable === 1 && firstItem.DetailsPage?.length > 0) { if (firstItem.clickable === 1 && firstItem.DetailsPage?.length > 0) {
setSelectedDetails(firstItem.DetailsPage); navigation.navigate('Details', {
setShowDetailsModal(true); selectedDetails: firstItem.DetailsPage,
fetchDetailGraphs(firstItem.DetailsPage); storeData,
year,
month,
mainTabIndex
});
} }
}} }}
> >
@@ -17,12 +17,6 @@
"TabRow": 1, "TabRow": 1,
"TabCol": 2 "TabCol": 2
}, },
// {
// "TabId": 3,
// "TabName": "SOS Compliance",
// "TabRow": 1,
// "TabCol": 3
// },
{ {
"TabId": 4, "TabId": 4,
"TabName": "OSA", "TabName": "OSA",
@@ -42,6 +36,9 @@
"TabCol": 6 "TabCol": 6
} }
], ],
"graphDetails": [ "graphDetails": [
{ {
"TabId": 1, "TabId": 1,
@@ -61,15 +58,6 @@
"GraphBackground": "#E2C8FE", "GraphBackground": "#E2C8FE",
"GraphOptions": {} "GraphOptions": {}
}, },
// {
// "TabId": 1,
// "GraphId": 3,
// "GraphType": "ScoreCard",
// "GraphTitle": "SOS Compliance",
// "GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/SOS_Compliance_Perc",
// "GraphBackground": "#FFD7C3",
// "GraphOptions": {}
// },
{ {
"TabId": 1, "TabId": 1,
"GraphId": 4, "GraphId": 4,
@@ -262,16 +250,26 @@
"TabId": 6, "TabId": 6,
"GraphId": 9, "GraphId": 9,
"GraphType": "Table", "GraphType": "Table",
"GraphTitle": "Promotion Availability", "GraphTitle": "Promotion Not Executed",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/promotion_availability_mtd", "GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/promotion_not_executed_mtd",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 6,
"GraphId": 10,
"GraphType": "Table",
"GraphTitle": "Promotion executed",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/promotion_executed_mtd",
"GraphBackground": "#ECFFFA", "GraphBackground": "#ECFFFA",
"GraphOptions": {} "GraphOptions": {}
} }
] ]
}, },
{ {
"TabId": 6, "TabId": 6,
"GraphId": 10, "GraphId": 11,
"GraphType": "BarGraph", "GraphType": "BarGraph",
"GraphTitle": "Promotion", "GraphTitle": "Promotion",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/promotion_trend_perc_mtd", "GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/promotion_trend_perc_mtd",
@@ -298,12 +296,6 @@
"TabRow": 1, "TabRow": 1,
"TabCol": 2 "TabCol": 2
}, },
// {
// "TabId": 3,
// "TabName": "SOS Compliance",
// "TabRow": 1,
// "TabCol": 3
// },
{ {
"TabId": 4, "TabId": 4,
"TabName": "OSA", "TabName": "OSA",
@@ -342,15 +334,6 @@
"GraphBackground": "#E2C8FE", "GraphBackground": "#E2C8FE",
"GraphOptions": {} "GraphOptions": {}
}, },
// {
// "TabId": 1,
// "GraphId": 3,
// "GraphType": "ScoreCard",
// "GraphTitle": "SOS Compliance",
// "GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/sos_compliance_lsv_perc",
// "GraphBackground": "#FFD7C3",
// "GraphOptions": {}
// },
{ {
"TabId": 1, "TabId": 1,
"GraphId": 4, "GraphId": 4,
@@ -474,7 +457,7 @@
"TabId": 5, "TabId": 5,
"GraphId": 5, "GraphId": 5,
"GraphType": "Table", "GraphType": "Table",
"GraphTitle": "Asset Availability", "GraphTitle": "Visibility",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/asset_availability_lsv", "GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/asset_availability_lsv",
"GraphBackground": "#ECFFFA", "GraphBackground": "#ECFFFA",
"GraphOptions": {} "GraphOptions": {}
@@ -483,7 +466,7 @@
"TabId": 5, "TabId": 5,
"GraphId": 5, "GraphId": 5,
"GraphType": "Table", "GraphType": "Table",
"GraphTitle": "Asset", "GraphTitle": "Additional Visibility",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/additional_visibility_lsv", "GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/additional_visibility_lsv",
"GraphBackground": "#ECFFFA", "GraphBackground": "#ECFFFA",
"GraphOptions": {} "GraphOptions": {}
@@ -524,7 +507,7 @@
"TabId": 4, "TabId": 4,
"GraphId": 7, "GraphId": 7,
"GraphType": "Table", "GraphType": "Table",
"GraphTitle": "SOS Actual - Category", "GraphTitle": "OSA - Category",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/osa_lsv_perc_on_category", "GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/osa_lsv_perc_on_category",
"GraphBackground": "#ECFFFA", "GraphBackground": "#ECFFFA",
"GraphOptions": {} "GraphOptions": {}
@@ -554,8 +537,17 @@
"TabId": 6, "TabId": 6,
"GraphId": 9, "GraphId": 9,
"GraphType": "Table", "GraphType": "Table",
"GraphTitle": "Promotion Availability", "GraphTitle": "Promotion Not Executed",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/promotion_availability_lsv", "GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/promotion_not_executed_lsv",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 6,
"GraphId": 10,
"GraphType": "Table",
"GraphTitle": "Promotion executed",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/promotion_executed_lsv",
"GraphBackground": "#ECFFFA", "GraphBackground": "#ECFFFA",
"GraphOptions": {} "GraphOptions": {}
} }
@@ -563,7 +555,7 @@
}, },
{ {
"TabId": 6, "TabId": 6,
"GraphId": 10, "GraphId": 11,
"GraphType": "BarGraph", "GraphType": "BarGraph",
"GraphTitle": "Promotion", "GraphTitle": "Promotion",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/promotion_trend_lsv_perc", "GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/promotion_trend_lsv_perc",