store dna all done

This commit is contained in:
CPM
2025-07-30 10:35:06 +05:30
commit b0399b39c6
157 changed files with 35444 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
import React, { useEffect, useState } from 'react';
import { View, Text, Image, Platform, ImageBackground } from 'react-native';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import IMAGES from '../../../constants/Images';
import { styles } from './style';
import { useDispatch } from 'react-redux';
import CustomTextInput from '../../../components/CustomTextInput';
import CustomButton from '../../../components/CustomButton';
import Loader from '../../../constants/Loader';
import { ApiURL } from '../../../api/ApiConstant';
import { toastError, toastSuccess } from '../../../constants/Toast';
import axios from 'axios';
import { SafeAreaView } from 'react-native-safe-area-context';
import { GlobalTheme } from '../../../theme';
import Geolocation from '@react-native-community/geolocation';
const Login = ({ navigation }) => {
const [loading, setLoading] = useState(false);
const dispatch = useDispatch();
const [username, setUsername] = useState('testah');
// geo loc
useEffect(() => {
Geolocation.getCurrentPosition(info => console.log("Location infoooo====>", JSON.stringify(info)));
}, [])
// end geo loc
const onSubmit = () => {
setLoading(true);
getOTP();
setTimeout(() => {
setLoading(false);
}, 100);
};
const getOTP = async () => {
try {
const params = {
"UserId": username
};
const config = {
method: 'post',
url: ApiURL.getotpApi,
headers: {
'api_key': '9a1f056fecb84eaf8eb4152dda22ab0501955c4f9bbe7daa8780740459fdde7a',
'Content-Type': 'application/json'
},
data: params
};
const response = await axios.request(config);
const res = response.data || [];
console.log('OTP is ===> ', res?.SendOTP);
if (res?.SendOTP[0].OTP === '0' || res?.SendOTP[0].OTP === 0) {
toastError("Alert", "Invalid User");
} else {
toastSuccess("Alert", res?.SendOTP[0]?.Messages);
navigation.navigate('VerifyOTP', { username: username });
}
// console.log('getotpApi res==>', JSON.stringify(res?.SendOTP[0]));
} catch (error) {
console.log("❌ OTP API error:", error);
}
};
return (
<SafeAreaView style={styles.container}>
<KeyboardAwareScrollView keyboardShouldPersistTaps="handled" enableOnAndroid={true} contentContainerStyle={{ justifyContent: 'center', flexGrow: 1, marginTop: 0 }} style={styles.container}>
<View style={{ backgroundColor: GlobalTheme.colors.primary, height: 40 }} />
<Image style={styles.appLogo} source={IMAGES.AuthTopBGNew} resizeMode='contain' />
<View style={styles.card}>
<Text style={styles.loginTitle}>Login</Text>
<CustomTextInput
label="Username"
placeholder="Enter username"
value={username}
onChangeText={setUsername}
containerStyle={styles.inputWrapper}
/>
<CustomButton onPress={() => onSubmit()} title={'Continue'} style={styles.btnbg} textstyle={styles.btntext} />
<ImageBackground source={IMAGES.AuthBottomBG} style={styles.AuthBottomBG} >
<Text style={{ position: 'absolute', textAlign: 'center', alignContent: 'center', alignSelf: 'center', bottom: 0, color: GlobalTheme.colors.gray }}> Copyright CPM India - 2025</Text>
{/* <Image source={IMAGES.Logo} style={styles.footerImage} resizeMode="contain"/> */}
</ImageBackground>
</View>
</KeyboardAwareScrollView>
<Loader visible={loading} loadingtext={'Loading ...'} />
</SafeAreaView>
);
};
export default Login;
+82
View File
@@ -0,0 +1,82 @@
import { StyleSheet, Dimensions } from 'react-native';
import { GlobalTheme } from '../../../theme';
import { normalize } from '../../../utilis/responsive';
const { width, height } = Dimensions.get('window');
export const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: GlobalTheme.colors.primary,
},
logoContainer: {
width: '100%',
},
appLogo: {
height: normalize(170),
width: '100%',
resizeMode: 'contain',
},
titleContainer: {
alignSelf: 'center',
marginBottom: normalize(10),
},
titleText: {
fontSize: normalize(20),
fontWeight: 'bold',
color: GlobalTheme.colors.black,
},
card: {
backgroundColor: GlobalTheme.colors.white,
borderTopLeftRadius: GlobalTheme.borderRadius.xxlg || normalize(20),
borderTopRightRadius: GlobalTheme.borderRadius.xxlg || normalize(20),
padding: normalize(20),
width: width,
alignSelf: 'center',
// marginTop: normalize(30),
shadowColor: '#000',
shadowOpacity: 0.05,
shadowOffset: { width: 0, height: 4 },
shadowRadius: 8,
elevation: 4,
flex: 1,
},
btnbg: {
backgroundColor: GlobalTheme.colors.secondary,
borderRadius: GlobalTheme.borderRadius.md,
marginTop: normalize(30),
paddingVertical: normalize(12),
alignItems: 'center',
},
btntext: {
color: GlobalTheme.colors.white,
fontSize: normalize(GlobalTheme.typography.fontSize.small),
fontWeight: GlobalTheme.typography.fontWeight.regular,
},
footer: {
marginTop: normalize(60),
alignItems: 'center',
},
footerImage: {
width: '100%',
height: normalize(80),
resizeMode: 'contain',
marginTop : normalize(160),
},
AuthBottomBG: {
width: '100%',
resizeMode: 'contain',
height: normalize(300),
justifyContent: 'center',
},
loginTitle: {
color: GlobalTheme.colors.black,
fontSize: normalize(GlobalTheme.typography.fontSize.large),
fontWeight: GlobalTheme.typography.fontWeight.medium,
textAlign: 'center',
marginBottom: normalize(20),
},
mainContainer: {
flex: 1,
backgroundColor: '#EAF0FF',
},
});
+95
View File
@@ -0,0 +1,95 @@
import { View, Text, Image, Dimensions, StyleSheet } from 'react-native';
import React, { useEffect } from 'react';
import IMAGES from '../../../constants/Images';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useDispatch } from 'react-redux';
import { setUser } from '../../../redux/slices/userSlice';
const SplashScreen = ({ navigation }) => {
const dispatch = useDispatch();
useEffect(() => {
const checkLoginStatus = async () => {
try {
const isuserlogin = await AsyncStorage.getItem('@Dabur_DNA_User');
console.log("isuserlogin", isuserlogin)
const parsedUser = JSON.parse(isuserlogin);
if (isuserlogin) {
dispatch(setUser(parsedUser));
navigation.reset({
index: 0,
routes: [{ name: 'Welcome' }],
});
} else {
navigation.reset({
index: 0,
routes: [{ name: 'Login' }],
});
}
} catch (error) {
console.error('Error checking login status:', error);
}
};
checkLoginStatus();
}, []);
return (
<View style={styles.container}>
<View style={styles.backgroundContainer}>
<Image
source={IMAGES.splashFullImg}
resizeMode="stretch"
style={styles.backdrop}
/>
</View>
<View style={styles.overlay}>
<Image style={styles.logo} source={IMAGES.AppLogo} />
</View>
</View>
);
};
export default SplashScreen;
const styles = StyleSheet.create({
backgroundContainer: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
},
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
overlay: {
opacity: 1,
alignItems: 'center',
justifyContent: 'center',
},
logo: {
backgroundColor: 'rgba(0,0,0,0)',
height: 200,
width: 200,
overflow: 'hidden',
resizeMode: 'contain',
marginTop: 5,
justifyContent: 'center',
},
backdrop: {
flex: 1,
width: '100%',
height: '100%',
},
headline: {
fontSize: 18,
textAlign: 'center',
backgroundColor: 'black',
color: 'white',
//borderWidth:1
},
});
+219
View File
@@ -0,0 +1,219 @@
import React, { useEffect, useState } from 'react';
import { View, Text, Image, TouchableOpacity, ImageBackground } from 'react-native';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import { useDispatch } from 'react-redux';
import { OtpInput } from "react-native-otp-entry";
import CustomButton from '../../../components/CustomButton';
import Background from '../../../components/Background';
import IMAGES from '../../../constants/Images';
import { GlobalTheme } from '../../../theme';
import { styles } from './style';
import { toastError, toastSuccess } from '../../../constants/Toast';
import Loader from '../../../constants/Loader';
import { ApiURL } from '../../../api/ApiConstant';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { setUser } from '../../../redux/slices/userSlice';
import axios from 'axios';
import { SafeAreaView } from 'react-native-safe-area-context';
const VerifyOTP = ({ navigation, route }) => {
const [loading, setLoading] = useState(false);
const dispatch = useDispatch();
const [otp, setOTP] = useState('');
const [timer, setTimer] = useState(300); // 5 minutes = 300 seconds
const [showResend, setShowResend] = useState(false);
const username = route.params.username;
useEffect(() => {
if (timer === 0) {
setShowResend(true);
return;
}
const interval = setInterval(() => {
setTimer(prev => prev - 1);
}, 1000);
return () => clearInterval(interval);
}, [timer]);
const formatTime = (secs) => {
const minutes = Math.floor(secs / 60);
const seconds = secs % 60;
return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
};
const handle_validate = () => {
if (otp.length < 6) {
toastError('Alert', "Please enter 6 digit PIN");
} else {
onSubmit();
}
}
const resendOTP = async () => {
try {
const params = {
"UserId": username
}
const config = {
method: 'post',
url: ApiURL.getotpApi,
headers: {
'api_key': '9a1f056fecb84eaf8eb4152dda22ab0501955c4f9bbe7daa8780740459fdde7a',
'Content-Type': 'application/json'
},
data: params
};
const response = await axios.request(config);
const res = response.data || [];
// console.log('storeSearchApi====>', res);
if (res?.SendOTP[0].OTP === '0' || res?.SendOTP[0].OTP === 0) {
toastError("Alert", res?.SendOTP[0]?.Messages)
} else {
toastSuccess("Alert", res?.SendOTP[0]?.Messages)
navigation.navigate('VerifyOTP', { username: username });
}
// console.log('getotpApi res==>', JSON.stringify(res?.SendOTP[0]));
// setLoading(false)
} catch (error) {
// setLoading(false)
console.log("❌ Filter API error:", error);
}
};
const resend_OTP = () => {
setTimer(300);
setShowResend(false);
toastSuccess('Resend OTP Successfully.')
resendOTP();
}
const onSubmit = () => {
setLoading(true)
VerifyOTP();
setTimeout(() => {
setLoading(false)
}, 100);
}
const VerifyOTP = async () => {
try {
const params = {
"UserId": username,
"OTP": otp
}
const config = {
method: 'post',
url: ApiURL.verifyotpApi,
headers: {
'api_key': '9a1f056fecb84eaf8eb4152dda22ab0501955c4f9bbe7daa8780740459fdde7a',
'Content-Type': 'application/json'
},
data: params
};
const response = await axios.request(config);
const res = response.data || [];
if (res?.AuthenticateOTP[0].Message == 'OTP is matched') {
toastSuccess("Alert", res?.AuthenticateOTP[0]?.Message || "Alert", "Login Successfully.")
await AsyncStorage.setItem('@Dabur_DNA_User', JSON.stringify(res?.AuthenticateOTP[0]));
dispatch(setUser(res?.AuthenticateOTP[0]));
navigation.reset({ index: 0, routes: [{ name: 'Welcome' }] })
} else {
toastError("Alert", res?.AuthenticateOTP[0]?.Message);
}
} catch (error) {
// setLoading(false)
console.log("❌ Filter API error:", error);
}
}
return (
<SafeAreaView style={styles.container}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<View style={styles.backTextView}>
<Image source={IMAGES.leftArrowIcon} style={styles.iconStyle} />
<Text style={styles.backIconText}>Back</Text>
</View>
</TouchableOpacity>
<KeyboardAwareScrollView keyboardShouldPersistTaps="handled" enableOnAndroid={true} style={styles.container} contentContainerStyle={{ justifyContent: 'center', flex: 1 }}>
<View style={{ backgroundColor: GlobalTheme.colors.primary, height: 40 }} />
<Image style={styles.appLogo} source={IMAGES.AuthTopBGNew} resizeMode='contain' />
<View style={styles.card}>
<View style={styles.titleContainer}>
<Text style={styles.titleText}>Enter the OTP sent to your {"\n"} registered contact</Text>
</View>
<View style={{ marginTop: 50 }}>
<OtpInput
numberOfDigits={6}
focusColor={GlobalTheme.colors.primary}
autoFocus={false}
hideStick={true}
placeholder=""
blurOnFilled={true}
disabled={false}
type="numeric"
secureTextEntry={false}
focusStickBlinkingDuration={500}
// onFocus={() => console.log("Focused")}
// onBlur={() => console.log("Blurred")}
onTextChange={(text) => setOTP(text)}
onFilled={(text) => {
setOTP(text);
console.log(`OTP is ${text}`);
}}
textInputProps={{
accessibilityLabel: "One-Time Password",
}}
textProps={{
accessibilityRole: "text",
accessibilityLabel: "OTP digit",
allowFontScaling: false,
}}
theme={{
containerStyle: styles.container,
pinCodeContainerStyle: styles.pinCodeContainer,
pinCodeTextStyle: styles.pinCodeText,
focusStickStyle: styles.focusStick,
focusedPinCodeContainerStyle: styles.activePinCodeContainer,
placeholderTextStyle: styles.placeholderText,
filledPinCodeContainerStyle: styles.filledPinCodeContainer,
disabledPinCodeContainerStyle: styles.disabledPinCodeContainer,
}}
/>
</View>
<View style={{ marginTop: 100}}>
<CustomButton onPress={handle_validate} title={'Verify'} style={styles.btnbg} textstyle={styles.btntext} />
</View>
<View style={{ alignItems: 'center', marginTop: 20 }}>
{!showResend ? (
<Text style={{ color: GlobalTheme.colors.darkGray }}>Resend OTP in {formatTime(timer)}</Text>
) : (
<TouchableOpacity onPress={resend_OTP}>
<Text style={styles.resendOTP} >
Resend OTP
</Text>
</TouchableOpacity>
)}
</View>
<ImageBackground source={IMAGES.AuthBottomBG} style={styles.AuthBottomBG} >
{/* <Image source={IMAGES.Logo} style={styles.footerImage} resizeMode="contain"/> */}
<Text style={{ position : 'absolute', textAlign:'center', alignContent:'center',alignSelf:'center', bottom:0 , color : GlobalTheme.colors.gray}}> Copyright CPM India - 2025</Text>
</ImageBackground>
</View>
</KeyboardAwareScrollView>
<Loader visible={loading} />
</SafeAreaView>
);
};
export default VerifyOTP;
+149
View File
@@ -0,0 +1,149 @@
import { Dimensions, StyleSheet } from 'react-native';
import { GlobalTheme, Screen } from '../../../theme';
import { normalize } from '../../../utilis/responsive';
const { width, height } = Dimensions.get('window');
export const styles = StyleSheet.create({
container: {
// flex: 1,
// paddingHorizontal: 5
flex: 1,
backgroundColor: GlobalTheme.colors.primary,
},
logoContainer: {
alignItems: 'center',
},
appLogo: {
height: normalize(170),
width: '100%',
resizeMode: 'contain',
marginTop: 40
},
titleContainer: {
alignSelf: 'center',
},
titleText: {
fontSize: 18,
fontWeight: '500',
color: GlobalTheme.colors.black,
textAlign: 'center'
},
btnbg: {
backgroundColor: GlobalTheme.colors.secondary, borderRadius: GlobalTheme.borderRadius.md
},
btntext: {
color: GlobalTheme.colors.white,
fontSize: GlobalTheme.typography.fontSize.medium
},
otp_inputStyle: {
textAlign: 'center',
backgroundColor: 'red',
width: 100,
borderRadius: 4,
paddingVertical: 10,
paddingHorizontal: 15,
height: 50,
color: 'red',
marginBottom: 10,
marginRight: 10,
fontSize: 25,
},
// OTP
pinCodeContainer: {
width: 45,
height: 55,
borderWidth: 1,
borderColor: '#D8E3F1',
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
activePinCodeContainer: {
borderColor: GlobalTheme.colors.primary,
borderWidth: 2,
},
filledPinCodeContainer: {
backgroundColor: '#D8E3F1',
},
disabledPinCodeContainer: {
backgroundColor: '#f0f0f0',
borderColor: '#ddd',
},
pinCodeText: {
fontSize: 20,
fontWeight: 'bold',
color: '#333',
},
focusStick: {
height: 2,
width: 20,
backgroundColor: GlobalTheme.colors.primary,
marginTop: 4,
},
placeholderText: {
color: '#aaa',
fontSize: 18,
},
resendText: {
color: GlobalTheme.colors.primary,
fontWeight: 'bold',
fontSize: 14,
marginTop: 20,
textAlign: 'center',
},
timerText: {
color: 'gray',
fontSize: 14,
marginTop: 20,
textAlign: 'center',
},
resendOTP: {
color: GlobalTheme.colors.secondary, fontWeight: GlobalTheme.typography.fontWeight.medium, fontSize: GlobalTheme.typography.fontSize.small
},
iconStyle: {
height: 20,
width: 20,
resizeMode: 'contain',
tintColor: GlobalTheme.colors.white
},
backIconText: { fontSize: GlobalTheme.typography.fontSize.small, color: GlobalTheme.colors.white, fontWeight: GlobalTheme.typography.fontWeight.medium, marginLeft: 8 },
backTextView: {
flexDirection: 'row', alignItems: 'center', paddingTop: 20, paddingHorizontal: 10
},
card: {
backgroundColor: GlobalTheme.colors.white,
borderTopLeftRadius: GlobalTheme.borderRadius.xxlg || normalize(20),
borderTopRightRadius: GlobalTheme.borderRadius.xxlg || normalize(20),
padding: normalize(20),
width: width,
alignSelf: 'center',
// marginTop: normalize(30),
shadowColor: '#000',
shadowOpacity: 0.05,
shadowOffset: { width: 0, height: 4 },
shadowRadius: 8,
elevation: 4,
flex: 1,
minHeight : height * 0.7
},
footer: {
marginTop: 60,
alignItems: 'center',
},
footerImage: {
width: '100%',
height: normalize(80),
resizeMode: 'contain',
marginTop: normalize(160),
},
AuthBottomBG: {
width: '100%',
resizeMode: 'contain',
height: normalize(270),
justifyContent: 'center',
marginTop: normalize(-40),
},
});
@@ -0,0 +1,209 @@
{
"subTabs": [
{
"TabId": 1,
"TabName": "PSS Score",
"TabRow": 1,
"TabCol": 1
},
{
"TabId": 2,
"TabName": "SOS Actual",
"TabRow": 1,
"TabCol": 2
},
{
"TabId": 3,
"TabName": "SOS Compliance",
"TabRow": 1,
"TabCol": 3
},
{
"TabId": 4,
"TabName": "OSA",
"TabRow": 1,
"TabCol": 4
},
{
"TabId": 5,
"TabName": "Asset",
"TabRow": 1,
"TabCol": 5
},
{
"TabId": 6,
"TabName": "Promotion",
"TabRow": 1,
"TabCol": 6
}
],
"graphDetails": [
{
"TabId": 1,
"GraphId": 1,
"GraphType": "ScoreCard",
"GraphTitle": "PSS Score",
"GraphUrl": "http://localhost:3000/MTD/PSSScore",
"GraphBackground": "#F4EAFF",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 2,
"GraphType": "ScoreCard",
"GraphTitle": "SOS Actual",
"GraphUrl": "http://localhost:3000/MTD/SOSActual",
"GraphBackground": "#FFF3ED",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 3,
"GraphType": "ScoreCard",
"GraphTitle": "SOS Compliance",
"GraphUrl": "http://localhost:3000/MTD/SOSCompliance",
"GraphBackground": "#FFFEF0",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 4,
"GraphType": "ScoreCard",
"GraphTitle": "OSA",
"GraphUrl": "http://localhost:3000/MTD/OSA",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 5,
"GraphType": "ScoreCard",
"GraphTitle": "Asset",
"GraphUrl": "http://localhost:3000/MTD/Asset",
"GraphBackground": "#EBECFF",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 6,
"GraphType": "ScoreCard",
"GraphTitle": "Promotion",
"GraphUrl": "http://localhost:3000/MTD/Promotion",
"GraphBackground": "#FFFEF0",
"GraphOptions": {}
},
{
"TabId": 2,
"GraphId": 1,
"GraphType": "ScoreCard",
"GraphTitle": "SOS Actual",
"GraphUrl": "http://localhost:3000/MTD/SOSActual",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 2,
"GraphId": 2,
"GraphType": "BarGraph",
"GraphTitle": "SOS Actual Trend",
"GraphUrl": "http://localhost:3000/MTD/SOSActualTrend",
"GraphBackground": "#F4EAFF",
"GraphOptions": {
"axisX": "month",
"axisY": "score",
"labelShow": 1,
"barColors": [
"#ffaa11",
"#ffbb11",
"#ffcc11"
],
"gridLinesH": 1,
"gridLinesV": 1
}
},
{
"TabId": 3,
"GraphId": 3,
"GraphType": "ScoreCard",
"GraphTitle": "SOS Compliance",
"GraphUrl": "http://localhost:3000/MTD/SOSActual",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 3,
"GraphId": 4,
"GraphType": "BarGraph",
"GraphTitle": "SOS Compliance Trend",
"GraphUrl": "http://localhost:3000/MTD/SOSActualTrend",
"GraphBackground": "#fff",
"GraphOptions": {
"axisX": "month",
"axisY": "score",
"labelShow": 1,
"barColors": [
"#ffaa11",
"#ffbb11",
"#ffcc11"
],
"gridLinesH": 1,
"gridLinesV": 1
}
},
{
"TabId": 5,
"GraphId": 5,
"GraphType": "ScoreCard",
"GraphTitle": "Asset",
"GraphUrl": "http://localhost:3000/MTD/SOSActual",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 5,
"GraphId": 6,
"GraphType": "Table",
"GraphTitle": "Asset Details",
"GraphUrl": "http://localhost:3000/MTD/SOSActual",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 4,
"GraphId": 7,
"GraphType": "ScoreCard",
"GraphTitle": "OSA",
"GraphUrl": "http://localhost:3000/MTD/SOSActual",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 4,
"GraphId": 8,
"GraphType": "PieChart",
"GraphTitle": "OSA",
"GraphUrl": "http://localhost:3000/MTD/SOSActual",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 6,
"GraphId": 9,
"GraphType": "ScoreCard",
"GraphTitle": "Promotion",
"GraphUrl": "http://localhost:3000/MTD/SOSActual",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 6,
"GraphId": 10,
"GraphType": "LineChart",
"GraphTitle": "Promotion",
"GraphUrl": "http://localhost:3000/MTD/SOSActual",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
}
@@ -0,0 +1,906 @@
import { View, Text, StyleSheet, Image, TouchableOpacity, Dimensions, TextInput, ScrollView, FlatList, Button, Alert, Modal, TouchableWithoutFeedback, StatusBar } from 'react-native'
import React, { useEffect, useRef, useState } from 'react'
import RBSheet from 'react-native-raw-bottom-sheet';
import { styles } from './style';
import LinearGradient from 'react-native-linear-gradient';
import CustomHeader from '../../../components/CustomHeader';
import IMAGES from '../../../constants/Images';
import CustomButton from '../../../components/CustomButton';
import CustomDropdown from '../../../components/CustomDropdown';
import { GlobalTheme } from '../../../theme';
import { SafeAreaView } from 'react-native-safe-area-context';
import displayData from './display.json'
import { BarChart, LineChart, PieChart } from 'react-native-chart-kit';
import { getDownloadJson } from '../../../constants/function';
import db, { getAllFromTable } from '../../../constants/database';
import { post } from '../../../api/ApiService';
import { ApiURL } from '../../../api/ApiConstant';
import mainDisplayJson from './mainDisplay.json'
const Dashboard = (props) => {
const { navigation } = props;
const screenHeight = Dimensions.get('screen').height;
const screenWidth = Dimensions.get('screen').width;
const refRBSheet = useRef();
const [isStoreSelected, setIsStoreSelected] = useState(false)
const [searchResult, setSearchResult] = useState(false)
const [storeData, setStoreData] = useState({})
const [mainTab, setMainTab] = useState('mtd')
const [activeTab, setActiveTab] = useState(displayData?.subTabs?.[0]?.TabId || 1)
const [state, setState] = useState(null);
const [year, setYear] = useState(null)
const [month, setMonth] = useState(null)
const [city, setCity] = useState(null);
const [asmArea, setAsmArea] = useState(null);
const scrollViewRef = useRef(null);
const [showButton, setShowButton] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
// Handle scroll event to show/hide button
const handleScroll = (event) => {
const offsetY = event.nativeEvent.contentOffset.y;
setShowButton(offsetY > 0); // Show button only if not at the top
};
const openBottomSheet = () => {
refRBSheet.current.open()
}
const onSelectStore = (item) => {
setStoreData(item)
setIsStoreSelected(true)
refRBSheet.current.close()
}
const data = [
{ label: 'Delhi', value: '1' },
{ label: 'Mumbai', value: '2' },
];
const cityData = [
{ label: 'Okhla', value: '1' },
{ label: 'Kalkaji', value: '2' },
{ label: 'South Ex.', value: '3' },
];
const storeJson = [
{
id: 1,
name: "Banarasi & Sons",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "pending"
},
{
id: 2,
name: "Gupta store & Sons",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
{
id: 3,
name: "Bansal General store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
{
id: 4,
name: "Mohan Mahalaxmi store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
{
id: 5,
name: "Chawla Store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
{
id: 6,
name: "Chawla Store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
{
id: 7,
name: "Chawla Store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
{
id: 8,
name: "Chawla Store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
{
id: 9,
name: "Chawla Store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
];
const visitedStoreData = [
{
id: 1,
name: "Chawla Store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "pending"
},
{
id: 2,
name: "Mohan Mahalaxmi store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
{
id: 3,
name: "Bansal General store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
{
id: 4,
name: "Bansal General store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
{
id: 5,
name: "Bansal General store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
{
id: 6,
name: "Bansal General store",
address: "G-8, Mahakavi Goswami Tulsidas Marg, Paraag Vihar, Press Colony, Hari Nagar, New Delhi, 110027",
status: "completed"
},
];
const monthData = [
{ label: 'January', value: '1' },
{ label: 'February', value: '2' },
{ label: 'March', value: '3' },
{ label: 'April', value: '4' },
{ label: 'May', value: '5' },
{ label: 'June', value: '6' },
{ label: 'July', value: '7' },
{ label: 'August', value: '8' },
{ label: 'September', value: '9' },
{ label: 'October', value: '10' },
{ label: 'November', value: '11' },
{ label: 'December', value: '12' },
];
const yearData = [
{ label: '2025', value: '2025' },
{ label: '2024', value: '2024' },
{ label: '2023', value: '2023' },
{ label: '2022', value: '2022' },
{ label: '2021', value: '2021' },
{ label: '2020', value: '2020' },
{ label: '2019', value: '2019' },
{ label: '2018', value: '2018' },
{ label: '2017', value: '2017' },
{ label: '2016', value: '2016' },
{ label: '2015', value: '2015' },
];
const assetData = [
{
"section": "Asset%",
"data": [
{ "display": "Real Endcap", "present": "No" },
{ "display": "Honey Parasite", "present": "Yes" },
{ "display": "Odonil Floor Stack", "present": "YHes" }
]
},
{
"section": "Additional Visibility",
"data": [
{ "display": "Active 1 Ltr Endcap", "present": "Yes" },
{ "display": "Chyawanprash Stack", "present": "Yes" }
]
},
{
"section": "Promotion",
"data": [
{ "display": "Activ 100% Juice", "present": "Yes" },
{ "display": "Airfresher", "present": "No" },
{ "display": "BABY CARE", "present": "No" },
{ "display": "Chyawanprash", "present": "No" },
{ "display": "Hair Oil", "present": "No" },
]
}
]
const PieData = [
{
name: "Seoul",
population: 21500000,
color: "rgba(131, 167, 234, 1)",
legendFontColor: "#7F7F7F",
legendFontSize: 15
},
{
name: "Toronto",
population: 2800000,
color: "#F00",
legendFontColor: "#7F7F7F",
legendFontSize: 15
},
{
name: "Beijing",
population: 527612,
color: "red",
legendFontColor: "#7F7F7F",
legendFontSize: 15
},
{
name: "New York",
population: 8538000,
color: "#ffffff",
legendFontColor: "#7F7F7F",
legendFontSize: 15
},
{
name: "Moscow",
population: 11920000,
color: "rgb(0, 0, 255)",
legendFontColor: "#7F7F7F",
legendFontSize: 15
}
];
const barData = {
labels: ["Jan", "Feb", "March", "April",],
datasets: [
{
data: [20, 45, 28, 80]
}
]
};
const chartConfig = {
backgroundColor: '#ffffff',
backgroundGradientFrom: '#ffffff',
backgroundGradientTo: '#ffffff',
fillShadowGradientFrom: '#FF8C61', // For bar colors
fillShadowGradientTo: '#FF8C61',
fillShadowGradientFromOpacity: 1,
fillShadowGradientToOpacity: 1,
decimalPlaces: 0,
color: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`, // Text color
labelColor: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
// barPercentage: 0.5,
barPercentage: 1,
};
const onSelectSubTab = (item) => {
setActiveTab(item?.TabId)
}
const filteredGraphs = displayData?.graphDetails?.filter(graph => graph.TabId === activeTab);
const firstItem = filteredGraphs?.[0];
const restItems = filteredGraphs?.slice(1);
const renderItem = ({ item }) => {
switch (item.GraphType) {
case "ScoreCard":
return (
<View style={[styles.percentBox, { backgroundColor: item.GraphBackground }]}>
<Text style={styles.boxText}>{item.GraphTitle}</Text>
<Text style={[styles.boxText, { fontWeight: '500', fontSize: 24, marginTop: 10 }]}>{"45%"}</Text>
</View>
);
case "BarGraph":
const barData = {
labels: ["Jan", "Feb", "Mar", "Apr"],
datasets: [{ data: [35, 45, 20, 55] }]
};
return (
<View style={{ padding: 10, backgroundColor: '', borderRadius: 8 }}>
<Text style={{ fontSize: 16, fontWeight: '600', marginBottom: 10 }}>
SOS Compliance Trend
</Text>
<BarChart
data={barData}
width={screenWidth - 40}
height={220}
yAxisSuffix="%"
chartConfig={chartConfig}
fromZero
showValuesOnTopOfBars
withInnerLines
/>
</View>
);
case "Table":
return (
<View style={{ flex: 1, }}>
<ScrollView>
<View style={{ flexDirection: 'row', borderWidth: 1, borderColor: '#EAEAEA', alignItems: 'center', alignSelf: 'flex-start', padding: 5, borderRadius: 15 }}>
<TouchableOpacity
style={[styles.subTab, { backgroundColor: '#113F8C' }]}>
<Text style={[styles.tabText, { color: "#fff" }]}>{'Asset'}</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.subTab, { backgroundColor: '#113F8C' }]}>
<Text style={[styles.tabText, { color: "#fff" }]}>{'Asset Details'}</Text>
</TouchableOpacity>
</View>
{assetData && assetData.map((table, index) => (
<View key={index} style={{ marginTop: 10 }}>
<Text style={{ fontSize: 16, color: '#000', fontWeight: '500' }}>{table.section}</Text>
<View style={{ marginTop: 5, backgroundColor: '#fff', elevation: 5, padding: 10, borderRadius: 10 }}>
<View style={{ flexDirection: 'row', borderBottomWidth: 1, borderColor: '#E0E0E0', paddingBottom: 7, }}>
<Text style={{ width: '50%', color: '#676767', fontSize: 14 }}>Display</Text>
<Text style={{ width: '50%', color: '#676767', fontSize: 14 }}>Present</Text>
</View>
{
table && table.data.map((item) => (
<View style={{ marginTop: 3, flexDirection: 'row', paddingBottom: 7, borderBottomWidth: 1, borderColor: '#E0E0E0', }}>
<Text style={{ width: '50%', color: '#000', fontSize: 14, }}>{item.display}</Text>
<Text style={{ width: '50%', color: '#000', fontSize: 14, }}>{item?.present}</Text>
</View>
))}
</View>
</View>
))}
<View style={{ marginBottom: 500 }} />
</ScrollView>
</View>
)
case "LineChart":
return (
<View style={{ flex: 1, }}>
<Text>Bezier Line Chart</Text>
<LineChart
data={{
labels: ["January", "February", "March", "April", "May", "June"],
datasets: [
{
data: [
Math.random() * 100,
Math.random() * 100,
Math.random() * 100,
Math.random() * 100,
Math.random() * 100,
Math.random() * 100
]
}
]
}}
width={Dimensions.get("window").width} // from react-native
height={220}
yAxisLabel="$"
yAxisSuffix="k"
yAxisInterval={1} // optional, defaults to 1
chartConfig={{
backgroundColor: "#e26a00",
backgroundGradientFrom: "lgray",
backgroundGradientTo: "lightblue",
decimalPlaces: 2, // optional, defaults to 2dp
color: (opacity = 1) => `rgba(255, 255, 255, ${opacity})`,
labelColor: (opacity = 1) => `rgba(255, 255, 255, ${opacity})`,
style: {
borderRadius: 16
},
propsForDots: {
r: "6",
strokeWidth: "2",
stroke: "#ffa726"
}
}}
bezier
style={{
marginVertical: 8,
borderRadius: 16
}}
/>
</View>
)
case "PieChart":
return (
<View style={{ flex: 1, }}>
<PieChart
data={PieData}
width={screenWidth}
height={220}
chartConfig={chartConfig}
accessor={"population"}
backgroundColor={"transparent"}
paddingLeft={"15"}
center={[10, 50]}
absolute
/>
</View>
)
default:
return (
<View style={[styles.percentBox, { backgroundColor: "#f8d7da", padding: 10, marginVertical: 5 }]}>
<Text style={[styles.boxText, { color: "#721c24" }]}>
Unsupported Graph Type: {item.GraphType}
</Text>
</View>
);
}
};
// download data
useEffect(() => {
const init = async () => {
await getData(); // <-- Only dynamic live data
};
init();
getTabData()
}, []);
// Insert fallback mock data if empty
const insertMasterSurveyQuestion = (data_arr = []) => {
return new Promise((resolve, reject) => {
if (!Array.isArray(data_arr) || data_arr.length === 0) {
resolve(true);
return;
}
const escape = str => (str || '').toString().replace(/'/g, "''");
const values = data_arr.map(item => {
const {
MenuId, SurveyId, SurveyName, CategoryId, Category, CategorySequence,
QuestionId, Question, QuestionType, QuestionImageAllow, QEnable,
LengthValidation, MinLength, MaxLength, OTP, DateRange, QuestionSequence,
AnswerId, Answer, ImageAllow1, ImageAllow2, Image1Mandatory, Image2Mandatory,
QuestionImageMandatory, EnableQuestion, DisableQuestion, AnswerSequence,
ShowCat, SubCategoryId, SubCategory, SubCategorySequence,
QuestionTypeNew, CalFormula, RefImage, QuestionRefImage
} = item;
return `(
'${MenuId}', '${SurveyId}', '${escape(SurveyName)}', '${CategoryId}', '${escape(Category)}',
'${CategorySequence}', '${QuestionId}', '${escape(Question)}', '${escape(QuestionType)}',
'${QuestionImageAllow ? 1 : 0}', '${QEnable ? 1 : 0}', '${LengthValidation ? 1 : 0}',
'${MinLength}', '${MaxLength}', '${OTP ? 1 : 0}', '${escape(DateRange)}',
'${QuestionSequence}', '${AnswerId}', '${escape(Answer)}',
'${ImageAllow1 ? 1 : 0}', '${ImageAllow2 ? 1 : 0}', '${Image1Mandatory ? 1 : 0}',
'${Image2Mandatory ? 1 : 0}', '${QuestionImageMandatory ? 1 : 0}',
'${escape(EnableQuestion)}', '${escape(DisableQuestion)}', '${AnswerSequence}',
'${ShowCat ? 1 : 0}', '${SubCategoryId}', '${escape(SubCategory)}',
'${SubCategorySequence}', '${escape(QuestionTypeNew)}', '${escape(CalFormula)}',
'${escape(RefImage)}', '${escape(QuestionRefImage)}'
)`;
});
const sql = `
INSERT INTO FeedbackCategory (
MenuId, SurveyId, SurveyName, CategoryId, Category, CategorySequence,
QuestionId, Question, QuestionType, QuestionImageAllow, QEnable,
LengthValidation, MinLength, MaxLength, OTP, DateRange, QuestionSequence,
AnswerId, Answer, ImageAllow1, ImageAllow2, Image1Mandatory, Image2Mandatory,
QuestionImageMandatory, EnableQuestion, DisableQuestion, AnswerSequence,
ShowCat, SubCategoryId, SubCategory, SubCategorySequence,
QuestionTypeNew, CalFormula, RefImage,QuestionRefImage
) VALUES ${values.join(',')}
`;
db.transaction(tx => {
tx.executeSql(`DELETE FROM FeedbackCategory`, [], () => {
tx.executeSql(sql, [], () => {
console.log('FeedbackCategory inserted successfully');
resolve(true);
}, (err) => {
console.log('Insert error in FeedbackCategory:', err);
reject(err);
});
}, (err) => {
console.log('Delete error before insert FeedbackCategory:', err);
reject(err);
});
});
});
};
async function getData() {
try {
const data1 = {
Downloadtype: "Master_SurveyQuestion",
Param1: "",
Param2: "",
username: "testmer",
};
const url = "https://di1.parinaam.in/Webservice/GenericService.svc/DownloadJson";
console.log("Downloading from:", url, data1);
const res = await getDownloadJson(url, data1);
console.log('Response:', res);
if (res?.Master_SurveyQuestion?.length) {
const data = res.Master_SurveyQuestion || [];
console.log("Inserting downloaded data into FeedbackCategory...");
const all = await getAllFromTable('FeedbackCategory');
if (all?.length === 0) {
console.log("Table empty. Inserting fallback mock data...");
await insertMasterSurveyQuestion(data);
} else {
console.log("FeedbackCategory table already has data.");
}
} else {
console.log("No Master_SurveyQuestion data found in response");
}
} catch (err) {
console.log("Dashboard feedback survey download error:", err);
}
}
// end download data.
const getTabData = () => {
let params = {
"parameters": {
"projectid": 41654,
"year": 2025,
"monthno": 6,
"storeid": 2702
}
}
post(ApiURL.pssscoreApi, params)
.then(res => {
console.log('psscoreApi res==>', res);
})
}
return (
<SafeAreaView style={{ flex: 1, backgroundColor: GlobalTheme.colors.primary }}>
<View style={{ flex: 1, backgroundColor: '#fff' }}>
{!isStoreSelected ?
<CustomHeader
title=" Dashboard"
rightIcon={IMAGES.menuIcon}
onRightPress={() => setModalVisible(true)}
/>
:
<CustomHeader
title="Dashboard"
leftIcon={IMAGES.backIcon}
onLeftPress={() => setIsStoreSelected(false)}
rightIcon={IMAGES.menuIcon}
onRightPress={() => setModalVisible(true)}
/>
}
{/* MAIN DASHBOARD */}
{!isStoreSelected ? (
<View style={{ flex: 1 }} >
<TouchableOpacity onPress={() => openBottomSheet()} activeOpacity={0.8} style={styles.selectCard}>
<Image source={IMAGES.reportIcon} style={styles.iconStyle} />
<Text style={styles.storeText}> Select Store:</Text>
<Image source={IMAGES.downIcon} style={{ height: 20, width: 20, resizeMode: 'contain' }} />
</TouchableOpacity>
<Text style={styles.selectStoreText}>Select a store to view it's data.</Text>
<LinearGradient
colors={['#D8E7FF', '#FFFFFF',]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.storeGredient}>
<View style={styles.todayStoreCard}>
<View style={{ marginLeft: 5, flexDirection: 'row', padding: 10, alignItems: 'center' }}>
<View style={{ backgroundColor: '#E3EBF8', padding: 5, borderRadius: 7 }}>
<Image source={IMAGES.storeIcon} style={{ height: 18, width: 18 }} />
</View>
<Text style={styles.todayStoreText}>Today's store</Text>
</View>
<View style={{ borderBottomWidth: 1.5, borderColor: '#E3EBF8' }} />
<View style={[styles.row, { paddingHorizontal: 10 }]}>
<View style={{ width: '48%', alignItems: 'center' }}>
<Text style={{ color: '#000', fontSize: 20, fontWeight: 'bold' }}>1</Text>
<Text style={{ color: '#808CA3', fontSize: 15, fontWeight: '400' }}>View</Text>
</View>
<View style={{ borderWidth: 1, height: 50, borderColor: '#E3EBF8' }} />
<View style={{ width: '48%', alignItems: 'center' }}>
<Text style={{ color: '#000', fontSize: 20, fontWeight: 'bold', textAlign: 'right' }}>2</Text>
<Text style={{ color: '#808CA3', fontSize: 15, fontWeight: '400', textAlign: 'right' }}>Feedback</Text>
</View>
</View>
<ScrollView showsVerticalScrollIndicator={false}>
<View style={{ marginTop: 5, marginHorizontal: 10 }}>
{visitedStoreData && visitedStoreData.map(store => (
<TouchableOpacity onPress={() => navigation.navigate("FeedbackCategories")}
key={store.id} style={styles.storeCard}>
<View style={[styles.row, { margin: 0 }]}>
<Text style={styles.cardTextBold}>{store.name}</Text>
{store?.status === 'completed' ?
<Image source={IMAGES.greenTick} style={{ height: 20, width: 20 }} />
:
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<View style={{ backgroundColor: '#FFF8CD', padding: 5, paddingHorizontal: 7, borderRadius: 15 }}>
<Text style={{ color: 'orange', fontSize: 13, fontWeight: '500' }}>Pending</Text>
</View>
<Image source={IMAGES.rightArrowIcon} style={{ marginLeft: 10, height: 20, width: 20, resizeMode: 'contain', tintColor: '#97ADD6' }} />
</View>
}
</View>
<Text style={styles.cardText}>{store.address}</Text>
</TouchableOpacity>
))}
</View>
</ScrollView>
</View>
</LinearGradient>
</View>
) : (
// STORE DASHBOARD
<View style={styles.mainContainer}>
<View style={styles.row}>
<View style={styles.selectedStoreText}>
<Text style={styles.storeNameText}> {storeData?.name}</Text>
<TouchableOpacity onPress={() => openBottomSheet()} style={styles.filterIcon}>
<Image source={IMAGES.filterIcon} style={{ height: 18, width: 18 }} />
</TouchableOpacity>
</View>
<TouchableOpacity onPress={() => navigation.navigate('StoreInfo')} style={{ width: '25%', alignItems: 'flex-end' }}>
<Text style={styles.storeInfoText}>Store info</Text>
</TouchableOpacity>
</View>
{/* Year & Month Selector */}
<View style={{ flexDirection: 'row', alignItems: 'center', marginHorizontal: 10, marginTop: 5 }}>
<View style={{ width: 100, }}>
<CustomDropdown
data={yearData}
value={year}
placeholder='Year'
onChange={item => setYear(item.value)}
containerStyle={styles.yearDropDown}
/>
</View>
<View style={{ width: 120, marginHorizontal: 10, }}>
<CustomDropdown
data={monthData}
value={month}
placeholder='Month'
onChange={item => setMonth(item.value)}
containerStyle={[styles.yearDropDown, { width: 120 }]}
/>
</View>
<CustomButton title={'Show'}
style={styles.btnbg}
textstyle={styles.btntext}
/>
</View>
<View style={styles.seperator} />
{/* Main Tab */}
<View style={{ flexDirection: 'row' }}>
<TouchableOpacity onPress={() => setMainTab('mtd')}
style={[styles.mtdTab, { borderBottomWidth: mainTab === 'mtd' ? 3 : 0, backgroundColor: mainTab === 'mtd' ? '#EFF6FF' : '#fff' }]} >
<Text style={[styles.mtdTabText, { color: mainTab === 'mtd' ? '#113F8C' : '#000', }]}>MTD</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => setMainTab('lastVisit')}
style={[styles.mtdTab, { borderBottomWidth: mainTab === 'lastVisit' ? 3 : 0, backgroundColor: mainTab === 'lastVisit' ? '#EFF6FF' : '#fff' }]} >
<Text style={[styles.mtdTabText, { color: mainTab === 'lastVisit' ? '#113F8C' : '#000' }]}>Last Visit</Text>
</TouchableOpacity>
</View>
{/* <View style={{ flexDirection: 'row' }}>
{mainDisplayJson && mainDisplayJson?.Tabs.map((item, index) => (
<TouchableOpacity onPress={() => setMainTab('mtd')}
style={[styles.mtdTab, { borderBottomWidth: mainTab === 'mtd' ? 3 : 0, backgroundColor: mainTab === 'mtd' ? '#EFF6FF' : '#fff' }]} >
<Text style={[styles.mtdTabText, { color: mainTab === 'mtd' ? '#113F8C' : '#000', }]}>MTD</Text>
</TouchableOpacity>
))}
</View> */}
{/* Sub Tab */}
<View style={styles.subTabView}>
<FlatList
data={displayData?.subTabs}
horizontal
keyExtractor={(item) => item.TabId}
showsHorizontalScrollIndicator={false}
contentContainerStyle={{ paddingHorizontal: 10 }}
renderItem={({ item }) => {
const isSelected = activeTab === item.TabId;
return (
<TouchableOpacity onPress={() => onSelectSubTab(item)}
style={[styles.subTab, { backgroundColor: isSelected ? '#113F8C' : '#fff' }]}>
<Text style={[styles.tabText, { color: isSelected ? "#fff" : "#000" }]}>{item.TabName}</Text>
</TouchableOpacity>
)
}}
/>
</View>
{/* Boxes */}
<View style={{ margin: 10 }}>
{/* First full-width item */}
{firstItem && (
<View style={[styles.percentBox, { backgroundColor: firstItem.GraphBackground, width: '100%', elevation: 0 },]}>
<Text style={styles.boxText}>{firstItem.GraphTitle}</Text>
<Text style={[styles.boxText, { fontWeight: '500', fontSize: 24, marginTop: 10 }]}>{"45%"}</Text>
</View>
)}
<FlatList
data={restItems}
keyExtractor={(item, index) => `${item.GraphId}-${index}`}
renderItem={renderItem}
numColumns={2}
columnWrapperStyle={{ justifyContent: 'space-between', marginTop: 10 }}
contentContainerStyle={{ paddingHorizontal: 2, paddingVertical: 2 }}
showsVerticalScrollIndicator={false}
/>
</View>
</View>
)}
{/* Pop-up Modal */}
<Modal transparent visible={modalVisible} animationType="fade" >
<TouchableOpacity style={styles.modalOverlay} activeOpacity={0.1} onPress={() => setModalVisible(false)}>
<View style={styles.modalContainer}>
<TouchableOpacity onPress={() => { navigation.navigate('FeedbackCategories'), setModalVisible(false) }} style={{ padding: 15 }}>
<Text style={{}}>Feedback and Rating</Text>
</TouchableOpacity>
<View style={{ borderBottomWidth: 1, borderColor: '#D8E3F1' }} />
<TouchableOpacity onPress={() => setModalVisible(false)} style={{ padding: 15 }}>
<Text style={{}}>Dashboard</Text>
</TouchableOpacity>
</View>
</TouchableOpacity>
</Modal>
{/* Bottom Sheet */}
<RBSheet
ref={refRBSheet}
useNativeDriver={true}
customStyles={{
wrapper: {
backgroundColor: 'rgba(0,0,0,0.2)',
},
container: {
height: '86%',
backgroundColor: '#fff',
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
paddingBottom: 10,
},
draggableIcon: {
backgroundColor: 'gray',
},
}}
customModalProps={{
animationType: 'slide',
statusBarTranslucent: true,
}}
customAvoidingViewProps={{
enabled: false,
}}>
<View style={{ flex: 1, }}>
<View style={styles.header}>
<Text style={{ color: '#000', fontWeight: '500', fontSize: 16 }}>Select Store</Text>
<TouchableOpacity onPress={() => refRBSheet.current.close()}>
<Image source={IMAGES.crossIcon} style={styles.iconStyle} />
</TouchableOpacity>
</View>
<View style={{ marginVertical: 0, borderBottomWidth: 1, borderColor: '#ccc' }} />
<ScrollView showsVerticalScrollIndicator={false}
ref={scrollViewRef}
onScroll={handleScroll} >
<View style={{ flex: 1, margin: 15, }}>
<View style={[styles.row, {}]}>
<Text style={styles.dropHeaderText}>State</Text>
<Text style={styles.dropHeaderText}> City</Text>
<Text style={styles.dropHeaderText}> ASM Area</Text>
</View>
{/* Dropdown */}
<View style={{ marginTop: 5, flexDirection: 'row', alignItems: 'center' }}>
<CustomDropdown
data={data}
value={state}
onChange={item => setState(item.value)}
containerStyle={{ flex: 1, }}
/>
<CustomDropdown
data={cityData}
value={city}
onChange={item => setCity(item.value)}
containerStyle={{ flex: 1, marginHorizontal: 10 }}
/>
<CustomDropdown
data={cityData}
value={asmArea}
onChange={item => setAsmArea(item.value)}
containerStyle={{ flex: 1, }}
/>
</View>
<View style={[styles.searchBox, { marginTop: 20 }]}>
{/* <Image source={IMAGES.searchIcon} style={{ height: 15, width: 15 }} /> */}
<TextInput
style={styles.inputStyle}
placeholder='Search store...'
/>
</View>
<View style={{ marginTop: 20 }}>
<CustomButton title={'Apply'} onPress={() => setSearchResult(!searchResult)}
style={{ backgroundColor: GlobalTheme.colors.secondary, borderRadius: GlobalTheme.borderRadius.md }}
textstyle={{ color: GlobalTheme.colors.white, fontSize: GlobalTheme.typography.fontSize.medium }}
/>
</View>
{searchResult ?
<View style={{ marginBottom: 70 }}>
<View style={{ marginTop: 20 }}>
<Text style={{ color: '#000', fontSize: 14 }}>11 Store</Text>
</View>
{storeJson && storeJson.map(store => (
<TouchableOpacity onPress={() => onSelectStore(store)}
key={store.id} style={styles.storeCard}>
<Text style={styles.cardTextBold}>{store.name}</Text>
<Text style={styles.cardText}>{store.address}</Text>
</TouchableOpacity>
))}
</View>
: null
}
</View>
</ScrollView>
{showButton && (
<TouchableOpacity style={styles.floatingBtn} onPress={() => { scrollViewRef.current?.scrollTo({ y: 0, animated: true }) }}>
<Image source={IMAGES.upArrow} style={{ height: 15, width: 15 }} />
</TouchableOpacity>
)}
</View>
</RBSheet>
</View>
</SafeAreaView>
)
}
export default Dashboard
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,567 @@
{
"Tabs": [
{
"MainTabId": 1,
"MainTabName": "MTD",
"MainTabData": {
"subTabs": [
{
"TabId": 1,
"TabName": "PSS Score",
"TabRow": 1,
"TabCol": 1
},
{
"TabId": 2,
"TabName": "SOS Actual",
"TabRow": 1,
"TabCol": 2
},
{
"TabId": 3,
"TabName": "SOS Compliance",
"TabRow": 1,
"TabCol": 3
},
{
"TabId": 4,
"TabName": "OSA",
"TabRow": 1,
"TabCol": 4
},
{
"TabId": 5,
"TabName": "Asset",
"TabRow": 1,
"TabCol": 5
},
{
"TabId": 6,
"TabName": "Promotion",
"TabRow": 1,
"TabCol": 6
}
],
"graphDetails": [
{
"TabId": 1,
"GraphId": 1,
"GraphType": "ScoreCard",
"GraphTitle": "PSS Score",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/pssscore",
"GraphBackground": "#C3D7FF",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 2,
"GraphType": "ScoreCard",
"GraphTitle": "SOS Actual",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/SOS_Actual_Perc",
"GraphBackground": "#E2C8FE",
"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,
"GraphId": 4,
"GraphType": "ScoreCard",
"GraphTitle": "OSA",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/OSA_Perc",
"GraphBackground": "#FFF9A1",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 5,
"GraphType": "ScoreCard",
"GraphTitle": "Asset",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/Asset_Perc",
"GraphBackground": "#A2F3DE",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 6,
"GraphType": "ScoreCard",
"GraphTitle": "Promotion",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/Promotion_Perc",
"GraphBackground": "#BFC2FF",
"GraphOptions": {}
},
{
"TabId": 2,
"GraphId": 1,
"GraphType": "ScoreCard",
"GraphTitle": "SOS Actual",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/SOS_Actual_Perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {},
"clickable": 1,
"DetailsPage": [
{
"TabId": 2,
"GraphId": 1,
"GraphType": "Table",
"GraphTitle": "SOS - Category",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/sos_actual_perc_on_category",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
},
{
"TabId": 2,
"GraphId": 2,
"GraphType": "BarGraph",
"GraphTitle": "SOS Actual Trend",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/sos_actual_trend_perc_mtd",
"GraphBackground": "#F4EAFF",
"GraphOptions": {
"axisX": "month",
"axisY": "score",
"labelShow": 1,
"barColors": [
"#11a4ff",
"#0ea3e3",
"#0b9ddb"
],
"gridLinesH": 0,
"gridLinesV": 0
}
},
{
"TabId": 3,
"GraphId": 3,
"GraphType": "ScoreCard",
"GraphTitle": "SOS Compliance",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/SOS_Compliance_Perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {},
"clickable": 1,
"DetailsPage": [
{
"TabId": 3,
"GraphId": 3,
"GraphType": "Table",
"GraphTitle": "SOS Compliance - Category",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/sos_sompliance_perc_on_category",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
},
{
"TabId": 3,
"GraphId": 4,
"GraphType": "BarGraph",
"GraphTitle": "SOS Compliance Trend",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/sos_compliance_trend_perc_mtd",
"GraphBackground": "#fff",
"GraphOptions": {
"axisX": "month",
"axisY": "score",
"labelShow": 1,
"barColors": [
"#11a4ff",
"#0ea3e3",
"#0b9ddb"
],
"gridLinesH": 1,
"gridLinesV": 1
}
},
{
"TabId": 5,
"GraphId": 5,
"GraphType": "ScoreCard",
"GraphTitle": "Asset",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/Asset_Perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {},
"clickable": 1,
"DetailsPage": [
{
"TabId": 5,
"GraphId": 5,
"GraphType": "Table",
"GraphTitle": "Asset Availability",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/asset_availability_mtd",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 5,
"GraphId": 5,
"GraphType": "Table",
"GraphTitle": "Asset",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/additional_visibility_mtd",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
},
{
"TabId": 5,
"GraphId": 6,
"GraphType": "BarGraph",
"GraphTitle": "Asset Details",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/asset_trend_perc_mtd",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 4,
"GraphId": 7,
"GraphType": "ScoreCard",
"GraphTitle": "OSA",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/OSA_Perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {},
"clickable": 1,
"DetailsPage": [
{
"TabId": 3,
"GraphId": 3,
"GraphType": "Table",
"GraphTitle": "SOS Compliance - Category",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/osa_perc_on_category",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
},
{
"TabId": 4,
"GraphId": 8,
"GraphType": "BarGraph",
"GraphTitle": "OSA",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/osa_trend_perc_mtd",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 6,
"GraphId": 9,
"GraphType": "ScoreCard",
"GraphTitle": "Promotion",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/Promotion_Perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {},
"clickable": 1,
"DetailsPage": [
{
"TabId": 6,
"GraphId": 9,
"GraphType": "Table",
"GraphTitle": "Promotion Availability",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/promotion_availability_mtd",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
},
{
"TabId": 6,
"GraphId": 10,
"GraphType": "BarGraph",
"GraphTitle": "Promotion",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/promotion_trend_perc_mtd",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
}
},
{
"MainTabId": 2,
"MainTabName": "Last Visit",
"MainTabData": {
"subTabs": [
{
"TabId": 1,
"TabName": "PSS Score",
"TabRow": 1,
"TabCol": 1
},
{
"TabId": 2,
"TabName": "SOS Actual",
"TabRow": 1,
"TabCol": 2
},
{
"TabId": 3,
"TabName": "SOS Compliance",
"TabRow": 1,
"TabCol": 3
},
{
"TabId": 4,
"TabName": "OSA",
"TabRow": 1,
"TabCol": 4
},
{
"TabId": 5,
"TabName": "Asset",
"TabRow": 1,
"TabCol": 5
},
{
"TabId": 6,
"TabName": "Promotion",
"TabRow": 1,
"TabCol": 6
}
],
"graphDetails": [
{
"TabId": 1,
"GraphId": 1,
"GraphType": "ScoreCard",
"GraphTitle": "PSS Score",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/PSS_Score_LSV_Perc",
"GraphBackground": "#F4EAFF",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 2,
"GraphType": "ScoreCard",
"GraphTitle": "SOS Actual",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/sos_actual_lsv_perc",
"GraphBackground": "#FFF3ED",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 3,
"GraphType": "ScoreCard",
"GraphTitle": "SOS Compliance",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/sos_compliance_lsv_perc",
"GraphBackground": "#FFFEF0",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 4,
"GraphType": "ScoreCard",
"GraphTitle": "OSA",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/osa_lsv_perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 5,
"GraphType": "ScoreCard",
"GraphTitle": "Asset",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/asset_lsv_perc",
"GraphBackground": "#EBECFF",
"GraphOptions": {}
},
{
"TabId": 1,
"GraphId": 6,
"GraphType": "ScoreCard",
"GraphTitle": "Promotion",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/promotion_lsv_perc",
"GraphBackground": "#FFFEF0",
"GraphOptions": {}
},
{
"TabId": 2,
"GraphId": 1,
"GraphType": "ScoreCard",
"GraphTitle": "SOS Actual",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/sos_actual_lsv_perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {},
"clickable": 1,
"DetailsPage": [
{
"TabId": 2,
"GraphId": 1,
"GraphType": "Table",
"GraphTitle": "SOS Actual - Category",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/sos_actual_lsv_perc_on_category",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
},
{
"TabId": 2,
"GraphId": 2,
"GraphType": "BarGraph",
"GraphTitle": "SOS Actual Trend",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/sos_actual_trend_lsv_perc",
"GraphBackground": "#F4EAFF",
"GraphOptions": {
"axisX": "month",
"axisY": "score",
"labelShow": 1,
"barColors": [
"#11a4ff",
"#0ea3e3",
"#0b9ddb"
],
"gridLinesH": 1,
"gridLinesV": 1
}
},
{
"TabId": 3,
"GraphId": 3,
"GraphType": "ScoreCard",
"GraphTitle": "SOS Compliance",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/sos_compliance_lsv_perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {},
"clickable": 1,
"DetailsPage": [
{
"TabId": 3,
"GraphId": 3,
"GraphType": "Table",
"GraphTitle": "SOS Actual - Category",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/sos_compliance_lsv_perc_on_category",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
},
{
"TabId": 3,
"GraphId": 4,
"GraphType": "BarGraph",
"GraphTitle": "SOS Compliance Trend",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/sos_compliance_trend_lsv_perc",
"GraphBackground": "#fff",
"GraphOptions": {
"axisX": "month",
"axisY": "score",
"labelShow": 1,
"barColors": [
"#11a4ff",
"#0ea3e3",
"#0b9ddb"
],
"gridLinesH": 1,
"gridLinesV": 1
}
},
{
"TabId": 5,
"GraphId": 5,
"GraphType": "ScoreCard",
"GraphTitle": "Asset",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/asset_lsv_perc",
"GraphBa ckground": "#ECFFFA",
"GraphOptions": {},
"clickable": 1,
"DetailsPage": [
{
"TabId": 5,
"GraphId": 5,
"GraphType": "Table",
"GraphTitle": "Asset Availability",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/asset_availability_lsv",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 5,
"GraphId": 5,
"GraphType": "Table",
"GraphTitle": "Asset",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/additional_visibility_lsv",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
},
{
"TabId": 5,
"GraphId": 6,
"GraphType": "Table",
"GraphTitle": "Asset Details",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/asset_trend_lsv_perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 4,
"GraphId": 7,
"GraphType": "ScoreCard",
"GraphTitle": "OSA",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/osa_lsv_perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {},
"clickable": 1,
"DetailsPage": [
{
"TabId": 4,
"GraphId": 7,
"GraphType": "Table",
"GraphTitle": "SOS Actual - Category",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/osa_lsv_perc_on_category",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
},
{
"TabId": 4,
"GraphId": 8,
"GraphType": "PieChart",
"GraphTitle": "OSA",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/osa_trend_lsv_perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
},
{
"TabId": 6,
"GraphId": 9,
"GraphType": "ScoreCard",
"GraphTitle": "Promotion",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/promotion_lsv_perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {},
"clickable": 1,
"DetailsPage": [
{
"TabId": 6,
"GraphId": 9,
"GraphType": "Table",
"GraphTitle": "Promotion Availability",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/promotion_availability_lsv",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
},
{
"TabId": 6,
"GraphId": 10,
"GraphType": "BarGraph",
"GraphTitle": "Promotion",
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/promotion_trend_lsv_perc",
"GraphBackground": "#ECFFFA",
"GraphOptions": {}
}
]
}
}
]
}
+343
View File
@@ -0,0 +1,343 @@
import { Platform, StyleSheet } from 'react-native';
import { GlobalTheme, Screen } from '../../../theme';
export const styles = StyleSheet.create({
container: {
flex: 1,
// backgroundColor: GlobalTheme.colors.white,
paddingHorizontal: 10,
// justifyContent:'center'
},
logoContainer: {
alignItems: 'center',
},
appLogo: {
height: 200,
width: 200,
resizeMode: 'contain',
},
appMaskLogo: {
resizeMode: 'stretch',
width: Screen.screenHeight * 0.9,
height: Screen.screenHeight * 0.33,
},
titleContainer: {
alignSelf: 'center',
// marginTop: 15,
},
titleText: {
fontSize: 25,
fontWeight: 'bold',
color: GlobalTheme.colors.black,
},
subtitleText: {
fontSize: 15,
color: '#1F2128',
marginBottom: 14,
fontWeight: '600',
alignSelf: 'center',
width: '80%',
textAlign: 'center',
},
subtitleHighlight: {
color: '#2381E9',
},
headerStyle: {
height: '8%',
backgroundColor: '#113F8C',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 15
},
headerText: {
color: GlobalTheme.colors.white,
fontSize: GlobalTheme.typography.fontSize.medium,
fontWeight: '600'
},
headerDotBtn: {
backgroundColor: '#295398',
flexDirection: 'row',
paddingHorizontal: 5,
alignItems: 'center',
borderRadius: 10
},
selectCard: {
backgroundColor: '#FFFFFF',
borderRadius: 30,
elevation: 7,
padding: 15,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 15,
marginHorizontal: 15,
borderWidth: 1.5,
borderColor: '#C9DAFF'
},
iconStyle: {
height: 25,
width: 25,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
margin: 15,
},
dropdown: {
flex: 1,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 8,
paddingHorizontal: 10,
marginHorizontal: 4,
height: 45,
},
row: {
margin: 10,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center'
},
inputStyle: {
paddingHorizontal: 7,
width: '100%',
color: '#000'
// backgroundColor:'red'
},
searchBox: {
height: 45,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 10,
marginTop: 10,
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 10
},
storeNameText: {
color: '#000',
fontWeight: '500',
fontSize: 16
},
storeCard: {
backgroundColor: '#fff',
borderWidth: 1,
borderColor: '#DFDFDF',
marginTop: 10,
padding: 10,
paddingHorizontal: 12,
borderRadius: 20,
// elevation: 2
},
cardTextBold: {
fontSize: 16,
color: '#000',
fontWeight: '500',
flexWrap: 'wrap',
width: '72%'
},
cardText: {
marginTop: 2,
fontSize: 14,
color: '#494949',
fontWeight: '400'
},
dropHeaderText: {
width: '33%',
color: '#000',
fontWeight: '400',
fontSize: 14
},
selectStoreText: {
fontSize: 15,
color: '#808CA3',
fontWeight: '400',
alignSelf: 'center',
marginTop: 5
},
storeText: {
color: '#000',
fontSize: 15,
fontWeight: '500',
marginLeft: 10
},
todayStoreText: {
marginLeft: 10,
color: '#2357C6',
fontSize: 16,
fontWeight: '500'
},
storeGredient: {
flex: 1,
padding: 10,
marginTop: 15,
borderTopLeftRadius: 25,
borderTopRightRadius: 25
},
todayStoreCard: {
flex: 1,
// width:'95%',
backgroundColor: '#fff',
borderRadius: 25,
paddingBottom: 10,
marginRight: Platform.OS === 'ios' ? 20 : 0,
// width:'100%'
},
mainContainer: {
flex: 1,
},
selectedStoreText: {
width: '75%',
borderWidth: 1.5,
borderColor: '#D8E3F1',
borderRadius: 10,
padding: 7,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
filterIcon: {
backgroundColor: '#D8E3F1',
padding: 3,
borderRadius: 5
},
storeInfoText: {
color: '#000',
fontWeight: '500',
fontSize: 16,
textDecorationLine: 'underline'
},
percentBox: {
width: '48%',
height: 100,
minHeight: 100,
backgroundColor: '#EAF1FF',
borderRadius: 20,
elevation: 2,
justifyContent: 'center',
alignItems: 'center',
},
boxText: {
color: '#000',
fontWeight: '400',
fontSize: 16
},
yearLable: {
color: '#000',
fontSize: 14,
fontWeight: '500'
},
yearText: {
color: '#7F83AB',
fontSize: 14,
},
yearView: {
borderWidth: 2,
borderColor: '#D8E3F1',
borderRadius: 15,
paddingHorizontal: 10,
padding: 5,
width: '28%',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between'
},
yearDropDown: {
height: 40,
borderWidth: 2,
borderColor: '#D8E3F1',
width: 100,
borderRadius: 20
},
btnbg: {
// marginLeft: 10,
backgroundColor: GlobalTheme.colors.secondary,
borderRadius: GlobalTheme.borderRadius.lgg,
padding: 18,
paddingVertical: 8
},
btntext: {
color: GlobalTheme.colors.white,
fontSize: GlobalTheme.typography.fontSize.xsmall,
fontWeight: GlobalTheme.typography.fontWeight.medium
},
seperator: {
marginTop: 10,
borderBottomWidth: 2,
borderColor: '#E4E4E4'
},
dropdownIcon: {
height: 12,
width: 12,
resizeMode: 'contain',
tintColor: 'gray'
},
mtdTab: {
borderBottomWidth: 2,
borderColor: '#113F8C',
width: '50%',
padding: 10,
alignItems: 'center',
// borderWidth:0.5,
},
mtdTabText: {
fontSize: 15,
color: '#000',
fontWeight: '600'
},
subTabView: {
backgroundColor: '#EAEAEA',
paddingVertical: 10,
paddingHorizontal: 5,
flexDirection: 'row',
alignItems: 'center'
},
subTab: {
backgroundColor: '#113F8C',
padding: 5,
paddingHorizontal: 10,
borderRadius: 10,
marginRight: 10
},
tabText: {
color: '#fff',
fontSize: 14,
fontWeight: '400',
},
floatingBtn: {
alignSelf: 'center',
position: 'absolute',
bottom: 10,
backgroundColor: 'rgba(255, 255, 255, 0.4)',
// backgroundColor:'#fff',
padding: 10,
paddingHorizontal: 20,
borderRadius: 30,
// elevation: 2,
zIndex: 999,
borderWidth: 1,
borderColor: '#DFDFDF'
},
modalOverlay: {
flex: 1,
// backgroundColor: 'transparent',
backgroundColor: 'rgba(87, 79, 79, 0.2)'
},
modalContainer: {
position: 'absolute',
top: Platform.OS === 'ios' ? 100 : 50,
right: 10,
width: '50%',
backgroundColor: 'white',
borderRadius: 8,
elevation: 8,
shadowColor: '#000',
shadowOpacity: 0.2,
shadowRadius: 5,
shadowOffset: { width: 0, height: 2 },
},
});
@@ -0,0 +1,659 @@
import React, { useState, useEffect } from 'react';
import { View, Text, ScrollView, TouchableOpacity, Platform, Keyboard } from 'react-native';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import Icon from 'react-native-vector-icons/FontAwesome';
import Ionicons from 'react-native-vector-icons/Ionicons';
import mockDataFlat1 from '../data.json';
import * as RNFS from 'react-native-fs';
import moment from 'moment';
import CustomHeader from '../../../../components/CustomHeader';
import IMAGES from '../../../../constants/Images';
import CustomButton from '../../../../components/CustomButton';
import { styles } from './style';
import { GlobalTheme } from '../../../../theme';
import db, { getAllFromTable } from '../../../../constants/database';
import { UploadData2, UploadImagesWithoutWait } from '../../../../constants/uploadData';
import { getAllFileForAFolder, getDownloadJson } from '../../../../constants/function';
import { common_ImagePath, FeedbackImagesFolderPath } from '../../../../constants/constant';
import { toastError } from '../../../../constants/Toast';
import { SafeAreaView } from 'react-native-safe-area-context';
import Loader from '../../../../constants/Loader';
import { ConfirmSaveAlert } from '../../../../components/Alert';
import { useSelector } from 'react-redux';
// image upload url
const imageUploadURL = "https://di1.parinaam.in/webservice/Imageupload.asmx/Uploadimages";
const FeedbackCategories = ({ navigation, route }) => {
let user_exist_data = useSelector(state => state?.user);
const userId = user_exist_data.UserId || '';
const storeData = route?.params?.storeData;
console.log("user_exist_data===", (user_exist_data.UserId, storeData?.StoreId));
const [completedCategories, setCompletedCategories] = useState([]);
const [uniqueCategories, setUniqueCategories] = useState([]);
const [loading, setLoading] = useState(false);
const [loadingdownload, setLoadingDownload] = useState(false);
const [loadingupload, setLoadingUpload] = useState(false);
const [showAlert, setShowAlert] = useState(false);
// get data
// Insert fallback mock data if empty
const insertMasterSurveyQuestion = (data_arr = []) => {
return new Promise((resolve, reject) => {
if (!Array.isArray(data_arr) || data_arr.length === 0) {
resolve(true);
return;
}
const escape = str => (str || '').toString().replace(/'/g, "''");
const values = data_arr.map(item => {
const {
MenuId, SurveyId, SurveyName, CategoryId, Category, CategorySequence,
QuestionId, Question, QuestionType, QuestionImageAllow, QEnable,
LengthValidation, MinLength, MaxLength, OTP, DateRange, QuestionSequence,
AnswerId, Answer, ImageAllow1, ImageAllow2, Image1Mandatory, Image2Mandatory,
QuestionImageMandatory, EnableQuestion, DisableQuestion, AnswerSequence,
ShowCat, SubCategoryId, SubCategory, SubCategorySequence,
QuestionTypeNew, CalFormula, RefImage, QuestionRefImage
} = item;
return `(
'${MenuId}', '${SurveyId}', '${escape(SurveyName)}', '${CategoryId}', '${escape(Category)}',
'${CategorySequence}', '${QuestionId}', '${escape(Question)}', '${escape(QuestionType)}',
'${QuestionImageAllow ? 1 : 0}', '${QEnable ? 1 : 0}', '${LengthValidation ? 1 : 0}',
'${MinLength}', '${MaxLength}', '${OTP ? 1 : 0}', '${escape(DateRange)}',
'${QuestionSequence}', '${AnswerId}', '${escape(Answer)}',
'${ImageAllow1 ? 1 : 0}', '${ImageAllow2 ? 1 : 0}', '${Image1Mandatory ? 1 : 0}',
'${Image2Mandatory ? 1 : 0}', '${QuestionImageMandatory ? 1 : 0}',
'${escape(EnableQuestion)}', '${escape(DisableQuestion)}', '${AnswerSequence}',
'${ShowCat ? 1 : 0}', '${SubCategoryId}', '${escape(SubCategory)}',
'${SubCategorySequence}', '${escape(QuestionTypeNew)}', '${escape(CalFormula)}',
'${escape(RefImage)}', '${escape(QuestionRefImage)}'
)`;
});
const sql = `
INSERT INTO FeedbackCategory (
MenuId, SurveyId, SurveyName, CategoryId, Category, CategorySequence,
QuestionId, Question, QuestionType, QuestionImageAllow, QEnable,
LengthValidation, MinLength, MaxLength, OTP, DateRange, QuestionSequence,
AnswerId, Answer, ImageAllow1, ImageAllow2, Image1Mandatory, Image2Mandatory,
QuestionImageMandatory, EnableQuestion, DisableQuestion, AnswerSequence,
ShowCat, SubCategoryId, SubCategory, SubCategorySequence,
QuestionTypeNew, CalFormula, RefImage,QuestionRefImage
) VALUES ${values.join(',')}
`;
db.transaction(tx => {
tx.executeSql(`DELETE FROM FeedbackCategory`, [], () => {
tx.executeSql(sql, [], () => {
console.log('FeedbackCategory inserted successfully');
resolve(true);
}, (err) => {
console.log('Insert error in FeedbackCategory:', err);
reject(err);
});
}, (err) => {
console.log('Delete error before insert FeedbackCategory:', err);
reject(err);
});
});
});
};
async function getData() {
setLoadingDownload(true);
try {
const data1 = {
Downloadtype: "Master_SurveyQuestion_storeDNA",
Param1: storeData?.StoreId || '',
Param2: "",
username: user_exist_data?.UserId || '',
};
const url = "https://di1.parinaam.in/Webservice/GenericService.svc/DownloadJson";
console.log("Downloading from:", url, data1);
const res = await getDownloadJson(url, data1);
console.log('Response:', res);
if (res?.Master_SurveyQuestion_storeDNA?.length) {
const data = res.Master_SurveyQuestion_storeDNA || [];
console.log("Inserting downloaded data into FeedbackCategory...");
const all = await getAllFromTable('FeedbackCategory');
// if (all?.length === 0) {
console.log("Table empty. Inserting fallback mock data...");
await insertMasterSurveyQuestion(data);
setLoadingDownload(false);
// } else {
// console.log("FeedbackCategory table already has data.");
// setLoadingDownload(false);
// }
} else {
console.log("No Master_SurveyQuestion_storeDNA data found in response");
setLoadingDownload(false);
}
} catch (err) {
console.log("Dashboard feedback survey download error:", err);
setLoadingDownload(false);
}
}
// download data
// 1. Initial load → download and populate local DB and categories
useEffect(() => {
const init = async () => {
await getData(); // Download and insert
await loadCompleted(); // Load completion info
const data = await getUniqueCategoriesFromDB(); // Load categories
setUniqueCategories(data); // Update UI
};
init();
}, []);
// 2. Every time screen is focused → recheck completed categories and ticks
useEffect(() => {
const loadOnFocus = async () => {
await loadCompleted();
const data = await getUniqueCategoriesFromDB();
setUniqueCategories(data);
};
const unsubscribe = navigation.addListener('focus', loadOnFocus);
return unsubscribe;
}, [navigation]);
// useEffect(() => {
// const init = async () => {
// await getData(); // fetch and insert data
// await loadCompleted(); // ← added
// const data = await getUniqueCategoriesFromDB(); // ← added
// setUniqueCategories(data); // ← added
// };
// init();
// // const unsubscribe = navigation.addListener('focus', loadOnFocus);
// // return unsubscribe;
// }, [navigation]);
// end download data.
const isCompleted = (cat) => {
return completedCategories.find(
c => c?.CategoryId === cat?.CategoryId && c?.SurveyId === cat?.SurveyId
);
};
const loadCompleted = async () => {
const d2 = moment().format('MM/DD/YYYY');
console.log("loaddddd", `SELECT DISTINCT CATEGORY_ID, SURVEY_ID , IFNULL(STATUS , '') AS STATUS FROM FeedBackLocalTable WHERE STORE_ID='${storeData?.StoreId}' AND VISIT_DATE='${d2}'`)
return new Promise((resolve, reject) => {
db.transaction(tx => {
tx.executeSql(
`SELECT DISTINCT CATEGORY_ID, SURVEY_ID , IFNULL(STATUS , '') AS STATUS FROM FeedBackLocalTable WHERE STORE_ID='${storeData?.StoreId}' AND VISIT_DATE='${d2}'`,
[],
(tx, results) => {
const completed = [];
for (let i = 0; i < results.rows.length; i++) {
const row = results.rows.item(i);
completed.push({
CategoryId: row.CATEGORY_ID,
SurveyId: row.SURVEY_ID,
Status: row.STATUS
});
}
setCompletedCategories(completed);
// console.log('Completed categories from table:', completed);
resolve();
},
(tx, error) => {
console.log('DB error loading completed:', error);
reject(error);
}
);
});
});
};
const getUniqueCategoriesFromDB = () => {
return new Promise((resolve, reject) => {
db.transaction(tx => {
tx.executeSql(
`SELECT CategoryId, Category, SurveyId, MIN(QuestionSequence) AS minSeq
FROM FeedbackCategory
GROUP BY CategoryId, Category, SurveyId
ORDER BY minSeq ASC`,
[],
(tx, results) => {
const unique = [];
for (let i = 0; i < results.rows.length; i++) {
const row = results.rows.item(i);
unique.push({
CategoryId: row.CategoryId,
Category: row.Category,
SurveyId: row.SurveyId
});
}
resolve(unique);
},
(tx, error) => {
console.log('Error fetching distinct categories:', error);
reject(error);
}
);
});
});
};
// upload data
async function Upload_Data() {
// await saveData("P");
setLoadingUpload(true);
const UserId = userId;
const UploadKeyName = 'Sales_DNA_FeedBack';
const MID = 0;
let postData = {};
let allChilds = [];
const d2 = moment().format('MM/DD/YYYY');
await new Promise((resolve, reject) => {
db.transaction(async function (txn) {
let query = `SELECT * FROM FeedBackLocalTable WHERE STORE_ID='${storeData?.StoreId}' AND VISIT_DATE='${d2}'`;
console.log("query=====", query)
await txn.executeSql(
query,
[],
async (txn2, txnres2) => {
// console.log("Records found:", txnres2.rows.length);
if (txnres2.rows.length > 0) {
for (let i = 0; i < txnres2.rows.length; i++) {
let data = txnres2.rows.item(i);
let {
SURVEY_ID, CATEGORY_ID, MaxLength, MinLength, QuestionImageAllow, QUESTION,
QUESTION_ID, QUESTION_TYPE, OTP, ANSWER, ANSWER_ID, IMAGE_ALLOW1, IMAGE1,
IMAGE_ALLOW2, IMAGE2, MULTI_OPTIONS_IDS, IS_DISABLED, IS_ENABLED
} = data;
let renamedObj = {
SurveyId: SURVEY_ID,
CategoryId: CATEGORY_ID,
QuestionId: QUESTION_ID,
QuestionType: QUESTION_TYPE,
Answer: ANSWER,
AnswerId: ANSWER_ID,
MultiOptionsIds: MULTI_OPTIONS_IDS,
Image1: IMAGE1,
Image2: IMAGE2,
ImageAllow1: IMAGE_ALLOW1,
ImageAllow2: IMAGE_ALLOW2
};
// Fix: IS_DISABLED is expected to be 0 or 1
let isQtnEnabled = JSON.parse(IS_DISABLED)[QUESTION_ID]
let obj = {};
if (isQtnEnabled) {
obj['MID'] = 0;
obj['UserId'] = userId;
// obj['Store_Id'] = '';
// obj['MenuId'] = '';
obj['SurveyId'] = renamedObj?.SurveyId;
obj['CategoryId'] = renamedObj?.CategoryId;
obj['QuestionId'] = renamedObj?.QuestionId;
obj['QuestionType'] = renamedObj?.QuestionType;
obj['Answer'] = renamedObj?.Answer;
obj['AnswerId'] = renamedObj?.AnswerId;
obj['MultiAnswerId'] = (renamedObj?.MultiOptionsIds != null && renamedObj?.MultiOptionsIds != 'undefined') ? renamedObj?.MultiOptionsIds : '';
obj['AnswerImage1'] = renamedObj?.Image1;
obj['AnswerImage2'] = renamedObj?.Image2;
allChilds.push(obj);
}
}
postData['MID'] = MID;
postData['Keys'] = UploadKeyName || 'Sales_DNA_FeedBack';
postData['JsonData'] = JSON.stringify(allChilds);
postData['UserId'] = UserId;
resolve({ PostData: postData });
} else {
resolve({ PostData: postData });
}
},
(txnE, txnerr) => {
// console.log('Query error:', txnerr);
resolve({ PostData: postData });
}
);
});
}).catch((err) => {
console.log("Transaction error:", err);
});
// console.log("Preparing to upload data...");
const url = 'https://di1.parinaam.in/Webservice/GenericService.svc/UploadJson';
console.log("URL:", url);
console.log("PostData:", postData);
let allFilesToUpload = await getAllFileForAFolder(FeedbackImagesFolderPath, "Store_DNA");
console.log('All files to upload:', allFilesToUpload);
await uploadImagesFormFiles12(allFilesToUpload);
await UploadData2(url, postData)
.then(async (res) => {
// console.log('Upload Response:', res);
if (res?.UploadJsonResult === "Success") {
// console.log("Upload successful.");
try {
await updateAllUploadStatuses(d2);
await loadCompleted(); // Refresh the UI with updated STATUS
} catch (err) {
console.log("Failed to update status after upload:", err);
}
setLoadingUpload(false);
setShowAlert(false);
} else {
console.log("Upload failed.");
setLoadingUpload(false);
}
})
.catch((err) => {
console.log("Upload error:", err);
setLoadingUpload(false);
});
}
async function uploadImagesFormFiles12(allfiles = []) {
// console.log("allfiles===========", allfiles);
let d1 = new Date();
let d2 = moment(d1).format('MM/DD/YYYY');
let uploadCount = 0;
const url = imageUploadURL;
var isAllUploaded = false;
return await Promise.all(
allfiles.map(async (file, i) => {
let index = i;
let actualfilepath = 'file://' + file.uri;
// console.log("actualfilepath==", actualfilepath);
let isExists = await RNFS.exists(actualfilepath).then((res) => { return res; });
console.log('isExists file', isExists, file.uri, file);
let isImageUploaded = false;
// isExists==false
if (isExists) {
let postData = new FormData();
postData.append('file', {
uri: actualfilepath,
type: file.type,
name: file.name,
});
postData.append('Foldername', "AddNewStoreImages");
postData.append('Path', d2);
isImageUploaded = await UploadImagesWithoutWait(postData, url);
if (isImageUploaded == true) {
// console.log('isExists file uploaded', isExists, uploadCount, index, allfiles.length - 1);
await RNFS.unlink(actualfilepath);
uploadCount++;
// UpdateUploadCountLocal(uploadCount+1)
if (uploadCount == allfiles.length) {
isAllUploaded = true;
}
} else {
console.log('file not uploaded:', isExists, actualfilepath);
}
}
else {//file does not exists
uploadCount++;
// UpdateUploadCountLocal(uploadCount+1)
if (uploadCount == allfiles.length) {
isAllUploaded = true;
}
}
return isImageUploaded;
})
).then((val) => {
console.log(val);
return isAllUploaded;
})
.catch((err) => {
console.log(err);
return false;
})
}
// end upload image
const areAllCategoriesCompleted = () => {
return uniqueCategories?.every(cat => isCompleted(cat));
};
const areAllCategoriesUploaded = () => {
return uniqueCategories?.every(cat => {
const completed = isCompleted(cat);
return completed?.Status === 'U';
});
};
async function onSubmitData() {
// let isvalid = await validate();
if (areAllCategoriesCompleted()) {
Keyboard.dismiss();
setShowAlert(true);
} else {
toastError("Alert", "Please fill all categories.");
}
}
function onSaveCancel() {
setShowAlert(false);
}
// update status of uploading data
const updateFeedbackStatusToUploaded = async (visitDate) => {
return new Promise((resolve, reject) => {
db.transaction(tx => {
tx.executeSql(
`UPDATE FeedBackLocalTable SET STATUS = 'U' WHERE STORE_ID=? and VISIT_DATE = ?`,
[storeData?.StoreId, visitDate],
(_, result) => {
// console.log("Local STATUS updated to 'U' after upload.");
resolve(result);
},
(tx, error) => {
console.log("Error updating STATUS locally:", error);
reject(error);
}
);
});
});
};
const updateUploadStatusForStores = (visitDate) => {
return new Promise((resolve, reject) => {
db.transaction(tx => {
// First: Set uploaded stores to 'U' for the given visit date
tx.executeSql(
`
UPDATE StoreInfoDNALocal
SET UPLOAD_STATUS = 'U'
WHERE EXISTS (
SELECT 1
FROM FeedBackLocalTable
WHERE FeedBackLocalTable.STORE_ID = StoreInfoDNALocal.StoreId
AND FeedBackLocalTable.STATUS = 'U'
AND FeedBackLocalTable.VISIT_DATE = ?
)
AND EXISTS (
SELECT 1
FROM FeedBackLocalTable
WHERE FeedBackLocalTable.STORE_ID = StoreInfoDNALocal.StoreId
AND FeedBackLocalTable.VISIT_DATE = ?
)
`,
[visitDate, visitDate],
() => {
console.log('Uploaded stores marked as U');
// Second: Set non-uploaded stores to 'P' for the given visit date
tx.executeSql(
`
UPDATE StoreInfoDNALocal
SET UPLOAD_STATUS = 'P'
WHERE EXISTS (
SELECT 1
FROM FeedBackLocalTable
WHERE FeedBackLocalTable.STORE_ID = StoreInfoDNALocal.StoreId
AND FeedBackLocalTable.VISIT_DATE = ?
)
AND NOT EXISTS (
SELECT 1
FROM FeedBackLocalTable
WHERE FeedBackLocalTable.STORE_ID = StoreInfoDNALocal.StoreId
AND FeedBackLocalTable.STATUS = 'U'
AND FeedBackLocalTable.VISIT_DATE = ?
)
`,
[visitDate, visitDate],
() => {
console.log('Pending stores marked as P');
resolve(true);
},
(tx, error) => {
console.log('Error updating pending stores:', error);
reject(error);
}
);
},
(tx, error) => {
console.log('Error updating uploaded stores:', error);
reject(error);
}
);
});
});
};
const updateAllUploadStatuses = async (visitDate) => {
try {
await updateFeedbackStatusToUploaded(visitDate);
await updateUploadStatusForStores(visitDate);
console.log('All upload statuses updated successfully');
} catch (error) {
console.log('Error in updating upload statuses:', error);
}
};
return (
<KeyboardAwareScrollView style={{ flex: 1 }} contentContainerStyle={{ justifyContent: 'center', flex: 1 }}>
<SafeAreaView style={{ flex: 1, backgroundColor: GlobalTheme.colors.primary }}>
<View style={{ flex: 1, backgroundColor: GlobalTheme.colors.white }}>
<CustomHeader
title="Feedback Categories"
// rightIcon={IMAGES.menuIcon}
// onRightPress={() => navigation.navigate('Dashboard')}
onLeftPress={() => navigation.goBack()}
leftIcon={IMAGES.backIcon}
/>
<Loader visible={loading} loadingtext={'Loading ...'} />
<Loader visible={loadingdownload} loadingtext={'data downloading ...'} />
<Loader visible={loadingupload} loadingtext={'Uploading Data...'} />
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ paddingBottom: 10 }}>
<View style={styles.container}>
{uniqueCategories?.map((cat, index) => {
const completedEntry = isCompleted(cat);
{/* console.log("completedEntry", completedEntry); */ }
const isUploaded = completedEntry?.Status === 'U';
{/* console.log("isUploaded", isUploaded); */ }
return (
<TouchableOpacity
key={index}
disabled={isUploaded} // 👈 disable if already uploaded
style={[styles.cardview, styles.categoryItem]} // 👈 visually indicate disabled
onPress={() => {
if (!isUploaded) {
navigation.navigate('Feedback', {
CategoryId: cat?.CategoryId,
SurveyId: cat?.SurveyId,
CategoryName: cat?.Category,
storeData: storeData
});
}
}}
>
<View style={styles.circleIcon}>
<Text style={styles.circleText}>{cat.Category?.charAt(0)}</Text>
</View>
<View style={{ flex: 1 }}>
<Text style={styles.categoryName}>{cat.Category}</Text>
</View>
{isUploaded ? (
<Ionicons name="checkmark-done" size={22} color="green" />
) : completedEntry ? (
<Icon name="check-circle" size={22} color="green" />
) : null}
</TouchableOpacity>
);
})}
{!loading && uniqueCategories.length === 0 && (
<Text style={{ textAlign: 'center', marginTop: 20 }}>No categories available.</Text>
)}
</View>
</ScrollView>
<CustomButton
onPress={() => {
if (areAllCategoriesUploaded()) return;
onSubmitData();
}}
title={areAllCategoriesUploaded() ? 'Data Uploaded' : 'Upload & Save'}
style={areAllCategoriesUploaded() ? styles.btnbg1 : styles.btnbg}
textstyle={areAllCategoriesUploaded() ? styles.btntext1 : styles.btntext}
disabled={areAllCategoriesUploaded()}
/>
<ConfirmSaveAlert
showAlert={showAlert}
onCancelCallBack={onSaveCancel}
onYesCallBack={Upload_Data}
msg="Do you really want to upload data?"
/>
</View>
</SafeAreaView>
</KeyboardAwareScrollView>
);
};
export default FeedbackCategories;
@@ -0,0 +1,66 @@
import { StyleSheet } from 'react-native';
import { GlobalTheme } from '../../../../theme';
export const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: GlobalTheme.colors.white,
paddingHorizontal: 10,
paddingVertical: 10
},
btnbg: {
backgroundColor: GlobalTheme.colors.secondary, borderRadius: GlobalTheme.borderRadius.md, marginVertical: 10, marginHorizontal: 10, bottom:0
},
btntext: {
color: GlobalTheme.colors.white,
fontSize: GlobalTheme.typography.fontSize.medium
},
btnbg1: {
backgroundColor: GlobalTheme.colors.gray, borderRadius: GlobalTheme.borderRadius.md, marginVertical: 10, marginHorizontal: 10, bottom:0 , opacity : 0.5
},
btntext1: {
color: GlobalTheme.colors.white,
fontSize: GlobalTheme.typography.fontSize.medium
},
cardview: {
borderColor: GlobalTheme.colors.lightblue, borderWidth: 1, borderRadius: GlobalTheme.borderRadius.sm, padding: 8, backgroundColor: GlobalTheme.colors.lightblue, marginVertical: 10
},
categoryName: {
fontSize: GlobalTheme.typography.fontSize.small, fontWeight: GlobalTheme.typography.fontWeight.medium, color: GlobalTheme.colors.black, marginVertical: 10
},
//
categoryItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
paddingHorizontal: 10,
backgroundColor: '#f6f8fd',
borderRadius: 10,
marginVertical: 6,
elevation: 1,
},
circleIcon: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: '#dce6f9',
justifyContent: 'center',
alignItems: 'center',
marginRight: 12,
},
circleText: {
fontSize: 16,
fontWeight: 'bold',
color: '#4c6ef5',
},
categoryName: {
fontSize: 15,
fontWeight: '600',
color: '#333',
},
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,333 @@
import React, { useState, useEffect } from 'react';
import {
View,
Text,
TextInput,
ScrollView,
TouchableOpacity,
Platform,
Image,
Modal,
} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import ModalSelector from 'react-native-modal-selector';
import MultiSelect from 'react-native-multiple-select';
import CustomHeader from '../../../components/CustomHeader';
import IMAGES from '../../../constants/Images';
import { GlobalTheme } from '../../../theme';
import CustomButton from '../../../components/CustomButton';
import { styles } from './style';
import { toastError, toastSuccess } from '../../../constants/Toast';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import Icon from 'react-native-vector-icons/FontAwesome';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import mockDataFlat1 from './data.json';
import CustomCamera from '../../../components/CustomCamera';
import CustomModal from '../../../components/CustomModal';
import { SafeAreaView } from 'react-native-safe-area-context';
const transformSurveyData = (flatData) => {
const grouped = {};
flatData?.forEach(item => {
if (!grouped[item.QuestionId]) {
grouped[item.QuestionId] = {
QuestionId: item.QuestionId,
Question: item.Question,
QuestionType: item.QuestionTypeNew,
QEnable: item.QEnable,
EnableQuestion: item.EnableQuestion,
DisableQuestion: item.DisableQuestion,
QuestionSequence: item.QuestionSequence,
CategorySequence: item.CategorySequence,
Answers: []
};
}
if (!['Number', 'Text', 'OnlyText'].includes(item.QuestionTypeNew)) {
if (item.AnswerId || item.Answer) {
grouped[item.QuestionId].Answers.push({
AnswerId: item.AnswerId,
Answer: item.Answer
});
}
}
});
return Object.values(grouped).sort((a, b) => a.QuestionSequence - b.QuestionSequence);
};
const FeedbackFormScreen = ({ route, navigation }) => {
const { CategoryId, SurveyId, CategoryName } = route.params || {};
const [questions, setQuestions] = useState([]);
const [answers, setAnswers] = useState({});
const [enabledQuestions, setEnabledQuestions] = useState({});
const [showCamera, setShowCamera] = useState(false);
const [activeCameraQId, setActiveCameraQId] = useState(null);
// For modal
const [showModal, setShowModal] = useState(false);
const [selectedImg, setSelectedImg] = useState(null);
const [reCapImgModalObj, setReCapImgModalObj] = useState({});
useEffect(() => {
const filtered = mockDataFlat1?.mockDataFlat?.filter(
item => item.CategoryId === CategoryId && item.SurveyId === SurveyId
);
const transformed = transformSurveyData(filtered);
setQuestions(transformed);
const enableMap = {};
transformed.forEach(q => {
enableMap[q.QuestionId] = q.QEnable;
});
setEnabledQuestions(enableMap);
loadSavedAnswers();
}, []);
const loadSavedAnswers = async () => {
try {
const saved = await AsyncStorage.getItem('feedback_answers');
if (saved) setAnswers(JSON.parse(saved));
} catch (e) {
console.warn('Failed to load saved answers');
}
};
const saveAnswers = async () => {
try {
await AsyncStorage.setItem('feedback_answers', JSON.stringify(answers));
const completedRaw = await AsyncStorage.getItem('completed_categories');
let completed = completedRaw ? JSON.parse(completedRaw) : [];
if (!completed.includes(CategoryId)) {
completed.push(CategoryId);
await AsyncStorage.setItem('completed_categories', JSON.stringify(completed));
}
toastSuccess('Success', 'Answers Saved Successfully');
navigation.goBack();
} catch (e) {
toastError('Error', 'Failed to save answers');
}
};
const handleAnswerChange = (questionId, value, enableList = '', disableList = '') => {
setAnswers(prev => ({ ...prev, [questionId]: value }));
const updated = { ...enabledQuestions };
if (disableList) {
disableList.split(',').forEach(id => {
updated[parseInt(id)] = false;
});
}
if (enableList) {
enableList.split(',').forEach(id => {
updated[parseInt(id)] = true;
});
}
setEnabledQuestions(updated);
};
const handleImageCaptured = (photo) => {
if (activeCameraQId) {
setAnswers(prev => ({ ...prev, [activeCameraQId]: photo.uri }));
setSelectedImg(photo.uri);
setShowModal(true);
}
setShowCamera(false);
setActiveCameraQId(null);
};
const openCamera = (questionId) => {
setActiveCameraQId(questionId);
setShowCamera(true);
};
const _OpenCaptureImage_Modal = (imgUri) => {
const isImageCap = imgUri !== '' && imgUri != null;
let { cameraType } = reCapImgModalObj;
return (
<CustomModal
showModal={showModal}
title={'Captured Image'}
message={''}
style={{ alignItems: 'center' }}
hideDefaultClose={true}
>
{isImageCap && (
<View style={{ alignItems: 'center' }}>
<Image
source={{ uri: selectedImg }}
style={{
width: 250,
height: 250,
marginBottom: 10,
borderRadius: 10,
}}
/>
<TouchableOpacity
style={{ marginBottom: 10 }}
onPress={() => {
setShowModal(false);
openCamera(activeCameraQId);
}}
>
<MaterialCommunityIcons name="camera-retake" size={50} color={GlobalTheme.colors.primary} />
</TouchableOpacity>
<TouchableOpacity
onPress={() => setShowModal(false)}
style={{ paddingVertical: 10, paddingHorizontal: 30, backgroundColor: GlobalTheme.colors.primary, borderRadius: 6 }}
>
<Text style={{ color: '#fff' }}>Close</Text>
</TouchableOpacity>
</View>
)}
</CustomModal>
);
};
return (
<KeyboardAwareScrollView style={{ flex: 1 }} contentContainerStyle={{ flexGrow: 1 }}>
<SafeAreaView style={{ flex: 1, backgroundColor: GlobalTheme.colors.white }}>
<CustomHeader
title="Feedback and Rating"
rightIcon={IMAGES.menuIcon}
leftIcon={IMAGES.leftArrowIcon}
onLeftPress={() => navigation.goBack()}
onRightPress={() => navigation.navigate('Dashboard')}
/>
<ScrollView>
<View style={styles.container}>
<View style={styles.cardview}>
<Text style={styles.categoryName}>{CategoryName}</Text>
{questions.map((qtn) => {
if (!enabledQuestions[qtn.QuestionId]) return null;
return (
<View key={qtn.QuestionId} style={styles.questionBlock}>
<Text style={styles.questionText}>{qtn.Question}</Text>
{qtn.QuestionType === 'Single choice list' && (
<ModalSelector
animationType='fade'
optionContainerStyle={{ backgroundColor: '#fff' }}
cancelContainerStyle={{backgroundColor: '#fff' }}
key={qtn.QuestionId}
data={qtn.Answers.map(ans => ({ key: ans.AnswerId, label: ans.Answer }))}
initValue="Select one"
onChange={option => {
const selectedAnswer = option.label;
// Look up the matching full answer entry from mockDataFlat1
const matchedRow = mockDataFlat1.mockDataFlat.find(item =>
item.QuestionId === qtn.QuestionId && item.Answer === selectedAnswer
);
const enableQ = matchedRow?.EnableQuestion || '';
const disableQ = matchedRow?.DisableQuestion || '';
handleAnswerChange(
qtn.QuestionId,
selectedAnswer,
enableQ,
disableQ
);
}}
>
<Text style={styles.selectorBox}>
{answers[qtn.QuestionId] || '---Select option---'}
</Text>
</ModalSelector>
)}
{qtn.QuestionType === 'Multi choice list' && (
<MultiSelect
items={qtn.Answers}
uniqueKey="AnswerId"
displayKey="Answer"
onSelectedItemsChange={(selected) =>
handleAnswerChange(
qtn.QuestionId,
selected,
qtn.EnableQuestion,
qtn.DisableQuestion
)
}
selectedItems={answers[qtn.QuestionId] || []}
selectText="Pick Items"
searchInputPlaceholderText="Search Items..."
searchIcon={<Icon name="search" size={18} color="#888" />}
tagRemoveIconColor="#ccc"
tagBorderColor="#ccc"
tagTextColor="#000"
selectedItemTextColor={GlobalTheme.colors.primary}
selectedItemIconColor="#000"
itemTextColor="#000"
styleMainWrapper={styles.multiSelect}
submitButtonText={'Submit'}
textColor={GlobalTheme.colors.black}
submitButtonColor={GlobalTheme.colors.secondary}
styleItemsContainer={styles.styleItemsContainer}
styleListContainer={styles.styleListContainer}
styleInputGroup={styles.styleInputGroup}
styleDropdownMenuSubsection={styles.styleDropdownMenuSubsection}
styleDropdownMenu={styles.styleDropdownMenu}
/>
)}
{(qtn.QuestionType === 'Text' || qtn.QuestionType === 'OnlyText' || qtn.QuestionType === 'Number') && (
<TextInput
style={styles.textInput}
placeholder="Enter here..."
keyboardType={(qtn.QuestionType == 'Decimal' ? 'decimal-pad' : (qtn.QuestionType == 'Text' || qtn.QuestionType == 'OnlyText' ? 'default' : 'number-pad'))}
value={answers[qtn.QuestionId] || ''}
onChangeText={(text) => handleAnswerChange(qtn.QuestionId, text)}
returnKeyType={(Platform.OS === 'ios') ? 'done' : 'next'}
/>
)}
{qtn.QuestionType === 'Image' && (
<>
<TouchableOpacity
onPress={() => {
if (answers[qtn.QuestionId]) {
setSelectedImg(answers[qtn.QuestionId]);
setReCapImgModalObj({ cameraType: '1' });
setActiveCameraQId(qtn.QuestionId);
setShowModal(true);
} else {
openCamera(qtn.QuestionId); // if no image yet, open camera directly
}
}}
>
<Image
source={answers[qtn.QuestionId] ? IMAGES.greenCameraIcon : IMAGES.redCameraIcon}
style={{ width: 40, height: 40 }}
/>
</TouchableOpacity>
</>
)}
</View>
);
})}
</View>
</View>
</ScrollView>
<CustomButton
onPress={saveAnswers}
title={'Save'}
style={styles.btnbg}
textstyle={styles.btntext}
/>
{showCamera && (
<Modal visible={showCamera} animationType="slide" presentationStyle="fullScreen">
<CustomCamera
onImageCaptured={handleImageCaptured}
onClose={() => setShowCamera(false)}
/>
</Modal>
)}
{/* Modal Preview with Retake */}
{_OpenCaptureImage_Modal(selectedImg)}
</SafeAreaView>
</KeyboardAwareScrollView>
);
};
export default FeedbackFormScreen;
+816
View File
@@ -0,0 +1,816 @@
import React, { useState, useEffect, useRef } from 'react';
import { View, Text, TextInput, ScrollView, TouchableOpacity, Platform, Image, Modal, BackHandler, Keyboard } from 'react-native';
import ModalSelector from 'react-native-modal-selector';
import MultiSelect from 'react-native-multiple-select';
import CustomHeader from '../../../components/CustomHeader';
import IMAGES from '../../../constants/Images';
import { GlobalTheme, Screen } from '../../../theme';
import CustomButton from '../../../components/CustomButton';
import { styles } from './style';
import { toastError, toastSuccess } from '../../../constants/Toast';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import Icon from 'react-native-vector-icons/FontAwesome';
import CustomCamera from '../../../components/CustomCamera';
import db from '../../../constants/database';
import { getImage } from '../../../constants/function';
import CustomIcon from '../../../constants/IconGenerator';
import { ImageZoom } from '@likashefqet/react-native-image-zoom';
import { useFocusEffect } from '@react-navigation/native';
import { validateNumber } from '../../../constants/validations';
import { ConfirmSaveAlert } from '../../../components/Alert';
import { SafeAreaView } from 'react-native-safe-area-context';
import moment from 'moment';
import Loader from '../../../constants/Loader';
import { useSelector } from 'react-redux';
const FeedbackFormScreen = ({ route, navigation }) => {
const { CategoryId, SurveyId, CategoryName , storeData} = route.params || {};
let user_exist_data = useSelector(state => state?.user);
const userId = user_exist_data.UserId || '' ;
const [questions, setQuestions] = useState([]);
const [answers, setAnswers] = useState({});
const [enabledQuestions, setEnabledQuestions] = useState({});
const [showCamera, setShowCamera] = useState(false);
const [activeCameraQId, setActiveCameraQId] = useState(null);
const [selectedImg, setSelectedImg] = useState(null);
const [questionsFlat, setQuestionsFlat] = useState([]);
const [showModal, setShowModal] = useState(false);
const [zoomImageUrl, setZoomImageUrl] = useState(null);
const [showZoomImage, setShowZoomImage] = useState(false);
const [showAlert, setShowAlert] = useState(false);
const [loadingsave, setLoadingSave] = useState(false);
const d2 = moment().format('MM/DD/YYYY');
// handle disable and enable functions
console.log("storeDatastoreDatastoreData",storeData)
const disableQtn = () => {
let disableQ = questions?.map(q => {
});
}
// end handle disable and enable functions
const loadSavedAnswers = async () => {
const query = `SELECT * FROM FeedBackLocalTable WHERE CATEGORY_ID='${CategoryId}' AND SURVEY_ID='${SurveyId}' AND STORE_ID='${storeData?.StoreId}' AND VISIT_DATE='${d2}'`;
// console.log("local saved answers query ===>", query)
db.transaction(tx => {
tx.executeSql(
query,
[],
(tx, results) => {
const localAnswers = {};
let enb = {}
for (let i = 0; i < results.rows.length; i++) {
const item = results.rows.item(i);
let answer = null;
if (item.IMAGE1 && item.IMAGE1 !== '') {
answer = item.IMAGE1.trim();
} else if (item.ANSWER && item.ANSWER !== '') {
if (typeof item.ANSWER === 'string' && item.ANSWER.includes(',')) {
answer = item.ANSWER.split(',').map(val => parseInt(val.trim(), 10));
} else {
answer = item.ANSWER;
}
}
if (item.QUESTION_TYPE == 'Single choice list') {
answer = `${item?.ANSWER_ID},${item.ANSWER}`
} else if (item.QUESTION_TYPE == 'Multi choice list') {
answer = item?.MULTI_OPTIONS_IDS && JSON.parse(item?.MULTI_OPTIONS_IDS)
// console.log("answer------multi", answer, item)
}
enb = item?.IS_DISABLED && JSON.parse(item?.IS_DISABLED)
localAnswers[item.QUESTION_ID] = answer;
// console.log("item?.IMAGE1", item?.IMAGE1)
localAnswers[item.QUESTION_ID + "Img1"] = item?.IMAGE1 ? ('file:///data/user/0/com.performicsstoredna/files/PerformicsAllImages/Store_DNA/' + item.IMAGE1) : "";
localAnswers[item.QUESTION_ID + "Img2"] = item?.IMAGE1 ? ('file:///data/user/0/com.performicsstoredna/files/PerformicsAllImages/Store_DNA/' + item.IMAGE2) : "";
// enabledMap[item.QUESTION_ID] = item.IS_DISABLED === 1 ? false : true; // isDisabled
}
setAnswers(prev => ({
...prev,
...localAnswers
}));
// console.log("enb----",enb)
if (Object.keys(enb).length > 0) {
setEnabledQuestions(enb); // ✅ restore isDisabled state
}
},
(tx, error) => {
console.log('Error fetching saved answers:', error);
}
);
});
};
const saveData = async () => {
setLoadingSave(true);
try {
let valuesArray = [];
questions?.map(q => {
let answer = answers[q.QuestionId] || '';
let img1 = answers[q.QuestionId + "Img1"] || '';
let img2 = answers[q.QuestionId + "Img2"] || '';
let selanswer = "", selansid = "", multi = "";
// console.log("questionlist======", q, "questionlist====");
if (q.QuestionType == 'Single choice list') {
let slValue = answer?.split(",")
selanswer = slValue?.[1] || ''
selansid = slValue?.[0] || "0"
// console.log("selanswer==", selanswer, "selansid", selansid, "answ", answer, q)
} else if (q.QuestionType == 'Multi choice list') {
// console.log("multi------", answer)
multi = JSON.stringify(answer)
selansid = "0"
} else {
selanswer = answer
selansid = "0"
}
// console.log("selanswer==",selanswer ,"selansid" , selansid , "answ",answer)
let imgName1 = img1.split("/").pop();
let imgName2 = img2.split("/").pop();
// console.log("multi------multi",multi)
let enb = JSON.stringify(enabledQuestions)
valuesArray.push(` ('${d2}','${SurveyId}', '${CategoryId}','${q.Question}','${q.QuestionId}','${q.QuestionType}','${selanswer || ''}','${selansid}','${q.QuestionImageAllow}','${imgName1 || ''}','${imgName2 || ''}','${q.ImageAllow1}','${q.ImageAllow2}','${multi}','${q.QuestionRefImage}', '${enb}', '${q.EnableQuestion}' , '${storeData.StoreId || ''}') `);
});
const queryStr = valuesArray.join(', ');
let insertQuery = `INSERT INTO FeedBackLocalTable (VISIT_DATE, SURVEY_ID, CATEGORY_ID, QUESTION, QUESTION_ID, QUESTION_TYPE ,ANSWER,ANSWER_ID,QuestionImageAllow,IMAGE1,IMAGE2,IMAGE_ALLOW1,IMAGE_ALLOW2,MULTI_OPTIONS_IDS, QUESTION_REF_IMAGE, IS_DISABLED , IS_ENABLED, STORE_ID) VALUES ${queryStr}`
db.transaction(tx => {
let deleteQuery = `DELETE FROM FeedBackLocalTable WHERE CATEGORY_ID='${CategoryId}' AND SURVEY_ID='${SurveyId}' AND STORE_ID='${storeData?.StoreId}' AND VISIT_DATE='${d2}'`;
console.log('Executing DELETE query:', deleteQuery);
tx.executeSql(deleteQuery, [], () => {
// console.log("insertQuery------",insertQuery)
tx.executeSql(insertQuery, [], async function (txn2, txnres) {
toastSuccess('Success', 'Data saved successfully.');
setLoadingSave(false);
navigation.goBack();
}, function (txnE, txnerr) {
console.log('Delete error:', txnerr); toastError('Error', txnerr);
setLoadingSave(false);
},);
}, err => console.log('Delete error:', err));
});
} catch (e) {
toastError('Error', e);
setLoadingSave(false);
console.log('Transaction error:', e);
}
};
// const handleAnswerChange = (questionId, value, enableList = '', disableList = '', qtn) => {
// setAnswers(prev => ({ ...prev, [questionId]: value }));
// const updated = { ...enabledQuestions };
// if (disableList) {
// disableList.split(',').forEach(id => {
// updated[parseInt(id)] = false;
// });
// }
// if (enableList) {
// enableList.split(',').forEach(id => {
// updated[parseInt(id)] = true;
// });
// }
// setEnabledQuestions(updated);
// };
function cleanSelector(disableList,qtn,showImageTag,showImageTag2) {
let objTemp={...answers}
disableList?.split(",").map((itm,index)=>{
objTemp[itm]=""
delete objTemp[itm+"Img1"]
delete objTemp[itm+"Img2"]
})
if(showImageTag){
delete objTemp[qtn?.QuestionId+"Img1"]
}
if(showImageTag2){
delete objTemp[qtn?.QuestionId+"Img2"]
}
// console.log("objTemp-----3",showImageTag,showImageTag2,objTemp)s
setAnswers(objTemp);
}
const handleAnswerChange = (questionId, value, enableList = '', disableList = '', qtn,showImageTag=false,showImageTag2=false) => {
const prevValue = answers[questionId];
// Clean selector logic: only for Single choice list
if (
qtn.QuestionType === 'Single choice list' &&
prevValue &&
prevValue !== value
) {
cleanSelector(disableList,qtn,showImageTag,showImageTag2);
console.log('qtn------',qtn)
}
// Update the answer
setAnswers(prev => ({ ...prev, [questionId]: value }));
// Enable/disable dependent questions
const updated = { ...enabledQuestions };
if (disableList) {
disableList.split(',').forEach(id => {
updated[parseInt(id)] = false;
});
}
if (enableList) {
enableList.split(',').forEach(id => {
updated[parseInt(id)] = true;
});
}
setEnabledQuestions(updated);
};
const handleImageCaptured = async (photo) => {
console.log("activeCameraQId-----", activeCameraQId)
if (activeCameraQId && photo?.uri) {
try {
const imgdata = {
uri: photo.uri,
width: photo.width,
ExtendedHeight: photo.height,
storeData: {}
};
const res = await getImage(imgdata ,userId , storeData);
const imagePath = 'file://' + (res?.finalPath || photo.uri);
setAnswers(prev => ({
...prev,
[activeCameraQId]: imagePath
}));
setSelectedImg(imagePath);
setShowModal(true);
} catch (e) {
console.warn("Watermarking failed:", e);
}
}
setShowCamera(false);
};
const openCamera = (questionId, type = 0) => {
setActiveCameraQId(questionId);
setShowCamera(true);
};
// Fixed function to handle image tap - properly set activeCameraQId
const handleImageTap = (questionId, type = 0) => {
const currentImage = answers[questionId];
// Set the active camera question ID
setActiveCameraQId(questionId);
if (currentImage) {
setSelectedImg(currentImage);
setShowModal(true);
// setActiveCameraQId(questionId+"Img"+type);
} else {
openCamera(questionId, type);
}
};
const transformSurveyData = (flatData) => {
const grouped = {};
flatData?.forEach(item => {
if (!grouped[item.QuestionId]) {
grouped[item.QuestionId] = {
QuestionId: item.QuestionId,
Question: item.Question,
QuestionType: item.QuestionTypeNew,
QEnable: item.QEnable,
EnableQuestion: item.EnableQuestion,
DisableQuestion: item.DisableQuestion,
QuestionSequence: item.QuestionSequence,
CategorySequence: item.CategorySequence,
QuestionRefImage: item.QuestionRefImage,
MinLength: item.MinLength,
MaxLength: item.MaxLength,
LengthValidation: item.LengthValidation,
QuestionImageMandatory: item.QuestionImageMandatory, // images
QuestionImageAllow: item.QuestionImageAllow,
ImageAllow1: item.ImageAllow1,
ImageAllow2: item.ImageAllow2,
Image1Mandatory: item.Image1Mandatory,
Image2Mandatory: item.Image2Mandatory,
Answers: []
};
}
if (!['Number', 'Text', 'OnlyText'].includes(item.QuestionTypeNew)) {
if (item.AnswerId || item.Answer) {
grouped[item.QuestionId].Answers.push({
AnswerId: item.AnswerId,
Answer: item.Answer,
isDisabled: item.DisableQuestion,
isEnabled: item.EnableQuestion,
ImageAllow2: item.ImageAllow2,
ImageAllow1: item.ImageAllow1
});
}
}
});
return Object.values(grouped).sort((a, b) => a.QuestionSequence - b.QuestionSequence);
};
const loadQuestions = () => {
const query = `SELECT * FROM FeedbackCategory WHERE CategoryId = '${CategoryId}' AND SurveyId = '${SurveyId}' ORDER BY QuestionSequence ASC`;
console.log('Executing SELECT query:', query);
db.transaction(tx => {
tx.executeSql(
`SELECT * FROM FeedbackCategory WHERE CategoryId = ? AND SurveyId = ? ORDER BY QuestionSequence ASC`,
[CategoryId, SurveyId],
(tx, results) => {
const rows = [];
for (let i = 0; i < results.rows.length; i++) {
rows.push(results.rows.item(i));
}
// console.log('Fetched Questions:', rows);
setQuestionsFlat(rows);
const transformed = transformSurveyData(rows);
setQuestions(transformed);
const enableMap = {};
transformed.forEach(q => {
enableMap[q.QuestionId] = q.QEnable === true || q.QEnable === 1;
});
setEnabledQuestions(enableMap);
loadSavedAnswers();
},
(tx, err) => {
console.log('Error loading questions:', err);
}
);
});
};
const openRefImageView = (imgUrl) => {
setZoomImageUrl(imgUrl);
setShowZoomImage(true);
};
useEffect(() => {
loadQuestions();
}, []);
useFocusEffect(
React.useCallback(() => {
const onBackPress = () => {
if (showZoomImage) {
setShowZoomImage(false);
return true; // block default back action
}
return false; // allow default back behavior
};
const subscription = BackHandler.addEventListener('hardwareBackPress', onBackPress);
return () => subscription.remove(); // ✅ modern cleanup
}, [showZoomImage])
);
async function validate() {
let isValid = true;
for (let qtn of questions) {
// Skip disabled questions
// console.log("enabledQuestions[qtn.QuestionId]", typeof enabledQuestions[qtn.QuestionId] , qtn)
if (!enabledQuestions[qtn.QuestionId]) continue;
let selansid = '', selanswer = '', selimg = '', showImageTag = false, showImageTag2 = false, isImageMandateforAns = false;
// console.log("answers===", JSON.stringify(answers))
const answer = answers[qtn.QuestionId];
const qType = qtn.QuestionType;
const qText = qtn.Question;
let image1 = answers[qtn?.QuestionId + "Img1"] || '';
let image2 = answers[qtn.QuestionId + "Img2"] || '';
// console.log(answers, "answer==========", image1, answer)
// images
const isTruthy = (val) => val === true || val === 'true' || val === 1 || val === '1';
let isImg1mandatory = isTruthy(qtn.Image1Mandatory) || isTruthy(qtn.QuestionImageMandatory);
let isImg2mandatory = isTruthy(qtn.Image2Mandatory);
let isImgAllowed = ((qtn.QuestionImageAllow == true || qtn.QuestionImageAllow == 'true' || qtn.QuestionImageAllow == 1) || (qtn.QuestionType == "Image"));
// let isImg1mandatory = (qtn.Image1Mandatory == "true") || (qtn.Image1Mandatory == true) || (qtn.QuestionImageMandatory == "true") || (qtn.QuestionImageMandatory == true)
// let isImg2mandatory = (qtn?.Image2Mandatory == "true" || qtn?.Image2Mandatory == 'true');
let isQuestionImageMandatory = qtn.QuestionType == "Image" ? (qtn?.QuestionImageMandatory == "true" || qtn?.QuestionImageMandatory == true) : true;
// console.log("isImg1mandatory qutn", qtn.Image1Mandatory, "questionss", qtn)
// TEXT / NUMBER / ONLYTEXT fields
if (['Text', 'Number', 'OnlyText'].includes(qType)) {
if (!answer || answer === '') {
toastError('Please fill details for: ', `${qText}`);
isValid = false;
break;
}
const minL = qtn.MinLength || 0;
const maxL = qtn.MaxLength || 10;
const needsLengthValidation = qtn.LengthValidation === 'true' || qtn.LengthValidation === true || qtn.LengthValidation === 1; // works file recheck
// console.log("needsLengthValidation",needsLengthValidation)
if (needsLengthValidation) {
if (answer?.length < minL) {
toastError('', `Minimum ${minL} characters required in: ${qText}`);
isValid = false;
break;
}
if (answer?.length > maxL) {
toastError('', `Maximum ${maxL} characters allowed in: ${qText}`);
isValid = false;
break;
}
}
}
// SINGLE CHOICE
else if (qType === 'Single choice list') {
console.log("Single choice questions", qType, answer)
const selectedValue = answer?.split(',')[1]; // get the actual ANSWER value
if (!selectedValue || selectedValue.trim() === '') {
toastError('Please select an option for:', `${qText}`);
isValid = false;
break;
}
// if (!answer || answer === '') {
// toastError('Please select an option for:', `${qText}`);
// isValid = false;
// break;
// }
}
// MULTI CHOICE
else if (qType === 'Multi choice list') {
if (!Array.isArray(answer) || answer.length === 0) {
toastError('Please select at least one option for:', `${qText}`);
isValid = false;
break;
}
}
// IMAGE
else if (qType === 'Image') {
if (!image1 || image1 === '') {
toastError('', `Please capture image for: ${qText}`);
isValid = false;
break;
}
}
if ((qtn.QuestionType == 'Single choice list' || qtn.QuestionType == 'Multi choice list')) {
// console.log("answers[qtn?.QuestionId]----", answers[qtn?.QuestionId], qtn?.QuestionId)
if (qtn.QuestionType != 'Multi choice list') {
let slValue = answers[qtn?.QuestionId]?.split(",")
selanswer = slValue?.[1] || ''
selansid = slValue?.[0] || '0'
}
if (qtn.Answers && qtn.Answers.length > 0) {
for (var i = 0; i < qtn.Answers.length; i++) {
let ansss = qtn.Answers[i];
let isandImgAl = (ansss.ImageAllow1 == true || ansss.ImageAllow1 == 'true' || ansss.ImageAllow1 == 1);
let isandImgAl2 = (ansss.ImageAllow2 == true || ansss.ImageAllow2 == 'true' || ansss.ImageAllow2 == 1);
let an = answers[qtn.QuestionId]
// console.log("showImageTag2------", isandImgAl, ansss, qtn.AnswerId, qtn, an, answers)
if (qtn.QuestionType == 'Single choice list' && (isandImgAl && ansss.AnswerId == selansid)) {
showImageTag = true;
}
else if (qtn.QuestionType == 'Multi choice list' && (isandImgAl && qtn?.AnswerId?.indexOf(ansss?.AnswerId) >= 0)) {
showImageTag = true;
}
if (qtn.QuestionType == 'Single choice list' && (isandImgAl2 && ansss.AnswerId == selansid)) {
showImageTag2 = true;
}
else if (qtn.QuestionType == 'Multi choice list' && (isandImgAl2 && qtn?.AnswerId?.indexOf(ansss?.AnswerId) >= 0)) {
showImageTag2 = true;
}
}
}
}
// console.log("isImg1mandatory answerrrrrrrr", isImg1mandatory, image1, image2)
if (showImageTag && isImg1mandatory && (!image1 || image1 === '')) {
isValid = false;
toastError('', `Please capture image for: ${qText}`);
break;
}
if (showImageTag2 && isImg2mandatory && (!image2 || image2 === '')) {
isValid = false;
toastError('', `Please capture image for 2: ${qText}`);
break;
}
// Other types like Audio, Rating, Date can be added similarly here
}
return isValid;
}
function setTextValue(value, qtn, type = '') {
if (value !== '') {
const isValid = validateNumber(value, type);
if (!isValid) return;
}
let valToSave = value;
// If it's decimal type and requires rounding
if (qtn.QuestionType === 'Decimal' && qtn.LengthValidation === 'true') {
const valSplit = value.split('.')[1]?.length;
if (valSplit === undefined || valSplit < 2) {
valToSave = value;
} else if (/^\d+\.\d{2}$/.test(value)) {
valToSave = Number(value).toFixed(2);
}
}
setAnswers(prev => ({
...prev,
[qtn.QuestionId]: valToSave
}));
}
async function onSubmitData() {
let isvalid = await validate();
if (isvalid) {
Keyboard.dismiss();
setShowAlert(true);
}
}
function onSaveCancel() {
setShowAlert(false);
}
return (
<SafeAreaView style={{ flex: 1, backgroundColor: GlobalTheme.colors.primary }}>
<CustomHeader title="Feedback and Rating" rightIcon={IMAGES.menuIcon} leftIcon={IMAGES.leftArrowIcon} onLeftPress={() => navigation.goBack()} onRightPress={() => navigation.navigate('Dashboard')} />
<KeyboardAwareScrollView extraScrollHeight={Platform.OS === 'android' ? 200 : 0} keyboardShouldPersistTaps="handled" enableOnAndroid contentContainerStyle={{ flexGrow: 1 }} style={{ flex: 1, backgroundColor: GlobalTheme.colors.white }}>
<View style={{ flex: 1, backgroundColor: GlobalTheme.colors.white }}>
<View style={styles.container}>
<View style={styles.cardview}>
<Text style={styles.categoryName}>{CategoryName}</Text>
{questions.map((qtn) => {
let selansid = '', selanswer = '';
let imgQS = qtn.QuestionId + "Img"
if (!enabledQuestions[qtn.QuestionId]) return null;
let maxlen = (qtn.LengthValidation == true || qtn.LengthValidation == 'true') && qtn.MaxLength != null && qtn.MaxLength != '' ? qtn.MaxLength : 10;
let minlen = (qtn.LengthValidation == true || qtn.LengthValidation == 'true') && qtn.MinLength != null && qtn.MinLength != '' ? qtn.MinLength : 0;
// question image
let isImgAllowed = ((qtn.QuestionImageAllow == true || qtn.QuestionImageAllow == 'true' || qtn.QuestionImageAllow == 1) || (qtn.QuestionType == "Image"));
let isQuestionImageMandatory = qtn.QuestionType == "Image" ? (qtn?.QuestionImageMandatory == "true" || qtn?.QuestionImageMandatory == true) : true;
// Answer Images
let Image1Mandatory = (qtn.Image1Mandatory == "true") || (qtn.Image1Mandatory == true) || (qtn.QuestionImageMandatory == "true") || (qtn.QuestionImageMandatory == true)
let Image2Mandatory = (qtn.Image2Mandatory == "true") || (qtn.Image2Mandatory == true)
{/* console.log(Image1Mandatory, "Image1Mandatory", qtn.QuestionImageMandatory, "qtn.QuestionImageMandatory") */ }
{/* console.log("maxlen", maxlen, "===========", "minlen", minlen); */ }
let showImageTag = false, showImageTag2 = false
if (qtn.QuestionType == 'Image' && isImgAllowed) {
showImageTag = true;
}
if ((qtn.QuestionType == 'Single choice list' || qtn.QuestionType == 'Multi choice list')) {
{/* console.log("answers[qtn?.QuestionId]----", answers[qtn?.QuestionId], qtn?.QuestionId) */ }
if (qtn.QuestionType != 'Multi choice list') {
let slValue = answers[qtn?.QuestionId]?.split(",")
selanswer = slValue?.[1] || ''
selansid = slValue?.[0] || '0'
}
if (qtn.Answers && qtn.Answers.length > 0) {
for (var i = 0; i < qtn.Answers.length; i++) {
let ansss = qtn.Answers[i];
let isandImgAl = (ansss.ImageAllow1 == true || ansss.ImageAllow1 == 'true' || ansss.ImageAllow1 == 1);
let isandImgAl2 = (ansss.ImageAllow2 == true || ansss.ImageAllow2 == 'true' || ansss.ImageAllow2 == 1);
let an = answers[qtn.QuestionId]
{/* console.log("showImageTag2------", isandImgAl, ansss, qtn.AnswerId, qtn, an, answers) */ }
if (qtn.QuestionType == 'Single choice list' && (isandImgAl && ansss.AnswerId == selansid)) {
showImageTag = true;
}
else if (qtn.QuestionType == 'Multi choice list' && (isandImgAl && qtn?.AnswerId?.indexOf(ansss?.AnswerId) >= 0)) {
showImageTag = true;
}
if (qtn.QuestionType == 'Single choice list' && (isandImgAl2 && ansss.AnswerId == selansid)) {
showImageTag2 = true;
}
else if (qtn.QuestionType == 'Multi choice list' && (isandImgAl2 && qtn?.AnswerId?.indexOf(ansss?.AnswerId) >= 0)) {
showImageTag2 = true;
}
}
}
}
return (
<View key={qtn.QuestionId} style={styles.questionBlock}>
<Text style={styles.questionText}>{qtn.Question}</Text>
{/* {console.log("qtn.QuestionRefImage", qtn.QuestionRefImage, Image2Mandatory)} */}
{qtn.QuestionRefImage != null && qtn.QuestionRefImage != '' && (
<TouchableOpacity style={styles.WItem_RefImgBtn} onPress={() => openRefImageView(qtn.QuestionRefImage)}>
<CustomIcon iconLibrary={'Entypo'} icon='image-inverted' size={30} color="black" />
</TouchableOpacity>
)}
{qtn.QuestionType === 'Single choice list' && (
<ModalSelector animationType='fade' optionContainerStyle={{ backgroundColor: '#fff' }} cancelContainerStyle={{ backgroundColor: '#fff' }}
key={qtn.QuestionId}
data={qtn.Answers.map(ans => ({ key: ans.AnswerId, label: ans.Answer }))}
initValue="Select one"
onChange={option => {
// console.log("option---", option)
const selectedAnswer = option.label;
const matchedRow = questionsFlat?.find(item =>
item.QuestionId === qtn.QuestionId && item.Answer === selectedAnswer
);
const enableQ = matchedRow?.EnableQuestion || '';
const disableQ = matchedRow?.DisableQuestion || '';
console.log("showImageTag------",showImageTag,showImageTag2)
handleAnswerChange(
qtn.QuestionId,
`${option?.key},${selectedAnswer}`,
enableQ,
disableQ,
qtn,
showImageTag,
showImageTag2
);
}}
>
<Text style={styles.selectorBox}>
{selanswer || '---Select option---'}
</Text>
</ModalSelector>
)}
{qtn.QuestionType === 'Multi choice list' && (
<MultiSelect
// items={list_mcdata}
// uniqueKey="AnswerId"
// onSelectedItemsChange={(selectedItems) => { console.log('onSelectedItemsChange'); setMultiSelectValue(selectedItems, qtn) }}
// selectedItems={selansid}
items={qtn.Answers}
uniqueKey="AnswerId"
displayKey="Answer"
onSelectedItemsChange={(selected) =>
handleAnswerChange(qtn.QuestionId, selected, qtn.EnableQuestion, qtn.DisableQuestion, qtn , "", "")
}
selectedItems={
Array.isArray(answers[qtn.QuestionId])
? answers[qtn.QuestionId].map(a => parseInt(a))
: []
}
selectText="Pick Items"
searchInputPlaceholderText="Search Items..."
searchIcon={<Icon name="search" size={18} color="#888" />}
tagRemoveIconColor="#ccc"
tagBorderColor="#ccc"
tagTextColor="#000"
selectedItemTextColor={GlobalTheme.colors.primary}
selectedItemIconColor="#000"
itemTextColor="#000"
styleMainWrapper={styles.multiSelect}
submitButtonText={'Submit'}
textColor={GlobalTheme.colors.black}
submitButtonColor={GlobalTheme.colors.secondary}
styleItemsContainer={styles.styleItemsContainer}
styleListContainer={styles.styleListContainer}
styleInputGroup={styles.styleInputGroup}
styleDropdownMenuSubsection={styles.styleDropdownMenuSubsection}
styleDropdownMenu={styles.styleDropdownMenu}
/>
)}
{(qtn.QuestionType === 'Text' || qtn.QuestionType === 'OnlyText' || qtn.QuestionType === 'Number') && (
<TextInput
style={styles.textInput}
placeholder="Enter here..."
placeholderTextColor={'gray'}
keyboardType={qtn.QuestionType === 'Number' ? 'number-pad' : 'default'}
value={answers[qtn.QuestionId] || ''}
minLength={minlen}
maxLength={maxlen}
onChangeText={(val) => { setTextValue(val, qtn, qtn.QuestionType == 'Decimal' ? 'decimal' : qtn.QuestionType == 'Text' ? 'text' : qtn.QuestionType == 'OnlyText' ? 'onlytext' : 'numeric') }}
// onChangeText={(text) => handleAnswerChange(qtn.QuestionId, text)}
returnKeyType={(Platform.OS === 'ios') ? 'done' : 'next'}
/>
)}
<View style={{ flexDirection: 'row', alignItems: 'center', flex: 1 }}>
{showImageTag &&
<View style={[]}>
<TouchableOpacity onPress={() => { handleImageTap(imgQS + 1) }}>
<Image source={answers[imgQS + 1] ? IMAGES.greenCameraIcon : Image1Mandatory ? IMAGES.redCameraIcon : IMAGES.normalCameraIcon} style={styles.iconStyle} />
</TouchableOpacity>
</View>
}
{showImageTag2 &&
<View style={[]}>
<TouchableOpacity onPress={() => { handleImageTap(imgQS + 2) }}>
<Image source={answers[imgQS + 2] ? IMAGES.greenCameraIcon : Image2Mandatory ? IMAGES.redCameraIcon : IMAGES.normalCameraIcon} style={styles.iconStyle} />
</TouchableOpacity>
</View>
}
</View>
</View>
);
})}
</View>
</View>
{showZoomImage && zoomImageUrl && (
<View style={styles.album_ZOOM_wrap}>
<TouchableOpacity style={styles.closeIcon} onPress={() => setShowZoomImage(false)} >
<Icon name="times-circle" size={35} color="#fff" />
</TouchableOpacity>
<ImageZoom cropWidth={Screen.screenWidth} cropHeight={Screen.screenHeight} imageWidth={Screen.screenWidth} imageHeight={Screen.screenHeight} style={styles.album_ZOOM} uri={zoomImageUrl} minScale={1} maxScale={5} doubleTapScale={3} isSingleTapEnabled isDoubleTapEnabled resizeMode="contain" />
</View>
)}
{showModal && selectedImg && (
<Modal visible={showModal} transparent={true} animationType="fade" onRequestClose={() => setShowModal(false)}>
<View style={{ flex: 1, backgroundColor: '#00000088', justifyContent: 'center', alignItems: 'center' }}>
<View style={styles.modal_view}>
<Image source={{ uri: selectedImg }} style={{ width: 250, height: 250, borderRadius: 10, marginBottom: 15 }} resizeMode="contain" />
<TouchableOpacity
style={styles.modal_view2}
onPress={() => {
setShowModal(false);
if (activeCameraQId) {
openCamera(activeCameraQId);
}
}}
>
<Text style={{ color: '#fff' }}>Retake</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.modal_view2} onPress={() => setShowModal(false)}>
<Text style={{ color: '#fff' }}>Close</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
)}
<ConfirmSaveAlert showAlert={showAlert} onCancelCallBack={onSaveCancel} onYesCallBack={saveData} msg="Do you really want to save data?" />
{showCamera && (
<Modal visible={showCamera} animationType="slide" presentationStyle="fullScreen">
<CustomCamera
onImageCaptured={handleImageCaptured}
onClose={() => setShowCamera(false)}
/>
</Modal>
)}
<Loader visible={loadingsave} loadingtext={'Saving Data...'} />
</View>
</KeyboardAwareScrollView>
<View style={{ backgroundColor: GlobalTheme.colors.white }}>
<CustomButton onPress={onSubmitData} title={'Save'} style={styles.btnbg} textstyle={styles.btntext} />
</View>
</SafeAreaView>
);
};
export default FeedbackFormScreen;
@@ -0,0 +1,232 @@
{
"mappingsurvey":[
{
"StoreId": 403,
"SurveyId": 1
},
{
"StoreId": 484,
"SurveyId": 1
},
{
"StoreId": 510,
"SurveyId": 1
},
{
"StoreId": 583,
"SurveyId": 1
},
{
"StoreId": 2928,
"SurveyId": 1
},
{
"StoreId": 2949,
"SurveyId": 1
},
{
"StoreId": 1,
"SurveyId": 1
},
{
"StoreId": 2,
"SurveyId": 1
},
{
"StoreId": 3,
"SurveyId": 1
},
{
"StoreId": 4,
"SurveyId": 1
},
{
"StoreId": 5,
"SurveyId": 1
},
{
"StoreId": 1,
"SurveyId": 9
},
{
"StoreId": 2,
"SurveyId": 9
},
{
"StoreId": 3,
"SurveyId": 9
},
{
"StoreId": 4,
"SurveyId": 9
},
{
"StoreId": 5,
"SurveyId": 9
},
{
"StoreId": 585,
"SurveyId": 9
},
{
"StoreId": 2532,
"SurveyId": 9
},
{
"StoreId": 1,
"SurveyId": 10
},
{
"StoreId": 2,
"SurveyId": 10
},
{
"StoreId": 3,
"SurveyId": 10
},
{
"StoreId": 4,
"SurveyId": 10
},
{
"StoreId": 1171,
"SurveyId": 11
},
{
"StoreId": 1,
"SurveyId": 12
},
{
"StoreId": 2,
"SurveyId": 12
},
{
"StoreId": 3,
"SurveyId": 12
},
{
"StoreId": 4,
"SurveyId": 12
},
{
"StoreId": 5,
"SurveyId": 12
},
{
"StoreId": 691,
"SurveyId": 10
},
{
"StoreId": 2949,
"SurveyId": 10
},
{
"StoreId": 402,
"SurveyId": 10
},
{
"StoreId": 484,
"SurveyId": 10
},
{
"StoreId": 2928,
"SurveyId": 10
},
{
"StoreId": 403,
"SurveyId": 10
},
{
"StoreId": 583,
"SurveyId": 10
},
{
"StoreId": 3739,
"SurveyId": 10
},
{
"StoreId": 510,
"SurveyId": 10
},
{
"StoreId": 2958,
"SurveyId": 10
},
{
"StoreId": 4041,
"SurveyId": 10
},
{
"StoreId": 2920,
"SurveyId": 10
},
{
"StoreId": 45,
"SurveyId": 11
},
{
"StoreId": 402,
"SurveyId": 11
},
{
"StoreId": 484,
"SurveyId": 11
},
{
"StoreId": 510,
"SurveyId": 11
},
{
"StoreId": 653,
"SurveyId": 11
},
{
"StoreId": 1122,
"SurveyId": 11
},
{
"StoreId": 1483,
"SurveyId": 11
},
{
"StoreId": 1532,
"SurveyId": 11
},
{
"StoreId": 1605,
"SurveyId": 11
},
{
"StoreId": 1711,
"SurveyId": 11
},
{
"StoreId": 2091,
"SurveyId": 11
},
{
"StoreId": 2308,
"SurveyId": 11
},
{
"StoreId": 2532,
"SurveyId": 11
},
{
"StoreId": 2928,
"SurveyId": 11
},
{
"StoreId": 2949,
"SurveyId": 11
},
{
"StoreId": 2958,
"SurveyId": 11
},
{
"StoreId": 3198,
"SurveyId": 11
}
]
}
+103
View File
@@ -0,0 +1,103 @@
import { StyleSheet } from 'react-native';
import { GlobalTheme, Screen } from '../../../theme';
export const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: GlobalTheme.colors.white,
paddingHorizontal: 10,
paddingVertical: 10
},
btnbg: {
backgroundColor: GlobalTheme.colors.secondary, borderRadius: GlobalTheme.borderRadius.md , marginVertical:10 , marginHorizontal:10 , bottom:0
},
btntext: {
color: GlobalTheme.colors.white,
fontSize: GlobalTheme.typography.fontSize.medium
},
cardview: {
borderColor: GlobalTheme.colors.lightblue, borderWidth: 1, borderRadius: GlobalTheme.borderRadius.lg, padding: 10
},
questionBlock: {
marginBottom: 16,
},
questionText: {
color:'#000',
fontSize: GlobalTheme.typography.fontSize.xsmall,
marginVertical: 5
},
categoryName: {
fontSize: GlobalTheme.typography.fontSize.medium , fontWeight : GlobalTheme.typography.fontWeight.medium , color : GlobalTheme.colors.black , marginVertical:0
},
textInput: {
borderWidth: 1.5,
borderColor: GlobalTheme.colors.lightblue,
borderRadius: 6,
padding: 10,
color : GlobalTheme.colors.black,
marginVertical: 5
},
multiSelect: {
// marginTop: 8,
// backgroundColor:'red'
},
selectorBox: {
color:'gray',
padding: 15,
backgroundColor: GlobalTheme.colors.bluebgcolor,
borderRadius: GlobalTheme.borderRadius.sm,
marginVertical: 5
},
//multi select
styleItemsContainer:{
backgroundColor: GlobalTheme.colors.bluebgcolor , borderRadius :GlobalTheme.borderRadius.md , overflow:'hidden', paddingHorizontal:10, marginHorizontal:0,marginVertical:0 , marginBottom:20
},
styleListContainer:{
backgroundColor: GlobalTheme.colors.bluebgcolor , borderRadius :GlobalTheme.borderRadius.md , padding:0 , overflow:'hidden', paddingHorizontal:0, marginHorizontal:0,marginVertical:10
},
styleInputGroup:{
backgroundColor: GlobalTheme.colors.bluebgcolor , borderRadius :GlobalTheme.borderRadius.md , padding:0 , overflow:'hidden',paddingHorizontal:10, marginVertical:10,height:50
},
styleDropdownMenuSubsection:{
backgroundColor: GlobalTheme.colors.bluebgcolor , borderRadius :GlobalTheme.borderRadius.md , padding:0 , overflow:'hidden',paddingHorizontal:10, marginHorizontal:10
},
styleDropdownMenu:{
backgroundColor: GlobalTheme.colors.bluebgcolor , borderRadius :GlobalTheme.borderRadius.md , overflow:'hidden', marginVertical:10, height:50
},
// camera
openStk_prdCameras:{marginTop:10,flexDirection:'row',flexWrap:'wrap'},
openStkCameraCon:{marginRight:10,},
openStkCameraCon2:{marginRight:10,alignItems: 'center'},
iconStyle : {
height :70 , width : 70 , resizeMode : "contain"
},
// refImage
WItem_RefImgBtn:{position:'absolute',right:0,top:-5,width:40,height:40,borderRadius:20,alignItems:'center',justifyContent:'center'},
album_ZOOM_wrap:{backgroundColor:'#000',position:'absolute',top:0,left:0,width:Screen.screenWidth,height:Screen.screenHeight,zIndex:1},
album_ZOOM:{backgroundColor:'#000',width:Screen.screenWidth,height:Screen.screenHeight},
closeIcon : {
resizeMode : 'contain',
alignSelf:'flex-end',
right:20,
marginTop:20
},
store_audit_img_con:{width:'100%',marginTop:10,height:150,marginHorizontal:5,alignSelf:'center',alignItems:'center',justifyContent:'center',position:'relative',backgroundColor:'#f5f5f5'},
modal_view : {
backgroundColor: 'white',
borderRadius: 12,
padding: 20,
width: 300,
alignItems: 'center',
elevation: 10
},
modal_view2: {
marginBottom: 15,
paddingHorizontal: 25,
paddingVertical: 10,
backgroundColor: GlobalTheme.colors.secondary,
borderRadius: 6
}
});
@@ -0,0 +1,437 @@
import React, { useState, useRef, useEffect } from 'react';
import { View, Text, TouchableOpacity, ScrollView, FlatList } from 'react-native';
import { styles } from './style';
import { horizonalLine, Screen } from '../../../theme/theme';
import CustomHeader from '../../../components/CustomHeader';
import IMAGES from '../../../constants/Images';
import axios from 'axios';
import Loader from '../../../constants/Loader';
const storeinfodata = [
{
tabId: 0,
section: 'Store details',
items: [
{ brand: 'Store Name', name: 'Reliance Smart' },
{ brand: 'Store ID', name: '#98440' },
{ brand: 'Address', name: 'Rabindra Nagar, Delhi 110003' },
{ brand: 'City', name: 'Okhla' },
{ brand: 'Store Size', name: '15002000 sq ft' },
{ brand: 'Average Footfall', name: '10000' },
{ brand: 'Store age', name: '8' },
{ brand: 'Store Ranking', name: '24' },
],
},
{
tabId: 0,
section: 'Dabur Employees',
items: [
{ brand: 'RKAM', name: 'Rajesh Paal Singh' },
{ brand: 'SO', name: 'Soniya Singhal' },
],
},
{
tabId: 0,
section: '3P employees',
items: [
{ brand: 'Promoter Name', name: 'Payal Singh' },
{ brand: 'AM Name', name: 'Soniya Singhal' },
{ brand: 'Supervisor Name', name: 'Soniya Dhankar' },
{ brand: 'City', name: 'Okhla' },
{ brand: 'Promoter duration', name: '2 Year 7 months' },
{ brand: 'Average incentive', name: '5000' },
],
},
// Last Visit Details (tabId: 1)
{
tabId: 1,
section: 'Dabur employee',
items: [
{ brand: 'SO', name: 'Rajesh Paal Singh', date: '23/05/2025', test: 'gxsjhxbas' },
{ brand: 'AH', name: 'Umesh Singh', date: '18/04/2025' },
{ brand: 'KAM', name: 'Rahul Tawde', date: '15/05/2025' },
{ brand: 'Others', name: 'Singhal Singh', date: '06/05/2025' },
],
},
{
tabId: 1,
section: '3P team',
items: [
{ brand: 'Supervisor', name: 'Ashish Talwar', date: '27/05/2025' },
{ brand: 'AM', name: 'Soniya Singhal', date: '23/05/2025' },
],
},
// Competition (tabId: 2)
{
tabId: 2,
section: 'Competition assets',
items: [
{ brand: 'Marico', value: 5 },
{ brand: 'Colgate', value: 1 },
{ brand: 'Godrej', value: 2 },
{ brand: 'HUL', value: 1 },
{ brand: 'XX', value: 2 },
],
},
{
tabId: 2,
section: 'Promoter details',
items: [
{ brand: 'Marico', value: 1 },
{ brand: 'Colgate', value: 1 },
{ brand: 'Godrej', value: 1 },
{ brand: 'HUL', value: 1 },
{ brand: 'XX', value: 0 },
],
},
];
const tabs = [
{ id: 0, title: 'Store Info' },
{ id: 1, title: 'Last visit details' },
{ id: 2, title: 'Competition' },
];
const RenderHeader = ({ columns }) => {
const colWidth = Screen.screenWidth * 0.95 / columns?.length;
return (
null
// <View style={{ flexDirection: 'row', paddingVertical: 6 }}>
// {columns.map((col, index) => (
// <View key={index} style={{ width: colWidth }}>
// <Text style={styles.subheaderText}>{col.toUpperCase()}</Text>
// </View>
// ))}
// </View>
);
};
const RenderItem = ({ item, columns }) => {
const colWidth = Screen.screenWidth * 0.95 / columns?.length;
console.log('item------>',item);
let col=item?.brand == 'Designation' ? [...columns[0]]:columns
return (
<View>
<View style={styles.row}>
{col.map((key, index) => (
<View key={index} style={{ width: colWidth }}>
{console.log("columns---",columns,"Designation",item)}
<Text style={styles.name}>{item?.brand == 'Designation'? item?.name :String(item[key] ?? '')}</Text>
</View>
))}
</View>
<View style={[horizonalLine, { marginVertical: 4 }]} />
</View>
);
};
const SectionListView = ({ listData }) => {
const scrollRef = useRef(null); // shared horizontal scroll ref
console.log("/-",listData)
return (
<FlatList
data={listData}
keyExtractor={(item, index) => item.section + index}
contentContainerStyle={{ paddingBottom: 20 }}
ItemSeparatorComponent={() => <View style={{ paddingVertical: 5 }} />}
renderItem={({ item }) => {
const columns = Object.keys(item.items[0] || {}).filter(
key => item.items.some(obj => obj[key] !== undefined && obj[key] !== null)
);
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>{item.section}</Text>
<View style={[horizonalLine, { marginVertical: 10 }]} />
{/* Shared horizontal scroll view for header + rows */}
<ScrollView
horizontal
ref={scrollRef}
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
>
<View>
<RenderHeader columns={columns} />
{item.items.map((subItem, index) => (
<RenderItem key={index} item={subItem} columns={columns} />
))}
</View>
</ScrollView>
</View>
);
}}
/>
);
};
const StoreInfo = ({ navigation, route }) => {
const { storeData } = route.params || {};
const [loading, setLoading] = useState(false)
const [selectedTab, setSelectedTab] = useState(0);
const [storeInfoData, setStoreInfoData] = useState([])
useEffect(() => {
getStoreInfo()
}, [])
// const getStoreInfo = async () => {
// setLoading(true)
// try {
// const params = { StoreId: storeData?.StoreId || "723" };
// const config = {
// method: 'post',
// url: 'https://api1.parinaam.in/api/dabur/StoreDNAstoreInfo',
// headers: {
// 'api_key': '9a1f056fecb84eaf8eb4152dda22ab0501955c4f9bbe7daa8780740459fdde7a',
// 'Content-Type': 'application/json'
// },
// data: params
// };
// const response = await axios.request(config);
// const res = response.data?.StoreDNAstoreInfo || {};
// // Flatten & transform API response into format usable by SectionListView
// const tempData = [];
// Object.entries(res).forEach(([sectionTitle, dataArray]) => {
// if (dataArray?.length > 0) {
// const tabId = dataArray[0]?.TabId ?? 0;
// // Convert single object to list of { key: value } pairs
// dataArray.forEach(item => {
// const cleanItem = { ...item };
// delete cleanItem.TabId;
// delete cleanItem.TabName;
// const formattedItems = Object.entries(cleanItem).map(([key, value]) => ({
// brand: key,
// name: String(value)
// }));
// tempData.push({
// tabId,
// section: sectionTitle.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()),
// items: formattedItems
// });
// });
// }
// });
// setStoreInfoData(tempData);
// setLoading(false)
// } catch (error) {
// console.log("❌ store info api error:", error);
// setLoading(false)
// }
// };
const getStoreInfo = async () => {
setLoading(true);
try {
const params = { StoreId: storeData?.StoreId || "723" };
const config = {
method: 'post',
url: 'https://api1.parinaam.in/api/dabur/StoreDNAstoreInfo',
headers: {
'api_key': '9a1f056fecb84eaf8eb4152dda22ab0501955c4f9bbe7daa8780740459fdde7a',
'Content-Type': 'application/json'
},
data: params
};
const response = await axios.request(config);
const res = response.data?.StoreDNAstoreInfo || {};
// Step 1: Flatten API data
const tempData = [];
Object.entries(res).forEach(([sectionTitle, dataArray]) => {
if (dataArray?.length > 0) {
const tabId = dataArray[0]?.TabId ?? 0;
let sec =sectionTitle.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase())
dataArray.forEach(item => {
const cleanItem = { ...item };
delete cleanItem.TabId;
delete cleanItem.TabName;
let formattedItems=[]
if("Dabur Employee"==sec){
console.log("tempData---14",item,sec)
formattedItems = Object.entries(cleanItem).map(([key, value]) => ({
brand: key,
name: String(value)
}));
}else{
formattedItems = Object.entries(cleanItem).map(([key, value]) => ({
brand: key,
name: String(value)
}));
}
tempData.push({
tabId,
section: sec,
items: formattedItems
});
});
}
});
// Step 2: Group items by tabId and section
const groupedData = {};
tempData.forEach(entry => {
const key = `${entry.tabId}-${entry.section}`;
if (!groupedData[key]) {
groupedData[key] = {
tabId: entry.tabId,
section: entry.section,
items: []
};
}
groupedData[key].items.push(...entry.items);
});
// let objTemp={}
// groupedData['0-Dabur Employee']?.items
console.log("tempData-----gD",groupedData)
// Step 3: Set to state
setStoreInfoData(Object.values(groupedData));
setLoading(false);
} catch (error) {
console.log("❌ store info api error:", error);
setLoading(false);
}
};
const filteredData = Array.isArray(storeInfoData)
? storeInfoData.filter(item => item.tabId === selectedTab)
: [];
const selectedTabTitle = tabs?.find(tab => tab.id === selectedTab)?.title ?? 'NA';
const dynamicTabs = Array.isArray(storeInfoData)
? Array.from(new Set(storeInfoData.map(item => item.tabId))).map(id => ({
id,
title: tabs.find(tab => tab.id === id)?.title || `Tab ${id}`
}))
: [];
console.log("listData-----12",storeInfoData,filteredData)
// const getStoreInfo = async () => {
// try {
// const params = {
// "StoreId": "723"
// };
// const config = {
// method: 'post',
// url: 'https://api1.parinaam.in/api/dabur/StoreDNAstoreInfo',
// headers: {
// 'api_key': '9a1f056fecb84eaf8eb4152dda22ab0501955c4f9bbe7daa8780740459fdde7a',
// 'Content-Type': 'application/json'
// },
// data: params
// };
// const response = await axios.request(config);
// const res = response.data?.StoreDNAstoreInfo || {};
// console.log('GET STORE INFO ==> ', JSON.stringify(res));
// const allItems = res && Object.values(res).flat(); // Flattened array
// setStoreInfoData(allItems);
// // Now extract TabId and TabName from array
// const tabData = allItems.map(item => ({
// TabId: item.TabId,
// TabName: item.TabName
// })).filter(item => item.TabId !== undefined && item.TabName);
// // Remove duplicates
// const uniqueTabs = Array.from(
// new Map(tabData.map(item => [`${item.TabId}-${item.TabName}`, item])).values()
// );
// console.log("tabData----", tabData, uniqueTabs)
// let NewObj = {}
// let ourVlue = uniqueTabs.map((item, i) => {
// NewObj[item?.TabName] = allItems?.filter((val) => val?.TabName == item?.TabName)
// })
// console.log("Unique Tabs:", uniqueTabs, JSON.stringify(NewObj));
// console.log("All Items:", JSON.stringify(allItems));
// } catch (error) {
// console.log("❌ store info api error:", error);
// }
// };
const renderTabContent = () => {
switch (selectedTab) {
case 0:
return <SectionListView listData={filteredData} />
case 1:
return <SectionListView listData={filteredData} />
case 2:
return <SectionListView listData={filteredData} />;
default:
return null;
}
};
return (
<View style={[styles.container, { paddingHorizontal: 0 }]}>
<CustomHeader
title={'Store Info'}
leftIcon={IMAGES.leftArrowIcon}
onLeftPress={() => navigation.goBack()} />
<View style={styles.container}>
<View style={styles.tabstyle}>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={{ flexDirection: 'row' }}>
{/* {tabs?.map((tab) => ( */}
{dynamicTabs?.map((tab) => (
<TouchableOpacity key={tab.id} style={styles.tabview} activeOpacity={1} onPress={() => setSelectedTab(tab.id)}>
<View style={selectedTab === tab.id ? styles.selecttabView : styles.unselecttabView}>
<Text style={[styles.tabtext, selectedTab === tab.id ? styles.selecttabText : styles.unselecttabText]}>
{tab.title}
</Text>
</View>
</TouchableOpacity>
))}
</View>
</ScrollView>
</View>
<ScrollView horizontal={false} style={{ flex: 1 }}>
<Text style={styles.headerText}>{selectedTabTitle} </Text>
<View style={{ width: '100%' }}>{renderTabContent()}</View>
</ScrollView>
</View>
<Loader visible={loading} />
</View>
);
};
export default StoreInfo;
+319
View File
@@ -0,0 +1,319 @@
import React, { useState, useRef, useEffect } from 'react';
import { View, Text, TouchableOpacity, ScrollView, FlatList } from 'react-native';
import { styles } from './style';
import GlobalTheme, { horizonalLine, Screen } from '../../../theme/theme';
import CustomHeader from '../../../components/CustomHeader';
import IMAGES from '../../../constants/Images';
import axios from 'axios';
import Loader from '../../../constants/Loader';
import { SafeAreaView } from 'react-native-safe-area-context';
const storeinfodata = [
{
tabId: 0,
section: 'Store details',
items: [
{ brand: 'Store Name', name: 'Reliance Smart' },
{ brand: 'Store ID', name: '#98440' },
{ brand: 'Address', name: 'Rabindra Nagar, Delhi 110003' },
{ brand: 'City', name: 'Okhla' },
{ brand: 'Store Size', name: '15002000 sq ft' },
{ brand: 'Average Footfall', name: '10000' },
{ brand: 'Store age', name: '8' },
{ brand: 'Store Ranking', name: '24' },
],
},
{
tabId: 0,
section: 'Dabur Employees',
items: [
{ brand: 'RKAM', name: 'Rajesh Paal Singh' },
{ brand: 'SO', name: 'Soniya Singhal' },
],
},
{
tabId: 0,
section: '3P employees',
items: [
{ brand: 'Promoter Name', name: 'Payal Singh' },
{ brand: 'AM Name', name: 'Soniya Singhal' },
{ brand: 'Supervisor Name', name: 'Soniya Dhankar' },
{ brand: 'City', name: 'Okhla' },
{ brand: 'Promoter duration', name: '2 Year 7 months' },
{ brand: 'Average incentive', name: '5000' },
],
},
// Last Visit Details (tabId: 1)
{
tabId: 1,
section: 'Dabur employee',
items: [
{ brand: 'SO', name: 'Rajesh Paal Singh', date: '23/05/2025', test: 'gxsjhxbas' },
{ brand: 'AH', name: 'Umesh Singh', date: '18/04/2025' },
{ brand: 'KAM', name: 'Rahul Tawde', date: '15/05/2025' },
{ brand: 'Others', name: 'Singhal Singh', date: '06/05/2025' },
],
},
{
tabId: 1,
section: '3P team',
items: [
{ brand: 'Supervisor', name: 'Ashish Talwar', date: '27/05/2025' },
{ brand: 'AM', name: 'Soniya Singhal', date: '23/05/2025' },
],
},
// Competition (tabId: 2)
{
tabId: 2,
section: 'Competition assets',
items: [
{ brand: 'Marico', value: 5 },
{ brand: 'Colgate', value: 1 },
{ brand: 'Godrej', value: 2 },
{ brand: 'HUL', value: 1 },
{ brand: 'XX', value: 2 },
],
},
{
tabId: 2,
section: 'Promoter details',
items: [
{ brand: 'Marico', value: 1 },
{ brand: 'Colgate', value: 1 },
{ brand: 'Godrej', value: 1 },
{ brand: 'HUL', value: 1 },
{ brand: 'XX', value: 0 },
],
},
];
const tabs = [
{ id: 0, title: 'Store Info' },
{ id: 1, title: 'Last visit details' },
{ id: 2, title: 'Competition' },
];
const RenderHeader = ({ columns }) => {
const colWidth = Screen.screenWidth * 0.95 / columns?.length;
return (
null
// <View style={{ flexDirection: 'row', paddingVertical: 6 }}>
// {columns.map((col, index) => (
// <View key={index} style={{ width: colWidth }}>
// <Text style={styles.subheaderText}>{col.toUpperCase()}</Text>
// </View>
// ))}
// </View>
);
};
const RenderItem = ({ item, columns }) => {
const colWidth = Screen.screenWidth * 0.95 / columns?.length;
return (
<View>
<View style={styles.row}>
{columns.map((key, index) => (
<View key={index} style={{ width: colWidth }}>
<Text style={styles.name}>{String(item[key] ?? '')}</Text>
</View>
))}
</View>
<View style={[horizonalLine, { marginVertical: 4 }]} />
</View>
);
};
const SectionListView = ({ listData }) => {
const scrollRef = useRef(null); // shared horizontal scroll ref
console.log("/-", listData)
return (
<FlatList
data={listData}
keyExtractor={(item, index) => item.section + index}
contentContainerStyle={{ paddingBottom: 20 }}
ItemSeparatorComponent={() => <View style={{ paddingVertical: 5 }} />}
renderItem={({ item }) => {
const columns = Object.keys(item.items[0] || {}).filter(
key => item.items.some(obj => obj[key] !== undefined && obj[key] !== null)
);
return (
<View style={styles.section}>
<Text style={styles.sectionTitle}>{item.section}</Text>
<View style={[horizonalLine, { marginVertical: 10 }]} />
{/* Shared horizontal scroll view for header + rows */}
<ScrollView
horizontal
ref={scrollRef}
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
>
<View>
<RenderHeader columns={columns} />
{item.items.map((subItem, index) => (
<RenderItem key={index} item={subItem} columns={columns} />
))}
</View>
</ScrollView>
</View>
);
}}
/>
);
};
const StoreInfo = ({ navigation, route }) => {
const { storeData } = route.params || {};
const [loading, setLoading] = useState(false)
const [selectedTab, setSelectedTab] = useState(0);
const [storeInfoData, setStoreInfoData] = useState([])
useEffect(() => {
getStoreInfo()
}, [])
const getStoreInfo = async () => {
setLoading(true);
try {
const params = { StoreId: storeData?.StoreId || "723" };
const config = {
method: 'post',
url: 'https://api1.parinaam.in/api/dabur/StoreDNAstoreInfo',
headers: {
'api_key': '9a1f056fecb84eaf8eb4152dda22ab0501955c4f9bbe7daa8780740459fdde7a',
'Content-Type': 'application/json'
},
data: params
};
const response = await axios.request(config);
const res = response.data?.StoreDNAstoreInfo || {};
// Step 1: Flatten API data
const tempData = [];
Object.entries(res).forEach(([sectionTitle, dataArray]) => {
if (dataArray?.length > 0) {
const tabId = dataArray[0]?.TabId ?? 0;
dataArray.forEach(item => {
const cleanItem = { ...item };
delete cleanItem.TabId;
delete cleanItem.TabName;
console.log("tempData---", item)
const formattedItems = Object.entries(cleanItem).map(([key, value]) => ({
brand: key,
name: String(value)
}));
tempData.push({
tabId,
section: sectionTitle.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()),
items: formattedItems
});
});
}
});
// Step 2: Group items by tabId and section
const groupedData = {};
tempData.forEach(entry => {
const key = `${entry.tabId}-${entry.section}`;
if (!groupedData[key]) {
groupedData[key] = {
tabId: entry.tabId,
section: entry.section,
items: []
};
}
groupedData[key].items.push(...entry.items);
});
// let objTemp={}
// groupedData['0-Dabur Employee']?.items
// Step 3: Set to state
setStoreInfoData(Object.values(groupedData));
setLoading(false);
} catch (error) {
console.log("❌ store info api error:", error);
setLoading(false);
}
};
const filteredData = Array.isArray(storeInfoData)
? storeInfoData.filter(item => item.tabId === selectedTab)
: [];
const selectedTabTitle = tabs?.find(tab => tab.id === selectedTab)?.title ?? 'NA';
const dynamicTabs = Array.isArray(storeInfoData)
? Array.from(new Set(storeInfoData.map(item => item.tabId))).map(id => ({
id,
title: tabs.find(tab => tab.id === id)?.title || `Tab ${id}`
}))
: [];
const renderTabContent = () => {
switch (selectedTab) {
case 0:
return <SectionListView listData={filteredData} />
case 1:
return <SectionListView listData={filteredData} />
case 2:
return <SectionListView listData={filteredData} />;
default:
return null;
}
};
return (
<SafeAreaView style={{ flex: 1, backgroundColor: GlobalTheme.colors.primary }}>
<View style={[styles.container, { paddingHorizontal: 0 }]}>
<CustomHeader
title={'Store Info'}
leftIcon={IMAGES.leftArrowIcon}
onLeftPress={() => navigation.goBack()} />
<View style={styles.container}>
<View style={styles.tabstyle}>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<View style={{ flexDirection: 'row' }}>
{/* {tabs?.map((tab) => ( */}
{dynamicTabs?.map((tab) => (
<TouchableOpacity key={tab.id} style={styles.tabview} activeOpacity={1} onPress={() => setSelectedTab(tab.id)}>
<View style={selectedTab === tab.id ? styles.selecttabView : styles.unselecttabView}>
<Text style={[styles.tabtext, selectedTab === tab.id ? styles.selecttabText : styles.unselecttabText]}>
{tab.title}
</Text>
</View>
</TouchableOpacity>
))}
</View>
</ScrollView>
</View>
<ScrollView horizontal={false} style={{ flex: 1 }}>
<Text style={styles.headerText}>{selectedTabTitle} </Text>
<View style={{ width: '100%' }}>{renderTabContent()}</View>
</ScrollView>
</View>
<Loader visible={loading} />
</View>
</SafeAreaView>
);
};
export default StoreInfo;
+115
View File
@@ -0,0 +1,115 @@
import { StyleSheet } from 'react-native';
import { GlobalTheme, Screen, shadow } from '../../../theme';
export const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: GlobalTheme.colors.white,
paddingHorizontal: 10
},
// tabs
tabview: {
alignItems: 'center',
justifyContent: 'space-between',
marginVertical: 10,
flexDirection: 'row',
},
tabstyle: {
flexDirection: 'row',
justifyContent: 'space-between',
borderColor: GlobalTheme.colors.lightblue,
borderWidth: 1.5,
borderRadius: GlobalTheme.borderRadius.xxlg,
marginTop: 10,
paddingHorizontal: 10,
},
tabtext: {
overflow: 'hidden',
color:'#000'
},
selecttabView: {
backgroundColor: GlobalTheme.colors.primary,
paddingVertical: 8,
paddingHorizontal: 15,
borderRadius: GlobalTheme.borderRadius.lgg
},
selecttabText: {
color: GlobalTheme.colors.white,
fontSize: GlobalTheme.typography.fontSize.small,
fontWeight: GlobalTheme.typography.fontWeight.regular,
textAlign: 'center'
},
unselecttabView: {
paddingVertical: 8,
paddingHorizontal: 15,
borderRadius: GlobalTheme.borderRadius.md
},
unselecttabText: {
color: GlobalTheme.colors.lightbluetext,
fontSize: GlobalTheme.typography.fontSize.xsmall,
fontWeight: GlobalTheme.typography.fontWeight.regular,
textAlign: 'center'
},
// comp
section: {
backgroundColor: GlobalTheme.colors.white,
padding: 12,
marginHorizontal: 5,
borderRadius: 8,
elevation: 2,
width: Screen.screenWidth * 0.92,
marginVertical: 5,
borderWidth: 1,
borderColor: GlobalTheme.colors.lightblueborder,
// ...shadow,
},
sectionTitle: {
color:'#000',
fontWeight: GlobalTheme.typography.fontWeight.bold,
fontSize: GlobalTheme.typography.fontSize.small,
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: 4
},
brand: {
fontSize: GlobalTheme.typography.fontSize.xsmall,
color: GlobalTheme.colors.black,
fontWeight: GlobalTheme.typography.fontWeight.medium,
},
value: {
fontSize: GlobalTheme.typography.fontSize.xsmall,
fontWeight: GlobalTheme.typography.fontWeight.medium,
},
name: {
fontSize: GlobalTheme.typography.fontSize.xsmall,
fontWeight: GlobalTheme.typography.fontWeight.regular,
flexWrap:'wrap',
color:'#000'
},
date: {
fontSize: GlobalTheme.typography.fontSize.xsmall,
fontWeight: GlobalTheme.typography.fontWeight.regular,
flexWrap:'wrap'
},
subheaderText:{
fontSize: GlobalTheme.typography.fontSize.xsmall,
fontWeight: GlobalTheme.typography.fontWeight.regular,
flexWrap:'wrap',
color : GlobalTheme.colors.gray,
textTransform: 'capitalize',
},
headerText:{
fontSize: GlobalTheme.typography.fontSize.medium,
fontWeight: GlobalTheme.typography.fontWeight.medium,
flexWrap:'wrap',
color : GlobalTheme.colors.black,
paddingVertical:10,
paddingHorizontal:10
}
});
@@ -0,0 +1,28 @@
import React from 'react';
import { View, Text, ImageBackground, Image, TouchableOpacity } from 'react-native';
import IMAGES from '../../../constants/Images';
import { SafeAreaView } from 'react-native-safe-area-context';
import { styles } from './style';
const Welcome = ({ navigation }) => {
const goToDashboard=()=>{
navigation.reset({ index: 0, routes: [{ name: 'Dashboard' }] })
}
return (
<View style={styles.container}>
<ImageBackground source={IMAGES.WelcomeBackground} style={styles.background} resizeMode='cover'>
<View style={styles.content}>
<Image source={IMAGES.Welcomelogo} style={styles.illustration} />
<Text style={styles.title}>Welcome to {"\n"} Performics Store DNA</Text>
<Text style={styles.subtitle}>Get quick access to store feedback and performance reports.</Text>
<TouchableOpacity style={styles.button} onPress={() => goToDashboard()} >
<Text style={styles.buttonText}>Continue</Text>
</TouchableOpacity>
</View>
</ImageBackground>
</View>
);
};
export default Welcome;
@@ -0,0 +1,61 @@
import { Dimensions, StyleSheet } from 'react-native';
import { GlobalTheme, Screen, shadow } from '../../../theme';
const { width, height } = Dimensions.get('window');
export const styles = StyleSheet.create({
containermain: {
flex: 1,
// backgroundColor: 'red',
},
container: {
flex: 1,
},
background: {
flex: 1,
},
content: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 20,
},
illustration: {
width: 200,
height: 200,
marginBottom: 30,
resizeMode:"contain"
},
title: {
fontSize: GlobalTheme.typography.fontSize.large,
fontWeight: 'bold',
color: GlobalTheme.colors.primary,
marginBottom: 10,
textAlign: 'center',
},
subtitle: {
fontSize: 14,
color: GlobalTheme.colors.primary,
textAlign: 'center',
marginBottom: 30,
paddingHorizontal: 10,
},
button: {
backgroundColor: GlobalTheme.colors.white,
paddingVertical: 12,
paddingHorizontal: 30,
borderRadius: 30,
borderColor:'#91A2CB',
borderWidth:1
},
buttonText: {
color: GlobalTheme.colors.primary,
fontSize: GlobalTheme.typography.fontSize.xsmall,
// fontWeight: '600',
},
})