diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx new file mode 100644 index 0000000..9917912 --- /dev/null +++ b/app/(auth)/_layout.tsx @@ -0,0 +1,13 @@ +import { Stack } from "expo-router"; + +const AuthLayout = () => { + return ( + + + + + + ); +}; + +export default AuthLayout; diff --git a/app/(auth)/sign-in.tsx b/app/(auth)/sign-in.tsx new file mode 100644 index 0000000..60cae36 --- /dev/null +++ b/app/(auth)/sign-in.tsx @@ -0,0 +1,12 @@ +import { Text } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +const SignIn = () => { + return ( + + SignIn + + ); +}; + +export default SignIn; diff --git a/app/(auth)/sign-up.tsx b/app/(auth)/sign-up.tsx new file mode 100644 index 0000000..be38c49 --- /dev/null +++ b/app/(auth)/sign-up.tsx @@ -0,0 +1,12 @@ +import { Text } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +const SignUp = () => { + return ( + + SignUp + + ); +}; + +export default SignUp; diff --git a/app/(auth)/welcome.tsx b/app/(auth)/welcome.tsx new file mode 100644 index 0000000..72d5f1e --- /dev/null +++ b/app/(auth)/welcome.tsx @@ -0,0 +1,65 @@ +import { CustomButton } from "@/components/custom-button"; +import { onboarding } from "@/constants"; +import { router } from "expo-router"; +import { useRef, useState } from "react"; +import { Image, Text, TouchableOpacity, View } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +import Swiper from "react-native-swiper"; + +const Welcome = () => { + const swiperRef = useRef(null); + const [activeIndex, setActiveIndex] = useState(0); + const isLastSlide = activeIndex === onboarding.length - 1; + + return ( + + router.replace("/(auth)/sign-up")} + className="w-full flex justify-end items-end p-5" + > + Skip + + + } + activeDot={} + index={activeIndex} + onIndexChanged={setActiveIndex} + > + {onboarding.map((item) => ( + + + + + + {item.title} + + + + + {item.description} + + + ))} + + + + isLastSlide + ? router.replace("/(auth)/sign-up") + : swiperRef.current?.scrollBy(1) + } + title={isLastSlide ? "Get Started" : "Next"} + className="w-11/12 mt-10" + /> + + ); +}; + +export default Welcome; diff --git a/app/(root)/(tabs)/_layout.tsx b/app/(root)/(tabs)/_layout.tsx new file mode 100644 index 0000000..e69de29 diff --git a/app/(root)/(tabs)/chat.tsx b/app/(root)/(tabs)/chat.tsx new file mode 100644 index 0000000..16d0727 --- /dev/null +++ b/app/(root)/(tabs)/chat.tsx @@ -0,0 +1,12 @@ +import { Text } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +const Chat = () => { + return ( + + Chat + + ); +}; + +export default Chat; diff --git a/app/(root)/(tabs)/home.tsx b/app/(root)/(tabs)/home.tsx new file mode 100644 index 0000000..4fe8492 --- /dev/null +++ b/app/(root)/(tabs)/home.tsx @@ -0,0 +1,12 @@ +import { Text } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +const Home = () => { + return ( + + Home + + ); +}; + +export default Home; diff --git a/app/(root)/(tabs)/profile.tsx b/app/(root)/(tabs)/profile.tsx new file mode 100644 index 0000000..4d44dfd --- /dev/null +++ b/app/(root)/(tabs)/profile.tsx @@ -0,0 +1,12 @@ +import { Text } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +const Profile = () => { + return ( + + Profile + + ); +}; + +export default Profile; diff --git a/app/(root)/(tabs)/rides.tsx b/app/(root)/(tabs)/rides.tsx new file mode 100644 index 0000000..af2a22b --- /dev/null +++ b/app/(root)/(tabs)/rides.tsx @@ -0,0 +1,12 @@ +import { Text } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +const Rides = () => { + return ( + + Rides + + ); +}; + +export default Rides; diff --git a/app/(root)/_layout.tsx b/app/(root)/_layout.tsx new file mode 100644 index 0000000..e69de29 diff --git a/app/_layout.tsx b/app/_layout.tsx index ae7f26a..e9ca36f 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -7,7 +7,7 @@ import "react-native-reanimated"; // Prevent the splash screen from auto-hiding before asset loading is complete. SplashScreen.preventAutoHideAsync(); -export default function RootLayout() { +const RootLayout = () => { const [loaded] = useFonts({ "Jakarta-Bold": require("../assets/fonts/PlusJakartaSans-Bold.ttf"), "Jakarta-ExtraBold": require("../assets/fonts/PlusJakartaSans-ExtraBold.ttf"), @@ -31,7 +31,10 @@ export default function RootLayout() { return ( - + + ); -} +}; + +export default RootLayout; diff --git a/app/index.tsx b/app/index.tsx index 4fe8492..177abaa 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -1,12 +1,7 @@ -import { Text } from "react-native"; -import { SafeAreaView } from "react-native-safe-area-context"; +import { Redirect } from "expo-router"; -const Home = () => { - return ( - - Home - - ); +const App = () => { + return ; }; -export default Home; +export default App; diff --git a/components/custom-button.tsx b/components/custom-button.tsx new file mode 100644 index 0000000..698bc0d --- /dev/null +++ b/components/custom-button.tsx @@ -0,0 +1,58 @@ +import { ButtonProps } from "@/types/type"; +import React from "react"; +import { Text, TouchableOpacity } from "react-native"; + +const getBgVariantStyle = (variant: ButtonProps["bgVariant"]) => { + switch (variant) { + case "secondary": + return "bg-gray-500"; + case "danger": + return "bg-rose-500"; + case "success": + return "bg-emerald-500"; + case "outline": + return "bg-transparent-500 border-neutral-300 border-[0.5px]"; + default: + return "bg-[#0286ff]"; + } +}; + +const getTextVariantStyle = (variant: ButtonProps["textVariant"]) => { + switch (variant) { + case "primary": + return "text-black"; + case "secondary": + return "text-gray-100"; + case "danger": + return "text-rose-100"; + case "success": + return "text-emerald-100"; + default: + return "text-white"; + } +}; + +export const CustomButton = ({ + onPress, + title, + bgVariant = "primary", + textVariant = "default", + iconLeft: IconLeft, + iconRight: IconRight, + className, + ...props +}: ButtonProps) => ( + + {IconLeft && } + + + {title} + + + {IconRight && } + +); diff --git a/constants/index.ts b/constants/index.ts new file mode 100644 index 0000000..a73b69b --- /dev/null +++ b/constants/index.ts @@ -0,0 +1,100 @@ +import arrowDown from "@/assets/icons/arrow-down.png"; +import arrowUp from "@/assets/icons/arrow-up.png"; +import backArrow from "@/assets/icons/back-arrow.png"; +import chat from "@/assets/icons/chat.png"; +import checkmark from "@/assets/icons/check.png"; +import close from "@/assets/icons/close.png"; +import dollar from "@/assets/icons/dollar.png"; +import email from "@/assets/icons/email.png"; +import eyecross from "@/assets/icons/eyecross.png"; +import google from "@/assets/icons/google.png"; +import home from "@/assets/icons/home.png"; +import list from "@/assets/icons/list.png"; +import lock from "@/assets/icons/lock.png"; +import map from "@/assets/icons/map.png"; +import marker from "@/assets/icons/marker.png"; +import out from "@/assets/icons/out.png"; +import person from "@/assets/icons/person.png"; +import pin from "@/assets/icons/pin.png"; +import point from "@/assets/icons/point.png"; +import profile from "@/assets/icons/profile.png"; +import search from "@/assets/icons/search.png"; +import selectedMarker from "@/assets/icons/selected-marker.png"; +import star from "@/assets/icons/star.png"; +import target from "@/assets/icons/target.png"; +import to from "@/assets/icons/to.png"; +import check from "@/assets/images/check.png"; +import getStarted from "@/assets/images/get-started.png"; +import message from "@/assets/images/message.png"; +import noResult from "@/assets/images/no-result.png"; +import onboarding1 from "@/assets/images/onboarding1.png"; +import onboarding2 from "@/assets/images/onboarding2.png"; +import onboarding3 from "@/assets/images/onboarding3.png"; +import signUpCar from "@/assets/images/signup-car.png"; + +export const images = { + onboarding1, + onboarding2, + onboarding3, + getStarted, + signUpCar, + check, + noResult, + message, +}; + +export const icons = { + arrowDown, + arrowUp, + backArrow, + chat, + checkmark, + close, + dollar, + email, + eyecross, + google, + home, + list, + lock, + map, + marker, + out, + person, + pin, + point, + profile, + search, + selectedMarker, + star, + target, + to, +}; + +export const onboarding = [ + { + id: 1, + title: "The perfect ride is just a tap away!", + description: + "Your journey begins with Ryde. Find your ideal ride effortlessly.", + image: images.onboarding1, + }, + { + id: 2, + title: "Best car in your hands with Ryde", + description: + "Discover the convenience of finding your perfect ride with Ryde", + image: images.onboarding2, + }, + { + id: 3, + title: "Your ride, your way. Let's go!", + description: + "Enter your destination, sit back, and let us take care of the rest.", + image: images.onboarding3, + }, +]; + +export const data = { + onboarding, +}; diff --git a/types/image.d.ts b/types/image.d.ts new file mode 100644 index 0000000..bb75dcc --- /dev/null +++ b/types/image.d.ts @@ -0,0 +1,24 @@ +declare module "*.png" { + const value: any; + export default value; +} + +declare module "*.jpg" { + const value: any; + export default value; +} + +declare module "*.jpeg" { + const value: any; + export default value; +} + +declare module "*.gif" { + const value: any; + export default value; +} + +declare module "*.svg" { + const value: any; + export default value; +} diff --git a/types/type.d.ts b/types/type.d.ts new file mode 100644 index 0000000..79b07fd --- /dev/null +++ b/types/type.d.ts @@ -0,0 +1,139 @@ +import { TextInputProps, TouchableOpacityProps } from "react-native"; + +declare interface Driver { + driver_id: number; + first_name: string; + last_name: string; + profile_image_url: string; + car_image_url: string; + car_seats: number; + rating: number; +} + +declare interface MarkerData { + latitude: number; + longitude: number; + id: number; + title: string; + profile_image_url: string; + car_image_url: string; + car_seats: number; + rating: number; + first_name: string; + last_name: string; + time?: number; + price?: string; +} + +declare interface MapProps { + destinationLatitude?: number; + destinationLongitude?: number; + onDriverTimesCalculated?: (driversWithTimes: MarkerData[]) => void; + selectedDriver?: number | null; + onMapReady?: () => void; +} + +declare interface Ride { + origin_address: string; + destination_address: string; + origin_latitude: number; + origin_longitude: number; + destination_latitude: number; + destination_longitude: number; + ride_time: number; + fare_price: number; + payment_status: string; + driver_id: number; + user_email: string; + created_at: string; + driver: { + first_name: string; + last_name: string; + car_seats: number; + }; +} + +declare interface ButtonProps extends TouchableOpacityProps { + title: string; + bgVariant?: "primary" | "secondary" | "danger" | "outline" | "success"; + textVariant?: "primary" | "default" | "secondary" | "danger" | "success"; + iconLeft?: React.ComponentType; + iconRight?: React.ComponentType; + className?: string; +} + +declare interface GoogleInputProps { + icon?: string; + initialLocation?: string; + containerStyle?: string; + textInputBackgroundColor?: string; + handlePress: ({ + latitude, + longitude, + address, + }: { + latitude: number; + longitude: number; + address: string; + }) => void; +} + +declare interface InputFieldProps extends TextInputProps { + label: string; + icon?: any; + secureTextEntry?: boolean; + labelStyle?: string; + containerStyle?: string; + inputStyle?: string; + iconStyle?: string; + className?: string; +} + +declare interface PaymentProps { + fullName: string; + email: string; + amount: string; + driverId: number; + rideTime: number; +} + +declare interface LocationStore { + userLatitude: number | null; + userLongitude: number | null; + userAddress: string | null; + destinationLatitude: number | null; + destinationLongitude: number | null; + destinationAddress: string | null; + setUserLocation: ({ + latitude, + longitude, + address, + }: { + latitude: number; + longitude: number; + address: string; + }) => void; + setDestinationLocation: ({ + latitude, + longitude, + address, + }: { + latitude: number; + longitude: number; + address: string; + }) => void; +} + +declare interface DriverStore { + drivers: MarkerData[]; + selectedDriver: number | null; + setSelectedDriver: (driverId: number) => void; + setDrivers: (drivers: MarkerData[]) => void; + clearSelectedDriver: () => void; +} + +declare interface DriverCardProps { + item: MarkerData; + selected: number; + setSelected: () => void; +}