Files
Krikorios a0b297285a Add self-hosted auth, admin API, and owner web dashboard
- Replace Clerk with self-hosted JWT auth (register/login/verify, bcrypt
  passwords, Gmail OTP with console fallback)
- Add lib/db.ts pg pool + transaction helpers; seed script migrates legacy
  Clerk-era schema (drop clerk_id, enforce UUID ids and unique email)
- Add owner-gated admin API: stats, users, drivers CRUD, rides
- Add dashboard/ Vite React owner dashboard (login, overview, users,
  fleet, rides) with dev-server proxy to avoid Expo CORS middleware
- Add scripts/set-owner.mjs for role management
2026-08-23 16:38:41 +03:00

167 lines
5.0 KiB
TypeScript

import * as Location from "expo-location";
import { router } from "expo-router";
import { useEffect, useState } from "react";
import {
ActivityIndicator,
FlatList,
Image,
Text,
TouchableOpacity,
View,
} from "react-native";
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";
import { useSession } from "@/lib/session";
import { useLocationStore } from "@/store";
import { useFetch } from "@/lib/fetch";
import type { Ride } from "@/types/type";
const Home = () => {
const { setUserLocation, setDestinationLocation } = useLocationStore();
const { signOut, user } = useSession();
const { data: recentRides, loading } = useFetch<Ride[]>(
`/(api)/ride/${user?.id}`,
);
const [hasPermissions, setHasPermissions] = useState(false);
const handleSignOut = () => {
signOut();
router.replace("/(auth)/sign-in");
};
const handleDestinationPress = (location: {
latitude: number;
longitude: number;
address: string;
}) => {
setDestinationLocation(location);
router.push("/(root)/find-ride");
};
useEffect(() => {
const requestLocation = async () => {
try {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") return setHasPermissions(false);
setHasPermissions(true);
let location = await Location.getCurrentPositionAsync();
let addressText = "Unknown location";
try {
const address = await Location.reverseGeocodeAsync({
longitude: location.coords?.longitude,
latitude: location.coords?.latitude,
});
if (address[0]) {
addressText = `${address[0].name}, ${address[0].region}`;
}
} catch (geocodeErr) {
console.log("[REVERSE_GEOCODE]: ", geocodeErr);
}
setUserLocation({
latitude: location.coords.latitude,
longitude: location.coords.longitude,
address: addressText,
});
} catch (err) {
console.log("[LOCATION]: ", err);
setHasPermissions(false);
}
};
requestLocation();
}, [setUserLocation]);
return (
<SafeAreaView className="bg-general-500">
<FlatList
data={recentRides?.slice(0, 5)}
renderItem={({ item }) => <RideCard ride={item} />}
className="px-5"
keyboardShouldPersistTaps="handled"
contentContainerStyle={{
paddingBottom: 100,
}}
ListEmptyComponent={() => (
<View className="flex flex-col items-center justify-center">
{!loading ? (
<>
<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?.name || user?.email} 👋
</Text>
<View className="flex flex-row items-center gap-x-1">
<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>
</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]">
{hasPermissions ? (
<Map />
) : (
<View className="flex-1 items-center justify-center bg-white rounded-2xl h-full">
<Text className="text-general-200 text-center font-JakartaMedium px-5">
Location access is off.{"\n"}Enable it in your device
settings to see nearby drivers.
</Text>
</View>
)}
</View>
<Text className="text-xl font-JakartaBold mt-5 mb-3">
Recent Rides
</Text>
</>
)}
/>
</SafeAreaView>
);
};
export default Home;