Graph design updated

This commit is contained in:
CPM
2025-08-05 11:15:54 +05:30
parent 979f4f332f
commit f81cab33ff
5 changed files with 224 additions and 174 deletions
-1
View File
@@ -67,7 +67,6 @@ const Login = ({ navigation }) => {
setLoading(false);
}
};
return (
<SafeAreaView style={styles.container}>
<KeyboardAwareScrollView keyboardShouldPersistTaps="handled" enableOnAndroid={true} contentContainerStyle={{ justifyContent: 'center', flexGrow: 1, marginTop: 0 }} style={styles.container}>
+195 -148
View File
@@ -226,11 +226,22 @@ const Dashboard = (props) => {
getFilterStateCity()
}, []);
useEffect(() => {
setTabCache({}); // reset cache when filters change
}, [month, year]);
// useEffect(() => {
// if (mainDisplayJson && storeData?.StoreId) {
// getTabData(mainDisplayJson);
// }
// }, [mainTabIndex, storeData]);
useEffect(() => {
if (mainDisplayJson && storeData?.StoreId) {
getTabData(mainDisplayJson);
setTimeout(() => getTabData(mainDisplayJson), 0);
}
}, [mainTabIndex, storeData]);
}, [mainDisplayJson, storeData]);
useEffect(() => {
getDataFromLocal();
@@ -367,16 +378,15 @@ const Dashboard = (props) => {
const getTabData = async (tabData) => {
if (!storeData?.StoreId) return;
const tabKey = `${mainTabIndex}_${storeData?.StoreId}_${year}_${month}`;
// const tabKey = `${mainTabIndex}_${storeData?.StoreId}_${year}_${month}`;
const tabKey = `${mainTabIndex}_${storeData?.StoreId}_${year}_${month}_${Date.now()}`;
if (tabCache[tabKey]) {
console.log("Using cached data for tab:", tabKey);
setGraphApiData(tabCache[tabKey]);
return;
}
console.log("📦 TabKey:", tabKey);
setLoading(true);
try {
const params = {
parameters: {
@@ -386,11 +396,27 @@ const Dashboard = (props) => {
storeid: storeData?.StoreId
}
};
console.log("🔁 API Params:", params);
const graphDetails = tabData?.graphDetails || [];
const uniqueUrls = [...new Set(graphDetails.map(graph => graph.GraphUrl))];
const apiCalls = uniqueUrls.map(url => {
// const apiCalls = uniqueUrls.map(url => {
// const config = {
// method: 'post',
// url: url,
// headers: {
// 'X-API-Key': 'f7fa9b09-ced8-4862-8cb7-5e7599d90fa2',
// 'Content-Type': 'application/json'
// },
// data: params
// };
// return axios.request(config);
// });
const apiCalls = uniqueUrls.map(async url => {
const config = {
method: 'post',
url: url,
@@ -400,15 +426,27 @@ const Dashboard = (props) => {
},
data: params
};
return axios.request(config);
return await fetchWithRetry(config);
});
const results = await Promise.all(apiCalls);
console.log("📊 API Results:", results.map(r => r?.data));
const dataMap = {};
// uniqueUrls.forEach((url, idx) => {
// dataMap[url] = results[idx].data;
// });
uniqueUrls.forEach((url, idx) => {
dataMap[url] = results[idx].data;
const responseData = results[idx]?.data;
if (!responseData || !responseData.data || responseData.data.length === 0) {
console.warn(`⚠️ API ${url} returned empty or invalid data`);
// Optional: Retry or show error
}
dataMap[url] = responseData;
});
console.log('uniqueUrls-dataMap---------->', dataMap);
setGraphApiData(dataMap);
setTabCache(prev => ({ ...prev, [tabKey]: dataMap }));
@@ -420,6 +458,49 @@ const Dashboard = (props) => {
}
};
const fetchWithRetry = async (config, retries = 2, delay = 1000) => {
for (let i = 0; i <= retries; i++) {
try {
const res = await axios.request(config);
if (res?.data?.status === 'success' && res?.data?.data) {
return res;
} else {
console.warn(`Attempt ${i + 1} failed:`, res?.data?.error || 'No data');
}
} catch (err) {
console.warn(`Attempt ${i + 1} failed with error:`, err.message);
}
if (i < retries) await new Promise(res => setTimeout(res, delay));
}
return null; // all attempts failed
};
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
},
});
console.log(`Data for ${item.GraphTitle}:`, response?.data); // Add this
resultMap[item.GraphUrl] = response?.data || [];
}
setModalGraphData(resultMap);
} catch (error) {
console.log("❌ Error fetching detail graphs:", error);
}
};
const handleScroll = (event) => {
const offsetY = event.nativeEvent.contentOffset.y;
setShowButton(offsetY > 0); // Show button only if not at the top
@@ -521,23 +602,42 @@ const Dashboard = (props) => {
const renderItem = ({ item }) => {
const apiData = graphApiData[item.GraphUrl];
const values = apiData?.data || [];
// const values = apiData?.data || [];
const values = apiData?.data?.length ? apiData.data : [];
const scoreValue = values.length > 0 ? Object.values(values[0])[0] : null;
// <Text>{scoreValue !== undefined ? `${scoreValue.toFixed(1)}%` : "No Data"}</Text>
console.log('apiData --->', apiData);
if (!apiData) {
if (apiData?.status === 'error') {
return (
<View style={[styles.percentBox, { justifyContent: 'center', alignItems: 'center' }]}>
<ActivityIndicator size="small" color="#113F8C" />
<View style={styles.percentBox}>
<Text style={styles.boxText}>{item.GraphTitle}</Text>
<Text style={styles.boxText}></Text>
</View>
);
}
// if (!apiData) {
// return (
// <View style={[styles.percentBox, { justifyContent: 'center', alignItems: 'center' }]}>
// <ActivityIndicator size="small" color="#113F8C" />
// </View>
// );
// }
switch (item.GraphType) {
case "ScoreCard":
const firstDataObj = values[0] || {};
const scoreValue = Object.values(firstDataObj)[0]; // dynamically get first value
// const scoreValue = values.length > 0 ? Object.values(values[0])[0] : null;
return (
<View style={[styles.percentBox, { backgroundColor: item.GraphBackground }]}>
<Text style={styles.boxText}>{item.GraphTitle}</Text>
@@ -547,63 +647,11 @@ const Dashboard = (props) => {
</View>
);
// CHART KIT
// case "BarGraph":
// const barLabels = values.map(v => v.CalendarYear_Month);
// const barValues = values.map(v => {
// const num = Object.values(v).find(val => typeof val === 'number');
// return num ? parseFloat(num.toFixed(1)) : 0;
// });
// const barData = {
// labels: barLabels,
// datasets: [
// {
// data: barValues,
// }
// ]
// };
// return (
// <View style={{ padding: 10, borderWidth: 0.5, borderColor: 'gray' }}>
// <Text style={{ fontSize: 16, fontWeight: '600', marginBottom: 10 }}>{item.GraphTitle}</Text>
// <BarChart
// data={barData}
// width={screenWidth - 40}
// height={240}
// yAxisSuffix="%"
// fromZero={true}
// showValuesOnTopOfBars={true}
// segments={5} // Will give 0%, 20%, ..., 100%
// chartConfig={{
// backgroundColor: '#ffffff',
// backgroundGradientFrom: '#ffffff',
// backgroundGradientTo: '#ffffff',
// fillShadowGradientFrom: '#BFC2FF',
// fillShadowGradientTo: '#BFC2FF',
// fillShadowGradientFromOpacity: 1,
// fillShadowGradientToOpacity: 1,
// decimalPlaces: 0,
// color: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
// labelColor: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
// barPercentage: 1,
// propsForVerticalLabels: { fontSize: 10 },
// }}
// withInnerLines={true}
// withHorizontalLabels={true}
// withVerticalLabels={true}
// style={{ marginLeft: -10 }}
// yAxisInterval={20} // Trick to help scale till 100% cleanly
// />
// </View>
// );
//GIFTED CHART LIBRARY -----
case "BarGraph":
const chartWidth = screenWidth * 0.8;
const barWidth = 36;
const barColors = ['#0088FE', '#FF8042', '#00C49F'];
const chartWidth = screenWidth * 0.75;
const barWidth = 40;
const barColors = ['#1BF2E0', '#1B7BF2', '#1BC0F2'];
const barData = values.map((v, idx) => {
const value = parseFloat(Object.values(v).find(val => typeof val === 'number').toFixed(1));
@@ -612,7 +660,7 @@ const Dashboard = (props) => {
label: v.CalendarYear_Month,
frontColor: barColors[idx % barColors.length],
topLabelComponent: () => (
<Text style={{ color: '#000', fontSize: 10, marginBottom: 4 }}>{value}%</Text>
<Text style={{ color: '#000', fontSize: 12, fontWeight: '500', marginBottom: 5 }}>{value}%</Text>
)
};
});
@@ -626,7 +674,9 @@ const Dashboard = (props) => {
}
return (
<View style={{ alignItems: 'center', padding: 10, }}>
<View style={{ alignItems: 'center', justifyContent: 'center', padding: 10, borderWidth: 0.5, borderRadius: 10, }}>
{/* <View style={{ alignSelf: 'center', borderWidth: 0.5,borderRadius:20, paddingBottom: 10 }}> */}
<Text style={{ fontSize: 16, fontWeight: '600', marginBottom: 10 }}>
{item.GraphTitle}
</Text>
@@ -636,20 +686,37 @@ const Dashboard = (props) => {
barWidth={barWidth}
spacing={spacing}
noOfSections={5}
maxValue={100}
maxValue={105}
roundedTop={false}
disableScroll={true}
yAxisLabelTexts={['0%', '20%', '40%', '60%', '80%', '100%']}
// yAxisLabelTexts={['0%', '👉', '👉', '👉', '👉', '100%']} // 👈 only 0% and 100%
yAxisTextStyle={{ color: '#000', fontSize: 10 }}
xAxisLabelTextStyle={{ fontSize: 10, marginTop: 4 }}
yAxisTextStyle={{ color: '#000', fontSize: 12 }}
xAxisLabelTextStyle={{ color: '#000', fontSize: 12, fontWeight: '500', marginTop: 5 }}
isAnimated
width={chartWidth}
hideYAxisText={false}
hideYAxisText={true} //to hide y axis text
// hideRules={true}
hideYAxisLine={true}
yAxisThickness={0}
xAxisThickness={1}
rulesLength={chartWidth}
// xAxisLength={chartWidth}
hideAxesAndRules={true}
// backgroundColor={'red'}
backgroundColor={'yellow'}
// xAxisLabelTextStyle={{ color: '#000', fontSize: 10, marginTop: 4 }}
/>
{/* </View> */}
<View style={{height:10}} />
</View>
);
case "Table":
return (
<View style={{ flex: 1 }}>
@@ -721,36 +788,10 @@ const Dashboard = (props) => {
default:
return (
null
// <View style={[styles.percentBox, { backgroundColor: "#f8d7da", padding: 10, marginVertical: 5 }]}>
// <Text style={[styles.boxText, { color: "#721c24" }]}>
// Unsupported Graph Type: {item.GraphType}
// </Text>
// </View>
);
}
};
const fetchDetailGraphs = async (detailPages) => {
try {
const resultMap = {};
for (let item of detailPages) {
const response = await post(item.GraphUrl, {
parameters: {
projectid: 41654,
year: 2025,
monthno: 6,
storeid: 2702,
},
});
console.log(`Data for ${item.GraphTitle}:`, response?.data); // Add this
resultMap[item.GraphUrl] = response?.data || [];
}
setModalGraphData(resultMap);
} catch (error) {
console.log("❌ Error fetching detail graphs:", error);
}
};
// 🔧 Debounced search text handler (use lodash.debounce or implement custom debounce)
const debouncedSearch = useCallback(_.debounce((text) => {
@@ -896,7 +937,10 @@ const Dashboard = (props) => {
textstyle={styles.btntext}
onPress={() => {
if (storeData) {
getTabData(mainDisplayJson);
console.log("🔽 Triggered getTabData with filters:", { year, month });
setTimeout(() => {
getTabData(mainDisplayJson);
}, 50);
} else {
Alert.alert('Please select a store first.');
}
@@ -960,55 +1004,57 @@ const Dashboard = (props) => {
</View>
{/* Score Card */}
<View style={{ margin: 10 }}>
<View style={{ margin:5 }}>
{firstItem && (
<TouchableOpacity
activeOpacity={0.8}
onPress={() => {
if (firstItem.clickable === 1 && firstItem.DetailsPage?.length > 0) {
setSelectedDetails(firstItem.DetailsPage);
setShowDetailsModal(true);
fetchDetailGraphs(firstItem.DetailsPage);
}
}}
>
<View
style={[
styles.percentBox,
{
backgroundColor: firstItem.GraphBackground,
width: '100%',
elevation: 0,
},
]}
<View style={{ marginHorizontal: 5 }}>
{firstItem && (
<TouchableOpacity
activeOpacity={0.8}
onPress={() => {
if (firstItem.clickable === 1 && firstItem.DetailsPage?.length > 0) {
setSelectedDetails(firstItem.DetailsPage);
setShowDetailsModal(true);
fetchDetailGraphs(firstItem.DetailsPage);
}
}}
>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<View style={{ width: '80%', alignItems: 'center' }}>
<Text style={styles.boxText}>{firstItem.GraphTitle}</Text>
<Text
style={[
styles.boxText,
{ fontWeight: '500', fontSize: 24, marginTop: 10 },
]}
>
{(() => {
const values = graphApiData[firstItem.GraphUrl]?.data || [];
const score = Object.values(values[0] || {})[0];
return score !== undefined ? `${score.toFixed(1)}%` : '0.0%';
})()}
</Text>
</View>
<View>
{firstItem.clickable === 1 && (
<Image source={IMAGES.rightArrowIcon} style={{ tintColor: '#000', height: 20, width: 20, resizeMode: 'contain' }} />
)}
<View
style={[
styles.percentBox,
{
backgroundColor: "#C3D7FF",
width: '100%',
elevation: 0,
},
]}
>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<View style={{ width: '80%', alignItems: 'center' }}>
<Text style={styles.boxText}>{firstItem.GraphTitle}</Text>
<Text
style={[
styles.boxText,
{ fontWeight: '500', fontSize: 24, marginTop: 10 },
]}
>
{(() => {
const values = graphApiData[firstItem.GraphUrl]?.data || [];
const score = Object.values(values[0] || {})[0];
return score !== undefined ? `${score.toFixed(1)}%` : '0.0%';
})()}
</Text>
</View>
<View>
{firstItem.clickable === 1 && (
<Image source={IMAGES.rightArrowIcon} style={{ tintColor: '#000', height: 20, width: 20, resizeMode: 'contain' }} />
)}
</View>
</View>
</View>
</View>
</TouchableOpacity>
)}
</TouchableOpacity>
)}
</View>
<View style={{ marginHorizontal: 10 }}>
<FlatList
data={restItems}
keyExtractor={(item, index) => `${item.GraphId}-${index}`}
@@ -1018,6 +1064,7 @@ const Dashboard = (props) => {
contentContainerStyle={{ paddingHorizontal: 2, paddingVertical: 2 }}
showsVerticalScrollIndicator={false}
/>
</View>
</View>
<Loader visible={loading} />
@@ -1115,8 +1162,8 @@ const Dashboard = (props) => {
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
}}>
<Text style={{ color: '#000', fontWeight: 'bold', width: '80%' }}>{displayKey ? displayKey.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2') : 'Display'}</Text>
<Text style={{ color: '#000', fontWeight: 'bold', width: '20%', textAlign: 'center' }}>{presentKey ? presentKey.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2') : 'Present'}</Text>
<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={{
@@ -105,7 +105,7 @@
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/SOS_Actual_Perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {},
"clickable": 1,
"clickable": 1,
"DetailsPage": [
{
"TabId": 2,
@@ -158,7 +158,6 @@
"GraphOptions": {}
}
]
},
{
"TabId": 3,
@@ -331,7 +330,7 @@
"GraphType": "ScoreCard",
"GraphTitle": "PSS Score",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/PSS_Score_LSV_Perc",
"GraphBackground": "#F4EAFF",
"GraphBackground": "#E2C8FE",
"GraphOptions": {}
},
{
@@ -340,7 +339,7 @@
"GraphType": "ScoreCard",
"GraphTitle": "SOS Actual",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/sos_actual_lsv_perc",
"GraphBackground": "#FFF3ED",
"GraphBackground": "#E2C8FE",
"GraphOptions": {}
},
{
@@ -349,7 +348,7 @@
"GraphType": "ScoreCard",
"GraphTitle": "SOS Compliance",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/sos_compliance_lsv_perc",
"GraphBackground": "#FFFEF0",
"GraphBackground": "#FFD7C3",
"GraphOptions": {}
},
{
@@ -358,7 +357,7 @@
"GraphType": "ScoreCard",
"GraphTitle": "OSA",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/osa_lsv_perc",
"GraphBackground": "#ECFFFA",
"GraphBackground": "#FFF9A1",
"GraphOptions": {}
},
{
@@ -367,7 +366,7 @@
"GraphType": "ScoreCard",
"GraphTitle": "Asset",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/asset_lsv_perc",
"GraphBackground": "#EBECFF",
"GraphBackground": "#A2F3DE",
"GraphOptions": {}
},
{
@@ -376,7 +375,7 @@
"GraphType": "ScoreCard",
"GraphTitle": "Promotion",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/promotion_lsv_perc",
"GraphBackground": "#FFFEF0",
"GraphBackground": "#BFC2FF",
"GraphOptions": {}
},
{
@@ -387,7 +386,7 @@
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/sos_actual_lsv_perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {},
"clickable": 1,
"clickable": 1,
"DetailsPage": [
{
"TabId": 2,
@@ -494,11 +493,22 @@
{
"TabId": 5,
"GraphId": 6,
"GraphType": "Table",
"GraphType": "BarGraph",
"GraphTitle": "Asset Details",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/asset_trend_lsv_perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
"GraphBackground": "#fff",
"GraphOptions": {
"axisX": "month",
"axisY": "score",
"labelShow": 1,
"barColors": [
"#11a4ff",
"#0ea3e3",
"#0b9ddb"
],
"gridLinesH": 1,
"gridLinesV": 1
}
},
{
"TabId": 4,
@@ -524,7 +534,7 @@
{
"TabId": 4,
"GraphId": 8,
"GraphType": "PieChart",
"GraphType": "BarGraph",
"GraphTitle": "OSA",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/osa_trend_lsv_perc",
"GraphBackground": "#ECFFFA",