first commit
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
// In App.js in a new project
|
||||
import * as React from 'react';
|
||||
import {Provider} from 'react-redux';
|
||||
@@ -13,3 +14,30 @@ function App() {
|
||||
}
|
||||
|
||||
export default App;
|
||||
=======
|
||||
import * as React from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import Routes from './navigation/Routes';
|
||||
import { store } from './redux/store';
|
||||
import { initTables } from './constants/database';
|
||||
import { CreateImageFolders } from './constants/function';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
|
||||
function App() {
|
||||
useEffect(() => {
|
||||
initTables(); // Ensure DB tables are created
|
||||
CreateImageFolders();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||
<Provider store={store}>
|
||||
<Routes />
|
||||
</Provider>
|
||||
</GestureHandlerRootView>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
export const BASE_URL = 'https://dax.parinaam.in/execute/dabur';
|
||||
|
||||
export const ApiURL = {
|
||||
|
||||
pssscoreApi: `${BASE_URL}/mtd/pssscore`,
|
||||
getotpApi: `https://api1.parinaam.in/api/dabur/SendOTP`,
|
||||
verifyotpApi: `https://api1.parinaam.in/api/dabur/AuthenticateOTP`,
|
||||
storeDNAfilter:`https://api1.parinaam.in/api/dabur/StoreDNAfilter`,
|
||||
storeSearch :`https://api1.parinaam.in/api/dabur/StoreDNAstoreSearch`,
|
||||
storeInfo :`https://api1.parinaam.in/api/dabur/StoreDNAstoreInfo`,
|
||||
};
|
||||
@@ -0,0 +1,158 @@
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import axios from 'axios';
|
||||
import { Alert } from 'react-native';
|
||||
import { toastError } from '../components/Toast';
|
||||
|
||||
|
||||
|
||||
// Call this api function without token only for login
|
||||
export const request = async (url, formData) => {
|
||||
const header = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
return axios.post(url, formData, { headers: header })
|
||||
.then(response => {
|
||||
return response.data;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
return error;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Call this api function with token (POST type)
|
||||
// export const post = async (url: string, formData: any) => {
|
||||
// const header = {
|
||||
// Authorization: 'Bearer ',
|
||||
// Accept: 'application/json',
|
||||
// };
|
||||
// return AsyncStorage.getItem('token').then(async value => {
|
||||
|
||||
// const loginToken = JSON.parse(value as any);
|
||||
|
||||
// console.log('loginToken----->',loginToken);
|
||||
|
||||
// header.Authorization = `Bearer ${loginToken}`;
|
||||
|
||||
// try {
|
||||
// const response = await axios
|
||||
// .post(url, formData, { headers: header });
|
||||
// return response.data;
|
||||
// } catch (error) {
|
||||
// // console.log(url);
|
||||
// // console.log(formData);
|
||||
// console.log('AXIOS ERROR status: ', error);
|
||||
// }
|
||||
// });
|
||||
// };
|
||||
|
||||
export const post = async (url, formData) => {
|
||||
try {
|
||||
// const tokenValue = await AsyncStorage.getItem('token');
|
||||
// if (!tokenValue) {
|
||||
// console.error('No token found in AsyncStorage');
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// const loginToken = JSON.parse(tokenValue);
|
||||
// console.log('loginToken----->', loginToken);
|
||||
|
||||
const headers = {
|
||||
Authorization: ``,
|
||||
Accept: 'application/json',
|
||||
"X-API-Key":"f7fa9b09-ced8-4862-8cb7-5e7599d90fa2"
|
||||
};
|
||||
|
||||
// console.log('URL => ',url);
|
||||
// console.log('PARAMS => ',formData);
|
||||
|
||||
|
||||
const response = await axios.post(url, formData, { headers });
|
||||
return response.data;
|
||||
|
||||
} catch (error) {
|
||||
console.error('API Error:', error.response?.data || error.message);
|
||||
Alert.alert(`Alert`,`${error.response?.data?.message ?error.response?.data?.message:'Server Error'}`)
|
||||
// toastError('Alert',`${error.response?.data?.message}`)
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// CALL THIS API FUNCTION FOR GET TYPE API
|
||||
export const get = async (url) => {
|
||||
const header = {
|
||||
Authorization: 'Bearer ',
|
||||
Accept: 'application/json',
|
||||
};
|
||||
return AsyncStorage.getItem('token').then(async value => {
|
||||
const loginToken = JSON.parse(value);
|
||||
|
||||
// console.log('URL---->',url);
|
||||
// console.log('loginToken---->',loginToken);
|
||||
|
||||
header.Authorization = `Bearer ${loginToken}`;
|
||||
|
||||
try {
|
||||
const response = await axios
|
||||
.get(url, { headers: header });
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
if (error) {
|
||||
console.log('AXIOS ERROR status: ', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const uploadImage = async (url, formData) => {
|
||||
const header = {
|
||||
Authorization: 'Bearer ',
|
||||
Accept: 'application/json',
|
||||
'Content-Type': `multipart/form-data`,
|
||||
};
|
||||
|
||||
return AsyncStorage.getItem('userToken').then(value => {
|
||||
const token = JSON.parse(value);
|
||||
header.Authorization = `Bearer ${token}`;
|
||||
|
||||
let dataf = {
|
||||
uri: formData.path,
|
||||
type: formData.mime,
|
||||
name: formData.path,
|
||||
};
|
||||
var data = new FormData();
|
||||
data.append('image', dataf);
|
||||
console.log('header', header);
|
||||
return fetch(url, {
|
||||
headers: header,
|
||||
method: 'POST',
|
||||
body: data,
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
console.log('API', url);
|
||||
console.log('formData', formData);
|
||||
console.log('json data', json);
|
||||
return json;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('API', url);
|
||||
console.log('formData', formData);
|
||||
console.log('error ', error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const cityData = async () => {
|
||||
return AsyncStorage.getItem('cityshortname').then(e => {
|
||||
const user = JSON.parse(e);
|
||||
console.log('user,user', user);
|
||||
return user;
|
||||
});
|
||||
};
|
||||
|
After Width: | Height: | Size: 546 B |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 601 B |
|
After Width: | Height: | Size: 458 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 408 B |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 221 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Alert,
|
||||
View,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
} from 'react-native';
|
||||
import CustomModal from './CustomModal';
|
||||
import { useRoute } from '@react-navigation/native';
|
||||
import { GlobalTheme } from '../theme';
|
||||
|
||||
export const CustomAlert = (
|
||||
title = '',
|
||||
msg = '',
|
||||
onCancel = () => { },
|
||||
onDone = () => { }
|
||||
) => {
|
||||
Alert.alert(title, msg, [
|
||||
{
|
||||
text: 'Cancel',
|
||||
onPress: onCancel,
|
||||
style: 'cancel',
|
||||
},
|
||||
{ text: 'OK', onPress: onDone },
|
||||
]);
|
||||
};
|
||||
|
||||
export function ConfirmSaveAlert({
|
||||
showAlert = false,
|
||||
onCancelCallBack = () => { },
|
||||
onYesCallBack = () => { },
|
||||
msg = 'Do you really want to save data?',
|
||||
yesText = 'Yes',
|
||||
cancelText = 'No',
|
||||
}) {
|
||||
return (
|
||||
<CustomModal
|
||||
title={msg}
|
||||
showModal={showAlert}
|
||||
onClose={onCancelCallBack} // 👈 ensures modal closes when tapping close icon
|
||||
>
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 20, width: '60%' }}>
|
||||
<TouchableOpacity
|
||||
style={{
|
||||
paddingHorizontal: 25,
|
||||
paddingVertical: 10,
|
||||
backgroundColor: GlobalTheme.colors.primary,
|
||||
borderRadius: 5,
|
||||
}}
|
||||
onPress={onYesCallBack}
|
||||
>
|
||||
<Text style={{
|
||||
color: '#FFF',
|
||||
fontSize: GlobalTheme.typography.fontSize.small,
|
||||
fontWeight: GlobalTheme.typography.fontWeight.medium
|
||||
}}>
|
||||
{yesText}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={{
|
||||
paddingHorizontal: 25,
|
||||
paddingVertical: 10,
|
||||
backgroundColor: '#ccc',
|
||||
borderRadius: 5,
|
||||
}}
|
||||
onPress={onCancelCallBack}
|
||||
>
|
||||
<Text style={{
|
||||
color: '#000',
|
||||
fontSize: GlobalTheme.typography.fontSize.small,
|
||||
fontWeight: GlobalTheme.typography.fontWeight.medium
|
||||
}}>
|
||||
{cancelText}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</CustomModal>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export function ExitScreenAlert(props, callback) {
|
||||
Alert.alert(
|
||||
'',
|
||||
'Do you really want to exit the screen?',
|
||||
[
|
||||
{
|
||||
text: 'Cancel',
|
||||
onPress: () => { },
|
||||
style: 'cancel',
|
||||
},
|
||||
{
|
||||
text: 'OK',
|
||||
onPress: () => {
|
||||
if (typeof callback === 'function') callback();
|
||||
props.navigation.goBack();
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
// src/components/Background.js
|
||||
|
||||
import React from 'react';
|
||||
@@ -28,3 +29,38 @@ const styles = StyleSheet.create({
|
||||
});
|
||||
|
||||
export default Background;
|
||||
=======
|
||||
// src/components/Background.js
|
||||
|
||||
import React from 'react';
|
||||
import { StyleSheet, StatusBar, useColorScheme } from 'react-native';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { GlobalTheme } from '../theme';
|
||||
|
||||
const Background = ({ children, barcolor = 'light-content'}) => {
|
||||
const isDarkMode = useColorScheme() === 'dark';
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<StatusBar barStyle={'light-content'} />
|
||||
<LinearGradient colors={['#E3EBF8', '#ffffff']} start={{ x: 0.5, y: 0 }} end={{ x: 0.5, y: 1 }} style={styles.gradient}>
|
||||
{children}
|
||||
</LinearGradient>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: GlobalTheme.colors.primary,
|
||||
|
||||
},
|
||||
gradient: {
|
||||
flex: 1,
|
||||
// paddingHorizontal: 20,
|
||||
},
|
||||
});
|
||||
|
||||
export default Background;
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View, TouchableOpacity, Image, StyleSheet, Modal } from 'react-native';
|
||||
|
||||
import IMAGES from '../constants/Images';
|
||||
import CustomCamera from './CustomCamera';
|
||||
|
||||
const CameraScreen = () => {
|
||||
const [showCamera, setShowCamera] = useState(false);
|
||||
const [imageUri, setImageUri] = useState(null);
|
||||
|
||||
const handleImageCaptured = (photo) => {
|
||||
setImageUri(photo.uri);
|
||||
setShowCamera(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity onPress={() => setShowCamera(true)}>
|
||||
<Image
|
||||
source={imageUri ? { uri: imageUri } : IMAGES.cameraIcon}
|
||||
style={styles.imagePreview}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
{/* Fullscreen Modal Camera */}
|
||||
<Modal visible={showCamera} animationType="slide" presentationStyle="fullScreen">
|
||||
<CustomCamera
|
||||
onImageCaptured={handleImageCaptured}
|
||||
onClose={() => setShowCamera(false)}
|
||||
/>
|
||||
</Modal>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default CameraScreen;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
imagePreview: {
|
||||
width: 150,
|
||||
height: 150,
|
||||
borderRadius: 10,
|
||||
borderWidth: 1,
|
||||
borderColor: '#ccc',
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
import React from "react";
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
Modal,
|
||||
StyleSheet,
|
||||
} from "react-native";
|
||||
import { GlobalTheme } from "../theme";
|
||||
|
||||
const CustomAlertModal = ({
|
||||
showModal,
|
||||
title,
|
||||
message,
|
||||
buttons = [],
|
||||
style = {},
|
||||
titleStyle = {},
|
||||
messageStyle = {},
|
||||
children,
|
||||
|
||||
}) => {
|
||||
return (
|
||||
<Modal
|
||||
visible={showModal}
|
||||
transparent
|
||||
animationType="fade"
|
||||
>
|
||||
<View style={styles.backdrop}>
|
||||
<View style={[styles.modalContainer, style]}>
|
||||
<TouchableOpacity onPress={onClose} style={styles.closeIcon}>
|
||||
<Text style={{ fontSize: 18 }}>✕</Text>
|
||||
</TouchableOpacity>
|
||||
{title ? (
|
||||
<Text style={[styles.title, titleStyle]}>{title}</Text>
|
||||
) : null}
|
||||
|
||||
{message ? (
|
||||
<Text style={[styles.message, messageStyle]}>{message}</Text>
|
||||
) : null}
|
||||
|
||||
{children}
|
||||
|
||||
<View style={styles.buttonRow}>
|
||||
{buttons.map((btn, index) => (
|
||||
<TouchableOpacity
|
||||
key={index}
|
||||
style={[styles.button, { backgroundColor: btn.backgroundColor || GlobalTheme.colors.primary }]}
|
||||
onPress={btn.onPress}
|
||||
>
|
||||
<Text style={[styles.buttonText, { color: btn.color || "#FFF" }]}>
|
||||
{btn.label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomAlertModal;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
backdrop: {
|
||||
flex: 1,
|
||||
backgroundColor: "rgba(0,0,0,0.5)",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
modalContainer: {
|
||||
width: "80%",
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: 10,
|
||||
padding: 20,
|
||||
elevation: 5,
|
||||
},
|
||||
title: {
|
||||
fontSize: 18,
|
||||
fontWeight: "bold",
|
||||
marginBottom: 10,
|
||||
textAlign: "center",
|
||||
},
|
||||
message: {
|
||||
fontSize: 16,
|
||||
marginBottom: 20,
|
||||
textAlign: "center",
|
||||
},
|
||||
buttonRow: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
marginTop: 10,
|
||||
},
|
||||
button: {
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 5,
|
||||
marginLeft: 10,
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 14,
|
||||
},
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import { View, Text, TouchableOpacity } from 'react-native';
|
||||
|
||||
const CustomButton = ({ title, style, textstyle, onPress }) => {
|
||||
@@ -11,3 +12,18 @@ const CustomButton = ({ title, style, textstyle, onPress }) => {
|
||||
};
|
||||
|
||||
export default CustomButton;
|
||||
=======
|
||||
import { View, Text, TouchableOpacity } from 'react-native';
|
||||
|
||||
const CustomButton = ({ title, style, textstyle, onPress , disabled}) => {
|
||||
return (
|
||||
<TouchableOpacity disabled={disabled} onPress={onPress}>
|
||||
<View style={{ marginHorizontal: 0, paddingVertical: 10, ...style }}>
|
||||
<Text style={{ textAlign: 'center', ...textstyle }}>{title}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomButton;
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { View, Text, TouchableOpacity, Image, StyleSheet, PermissionsAndroid, Platform } from 'react-native';
|
||||
import { Camera, useCameraDevice } from 'react-native-vision-camera';
|
||||
import { useIsFocused } from '@react-navigation/native';
|
||||
|
||||
const CustomCamera = ({ onImageCaptured, onClose }) => {
|
||||
const cameraRef = useRef(null);
|
||||
const [hasPermission, setHasPermission] = useState(false);
|
||||
const [cameraPosition, setCameraPosition] = useState('back');
|
||||
const [capturedPhoto, setCapturedPhoto] = useState(null);
|
||||
const isFocused = useIsFocused();
|
||||
const device = useCameraDevice(cameraPosition);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const status = await Camera.requestCameraPermission();
|
||||
console.log(status, "Camera permission status");
|
||||
setHasPermission(status === 'granted');
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const takePhoto = async () => {
|
||||
if (cameraRef.current) {
|
||||
try {
|
||||
const photo = await cameraRef.current.takePhoto({
|
||||
flash: 'off',
|
||||
});
|
||||
const photoUri = Platform.OS === 'android' ? `file://${photo.path}` : photo.path;
|
||||
setCapturedPhoto(photoUri);
|
||||
} catch (error) {
|
||||
console.warn('Error taking photo:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const flipCamera = () => {
|
||||
setCameraPosition(prev => (prev === 'back' ? 'front' : 'back'));
|
||||
};
|
||||
|
||||
const confirmPhoto = async () => {
|
||||
if (onImageCaptured && capturedPhoto) {
|
||||
try {
|
||||
const imageInfo = await new Promise((resolve, reject) => {
|
||||
Image.getSize(
|
||||
capturedPhoto,
|
||||
(width, height) => resolve({ width, height }),
|
||||
(error) => reject(error)
|
||||
);
|
||||
});
|
||||
|
||||
const normalizedUri = Platform.OS === 'android' ? capturedPhoto : `file://${capturedPhoto}`;
|
||||
|
||||
onImageCaptured({
|
||||
uri: normalizedUri,
|
||||
width: imageInfo.width,
|
||||
height: imageInfo.height,
|
||||
storeData: {},
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('Failed to get image size:', e);
|
||||
onImageCaptured({
|
||||
uri: capturedPhoto,
|
||||
width: 0,
|
||||
height: 0,
|
||||
storeData: {},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const retakePhoto = () => {
|
||||
setCapturedPhoto(null);
|
||||
};
|
||||
|
||||
if (!device || !hasPermission) {
|
||||
return (
|
||||
<View style={styles.centered}>
|
||||
<Text>Loading Camera...</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{capturedPhoto ? (
|
||||
<View style={styles.previewContainer}>
|
||||
<Image source={{ uri: capturedPhoto }} style={styles.preview} resizeMode="contain" />
|
||||
<View style={styles.buttonRow}>
|
||||
<TouchableOpacity onPress={retakePhoto} style={styles.button}>
|
||||
<Text>Retake</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={confirmPhoto} style={styles.button}>
|
||||
<Text>Use Photo</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
{isFocused && !capturedPhoto && (
|
||||
<Camera
|
||||
style={StyleSheet.absoluteFill}
|
||||
device={device}
|
||||
isActive={true}
|
||||
photo={true}
|
||||
ref={cameraRef}
|
||||
/>
|
||||
)}
|
||||
<View style={styles.controls}>
|
||||
<TouchableOpacity onPress={flipCamera} style={styles.button}>
|
||||
<Text>Flip</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={takePhoto} style={styles.captureButton} />
|
||||
<TouchableOpacity onPress={onClose} style={styles.button}>
|
||||
<Text>Close</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomCamera;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: 'black',
|
||||
height: '100%',
|
||||
},
|
||||
previewContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
},
|
||||
preview: {
|
||||
width: '100%',
|
||||
height: '80%',
|
||||
},
|
||||
controls: {
|
||||
position: 'absolute',
|
||||
bottom: 30,
|
||||
width: '100%',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
alignItems: 'center',
|
||||
},
|
||||
captureButton: {
|
||||
width: 70,
|
||||
height: 70,
|
||||
borderRadius: 35,
|
||||
backgroundColor: 'white',
|
||||
borderWidth: 5,
|
||||
borderColor: 'gray',
|
||||
},
|
||||
button: {
|
||||
backgroundColor: 'white',
|
||||
padding: 10,
|
||||
borderRadius: 8,
|
||||
},
|
||||
buttonRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
marginTop: 20,
|
||||
width: '100%',
|
||||
},
|
||||
centered: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
// src/components/CustomDropdown.js
|
||||
import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { Dropdown } from 'react-native-element-dropdown';
|
||||
|
||||
const CustomDropdown = ({
|
||||
data = [],
|
||||
value,
|
||||
onChange,
|
||||
placeholder = '--Select--',
|
||||
containerStyle = {},
|
||||
}) => {
|
||||
return (
|
||||
<Dropdown
|
||||
style={[styles.dropdown, containerStyle]}
|
||||
data={data}
|
||||
labelField="label"
|
||||
valueField="value"
|
||||
placeholder={placeholder}
|
||||
placeholderStyle={{ fontSize: 13, fontWeight: '500', color: 'gray' }}
|
||||
value={value}
|
||||
selectedTextStyle={{fontSize: 13, fontWeight: '500', color: '#000'}}
|
||||
itemTextStyle={{fontSize:13,color:'#000'}}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
dropdown: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#ccc',
|
||||
borderRadius: 8,
|
||||
paddingHorizontal: 10,
|
||||
height: 45,
|
||||
marginBottom: 0, // ✅ no margin
|
||||
paddingBottom: 0, // ✅ no padding
|
||||
},
|
||||
});
|
||||
|
||||
export default CustomDropdown;
|
||||
@@ -0,0 +1,73 @@
|
||||
import { StyleSheet, Text, View, Image, TouchableOpacity } from 'react-native'
|
||||
import React from 'react'
|
||||
import { GlobalTheme } from '../theme'
|
||||
|
||||
const CustomHeader = ({ title, leftIcon, rightIcon, onLeftPress, onRightPress }) => {
|
||||
|
||||
return (
|
||||
<View style={styles.headerStyle}>
|
||||
{leftIcon &&
|
||||
<TouchableOpacity onPress={onLeftPress} style={styles.leftSection}>
|
||||
{leftIcon && <Image source={leftIcon} style={styles.leftIconStyle} />}
|
||||
</TouchableOpacity>
|
||||
}
|
||||
|
||||
<View style={styles.titleContainer}>
|
||||
<Text style={styles.headerText}>{title}</Text>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity onPress={onRightPress} style={styles.rightSection}>
|
||||
{rightIcon && <Image source={rightIcon} style={styles.rightIconStyle} />}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default CustomHeader
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
headerStyle: {
|
||||
height: '8%',
|
||||
backgroundColor: '#113F8C',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 15,
|
||||
},
|
||||
leftSection: {
|
||||
backgroundColor: '#295398',
|
||||
flexDirection: 'row',
|
||||
padding: 8,
|
||||
alignItems: 'center',
|
||||
borderRadius: 20
|
||||
// width: 70,
|
||||
},
|
||||
rightSection: {
|
||||
alignItems: 'flex-end',
|
||||
width: 40,
|
||||
},
|
||||
titleContainer: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
},
|
||||
headerText: {
|
||||
color: GlobalTheme.colors.white,
|
||||
fontSize: GlobalTheme.typography.fontSize.medium,
|
||||
fontWeight: '600',
|
||||
},
|
||||
backText: {
|
||||
marginLeft: 6,
|
||||
color: GlobalTheme.colors.white,
|
||||
fontSize: GlobalTheme.typography.fontSize.small,
|
||||
},
|
||||
leftIconStyle: {
|
||||
height: 20,
|
||||
width: 20,
|
||||
resizeMode: 'contain',
|
||||
},
|
||||
rightIconStyle: {
|
||||
height: 35,
|
||||
width: 35,
|
||||
resizeMode: 'contain',
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import { View, Text, Modal, StyleSheet, TouchableOpacity, Image } from 'react-native';
|
||||
import IMAGES from '../constants/Images';
|
||||
|
||||
const CustomModal = ({
|
||||
showModal,
|
||||
title,
|
||||
message,
|
||||
children,
|
||||
// onClose,
|
||||
style = {},
|
||||
titleStyle = {},
|
||||
messageStyle = {},
|
||||
hideDefaultClose = false, // 🔥 new prop
|
||||
onClose = null
|
||||
}) => {
|
||||
return (
|
||||
<Modal visible={showModal} transparent animationType="fade">
|
||||
<View style={styles.overlay}>
|
||||
<View style={[styles.modalContainer, style]}>
|
||||
{/* {onClose && (
|
||||
<TouchableOpacity onPress={onClose} style={styles.closeButton}>
|
||||
<Image source={IMAGES.crossIcon} style={styles.iconStyle} />
|
||||
</TouchableOpacity>
|
||||
)} */}
|
||||
|
||||
{/* <TouchableOpacity onPress={onClose} style={styles.closeButton}>
|
||||
<Image source={IMAGES.alert} style={styles.iconStyle} />
|
||||
</TouchableOpacity> */}
|
||||
<View style={{ marginTop: 10 }}>
|
||||
{title && <Text style={[styles.title, titleStyle]}>{title}</Text>}
|
||||
{message && <Text style={[styles.message, messageStyle]}>{message}</Text>}
|
||||
</View>
|
||||
|
||||
<View style={{ marginVertical: 10 }}>{children}</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomModal;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
flex: 1,
|
||||
backgroundColor: '#00000088',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
modalContainer: {
|
||||
width: '90%',
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: 12,
|
||||
padding: 15,
|
||||
alignItems: 'center',
|
||||
minHeight: 180,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
title: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
marginBottom: 6,
|
||||
color: "#000"
|
||||
},
|
||||
message: {
|
||||
fontSize: 14,
|
||||
color: '#555',
|
||||
textAlign: 'center',
|
||||
},
|
||||
closeButton: {
|
||||
alignSelf: 'center',
|
||||
marginBottom: 15
|
||||
},
|
||||
closeText: {
|
||||
color: '#fff',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
iconStyle: {
|
||||
height: 50,
|
||||
width: 50,
|
||||
resizeMode: 'contain',
|
||||
// tintColor:'red'
|
||||
}
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import { View, Text, TextInput, StyleSheet, } from 'react-native';
|
||||
import React, { useState } from 'react';
|
||||
import { GlobalTheme, Screen } from '../theme';
|
||||
@@ -83,3 +84,90 @@ const styles = StyleSheet.create({
|
||||
marginBottom: 16,
|
||||
},
|
||||
});
|
||||
=======
|
||||
import { View, Text, TextInput, StyleSheet, } from 'react-native';
|
||||
import React, { useState } from 'react';
|
||||
import { GlobalTheme, Screen } from '../theme';
|
||||
|
||||
const CustomTextInput = ({
|
||||
label,
|
||||
value,
|
||||
onChangeText,
|
||||
keyboardType,
|
||||
secureTextEntry,
|
||||
right,
|
||||
textstyle,
|
||||
viewstyle,
|
||||
maxLength
|
||||
}) => {
|
||||
const [isFocused, setFocused] = useState(false);
|
||||
const [hidepassword, setHidePassword] = useState(false);
|
||||
|
||||
const handleFocus = () => {
|
||||
setFocused(true);
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
setFocused(false);
|
||||
};
|
||||
|
||||
const inputStyle = {
|
||||
borderColor: isFocused ? '#2680EB' : '#DFDFDF',
|
||||
borderWidth: isFocused ? 2 : 1,
|
||||
backgroundColor: isFocused ? '#FFF' : '#Fff',
|
||||
borderRadius: GlobalTheme.borderRadius.md,
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.inputContainer}>
|
||||
<Text
|
||||
style={{
|
||||
color: GlobalTheme.colors.black,
|
||||
fontSize: GlobalTheme.typography.fontSize.small,
|
||||
fontWeight: GlobalTheme.typography.fontWeight.regular,
|
||||
marginTop: 10,
|
||||
marginHorizontal: 0,
|
||||
...textstyle,
|
||||
}}>
|
||||
{label}
|
||||
</Text>
|
||||
<View
|
||||
style={{
|
||||
borderRadius: GlobalTheme.borderRadius.lgg,
|
||||
// borderWidth: 6,
|
||||
borderColor: isFocused ? '#DFECFF' : 'transparent',
|
||||
marginTop: 8,
|
||||
...viewstyle,
|
||||
}}>
|
||||
<TextInput
|
||||
maxLength={maxLength}
|
||||
style={[styles.input, inputStyle]}
|
||||
value={value}
|
||||
onChangeText={onChangeText}
|
||||
keyboardType={keyboardType}
|
||||
autoCapitalize="none"
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
placeholder={`Enter your ${label.toLowerCase()}`}
|
||||
placeholderTextColor={'#555555'}
|
||||
/>
|
||||
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomTextInput;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
input: {
|
||||
height: 50,
|
||||
borderWidth: 1,
|
||||
padding: 10,
|
||||
color: GlobalTheme.colors.black,
|
||||
},
|
||||
inputContainer: {
|
||||
marginBottom: 16,
|
||||
},
|
||||
});
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
// colors.js
|
||||
export const colors = [
|
||||
{ bgColor: '#eddeb8', textColor: '#0c8fa5' },
|
||||
@@ -6,3 +7,13 @@ export const colors = [
|
||||
{ bgColor: '#d1c4e9', textColor: '#6A1B9A' },
|
||||
{ bgColor: '#ffe0b2', textColor: '#EF6C00' },
|
||||
];
|
||||
=======
|
||||
// colors.js
|
||||
export const colors = [
|
||||
{ bgColor: '#eddeb8', textColor: '#0c8fa5' },
|
||||
{ bgColor: '#dcf2ee', textColor: '#f39a19' },
|
||||
{ bgColor: '#eebdc3', textColor: '#ff5f5f' },
|
||||
{ bgColor: '#d1c4e9', textColor: '#6A1B9A' },
|
||||
{ bgColor: '#ffe0b2', textColor: '#EF6C00' },
|
||||
];
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import React from 'react';
|
||||
import AntDesign from 'react-native-vector-icons/AntDesign';
|
||||
import Entypo from 'react-native-vector-icons/Entypo';
|
||||
@@ -72,3 +73,80 @@ function CustomIcon({
|
||||
}
|
||||
|
||||
export default CustomIcon;
|
||||
=======
|
||||
import React from 'react';
|
||||
import AntDesign from 'react-native-vector-icons/AntDesign';
|
||||
import Entypo from 'react-native-vector-icons/Entypo';
|
||||
import EvilIcons from 'react-native-vector-icons/EvilIcons';
|
||||
import Feather from 'react-native-vector-icons/Feather';
|
||||
import FontAwesome from 'react-native-vector-icons/FontAwesome';
|
||||
import Fontisto from 'react-native-vector-icons/Fontisto';
|
||||
import Foundation from 'react-native-vector-icons/Foundation';
|
||||
import Ionicons from 'react-native-vector-icons/Ionicons';
|
||||
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||
import Octicons from 'react-native-vector-icons/Octicons';
|
||||
import Zocial from 'react-native-vector-icons/Zocial';
|
||||
import SimpleLineIcons from 'react-native-vector-icons/SimpleLineIcons';
|
||||
import { GlobalTheme } from '../theme';
|
||||
|
||||
|
||||
function CustomIcon({
|
||||
iconLibrary,
|
||||
icon,
|
||||
size = 22,
|
||||
color = GlobalTheme.colors.primary,
|
||||
style = {},
|
||||
...rest
|
||||
}) {
|
||||
let Icon = AntDesign;
|
||||
|
||||
switch (iconLibrary) {
|
||||
case 'AntDesign':
|
||||
Icon = AntDesign;
|
||||
break;
|
||||
case 'Entypo':
|
||||
Icon = Entypo;
|
||||
break;
|
||||
case 'EvilIcons':
|
||||
Icon = EvilIcons;
|
||||
break;
|
||||
case 'Feather':
|
||||
Icon = Feather;
|
||||
break;
|
||||
case 'FontAwesome':
|
||||
Icon = FontAwesome;
|
||||
break;
|
||||
case 'Fontisto':
|
||||
Icon = Fontisto;
|
||||
break;
|
||||
case 'Foundation':
|
||||
Icon = Foundation;
|
||||
break;
|
||||
case 'Ionicons':
|
||||
Icon = Ionicons;
|
||||
break;
|
||||
case 'MaterialIcons':
|
||||
Icon = MaterialIcons;
|
||||
break;
|
||||
case 'MaterialCommunityIcons':
|
||||
Icon = MaterialCommunityIcons;
|
||||
break;
|
||||
case 'Octicons':
|
||||
Icon = Octicons;
|
||||
break;
|
||||
case 'Zocial':
|
||||
Icon = Zocial;
|
||||
break;
|
||||
case 'SimpleLineIcons':
|
||||
Icon = SimpleLineIcons;
|
||||
break;
|
||||
default:
|
||||
Icon = AntDesign;
|
||||
}
|
||||
|
||||
return <Icon name={icon} size={size} style={style} color={color} {...rest} />;
|
||||
}
|
||||
|
||||
export default CustomIcon;
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
const IMAGES = {
|
||||
// AppLogo: require('../assets/Images/logo.png'),
|
||||
AppLogo: require('../assets/Images/logo.png'),
|
||||
@@ -10,3 +11,41 @@ const IMAGES = {
|
||||
};
|
||||
|
||||
export default IMAGES;
|
||||
=======
|
||||
const IMAGES = {
|
||||
// AppLogo: require('../assets/Images/logo.png'),
|
||||
AppLogo: require('../assets/Images/logo.png'),
|
||||
filterIcon: require('../assets/Icons/filter.png'),
|
||||
menuIcon: require('../assets/Icons/menu.png'),
|
||||
pluscircleIcon: require('../assets/Icons/pluscircle.png'),
|
||||
rightArrowIcon: require('../assets/Icons/rightarrow.png'),
|
||||
leftArrowIcon: require('../assets/Icons/leftarrow.png'),
|
||||
crossIcon: require('../assets/Icons/cross.png'),
|
||||
searchIcon: require('../assets/Icons/search.png'),
|
||||
greenCameraIcon: require('../assets/Icons/green_camera.png'),
|
||||
redCameraIcon: require('../assets/Icons/red_camera.png'),
|
||||
normalCameraIcon: require('../assets/Icons/camera.png'),
|
||||
WhiteBGIMG: require('../assets/Images/white.png'),
|
||||
backIcon:require('../assets/Icons/backIcon.png'),
|
||||
greenTick:require('../assets/Icons/greenTick.png'),
|
||||
storeIcon:require('../assets/Icons/store.png'),
|
||||
reportIcon:require('../assets/Icons/report.png'),
|
||||
downIcon:require('../assets/Icons/down.png'),
|
||||
upArrow:require('../assets/Icons/uparrow.png'),
|
||||
footerImage : require('../assets/Images/footerImage.png'),
|
||||
Logo: require('../assets/Images/applogo.png'),
|
||||
WelcomeBackground: require('../assets/Images/welcomebackground.png'),
|
||||
Welcomelogo: require('../assets/Images/welcomelogo.png'),
|
||||
AuthTopBG: require('../assets/Images/logintopBG.png'),
|
||||
AuthTopBGNew: require('../assets/Images/appLogoNew.png'),
|
||||
AuthBottomBG: require('../assets/Images/loginbottomBG.png'),
|
||||
projectIdImg: require('../assets/Images/projectId.png'),
|
||||
PerformicsLogo: require('../assets/Images/PerformicsLogo.png'),
|
||||
dotsIcon: require('../assets/Icons/dots.png'),
|
||||
pending: require('../assets/Icons/wall-clock.png'),
|
||||
alert: require('../assets/Icons/question-mark.png'),
|
||||
|
||||
};
|
||||
|
||||
export default IMAGES;
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
import { useWindowDimensions, Dimensions, View, Text, ActivityIndicator, StyleSheet, Image, StatusBar } from 'react-native';
|
||||
import LoaderKit from 'react-native-loader-kit'
|
||||
|
||||
|
||||
const Loader = ({ visible = false , loadingtext = 'Loading...'}) => {
|
||||
const { width, height } = useWindowDimensions();
|
||||
const height2 = height + 100
|
||||
|
||||
return (
|
||||
visible && (
|
||||
<View style={[style.container, { width }]}>
|
||||
<View style={{ marginTop: 20,alignItems:'center' }}>
|
||||
<LoaderKit
|
||||
style={{ width: 70, height: 70 }}
|
||||
name={'BallSpinFadeLoader'} // Optional: see list of animations below
|
||||
color={'#fff'} // Optional: color can be: 'red', 'green',... or '#ddd', '#ffffff',...
|
||||
/>
|
||||
<Text style={{ color: '#fff', fontSize: 14, textAlign: 'center', marginTop: 10, fontWeight: '500' }}>{loadingtext}</Text>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const style = StyleSheet.create({
|
||||
label: {
|
||||
marginLeft: 10,
|
||||
fontSize: 16,
|
||||
color: 'white',
|
||||
fontFamily: "FuturaPT-Book",
|
||||
},
|
||||
loader: {
|
||||
height: 100,
|
||||
backgroundColor: 'transparent',
|
||||
marginHorizontal: 50,
|
||||
borderRadius: 5,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 0,
|
||||
},
|
||||
container: {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
position: 'absolute',
|
||||
zIndex: 1050,
|
||||
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default Loader;
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
// Toast.js
|
||||
import Toast from 'react-native-toast-message';
|
||||
const showToast = (type, text1, text2) => {
|
||||
@@ -33,4 +34,41 @@ const toastError = (text1, text2) => {
|
||||
showToast('error', text1, text2);
|
||||
};
|
||||
|
||||
=======
|
||||
// Toast.js
|
||||
import Toast from 'react-native-toast-message';
|
||||
const showToast = (type, text1, text2) => {
|
||||
Toast.show({
|
||||
type: type,
|
||||
text1: text1,
|
||||
text2: text2,
|
||||
position: 'top',
|
||||
visibilityTime: 3000,
|
||||
autoHide: true,
|
||||
topOffset: 30,
|
||||
bottomOffset: 40,
|
||||
text1Style: {
|
||||
fontSize: 15, // Custom font size for the main text
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
text2Style: {
|
||||
fontSize: 14, // Custom font size for the secondary text
|
||||
color:'gray'
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const ToastComponent = () => {
|
||||
return <Toast innerRef={(ref) => Toast.setRef(ref)} />;
|
||||
};
|
||||
|
||||
const toastSuccess = (text1, text2) => {
|
||||
showToast('success', text1, text2);
|
||||
};
|
||||
|
||||
const toastError = (text1, text2) => {
|
||||
showToast('error', text1, text2);
|
||||
};
|
||||
|
||||
>>>>>>> dabur-store-dna
|
||||
export { ToastComponent, toastSuccess, toastError };
|
||||
@@ -0,0 +1,9 @@
|
||||
import * as RNFS from 'react-native-fs';
|
||||
|
||||
const common_ImagePath=`${RNFS.DocumentDirectoryPath}/`;
|
||||
const ImageFolderPath=`${common_ImagePath}PerformicsAllImages/`;
|
||||
const FeedbackImagesFolderPath=ImageFolderPath+`Store_DNA/`;
|
||||
const defUploadFolder='BulkImages';
|
||||
|
||||
export {common_ImagePath ,FeedbackImagesFolderPath , ImageFolderPath ,defUploadFolder};
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
import SQLite from 'react-native-sqlite-storage';
|
||||
|
||||
const db = SQLite.openDatabase({ name: 'feedback.db', location: 'default' });
|
||||
|
||||
// Initialize FeedbackCategory table (full structure from mockDataFlat)
|
||||
export const initTables = () => {
|
||||
db.transaction(tx => {
|
||||
// Table for API-fetched question/answer data
|
||||
tx.executeSql(`
|
||||
CREATE TABLE IF NOT EXISTS FeedbackCategory (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
MenuId INTEGER,
|
||||
SurveyId INTEGER,
|
||||
SurveyName TEXT,
|
||||
CategoryId INTEGER,
|
||||
Category TEXT,
|
||||
CategorySequence INTEGER,
|
||||
QuestionId INTEGER,
|
||||
Question TEXT,
|
||||
QuestionType TEXT,
|
||||
QuestionTypeNew TEXT,
|
||||
QEnable BOOLEAN,
|
||||
QuestionImageAllow BOOLEAN,
|
||||
QuestionImageMandatory BOOLEAN,
|
||||
LengthValidation BOOLEAN,
|
||||
MinLength INTEGER,
|
||||
MaxLength INTEGER,
|
||||
OTP BOOLEAN,
|
||||
DateRange TEXT,
|
||||
QuestionSequence INTEGER,
|
||||
AnswerId INTEGER,
|
||||
Answer TEXT,
|
||||
AnswerSequence INTEGER,
|
||||
ImageAllow1 BOOLEAN,
|
||||
ImageAllow2 BOOLEAN,
|
||||
Image1Mandatory BOOLEAN,
|
||||
Image2Mandatory BOOLEAN,
|
||||
EnableQuestion TEXT,
|
||||
DisableQuestion TEXT,
|
||||
ShowCat INTEGER,
|
||||
SubCategoryId INTEGER,
|
||||
SubCategory TEXT,
|
||||
SubCategorySequence INTEGER,
|
||||
CalFormula TEXT,
|
||||
RefImage TEXT,
|
||||
QuestionRefImage TEXT
|
||||
);
|
||||
`);
|
||||
|
||||
// Table for locally saved user responses
|
||||
tx.executeSql(`
|
||||
CREATE TABLE IF NOT EXISTS FeedBackLocalTable (
|
||||
KEY_ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
STORE_ID INTEGER,
|
||||
VISIT_DATE NVARCHAR,
|
||||
SURVEY_ID INTEGER,
|
||||
CATEGORY_ID INTEGER,
|
||||
SUB_CATEGORY_ID INTEGER,
|
||||
QUESTION NVARCHAR,
|
||||
QUESTION_ID INTEGER,
|
||||
QUESTION_TYPE NVARCHAR,
|
||||
ANSWER NVARCHAR,
|
||||
ANSWER_ID INTEGER,
|
||||
QuestionImageAllow INTEGER,
|
||||
IMAGE_ALLOW1 INTEGER,
|
||||
IMAGE1 NVARCHAR,
|
||||
IMAGE_ALLOW2 INTEGER,
|
||||
IMAGE2 NVARCHAR,
|
||||
MULTI_OPTIONS_IDS NVARCHAR,
|
||||
IS_DISABLED NVARCHAR,
|
||||
IS_ENABLED NVARCHAR,
|
||||
MENU_ID INTEGER,
|
||||
ADDED_DATE TEXT,
|
||||
VerifyOTP INTEGER,
|
||||
QUESTION_REF_IMAGE TEXT,
|
||||
STATUS NVARCHAR
|
||||
);
|
||||
`);
|
||||
|
||||
//
|
||||
|
||||
tx.executeSql(`
|
||||
CREATE TABLE IF NOT EXISTS StoreInfoDNALocal (
|
||||
KEY_ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
VISIT_DATE NVARCHAR ,
|
||||
StoreId INTEGER,
|
||||
StoreName NVARCHAR,
|
||||
ChainName NVARCHAR,
|
||||
Address NVARCHAR,
|
||||
Pincode NVARCHAR,
|
||||
CityName NVARCHAR,
|
||||
StateName NVARCHAR,
|
||||
StoreType NVARCHAR,
|
||||
UPLOAD_STATUS NVARCHAR
|
||||
);
|
||||
`);
|
||||
});
|
||||
};
|
||||
|
||||
// Bulk insert for FeedbackCategory (full schema)
|
||||
export const bulkInsertData = (tableName, dataArray) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!Array.isArray(dataArray) || dataArray.length === 0) {
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tableName !== 'FeedbackCategory') {
|
||||
reject(`Unsupported table: ${tableName}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const escape = str => (str || '').toString().replace(/'/g, "''");
|
||||
|
||||
const values = dataArray.map(item => `(
|
||||
'${item.MenuId}', '${item.SurveyId}', '${escape(item.SurveyName)}',
|
||||
'${item.CategoryId}', '${escape(item.Category)}', '${item.CategorySequence}',
|
||||
'${item.QuestionId}', '${escape(item.Question)}', '${escape(item.QuestionType)}',
|
||||
'${escape(item.QuestionTypeNew)}', '${item.QEnable ? 1 : 0}', '${item.QuestionImageAllow ? 1 : 0}',
|
||||
'${item.QuestionImageMandatory ? 1 : 0}', '${item.LengthValidation ? 1 : 0}', '${item.MinLength}',
|
||||
'${item.MaxLength}', '${item.OTP ? 1 : 0}', '${escape(item.DateRange)}', '${item.QuestionSequence}',
|
||||
'${item.AnswerId}', '${escape(item.Answer)}', '${item.AnswerSequence}',
|
||||
'${item.ImageAllow1 ? 1 : 0}', '${item.ImageAllow2 ? 1 : 0}', '${item.Image1Mandatory ? 1 : 0}',
|
||||
'${item.Image2Mandatory ? 1 : 0}', '${escape(item.EnableQuestion)}', '${escape(item.DisableQuestion)}',
|
||||
'${item.ShowCat}', '${item.SubCategoryId}', '${escape(item.SubCategory)}', '${item.SubCategorySequence}',
|
||||
'${escape(item.CalFormula)}', '${escape(item.RefImage)}', '${escape(item.QuestionRefImage)}'
|
||||
)`);
|
||||
|
||||
const sql = `
|
||||
INSERT INTO FeedbackCategory (
|
||||
MenuId, SurveyId, SurveyName, CategoryId, Category, CategorySequence,
|
||||
QuestionId, Question, QuestionType, QuestionTypeNew, QEnable, QuestionImageAllow,
|
||||
QuestionImageMandatory, LengthValidation, MinLength, MaxLength, OTP, DateRange,
|
||||
QuestionSequence, AnswerId, Answer, AnswerSequence, ImageAllow1, ImageAllow2,
|
||||
Image1Mandatory, Image2Mandatory, EnableQuestion, DisableQuestion,
|
||||
ShowCat, SubCategoryId, SubCategory, SubCategorySequence,
|
||||
CalFormula, RefImage, QuestionRefImage
|
||||
)
|
||||
VALUES ${values.join(',')}
|
||||
`;
|
||||
|
||||
db.transaction(tx => {
|
||||
tx.executeSql(`DELETE FROM FeedbackCategory`, [], () => {
|
||||
tx.executeSql(sql, [], () => {
|
||||
console.log(`${tableName} inserted successfully`);
|
||||
resolve(true);
|
||||
}, (e1, err) => {
|
||||
console.log('Insert error:', err);
|
||||
reject(err);
|
||||
});
|
||||
}, (err) => {
|
||||
console.log('Delete error:', err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Generic SELECT
|
||||
export const getAllFromTable = (tableName) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.transaction(tx => {
|
||||
tx.executeSql(`SELECT * FROM ${tableName}`, [], (tx, results) => {
|
||||
const rows = [];
|
||||
for (let i = 0; i < results.rows.length; i++) {
|
||||
rows.push(results.rows.item(i));
|
||||
}
|
||||
resolve(rows);
|
||||
}, (err) => {
|
||||
console.log(`Select error on ${tableName}:`, err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default db;
|
||||
@@ -0,0 +1,455 @@
|
||||
import { defUploadFolder, FeedbackImagesFolderPath, ImageFolderPath } from "./constant";
|
||||
import moment from 'moment';
|
||||
import { Image } from "react-native";
|
||||
import * as RNFS from 'react-native-fs';
|
||||
import PhotoManipulator from 'react-native-photo-manipulator';
|
||||
import ImageResizer from '@bam.tech/react-native-image-resizer';
|
||||
import { toastError } from "./Toast";
|
||||
import IMAGES from "./Images";
|
||||
|
||||
export async function CreateImageFolders() {
|
||||
console.log('CreateDirKPIimg:');
|
||||
await RNFS.mkdir(ImageFolderPath);
|
||||
await RNFS.mkdir(FeedbackImagesFolderPath);
|
||||
}
|
||||
|
||||
export async function getImage(imgdata , userId , item) {
|
||||
console.log("item===", item);
|
||||
if (typeof imgdata == 'object' && imgdata.errorCode === 'camera_unavailable') {
|
||||
console.log("camera not available");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof imgdata == 'object' && imgdata.uri != null) {
|
||||
try {
|
||||
const imgurl = imgdata.uri.startsWith('file://') ? imgdata.uri : `file://${imgdata.uri}`;
|
||||
|
||||
// 🔹 Get dynamic image width and height
|
||||
const { width: imgWidth, height: imgHeight } = await new Promise((resolve, reject) => {
|
||||
Image.getSize(imgurl, (width, height) => resolve({ width, height }), reject);
|
||||
});
|
||||
|
||||
console.log("Image size ===", imgWidth, imgHeight);
|
||||
|
||||
const picture_clickedd = new Date();
|
||||
const picture_clicked_time = moment(picture_clickedd).format('MM-DD-YYYY HH:mm:ss');
|
||||
const picture_clicked_time_for_pic_name = moment(picture_clickedd).format('MMDDYYYY_HHmmss');
|
||||
|
||||
const filename = userId + picture_clicked_time_for_pic_name + '.jpg';
|
||||
const mark_text1 = picture_clicked_time;
|
||||
const mark_text2 = `User Id: ${userId} | StoreName: ${item?.StoreName} | StoreId: ${item?.StoreId} | Date: ${picture_clicked_time}`;
|
||||
const imagePath = `${FeedbackImagesFolderPath}${filename}`;
|
||||
|
||||
console.log("Copying image to:", imagePath);
|
||||
await RNFS.copyFile(imgurl.replace('file://', ''), imagePath);
|
||||
|
||||
const restore_data = {
|
||||
destFilePath: imagePath,
|
||||
imgurl: imagePath,
|
||||
imgWidth,
|
||||
imgHeight,
|
||||
mark_text1,
|
||||
mark_text2,
|
||||
};
|
||||
|
||||
const is_restored = await restore_ImageWithMetaData(restore_data, imgdata);
|
||||
console.log("Image restored:", is_restored);
|
||||
console.log("Image imagePath:", imagePath);
|
||||
|
||||
return { finalPath: imagePath };
|
||||
} catch (err) {
|
||||
console.log('❌ ERROR in getImage:', err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// const waterMarkText = async (text = '', imgurl = '', text2 = '', imgWidth = 0, imgHeight = 0) => {
|
||||
|
||||
// const wrapText = (text2, maxCharsPerLine) => {
|
||||
// const words = text2.split(' ');
|
||||
// const lines = [];
|
||||
// let currentLine = '';
|
||||
|
||||
// for (let word of words) {
|
||||
// if ((currentLine + word).length <= maxCharsPerLine) {
|
||||
// currentLine += (currentLine ? ' ' : '') + word;
|
||||
// } else {
|
||||
// lines.push(currentLine);
|
||||
// currentLine = word;
|
||||
// }
|
||||
// }
|
||||
// console.log('currentLine====>', currentLine);
|
||||
|
||||
// if (currentLine) lines.push(currentLine);
|
||||
// return lines;
|
||||
// };
|
||||
|
||||
|
||||
|
||||
// try {
|
||||
// if (!imgurl) throw new Error("Image URL is missing");
|
||||
|
||||
// const cleanPath = imgurl.startsWith('file://') ? imgurl : `file://${imgurl}`;
|
||||
// const stripHeight = 250;
|
||||
|
||||
// // ✅ Copy from Android assets folder to filesystem
|
||||
// const destPath = `${RNFS.CachesDirectoryPath}/white.png`;
|
||||
// const exists = await RNFS.exists(destPath);
|
||||
|
||||
// if (!exists) {
|
||||
// await RNFS.copyFileAssets('white.png', destPath); // white.png is in android/app/src/main/assets
|
||||
// }
|
||||
|
||||
// const stripFilePath = `file://${destPath}`;
|
||||
|
||||
// // ✅ Step 2: Overlay strip
|
||||
// const imageWithStrip = await PhotoManipulator.overlayImage(
|
||||
// cleanPath,
|
||||
// stripFilePath,
|
||||
// { x: 0, y: imgHeight - stripHeight },
|
||||
// 'image/jpeg'
|
||||
// );
|
||||
|
||||
// // ✅ Step 3: Add text
|
||||
// const lines = wrapText(text2, 80); // You can adjust 25 depending on font size / image width
|
||||
|
||||
// const texts = [
|
||||
// {
|
||||
// position: { x: 30, y: 50 },
|
||||
// text: text,
|
||||
// textSize: 80,
|
||||
// color: '#FF0000',
|
||||
// },
|
||||
// ...lines.map((line, i) => ({
|
||||
// position: { x: 30, y: imgHeight - 120 + i * 90 }, // 90 = line height
|
||||
// text: line,
|
||||
// textSize: 80,
|
||||
// color: '#000000',
|
||||
// })),
|
||||
// ];
|
||||
|
||||
// const finalImagePath = await PhotoManipulator.printText(imageWithStrip, texts, 'image/jpeg');
|
||||
// const uri = finalImagePath.startsWith('file://') ? finalImagePath : `file://${finalImagePath}`;
|
||||
|
||||
// return { success: true, uri };
|
||||
// } catch (error) {
|
||||
// console.error("❌ Error in waterMarkText:", error);
|
||||
// return { success: false, error: error.message };
|
||||
// }
|
||||
// };
|
||||
|
||||
const waterMarkText = async (text = '', imgurl = '', text2 = '', imgWidth = 0, imgHeight = 0) => {
|
||||
const wrapTextByImageWidth = (text, imgWidth, fontSize = 80, padding = 60) => {
|
||||
const avgCharWidth = fontSize * 0.6; // Conservative estimate
|
||||
const maxChars = Math.floor((imgWidth - padding) / avgCharWidth);
|
||||
|
||||
const words = text.split(' ');
|
||||
const lines = [];
|
||||
let currentLine = '';
|
||||
|
||||
for (let word of words) {
|
||||
if ((currentLine + ' ' + word).trim().length <= maxChars) {
|
||||
currentLine += (currentLine ? ' ' : '') + word;
|
||||
} else {
|
||||
lines.push(currentLine);
|
||||
currentLine = word;
|
||||
}
|
||||
}
|
||||
if (currentLine) lines.push(currentLine);
|
||||
|
||||
return lines;
|
||||
};
|
||||
|
||||
try {
|
||||
if (!imgurl) throw new Error("Image URL is missing");
|
||||
|
||||
const cleanPath = imgurl.startsWith('file://') ? imgurl : `file://${imgurl}`;
|
||||
const stripHeight = 250;
|
||||
|
||||
// ✅ White strip setup
|
||||
const destPath = `${RNFS.CachesDirectoryPath}/white.png`;
|
||||
const exists = await RNFS.exists(destPath);
|
||||
if (!exists) {
|
||||
await RNFS.copyFileAssets('white.png', destPath);
|
||||
}
|
||||
|
||||
const stripFilePath = `file://${destPath}`;
|
||||
|
||||
// ✅ Overlay strip at bottom
|
||||
const imageWithStrip = await PhotoManipulator.overlayImage(
|
||||
cleanPath,
|
||||
stripFilePath,
|
||||
{ x: 0, y: imgHeight - stripHeight },
|
||||
'image/jpeg'
|
||||
);
|
||||
|
||||
// ✅ Wrap text2 according to image width
|
||||
const fontSize = 80;
|
||||
const lineHeight = 90;
|
||||
const padding = 60;
|
||||
const lines = wrapTextByImageWidth(text2, imgWidth, fontSize, padding);
|
||||
|
||||
const startY = imgHeight - stripHeight + 40; // Add top padding inside strip
|
||||
|
||||
// ✅ Build text objects
|
||||
const texts = [
|
||||
{
|
||||
position: { x: 30, y: 50 },
|
||||
text: text,
|
||||
textSize: fontSize,
|
||||
color: '#FF0000',
|
||||
},
|
||||
...lines.map((line, index) => ({
|
||||
position: { x: 30, y: startY + index * lineHeight },
|
||||
text: line,
|
||||
textSize: fontSize,
|
||||
color: '#000000',
|
||||
})),
|
||||
];
|
||||
|
||||
const finalImagePath = await PhotoManipulator.printText(imageWithStrip, texts, 'image/jpeg');
|
||||
const uri = finalImagePath.startsWith('file://') ? finalImagePath : `file://${finalImagePath}`;
|
||||
|
||||
return { success: true, uri };
|
||||
} catch (error) {
|
||||
console.error("❌ Error in waterMarkText:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
export async function restore_ImageWithMetaData(data, imgdata) {
|
||||
let { destFilePath, imgurl, imgWidth, imgHeight, mark_text1, mark_text2 } = data;
|
||||
|
||||
try {
|
||||
const cleanImgUrl = imgurl.startsWith('file://') ? imgurl : `file://${imgurl}`;
|
||||
const rawPath = cleanImgUrl.replace('file://', '');
|
||||
|
||||
// Fallback to imgdata values
|
||||
if (!imgWidth && imgdata?.width) imgWidth = imgdata.width;
|
||||
if (!imgHeight && imgdata?.height) imgHeight = imgdata.height;
|
||||
|
||||
if (!imgWidth || !imgHeight || !rawPath) {
|
||||
console.log('❌ Invalid resize parameters', { imgWidth, imgHeight, imgurl });
|
||||
return false;
|
||||
}
|
||||
|
||||
const fileExists = await RNFS.exists(rawPath);
|
||||
if (!fileExists) {
|
||||
console.log('❌ Image file not found at:', rawPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 1: Resize (only if dimensions are large)
|
||||
const resized = await resizeImage(cleanImgUrl, imgWidth, imgHeight);
|
||||
if (!resized.success) {
|
||||
console.log("❌ Resize failed:", resized.error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const resizedUrl = resized.uri;
|
||||
const resizedData = resized.imgData;
|
||||
const resizedWidth = parseInt(resizedData.width);
|
||||
const resizedHeight = parseInt(resizedData.height);
|
||||
|
||||
// Step 2: Watermark
|
||||
const marked = await waterMarkText(mark_text1, resizedUrl, mark_text2, resizedWidth, resizedHeight);
|
||||
if (!marked.success || !marked.uri) {
|
||||
console.log("❌ Watermark failed:", marked.error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const markedUrl = marked.uri;
|
||||
|
||||
// Step 3: Reduce pixels (if needed)
|
||||
const reduced = await reducePixels(markedUrl, resizedWidth, resizedHeight);
|
||||
if (!reduced.success) {
|
||||
console.error("❌ Pixel reduction failed:", reduced.error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const finalUri = reduced.uri;
|
||||
|
||||
// Step 4: Copy to destination
|
||||
const exists = await RNFS.exists(destFilePath);
|
||||
if (exists) await RNFS.unlink(destFilePath);
|
||||
|
||||
await RNFS.copyFile(finalUri, destFilePath);
|
||||
console.log("✅ Image with metadata saved at:", destFilePath);
|
||||
|
||||
// Step 5: DO NOT delete destFilePath — it's the one used in UI!
|
||||
// Optional: Clean up temp files
|
||||
const filesToDelete = [rawPath, resizedUrl, markedUrl]
|
||||
.map(f => f.replace('file://', ''))
|
||||
.filter(f => f !== destFilePath); // protect final image
|
||||
|
||||
await Promise.all(
|
||||
filesToDelete.map(f => RNFS.unlink(f).catch(() => {}))
|
||||
);
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log("❌ restore_ImageWithMetaData failed:", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function bytesToSize(bytes) {
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (bytes === 0) return '0 Byte';
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
export const resizeImage = async (imgurl = '', imgWidth, imgHeight) => {
|
||||
if (!imgurl || !imgWidth || !imgHeight || isNaN(imgWidth) || isNaN(imgHeight)) {
|
||||
console.log("❌ resizeImage: Invalid parameters", { imgurl, imgWidth, imgHeight });
|
||||
return { success: false, error: 'Invalid resize parameters' };
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await ImageResizer.createResizedImage(
|
||||
imgurl, imgWidth, imgHeight, 'JPEG', 100, 0, null, false, { onlyScaleDown: true }
|
||||
);
|
||||
let size = bytesToSize(response.size);
|
||||
console.log('✅ resizeImage: reduced filesize:', size);
|
||||
return { success: true, uri: response.uri, imgData: response };
|
||||
} catch (err) {
|
||||
console.log("❌ resizeImage error:", err);
|
||||
return { success: false, error: err };
|
||||
}
|
||||
};
|
||||
|
||||
export const reducePixels = async (imgurl = '', imgWidth, imgHeight) => {
|
||||
if (!imgurl || !imgWidth || !imgHeight || isNaN(imgWidth) || isNaN(imgHeight)) {
|
||||
console.log("❌ reducePixels: Invalid parameters", { imgurl, imgWidth, imgHeight });
|
||||
return { success: false, error: 'Invalid pixel reduction parameters' };
|
||||
}
|
||||
|
||||
// Resize only if image is large
|
||||
if (imgWidth > 1100 || imgHeight > 1100) {
|
||||
try {
|
||||
const response = await ImageResizer.createResizedImage(
|
||||
imgurl, 1100, 1100, 'JPEG', 40, 0, null, false,
|
||||
{ onlyScaleDown: true, mode: 'contain' }
|
||||
);
|
||||
let size = bytesToSize(response.size);
|
||||
console.log('✅ reducePixels: reduced size to', size, '→', response.width, 'x', response.height);
|
||||
return { success: true, uri: response.uri };
|
||||
} catch (err) {
|
||||
console.log("❌ reducePixels error:", err);
|
||||
return { success: false, error: err };
|
||||
}
|
||||
}
|
||||
|
||||
// No resizing needed
|
||||
return { success: true, uri: imgurl };
|
||||
};
|
||||
|
||||
|
||||
// end image resize and reduce pixel function
|
||||
|
||||
|
||||
export async function getAllFileForAFolder(path, Up_Foldername, KPIName = '') {
|
||||
|
||||
let subFolPath = path;
|
||||
const ImgFiles = await RNFS.readDir(subFolPath);
|
||||
Up_Foldername = Up_Foldername != null && Up_Foldername != '' ? Up_Foldername : defUploadFolder;
|
||||
|
||||
console.log("ImgFiles in ", subFolPath, ' are:', ImgFiles.length);
|
||||
let allKPIfiles = [];
|
||||
return new Promise.all(
|
||||
ImgFiles.map(async sfile => {
|
||||
if (sfile.isFile() && (sfile.name.includes('jpg') || sfile.name.includes('jpeg') || sfile.name.includes('png'))) {
|
||||
|
||||
let file = {
|
||||
uri: sfile.path,
|
||||
type: 'image/jpeg',
|
||||
name: sfile.name,
|
||||
filetype: 'image',
|
||||
folderName: Up_Foldername,
|
||||
}
|
||||
allKPIfiles.push(file);
|
||||
return file;
|
||||
}
|
||||
else if (sfile.isDirectory() && sfile.name == 'Recordings') {
|
||||
let recordingsPath = sfile.path;
|
||||
let recordingFiles = await RNFS.readDir(recordingsPath);
|
||||
let VoiceUp_FolderPath = KPIName != null && KPIName != '' ? FolderForREC[KPIName] : defUploadFolder;
|
||||
return new Promise.all(
|
||||
recordingFiles.map(async rfile => {
|
||||
|
||||
if (rfile.isFile() && (rfile.name.includes('m4a') || rfile.name.includes('mp3'))) {
|
||||
|
||||
let file = {
|
||||
uri: rfile.path,
|
||||
type: (Platform.OS == 'ios' ? 'audio/m4a' : 'audio/mp3'),
|
||||
name: rfile.name,
|
||||
filetype: 'audio',
|
||||
folderName: VoiceUp_FolderPath,
|
||||
}
|
||||
allKPIfiles.push(file);
|
||||
return file;
|
||||
}
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
|
||||
})
|
||||
).then((val2) => {
|
||||
return val2;
|
||||
});
|
||||
}
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
})
|
||||
).then((val1) => {
|
||||
console.log('getAllFileForAFolder', val1);
|
||||
return allKPIfiles;
|
||||
}).catch((err) => {
|
||||
console.log('error in getAllFileForAFolder', err);
|
||||
return allKPIfiles;
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// download
|
||||
|
||||
|
||||
export async function getDownloadJson(url, indata) {
|
||||
let postdata = {
|
||||
Downloadtype: indata.Downloadtype,
|
||||
Username: indata.username,
|
||||
Param1: '',
|
||||
Param2: '',
|
||||
};
|
||||
console.log(postdata);
|
||||
return await fetch(url, {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(postdata),
|
||||
})
|
||||
.then(response => {
|
||||
return response.json();
|
||||
})
|
||||
.then((res) => {
|
||||
// console.log('res',res);
|
||||
let resd = JSON.parse(res);
|
||||
return resd;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('getdashboard error', err);
|
||||
return { success: false };
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
|
||||
|
||||
export async function UploadImagesWithoutWait(postData,url){
|
||||
console.log(url,postData);
|
||||
return await UploadFormData(url,postData)
|
||||
.then((res)=>{
|
||||
console.log('test:',res);
|
||||
if(typeof res=='object' && res.error!=null){
|
||||
return false;
|
||||
}
|
||||
else if(res.includes('Success')){
|
||||
console.log('image uploaded');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.catch((err)=>{
|
||||
console.log(err);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
export async function UploadData2(url, indata) {
|
||||
|
||||
return await fetch(url, {
|
||||
method: 'post',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(indata),
|
||||
})
|
||||
.then(response => {
|
||||
return response.json();
|
||||
})
|
||||
.then((res) => {
|
||||
return res;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('UploadData error', err);
|
||||
return { success: false, error: true, errorMsg: err };
|
||||
});
|
||||
}
|
||||
|
||||
export async function UploadFormData(url, formData) {
|
||||
// console.log(url,formData._parts);
|
||||
return await fetch(url, {
|
||||
method: 'post',
|
||||
headers: new Headers({ 'Content-Type': 'multipart/form-data', }),
|
||||
body: formData,
|
||||
})
|
||||
.then(response => {
|
||||
console.log('UploadData error3', response);
|
||||
return response.text();
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('UploadData error1', res);
|
||||
return res;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('UploadData error2', err);
|
||||
return { success: false, error: err };
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { toastError } from "./Toast";
|
||||
|
||||
|
||||
export function validateNumber(val, type = '', showMsg = true) {
|
||||
console.log(val, "type=======", type);
|
||||
let isValid = true;
|
||||
if (type.toLowerCase() == 'numeric' && val != '') {
|
||||
let regex = new RegExp(/^\d+$/);
|
||||
let isNUmeric = regex.test(val);
|
||||
if (!isNUmeric) {
|
||||
isValid = false;
|
||||
if (showMsg) toastError('Alert', 'Please enter whole numbers only');
|
||||
}
|
||||
}
|
||||
else if (type.toLowerCase() == 'decimal' && val != '') {
|
||||
let regex = new RegExp(/^\d*\.?\d*$/);
|
||||
let isNUmeric = regex.test(val);
|
||||
if (!isNUmeric) {
|
||||
isValid = false;
|
||||
if (showMsg) toastError('Alert', 'Please enter decimal numbers only');
|
||||
}
|
||||
}
|
||||
else if (type.toLowerCase() == 'text' && val != '') {
|
||||
let regex = new RegExp(/^[a-zA-Z0-9@\s_.-]*$/);
|
||||
let isNUmeric = regex.test(val);
|
||||
if (!isNUmeric) {
|
||||
isValid = false;
|
||||
if (showMsg) toastError('Alert', 'Please enter only characters and digits');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if (type.toLowerCase() === 'onlytext' && val !== '') {
|
||||
let regex = /^[a-zA-Z\s]*$/; // ✅ allows only alphabets and space
|
||||
const isAlpha = regex.test(val);
|
||||
if (!isAlpha) {
|
||||
isValid = false;
|
||||
if (showMsg) toastError('Alert', 'Please enter only letters and spaces');
|
||||
}
|
||||
}
|
||||
|
||||
else if (type.toLowerCase() == 'textspc' && val != '') {
|
||||
let regex = new RegExp(/^[a-zA-Z0-9\/-]*$/);
|
||||
let isNUmeric = regex.test(val);
|
||||
if (!isNUmeric) {
|
||||
isValid = false;
|
||||
if (showMsg) toastError('Alert', 'Please enter only characters and digits');
|
||||
}
|
||||
} else if (type.toLowerCase() == 'alphanumeric' && val != '') {
|
||||
let regex = new RegExp(/^[a-zA-Z0-9]+$/);
|
||||
let isNUmeric = regex.test(val);
|
||||
if (!isNUmeric) {
|
||||
isValid = false;
|
||||
if (showMsg) toastError('Alert', 'Please enter only characters and digits');
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
|
||||
import React from 'react';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
@@ -27,4 +28,57 @@ const Routes = () => {
|
||||
);
|
||||
};
|
||||
|
||||
=======
|
||||
import React from 'react';
|
||||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
import Splash from '../screens/AuthScreen/Splash';
|
||||
import Login from '../screens/AuthScreen/Login/Login';
|
||||
import VerifyOTP from '../screens/AuthScreen/VerifyOTP';
|
||||
import { ToastComponent } from '../constants/Toast';
|
||||
import StoreInfo from '../screens/MainScreen/StoreInfo';
|
||||
|
||||
import Feedback from '../screens/MainScreen/Feedback';
|
||||
import Dashboard from '../screens/MainScreen/Dashboard';
|
||||
import FeedbackCategories from '../screens/MainScreen/Feedback/FeedbackCategories';
|
||||
import { Platform, StatusBar, View } from 'react-native';
|
||||
import Welcome from '../screens/MainScreen/WelcomePage';
|
||||
import Details from '../screens/MainScreen/Dashboard/Details';
|
||||
import Project from '../screens/AuthScreen/Project/Project';
|
||||
|
||||
const Stack = createNativeStackNavigator();
|
||||
|
||||
const Routes = () => {
|
||||
const STATUSBAR_HEIGHT = Platform.OS === 'ios' ? 50 : StatusBar.currentHeight;
|
||||
return (
|
||||
<NavigationContainer>
|
||||
{Platform.OS === 'ios' && (
|
||||
<View style={{
|
||||
height: STATUSBAR_HEIGHT,
|
||||
bbarStyle:'light-content',
|
||||
backgroundColor: '#113F8C',
|
||||
}} />
|
||||
)}
|
||||
<StatusBar barStyle={'light-content'} backgroundColor={'#113F8C'} />
|
||||
<Stack.Navigator
|
||||
screenOptions={{ headerShown: false }}
|
||||
initialRouteName="Splash">
|
||||
<Stack.Screen name="Splash" component={Splash} />
|
||||
<Stack.Screen name="Login" component={Login} />
|
||||
<Stack.Screen name="VerifyOTP" component={VerifyOTP} />
|
||||
<Stack.Screen name="Welcome" component={Welcome} />
|
||||
<Stack.Screen name="StoreInfo" component={StoreInfo} />
|
||||
<Stack.Screen name="Dashboard" component={Dashboard} />
|
||||
<Stack.Screen name="Feedback" component={Feedback} />
|
||||
<Stack.Screen name="FeedbackCategories" component={FeedbackCategories} />
|
||||
<Stack.Screen name="Details" component={Details} />
|
||||
<Stack.Screen name="Project" component={Project} />
|
||||
|
||||
</Stack.Navigator>
|
||||
<ToastComponent />
|
||||
</NavigationContainer>
|
||||
);
|
||||
};
|
||||
|
||||
>>>>>>> dabur-store-dna
|
||||
export default Routes;
|
||||
@@ -1,5 +1,13 @@
|
||||
<<<<<<< HEAD
|
||||
import userSlice from '../slices/userSlice';
|
||||
|
||||
export default {
|
||||
user: userSlice,
|
||||
};
|
||||
=======
|
||||
import userSlice from '../slices/userSlice';
|
||||
|
||||
export default {
|
||||
user: userSlice,
|
||||
};
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
// // src/redux/slices/authSlice.js
|
||||
// import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
@@ -30,4 +31,38 @@
|
||||
// });
|
||||
|
||||
// export const { loginStart, loginSuccess, loginFailure, logout } = authSlice.actions;
|
||||
=======
|
||||
// // src/redux/slices/authSlice.js
|
||||
// import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
// const initialState = {
|
||||
// token: null,
|
||||
// loading: false,
|
||||
// error: null,
|
||||
// };
|
||||
|
||||
// const authSlice = createSlice({
|
||||
// name: 'auth',
|
||||
// initialState,
|
||||
// reducers: {
|
||||
// loginStart: (state) => {
|
||||
// state.loading = true;
|
||||
// state.error = null;
|
||||
// },
|
||||
// loginSuccess: (state, action) => {
|
||||
// state.loading = false;
|
||||
// state.token = action.payload;
|
||||
// },
|
||||
// loginFailure: (state, action) => {
|
||||
// state.loading = false;
|
||||
// state.error = action.payload;
|
||||
// },
|
||||
// logout: (state) => {
|
||||
// state.token = null;
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
|
||||
// export const { loginStart, loginSuccess, loginFailure, logout } = authSlice.actions;
|
||||
>>>>>>> dabur-store-dna
|
||||
// export default authSlice.reducer;
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import {createSlice} from '@reduxjs/toolkit';
|
||||
|
||||
const initialState = {
|
||||
@@ -23,3 +24,30 @@ const userSlice = createSlice({
|
||||
|
||||
export const {setUser, resetUserState} = userSlice.actions;
|
||||
export default userSlice.reducer;
|
||||
=======
|
||||
import {createSlice} from '@reduxjs/toolkit';
|
||||
|
||||
const initialState = {
|
||||
token: '',
|
||||
};
|
||||
|
||||
const userSlice = createSlice({
|
||||
name: 'user',
|
||||
initialState,
|
||||
reducers: {
|
||||
setUser(state, action) {
|
||||
return {
|
||||
...state,
|
||||
...action.payload,
|
||||
token: action?.payload?.usertoken,
|
||||
};
|
||||
},
|
||||
resetUserState() {
|
||||
return initialState;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {setUser, resetUserState} = userSlice.actions;
|
||||
export default userSlice.reducer;
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<<<<<<< HEAD
|
||||
import { configureStore } from '@reduxjs/toolkit'
|
||||
import reducer from './reducer'
|
||||
|
||||
|
||||
=======
|
||||
import { configureStore } from '@reduxjs/toolkit'
|
||||
import reducer from './reducer'
|
||||
|
||||
|
||||
>>>>>>> dabur-store-dna
|
||||
export const store = configureStore({ reducer: reducer })
|
||||
@@ -0,0 +1,97 @@
|
||||
|
||||
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('');
|
||||
|
||||
|
||||
// geo loc
|
||||
useEffect(() => {
|
||||
Geolocation.getCurrentPosition(info => console.log("Location infoooo====>", JSON.stringify(info)));
|
||||
}, [])
|
||||
// end geo loc
|
||||
|
||||
|
||||
const onSubmit = () => {
|
||||
setLoading(true);
|
||||
getOTP();
|
||||
};
|
||||
|
||||
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) {
|
||||
setLoading(false);
|
||||
toastError("Alert", "Invalid User");
|
||||
} else {
|
||||
toastSuccess("Alert", res?.SendOTP[0]?.Messages);
|
||||
navigation.navigate('VerifyOTP', { username: username });
|
||||
setLoading(false);
|
||||
}
|
||||
// console.log('getotpApi res==>', JSON.stringify(res?.SendOTP[0]));
|
||||
} catch (error) {
|
||||
console.log("❌ OTP API error:", error);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
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;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { GlobalTheme, Screen } from '../../../theme';
|
||||
@@ -33,3 +34,87 @@ export const styles = StyleSheet.create({
|
||||
}
|
||||
|
||||
});
|
||||
=======
|
||||
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',
|
||||
},
|
||||
});
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { View, Text, Image, Platform, ImageBackground, TextInput } 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 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 Project = ({ navigation }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const [username, setUsername] = useState('');
|
||||
|
||||
|
||||
// geo loc
|
||||
useEffect(() => {
|
||||
Geolocation.getCurrentPosition(info => console.log("Location infoooo====>", JSON.stringify(info)));
|
||||
}, [])
|
||||
// end geo loc
|
||||
|
||||
|
||||
const onSubmit = () => {
|
||||
// setLoading(true);
|
||||
// getOTP();
|
||||
navigation.navigate("Login")
|
||||
};
|
||||
|
||||
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) {
|
||||
setLoading(false);
|
||||
toastError("Alert", "Invalid User");
|
||||
} else {
|
||||
toastSuccess("Alert", res?.SendOTP[0]?.Messages);
|
||||
navigation.navigate('VerifyOTP', { username: username });
|
||||
setLoading(false);
|
||||
}
|
||||
// console.log('getotpApi res==>', JSON.stringify(res?.SendOTP[0]));
|
||||
} catch (error) {
|
||||
console.log("❌ OTP API error:", error);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
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.performicsLogo} source={IMAGES.PerformicsLogo} resizeMode='contain' />
|
||||
<Image style={styles.appLogo} source={IMAGES.projectIdImg} resizeMode='contain' />
|
||||
<View style={styles.card}>
|
||||
<View style={{marginTop:25}}>
|
||||
<Text style={[styles.loginTitle, { fontSize: 15, color: '#555555', marginBottom: 10 }]}>Hey There</Text>
|
||||
<Text style={styles.loginTitle}>Enter Your Project Id</Text>
|
||||
</View>
|
||||
|
||||
<View style={{ marginTop: 20, height: 50, backgroundColor: '#eee', borderRadius: 10 }}>
|
||||
<TextInput
|
||||
style={{ height: 50, paddingHorizontal: 15 }}
|
||||
placeholder='Enter Project Id'
|
||||
placeholderTextColor={'gray'}
|
||||
keyboardType='numeric'
|
||||
/>
|
||||
</View>
|
||||
|
||||
<CustomButton onPress={() => onSubmit()} title={'Next'} style={styles.btnbg} textstyle={styles.btntext} />
|
||||
<ImageBackground source={IMAGES.AuthBottomBG} style={styles.AuthBottomBG} >
|
||||
<Text style={{ position: 'absolute', textAlign: 'center', alignContent: 'center', alignSelf: 'center', bottom: 20, color: GlobalTheme.colors.gray }}> Copyright CPM India - 2025</Text>
|
||||
</ImageBackground>
|
||||
</View>
|
||||
</KeyboardAwareScrollView>
|
||||
<Loader visible={loading} loadingtext={'Loading ...'} />
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
export default Project;
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
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: "#fff",
|
||||
},
|
||||
logoContainer: {
|
||||
width: '100%',
|
||||
},
|
||||
appLogo: {
|
||||
height: normalize(180),
|
||||
width: '100%',
|
||||
resizeMode: 'contain',
|
||||
marginTop: 30,
|
||||
},
|
||||
performicsLogo: {
|
||||
height: normalize(50),
|
||||
width: '55%',
|
||||
resizeMode: 'contain',
|
||||
alignSelf: 'center',
|
||||
marginTop:10,
|
||||
},
|
||||
titleContainer: {
|
||||
alignSelf: 'center',
|
||||
marginBottom: normalize(10),
|
||||
},
|
||||
titleText: {
|
||||
fontSize: normalize(20),
|
||||
fontWeight: 'bold',
|
||||
color: GlobalTheme.colors.black,
|
||||
},
|
||||
card: {
|
||||
padding: normalize(25),
|
||||
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(210),
|
||||
justifyContent: 'center',
|
||||
},
|
||||
loginTitle: {
|
||||
color: GlobalTheme.colors.black,
|
||||
fontSize: normalize(GlobalTheme.typography.fontSize.small),
|
||||
fontWeight: GlobalTheme.typography.fontWeight.medium,
|
||||
textAlign: 'center',
|
||||
marginBottom: normalize(20),
|
||||
},
|
||||
mainContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: '#EAF0FF',
|
||||
},
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import {View, Text, Image, Dimensions, StyleSheet} from 'react-native';
|
||||
import React, {useEffect} from 'react';
|
||||
import IMAGES from '../../../constants/Images';
|
||||
@@ -97,3 +98,104 @@ const styles = StyleSheet.create({
|
||||
//borderWidth:1
|
||||
},
|
||||
});
|
||||
=======
|
||||
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(() => {
|
||||
checkLoginStatus();
|
||||
}, []);
|
||||
|
||||
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));
|
||||
setTimeout(() => {
|
||||
navigation.reset({
|
||||
index: 0,
|
||||
routes: [{ name: 'Welcome' }],
|
||||
});
|
||||
}, 1000);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
navigation.reset({
|
||||
index: 0,
|
||||
routes: [{ name: 'Login' }],
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking login status:', error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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
|
||||
},
|
||||
});
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { View, Text, Image, Alert, TouchableOpacity } from 'react-native';
|
||||
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
|
||||
@@ -122,3 +123,224 @@ const VerifyOTP = ({ navigation }) => {
|
||||
};
|
||||
|
||||
export default VerifyOTP;
|
||||
=======
|
||||
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;
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { GlobalTheme, Screen } from '../../../theme';
|
||||
@@ -99,3 +100,154 @@ export const styles = StyleSheet.create({
|
||||
}
|
||||
|
||||
});
|
||||
=======
|
||||
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),
|
||||
},
|
||||
|
||||
});
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -0,0 +1,406 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { View, Text, TouchableOpacity, ScrollView, Image, Modal, Platform, StyleSheet, Alert } from 'react-native';
|
||||
import { useRoute, useNavigation } from '@react-navigation/native';
|
||||
import { post } from '../../../api/ApiService';
|
||||
import IMAGES from '../../../constants/Images';
|
||||
import { GlobalTheme } from '../../../theme';
|
||||
import Loader from '../../../constants/Loader';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { toastError } from '../../../constants/Toast';
|
||||
|
||||
const Details = () => {
|
||||
|
||||
const route = useRoute();
|
||||
const navigation = useNavigation();
|
||||
|
||||
const { selectedDetails = [], storeData, year, month, mainTabIndex } = route.params || {};
|
||||
const [modalGraphData, setModalGraphData] = useState({});
|
||||
const [activePromoTab, setActivePromoTab] = useState('Executed');
|
||||
const [allCatData, setAllCatData] = useState([])
|
||||
const [selectedCategoryData, setSelectedCategoryData] = useState([]);
|
||||
const [categoryModalVisible, setCategoryModalVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedDetails.length > 0) {
|
||||
fetchDetailGraphs(selectedDetails);
|
||||
}
|
||||
}, [selectedDetails]);
|
||||
|
||||
useEffect(() => {
|
||||
getAllCatData();
|
||||
}, [])
|
||||
|
||||
const getAllCatData = () => {
|
||||
let data = {
|
||||
parameters: {
|
||||
projectid: 41654,
|
||||
year: year,
|
||||
monthno: month,
|
||||
storeid: storeData?.StoreId
|
||||
},
|
||||
}
|
||||
|
||||
const apiUrl = mainTabIndex === 0
|
||||
? 'https://dax.parinaam.in/execute/dabur/detmtd/oos_sku_list_for_all_visits_mtd'
|
||||
: 'https://dax.parinaam.in/execute/dabur/detlsv/oos_sku_list_on_lsv';
|
||||
|
||||
post(apiUrl, data)
|
||||
.then(res => {
|
||||
setAllCatData(res?.data);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('Error =>', err);
|
||||
});
|
||||
}
|
||||
|
||||
const fetchDetailGraphs = async (detailPages) => {
|
||||
try {
|
||||
const resultMap = {};
|
||||
for (let item of detailPages) {
|
||||
const response = await post(item.GraphUrl, {
|
||||
parameters: {
|
||||
projectid: 41654,
|
||||
year: year,
|
||||
monthno: month,
|
||||
storeid: storeData?.StoreId
|
||||
},
|
||||
});
|
||||
resultMap[item.GraphUrl] = response?.data || [];
|
||||
}
|
||||
setModalGraphData(resultMap);
|
||||
} catch (error) {
|
||||
console.log("❌ Error fetching detail graphs:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const showCategoryDetails = (data) => {
|
||||
setCategoryModalVisible(!categoryModalVisible)
|
||||
// Filter SKUs from allCatData where the category name matches
|
||||
const filteredCategory = allCatData.filter(item =>
|
||||
item.Product_CategoryCategory_Name === data.Product_CategoryCategory_Name
|
||||
);
|
||||
// Save for display
|
||||
setSelectedCategoryData(filteredCategory);
|
||||
}
|
||||
|
||||
const isOSATab = selectedDetails.some(item => item.GraphTitle === "OSA - Category");
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: '#113F8C' }}>
|
||||
<View style={{ flex: 1, backgroundColor: '#fff' }}>
|
||||
{/* Header */}
|
||||
<View style={styled.header}>
|
||||
<TouchableOpacity onPress={() => navigation.goBack()} style={{ padding: 5 }}>
|
||||
<Image source={IMAGES.backIcon} style={{ height: 20, width: 20, tintColor: '#fff' }} />
|
||||
</TouchableOpacity>
|
||||
<Text style={{ color: '#fff', fontSize: 20, fontWeight: 'bold' }}>Details</Text>
|
||||
<View style={{ width: 25 }} /> {/* spacer */}
|
||||
</View>
|
||||
|
||||
<ScrollView style={{ padding: 20 }}>
|
||||
{selectedDetails.map((detail, index) => {
|
||||
const values = modalGraphData[detail.GraphUrl] || [];
|
||||
|
||||
if (!modalGraphData[detail.GraphUrl]) {
|
||||
return (
|
||||
<View key={index} style={{ flex: 1, marginBottom: 20 }}>
|
||||
<Loader visible={true} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
switch (detail.GraphType) {
|
||||
case 'Table':
|
||||
// Check if it's the Promotion table
|
||||
const isPromotionTable =
|
||||
values.length > 0 &&
|
||||
values[0].hasOwnProperty('Product_CategoryCategory_Name') &&
|
||||
values[0].hasOwnProperty('Promotion_MasterPromotion_Definition');
|
||||
|
||||
if (isPromotionTable) {
|
||||
// Filter data based on active tab
|
||||
const filteredValues = values.filter(row =>
|
||||
activePromoTab === 'Executed'
|
||||
? row.Executed?.toLowerCase() === 'yes'
|
||||
: row.Executed?.toLowerCase() === 'no'
|
||||
);
|
||||
// Group data by Product_CategoryCategory_Name
|
||||
const groupedData = filteredValues.reduce((acc, curr) => {
|
||||
const category = curr.Product_CategoryCategory_Name || 'Unknown';
|
||||
if (!acc[category]) acc[category] = [];
|
||||
acc[category].push(curr);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return (
|
||||
<View key={index} style={{ marginBottom: 10 }}>
|
||||
|
||||
{/* Horizontal Tabs - show once at top */}
|
||||
{index === 0 && ( // ensures only first promotion table renders the toggle
|
||||
<View style={{ flexDirection: 'row', marginBottom: 20 }}>
|
||||
{['Executed', 'Not Executed'].map(tab => {
|
||||
const isSelected = activePromoTab === tab;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={tab}
|
||||
onPress={() => setActivePromoTab(tab)}
|
||||
style={{
|
||||
flex: 1,
|
||||
paddingVertical: 10,
|
||||
backgroundColor: isSelected ? '#113F8C' : '#E3EBF8',
|
||||
borderRadius: 8,
|
||||
marginHorizontal: 5,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: isSelected ? '#fff' : '#000', fontWeight: '600' }}>
|
||||
{tab}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* No data message */}
|
||||
<View style={{}}>
|
||||
{filteredValues.length === 0 ? (
|
||||
<View style={{ marginTop: 0, backgroundColor: 'red' }} />
|
||||
) : (
|
||||
Object.keys(groupedData).map((categoryName, catIdx) => (
|
||||
<View key={catIdx} style={{ marginBottom: 20, }}>
|
||||
{/* Category Header */}
|
||||
<View style={{ backgroundColor: '#E8F0FF', padding: 8, borderRadius: 6 }}>
|
||||
<Text style={{ fontSize: 15, fontWeight: '700', color: '#113F8C' }}>
|
||||
{String(categoryName)}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Table Header */}
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: '#f5f5f5',
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 9,
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 8,
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#000', fontWeight: 'bold', width: '80%' }}>
|
||||
Definition
|
||||
</Text>
|
||||
<Text style={{ color: '#000', fontWeight: 'bold', width: '20%', textAlign: 'center' }}>
|
||||
Executed
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Table Rows */}
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: '#fff',
|
||||
paddingHorizontal: 10,
|
||||
borderBottomLeftRadius: 8,
|
||||
borderBottomRightRadius: 8,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 2,
|
||||
elevation: 2
|
||||
}}
|
||||
>
|
||||
{groupedData[categoryName].map((row, rowIdx) => (
|
||||
<View
|
||||
key={rowIdx}
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
paddingVertical: 8,
|
||||
borderBottomWidth: rowIdx === groupedData[categoryName].length - 1 ? 0 : 1,
|
||||
borderColor: '#eee'
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#000', width: '80%' }}>
|
||||
{String(row.Promotion_MasterPromotion_Definition) || '-'}
|
||||
</Text>
|
||||
<Text style={{ color: '#000', width: '20%', textAlign: 'center' }}>
|
||||
{row.Executed || '-'}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
))
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
let displayKey = null;
|
||||
let presentKey = null;
|
||||
if (values.length > 0) {
|
||||
const presentKeys = Object.keys(values[0]).filter(k =>
|
||||
(typeof values[0][k] === 'string' && ['Yes', 'No'].includes(values[0][k])) ||
|
||||
typeof values[0][k] === 'boolean' ||
|
||||
typeof values[0][k] === 'number'
|
||||
);
|
||||
presentKey = presentKeys.length > 0 ? presentKeys[0] : null;
|
||||
const displayKeys = Object.keys(values[0]).filter(k => k !== presentKey && typeof values[0][k] === 'string');
|
||||
displayKey = displayKeys.length > 0 ? displayKeys[0] : null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View key={index} style={{ marginBottom: 30 }}>
|
||||
<Text style={{ color: '#000', fontSize: 16, fontWeight: '600', marginBottom: 10 }}>
|
||||
{detail.GraphTitle}
|
||||
|
||||
</Text>
|
||||
<View style={styled.categoryHeader}>
|
||||
<Text style={{ color: '#000', fontWeight: 'bold', width: '80%' }}>
|
||||
{displayKey ? displayKey.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2') : ''}
|
||||
</Text>
|
||||
<Text style={{ color: '#000', fontWeight: 'bold', width: '20%', textAlign: 'center' }}>
|
||||
{presentKey ? presentKey.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2') : ''}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={styled.itemContainer}>
|
||||
{values.map((row, idx) => {
|
||||
const presentKeys = Object.keys(row).filter(k =>
|
||||
(typeof row[k] === 'string' && ['Yes', 'No'].includes(row[k])) ||
|
||||
typeof row[k] === 'boolean' ||
|
||||
typeof row[k] === 'number'
|
||||
);
|
||||
const presentKey = presentKeys.length > 0 ? presentKeys[0] : null;
|
||||
const displayKeys = Object.keys(row).filter(k => k !== presentKey && typeof row[k] === 'string');
|
||||
const displayKey = displayKeys.length > 0 ? displayKeys[0] : null;
|
||||
|
||||
return (
|
||||
<View
|
||||
key={idx}
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
paddingVertical: 8,
|
||||
borderBottomWidth: idx === values.length - 1 ? 0 : 1,
|
||||
borderColor: '#eee',
|
||||
}}>
|
||||
<Text style={{ color: '#000', width: '80%' }}>{row[displayKey] || '--'}</Text>
|
||||
<TouchableOpacity disabled={isOSATab ? false : true}
|
||||
onPress={() => {
|
||||
if (row[presentKey] == 100 || row[presentKey] == '100') {
|
||||
toastError('Alert', 'No Data Available')
|
||||
} else {
|
||||
showCategoryDetails(row)
|
||||
}
|
||||
}}
|
||||
style={{ width: '20%', }}>
|
||||
<Text style={{ color: '#113F8C', textAlign: 'center', fontWeight: '500' }}>
|
||||
{presentKey ? (
|
||||
typeof row[presentKey] === 'string' && ['Yes', 'No'].includes(row[presentKey]) ? row[presentKey] :
|
||||
typeof row[presentKey] === 'boolean' ? (row[presentKey] ? 'Yes' : 'No') :
|
||||
typeof row[presentKey] === 'number' ? row[presentKey].toFixed(2) : row[presentKey] || '-'
|
||||
) : '-'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<View key={index} style={{ marginBottom: 20 }}>
|
||||
<Text>Unsupported GraphType: {detail.GraphType}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</ScrollView>
|
||||
|
||||
<Modal
|
||||
visible={categoryModalVisible}
|
||||
animationType="slide"
|
||||
onRequestClose={() => setCategoryModalVisible(false)}
|
||||
>
|
||||
<View style={{ flex: 1, }}>
|
||||
{/* Header */}
|
||||
<View style={{ height: Platform.OS === 'ios' ? 55 : 0, backgroundColor: '#113F8C' }} />
|
||||
|
||||
<View style={{ width: '100%', backgroundColor: '#113F8C', borderBottomWidth: 1, borderColor: 'gray', padding: 5, paddingHorizontal: 20, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<View style={{ width: '93%', alignItems: 'center' }}>
|
||||
<Text style={{ color: '#fff', fontSize: 20, fontWeight: 'bold', marginBottom: 10, alignSelf: 'center' }}>{selectedCategoryData[0]?.Product_CategoryCategory_Name || 'Category'}</Text>
|
||||
</View>
|
||||
<TouchableOpacity onPress={() => setCategoryModalVisible(false)} style={{ width: '7%', alignItems: 'center' }}>
|
||||
<Image source={IMAGES.crossIcon} style={{ height: 25, width: 25, resizeMode: 'contain' }} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* SKU List */}
|
||||
<ScrollView contentContainerStyle={{ padding: 15 }}>
|
||||
{/* <Text style={{ color: '#000', fontSize: 16, fontWeight: 'bold', marginBottom: 5 }}>OOS SKU details</Text> */}
|
||||
{selectedCategoryData.map((item, idx) => (
|
||||
<View
|
||||
key={idx}
|
||||
style={{
|
||||
marginTop: 2,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
paddingVertical: 8,
|
||||
borderBottomWidth: idx === selectedCategoryData.length - 1 ? 0 : 1,
|
||||
borderColor: '#eee',
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#000', width: '80%' }}>
|
||||
{item.Product_MasterProduct_Name}
|
||||
</Text>
|
||||
{/* <Text style={{ color: '#000', width: '20%', textAlign: 'center' }}>
|
||||
{item['#_of_OOS_SKU_for_all_visits']}
|
||||
</Text> */}
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styled = StyleSheet.create({
|
||||
header: {
|
||||
width: '100%',
|
||||
backgroundColor: '#113F8C',
|
||||
padding: 10,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
},
|
||||
categoryHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: '#EDEDED',
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 10,
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 8,
|
||||
},
|
||||
itemContainer: {
|
||||
backgroundColor: '#fff',
|
||||
paddingHorizontal: 10,
|
||||
borderBottomLeftRadius: 8,
|
||||
borderBottomRightRadius: 8,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 2,
|
||||
elevation: 2
|
||||
}
|
||||
})
|
||||
|
||||
export default Details;
|
||||
@@ -0,0 +1,431 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { View, Text, TouchableOpacity, ScrollView, Image, Modal, Platform, StyleSheet, Alert } from 'react-native';
|
||||
import { useRoute, useNavigation } from '@react-navigation/native';
|
||||
import { post } from '../../../api/ApiService';
|
||||
import IMAGES from '../../../constants/Images';
|
||||
import { GlobalTheme } from '../../../theme';
|
||||
import Loader from '../../../constants/Loader';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { toastError } from '../../../constants/Toast';
|
||||
|
||||
const Details = () => {
|
||||
|
||||
const route = useRoute();
|
||||
const navigation = useNavigation();
|
||||
|
||||
const { selectedDetails = [], storeData, year, month, mainTabIndex } = route.params || {};
|
||||
const [modalGraphData, setModalGraphData] = useState({});
|
||||
const [activePromoTab, setActivePromoTab] = useState('Executed');
|
||||
const [allCatData, setAllCatData] = useState([])
|
||||
const [selectedCategoryData, setSelectedCategoryData] = useState([]);
|
||||
const [categoryModalVisible, setCategoryModalVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedDetails.length > 0) {
|
||||
fetchDetailGraphs(selectedDetails);
|
||||
}
|
||||
}, [selectedDetails]);
|
||||
|
||||
useEffect(() => {
|
||||
getAllCatData();
|
||||
}, [])
|
||||
|
||||
const getAllCatData = () => {
|
||||
let data = {
|
||||
parameters: {
|
||||
projectid: 41654,
|
||||
year: year,
|
||||
monthno: month,
|
||||
storeid: storeData?.StoreId
|
||||
},
|
||||
}
|
||||
|
||||
const apiUrl = mainTabIndex === 0
|
||||
? 'https://dax.parinaam.in/execute/dabur/detmtd/oos_sku_list_for_all_visits_mtd'
|
||||
: 'https://dax.parinaam.in/execute/dabur/detlsv/oos_sku_list_on_lsv';
|
||||
|
||||
post(apiUrl, data)
|
||||
.then(res => {
|
||||
setAllCatData(res?.data);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('Error =>', err);
|
||||
});
|
||||
}
|
||||
|
||||
const fetchDetailGraphs = async (detailPages) => {
|
||||
try {
|
||||
const resultMap = {};
|
||||
for (let item of detailPages) {
|
||||
const response = await post(item.GraphUrl, {
|
||||
parameters: {
|
||||
projectid: 41654,
|
||||
year: year,
|
||||
monthno: month,
|
||||
storeid: storeData?.StoreId
|
||||
},
|
||||
});
|
||||
resultMap[item.GraphUrl] = response?.data || [];
|
||||
}
|
||||
setModalGraphData(resultMap);
|
||||
} catch (error) {
|
||||
console.log("❌ Error fetching detail graphs:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const showCategoryDetails = (data) => {
|
||||
setCategoryModalVisible(!categoryModalVisible)
|
||||
// Filter SKUs from allCatData where the category name matches
|
||||
const filteredCategory = allCatData.filter(item =>
|
||||
item.Product_CategoryCategory_Name === data.Product_CategoryCategory_Name
|
||||
);
|
||||
// Save for display
|
||||
setSelectedCategoryData(filteredCategory);
|
||||
}
|
||||
|
||||
const isOSATab = selectedDetails.some(item => item.GraphTitle === "OSA - Category");
|
||||
console.log('isOSATab----',isOSATab);
|
||||
|
||||
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: '#113F8C' }}>
|
||||
<View style={{ flex: 1, backgroundColor: '#fff' }}>
|
||||
{/* Header */}
|
||||
<View style={styled.header}>
|
||||
<TouchableOpacity onPress={() => navigation.goBack()} style={{ padding: 5 }}>
|
||||
<Image source={IMAGES.backIcon} style={{ height: 20, width: 20, tintColor: '#fff' }} />
|
||||
</TouchableOpacity>
|
||||
<Text style={{ color: '#fff', fontSize: 20, fontWeight: 'bold' }}>Details</Text>
|
||||
<View style={{ width: 25 }} />
|
||||
|
||||
</View>
|
||||
|
||||
<ScrollView style={{ padding: 20 }}>
|
||||
{selectedDetails && selectedDetails.length > 0 ? selectedDetails.map((detail, index) => {
|
||||
const values = modalGraphData[detail.GraphUrl] || [];
|
||||
|
||||
if (!modalGraphData[detail.GraphUrl]) {
|
||||
return (
|
||||
<View key={index} style={{ flex: 1, marginBottom: 20 }}>
|
||||
<Loader visible={true} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
switch (detail.GraphType) {
|
||||
case 'Table':
|
||||
// Check if it's the Promotion table
|
||||
const isPromotionTable =
|
||||
values.length > 0 &&
|
||||
values[0].hasOwnProperty('Product_CategoryCategory_Name') &&
|
||||
values[0].hasOwnProperty('Promotion_MasterPromotion_Definition');
|
||||
|
||||
if (isPromotionTable) {
|
||||
// Filter data based on active tab
|
||||
const filteredValues = values.filter(row =>
|
||||
activePromoTab === 'Executed'
|
||||
? row.Executed?.toLowerCase() === 'yes'
|
||||
: row.Executed?.toLowerCase() === 'no'
|
||||
);
|
||||
// Group data by Product_CategoryCategory_Name
|
||||
const groupedData = filteredValues.reduce((acc, curr) => {
|
||||
const category = curr.Product_CategoryCategory_Name || 'Unknown';
|
||||
if (!acc[category]) acc[category] = [];
|
||||
acc[category].push(curr);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return (
|
||||
<View key={index} style={{ marginBottom: 10 }}>
|
||||
{/* Horizontal Tabs - show once at top */}
|
||||
{index === 0 && ( // ensures only first promotion table renders the toggle
|
||||
<View style={{ flexDirection: 'row', marginBottom: 20 }}>
|
||||
{['Executed', 'Not Executed'].map(tab => {
|
||||
const isSelected = activePromoTab === tab;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={tab}
|
||||
onPress={() => setActivePromoTab(tab)}
|
||||
style={{
|
||||
flex: 1,
|
||||
paddingVertical: 10,
|
||||
backgroundColor: isSelected ? '#113F8C' : '#E3EBF8',
|
||||
borderRadius: 8,
|
||||
marginHorizontal: 5,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: isSelected ? '#fff' : '#000', fontWeight: '600' }}>
|
||||
{tab}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* No data message */}
|
||||
<View style={{}}>
|
||||
{filteredValues.length === 0 ? (
|
||||
<View style={{ marginTop: 0, backgroundColor: 'red' }} />
|
||||
) : (
|
||||
Object.keys(groupedData).map((categoryName, catIdx) => (
|
||||
<View key={catIdx} style={{ marginBottom: 20 }}>
|
||||
{/* Category Header */}
|
||||
<View style={{ backgroundColor: '#E8F0FF', padding: 8, borderRadius: 6 }}>
|
||||
<Text style={{ fontSize: 15, fontWeight: '700', color: '#113F8C' }}>
|
||||
{String(categoryName)}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Table Header */}
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: '#f5f5f5',
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 9,
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 8,
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#000', fontWeight: 'bold', width: '80%' }}>
|
||||
Definition
|
||||
</Text>
|
||||
<Text style={{ color: '#000', fontWeight: 'bold', width: '20%', textAlign: 'center' }}>
|
||||
Executed
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Table Rows */}
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: '#fff',
|
||||
paddingHorizontal: 10,
|
||||
borderBottomLeftRadius: 8,
|
||||
borderBottomRightRadius: 8,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 2,
|
||||
elevation: 2
|
||||
}}
|
||||
>
|
||||
{groupedData[categoryName].map((row, rowIdx) => (
|
||||
<View
|
||||
key={rowIdx}
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
paddingVertical: 8,
|
||||
borderBottomWidth: rowIdx === groupedData[categoryName].length - 1 ? 0 : 1,
|
||||
borderColor: '#eee'
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#000', width: '80%' }}>
|
||||
{String(row.Promotion_MasterPromotion_Definition) || '-'}
|
||||
</Text>
|
||||
<Text style={{ color: '#000', width: '20%', textAlign: 'center' }}>
|
||||
{row.Executed || '-'}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
))
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
let displayKey = null;
|
||||
let presentKey = null;
|
||||
if (values.length > 0) {
|
||||
const presentKeys = Object.keys(values[0]).filter(k =>
|
||||
(typeof values[0][k] === 'string' && ['Yes', 'No'].includes(values[0][k])) ||
|
||||
typeof values[0][k] === 'boolean' ||
|
||||
typeof values[0][k] === 'number'
|
||||
);
|
||||
presentKey = presentKeys.length > 0 ? presentKeys[0] : null;
|
||||
const displayKeys = Object.keys(values[0]).filter(k => k !== presentKey && typeof values[0][k] === 'string');
|
||||
displayKey = displayKeys.length > 0 ? displayKeys[0] : null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View key={index} style={{ marginBottom: 30 }}>
|
||||
|
||||
<Text style={{ marginTop: 10, color: '#000', fontSize: 16, fontWeight: '600', marginBottom: 10 }}>
|
||||
{detail.GraphTitle}
|
||||
|
||||
</Text>
|
||||
<View style={styled.categoryHeader}>
|
||||
<Text style={{ color: '#000', fontWeight: 'bold', width: '80%' }}>
|
||||
{displayKey ? displayKey.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2') : ''}
|
||||
</Text>
|
||||
<Text style={{ color: '#000', fontWeight: 'bold', width: '20%', textAlign: 'center' }}>
|
||||
{presentKey ? presentKey.replace(/_/g, ' ').replace(/([a-z])([A-Z])/g, '$1 $2') : ''}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={styled.itemContainer}>
|
||||
{values.map((row, idx) => {
|
||||
const presentKeys = Object.keys(row).filter(k =>
|
||||
(typeof row[k] === 'string' && ['Yes', 'No'].includes(row[k])) ||
|
||||
typeof row[k] === 'boolean' ||
|
||||
typeof row[k] === 'number'
|
||||
);
|
||||
const presentKey = presentKeys.length > 0 ? presentKeys[0] : null;
|
||||
const displayKeys = Object.keys(row).filter(k => k !== presentKey && typeof row[k] === 'string');
|
||||
const displayKey = displayKeys.length > 0 ? displayKeys[0] : null;
|
||||
|
||||
return (
|
||||
<View
|
||||
key={idx}
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
paddingVertical: 8,
|
||||
borderBottomWidth: idx === values.length - 1 ? 0 : 1,
|
||||
borderColor: '#eee',
|
||||
}}>
|
||||
<Text style={{ color: '#000', width: '80%' }}>{row[displayKey] || '--'}</Text>
|
||||
|
||||
<TouchableOpacity disabled={isOSATab ? false : true}
|
||||
onPress={() => {
|
||||
if (row[presentKey] == 100 || row[presentKey] == '100') {
|
||||
toastError('Alert', 'No Data Available')
|
||||
} else {
|
||||
showCategoryDetails(row)
|
||||
}
|
||||
}}
|
||||
style={{ width: '20%', }}>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
<Text style={{ color: '#113F8C', textAlign: 'center', fontWeight: '500' }}>
|
||||
{presentKey ? (
|
||||
typeof row[presentKey] === 'string' && ['Yes', 'No'].includes(row[presentKey]) ? row[presentKey] :
|
||||
typeof row[presentKey] === 'boolean' ? (row[presentKey] ? 'Yes' : 'No') :
|
||||
typeof row[presentKey] === 'number' ? row[presentKey].toFixed(2) : row[presentKey] || '-'
|
||||
) : '-'}
|
||||
</Text>
|
||||
{ isOSATab && row[presentKey] != 100 || isOSATab && row[presentKey] != '100'? <Image source={IMAGES.rightArrowIcon} style={{marginLeft:6, height: 12, width: 12, resizeMode: 'contain' }} /> :null}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<View key={index} style={{ marginBottom: 20 }}>
|
||||
<Text>Unsupported GraphType: {detail.GraphType}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}) :
|
||||
<Loader visible={true} />
|
||||
}
|
||||
</ScrollView>
|
||||
|
||||
<Modal
|
||||
visible={categoryModalVisible}
|
||||
animationType="slide"
|
||||
onRequestClose={() => setCategoryModalVisible(false)}
|
||||
>
|
||||
<View style={{ flex: 1, }}>
|
||||
{/* Header */}
|
||||
<View style={{ height: Platform.OS === 'ios' ? 55 : 0, backgroundColor: '#113F8C' }} />
|
||||
|
||||
<View style={{ width: '100%', backgroundColor: '#113F8C', borderBottomWidth: 1, borderColor: 'gray', padding: 5, paddingHorizontal: 20, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<View style={{ width: '93%', alignItems: 'center' }}>
|
||||
<Text style={{ color: '#fff', fontSize: 20, fontWeight: 'bold', marginBottom: 10, alignSelf: 'center' }}>{selectedCategoryData[0]?.Product_CategoryCategory_Name || 'Category'}</Text>
|
||||
</View>
|
||||
<TouchableOpacity onPress={() => setCategoryModalVisible(false)} style={{ width: '7%', alignItems: 'center' }}>
|
||||
<Image source={IMAGES.crossIcon} style={{ height: 25, width: 25, resizeMode: 'contain' }} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={{ borderTopLeftRadius: 10, borderTopRightRadius: 10, marginTop: 15, marginHorizontal: 15, backgroundColor: '#eee', padding: 10, borderWidth: 1, borderColor: 'gray' }}>
|
||||
<Text style={{ color: '#000', fontSize: 16, fontWeight: '500' }}>{'Out of Stock SKUs:'}</Text>
|
||||
</View>
|
||||
|
||||
{/* SKU List */}
|
||||
<ScrollView contentContainerStyle={{
|
||||
marginHorizontal: 15,
|
||||
borderBottomWidth: 0.5,
|
||||
borderLeftWidth: 0.5,
|
||||
borderRightWidth: 0.5,
|
||||
borderColor: 'gray',
|
||||
borderBottomEndRadius: 10,
|
||||
borderBottomLeftRadius: 10,
|
||||
}}>
|
||||
{/* <Text style={{ color: '#000', fontSize: 16, fontWeight: 'bold', marginBottom: 5 }}>OOS SKU details</Text> */}
|
||||
{selectedCategoryData.map((item, idx) => (
|
||||
<View
|
||||
key={idx}
|
||||
style={{
|
||||
marginTop: 2,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
paddingVertical: 8,
|
||||
borderBottomWidth: idx === selectedCategoryData.length - 1 ? 0 : 1,
|
||||
borderColor: '#eee',
|
||||
|
||||
}}
|
||||
>
|
||||
<View style={{ flexDirection: 'row' }}>
|
||||
<Text style={{ color: '#000', marginLeft: 10 }}>{idx + 1}. </Text>
|
||||
<Text style={{ color: '#000', }}>
|
||||
{item.Product_MasterProduct_Name}
|
||||
</Text>
|
||||
</View>
|
||||
{/* <Text style={{ color: '#000', width: '20%', textAlign: 'center' }}>
|
||||
{item['#_of_OOS_SKU_for_all_visits']}
|
||||
</Text> */}
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
</Modal>
|
||||
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styled = StyleSheet.create({
|
||||
header: {
|
||||
width: '100%',
|
||||
backgroundColor: '#113F8C',
|
||||
padding: 10,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
},
|
||||
categoryHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: '#EDEDED',
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: 10,
|
||||
borderTopLeftRadius: 8,
|
||||
borderTopRightRadius: 8,
|
||||
},
|
||||
itemContainer: {
|
||||
backgroundColor: '#fff',
|
||||
paddingHorizontal: 10,
|
||||
borderBottomLeftRadius: 8,
|
||||
borderBottomRightRadius: 8,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 2,
|
||||
elevation: 2
|
||||
}
|
||||
})
|
||||
|
||||
export default Details;
|
||||
@@ -0,0 +1,318 @@
|
||||
{
|
||||
"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": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"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": 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
|
||||
}
|
||||
},
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,631 @@
|
||||
{
|
||||
"Tabs": [
|
||||
{
|
||||
"MainTabId": 1,
|
||||
"MainTabName": "MTD",
|
||||
"MainTabData": {
|
||||
"subTabs": [
|
||||
{
|
||||
"TabId": 1,
|
||||
"TabName": "PSS Score",
|
||||
"TabRow": 1,
|
||||
"TabCol": 1
|
||||
},
|
||||
{
|
||||
"TabId": 2,
|
||||
"TabName": "PSS Trend",
|
||||
"TabRow": 1,
|
||||
"TabCol": 2
|
||||
},
|
||||
{
|
||||
"TabId": 3,
|
||||
"TabName": "SOS Actual",
|
||||
"TabRow": 1,
|
||||
"TabCol": 2
|
||||
},
|
||||
{
|
||||
"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": 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": "PSS Score",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/SOS_Actual_Perc",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {},
|
||||
"clickable": 0,
|
||||
"DetailsPage": [
|
||||
{
|
||||
"TabId": 2,
|
||||
"GraphId": 1,
|
||||
"GraphType": "Table",
|
||||
"GraphTitle": "PSS - Category",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/sos_actual_perc_on_category",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 2,
|
||||
"GraphId": 2,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "PSS 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 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": 3,
|
||||
"GraphId": 4,
|
||||
"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": 4,
|
||||
"GraphId": 5,
|
||||
"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": "OSA - Category",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/osa_perc_on_category",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 4,
|
||||
"GraphId": 6,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "OSA",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/osa_trend_perc_mtd",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"TabId": 5,
|
||||
"GraphId": 7,
|
||||
"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": "Visibility",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/asset_availability_mtd",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 5,
|
||||
"GraphId": 5,
|
||||
"GraphType": "Table",
|
||||
"GraphTitle": "Additional Visibility",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/additional_visibility_mtd",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 5,
|
||||
"GraphId": 8,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "Asset Details",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/asset_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 Not Executed",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/promotion_not_executed_mtd",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 6,
|
||||
"GraphId": 10,
|
||||
"GraphType": "Table",
|
||||
"GraphTitle": "Promotion executed",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/promotion_executed_mtd",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 6,
|
||||
"GraphId": 11,
|
||||
"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": 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": "#E2C8FE",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 1,
|
||||
"GraphId": 2,
|
||||
"GraphType": "ScoreCard",
|
||||
"GraphTitle": "SOS Actual",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/sos_actual_lsv_perc",
|
||||
"GraphBackground": "#E2C8FE",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 1,
|
||||
"GraphId": 4,
|
||||
"GraphType": "ScoreCard",
|
||||
"GraphTitle": "OSA",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/osa_lsv_perc",
|
||||
"GraphBackground": "#FFF9A1",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 1,
|
||||
"GraphId": 5,
|
||||
"GraphType": "ScoreCard",
|
||||
"GraphTitle": "Asset",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/asset_lsv_perc",
|
||||
"GraphBackground": "#A2F3DE",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 1,
|
||||
"GraphId": 6,
|
||||
"GraphType": "ScoreCard",
|
||||
"GraphTitle": "Promotion",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/promotion_lsv_perc",
|
||||
"GraphBackground": "#BFC2FF",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"TabId": 2,
|
||||
"GraphId": 1,
|
||||
"GraphType": "ScoreCard",
|
||||
"GraphTitle": "PSS Score",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/SOS_Actual_Perc",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {},
|
||||
"clickable": 0,
|
||||
"DetailsPage": [
|
||||
{
|
||||
"TabId": 2,
|
||||
"GraphId": 1,
|
||||
"GraphType": "Table",
|
||||
"GraphTitle": "PSS - Category",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/sos_actual_perc_on_category",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"TabId": 2,
|
||||
"GraphId": 2,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "PSS 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 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": 3,
|
||||
"GraphId": 4,
|
||||
"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": 4,
|
||||
"GraphId": 5,
|
||||
"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": "OSA - Category",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/osa_lsv_perc_on_category",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 4,
|
||||
"GraphId": 6,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "OSA Trend",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/osa_trend_lsv_perc",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
|
||||
{
|
||||
"TabId": 5,
|
||||
"GraphId": 7,
|
||||
"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": "Visibility",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/asset_availability_lsv",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 5,
|
||||
"GraphId": 5,
|
||||
"GraphType": "Table",
|
||||
"GraphTitle": "Additional Visibility",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/additional_visibility_lsv",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 5,
|
||||
"GraphId": 8,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "Asset Trend",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/asset_trend_lsv_perc",
|
||||
"GraphBackground": "#fff",
|
||||
"GraphOptions": {
|
||||
"axisX": "month",
|
||||
"axisY": "score",
|
||||
"labelShow": 1,
|
||||
"barColors": [
|
||||
"#11a4ff",
|
||||
"#0ea3e3",
|
||||
"#0b9ddb"
|
||||
],
|
||||
"gridLinesH": 1,
|
||||
"gridLinesV": 1
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"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 Not Executed",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/promotion_not_executed_lsv",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 6,
|
||||
"GraphId": 10,
|
||||
"GraphType": "Table",
|
||||
"GraphTitle": "Promotion executed",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/promotion_executed_lsv",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 6,
|
||||
"GraphId": 11,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "Promotion",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/promotion_trend_lsv_perc",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,638 @@
|
||||
{
|
||||
"Tabs": [
|
||||
{
|
||||
"MainTabId": 1,
|
||||
"MainTabName": "MTD",
|
||||
"MainTabData": {
|
||||
"subTabs": [
|
||||
{
|
||||
"TabId": 1,
|
||||
"TabName": "PSS Score",
|
||||
"TabRow": 1,
|
||||
"TabCol": 1
|
||||
},
|
||||
{
|
||||
"TabId": 2,
|
||||
"TabName": "PSS Trend",
|
||||
"TabRow": 1,
|
||||
"TabCol": 2
|
||||
},
|
||||
{
|
||||
"TabId": 3,
|
||||
"TabName": "SOS Actual",
|
||||
"TabRow": 1,
|
||||
"TabCol": 2
|
||||
},
|
||||
{
|
||||
"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": 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": "PSS Score",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/mtd/pssscore",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {},
|
||||
"clickable": 0,
|
||||
"DetailsPage": [
|
||||
{
|
||||
"TabId": 2,
|
||||
"GraphId": 1,
|
||||
"GraphType": "Table",
|
||||
"GraphTitle": "PSS - Category",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/sos_actual_perc_on_category",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 2,
|
||||
"GraphId": 2,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "PSS Trend",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/pss_trend_perc_mtd_O",
|
||||
"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 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": 3,
|
||||
"GraphId": 4,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "SOS Actual Trend",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/sos_actual_trend_perc_mtd_O",
|
||||
"GraphBackground": "#F4EAFF",
|
||||
"GraphOptions": {
|
||||
"axisX": "month",
|
||||
"axisY": "score",
|
||||
"labelShow": 1,
|
||||
"barColors": [
|
||||
"#11a4ff",
|
||||
"#0ea3e3",
|
||||
"#0b9ddb"
|
||||
],
|
||||
"gridLinesH": 0,
|
||||
"gridLinesV": 0
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"TabId": 4,
|
||||
"GraphId": 5,
|
||||
"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": "OSA - Category",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/osa_perc_on_category",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 4,
|
||||
"GraphId": 6,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "OSA",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/osa_trend_perc_mtd_O",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"TabId": 5,
|
||||
"GraphId": 7,
|
||||
"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": "Visibility",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/asset_availability_mtd",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 5,
|
||||
"GraphId": 5,
|
||||
"GraphType": "Table",
|
||||
"GraphTitle": "Additional Visibility",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/additional_visibility_mtd",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 5,
|
||||
"GraphId": 8,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "Asset Details",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/asset_trend_perc_mtd_O",
|
||||
"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 Not Executed",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/promotion_not_executed_mtd",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 6,
|
||||
"GraphId": 10,
|
||||
"GraphType": "Table",
|
||||
"GraphTitle": "Promotion executed",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/promotion_executed_mtd",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 6,
|
||||
"GraphId": 11,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "Promotion",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpimtd/promotion_trend_perc_mtd_O",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"MainTabId": 2,
|
||||
"MainTabName": "Last Visit",
|
||||
"MainTabData": {
|
||||
"subTabs": [
|
||||
{
|
||||
"TabId": 1,
|
||||
"TabName": "PSS Score",
|
||||
"TabRow": 1,
|
||||
"TabCol": 1
|
||||
},
|
||||
{
|
||||
"TabId": 2,
|
||||
"TabName": "PSS Trend",
|
||||
"TabRow": 1,
|
||||
"TabCol": 2
|
||||
},
|
||||
|
||||
{
|
||||
"TabId": 3,
|
||||
"TabName": "SOS Actual",
|
||||
"TabRow": 1,
|
||||
"TabCol": 2
|
||||
},
|
||||
{
|
||||
"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": "#E2C8FE",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 1,
|
||||
"GraphId": 2,
|
||||
"GraphType": "ScoreCard",
|
||||
"GraphTitle": "SOS Actual",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/sos_actual_lsv_perc",
|
||||
"GraphBackground": "#E2C8FE",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 1,
|
||||
"GraphId": 4,
|
||||
"GraphType": "ScoreCard",
|
||||
"GraphTitle": "OSA",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/osa_lsv_perc",
|
||||
"GraphBackground": "#FFF9A1",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 1,
|
||||
"GraphId": 5,
|
||||
"GraphType": "ScoreCard",
|
||||
"GraphTitle": "Asset",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/asset_lsv_perc",
|
||||
"GraphBackground": "#A2F3DE",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 1,
|
||||
"GraphId": 6,
|
||||
"GraphType": "ScoreCard",
|
||||
"GraphTitle": "Promotion",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/promotion_lsv_perc",
|
||||
"GraphBackground": "#BFC2FF",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"TabId": 2,
|
||||
"GraphId": 1,
|
||||
"GraphType": "ScoreCard",
|
||||
"GraphTitle": "PSS Score",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/lsv/PSS_Score_LSV_Perc",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {},
|
||||
"clickable": 0,
|
||||
"DetailsPage": [
|
||||
{
|
||||
"TabId": 2,
|
||||
"GraphId": 1,
|
||||
"GraphType": "Table",
|
||||
"GraphTitle": "PSS - Category",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detmtd/sos_actual_perc_on_category",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"TabId": 2,
|
||||
"GraphId": 2,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "PSS Trend",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/pss_trend_perc_lsv_O",
|
||||
"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 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": 3,
|
||||
"GraphId": 4,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "SOS Actual Trend",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/sos_actual_trend_lsv_perc_O",
|
||||
"GraphBackground": "#F4EAFF",
|
||||
"GraphOptions": {
|
||||
"axisX": "month",
|
||||
"axisY": "score",
|
||||
"labelShow": 1,
|
||||
"barColors": [
|
||||
"#11a4ff",
|
||||
"#0ea3e3",
|
||||
"#0b9ddb"
|
||||
],
|
||||
"gridLinesH": 1,
|
||||
"gridLinesV": 1
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"TabId": 4,
|
||||
"GraphId": 5,
|
||||
"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": "OSA - Category",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/osa_lsv_perc_on_category",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 4,
|
||||
"GraphId": 6,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "OSA Trend",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/osa_trend_lsv_perc_O",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
|
||||
{
|
||||
"TabId": 5,
|
||||
"GraphId": 7,
|
||||
"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": "Visibility",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/asset_availability_lsv",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 5,
|
||||
"GraphId": 5,
|
||||
"GraphType": "Table",
|
||||
"GraphTitle": "Additional Visibility",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/additional_visibility_lsv",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 5,
|
||||
"GraphId": 8,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "Asset Trend",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/asset_trend_lsv_perc_O",
|
||||
"GraphBackground": "#fff",
|
||||
"GraphOptions": {
|
||||
"axisX": "month",
|
||||
"axisY": "score",
|
||||
"labelShow": 1,
|
||||
"barColors": [
|
||||
"#11a4ff",
|
||||
"#0ea3e3",
|
||||
"#0b9ddb"
|
||||
],
|
||||
"gridLinesH": 1,
|
||||
"gridLinesV": 1
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"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 Not Executed",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/promotion_not_executed_lsv",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
},
|
||||
{
|
||||
"TabId": 6,
|
||||
"GraphId": 10,
|
||||
"GraphType": "Table",
|
||||
"GraphTitle": "Promotion executed",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/detlsv/promotion_executed_lsv",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"TabId": 6,
|
||||
"GraphId": 11,
|
||||
"GraphType": "BarGraph",
|
||||
"GraphTitle": "Promotion",
|
||||
"GraphUrl": "https://dax.parinaam.in/execute/dabur/kpilsv/promotion_trend_lsv_perc_O",
|
||||
"GraphBackground": "#ECFFFA",
|
||||
"GraphOptions": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
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: '50%',
|
||||
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: 5,
|
||||
borderRadius: 5
|
||||
},
|
||||
storeInfoText: {
|
||||
color: '#000',
|
||||
fontWeight: '500',
|
||||
fontSize: 16,
|
||||
textDecorationLine: 'underline'
|
||||
},
|
||||
percentBox: {
|
||||
width: '48%',
|
||||
height: 100,
|
||||
minHeight: 100,
|
||||
backgroundColor: '#EAF1FF',
|
||||
borderRadius: 20,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
// Cross-platform shadow styles
|
||||
...Platform.select({
|
||||
ios: {
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 2,
|
||||
},
|
||||
android: {
|
||||
elevation: 2,
|
||||
},
|
||||
}),
|
||||
},
|
||||
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 },
|
||||
},
|
||||
searchByID: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
height: 40,
|
||||
width: '100%',
|
||||
borderColor: '#ccc',
|
||||
borderWidth: 1,
|
||||
borderRadius: 10,
|
||||
paddingHorizontal: 10,
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
|
||||
},
|
||||
barGraphView: {
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 10,
|
||||
borderWidth: 0.5,
|
||||
borderRadius: 25,
|
||||
},
|
||||
trendTitle: {
|
||||
color: '#000',
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
marginBottom: 10
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
|
||||
});
|
||||
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import React, { useState } from 'react';
|
||||
import {View,Text,TouchableOpacity,ScrollView,FlatList} from 'react-native';
|
||||
import { styles } from './style';
|
||||
@@ -202,3 +203,442 @@ const StoreInfo = () => {
|
||||
};
|
||||
|
||||
export default StoreInfo;
|
||||
=======
|
||||
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: '1500–2000 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;
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import React, { useState , useRef} from 'react';
|
||||
import {View,Text,TouchableOpacity,ScrollView,FlatList} from 'react-native';
|
||||
import { styles } from './style';
|
||||
@@ -208,3 +209,359 @@ const StoreInfo = () => {
|
||||
};
|
||||
|
||||
export default StoreInfo;
|
||||
=======
|
||||
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';
|
||||
import { ApiURL } from '../../../api/ApiConstant';
|
||||
|
||||
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: '1500–2000 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 (
|
||||
<>
|
||||
{!item.hideSectionTitle && (
|
||||
<>
|
||||
<Text style={styles.sectionTitle}>{item.section}</Text>
|
||||
{/* <View style={[horizonalLine, { marginVertical: 10 }]} /> */}
|
||||
</>
|
||||
)}
|
||||
<View style={styles.section}>
|
||||
<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: ApiURL.storeInfo,
|
||||
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);
|
||||
// });
|
||||
|
||||
// 👇 Replace Step 2 with this
|
||||
const groupedData = {};
|
||||
let threePEmployeeCount = 0;
|
||||
|
||||
tempData.forEach(entry => {
|
||||
if (entry.section === "3P Employee") {
|
||||
// increment counter
|
||||
threePEmployeeCount += 1;
|
||||
const uniqueKey = `${entry.tabId}-${entry.section}-${Math.random()}`;
|
||||
groupedData[uniqueKey] = {
|
||||
tabId: entry.tabId,
|
||||
section: entry.section,
|
||||
items: entry.items,
|
||||
hideSectionTitle: threePEmployeeCount > 1 // 👈 hide title after first card
|
||||
};
|
||||
} else {
|
||||
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%', marginTop: 20, }}>{renderTabContent()}</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
<Loader visible={loading} />
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
export default StoreInfo;
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { GlobalTheme, Screen, shadow } from '../../../theme';
|
||||
@@ -109,3 +110,123 @@ export const styles = StyleSheet.create({
|
||||
}
|
||||
|
||||
});
|
||||
=======
|
||||
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: 0.5,
|
||||
borderColor: 'gray',
|
||||
marginBottom:15,
|
||||
marginTop:10,
|
||||
// ...shadow,
|
||||
|
||||
},
|
||||
sectionTitle: {
|
||||
color:'#000',
|
||||
fontWeight: GlobalTheme.typography.fontWeight.bold,
|
||||
fontSize: GlobalTheme.typography.fontSize.small,
|
||||
marginHorizontal:10
|
||||
},
|
||||
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
|
||||
}
|
||||
|
||||
});
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -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='contain'>
|
||||
<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',
|
||||
},
|
||||
|
||||
})
|
||||
@@ -1,4 +1,11 @@
|
||||
<<<<<<< HEAD
|
||||
import GlobalTheme from './theme';
|
||||
import { shadow , Screen , horizonalLine } from './theme';
|
||||
|
||||
export {GlobalTheme, Screen , shadow, horizonalLine};
|
||||
=======
|
||||
import GlobalTheme from './theme';
|
||||
import { shadow , Screen , horizonalLine } from './theme';
|
||||
|
||||
export {GlobalTheme, Screen , shadow, horizonalLine};
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import { Dimensions, Platform } from 'react-native';
|
||||
|
||||
const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
|
||||
@@ -84,3 +85,92 @@ export const horizonalLine = {
|
||||
height:1.5,
|
||||
width: '100%',
|
||||
};
|
||||
=======
|
||||
import { Dimensions, Platform } from 'react-native';
|
||||
|
||||
const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
|
||||
|
||||
export const Screen = {
|
||||
screenWidth,
|
||||
screenHeight,
|
||||
isAndroid: Platform.OS === 'android',
|
||||
isIOS: Platform.OS === 'ios',
|
||||
};
|
||||
|
||||
const GlobalTheme = {
|
||||
colors: {
|
||||
// Primary Colors
|
||||
primary: '#113F8C', // Main color for buttons, headers
|
||||
secondary: '#2357C6',
|
||||
lightbluetext:'#7F83AB',
|
||||
text: '#333333', // Text color for most content
|
||||
lightblue:'#E2E7F2',
|
||||
lightblueborder:'#ECECEC',
|
||||
bluebgcolor :'#EAF0F4',
|
||||
|
||||
// Additional Colors
|
||||
success: '#4caf50', // Success or positive action color
|
||||
yellow: '#FFD661', // Warning or alert color
|
||||
error: '#ff5252', // Error or negative action color
|
||||
info: '#2196f3', // Information color
|
||||
|
||||
// Grayscale
|
||||
white: '#ffffff',
|
||||
lightGray: '#F1F1F1',
|
||||
gray: '#676767',
|
||||
darkGray: '#555555',
|
||||
black: '#000000',
|
||||
},
|
||||
typography: {
|
||||
// Define typography styles here (e.g., font family, sizes, line heights, etc.)
|
||||
fontFamily: 'Regular',
|
||||
fontSize: {
|
||||
xxsmall: 12,
|
||||
xsmall :14,
|
||||
small: 16,
|
||||
medium: 18,
|
||||
large: 20,
|
||||
},
|
||||
fontWeight: {
|
||||
regular: '400',
|
||||
medium: '500',
|
||||
bold: '700',
|
||||
},
|
||||
},
|
||||
spacing: {
|
||||
// Define spacing units (e.g., margin and padding) for consistent layout
|
||||
xs: 4,
|
||||
sm: 8,
|
||||
md: 16,
|
||||
lg: 24,
|
||||
xl: 32,
|
||||
},
|
||||
borderRadius: {
|
||||
// Define border radius values for consistent UI elements
|
||||
sm: 5,
|
||||
md: 10,
|
||||
lg: 15,
|
||||
lgg: 20,
|
||||
xlg: 25,
|
||||
xxlg: 35,
|
||||
|
||||
},
|
||||
// Add other global theme properties as needed
|
||||
};
|
||||
|
||||
export default GlobalTheme;
|
||||
|
||||
export const shadow = {
|
||||
shadowColor: '#00000029',
|
||||
shadowOffset: {width: 5, height: 5},
|
||||
shadowOpacity: 0.8,
|
||||
shadowRadius: 10,
|
||||
elevation: 20,
|
||||
};
|
||||
|
||||
export const horizonalLine = {
|
||||
backgroundColor :'#E2E7F2',
|
||||
height:1.5,
|
||||
width: '100%',
|
||||
};
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
import {SafeAreaView, StatusBar} from 'react-native';
|
||||
import React from 'react';
|
||||
import {GlobalTheme} from '../theme';
|
||||
@@ -18,3 +19,26 @@ const MyStatusBar = () => {
|
||||
};
|
||||
|
||||
export default MyStatusBar;
|
||||
=======
|
||||
import { StatusBar} from 'react-native';
|
||||
import React from 'react';
|
||||
import {GlobalTheme} from '../theme';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
|
||||
const MyStatusBar = () => {
|
||||
return (
|
||||
<SafeAreaView
|
||||
style={{
|
||||
backgroundColor: GlobalTheme.colors.primary,
|
||||
}}>
|
||||
<StatusBar
|
||||
translucent={false}
|
||||
backgroundColor={GlobalTheme.colors.black}
|
||||
barStyle={'light-content'}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
export default MyStatusBar;
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// responsive.js
|
||||
import { Dimensions, PixelRatio } from 'react-native';
|
||||
|
||||
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
|
||||
|
||||
const scale = SCREEN_WIDTH / 375; // base iPhone 11 width
|
||||
|
||||
export const normalize = (size) => {
|
||||
const newSize = size * scale;
|
||||
return Math.round(PixelRatio.roundToNearestPixel(newSize));
|
||||
};
|
||||