first commit
|
After Width: | Height: | Size: 168 KiB |
@@ -110,6 +110,11 @@ android {
|
||||
dependencies {
|
||||
// The version of react-native is set by the React Native Gradle Plugin
|
||||
implementation("com.facebook.react:react-android")
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
implementation project(':react-native-vector-icons')
|
||||
implementation project(':react-native-fs')
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
if (hermesEnabled.toBoolean()) {
|
||||
implementation("com.facebook.react:hermes-android")
|
||||
@@ -117,3 +122,9 @@ dependencies {
|
||||
implementation jscFlavor
|
||||
}
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
apply from: file("../../node_modules/react-native-vector-icons/fonts.gradle")
|
||||
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -1,3 +1,7 @@
|
||||
<resources>
|
||||
<<<<<<< HEAD
|
||||
<string name="app_name">PerformicsStoreDNA</string>
|
||||
=======
|
||||
<string name="app_name">Performics Store DNA</string>
|
||||
>>>>>>> dabur-store-dna
|
||||
</resources>
|
||||
|
||||
@@ -32,7 +32,12 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
||||
# your application. You should enable this flag either if you want
|
||||
# to write custom TurboModules/Fabric components OR use libraries that
|
||||
# are providing them.
|
||||
<<<<<<< HEAD
|
||||
newArchEnabled=true
|
||||
=======
|
||||
newArchEnabled=false
|
||||
org.gradle.java.home=/opt/homebrew/opt/openjdk@17
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
# Use this property to enable or disable the Hermes JS engine.
|
||||
# If set to false, you will be using JSC instead.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
@REM Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
@REM
|
||||
@REM This source code is licensed under the MIT license found in the
|
||||
@@ -97,3 +98,104 @@ exit /b %EXIT_CODE%
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
=======
|
||||
@REM Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
@REM
|
||||
@REM This source code is licensed under the MIT license found in the
|
||||
@REM LICENSE file in the root directory of this source tree.
|
||||
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -4,3 +4,9 @@ extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autoli
|
||||
rootProject.name = 'PerformicsStoreDNA'
|
||||
include ':app'
|
||||
includeBuild('../node_modules/@react-native/gradle-plugin')
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
include ':react-native-fs'
|
||||
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
|
||||
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
module.exports = {
|
||||
presets: ['module:@react-native/babel-preset'],
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
plugins: [
|
||||
// other plugins here...
|
||||
'react-native-reanimated/plugin', // 👈 must be last
|
||||
],
|
||||
>>>>>>> dabur-store-dna
|
||||
};
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
|
||||
// const getTabData = async (tabData) => {
|
||||
// try {
|
||||
// const params = {
|
||||
// parameters: {
|
||||
// projectid: 41654,
|
||||
// year: 2025,
|
||||
// monthno: 6,
|
||||
// storeid: storeData?.StoreId
|
||||
// }
|
||||
// };
|
||||
|
||||
// const graphDetails = tabData?.graphDetails || [];
|
||||
|
||||
// const uniqueUrls = [...new Set(graphDetails.map(graph => graph.GraphUrl))];
|
||||
// // console.log('🔄 Unique URLs:', uniqueUrls);
|
||||
|
||||
// const apiCalls = uniqueUrls.map(url => post(url, params));
|
||||
// const results = await Promise.all(apiCalls);
|
||||
|
||||
// const dataMap = {};
|
||||
// uniqueUrls.forEach((url, idx) => {
|
||||
// dataMap[url] = results[idx];
|
||||
// });
|
||||
|
||||
// setGraphApiData(dataMap);
|
||||
// } catch (err) {
|
||||
// console.log("❌ Error fetching tab data:", err);
|
||||
// }
|
||||
// };
|
||||
|
||||
// Handle scroll event to show/hide button
|
||||
|
||||
|
||||
|
||||
|
||||
// const getTabData = async () => {
|
||||
|
||||
// try {
|
||||
// const params = {
|
||||
// parameters: {
|
||||
// projectid: 41654,
|
||||
// year: 2025,
|
||||
// monthno: 6,
|
||||
// storeid: 2702
|
||||
// }
|
||||
// };
|
||||
|
||||
// // Extract all unique GraphUrls
|
||||
// const uniqueUrls = [...new Set(graphDetails.map(graph => graph.GraphUrl))];
|
||||
|
||||
// console.log('uniqueUrls---->', uniqueUrls);
|
||||
|
||||
// // Call all APIs in parallel
|
||||
// const apiCalls = uniqueUrls.map(url => post(url, params));
|
||||
|
||||
// const results = await Promise.all(apiCalls);
|
||||
|
||||
// // Optional: Map result to url if needed
|
||||
// const dataMap = {};
|
||||
// uniqueUrls.forEach((url, idx) => {
|
||||
// dataMap[url] = results[idx];
|
||||
// });
|
||||
// setGraphApiData(dataMap);
|
||||
|
||||
// console.log("✅ All API responses:", JSON.stringify(dataMap));
|
||||
|
||||
// // You can now store `dataMap` into a state if needed
|
||||
// // setGraphApiData(dataMap);
|
||||
|
||||
// } catch (err) {
|
||||
// console.log("❌ Error fetching tab data:", err);
|
||||
// }
|
||||
|
||||
|
||||
// };
|
||||
|
||||
|
||||
// const getTabData = () => {
|
||||
// let params = {
|
||||
// "parameters": {
|
||||
// "projectid": 41654,
|
||||
// "year": 2025,
|
||||
// "monthno": 6,
|
||||
// "storeid": 2702
|
||||
// }
|
||||
// }
|
||||
// post(ApiURL.pssscoreApi, params)
|
||||
// .then(res => {
|
||||
// console.log('psscoreApi res==>', res);
|
||||
|
||||
// })
|
||||
// }
|
||||
|
||||
// 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>
|
||||
// );
|
||||
// }
|
||||
// };
|
||||
|
||||
// const getFilterStateCity = () => {
|
||||
|
||||
// let data = JSON.stringify({
|
||||
// "UserId": "RH5"
|
||||
// });
|
||||
|
||||
// let config = {
|
||||
// method: 'post',
|
||||
// maxBodyLength: Infinity,
|
||||
// url: 'https://api1.parinaam.in/api/dabur/StoreDNAfilter',
|
||||
// headers: {
|
||||
// 'api_key': '9a1f056fecb84eaf8eb4152dda22ab0501955c4f9bbe7daa8780740459fdde7a',
|
||||
// 'Content-Type': 'application/json'
|
||||
// },
|
||||
// data: data
|
||||
// };
|
||||
|
||||
// axios.request(config)
|
||||
// .then((response) => {
|
||||
// console.log("filter", JSON.stringify(response.data));
|
||||
|
||||
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.log(error);
|
||||
// });
|
||||
|
||||
// }
|
||||
@@ -11,6 +11,7 @@
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||
<<<<<<< HEAD
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -25,6 +26,13 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
=======
|
||||
D36C74772E212F7200955E13 /* white.png in Resources */ = {isa = PBXBuildFile; fileRef = D36C74762E212F7200955E13 /* white.png */; };
|
||||
DB45BE4A6E0F03A0CC859A6F /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
>>>>>>> dabur-store-dna
|
||||
13B07F961A680F5B00A75B9A /* PerformicsStoreDNA.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PerformicsStoreDNA.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = PerformicsStoreDNA/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = PerformicsStoreDNA/Info.plist; sourceTree = "<group>"; };
|
||||
@@ -34,6 +42,10 @@
|
||||
5DCACB8F33CDC322A6C60F78 /* libPods-PerformicsStoreDNA.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PerformicsStoreDNA.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = PerformicsStoreDNA/AppDelegate.swift; sourceTree = "<group>"; };
|
||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = PerformicsStoreDNA/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
D36C74762E212F7200955E13 /* white.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = white.png; sourceTree = "<group>"; };
|
||||
>>>>>>> dabur-store-dna
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -49,6 +61,7 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
<<<<<<< HEAD
|
||||
00E356F01AD99517003FC87E /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -57,6 +70,8 @@
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
=======
|
||||
>>>>>>> dabur-store-dna
|
||||
13B07FAE1A68108700A75B9A /* PerformicsStoreDNA */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -88,6 +103,10 @@
|
||||
83CBB9F61A601CBA00E9B192 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
D36C74762E212F7200955E13 /* white.png */,
|
||||
>>>>>>> dabur-store-dna
|
||||
13B07FAE1A68108700A75B9A /* PerformicsStoreDNA */,
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
@@ -172,6 +191,7 @@
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
<<<<<<< HEAD
|
||||
00E356EC1AD99517003FC87E /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -179,12 +199,20 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
=======
|
||||
>>>>>>> dabur-store-dna
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
||||
<<<<<<< HEAD
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
=======
|
||||
D36C74772E212F7200955E13 /* white.png in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
DB45BE4A6E0F03A0CC859A6F /* PrivacyInfo.xcprivacy in Resources */,
|
||||
>>>>>>> dabur-store-dna
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -215,10 +243,20 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-PerformicsStoreDNA/Pods-PerformicsStoreDNA-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
inputPaths = (
|
||||
);
|
||||
>>>>>>> dabur-store-dna
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-PerformicsStoreDNA/Pods-PerformicsStoreDNA-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
outputPaths = (
|
||||
);
|
||||
>>>>>>> dabur-store-dna
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PerformicsStoreDNA/Pods-PerformicsStoreDNA-frameworks.sh\"\n";
|
||||
@@ -254,10 +292,20 @@
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-PerformicsStoreDNA/Pods-PerformicsStoreDNA-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
inputPaths = (
|
||||
);
|
||||
>>>>>>> dabur-store-dna
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-PerformicsStoreDNA/Pods-PerformicsStoreDNA-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
outputPaths = (
|
||||
);
|
||||
>>>>>>> dabur-store-dna
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PerformicsStoreDNA/Pods-PerformicsStoreDNA-resources.sh\"\n";
|
||||
@@ -276,6 +324,7 @@
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
<<<<<<< HEAD
|
||||
/* Begin PBXTargetDependency section */
|
||||
00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
@@ -284,6 +333,8 @@
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
=======
|
||||
>>>>>>> dabur-store-dna
|
||||
/* Begin XCBuildConfiguration section */
|
||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
@@ -291,7 +342,12 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
<<<<<<< HEAD
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
=======
|
||||
CURRENT_PROJECT_VERSION = 9;
|
||||
DEVELOPMENT_TEAM = JGDHGNH9XY;
|
||||
>>>>>>> dabur-store-dna
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = PerformicsStoreDNA/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
@@ -299,13 +355,21 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
<<<<<<< HEAD
|
||||
MARKETING_VERSION = 1.0;
|
||||
=======
|
||||
MARKETING_VERSION = 1.9;
|
||||
>>>>>>> dabur-store-dna
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
<<<<<<< HEAD
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
=======
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.performicsstoredna;
|
||||
>>>>>>> dabur-store-dna
|
||||
PRODUCT_NAME = PerformicsStoreDNA;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@@ -319,20 +383,33 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
<<<<<<< HEAD
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
=======
|
||||
CURRENT_PROJECT_VERSION = 9;
|
||||
DEVELOPMENT_TEAM = JGDHGNH9XY;
|
||||
>>>>>>> dabur-store-dna
|
||||
INFOPLIST_FILE = PerformicsStoreDNA/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
<<<<<<< HEAD
|
||||
MARKETING_VERSION = 1.0;
|
||||
=======
|
||||
MARKETING_VERSION = 1.9;
|
||||
>>>>>>> dabur-store-dna
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
<<<<<<< HEAD
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
=======
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.performicsstoredna;
|
||||
>>>>>>> dabur-store-dna
|
||||
PRODUCT_NAME = PerformicsStoreDNA;
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
@@ -408,7 +485,15 @@
|
||||
"-DFOLLY_CFG_NO_COROUTINES=1",
|
||||
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
|
||||
);
|
||||
<<<<<<< HEAD
|
||||
SDKROOT = iphoneos;
|
||||
=======
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||
USE_HERMES = true;
|
||||
>>>>>>> dabur-store-dna
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -473,7 +558,14 @@
|
||||
"-DFOLLY_CFG_NO_COROUTINES=1",
|
||||
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
|
||||
);
|
||||
<<<<<<< HEAD
|
||||
SDKROOT = iphoneos;
|
||||
=======
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
USE_HERMES = true;
|
||||
>>>>>>> dabur-store-dna
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:PerformicsStoreDNA.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
||||
|
After Width: | Height: | Size: 393 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
@@ -1,46 +1,82 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"filename" : "40.png",
|
||||
>>>>>>> dabur-store-dna
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"filename" : "60.png",
|
||||
>>>>>>> dabur-store-dna
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"filename" : "58.png",
|
||||
>>>>>>> dabur-store-dna
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"filename" : "87.png",
|
||||
>>>>>>> dabur-store-dna
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"filename" : "80.png",
|
||||
>>>>>>> dabur-store-dna
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"filename" : "120.png",
|
||||
>>>>>>> dabur-store-dna
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"filename" : "120 1.png",
|
||||
>>>>>>> dabur-store-dna
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"filename" : "180.png",
|
||||
>>>>>>> dabur-store-dna
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"filename" : "1024.png",
|
||||
>>>>>>> dabur-store-dna
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -26,16 +26,52 @@
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<<<<<<< HEAD
|
||||
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
|
||||
=======
|
||||
>>>>>>> dabur-store-dna
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<<<<<<< HEAD
|
||||
<string></string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
=======
|
||||
<string>This app uses your location to enhance reporting and store-related services.</string>
|
||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||
<string>We need access to your location for [your reason, e.g., providing location-based services].</string>
|
||||
<key>RCTNewArchEnabled</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>We need access to your camera to take photos.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>We need access to your microphone for video recording.</string>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>AntDesign.ttf</string>
|
||||
<string>Entypo.ttf</string>
|
||||
<string>EvilIcons.ttf</string>
|
||||
<string>Feather.ttf</string>
|
||||
<string>FontAwesome.ttf</string>
|
||||
<string>FontAwesome5_Brands.ttf</string>
|
||||
<string>FontAwesome5_Regular.ttf</string>
|
||||
<string>FontAwesome5_Solid.ttf</string>
|
||||
<string>Fontisto.ttf</string>
|
||||
<string>Foundation.ttf</string>
|
||||
<string>Ionicons.ttf</string>
|
||||
<string>MaterialIcons.ttf</string>
|
||||
<string>MaterialCommunityIcons.ttf</string>
|
||||
<string>SimpleLineIcons.ttf</string>
|
||||
<string>Octicons.ttf</string>
|
||||
<string>Zocial.ttf</string>
|
||||
</array>
|
||||
>>>>>>> dabur-store-dna
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIICiDCCAXACAQAwQzEmMCQGCSqGSIb3DQEJARYXY3BtaW5kaWFpdEBjcG1pbmRp
|
||||
YS5jb20xDDAKBgNVBAMMA0NQTTELMAkGA1UEBhMCSU4wggEiMA0GCSqGSIb3DQEB
|
||||
AQUAA4IBDwAwggEKAoIBAQD5H7EcZgD+77Vb5lruKpetnuU7vSZvZgnmL/4bkLuk
|
||||
R2iSNmu/kGGp/oY87kHeTEoNV6YJ7vveDAs5r1OInU6s/YKve6w94cK6IG0NFUnH
|
||||
6gR3TJbLduoKRvlPPne0Yrgap5YfYfnQ7GkMHohpa2zyCUlo1+u8KqjPos5f07S/
|
||||
6vSEAV4FMAgrYqSgRxRPPhw8UUJ9MFiSH3NtYYYXcbql1j/fFHiL/gJmClKRilNd
|
||||
TScorE4h95YeylJtBmiqRQSN76gophmjiWDVLnP7s3AyGKhw0FmwYJMc2b89/TwL
|
||||
uDGVFSxM9IGRb7UoKYWU9N8ThGoOghmiAwCiNd+VDKbnAgMBAAGgADANBgkqhkiG
|
||||
9w0BAQsFAAOCAQEAxXc475lytyZVl/Iahdgj9xHS2shwgL6k9Eu5OqIGkGovrx0F
|
||||
63Eu4pyqgjh0ZfTC2somOYvA39NHEDiNXfjNYLRUo6PVNMxuVRVjjSR0UvfBcC88
|
||||
Fjo8I8m/2ovTjNSTgIgDCexfNYFdlJP7mqGJ9nybmPdvzWLVuB9N77zElnusLsTl
|
||||
pr3q1RBnetkYf9xNGRdeQIRHtJJrnVTDgXHwCcd9W91z1O2vnxHubhwqHf8gsr8h
|
||||
GRL2bFZGJBc8JJ91lo2NNrXAzeFEUALOdfBPtzoB98+Npo1pjlUoFJNZvirrOyq3
|
||||
DZzLyLlz3PH9Qvw9GGVQ63NHsbTcX84YFpfRZg==
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
{
|
||||
"name": "PerformicsStoreDNA",
|
||||
"version": "0.0.1",
|
||||
@@ -56,3 +57,85 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
=======
|
||||
{
|
||||
"name": "PerformicsStoreDNA",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
"ios": "react-native run-ios",
|
||||
"lint": "eslint .",
|
||||
"start": "react-native start",
|
||||
"test": "jest",
|
||||
"postinstall": "patch-package"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bam.tech/react-native-image-resizer": "^3.0.11",
|
||||
"@gluestack-ui/nativewind-utils": "^1.0.26",
|
||||
"@gluestack-ui/overlay": "^0.1.22",
|
||||
"@gluestack-ui/toast": "^1.0.9",
|
||||
"@likashefqet/react-native-image-zoom": "^4.3.0",
|
||||
"@react-native-async-storage/async-storage": "^2.2.0",
|
||||
"@react-native-community/geolocation": "^3.4.0",
|
||||
"@react-native/new-app-screen": "0.80.0",
|
||||
"@react-navigation/elements": "^2.5.2",
|
||||
"@react-navigation/native": "^7.1.14",
|
||||
"@react-navigation/native-stack": "^7.3.21",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"axios": "^1.10.0",
|
||||
"deprecated-react-native-prop-types": "^5.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"patch-package": "^8.0.0",
|
||||
"react": "19.1.0",
|
||||
"react-native": "0.80.0",
|
||||
"react-native-chart-kit": "^6.12.0",
|
||||
"react-native-element-dropdown": "^2.12.4",
|
||||
"react-native-fs": "^2.20.0",
|
||||
"react-native-gesture-handler": "^2.27.1",
|
||||
"react-native-gifted-charts": "^1.4.63",
|
||||
"react-native-keyboard-aware-scroll-view": "^0.9.5",
|
||||
"react-native-linear-gradient": "^2.8.3",
|
||||
"react-native-loader-kit": "^3.0.0",
|
||||
"react-native-modal-selector": "^2.1.2",
|
||||
"react-native-multiple-select": "^0.5.12",
|
||||
"react-native-otp-entry": "^1.8.5",
|
||||
"react-native-permissions": "^5.4.1",
|
||||
"react-native-photo-manipulator": "^1.9.2",
|
||||
"react-native-raw-bottom-sheet": "^3.0.0",
|
||||
"react-native-reanimated": "^3.18.0",
|
||||
"react-native-safe-area-context": "^5.5.0",
|
||||
"react-native-screens": "^4.11.1",
|
||||
"react-native-sqlite-storage": "^6.0.1",
|
||||
"react-native-svg": "^15.12.0",
|
||||
"react-native-toast-message": "^2.3.1",
|
||||
"react-native-vector-icons": "^10.2.0",
|
||||
"react-native-vision-camera": "^4.7.1",
|
||||
"react-redux": "^9.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@babel/runtime": "^7.25.0",
|
||||
"@react-native-community/cli": "19.0.0",
|
||||
"@react-native-community/cli-platform-android": "19.0.0",
|
||||
"@react-native-community/cli-platform-ios": "19.0.0",
|
||||
"@react-native/babel-preset": "0.80.0",
|
||||
"@react-native/eslint-config": "0.80.0",
|
||||
"@react-native/metro-config": "0.80.0",
|
||||
"@react-native/typescript-config": "0.80.0",
|
||||
"@types/jest": "^29.5.13",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-test-renderer": "^19.1.0",
|
||||
"eslint": "^8.19.0",
|
||||
"jest": "^29.6.3",
|
||||
"prettier": "2.8.8",
|
||||
"react-test-renderer": "19.1.0",
|
||||
"typescript": "5.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
>>>>>>> dabur-store-dna
|
||||
|
||||
@@ -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": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||