first commit

This commit is contained in:
Gitea
2026-04-20 15:13:29 +05:30
120 changed files with 32938 additions and 0 deletions
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

+11
View File
@@ -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
+7
View File
@@ -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"
Binary file not shown.

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>
Binary file not shown.
Binary file not shown.
+5
View File
@@ -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.
+102
View File
@@ -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
+6
View File
@@ -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
+7
View File
@@ -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
};
+275
View File
@@ -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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

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"
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

+36
View File
@@ -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>
+3111
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.

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-----
Binary file not shown.
Binary file not shown.
Binary file not shown.
+83
View File
@@ -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
+28
View File
@@ -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
+11
View File
@@ -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`,
};
+158
View File
@@ -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;
});
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

+102
View File
@@ -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();
},
},
]
);
}
+36
View File
@@ -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
+51
View File
@@ -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',
},
});
+103
View File
@@ -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,
},
});
+16
View File
@@ -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
+173
View File
@@ -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',
},
});
+41
View File
@@ -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;
+73
View File
@@ -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',
},
})
+85
View File
@@ -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'
}
});
+88
View File
@@ -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
+11
View File
@@ -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
+78
View File
@@ -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
+39
View File
@@ -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
+54
View File
@@ -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;
+38
View File
@@ -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 };
+9
View File
@@ -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};
+177
View File
@@ -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;
+455
View File
@@ -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 };
});
}
+65
View File
@@ -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 };
});
}
+59
View File
@@ -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;
}
+54
View File
@@ -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;
+8
View File
@@ -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
+35
View File
@@ -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;
+28
View File
@@ -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
+7
View File
@@ -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 })
+97
View File
@@ -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;
+85
View File
@@ -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
+103
View File
@@ -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;
+87
View File
@@ -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',
},
});
+102
View File
@@ -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
+222
View File
@@ -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
+152
View File
@@ -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;
+431
View File
@@ -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
File diff suppressed because it is too large Load Diff
@@ -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": {}
}
]
}
}
]
}

Some files were not shown because too many files have changed in this diff Show More