Compare commits
7 Commits
feature/ap
...
rename-pac
| Author | SHA1 | Date |
|---|---|---|
|
|
8a60842bec | |
|
|
39a65e00e3 | |
|
|
c3dd8e3867 | |
|
|
4eb06631b4 | |
|
|
d44677a034 | |
|
|
04fd60d6bc | |
|
|
1488116909 |
|
|
@ -40,4 +40,45 @@ android/.gradle/
|
||||||
android/.idea/
|
android/.idea/
|
||||||
android/local.properties
|
android/local.properties
|
||||||
android/app/build/
|
android/app/build/
|
||||||
android/build/
|
android/build/
|
||||||
|
# @generated expo-cli sync-8d4afeec25ea8a192358fae2f8e2fc766bdce4ec
|
||||||
|
# The following patterns were generated by expo-cli
|
||||||
|
|
||||||
|
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Expo
|
||||||
|
.expo/
|
||||||
|
dist/
|
||||||
|
web-build/
|
||||||
|
expo-env.d.ts
|
||||||
|
|
||||||
|
# Native
|
||||||
|
*.orig.*
|
||||||
|
*.jks
|
||||||
|
*.p8
|
||||||
|
*.p12
|
||||||
|
*.key
|
||||||
|
*.mobileprovision
|
||||||
|
|
||||||
|
# Metro
|
||||||
|
.metro-health-check*
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.*
|
||||||
|
yarn-debug.*
|
||||||
|
yarn-error.*
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# @end expo-cli
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"java.configuration.updateBuildConfiguration": "interactive",
|
||||||
|
"java.compile.nullAnalysis.mode": "automatic"
|
||||||
|
}
|
||||||
|
|
@ -87,13 +87,13 @@ android {
|
||||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||||
compileSdk rootProject.ext.compileSdkVersion
|
compileSdk rootProject.ext.compileSdkVersion
|
||||||
|
|
||||||
namespace 'com.vinayjangra01.BatteryAsAService'
|
namespace "com.vecmocon.driversaathi"
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId 'com.vinayjangra01.BatteryAsAService'
|
applicationId 'com.vecmocon.driversaathi'
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 1
|
versionCode 11
|
||||||
versionName "1.0.0"
|
versionName "2.0.0"
|
||||||
}
|
}
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
debug {
|
debug {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.vecmocon.driversaathi" xmlns:tools="http://schemas.android.com/tools">
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
|
||||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|
@ -12,13 +8,15 @@
|
||||||
<data android:scheme="https"/>
|
<data android:scheme="https"/>
|
||||||
</intent>
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true">
|
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:requestLegacyExternalStorage="true">
|
||||||
<meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/>
|
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
|
||||||
|
<meta-data android:name="expo.modules.updates.EXPO_SDK_VERSION" android:value="53.0.0"/>
|
||||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
||||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.geo.API_KEY"
|
android:name="com.google.android.geo.API_KEY"
|
||||||
android:value="AIzaSyCJ4IkKM0ybTOwvdylLVx9BxL2ZV1PEvRk"/>
|
android:value="AIzaSyCJ4IkKM0ybTOwvdylLVx9BxL2ZV1PEvRk"/>
|
||||||
|
<meta-data android:name="expo.modules.updates.EXPO_UPDATE_URL" android:value="https://exp.host/@vecmocon/BatteryAsAService"/>
|
||||||
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
|
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
|
@ -29,6 +27,7 @@
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
<data android:scheme="batteryasaservice"/>
|
<data android:scheme="batteryasaservice"/>
|
||||||
|
<data android:scheme="com.vecmocon.driversaathi"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.vinayjangra01.BatteryAsAService
|
package com.vecmocon.driversaathi
|
||||||
import expo.modules.splashscreen.SplashScreenManager
|
import expo.modules.splashscreen.SplashScreenManager
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.vinayjangra01.BatteryAsAService
|
package com.vecmocon.driversaathi
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/iconBackground"/>
|
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 858 B After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 464 B After Width: | Height: | Size: 696 B |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 7.8 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -1,6 +1,6 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Driver Saathi</string>
|
<string name="app_name">Driver Saathi</string>
|
||||||
|
<string name="expo_system_ui_user_interface_style" translatable="false">light</string>
|
||||||
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
|
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
|
||||||
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
|
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
|
||||||
<string name="expo_system_ui_user_interface_style" translatable="false">light</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<style name="AppTheme" parent="Theme.EdgeToEdge">
|
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
|
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
<item name="android:statusBarColor">#252A34</item>
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="Theme.App.SplashScreen" parent="Theme.SplashScreen">
|
<style name="Theme.App.SplashScreen" parent="Theme.SplashScreen">
|
||||||
<item name="windowSplashScreenBackground">@color/splashscreen_background</item>
|
<item name="windowSplashScreenBackground">@color/splashscreen_background</item>
|
||||||
<item name="windowSplashScreenAnimatedIcon">@drawable/splashscreen_logo</item>
|
<item name="windowSplashScreenAnimatedIcon">@drawable/splashscreen_logo</item>
|
||||||
<item name="postSplashScreenTheme">@style/AppTheme</item>
|
<item name="postSplashScreenTheme">@style/AppTheme</item>
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -55,5 +55,4 @@ EX_DEV_CLIENT_NETWORK_INSPECTOR=true
|
||||||
# Use legacy packaging to compress native libraries in the resulting APK.
|
# Use legacy packaging to compress native libraries in the resulting APK.
|
||||||
expo.useLegacyPackaging=false
|
expo.useLegacyPackaging=false
|
||||||
|
|
||||||
# Whether the app is configured to use edge-to-edge via the app config or `react-native-edge-to-edge` plugin
|
expo.jsEngine=jsc
|
||||||
expo.edgeToEdgeEnabled=true
|
|
||||||
7
app.json
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"expo": {
|
"expo": {
|
||||||
"name": "BatteryAsAService",
|
"name": "Driver Saathi",
|
||||||
"slug": "BatteryAsAService",
|
"slug": "BatteryAsAService",
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"orientation": "portrait",
|
"orientation": "portrait",
|
||||||
"icon": "./assets/images/icon.png",
|
"icon": "./assets/images/icon.png",
|
||||||
"scheme": "batteryasaservice",
|
"scheme": "batteryasaservice",
|
||||||
|
|
@ -22,7 +22,8 @@
|
||||||
"backgroundColor": "#ffffff"
|
"backgroundColor": "#ffffff"
|
||||||
},
|
},
|
||||||
"edgeToEdgeEnabled": true,
|
"edgeToEdgeEnabled": true,
|
||||||
"package": "com.vinayjangra01.BatteryAsAService"
|
"package": "com.vecmocon.driversaathi",
|
||||||
|
"versionCode": 11
|
||||||
},
|
},
|
||||||
"web": {
|
"web": {
|
||||||
"bundler": "metro",
|
"bundler": "metro",
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ import { useSnackbar } from "@/contexts/Snackbar";
|
||||||
import NetInfo from "@react-native-community/netinfo";
|
import NetInfo from "@react-native-community/netinfo";
|
||||||
import { getPaymentSummary, getUserDetails } from "@/store/userSlice";
|
import { getPaymentSummary, getUserDetails } from "@/store/userSlice";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { logout } from "@/store/authSlice";
|
import { getLanguage, setLanguage } from "@/services/i18n";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export default function TabLayout() {
|
export default function TabLayout() {
|
||||||
const { isLoggedIn } = useSelector((state: RootState) => state.auth);
|
const { isLoggedIn } = useSelector((state: RootState) => state.auth);
|
||||||
|
|
@ -16,13 +17,14 @@ export default function TabLayout() {
|
||||||
const TAB_CONFIG = useTabConfig();
|
const TAB_CONFIG = useTabConfig();
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
const { showSnackbar } = useSnackbar();
|
const { showSnackbar } = useSnackbar();
|
||||||
|
const { t } = useTranslation();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = NetInfo.addEventListener((state) => {
|
const unsubscribe = NetInfo.addEventListener((state) => {
|
||||||
const isConnected = state.isConnected;
|
const isConnected = state.isConnected;
|
||||||
|
|
||||||
if (isConnected === false) {
|
if (isConnected === false) {
|
||||||
console.log("No internet connection");
|
console.log("No internet connection");
|
||||||
showSnackbar("No internet connection", "error");
|
showSnackbar(t("common.no-internet-connection"), "error");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -34,13 +36,19 @@ export default function TabLayout() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchAndInit = async () => {
|
const fetchAndInit = async () => {
|
||||||
try {
|
try {
|
||||||
|
const lang = await getLanguage();
|
||||||
|
setLanguage(lang || "en");
|
||||||
const result = await dispatch(getUserDetails()).unwrap();
|
const result = await dispatch(getUserDetails()).unwrap();
|
||||||
await dispatch(getPaymentSummary());
|
await dispatch(getPaymentSummary());
|
||||||
console.log("User details fetched", result);
|
|
||||||
initSocket();
|
if (result?.batteries?.[0]?.device_id) {
|
||||||
|
await initSocket();
|
||||||
|
} else {
|
||||||
|
console.warn("Skipping initSocket, no device_id yet");
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch user details", error);
|
console.error("Failed to fetch user details", error);
|
||||||
showSnackbar("Failed to fetch user details", "error");
|
showSnackbar(t("common.something-went-wrong"), "error");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,25 +35,32 @@ import {
|
||||||
getPaymentSummary,
|
getPaymentSummary,
|
||||||
getUserDetails,
|
getUserDetails,
|
||||||
} from "@/store/userSlice";
|
} from "@/store/userSlice";
|
||||||
import api from "@/services/axiosClient";
|
|
||||||
import { setDueAmount } from "@/store/paymentSlice";
|
import { setDueAmount } from "@/store/paymentSlice";
|
||||||
import { EmiResponse } from "./payments";
|
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import EMINotification from "@/components/Payments/EmiNotification";
|
import EMINotification from "@/components/Payments/EmiNotification";
|
||||||
|
import { initSocket } from "@/services/socket";
|
||||||
|
import { useSnackbar } from "@/contexts/Snackbar";
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const [isSupportModalVisible, setIsSupportModalVisible] = useState(false);
|
const [isSupportModalVisible, setIsSupportModalVisible] = useState(false);
|
||||||
const { SoC, SoH, chargingState, lat, lon, loading, error, totalDistance } =
|
const { SoC, SoH, chargingState, lat, lon, loading, totalDistance, error } =
|
||||||
useSelector((state: RootState) => state.telemetry);
|
useSelector((state: RootState) => state.telemetry);
|
||||||
|
|
||||||
|
const { showSnackbar } = useSnackbar();
|
||||||
|
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
const spinValue = useState(new Animated.Value(0))[0];
|
const spinValue = useState(new Animated.Value(0))[0];
|
||||||
|
|
||||||
const { data, paymentSummary } = useSelector(
|
const {
|
||||||
(state: RootState) => state.user
|
data,
|
||||||
);
|
paymentSummary,
|
||||||
|
loading: userLoading,
|
||||||
|
} = useSelector((state: RootState) => state.user);
|
||||||
|
|
||||||
|
const isRefreshing = refreshing || userLoading;
|
||||||
|
|
||||||
const [prevPosition, setPrevPosition] = useState<{
|
const [prevPosition, setPrevPosition] = useState<{
|
||||||
lat: number;
|
lat: number;
|
||||||
lon: number;
|
lon: number;
|
||||||
|
|
@ -122,18 +129,26 @@ export default function HomeScreen() {
|
||||||
},
|
},
|
||||||
headerTitle: () => (
|
headerTitle: () => (
|
||||||
<View style={styles.headerTitleContainer}>
|
<View style={styles.headerTitleContainer}>
|
||||||
<Text style={styles.title}>{model}</Text>
|
<View style={styles.logoContainer}>
|
||||||
<Text style={styles.subtitle}>{chasisNumber}</Text>
|
<Image
|
||||||
|
source={require("../../assets/images/lio_logo.png")}
|
||||||
|
style={styles.logo}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text style={styles.title}>{model}</Text>
|
||||||
|
<Text style={styles.subtitle}>{chasisNumber}</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
),
|
),
|
||||||
headerRight: () => (
|
headerRight: () => (
|
||||||
<View style={styles.rightContainer}>
|
<View style={styles.rightContainer}>
|
||||||
<Pressable onPress={handleManualRefresh} disabled={refreshing}>
|
<Pressable onPress={handleManualRefresh} disabled={isRefreshing}>
|
||||||
<Animated.View style={{ transform: [{ rotate: spin }] }}>
|
<Animated.View style={{ transform: [{ rotate: spin }] }}>
|
||||||
<RefreshIcon
|
<RefreshIcon
|
||||||
width={50}
|
width={50}
|
||||||
height={50}
|
height={50}
|
||||||
opacity={refreshing ? 0.5 : 1}
|
opacity={isRefreshing ? 0.5 : 1}
|
||||||
/>
|
/>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
|
@ -159,7 +174,7 @@ export default function HomeScreen() {
|
||||||
</View>
|
</View>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}, [navigation, data, model, chasisNumber, refreshing]);
|
}, [navigation, data, model, chasisNumber, isRefreshing]);
|
||||||
|
|
||||||
const openInGoogleMaps = () => {
|
const openInGoogleMaps = () => {
|
||||||
const url = `https://www.google.com/maps/search/?api=1&query=${lat},${lon}`;
|
const url = `https://www.google.com/maps/search/?api=1&query=${lat},${lon}`;
|
||||||
|
|
@ -196,12 +211,18 @@ export default function HomeScreen() {
|
||||||
startSpin();
|
startSpin();
|
||||||
|
|
||||||
await dispatch(clearUser());
|
await dispatch(clearUser());
|
||||||
await dispatch(getUserDetails()).unwrap();
|
await Promise.all([
|
||||||
await dispatch(getPaymentSummary()).unwrap();
|
dispatch(getUserDetails()).unwrap(),
|
||||||
|
dispatch(getPaymentSummary()).unwrap(),
|
||||||
|
]);
|
||||||
|
|
||||||
console.log("Manual refresh complete");
|
await initSocket();
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error("Manual refresh failed", error);
|
if (error.message === "Network Error" || !error.response) {
|
||||||
|
showSnackbar(t("common.no-internet-connection"), "error");
|
||||||
|
} else {
|
||||||
|
showSnackbar(t("common.something-went-wrong"), "error");
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
stopSpin();
|
stopSpin();
|
||||||
setRefreshing(false);
|
setRefreshing(false);
|
||||||
|
|
@ -225,27 +246,32 @@ export default function HomeScreen() {
|
||||||
<View style={{ flex: 1, backgroundColor: "#F3F5F8" }}>
|
<View style={{ flex: 1, backgroundColor: "#F3F5F8" }}>
|
||||||
<StatusBar style="dark" />
|
<StatusBar style="dark" />
|
||||||
<ScrollView contentContainerStyle={styles.container} style={{ flex: 1 }}>
|
<ScrollView contentContainerStyle={styles.container} style={{ flex: 1 }}>
|
||||||
{daysLeftToPayEmi && daysLeftToPayEmiText && daysLeftToPayEmi > 0 && (
|
{daysLeftToPayEmi != undefined &&
|
||||||
<ServiceReminderCard
|
Boolean(daysLeftToPayEmi && daysLeftToPayEmi > 0) && (
|
||||||
type={
|
<ServiceReminderCard
|
||||||
daysLeftToPayEmi >= payments.EMI_WARNING_DAYS_THRESHOLD
|
type={
|
||||||
? "warning"
|
daysLeftToPayEmi >= payments.EMI_WARNING_DAYS_THRESHOLD
|
||||||
: "danger"
|
? "warning"
|
||||||
}
|
: "danger"
|
||||||
message={`${daysLeftToPayEmi} ${t("home.days-left-to-pay-emi")}`}
|
}
|
||||||
subMessage={t("home.pay-now")}
|
message={`${daysLeftToPayEmi} ${t("home.days-left-to-pay-emi")}`}
|
||||||
redirectPath="/(tabs)/payments"
|
subMessage={t("home.pay-now")}
|
||||||
/>
|
redirectPath="/(tabs)/payments"
|
||||||
)}
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{regularServiceDueInDaysText && (
|
{regularServiceDueInDaysText &&
|
||||||
<ServiceReminderCard
|
Boolean(
|
||||||
type="info"
|
regularServiceDueInDaysText &&
|
||||||
message={regularServiceDueInDaysText}
|
regularServiceDueInDaysText.trim() !== ""
|
||||||
subMessage="Service Reminder"
|
) && (
|
||||||
redirectPath="/(tabs)/service"
|
<ServiceReminderCard
|
||||||
/>
|
type="info"
|
||||||
)}
|
message={regularServiceDueInDaysText}
|
||||||
|
subMessage="Service Reminder"
|
||||||
|
redirectPath="/(tabs)/service"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
<SemiCircleProgress progress={SoC} status={chargingState} />
|
<SemiCircleProgress progress={SoC} status={chargingState} />
|
||||||
|
|
@ -275,12 +301,10 @@ export default function HomeScreen() {
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<View style={styles.map}>
|
<View style={styles.map}>
|
||||||
{loading ? (
|
{error ? (
|
||||||
<View style={styles.errorContainer}>
|
<View style={styles.errorContainer}>
|
||||||
<LocationOff />
|
<LocationOff />
|
||||||
<Text style={styles.errorText}>
|
<Text style={styles.errorText}>{t("home.error-location")}</Text>
|
||||||
{t("home.fetching-location")}
|
|
||||||
</Text>
|
|
||||||
</View>
|
</View>
|
||||||
) : lat != null && lon != null && !(lat == 0 && lon == 0) ? (
|
) : lat != null && lon != null && !(lat == 0 && lon == 0) ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -326,18 +350,19 @@ export default function HomeScreen() {
|
||||||
) : (
|
) : (
|
||||||
<View style={styles.errorContainer}>
|
<View style={styles.errorContainer}>
|
||||||
<LocationOff />
|
<LocationOff />
|
||||||
<Text style={styles.errorText}>{t("home.error-location")}</Text>
|
<Text style={styles.errorText}>
|
||||||
|
{t("home.fetching-location")}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
{warrantyEndDate && warrantyStartDate && (
|
|
||||||
<TouchableOpacity onPress={() => router.push("/(tabs)/my-battery")}>
|
<TouchableOpacity onPress={() => router.push("/(tabs)/my-battery")}>
|
||||||
<BatteryWarrantyCard
|
<BatteryWarrantyCard
|
||||||
warrantyStartDate={warrantyStartDate}
|
warrantyStartDate={warrantyStartDate}
|
||||||
warrantyEndDate={warrantyEndDate}
|
warrantyEndDate={warrantyEndDate}
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
<CustomerSupportModal
|
<CustomerSupportModal
|
||||||
visible={isSupportModalVisible}
|
visible={isSupportModalVisible}
|
||||||
|
|
@ -429,6 +454,22 @@ const mapStyle = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
logo: {
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
resizeMode: "contain",
|
||||||
|
},
|
||||||
|
logoContainer: {
|
||||||
|
padding: 8,
|
||||||
|
borderRadius: 44 / 2, // make it perfectly round
|
||||||
|
borderWidth: 2,
|
||||||
|
borderColor: "#E5E9F0",
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
overflow: "hidden", // ensures image doesn't overflow
|
||||||
|
},
|
||||||
errorText: {
|
errorText: {
|
||||||
color: "#565F70",
|
color: "#565F70",
|
||||||
fontWeight: "400",
|
fontWeight: "400",
|
||||||
|
|
@ -480,8 +521,9 @@ const styles = StyleSheet.create({
|
||||||
backgroundColor: "#F3F5F8",
|
backgroundColor: "#F3F5F8",
|
||||||
},
|
},
|
||||||
headerTitleContainer: {
|
headerTitleContainer: {
|
||||||
flexDirection: "column",
|
flexDirection: "row",
|
||||||
backgroundColor: "#F3F5F8",
|
backgroundColor: "#F3F5F8",
|
||||||
|
gap: 8,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ const BatteryDetails = () => {
|
||||||
const model = data?.batteries[0]?.battery_model ?? "---";
|
const model = data?.batteries[0]?.battery_model ?? "---";
|
||||||
const batteryId = data?.batteries[0]?.battery_id ?? "---";
|
const batteryId = data?.batteries[0]?.battery_id ?? "---";
|
||||||
const bmsId = data?.batteries[0]?.bms_id ?? "---";
|
const bmsId = data?.batteries[0]?.bms_id ?? "---";
|
||||||
const warrantyStartDate = data?.batteries[0]?.warranty_start_date ?? "---";
|
|
||||||
const warrantyEndDate = data?.batteries[0]?.warranty_end_date ?? "---";
|
|
||||||
const vimId = data?.batteries[0]?.vim_id ?? "---";
|
const vimId = data?.batteries[0]?.vim_id ?? "---";
|
||||||
const serialNumber = data?.batteries[0]?.serial_no ?? "---";
|
const serialNumber = data?.batteries[0]?.serial_no ?? "---";
|
||||||
const chargerUid = data?.batteries[0]?.charger_uid ?? "---";
|
const chargerUid = data?.batteries[0]?.charger_uid ?? "---";
|
||||||
|
|
@ -27,13 +25,7 @@ const BatteryDetails = () => {
|
||||||
let progress = 0;
|
let progress = 0;
|
||||||
let durationText = "---";
|
let durationText = "---";
|
||||||
|
|
||||||
if (
|
if (start && end && !isNaN(start.getTime()) && !isNaN(end.getTime())) {
|
||||||
start &&
|
|
||||||
end &&
|
|
||||||
!isNaN(start.getTime()) &&
|
|
||||||
!isNaN(end.getTime()) &&
|
|
||||||
now < end
|
|
||||||
) {
|
|
||||||
const totalDuration = end.getTime() - start.getTime();
|
const totalDuration = end.getTime() - start.getTime();
|
||||||
const elapsed = now.getTime() - start.getTime();
|
const elapsed = now.getTime() - start.getTime();
|
||||||
const remaining = Math.max(end.getTime() - now.getTime(), 0);
|
const remaining = Math.max(end.getTime() - now.getTime(), 0);
|
||||||
|
|
@ -64,6 +56,8 @@ const BatteryDetails = () => {
|
||||||
parts.push(`${daysLeft} ${t("home.day")}`);
|
parts.push(`${daysLeft} ${t("home.day")}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(parts, "parts");
|
||||||
|
|
||||||
return parts.join(", ");
|
return parts.join(", ");
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import { useSnackbar } from "@/contexts/Snackbar";
|
||||||
import CustomerCareIcon from "../../assets/icons/customer-care.svg";
|
import CustomerCareIcon from "../../assets/icons/customer-care.svg";
|
||||||
import api from "@/services/axiosClient";
|
import api from "@/services/axiosClient";
|
||||||
import PaymentHistoryCard from "@/components/Payments/PaymentHistoryCard";
|
import PaymentHistoryCard from "@/components/Payments/PaymentHistoryCard";
|
||||||
import { BASE_URL } from "@/constants/config";
|
import { BASE_URL, STORAGE_KEYS } from "@/constants/config";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import {
|
import {
|
||||||
setAdvanceBalance,
|
setAdvanceBalance,
|
||||||
|
|
@ -38,6 +38,8 @@ import { displayValue } from "@/utils/Common";
|
||||||
import RefreshIcon from "@/assets/icons/refresh.svg";
|
import RefreshIcon from "@/assets/icons/refresh.svg";
|
||||||
import CustomerSupport from "@/components/home/CustomerSupportModal";
|
import CustomerSupport from "@/components/home/CustomerSupportModal";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Image } from "expo-image";
|
||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
|
|
||||||
export interface MyPlan {
|
export interface MyPlan {
|
||||||
no_of_emi: number;
|
no_of_emi: number;
|
||||||
|
|
@ -148,6 +150,12 @@ export default function PaymentsTabScreen() {
|
||||||
|
|
||||||
const fetchEmiDetails = async () => {
|
const fetchEmiDetails = async () => {
|
||||||
try {
|
try {
|
||||||
|
const token = await AsyncStorage.getItem(STORAGE_KEYS.AUTH_TOKEN);
|
||||||
|
if (!token) {
|
||||||
|
console.log("No auth token found, skipping API call");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setEmiDetails(null);
|
setEmiDetails(null);
|
||||||
setAdvanceBalance(null);
|
setAdvanceBalance(null);
|
||||||
|
|
@ -166,13 +174,13 @@ export default function PaymentsTabScreen() {
|
||||||
} else {
|
} else {
|
||||||
showSnackbar("No EMI details found", "error");
|
showSnackbar("No EMI details found", "error");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (error: any) {
|
||||||
console.error("Error fetching EMI details:", err);
|
if (error.message === "Network Error" || !error.response) {
|
||||||
const errorMessage =
|
showSnackbar(t("common.no-internet-connection"), "error");
|
||||||
err instanceof Error
|
} else {
|
||||||
? err.message
|
showSnackbar(t("common.something-went-wrong"), "error");
|
||||||
: `${t("service.something-went-wrong")}`;
|
}
|
||||||
showSnackbar(errorMessage, "error");
|
console.error("Manual refresh failed", error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -180,13 +188,19 @@ export default function PaymentsTabScreen() {
|
||||||
|
|
||||||
const handleRefresh = async () => {
|
const handleRefresh = async () => {
|
||||||
try {
|
try {
|
||||||
|
console.log("payments refresh");
|
||||||
setShowFullHistory(false);
|
setShowFullHistory(false);
|
||||||
setRefreshing(true);
|
setRefreshing(true);
|
||||||
startSpin();
|
startSpin();
|
||||||
|
|
||||||
await Promise.all([fetchEmiDetails(), fetchPaymentHistory(1, false)]);
|
await Promise.all([fetchEmiDetails(), fetchPaymentHistory(1, false)]);
|
||||||
console.log("Manual refresh complete");
|
console.log("Manual refresh complete");
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
|
if (error.message === "Network Error" || !error.response) {
|
||||||
|
showSnackbar(t("common.no-internet-connection"), "error");
|
||||||
|
} else {
|
||||||
|
showSnackbar(t("common.something-went-wrong"), "error");
|
||||||
|
}
|
||||||
console.error("Manual refresh failed", error);
|
console.error("Manual refresh failed", error);
|
||||||
} finally {
|
} finally {
|
||||||
stopSpin();
|
stopSpin();
|
||||||
|
|
@ -211,6 +225,8 @@ export default function PaymentsTabScreen() {
|
||||||
spinValue.setValue(0);
|
spinValue.setValue(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { isLoggedIn } = useSelector((state: RootState) => state.auth);
|
||||||
|
|
||||||
const spin = spinValue.interpolate({
|
const spin = spinValue.interpolate({
|
||||||
inputRange: [0, 1],
|
inputRange: [0, 1],
|
||||||
outputRange: ["0deg", "360deg"],
|
outputRange: ["0deg", "360deg"],
|
||||||
|
|
@ -218,10 +234,12 @@ export default function PaymentsTabScreen() {
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
setShowFullHistory(false);
|
if (isLoggedIn) {
|
||||||
fetchEmiDetails();
|
setShowFullHistory(false);
|
||||||
fetchPaymentHistory(1, false);
|
fetchEmiDetails();
|
||||||
}, [])
|
fetchPaymentHistory(1, false);
|
||||||
|
}
|
||||||
|
}, [isLoggedIn])
|
||||||
);
|
);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
|
@ -231,8 +249,16 @@ export default function PaymentsTabScreen() {
|
||||||
},
|
},
|
||||||
headerTitle: () => (
|
headerTitle: () => (
|
||||||
<View style={styles.headerTitleContainer}>
|
<View style={styles.headerTitleContainer}>
|
||||||
<Text style={styles.title}>{model}</Text>
|
<View style={styles.logoContainer}>
|
||||||
<Text style={styles.subtitle}>{chasisNumber}</Text>
|
<Image
|
||||||
|
source={require("../../assets/images/lio_logo.png")}
|
||||||
|
style={styles.logo}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text style={styles.title}>{model}</Text>
|
||||||
|
<Text style={styles.subtitle}>{chasisNumber}</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
),
|
),
|
||||||
headerRight: () => (
|
headerRight: () => (
|
||||||
|
|
@ -270,7 +296,6 @@ export default function PaymentsTabScreen() {
|
||||||
|
|
||||||
// Format currency
|
// Format currency
|
||||||
const formatCurrency = (amount: number) => {
|
const formatCurrency = (amount: number) => {
|
||||||
console.log(amount, "amount format current");
|
|
||||||
return `₹${amount.toLocaleString()}`;
|
return `₹${amount.toLocaleString()}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -305,6 +330,12 @@ export default function PaymentsTabScreen() {
|
||||||
pageNumber: number = 1,
|
pageNumber: number = 1,
|
||||||
isLoadMore: boolean = false
|
isLoadMore: boolean = false
|
||||||
) => {
|
) => {
|
||||||
|
const token = await AsyncStorage.getItem(STORAGE_KEYS.AUTH_TOKEN);
|
||||||
|
if (!token) {
|
||||||
|
console.log("No auth token found, skipping API call");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isLoadMore) {
|
if (isLoadMore) {
|
||||||
setIsLoadingMore(true);
|
setIsLoadingMore(true);
|
||||||
|
|
@ -337,13 +368,13 @@ export default function PaymentsTabScreen() {
|
||||||
} else {
|
} else {
|
||||||
showSnackbar("No payment history found", "error");
|
showSnackbar("No payment history found", "error");
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (error: any) {
|
||||||
console.error("Error fetching payment history:", err);
|
if (error.message === "Network Error" || !error.response) {
|
||||||
const errorMessage =
|
showSnackbar(t("common.no-internet-connection"), "error");
|
||||||
err instanceof Error
|
} else {
|
||||||
? err.message
|
showSnackbar(t("common.something-went-wrong"), "error");
|
||||||
: `${t("service.something-went-wrong")}`;
|
}
|
||||||
showSnackbar(errorMessage, "error");
|
console.error("Manual refresh failed", error);
|
||||||
} finally {
|
} finally {
|
||||||
if (isLoadMore) {
|
if (isLoadMore) {
|
||||||
setIsLoadingMore(false);
|
setIsLoadingMore(false);
|
||||||
|
|
@ -590,6 +621,22 @@ const StatusBadge = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
logo: {
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
resizeMode: "contain",
|
||||||
|
},
|
||||||
|
logoContainer: {
|
||||||
|
padding: 8,
|
||||||
|
borderRadius: 44 / 2, // make it perfectly round
|
||||||
|
borderWidth: 2,
|
||||||
|
borderColor: "#E5E9F0",
|
||||||
|
width: 44,
|
||||||
|
height: 44,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
overflow: "hidden", // ensures image doesn't overflow
|
||||||
|
},
|
||||||
loadingContainer: {
|
loadingContainer: {
|
||||||
padding: 16,
|
padding: 16,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
|
@ -611,8 +658,9 @@ const styles = StyleSheet.create({
|
||||||
width: "80%",
|
width: "80%",
|
||||||
},
|
},
|
||||||
headerTitleContainer: {
|
headerTitleContainer: {
|
||||||
flexDirection: "column",
|
flexDirection: "row",
|
||||||
backgroundColor: "#F3F5F8",
|
backgroundColor: "#F3F5F8",
|
||||||
|
gap: 8,
|
||||||
},
|
},
|
||||||
subtitle: {
|
subtitle: {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,13 @@ import {
|
||||||
ScrollView,
|
ScrollView,
|
||||||
KeyboardAvoidingView,
|
KeyboardAvoidingView,
|
||||||
Platform,
|
Platform,
|
||||||
|
ActivityIndicator,
|
||||||
|
Keyboard,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { Dropdown } from "react-native-element-dropdown";
|
import { Dropdown } from "react-native-element-dropdown";
|
||||||
import { DateTimePickerAndroid } from "@react-native-community/datetimepicker";
|
import { DateTimePickerAndroid } from "@react-native-community/datetimepicker";
|
||||||
import * as ImagePicker from "expo-image-picker";
|
import * as ImagePicker from "expo-image-picker";
|
||||||
import { Formik, FormikHelpers } from "formik";
|
import { FastField, Formik, FormikHelpers } from "formik";
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import ChevronRight from "../../assets/icons/chevron_rightside.svg";
|
import ChevronRight from "../../assets/icons/chevron_rightside.svg";
|
||||||
import AddPhoto from "../../assets/icons/add_photo_alternate.svg";
|
import AddPhoto from "../../assets/icons/add_photo_alternate.svg";
|
||||||
|
|
@ -22,37 +24,107 @@ import IssueSelectorModal from "@/components/service/IssueSelectorModal";
|
||||||
import { uploadImage } from "@/utils/User";
|
import { uploadImage } from "@/utils/User";
|
||||||
import api from "@/services/axiosClient";
|
import api from "@/services/axiosClient";
|
||||||
import { useSnackbar } from "@/contexts/Snackbar";
|
import { useSnackbar } from "@/contexts/Snackbar";
|
||||||
import { BASE_URL } from "@/constants/config";
|
import { BASE_URL, SERVICE } from "@/constants/config";
|
||||||
import { Overlay } from "@/components/common/Overlay";
|
import { Overlay } from "@/components/common/Overlay";
|
||||||
import CrossIcon from "@/assets/icons/close_white.svg";
|
import CrossIcon from "@/assets/icons/close_white.svg";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import CalendarIcon from "@/assets/icons/calendar.svg";
|
import CalendarIcon from "@/assets/icons/calendar.svg";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "@/store";
|
||||||
|
|
||||||
interface FormValues {
|
interface FormValues {
|
||||||
serviceType: string | null;
|
serviceType: string | null;
|
||||||
issues: string[];
|
issues: number[];
|
||||||
comments: string | null;
|
comments: string | null;
|
||||||
date: Date | null;
|
date: Date | null;
|
||||||
photos: string[];
|
photos: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
interface Issue {
|
||||||
serviceType: Yup.string().required("Service Type is required"),
|
id: number;
|
||||||
issues: Yup.array().min(1, "At least one issue is required"),
|
name: {
|
||||||
date: Yup.date().required("Date and Time is required"),
|
en: String;
|
||||||
photos: Yup.array().min(1, "At least one photo is required"),
|
hi: String;
|
||||||
comments: Yup.string(),
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
export default function ServiceFormScreen(): JSX.Element {
|
export default function ServiceFormScreen(): JSX.Element {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const validationSchema = Yup.object().shape({
|
||||||
|
serviceType: Yup.string().required(t("service.service-type-is-required")),
|
||||||
|
issues: Yup.array().when("serviceType", {
|
||||||
|
is: (val: string) => val !== "Regular",
|
||||||
|
then: (schema) =>
|
||||||
|
schema.min(1, t("service.atleast-one-issue-is-required")),
|
||||||
|
otherwise: (schema) => schema.notRequired(),
|
||||||
|
}),
|
||||||
|
date: Yup.date().required(t("service.date-and-time-is-required")),
|
||||||
|
photos: Yup.array().min(1, t("service.atleast-one-photo-is-required")),
|
||||||
|
comments: Yup.string(),
|
||||||
|
});
|
||||||
|
|
||||||
const [isFocus, setIsFocus] = useState<boolean>(false);
|
const [isFocus, setIsFocus] = useState<boolean>(false);
|
||||||
const [isIssueSelectorVisible, setIssueSelectorVisible] = useState(false);
|
const [isIssueSelectorVisible, setIssueSelectorVisible] = useState(false);
|
||||||
|
|
||||||
|
const { data } = useSelector((state: RootState) => state.user);
|
||||||
|
|
||||||
|
const [issues, setIssues] = useState<Issue[]>([]);
|
||||||
|
const [isLoadingIssues, setIsLoadingIssues] = useState(false);
|
||||||
const { showSnackbar } = useSnackbar();
|
const { showSnackbar } = useSnackbar();
|
||||||
|
|
||||||
function toggleIssueSelector() {
|
function toggleIssueSelector() {
|
||||||
setIssueSelectorVisible(!isIssueSelectorVisible);
|
setIssueSelectorVisible(!isIssueSelectorVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkSixMonthsCondition(warrantyStartDate: string | undefined) {
|
||||||
|
if (warrantyStartDate) {
|
||||||
|
const startDate = new Date(warrantyStartDate);
|
||||||
|
|
||||||
|
const monthsLater = new Date(startDate);
|
||||||
|
monthsLater.setMonth(
|
||||||
|
monthsLater.getMonth() + SERVICE.ENABLE_REGULAR_SERVICE_AFTER_IN_MONTHS
|
||||||
|
);
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
|
||||||
|
if (today >= monthsLater) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const warrantyStartDay = data?.batteries[0]?.warranty_start_date;
|
||||||
|
const isAfterSixMonths = checkSixMonthsCondition(warrantyStartDay);
|
||||||
|
|
||||||
|
const dropdownData = [
|
||||||
|
{ label: "Regular", value: "Regular", disabled: !isAfterSixMonths },
|
||||||
|
{ label: "On-demand", value: "On-demand", disabled: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
const fetchIssues = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoadingIssues(true);
|
||||||
|
const response = await api.get(`${BASE_URL}/api/v1/service-issue-list`);
|
||||||
|
|
||||||
|
if (response.data.success) {
|
||||||
|
setIssues(response.data.data);
|
||||||
|
} else {
|
||||||
|
throw new Error(response.data?.message || "Failed to fetch issues");
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("Error fetching issues:", error);
|
||||||
|
showSnackbar(`${t("common.something-went-wrong")}`, "error");
|
||||||
|
setIssues([]);
|
||||||
|
} finally {
|
||||||
|
setIsLoadingIssues(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const initialValues: FormValues = {
|
const initialValues: FormValues = {
|
||||||
serviceType: null,
|
serviceType: null,
|
||||||
issues: [],
|
issues: [],
|
||||||
|
|
@ -79,13 +151,9 @@ export default function ServiceFormScreen(): JSX.Element {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const showPicker = (
|
const showPicker = (setFieldValue: (field: string, value: any) => void) => {
|
||||||
currentDate: Date | null,
|
const now = new Date();
|
||||||
setFieldValue: (field: string, value: any) => void
|
|
||||||
) => {
|
|
||||||
const now = currentDate || new Date();
|
|
||||||
|
|
||||||
// First, show the date picker
|
|
||||||
DateTimePickerAndroid.open({
|
DateTimePickerAndroid.open({
|
||||||
value: now,
|
value: now,
|
||||||
mode: "date",
|
mode: "date",
|
||||||
|
|
@ -115,6 +183,16 @@ export default function ServiceFormScreen(): JSX.Element {
|
||||||
showSnackbar(`${t("service.select-valid-time")}`, "error");
|
showSnackbar(`${t("service.select-valid-time")}`, "error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//(10AM - 5PM)
|
||||||
|
const hours = combinedDate.getHours();
|
||||||
|
if (hours < 10 || hours >= 17) {
|
||||||
|
showSnackbar(
|
||||||
|
t("service.time-must-be-between-10-and-5"),
|
||||||
|
"error"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
setFieldValue("date", combinedDate);
|
setFieldValue("date", combinedDate);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -124,7 +202,50 @@ export default function ServiceFormScreen(): JSX.Element {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const handleServiceTypeChange = async (
|
||||||
|
item: { label: string; value: string },
|
||||||
|
setFieldValue: (field: string, value: any) => void,
|
||||||
|
setFieldTouched: (field: string, touched: boolean) => void
|
||||||
|
) => {
|
||||||
|
if (item.value === "Regular") {
|
||||||
|
// Regular service can be selected immediately
|
||||||
|
setFieldValue("serviceType", item.value);
|
||||||
|
setFieldValue("issues", []);
|
||||||
|
setFieldTouched("issues", false);
|
||||||
|
setIssues([]);
|
||||||
|
setIsFocus(false);
|
||||||
|
} else if (item.value === "On-demand") {
|
||||||
|
try {
|
||||||
|
setFieldValue("serviceType", item.value);
|
||||||
|
setIsLoadingIssues(true);
|
||||||
|
const response = await api.get(`${BASE_URL}/api/v1/service-issue-list`);
|
||||||
|
|
||||||
|
if (response.data.success) {
|
||||||
|
setIssues(response.data.data);
|
||||||
|
setFieldValue("serviceType", item.value);
|
||||||
|
setFieldValue("issues", []);
|
||||||
|
setFieldTouched("issues", false);
|
||||||
|
setIsFocus(false);
|
||||||
|
} else {
|
||||||
|
setFieldValue("serviceType", null);
|
||||||
|
throw new Error(response.data?.message || "Failed to fetch issues");
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("Error fetching issues:", error);
|
||||||
|
showSnackbar(`${t("common.something-went-wrong")}`, "error");
|
||||||
|
setIssues([]);
|
||||||
|
setFieldValue("serviceType", null);
|
||||||
|
} finally {
|
||||||
|
setIsLoadingIssues(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSelectedIssuesText = (selectedIssueIds: number[]) => {
|
||||||
|
if (selectedIssueIds.length === 0) return `${t("service.select-issue")}`;
|
||||||
|
if (selectedIssueIds.length === 1) return "1 issue selected";
|
||||||
|
return `${selectedIssueIds.length} issues selected`;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
|
|
@ -145,6 +266,7 @@ export default function ServiceFormScreen(): JSX.Element {
|
||||||
values: FormValues,
|
values: FormValues,
|
||||||
actions: FormikHelpers<FormValues>
|
actions: FormikHelpers<FormValues>
|
||||||
) => {
|
) => {
|
||||||
|
Keyboard.dismiss();
|
||||||
try {
|
try {
|
||||||
const uploadedPhotoUrls: string[] = [];
|
const uploadedPhotoUrls: string[] = [];
|
||||||
for (const uri of values.photos) {
|
for (const uri of values.photos) {
|
||||||
|
|
@ -181,7 +303,7 @@ export default function ServiceFormScreen(): JSX.Element {
|
||||||
actions.resetForm();
|
actions.resetForm();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("Error during submission:", error);
|
console.error("Error during submission:", error);
|
||||||
showSnackbar(`${t("service.something-went-wrong")}`, "error");
|
showSnackbar(`${t("common.something-went-wrong")}`, "error");
|
||||||
} finally {
|
} finally {
|
||||||
actions.setSubmitting(false);
|
actions.setSubmitting(false);
|
||||||
}
|
}
|
||||||
|
|
@ -196,6 +318,7 @@ export default function ServiceFormScreen(): JSX.Element {
|
||||||
errors,
|
errors,
|
||||||
touched,
|
touched,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
|
setFieldTouched,
|
||||||
}) => (
|
}) => (
|
||||||
<View style={styles.formContainer}>
|
<View style={styles.formContainer}>
|
||||||
<View style={styles.inputContainer}>
|
<View style={styles.inputContainer}>
|
||||||
|
|
@ -212,10 +335,7 @@ export default function ServiceFormScreen(): JSX.Element {
|
||||||
selectedTextStyle={styles.selectedTextStyle}
|
selectedTextStyle={styles.selectedTextStyle}
|
||||||
inputSearchStyle={styles.inputSearchStyle}
|
inputSearchStyle={styles.inputSearchStyle}
|
||||||
iconStyle={styles.iconStyle}
|
iconStyle={styles.iconStyle}
|
||||||
data={[
|
data={dropdownData}
|
||||||
{ label: "Regular", value: "Regular" },
|
|
||||||
{ label: "On-demand", value: "On-demand" },
|
|
||||||
]}
|
|
||||||
maxHeight={200}
|
maxHeight={200}
|
||||||
labelField="label"
|
labelField="label"
|
||||||
valueField="value"
|
valueField="value"
|
||||||
|
|
@ -224,33 +344,67 @@ export default function ServiceFormScreen(): JSX.Element {
|
||||||
onFocus={() => setIsFocus(true)}
|
onFocus={() => setIsFocus(true)}
|
||||||
onBlur={() => setIsFocus(false)}
|
onBlur={() => setIsFocus(false)}
|
||||||
onChange={(item) => {
|
onChange={(item) => {
|
||||||
setFieldValue("serviceType", item.value);
|
if (item.disabled) {
|
||||||
setIsFocus(false);
|
showSnackbar(
|
||||||
|
t("service.regular-available-after-6-months"),
|
||||||
|
"error"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleServiceTypeChange(
|
||||||
|
item,
|
||||||
|
setFieldValue,
|
||||||
|
setFieldTouched
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
renderLeftIcon={() => (
|
renderItem={(item) => (
|
||||||
<View style={{ marginRight: 10 }}>
|
<View
|
||||||
{/* Add your icon component here if needed */}
|
style={{
|
||||||
|
padding: 12,
|
||||||
|
opacity: item.disabled ? 0.5 : 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={{ color: item.disabled ? "#949CAC" : "#252A34" }}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{touched.serviceType && errors.serviceType && (
|
{touched.serviceType && errors.serviceType && (
|
||||||
<Text style={styles.error}>{errors.serviceType}</Text>
|
<Text style={styles.error}>{errors.serviceType}</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<View style={{ marginTop: 8 }}>
|
<View style={{ marginTop: 8 }}>
|
||||||
<Text style={styles.label}>
|
<Text style={styles.label}>
|
||||||
{t("service.issue")} <Text style={styles.required}>*</Text>
|
{t("service.issue")}{" "}
|
||||||
|
{values.serviceType == "On-demand" && (
|
||||||
|
<Text style={styles.required}>*</Text>
|
||||||
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.inputBox}
|
style={[styles.inputBox]}
|
||||||
onPress={toggleIssueSelector}
|
onPress={toggleIssueSelector}
|
||||||
|
disabled={
|
||||||
|
values.serviceType === "Regular" ||
|
||||||
|
isLoadingIssues ||
|
||||||
|
values.serviceType == null
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Text style={styles.issueText}>
|
<Text
|
||||||
{values.issues.length > 0
|
style={[
|
||||||
? values.issues.length + " issues selected"
|
styles.issueText,
|
||||||
: `${t("service.select-issue")}`}
|
(values.serviceType === "Regular" ||
|
||||||
|
values.serviceType == null ||
|
||||||
|
isLoadingIssues) &&
|
||||||
|
styles.issueTextDisabled,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{getSelectedIssuesText(values.issues)}
|
||||||
</Text>
|
</Text>
|
||||||
<ChevronRight />
|
{isLoadingIssues ? <ActivityIndicator /> : <ChevronRight />}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
{touched.issues && errors.issues && (
|
{touched.issues && errors.issues && (
|
||||||
<Text style={styles.error}>{errors.issues}</Text>
|
<Text style={styles.error}>{errors.issues}</Text>
|
||||||
|
|
@ -263,7 +417,7 @@ export default function ServiceFormScreen(): JSX.Element {
|
||||||
<Text style={styles.required}>*</Text>
|
<Text style={styles.required}>*</Text>
|
||||||
</Text>
|
</Text>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => showPicker(values.date, setFieldValue)}
|
onPress={() => showPicker(setFieldValue)}
|
||||||
style={styles.inputBoxDate}
|
style={styles.inputBoxDate}
|
||||||
>
|
>
|
||||||
<Text style={styles.dateText}>
|
<Text style={styles.dateText}>
|
||||||
|
|
@ -341,6 +495,7 @@ export default function ServiceFormScreen(): JSX.Element {
|
||||||
setFieldValue("issues", selectedIssues);
|
setFieldValue("issues", selectedIssues);
|
||||||
}}
|
}}
|
||||||
initialSelectedValues={values.issues}
|
initialSelectedValues={values.issues}
|
||||||
|
issues={issues}
|
||||||
/>
|
/>
|
||||||
{isSubmitting && <Overlay isUploading={isSubmitting} />}
|
{isSubmitting && <Overlay isUploading={isSubmitting} />}
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -352,6 +507,9 @@ export default function ServiceFormScreen(): JSX.Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
issueTextDisabled: {
|
||||||
|
color: "#949CAC",
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ export default function WelcomeScreen() {
|
||||||
|
|
||||||
const phoneValidationSchema = Yup.object().shape({
|
const phoneValidationSchema = Yup.object().shape({
|
||||||
phone: Yup.string()
|
phone: Yup.string()
|
||||||
.required("Phone number is required")
|
.required(t("onboarding.phone-number-is-required"))
|
||||||
.matches(/^\d{10}$/, "Phone number must be exactly 10 digits"),
|
.matches(/^\d{10}$/, t("onboarding.must-be-10-digits")),
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (status === AUTH_STATUSES.SUCCESS && otpId) {
|
if (status === AUTH_STATUSES.SUCCESS && otpId) {
|
||||||
|
|
@ -109,24 +109,14 @@ export default function WelcomeScreen() {
|
||||||
<Text style={styles.error}>{sendOTPError}</Text>
|
<Text style={styles.error}>{sendOTPError}</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={styles.bottomSection}>
|
|
||||||
<View style={styles.contactContainer}>
|
|
||||||
<View style={{ flexDirection: "row" }}>
|
|
||||||
<Text>{t("onboarding.for-any-queries")}</Text>
|
|
||||||
<TouchableOpacity onPress={makePhoneCall}>
|
|
||||||
<Text style={styles.link}>
|
|
||||||
{t("onboarding.contact-us")}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
|
{/* ✅ Send OTP button placed just below input box */}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={handleSubmit as unknown as () => void}
|
onPress={handleSubmit as unknown as () => void}
|
||||||
style={{
|
style={{
|
||||||
...styles.button,
|
...styles.button,
|
||||||
|
marginTop: 16,
|
||||||
|
marginBottom: 16,
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
values.phone.length === 10 &&
|
values.phone.length === 10 &&
|
||||||
!errors.phone &&
|
!errors.phone &&
|
||||||
|
|
@ -144,6 +134,17 @@ export default function WelcomeScreen() {
|
||||||
{t("onboarding.send-otp")}
|
{t("onboarding.send-otp")}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<View style={styles.contactContainer}>
|
||||||
|
<View style={{ flexDirection: "row" }}>
|
||||||
|
<Text>{t("onboarding.for-any-queries")}</Text>
|
||||||
|
<TouchableOpacity onPress={makePhoneCall}>
|
||||||
|
<Text style={styles.link}>
|
||||||
|
{t("onboarding.contact-us")}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -106,8 +106,7 @@ const styles = StyleSheet.create({
|
||||||
color: "#2f465e",
|
color: "#2f465e",
|
||||||
},
|
},
|
||||||
selectButton: {
|
selectButton: {
|
||||||
marginTop: "auto",
|
marginTop: 16,
|
||||||
marginBottom: 16,
|
|
||||||
padding: 16,
|
padding: 16,
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import Pending from "@/assets/icons/pending.svg";
|
||||||
import Failed from "@/assets/icons/cancel.svg";
|
import Failed from "@/assets/icons/cancel.svg";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { payments } from "@/constants/config";
|
||||||
|
import { formatDate } from "@/utils/Payments";
|
||||||
|
|
||||||
const PaymentConfirmationScreen = () => {
|
const PaymentConfirmationScreen = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
@ -22,35 +24,11 @@ const PaymentConfirmationScreen = () => {
|
||||||
const paymentStatus = paymentOrder?.status || "confirmed";
|
const paymentStatus = paymentOrder?.status || "confirmed";
|
||||||
|
|
||||||
// Format amount with currency
|
// Format amount with currency
|
||||||
const formatAmount = (amount: number | null) => {
|
const formatAmount = (amount: number | null | undefined) => {
|
||||||
if (!amount) return "₹0";
|
if (!amount) return "₹0";
|
||||||
return `₹${amount.toLocaleString("en-IN")}`;
|
return `₹${amount.toLocaleString("en-IN")}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Format date
|
|
||||||
const formatDate = (dateString?: string) => {
|
|
||||||
if (!dateString)
|
|
||||||
return new Date().toLocaleString("en-IN", {
|
|
||||||
day: "numeric",
|
|
||||||
month: "long",
|
|
||||||
year: "numeric",
|
|
||||||
hour: "numeric",
|
|
||||||
minute: "2-digit",
|
|
||||||
hour12: true,
|
|
||||||
weekday: "long",
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Date(dateString).toLocaleString("en-IN", {
|
|
||||||
day: "numeric",
|
|
||||||
month: "long",
|
|
||||||
year: "numeric",
|
|
||||||
hour: "numeric",
|
|
||||||
minute: "2-digit",
|
|
||||||
hour12: true,
|
|
||||||
weekday: "long",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get icon and text based on status
|
// Get icon and text based on status
|
||||||
const getStatusDisplay = () => {
|
const getStatusDisplay = () => {
|
||||||
switch (paymentStatus) {
|
switch (paymentStatus) {
|
||||||
|
|
@ -95,15 +73,15 @@ const PaymentConfirmationScreen = () => {
|
||||||
<View style={styles.contentFrame}>
|
<View style={styles.contentFrame}>
|
||||||
<View style={styles.qrFrame}>
|
<View style={styles.qrFrame}>
|
||||||
<View style={styles.paymentStatusContainer}>
|
<View style={styles.paymentStatusContainer}>
|
||||||
|
<Text style={styles.statusText}>{statusDisplay.text}</Text>
|
||||||
<View style={statusDisplay.iconContainerStyle}>
|
<View style={statusDisplay.iconContainerStyle}>
|
||||||
{statusDisplay.icon}
|
{statusDisplay.icon}
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.amountText}>
|
<Text style={styles.amountText}>
|
||||||
{formatAmount(paymentOrder?.amount || due_amount)}
|
{formatAmount(paymentOrder?.amount)}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text>Payment to {payments.AMOUNT_PAID_TO}</Text>
|
||||||
<View style={styles.statusContainer}>
|
<View style={styles.statusContainer}>
|
||||||
<Text style={styles.statusText}>{statusDisplay.text}</Text>
|
|
||||||
<Text style={styles.dateText}>
|
<Text style={styles.dateText}>
|
||||||
{formatDate(paymentOrder?.transaction_date)}
|
{formatDate(paymentOrder?.transaction_date)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
@ -200,7 +178,6 @@ const styles = StyleSheet.create({
|
||||||
backgroundColor: "#fcfcfc",
|
backgroundColor: "#fcfcfc",
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
padding: 16,
|
padding: 16,
|
||||||
paddingVertical: 32,
|
|
||||||
},
|
},
|
||||||
paymentStatusContainer: {
|
paymentStatusContainer: {
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
|
@ -220,6 +197,13 @@ const styles = StyleSheet.create({
|
||||||
successIcon: {
|
successIcon: {
|
||||||
// Icon styling handled by Ionicons
|
// Icon styling handled by Ionicons
|
||||||
},
|
},
|
||||||
|
paidTo: {
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: "700",
|
||||||
|
color: "#253342",
|
||||||
|
textAlign: "center",
|
||||||
|
lineHeight: 20,
|
||||||
|
},
|
||||||
amountText: {
|
amountText: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: "600",
|
fontWeight: "600",
|
||||||
|
|
|
||||||
|
|
@ -9,16 +9,15 @@ import {
|
||||||
import { useNavigation, useRoute } from "@react-navigation/native";
|
import { useNavigation, useRoute } from "@react-navigation/native";
|
||||||
import { useRouter } from "expo-router";
|
import { useRouter } from "expo-router";
|
||||||
import api from "@/services/axiosClient";
|
import api from "@/services/axiosClient";
|
||||||
import { BASE_URL } from "@/constants/config";
|
import { BASE_URL, payments } from "@/constants/config";
|
||||||
import { useSnackbar } from "@/contexts/Snackbar";
|
import { useSnackbar } from "@/contexts/Snackbar";
|
||||||
import { SafeAreaView } from "react-native-safe-area-context";
|
|
||||||
|
|
||||||
// Import your success/failure icons
|
|
||||||
import SuccessIcon from "@/assets/icons/check_circle.svg";
|
import SuccessIcon from "@/assets/icons/check_circle.svg";
|
||||||
import FailureIcon from "@/assets/icons/cancel.svg";
|
import FailureIcon from "@/assets/icons/cancel.svg";
|
||||||
import PendingIcon from "@/assets/icons/pending.svg";
|
import PendingIcon from "@/assets/icons/pending.svg";
|
||||||
import Header from "@/components/common/Header";
|
import Header from "@/components/common/Header";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { formatDate } from "@/utils/Payments";
|
||||||
|
|
||||||
interface TransactionDetailData {
|
interface TransactionDetailData {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -83,7 +82,7 @@ export default function TransactionDetailScreen() {
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
err instanceof Error
|
err instanceof Error
|
||||||
? err.message
|
? err.message
|
||||||
: `${t("service.something-went-wrong")}`;
|
: `${t("common.something-went-wrong")}`;
|
||||||
showSnackbar(errorMessage, "error");
|
showSnackbar(errorMessage, "error");
|
||||||
router.back();
|
router.back();
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -152,18 +151,18 @@ export default function TransactionDetailScreen() {
|
||||||
) : (
|
) : (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<View style={styles.content}>
|
<View style={styles.content}>
|
||||||
|
<Text style={styles.statusText}>
|
||||||
|
{getStatusText(transactionData.status)}
|
||||||
|
</Text>
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
{getStatusIcon(transactionData.status)}
|
{getStatusIcon(transactionData.status)}
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.amount}>
|
<Text style={styles.amount}>
|
||||||
{formatCurrency(transactionData.amount)}
|
{formatCurrency(transactionData.amount)}
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text>Payment to {payments.AMOUNT_PAID_TO}</Text>
|
||||||
<Text style={styles.statusText}>
|
|
||||||
{getStatusText(transactionData.status)}
|
|
||||||
</Text>
|
|
||||||
<Text style={styles.dateTime}>
|
<Text style={styles.dateTime}>
|
||||||
{formatDateTime(transactionData.transaction_date)}
|
{formatDate(transactionData?.transaction_date)}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<View style={styles.divider} />
|
<View style={styles.divider} />
|
||||||
|
|
@ -305,7 +304,6 @@ const styles = StyleSheet.create({
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: "400",
|
fontWeight: "400",
|
||||||
color: "#252A34",
|
color: "#252A34",
|
||||||
marginBottom: 4,
|
|
||||||
},
|
},
|
||||||
dateTime: {
|
dateTime: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ import {
|
||||||
Text,
|
Text,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
Alert,
|
|
||||||
Share,
|
|
||||||
Linking,
|
Linking,
|
||||||
BackHandler,
|
BackHandler,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
|
|
@ -27,6 +25,7 @@ import { payments } from "@/constants/config";
|
||||||
import { useFocusEffect, useRouter } from "expo-router";
|
import { useFocusEffect, useRouter } from "expo-router";
|
||||||
import { useSocket } from "@/contexts/SocketContext";
|
import { useSocket } from "@/contexts/SocketContext";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import CancelPayment from "@/components/Payments/CancelPaymentModal";
|
||||||
|
|
||||||
const { height: screenHeight } = Dimensions.get("window");
|
const { height: screenHeight } = Dimensions.get("window");
|
||||||
|
|
||||||
|
|
@ -34,9 +33,6 @@ const UpiPaymentScreen = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
//paymentorder.amount is undefined
|
//paymentorder.amount is undefined
|
||||||
console.log("inside payemi ✅✅");
|
console.log("inside payemi ✅✅");
|
||||||
useEffect(() => {
|
|
||||||
console.log("inside pay emi useeffect 🔥🔥🔥🔥🔥");
|
|
||||||
}, []);
|
|
||||||
const paymentOrder = useSelector(
|
const paymentOrder = useSelector(
|
||||||
(state: RootState) => state.payments.paymentOrder
|
(state: RootState) => state.payments.paymentOrder
|
||||||
);
|
);
|
||||||
|
|
@ -46,36 +42,36 @@ const UpiPaymentScreen = () => {
|
||||||
const { showSnackbar } = useSnackbar();
|
const { showSnackbar } = useSnackbar();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
|
const [isCancelModalVisible, setIsCancelModalVisible] =
|
||||||
|
useState<boolean>(false);
|
||||||
|
|
||||||
|
function toggleCancelModal() {
|
||||||
|
setIsCancelModalVisible(!isCancelModalVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCancelPayment() {
|
||||||
|
offPaymentConfirmation();
|
||||||
|
disconnect();
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
React.useCallback(() => {
|
React.useCallback(() => {
|
||||||
let backPressCount = 0;
|
|
||||||
let backPressTimer: NodeJS.Timeout | null = null;
|
let backPressTimer: NodeJS.Timeout | null = null;
|
||||||
const backAction = () => {
|
const backAction = () => {
|
||||||
if (backPressCount === 0) {
|
setIsCancelModalVisible(true);
|
||||||
backPressCount++;
|
return true;
|
||||||
showSnackbar("Press back again to cancel payment", "info");
|
|
||||||
backPressTimer = setTimeout(() => {
|
|
||||||
backPressCount = 0;
|
|
||||||
}, 2000);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (backPressTimer) clearTimeout(backPressTimer);
|
|
||||||
offPaymentConfirmation();
|
|
||||||
disconnect();
|
|
||||||
router.back();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
const backHandler = BackHandler.addEventListener(
|
const backHandler = BackHandler.addEventListener(
|
||||||
"hardwareBackPress",
|
"hardwareBackPress",
|
||||||
backAction
|
backAction
|
||||||
);
|
);
|
||||||
// ✅ Cleanup when screen loses focus
|
|
||||||
return () => {
|
return () => {
|
||||||
if (backPressTimer) clearTimeout(backPressTimer);
|
if (backPressTimer) clearTimeout(backPressTimer);
|
||||||
backHandler.remove();
|
backHandler.remove();
|
||||||
};
|
};
|
||||||
}, [offPaymentConfirmation, disconnect, router])
|
}, [])
|
||||||
);
|
);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
|
|
@ -99,7 +95,7 @@ const UpiPaymentScreen = () => {
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
const formatAmount = (amount: number): string => {
|
const formatAmount = (amount: number | null | undefined): string => {
|
||||||
if (amount == null || amount == undefined) return `₹${0}`;
|
if (amount == null || amount == undefined) return `₹${0}`;
|
||||||
return `₹${amount.toLocaleString("en-IN")}`;
|
return `₹${amount.toLocaleString("en-IN")}`;
|
||||||
};
|
};
|
||||||
|
|
@ -143,18 +139,28 @@ const UpiPaymentScreen = () => {
|
||||||
|
|
||||||
const shareQR = async (): Promise<void> => {
|
const shareQR = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
|
const fileUri =
|
||||||
|
FileSystem.documentDirectory + `qr-${paymentOrder?.order_id}.png`;
|
||||||
|
|
||||||
|
// Download QR code image to local file
|
||||||
|
const downloadResult = await FileSystem.downloadAsync(
|
||||||
|
paymentOrder?.qr_code_url,
|
||||||
|
fileUri
|
||||||
|
);
|
||||||
|
|
||||||
|
// Share using expo-sharing
|
||||||
if (await Sharing.isAvailableAsync()) {
|
if (await Sharing.isAvailableAsync()) {
|
||||||
await Share.share({
|
await Sharing.shareAsync(downloadResult.uri, {
|
||||||
message: `Pay ${formatAmount(
|
mimeType: "image/png",
|
||||||
paymentOrder.amount
|
dialogTitle: "Share QR Code",
|
||||||
)} using this QR code`,
|
UTI: "public.png",
|
||||||
url: paymentOrder.qr_code_url,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Alert.alert("Error", "Sharing is not available on this device");
|
showSnackbar("Sharing is not available on this device.", "error");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Alert.alert("Error", "Failed to share QR code");
|
console.error("Error sharing QR code:", error);
|
||||||
|
showSnackbar("Failed to share QR code", "error");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -184,80 +190,90 @@ const UpiPaymentScreen = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
function handlePaymentDone() {
|
function handlePaymentDone() {
|
||||||
router.navigate("/(tabs)/payments");
|
offPaymentConfirmation();
|
||||||
|
disconnect();
|
||||||
|
router.replace("/(tabs)/payments");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<>
|
||||||
<Header title={t("payment.pay-emi")} showBackButton={false} />
|
<View style={styles.container}>
|
||||||
<ScrollView
|
<Header title={t("payment.pay-emi")} showBackButton={false} />
|
||||||
style={styles.scrollView}
|
<ScrollView
|
||||||
contentContainerStyle={styles.scrollContent}
|
style={styles.scrollView}
|
||||||
showsVerticalScrollIndicator={false}
|
contentContainerStyle={styles.scrollContent}
|
||||||
>
|
showsVerticalScrollIndicator={false}
|
||||||
<View style={styles.content}>
|
>
|
||||||
<View style={styles.qrFrame}>
|
<View style={styles.content}>
|
||||||
<View style={styles.amountSection}>
|
<View style={styles.qrFrame}>
|
||||||
<Text style={styles.amountLabel}>
|
<View style={styles.amountSection}>
|
||||||
{t("payment.amount-to-be-paid")}
|
<Text style={styles.amountLabel}>
|
||||||
</Text>
|
{t("payment.amount-to-be-paid")}
|
||||||
<Text style={styles.amount}>
|
|
||||||
{formatAmount(paymentOrder?.amount)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<View style={styles.qrCodeContainer}>
|
|
||||||
<Image
|
|
||||||
source={{ uri: paymentOrder?.qr_code_url }}
|
|
||||||
style={styles.qrCode}
|
|
||||||
contentFit="contain"
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.buttonsContainer}>
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={shareQR}
|
|
||||||
style={styles.secondaryButton}
|
|
||||||
>
|
|
||||||
<ShareIcon />
|
|
||||||
<Text style={styles.secondaryButtonText}>
|
|
||||||
{t("payment.share-qr")}
|
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
<Text style={styles.paidTo}>{payments.AMOUNT_PAID_TO}</Text>
|
||||||
|
<Text style={styles.amount}>
|
||||||
|
{formatAmount(paymentOrder?.amount)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.qrCodeContainer}>
|
||||||
|
<Image
|
||||||
|
source={{ uri: paymentOrder?.qr_code_url }}
|
||||||
|
style={styles.qrCode}
|
||||||
|
contentFit="contain"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={styles.buttonsContainer}>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={shareQR}
|
||||||
|
style={styles.secondaryButton}
|
||||||
|
>
|
||||||
|
<ShareIcon />
|
||||||
|
<Text style={styles.secondaryButtonText}>
|
||||||
|
{t("payment.share-qr")}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={downloadQR}
|
||||||
|
style={styles.secondaryButton}
|
||||||
|
>
|
||||||
|
<DownloadIcon />
|
||||||
|
<Text style={styles.secondaryButtonText}>
|
||||||
|
{t("payment.download-qr")}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={downloadQR}
|
onPress={payUsingUpiApp}
|
||||||
style={styles.secondaryButton}
|
style={[styles.primaryButton]}
|
||||||
>
|
>
|
||||||
<DownloadIcon />
|
<Text style={[styles.primaryButtonText]}>
|
||||||
<Text style={styles.secondaryButtonText}>
|
{t("payment.pay-using-upi")}
|
||||||
{t("payment.download-qr")}
|
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity
|
|
||||||
onPress={payUsingUpiApp}
|
|
||||||
style={[styles.primaryButton]}
|
|
||||||
>
|
|
||||||
<Text style={[styles.primaryButtonText]}>
|
|
||||||
{t("payment.pay-using-upi")}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View style={[styles.confirm, { marginBottom: insets.bottom }]}>
|
<View style={[styles.confirm, { marginBottom: insets.bottom }]}>
|
||||||
<Text style={styles.confirmTitle}>
|
<Text style={styles.confirmTitle}>
|
||||||
{t("payment.confirm-payment")}
|
{t("payment.confirm-payment")}
|
||||||
</Text>
|
</Text>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() => handlePaymentDone()}
|
onPress={() => handlePaymentDone()}
|
||||||
style={styles.paymentDone}
|
style={styles.paymentDone}
|
||||||
>
|
>
|
||||||
<Text style={styles.doneText}>{t("payment.payment-done")}</Text>
|
<Text style={styles.doneText}>{t("payment.payment-done")}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</ScrollView>
|
||||||
</ScrollView>
|
</View>
|
||||||
</View>
|
<CancelPayment
|
||||||
|
visible={isCancelModalVisible}
|
||||||
|
onClose={toggleCancelModal}
|
||||||
|
onCancel={handleCancelPayment}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -305,6 +321,7 @@ const styles = StyleSheet.create({
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
},
|
},
|
||||||
confirmTitle: {
|
confirmTitle: {
|
||||||
|
padding: 2,
|
||||||
fontWeight: "400",
|
fontWeight: "400",
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
lineHeight: 14,
|
lineHeight: 14,
|
||||||
|
|
@ -346,6 +363,13 @@ const styles = StyleSheet.create({
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
},
|
},
|
||||||
|
paidTo: {
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: "700",
|
||||||
|
color: "#253342",
|
||||||
|
textAlign: "center",
|
||||||
|
lineHeight: 20,
|
||||||
|
},
|
||||||
amount: {
|
amount: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: "600",
|
fontWeight: "600",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import {
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
TextInput,
|
TextInput,
|
||||||
ScrollView,
|
ScrollView,
|
||||||
KeyboardAvoidingView,
|
|
||||||
Keyboard,
|
Keyboard,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
@ -23,31 +22,32 @@ import { useSocket } from "@/contexts/SocketContext";
|
||||||
import { useSnackbar } from "@/contexts/Snackbar";
|
import { useSnackbar } from "@/contexts/Snackbar";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { displayValue, formatCurrency } from "@/utils/Common";
|
||||||
const validationSchema = Yup.object().shape({
|
|
||||||
paymentType: Yup.string().required("Please select a payment option"),
|
|
||||||
customAmount: Yup.string().when("paymentType", {
|
|
||||||
is: "custom",
|
|
||||||
then: (schema) =>
|
|
||||||
schema
|
|
||||||
.required("Amount is required")
|
|
||||||
.test("valid-number", "Please enter a valid amount", (value) => {
|
|
||||||
const numValue = parseFloat(value);
|
|
||||||
return !isNaN(numValue) && numValue > 0;
|
|
||||||
})
|
|
||||||
.test(
|
|
||||||
"min-amount",
|
|
||||||
`Minimum amount is ₹${payments.MIN_AMOUNT}`,
|
|
||||||
(value) => {
|
|
||||||
const numValue = parseFloat(value);
|
|
||||||
return !isNaN(numValue) && numValue >= payments.MIN_AMOUNT;
|
|
||||||
}
|
|
||||||
),
|
|
||||||
otherwise: (schema) => schema.notRequired(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const SelectAmountScreen = () => {
|
const SelectAmountScreen = () => {
|
||||||
|
const validationSchema = Yup.object().shape({
|
||||||
|
paymentType: Yup.string().required("Please select a payment option"),
|
||||||
|
customAmount: Yup.string().when("paymentType", {
|
||||||
|
is: "custom",
|
||||||
|
then: (schema) =>
|
||||||
|
schema
|
||||||
|
.required(t("payment.amount-is-required"))
|
||||||
|
.test("valid-number", "Please enter a valid amount", (value) => {
|
||||||
|
const numValue = parseFloat(value);
|
||||||
|
return !isNaN(numValue) && numValue > 0;
|
||||||
|
})
|
||||||
|
.test(
|
||||||
|
"min-amount",
|
||||||
|
`${t("payment.minimum")}: ₹${payments.MIN_AMOUNT}`,
|
||||||
|
(value) => {
|
||||||
|
const numValue = parseFloat(value);
|
||||||
|
return !isNaN(numValue) && numValue >= payments.MIN_AMOUNT;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
otherwise: (schema) => schema.notRequired(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
const dueAmount = useSelector(
|
const dueAmount = useSelector(
|
||||||
(state: RootState) => state.payments?.due_amount || 0
|
(state: RootState) => state.payments?.due_amount || 0
|
||||||
);
|
);
|
||||||
|
|
@ -148,7 +148,7 @@ const SelectAmountScreen = () => {
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err, "Error in creating order.");
|
console.error(err, "Error in creating order.");
|
||||||
showSnackbar(`${t("service.something-went-wrong")}`, "error");
|
showSnackbar(`${t("common.something-went-wrong")}`, "error");
|
||||||
} finally {
|
} finally {
|
||||||
setIsFetching(false);
|
setIsFetching(false);
|
||||||
}
|
}
|
||||||
|
|
@ -169,34 +169,46 @@ const SelectAmountScreen = () => {
|
||||||
values,
|
values,
|
||||||
errors,
|
errors,
|
||||||
touched,
|
touched,
|
||||||
handleChange,
|
|
||||||
handleBlur,
|
handleBlur,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
setFieldValue,
|
setFieldValue,
|
||||||
isValid,
|
|
||||||
validateField,
|
|
||||||
dirty,
|
|
||||||
}) => {
|
}) => {
|
||||||
const handleQuickAmountPress = (amount: number) => {
|
const handleQuickAmountPress = (amount: number) => {
|
||||||
const currentAmount = values.customAmount
|
let newAmount = values.customAmount
|
||||||
? parseFloat(values.customAmount)
|
? parseFloat(values.customAmount) + amount
|
||||||
: 0;
|
: amount;
|
||||||
const newAmount = currentAmount + amount;
|
|
||||||
|
if (newAmount > payments.MAX_AMOUNT) {
|
||||||
|
newAmount = payments.MAX_AMOUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFieldValue("paymentType", "custom");
|
||||||
setFieldValue("customAmount", newAmount.toString());
|
setFieldValue("customAmount", newAmount.toString());
|
||||||
validateField("customAmount");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCustomAmountChange = (text: string) => {
|
const handleCustomAmountChange = (text: string) => {
|
||||||
const numericText = text.replace(/[^0-9.]/g, "");
|
let numericText = text.replace(/[^0-9.]/g, "");
|
||||||
const parts = numericText.split(".");
|
const parts = numericText.split(".");
|
||||||
const formattedText =
|
|
||||||
parts.length > 2
|
|
||||||
? parts[0] + "." + parts.slice(1).join("")
|
|
||||||
: numericText;
|
|
||||||
|
|
||||||
setFieldValue("customAmount", formattedText, true);
|
// Only allow one decimal
|
||||||
|
if (parts.length > 2) {
|
||||||
|
numericText = parts[0] + "." + parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only allow two digits after decimal
|
||||||
|
if (parts[1]?.length > 2) {
|
||||||
|
numericText = parts[0] + "." + parts[1].slice(0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const numValue = parseFloat(numericText);
|
||||||
|
|
||||||
|
// If over max, don't update field value — just return early
|
||||||
|
if (!isNaN(numValue) && numValue > payments.MAX_AMOUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFieldValue("customAmount", numericText, true);
|
||||||
setFieldValue("paymentType", "custom");
|
setFieldValue("paymentType", "custom");
|
||||||
validateField("customAmount"); // Trigger validation after change
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPaymentAmount = () => {
|
const getPaymentAmount = () => {
|
||||||
|
|
@ -214,34 +226,15 @@ const SelectAmountScreen = () => {
|
||||||
!isNaN(paymentAmount) &&
|
!isNaN(paymentAmount) &&
|
||||||
paymentAmount >= payments.MIN_AMOUNT);
|
paymentAmount >= payments.MIN_AMOUNT);
|
||||||
|
|
||||||
// Button text logic
|
|
||||||
let buttonText = "";
|
|
||||||
if (values.paymentType === "due") {
|
|
||||||
buttonText = `Pay ₹${paymentAmount.toFixed(2)}`;
|
|
||||||
} else {
|
|
||||||
buttonText =
|
|
||||||
paymentAmount >= payments.MIN_AMOUNT
|
|
||||||
? `Pay ₹${paymentAmount.toFixed(2)}`
|
|
||||||
: "Select Amount";
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KeyboardAvoidingView
|
<View style={styles.container}>
|
||||||
style={styles.container}
|
|
||||||
behavior={"height"}
|
|
||||||
// Improved keyboard offset
|
|
||||||
// keyboardVerticalOffset={Platform.OS === "ios" ? 88 : 0}
|
|
||||||
>
|
|
||||||
<Header
|
<Header
|
||||||
title={t("payment.select-amount")}
|
title={t("payment.select-amount")}
|
||||||
showBackButton={true}
|
showBackButton={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
style={styles.content}
|
style={styles.content}
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
// Add keyboard dismiss on scroll
|
|
||||||
keyboardShouldPersistTaps="handled"
|
|
||||||
contentContainerStyle={styles.scrollContent}
|
contentContainerStyle={styles.scrollContent}
|
||||||
>
|
>
|
||||||
<View style={styles.selectAmountContainer}>
|
<View style={styles.selectAmountContainer}>
|
||||||
|
|
@ -269,7 +262,7 @@ const SelectAmountScreen = () => {
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.amountText}>
|
<Text style={styles.amountText}>
|
||||||
₹{dueAmount?.toFixed(2)}
|
{displayValue(dueAmount, formatCurrency)}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
|
@ -317,7 +310,6 @@ const SelectAmountScreen = () => {
|
||||||
placeholderTextColor="#94A3B8"
|
placeholderTextColor="#94A3B8"
|
||||||
keyboardType="numeric"
|
keyboardType="numeric"
|
||||||
onFocus={() => setFieldValue("paymentType", "custom")}
|
onFocus={() => setFieldValue("paymentType", "custom")}
|
||||||
// Add return key handling
|
|
||||||
returnKeyType="done"
|
returnKeyType="done"
|
||||||
/>
|
/>
|
||||||
<View style={styles.helperContainer}>
|
<View style={styles.helperContainer}>
|
||||||
|
|
@ -358,34 +350,29 @@ const SelectAmountScreen = () => {
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
<View style={[styles.buttonContainer]}>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[
|
||||||
|
styles.payButton,
|
||||||
|
!isButtonEnabled && styles.disabledButton,
|
||||||
|
]}
|
||||||
|
onPress={() => handleSubmit()}
|
||||||
|
disabled={!isButtonEnabled}
|
||||||
|
>
|
||||||
|
{getPaymentAmount() < payments.MIN_AMOUNT ? (
|
||||||
|
<Text style={styles.payButtonText}>
|
||||||
|
{t("payment.select-amount")}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<Text style={styles.payButtonText}>
|
||||||
|
{`${t("payment.pay")}`}{" "}
|
||||||
|
{displayValue(getPaymentAmount(), formatCurrency)}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
</View>
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
styles.buttonContainer,
|
|
||||||
!keyboardVisible && { marginBottom: insets.bottom },
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={[
|
|
||||||
styles.payButton,
|
|
||||||
!isButtonEnabled && styles.disabledButton,
|
|
||||||
]}
|
|
||||||
onPress={() => handleSubmit()}
|
|
||||||
disabled={!isButtonEnabled}
|
|
||||||
>
|
|
||||||
{getPaymentAmount() < payments.MIN_AMOUNT ? (
|
|
||||||
<Text style={styles.payButtonText}>
|
|
||||||
{t("payment.select-amount")}
|
|
||||||
</Text>
|
|
||||||
) : (
|
|
||||||
<Text style={styles.payButtonText}>
|
|
||||||
Pay ₹{getPaymentAmount().toFixed(2)}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</KeyboardAvoidingView>
|
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ export default function EditName() {
|
||||||
showSnackbar(`${t("profile.name-changed")}`, "success");
|
showSnackbar(`${t("profile.name-changed")}`, "success");
|
||||||
router.back();
|
router.back();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showSnackbar(`${t("service.something-went-wrong")}`, "error");
|
showSnackbar(`${t("common.something-went-wrong")}`, "error");
|
||||||
console.error("Error updating name:", error);
|
console.error("Error updating name:", error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|
@ -148,7 +148,6 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
formContainer: {
|
formContainer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
justifyContent: "space-between",
|
|
||||||
},
|
},
|
||||||
inputContainer: {
|
inputContainer: {
|
||||||
marginBottom: 24,
|
marginBottom: 24,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import { setUserData } from "@/store/userSlice";
|
||||||
import { Overlay } from "@/components/common/Overlay";
|
import { Overlay } from "@/components/common/Overlay";
|
||||||
import Header from "@/components/common/Header";
|
import Header from "@/components/common/Header";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { clearStackAndRouteTo } from "@/utils/Common";
|
||||||
|
|
||||||
export default function ProfileScreen() {
|
export default function ProfileScreen() {
|
||||||
const [isLangaugeModalVisible, setLanguageModalVisible] =
|
const [isLangaugeModalVisible, setLanguageModalVisible] =
|
||||||
|
|
@ -43,13 +44,13 @@ export default function ProfileScreen() {
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
dispatch(logout());
|
dispatch(logout());
|
||||||
router.replace("/auth/login");
|
clearStackAndRouteTo("/auth/login");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePickImage = async () => {
|
const handlePickImage = async () => {
|
||||||
let result = await ImagePicker.launchImageLibraryAsync({
|
let result = await ImagePicker.launchImageLibraryAsync({
|
||||||
mediaTypes: ImagePicker.MediaTypeOptions.Images,
|
mediaTypes: ImagePicker.MediaTypeOptions.Images,
|
||||||
quality: 1,
|
quality: 0.5,
|
||||||
allowsEditing: true,
|
allowsEditing: true,
|
||||||
aspect: [1, 1],
|
aspect: [1, 1],
|
||||||
});
|
});
|
||||||
|
|
@ -147,8 +148,8 @@ export default function ProfileScreen() {
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
{menuItem(`${t("profile.about-app")}`)}
|
{/* {menuItem(`${t("profile.about-app")}`)}
|
||||||
<View style={styles.divider} />
|
<View style={styles.divider} /> */}
|
||||||
{menuItem(`${t("profile.logout")}`, handleLogout)}
|
{menuItem(`${t("profile.logout")}`, handleLogout)}
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -0,0 +1,91 @@
|
||||||
|
import BottomSheetModal from "@/components/common/BottomSheetModal";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
||||||
|
|
||||||
|
interface CancelPaymentProps {
|
||||||
|
visible: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CancelPayment({
|
||||||
|
visible,
|
||||||
|
onClose,
|
||||||
|
onCancel,
|
||||||
|
}: CancelPaymentProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<BottomSheetModal
|
||||||
|
visible={visible}
|
||||||
|
onClose={onClose}
|
||||||
|
heading={t("payment.cancel-payment")}
|
||||||
|
>
|
||||||
|
<View style={styles.row}>
|
||||||
|
<Text>{t("payment.do-you-want-to-cancel-payment")}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.buttonsContainer}>
|
||||||
|
<TouchableOpacity onPress={onCancel} style={styles.button}>
|
||||||
|
<Text style={styles.bottomButtonText}>
|
||||||
|
{t("payment.cancel-payment")}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity onPress={onClose} style={styles.button}>
|
||||||
|
<Text style={styles.bottomButtonText}>
|
||||||
|
{t("payment.continue-payment")}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</BottomSheetModal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
buttonsContainer: {
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
paddingVertical: 8,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderColor: "#D8DDE7",
|
||||||
|
},
|
||||||
|
bottomButtonText: {
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
width: "45%",
|
||||||
|
height: 44,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 8,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "#E5E9F0",
|
||||||
|
borderRadius: 4,
|
||||||
|
backgroundColor: "#F3F5F8",
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
flexDirection: "row",
|
||||||
|
gap: 16,
|
||||||
|
justifyContent: "space-between",
|
||||||
|
},
|
||||||
|
secondaryButton: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
paddingVertical: 8,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
height: 40,
|
||||||
|
borderRadius: 4,
|
||||||
|
backgroundColor: "#F3F5F8",
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "#D8DDE7",
|
||||||
|
width: 156,
|
||||||
|
gap: 8,
|
||||||
|
},
|
||||||
|
fullButton: {
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: "500",
|
||||||
|
color: "#252A34",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -18,8 +18,6 @@ export default ({
|
||||||
method,
|
method,
|
||||||
status,
|
status,
|
||||||
}: PaymentHistoryCardProps) => {
|
}: PaymentHistoryCardProps) => {
|
||||||
//failure, confirmed, pending
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const handlePress = () => {
|
const handlePress = () => {
|
||||||
|
|
@ -58,7 +56,6 @@ export default ({
|
||||||
const statusStyle = getStatusStyle(status);
|
const statusStyle = getStatusStyle(status);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// <TouchableOpacity style={styles.paymentCard} onPress={handlePress}>
|
|
||||||
<TouchableOpacity style={styles.paymentCard} onPress={handlePress}>
|
<TouchableOpacity style={styles.paymentCard} onPress={handlePress}>
|
||||||
<View style={styles.paymentCardTop}>
|
<View style={styles.paymentCardTop}>
|
||||||
<Text style={styles.paymentDate}>{date}</Text>
|
<Text style={styles.paymentDate}>{date}</Text>
|
||||||
|
|
@ -82,7 +79,6 @@ export default ({
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
// </TouchableOpacity>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -124,7 +120,6 @@ const styles = StyleSheet.create({
|
||||||
textAlign: "left",
|
textAlign: "left",
|
||||||
},
|
},
|
||||||
paymentAmount: {
|
paymentAmount: {
|
||||||
width: 69,
|
|
||||||
height: 20,
|
height: 20,
|
||||||
fontFamily: "Inter-SemiBold",
|
fontFamily: "Inter-SemiBold",
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
|
@ -140,7 +135,6 @@ const styles = StyleSheet.create({
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
paymentDetails: {
|
paymentDetails: {
|
||||||
width: 237,
|
|
||||||
height: 16,
|
height: 16,
|
||||||
fontFamily: "Inter-Regular",
|
fontFamily: "Inter-Regular",
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
|
|
@ -156,7 +150,6 @@ const styles = StyleSheet.create({
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
paddingHorizontal: 8,
|
paddingHorizontal: 8,
|
||||||
paddingVertical: 2,
|
paddingVertical: 2,
|
||||||
marginLeft: 4, // simulate gap
|
|
||||||
},
|
},
|
||||||
statusText: {
|
statusText: {
|
||||||
fontFamily: "Inter-Medium",
|
fontFamily: "Inter-Medium",
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ const styles = StyleSheet.create({
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: 8,
|
gap: 8,
|
||||||
|
paddingHorizontal: 8,
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
fontStyle: "normal",
|
fontStyle: "normal",
|
||||||
|
|
@ -80,6 +81,7 @@ const styles = StyleSheet.create({
|
||||||
color: "#242C3B",
|
color: "#242C3B",
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: "#B6ECDD",
|
borderColor: "#B6ECDD",
|
||||||
|
backgroundColor: "green",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Text,
|
Text,
|
||||||
|
|
@ -11,12 +11,23 @@ import {
|
||||||
import Checkbox from "expo-checkbox";
|
import Checkbox from "expo-checkbox";
|
||||||
import { issueConfig } from "@/constants/config";
|
import { issueConfig } from "@/constants/config";
|
||||||
import CloseIcon from "@/assets/icons/close.svg";
|
import CloseIcon from "@/assets/icons/close.svg";
|
||||||
|
import { getLanguage } from "@/services/i18n";
|
||||||
|
import { useFocusEffect } from "expo-router";
|
||||||
|
|
||||||
|
interface Issue {
|
||||||
|
id: number;
|
||||||
|
name: {
|
||||||
|
en: String;
|
||||||
|
hi: String;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface IssueSelectorModalProps {
|
interface IssueSelectorModalProps {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSelect: (selectedValues: string[]) => void;
|
onSelect: (selectedValues: number[]) => void;
|
||||||
initialSelectedValues?: string[]; // Previously saved selections
|
initialSelectedValues?: number[]; // Previously saved selections
|
||||||
|
issues: Issue[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function IssueSelectorModal({
|
export default function IssueSelectorModal({
|
||||||
|
|
@ -24,26 +35,24 @@ export default function IssueSelectorModal({
|
||||||
onClose,
|
onClose,
|
||||||
onSelect,
|
onSelect,
|
||||||
initialSelectedValues = [],
|
initialSelectedValues = [],
|
||||||
|
issues = [],
|
||||||
}: IssueSelectorModalProps) {
|
}: IssueSelectorModalProps) {
|
||||||
const [selectedValues, setSelectedValues] = useState<string[]>(
|
const [selectedValues, setSelectedValues] = useState<number[]>(
|
||||||
initialSelectedValues
|
initialSelectedValues
|
||||||
);
|
);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
const toggleValue = (value: string) => {
|
const [lang, setLang] = useState<"en" | "hi">("en");
|
||||||
|
|
||||||
|
const toggleValue = (id: number) => {
|
||||||
setSelectedValues((prev) =>
|
setSelectedValues((prev) =>
|
||||||
prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value]
|
prev.includes(id) ? prev.filter((v) => v !== id) : [...prev, id]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredConfig = issueConfig
|
const filteredIssues = issues.filter((issue) =>
|
||||||
.map((group) => ({
|
issue.name[lang].toLowerCase().includes(search.toLowerCase().trim())
|
||||||
...group,
|
);
|
||||||
options: group.options.filter((option) =>
|
|
||||||
option.label.toLowerCase().includes(search.toLowerCase())
|
|
||||||
),
|
|
||||||
}))
|
|
||||||
.filter((group) => group.options.length > 0);
|
|
||||||
|
|
||||||
const clearSelection = () => setSelectedValues([]);
|
const clearSelection = () => setSelectedValues([]);
|
||||||
|
|
||||||
|
|
@ -71,6 +80,17 @@ export default function IssueSelectorModal({
|
||||||
}
|
}
|
||||||
}, [visible, initialSelectedValues]);
|
}, [visible, initialSelectedValues]);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
const fetchLang = async () => {
|
||||||
|
const selectedLang = await getLanguage();
|
||||||
|
setLang((selectedLang as "en") || "hi");
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchLang();
|
||||||
|
}, []) // dependencies, can include anything that triggers refetch
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal visible={visible} animationType="slide">
|
<Modal visible={visible} animationType="slide">
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
|
|
@ -95,13 +115,13 @@ export default function IssueSelectorModal({
|
||||||
<View style={styles.counterBar}>
|
<View style={styles.counterBar}>
|
||||||
<Text
|
<Text
|
||||||
style={styles.counterText}
|
style={styles.counterText}
|
||||||
>{`${selectedValues.length}/23 Selected`}</Text>
|
>{`${selectedValues.length}/${issues.length} Selected`}</Text>
|
||||||
<TouchableOpacity onPress={clearSelection} disabled={!hasSelection}>
|
<TouchableOpacity onPress={clearSelection} disabled={!hasSelection}>
|
||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.clearText,
|
styles.clearText,
|
||||||
!hasSelection && styles.clearTextDisabled,
|
!hasSelection && styles.clearTextDisabled,
|
||||||
selectedValues.length > 0 && { color: "#006C4D" }, // 👈 Add this line
|
selectedValues.length > 0 && { color: "#006C4D" },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
Clear
|
Clear
|
||||||
|
|
@ -110,29 +130,34 @@ export default function IssueSelectorModal({
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<ScrollView style={styles.scrollArea}>
|
<ScrollView style={styles.scrollArea}>
|
||||||
{filteredConfig.map((group) => (
|
{filteredIssues.length > 0 ? (
|
||||||
<View key={group.category}>
|
filteredIssues.map((issue) => (
|
||||||
<Text style={styles.category}>{group.category}</Text>
|
<TouchableOpacity
|
||||||
{group.options.map((option) => (
|
key={issue.id}
|
||||||
<TouchableOpacity
|
style={styles.itemRow}
|
||||||
key={option.value}
|
onPress={() => toggleValue(issue.id)}
|
||||||
style={styles.itemRow}
|
>
|
||||||
onPress={() => toggleValue(option.value)}
|
<Checkbox
|
||||||
>
|
value={selectedValues.includes(issue.id)}
|
||||||
<Checkbox
|
onValueChange={() => toggleValue(issue.id)}
|
||||||
value={selectedValues.includes(option.value)}
|
color={
|
||||||
onValueChange={() => toggleValue(option.value)}
|
selectedValues.includes(issue.id) ? "#009E71" : undefined
|
||||||
color={
|
}
|
||||||
selectedValues.includes(option.value)
|
/>
|
||||||
? "#009E71"
|
<Text style={styles.itemLabel}>{issue.name[lang]}</Text>
|
||||||
: undefined
|
</TouchableOpacity>
|
||||||
}
|
))
|
||||||
/>
|
) : issues.length === 0 ? (
|
||||||
<Text style={styles.itemLabel}>{option.label}</Text>
|
<View style={styles.emptyContainer}>
|
||||||
</TouchableOpacity>
|
<Text style={styles.emptyText}>No issues available</Text>
|
||||||
))}
|
|
||||||
</View>
|
</View>
|
||||||
))}
|
) : (
|
||||||
|
<View style={styles.emptyContainer}>
|
||||||
|
<Text style={styles.emptyText}>
|
||||||
|
No issues found for "{search}"
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<View style={styles.buttonsContainer}>
|
<View style={styles.buttonsContainer}>
|
||||||
|
|
@ -163,6 +188,16 @@ export default function IssueSelectorModal({
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
emptyContainer: {
|
||||||
|
padding: 20,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
},
|
||||||
|
emptyText: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: "#717B8F",
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
buttonsContainer: {
|
buttonsContainer: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,8 @@ import DangerIcon from "../assets/icons/danger.svg";
|
||||||
import type { BmsState } from "./types";
|
import type { BmsState } from "./types";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export const BASE_URL = "https://dev-driver-saathi-api.vecmocon.com";
|
export const BASE_URL = "https://driver-saathi-api.vecmocon.com";
|
||||||
export const PAYMENT_SOCKET_BASE_URL =
|
export const PAYMENT_SOCKET_BASE_URL = "wss://driver-saathi-api.vecmocon.com";
|
||||||
"wss://dev-driver-saathi-api.vecmocon.com";
|
|
||||||
// export const BASE_URL = "https://46fa2cacfc37.ngrok-free.app";
|
// export const BASE_URL = "https://46fa2cacfc37.ngrok-free.app";
|
||||||
|
|
||||||
// const SERVER_URL =
|
// const SERVER_URL =
|
||||||
|
|
@ -181,11 +180,17 @@ export const issueConfig = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export const payments = {
|
export const payments = {
|
||||||
MIN_AMOUNT: 1,
|
MIN_AMOUNT: 200,
|
||||||
|
MAX_AMOUNT: 500000,
|
||||||
LINK_EXPIRED: "Payment link expired",
|
LINK_EXPIRED: "Payment link expired",
|
||||||
SOCKET_CONNECTION_TIMEOUT_IN_SECS: 5,
|
SOCKET_CONNECTION_TIMEOUT_IN_SECS: 5,
|
||||||
REGISTER_TRANSACTION_EMIT_EVENT_NAME: "register-transaction",
|
REGISTER_TRANSACTION_EMIT_EVENT_NAME: "register-transaction",
|
||||||
REGISTER_TRANSACTION_LISTEN_EVENT_NAME: "registration-ack",
|
REGISTER_TRANSACTION_LISTEN_EVENT_NAME: "registration-ack",
|
||||||
PAYMENT_CONFIRMATION_EVENT_NAME: "register-transaction",
|
PAYMENT_CONFIRMATION_EVENT_NAME: "register-transaction",
|
||||||
EMI_WARNING_DAYS_THRESHOLD: 14,
|
EMI_WARNING_DAYS_THRESHOLD: 14,
|
||||||
|
AMOUNT_PAID_TO: "Nav Shakti Lithium Pvt. Ltd.",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SERVICE = {
|
||||||
|
ENABLE_REGULAR_SERVICE_AFTER_IN_MONTHS: 6,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
5
eas.json
|
|
@ -9,7 +9,10 @@
|
||||||
},
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"autoIncrement": true,
|
"autoIncrement": true,
|
||||||
"distribution": "internal"
|
"distribution": "store"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"cli": {
|
||||||
|
"appVersionSource": "local"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { registerRootComponent } from 'expo';
|
||||||
|
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
|
||||||
|
// It also ensures that whether you load the app in Expo Go or in a native build,
|
||||||
|
// the environment is set up appropriately
|
||||||
|
registerRootComponent(App);
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-i18next": "^15.5.3",
|
"react-i18next": "^15.5.3",
|
||||||
"react-native": "0.79.4",
|
"react-native": "0.79.6",
|
||||||
"react-native-element-dropdown": "^2.12.4",
|
"react-native-element-dropdown": "^2.12.4",
|
||||||
"react-native-maps": "1.24.2",
|
"react-native-maps": "1.24.2",
|
||||||
"react-native-otp-entry": "^1.8.5",
|
"react-native-otp-entry": "^1.8.5",
|
||||||
|
|
@ -143,15 +143,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/generator": {
|
"node_modules/@babel/generator": {
|
||||||
"version": "7.27.5",
|
"version": "7.28.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
|
||||||
"integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==",
|
"integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.27.5",
|
"@babel/parser": "^7.28.3",
|
||||||
"@babel/types": "^7.27.3",
|
"@babel/types": "^7.28.2",
|
||||||
"@jridgewell/gen-mapping": "^0.3.5",
|
"@jridgewell/gen-mapping": "^0.3.12",
|
||||||
"@jridgewell/trace-mapping": "^0.3.25",
|
"@jridgewell/trace-mapping": "^0.3.28",
|
||||||
"jsesc": "^3.0.2"
|
"jsesc": "^3.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -240,6 +240,15 @@
|
||||||
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
|
"@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/helper-globals": {
|
||||||
|
"version": "7.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
|
||||||
|
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/helper-member-expression-to-functions": {
|
"node_modules/@babel/helper-member-expression-to-functions": {
|
||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz",
|
||||||
|
|
@ -492,12 +501,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.27.5",
|
"version": "7.28.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
|
||||||
"integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==",
|
"integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.27.3"
|
"@babel/types": "^7.28.2"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
|
|
@ -1509,27 +1518,27 @@
|
||||||
},
|
},
|
||||||
"node_modules/@babel/traverse--for-generate-function-map": {
|
"node_modules/@babel/traverse--for-generate-function-map": {
|
||||||
"name": "@babel/traverse",
|
"name": "@babel/traverse",
|
||||||
"version": "7.27.4",
|
"version": "7.28.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
|
||||||
"integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==",
|
"integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.27.3",
|
"@babel/generator": "^7.28.3",
|
||||||
"@babel/parser": "^7.27.4",
|
"@babel/helper-globals": "^7.28.0",
|
||||||
|
"@babel/parser": "^7.28.3",
|
||||||
"@babel/template": "^7.27.2",
|
"@babel/template": "^7.27.2",
|
||||||
"@babel/types": "^7.27.3",
|
"@babel/types": "^7.28.2",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.1"
|
||||||
"globals": "^11.1.0"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.27.6",
|
"version": "7.28.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
|
||||||
"integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==",
|
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.27.1",
|
"@babel/helper-string-parser": "^7.27.1",
|
||||||
|
|
@ -2636,17 +2645,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.8",
|
"version": "0.3.13",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||||
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
|
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/set-array": "^1.2.1",
|
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
|
||||||
"@jridgewell/trace-mapping": "^0.3.24"
|
"@jridgewell/trace-mapping": "^0.3.24"
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/resolve-uri": {
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
|
|
@ -2658,15 +2663,6 @@
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/set-array": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
|
||||||
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@jridgewell/source-map": {
|
"node_modules/@jridgewell/source-map": {
|
||||||
"version": "0.3.6",
|
"version": "0.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
|
||||||
|
|
@ -2684,9 +2680,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.25",
|
"version": "0.3.30",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
|
||||||
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
|
"integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "^3.1.0",
|
"@jridgewell/resolve-uri": "^3.1.0",
|
||||||
|
|
@ -3401,9 +3397,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@react-native/assets-registry": {
|
"node_modules/@react-native/assets-registry": {
|
||||||
"version": "0.79.4",
|
"version": "0.79.6",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.79.4.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.79.6.tgz",
|
||||||
"integrity": "sha512-7PjHNRtYlc36B7P1PHme8ZV0ZJ/xsA/LvMoXe6EX++t7tSPJ8iYCMBryZhcdnztgce73b94Hfx6TTGbLF+xtUg==",
|
"integrity": "sha512-UVSP1224PWg0X+mRlZNftV5xQwZGfawhivuW8fGgxNK9MS/U84xZ+16lkqcPh1ank6MOt239lIWHQ1S33CHgqA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
|
|
@ -3544,12 +3540,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@react-native/community-cli-plugin": {
|
"node_modules/@react-native/community-cli-plugin": {
|
||||||
"version": "0.79.4",
|
"version": "0.79.6",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.79.4.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.79.6.tgz",
|
||||||
"integrity": "sha512-lx1RXEJwU9Tcs2B2uiDZBa6yghU6m6STvwYqHbJlFZyNN1k3JRa9j0/CDu+0fCFacIn7rEfZpb4UWi5YhsHpQg==",
|
"integrity": "sha512-ZHVst9vByGsegeaddkD2YbZ6NvYb4n3pD9H7Pit94u+NlByq2uBJghoOjT6EKqg+UVl8tLRdi88cU2pDPwdHqA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-native/dev-middleware": "0.79.4",
|
"@react-native/dev-middleware": "0.79.6",
|
||||||
"chalk": "^4.0.0",
|
"chalk": "^4.0.0",
|
||||||
"debug": "^2.2.0",
|
"debug": "^2.2.0",
|
||||||
"invariant": "^2.2.4",
|
"invariant": "^2.2.4",
|
||||||
|
|
@ -3570,6 +3566,37 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-native/community-cli-plugin/node_modules/@react-native/debugger-frontend": {
|
||||||
|
"version": "0.79.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.79.6.tgz",
|
||||||
|
"integrity": "sha512-lIK/KkaH7ueM22bLO0YNaQwZbT/oeqhaghOvmZacaNVbJR1Cdh/XAqjT8FgCS+7PUnbxA8B55NYNKGZG3O2pYw==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@react-native/community-cli-plugin/node_modules/@react-native/dev-middleware": {
|
||||||
|
"version": "0.79.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.79.6.tgz",
|
||||||
|
"integrity": "sha512-BK3GZBa9c7XSNR27EDRtxrgyyA3/mf1j3/y+mPk7Ac0Myu85YNrXnC9g3mL5Ytwo0g58TKrAIgs1fF2Q5Mn6mQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@isaacs/ttlcache": "^1.4.1",
|
||||||
|
"@react-native/debugger-frontend": "0.79.6",
|
||||||
|
"chrome-launcher": "^0.15.2",
|
||||||
|
"chromium-edge-launcher": "^0.2.0",
|
||||||
|
"connect": "^3.6.5",
|
||||||
|
"debug": "^2.2.0",
|
||||||
|
"invariant": "^2.2.4",
|
||||||
|
"nullthrows": "^1.1.1",
|
||||||
|
"open": "^7.0.3",
|
||||||
|
"serve-static": "^1.16.2",
|
||||||
|
"ws": "^6.2.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@react-native/community-cli-plugin/node_modules/debug": {
|
"node_modules/@react-native/community-cli-plugin/node_modules/debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
|
@ -3597,6 +3624,15 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-native/community-cli-plugin/node_modules/ws": {
|
||||||
|
"version": "6.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz",
|
||||||
|
"integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"async-limiter": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@react-native/debugger-frontend": {
|
"node_modules/@react-native/debugger-frontend": {
|
||||||
"version": "0.79.4",
|
"version": "0.79.4",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.79.4.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.79.4.tgz",
|
||||||
|
|
@ -3653,18 +3689,18 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@react-native/gradle-plugin": {
|
"node_modules/@react-native/gradle-plugin": {
|
||||||
"version": "0.79.4",
|
"version": "0.79.6",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.79.4.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.79.6.tgz",
|
||||||
"integrity": "sha512-Gv5ryy23k7Sib2xVgqw65GTryg9YTij6URcMul5cI7LRcW0Aa1/FPb26l388P4oeNGNdDoAkkS+CuCWNunRuWg==",
|
"integrity": "sha512-C5odetI6py3CSELeZEVz+i00M+OJuFZXYnjVD4JyvpLn462GesHRh+Se8mSkU5QSaz9cnpMnyFLJAx05dokWbA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@react-native/js-polyfills": {
|
"node_modules/@react-native/js-polyfills": {
|
||||||
"version": "0.79.4",
|
"version": "0.79.6",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.79.4.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.79.6.tgz",
|
||||||
"integrity": "sha512-VyKPo/l9zP4+oXpQHrJq4vNOtxF7F5IMdQmceNzTnRpybRvGGgO/9jYu9mdmdKRO2KpQEc5dB4W2rYhVKdGNKg==",
|
"integrity": "sha512-6wOaBh1namYj9JlCNgX2ILeGUIwc6OP6MWe3Y5jge7Xz9fVpRqWQk88Q5Y9VrAtTMTcxoX3CvhrfRr3tGtSfQw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
|
|
@ -10237,9 +10273,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro": {
|
"node_modules/metro": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro/-/metro-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro/-/metro-0.82.5.tgz",
|
||||||
"integrity": "sha512-/gFmw3ux9CPG5WUmygY35hpyno28zi/7OUn6+OFfbweA8l0B+PPqXXLr0/T6cf5nclCcH0d22o+02fICaShVxw==",
|
"integrity": "sha512-8oAXxL7do8QckID/WZEKaIFuQJFUTLzfVcC48ghkHhNK2RGuQq8Xvf4AVd+TUA0SZtX0q8TGNXZ/eba1ckeGCg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.24.7",
|
"@babel/code-frame": "^7.24.7",
|
||||||
|
|
@ -10257,24 +10293,24 @@
|
||||||
"error-stack-parser": "^2.0.6",
|
"error-stack-parser": "^2.0.6",
|
||||||
"flow-enums-runtime": "^0.0.6",
|
"flow-enums-runtime": "^0.0.6",
|
||||||
"graceful-fs": "^4.2.4",
|
"graceful-fs": "^4.2.4",
|
||||||
"hermes-parser": "0.28.1",
|
"hermes-parser": "0.29.1",
|
||||||
"image-size": "^1.0.2",
|
"image-size": "^1.0.2",
|
||||||
"invariant": "^2.2.4",
|
"invariant": "^2.2.4",
|
||||||
"jest-worker": "^29.7.0",
|
"jest-worker": "^29.7.0",
|
||||||
"jsc-safe-url": "^0.2.2",
|
"jsc-safe-url": "^0.2.2",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"metro-babel-transformer": "0.82.4",
|
"metro-babel-transformer": "0.82.5",
|
||||||
"metro-cache": "0.82.4",
|
"metro-cache": "0.82.5",
|
||||||
"metro-cache-key": "0.82.4",
|
"metro-cache-key": "0.82.5",
|
||||||
"metro-config": "0.82.4",
|
"metro-config": "0.82.5",
|
||||||
"metro-core": "0.82.4",
|
"metro-core": "0.82.5",
|
||||||
"metro-file-map": "0.82.4",
|
"metro-file-map": "0.82.5",
|
||||||
"metro-resolver": "0.82.4",
|
"metro-resolver": "0.82.5",
|
||||||
"metro-runtime": "0.82.4",
|
"metro-runtime": "0.82.5",
|
||||||
"metro-source-map": "0.82.4",
|
"metro-source-map": "0.82.5",
|
||||||
"metro-symbolicate": "0.82.4",
|
"metro-symbolicate": "0.82.5",
|
||||||
"metro-transform-plugins": "0.82.4",
|
"metro-transform-plugins": "0.82.5",
|
||||||
"metro-transform-worker": "0.82.4",
|
"metro-transform-worker": "0.82.5",
|
||||||
"mime-types": "^2.1.27",
|
"mime-types": "^2.1.27",
|
||||||
"nullthrows": "^1.1.1",
|
"nullthrows": "^1.1.1",
|
||||||
"serialize-error": "^2.1.0",
|
"serialize-error": "^2.1.0",
|
||||||
|
|
@ -10291,14 +10327,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-babel-transformer": {
|
"node_modules/metro-babel-transformer": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.82.5.tgz",
|
||||||
"integrity": "sha512-4juJahGRb1gmNbQq48lNinB6WFNfb6m0BQqi/RQibEltNiqTCxew/dBspI2EWA4xVCd3mQWGfw0TML4KurQZnQ==",
|
"integrity": "sha512-W/scFDnwJXSccJYnOFdGiYr9srhbHPdxX9TvvACOFsIXdLilh3XuxQl/wXW6jEJfgIb0jTvoTlwwrqvuwymr6Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
"flow-enums-runtime": "^0.0.6",
|
"flow-enums-runtime": "^0.0.6",
|
||||||
"hermes-parser": "0.28.1",
|
"hermes-parser": "0.29.1",
|
||||||
"nullthrows": "^1.1.1"
|
"nullthrows": "^1.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -10306,39 +10342,39 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-babel-transformer/node_modules/hermes-estree": {
|
"node_modules/metro-babel-transformer/node_modules/hermes-estree": {
|
||||||
"version": "0.28.1",
|
"version": "0.29.1",
|
||||||
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.28.1.tgz",
|
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz",
|
||||||
"integrity": "sha512-w3nxl/RGM7LBae0v8LH2o36+8VqwOZGv9rX1wyoWT6YaKZLqpJZ0YQ5P0LVr3tuRpf7vCx0iIG4i/VmBJejxTQ==",
|
"integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/metro-babel-transformer/node_modules/hermes-parser": {
|
"node_modules/metro-babel-transformer/node_modules/hermes-parser": {
|
||||||
"version": "0.28.1",
|
"version": "0.29.1",
|
||||||
"resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.28.1.tgz",
|
"resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz",
|
||||||
"integrity": "sha512-nf8o+hE8g7UJWParnccljHumE9Vlq8F7MqIdeahl+4x0tvCUJYRrT0L7h0MMg/X9YJmkNwsfbaNNrzPtFXOscg==",
|
"integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hermes-estree": "0.28.1"
|
"hermes-estree": "0.29.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-cache": {
|
"node_modules/metro-cache": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.82.5.tgz",
|
||||||
"integrity": "sha512-vX0ylSMGtORKiZ4G8uP6fgfPdDiCWvLZUGZ5zIblSGylOX6JYhvExl0Zg4UA9pix/SSQu5Pnp9vdODMFsNIxhw==",
|
"integrity": "sha512-AwHV9607xZpedu1NQcjUkua8v7HfOTKfftl6Vc9OGr/jbpiJX6Gpy8E/V9jo/U9UuVYX2PqSUcVNZmu+LTm71Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"exponential-backoff": "^3.1.1",
|
"exponential-backoff": "^3.1.1",
|
||||||
"flow-enums-runtime": "^0.0.6",
|
"flow-enums-runtime": "^0.0.6",
|
||||||
"https-proxy-agent": "^7.0.5",
|
"https-proxy-agent": "^7.0.5",
|
||||||
"metro-core": "0.82.4"
|
"metro-core": "0.82.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.18"
|
"node": ">=18.18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-cache-key": {
|
"node_modules/metro-cache-key": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.82.5.tgz",
|
||||||
"integrity": "sha512-2JCTqcpF+f2OghOpe/+x+JywfzDkrHdAqinPFWmK2ezNAU/qX0jBFaTETogPibFivxZJil37w9Yp6syX8rFUng==",
|
"integrity": "sha512-qpVmPbDJuRLrT4kcGlUouyqLGssJnbTllVtvIgXfR7ZuzMKf0mGS+8WzcqzNK8+kCyakombQWR0uDd8qhWGJcA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"flow-enums-runtime": "^0.0.6"
|
"flow-enums-runtime": "^0.0.6"
|
||||||
|
|
@ -10348,9 +10384,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-cache/node_modules/agent-base": {
|
"node_modules/metro-cache/node_modules/agent-base": {
|
||||||
"version": "7.1.3",
|
"version": "7.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||||
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
|
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
|
|
@ -10370,42 +10406,42 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-config": {
|
"node_modules/metro-config": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.82.5.tgz",
|
||||||
"integrity": "sha512-Ki3Wumr3hKHGDS7RrHsygmmRNc/PCJrvkLn0+BWWxmbOmOcMMJDSmSI+WRlT8jd5VPZFxIi4wg+sAt5yBXAK0g==",
|
"integrity": "sha512-/r83VqE55l0WsBf8IhNmc/3z71y2zIPe5kRSuqA5tY/SL/ULzlHUJEMd1szztd0G45JozLwjvrhAzhDPJ/Qo/g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"connect": "^3.6.5",
|
"connect": "^3.6.5",
|
||||||
"cosmiconfig": "^5.0.5",
|
"cosmiconfig": "^5.0.5",
|
||||||
"flow-enums-runtime": "^0.0.6",
|
"flow-enums-runtime": "^0.0.6",
|
||||||
"jest-validate": "^29.7.0",
|
"jest-validate": "^29.7.0",
|
||||||
"metro": "0.82.4",
|
"metro": "0.82.5",
|
||||||
"metro-cache": "0.82.4",
|
"metro-cache": "0.82.5",
|
||||||
"metro-core": "0.82.4",
|
"metro-core": "0.82.5",
|
||||||
"metro-runtime": "0.82.4"
|
"metro-runtime": "0.82.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.18"
|
"node": ">=18.18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-core": {
|
"node_modules/metro-core": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.82.5.tgz",
|
||||||
"integrity": "sha512-Xo4ozbxPg2vfgJGCgXZ8sVhC2M0lhTqD+tsKO2q9aelq/dCjnnSb26xZKcQO80CQOQUL7e3QWB7pLFGPjZm31A==",
|
"integrity": "sha512-OJL18VbSw2RgtBm1f2P3J5kb892LCVJqMvslXxuxjAPex8OH7Eb8RBfgEo7VZSjgb/LOf4jhC4UFk5l5tAOHHA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"flow-enums-runtime": "^0.0.6",
|
"flow-enums-runtime": "^0.0.6",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"metro-resolver": "0.82.4"
|
"metro-resolver": "0.82.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.18"
|
"node": ">=18.18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-file-map": {
|
"node_modules/metro-file-map": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.82.5.tgz",
|
||||||
"integrity": "sha512-eO7HD1O3aeNsbEe6NBZvx1lLJUrxgyATjnDmb7bm4eyF6yWOQot9XVtxTDLNifECuvsZ4jzRiTInrbmIHkTdGA==",
|
"integrity": "sha512-vpMDxkGIB+MTN8Af5hvSAanc6zXQipsAUO+XUx3PCQieKUfLwdoa8qaZ1WAQYRpaU+CJ8vhBcxtzzo3d9IsCIQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
|
|
@ -10423,9 +10459,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-minify-terser": {
|
"node_modules/metro-minify-terser": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.82.5.tgz",
|
||||||
"integrity": "sha512-W79Mi6BUwWVaM8Mc5XepcqkG+TSsCyyo//dmTsgYfJcsmReQorRFodil3bbJInETvjzdnS1mCsUo9pllNjT1Hg==",
|
"integrity": "sha512-v6Nx7A4We6PqPu/ta1oGTqJ4Usz0P7c+3XNeBxW9kp8zayS3lHUKR0sY0wsCHInxZlNAEICx791x+uXytFUuwg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"flow-enums-runtime": "^0.0.6",
|
"flow-enums-runtime": "^0.0.6",
|
||||||
|
|
@ -10436,9 +10472,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-resolver": {
|
"node_modules/metro-resolver": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.82.5.tgz",
|
||||||
"integrity": "sha512-uWoHzOBGQTPT5PjippB8rRT3iI9CTgFA9tRiLMzrseA5o7YAlgvfTdY9vFk2qyk3lW3aQfFKWkmqENryPRpu+Q==",
|
"integrity": "sha512-kFowLnWACt3bEsuVsaRNgwplT8U7kETnaFHaZePlARz4Fg8tZtmRDUmjaD68CGAwc0rwdwNCkWizLYpnyVcs2g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"flow-enums-runtime": "^0.0.6"
|
"flow-enums-runtime": "^0.0.6"
|
||||||
|
|
@ -10448,9 +10484,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-runtime": {
|
"node_modules/metro-runtime": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.82.5.tgz",
|
||||||
"integrity": "sha512-vVyFO7H+eLXRV2E7YAUYA7aMGBECGagqxmFvC2hmErS7oq90BbPVENfAHbUWq1vWH+MRiivoRxdxlN8gBoF/dw==",
|
"integrity": "sha512-rQZDoCUf7k4Broyw3Ixxlq5ieIPiR1ULONdpcYpbJQ6yQ5GGEyYjtkztGD+OhHlw81LCR2SUAoPvtTus2WDK5g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.25.0",
|
"@babel/runtime": "^7.25.0",
|
||||||
|
|
@ -10461,9 +10497,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-source-map": {
|
"node_modules/metro-source-map": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.82.5.tgz",
|
||||||
"integrity": "sha512-9jzDQJ0FPas1FuQFtwmBHsez2BfhFNufMowbOMeG3ZaFvzeziE8A0aJwILDS3U+V5039ssCQFiQeqDgENWvquA==",
|
"integrity": "sha512-wH+awTOQJVkbhn2SKyaw+0cd+RVSCZ3sHVgyqJFQXIee/yLs3dZqKjjeKKhhVeudgjXo7aE/vSu/zVfcQEcUfw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/traverse": "^7.25.3",
|
"@babel/traverse": "^7.25.3",
|
||||||
|
|
@ -10471,9 +10507,9 @@
|
||||||
"@babel/types": "^7.25.2",
|
"@babel/types": "^7.25.2",
|
||||||
"flow-enums-runtime": "^0.0.6",
|
"flow-enums-runtime": "^0.0.6",
|
||||||
"invariant": "^2.2.4",
|
"invariant": "^2.2.4",
|
||||||
"metro-symbolicate": "0.82.4",
|
"metro-symbolicate": "0.82.5",
|
||||||
"nullthrows": "^1.1.1",
|
"nullthrows": "^1.1.1",
|
||||||
"ob1": "0.82.4",
|
"ob1": "0.82.5",
|
||||||
"source-map": "^0.5.6",
|
"source-map": "^0.5.6",
|
||||||
"vlq": "^1.0.0"
|
"vlq": "^1.0.0"
|
||||||
},
|
},
|
||||||
|
|
@ -10491,14 +10527,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-symbolicate": {
|
"node_modules/metro-symbolicate": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.82.5.tgz",
|
||||||
"integrity": "sha512-LwEwAtdsx7z8rYjxjpLWxuFa2U0J6TS6ljlQM4WAATKa4uzV8unmnRuN2iNBWTmRqgNR77mzmI2vhwD4QSCo+w==",
|
"integrity": "sha512-1u+07gzrvYDJ/oNXuOG1EXSvXZka/0JSW1q2EYBWerVKMOhvv9JzDGyzmuV7hHbF2Hg3T3S2uiM36sLz1qKsiw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"flow-enums-runtime": "^0.0.6",
|
"flow-enums-runtime": "^0.0.6",
|
||||||
"invariant": "^2.2.4",
|
"invariant": "^2.2.4",
|
||||||
"metro-source-map": "0.82.4",
|
"metro-source-map": "0.82.5",
|
||||||
"nullthrows": "^1.1.1",
|
"nullthrows": "^1.1.1",
|
||||||
"source-map": "^0.5.6",
|
"source-map": "^0.5.6",
|
||||||
"vlq": "^1.0.0"
|
"vlq": "^1.0.0"
|
||||||
|
|
@ -10520,9 +10556,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-transform-plugins": {
|
"node_modules/metro-transform-plugins": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.82.5.tgz",
|
||||||
"integrity": "sha512-NoWQRPHupVpnDgYguiEcm7YwDhnqW02iWWQjO2O8NsNP09rEMSq99nPjARWfukN7+KDh6YjLvTIN20mj3dk9kw==",
|
"integrity": "sha512-57Bqf3rgq9nPqLrT2d9kf/2WVieTFqsQ6qWHpEng5naIUtc/Iiw9+0bfLLWSAw0GH40iJ4yMjFcFJDtNSYynMA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
|
|
@ -10537,9 +10573,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro-transform-worker": {
|
"node_modules/metro-transform-worker": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.82.5.tgz",
|
||||||
"integrity": "sha512-kPI7Ad/tdAnI9PY4T+2H0cdgGeSWWdiPRKuytI806UcN4VhFL6OmYa19/4abYVYF+Cd2jo57CDuwbaxRfmXDhw==",
|
"integrity": "sha512-mx0grhAX7xe+XUQH6qoHHlWedI8fhSpDGsfga7CpkO9Lk9W+aPitNtJWNGrW8PfjKEWbT9Uz9O50dkI8bJqigw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
|
|
@ -10547,13 +10583,13 @@
|
||||||
"@babel/parser": "^7.25.3",
|
"@babel/parser": "^7.25.3",
|
||||||
"@babel/types": "^7.25.2",
|
"@babel/types": "^7.25.2",
|
||||||
"flow-enums-runtime": "^0.0.6",
|
"flow-enums-runtime": "^0.0.6",
|
||||||
"metro": "0.82.4",
|
"metro": "0.82.5",
|
||||||
"metro-babel-transformer": "0.82.4",
|
"metro-babel-transformer": "0.82.5",
|
||||||
"metro-cache": "0.82.4",
|
"metro-cache": "0.82.5",
|
||||||
"metro-cache-key": "0.82.4",
|
"metro-cache-key": "0.82.5",
|
||||||
"metro-minify-terser": "0.82.4",
|
"metro-minify-terser": "0.82.5",
|
||||||
"metro-source-map": "0.82.4",
|
"metro-source-map": "0.82.5",
|
||||||
"metro-transform-plugins": "0.82.4",
|
"metro-transform-plugins": "0.82.5",
|
||||||
"nullthrows": "^1.1.1"
|
"nullthrows": "^1.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -10567,18 +10603,18 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/metro/node_modules/hermes-estree": {
|
"node_modules/metro/node_modules/hermes-estree": {
|
||||||
"version": "0.28.1",
|
"version": "0.29.1",
|
||||||
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.28.1.tgz",
|
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz",
|
||||||
"integrity": "sha512-w3nxl/RGM7LBae0v8LH2o36+8VqwOZGv9rX1wyoWT6YaKZLqpJZ0YQ5P0LVr3tuRpf7vCx0iIG4i/VmBJejxTQ==",
|
"integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/metro/node_modules/hermes-parser": {
|
"node_modules/metro/node_modules/hermes-parser": {
|
||||||
"version": "0.28.1",
|
"version": "0.29.1",
|
||||||
"resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.28.1.tgz",
|
"resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz",
|
||||||
"integrity": "sha512-nf8o+hE8g7UJWParnccljHumE9Vlq8F7MqIdeahl+4x0tvCUJYRrT0L7h0MMg/X9YJmkNwsfbaNNrzPtFXOscg==",
|
"integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hermes-estree": "0.28.1"
|
"hermes-estree": "0.29.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/metro/node_modules/source-map": {
|
"node_modules/metro/node_modules/source-map": {
|
||||||
|
|
@ -10981,9 +11017,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/ob1": {
|
"node_modules/ob1": {
|
||||||
"version": "0.82.4",
|
"version": "0.82.5",
|
||||||
"resolved": "https://registry.npmjs.org/ob1/-/ob1-0.82.4.tgz",
|
"resolved": "https://registry.npmjs.org/ob1/-/ob1-0.82.5.tgz",
|
||||||
"integrity": "sha512-n9S8e4l5TvkrequEAMDidl4yXesruWTNTzVkeaHSGywoTOIwTzZzKw7Z670H3eaXDZui5MJXjWGNzYowVZIxCA==",
|
"integrity": "sha512-QyQQ6e66f+Ut/qUVjEce0E/wux5nAGLXYZDn1jr15JWstHsCH3l6VVrg8NKDptW9NEiBXKOJeGF/ydxeSDF3IQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"flow-enums-runtime": "^0.0.6"
|
"flow-enums-runtime": "^0.0.6"
|
||||||
|
|
@ -11907,19 +11943,19 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/react-native": {
|
"node_modules/react-native": {
|
||||||
"version": "0.79.4",
|
"version": "0.79.6",
|
||||||
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.79.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.79.6.tgz",
|
||||||
"integrity": "sha512-CfxYMuszvnO/33Q5rB//7cU1u9P8rSOvzhE2053Phdb8+6bof9NLayCllU2nmPrm8n9o6RU1Fz5H0yquLQ0DAw==",
|
"integrity": "sha512-kvIWSmf4QPfY41HC25TR285N7Fv0Pyn3DAEK8qRL9dA35usSaxsJkHfw+VqnonqJjXOaoKCEanwudRAJ60TBGA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jest/create-cache-key-function": "^29.7.0",
|
"@jest/create-cache-key-function": "^29.7.0",
|
||||||
"@react-native/assets-registry": "0.79.4",
|
"@react-native/assets-registry": "0.79.6",
|
||||||
"@react-native/codegen": "0.79.4",
|
"@react-native/codegen": "0.79.6",
|
||||||
"@react-native/community-cli-plugin": "0.79.4",
|
"@react-native/community-cli-plugin": "0.79.6",
|
||||||
"@react-native/gradle-plugin": "0.79.4",
|
"@react-native/gradle-plugin": "0.79.6",
|
||||||
"@react-native/js-polyfills": "0.79.4",
|
"@react-native/js-polyfills": "0.79.6",
|
||||||
"@react-native/normalize-colors": "0.79.4",
|
"@react-native/normalize-colors": "0.79.6",
|
||||||
"@react-native/virtualized-lists": "0.79.4",
|
"@react-native/virtualized-lists": "0.79.6",
|
||||||
"abort-controller": "^3.0.0",
|
"abort-controller": "^3.0.0",
|
||||||
"anser": "^1.4.9",
|
"anser": "^1.4.9",
|
||||||
"ansi-regex": "^5.0.0",
|
"ansi-regex": "^5.0.0",
|
||||||
|
|
@ -12207,10 +12243,37 @@
|
||||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
|
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/react-native/node_modules/@react-native/codegen": {
|
||||||
|
"version": "0.79.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.79.6.tgz",
|
||||||
|
"integrity": "sha512-iRBX8Lgbqypwnfba7s6opeUwVyaR23mowh9ILw7EcT2oLz3RqMmjJdrbVpWhGSMGq2qkPfqAH7bhO8C7O+xfjQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/core": "^7.25.2",
|
||||||
|
"@babel/parser": "^7.25.3",
|
||||||
|
"glob": "^7.1.1",
|
||||||
|
"hermes-parser": "0.25.1",
|
||||||
|
"invariant": "^2.2.4",
|
||||||
|
"nullthrows": "^1.1.1",
|
||||||
|
"yargs": "^17.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/react-native/node_modules/@react-native/normalize-colors": {
|
||||||
|
"version": "0.79.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.79.6.tgz",
|
||||||
|
"integrity": "sha512-0v2/ruY7eeKun4BeKu+GcfO+SHBdl0LJn4ZFzTzjHdWES0Cn+ONqKljYaIv8p9MV2Hx/kcdEvbY4lWI34jC/mQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/react-native/node_modules/@react-native/virtualized-lists": {
|
"node_modules/react-native/node_modules/@react-native/virtualized-lists": {
|
||||||
"version": "0.79.4",
|
"version": "0.79.6",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.79.4.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.79.6.tgz",
|
||||||
"integrity": "sha512-0Mdcox6e5PTonuM1WIo3ks7MBAa3IDzj0pKnE5xAwSgQ0DJW2P5dYf+KjWmpkE+Yb0w41ZbtXPhKq+U2JJ6C/Q==",
|
"integrity": "sha512-khA/Hrbb+rB68YUHrLubfLgMOD9up0glJhw25UE3Kntj32YDyuO0Tqc81ryNTcCekFKJ8XrAaEjcfPg81zBGPw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"invariant": "^2.2.4",
|
"invariant": "^2.2.4",
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "batteryasaservice",
|
"name": "batteryasaservice",
|
||||||
"main": "expo-router/entry",
|
"main": "expo-router/entry",
|
||||||
"version": "1.0.0",
|
"version": "2.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "expo start",
|
"start": "expo start --dev-client",
|
||||||
"android": "expo run:android",
|
"android": "expo run:android",
|
||||||
"ios": "expo run:ios",
|
"ios": "expo run:ios",
|
||||||
"web": "expo start --web",
|
"web": "expo start --web",
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-i18next": "^15.5.3",
|
"react-i18next": "^15.5.3",
|
||||||
"react-native": "0.79.4",
|
"react-native": "0.79.6",
|
||||||
"react-native-element-dropdown": "^2.12.4",
|
"react-native-element-dropdown": "^2.12.4",
|
||||||
"react-native-maps": "1.24.2",
|
"react-native-maps": "1.24.2",
|
||||||
"react-native-otp-entry": "^1.8.5",
|
"react-native-otp-entry": "^1.8.5",
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ export const setLanguage = async (language: string) => {
|
||||||
export const getLanguage = async () => {
|
export const getLanguage = async () => {
|
||||||
const lang = await AsyncStorage.getItem(STORAGE_KEYS.LANGUAGE);
|
const lang = await AsyncStorage.getItem(STORAGE_KEYS.LANGUAGE);
|
||||||
|
|
||||||
|
if (!lang) return "en";
|
||||||
return lang;
|
return lang;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,11 @@
|
||||||
"enter-otp": "Please enter OTP sent to your mobile number",
|
"enter-otp": "Please enter OTP sent to your mobile number",
|
||||||
"verify-otp": "Verify OTP",
|
"verify-otp": "Verify OTP",
|
||||||
"otp-incorrect": "OTP incorrect.",
|
"otp-incorrect": "OTP incorrect.",
|
||||||
"resend-otp": "Resend OTP in",
|
"resend-otp": "Resend OTP ",
|
||||||
"verification-limit-exceeded": "Verification limit exceeded, try again later",
|
"verification-limit-exceeded": "Verification limit exceeded, try again later",
|
||||||
"send-otp": "Send OTP"
|
"send-otp": "Send OTP",
|
||||||
|
"phone-number-is-required": "Phone number is required",
|
||||||
|
"must-be-10-digits": "Phone number must be exactly 10 digits"
|
||||||
},
|
},
|
||||||
"navigation": {
|
"navigation": {
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
|
|
@ -40,7 +42,8 @@
|
||||||
"alerts": "Alerts",
|
"alerts": "Alerts",
|
||||||
"emi-alert": "14 days left to pay the EMI!",
|
"emi-alert": "14 days left to pay the EMI!",
|
||||||
"km": "km",
|
"km": "km",
|
||||||
"days-left-to-pay-emi": "days left to pay the EMI!"
|
"days-left-to-pay-emi": "days left to pay the EMI!",
|
||||||
|
"fetching-location": "Fetching location..."
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"my-account": "My Account",
|
"my-account": "My Account",
|
||||||
|
|
@ -62,8 +65,7 @@
|
||||||
"whatsapp": "Whatsapp",
|
"whatsapp": "Whatsapp",
|
||||||
"call-us": "Call Us",
|
"call-us": "Call Us",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
"lang-changed": "भाषा सफलतापूर्वक बदल दी गई",
|
"lang-changed": "भाषा सफलतापूर्वक बदल दी गई"
|
||||||
"fetching-location": "Fetching location..."
|
|
||||||
},
|
},
|
||||||
"payment": {
|
"payment": {
|
||||||
"last-emi-details": "Last EMI Details",
|
"last-emi-details": "Last EMI Details",
|
||||||
|
|
@ -113,14 +115,19 @@
|
||||||
"pay-using-upi": "Pay using UPI App",
|
"pay-using-upi": "Pay using UPI App",
|
||||||
"confirm-payment": "Confirm once your payment is completed",
|
"confirm-payment": "Confirm once your payment is completed",
|
||||||
"payment-done": "Payment Done",
|
"payment-done": "Payment Done",
|
||||||
"view-plan": "View Plan"
|
"view-plan": "View Plan",
|
||||||
|
"cancel-payment": "Cancel Payment",
|
||||||
|
"continue-payment": "Continue Payment",
|
||||||
|
"do-you-want-to-cancel-payment": "Are you sure you want to cancel the payment?",
|
||||||
|
"amount-is-required": "Amount is required",
|
||||||
|
"pay": "Pay"
|
||||||
},
|
},
|
||||||
"service": {
|
"service": {
|
||||||
"schedule-maintenance": "Schedule Maintenance",
|
"schedule-maintenance": "Schedule Maintenance",
|
||||||
"service-type": "Service Type",
|
"service-type": "Service Type",
|
||||||
"issue": "Issue",
|
"issue": "Issue",
|
||||||
"select-issue": "Select Issue",
|
"select-issue": "Select Issue",
|
||||||
"select-datetime": "Select Date and Time",
|
"select-datetime": "Select Date and Time (10AM - 5PM)",
|
||||||
"select": "-Select-",
|
"select": "-Select-",
|
||||||
"add-photos": "Add photos",
|
"add-photos": "Add photos",
|
||||||
"supported-formats": "Supported formats include JPG, JPEG and PNG.",
|
"supported-formats": "Supported formats include JPG, JPEG and PNG.",
|
||||||
|
|
@ -129,9 +136,14 @@
|
||||||
"clear": "Clear",
|
"clear": "Clear",
|
||||||
"issues-selected": "issue selected",
|
"issues-selected": "issue selected",
|
||||||
"service-request-success": "Service request submitted successfully",
|
"service-request-success": "Service request submitted successfully",
|
||||||
"something-went-wrong": "Something went wrong!",
|
|
||||||
"select-valid-time": "Select valid time",
|
"select-valid-time": "Select valid time",
|
||||||
"words": "words"
|
"words": "words",
|
||||||
|
"time-must-be-between-10-and-5": "Select a time between 10 AM – 5 PM.",
|
||||||
|
"regular-available-after-6-months": "Regular service available after 6 months of purchase",
|
||||||
|
"service-type-is-required": "Service Type is required",
|
||||||
|
"atleast-one-issue-is-required": "At least one issue is required",
|
||||||
|
"date-and-time-is-required": "Date and time is required",
|
||||||
|
"atleast-one-photo-is-required": "At least one photo is required"
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
"battery-and-warranty": "My Battery and Warranty",
|
"battery-and-warranty": "My Battery and Warranty",
|
||||||
|
|
@ -150,5 +162,9 @@
|
||||||
"serial-number": "Serial Number",
|
"serial-number": "Serial Number",
|
||||||
"charger-details": "Charger Details",
|
"charger-details": "Charger Details",
|
||||||
"uid": "UID"
|
"uid": "UID"
|
||||||
|
},
|
||||||
|
"common": {
|
||||||
|
"no-internet-connection": "No internet connection",
|
||||||
|
"something-went-wrong": "Something went wrong!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@
|
||||||
"otp-incorrect": "OTP गलत है।",
|
"otp-incorrect": "OTP गलत है।",
|
||||||
"resend-otp": "OTP पुनः भेजें",
|
"resend-otp": "OTP पुनः भेजें",
|
||||||
"verification-limit-exceeded": "वेरिफिकेशन लिमिट पार हो गई है, बाद में फिर से कोशिश करो",
|
"verification-limit-exceeded": "वेरिफिकेशन लिमिट पार हो गई है, बाद में फिर से कोशिश करो",
|
||||||
"send-otp": "OTP भेजें"
|
"send-otp": "OTP भेजें",
|
||||||
|
"phone-number-is-required": "फ़ोन नंबर अनिवार्य है",
|
||||||
|
"must-be-10-digits": "फ़ोन नंबर बिल्कुल 10 अंकों का होना चाहिए"
|
||||||
},
|
},
|
||||||
"navigation": {
|
"navigation": {
|
||||||
"home": "होम",
|
"home": "होम",
|
||||||
|
|
@ -113,14 +115,19 @@
|
||||||
"amount-to-be-paid": "भुगतान की जाने वाली राशि",
|
"amount-to-be-paid": "भुगतान की जाने वाली राशि",
|
||||||
"pay-using-upi": "UPI ऐप का उपयोग करके भुगतान करें",
|
"pay-using-upi": "UPI ऐप का उपयोग करके भुगतान करें",
|
||||||
"confirm-payment": "भुगतान पूरा होने पर पुष्टि करें।",
|
"confirm-payment": "भुगतान पूरा होने पर पुष्टि करें।",
|
||||||
"payment-done": "भुगतान हो गया"
|
"payment-done": "भुगतान हो गया",
|
||||||
|
"cancel-payment": "भुगतान रद्द करें",
|
||||||
|
"continue-payment": "भुगतान जारी रखें",
|
||||||
|
"do-you-want-to-cancel-payment": "क्या आप वाकई भुगतान रद्द करना चाहते हैं?",
|
||||||
|
"amount-is-required": "राशि दर्ज करें",
|
||||||
|
"pay": "भुगतान करें"
|
||||||
},
|
},
|
||||||
"service": {
|
"service": {
|
||||||
"schedule-maintenance": "शेड्यूल मेंटेनेंस",
|
"schedule-maintenance": "शेड्यूल मेंटेनेंस",
|
||||||
"service-type": "सेवा प्रकार",
|
"service-type": "सेवा प्रकार",
|
||||||
"issue": "समस्या",
|
"issue": "समस्या",
|
||||||
"select-issue": "समस्या का चयन करें",
|
"select-issue": "समस्या का चयन करें",
|
||||||
"select-datetime": "दिनांक और समय का चयन करें",
|
"select-datetime": "दिनांक और समय (10AM - 5PM) का चयन करें",
|
||||||
"select": "-चुनें-",
|
"select": "-चुनें-",
|
||||||
"add-photos": "फ़ोटो जोड़ें",
|
"add-photos": "फ़ोटो जोड़ें",
|
||||||
"supported-formats": "समर्थित प्रारूपों में JPG, JPEG और PNG शामिल हैं।",
|
"supported-formats": "समर्थित प्रारूपों में JPG, JPEG और PNG शामिल हैं।",
|
||||||
|
|
@ -129,9 +136,14 @@
|
||||||
"clear": "साफ़ करें",
|
"clear": "साफ़ करें",
|
||||||
"issues-selected": "समस्याएँ चुनी गई",
|
"issues-selected": "समस्याएँ चुनी गई",
|
||||||
"service-request-success": "सेवा अनुरोध सफलतापूर्वक सबमिट किया गया",
|
"service-request-success": "सेवा अनुरोध सफलतापूर्वक सबमिट किया गया",
|
||||||
"something-went-wrong": "कुछ गलत हो गया!",
|
|
||||||
"words": "शब्द",
|
"words": "शब्द",
|
||||||
"select-valid-time": "सही समय चुनें"
|
"select-valid-time": "सही समय चुनें",
|
||||||
|
"time-must-be-between-10-and-5": "समय सुबह 10:00 बजे से शाम 5:00 बजे के बीच चुनें।",
|
||||||
|
"regular-available-after-6-months": "रेगुलर सेवा खरीद के 6 महीने बाद उपलब्ध होगी",
|
||||||
|
"service-type-is-required": "सेवा का प्रकार चुनें",
|
||||||
|
"atleast-one-issue-is-required": "कम से कम एक समस्या चुनें",
|
||||||
|
"date-and-time-is-required": "तारीख और समय चुनें",
|
||||||
|
"atleast-one-photo-is-required": "कम से कम एक फोटो चुनें"
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
"battery-and-warranty": "मेरी बैटरी और वारंटी",
|
"battery-and-warranty": "मेरी बैटरी और वारंटी",
|
||||||
|
|
@ -150,5 +162,9 @@
|
||||||
"serial-number": "सीरियल नंबर",
|
"serial-number": "सीरियल नंबर",
|
||||||
"charger-details": "चार्जर विवरण",
|
"charger-details": "चार्जर विवरण",
|
||||||
"uid": "UID"
|
"uid": "UID"
|
||||||
|
},
|
||||||
|
"common": {
|
||||||
|
"no-internet-connection": "इंटरनेट कनेक्शन नहीं है",
|
||||||
|
"something-went-wrong": "कुछ गलत हो गया!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import {
|
||||||
updateTelemetry,
|
updateTelemetry,
|
||||||
setTelemetryLoading,
|
setTelemetryLoading,
|
||||||
setTelemetryError,
|
setTelemetryError,
|
||||||
|
clearTelemetry,
|
||||||
} from "../store/telemetrySlice";
|
} from "../store/telemetrySlice";
|
||||||
import { BmsState } from "@/constants/types";
|
import { BmsState } from "@/constants/types";
|
||||||
import { BASE_URL, SOCKET_BASE_URL, VECTOR_BASE_URL } from "@/constants/config";
|
import { BASE_URL, SOCKET_BASE_URL, VECTOR_BASE_URL } from "@/constants/config";
|
||||||
|
|
@ -29,6 +30,7 @@ const fetchToken = async (): Promise<string> => {
|
||||||
`${BASE_URL}/api/v1/vec-token`
|
`${BASE_URL}/api/v1/vec-token`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log("response.data.data.token", response.data.data.token);
|
||||||
return response.data.data.token;
|
return response.data.data.token;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching token:", error);
|
console.error("Error fetching token:", error);
|
||||||
|
|
@ -36,8 +38,11 @@ const fetchToken = async (): Promise<string> => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchControllingServer = async (token: string): Promise<string> => {
|
const fetchControllingServer = async (
|
||||||
const hardwareDeviceId = store.getState().user.data?.batteries[0]?.device_id;
|
token: string,
|
||||||
|
hardwareDeviceId: string
|
||||||
|
): Promise<string> => {
|
||||||
|
// const hardwareDeviceId = store.getState().user.data?.batteries[0]?.device_id;
|
||||||
console.log("Hardware Device ID:", store.getState().user.data);
|
console.log("Hardware Device ID:", store.getState().user.data);
|
||||||
// const hardwareDeviceId = "VEC16000866082076280974";
|
// const hardwareDeviceId = "VEC16000866082076280974";
|
||||||
|
|
||||||
|
|
@ -103,15 +108,31 @@ export const connectSocket = () => {
|
||||||
|
|
||||||
export const initSocket = async () => {
|
export const initSocket = async () => {
|
||||||
try {
|
try {
|
||||||
|
if (socket) {
|
||||||
|
socket.disconnect();
|
||||||
|
socket = null;
|
||||||
|
console.log("Previous socket disconnected before reinitializing");
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(clearTelemetry());
|
||||||
|
const hardwareDeviceId =
|
||||||
|
store.getState().user.data?.batteries?.[0]?.device_id;
|
||||||
|
if (!hardwareDeviceId) {
|
||||||
|
console.warn("initSocket skipped: hardwareDeviceId not ready");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(hardwareDeviceId, "hardware deviced");
|
||||||
//get latest telemetry as fallback option
|
//get latest telemetry as fallback option
|
||||||
token = await fetchToken();
|
token = await fetchToken();
|
||||||
controllingServer = await fetchControllingServer(token);
|
controllingServer = await fetchControllingServer(token, hardwareDeviceId);
|
||||||
store.dispatch(setTelemetryLoading());
|
store.dispatch(setTelemetryLoading());
|
||||||
await fetchLatestTelemetry(token, controllingServer);
|
await fetchLatestTelemetry(token, controllingServer, hardwareDeviceId);
|
||||||
connectSocket();
|
connectSocket();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err, "");
|
console.log(err, "");
|
||||||
store.dispatch(setTelemetryError("Initialization failed"));
|
store.dispatch(setTelemetryError("Initialization failed"));
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -171,11 +192,12 @@ const handleSocketData = (data: any) => {
|
||||||
|
|
||||||
const fetchLatestTelemetry = async (
|
const fetchLatestTelemetry = async (
|
||||||
token: string,
|
token: string,
|
||||||
controllingServer: string
|
controllingServer: string,
|
||||||
|
hardwareDeviceId: string
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const hardwareDeviceId =
|
// const hardwareDeviceId =
|
||||||
store.getState().user.data?.batteries[0]?.device_id;
|
// store.getState().user.data?.batteries[0]?.device_id;
|
||||||
if (!hardwareDeviceId || !token || !controllingServer)
|
if (!hardwareDeviceId || !token || !controllingServer)
|
||||||
throw new Error("Missing hardwareDeviceId");
|
throw new Error("Missing hardwareDeviceId");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,14 @@ export const telemetrySlice = createSlice({
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.error = action.payload;
|
state.error = action.payload;
|
||||||
},
|
},
|
||||||
|
clearTelemetry: () => initialState,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { updateTelemetry, setTelemetryLoading, setTelemetryError } =
|
export const {
|
||||||
telemetrySlice.actions;
|
updateTelemetry,
|
||||||
|
setTelemetryLoading,
|
||||||
|
clearTelemetry,
|
||||||
|
setTelemetryError,
|
||||||
|
} = telemetrySlice.actions;
|
||||||
export default telemetrySlice.reducer;
|
export default telemetrySlice.reducer;
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ const userSlice = createSlice({
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
clearUser: (state) => {
|
clearUser: (state) => {
|
||||||
state.data = null;
|
state.loading = true;
|
||||||
state.error = null;
|
state.error = null;
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
state.paymentSummary = null;
|
state.paymentSummary = null;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"extends": "expo/tsconfig.base",
|
"extends": "expo/tsconfig.base",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": ["./*"]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,21 @@
|
||||||
|
import { router, type Href } from "expo-router";
|
||||||
|
import type { NavigationOptions } from "expo-router/build/global-state/routing";
|
||||||
|
|
||||||
export const displayValue = (value: any, formatter?: (val: any) => string) => {
|
export const displayValue = (value: any, formatter?: (val: any) => string) => {
|
||||||
if (value === null || value === undefined || value === "") {
|
if (value === null || value === undefined || value === "") {
|
||||||
return "--";
|
return "--";
|
||||||
}
|
}
|
||||||
return formatter ? formatter(value) : value;
|
return formatter ? formatter(value) : value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const formatCurrency = (amount: number) => {
|
||||||
|
return `₹${amount.toLocaleString()}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearStackAndRouteTo = (
|
||||||
|
link: Href,
|
||||||
|
options?: NavigationOptions
|
||||||
|
): void => {
|
||||||
|
router.dismissAll();
|
||||||
|
router.replace(link, options);
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,3 +10,28 @@ export const toCamel = (obj: any): any => {
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const formatDate = (dateString?: string | null) => {
|
||||||
|
if (!dateString) {
|
||||||
|
return "--";
|
||||||
|
}
|
||||||
|
const date = dateString ? new Date(dateString) : new Date();
|
||||||
|
|
||||||
|
const optionsDate = {
|
||||||
|
day: "numeric",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
} as const;
|
||||||
|
const optionsTime = {
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "2-digit",
|
||||||
|
hour12: true,
|
||||||
|
} as const;
|
||||||
|
const optionsWeekday = { weekday: "long" } as const;
|
||||||
|
|
||||||
|
const formattedDate = date.toLocaleDateString("en-IN", optionsDate);
|
||||||
|
const formattedTime = date.toLocaleTimeString("en-IN", optionsTime);
|
||||||
|
const formattedWeekday = date.toLocaleDateString("en-IN", optionsWeekday);
|
||||||
|
|
||||||
|
return `${formattedDate}, ${formattedTime}, ${formattedWeekday}`;
|
||||||
|
};
|
||||||
|
|
|
||||||