diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index c7bbf51..0721283 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -232,7 +232,7 @@ export default function HomeScreen() { ? "warning" : "danger" } - message={daysLeftToPayEmiText} + message={`${daysLeftToPayEmi} ${t("home.days-left-to-pay-emi")}`} subMessage={t("home.pay-now")} redirectPath="/(tabs)/payments" /> @@ -258,28 +258,29 @@ export default function HomeScreen() { unit={t("home.km")} /> - {due_amount && ( - <> - { - router.push("/payments/selectAmount"); - }} - /> + {due_amount && due_amount > 0 ? ( + { + router.push("/payments/selectAmount"); + }} + /> + ) : due_amount == 0 ? ( + router.push("/(tabs)/payments")} + /> + ) : null} - router.push("/(tabs)/payments")} - /> - - )} {loading ? ( - Fetching Location... + + {t("home.fetching-location")} + ) : lat != null && lon != null && !(lat == 0 && lon == 0) ? ( <> diff --git a/app/(tabs)/my-battery.tsx b/app/(tabs)/my-battery.tsx index 47a74ea..8140818 100644 --- a/app/(tabs)/my-battery.tsx +++ b/app/(tabs)/my-battery.tsx @@ -49,19 +49,23 @@ const BatteryDetails = () => { (remaining % (30.44 * 24 * 60 * 60 * 1000)) / (24 * 60 * 60 * 1000) ); - const parts: string[] = []; + durationText = (() => { + const parts: string[] = []; - if (yearsLeft > 0) { - parts.push(`${yearsLeft} year${yearsLeft !== 1 ? "s" : ""}`); - } - if (monthsLeft > 0) { - parts.push(`${monthsLeft} month${monthsLeft !== 1 ? "s" : ""}`); - } - if (daysLeft > 0 || parts.length === 0) { - parts.push(`${daysLeft} day${daysLeft !== 1 ? "s" : ""}`); - } + if (yearsLeft > 0) { + parts.push(`${yearsLeft} ${t("home.year")}`); + } - durationText = parts.join(", "); + if (monthsLeft > 0) { + parts.push(`${monthsLeft} ${t("home.month")}`); + } + + if (daysLeft > 0 || parts.length === 0) { + parts.push(`${daysLeft} ${t("home.day")}`); + } + + return parts.join(", "); + })(); } const formatDate = (date?: Date | null): string => diff --git a/app/(tabs)/payments.tsx b/app/(tabs)/payments.tsx index 4292dfc..c97a5ba 100644 --- a/app/(tabs)/payments.tsx +++ b/app/(tabs)/payments.tsx @@ -169,7 +169,9 @@ export default function PaymentsTabScreen() { } catch (err) { console.error("Error fetching EMI details:", err); const errorMessage = - err instanceof Error ? err.message : "Something went wrong"; + err instanceof Error + ? err.message + : `${t("service.something-went-wrong")}`; showSnackbar(errorMessage, "error"); } finally { setIsLoading(false); @@ -255,7 +257,7 @@ export default function PaymentsTabScreen() { router.push("/user/profile")} textSize={20} boxSize={40} @@ -268,6 +270,7 @@ export default function PaymentsTabScreen() { // Format currency const formatCurrency = (amount: number) => { + console.log(amount, "amount format current"); return `₹${amount.toLocaleString()}`; }; @@ -337,7 +340,9 @@ export default function PaymentsTabScreen() { } catch (err) { console.error("Error fetching payment history:", err); const errorMessage = - err instanceof Error ? err.message : "Something went wrong"; + err instanceof Error + ? err.message + : `${t("service.something-went-wrong")}`; showSnackbar(errorMessage, "error"); } finally { if (isLoadMore) { diff --git a/app/(tabs)/service.tsx b/app/(tabs)/service.tsx index 5cd5ac6..dc60fea 100644 --- a/app/(tabs)/service.tsx +++ b/app/(tabs)/service.tsx @@ -26,6 +26,7 @@ import { BASE_URL } from "@/constants/config"; import { Overlay } from "@/components/common/Overlay"; import CrossIcon from "@/assets/icons/close_white.svg"; import { useTranslation } from "react-i18next"; +import CalendarIcon from "@/assets/icons/calendar.svg"; interface FormValues { serviceType: string | null; @@ -173,17 +174,14 @@ export default function ServiceFormScreen(): JSX.Element { console.log("Submission successful:", response.data); - actions.resetForm(); showSnackbar( `${t("service.service-request-success")}`, "success" ); + actions.resetForm(); } catch (error: any) { console.error("Error during submission:", error); - showSnackbar( - error.message || `${t("service.something-went-wrong")}`, - "error" - ); + showSnackbar(`${t("service.something-went-wrong")}`, "error"); } finally { actions.setSubmitting(false); } @@ -269,8 +267,11 @@ export default function ServiceFormScreen(): JSX.Element { style={styles.inputBoxDate} > - {values.date && values.date.toLocaleString()} + {values.date + ? values.date.toLocaleString() + : `${t("service.select")}`} + {touched.date && errors.date && ( {`${errors.date}`} diff --git a/app/auth/login.tsx b/app/auth/login.tsx index 8ea1725..d989afd 100644 --- a/app/auth/login.tsx +++ b/app/auth/login.tsx @@ -114,9 +114,11 @@ export default function WelcomeScreen() { - For any queries, + {t("onboarding.for-any-queries")} - contact us. + + {t("onboarding.contact-us")} + diff --git a/app/payments/Confirmation.tsx b/app/payments/Confirmation.tsx index 959945b..a7a680f 100644 --- a/app/payments/Confirmation.tsx +++ b/app/payments/Confirmation.tsx @@ -8,6 +8,7 @@ import CheckCircle from "@/assets/icons/check_circle.svg"; import Pending from "@/assets/icons/pending.svg"; import Failed from "@/assets/icons/cancel.svg"; import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useTranslation } from "react-i18next"; const PaymentConfirmationScreen = () => { const router = useRouter(); @@ -86,10 +87,10 @@ const PaymentConfirmationScreen = () => { const displayValue = (value: any) => { return value ? String(value) : "--"; }; - + const { t } = useTranslation(); return ( -
+
@@ -112,37 +113,49 @@ const PaymentConfirmationScreen = () => { - Transaction Details + {`${t( + "payment.transaction-details" + )}`} - Payment mode + {`${t( + "payment.payment-mode" + )}`} {displayValue(paymentOrder?.payment_mode?.[0])} - Paid to + {`${t( + "payment.paid-to" + )}`} {displayValue(paymentOrder?.upi_handle)} - Paid by + {`${t( + "payment.paid-by" + )}`} {displayValue(paymentOrder?.paid_by_upi_handle)} - Order ID + {`${t( + "payment.order-id" + )}`} {displayValue(paymentOrder?.order_id)} - Transaction ID + {`${t( + "payment.transaction-id" + )}`} {displayValue( paymentOrder?.transaction_id || diff --git a/app/payments/TransactionDetails.tsx b/app/payments/TransactionDetails.tsx index 801ff67..021c600 100644 --- a/app/payments/TransactionDetails.tsx +++ b/app/payments/TransactionDetails.tsx @@ -18,6 +18,7 @@ import SuccessIcon from "@/assets/icons/check_circle.svg"; import FailureIcon from "@/assets/icons/cancel.svg"; import PendingIcon from "@/assets/icons/pending.svg"; import Header from "@/components/common/Header"; +import { useTranslation } from "react-i18next"; interface TransactionDetailData { id: number; @@ -61,6 +62,8 @@ export default function TransactionDetailScreen() { fetchTransactionDetail(); }, [paymentId]); + const { t } = useTranslation(); + const fetchTransactionDetail = async () => { try { setIsLoading(true); @@ -78,7 +81,9 @@ export default function TransactionDetailScreen() { } catch (err) { console.error("Error fetching transaction details:", err); const errorMessage = - err instanceof Error ? err.message : "Something went wrong"; + err instanceof Error + ? err.message + : `${t("service.something-went-wrong")}`; showSnackbar(errorMessage, "error"); router.back(); } finally { @@ -132,7 +137,10 @@ export default function TransactionDetailScreen() { return ( <> -
+
{isLoading ? ( @@ -160,30 +168,32 @@ export default function TransactionDetailScreen() { - Transaction Details + + {`${t("payment.transaction-details")}`} + diff --git a/app/payments/payEmi.tsx b/app/payments/payEmi.tsx index ceb929f..c5c7c9f 100644 --- a/app/payments/payEmi.tsx +++ b/app/payments/payEmi.tsx @@ -4,12 +4,12 @@ import { Text, TouchableOpacity, StyleSheet, - SafeAreaView, Alert, Share, Linking, BackHandler, ScrollView, + Dimensions, } from "react-native"; import * as FileSystem from "expo-file-system"; import * as MediaLibrary from "expo-media-library"; @@ -22,79 +22,85 @@ import Header from "@/components/common/Header"; import ShareIcon from "@/assets/icons/share.svg"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSnackbar } from "@/contexts/Snackbar"; - import DownloadIcon from "@/assets/icons/download.svg"; import { payments } from "@/constants/config"; -import { useRouter } from "expo-router"; +import { useFocusEffect, useRouter } from "expo-router"; import { useSocket } from "@/contexts/SocketContext"; import { useTranslation } from "react-i18next"; +const { height: screenHeight } = Dimensions.get("window"); + const UpiPaymentScreen = () => { const dispatch = useDispatch(); + //paymentorder.amount is undefined + console.log("inside payemi ✅✅"); + useEffect(() => { + console.log("inside pay emi useeffect 🔥🔥🔥🔥🔥"); + }, []); const paymentOrder = useSelector( (state: RootState) => state.payments.paymentOrder ); - const router = useRouter(); const { onPaymentConfirmation, offPaymentConfirmation, disconnect } = useSocket(); - const { showSnackbar } = useSnackbar(); - const insets = useSafeAreaInsets(); - useEffect(() => { - let backPressCount = 0; - let backPressTimer: NodeJS.Timeout | null = null; - const handlePaymentConfirmation = (data: any) => { - console.log("Payment confirmation received:", data); - - dispatch( - setPaymentOrder({ - ...paymentOrder, - ...data, - }) + useFocusEffect( + React.useCallback(() => { + let backPressCount = 0; + let backPressTimer: NodeJS.Timeout | null = null; + const backAction = () => { + if (backPressCount === 0) { + backPressCount++; + 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( + "hardwareBackPress", + backAction ); - - offPaymentConfirmation(); - disconnect(); - - router.replace("/payments/Confirmation"); - }; - - onPaymentConfirmation(handlePaymentConfirmation); - - const backAction = () => { - if (backPressCount === 0) { - backPressCount++; - console.log("Press back again to cancel payment"); - showSnackbar("Press back again to cancel payment", "success"); - - backPressTimer = setTimeout(() => { - backPressCount = 0; - }, 2000); - - return true; - } else { + // ✅ Cleanup when screen loses focus + return () => { if (backPressTimer) clearTimeout(backPressTimer); + backHandler.remove(); + }; + }, [offPaymentConfirmation, disconnect, router]) + ); + + useFocusEffect( + React.useCallback(() => { + const handlePaymentConfirmation = (data: any) => { + dispatch(setPaymentOrder({ ...paymentOrder, ...data })); offPaymentConfirmation(); disconnect(); - router.back(); - return true; - } - }; - - const backHandler = BackHandler.addEventListener( - "hardwareBackPress", - backAction - ); - return () => { - offPaymentConfirmation(); - backHandler.remove(); - }; - }, [onPaymentConfirmation, offPaymentConfirmation, router]); + router.replace("/payments/Confirmation"); + }; + onPaymentConfirmation(handlePaymentConfirmation); + return () => { + offPaymentConfirmation(); + }; + }, [ + paymentOrder, + onPaymentConfirmation, + offPaymentConfirmation, + disconnect, + router, + ]) + ); const formatAmount = (amount: number): string => { + if (amount == null || amount == undefined) return `₹${0}`; return `₹${amount.toLocaleString("en-IN")}`; }; @@ -104,14 +110,8 @@ const UpiPaymentScreen = () => { return currentDate > expiryDate; }; - const getExpiryTime = (): string => { - const expiryDate = new Date(paymentOrder.expiry_date); - return expiryDate.toLocaleString("en-IN"); - }; - const getUpiUrl = (): string => { const upiString = paymentOrder.payment_link; - const upiMatch = upiString.match(/upi_string=([^&]+)/); if (upiMatch) { return decodeURIComponent(upiMatch[1]); @@ -125,14 +125,10 @@ const UpiPaymentScreen = () => { showSnackbar(payments.LINK_EXPIRED, "error"); return; } - const upiUrl = getUpiUrl(); console.log("Opening UPI URL:", upiUrl); - dispatch(updatePaymentStatus("processing")); - const canOpenUrl = await Linking.canOpenURL(upiUrl); - if (canOpenUrl) { await Linking.openURL(upiUrl); } else { @@ -169,14 +165,12 @@ const UpiPaymentScreen = () => { showSnackbar("Please grant permission to save images", "error"); return; } - const fileUri = FileSystem.documentDirectory + `qr-${paymentOrder.order_id}.png`; const downloadResult = await FileSystem.downloadAsync( paymentOrder.qr_code_url, fileUri ); - if (downloadResult.status === 200) { const asset = await MediaLibrary.createAssetAsync(downloadResult.uri); await MediaLibrary.createAlbumAsync("Payment QR Codes", asset, false); @@ -190,78 +184,114 @@ const UpiPaymentScreen = () => { }; function handlePaymentDone() { - router.push("/(tabs)/payments"); + router.navigate("/(tabs)/payments"); } const { t } = useTranslation(); return ( - -
- - - - - - {t("payment.amount-to-be-paid")} - - - {formatAmount(paymentOrder?.amount)} - - - - - - - - - - - - {t("payment.share-qr")} + +
+ + + + + + {t("payment.amount-to-be-paid")} - - + + {formatAmount(paymentOrder?.amount)} + + + + + + + + + + {t("payment.share-qr")} + + + + + + {t("payment.download-qr")} + + + - - - {t("payment.download-qr")} + + {t("payment.pay-using-upi")} - - - {t("payment.pay-using-upi")} + + + + {t("payment.confirm-payment")} - + handlePaymentDone()} + style={styles.paymentDone} + > + {t("payment.payment-done")} + + - - - {t("payment.confirm-payment")} - - handlePaymentDone()} - style={styles.paymentDone} - > - {t("payment.payment-done")} - - - - + + ); }; const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "#F3F5F8", + }, + scrollView: { + flex: 1, + }, + scrollContent: { + flexGrow: 1, + }, + content: { + flex: 1, + paddingHorizontal: 16, + paddingVertical: 16, + justifyContent: "space-between", + minHeight: screenHeight * 0.8, // Ensures minimum height for space-between to work + }, + qrFrame: { + backgroundColor: "#FCFCFC", + borderRadius: 8, + padding: 16, + alignItems: "center", + }, + confirm: { + padding: 16, + flexDirection: "column", + backgroundColor: "#FCFCFC", + gap: 16, + borderRadius: 8, + }, doneText: { textAlign: "center", fontWeight: "500", @@ -272,6 +302,7 @@ const styles = StyleSheet.create({ padding: 16, borderWidth: 1, borderColor: "#DCE1E9", + borderRadius: 4, }, confirmTitle: { fontWeight: "400", @@ -279,16 +310,6 @@ const styles = StyleSheet.create({ lineHeight: 14, color: "#252A34", }, - confirm: { - padding: 16, - flexDirection: "column", - backgroundColor: "#FCFCFC", - gap: 16, - }, - container: { - flex: 1, - backgroundColor: "#F3F5F8", - }, loadingContainer: { flex: 1, justifyContent: "center", @@ -315,19 +336,6 @@ const styles = StyleSheet.create({ fontWeight: "600", color: "#253342", }, - content: { - flex: 1, - paddingHorizontal: 16, - paddingBottom: 16, - justifyContent: "space-between", - }, - qrFrame: { - backgroundColor: "#FCFCFC", - borderRadius: 8, - padding: 16, - alignItems: "center", - marginTop: 16, - }, amountSection: { alignItems: "center", marginBottom: 16, diff --git a/app/payments/selectAmount.tsx b/app/payments/selectAmount.tsx index 9371289..9e5be81 100644 --- a/app/payments/selectAmount.tsx +++ b/app/payments/selectAmount.tsx @@ -90,6 +90,8 @@ const SelectAmountScreen = () => { }; }, []); + const { t } = useTranslation(); + useFocusEffect( React.useCallback(() => { console.log( @@ -146,7 +148,7 @@ const SelectAmountScreen = () => { } } catch (err) { console.error(err, "Error in creating order."); - showSnackbar("Something went wrong.", "error"); + showSnackbar(`${t("service.something-went-wrong")}`, "error"); } finally { setIsFetching(false); } @@ -204,7 +206,7 @@ const SelectAmountScreen = () => { return values.customAmount ? parseFloat(values.customAmount) : 0; }; - const paymentAmount = getPaymentAmount(); + const paymentAmount = getPaymentAmount() || 0; const isButtonEnabled = values.paymentType === "due" || @@ -223,8 +225,6 @@ const SelectAmountScreen = () => { : "Select Amount"; } - const { t } = useTranslation(); - return ( { // Improved keyboard offset // keyboardVerticalOffset={Platform.OS === "ios" ? 88 : 0} > -
+
{ disabled={!isButtonEnabled} > {getPaymentAmount() < payments.MIN_AMOUNT ? ( - Select Amount + + {t("payment.select-amount")} + ) : ( Pay ₹{getPaymentAmount().toFixed(2)} diff --git a/app/user/edit_name.tsx b/app/user/edit_name.tsx index bac801f..02e75f6 100644 --- a/app/user/edit_name.tsx +++ b/app/user/edit_name.tsx @@ -1,4 +1,4 @@ -import React, { useTransition } from "react"; +import React, { useState } from "react"; import { View, Text, @@ -22,9 +22,11 @@ import { router } from "expo-router"; import { useSnackbar } from "@/contexts/Snackbar"; import Header from "@/components/common/Header"; import { useTranslation } from "react-i18next"; +import { Overlay } from "@/components/common/Overlay"; export default function EditName() { const { data } = useSelector((state: RootState) => state.user); + const [isLoading, setIsLoading] = useState(); const originalName = data?.name || ""; const dispatch = useDispatch(); const insets = useSafeAreaInsets(); @@ -34,23 +36,27 @@ export default function EditName() { .max(57, "Name cannot exceed 57 characters"), }); const { showSnackbar } = useSnackbar(); + + const { t } = useTranslation(); const handleSave = async (values: { name: string }) => { try { + setIsLoading(true); await api.put(`${BASE_URL}/api/v1/update-user-information`, { name: values.name, mobile: data?.mobile, }); dispatch(setUserData({ name: values.name })); - showSnackbar("Name updated successfully", "success"); + showSnackbar(`${t("profile.name-changed")}`, "success"); router.back(); } catch (error) { + showSnackbar(`${t("service.something-went-wrong")}`, "error"); console.error("Error updating name:", error); + } finally { + setIsLoading(false); } }; - const { t } = useTranslation(); - return ( <>
@@ -124,6 +130,7 @@ export default function EditName() { + ); } diff --git a/assets/icons/calendar.svg b/assets/icons/calendar.svg new file mode 100644 index 0000000..5f7cf77 --- /dev/null +++ b/assets/icons/calendar.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/components/Profile/LangaugeModal.tsx b/components/Profile/LangaugeModal.tsx index dd18de1..54cf898 100644 --- a/components/Profile/LangaugeModal.tsx +++ b/components/Profile/LangaugeModal.tsx @@ -4,6 +4,7 @@ import { StyleSheet, Text, TouchableOpacity } from "react-native"; import { useEffect, useState } from "react"; import { getLanguage, setLanguage } from "@/services/i18n"; import { useTranslation } from "react-i18next"; +import { useSnackbar } from "@/contexts/Snackbar"; interface CustomerSupportProps { visible: boolean; @@ -23,12 +24,15 @@ export default function LanguageModal({ })(); }, []); + const { showSnackbar } = useSnackbar(); + const { t } = useTranslation(); const handleLanguagePress = (lang: "en" | "hi") => { setSelectedLang(lang); setLanguage(lang); onClose(); + showSnackbar(`${t("profile.lang-changed")}`, "success"); }; return ( handleLanguagePress("en")} + disabled={selectedLang === "en"} > English @@ -52,6 +57,7 @@ export default function LanguageModal({ selectedLang === "hi" && styles.selectedCard, ]} onPress={() => handleLanguagePress("hi")} + disabled={selectedLang === "hi"} > हिन्दी diff --git a/components/common/CustomSnackbar.tsx b/components/common/CustomSnackbar.tsx index 1dd1532..20c490b 100644 --- a/components/common/CustomSnackbar.tsx +++ b/components/common/CustomSnackbar.tsx @@ -1,11 +1,15 @@ import React, { useEffect } from "react"; -import { Snackbar, Portal } from "react-native-paper"; -import { StyleSheet, Text } from "react-native"; +import { Snackbar, Portal, Checkbox } from "react-native-paper"; +import { StyleSheet, Text, View } from "react-native"; +import CheckBox from "@/assets/icons/check_circle.svg"; +import CrossIcon from "@/assets/icons/cancel.svg"; interface CustomSnackbarProps { message: string; bgColor: string; textColor: string; + borderColor: string; + icon: React.ReactNode; duration: number; visible: boolean; onDismiss: () => void; @@ -15,6 +19,8 @@ const CustomSnackbar: React.FC = ({ message, bgColor, textColor, + borderColor, + icon, duration, visible, onDismiss, @@ -35,16 +41,29 @@ const CustomSnackbar: React.FC = ({ onDismiss={onDismiss} style={[ styles.snackbar, - { backgroundColor: bgColor, zIndex: 999, elevation: 999 }, + { + backgroundColor: bgColor, + borderColor, + zIndex: 999, + elevation: 999, + }, ]} duration={Snackbar.DURATION_SHORT} > - {message} + + {icon} + {message} + ); }; const styles = StyleSheet.create({ + content: { + flexDirection: "row", + alignItems: "center", + gap: 8, + }, message: { fontStyle: "normal", fontWeight: "bold", @@ -59,6 +78,8 @@ const styles = StyleSheet.create({ borderRadius: 5, marginBottom: 60, color: "#242C3B", + borderWidth: 1, + borderColor: "#B6ECDD", }, }); diff --git a/components/home/BatteryWarrantyCars.tsx b/components/home/BatteryWarrantyCars.tsx index 192e378..da25869 100644 --- a/components/home/BatteryWarrantyCars.tsx +++ b/components/home/BatteryWarrantyCars.tsx @@ -57,9 +57,9 @@ const BatteryWarrantyCard: React.FC = ({ })()} - + - + diff --git a/components/service/IssueSelectorModal.tsx b/components/service/IssueSelectorModal.tsx index b51982d..aae6619 100644 --- a/components/service/IssueSelectorModal.tsx +++ b/components/service/IssueSelectorModal.tsx @@ -101,6 +101,7 @@ export default function IssueSelectorModal({ style={[ styles.clearText, !hasSelection && styles.clearTextDisabled, + selectedValues.length > 0 && { color: "#006C4D" }, // 👈 Add this line ]} > Clear @@ -123,7 +124,7 @@ export default function IssueSelectorModal({ onValueChange={() => toggleValue(option.value)} color={ selectedValues.includes(option.value) - ? "#252A34" + ? "#009E71" : undefined } /> diff --git a/constants/config.ts b/constants/config.ts index 92c5943..bba1977 100644 --- a/constants/config.ts +++ b/constants/config.ts @@ -71,7 +71,7 @@ export const useTabConfig = () => { export const MESSAGES = { AUTHENTICATION: { INVALID_TOKEN: "Invalid Token", - VERIFICATION_FAILED: "Verification failed, try again later", + VERIFICATION_FAILED: "OTP incorrect", }, }; @@ -91,10 +91,10 @@ export const ALERT_STYLES = { }; export const SUPPORT = { - WHATSAPP_NUMBER: "918685846459", + WHATSAPP_NUMBER: "919717116943", WHATSAPP_PLACEHOLDER: "Hi, I need help regarding my vehicle.", - PHONE: "+911234567890", - EMAIL: "support@vecmocon.com", + PHONE: "+919717116943", + EMAIL: "baas@vecmocon.com", EMAIL_SUBJECT: "Support Request", EMAIL_BODY: "Hello,\n\nI need assistance with...", }; diff --git a/contexts/Snackbar.tsx b/contexts/Snackbar.tsx index 6013103..aaa5cb7 100644 --- a/contexts/Snackbar.tsx +++ b/contexts/Snackbar.tsx @@ -1,8 +1,10 @@ import React, { createContext, useContext, useState, ReactNode } from "react"; import CustomSnackbar from "../components/common/CustomSnackbar"; +import CheckIcon from "@/assets/icons/check_circle.svg"; +import CrossIcon from "@/assets/icons/cancel.svg"; interface SnackbarContextProps { - showSnackbar: (message: string, type: "success" | "error") => void; + showSnackbar: (message: string, type: "success" | "error" | "info") => void; } const SnackbarContext = createContext( @@ -30,20 +32,44 @@ export const SnackbarProvider: React.FC = ({ textColor: "", duration: 2, visible: false, + icon: <>, + borderColor: "", }); const showSnackbar = ( message: string, type: "success" | "error" | "info" ) => { - const bgColor = - type === "success" ? "#DFF2E9" : type === "error" ? "#FDEDED" : "#E0F7FA"; - const textColor = - type === "success" ? "#242C3B" : type === "error" ? "#D51D10" : "#00796B"; + let bgColor = ""; + let textColor = ""; + let borderColor = ""; + let icon = <>; + + console.log(type); + + if (type === "success") { + bgColor = "#DFF2E9"; + textColor = "#242C3B"; + borderColor = "#B6ECDD"; + icon = ; + } else if (type === "error") { + bgColor = "#FDEDED"; + textColor = "#D51D10"; + borderColor = "#F5C6CB"; + icon = ; + } else if (type === "info") { + bgColor = "#ffffff"; + textColor = "#055160"; // dark blue text + borderColor = "#B6E0FE"; // optional subtle border + // icon = ; + } + setSnackbar({ message, bgColor, textColor, + borderColor, + icon, duration: 2, visible: true, }); @@ -63,6 +89,8 @@ export const SnackbarProvider: React.FC = ({ message={snackbar.message} bgColor={snackbar.bgColor} textColor={snackbar.textColor} + borderColor={snackbar.borderColor} + icon={snackbar.icon} duration={snackbar.duration} visible={snackbar.visible} onDismiss={hideSnackbar} diff --git a/services/i18n/locals/en.json b/services/i18n/locals/en.json index 06f6b34..ec7fdca 100644 --- a/services/i18n/locals/en.json +++ b/services/i18n/locals/en.json @@ -3,7 +3,8 @@ "welcome": "Welcome to Driver Saathi", "enter-mobile-number": "Enter Mobile Number", "enter-registered-mobile-number": "Enter your registered mobile number", - "for-any-queries-contact-us": "For any queries, contact us", + "for-any-queries": "For any queries, ", + "contact-us": "contact us", "number-not-registered": "Number not registered.", "enter-otp": "Please enter OTP sent to your mobile number", "verify-otp": "Verify OTP", @@ -38,7 +39,8 @@ "day": "day(s)", "alerts": "Alerts", "emi-alert": "14 days left to pay the EMI!", - "km": "km" + "km": "km", + "days-left-to-pay-emi": "days left to pay the EMI!" }, "profile": { "my-account": "My Account", @@ -59,7 +61,9 @@ "customer-support": "Customer Support", "whatsapp": "Whatsapp", "call-us": "Call Us", - "email": "Email" + "email": "Email", + "lang-changed": "भाषा सफलतापूर्वक बदल दी गई", + "fetching-location": "Fetching location..." }, "payment": { "last-emi-details": "Last EMI Details", @@ -85,10 +89,13 @@ "monthly-emi": "Monthly EMI", "installments-paid": "Installments Paid", "emi-paid-till-now": "EMI Paid Till Now", - "transaction-detail": "Transaction Detail", + "transaction-details": "Transaction Details", "transaction-successful": "Transaction successful", "payment-mode": "Payment mode", - "paid-to": "Paid to", + "paid-to": "Paid To", + "paid-by": "Paid By", + "order-id": "Order ID", + "transaction-id": "Transaction ID", "upi-transaction-id": "UPI Transaction ID", "all-emi-paid": "All EMI Paid", "contact-dealer": "For further queries, contact your dealer!", @@ -119,12 +126,11 @@ "supported-formats": "Supported formats include JPG, JPEG and PNG.", "comments": "Comments", "submit": "Submit", - "select-issues-tba": "Select issues", "clear": "Clear", "issues-selected": "issue selected", "service-request-success": "Service request submitted successfully", "something-went-wrong": "Something went wrong!", - "select-valid-time": "Select valid", + "select-valid-time": "Select valid time", "words": "words" }, "battery": { diff --git a/services/i18n/locals/hi.json b/services/i18n/locals/hi.json index ded0623..72ab2af 100644 --- a/services/i18n/locals/hi.json +++ b/services/i18n/locals/hi.json @@ -3,7 +3,8 @@ "welcome": "ड्राइवर साथी में आपका स्वागत है", "enter-mobile-number": "मोबाइल नंबर दर्ज करें", "enter-registered-mobile-number": "अपना पंजीकृत मोबाइल नंबर दर्ज करें", - "for-any-queries-contact-us": "किसी भी प्रकार की सहायता के लिए, हमसे संपर्क करें", + "for-any-queries": "किसी भी प्रकार की सहायता के लिए, ", + "contact-us": " हमसे संपर्क करें", "number-not-registered": "नंबर पंजीकृत नहीं है।", "enter-otp": "कृपया अपने मोबाइल नंबर पर भेजा गया OTP दर्ज करें।", "verify-otp": "ओटीपी वेरिफाई करें", @@ -38,7 +39,9 @@ "day": "दिन", "alerts": "अलर्ट", "emi-alert": "EMI का भुगतान करने के लिए 14 दिन शेष!", - "km": "कि.मी." + "km": "कि.मी.", + "days-left-to-pay-emi": "दिन बाकी हैं ईएमआई भरने के लिए!", + "fetching-location": "स्थान प्राप्त किया जा रहा है..." }, "profile": { "my-account": "मेरा खाता", @@ -59,7 +62,8 @@ "customer-support": "ग्राहक सहायता", "whatsapp": "व्हाट्सएप", "call-us": "हमें कॉल करें", - "email": "ईमेल" + "email": "ईमेल", + "lang-changed": "Language changed successfully" }, "payment": { "last-emi-details": "अंतिम EMI विवरण", @@ -85,10 +89,13 @@ "monthly-emi": "मासिक EMI", "installments-paid": "भुगतान की गई किश्तें", "emi-paid-till-now": "अभी तक भुगतान की गई EMI", - "transaction-detail": "लेनदेन विवरण", + "transaction-details": "लेनदेन विवरण", "transaction-successful": "लेनदेन सफल", "payment-mode": "भुगतान मोड", - "paid-to": "भुगतान किया गया", + "paid-to": "भुगतान प्राप्तकर्ता", + "paid-by": "भुगतानकर्ता", + "order-id": "आर्डर ID", + "transaction-id": "लेनदेन ID", "upi-transaction-id": "यूपीआई ट्रांजैक्शन आईडी", "all-emi-paid": "सभी EMI का भुगतान किया गया", "contact-dealer": "अधिक जानकारी के लिए, अपने डीलर से संपर्क करें!", @@ -96,7 +103,7 @@ "emi-completed": "EMI पूर्ण", "plan-name": "18 महीने की योजना", "view-plan": "प्लान देखें", - "advance-amount": "अतिरिक्त राशि", + "advance-amount": "एडवांस राशि", "failed": "फेल हो गया", "select-amount": "राशि का चयन करें", "pay-amount-due": "बकाया राशि भुगतान करें", @@ -119,7 +126,6 @@ "supported-formats": "समर्थित प्रारूपों में JPG, JPEG और PNG शामिल हैं।", "comments": "टिप्पणियाँ", "submit": "सबमिट करें", - "select-issues-tba": "समस्याओं का चयन करें", "clear": "साफ़ करें", "issues-selected": "समस्याएँ चुनी गई", "service-request-success": "सेवा अनुरोध सफलतापूर्वक सबमिट किया गया", diff --git a/services/socket.ts b/services/socket.ts index af5ca4e..e47a519 100644 --- a/services/socket.ts +++ b/services/socket.ts @@ -106,6 +106,7 @@ export const initSocket = async () => { //get latest telemetry as fallback option token = await fetchToken(); controllingServer = await fetchControllingServer(token); + store.dispatch(setTelemetryLoading()); await fetchLatestTelemetry(token, controllingServer); connectSocket(); } catch (err) { diff --git a/store/authSlice.ts b/store/authSlice.ts index df875e4..675a650 100644 --- a/store/authSlice.ts +++ b/store/authSlice.ts @@ -70,36 +70,52 @@ export const sendOTP = createAsyncThunk( return response.data; // if (!response.data.status) throw new Error(response.data.message); } catch (error: any) { - const serverMessage = error.response?.data?.message; - return rejectWithValue(serverMessage || "Something went wrong"); + let message = "Something went wrong. Please try again."; + + if (axios.isAxiosError(error)) { + // try to read backend message safely + message = error.response?.data?.message || error.message || message; + } else if (error instanceof Error) { + message = error.message; + } + console.log(message, "message"); + return rejectWithValue(message); } } ); -export const verifyOTP = createAsyncThunk( - "auth/verifyOTP", - async (params, { rejectWithValue }) => { - try { - console.log("params", params); - const response = await axios.post( - `${BASE_URL}/api/v1/verify-otp`, - params - ); - if (!response.data.success) throw new Error(response.data.message); +export const verifyOTP = createAsyncThunk< + VerifyOTPResponse, + VerifyOTPParams, + { rejectValue: string } +>("auth/verifyOTP", async (params, { rejectWithValue }) => { + try { + console.log("params", params); + const response = await axios.post( + `${BASE_URL}/api/v1/verify-otp`, + params + ); + if (!response.data.success) return rejectWithValue(response.data.message); - // Store tokens - await AsyncStorage.setItem( - STORAGE_KEYS.AUTH_TOKEN, - response.data.data.token - ); + // Store tokens + await AsyncStorage.setItem( + STORAGE_KEYS.AUTH_TOKEN, + response.data.data.token + ); - return response.data; - } catch (error: any) { - console.log(error.message); - return rejectWithValue(MESSAGES.AUTHENTICATION.VERIFICATION_FAILED); + return response.data; + } catch (error: any) { + let message = "Something went wrong. Please try again."; + + if (axios.isAxiosError(error)) { + message = error.response?.data?.message || error.message || message; + } else if (error instanceof Error) { + message = error.message; } + console.log(message, "message"); + return rejectWithValue(message); } -); +}); export const logout = createAsyncThunk( "auth/logout", @@ -173,7 +189,7 @@ const authSlice = createSlice({ }) .addCase(verifyOTP.rejected, (state, action) => { state.status = AUTH_STATUSES.FAILED; - state.verifyOTPError = action.error.message || "Failed to verify OTP"; + state.verifyOTPError = action.payload || "Failed to verify OTP"; }) .addCase(logout.pending, (state) => { state.status = AUTH_STATUSES.LOADING;