home page initial ui implemented
This commit is contained in:
committed by
GitHub
parent
7f0d38eb3c
commit
b313417609
@@ -10,7 +10,7 @@ export async function POST(req: Request) {
|
|||||||
error: "Missing required fields!",
|
error: "Missing required fields!",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: 400,
|
status: 404,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,6 @@ export async function POST(req: Request) {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
||||||
return Response.json({ error });
|
return Response.json({ error }, { status: 500 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ const SignIn = () => {
|
|||||||
|
|
||||||
<Link
|
<Link
|
||||||
href="/sign-up"
|
href="/sign-up"
|
||||||
className="text-lg text-center text-general-200 mt-10"
|
className="text-base text-center text-general-200 mt-10"
|
||||||
>
|
>
|
||||||
<Text>Don't have an account? </Text>
|
<Text>Don't have an account? </Text>
|
||||||
<Text className="text-primary-500">Sign up</Text>
|
<Text className="text-primary-500">Sign up</Text>
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ const SignUp = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await signUp.create({
|
await signUp.create({
|
||||||
|
firstName: form.name,
|
||||||
|
lastName: "",
|
||||||
emailAddress: form.email,
|
emailAddress: form.email,
|
||||||
password: form.password,
|
password: form.password,
|
||||||
});
|
});
|
||||||
@@ -121,7 +123,7 @@ const SignUp = () => {
|
|||||||
name: value,
|
name: value,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
autoCapitalize="sentences"
|
autoCapitalize="words"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<InputField
|
<InputField
|
||||||
@@ -162,7 +164,7 @@ const SignUp = () => {
|
|||||||
|
|
||||||
<Link
|
<Link
|
||||||
href="/sign-in"
|
href="/sign-in"
|
||||||
className="text-lg text-center text-general-200 mt-10"
|
className="text-base text-center text-general-200 mt-10"
|
||||||
>
|
>
|
||||||
<Text>Already have an account? </Text>
|
<Text>Already have an account? </Text>
|
||||||
<Text className="text-primary-500">Sign in</Text>
|
<Text className="text-primary-500">Sign in</Text>
|
||||||
@@ -228,7 +230,7 @@ const SignUp = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Text className="text-base text-gray-400 font-Jakarta text-center mt-2">
|
<Text className="text-base text-gray-400 font-Jakarta text-center mt-2">
|
||||||
You've succesfully verified your accound.
|
You've succesfully verified your account.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<CustomButton
|
<CustomButton
|
||||||
|
|||||||
+194
-6
@@ -1,15 +1,203 @@
|
|||||||
import { SignedIn, useUser } from "@clerk/clerk-expo";
|
import { useUser } from "@clerk/clerk-expo";
|
||||||
import { Text } from "react-native";
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
FlatList,
|
||||||
|
Image,
|
||||||
|
Text,
|
||||||
|
TouchableOpacity,
|
||||||
|
View,
|
||||||
|
} from "react-native";
|
||||||
import { SafeAreaView } from "react-native-safe-area-context";
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
|
|
||||||
|
import { GoogleTextInput } from "@/components/google-text-input";
|
||||||
|
import { Map } from "@/components/map";
|
||||||
|
import { RideCard } from "@/components/ride-card";
|
||||||
|
import { icons, images } from "@/constants";
|
||||||
|
|
||||||
|
const recentRides = [
|
||||||
|
{
|
||||||
|
ride_id: "1",
|
||||||
|
origin_address: "Kathmandu, Nepal",
|
||||||
|
destination_address: "Pokhara, Nepal",
|
||||||
|
origin_latitude: 27.717245,
|
||||||
|
origin_longitude: 85.323961,
|
||||||
|
destination_latitude: 28.209583,
|
||||||
|
destination_longitude: 83.985567,
|
||||||
|
ride_time: 391,
|
||||||
|
fare_price: 19500.0,
|
||||||
|
payment_status: "paid",
|
||||||
|
driver_id: 2,
|
||||||
|
user_id: "1",
|
||||||
|
user_email: "betatesteruserr@gmail.com",
|
||||||
|
created_at: "2024-08-12 05:19:20.620007",
|
||||||
|
driver: {
|
||||||
|
driver_id: "2",
|
||||||
|
first_name: "David",
|
||||||
|
last_name: "Brown",
|
||||||
|
profile_image_url:
|
||||||
|
"https://ucarecdn.com/6ea6d83d-ef1a-483f-9106-837a3a5b3f67/-/preview/1000x666/",
|
||||||
|
car_image_url:
|
||||||
|
"https://ucarecdn.com/a3872f80-c094-409c-82f8-c9ff38429327/-/preview/930x932/",
|
||||||
|
car_seats: 5,
|
||||||
|
rating: "4.60",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ride_id: "2",
|
||||||
|
origin_address: "Jalkot, MH",
|
||||||
|
destination_address: "Pune, Maharashtra, India",
|
||||||
|
origin_latitude: 18.609116,
|
||||||
|
origin_longitude: 77.165873,
|
||||||
|
destination_latitude: 18.52043,
|
||||||
|
destination_longitude: 73.856744,
|
||||||
|
ride_time: 491,
|
||||||
|
fare_price: 24500.0,
|
||||||
|
payment_status: "paid",
|
||||||
|
driver_id: 1,
|
||||||
|
user_id: "1",
|
||||||
|
user_email: "betatesteruserr@gmail.com",
|
||||||
|
created_at: "2024-08-12 06:12:17.683046",
|
||||||
|
driver: {
|
||||||
|
driver_id: "1",
|
||||||
|
first_name: "James",
|
||||||
|
last_name: "Wilson",
|
||||||
|
profile_image_url:
|
||||||
|
"https://ucarecdn.com/dae59f69-2c1f-48c3-a883-017bcf0f9950/-/preview/1000x666/",
|
||||||
|
car_image_url:
|
||||||
|
"https://ucarecdn.com/a2dc52b2-8bf7-4e49-9a36-3ffb5229ed02/-/preview/465x466/",
|
||||||
|
car_seats: 4,
|
||||||
|
rating: "4.80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ride_id: "3",
|
||||||
|
origin_address: "Zagreb, Croatia",
|
||||||
|
destination_address: "Rijeka, Croatia",
|
||||||
|
origin_latitude: 45.815011,
|
||||||
|
origin_longitude: 15.981919,
|
||||||
|
destination_latitude: 45.327063,
|
||||||
|
destination_longitude: 14.442176,
|
||||||
|
ride_time: 124,
|
||||||
|
fare_price: 6200.0,
|
||||||
|
payment_status: "paid",
|
||||||
|
driver_id: 1,
|
||||||
|
user_id: "1",
|
||||||
|
user_email: "betatesteruserr@gmail.com",
|
||||||
|
created_at: "2024-08-12 08:49:01.809053",
|
||||||
|
driver: {
|
||||||
|
driver_id: "1",
|
||||||
|
first_name: "James",
|
||||||
|
last_name: "Wilson",
|
||||||
|
profile_image_url:
|
||||||
|
"https://ucarecdn.com/dae59f69-2c1f-48c3-a883-017bcf0f9950/-/preview/1000x666/",
|
||||||
|
car_image_url:
|
||||||
|
"https://ucarecdn.com/a2dc52b2-8bf7-4e49-9a36-3ffb5229ed02/-/preview/465x466/",
|
||||||
|
car_seats: 4,
|
||||||
|
rating: "4.80",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ride_id: "4",
|
||||||
|
origin_address: "Okayama, Japan",
|
||||||
|
destination_address: "Osaka, Japan",
|
||||||
|
origin_latitude: 34.655531,
|
||||||
|
origin_longitude: 133.919795,
|
||||||
|
destination_latitude: 34.693725,
|
||||||
|
destination_longitude: 135.502254,
|
||||||
|
ride_time: 159,
|
||||||
|
fare_price: 7900.0,
|
||||||
|
payment_status: "paid",
|
||||||
|
driver_id: 3,
|
||||||
|
user_id: "1",
|
||||||
|
user_email: "betatesteruserr@gmail.com",
|
||||||
|
created_at: "2024-08-12 18:43:54.297838",
|
||||||
|
driver: {
|
||||||
|
driver_id: "3",
|
||||||
|
first_name: "Michael",
|
||||||
|
last_name: "Johnson",
|
||||||
|
profile_image_url:
|
||||||
|
"https://ucarecdn.com/0330d85c-232e-4c30-bd04-e5e4d0e3d688/-/preview/826x822/",
|
||||||
|
car_image_url:
|
||||||
|
"https://ucarecdn.com/289764fb-55b6-4427-b1d1-f655987b4a14/-/preview/930x932/",
|
||||||
|
car_seats: 4,
|
||||||
|
rating: "4.70",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
|
const isLoading = false;
|
||||||
|
|
||||||
|
const handleSignOut = () => {};
|
||||||
|
const handleDestinationPress = () => {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView>
|
<SafeAreaView className="bg-general-500">
|
||||||
<SignedIn>
|
<FlatList
|
||||||
<Text>Hello {user?.emailAddresses[0].emailAddress}</Text>
|
data={recentRides?.slice(0, 5)}
|
||||||
</SignedIn>
|
renderItem={({ item }) => <RideCard ride={item} />}
|
||||||
|
className="px-5"
|
||||||
|
keyboardShouldPersistTaps="handled"
|
||||||
|
contentContainerStyle={{
|
||||||
|
paddingBottom: 100,
|
||||||
|
}}
|
||||||
|
ListEmptyComponent={() => (
|
||||||
|
<View className="flex flex-col items-center justify-center">
|
||||||
|
{!isLoading ? (
|
||||||
|
<>
|
||||||
|
<Image
|
||||||
|
source={images.noResult}
|
||||||
|
alt="No recent rides found"
|
||||||
|
className="w-40 h-40"
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
<Text className="text-sm">No recent rides found.</Text>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<ActivityIndicator size="small" color="#000" />
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
ListHeaderComponent={() => (
|
||||||
|
<>
|
||||||
|
<View className="flex flex-row items-center justify-between my-5">
|
||||||
|
<Text
|
||||||
|
className="text-base font-JakartaExtraBold"
|
||||||
|
numberOfLines={1}
|
||||||
|
>
|
||||||
|
Welcome{" "}
|
||||||
|
{user?.firstName || user?.emailAddresses[0].emailAddress} 👋
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={handleSignOut}
|
||||||
|
className="justify-center items-center w-10 h-10 rounded-full bg-white"
|
||||||
|
>
|
||||||
|
<Image source={icons.out} className="w-4 h-4" alt="Logout" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<GoogleTextInput
|
||||||
|
icon={icons.search}
|
||||||
|
containerStyles="bg-white shadow-md shadow-neutral-300"
|
||||||
|
handlePress={handleDestinationPress}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text className="text-xl font-JakartaBold mt-5 mb-3">
|
||||||
|
Your current location
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<View className="flex flex-row items-center bg-transparent h-[300px]">
|
||||||
|
<Map />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text className="text-xl font-JakartaBold mt-5 mb-3">
|
||||||
|
Recent Rides
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ClerkProvider, ClerkLoaded } from "@clerk/clerk-expo";
|
|||||||
import { useFonts } from "expo-font";
|
import { useFonts } from "expo-font";
|
||||||
import { Stack } from "expo-router";
|
import { Stack } from "expo-router";
|
||||||
import * as SplashScreen from "expo-splash-screen";
|
import * as SplashScreen from "expo-splash-screen";
|
||||||
|
import { StatusBar } from "expo-status-bar";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import "react-native-reanimated";
|
import "react-native-reanimated";
|
||||||
|
|
||||||
@@ -43,6 +44,8 @@ const RootLayout = () => {
|
|||||||
<Stack.Screen name="(root)" options={{ headerShown: false }} />
|
<Stack.Screen name="(root)" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="(auth)" options={{ headerShown: false }} />
|
<Stack.Screen name="(auth)" options={{ headerShown: false }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
|
<StatusBar style="dark" />
|
||||||
</ClerkLoaded>
|
</ClerkLoaded>
|
||||||
</ClerkProvider>
|
</ClerkProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { GoogleInputProps } from "@/types/type";
|
||||||
|
import { Text, View } from "react-native";
|
||||||
|
|
||||||
|
export const GoogleTextInput = ({
|
||||||
|
icon,
|
||||||
|
initialLocation,
|
||||||
|
containerStyles,
|
||||||
|
textInputBackgroundColor,
|
||||||
|
handlePress,
|
||||||
|
}: GoogleInputProps) => (
|
||||||
|
<View
|
||||||
|
className={`flex flex-row items-center justify-center realtive z-50 rounded-xl mb-5 ${containerStyles}`}
|
||||||
|
>
|
||||||
|
<Text>Google Text Input</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Text, View } from "react-native";
|
||||||
|
|
||||||
|
export const Map = () => {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Text>Map</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { Image, Text, View } from "react-native";
|
||||||
|
|
||||||
|
import { icons } from "@/constants";
|
||||||
|
import { formatDate, formatTime } from "@/lib/utils";
|
||||||
|
import type { Ride } from "@/types/type";
|
||||||
|
|
||||||
|
export const RideCard = ({ ride }: { ride: Ride }) => {
|
||||||
|
const {
|
||||||
|
destination_latitude,
|
||||||
|
destination_longitude,
|
||||||
|
origin_address,
|
||||||
|
destination_address,
|
||||||
|
created_at,
|
||||||
|
ride_time,
|
||||||
|
driver,
|
||||||
|
payment_status,
|
||||||
|
} = ride;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="flex flex-row items-center justify-center bg-white rounded-lg shadow-sm shadow-neutral-300 mb-3">
|
||||||
|
<View className="flex flex-col items-center justify-center p-3">
|
||||||
|
<View className="flex flex-row items-center justify-between">
|
||||||
|
<Image
|
||||||
|
source={{
|
||||||
|
uri: `https://maps.geoapify.com/v1/staticmap?style=osm-bright&width=600&height=400¢er=lonlat:${destination_longitude},${destination_latitude}&zoom=14&apiKey=${process.env.EXPO_PUBLIC_GEOAPIFY_API_KEY}`,
|
||||||
|
}}
|
||||||
|
className="w-[80px] h-[90px] rounded-lg"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<View className="flex flex-col mx-5 gap-y-5 flex-1">
|
||||||
|
<View className="flex flex-row items-center gap-x-2">
|
||||||
|
<Image source={icons.to} alt="Origin" className="w-5 h-5" />
|
||||||
|
|
||||||
|
<Text className="font-JakartaMedium" numberOfLines={1}>
|
||||||
|
{origin_address}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="flex flex-row items-center gap-x-2">
|
||||||
|
<Image
|
||||||
|
source={icons.point}
|
||||||
|
alt="Destination"
|
||||||
|
className="w-5 h-5"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Text className="font-JakartaMedium" numberOfLines={1}>
|
||||||
|
{destination_address}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="flex flex-col w-full mt-5 bg-general-500 rounded-lg p-3 items-start justify-center">
|
||||||
|
<View className="flex flex-row items-center w-full justify-between mb-5">
|
||||||
|
<Text className="font-JakartaMedium text-gray-500 text-xs">
|
||||||
|
Date & Time
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text className="font-JakartaMedium text-gray-500 text-xs">
|
||||||
|
{formatDate(created_at)}, {formatTime(ride_time)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="flex flex-row items-center w-full justify-between mb-5">
|
||||||
|
<Text className="font-JakartaMedium text-gray-500 text-xs">
|
||||||
|
Driver
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text className="font-JakartaMedium text-gray-500 text-xs">
|
||||||
|
{driver.first_name} {driver.last_name}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="flex flex-row items-center w-full justify-between mb-5">
|
||||||
|
<Text className="font-JakartaMedium text-gray-500 text-xs">
|
||||||
|
Car Seats
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text className="font-JakartaMedium text-gray-500 text-xs">
|
||||||
|
{driver.car_seats}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="flex flex-row items-center w-full justify-between mb-5">
|
||||||
|
<Text className="font-JakartaMedium text-gray-500 text-xs">
|
||||||
|
Payment Status
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Text
|
||||||
|
className={`font-JakartaMedium capitalize text-gray-500 text-xs ${payment_status === "paid" ? "text-emerald-500" : "text-rose-500"}`}
|
||||||
|
>
|
||||||
|
{payment_status}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
Vendored
+3
@@ -9,6 +9,9 @@ declare global {
|
|||||||
|
|
||||||
// postgres db url (neon db)
|
// postgres db url (neon db)
|
||||||
DATABASE_URL: string;
|
DATABASE_URL: string;
|
||||||
|
|
||||||
|
// geoapify api key
|
||||||
|
EXPO_PUBLIC_GEOAPIFY_API_KEY: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { Ride } from "@/types/type";
|
||||||
|
|
||||||
|
export const sortRides = (rides: Ride[]): Ride[] => {
|
||||||
|
const result = rides.sort((a, b) => {
|
||||||
|
const dateA = new Date(`${a.created_at}T${a.ride_time}`);
|
||||||
|
const dateB = new Date(`${b.created_at}T${b.ride_time}`);
|
||||||
|
return dateB.getTime() - dateA.getTime();
|
||||||
|
});
|
||||||
|
|
||||||
|
return result.reverse();
|
||||||
|
};
|
||||||
|
|
||||||
|
export function formatTime(minutes: number): string {
|
||||||
|
const formattedMinutes = +minutes?.toFixed(0) || 0;
|
||||||
|
|
||||||
|
if (formattedMinutes < 60) {
|
||||||
|
return `${minutes} min`;
|
||||||
|
} else {
|
||||||
|
const hours = Math.floor(formattedMinutes / 60);
|
||||||
|
const remainingMinutes = formattedMinutes % 60;
|
||||||
|
return `${hours}h ${remainingMinutes}m`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDate(dateString: string): string {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
const day = date.getDate();
|
||||||
|
const monthNames = [
|
||||||
|
"Jan",
|
||||||
|
"Feb",
|
||||||
|
"Mar",
|
||||||
|
"Apr",
|
||||||
|
"May",
|
||||||
|
"Jun",
|
||||||
|
"Jul",
|
||||||
|
"Aug",
|
||||||
|
"Sep",
|
||||||
|
"Oct",
|
||||||
|
"Nov",
|
||||||
|
"Dec",
|
||||||
|
];
|
||||||
|
const month = monthNames[date.getMonth()];
|
||||||
|
const year = date.getFullYear();
|
||||||
|
|
||||||
|
return `${day < 10 ? "0" + day : day} ${month} ${year}`;
|
||||||
|
}
|
||||||
Vendored
+1
-1
@@ -65,7 +65,7 @@ declare interface ButtonProps extends TouchableOpacityProps {
|
|||||||
declare interface GoogleInputProps {
|
declare interface GoogleInputProps {
|
||||||
icon?: string;
|
icon?: string;
|
||||||
initialLocation?: string;
|
initialLocation?: string;
|
||||||
containerStyle?: string;
|
containerStyles?: string;
|
||||||
textInputBackgroundColor?: string;
|
textInputBackgroundColor?: string;
|
||||||
handlePress: ({
|
handlePress: ({
|
||||||
latitude,
|
latitude,
|
||||||
|
|||||||
Reference in New Issue
Block a user