Fix env issue
parent
4457adfbe5
commit
eeb6683b59
|
|
@ -4,12 +4,30 @@ import { useTabConfig } from "@/constants/config";
|
|||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "@/store";
|
||||
import { initSocket } from "@/services/socket";
|
||||
import { useSnackbar } from "@/contexts/Snackbar";
|
||||
import NetInfo from "@react-native-community/netinfo";
|
||||
|
||||
export default function TabLayout() {
|
||||
const { isLoggedIn } = useSelector((state: RootState) => state.auth);
|
||||
if (!isLoggedIn) return null;
|
||||
const TAB_CONFIG = useTabConfig();
|
||||
|
||||
const { showSnackbar } = useSnackbar();
|
||||
useEffect(() => {
|
||||
const unsubscribe = NetInfo.addEventListener((state) => {
|
||||
const isConnected = state.isConnected;
|
||||
|
||||
if (isConnected === false) {
|
||||
console.log("No internet connection");
|
||||
showSnackbar("No internet connection", "error");
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
initSocket();
|
||||
}, [isLoggedIn]);
|
||||
|
|
|
|||
|
|
@ -29,9 +29,8 @@ export default function HomeScreen() {
|
|||
const { t } = useTranslation();
|
||||
const navigation = useNavigation();
|
||||
const [isSupportModalVisible, setIsSupportModalVisible] = useState(false);
|
||||
const { SoC, SoH, chargingState, lat, lon, loading, error } = useSelector(
|
||||
(state: RootState) => state.telemetry
|
||||
);
|
||||
const { SoC, SoH, chargingState, lat, lon, loading, error, totalDistance } =
|
||||
useSelector((state: RootState) => state.telemetry);
|
||||
const [prevPosition, setPrevPosition] = useState<{
|
||||
lat: number;
|
||||
lon: number;
|
||||
|
|
@ -139,7 +138,7 @@ export default function HomeScreen() {
|
|||
<MetricCard heading={t("home.battery-health")} value={SoH} unit="%" />
|
||||
<MetricCard
|
||||
heading={t("home.total-distance")}
|
||||
value={20009}
|
||||
value={totalDistance}
|
||||
unit="km"
|
||||
/>
|
||||
</View>
|
||||
|
|
@ -156,7 +155,7 @@ export default function HomeScreen() {
|
|||
<LocationOff />
|
||||
<Text style={styles.errorText}>Fetching Location...</Text>
|
||||
</View>
|
||||
) : lat && lon ? (
|
||||
) : lat != null && lon != null ? (
|
||||
<>
|
||||
<View style={styles.mapContainer}>
|
||||
<MapView
|
||||
|
|
@ -192,10 +191,12 @@ export default function HomeScreen() {
|
|||
</TouchableOpacity>
|
||||
</>
|
||||
) : (
|
||||
error && (
|
||||
<View style={styles.errorContainer}>
|
||||
<LocationOff />
|
||||
<Text style={styles.errorText}>Error fetching location</Text>
|
||||
</View>
|
||||
)
|
||||
)}
|
||||
</View>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import "react-native-reanimated";
|
|||
import { I18nextProvider } from "react-i18next";
|
||||
import { Provider } from "react-redux";
|
||||
import { store } from "@/store";
|
||||
import { PaperProvider } from "react-native-paper";
|
||||
import SnackbarProvider from "@/contexts/Snackbar";
|
||||
|
||||
export { ErrorBoundary } from "expo-router";
|
||||
|
||||
|
|
@ -55,6 +57,8 @@ export default function RootLayout() {
|
|||
|
||||
function RootLayoutNav() {
|
||||
return (
|
||||
<PaperProvider>
|
||||
<SnackbarProvider>
|
||||
<Provider store={store}>
|
||||
<I18nextProvider i18n={i18next}>
|
||||
<Stack
|
||||
|
|
@ -67,5 +71,7 @@ function RootLayoutNav() {
|
|||
</Stack>
|
||||
</I18nextProvider>
|
||||
</Provider>
|
||||
</SnackbarProvider>
|
||||
</PaperProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import {
|
|||
TouchableWithoutFeedback,
|
||||
Keyboard,
|
||||
KeyboardAvoidingView,
|
||||
StatusBar,
|
||||
Linking,
|
||||
Platform,
|
||||
} from "react-native";
|
||||
import { useRouter } from "expo-router";
|
||||
import { Formik } from "formik";
|
||||
|
|
@ -22,16 +22,8 @@ import { RootState } from "../../store";
|
|||
import { AUTH_STATUSES } from "@/constants/types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SUPPORT } from "@/constants/config";
|
||||
// import { useNavigation } from "expo-router";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
|
||||
// type VerifyOTPNavigationProp = StackNavigationProp<
|
||||
// RootStackParamList,
|
||||
// "VerifyOTP"
|
||||
// >;
|
||||
|
||||
// import OTPInputView from "@twotalltotems/react-native-otp-input";
|
||||
//handleblue => when input field looses focus (mark as touched)
|
||||
// handleBlur marks the field as touched, when field looses focus
|
||||
export default function WelcomeScreen() {
|
||||
const {
|
||||
status,
|
||||
|
|
@ -42,6 +34,8 @@ export default function WelcomeScreen() {
|
|||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const router = useRouter();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
// const navigation = useNavigation();
|
||||
|
||||
const phoneValidationSchema = Yup.object().shape({
|
||||
|
|
@ -63,10 +57,13 @@ export default function WelcomeScreen() {
|
|||
};
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView style={styles.container} behavior="padding">
|
||||
<StatusBar barStyle="dark-content" backgroundColor="#F3F5F8" />
|
||||
<KeyboardAvoidingView
|
||||
style={styles.container}
|
||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||
keyboardVerticalOffset={Platform.OS === "ios" ? 0 : 0}
|
||||
>
|
||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
||||
<View style={styles.inner}>
|
||||
<View style={[styles.inner, { paddingBottom: insets.bottom }]}>
|
||||
<Text style={styles.title}>{t("onboarding.welcome")}</Text>
|
||||
|
||||
<Formik
|
||||
|
|
@ -82,7 +79,8 @@ export default function WelcomeScreen() {
|
|||
errors,
|
||||
touched,
|
||||
}) => (
|
||||
<View style={styles.form}>
|
||||
<View style={styles.formContainer}>
|
||||
<View style={styles.inputContainer}>
|
||||
<Text style={styles.label}>
|
||||
{t("onboarding.enter-mobile-number")}
|
||||
</Text>
|
||||
|
|
@ -111,6 +109,9 @@ export default function WelcomeScreen() {
|
|||
<Text style={styles.error}>{sendOTPError}</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.bottomSection}>
|
||||
<View style={styles.contactContainer}>
|
||||
<View style={{ flexDirection: "row" }}>
|
||||
<Text>For any queries, </Text>
|
||||
|
|
@ -119,6 +120,7 @@ export default function WelcomeScreen() {
|
|||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
onPress={handleSubmit as unknown as () => void}
|
||||
style={{
|
||||
|
|
@ -141,6 +143,7 @@ export default function WelcomeScreen() {
|
|||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</Formik>
|
||||
</View>
|
||||
|
|
@ -150,12 +153,29 @@ export default function WelcomeScreen() {
|
|||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
padding: 16,
|
||||
backgroundColor: "#F3F5F8",
|
||||
},
|
||||
inner: {
|
||||
flex: 1,
|
||||
},
|
||||
formContainer: {
|
||||
flex: 1,
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
inputContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
bottomSection: {
|
||||
width: "100%",
|
||||
},
|
||||
contactContainer: {
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
position: "absolute",
|
||||
bottom: 110, // slightly above the "Send OTP" button
|
||||
marginBottom: 16,
|
||||
},
|
||||
link: {
|
||||
fontSize: 14,
|
||||
|
|
@ -175,24 +195,12 @@ const styles = StyleSheet.create({
|
|||
fontWeight: "bold",
|
||||
lineHeight: 20,
|
||||
},
|
||||
inner: {
|
||||
flex: 1,
|
||||
justifyContent: "flex-start",
|
||||
position: "relative",
|
||||
},
|
||||
form: {
|
||||
height: "90%",
|
||||
position: "relative",
|
||||
},
|
||||
button: {
|
||||
height: 48,
|
||||
borderRadius: 4,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
marginTop: 20,
|
||||
width: "100%",
|
||||
position: "absolute",
|
||||
bottom: 50,
|
||||
},
|
||||
buttonText: {
|
||||
color: "#FCFCFC",
|
||||
|
|
@ -201,12 +209,6 @@ const styles = StyleSheet.create({
|
|||
fontWeight: "bold",
|
||||
lineHeight: 20,
|
||||
},
|
||||
container: {
|
||||
flex: 1,
|
||||
padding: 16,
|
||||
backgroundColor: "#F3F5F8",
|
||||
paddingTop: 0,
|
||||
},
|
||||
title: {
|
||||
fontSize: 28,
|
||||
fontWeight: "bold",
|
||||
|
|
|
|||
|
|
@ -2,11 +2,14 @@ import React, { useEffect, useState } from "react";
|
|||
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
|
||||
import { setLanguage } from "../../services/i18n/index";
|
||||
import { useRouter, useNavigation } from "expo-router";
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
|
||||
const LanguageScreen = () => {
|
||||
const router = useRouter();
|
||||
const navigation = useNavigation();
|
||||
const [selectedLang, setSelectedLang] = useState<string | null>(null);
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
useEffect(() => {
|
||||
navigation.setOptions({ headerShown: false });
|
||||
|
|
@ -24,9 +27,13 @@ const LanguageScreen = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<StatusBar style="dark" />
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Select Language</Text>
|
||||
<Text style={styles.subtitle}>Please select your preferred language</Text>
|
||||
<Text style={styles.subtitle}>
|
||||
Please select your preferred language
|
||||
</Text>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
|
|
@ -52,6 +59,7 @@ const LanguageScreen = () => {
|
|||
style={[
|
||||
styles.selectButton,
|
||||
selectedLang ? styles.selectEnabled : styles.selectDisabled,
|
||||
{ marginBottom: insets.bottom + 16 },
|
||||
]}
|
||||
onPress={handleSelect}
|
||||
disabled={!selectedLang}
|
||||
|
|
@ -59,6 +67,7 @@ const LanguageScreen = () => {
|
|||
<Text style={styles.selectButtonText}>Select</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -68,7 +77,6 @@ const styles = StyleSheet.create({
|
|||
backgroundColor: "#f3f5f8",
|
||||
paddingTop: 108,
|
||||
paddingHorizontal: 16,
|
||||
paddingBottom: 80,
|
||||
},
|
||||
title: {
|
||||
fontSize: 28,
|
||||
|
|
@ -91,7 +99,7 @@ const styles = StyleSheet.create({
|
|||
justifyContent: "center",
|
||||
},
|
||||
selectedCard: {
|
||||
borderColor: "#008000",
|
||||
borderColor: "#009E71",
|
||||
},
|
||||
languageText: {
|
||||
fontSize: 16,
|
||||
|
|
@ -99,6 +107,7 @@ const styles = StyleSheet.create({
|
|||
},
|
||||
selectButton: {
|
||||
marginTop: "auto",
|
||||
marginBottom: 16,
|
||||
padding: 16,
|
||||
borderRadius: 4,
|
||||
alignItems: "center",
|
||||
|
|
|
|||
|
|
@ -8,9 +8,17 @@ import {
|
|||
ScrollView,
|
||||
} from "react-native";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
import LanguageModal from "@/components/Profile/LangaugeModal";
|
||||
|
||||
export default function ProfileScreen() {
|
||||
const [isLangaugeModalVisible, setLanguageModalVisible] =
|
||||
React.useState(false);
|
||||
|
||||
const toggleLanguageModal = () => {
|
||||
setLanguageModalVisible(!isLangaugeModalVisible);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<ScrollView contentContainerStyle={styles.scrollContent}>
|
||||
<View style={styles.avatarContainer}>
|
||||
<Image
|
||||
|
|
@ -40,11 +48,10 @@ export default function ProfileScreen() {
|
|||
</View>
|
||||
</View>
|
||||
|
||||
{/* Other Menu Items */}
|
||||
<View style={styles.card}>
|
||||
{menuItem("My Vehicle")}
|
||||
<View style={styles.divider} />
|
||||
{menuItem("Language")}
|
||||
{menuItem("Language", () => toggleLanguageModal())}
|
||||
</View>
|
||||
|
||||
<View style={styles.card}>
|
||||
|
|
@ -53,11 +60,16 @@ export default function ProfileScreen() {
|
|||
{menuItem("Logout")}
|
||||
</View>
|
||||
</ScrollView>
|
||||
<LanguageModal
|
||||
onClose={() => setLanguageModalVisible(false)}
|
||||
visible={isLangaugeModalVisible}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const menuItem = (title: string) => (
|
||||
<TouchableOpacity style={styles.menuRow}>
|
||||
const menuItem = (title: string, onPress?: () => void) => (
|
||||
<TouchableOpacity style={styles.menuRow} onPress={onPress}>
|
||||
<Text style={styles.menuText}>{title}</Text>
|
||||
<MaterialIcons name="chevron-right" size={20} color="#555C70" />
|
||||
</TouchableOpacity>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: ["babel-preset-expo"],
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
import BottomSheetModal from "@/components/common/BottomSheetModal"; // adjust path
|
||||
|
||||
import { StyleSheet, Text, TouchableOpacity } from "react-native";
|
||||
import { useEffect, useState } from "react";
|
||||
import { getLanguage, setLanguage } from "@/services/i18n";
|
||||
|
||||
interface CustomerSupportProps {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function LanguageModal({
|
||||
visible,
|
||||
onClose,
|
||||
}: CustomerSupportProps) {
|
||||
const [selectedLang, setSelectedLang] = useState<"en" | "hi">("en");
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const lang = await getLanguage();
|
||||
setSelectedLang(lang === "hi" ? "hi" : "en");
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const handleLanguagePress = (lang: "en" | "hi") => {
|
||||
setSelectedLang(lang);
|
||||
setLanguage(lang);
|
||||
onClose();
|
||||
};
|
||||
return (
|
||||
<BottomSheetModal
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
heading="Select Language"
|
||||
>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.languageCard,
|
||||
selectedLang === "en" && styles.selectedCard,
|
||||
]}
|
||||
onPress={() => handleLanguagePress("en")}
|
||||
>
|
||||
<Text style={styles.languageText}>English</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.languageCard,
|
||||
selectedLang === "hi" && styles.selectedCard,
|
||||
]}
|
||||
onPress={() => handleLanguagePress("hi")}
|
||||
>
|
||||
<Text style={styles.languageText}>हिन्दी</Text>
|
||||
</TouchableOpacity>
|
||||
</BottomSheetModal>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
languageText: {
|
||||
fontSize: 16,
|
||||
color: "#2f465e",
|
||||
},
|
||||
selectedCard: {
|
||||
borderColor: "#009E71",
|
||||
},
|
||||
languageCard: {
|
||||
width: "100%",
|
||||
borderWidth: 1,
|
||||
borderColor: "#d8dde7",
|
||||
borderRadius: 4,
|
||||
padding: 16,
|
||||
marginBottom: 16,
|
||||
justifyContent: "center",
|
||||
},
|
||||
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",
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
import React from "react";
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
Modal,
|
||||
Pressable,
|
||||
StyleProp,
|
||||
ViewStyle,
|
||||
} from "react-native";
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import CloseIcon from "../../assets/icons/close.svg";
|
||||
|
||||
type Props = {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
heading: string;
|
||||
children: React.ReactNode;
|
||||
containerStyle?: StyleProp<ViewStyle>;
|
||||
};
|
||||
|
||||
const BottomSheetModal: React.FC<Props> = ({
|
||||
visible,
|
||||
onClose,
|
||||
heading,
|
||||
children,
|
||||
containerStyle,
|
||||
}) => {
|
||||
return (
|
||||
<Modal visible={visible} animationType="fade" transparent>
|
||||
<StatusBar style="dark" />
|
||||
<View style={styles.overlay}>
|
||||
<View style={[styles.modalContainer, containerStyle]}>
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.headerText}>{heading}</Text>
|
||||
<Pressable onPress={onClose} style={styles.iconButton}>
|
||||
<CloseIcon />
|
||||
</Pressable>
|
||||
</View>
|
||||
<View style={styles.content}>{children}</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default BottomSheetModal;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
flex: 1,
|
||||
justifyContent: "flex-end",
|
||||
backgroundColor: "rgba(0,0,0,0.5)",
|
||||
},
|
||||
modalContainer: {
|
||||
backgroundColor: "#fff",
|
||||
borderTopLeftRadius: 12,
|
||||
borderTopRightRadius: 12,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 16,
|
||||
},
|
||||
header: {
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: "#E5E9F0",
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
headerText: {
|
||||
fontSize: 14,
|
||||
fontWeight: "600",
|
||||
color: "#252A34",
|
||||
},
|
||||
iconButton: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
padding: 10,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
content: {
|
||||
paddingVertical: 16,
|
||||
gap: 16,
|
||||
},
|
||||
});
|
||||
|
|
@ -31,7 +31,7 @@ const CustomSnackbar: React.FC<CustomSnackbarProps> = ({
|
|||
|
||||
return (
|
||||
<Snackbar
|
||||
visible={true}
|
||||
visible={visible}
|
||||
onDismiss={onDismiss}
|
||||
style={[
|
||||
styles.snackbar,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const BatteryStatus: React.FC<BatteryStatusProps> = ({ status, style }) => {
|
|||
case 1:
|
||||
return {
|
||||
text: "Charging",
|
||||
backgroundColor: "#006C4D",
|
||||
backgroundColor: "#DAF5ED",
|
||||
textColor: "#006C4D",
|
||||
};
|
||||
case -1:
|
||||
|
|
@ -28,20 +28,14 @@ const BatteryStatus: React.FC<BatteryStatusProps> = ({ status, style }) => {
|
|||
case 0:
|
||||
return {
|
||||
text: "Idle",
|
||||
backgroundColor: "#565F70",
|
||||
backgroundColor: "#D8DDE7",
|
||||
textColor: "#565F70",
|
||||
};
|
||||
case null:
|
||||
return {
|
||||
text: "---",
|
||||
backgroundColor: "#e2e3e5",
|
||||
textColor: "#6c757d",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
text: "Unknown",
|
||||
backgroundColor: "#f8f9fa",
|
||||
textColor: "#6c757d",
|
||||
text: "---",
|
||||
backgroundColor: "#D8DDE7",
|
||||
textColor: "#565F70",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,31 +1,23 @@
|
|||
import React from "react";
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
Pressable,
|
||||
Linking,
|
||||
Modal,
|
||||
} from "react-native";
|
||||
import BottomSheetModal from "@/components/common/BottomSheetModal"; // adjust path
|
||||
import WhatsappIcon from "../../assets/icons/whatsapp.svg";
|
||||
import CallIcon from "../../assets/icons/call.svg";
|
||||
import EmailIcon from "../../assets/icons/mail.svg";
|
||||
import CloseIcon from "../../assets/icons/close.svg";
|
||||
|
||||
import { Linking, Pressable, StyleSheet, Text, View } from "react-native";
|
||||
import { SUPPORT } from "@/constants/config";
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
|
||||
type Props = {
|
||||
interface CustomerSupportProps {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
};
|
||||
}
|
||||
|
||||
const CustomerSupportModal: React.FC<Props> = ({ visible, onClose }) => {
|
||||
const openWhatsApp = async () => {
|
||||
export default function CustomerSupport({
|
||||
visible,
|
||||
onClose,
|
||||
}: CustomerSupportProps) {
|
||||
const openWhatsApp = () => {
|
||||
const url = `https://wa.me/${
|
||||
SUPPORT.WHATSAPP_NUMBER
|
||||
}?text=${encodeURIComponent(SUPPORT.WHATSAPP_PLACEHOLDER)}`;
|
||||
|
||||
Linking.openURL(url).catch((err) =>
|
||||
console.log("Failed to open WhatsApp:", err)
|
||||
);
|
||||
|
|
@ -43,20 +35,11 @@ const CustomerSupportModal: React.FC<Props> = ({ visible, onClose }) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Modal visible={visible} animationType="fade" transparent>
|
||||
<StatusBar style="dark" />
|
||||
<View style={styles.overlay}>
|
||||
<View style={styles.modalContainer}>
|
||||
{/* Header */}
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.headerText}>Customer Support</Text>
|
||||
<Pressable onPress={onClose} style={styles.iconButton}>
|
||||
<CloseIcon />
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
{/* Buttons */}
|
||||
<View style={styles.content}>
|
||||
<BottomSheetModal
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
heading="Customer Support"
|
||||
>
|
||||
<View style={styles.row}>
|
||||
<Pressable style={styles.secondaryButton} onPress={openWhatsApp}>
|
||||
<WhatsappIcon />
|
||||
|
|
@ -74,53 +57,11 @@ const CustomerSupportModal: React.FC<Props> = ({ visible, onClose }) => {
|
|||
<EmailIcon />
|
||||
<Text style={styles.buttonText}>Email</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
</BottomSheetModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomerSupportModal;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
flex: 1,
|
||||
justifyContent: "flex-end",
|
||||
backgroundColor: "rgba(0,0,0,0.5)",
|
||||
},
|
||||
modalContainer: {
|
||||
backgroundColor: "#fff",
|
||||
borderTopLeftRadius: 12,
|
||||
borderTopRightRadius: 12,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 16,
|
||||
},
|
||||
header: {
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: "#E5E9F0",
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
headerText: {
|
||||
fontSize: 14,
|
||||
fontWeight: "600",
|
||||
color: "#252A34",
|
||||
},
|
||||
iconButton: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
padding: 10,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
content: {
|
||||
paddingVertical: 16,
|
||||
gap: 16,
|
||||
},
|
||||
row: {
|
||||
flexDirection: "row",
|
||||
gap: 16,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const MetricCard: React.FC<MetricCardProps> = ({ heading, value, unit }) => {
|
|||
<View style={styles.textContainer}>
|
||||
<Text style={styles.heading}>{heading}</Text>
|
||||
<Text style={styles.value}>
|
||||
{value ?? "---"} {unit}
|
||||
{value?.toFixed(2) ?? "---"} {unit}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
|||
4
eas.json
4
eas.json
|
|
@ -3,13 +3,13 @@
|
|||
"development": {
|
||||
"env": {
|
||||
"ENV": "development",
|
||||
"BASE_URL": "https://dev-api-service.vecmocon.com/service-buddy"
|
||||
"BASE_URL": "https://dev-driver-saathi-api.vecmocon.com"
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"env": {
|
||||
"ENV": "production",
|
||||
"BASE_URL": "https://dev-api-service.vecmocon.com/service-buddy"
|
||||
"BASE_URL": "https://dev-driver-saathi-api.vecmocon.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"dependencies": {
|
||||
"@expo/vector-icons": "^14.1.0",
|
||||
"@react-native-async-storage/async-storage": "^2.2.0",
|
||||
"@react-native-community/netinfo": "^11.4.1",
|
||||
"@react-navigation/native": "^7.1.6",
|
||||
"@react-navigation/stack": "^7.4.2",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
|
|
@ -30,9 +31,9 @@
|
|||
"react-dom": "19.0.0",
|
||||
"react-i18next": "^15.5.3",
|
||||
"react-native": "0.79.4",
|
||||
"react-native-dotenv": "^3.4.11",
|
||||
"react-native-maps": "^1.24.3",
|
||||
"react-native-otp-entry": "^1.8.5",
|
||||
"react-native-paper": "^5.14.5",
|
||||
"react-native-reanimated": "~3.17.4",
|
||||
"react-native-safe-area-context": "^5.4.0",
|
||||
"react-native-screens": "~4.11.1",
|
||||
|
|
@ -1537,6 +1538,28 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@callstack/react-theme-provider": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@callstack/react-theme-provider/-/react-theme-provider-3.0.9.tgz",
|
||||
"integrity": "sha512-tTQ0uDSCL0ypeMa8T/E9wAZRGKWj8kXP7+6RYgPTfOPs9N07C9xM8P02GJ3feETap4Ux5S69D9nteq9mEj86NA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"deepmerge": "^3.2.0",
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@callstack/react-theme-provider/node_modules/deepmerge": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz",
|
||||
"integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@egjs/hammerjs": {
|
||||
"version": "2.0.17",
|
||||
"resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
|
||||
|
|
@ -3324,6 +3347,15 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-community/netinfo": {
|
||||
"version": "11.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-11.4.1.tgz",
|
||||
"integrity": "sha512-B0BYAkghz3Q2V09BF88RA601XursIEA111tnc2JOaN7axJWmNefmfjZqw/KdSxKZp7CZUuPpjBmz/WCR9uaHYg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react-native": ">=0.59"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native/assets-registry": {
|
||||
"version": "0.79.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.79.4.tgz",
|
||||
|
|
@ -11816,18 +11848,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-dotenv": {
|
||||
"version": "3.4.11",
|
||||
"resolved": "https://registry.npmjs.org/react-native-dotenv/-/react-native-dotenv-3.4.11.tgz",
|
||||
"integrity": "sha512-6vnIE+WHABSeHCaYP6l3O1BOEhWxKH6nHAdV7n/wKn/sciZ64zPPp2NUdEUf1m7g4uuzlLbjgr+6uDt89q2DOg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.4.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/runtime": "^7.20.6"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-edge-to-edge": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-edge-to-edge/-/react-native-edge-to-edge-1.6.0.tgz",
|
||||
|
|
@ -11896,6 +11916,51 @@
|
|||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-paper": {
|
||||
"version": "5.14.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-paper/-/react-native-paper-5.14.5.tgz",
|
||||
"integrity": "sha512-eaIH5bUQjJ/mYm4AkI6caaiyc7BcHDwX6CqNDi6RIxfxfWxROsHpll1oBuwn/cFvknvA8uEAkqLk/vzVihI3AQ==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"example",
|
||||
"docs"
|
||||
],
|
||||
"dependencies": {
|
||||
"@callstack/react-theme-provider": "^3.0.9",
|
||||
"color": "^3.1.2",
|
||||
"use-latest-callback": "^0.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*",
|
||||
"react-native-safe-area-context": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-paper/node_modules/color": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
|
||||
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.3",
|
||||
"color-string": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-paper/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-paper/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-native-reanimated": {
|
||||
"version": "3.17.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.17.5.tgz",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"dependencies": {
|
||||
"@expo/vector-icons": "^14.1.0",
|
||||
"@react-native-async-storage/async-storage": "^2.2.0",
|
||||
"@react-native-community/netinfo": "^11.4.1",
|
||||
"@react-navigation/native": "^7.1.6",
|
||||
"@react-navigation/stack": "^7.4.2",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
|
|
@ -35,9 +36,9 @@
|
|||
"react-dom": "19.0.0",
|
||||
"react-i18next": "^15.5.3",
|
||||
"react-native": "0.79.4",
|
||||
"react-native-dotenv": "^3.4.11",
|
||||
"react-native-maps": "^1.24.3",
|
||||
"react-native-otp-entry": "^1.8.5",
|
||||
"react-native-paper": "^5.14.5",
|
||||
"react-native-reanimated": "~3.17.4",
|
||||
"react-native-safe-area-context": "^5.4.0",
|
||||
"react-native-screens": "~4.11.1",
|
||||
|
|
|
|||
Loading…
Reference in New Issue