432 lines
26 KiB
JavaScript
432 lines
26 KiB
JavaScript
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");
|
|
console.log('isOSATab----',isOSATab);
|
|
|
|
|
|
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 }} />
|
|
|
|
</View>
|
|
|
|
<ScrollView style={{ padding: 20 }}>
|
|
{selectedDetails && selectedDetails.length > 0 ? 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={{ marginTop: 10, 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%', }}>
|
|
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
|
<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>
|
|
{ isOSATab && row[presentKey] != 100 || isOSATab && row[presentKey] != '100'? <Image source={IMAGES.rightArrowIcon} style={{marginLeft:6, height: 12, width: 12, resizeMode: 'contain' }} /> :null}
|
|
</View>
|
|
</TouchableOpacity>
|
|
</View>
|
|
);
|
|
})}
|
|
</View>
|
|
</View>
|
|
);
|
|
default:
|
|
return (
|
|
<View key={index} style={{ marginBottom: 20 }}>
|
|
<Text>Unsupported GraphType: {detail.GraphType}</Text>
|
|
</View>
|
|
);
|
|
}
|
|
}) :
|
|
<Loader visible={true} />
|
|
}
|
|
</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>
|
|
|
|
<View style={{ borderTopLeftRadius: 10, borderTopRightRadius: 10, marginTop: 15, marginHorizontal: 15, backgroundColor: '#eee', padding: 10, borderWidth: 1, borderColor: 'gray' }}>
|
|
<Text style={{ color: '#000', fontSize: 16, fontWeight: '500' }}>{'Out of Stock SKUs:'}</Text>
|
|
</View>
|
|
|
|
{/* SKU List */}
|
|
<ScrollView contentContainerStyle={{
|
|
marginHorizontal: 15,
|
|
borderBottomWidth: 0.5,
|
|
borderLeftWidth: 0.5,
|
|
borderRightWidth: 0.5,
|
|
borderColor: 'gray',
|
|
borderBottomEndRadius: 10,
|
|
borderBottomLeftRadius: 10,
|
|
}}>
|
|
{/* <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',
|
|
|
|
}}
|
|
>
|
|
<View style={{ flexDirection: 'row' }}>
|
|
<Text style={{ color: '#000', marginLeft: 10 }}>{idx + 1}. </Text>
|
|
<Text style={{ color: '#000', }}>
|
|
{item.Product_MasterProduct_Name}
|
|
</Text>
|
|
</View>
|
|
{/* <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;
|